Skip to content

Commit

Permalink
Merge pull request #1469 from evoskuil/master
Browse files Browse the repository at this point in the history
Optimize size computations, use ceilinged_add, style.
  • Loading branch information
evoskuil authored May 31, 2024
2 parents 434772a + 0155ccf commit a71ad16
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 98 deletions.
6 changes: 3 additions & 3 deletions include/bitcoin/system/chain/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ class BC_API block
public:
DEFAULT_COPY_MOVE_DESTRUCT(block);

typedef std_vector<size_t> sizes;
typedef std::shared_ptr<const block> cptr;

static bool is_malleable64(const transaction_cptrs& txs) NOEXCEPT;
Expand Down Expand Up @@ -167,6 +166,7 @@ class BC_API block
bool is_unspent_coinbase_collision() const NOEXCEPT;

private:
typedef struct { size_t nominal; size_t witnessed; } sizes;
using point_cref = std::reference_wrapper<const point>;
using point_hash = std::hash<std::reference_wrapper<const point>>;
using hash_cref = std::reference_wrapper<const hash_digest>;
Expand All @@ -180,6 +180,7 @@ class BC_API block
std::unordered_set<hash_cref, hash_hash>;

static block from_data(reader& source, bool witness) NOEXCEPT;
static sizes serialized_size(const chain::transaction_cptrs& txs) NOEXCEPT;

// context free
hash_digest generate_merkle_root(bool witness) const NOEXCEPT;
Expand All @@ -202,8 +203,7 @@ class BC_API block

// Cache.
bool valid_;
////size_t nominal_size_;
////size_t witness_size_;
sizes size_;
};

typedef std::vector<block> blocks;
Expand Down
15 changes: 11 additions & 4 deletions include/bitcoin/system/chain/input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,22 @@ class BC_API input
bool is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT;

protected:
// So that witness may be set late in deserialization.
friend class transaction;

input(const chain::point::cptr& point, const chain::script::cptr& script,
const chain::witness::cptr& witness, uint32_t sequence,
bool valid) NOEXCEPT;

private:
// So that witness may be set late in deserialization.
friend class transaction;
size_t nominal_size() const NOEXCEPT;
size_t witnessed_size() const NOEXCEPT;
void set_witness(reader& source) NOEXCEPT;

typedef struct { size_t nominal; size_t witnessed; } sizes;

static input from_data(reader& source) NOEXCEPT;
static sizes serialized_size(const chain::script& script,
const chain::witness& witness) NOEXCEPT;
bool extract_sigop_script(chain::script& out,
const chain::script& prevout_script) const NOEXCEPT;

Expand All @@ -141,7 +148,7 @@ class BC_API input

// Cache.
bool valid_;
////size_t size_;
sizes size_;

public:
/// Public mutable metadata access, copied but not compared for equality.
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/system/chain/operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class BC_API operation
private:
// So script may call count_op.
friend class script;
static bool count_op(reader& source) NOEXCEPT;

static operation from_data(reader& source) NOEXCEPT;
static operation from_push_data(const chunk_cptr& data,
Expand All @@ -166,7 +167,6 @@ class BC_API operation

static chunk_cptr no_data_ptr() NOEXCEPT;
static chunk_cptr any_data_ptr() NOEXCEPT;
static bool count_op(reader& source) NOEXCEPT;
static uint32_t read_data_size(opcode code, reader& source) NOEXCEPT;

static inline opcode opcode_from_data(const data_chunk& push_data,
Expand Down
4 changes: 3 additions & 1 deletion include/bitcoin/system/chain/output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ class BC_API output

private:
static output from_data(reader& source) NOEXCEPT;
static size_t serialized_size(const chain::script& script,
uint64_t value) NOEXCEPT;

// Output should be stored as shared (adds 16 bytes).
// copy: 3 * 64 + 1 = 25 bytes (vs. 16 when shared).
Expand All @@ -104,7 +106,7 @@ class BC_API output

// Cache.
bool valid_;
////size_t size_;
size_t size_;
};

typedef std::vector<output> outputs;
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/system/chain/script.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class BC_API script
static size_t serialized_size(const operations& ops) NOEXCEPT;
static inline size_t op_size(size_t total, const operation& op) NOEXCEPT
{
return total + op.serialized_size();
return ceilinged_add(total, op.serialized_size());
};

// Script should be stored as shared.
Expand Down
6 changes: 4 additions & 2 deletions include/bitcoin/system/chain/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class BC_API transaction
bool is_confirmed_double_spend(size_t height) const NOEXCEPT;

private:
typedef struct { size_t nominal; size_t witnessed; } sizes;
typedef struct
{
hash_digest outputs;
Expand All @@ -234,6 +235,8 @@ class BC_API transaction
static transaction from_data(reader& source, bool witness) NOEXCEPT;
static bool segregated(const chain::inputs& inputs) NOEXCEPT;
static bool segregated(const chain::input_cptrs& inputs) NOEXCEPT;
static sizes serialized_size(const chain::input_cptrs& inputs,
const chain::output_cptrs& outputs, bool segregated) NOEXCEPT;

// signature hash
hash_digest output_hash(const input_iterator& input) const NOEXCEPT;
Expand Down Expand Up @@ -268,8 +271,7 @@ class BC_API transaction
// Cache.
bool segregated_;
bool valid_;
////size_t nominal_size_;
////size_t witness_size_;
sizes size_;

// TODO: use std::optional to avoid these pointer allocations (0.16%).
// Signature and identity hash caching (witness hash if witnessed).
Expand Down
8 changes: 4 additions & 4 deletions include/bitcoin/system/intrinsics/haves.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,23 +178,23 @@ inline bool have_avx512() NOEXCEPT
inline bool have_avx2() NOEXCEPT
{
if constexpr (with_avx2)
return try_avx2();
return true;//// try_avx2();
else
return false;
}

inline bool have_sse41() NOEXCEPT
{
if constexpr (with_sse41)
return try_sse41();
return true;//// try_sse41();
else
return false;
}

inline bool have_neon() NOEXCEPT
{
if constexpr (with_shani)
return try_shani();
if constexpr (with_neon)
return try_neon();
else
return false;
}
Expand Down
65 changes: 43 additions & 22 deletions src/chain/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ block::block(reader& source, bool witness) NOEXCEPT
// protected
block::block(const chain::header::cptr& header,
const chain::transactions_cptr& txs, bool valid) NOEXCEPT
: header_(header), txs_(txs), valid_(valid)
: header_(header),
txs_(txs),
valid_(valid),
size_(serialized_size(*txs))
{
}

Expand Down Expand Up @@ -213,7 +216,7 @@ const chain::header::cptr block::header_ptr() const NOEXCEPT
const inputs_cptr block::inputs_ptr() const NOEXCEPT
{
const auto inputs = std::make_shared<input_cptrs>();
const auto append_ins = [&inputs](const transaction::cptr& tx) NOEXCEPT
const auto append_ins = [&inputs](const auto& tx) NOEXCEPT
{
const auto& tx_ins = *tx->inputs_ptr();
inputs->insert(inputs->end(), tx_ins.begin(), tx_ins.end());
Expand All @@ -240,7 +243,7 @@ hashes block::transaction_hashes(bool witness) const NOEXCEPT
// Vector capacity is never reduced when resizing to smaller size.
out.resize(count);

const auto hash = [witness](const transaction::cptr& tx) NOEXCEPT
const auto hash = [witness](const auto& tx) NOEXCEPT
{
return tx->hash(witness);
};
Expand All @@ -255,18 +258,35 @@ hash_digest block::hash() const NOEXCEPT
return header_->hash();
}

// TODO: this is expensive.
size_t block::serialized_size(bool witness) const NOEXCEPT
// static/private
block::sizes block::serialized_size(
const chain::transaction_cptrs& txs) NOEXCEPT
{
// Overflow returns max_size_t.
const auto sum = [=](size_t total, const transaction::cptr& tx) NOEXCEPT
sizes size{};
std::for_each(txs.begin(), txs.end(), [&](const auto& tx) NOEXCEPT
{
return ceilinged_add(total, tx->serialized_size(witness));
};
size.nominal = ceilinged_add(size.nominal, tx->serialized_size(false));
size.witnessed = ceilinged_add(size.witnessed, tx->serialized_size(true));
});

const auto common_size = ceilinged_add(
header::serialized_size(),
variable_size(txs.size()));

const auto nominal_size = ceilinged_add(
common_size,
size.nominal);

return header::serialized_size()
+ variable_size(txs_->size())
+ std::accumulate(txs_->begin(), txs_->end(), zero, sum);
const auto witnessed_size = ceilinged_add(
common_size,
size.witnessed);

return { nominal_size, witnessed_size };
}

size_t block::serialized_size(bool witness) const NOEXCEPT
{
return witness ? size_.witnessed : size_.nominal;
}

// Connect.
Expand Down Expand Up @@ -294,7 +314,7 @@ bool block::is_extra_coinbases() const NOEXCEPT
if (txs_->empty())
return false;

const auto value = [](const transaction::cptr& tx) NOEXCEPT
const auto value = [](const auto& tx) NOEXCEPT
{
return tx->is_coinbase();
};
Expand All @@ -318,7 +338,7 @@ bool block::is_forward_reference() const NOEXCEPT
return hashes.find(std::ref(input->point().hash())) != hashes.end();
};

const auto spend = [&spent, &hashes](const transaction::cptr& tx) NOEXCEPT
const auto spend = [&spent, &hashes](const auto& tx) NOEXCEPT
{
const auto& ins = *tx->inputs_ptr();
const auto forward = std::any_of(ins.begin(), ins.end(), spent);
Expand All @@ -338,7 +358,7 @@ bool block::is_internal_double_spend() const NOEXCEPT
return false;

// Overflow returns max_size_t.
const auto sum_ins = [](size_t total, const transaction::cptr& tx) NOEXCEPT
const auto sum_ins = [](size_t total, const auto& tx) NOEXCEPT
{
return ceilinged_add(total, tx->inputs());
};
Expand All @@ -351,7 +371,7 @@ bool block::is_internal_double_spend() const NOEXCEPT
return !points.emplace(in->point()).second;
};

const auto double_spent = [&spent](const transaction::cptr& tx) NOEXCEPT
const auto double_spent = [&spent](const auto& tx) NOEXCEPT
{
const auto& ins = *tx->inputs_ptr();
return std::any_of(ins.begin(), ins.end(), spent);
Expand All @@ -377,8 +397,9 @@ bool block::is_invalid_merkle_root() const NOEXCEPT
size_t block::weight() const NOEXCEPT
{
// Block weight is 3 * nominal size * + 1 * witness size (bip141).
return base_size_contribution * serialized_size(false) +
total_size_contribution * serialized_size(true);
return ceilinged_add(
ceilinged_multiply(base_size_contribution, serialized_size(false)),
ceilinged_multiply(total_size_contribution, serialized_size(true)));
}

bool block::is_overweight() const NOEXCEPT
Expand Down Expand Up @@ -484,7 +505,7 @@ bool block::is_malleable64() const NOEXCEPT
// This form of malleability does not imply current block instance is invalid.
bool block::is_malleable64(const transaction_cptrs& txs) NOEXCEPT
{
const auto two_leaves = [](const transaction::cptr& tx) NOEXCEPT
const auto two_leaves = [](const auto& tx) NOEXCEPT
{
return tx->serialized_size(false) == two * hash_size;
};
Expand All @@ -494,7 +515,7 @@ bool block::is_malleable64(const transaction_cptrs& txs) NOEXCEPT

bool block::is_segregated() const NOEXCEPT
{
const auto segregated = [](const transaction::cptr& tx) NOEXCEPT
const auto segregated = [](const auto& tx) NOEXCEPT
{
return tx->is_segregated();
};
Expand Down Expand Up @@ -547,7 +568,7 @@ static uint64_t block_subsidy(size_t height, uint64_t subsidy_interval,
uint64_t block::fees() const NOEXCEPT
{
// Overflow returns max_uint64.
const auto value = [](uint64_t total, const transaction::cptr& tx) NOEXCEPT
const auto value = [](uint64_t total, const auto& tx) NOEXCEPT
{
return ceilinged_add(total, tx->fee());
};
Expand Down Expand Up @@ -578,7 +599,7 @@ bool block::is_overspent(size_t height, uint64_t subsidy_interval,
size_t block::signature_operations(bool bip16, bool bip141) const NOEXCEPT
{
// Overflow returns max_size_t.
const auto value = [=](size_t total, const transaction::cptr& tx) NOEXCEPT
const auto value = [=](size_t total, const auto& tx) NOEXCEPT
{
return ceilinged_add(total, tx->signature_operations(bip16, bip141));
};
Expand Down
55 changes: 46 additions & 9 deletions src/chain/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ input::input(const chain::point::cptr& point, const chain::script::cptr& script,
script_(script),
witness_(witness),
sequence_(sequence),
valid_(valid)
valid_(valid),
size_(serialized_size(*script, *witness))
{
}

Expand Down Expand Up @@ -216,16 +217,52 @@ void input::to_data(writer& sink) const NOEXCEPT
sink.write_4_bytes_little_endian(sequence_);
}

// static/private
input::sizes input::serialized_size(const chain::script& script,
const chain::witness& witness) NOEXCEPT
{
constexpr auto const_size = ceilinged_add(
point::serialized_size(),
sizeof(sequence_));

const auto nominal_size = ceilinged_add(
const_size,
script.serialized_size(true));

const auto witnessed_size = ceilinged_add(
nominal_size,
witness.serialized_size(true));

return { nominal_size, witnessed_size };
}

// input.serialized_size(witness) provides sizing for witness, however
// witnesses are serialized by the transaction. This is an ugly hack as a
// consequence of bip144 not serializing witnesses as part of inputs, which
// is logically the proper association.
size_t input::serialized_size(bool witness) const NOEXCEPT
{
// input.serialized_size(witness) provides sizing for witness, however
// witnesses are serialized by the transaction. This is an ugly hack as a
// consequence of bip144 not serializing witnesses as part of inputs, which
// is logically the proper association.
return point::serialized_size()
+ script_->serialized_size(true)
+ (witness ? witness_->serialized_size(true) : zero)
+ sizeof(sequence_);
return witness ? size_.witnessed : size_.nominal;
}

// Friend accessors (private).
// ----------------------------------------------------------------------------

size_t input::nominal_size() const NOEXCEPT
{
return size_.nominal;
}

size_t input::witnessed_size() const NOEXCEPT
{
return size_.witnessed;
}

void input::set_witness(reader& source) NOEXCEPT
{
witness_ = to_shared<chain::witness>(source, true);
size_.witnessed = ceilinged_add(size_.nominal,
witness_->serialized_size(true));
}

// Properties.
Expand Down
Loading

0 comments on commit a71ad16

Please sign in to comment.