commit 9173093dbf46de46419837d65b84a3faadde4b4f Author: Selim Mustafaev Date: Thu May 6 16:58:11 2021 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da3b2bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +cmake-build-*/ + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..394c768 --- /dev/null +++ b/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/Models/Block.cpp b/Models/Block.cpp new file mode 100644 index 0000000..4561dd9 --- /dev/null +++ b/Models/Block.cpp @@ -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& Block::transactions() const { + return _transactions; +} + +size_t Block::size() const { + return _size; +} diff --git a/Models/Block.h b/Models/Block.h new file mode 100644 index 0000000..721e454 --- /dev/null +++ b/Models/Block.h @@ -0,0 +1,44 @@ +#ifndef BTCEXPLORER_BLOCK_H +#define BTCEXPLORER_BLOCK_H + +#include +#include +#include + +#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 prevBlockHeaderHash; + std::array merkleRootHash; + uint32_t time; + uint32_t nBits; + uint32_t nOnce; +}; + +class Block { +private: + BlockHeader _header; + std::vector _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& transactions() const; + [[nodiscard]] size_t size() const; +}; + +#endif //BTCEXPLORER_BLOCK_H diff --git a/Models/Script.cpp b/Models/Script.cpp new file mode 100644 index 0000000..08d6f4c --- /dev/null +++ b/Models/Script.cpp @@ -0,0 +1,5 @@ +// +// Created by Selim Mustafaev on 05.05.2021. +// + +#include "Script.h" diff --git a/Models/Script.h b/Models/Script.h new file mode 100644 index 0000000..3e3d9c8 --- /dev/null +++ b/Models/Script.h @@ -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 diff --git a/Models/Transaction.cpp b/Models/Transaction.cpp new file mode 100644 index 0000000..470a464 --- /dev/null +++ b/Models/Transaction.cpp @@ -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; +} diff --git a/Models/Transaction.h b/Models/Transaction.h new file mode 100644 index 0000000..01343df --- /dev/null +++ b/Models/Transaction.h @@ -0,0 +1,24 @@ +#ifndef BTCEXPLORER_TRANSACTION_H +#define BTCEXPLORER_TRANSACTION_H + +#include +#include +#include + +#include "TxInput.h" +#include "TxOutput.h" + +class Transaction { +private: + uint32_t _version; + size_t _size; + std::vector _inputs; + std::vector _outputs; + uint32_t _lockTime; + +public: + explicit Transaction(const std::byte* data); + [[nodiscard]] size_t size() const; +}; + +#endif //BTCEXPLORER_TRANSACTION_H diff --git a/Models/TxInput.cpp b/Models/TxInput.cpp new file mode 100644 index 0000000..386fc63 --- /dev/null +++ b/Models/TxInput.cpp @@ -0,0 +1,21 @@ +#include "TxInput.h" +#include "VarInt.h" +#include + +TxInput::TxInput(const std::byte *data) { + std::copy_n(data, _txId.size(), _txId.begin()); + data += _txId.size(); + _vOut = *reinterpret_cast(data); + data += sizeof(_vOut); + VarInt scriptSigSize(data); + + // TODO: save ScriptSig + + data += scriptSigSize.size(); + _sequence = *reinterpret_cast(data); + _size = _txId.size() + sizeof(_vOut) + scriptSigSize.size() + scriptSigSize.value() + sizeof(_sequence); +} + +size_t TxInput::size() const { + return _size; +} diff --git a/Models/TxInput.h b/Models/TxInput.h new file mode 100644 index 0000000..fa3091c --- /dev/null +++ b/Models/TxInput.h @@ -0,0 +1,18 @@ +#ifndef BTCEXPLORER_TXINPUT_H +#define BTCEXPLORER_TXINPUT_H + +#include + +class TxInput { +private: + std::array _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 diff --git a/Models/TxOutput.cpp b/Models/TxOutput.cpp new file mode 100644 index 0000000..7513e01 --- /dev/null +++ b/Models/TxOutput.cpp @@ -0,0 +1,16 @@ +#include "TxOutput.h" +#include "VarInt.h" + +TxOutput::TxOutput(const std::byte *data) { + _value = *reinterpret_cast(data); + data += sizeof(_value); + VarInt scriptPubKeySize(data); + + // TODO: Read ScriptSizeKey + + _size = sizeof(_value) + scriptPubKeySize.size() + scriptPubKeySize.value(); +} + +size_t TxOutput::size() const { + return _size; +} diff --git a/Models/TxOutput.h b/Models/TxOutput.h new file mode 100644 index 0000000..c0309de --- /dev/null +++ b/Models/TxOutput.h @@ -0,0 +1,17 @@ +#ifndef BTCEXPLORER_TXOUTPUT_H +#define BTCEXPLORER_TXOUTPUT_H + +#include +#include + +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 diff --git a/Models/VarInt.cpp b/Models/VarInt.cpp new file mode 100644 index 0000000..52f1e3e --- /dev/null +++ b/Models/VarInt.cpp @@ -0,0 +1,19 @@ +#include "VarInt.h" + +VarInt::VarInt(const std::byte *data) { + auto pByte = reinterpret_cast(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; +} diff --git a/Models/VarInt.h b/Models/VarInt.h new file mode 100644 index 0000000..e7f5f56 --- /dev/null +++ b/Models/VarInt.h @@ -0,0 +1,19 @@ +#ifndef BTCEXPLORER_VARINT_H +#define BTCEXPLORER_VARINT_H + +#include +#include + +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 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..14f22f0 --- /dev/null +++ b/main.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include "Models/Block.h" + +int main() { + std::string path = "/Users/selim/Documents/blk00000.dat"; + std::fstream file(path); + + std::vector 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(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; +}