Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move hash parsing optimizations to system, delete block memory if fails. #433

Merged
merged 3 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions include/bitcoin/network/messages/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ struct BCT_API block
static const uint32_t version_minimum;
static const uint32_t version_maximum;

/// Populate header and tx hashes onto the block.
static void set_hashes(const system::chain::block& block,
const system::data_chunk& data) NOEXCEPT;

static cptr deserialize(arena& arena, uint32_t version,
const system::data_chunk& data, bool witness=true) NOEXCEPT;
static cptr deserialize(uint32_t version, const system::data_chunk& data,
Expand Down
3 changes: 0 additions & 3 deletions include/bitcoin/network/messages/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ struct BCT_API transaction
static const uint32_t version_minimum;
static const uint32_t version_maximum;

static system::hash_digest desegregated_hash(size_t witnessed,
size_t unwitnessed, const uint8_t* data) NOEXCEPT;

static cptr deserialize(uint32_t version, const system::data_chunk& data,
bool witness=true) NOEXCEPT;
static transaction deserialize(uint32_t version, system::reader& source,
Expand Down
56 changes: 13 additions & 43 deletions src/messages/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,48 +44,9 @@ const identifier block::id = identifier::block;
const uint32_t block::version_minimum = level::minimum_protocol;
const uint32_t block::version_maximum = level::maximum_protocol;

// static
void block::set_hashes(const chain::block& block, const data_chunk& data) NOEXCEPT
{
constexpr auto header_size = chain::header::serialized_size();

// Cache header hash.
block.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: *block.transactions_ptr())
{
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
typename block::cptr block::deserialize(uint32_t version,
const system::data_chunk& data, bool witness) NOEXCEPT
const data_chunk& data, bool witness) NOEXCEPT
{
static default_memory memory{};
return deserialize(*memory.get_arena(), version, data, witness);
Expand All @@ -105,10 +66,20 @@ typename block::cptr block::deserialize(arena& arena, uint32_t version,
byte_reader reader{ source, &arena };
auto& allocator = reader.get_allocator();
const auto block = allocator.new_object<chain::block>(reader, witness);
if (is_null(block) || !reader)

// Destruct block if created but failed to deserialize.
if (!reader && !is_null(block))
byte_allocator::deleter<chain::block>(&arena);

// Release memory if block construction or deserialization failed.
if (!reader || is_null(block))
{
arena.release(memory);
return nullptr;
}

set_hashes(*block, data);
// Cache hashes as extracted from serialized block.
block->set_hashes(data);

// Set size of block allocation owned by memory (zero if non-detachable).
block->set_allocation(arena.detach());
Expand All @@ -121,7 +92,6 @@ typename block::cptr block::deserialize(arena& arena, uint32_t version,
byte_allocator::deleter<chain::block>(&arena);

// Deallocate detached memory (nop if not detachable).
// Follows destructor just in case a destructor traverses memory.
arena.release(memory);
}));
}
Expand Down
25 changes: 2 additions & 23 deletions src/messages/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,6 @@ const identifier transaction::id = identifier::transaction;
const uint32_t transaction::version_minimum = level::minimum_protocol;
const uint32_t transaction::version_maximum = level::maximum_protocol;

// Optimized non-witness hash derivation using witness-serialized tx.
hash_digest transaction::desegregated_hash(size_t witnessed,
size_t unwitnessed, const uint8_t* data) NOEXCEPT
{
BC_ASSERT_MSG(!is_null(data), "nullptr");

using namespace system;
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;
}

// static
typename transaction::cptr transaction::deserialize(uint32_t version,
const data_chunk& data, bool witness) NOEXCEPT
Expand All @@ -75,8 +54,8 @@ typename transaction::cptr transaction::deserialize(uint32_t version,
const auto true_size = tx.serialized_size(true);
const auto false_size = tx.serialized_size(false);
tx.set_witness_hash(bitcoin_hash(true_size, data.data()));
tx.set_nominal_hash(desegregated_hash(true_size, false_size,
data.data()));
tx.set_nominal_hash(chain::transaction::desegregated_hash(
true_size, false_size, data.data()));
}
else
{
Expand Down
Loading