Basic support of parsing scripts

This commit is contained in:
Selim Mustafaev 2021-05-09 08:07:38 +03:00
parent 9173093dbf
commit 888e1967c4
10 changed files with 112 additions and 17 deletions

View File

@ -3,5 +3,6 @@ project(btcexplorer)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
add_executable(btcexplorer main.cpp Models/Block.cpp Models/Block.h Models/Transaction.cpp Models/Transaction.h Models/VarInt.cpp Models/VarInt.h Models/TxInput.cpp Models/TxInput.h Models/TxOutput.cpp Models/TxOutput.h Models/Script.cpp Models/Script.h)

View File

@ -9,8 +9,8 @@ Block::Block(const std::byte* pBlock, size_t size): _header{} {
auto pTxData = pBlock + sizeof(BlockHeader) + txCount.size();
for(size_t i = 0; i < txCount.value(); ++i) {
Transaction transaction(pTxData);
_transactions.push_back(transaction);
pTxData += transaction.size();
_transactions.emplace_back(std::move(transaction));
}
}

View File

@ -1,5 +1,52 @@
//
// Created by Selim Mustafaev on 05.05.2021.
//
#include "Script.h"
#include <iostream>
#include <iomanip>
Script::Script(std::span<uint8_t> data) {
for(auto iter = data.begin(); iter != data.end();) {
ScriptOperation operation;
uint8_t* ptr = data.data();
size_t bytes = data.size_bytes();
operation.opCode = OpCode(*iter++);
//std::cout << "found opcode: " << std::showbase << std::hex << std::setw(4) << (int)operation.opCode << std::dec << std::endl;
if(operation.opCode <= OpCode::OP_PUSHDATA4) {
size_t dataSize = 0;
if(operation.opCode < OpCode::OP_PUSHDATA1) {
dataSize = operation.opCode;
} else if(operation.opCode == OpCode::OP_PUSHDATA1) {
dataSize = *iter++;
} else if(operation.opCode == OpCode::OP_PUSHDATA2) {
dataSize = *((uint16_t*)&iter[0]);
iter += 2;
} else if(operation.opCode == OpCode::OP_PUSHDATA4) {
dataSize = *((uint32_t *) &iter[0]);
iter += 4;
}
operation.input = std::vector(iter, iter + dataSize);
iter += dataSize;
}
_operations.emplace_back(operation);
}
_type = getType();
}
Script::Script() {
}
ScriptType Script::getType() {
if(_operations[0].opCode <= OpCode::OP_PUSHDATA4 && _operations[1].opCode == OpCode::OP_CHECKSIG) {
return ScriptType::P2PK;
} else if(_operations[0].opCode == OpCode::OP_DUP
&& _operations[1].opCode == OpCode::OP_HASH160
&& _operations[2].opCode <= OpCode::OP_PUSHDATA4
&& _operations[3].opCode == OpCode::OP_EQUALVERIFY
&& _operations[4].opCode == OpCode::OP_CHECKSIG)
{
return ScriptType::P2PKH;
} else {
throw std::runtime_error("Unsupported script type");
}
}

View File

@ -1,7 +1,12 @@
#ifndef BTCEXPLORER_SCRIPT_H
#define BTCEXPLORER_SCRIPT_H
enum OpCode {
#include <vector>
#include <cstddef>
#include <cstdint>
#include <span>
enum OpCode: uint8_t {
// push value
OP_0 = 0x00,
OP_FALSE = OP_0,
@ -142,8 +147,26 @@ enum OpCode {
OP_INVALIDOPCODE = 0xff,
};
class Script {
enum ScriptType {
P2PK, P2PKH
};
struct ScriptOperation {
OpCode opCode;
std::vector<uint8_t> input;
};
class Script {
private:
std::vector<ScriptOperation> _operations;
ScriptType _type;
private:
ScriptType getType();
public:
Script();
explicit Script(std::span<uint8_t> data);
};
#endif //BTCEXPLORER_SCRIPT_H

View File

@ -9,7 +9,7 @@ Transaction::Transaction(const std::byte *data) {
for(size_t i = 0; i < inputCount.value(); ++i) {
TxInput input(curPtr);
curPtr += input.size();
_inputs.push_back(input);
_inputs.emplace_back(std::move(input));
}
VarInt outputCount(curPtr);
@ -17,7 +17,7 @@ Transaction::Transaction(const std::byte *data) {
for(size_t i = 0; i < outputCount.value(); ++i) {
TxOutput output(curPtr);
curPtr += output.size();
_outputs.push_back(output);
_outputs.emplace_back(std::move(output));
}
_lockTime = *((uint32_t*)curPtr);

View File

@ -1,6 +1,7 @@
#include "TxInput.h"
#include "VarInt.h"
#include <algorithm>
#include <iostream>
TxInput::TxInput(const std::byte *data) {
std::copy_n(data, _txId.size(), _txId.begin());
@ -8,10 +9,12 @@ TxInput::TxInput(const std::byte *data) {
_vOut = *reinterpret_cast<const uint32_t*>(data);
data += sizeof(_vOut);
VarInt scriptSigSize(data);
// TODO: save ScriptSig
data += scriptSigSize.size();
//std::cout << "=== Creating signature script of size: " << scriptSigSize.value() << std::endl;
_signatureScript = std::make_unique<Script>(std::span((uint8_t*)data, scriptSigSize.value()));
data += scriptSigSize.value();
_sequence = *reinterpret_cast<const uint32_t*>(data);
_size = _txId.size() + sizeof(_vOut) + scriptSigSize.size() + scriptSigSize.value() + sizeof(_sequence);
}

View File

@ -1,7 +1,12 @@
#ifndef BTCEXPLORER_TXINPUT_H
#define BTCEXPLORER_TXINPUT_H
#include "Script.h"
#include <cstdint>
#include <cstddef>
#include <array>
#include <memory>
class TxInput {
private:
@ -9,6 +14,7 @@ private:
uint32_t _vOut;
uint32_t _sequence;
size_t _size;
std::unique_ptr<Script> _signatureScript;
public:
explicit TxInput(const std::byte* data);

View File

@ -1,12 +1,16 @@
#include "TxOutput.h"
#include "VarInt.h"
#include <iostream>
TxOutput::TxOutput(const std::byte *data) {
_value = *reinterpret_cast<const uint64_t*>(data);
data += sizeof(_value);
VarInt scriptPubKeySize(data);
data += scriptPubKeySize.size();
// TODO: Read ScriptSizeKey
//std::cout << "=== Creating PubKey script of size: " << scriptPubKeySize.value() << std::endl;
_pubKeyScript = std::make_unique<Script>(std::span((uint8_t*)data, scriptPubKeySize.value()));
_size = sizeof(_value) + scriptPubKeySize.size() + scriptPubKeySize.value();
}

View File

@ -1,13 +1,17 @@
#ifndef BTCEXPLORER_TXOUTPUT_H
#define BTCEXPLORER_TXOUTPUT_H
#include "Script.h"
#include <cstdint>
#include <cstddef>
#include <memory>
class TxOutput {
private:
uint64_t _value;
size_t _size;
std::unique_ptr<Script> _pubKeyScript;
public:
explicit TxOutput(const std::byte* data);

View File

@ -2,15 +2,18 @@
#include <fstream>
#include <memory>
#include <vector>
#include <chrono>
#include "Models/Block.h"
int main() {
std::string path = "/Users/selim/Documents/blk00000.dat";
std::string path = "/home/selim/dl/blk00000.dat";
std::fstream file(path);
std::vector<Block> blocks;
auto start = std::chrono::high_resolution_clock::now();
PreBlockHeader header{};
size_t index = 0;
while (true) {
@ -24,13 +27,17 @@ int main() {
auto blockData = std::make_unique<std::byte[]>(header.blockSize);
file.read((char*)blockData.get(), header.blockSize);
Block block(blockData.get(), header.blockSize);
blocks.push_back(block);
blocks.emplace_back(blockData.get(), header.blockSize);
//std::cout << "Parsed new block " << index++ << " with size: " << header.blockSize << std::endl;
std::cout << "Parsed new block " << index++ << " with size: " << header.blockSize << std::endl;
break;
}
auto stop = std::chrono::high_resolution_clock::now();
auto duration = duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Blocks found: " << blocks.size() << std::endl;
std::cout << "Duration: " << double(duration.count())/1000000 << " seconds" << std::endl;
// for(size_t i = 0; i < 100; ++i) {
// std::cout << "version: " << blocks[i].time() << std::endl;
// }