diff --git a/include/bitcoin/system/chain/block.hpp b/include/bitcoin/system/chain/block.hpp index 6db8d06e2f..3d1869dad2 100644 --- a/include/bitcoin/system/chain/block.hpp +++ b/include/bitcoin/system/chain/block.hpp @@ -112,6 +112,9 @@ class BC_API block /// Reference used to avoid copy, sets cache if not set (not thread safe). const hash_digest& get_hash() const NOEXCEPT; + /// Optimized hash derivations using wire serialization of same block. + void set_hashes(const data_chunk& data) NOEXCEPT; + /// Set/get memory allocation. void set_allocation(size_t allocation) const NOEXCEPT; size_t get_allocation() const NOEXCEPT; diff --git a/include/bitcoin/system/chain/transaction.hpp b/include/bitcoin/system/chain/transaction.hpp index 9d9f6f5fb3..ec1b1fb055 100644 --- a/include/bitcoin/system/chain/transaction.hpp +++ b/include/bitcoin/system/chain/transaction.hpp @@ -45,9 +45,14 @@ class BC_API transaction typedef std::shared_ptr cptr; typedef input_cptrs::const_iterator input_iterator; + /// Not genesis and at least 100 blocks deep. static bool is_coinbase_mature(size_t coinbase_height, size_t height) NOEXCEPT; + /// Optimized non-witness hash derivation using witness-serialized tx. + static hash_digest desegregated_hash(size_t witnessed, + size_t unwitnessed, const uint8_t* data) NOEXCEPT; + /// Constructors. /// ----------------------------------------------------------------------- diff --git a/src/chain/block.cpp b/src/chain/block.cpp index 33d5d84319..1bff491f9b 100644 --- a/src/chain/block.cpp +++ b/src/chain/block.cpp @@ -263,6 +263,44 @@ const hash_digest& block::get_hash() const NOEXCEPT return header_->get_hash(); } +void block::set_hashes(const data_chunk& data) NOEXCEPT +{ + constexpr auto header_size = chain::header::serialized_size(); + + // Cache header hash. + header_->set_hash(bitcoin_hash(header_size, data.data())); + + // Skip transaction count, guarded by preceding successful block construct. + auto start = std::next(data.data(), header_size); + std::advance(start, size_variable(*start)); + + // Cache transaction hashes. + auto coinbase = true; + for (const auto& tx: *txs_) + { + const auto witness_size = tx->serialized_size(true); + + // If !witness then wire txs cannot have been segregated. + if (tx->is_segregated()) + { + const auto nominal_size = tx->serialized_size(false); + + tx->set_nominal_hash(transaction::desegregated_hash( + witness_size, nominal_size, start)); + + if (!coinbase) + tx->set_witness_hash(bitcoin_hash(witness_size, start)); + } + else + { + tx->set_nominal_hash(bitcoin_hash(witness_size, start)); + } + + coinbase = false; + std::advance(start, witness_size); + } +} + // static/private block::sizes block::serialized_size( const chain::transaction_cptrs& txs) NOEXCEPT diff --git a/src/chain/transaction.cpp b/src/chain/transaction.cpp index c270817f12..488531738a 100644 --- a/src/chain/transaction.cpp +++ b/src/chain/transaction.cpp @@ -442,6 +442,27 @@ hash_digest transaction::hash(bool witness) const NOEXCEPT return digest; } +// static +hash_digest transaction::desegregated_hash(size_t witnessed, + size_t unwitnessed, const uint8_t* data) NOEXCEPT +{ + if (is_null(data)) + return null_hash; + + constexpr auto preamble = sizeof(uint32_t) + two * sizeof(uint8_t); + const auto puts = floored_subtract(unwitnessed, two * sizeof(uint32_t)); + const auto locktime = floored_subtract(witnessed, sizeof(uint32_t)); + + hash_digest digest{}; + stream::out::fast stream{ digest }; + hash::sha256x2::fast sink{ stream }; + sink.write_bytes(data, sizeof(uint32_t)); + sink.write_bytes(std::next(data, preamble), puts); + sink.write_bytes(std::next(data, locktime), sizeof(uint32_t)); + sink.flush(); + return digest; +} + // Methods. // ----------------------------------------------------------------------------