Initial commit
This commit is contained in:
commit
9173093dbf
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.idea/
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
7
CMakeLists.txt
Normal file
7
CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.19)
|
||||||
|
project(btcexplorer)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||||
|
|
||||||
|
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)
|
||||||
31
Models/Block.cpp
Normal file
31
Models/Block.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "Block.h"
|
||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
Block::Block(const std::byte* pBlock, size_t size): _header{} {
|
||||||
|
_size = size;
|
||||||
|
_header = *((BlockHeader*)pBlock);
|
||||||
|
VarInt txCount(pBlock + sizeof(BlockHeader));
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Block::version() const {
|
||||||
|
return _header.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Block::time() const {
|
||||||
|
return _header.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Transaction>& Block::transactions() const {
|
||||||
|
return _transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Block::size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
44
Models/Block.h
Normal file
44
Models/Block.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef BTCEXPLORER_BLOCK_H
|
||||||
|
#define BTCEXPLORER_BLOCK_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Transaction.h"
|
||||||
|
|
||||||
|
enum BlockMagic: uint32_t {
|
||||||
|
Mainnet = 0xf9beb4d9,
|
||||||
|
Testnet3 = 0x0b110907,
|
||||||
|
Regtest = 0xfabfb5da
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PreBlockHeader {
|
||||||
|
BlockMagic magic;
|
||||||
|
uint32_t blockSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlockHeader {
|
||||||
|
uint32_t version;
|
||||||
|
std::array<uint8_t,32> prevBlockHeaderHash;
|
||||||
|
std::array<uint8_t,32> merkleRootHash;
|
||||||
|
uint32_t time;
|
||||||
|
uint32_t nBits;
|
||||||
|
uint32_t nOnce;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Block {
|
||||||
|
private:
|
||||||
|
BlockHeader _header;
|
||||||
|
std::vector<Transaction> _transactions;
|
||||||
|
size_t _size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Block(const std::byte* pBlock, size_t size);
|
||||||
|
[[nodiscard]] uint32_t version() const;
|
||||||
|
[[nodiscard]] uint32_t time() const;
|
||||||
|
[[nodiscard]] const std::vector<Transaction>& transactions() const;
|
||||||
|
[[nodiscard]] size_t size() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BTCEXPLORER_BLOCK_H
|
||||||
5
Models/Script.cpp
Normal file
5
Models/Script.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 05.05.2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Script.h"
|
||||||
149
Models/Script.h
Normal file
149
Models/Script.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#ifndef BTCEXPLORER_SCRIPT_H
|
||||||
|
#define BTCEXPLORER_SCRIPT_H
|
||||||
|
|
||||||
|
enum OpCode {
|
||||||
|
// push value
|
||||||
|
OP_0 = 0x00,
|
||||||
|
OP_FALSE = OP_0,
|
||||||
|
OP_PUSHDATA1 = 0x4c,
|
||||||
|
OP_PUSHDATA2 = 0x4d,
|
||||||
|
OP_PUSHDATA4 = 0x4e,
|
||||||
|
OP_1NEGATE = 0x4f,
|
||||||
|
OP_RESERVED = 0x50,
|
||||||
|
OP_1 = 0x51,
|
||||||
|
OP_TRUE=OP_1,
|
||||||
|
OP_2 = 0x52,
|
||||||
|
OP_3 = 0x53,
|
||||||
|
OP_4 = 0x54,
|
||||||
|
OP_5 = 0x55,
|
||||||
|
OP_6 = 0x56,
|
||||||
|
OP_7 = 0x57,
|
||||||
|
OP_8 = 0x58,
|
||||||
|
OP_9 = 0x59,
|
||||||
|
OP_10 = 0x5a,
|
||||||
|
OP_11 = 0x5b,
|
||||||
|
OP_12 = 0x5c,
|
||||||
|
OP_13 = 0x5d,
|
||||||
|
OP_14 = 0x5e,
|
||||||
|
OP_15 = 0x5f,
|
||||||
|
OP_16 = 0x60,
|
||||||
|
|
||||||
|
// control
|
||||||
|
OP_NOP = 0x61,
|
||||||
|
OP_VER = 0x62,
|
||||||
|
OP_IF = 0x63,
|
||||||
|
OP_NOTIF = 0x64,
|
||||||
|
OP_VERIF = 0x65,
|
||||||
|
OP_VERNOTIF = 0x66,
|
||||||
|
OP_ELSE = 0x67,
|
||||||
|
OP_ENDIF = 0x68,
|
||||||
|
OP_VERIFY = 0x69,
|
||||||
|
OP_RETURN = 0x6a,
|
||||||
|
|
||||||
|
// stack ops
|
||||||
|
OP_TOALTSTACK = 0x6b,
|
||||||
|
OP_FROMALTSTACK = 0x6c,
|
||||||
|
OP_2DROP = 0x6d,
|
||||||
|
OP_2DUP = 0x6e,
|
||||||
|
OP_3DUP = 0x6f,
|
||||||
|
OP_2OVER = 0x70,
|
||||||
|
OP_2ROT = 0x71,
|
||||||
|
OP_2SWAP = 0x72,
|
||||||
|
OP_IFDUP = 0x73,
|
||||||
|
OP_DEPTH = 0x74,
|
||||||
|
OP_DROP = 0x75,
|
||||||
|
OP_DUP = 0x76,
|
||||||
|
OP_NIP = 0x77,
|
||||||
|
OP_OVER = 0x78,
|
||||||
|
OP_PICK = 0x79,
|
||||||
|
OP_ROLL = 0x7a,
|
||||||
|
OP_ROT = 0x7b,
|
||||||
|
OP_SWAP = 0x7c,
|
||||||
|
OP_TUCK = 0x7d,
|
||||||
|
|
||||||
|
// splice ops
|
||||||
|
OP_CAT = 0x7e,
|
||||||
|
OP_SUBSTR = 0x7f,
|
||||||
|
OP_LEFT = 0x80,
|
||||||
|
OP_RIGHT = 0x81,
|
||||||
|
OP_SIZE = 0x82,
|
||||||
|
|
||||||
|
// bit logic
|
||||||
|
OP_INVERT = 0x83,
|
||||||
|
OP_AND = 0x84,
|
||||||
|
OP_OR = 0x85,
|
||||||
|
OP_XOR = 0x86,
|
||||||
|
OP_EQUAL = 0x87,
|
||||||
|
OP_EQUALVERIFY = 0x88,
|
||||||
|
OP_RESERVED1 = 0x89,
|
||||||
|
OP_RESERVED2 = 0x8a,
|
||||||
|
|
||||||
|
// numeric
|
||||||
|
OP_1ADD = 0x8b,
|
||||||
|
OP_1SUB = 0x8c,
|
||||||
|
OP_2MUL = 0x8d,
|
||||||
|
OP_2DIV = 0x8e,
|
||||||
|
OP_NEGATE = 0x8f,
|
||||||
|
OP_ABS = 0x90,
|
||||||
|
OP_NOT = 0x91,
|
||||||
|
OP_0NOTEQUAL = 0x92,
|
||||||
|
|
||||||
|
OP_ADD = 0x93,
|
||||||
|
OP_SUB = 0x94,
|
||||||
|
OP_MUL = 0x95,
|
||||||
|
OP_DIV = 0x96,
|
||||||
|
OP_MOD = 0x97,
|
||||||
|
OP_LSHIFT = 0x98,
|
||||||
|
OP_RSHIFT = 0x99,
|
||||||
|
|
||||||
|
OP_BOOLAND = 0x9a,
|
||||||
|
OP_BOOLOR = 0x9b,
|
||||||
|
OP_NUMEQUAL = 0x9c,
|
||||||
|
OP_NUMEQUALVERIFY = 0x9d,
|
||||||
|
OP_NUMNOTEQUAL = 0x9e,
|
||||||
|
OP_LESSTHAN = 0x9f,
|
||||||
|
OP_GREATERTHAN = 0xa0,
|
||||||
|
OP_LESSTHANOREQUAL = 0xa1,
|
||||||
|
OP_GREATERTHANOREQUAL = 0xa2,
|
||||||
|
OP_MIN = 0xa3,
|
||||||
|
OP_MAX = 0xa4,
|
||||||
|
|
||||||
|
OP_WITHIN = 0xa5,
|
||||||
|
|
||||||
|
// crypto
|
||||||
|
OP_RIPEMD160 = 0xa6,
|
||||||
|
OP_SHA1 = 0xa7,
|
||||||
|
OP_SHA256 = 0xa8,
|
||||||
|
OP_HASH160 = 0xa9,
|
||||||
|
OP_HASH256 = 0xaa,
|
||||||
|
OP_CODESEPARATOR = 0xab,
|
||||||
|
OP_CHECKSIG = 0xac,
|
||||||
|
OP_CHECKSIGVERIFY = 0xad,
|
||||||
|
OP_CHECKMULTISIG = 0xae,
|
||||||
|
OP_CHECKMULTISIGVERIFY = 0xaf,
|
||||||
|
|
||||||
|
// expansion
|
||||||
|
OP_NOP1 = 0xb0,
|
||||||
|
OP_CHECKLOCKTIMEVERIFY = 0xb1,
|
||||||
|
OP_NOP2 = OP_CHECKLOCKTIMEVERIFY,
|
||||||
|
OP_CHECKSEQUENCEVERIFY = 0xb2,
|
||||||
|
OP_NOP3 = OP_CHECKSEQUENCEVERIFY,
|
||||||
|
OP_NOP4 = 0xb3,
|
||||||
|
OP_NOP5 = 0xb4,
|
||||||
|
OP_NOP6 = 0xb5,
|
||||||
|
OP_NOP7 = 0xb6,
|
||||||
|
OP_NOP8 = 0xb7,
|
||||||
|
OP_NOP9 = 0xb8,
|
||||||
|
OP_NOP10 = 0xb9,
|
||||||
|
|
||||||
|
// Opcode added by BIP 342 (Tapscript)
|
||||||
|
OP_CHECKSIGADD = 0xba,
|
||||||
|
|
||||||
|
OP_INVALIDOPCODE = 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Script {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BTCEXPLORER_SCRIPT_H
|
||||||
29
Models/Transaction.cpp
Normal file
29
Models/Transaction.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "Transaction.h"
|
||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
Transaction::Transaction(const std::byte *data) {
|
||||||
|
_version = *((uint32_t*)data);
|
||||||
|
VarInt inputCount(data + sizeof(_version));
|
||||||
|
const std::byte* curPtr = data + sizeof(_version) + inputCount.size();
|
||||||
|
|
||||||
|
for(size_t i = 0; i < inputCount.value(); ++i) {
|
||||||
|
TxInput input(curPtr);
|
||||||
|
curPtr += input.size();
|
||||||
|
_inputs.push_back(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
VarInt outputCount(curPtr);
|
||||||
|
curPtr += outputCount.size();
|
||||||
|
for(size_t i = 0; i < outputCount.value(); ++i) {
|
||||||
|
TxOutput output(curPtr);
|
||||||
|
curPtr += output.size();
|
||||||
|
_outputs.push_back(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lockTime = *((uint32_t*)curPtr);
|
||||||
|
_size = curPtr - data + sizeof(_lockTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Transaction::size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
24
Models/Transaction.h
Normal file
24
Models/Transaction.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef BTCEXPLORER_TRANSACTION_H
|
||||||
|
#define BTCEXPLORER_TRANSACTION_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "TxInput.h"
|
||||||
|
#include "TxOutput.h"
|
||||||
|
|
||||||
|
class Transaction {
|
||||||
|
private:
|
||||||
|
uint32_t _version;
|
||||||
|
size_t _size;
|
||||||
|
std::vector<TxInput> _inputs;
|
||||||
|
std::vector<TxOutput> _outputs;
|
||||||
|
uint32_t _lockTime;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Transaction(const std::byte* data);
|
||||||
|
[[nodiscard]] size_t size() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BTCEXPLORER_TRANSACTION_H
|
||||||
21
Models/TxInput.cpp
Normal file
21
Models/TxInput.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "TxInput.h"
|
||||||
|
#include "VarInt.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
TxInput::TxInput(const std::byte *data) {
|
||||||
|
std::copy_n(data, _txId.size(), _txId.begin());
|
||||||
|
data += _txId.size();
|
||||||
|
_vOut = *reinterpret_cast<const uint32_t*>(data);
|
||||||
|
data += sizeof(_vOut);
|
||||||
|
VarInt scriptSigSize(data);
|
||||||
|
|
||||||
|
// TODO: save ScriptSig
|
||||||
|
|
||||||
|
data += scriptSigSize.size();
|
||||||
|
_sequence = *reinterpret_cast<const uint32_t*>(data);
|
||||||
|
_size = _txId.size() + sizeof(_vOut) + scriptSigSize.size() + scriptSigSize.value() + sizeof(_sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TxInput::size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
18
Models/TxInput.h
Normal file
18
Models/TxInput.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef BTCEXPLORER_TXINPUT_H
|
||||||
|
#define BTCEXPLORER_TXINPUT_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
class TxInput {
|
||||||
|
private:
|
||||||
|
std::array<std::byte,32> _txId;
|
||||||
|
uint32_t _vOut;
|
||||||
|
uint32_t _sequence;
|
||||||
|
size_t _size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TxInput(const std::byte* data);
|
||||||
|
[[nodiscard]] size_t size() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BTCEXPLORER_TXINPUT_H
|
||||||
16
Models/TxOutput.cpp
Normal file
16
Models/TxOutput.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "TxOutput.h"
|
||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
TxOutput::TxOutput(const std::byte *data) {
|
||||||
|
_value = *reinterpret_cast<const uint64_t*>(data);
|
||||||
|
data += sizeof(_value);
|
||||||
|
VarInt scriptPubKeySize(data);
|
||||||
|
|
||||||
|
// TODO: Read ScriptSizeKey
|
||||||
|
|
||||||
|
_size = sizeof(_value) + scriptPubKeySize.size() + scriptPubKeySize.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TxOutput::size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
17
Models/TxOutput.h
Normal file
17
Models/TxOutput.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef BTCEXPLORER_TXOUTPUT_H
|
||||||
|
#define BTCEXPLORER_TXOUTPUT_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
class TxOutput {
|
||||||
|
private:
|
||||||
|
uint64_t _value;
|
||||||
|
size_t _size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TxOutput(const std::byte* data);
|
||||||
|
[[nodiscard]] size_t size() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BTCEXPLORER_TXOUTPUT_H
|
||||||
19
Models/VarInt.cpp
Normal file
19
Models/VarInt.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
VarInt::VarInt(const std::byte *data) {
|
||||||
|
auto pByte = reinterpret_cast<const uint8_t*>(data);
|
||||||
|
switch (*pByte) {
|
||||||
|
case 0xfd: _value = *((uint16_t*)(pByte + 1)); _size = 2 + 1; break;
|
||||||
|
case 0xfe: _value = *((uint32_t*)(pByte + 1)); _size = 4 + 1; break;
|
||||||
|
case 0xff: _value = *((uint64_t*)(pByte + 1)); _size = 8 + 1; break;
|
||||||
|
default: _value = *pByte; _size = 1; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VarInt::value() const {
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t VarInt::size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
19
Models/VarInt.h
Normal file
19
Models/VarInt.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef BTCEXPLORER_VARINT_H
|
||||||
|
#define BTCEXPLORER_VARINT_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
class VarInt {
|
||||||
|
private:
|
||||||
|
uint64_t _value;
|
||||||
|
uint8_t _size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VarInt(const std::byte* data);
|
||||||
|
|
||||||
|
[[nodiscard]] uint64_t value() const;
|
||||||
|
[[nodiscard]] uint8_t size() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BTCEXPLORER_VARINT_H
|
||||||
39
main.cpp
Normal file
39
main.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Models/Block.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string path = "/Users/selim/Documents/blk00000.dat";
|
||||||
|
std::fstream file(path);
|
||||||
|
|
||||||
|
std::vector<Block> blocks;
|
||||||
|
|
||||||
|
PreBlockHeader header{};
|
||||||
|
size_t index = 0;
|
||||||
|
while (true) {
|
||||||
|
file.read((char*)&header, sizeof(header));
|
||||||
|
header.magic = (BlockMagic)__builtin_bswap32(header.magic);
|
||||||
|
|
||||||
|
if(file.eof()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
//std::cout << "Parsed new block " << index++ << " with size: " << header.blockSize << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Blocks found: " << blocks.size() << std::endl;
|
||||||
|
// for(size_t i = 0; i < 100; ++i) {
|
||||||
|
// std::cout << "version: " << blocks[i].time() << std::endl;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user