From 984202de682375d6239a5b33a4a817ad77d1ed74 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 26 Feb 2024 14:05:52 -0500 Subject: [PATCH 1/9] Style, remove duplicate object logging, reduce job buffering. --- src/protocols/protocol_block_in.cpp | 23 +++++++++--------- src/protocols/protocol_header_in_31800.cpp | 27 ++++++++++++---------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/protocols/protocol_block_in.cpp b/src/protocols/protocol_block_in.cpp index 613564cc..961f3a40 100644 --- a/src/protocols/protocol_block_in.cpp +++ b/src/protocols/protocol_block_in.cpp @@ -136,15 +136,17 @@ bool protocol_block_in::handle_receive_block(const code& ec, // Alias. const auto& block_ptr = message->block_ptr; + const auto& block = *block_ptr; + const auto hash = block.hash(); // Unrequested block, may not have been announced via inventory. - if (tracker->hashes.back() != block_ptr->hash()) + if (tracker->hashes.back() != hash) return true; // Out of order or invalid. - if (block_ptr->header().previous_block_hash() != top_.hash()) + if (block.header().previous_block_hash() != top_.hash()) { - LOGP("Orphan block [" << encode_hash(block_ptr->hash()) + LOGP("Orphan block [" << encode_hash(hash) << "] from [" << authority() << "]."); return false; } @@ -159,7 +161,7 @@ bool protocol_block_in::handle_receive_block(const code& ec, organize(block_ptr, BIND3(handle_organize, _1, height, block_ptr)); // Set the new top and continue. Organize error will stop the channel. - top_ = { block_ptr->hash(), height }; + top_ = { hash, height }; // Order is reversed, so next is at back. tracker->hashes.pop_back(); @@ -199,23 +201,22 @@ void protocol_block_in::complete() NOEXCEPT void protocol_block_in::handle_organize(const code& ec, size_t height, const chain::block::cptr& block_ptr) NOEXCEPT { - if (ec == network::error::service_stopped) + if (ec == network::error::service_stopped || ec == error::duplicate_block) return; - if (!ec || ec == error::duplicate_block) + if (ec) { - LOGP("Block [" << encode_hash(block_ptr->hash()) + // Assuming no store failure this is a consensus failure. + LOGR("Block [" << encode_hash(block_ptr->hash()) << "] at (" << height << ") from [" << authority() << "] " << ec.message()); + stop(ec); return; } - // Assuming no store failure this is a consensus failure. - LOGR("Block [" << encode_hash(block_ptr->hash()) + LOGP("Block [" << encode_hash(block_ptr->hash()) << "] at (" << height << ") from [" << authority() << "] " << ec.message()); - - stop(ec); } // private diff --git a/src/protocols/protocol_header_in_31800.cpp b/src/protocols/protocol_header_in_31800.cpp index 258d5448..defee403 100644 --- a/src/protocols/protocol_header_in_31800.cpp +++ b/src/protocols/protocol_header_in_31800.cpp @@ -71,6 +71,7 @@ bool protocol_header_in_31800::handle_receive_headers(const code& ec, if (stopped(ec)) return false; + const auto& query = archive(); LOGP("Headers (" << message->header_ptrs.size() << ") from [" << authority() << "]."); @@ -90,15 +91,18 @@ bool protocol_header_in_31800::handle_receive_headers(const code& ec, } // Add header at next height. + const auto hash = header_ptr->hash(); const auto height = add1(top_.height()); - // Asynchronous organization serves all channels. - // A job backlog will occur when organize is slower than download. - // This is not a material issue for headers, even with validation. - organize(header_ptr, BIND3(handle_organize, _1, height, header_ptr)); + // Avoid queuing up job if already stored. + if (!query.is_header(hash)) + { + // Asynchronous organization serves all channels. + organize(header_ptr, BIND3(handle_organize, _1, height, header_ptr)); + } // Set the new top and continue. Organize error will stop the channel. - top_ = { header_ptr->hash(), height }; + top_ = { hash, height }; } // Protocol presumes max_get_headers unless complete. @@ -130,23 +134,22 @@ void protocol_header_in_31800::complete() NOEXCEPT void protocol_header_in_31800::handle_organize(const code& ec, size_t height, const chain::header::cptr& header_ptr) NOEXCEPT { - if (ec == network::error::service_stopped) + if (ec == network::error::service_stopped || ec == error::duplicate_header) return; - if (!ec || ec == error::duplicate_header) + if (ec) { - LOGP("Header [" << encode_hash(header_ptr->hash()) + // Assuming no store failure this is a consensus failure. + LOGR("Header [" << encode_hash(header_ptr->hash()) << "] at (" << height << ") from [" << authority() << "] " << ec.message()); + stop(ec); return; } - // Assuming no store failure this is a consensus failure. - LOGR("Header [" << encode_hash(header_ptr->hash()) + LOGP("Header [" << encode_hash(header_ptr->hash()) << "] at (" << height << ") from [" << authority() << "] " << ec.message()); - - stop(ec); } // private From 8427e7c965570ef7a1fee511e047427d91a21900 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 26 Feb 2024 14:09:12 -0500 Subject: [PATCH 2/9] Remove unused includes. --- src/protocols/protocol_block_in_31800.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index 7227cdc2..0bd859a6 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -18,9 +18,7 @@ */ #include -#include #include -#include #include #include #include From 7e0b95a43f82a156cb51f7a41bbbd5876812fea1 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 26 Feb 2024 14:58:38 -0500 Subject: [PATCH 3/9] Refactor is_current for readability. --- src/chasers/chaser_header.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/chasers/chaser_header.cpp b/src/chasers/chaser_header.cpp index 96b14533..a267869a 100644 --- a/src/chasers/chaser_header.cpp +++ b/src/chasers/chaser_header.cpp @@ -138,6 +138,7 @@ void chaser_header::do_organize(const header::cptr& header_ptr, // Validate header. // ------------------------------------------------------------------------ + // Header validations are not bypassed when under checkpoint/milestone. // Rolling forward chain_state eliminates requery cost. state_.reset(new chain_state(*state_, header, coin)); @@ -151,8 +152,6 @@ void chaser_header::do_organize(const header::cptr& header_ptr, return; } - // Header validations are not bypassed when under checkpoint/milestone. - auto error = header.check(coin.timestamp_limit_seconds, coin.proof_of_work_limit, coin.scrypt_proof_of_work); if (error) @@ -170,8 +169,8 @@ void chaser_header::do_organize(const header::cptr& header_ptr, // Compute relative work. // ------------------------------------------------------------------------ - // Header is new top of stale branch (strength not computed). - if (!is_current(header, context.height)) + if (!is_current(header, context.height) && + !checkpoint::is_at(checkpoints_, height)) { save(header_ptr, context); handler(error::success); @@ -268,10 +267,6 @@ bool chaser_header::is_current(const header& header, if (!use_currency_window()) return true; - // Checkpoints are already validated. Current if at a checkpoint height. - if (checkpoint::is_at(checkpoints_, height)) - return true; - // en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2106 const auto time = wall_clock::from_time_t(header.timestamp()); const auto current = wall_clock::now() - currency_window(); From 14bf8b954b24dcbedb977b84fa25bcdc93e66e35 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 26 Feb 2024 15:02:20 -0500 Subject: [PATCH 4/9] Comments. --- src/protocols/protocol_header_in_31800.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocols/protocol_header_in_31800.cpp b/src/protocols/protocol_header_in_31800.cpp index defee403..96950687 100644 --- a/src/protocols/protocol_header_in_31800.cpp +++ b/src/protocols/protocol_header_in_31800.cpp @@ -94,7 +94,7 @@ bool protocol_header_in_31800::handle_receive_headers(const code& ec, const auto hash = header_ptr->hash(); const auto height = add1(top_.height()); - // Avoid queuing up job if already stored. + // Redundant query to avoid queuing up excess jobs. if (!query.is_header(hash)) { // Asynchronous organization serves all channels. From 78c5519a622f5a8fc0e146db6133f02d220429bb Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 26 Feb 2024 21:21:12 -0500 Subject: [PATCH 5/9] Remove bc::blockchain namespace references/includes. --- include/bitcoin/node/configuration.hpp | 2 -- src/configuration.cpp | 1 - test/test.hpp | 1 - 3 files changed, 4 deletions(-) diff --git a/include/bitcoin/node/configuration.hpp b/include/bitcoin/node/configuration.hpp index 75a6a328..2b70890b 100644 --- a/include/bitcoin/node/configuration.hpp +++ b/include/bitcoin/node/configuration.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_NODE_CONFIGURATION_HPP #define LIBBITCOIN_NODE_CONFIGURATION_HPP -////#include #include #include #include @@ -72,7 +71,6 @@ class BCN_API configuration /// Settings. log::settings log; node::settings node; - ////blockchain::settings chain; database::settings database; network::settings network; system::settings bitcoin; diff --git a/src/configuration.cpp b/src/configuration.cpp index d62ae3db..279f3faa 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -19,7 +19,6 @@ #include #include -////#include #include namespace libbitcoin { diff --git a/test/test.hpp b/test/test.hpp index 3f656089..5b6922d5 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -47,7 +47,6 @@ using namespace bc; ////using namespace bc::system; ////using namespace bc::network; -////using namespace bc::blockchain; using namespace bc::node; namespace std { From ad82e08ad19d5b50af2e359500c98652d7c5bbc4 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 26 Feb 2024 21:21:46 -0500 Subject: [PATCH 6/9] Style, comments. --- src/chasers/chaser_block.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/chasers/chaser_block.cpp b/src/chasers/chaser_block.cpp index a252dcd8..edd812b0 100644 --- a/src/chasers/chaser_block.cpp +++ b/src/chasers/chaser_block.cpp @@ -99,7 +99,7 @@ void chaser_block::do_organize(const block::cptr& block_ptr, const auto& coin = config().bitcoin; const auto hash = header.hash(); - // Skip existing, fail orphan. + // Skip existing, orphan. // ------------------------------------------------------------------------ if (closed()) @@ -127,8 +127,8 @@ void chaser_block::do_organize(const block::cptr& block_ptr, // ------------------------------------------------------------------------ // Rolling forward chain_state eliminates requery cost. - // Do not use block ref here as the block override is for tx pool. - state_.reset(new chain_state(*state_, header, coin)); + // Do not use block parameter here as that override is for tx pool. + state_.reset(new chain_state{ *state_, header, coin }); const auto context = state_->context(); const auto height = state_->height(); From 76c9e1227d51762d91b2899bb330f553e9e09d67 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 26 Feb 2024 21:28:02 -0500 Subject: [PATCH 7/9] Remove chaser_header.is_current checkpoint, add milestone/minwork. --- include/bitcoin/node/chasers/chaser_header.hpp | 8 ++++---- src/chasers/chaser_header.cpp | 18 +++++++++++------- src/parser.cpp | 13 ++++++++----- test/chasers/chaser_header.cpp | 13 ++++++------- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/include/bitcoin/node/chasers/chaser_header.hpp b/include/bitcoin/node/chasers/chaser_header.hpp index 4ba5c73b..d1db79f3 100644 --- a/include/bitcoin/node/chasers/chaser_header.hpp +++ b/include/bitcoin/node/chasers/chaser_header.hpp @@ -76,10 +76,8 @@ class BCN_API chaser_header virtual bool get_is_strong(bool& strong, const uint256_t& work, size_t point) const NOEXCEPT; - /// Header timestamp is within configured span from current time, - /// or current header is a checkpoint or milestone. - virtual bool is_current(const system::chain::header& header, - size_t height) const NOEXCEPT; + /// Header timestamp is within configured span from current time. + virtual bool is_current(const system::chain::header& header) const NOEXCEPT; /// Save header to tree with validation context. virtual void save(const system::chain::header::cptr& header, @@ -105,6 +103,8 @@ class BCN_API chaser_header void do_handle_event(const code& ec, chase event_, link value) NOEXCEPT; // These are thread safe. + const uint256_t minimum_work_; + const system::chain::checkpoint& milestone_; const system::chain::checkpoints& checkpoints_; const network::wall_clock::duration currency_window_; const bool use_currency_window_; diff --git a/src/chasers/chaser_header.cpp b/src/chasers/chaser_header.cpp index a267869a..09e14792 100644 --- a/src/chasers/chaser_header.cpp +++ b/src/chasers/chaser_header.cpp @@ -38,6 +38,8 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser_header::chaser_header(full_node& node) NOEXCEPT : chaser(node), + minimum_work_(config().bitcoin.minimum_work), + milestone_(config().bitcoin.milestone), checkpoints_(config().bitcoin.checkpoints), currency_window_(config().node.currency_window()), use_currency_window_(to_bool(config().node.currency_window_minutes)) @@ -113,7 +115,7 @@ void chaser_header::do_organize(const header::cptr& header_ptr, const auto& coin = config().bitcoin; const auto hash = header.hash(); - // Skip existing, fail orphan. + // Skip existing, orphan. // ------------------------------------------------------------------------ if (closed()) @@ -141,7 +143,7 @@ void chaser_header::do_organize(const header::cptr& header_ptr, // Header validations are not bypassed when under checkpoint/milestone. // Rolling forward chain_state eliminates requery cost. - state_.reset(new chain_state(*state_, header, coin)); + state_.reset(new chain_state{ *state_, header, coin }); const auto context = state_->context(); const auto height = state_->height(); @@ -169,8 +171,11 @@ void chaser_header::do_organize(const header::cptr& header_ptr, // Compute relative work. // ------------------------------------------------------------------------ - if (!is_current(header, context.height) && - !checkpoint::is_at(checkpoints_, height)) + // A checkpointed or milestoned branch always gets disk stored. Otherwise + // branch must be both current and of sufficient chain work to be stored. + if (!checkpoint::is_at(checkpoints_, height) && + !milestone_.equals(hash, height) && + !(is_current(header) && state_->cumulative_work() >= minimum_work_)) { save(header_ptr, context); handler(error::success); @@ -194,9 +199,9 @@ void chaser_header::do_organize(const header::cptr& header_ptr, return; } - // Header is new top of current weak branch. if (!strong) { + // Header is new top of current weak branch. save(header_ptr, context); handler(error::success); return; @@ -261,8 +266,7 @@ void chaser_header::do_organize(const header::cptr& header_ptr, handler(error::success); } -bool chaser_header::is_current(const header& header, - size_t height) const NOEXCEPT +bool chaser_header::is_current(const header& header) const NOEXCEPT { if (!use_currency_window()) return true; diff --git a/src/parser.cpp b/src/parser.cpp index 91a9d11b..a4512bbf 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -20,7 +20,6 @@ #include #include -////#include #include #include #include @@ -365,7 +364,6 @@ options_metadata parser::load_settings() THROWS value(&configured.bitcoin.genesis_block), "The genesis block, defaults to mainnet." ) - // TODO: checkpoint/checkpoints fail to parse. ( "bitcoin.checkpoint", value(&configured.bitcoin.checkpoints), @@ -413,9 +411,14 @@ options_metadata parser::load_settings() THROWS "The hash:height checkpoint for bip9 bit0 activation, defaults to 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5:419328." ) ( - "bitcoin.bip9_bit1_active_checkpoint", - value(&configured.bitcoin.bip9_bit1_active_checkpoint), - "The hash:height checkpoint for bip9 bit1 activation, defaults to 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893:481824." + "bitcoin.milestone", + value(&configured.bitcoin.milestone), + "A block presumed to be valid but not required to be present, defaults to 00000000000000000001a0a448d6cf2546b06801389cc030b2b18c6491266815:804000." + ) + ( + "bitcoin.minimum_work", + value(&configured.bitcoin.minimum_work), + "The minimum work for any branch to be considered valid, defaults to 000000000000000000000000000000000000000052b2559353df4117b7348b64." ) /* [network] */ diff --git a/test/chasers/chaser_header.cpp b/test/chasers/chaser_header.cpp index 0d040401..bb37e6b7 100644 --- a/test/chasers/chaser_header.cpp +++ b/test/chasers/chaser_header.cpp @@ -62,10 +62,9 @@ class mock_chaser_header return chaser_header::get_is_strong(strong, work, point); } - bool is_current(const system::chain::header& header, - size_t height) const NOEXCEPT override + bool is_current(const system::chain::header& header) const NOEXCEPT override { - return chaser_header::is_current(header, height); + return chaser_header::is_current(header); } void save(const system::chain::header::cptr& header, @@ -139,8 +138,8 @@ BOOST_AUTO_TEST_CASE(chaser_header_test__is_current__zero_currency_window__true) full_node node(query, config, log); mock_chaser_header instance(node); - BOOST_REQUIRE(instance.is_current(system::chain::header{ {}, {}, {}, 0, {}, {} }, 0)); - BOOST_REQUIRE(instance.is_current(system::chain::header{ {}, {}, {}, max_uint32, {}, {} }, 0)); + BOOST_REQUIRE(instance.is_current(system::chain::header{ {}, {}, {}, 0, {}, {} })); + BOOST_REQUIRE(instance.is_current(system::chain::header{ {}, {}, {}, max_uint32, {}, {} })); } BOOST_AUTO_TEST_CASE(chaser_header_test__is_current__one_minute_currency_window__expected) @@ -154,8 +153,8 @@ BOOST_AUTO_TEST_CASE(chaser_header_test__is_current__one_minute_currency_window_ full_node node(query, config, log); mock_chaser_header instance(node); - BOOST_REQUIRE(!instance.is_current(system::chain::header{ {}, {}, {}, 0, {}, {} }, 0)); - BOOST_REQUIRE(instance.is_current(system::chain::header{ {}, {}, {}, max_uint32, {}, {} }, 0)); + BOOST_REQUIRE(!instance.is_current(system::chain::header{ {}, {}, {}, 0, {}, {} })); + BOOST_REQUIRE(instance.is_current(system::chain::header{ {}, {}, {}, max_uint32, {}, {} })); } BOOST_AUTO_TEST_SUITE_END() From d630ca8c60f0ab4405cb1edaae4ba27295bbd4df Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 27 Feb 2024 01:30:06 -0500 Subject: [PATCH 8/9] Style. --- src/chasers/chaser_block.cpp | 35 +++++++++++++++-------------------- src/chasers/chaser_header.cpp | 12 +++++------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/chasers/chaser_block.cpp b/src/chasers/chaser_block.cpp index edd812b0..49f4f2fc 100644 --- a/src/chasers/chaser_block.cpp +++ b/src/chasers/chaser_block.cpp @@ -143,18 +143,16 @@ void chaser_block::do_organize(const block::cptr& block_ptr, if (!checkpoint::is_under(coin.checkpoints, height)) { // Requires no population. - auto error = block.check(); - if (error) + if (const auto error = block.check()) { - handler(network::error::protocol_violation); + handler(error); return; } // Requires no population. - error = block.check(context); - if (error) + if (const auto error = block.check(context)) { - handler(network::error::protocol_violation); + handler(error); return; } @@ -172,19 +170,17 @@ void chaser_block::do_organize(const block::cptr& block_ptr, } // Requires only prevout population. - error = block.accept(context, coin.subsidy_interval_blocks, - coin.initial_subsidy()); - if (error) + if (const auto error = block.accept(context, + coin.subsidy_interval_blocks, coin.initial_subsidy())) { - handler(network::error::protocol_violation); + handler(error); return; } // Requires only prevout population. - error = block.connect(context); - if (error) + if (const auto error = block.connect(context)) { - handler(network::error::protocol_violation); + handler(error); return; } } @@ -267,8 +263,7 @@ void chaser_block::do_organize(const block::cptr& block_ptr, } // Push new block as top of candidate chain. - const auto link = push(block_ptr, context); - if (link.is_terminal()) + if (push(block_ptr, context).is_terminal()) { handler(error::store_integrity); return; @@ -367,11 +362,11 @@ database::header_link chaser_block::push(const block::cptr& block, { auto& query = archive(); const auto link = query.set_link(*block, database::context - { - possible_narrow_cast(context.forks), - possible_narrow_cast(context.height), - context.median_time_past, - }); + { + possible_narrow_cast(context.forks), + possible_narrow_cast(context.height), + context.median_time_past, + }); if (!query.push_candidate(link)) return {}; diff --git a/src/chasers/chaser_header.cpp b/src/chasers/chaser_header.cpp index 09e14792..10e94eaa 100644 --- a/src/chasers/chaser_header.cpp +++ b/src/chasers/chaser_header.cpp @@ -154,18 +154,17 @@ void chaser_header::do_organize(const header::cptr& header_ptr, return; } - auto error = header.check(coin.timestamp_limit_seconds, - coin.proof_of_work_limit, coin.scrypt_proof_of_work); - if (error) + if (const auto error = header.check(coin.timestamp_limit_seconds, + coin.proof_of_work_limit, coin.scrypt_proof_of_work)) { handler(error); return; } - error = header.accept(context); - if (error) + if (const auto error = header.accept(context)) { handler(error); + return; } // Compute relative work. @@ -249,8 +248,7 @@ void chaser_header::do_organize(const header::cptr& header_ptr, } // Push new header as top of candidate chain. - const auto link = push(header_ptr, context); - if (link.is_terminal()) + if (push(header_ptr, context).is_terminal()) { handler(error::store_integrity); return; From a4e4478c814a7b6dde4d9678dd59606e6b7c71e5 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 27 Feb 2024 01:33:14 -0500 Subject: [PATCH 9/9] Comments. --- src/protocols/protocol_block_in.cpp | 6 +++--- src/protocols/protocol_block_in_31800.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/protocols/protocol_block_in.cpp b/src/protocols/protocol_block_in.cpp index 961f3a40..f8b11e12 100644 --- a/src/protocols/protocol_block_in.cpp +++ b/src/protocols/protocol_block_in.cpp @@ -256,11 +256,11 @@ get_blocks protocol_block_in::create_get_inventory( get_data protocol_block_in::create_get_data( const inventory& message) const NOEXCEPT { - get_data getter{}; - getter.items.reserve(message.count(type_id::block)); - // clang emplace_back bug (no matching constructor), using push_back. // bip144: get_data uses witness constant but inventory does not. + + get_data getter{}; + getter.items.reserve(message.count(type_id::block)); for (const auto& item: message.items) if ((item.type == type_id::block) && !archive().is_block(item.hash)) getter.items.push_back({ block_type_, item.hash }); diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index 0bd859a6..f4d4e1f1 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -236,11 +236,11 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, get_data protocol_block_in_31800::create_get_data( const chaser_check::map& map) const NOEXCEPT { - get_data getter{}; - getter.items.reserve(map.size()); - // clang emplace_back bug (no matching constructor), using push_back. // bip144: get_data uses witness constant but inventory does not. + + get_data getter{}; + getter.items.reserve(map.size()); for (const auto& item: map) getter.items.push_back({ block_type_, item.first });