diff --git a/CMakeLists.txt b/CMakeLists.txt index 691a592..b8cb89d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,13 @@ cmake_minimum_required(VERSION 3.19) project(btcexplorer) set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fconcepts-diagnostics-depth=2") #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) \ No newline at end of file +find_package(OpenSSL REQUIRED) + +include_directories(OPENSSL_INCLUDE_DIR) + + +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 hash.h) +target_link_libraries(btcexplorer OpenSSL::Crypto) \ No newline at end of file diff --git a/Models/Script.cpp b/Models/Script.cpp index 6312341..ad6667a 100644 --- a/Models/Script.cpp +++ b/Models/Script.cpp @@ -1,13 +1,12 @@ #include "Script.h" +#include "../hash.h" #include #include -Script::Script(std::span data) { +Script::Script(std::span data, bool coinbase) { 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) { @@ -20,7 +19,7 @@ Script::Script(std::span data) { dataSize = *((uint16_t*)&iter[0]); iter += 2; } else if(operation.opCode == OpCode::OP_PUSHDATA4) { - dataSize = *((uint32_t *) &iter[0]); + dataSize = *((uint32_t *)&iter[0]); iter += 4; } operation.input = std::vector(iter, iter + dataSize); @@ -29,14 +28,10 @@ Script::Script(std::span data) { _operations.emplace_back(operation); } - _type = getType(); + _type = coinbase ? ScriptType::Coinbase : type(); } -Script::Script() { - -} - -ScriptType Script::getType() { +ScriptType Script::type() const { if(_operations[0].opCode <= OpCode::OP_PUSHDATA4 && _operations[1].opCode == OpCode::OP_CHECKSIG) { return ScriptType::P2PK; } else if(_operations[0].opCode == OpCode::OP_DUP @@ -50,3 +45,27 @@ ScriptType Script::getType() { throw std::runtime_error("Unsupported script type"); } } + +std::string Script::address() { + auto pubKey = publicKey(); + if(!pubKey.empty()) { + auto hash160 = hash::hash160(pubKey); + std::array prefixedHash{}; + prefixedHash[0] = 0; // TODO: this value should depend on BlockMagic + std::copy_n(hash160.begin(), hash160.size(), prefixedHash.begin() + 1); + auto secondHash = hash::sha256(prefixedHash); + std::array finalHash{}; + std::copy(prefixedHash.begin(), prefixedHash.end(), finalHash.begin()); + std::copy_n(secondHash.begin(), 4, finalHash.begin() + prefixedHash.size()); + return hash::base58(finalHash); + } else { + return ""; + } +} + +std::span Script::publicKey() { + switch (_type) { + case P2PK: return _operations[0].input; + default: return {}; + } +} diff --git a/Models/Script.h b/Models/Script.h index 96e36b0..e1a110f 100644 --- a/Models/Script.h +++ b/Models/Script.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include enum OpCode: uint8_t { // push value @@ -148,7 +150,7 @@ enum OpCode: uint8_t { }; enum ScriptType { - P2PK, P2PKH + Unknown, Coinbase, P2PK, P2PKH }; struct ScriptOperation { @@ -159,14 +161,16 @@ struct ScriptOperation { class Script { private: std::vector _operations; - ScriptType _type; + ScriptType _type = ScriptType::Unknown; private: - ScriptType getType(); + [[nodiscard]] ScriptType type() const; + [[nodiscard]] std::span publicKey(); public: - Script(); - explicit Script(std::span data); + Script() = default; + explicit Script(std::span data, bool coinbase = false); + [[nodiscard]] std::string address(); }; #endif //BTCEXPLORER_SCRIPT_H diff --git a/Models/Transaction.cpp b/Models/Transaction.cpp index 8ae0015..eccb860 100644 --- a/Models/Transaction.cpp +++ b/Models/Transaction.cpp @@ -1,11 +1,15 @@ #include "Transaction.h" #include "VarInt.h" +#include + Transaction::Transaction(const std::byte *data) { _version = *((uint32_t*)data); VarInt inputCount(data + sizeof(_version)); const std::byte* curPtr = data + sizeof(_version) + inputCount.size(); + std::cout << "======== Transaction ========" << std::endl; + for(size_t i = 0; i < inputCount.value(); ++i) { TxInput input(curPtr); curPtr += input.size(); @@ -17,6 +21,7 @@ Transaction::Transaction(const std::byte *data) { for(size_t i = 0; i < outputCount.value(); ++i) { TxOutput output(curPtr); curPtr += output.size(); + std::cout << "Output " << output.value()/100000000.0 << " BTC to address: " << output.address() << std::endl; _outputs.emplace_back(std::move(output)); } diff --git a/Models/TxInput.cpp b/Models/TxInput.cpp index 671d5ef..128482b 100644 --- a/Models/TxInput.cpp +++ b/Models/TxInput.cpp @@ -2,6 +2,9 @@ #include "VarInt.h" #include #include +#include + +static const std::array nullArray {}; TxInput::TxInput(const std::byte *data) { std::copy_n(data, _txId.size(), _txId.begin()); @@ -11,8 +14,11 @@ TxInput::TxInput(const std::byte *data) { VarInt scriptSigSize(data); data += scriptSigSize.size(); + int cmpResult = memcmp(_txId.data(), nullArray.data(), idSize); + bool coinbase = cmpResult == 0; + //std::cout << "=== Creating signature script of size: " << scriptSigSize.value() << std::endl; - _signatureScript = std::make_unique