Basic support of parsing scripts
This commit is contained in:
parent
9173093dbf
commit
888e1967c4
@ -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)
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
15
main.cpp
15
main.cpp
@ -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;
|
||||
// }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user