From 41c30c59a50d4c8828b103d9720737a991a705cd Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 19:21:13 -0400 Subject: [PATCH 1/3] Nest per-table storage settings. --- include/bitcoin/database/impl/store/store.ipp | 66 +- include/bitcoin/database/settings.hpp | 114 +- src/settings.cpp | 83 - test/query/archive/chain_reader.cpp | 1812 +++++----- test/query/archive/chain_writer.cpp | 3036 ++++++++--------- test/query/archive/wire_writer.cpp | 220 +- test/query/extent.cpp | 346 +- test/query/navigate/navigate_reverse.cpp | 896 ++--- test/query/properties_tx.cpp | 276 +- test/settings.cpp | 190 +- 10 files changed, 3463 insertions(+), 3576 deletions(-) diff --git a/include/bitcoin/database/impl/store/store.ipp b/include/bitcoin/database/impl/store/store.ipp index 7f9b35a28..36bcb78b4 100644 --- a/include/bitcoin/database/impl/store/store.ipp +++ b/include/bitcoin/database/impl/store/store.ipp @@ -32,78 +32,78 @@ CLASS::store(const settings& config) NOEXCEPT // ------------------------------------------------------------------------ header_head_(head(config.path / schema::dir::heads, schema::archive::header), 1, 0, random), - header_body_(body(config.path, schema::archive::header), config.header_size, config.header_rate, sequential, staged), + header_body_(body(config.path, schema::archive::header), config.header.size, config.header.rate, sequential, staged), input_head_(head(config.path / schema::dir::heads, schema::archive::input), 1, 0, sequential), - input_body_(body(config.path, schema::archive::input), config.input_size, config.input_rate, sequential, staged), + input_body_(body(config.path, schema::archive::input), config.input.size, config.input.rate, sequential, staged), output_head_(head(config.path / schema::dir::heads, schema::archive::output), 1, 0, sequential), - output_body_(body(config.path, schema::archive::output), config.output_size, config.output_rate, sequential, staged), + output_body_(body(config.path, schema::archive::output), config.output.size, config.output.rate, sequential, staged), ins_head_(head(config.path / schema::dir::heads, schema::archive::ins), 1, 0, random), - ins_body_(body(config.path, schema::archive::ins), config.ins_size, config.ins_rate, sequential, staged), + ins_body_(body(config.path, schema::archive::ins), config.ins.size, config.ins.rate, sequential, staged), outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), 1, 0, sequential), - outs_body_(body(config.path, schema::archive::outs), config.outs_size, config.outs_rate, sequential, staged), + outs_body_(body(config.path, schema::archive::outs), config.outs.size, config.outs.rate, sequential, staged), tx_head_(head(config.path / schema::dir::heads, schema::archive::tx), 1, 0, random), - tx_body_(body(config.path, schema::archive::tx), config.tx_size, config.tx_rate, sequential, staged), + tx_body_(body(config.path, schema::archive::tx), config.tx.size, config.tx.rate, sequential, staged), txs_head_(head(config.path / schema::dir::heads, schema::archive::txs), 1, 0, random), - txs_body_(body(config.path, schema::archive::txs), config.txs_size, config.txs_rate, sequential, staged), + txs_body_(body(config.path, schema::archive::txs), config.txs.size, config.txs.rate, sequential, staged), // Indexes. // ------------------------------------------------------------------------ candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), 1, 0, sequential), - candidate_body_(body(config.path, schema::indexes::candidate), config.candidate_size, config.candidate_rate, sequential, staged), + candidate_body_(body(config.path, schema::indexes::candidate), config.candidate.size, config.candidate.rate, sequential, staged), confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), 1, 0, sequential), - confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed_size, config.confirmed_rate, sequential, staged), + confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed.size, config.confirmed.rate, sequential, staged), strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx), 1, 0, random), - strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx_size, config.strong_tx_rate, sequential, staged), + strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx.size, config.strong_tx.rate, sequential, staged), // Caches. // ------------------------------------------------------------------------ // TODO: body not random, but keep in memory. ecdsa_head_(head(config.path / schema::dir::heads, schema::caches::ecdsa), 1, 0, sequential), - ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa_size, config.ecdsa_rate, sequential, staged), + ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa.size, config.ecdsa.rate, sequential, staged), // TODO: body not random, but keep in memory. schnorr_head_(head(config.path / schema::dir::heads, schema::caches::schnorr), 1, 0, sequential), - schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr_size, config.schnorr_rate, sequential, staged), + schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr.size, config.schnorr.rate, sequential, staged), silent_head_(head(config.path / schema::dir::heads, schema::caches::silent), 1, 0, sequential), - silent_body_(body(config.path, schema::caches::silent), config.silent_size, config.silent_rate, sequential, staged), + silent_body_(body(config.path, schema::caches::silent), config.silent.size, config.silent.rate, sequential, staged), duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate), 1, 0, random), - duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate_size, config.duplicate_rate, sequential, staged), + duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate.size, config.duplicate.rate, sequential, staged), prevalid_head_(head(config.path / schema::dir::heads, schema::caches::prevalid), 1, 0, sequential), - prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid_size, config.prevalid_rate, sequential, staged), + prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid.size, config.prevalid.rate, sequential, staged), prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout), 1, 0, random), - prevout_body_(body(config.path, schema::caches::prevout), config.prevout_size, config.prevout_rate, sequential, staged), + prevout_body_(body(config.path, schema::caches::prevout), config.prevout.size, config.prevout.rate, sequential, staged), validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk), 1, 0, random), - validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk_size, config.validated_bk_rate, sequential, staged), + validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk.size, config.validated_bk.rate, sequential, staged), validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx), 1, 0, random), - validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx_size, config.validated_tx_rate, sequential, staged), + validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx.size, config.validated_tx.rate, sequential, staged), // Optionals. // ------------------------------------------------------------------------ address_head_(head(config.path / schema::dir::heads, schema::optionals::address), 1, 0, random), - address_body_(body(config.path, schema::optionals::address), config.address_size, config.address_rate, sequential, staged), + address_body_(body(config.path, schema::optionals::address), config.address.size, config.address.rate, sequential, staged), filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk), 1, 0, random), - filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk_size, config.filter_bk_rate, sequential, staged), + filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk.size, config.filter_bk.rate, sequential, staged), filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx), 1, 0, random), - filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx_size, config.filter_tx_rate, sequential, staged), + filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx.size, config.filter_tx.rate, sequential, staged), // Locks. // ------------------------------------------------------------------------ @@ -114,30 +114,30 @@ CLASS::store(const settings& config) NOEXCEPT // Tables. // ------------------------------------------------------------------------ - header(header_head_, header_body_, config.header_buckets), + header(header_head_, header_body_, config.header.buckets), input(input_head_, input_body_), output(output_head_, output_body_), - ins(ins_head_, ins_body_, config.ins_buckets), + ins(ins_head_, ins_body_, config.ins.buckets), outs(outs_head_, outs_body_), - tx(tx_head_, tx_body_, config.tx_buckets), - txs(txs_head_, txs_body_, config.txs_buckets), + tx(tx_head_, tx_body_, config.tx.buckets), + txs(txs_head_, txs_body_, config.txs.buckets), candidate(candidate_head_, candidate_body_), confirmed(confirmed_head_, confirmed_body_), - strong_tx(strong_tx_head_, strong_tx_body_, config.strong_tx_buckets), + strong_tx(strong_tx_head_, strong_tx_body_, config.strong_tx.buckets), ecdsa(ecdsa_head_, ecdsa_body_), schnorr(schnorr_head_, schnorr_body_), silent(silent_head_, silent_body_), - duplicate(duplicate_head_, duplicate_body_, config.duplicate_buckets), + duplicate(duplicate_head_, duplicate_body_, config.duplicate.buckets), prevalid(prevalid_head_, prevalid_body_), - prevout(prevout_head_, prevout_body_, config.prevout_buckets), - validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk_buckets), - validated_tx(validated_tx_head_, validated_tx_body_, config.validated_tx_buckets), + prevout(prevout_head_, prevout_body_, config.prevout.buckets), + validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk.buckets), + validated_tx(validated_tx_head_, validated_tx_body_, config.validated_tx.buckets), - address(address_head_, address_body_, config.address_buckets), - filter_bk(filter_bk_head_, filter_bk_body_, config.filter_bk_buckets), - filter_tx(filter_tx_head_, filter_tx_body_, config.filter_tx_buckets) + address(address_head_, address_body_, config.address.buckets), + filter_bk(filter_bk_head_, filter_bk_body_, config.filter_bk.buckets), + filter_tx(filter_tx_head_, filter_tx_body_, config.filter_tx.buckets) { } diff --git a/include/bitcoin/database/settings.hpp b/include/bitcoin/database/settings.hpp index a41f63abe..87f55e54e 100644 --- a/include/bitcoin/database/settings.hpp +++ b/include/bitcoin/database/settings.hpp @@ -36,6 +36,27 @@ struct BCD_API settings settings() NOEXCEPT; settings(system::chain::selection context) NOEXCEPT; + /// Table settings. + /// ----------------------------------------------------------------------- + + /// Body storage sizing (all tables). + struct simple_table + { + /// Minimum body allocation (bytes). + uint64_t size{ 1 }; + + /// Body expansion rate (percentage). + uint16_t rate{ 50 }; + }; + + /// Adds the head bucket count (hashmap hash space or arraymap length). + struct bucket_table + : public simple_table + { + /// Head bucket count. + uint32_t buckets{ 128 }; + }; + /// Properties. /// ----------------------------------------------------------------------- @@ -57,90 +78,39 @@ struct BCD_API settings /// Archives. /// ----------------------------------------------------------------------- - uint32_t header_buckets; - uint64_t header_size; - uint16_t header_rate; - - uint64_t input_size; - uint16_t input_rate; - - uint64_t output_size; - uint16_t output_rate; - - uint32_t ins_buckets; - uint64_t ins_size; - uint16_t ins_rate; - - uint64_t outs_size; - uint16_t outs_rate; - - uint32_t tx_buckets; - uint64_t tx_size; - uint16_t tx_rate; - - uint32_t txs_buckets; - uint64_t txs_size; - uint16_t txs_rate; + bucket_table header{}; + simple_table input{}; + simple_table output{}; + bucket_table ins{}; + simple_table outs{}; + bucket_table tx{}; + bucket_table txs{}; /// Indexes. /// ----------------------------------------------------------------------- - uint64_t candidate_size; - uint16_t candidate_rate; - - uint64_t confirmed_size; - uint16_t confirmed_rate; - - uint32_t strong_tx_buckets; - uint64_t strong_tx_size; - uint16_t strong_tx_rate; + simple_table candidate{}; + simple_table confirmed{}; + bucket_table strong_tx{}; /// Caches. /// ----------------------------------------------------------------------- - uint64_t ecdsa_size; - uint16_t ecdsa_rate; - - uint64_t schnorr_size; - uint16_t schnorr_rate; - - uint64_t silent_size; - uint16_t silent_rate; - - // This one is 16 bit (could use table link::integer) for these. - uint16_t duplicate_buckets; - uint64_t duplicate_size; - uint16_t duplicate_rate; - - uint64_t prevalid_size; - uint16_t prevalid_rate; - - uint32_t prevout_buckets; - uint64_t prevout_size; - uint16_t prevout_rate; - - uint32_t validated_bk_buckets; - uint64_t validated_bk_size; - uint16_t validated_bk_rate; - - uint32_t validated_tx_buckets; - uint64_t validated_tx_size; - uint16_t validated_tx_rate; + simple_table ecdsa{}; + simple_table schnorr{}; + simple_table silent{}; + bucket_table duplicate{}; + simple_table prevalid{}; + bucket_table prevout{}; + bucket_table validated_bk{}; + bucket_table validated_tx{}; /// Optionals. /// ----------------------------------------------------------------------- - uint32_t address_buckets; - uint64_t address_size; - uint16_t address_rate; - - uint32_t filter_bk_buckets; - uint64_t filter_bk_size; - uint16_t filter_bk_rate; - - uint32_t filter_tx_buckets; - uint64_t filter_tx_size; - uint16_t filter_tx_rate; + bucket_table address{}; + bucket_table filter_bk{}; + bucket_table filter_tx{}; }; } // namespace database diff --git a/src/settings.cpp b/src/settings.cpp index d956ded4c..d936fe468 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -26,89 +26,6 @@ namespace database { using namespace bc::system; settings::settings() NOEXCEPT - : - // Archives. - - header_buckets{ 128 }, - header_size{ 1 }, - header_rate{ 50 }, - - input_size{ 1 }, - input_rate{ 50 }, - - output_size{ 1 }, - output_rate{ 50 }, - - ins_buckets{ 128 }, - ins_size{ 1 }, - ins_rate{ 50 }, - - outs_size{ 1 }, - outs_rate{ 50 }, - - tx_buckets{ 128 }, - tx_size{ 1 }, - tx_rate{ 50 }, - - txs_buckets{ 128 }, - txs_size{ 1 }, - txs_rate{ 50 }, - - // Indexes. - - candidate_size{ 1 }, - candidate_rate{ 50 }, - - confirmed_size{ 1 }, - confirmed_rate{ 50 }, - - strong_tx_buckets{ 128 }, - strong_tx_size{ 1 }, - strong_tx_rate{ 50 }, - - // Caches. - - ecdsa_size{ 1 }, - ecdsa_rate{ 50 }, - - schnorr_size{ 1 }, - schnorr_rate{ 50 }, - - silent_size{ 1 }, - silent_rate{ 50 }, - - duplicate_buckets{ 128 }, - duplicate_size{ 1 }, - duplicate_rate{ 50 }, - - prevalid_size{ 1 }, - prevalid_rate{ 50 }, - - prevout_buckets{ 128 }, - prevout_size{ 1 }, - prevout_rate{ 50 }, - - validated_bk_buckets{ 128 }, - validated_bk_size{ 1 }, - validated_bk_rate{ 50 }, - - validated_tx_buckets{ 128 }, - validated_tx_size{ 1 }, - validated_tx_rate{ 50 }, - - // Optionals. - - address_buckets{ 128 }, - address_size{ 1 }, - address_rate{ 50 }, - - filter_bk_buckets{ 128 }, - filter_bk_size{ 1 }, - filter_bk_rate{ 50 }, - - filter_tx_buckets{ 128 }, - filter_tx_size{ 1 }, - filter_tx_rate{ 50 } { } diff --git a/test/query/archive/chain_reader.cpp b/test/query/archive/chain_reader.cpp index 57ab00c53..1cfd0e764 100644 --- a/test/query/archive/chain_reader.cpp +++ b/test/query/archive/chain_reader.cpp @@ -1,906 +1,906 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "../../test.hpp" -#include "../../mocks/blocks.hpp" -#include "../../mocks/chunk_store.hpp" - -// ensure context::flags is same size as chain_context::flags. -static_assert(is_same_type < database::context::flag_t::integer, decltype(system::chain::context{}.flags) > ); - -BOOST_FIXTURE_TEST_SUITE(query_chain_reader_tests, test::directory_setup_fixture) - -// get_header - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_header__invalid_parent__expected) -{ - constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); - constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); - const system::chain::header header - { - 0x31323334, // version - system::null_hash, // previous_block_hash - root, // merkle_root - 0x41424344, // timestamp - 0x51525354, // bits - 0x61626364 // nonce - }; - const auto expected_header_head = system::base16_chunk( - "010000" // record count - "ffffff" // bucket[0]... - "000000" // pk-> - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff"); - const auto expected_header_body = system::base16_chunk( - "ffffff" // next-> - "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) - "14131211" // flags - "040302" // height - "24232221" // mtp - "424242" // previous_block_hash (header_fk - invalid) (milestone false) - "34333231" // version - "44434241" // timestamp - "54535251" // bits - "64636261" // nonce - "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root - - settings settings{}; - settings.header_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - store.header_head() = expected_header_head; - store.header_body() = expected_header_body; - BOOST_CHECK(!query.get_header(query.to_header(block_hash))); - BOOST_CHECK(!query.get_header(header_link::terminal)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_header__default__expected) -{ - constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); - constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); - static_assert(0x45d6f6162ab0d085_u64 % 10u == 5u); - const system::chain::header header - { - 0x31323334, // version - system::null_hash, // previous_block_hash - root, // merkle_root - 0x41424344, // timestamp - 0x51525354, // bits - 0x61626364 // nonce - }; - - // TODO: heads may be undersized due to the change to base2 sizing. - const auto expected_header_head = system::base16_chunk( - "000000ff" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "000080" // filter[8], pk-> - "de" // filter[0-7] - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - - const auto expected_header_body = system::base16_chunk( - "ffff7f" // next-> - "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) - "14131211" // flags - "040302" // height - "24232221" // mtp - "ffffff" // previous_block_hash (header_fk - terminal) (milestone true) - "34333231" // version - "44434241" // timestamp - "54535251" // bits - "64636261" // nonce - "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root - - settings settings{}; - settings.header_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - ////store.header_head() = expected_header_head; - ////store.header_body() = expected_header_body; - BOOST_CHECK(query.set(header, context{ 0x11121314, 0x01020304, 0x21222324 }, true)); - BOOST_CHECK_EQUAL(store.header_head(), expected_header_head); - BOOST_CHECK_EQUAL(store.header_body(), expected_header_body); - - const auto foo = query.to_header(block_hash); - const auto pointer1 = query.get_header(foo); - BOOST_CHECK(pointer1); - BOOST_CHECK(*pointer1 == header); - - // Verify hash caching. - BOOST_CHECK_EQUAL(pointer1->hash(), block_hash); -} - -// is_coinbase - -BOOST_AUTO_TEST_CASE(query_chain_reader__is_coinbase__coinbase__true) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{}, false, false)); - BOOST_CHECK(query.set(test::block2, context{}, false, false)); - BOOST_CHECK(query.set(test::block3, context{}, false, false)); - BOOST_CHECK(query.is_coinbase(0)); - BOOST_CHECK(query.is_coinbase(1)); - BOOST_CHECK(query.is_coinbase(2)); - BOOST_CHECK(query.is_coinbase(3)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__is_coinbase__non_coinbase__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{}, false, false)); - BOOST_CHECK(query.set(test::block2a, context{}, false, false)); - BOOST_CHECK(!query.is_coinbase(1)); - BOOST_CHECK(!query.is_coinbase(2)); - BOOST_CHECK(!query.is_coinbase(3)); - BOOST_CHECK(!query.is_coinbase(4)); - BOOST_CHECK(!query.is_coinbase(5)); - BOOST_CHECK(!query.is_coinbase(42)); -} - -// is_tx_segregated - -BOOST_AUTO_TEST_CASE(query_chain_reader__is_tx_segregated__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{}, false, false)); - BOOST_CHECK(!query.is_tx_segregated(0)); - BOOST_CHECK( query.is_tx_segregated(1)); - BOOST_CHECK(!query.is_tx_segregated(2)); -} - -// is_block_segregated - -BOOST_AUTO_TEST_CASE(query_chain_reader__is_block_segregated__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{}, false, false)); - BOOST_CHECK(!query.is_block_segregated(0)); - BOOST_CHECK( query.is_block_segregated(1)); - BOOST_CHECK(!query.is_block_segregated(2)); -} - -// is_milestone - -BOOST_AUTO_TEST_CASE(query_chain_reader__is_milestone__genesis__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(!query.is_milestone(0)); - BOOST_CHECK(!query.is_milestone(1)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__is_milestone__set__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{}, true, false)); - BOOST_CHECK(query.set(test::block2, context{}, false, false));; - BOOST_CHECK(!query.is_milestone(0)); - BOOST_CHECK(query.is_milestone(1)); - BOOST_CHECK(!query.is_milestone(2)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_keys__not_found__empty) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.get_tx_keys(query.to_header(system::null_hash)).empty()); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_header_key__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_header_key(0), test::genesis.hash()); - BOOST_CHECK_EQUAL(query.get_header_key(1), system::null_hash); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_point_key__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_point_hash(0), system::null_hash); - - // tx4/5 prevouts are all block1a.tx1. - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(query.set(test::tx5)); - BOOST_CHECK_EQUAL(query.get_point_hash(1), test::block1a.transactions_ptr()->front()->hash(false)); - BOOST_CHECK_EQUAL(query.get_point_hash(2), test::block1a.transactions_ptr()->front()->hash(false)); - - // block1a adds three prevouts of two txs. - BOOST_CHECK(query.set(test::block1a, context{}, false, false)); - BOOST_CHECK_EQUAL(query.get_point_hash(4), system::one_hash); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_key__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_tx_key(0), test::genesis.transactions_ptr()->front()->hash(false)); - BOOST_CHECK_EQUAL(query.get_tx_key(1), system::null_hash); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(query.set(test::tx5)); - BOOST_CHECK_EQUAL(query.get_tx_key(1), test::tx4.hash(false)); - BOOST_CHECK_EQUAL(query.get_tx_key(2), test::tx5.hash(false)); - BOOST_CHECK_EQUAL(query.get_tx_key(3), system::null_hash); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_height1__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - - size_t out{}; - BOOST_CHECK(query.get_height(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_height(out, 1)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, 2)); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(query.get_height(out, 3)); - BOOST_CHECK_EQUAL(out, 3u); - BOOST_CHECK(query.get_height(out, 4)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, 5)); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(!query.get_height(out, 6)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_height2__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - - size_t out{}; - BOOST_CHECK(query.get_height(out, test::genesis.hash())); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_height(out, test::block1.hash())); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, test::block2.hash())); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(query.get_height(out, test::block3.hash())); - BOOST_CHECK_EQUAL(out, 3u); - BOOST_CHECK(query.get_height(out, test::block1a.hash())); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, test::block2a.hash())); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(!query.get_height(out, system::one_hash)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_height__not_strong__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - - size_t out{}; - BOOST_CHECK(!query.get_tx_height(out, 1)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_position__strong__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); - - size_t out{}; - const auto foo = query.get_tx_position(out, 0); - BOOST_CHECK(foo); - BOOST_CHECK_EQUAL(out, 0u); - - BOOST_CHECK(!query.get_tx_position(out, 1)); - BOOST_CHECK(!query.get_tx_position(out, 2)); - BOOST_CHECK(!query.get_tx_position(out, 3)); - BOOST_CHECK(!query.get_tx_position(out, 4)); - BOOST_CHECK(query.set_strong(1)); - BOOST_CHECK(query.set_strong(2)); - BOOST_CHECK(query.set_strong(3)); - - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 1)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 2)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 3)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_tx_position(out, 4)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(!query.get_tx_position(out, 5)); - - BOOST_CHECK_EQUAL(query.get_position_tx(0, 0), 0u); - BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u); - BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u); - BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u); - BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_position__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); - BOOST_CHECK(query.set(test::tx4)); - - size_t out{}; - BOOST_CHECK(query.get_tx_position(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - - BOOST_CHECK(!query.get_tx_position(out, 1)); - BOOST_CHECK(!query.get_tx_position(out, 2)); - BOOST_CHECK(!query.get_tx_position(out, 3)); - BOOST_CHECK(!query.get_tx_position(out, 4)); - BOOST_CHECK(query.set_strong(1)); - BOOST_CHECK(query.set_strong(2)); - BOOST_CHECK(query.set_strong(3)); - - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 1)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 2)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 3)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_tx_position(out, 4)); - BOOST_CHECK_EQUAL(out, 0u); - - // tx4 is unconfirmed. - BOOST_CHECK(!query.get_tx_position(out, 5)); -} - - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_position_tx__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); - - BOOST_CHECK_EQUAL(query.get_position_tx(0, 0), 0u); - BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u); - BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u); - BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u); - BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u); - BOOST_CHECK(query.get_position_tx(3, 1).is_terminal()); - BOOST_CHECK(query.get_position_tx({}, 0).is_terminal()); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_sizes__coinbase__204) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - - size_t light{}; - size_t heavy{}; - BOOST_CHECK(query.get_tx_sizes(light, heavy, 0)); - BOOST_CHECK_EQUAL(light, 204u); - BOOST_CHECK_EQUAL(heavy, 204u); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_count__coinbase__1) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_tx_count(0), 1u); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_witness__not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.get_witness(query.to_point({}, {}))); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_input_script__not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.get_input_script(query.to_point(tx_link{}, {}))); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_input__not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.get_input(query.to_point(tx_link{}, {}), false)); - BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), {}, false)); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_input__genesis__expected) -{ - settings settings{}; - settings.header_buckets = 8; - settings.tx_buckets = 8; - settings.txs_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.set(test::genesis, test::context, false, false)); - - const auto tx = test::genesis.transactions_ptr()->front(); - const auto input = tx->inputs_ptr()->front(); - const auto point = query.to_point(query.to_tx(tx->hash(false)), 0u); - BOOST_CHECK(*input == *query.get_input(point, false)); - BOOST_CHECK(input->witness() == *query.get_witness(point)); - BOOST_CHECK(input->script() == *query.get_input_script(point)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_inputs__tx_not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(!query.get_inputs(1, false)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_inputs__found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK_EQUAL(query.get_inputs(1, false)->size(), 2u); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_output_script__not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.get_output_script(query.to_output(system::null_hash, 0u))); - BOOST_CHECK(!query.get_output(0)); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_output__not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.get_output(query.to_tx(system::null_hash), 0u)); - BOOST_CHECK(!query.get_output(query.to_output(system::null_hash, 0u))); - BOOST_CHECK(!query.get_output(0)); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_output__genesis__expected) -{ - settings settings{}; - settings.header_buckets = 8; - settings.tx_buckets = 8; - settings.txs_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.set(test::genesis, test::context, false, false)); - - const auto tx = test::genesis.transactions_ptr()->front(); - const auto output = tx->outputs_ptr()->front(); - BOOST_CHECK(*output == *query.get_output(0)); - BOOST_CHECK(*output == *query.get_output(query.to_tx(tx->hash(false)), 0u)); - BOOST_CHECK(*output == *query.get_output(query.to_output(tx->hash(false), 0u))); - BOOST_CHECK(output->script() == *query.get_output_script(query.to_output(tx->hash(false), 0u))); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_outputs__tx_not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(!query.get_outputs(1)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_outputs__found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK_EQUAL(query.get_outputs(1)->size(), 1u); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_transactions__tx_not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(!query.get_transactions(3, false)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_transactions__found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK_EQUAL(query.get_transactions(0, false)->size(), 1u); - BOOST_CHECK_EQUAL(query.get_transactions(1, false)->size(), 1u); - BOOST_CHECK_EQUAL(query.get_transactions(2, false)->size(), 2u); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_spenders__unspent_or_not_found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, test::context, false, false)); - BOOST_CHECK(query.set(test::block2, test::context, false, false)); - BOOST_CHECK(query.set(test::block3, test::context, false, false)); - - // Caller should always test for nullptr. - BOOST_CHECK(query.get_spenders(output_link::terminal, true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(0, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(0, 1), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(1, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(1, 1), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(2, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(2, 1), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(3, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(3, 1), true)->empty()); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__get_value__genesis__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - - uint64_t value{}; - BOOST_CHECK(query.get_value(value, query.to_output(0, 0))); - BOOST_CHECK_EQUAL(value, 5000000000u); -} - -// populate_with_metadata -// ---------------------------------------------------------------------------- - -// METADATA IS SETTABLE ON CONST TEST OBJECTS. -// COPY CONSTRUCTION CREATES SAHRED POINTER REFERENCES. -// SO POPULATE ON CONST OBJECTS HAS SIDE EFFECTS IF THE OBJECTS SPAN TESTS. -const auto& clean_(const auto& block_or_tx) NOEXCEPT -{ - const auto inputs = block_or_tx.inputs_ptr(); - for (const auto& input : *inputs) - { - input->prevout.reset(); - input->metadata = system::chain::prevout{}; - } - - return block_or_tx; -} - -// First four blocks have only coinbase txs. -BOOST_AUTO_TEST_CASE(query_chain_reader__populate_with_metadata__null_prevouts__true) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, test::context, false, false)); - BOOST_CHECK(query.set(test::block2, test::context, false, false)); - BOOST_CHECK(query.set(test::block3, test::context, false, false)); - - const auto& copy = clean_(test::genesis); - const auto& copy1 = clean_(test::block1); - const auto& copy2 = clean_(test::block2); - const auto& copy3 = clean_(test::block3); - - BOOST_CHECK(query.populate_with_metadata(copy)); - BOOST_CHECK(query.populate_with_metadata(copy1)); - BOOST_CHECK(query.populate_with_metadata(copy2)); - BOOST_CHECK(query.populate_with_metadata(copy3)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__populate_with_metadata__partial_prevouts__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - - const auto& block1a = clean_(test::block1a); - - // Block populate treates first tx as null point. - BOOST_CHECK(query.populate_with_metadata(block1a)); - BOOST_CHECK(!query.populate_with_metadata(*block1a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(2))); - - // Block populate treates first tx as null point and other has missing prevouts. - const auto& block2a = clean_(test::block2a); - BOOST_CHECK(!query.populate_with_metadata(block2a)); - BOOST_CHECK(query.populate_with_metadata(*block2a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block2a.transactions_ptr()->at(1))); - BOOST_CHECK(query.populate_with_metadata(*block2a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block2a.inputs_ptr()->at(3))); - - const auto& tx4 = clean_(test::tx4); - BOOST_CHECK(query.populate_with_metadata(tx4)); - BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(0))); - BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(1))); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__populate_with_metadata__metadata__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(!query.is_coinbase(1)); - - // Genesis only has coinbase, which does not spend. - const auto& genesis = clean_(test::genesis); - - BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); - BOOST_CHECK(genesis.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); - - BOOST_CHECK(query.populate_with_metadata(genesis)); - - BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); - BOOST_CHECK(genesis.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); - - // Transaction population. - const auto& tx4 = clean_(test::tx4); - - BOOST_CHECK(!tx4.inputs_ptr()->at(0)->prevout); - BOOST_CHECK(tx4.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); - BOOST_CHECK(!tx4.inputs_ptr()->at(1)->prevout); - BOOST_CHECK(tx4.inputs_ptr()->at(1)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, max_uint32); - - BOOST_CHECK(query.populate_with_metadata(tx4)); - - // TODO: test non-coinbase and other parent. - // spent/mtp are defaults, coinbase/parent are set (to non-default values). - BOOST_CHECK(tx4.inputs_ptr()->at(0)->prevout); - BOOST_CHECK(!tx4.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, 1u); - BOOST_CHECK(tx4.inputs_ptr()->at(1)->prevout); - BOOST_CHECK(!tx4.inputs_ptr()->at(1)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, 1u); -} - -// populate_without_metadata -// ---------------------------------------------------------------------------- - -// First four blocks have only coinbase txs. -BOOST_AUTO_TEST_CASE(query_chain_reader__populate_without_metadata__null_prevouts__true) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, test::context, false, false)); - BOOST_CHECK(query.set(test::block2, test::context, false, false)); - BOOST_CHECK(query.set(test::block3, test::context, false, false)); - BOOST_CHECK(query.populate_without_metadata(test::genesis)); - BOOST_CHECK(query.populate_without_metadata(test::block1)); - BOOST_CHECK(query.populate_without_metadata(test::block2)); - BOOST_CHECK(query.populate_without_metadata(test::block3)); -} - -BOOST_AUTO_TEST_CASE(query_chain_reader__populate_without_metadata__partial_prevouts__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - - // Block populate treates first tx as null point. - BOOST_CHECK(query.populate_without_metadata(test::block1a)); - BOOST_CHECK(!query.populate_without_metadata(*test::block1a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(2))); - - // Block populate treates first tx as null point and other has missing prevouts. - BOOST_CHECK(!query.populate_without_metadata(test::block2a)); - BOOST_CHECK(query.populate_without_metadata(*test::block2a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block2a.transactions_ptr()->at(1))); - BOOST_CHECK(query.populate_without_metadata(*test::block2a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block2a.inputs_ptr()->at(3))); - - BOOST_CHECK(query.populate_without_metadata(test::tx4)); - BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(0))); - BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(1))); -} - -BOOST_AUTO_TEST_SUITE_END() +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../../test.hpp" +#include "../../mocks/blocks.hpp" +#include "../../mocks/chunk_store.hpp" + +// ensure context::flags is same size as chain_context::flags. +static_assert(is_same_type < database::context::flag_t::integer, decltype(system::chain::context{}.flags) > ); + +BOOST_FIXTURE_TEST_SUITE(query_chain_reader_tests, test::directory_setup_fixture) + +// get_header + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_header__invalid_parent__expected) +{ + constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); + constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); + const system::chain::header header + { + 0x31323334, // version + system::null_hash, // previous_block_hash + root, // merkle_root + 0x41424344, // timestamp + 0x51525354, // bits + 0x61626364 // nonce + }; + const auto expected_header_head = system::base16_chunk( + "010000" // record count + "ffffff" // bucket[0]... + "000000" // pk-> + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff"); + const auto expected_header_body = system::base16_chunk( + "ffffff" // next-> + "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) + "14131211" // flags + "040302" // height + "24232221" // mtp + "424242" // previous_block_hash (header_fk - invalid) (milestone false) + "34333231" // version + "44434241" // timestamp + "54535251" // bits + "64636261" // nonce + "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root + + settings settings{}; + settings.header.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + store.header_head() = expected_header_head; + store.header_body() = expected_header_body; + BOOST_CHECK(!query.get_header(query.to_header(block_hash))); + BOOST_CHECK(!query.get_header(header_link::terminal)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_header__default__expected) +{ + constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); + constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); + static_assert(0x45d6f6162ab0d085_u64 % 10u == 5u); + const system::chain::header header + { + 0x31323334, // version + system::null_hash, // previous_block_hash + root, // merkle_root + 0x41424344, // timestamp + 0x51525354, // bits + 0x61626364 // nonce + }; + + // TODO: heads may be undersized due to the change to base2 sizing. + const auto expected_header_head = system::base16_chunk( + "000000ff" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "000080" // filter[8], pk-> + "de" // filter[0-7] + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + + const auto expected_header_body = system::base16_chunk( + "ffff7f" // next-> + "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) + "14131211" // flags + "040302" // height + "24232221" // mtp + "ffffff" // previous_block_hash (header_fk - terminal) (milestone true) + "34333231" // version + "44434241" // timestamp + "54535251" // bits + "64636261" // nonce + "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root + + settings settings{}; + settings.header.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + ////store.header_head() = expected_header_head; + ////store.header_body() = expected_header_body; + BOOST_CHECK(query.set(header, context{ 0x11121314, 0x01020304, 0x21222324 }, true)); + BOOST_CHECK_EQUAL(store.header_head(), expected_header_head); + BOOST_CHECK_EQUAL(store.header_body(), expected_header_body); + + const auto foo = query.to_header(block_hash); + const auto pointer1 = query.get_header(foo); + BOOST_CHECK(pointer1); + BOOST_CHECK(*pointer1 == header); + + // Verify hash caching. + BOOST_CHECK_EQUAL(pointer1->hash(), block_hash); +} + +// is_coinbase + +BOOST_AUTO_TEST_CASE(query_chain_reader__is_coinbase__coinbase__true) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{}, false, false)); + BOOST_CHECK(query.set(test::block2, context{}, false, false)); + BOOST_CHECK(query.set(test::block3, context{}, false, false)); + BOOST_CHECK(query.is_coinbase(0)); + BOOST_CHECK(query.is_coinbase(1)); + BOOST_CHECK(query.is_coinbase(2)); + BOOST_CHECK(query.is_coinbase(3)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__is_coinbase__non_coinbase__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{}, false, false)); + BOOST_CHECK(query.set(test::block2a, context{}, false, false)); + BOOST_CHECK(!query.is_coinbase(1)); + BOOST_CHECK(!query.is_coinbase(2)); + BOOST_CHECK(!query.is_coinbase(3)); + BOOST_CHECK(!query.is_coinbase(4)); + BOOST_CHECK(!query.is_coinbase(5)); + BOOST_CHECK(!query.is_coinbase(42)); +} + +// is_tx_segregated + +BOOST_AUTO_TEST_CASE(query_chain_reader__is_tx_segregated__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{}, false, false)); + BOOST_CHECK(!query.is_tx_segregated(0)); + BOOST_CHECK( query.is_tx_segregated(1)); + BOOST_CHECK(!query.is_tx_segregated(2)); +} + +// is_block_segregated + +BOOST_AUTO_TEST_CASE(query_chain_reader__is_block_segregated__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{}, false, false)); + BOOST_CHECK(!query.is_block_segregated(0)); + BOOST_CHECK( query.is_block_segregated(1)); + BOOST_CHECK(!query.is_block_segregated(2)); +} + +// is_milestone + +BOOST_AUTO_TEST_CASE(query_chain_reader__is_milestone__genesis__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(!query.is_milestone(0)); + BOOST_CHECK(!query.is_milestone(1)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__is_milestone__set__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{}, true, false)); + BOOST_CHECK(query.set(test::block2, context{}, false, false));; + BOOST_CHECK(!query.is_milestone(0)); + BOOST_CHECK(query.is_milestone(1)); + BOOST_CHECK(!query.is_milestone(2)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_keys__not_found__empty) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.get_tx_keys(query.to_header(system::null_hash)).empty()); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_header_key__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_header_key(0), test::genesis.hash()); + BOOST_CHECK_EQUAL(query.get_header_key(1), system::null_hash); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_point_key__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_point_hash(0), system::null_hash); + + // tx4/5 prevouts are all block1a.tx1. + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(query.set(test::tx5)); + BOOST_CHECK_EQUAL(query.get_point_hash(1), test::block1a.transactions_ptr()->front()->hash(false)); + BOOST_CHECK_EQUAL(query.get_point_hash(2), test::block1a.transactions_ptr()->front()->hash(false)); + + // block1a adds three prevouts of two txs. + BOOST_CHECK(query.set(test::block1a, context{}, false, false)); + BOOST_CHECK_EQUAL(query.get_point_hash(4), system::one_hash); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_key__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_tx_key(0), test::genesis.transactions_ptr()->front()->hash(false)); + BOOST_CHECK_EQUAL(query.get_tx_key(1), system::null_hash); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(query.set(test::tx5)); + BOOST_CHECK_EQUAL(query.get_tx_key(1), test::tx4.hash(false)); + BOOST_CHECK_EQUAL(query.get_tx_key(2), test::tx5.hash(false)); + BOOST_CHECK_EQUAL(query.get_tx_key(3), system::null_hash); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_height1__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + + size_t out{}; + BOOST_CHECK(query.get_height(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_height(out, 1)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, 2)); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(query.get_height(out, 3)); + BOOST_CHECK_EQUAL(out, 3u); + BOOST_CHECK(query.get_height(out, 4)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, 5)); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(!query.get_height(out, 6)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_height2__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + + size_t out{}; + BOOST_CHECK(query.get_height(out, test::genesis.hash())); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_height(out, test::block1.hash())); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, test::block2.hash())); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(query.get_height(out, test::block3.hash())); + BOOST_CHECK_EQUAL(out, 3u); + BOOST_CHECK(query.get_height(out, test::block1a.hash())); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, test::block2a.hash())); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(!query.get_height(out, system::one_hash)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_height__not_strong__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + + size_t out{}; + BOOST_CHECK(!query.get_tx_height(out, 1)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_position__strong__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); + + size_t out{}; + const auto foo = query.get_tx_position(out, 0); + BOOST_CHECK(foo); + BOOST_CHECK_EQUAL(out, 0u); + + BOOST_CHECK(!query.get_tx_position(out, 1)); + BOOST_CHECK(!query.get_tx_position(out, 2)); + BOOST_CHECK(!query.get_tx_position(out, 3)); + BOOST_CHECK(!query.get_tx_position(out, 4)); + BOOST_CHECK(query.set_strong(1)); + BOOST_CHECK(query.set_strong(2)); + BOOST_CHECK(query.set_strong(3)); + + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 1)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 2)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 3)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_tx_position(out, 4)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(!query.get_tx_position(out, 5)); + + BOOST_CHECK_EQUAL(query.get_position_tx(0, 0), 0u); + BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u); + BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u); + BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u); + BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_position__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); + BOOST_CHECK(query.set(test::tx4)); + + size_t out{}; + BOOST_CHECK(query.get_tx_position(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + + BOOST_CHECK(!query.get_tx_position(out, 1)); + BOOST_CHECK(!query.get_tx_position(out, 2)); + BOOST_CHECK(!query.get_tx_position(out, 3)); + BOOST_CHECK(!query.get_tx_position(out, 4)); + BOOST_CHECK(query.set_strong(1)); + BOOST_CHECK(query.set_strong(2)); + BOOST_CHECK(query.set_strong(3)); + + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 1)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 2)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 3)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_tx_position(out, 4)); + BOOST_CHECK_EQUAL(out, 0u); + + // tx4 is unconfirmed. + BOOST_CHECK(!query.get_tx_position(out, 5)); +} + + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_position_tx__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); + + BOOST_CHECK_EQUAL(query.get_position_tx(0, 0), 0u); + BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u); + BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u); + BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u); + BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u); + BOOST_CHECK(query.get_position_tx(3, 1).is_terminal()); + BOOST_CHECK(query.get_position_tx({}, 0).is_terminal()); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_sizes__coinbase__204) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + + size_t light{}; + size_t heavy{}; + BOOST_CHECK(query.get_tx_sizes(light, heavy, 0)); + BOOST_CHECK_EQUAL(light, 204u); + BOOST_CHECK_EQUAL(heavy, 204u); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_tx_count__coinbase__1) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_tx_count(0), 1u); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_witness__not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.get_witness(query.to_point({}, {}))); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_input_script__not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.get_input_script(query.to_point(tx_link{}, {}))); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_input__not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.get_input(query.to_point(tx_link{}, {}), false)); + BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), {}, false)); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_input__genesis__expected) +{ + settings settings{}; + settings.header.buckets = 8; + settings.tx.buckets = 8; + settings.txs.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.set(test::genesis, test::context, false, false)); + + const auto tx = test::genesis.transactions_ptr()->front(); + const auto input = tx->inputs_ptr()->front(); + const auto point = query.to_point(query.to_tx(tx->hash(false)), 0u); + BOOST_CHECK(*input == *query.get_input(point, false)); + BOOST_CHECK(input->witness() == *query.get_witness(point)); + BOOST_CHECK(input->script() == *query.get_input_script(point)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_inputs__tx_not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(!query.get_inputs(1, false)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_inputs__found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK_EQUAL(query.get_inputs(1, false)->size(), 2u); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_output_script__not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.get_output_script(query.to_output(system::null_hash, 0u))); + BOOST_CHECK(!query.get_output(0)); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_output__not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.get_output(query.to_tx(system::null_hash), 0u)); + BOOST_CHECK(!query.get_output(query.to_output(system::null_hash, 0u))); + BOOST_CHECK(!query.get_output(0)); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_output__genesis__expected) +{ + settings settings{}; + settings.header.buckets = 8; + settings.tx.buckets = 8; + settings.txs.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.set(test::genesis, test::context, false, false)); + + const auto tx = test::genesis.transactions_ptr()->front(); + const auto output = tx->outputs_ptr()->front(); + BOOST_CHECK(*output == *query.get_output(0)); + BOOST_CHECK(*output == *query.get_output(query.to_tx(tx->hash(false)), 0u)); + BOOST_CHECK(*output == *query.get_output(query.to_output(tx->hash(false), 0u))); + BOOST_CHECK(output->script() == *query.get_output_script(query.to_output(tx->hash(false), 0u))); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_outputs__tx_not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(!query.get_outputs(1)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_outputs__found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK_EQUAL(query.get_outputs(1)->size(), 1u); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_transactions__tx_not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(!query.get_transactions(3, false)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_transactions__found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK_EQUAL(query.get_transactions(0, false)->size(), 1u); + BOOST_CHECK_EQUAL(query.get_transactions(1, false)->size(), 1u); + BOOST_CHECK_EQUAL(query.get_transactions(2, false)->size(), 2u); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_spenders__unspent_or_not_found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, test::context, false, false)); + BOOST_CHECK(query.set(test::block2, test::context, false, false)); + BOOST_CHECK(query.set(test::block3, test::context, false, false)); + + // Caller should always test for nullptr. + BOOST_CHECK(query.get_spenders(output_link::terminal, true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(0, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(0, 1), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(1, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(1, 1), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(2, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(2, 1), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(3, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(3, 1), true)->empty()); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__get_value__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + + uint64_t value{}; + BOOST_CHECK(query.get_value(value, query.to_output(0, 0))); + BOOST_CHECK_EQUAL(value, 5000000000u); +} + +// populate_with_metadata +// ---------------------------------------------------------------------------- + +// METADATA IS SETTABLE ON CONST TEST OBJECTS. +// COPY CONSTRUCTION CREATES SAHRED POINTER REFERENCES. +// SO POPULATE ON CONST OBJECTS HAS SIDE EFFECTS IF THE OBJECTS SPAN TESTS. +const auto& clean_(const auto& block_or_tx) NOEXCEPT +{ + const auto inputs = block_or_tx.inputs_ptr(); + for (const auto& input : *inputs) + { + input->prevout.reset(); + input->metadata = system::chain::prevout{}; + } + + return block_or_tx; +} + +// First four blocks have only coinbase txs. +BOOST_AUTO_TEST_CASE(query_chain_reader__populate_with_metadata__null_prevouts__true) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, test::context, false, false)); + BOOST_CHECK(query.set(test::block2, test::context, false, false)); + BOOST_CHECK(query.set(test::block3, test::context, false, false)); + + const auto& copy = clean_(test::genesis); + const auto& copy1 = clean_(test::block1); + const auto& copy2 = clean_(test::block2); + const auto& copy3 = clean_(test::block3); + + BOOST_CHECK(query.populate_with_metadata(copy)); + BOOST_CHECK(query.populate_with_metadata(copy1)); + BOOST_CHECK(query.populate_with_metadata(copy2)); + BOOST_CHECK(query.populate_with_metadata(copy3)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__populate_with_metadata__partial_prevouts__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + + const auto& block1a = clean_(test::block1a); + + // Block populate treates first tx as null point. + BOOST_CHECK(query.populate_with_metadata(block1a)); + BOOST_CHECK(!query.populate_with_metadata(*block1a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(2))); + + // Block populate treates first tx as null point and other has missing prevouts. + const auto& block2a = clean_(test::block2a); + BOOST_CHECK(!query.populate_with_metadata(block2a)); + BOOST_CHECK(query.populate_with_metadata(*block2a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block2a.transactions_ptr()->at(1))); + BOOST_CHECK(query.populate_with_metadata(*block2a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block2a.inputs_ptr()->at(3))); + + const auto& tx4 = clean_(test::tx4); + BOOST_CHECK(query.populate_with_metadata(tx4)); + BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(0))); + BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(1))); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__populate_with_metadata__metadata__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(!query.is_coinbase(1)); + + // Genesis only has coinbase, which does not spend. + const auto& genesis = clean_(test::genesis); + + BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); + BOOST_CHECK(genesis.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); + + BOOST_CHECK(query.populate_with_metadata(genesis)); + + BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); + BOOST_CHECK(genesis.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); + + // Transaction population. + const auto& tx4 = clean_(test::tx4); + + BOOST_CHECK(!tx4.inputs_ptr()->at(0)->prevout); + BOOST_CHECK(tx4.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); + BOOST_CHECK(!tx4.inputs_ptr()->at(1)->prevout); + BOOST_CHECK(tx4.inputs_ptr()->at(1)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, max_uint32); + + BOOST_CHECK(query.populate_with_metadata(tx4)); + + // TODO: test non-coinbase and other parent. + // spent/mtp are defaults, coinbase/parent are set (to non-default values). + BOOST_CHECK(tx4.inputs_ptr()->at(0)->prevout); + BOOST_CHECK(!tx4.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, 1u); + BOOST_CHECK(tx4.inputs_ptr()->at(1)->prevout); + BOOST_CHECK(!tx4.inputs_ptr()->at(1)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, 1u); +} + +// populate_without_metadata +// ---------------------------------------------------------------------------- + +// First four blocks have only coinbase txs. +BOOST_AUTO_TEST_CASE(query_chain_reader__populate_without_metadata__null_prevouts__true) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, test::context, false, false)); + BOOST_CHECK(query.set(test::block2, test::context, false, false)); + BOOST_CHECK(query.set(test::block3, test::context, false, false)); + BOOST_CHECK(query.populate_without_metadata(test::genesis)); + BOOST_CHECK(query.populate_without_metadata(test::block1)); + BOOST_CHECK(query.populate_without_metadata(test::block2)); + BOOST_CHECK(query.populate_without_metadata(test::block3)); +} + +BOOST_AUTO_TEST_CASE(query_chain_reader__populate_without_metadata__partial_prevouts__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + + // Block populate treates first tx as null point. + BOOST_CHECK(query.populate_without_metadata(test::block1a)); + BOOST_CHECK(!query.populate_without_metadata(*test::block1a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(2))); + + // Block populate treates first tx as null point and other has missing prevouts. + BOOST_CHECK(!query.populate_without_metadata(test::block2a)); + BOOST_CHECK(query.populate_without_metadata(*test::block2a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block2a.transactions_ptr()->at(1))); + BOOST_CHECK(query.populate_without_metadata(*test::block2a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block2a.inputs_ptr()->at(3))); + + BOOST_CHECK(query.populate_without_metadata(test::tx4)); + BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(0))); + BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(1))); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/archive/chain_writer.cpp b/test/query/archive/chain_writer.cpp index 6bcf6d4a9..725b39383 100644 --- a/test/query/archive/chain_writer.cpp +++ b/test/query/archive/chain_writer.cpp @@ -1,1518 +1,1518 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "../../test.hpp" -#include "../../mocks/blocks.hpp" -#include "../../mocks/chunk_store.hpp" - -// ensure context::flags is same size as chain_context::flags. -static_assert(is_same_type < database::context::flag_t::integer, decltype(system::chain::context{}.flags) > ); - -BOOST_FIXTURE_TEST_SUITE(query_chain_writer_tests, test::directory_setup_fixture) - -// slow test (mmap) -BOOST_AUTO_TEST_CASE(query_chain_writer__set_header__mmap_get_header__expected) -{ - constexpr auto milestone = false; - constexpr auto parent = system::null_hash; - constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); - constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); - const system::chain::header header - { - 0x31323334, // version - parent, // previous_block_hash - merkle_root,// merkle_root - 0x41424344, // timestamp - 0x51525354, // bits - 0x61626364 // nonce - }; - - settings settings{}; - settings.header_buckets = 16; - settings.path = TEST_DIRECTORY; - store store{ settings }; - query> query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.set(header, test::context, milestone)); - - table::header::record element1{}; - BOOST_CHECK(store.header.get(query.to_header(block_hash), element1)); - - const auto pointer = query.get_header(query.to_header(block_hash)); - BOOST_CHECK(pointer); - BOOST_CHECK(*pointer == header); - - // must open/close mmap - BOOST_CHECK(!store.close(test::events_handler)); - BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits)); - BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags); - BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp); - BOOST_CHECK_EQUAL(element1.milestone, milestone); - BOOST_CHECK_EQUAL(element1.version, header.version()); - BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal); - BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root()); - BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp()); - BOOST_CHECK_EQUAL(element1.bits, header.bits()); - BOOST_CHECK_EQUAL(element1.nonce, header.nonce()); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_header__is_header__expected) -{ - constexpr auto milestone = true; - constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); - constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); - const system::chain::header header - { - 0x31323334, // version - system::null_hash, // previous_block_hash - merkle_root,// merkle_root - 0x41424344, // timestamp - 0x51525354, // bits - 0x61626364 // nonce - }; - - // nosh - const auto expected_header_head = system::base16_chunk( - "010000ff" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "000080" // filter[8], pk-> - "de" // filter[0-7] - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - - const auto expected_header_body = system::base16_chunk( - "ffff7f" // next-> - "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) - "04030201" // flags - "141312" // height - "24232221" // mtp - "ffffff" // previous_block_hash (header_fk - not found) (milestone true) - "34333231" // version - "44434241" // timestamp - "54535251" // bits - "64636261" // nonce - "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); //merkle_root - - header_link link{}; - settings settings{}; - settings.header_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - // store open/close flushes record count to head. - BOOST_CHECK(!query.is_header(header.hash())); - BOOST_CHECK(!query.is_associated(0)); - BOOST_CHECK(!query.set_code(link, header, test::context, milestone)); - BOOST_CHECK(!link.is_terminal()); - BOOST_CHECK(query.is_header(header.hash())); - BOOST_CHECK(!query.is_associated(0)); - table::header::record element1{}; - BOOST_CHECK(store.header.get(query.to_header(block_hash), element1)); - BOOST_CHECK(!store.close(test::events_handler)); - BOOST_CHECK_EQUAL(store.header_head(), expected_header_head); - BOOST_CHECK_EQUAL(store.header_body(), expected_header_body); - - BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits)); - BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags); - BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp); - BOOST_CHECK_EQUAL(element1.milestone, milestone); - BOOST_CHECK_EQUAL(element1.version, header.version()); - BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal); - BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root()); - BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp()); - BOOST_CHECK_EQUAL(element1.bits, header.bits()); - BOOST_CHECK_EQUAL(element1.nonce, header.nonce()); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__empty__expected) -{ - const system::chain::transaction tx{}; - const auto expected_head5_array = system::base16_chunk("0000000000"); - const auto expected_head4_array = system::base16_chunk("00000000"); - const auto expected_head4_hash = system::base16_chunk( - "01000000" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - const auto expected_head5_hash = system::base16_chunk( - "0000000000" // record count - "ffffffffff" // bucket[0]... - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff"); - - // data_chunk store. - settings settings{}; - settings.tx_buckets = 8; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - // store open/close flushes record count to heads. - BOOST_CHECK(!query.set(tx)); - BOOST_CHECK(!store.close(test::events_handler)); - BOOST_CHECK_EQUAL(store.tx_head(), expected_head4_hash); - BOOST_CHECK_EQUAL(store.input_head(), expected_head5_array); - BOOST_CHECK_EQUAL(store.output_head(), expected_head5_array); - BOOST_CHECK_EQUAL(store.outs_head(), expected_head4_array); - BOOST_CHECK_EQUAL(store.tx_body().size(), schema::transaction::minrow); - BOOST_CHECK(store.ins_body().empty()); - BOOST_CHECK(store.input_body().empty()); - BOOST_CHECK(store.output_body().empty()); - BOOST_CHECK(store.outs_body().empty()); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_tx__null_input__expected) -{ - using namespace system::chain; - const transaction tx - { - 0x01020304, // version - inputs - { - input{ point{}, script{}, witness{}, 0 } - }, - outputs - { - output{ 0, script{} } - }, - 0x11121314 // locktime - }; - const auto expected_tx_head = system::base16_chunk( - "01000000" // record count - "00000000" // bucket[0]... pk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - const auto expected_tx_body = system::base16_chunk( - "ffffff7f" // next-> - "601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62" // sk (tx.hash(false)) - "3c0080" // light (coinbase merged) - "3c0000" // heavy - "14131211" // locktime - "04030201" // version - "010000" // ins_count - "010000" // outs_count - "00000000" // point_fk-> (ins_fk) - "00000000"); // outs_fk-> - const auto expected_outs_head = system::base16_chunk("01000000"); - const auto expected_outs_body = system::base16_chunk( - "0000000000"); // output0_fk-> - const auto expected_output_head = system::base16_chunk("0600000000"); - const auto expected_output_body = system::base16_chunk( - "00000000" // parent_fk-> - "00" // value - "00"); // script - const auto expected_ins_body = system::base16_chunk( - "ffffffff" // next-> - "0000000000000000000000000000000000000000000000000000000000000000" - "ffffff"); // index - ////const auto expected_spend_head = system::base16_chunk( - //// "01000000" // record count - //// "00000000" // pk-> - //// "ffffffff" - //// "ffffffff" - //// "ffffffff" - //// "ffffffff"); - ////const auto expected_spend_body = system::base16_chunk( - //// "ffffffff" // terminal-> - //// "00000000" // fp: point_stub - //// "ffffff" // fp: point_index (null) - //// "ffffffff" // point_fk-> - //// "00000000" // parent_fk-> - //// "00000000" // sequence - //// "0000000000"); // input_fk-> - const auto expected_input_head = system::base16_chunk("0200000000"); - const auto expected_input_body = system::base16_chunk( - "00" // script - "00"); // witness - constexpr auto tx_hash = system::base16_array("601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62"); - BOOST_CHECK_EQUAL(tx_hash, tx.hash(false)); - - // data_chunk store. - settings settings{}; - settings.tx_buckets = 8; - settings.ins_buckets = 8; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.set_code(tx)); - BOOST_CHECK(!store.close(test::events_handler)); - - BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head); - BOOST_CHECK_EQUAL(store.input_head(), expected_input_head); - BOOST_CHECK_EQUAL(store.output_head(), expected_output_head); - BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head); - ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head); - - BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body); - BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body); - BOOST_CHECK_EQUAL(store.input_body(), expected_input_body); - BOOST_CHECK_EQUAL(store.output_body(), expected_output_body); - BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body); - ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__get_tx__expected) -{ - using namespace system::chain; - const transaction tx - { - 0x2a, // version - inputs - { - input - { - point{ system::one_hash, 0x18 }, - script{ { { opcode::op_return }, { opcode::pick } } }, - witness{ "[242424]" }, - 0x2a // sequence - }, - input - { - point{ system::one_hash, 0x2a }, - script{ { { opcode::op_return }, { opcode::roll } } }, - witness{ "[424242]" }, - 0x18 // sequence - } - }, - outputs - { - output - { - 0x18, // value - script{ { { opcode::pick } } } - }, - output - { - 0x2a, // value - script{ { { opcode::roll } } } - } - }, - 0x18 // locktime - }; - const auto expected_tx_head = system::base16_chunk( - "01000000" // record count - "00000000" // bucket[0]... pk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - const auto expected_tx_body = system::base16_chunk( - "ffffff7f" // next-> - "d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a" // sk (tx.hash(false)) - "740000" // light (not coinbase) - "800000" // heavy - "18000000" // locktime - "2a000000" // version - "020000" // ins_count - "020000" // outs_count - "00000000" // point_fk-> (ins_fk) - "00000000"); // outs_fk-> - const auto expected_outs_head = system::base16_chunk("02000000"); - const auto expected_outs_body = system::base16_chunk( - "0000000000" // output0_fk-> - "0700000000"); // output1_fk-> - const auto expected_output_head = system::base16_chunk("0e00000000"); - const auto expected_output_body = system::base16_chunk( - "00000000" // parent_fk-> - "18" // value - "0179" // script - "00000000" // parent_fk-> - "2a" // value - "017a"); // script - const auto expected_ins_body = system::base16_chunk( - "ffffffff" // next-> - "0100000000000000000000000000000000000000000000000000000000000000" - "180000" // index - "ffffffff" // next-> - "0100000000000000000000000000000000000000000000000000000000000000" - "2a0000"); // index - ////const auto expected_spend_head = system::base16_chunk( - //// "02000000" // record count - //// "00000000" // spend0_fk-> - //// "ffffffff" - //// "ffffffff" - //// "01000000" // spend1_fk-> - //// "ffffffff"); - ////const auto expected_spend_body = system::base16_chunk( - //// "ffffffff" // terminal-> - //// "01000000" // fp: point_stub - //// "180000" // fp: point_index - //// "00000000" // point_fk-> - //// "00000000" // parent_fk-> - //// "2a000000" // sequence - //// "0000000000" // input_fk-> - //// - //// "ffffffff" // terminal-> - //// "01000000" // fp: point_stub - //// "2a0000" // fp: point_index - //// "01000000" // point_fk-> - //// "00000000" // parent_fk-> - //// "18000000" // sequence - //// "0800000000"); // input_fk-> - const auto expected_input_head = system::base16_chunk("1000000000"); - const auto expected_input_body = system::base16_chunk( - "026a79" // script - "0103242424" // witness - "026a7a" // script - "0103424242"); // witness - - constexpr auto tx_hash = system::base16_array("d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a"); - BOOST_CHECK_EQUAL(tx_hash, tx.hash(false)); - - // data_chunk store. - settings settings{}; - settings.tx_buckets = 8; - settings.ins_buckets = 8; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.is_tx(tx.hash(false))); - BOOST_CHECK(query.set(tx)); - BOOST_CHECK(query.is_tx(tx.hash(false))); - - const auto pointer = query.get_transaction(query.to_tx(tx_hash), true); - BOOST_CHECK(pointer); - BOOST_CHECK(*pointer == tx); - BOOST_CHECK_EQUAL(pointer->hash(false), tx_hash); - BOOST_CHECK(!store.close(test::events_handler)); - - BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head); - BOOST_CHECK_EQUAL(store.input_head(), expected_input_head); - BOOST_CHECK_EQUAL(store.output_head(), expected_output_head); - BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head); - ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head); - - BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body); - BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body); - BOOST_CHECK_EQUAL(store.input_body(), expected_input_body); - BOOST_CHECK_EQUAL(store.output_body(), expected_output_body); - BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body); - ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__set_block__get_block__expected) -{ - constexpr auto milestone = true; - const auto genesis_header_head = system::base16_chunk( - "010000ff" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "000080" // filter[8], pk-> - "da"); // filter[0-7] - const auto genesis_header_body = system::base16_chunk( - "ffff7f" // next-> - "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash) - "04030201" // flags - "141312" // height - "24232221" // mtp - "ffffff" // previous_block_hash (header_fk - not found) (milestone true) - "01000000" // version - "29ab5f49" // timestamp - "ffff001d" // bits - "1dac2b7c" // nonce - "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root - const auto genesis_tx_head = system::base16_chunk( - "01000000" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "00000000" // pk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - const auto genesis_tx_body = system::base16_chunk( - "ffffff7f" // next-> - "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false)) - "cc0080" // light (coinbase merged) - "cc0000" // heavy - "00000000" // locktime - "01000000" // version - "010000" // ins_count - "010000" // outs_count - "00000000" // point_fk-> (ins_fk) - "00000000"); // outs_fk-> - const auto genesis_outs_head = system::base16_chunk("01000000"); - const auto genesis_outs_body = system::base16_chunk( - "0000000000"); // output0_fk-> - const auto genesis_output_head = system::base16_chunk("5100000000"); - const auto genesis_output_body = system::base16_chunk( - "00000000" // parent_fk-> - "ff00f2052a01000000" // value - "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script - const auto genesis_ins_body = system::base16_chunk( - "ffffffff" // next-> - "0000000000000000000000000000000000000000000000000000000000000000" - "ffffff"); // index - const auto genesis_spend_head = system::base16_chunk( - "01000000" // record count - "00000000" // spend0_fk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - const auto genesis_spend_body = system::base16_chunk( - "ffffffff" // terminal-> - "00000000" // fp: point_stub - "ffffff" // fp: point_index (null) - "ffffffff" // point_fk-> - "00000000" // parent_fk-> - "ffffffff" // sequence - "0000000000"); // input_fk-> (coinbase) - const auto genesis_input_head = system::base16_chunk("4f00000000"); - const auto genesis_input_body = system::base16_chunk( - "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script - "00"); // witness - const auto genesis_txs_head = system::base16_chunk( - "1100000000" // slab size - "0000000000" // pk-> - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff"); - const auto genesis_txs_body = system::base16_chunk( - "1d0100" // size light (285) - "1d0100" // size heavy (285) - "0100" // txs count (1) - "00000000" // transaction[0] - "ff" // depth (255) - genesis only - "00000000"); // forks (0) - genesis only - - settings settings{}; - settings.header_buckets = 8; - settings.tx_buckets = 8; - settings.ins_buckets = 8; - settings.txs_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - // Set block (header/txs). - BOOST_CHECK(!query.is_block(test::genesis.hash())); - BOOST_CHECK(query.set(test::genesis, test::context, milestone, false)); - BOOST_CHECK(query.is_block(test::genesis.hash())); - - table::header::record element1{}; - BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1)); - BOOST_CHECK(!store.close(test::events_handler)); - - BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head); - BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head); - BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head); - BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head); - BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head); - ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head); - BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head); - - BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body); - BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body); - BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body); - BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body); - BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body); - ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body); - BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body); - - const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false); - BOOST_CHECK(pointer); - BOOST_CHECK(*pointer == test::genesis); - - const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash())); - BOOST_CHECK_EQUAL(hashes.size(), 1u); - BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__set_block_txs__get_block__expected) -{ - constexpr auto milestone = true; - const auto genesis_header_head = system::base16_chunk( - "010000ff" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "000080" // filter[8], pk-> - "da"); // filter[0-7] - const auto genesis_header_body = system::base16_chunk( - "ffff7f" // next-> - "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash) - "04030201" // flags - "141312" // height - "24232221" // mtp - "ffffff" // previous_block_hash (header_fk - not found) (milestone true) - "01000000" // version - "29ab5f49" // timestamp - "ffff001d" // bits - "1dac2b7c" // nonce - "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root - const auto genesis_tx_head = system::base16_chunk( - "01000000" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "00000000" // pk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - const auto genesis_tx_body = system::base16_chunk( - "ffffff7f" // next-> - "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false)) - "cc0080" // light (coinbase merged) - "cc0000" // heavy - "00000000" // locktime - "01000000" // version - "010000" // ins_count - "010000" // outs_count - "00000000" // point_fk-> (ins_fk) - "00000000"); // outs_fk-> - const auto genesis_outs_head = system::base16_chunk("01000000"); - const auto genesis_outs_body = system::base16_chunk( - "00000000" // spend0_fk-> - "0000000000"); // output0_fk-> - const auto genesis_output_head = system::base16_chunk("5100000000"); - const auto genesis_output_body = system::base16_chunk( - "00000000" // parent_fk-> - "ff00f2052a01000000" // value - "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script - const auto genesis_ins_body = system::base16_chunk( - "ffffffff" // next-> - "0000000000000000000000000000000000000000000000000000000000000000" - "ffffff"); // index - const auto genesis_spend_head = system::base16_chunk( - "01000000" // record count - "00000000" // spend0_fk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - const auto genesis_spend_body = system::base16_chunk( - "ffffffff" // terminal-> - "00000000" // fp: point_hash - "ffffff" // fp: point_index (null) - "ffffffff" // point_fk-> - "00000000" // parent_fk-> - "ffffffff" // sequence - "0000000000"); // input_fk-> (coinbase) - const auto genesis_input_head = system::base16_chunk("4f00000000"); - const auto genesis_input_body = system::base16_chunk( - "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script - "00"); // witness - const auto genesis_txs_head = system::base16_chunk( - "1100000000" // slab size - "0000000000" // pk-> - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff" - "ffffffffff"); - const auto genesis_txs_body = system::base16_chunk( - "1d0100" // size light (285) - "1d0100" // size heavy (285) - "0100" // txs count (1) - "00000000" // transaction[0] - "ff" // depth (255) - genesis only - "00000000"); // forks (0) - genesis only - - settings settings{}; - settings.header_buckets = 8; - settings.tx_buckets = 8; - settings.ins_buckets = 8; - settings.txs_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - // Set header and then txs. - BOOST_CHECK(!query.is_block(test::genesis.hash())); - BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone)); - BOOST_CHECK(!query.is_associated(0)); - BOOST_CHECK(query.set(test::genesis, false, false)); - BOOST_CHECK(query.is_block(test::genesis.hash())); - BOOST_CHECK(query.is_associated(0)); - - table::header::record element1{}; - BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1)); - BOOST_CHECK(!store.close(test::events_handler)); - - BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head); - BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head); - BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head); - BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head); - BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head); - ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head); - BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head); - - BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body); - BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body); - BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body); - BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body); - BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body); - ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body); - BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body); - - const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false); - BOOST_CHECK(pointer); - BOOST_CHECK(*pointer == test::genesis); - - const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash())); - BOOST_CHECK_EQUAL(hashes.size(), 1u); - BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false)); -} - -// populate_with_metadata -// ---------------------------------------------------------------------------- - -// METADATA IS SETTABLE ON CONST TEST OBJECTS. -// COPY CONSTRUCTION CREATES SHARED POINTER REFERENCES. -// SO POPULATE ON CONST OBJECTS HAS SIDE EFFECTS IF THE OBJECTS SPAN TESTS. -const auto& clean_(const auto& block_or_tx) NOEXCEPT -{ - const auto inputs = block_or_tx.inputs_ptr(); - for (const auto& input: *inputs) - { - input->prevout.reset(); - input->metadata = system::chain::prevout{}; - } - - return block_or_tx; -} - -// First four blocks have only coinbase txs. -BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__null_prevouts__true) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, test::context, false, false)); - BOOST_CHECK(query.set(test::block2, test::context, false, false)); - BOOST_CHECK(query.set(test::block3, test::context, false, false)); - - const auto& copy = clean_(test::genesis); - const auto& copy1 = clean_(test::block1); - const auto& copy2 = clean_(test::block2); - const auto& copy3 = clean_(test::block3); - - BOOST_CHECK(query.populate_with_metadata(copy)); - BOOST_CHECK(query.populate_with_metadata(copy1)); - BOOST_CHECK(query.populate_with_metadata(copy2)); - BOOST_CHECK(query.populate_with_metadata(copy3)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__partial_prevouts__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - - const auto& block1a = clean_(test::block1a); - - // Block populate treates first tx as null point. - BOOST_CHECK( query.populate_with_metadata(block1a)); - BOOST_CHECK(!query.populate_with_metadata(*block1a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(2))); - - // Block populate treates first tx as null point and other has missing prevouts. - const auto& block2a = clean_(test::block2a); - BOOST_CHECK(!query.populate_with_metadata(block2a)); - BOOST_CHECK( query.populate_with_metadata(*block2a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block2a.transactions_ptr()->at(1))); - BOOST_CHECK( query.populate_with_metadata(*block2a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_with_metadata(*block2a.inputs_ptr()->at(3))); - - const auto& tx4 = clean_(test::tx4); - BOOST_CHECK(query.populate_with_metadata(tx4)); - BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(0))); - BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(1))); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__metadata__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(!query.is_coinbase(1)); - - // Genesis only has coinbase, which does not spend. - const auto& genesis = clean_(test::genesis); - - BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); - BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); - - BOOST_CHECK(query.populate_with_metadata(genesis)); - - BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); - BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); - - // Transaction population. - const auto& tx4 = clean_(test::tx4); - - BOOST_CHECK(!tx4.inputs_ptr()->at(0)->prevout); - BOOST_CHECK( tx4.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); - BOOST_CHECK(!tx4.inputs_ptr()->at(1)->prevout); - BOOST_CHECK( tx4.inputs_ptr()->at(1)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, max_uint32); - - BOOST_CHECK(query.populate_with_metadata(tx4)); - - // TODO: test non-coinbase and other parent. - // spent/mtp are defaults, coinbase/parent are set (to non-default values). - BOOST_CHECK( tx4.inputs_ptr()->at(0)->prevout); - BOOST_CHECK(!tx4.inputs_ptr()->at(0)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, 1u); - BOOST_CHECK( tx4.inputs_ptr()->at(1)->prevout); - BOOST_CHECK(!tx4.inputs_ptr()->at(1)->metadata.coinbase); - BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, 1u); -} - -// populate_without_metadata -// ---------------------------------------------------------------------------- - -// First four blocks have only coinbase txs. -BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__null_prevouts__true) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, test::context, false, false)); - BOOST_CHECK(query.set(test::block2, test::context, false, false)); - BOOST_CHECK(query.set(test::block3, test::context, false, false)); - BOOST_CHECK(query.populate_without_metadata(test::genesis)); - BOOST_CHECK(query.populate_without_metadata(test::block1)); - BOOST_CHECK(query.populate_without_metadata(test::block2)); - BOOST_CHECK(query.populate_without_metadata(test::block3)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__partial_prevouts__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - - // Block populate treates first tx as null point. - BOOST_CHECK( query.populate_without_metadata(test::block1a)); - BOOST_CHECK(!query.populate_without_metadata(*test::block1a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(2))); - - // Block populate treates first tx as null point and other has missing prevouts. - BOOST_CHECK(!query.populate_without_metadata(test::block2a)); - BOOST_CHECK( query.populate_without_metadata(*test::block2a.transactions_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block2a.transactions_ptr()->at(1))); - BOOST_CHECK( query.populate_without_metadata(*test::block2a.inputs_ptr()->at(0))); - BOOST_CHECK(!query.populate_without_metadata(*test::block2a.inputs_ptr()->at(3))); - - BOOST_CHECK(query.populate_without_metadata(test::tx4)); - BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(0))); - BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(1))); -} - -// ---------------------------------------------------------------------------- - -// archive_write (foreign-keyed) - -BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__coinbase__true) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{}, false, false)); - BOOST_CHECK(query.set(test::block2, context{}, false, false)); - BOOST_CHECK(query.set(test::block3, context{}, false, false)); - BOOST_CHECK(query.is_coinbase(0)); - BOOST_CHECK(query.is_coinbase(1)); - BOOST_CHECK(query.is_coinbase(2)); - BOOST_CHECK(query.is_coinbase(3)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__non_coinbase__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{}, false, false)); - BOOST_CHECK(query.set(test::block2a, context{}, false, false)); - BOOST_CHECK(!query.is_coinbase(1)); - BOOST_CHECK(!query.is_coinbase(2)); - BOOST_CHECK(!query.is_coinbase(3)); - BOOST_CHECK(!query.is_coinbase(4)); - BOOST_CHECK(!query.is_coinbase(5)); - BOOST_CHECK(!query.is_coinbase(42)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__genesis__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(!query.is_milestone(0)); - BOOST_CHECK(!query.is_milestone(1)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__set__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{}, true, false)); - BOOST_CHECK(query.set(test::block2, context{}, false, false));; - BOOST_CHECK(!query.is_milestone(0)); - BOOST_CHECK(query.is_milestone(1)); - BOOST_CHECK(!query.is_milestone(2)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__invalid_parent__expected) -{ - constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); - constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); - const system::chain::header header - { - 0x31323334, // version - system::null_hash, // previous_block_hash - root, // merkle_root - 0x41424344, // timestamp - 0x51525354, // bits - 0x61626364 // nonce - }; - const auto expected_header_head = system::base16_chunk( - "010000" // record count - "ffffff" // bucket[0]... - "000000" // pk-> - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff" - "ffffff"); - const auto expected_header_body = system::base16_chunk( - "ffffff" // next-> - "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) - "14131211" // flags - "040302" // height - "24232221" // mtp - "424242" // previous_block_hash (header_fk - invalid) (milestone false) - "34333231" // version - "44434241" // timestamp - "54535251" // bits - "64636261" // nonce - "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root - - settings settings{}; - settings.header_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - store.header_head() = expected_header_head; - store.header_body() = expected_header_body; - BOOST_CHECK(!query.get_header(query.to_header(block_hash))); - BOOST_CHECK(!query.get_header(header_link::terminal)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__default__expected) -{ - constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); - constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); - static_assert(0x45d6f6162ab0d085_u64 % 10u == 5u); - const system::chain::header header - { - 0x31323334, // version - system::null_hash, // previous_block_hash - root, // merkle_root - 0x41424344, // timestamp - 0x51525354, // bits - 0x61626364 // nonce - }; - - // TODO: heads may be undersized due to the change to base2 sizing. - const auto expected_header_head = system::base16_chunk( - "000000ff" // record count - "ffffffff" // bucket[0]... - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "000080" // filter[8], pk-> - "de" // filter[0-7] - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); - - const auto expected_header_body = system::base16_chunk( - "ffff7f" // next-> - "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) - "14131211" // flags - "040302" // height - "24232221" // mtp - "ffffff" // previous_block_hash (header_fk - terminal) (milestone true) - "34333231" // version - "44434241" // timestamp - "54535251" // bits - "64636261" // nonce - "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root - - settings settings{}; - settings.header_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - ////store.header_head() = expected_header_head; - ////store.header_body() = expected_header_body; - BOOST_CHECK(query.set(header, context{ 0x11121314, 0x01020304, 0x21222324 }, true)); - BOOST_CHECK_EQUAL(store.header_head(), expected_header_head); - BOOST_CHECK_EQUAL(store.header_body(), expected_header_body); - - const auto foo = query.to_header(block_hash); - const auto pointer1 = query.get_header(foo); - BOOST_CHECK(pointer1); - BOOST_CHECK(*pointer1 == header); - - // Verify hash caching. - BOOST_CHECK_EQUAL(pointer1->hash(), block_hash); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_keys__not_found__empty) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.get_tx_keys(query.to_header(system::null_hash)).empty()); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_header_key__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_header_key(0), test::genesis.hash()); - BOOST_CHECK_EQUAL(query.get_header_key(1), system::null_hash); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_point_key__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_point_hash(0), system::null_hash); - - // tx4/5 prevouts are all block1a.tx1. - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(query.set(test::tx5)); - BOOST_CHECK_EQUAL(query.get_point_hash(1), test::block1a.transactions_ptr()->front()->hash(false)); - BOOST_CHECK_EQUAL(query.get_point_hash(2), test::block1a.transactions_ptr()->front()->hash(false)); - - // block1a adds three prevouts of two txs. - BOOST_CHECK(query.set(test::block1a, context{}, false, false)); - BOOST_CHECK_EQUAL(query.get_point_hash(4), system::one_hash); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_key__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_tx_key(0), test::genesis.transactions_ptr()->front()->hash(false)); - BOOST_CHECK_EQUAL(query.get_tx_key(1), system::null_hash); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(query.set(test::tx5)); - BOOST_CHECK_EQUAL(query.get_tx_key(1), test::tx4.hash(false)); - BOOST_CHECK_EQUAL(query.get_tx_key(2), test::tx5.hash(false)); - BOOST_CHECK_EQUAL(query.get_tx_key(3), system::null_hash); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_height1__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - - size_t out{}; - BOOST_CHECK(query.get_height(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_height(out, 1)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, 2)); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(query.get_height(out, 3)); - BOOST_CHECK_EQUAL(out, 3u); - BOOST_CHECK(query.get_height(out, 4)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, 5)); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(!query.get_height(out, 6)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_height2__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - - size_t out{}; - BOOST_CHECK(query.get_height(out, test::genesis.hash())); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_height(out, test::block1.hash())); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, test::block2.hash())); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(query.get_height(out, test::block3.hash())); - BOOST_CHECK_EQUAL(out, 3u); - BOOST_CHECK(query.get_height(out, test::block1a.hash())); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_height(out, test::block2a.hash())); - BOOST_CHECK_EQUAL(out, 2u); - BOOST_CHECK(!query.get_height(out, system::one_hash)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_height__not_strong__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - - size_t out{}; - BOOST_CHECK(!query.get_tx_height(out, 1)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__confirmed__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); - - size_t out{}; - const auto foo = query.get_tx_position(out, 0); - BOOST_CHECK(foo); - BOOST_CHECK_EQUAL(out, 0u); - - BOOST_CHECK(!query.get_tx_position(out, 1)); - BOOST_CHECK(!query.get_tx_position(out, 2)); - BOOST_CHECK(!query.get_tx_position(out, 3)); - BOOST_CHECK(!query.get_tx_position(out, 4)); - BOOST_CHECK(query.set_strong(1)); - BOOST_CHECK(query.set_strong(2)); - BOOST_CHECK(query.set_strong(3)); - - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 1)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 2)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 3)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_tx_position(out, 4)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(!query.get_tx_position(out, 5)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); - BOOST_CHECK(query.set(test::tx4)); - - size_t out{}; - BOOST_CHECK(query.get_tx_position(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - - BOOST_CHECK(!query.get_tx_position(out, 1)); - BOOST_CHECK(!query.get_tx_position(out, 2)); - BOOST_CHECK(!query.get_tx_position(out, 3)); - BOOST_CHECK(!query.get_tx_position(out, 4)); - BOOST_CHECK(query.set_strong(1)); - BOOST_CHECK(query.set_strong(2)); - BOOST_CHECK(query.set_strong(3)); - - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 0)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 1)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 2)); - BOOST_CHECK_EQUAL(out, 0u); - BOOST_CHECK(query.get_tx_position(out, 3)); - BOOST_CHECK_EQUAL(out, 1u); - BOOST_CHECK(query.get_tx_position(out, 4)); - BOOST_CHECK_EQUAL(out, 0u); - - // tx4 is unconfirmed. - BOOST_CHECK(!query.get_tx_position(out, 5)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_sizes__coinbase__204) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - - size_t light{}; - size_t heavy{}; - BOOST_CHECK(query.get_tx_sizes(light, heavy, 0)); - BOOST_CHECK_EQUAL(light, 204u); - BOOST_CHECK_EQUAL(heavy, 204u); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_count__coinbase__1) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK_EQUAL(query.get_tx_count(0), 1u); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), 0u, false)); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__genesis__expected) -{ - settings settings{}; - settings.header_buckets = 8; - settings.tx_buckets = 8; - settings.txs_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.set(test::genesis, test::context, false, false)); - - const auto tx = test::genesis.transactions_ptr()->front(); - const auto input = tx->inputs_ptr()->front(); - ////BOOST_CHECK(*input == *query.get_input(query.to_tx(tx->hash(false)), 0u)); - ////BOOST_CHECK(*input == *query.get_input(0)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__tx_not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(!query.get_inputs(1, false)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK_EQUAL(query.get_inputs(1, false)->size(), 2u); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(!query.get_output(query.to_tx(system::null_hash), 0u)); - BOOST_CHECK(!query.get_output(query.to_output(system::chain::point{ system::null_hash, 0u }))); - BOOST_CHECK(!query.get_output(0)); - BOOST_CHECK(!store.close(test::events_handler)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__genesis__expected) -{ - settings settings{}; - settings.header_buckets = 8; - settings.tx_buckets = 8; - settings.txs_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.set(test::genesis, test::context, false, false)); - - const auto tx = test::genesis.transactions_ptr()->front(); - const auto output1 = tx->outputs_ptr()->front(); - BOOST_CHECK(*output1 == *query.get_output(query.to_tx(tx->hash(false)), 0u)); - BOOST_CHECK(*output1 == *query.get_output(query.to_output(tx->hash(false), 0u))); - BOOST_CHECK(*output1 == *query.get_output(0)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__tx_not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(!query.get_outputs(1)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK_EQUAL(query.get_outputs(1)->size(), 1u); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__tx_not_found__nullptr) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK(!query.get_transactions(3, false)); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1a, test::context, false, false)); - BOOST_CHECK(query.set(test::block2a, test::context, false, false)); - BOOST_CHECK(query.set(test::tx4)); - BOOST_CHECK_EQUAL(query.get_transactions(0, false)->size(), 1u); - BOOST_CHECK_EQUAL(query.get_transactions(1, false)->size(), 1u); - BOOST_CHECK_EQUAL(query.get_transactions(2, false)->size(), 2u); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_spenders__unspent_or_not_found__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - BOOST_CHECK(query.set(test::block1, test::context, false, false)); - BOOST_CHECK(query.set(test::block2, test::context, false, false)); - BOOST_CHECK(query.set(test::block3, test::context, false, false)); - - // Caller should always test for nullptr. - BOOST_CHECK(query.get_spenders(output_link::terminal, true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(0, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(0, 1), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(1, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(1, 1), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(2, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(2, 1), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(3, 0), true)->empty()); - BOOST_CHECK(query.get_spenders(query.to_output(3, 1), true)->empty()); -} - -BOOST_AUTO_TEST_CASE(query_chain_writer__get_value__genesis__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - BOOST_CHECK(query.initialize(test::genesis)); - - uint64_t value{}; - BOOST_CHECK(query.get_value(value, query.to_output(0, 0))); - BOOST_CHECK_EQUAL(value, 5000000000u); -} - -BOOST_AUTO_TEST_SUITE_END() +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../../test.hpp" +#include "../../mocks/blocks.hpp" +#include "../../mocks/chunk_store.hpp" + +// ensure context::flags is same size as chain_context::flags. +static_assert(is_same_type < database::context::flag_t::integer, decltype(system::chain::context{}.flags) > ); + +BOOST_FIXTURE_TEST_SUITE(query_chain_writer_tests, test::directory_setup_fixture) + +// slow test (mmap) +BOOST_AUTO_TEST_CASE(query_chain_writer__set_header__mmap_get_header__expected) +{ + constexpr auto milestone = false; + constexpr auto parent = system::null_hash; + constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); + constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); + const system::chain::header header + { + 0x31323334, // version + parent, // previous_block_hash + merkle_root,// merkle_root + 0x41424344, // timestamp + 0x51525354, // bits + 0x61626364 // nonce + }; + + settings settings{}; + settings.header.buckets = 16; + settings.path = TEST_DIRECTORY; + store store{ settings }; + query> query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.set(header, test::context, milestone)); + + table::header::record element1{}; + BOOST_CHECK(store.header.get(query.to_header(block_hash), element1)); + + const auto pointer = query.get_header(query.to_header(block_hash)); + BOOST_CHECK(pointer); + BOOST_CHECK(*pointer == header); + + // must open/close mmap + BOOST_CHECK(!store.close(test::events_handler)); + BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits)); + BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags); + BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp); + BOOST_CHECK_EQUAL(element1.milestone, milestone); + BOOST_CHECK_EQUAL(element1.version, header.version()); + BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal); + BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root()); + BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp()); + BOOST_CHECK_EQUAL(element1.bits, header.bits()); + BOOST_CHECK_EQUAL(element1.nonce, header.nonce()); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_header__is_header__expected) +{ + constexpr auto milestone = true; + constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); + constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); + const system::chain::header header + { + 0x31323334, // version + system::null_hash, // previous_block_hash + merkle_root,// merkle_root + 0x41424344, // timestamp + 0x51525354, // bits + 0x61626364 // nonce + }; + + // nosh + const auto expected_header_head = system::base16_chunk( + "010000ff" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "000080" // filter[8], pk-> + "de" // filter[0-7] + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + + const auto expected_header_body = system::base16_chunk( + "ffff7f" // next-> + "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) + "04030201" // flags + "141312" // height + "24232221" // mtp + "ffffff" // previous_block_hash (header_fk - not found) (milestone true) + "34333231" // version + "44434241" // timestamp + "54535251" // bits + "64636261" // nonce + "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); //merkle_root + + header_link link{}; + settings settings{}; + settings.header.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + // store open/close flushes record count to head. + BOOST_CHECK(!query.is_header(header.hash())); + BOOST_CHECK(!query.is_associated(0)); + BOOST_CHECK(!query.set_code(link, header, test::context, milestone)); + BOOST_CHECK(!link.is_terminal()); + BOOST_CHECK(query.is_header(header.hash())); + BOOST_CHECK(!query.is_associated(0)); + table::header::record element1{}; + BOOST_CHECK(store.header.get(query.to_header(block_hash), element1)); + BOOST_CHECK(!store.close(test::events_handler)); + BOOST_CHECK_EQUAL(store.header_head(), expected_header_head); + BOOST_CHECK_EQUAL(store.header_body(), expected_header_body); + + BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits)); + BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags); + BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp); + BOOST_CHECK_EQUAL(element1.milestone, milestone); + BOOST_CHECK_EQUAL(element1.version, header.version()); + BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal); + BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root()); + BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp()); + BOOST_CHECK_EQUAL(element1.bits, header.bits()); + BOOST_CHECK_EQUAL(element1.nonce, header.nonce()); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__empty__expected) +{ + const system::chain::transaction tx{}; + const auto expected_head5_array = system::base16_chunk("0000000000"); + const auto expected_head4_array = system::base16_chunk("00000000"); + const auto expected_head4_hash = system::base16_chunk( + "01000000" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + const auto expected_head5_hash = system::base16_chunk( + "0000000000" // record count + "ffffffffff" // bucket[0]... + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff"); + + // data_chunk store. + settings settings{}; + settings.tx.buckets = 8; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + // store open/close flushes record count to heads. + BOOST_CHECK(!query.set(tx)); + BOOST_CHECK(!store.close(test::events_handler)); + BOOST_CHECK_EQUAL(store.tx_head(), expected_head4_hash); + BOOST_CHECK_EQUAL(store.input_head(), expected_head5_array); + BOOST_CHECK_EQUAL(store.output_head(), expected_head5_array); + BOOST_CHECK_EQUAL(store.outs_head(), expected_head4_array); + BOOST_CHECK_EQUAL(store.tx_body().size(), schema::transaction::minrow); + BOOST_CHECK(store.ins_body().empty()); + BOOST_CHECK(store.input_body().empty()); + BOOST_CHECK(store.output_body().empty()); + BOOST_CHECK(store.outs_body().empty()); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_tx__null_input__expected) +{ + using namespace system::chain; + const transaction tx + { + 0x01020304, // version + inputs + { + input{ point{}, script{}, witness{}, 0 } + }, + outputs + { + output{ 0, script{} } + }, + 0x11121314 // locktime + }; + const auto expected_tx_head = system::base16_chunk( + "01000000" // record count + "00000000" // bucket[0]... pk-> + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + const auto expected_tx_body = system::base16_chunk( + "ffffff7f" // next-> + "601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62" // sk (tx.hash(false)) + "3c0080" // light (coinbase merged) + "3c0000" // heavy + "14131211" // locktime + "04030201" // version + "010000" // ins_count + "010000" // outs_count + "00000000" // point_fk-> (ins_fk) + "00000000"); // outs_fk-> + const auto expected_outs_head = system::base16_chunk("01000000"); + const auto expected_outs_body = system::base16_chunk( + "0000000000"); // output0_fk-> + const auto expected_output_head = system::base16_chunk("0600000000"); + const auto expected_output_body = system::base16_chunk( + "00000000" // parent_fk-> + "00" // value + "00"); // script + const auto expected_ins_body = system::base16_chunk( + "ffffffff" // next-> + "0000000000000000000000000000000000000000000000000000000000000000" + "ffffff"); // index + ////const auto expected_spend_head = system::base16_chunk( + //// "01000000" // record count + //// "00000000" // pk-> + //// "ffffffff" + //// "ffffffff" + //// "ffffffff" + //// "ffffffff"); + ////const auto expected_spend_body = system::base16_chunk( + //// "ffffffff" // terminal-> + //// "00000000" // fp: point_stub + //// "ffffff" // fp: point_index (null) + //// "ffffffff" // point_fk-> + //// "00000000" // parent_fk-> + //// "00000000" // sequence + //// "0000000000"); // input_fk-> + const auto expected_input_head = system::base16_chunk("0200000000"); + const auto expected_input_body = system::base16_chunk( + "00" // script + "00"); // witness + constexpr auto tx_hash = system::base16_array("601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62"); + BOOST_CHECK_EQUAL(tx_hash, tx.hash(false)); + + // data_chunk store. + settings settings{}; + settings.tx.buckets = 8; + settings.ins.buckets = 8; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.set_code(tx)); + BOOST_CHECK(!store.close(test::events_handler)); + + BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head); + BOOST_CHECK_EQUAL(store.input_head(), expected_input_head); + BOOST_CHECK_EQUAL(store.output_head(), expected_output_head); + BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head); + ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head); + + BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body); + BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body); + BOOST_CHECK_EQUAL(store.input_body(), expected_input_body); + BOOST_CHECK_EQUAL(store.output_body(), expected_output_body); + BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body); + ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__get_tx__expected) +{ + using namespace system::chain; + const transaction tx + { + 0x2a, // version + inputs + { + input + { + point{ system::one_hash, 0x18 }, + script{ { { opcode::op_return }, { opcode::pick } } }, + witness{ "[242424]" }, + 0x2a // sequence + }, + input + { + point{ system::one_hash, 0x2a }, + script{ { { opcode::op_return }, { opcode::roll } } }, + witness{ "[424242]" }, + 0x18 // sequence + } + }, + outputs + { + output + { + 0x18, // value + script{ { { opcode::pick } } } + }, + output + { + 0x2a, // value + script{ { { opcode::roll } } } + } + }, + 0x18 // locktime + }; + const auto expected_tx_head = system::base16_chunk( + "01000000" // record count + "00000000" // bucket[0]... pk-> + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + const auto expected_tx_body = system::base16_chunk( + "ffffff7f" // next-> + "d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a" // sk (tx.hash(false)) + "740000" // light (not coinbase) + "800000" // heavy + "18000000" // locktime + "2a000000" // version + "020000" // ins_count + "020000" // outs_count + "00000000" // point_fk-> (ins_fk) + "00000000"); // outs_fk-> + const auto expected_outs_head = system::base16_chunk("02000000"); + const auto expected_outs_body = system::base16_chunk( + "0000000000" // output0_fk-> + "0700000000"); // output1_fk-> + const auto expected_output_head = system::base16_chunk("0e00000000"); + const auto expected_output_body = system::base16_chunk( + "00000000" // parent_fk-> + "18" // value + "0179" // script + "00000000" // parent_fk-> + "2a" // value + "017a"); // script + const auto expected_ins_body = system::base16_chunk( + "ffffffff" // next-> + "0100000000000000000000000000000000000000000000000000000000000000" + "180000" // index + "ffffffff" // next-> + "0100000000000000000000000000000000000000000000000000000000000000" + "2a0000"); // index + ////const auto expected_spend_head = system::base16_chunk( + //// "02000000" // record count + //// "00000000" // spend0_fk-> + //// "ffffffff" + //// "ffffffff" + //// "01000000" // spend1_fk-> + //// "ffffffff"); + ////const auto expected_spend_body = system::base16_chunk( + //// "ffffffff" // terminal-> + //// "01000000" // fp: point_stub + //// "180000" // fp: point_index + //// "00000000" // point_fk-> + //// "00000000" // parent_fk-> + //// "2a000000" // sequence + //// "0000000000" // input_fk-> + //// + //// "ffffffff" // terminal-> + //// "01000000" // fp: point_stub + //// "2a0000" // fp: point_index + //// "01000000" // point_fk-> + //// "00000000" // parent_fk-> + //// "18000000" // sequence + //// "0800000000"); // input_fk-> + const auto expected_input_head = system::base16_chunk("1000000000"); + const auto expected_input_body = system::base16_chunk( + "026a79" // script + "0103242424" // witness + "026a7a" // script + "0103424242"); // witness + + constexpr auto tx_hash = system::base16_array("d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a"); + BOOST_CHECK_EQUAL(tx_hash, tx.hash(false)); + + // data_chunk store. + settings settings{}; + settings.tx.buckets = 8; + settings.ins.buckets = 8; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.is_tx(tx.hash(false))); + BOOST_CHECK(query.set(tx)); + BOOST_CHECK(query.is_tx(tx.hash(false))); + + const auto pointer = query.get_transaction(query.to_tx(tx_hash), true); + BOOST_CHECK(pointer); + BOOST_CHECK(*pointer == tx); + BOOST_CHECK_EQUAL(pointer->hash(false), tx_hash); + BOOST_CHECK(!store.close(test::events_handler)); + + BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head); + BOOST_CHECK_EQUAL(store.input_head(), expected_input_head); + BOOST_CHECK_EQUAL(store.output_head(), expected_output_head); + BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head); + ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head); + + BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body); + BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body); + BOOST_CHECK_EQUAL(store.input_body(), expected_input_body); + BOOST_CHECK_EQUAL(store.output_body(), expected_output_body); + BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body); + ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__set_block__get_block__expected) +{ + constexpr auto milestone = true; + const auto genesis_header_head = system::base16_chunk( + "010000ff" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "000080" // filter[8], pk-> + "da"); // filter[0-7] + const auto genesis_header_body = system::base16_chunk( + "ffff7f" // next-> + "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash) + "04030201" // flags + "141312" // height + "24232221" // mtp + "ffffff" // previous_block_hash (header_fk - not found) (milestone true) + "01000000" // version + "29ab5f49" // timestamp + "ffff001d" // bits + "1dac2b7c" // nonce + "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root + const auto genesis_tx_head = system::base16_chunk( + "01000000" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "00000000" // pk-> + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + const auto genesis_tx_body = system::base16_chunk( + "ffffff7f" // next-> + "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false)) + "cc0080" // light (coinbase merged) + "cc0000" // heavy + "00000000" // locktime + "01000000" // version + "010000" // ins_count + "010000" // outs_count + "00000000" // point_fk-> (ins_fk) + "00000000"); // outs_fk-> + const auto genesis_outs_head = system::base16_chunk("01000000"); + const auto genesis_outs_body = system::base16_chunk( + "0000000000"); // output0_fk-> + const auto genesis_output_head = system::base16_chunk("5100000000"); + const auto genesis_output_body = system::base16_chunk( + "00000000" // parent_fk-> + "ff00f2052a01000000" // value + "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script + const auto genesis_ins_body = system::base16_chunk( + "ffffffff" // next-> + "0000000000000000000000000000000000000000000000000000000000000000" + "ffffff"); // index + const auto genesis_spend_head = system::base16_chunk( + "01000000" // record count + "00000000" // spend0_fk-> + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + const auto genesis_spend_body = system::base16_chunk( + "ffffffff" // terminal-> + "00000000" // fp: point_stub + "ffffff" // fp: point_index (null) + "ffffffff" // point_fk-> + "00000000" // parent_fk-> + "ffffffff" // sequence + "0000000000"); // input_fk-> (coinbase) + const auto genesis_input_head = system::base16_chunk("4f00000000"); + const auto genesis_input_body = system::base16_chunk( + "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script + "00"); // witness + const auto genesis_txs_head = system::base16_chunk( + "1100000000" // slab size + "0000000000" // pk-> + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff"); + const auto genesis_txs_body = system::base16_chunk( + "1d0100" // size light (285) + "1d0100" // size heavy (285) + "0100" // txs count (1) + "00000000" // transaction[0] + "ff" // depth (255) - genesis only + "00000000"); // forks (0) - genesis only + + settings settings{}; + settings.header.buckets = 8; + settings.tx.buckets = 8; + settings.ins.buckets = 8; + settings.txs.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + // Set block (header/txs). + BOOST_CHECK(!query.is_block(test::genesis.hash())); + BOOST_CHECK(query.set(test::genesis, test::context, milestone, false)); + BOOST_CHECK(query.is_block(test::genesis.hash())); + + table::header::record element1{}; + BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1)); + BOOST_CHECK(!store.close(test::events_handler)); + + BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head); + BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head); + BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head); + BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head); + BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head); + ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head); + BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head); + + BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body); + BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body); + BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body); + BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body); + BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body); + ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body); + BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body); + + const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false); + BOOST_CHECK(pointer); + BOOST_CHECK(*pointer == test::genesis); + + const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash())); + BOOST_CHECK_EQUAL(hashes.size(), 1u); + BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__set_block_txs__get_block__expected) +{ + constexpr auto milestone = true; + const auto genesis_header_head = system::base16_chunk( + "010000ff" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "000080" // filter[8], pk-> + "da"); // filter[0-7] + const auto genesis_header_body = system::base16_chunk( + "ffff7f" // next-> + "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash) + "04030201" // flags + "141312" // height + "24232221" // mtp + "ffffff" // previous_block_hash (header_fk - not found) (milestone true) + "01000000" // version + "29ab5f49" // timestamp + "ffff001d" // bits + "1dac2b7c" // nonce + "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root + const auto genesis_tx_head = system::base16_chunk( + "01000000" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "00000000" // pk-> + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + const auto genesis_tx_body = system::base16_chunk( + "ffffff7f" // next-> + "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false)) + "cc0080" // light (coinbase merged) + "cc0000" // heavy + "00000000" // locktime + "01000000" // version + "010000" // ins_count + "010000" // outs_count + "00000000" // point_fk-> (ins_fk) + "00000000"); // outs_fk-> + const auto genesis_outs_head = system::base16_chunk("01000000"); + const auto genesis_outs_body = system::base16_chunk( + "00000000" // spend0_fk-> + "0000000000"); // output0_fk-> + const auto genesis_output_head = system::base16_chunk("5100000000"); + const auto genesis_output_body = system::base16_chunk( + "00000000" // parent_fk-> + "ff00f2052a01000000" // value + "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script + const auto genesis_ins_body = system::base16_chunk( + "ffffffff" // next-> + "0000000000000000000000000000000000000000000000000000000000000000" + "ffffff"); // index + const auto genesis_spend_head = system::base16_chunk( + "01000000" // record count + "00000000" // spend0_fk-> + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + const auto genesis_spend_body = system::base16_chunk( + "ffffffff" // terminal-> + "00000000" // fp: point_hash + "ffffff" // fp: point_index (null) + "ffffffff" // point_fk-> + "00000000" // parent_fk-> + "ffffffff" // sequence + "0000000000"); // input_fk-> (coinbase) + const auto genesis_input_head = system::base16_chunk("4f00000000"); + const auto genesis_input_body = system::base16_chunk( + "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script + "00"); // witness + const auto genesis_txs_head = system::base16_chunk( + "1100000000" // slab size + "0000000000" // pk-> + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff"); + const auto genesis_txs_body = system::base16_chunk( + "1d0100" // size light (285) + "1d0100" // size heavy (285) + "0100" // txs count (1) + "00000000" // transaction[0] + "ff" // depth (255) - genesis only + "00000000"); // forks (0) - genesis only + + settings settings{}; + settings.header.buckets = 8; + settings.tx.buckets = 8; + settings.ins.buckets = 8; + settings.txs.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + // Set header and then txs. + BOOST_CHECK(!query.is_block(test::genesis.hash())); + BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone)); + BOOST_CHECK(!query.is_associated(0)); + BOOST_CHECK(query.set(test::genesis, false, false)); + BOOST_CHECK(query.is_block(test::genesis.hash())); + BOOST_CHECK(query.is_associated(0)); + + table::header::record element1{}; + BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1)); + BOOST_CHECK(!store.close(test::events_handler)); + + BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head); + BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head); + BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head); + BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head); + BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head); + ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head); + BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head); + + BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body); + BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body); + BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body); + BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body); + BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body); + ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body); + BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body); + + const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false); + BOOST_CHECK(pointer); + BOOST_CHECK(*pointer == test::genesis); + + const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash())); + BOOST_CHECK_EQUAL(hashes.size(), 1u); + BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false)); +} + +// populate_with_metadata +// ---------------------------------------------------------------------------- + +// METADATA IS SETTABLE ON CONST TEST OBJECTS. +// COPY CONSTRUCTION CREATES SHARED POINTER REFERENCES. +// SO POPULATE ON CONST OBJECTS HAS SIDE EFFECTS IF THE OBJECTS SPAN TESTS. +const auto& clean_(const auto& block_or_tx) NOEXCEPT +{ + const auto inputs = block_or_tx.inputs_ptr(); + for (const auto& input: *inputs) + { + input->prevout.reset(); + input->metadata = system::chain::prevout{}; + } + + return block_or_tx; +} + +// First four blocks have only coinbase txs. +BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__null_prevouts__true) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, test::context, false, false)); + BOOST_CHECK(query.set(test::block2, test::context, false, false)); + BOOST_CHECK(query.set(test::block3, test::context, false, false)); + + const auto& copy = clean_(test::genesis); + const auto& copy1 = clean_(test::block1); + const auto& copy2 = clean_(test::block2); + const auto& copy3 = clean_(test::block3); + + BOOST_CHECK(query.populate_with_metadata(copy)); + BOOST_CHECK(query.populate_with_metadata(copy1)); + BOOST_CHECK(query.populate_with_metadata(copy2)); + BOOST_CHECK(query.populate_with_metadata(copy3)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__partial_prevouts__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + + const auto& block1a = clean_(test::block1a); + + // Block populate treates first tx as null point. + BOOST_CHECK( query.populate_with_metadata(block1a)); + BOOST_CHECK(!query.populate_with_metadata(*block1a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(2))); + + // Block populate treates first tx as null point and other has missing prevouts. + const auto& block2a = clean_(test::block2a); + BOOST_CHECK(!query.populate_with_metadata(block2a)); + BOOST_CHECK( query.populate_with_metadata(*block2a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block2a.transactions_ptr()->at(1))); + BOOST_CHECK( query.populate_with_metadata(*block2a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_with_metadata(*block2a.inputs_ptr()->at(3))); + + const auto& tx4 = clean_(test::tx4); + BOOST_CHECK(query.populate_with_metadata(tx4)); + BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(0))); + BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(1))); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__metadata__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(!query.is_coinbase(1)); + + // Genesis only has coinbase, which does not spend. + const auto& genesis = clean_(test::genesis); + + BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); + BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); + + BOOST_CHECK(query.populate_with_metadata(genesis)); + + BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout); + BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); + + // Transaction population. + const auto& tx4 = clean_(test::tx4); + + BOOST_CHECK(!tx4.inputs_ptr()->at(0)->prevout); + BOOST_CHECK( tx4.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32); + BOOST_CHECK(!tx4.inputs_ptr()->at(1)->prevout); + BOOST_CHECK( tx4.inputs_ptr()->at(1)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, max_uint32); + + BOOST_CHECK(query.populate_with_metadata(tx4)); + + // TODO: test non-coinbase and other parent. + // spent/mtp are defaults, coinbase/parent are set (to non-default values). + BOOST_CHECK( tx4.inputs_ptr()->at(0)->prevout); + BOOST_CHECK(!tx4.inputs_ptr()->at(0)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, 1u); + BOOST_CHECK( tx4.inputs_ptr()->at(1)->prevout); + BOOST_CHECK(!tx4.inputs_ptr()->at(1)->metadata.coinbase); + BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, 1u); +} + +// populate_without_metadata +// ---------------------------------------------------------------------------- + +// First four blocks have only coinbase txs. +BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__null_prevouts__true) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, test::context, false, false)); + BOOST_CHECK(query.set(test::block2, test::context, false, false)); + BOOST_CHECK(query.set(test::block3, test::context, false, false)); + BOOST_CHECK(query.populate_without_metadata(test::genesis)); + BOOST_CHECK(query.populate_without_metadata(test::block1)); + BOOST_CHECK(query.populate_without_metadata(test::block2)); + BOOST_CHECK(query.populate_without_metadata(test::block3)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__partial_prevouts__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + + // Block populate treates first tx as null point. + BOOST_CHECK( query.populate_without_metadata(test::block1a)); + BOOST_CHECK(!query.populate_without_metadata(*test::block1a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(2))); + + // Block populate treates first tx as null point and other has missing prevouts. + BOOST_CHECK(!query.populate_without_metadata(test::block2a)); + BOOST_CHECK( query.populate_without_metadata(*test::block2a.transactions_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block2a.transactions_ptr()->at(1))); + BOOST_CHECK( query.populate_without_metadata(*test::block2a.inputs_ptr()->at(0))); + BOOST_CHECK(!query.populate_without_metadata(*test::block2a.inputs_ptr()->at(3))); + + BOOST_CHECK(query.populate_without_metadata(test::tx4)); + BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(0))); + BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(1))); +} + +// ---------------------------------------------------------------------------- + +// archive_write (foreign-keyed) + +BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__coinbase__true) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{}, false, false)); + BOOST_CHECK(query.set(test::block2, context{}, false, false)); + BOOST_CHECK(query.set(test::block3, context{}, false, false)); + BOOST_CHECK(query.is_coinbase(0)); + BOOST_CHECK(query.is_coinbase(1)); + BOOST_CHECK(query.is_coinbase(2)); + BOOST_CHECK(query.is_coinbase(3)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__non_coinbase__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{}, false, false)); + BOOST_CHECK(query.set(test::block2a, context{}, false, false)); + BOOST_CHECK(!query.is_coinbase(1)); + BOOST_CHECK(!query.is_coinbase(2)); + BOOST_CHECK(!query.is_coinbase(3)); + BOOST_CHECK(!query.is_coinbase(4)); + BOOST_CHECK(!query.is_coinbase(5)); + BOOST_CHECK(!query.is_coinbase(42)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__genesis__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(!query.is_milestone(0)); + BOOST_CHECK(!query.is_milestone(1)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__set__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{}, true, false)); + BOOST_CHECK(query.set(test::block2, context{}, false, false));; + BOOST_CHECK(!query.is_milestone(0)); + BOOST_CHECK(query.is_milestone(1)); + BOOST_CHECK(!query.is_milestone(2)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__invalid_parent__expected) +{ + constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); + constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); + const system::chain::header header + { + 0x31323334, // version + system::null_hash, // previous_block_hash + root, // merkle_root + 0x41424344, // timestamp + 0x51525354, // bits + 0x61626364 // nonce + }; + const auto expected_header_head = system::base16_chunk( + "010000" // record count + "ffffff" // bucket[0]... + "000000" // pk-> + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff"); + const auto expected_header_body = system::base16_chunk( + "ffffff" // next-> + "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) + "14131211" // flags + "040302" // height + "24232221" // mtp + "424242" // previous_block_hash (header_fk - invalid) (milestone false) + "34333231" // version + "44434241" // timestamp + "54535251" // bits + "64636261" // nonce + "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root + + settings settings{}; + settings.header.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + store.header_head() = expected_header_head; + store.header_body() = expected_header_body; + BOOST_CHECK(!query.get_header(query.to_header(block_hash))); + BOOST_CHECK(!query.get_header(header_link::terminal)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__default__expected) +{ + constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); + constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2"); + static_assert(0x45d6f6162ab0d085_u64 % 10u == 5u); + const system::chain::header header + { + 0x31323334, // version + system::null_hash, // previous_block_hash + root, // merkle_root + 0x41424344, // timestamp + 0x51525354, // bits + 0x61626364 // nonce + }; + + // TODO: heads may be undersized due to the change to base2 sizing. + const auto expected_header_head = system::base16_chunk( + "000000ff" // record count + "ffffffff" // bucket[0]... + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "000080" // filter[8], pk-> + "de" // filter[0-7] + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff" + "ffffffff"); + + const auto expected_header_body = system::base16_chunk( + "ffff7f" // next-> + "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash) + "14131211" // flags + "040302" // height + "24232221" // mtp + "ffffff" // previous_block_hash (header_fk - terminal) (milestone true) + "34333231" // version + "44434241" // timestamp + "54535251" // bits + "64636261" // nonce + "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root + + settings settings{}; + settings.header.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + ////store.header_head() = expected_header_head; + ////store.header_body() = expected_header_body; + BOOST_CHECK(query.set(header, context{ 0x11121314, 0x01020304, 0x21222324 }, true)); + BOOST_CHECK_EQUAL(store.header_head(), expected_header_head); + BOOST_CHECK_EQUAL(store.header_body(), expected_header_body); + + const auto foo = query.to_header(block_hash); + const auto pointer1 = query.get_header(foo); + BOOST_CHECK(pointer1); + BOOST_CHECK(*pointer1 == header); + + // Verify hash caching. + BOOST_CHECK_EQUAL(pointer1->hash(), block_hash); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_keys__not_found__empty) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.get_tx_keys(query.to_header(system::null_hash)).empty()); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_header_key__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_header_key(0), test::genesis.hash()); + BOOST_CHECK_EQUAL(query.get_header_key(1), system::null_hash); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_point_key__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_point_hash(0), system::null_hash); + + // tx4/5 prevouts are all block1a.tx1. + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(query.set(test::tx5)); + BOOST_CHECK_EQUAL(query.get_point_hash(1), test::block1a.transactions_ptr()->front()->hash(false)); + BOOST_CHECK_EQUAL(query.get_point_hash(2), test::block1a.transactions_ptr()->front()->hash(false)); + + // block1a adds three prevouts of two txs. + BOOST_CHECK(query.set(test::block1a, context{}, false, false)); + BOOST_CHECK_EQUAL(query.get_point_hash(4), system::one_hash); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_key__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_tx_key(0), test::genesis.transactions_ptr()->front()->hash(false)); + BOOST_CHECK_EQUAL(query.get_tx_key(1), system::null_hash); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(query.set(test::tx5)); + BOOST_CHECK_EQUAL(query.get_tx_key(1), test::tx4.hash(false)); + BOOST_CHECK_EQUAL(query.get_tx_key(2), test::tx5.hash(false)); + BOOST_CHECK_EQUAL(query.get_tx_key(3), system::null_hash); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_height1__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + + size_t out{}; + BOOST_CHECK(query.get_height(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_height(out, 1)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, 2)); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(query.get_height(out, 3)); + BOOST_CHECK_EQUAL(out, 3u); + BOOST_CHECK(query.get_height(out, 4)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, 5)); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(!query.get_height(out, 6)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_height2__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + + size_t out{}; + BOOST_CHECK(query.get_height(out, test::genesis.hash())); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_height(out, test::block1.hash())); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, test::block2.hash())); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(query.get_height(out, test::block3.hash())); + BOOST_CHECK_EQUAL(out, 3u); + BOOST_CHECK(query.get_height(out, test::block1a.hash())); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_height(out, test::block2a.hash())); + BOOST_CHECK_EQUAL(out, 2u); + BOOST_CHECK(!query.get_height(out, system::one_hash)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_height__not_strong__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + + size_t out{}; + BOOST_CHECK(!query.get_tx_height(out, 1)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__confirmed__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); + + size_t out{}; + const auto foo = query.get_tx_position(out, 0); + BOOST_CHECK(foo); + BOOST_CHECK_EQUAL(out, 0u); + + BOOST_CHECK(!query.get_tx_position(out, 1)); + BOOST_CHECK(!query.get_tx_position(out, 2)); + BOOST_CHECK(!query.get_tx_position(out, 3)); + BOOST_CHECK(!query.get_tx_position(out, 4)); + BOOST_CHECK(query.set_strong(1)); + BOOST_CHECK(query.set_strong(2)); + BOOST_CHECK(query.set_strong(3)); + + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 1)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 2)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 3)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_tx_position(out, 4)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(!query.get_tx_position(out, 5)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); + BOOST_CHECK(query.set(test::tx4)); + + size_t out{}; + BOOST_CHECK(query.get_tx_position(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + + BOOST_CHECK(!query.get_tx_position(out, 1)); + BOOST_CHECK(!query.get_tx_position(out, 2)); + BOOST_CHECK(!query.get_tx_position(out, 3)); + BOOST_CHECK(!query.get_tx_position(out, 4)); + BOOST_CHECK(query.set_strong(1)); + BOOST_CHECK(query.set_strong(2)); + BOOST_CHECK(query.set_strong(3)); + + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 0)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 1)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 2)); + BOOST_CHECK_EQUAL(out, 0u); + BOOST_CHECK(query.get_tx_position(out, 3)); + BOOST_CHECK_EQUAL(out, 1u); + BOOST_CHECK(query.get_tx_position(out, 4)); + BOOST_CHECK_EQUAL(out, 0u); + + // tx4 is unconfirmed. + BOOST_CHECK(!query.get_tx_position(out, 5)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_sizes__coinbase__204) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + + size_t light{}; + size_t heavy{}; + BOOST_CHECK(query.get_tx_sizes(light, heavy, 0)); + BOOST_CHECK_EQUAL(light, 204u); + BOOST_CHECK_EQUAL(heavy, 204u); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_count__coinbase__1) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK_EQUAL(query.get_tx_count(0), 1u); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), 0u, false)); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__genesis__expected) +{ + settings settings{}; + settings.header.buckets = 8; + settings.tx.buckets = 8; + settings.txs.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.set(test::genesis, test::context, false, false)); + + const auto tx = test::genesis.transactions_ptr()->front(); + const auto input = tx->inputs_ptr()->front(); + ////BOOST_CHECK(*input == *query.get_input(query.to_tx(tx->hash(false)), 0u)); + ////BOOST_CHECK(*input == *query.get_input(0)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__tx_not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(!query.get_inputs(1, false)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK_EQUAL(query.get_inputs(1, false)->size(), 2u); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(!query.get_output(query.to_tx(system::null_hash), 0u)); + BOOST_CHECK(!query.get_output(query.to_output(system::chain::point{ system::null_hash, 0u }))); + BOOST_CHECK(!query.get_output(0)); + BOOST_CHECK(!store.close(test::events_handler)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__genesis__expected) +{ + settings settings{}; + settings.header.buckets = 8; + settings.tx.buckets = 8; + settings.txs.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.set(test::genesis, test::context, false, false)); + + const auto tx = test::genesis.transactions_ptr()->front(); + const auto output1 = tx->outputs_ptr()->front(); + BOOST_CHECK(*output1 == *query.get_output(query.to_tx(tx->hash(false)), 0u)); + BOOST_CHECK(*output1 == *query.get_output(query.to_output(tx->hash(false), 0u))); + BOOST_CHECK(*output1 == *query.get_output(0)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__tx_not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(!query.get_outputs(1)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK_EQUAL(query.get_outputs(1)->size(), 1u); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__tx_not_found__nullptr) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK(!query.get_transactions(3, false)); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1a, test::context, false, false)); + BOOST_CHECK(query.set(test::block2a, test::context, false, false)); + BOOST_CHECK(query.set(test::tx4)); + BOOST_CHECK_EQUAL(query.get_transactions(0, false)->size(), 1u); + BOOST_CHECK_EQUAL(query.get_transactions(1, false)->size(), 1u); + BOOST_CHECK_EQUAL(query.get_transactions(2, false)->size(), 2u); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_spenders__unspent_or_not_found__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + BOOST_CHECK(query.set(test::block1, test::context, false, false)); + BOOST_CHECK(query.set(test::block2, test::context, false, false)); + BOOST_CHECK(query.set(test::block3, test::context, false, false)); + + // Caller should always test for nullptr. + BOOST_CHECK(query.get_spenders(output_link::terminal, true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(0, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(0, 1), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(1, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(1, 1), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(2, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(2, 1), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(3, 0), true)->empty()); + BOOST_CHECK(query.get_spenders(query.to_output(3, 1), true)->empty()); +} + +BOOST_AUTO_TEST_CASE(query_chain_writer__get_value__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(query.initialize(test::genesis)); + + uint64_t value{}; + BOOST_CHECK(query.get_value(value, query.to_output(0, 0))); + BOOST_CHECK_EQUAL(value, 5000000000u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/archive/wire_writer.cpp b/test/query/archive/wire_writer.cpp index 1cd2a4aaf..ee80619b1 100644 --- a/test/query/archive/wire_writer.cpp +++ b/test/query/archive/wire_writer.cpp @@ -1,110 +1,110 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "../../test.hpp" -#include "../../mocks/blocks.hpp" -#include "../../mocks/chunk_store.hpp" - -BOOST_FIXTURE_TEST_SUITE(query_wire_writer_writer_tests, test::directory_setup_fixture) - -// The view (wire) block writer must produce a store byte-identical to the -// chain block writer (see analogous chain_writer expectations). -BOOST_AUTO_TEST_CASE(query_wire_writer__set_block_view__genesis__expected) -{ - constexpr auto milestone = true; - const auto genesis_header_body = system::base16_chunk( - "ffff7f" // next-> - "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash) - "04030201" // flags - "141312" // height - "24232221" // mtp - "ffffff" // previous_block_hash (header_fk - not found) (milestone true) - "01000000" // version - "29ab5f49" // timestamp - "ffff001d" // bits - "1dac2b7c" // nonce - "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root - const auto genesis_tx_body = system::base16_chunk( - "ffffff7f" // next-> - "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false)) - "cc0080" // light (coinbase merged) - "cc0000" // heavy - "00000000" // locktime - "01000000" // version - "010000" // ins_count - "010000" // outs_count - "00000000" // point_fk-> (ins_fk) - "00000000"); // outs_fk-> - const auto genesis_outs_body = system::base16_chunk( - "0000000000"); // output0_fk-> - const auto genesis_output_body = system::base16_chunk( - "00000000" // parent_fk-> - "ff00f2052a01000000" // value - "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script - const auto genesis_ins_body = system::base16_chunk( - "ffffffff" // next-> - "0000000000000000000000000000000000000000000000000000000000000000" - "ffffff"); // index - const auto genesis_sequence_body = system::base16_chunk( - "ffffffff" // sequence - "0000000000" // input_fk-> (coinbase) - "00000000"); // parent_fk-> - const auto genesis_input_body = system::base16_chunk( - "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script - "00"); // witness - const auto genesis_txs_body = system::base16_chunk( - "1d0100" // size light (285) - "1d0100" // size heavy (285) - "0100" // txs count (1) - "00000000" // transaction[0] - "ff" // depth (255) - genesis only - "00000000"); // forks (0) - genesis only - - settings settings{}; - settings.header_buckets = 8; - settings.tx_buckets = 8; - settings.ins_buckets = 8; - settings.txs_buckets = 16; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_CHECK(!store.create(test::events_handler)); - - // Set header, then txs from the parsed view of the serialized block. - BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone)); - system::chain::block_view view{ test::genesis.to_data(true), true }; - BOOST_CHECK(view.is_valid()); - BOOST_CHECK_EQUAL(query.set_code(view, false, true), error::success); - BOOST_CHECK(query.is_block(test::genesis.hash())); - BOOST_CHECK(!store.close(test::events_handler)); - - BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body); - BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body); - BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body); - BOOST_CHECK_EQUAL(store.ins_sequence_body(), genesis_sequence_body); - BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body); - BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body); - BOOST_CHECK_EQUAL(store.outs_body(), genesis_outs_body); - BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body); - - const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false); - BOOST_CHECK(pointer); - BOOST_CHECK(*pointer == test::genesis); -} - -BOOST_AUTO_TEST_SUITE_END() +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../../test.hpp" +#include "../../mocks/blocks.hpp" +#include "../../mocks/chunk_store.hpp" + +BOOST_FIXTURE_TEST_SUITE(query_wire_writer_writer_tests, test::directory_setup_fixture) + +// The view (wire) block writer must produce a store byte-identical to the +// chain block writer (see analogous chain_writer expectations). +BOOST_AUTO_TEST_CASE(query_wire_writer__set_block_view__genesis__expected) +{ + constexpr auto milestone = true; + const auto genesis_header_body = system::base16_chunk( + "ffff7f" // next-> + "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash) + "04030201" // flags + "141312" // height + "24232221" // mtp + "ffffff" // previous_block_hash (header_fk - not found) (milestone true) + "01000000" // version + "29ab5f49" // timestamp + "ffff001d" // bits + "1dac2b7c" // nonce + "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root + const auto genesis_tx_body = system::base16_chunk( + "ffffff7f" // next-> + "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false)) + "cc0080" // light (coinbase merged) + "cc0000" // heavy + "00000000" // locktime + "01000000" // version + "010000" // ins_count + "010000" // outs_count + "00000000" // point_fk-> (ins_fk) + "00000000"); // outs_fk-> + const auto genesis_outs_body = system::base16_chunk( + "0000000000"); // output0_fk-> + const auto genesis_output_body = system::base16_chunk( + "00000000" // parent_fk-> + "ff00f2052a01000000" // value + "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script + const auto genesis_ins_body = system::base16_chunk( + "ffffffff" // next-> + "0000000000000000000000000000000000000000000000000000000000000000" + "ffffff"); // index + const auto genesis_sequence_body = system::base16_chunk( + "ffffffff" // sequence + "0000000000" // input_fk-> (coinbase) + "00000000"); // parent_fk-> + const auto genesis_input_body = system::base16_chunk( + "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script + "00"); // witness + const auto genesis_txs_body = system::base16_chunk( + "1d0100" // size light (285) + "1d0100" // size heavy (285) + "0100" // txs count (1) + "00000000" // transaction[0] + "ff" // depth (255) - genesis only + "00000000"); // forks (0) - genesis only + + settings settings{}; + settings.header.buckets = 8; + settings.tx.buckets = 8; + settings.ins.buckets = 8; + settings.txs.buckets = 16; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + + // Set header, then txs from the parsed view of the serialized block. + BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone)); + system::chain::block_view view{ test::genesis.to_data(true), true }; + BOOST_CHECK(view.is_valid()); + BOOST_CHECK_EQUAL(query.set_code(view, false, true), error::success); + BOOST_CHECK(query.is_block(test::genesis.hash())); + BOOST_CHECK(!store.close(test::events_handler)); + + BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body); + BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body); + BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body); + BOOST_CHECK_EQUAL(store.ins_sequence_body(), genesis_sequence_body); + BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body); + BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body); + BOOST_CHECK_EQUAL(store.outs_body(), genesis_outs_body); + BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body); + + const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false); + BOOST_CHECK(pointer); + BOOST_CHECK(*pointer == test::genesis); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/extent.cpp b/test/query/extent.cpp index 230749fa5..628f0d20a 100644 --- a/test/query/extent.cpp +++ b/test/query/extent.cpp @@ -1,173 +1,173 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "../test.hpp" -#include "../mocks/blocks.hpp" -#include "../mocks/chunk_store.hpp" - -BOOST_FIXTURE_TEST_SUITE(query_extent_tests, test::directory_setup_fixture) - -BOOST_AUTO_TEST_CASE(query__is_full__chunk_store__false) -{ - const settings configuration{}; - test::chunk_store store{ configuration }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!query.is_full()); -} - -BOOST_AUTO_TEST_CASE(query_extent__body_sizes__genesis__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - - BOOST_REQUIRE_EQUAL(query.header_body_size(), schema::header::minrow); - BOOST_REQUIRE_EQUAL(query.output_body_size(), 81u); - BOOST_REQUIRE_EQUAL(query.input_body_size(), 79u); - BOOST_REQUIRE_EQUAL(query.ins_body_size(), schema::ins::minrow + schema::ins_sequence::minrow); - BOOST_REQUIRE_EQUAL(query.outs_body_size(), schema::outs::minrow); - BOOST_REQUIRE_EQUAL(query.txs_body_size(), schema::txs::minrow + one + schema::flags); - BOOST_REQUIRE_EQUAL(query.tx_body_size(), schema::transaction::minrow); - - BOOST_REQUIRE_EQUAL(query.candidate_body_size(), schema::height::minrow); - BOOST_REQUIRE_EQUAL(query.confirmed_body_size(), schema::height::minrow); - BOOST_REQUIRE_EQUAL(query.strong_tx_body_size(), schema::strong_tx::minrow); - BOOST_REQUIRE_EQUAL(query.ecdsa_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.schnorr_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.silent_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.duplicate_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.prevalid_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.prevout_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.validated_bk_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.validated_tx_body_size(), zero); - BOOST_REQUIRE_EQUAL(query.filter_bk_body_size(), schema::filter_bk::minrow); - BOOST_REQUIRE_EQUAL(query.filter_tx_body_size(), 5u); - BOOST_REQUIRE_EQUAL(query.address_body_size(), schema::address::minrow); -} - -BOOST_AUTO_TEST_CASE(query_extent__buckets__genesis__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - - BOOST_REQUIRE_EQUAL(query.header_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.ins_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.txs_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.tx_buckets(), 128u); - - BOOST_REQUIRE_EQUAL(query.strong_tx_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.duplicate_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.prevout_buckets(), 128); - BOOST_REQUIRE_EQUAL(query.validated_tx_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.validated_bk_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.filter_tx_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.filter_bk_buckets(), 128u); - BOOST_REQUIRE_EQUAL(query.address_buckets(), 128u); -} - -BOOST_AUTO_TEST_CASE(query_extent__records__genesis__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - - BOOST_REQUIRE_EQUAL(query.header_records(), one); - BOOST_REQUIRE_EQUAL(query.ins_records(), one); - BOOST_REQUIRE_EQUAL(query.tx_records(), one); - - BOOST_REQUIRE_EQUAL(query.candidate_records(), one); - BOOST_REQUIRE_EQUAL(query.confirmed_records(), one); - BOOST_REQUIRE_EQUAL(query.strong_tx_records(), one); - BOOST_REQUIRE_EQUAL(query.ecdsa_records(), zero); - BOOST_REQUIRE_EQUAL(query.schnorr_records(), zero); - BOOST_REQUIRE_EQUAL(query.silent_records(), zero); - BOOST_REQUIRE_EQUAL(query.duplicate_records(), zero); - BOOST_REQUIRE_EQUAL(query.prevalid_records(), zero); - BOOST_REQUIRE_EQUAL(query.filter_bk_records(), one); - BOOST_REQUIRE_EQUAL(query.address_records(), one); -} - -BOOST_AUTO_TEST_CASE(query_extent__input_output_count__genesis__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - - BOOST_REQUIRE_EQUAL(query.input_count(0), one); - BOOST_REQUIRE_EQUAL(query.output_count(0), one); - BOOST_REQUIRE_EQUAL(query.put_counts(0).first, one); - BOOST_REQUIRE_EQUAL(query.put_counts(0).second, one); - - BOOST_REQUIRE_EQUAL(query.input_count(1), zero); - BOOST_REQUIRE_EQUAL(query.output_count(1), zero); - BOOST_REQUIRE_EQUAL(query.put_counts(1).first, zero); - BOOST_REQUIRE_EQUAL(query.put_counts(1).second, zero); -} - -BOOST_AUTO_TEST_CASE(query_extent__optionals_enabled__default__true) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.address_enabled()); - BOOST_REQUIRE(query.filter_enabled()); -} - -BOOST_AUTO_TEST_CASE(query_extent__address_enabled__disabled__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - settings.address_buckets = 0; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(!query.address_enabled()); - BOOST_REQUIRE(query.filter_enabled()); -} - -BOOST_AUTO_TEST_CASE(query_extent__filter_enabled__disabled__false) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - settings.filter_tx_buckets = 0; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.address_enabled()); - BOOST_REQUIRE(!query.filter_enabled()); -} - -BOOST_AUTO_TEST_SUITE_END() +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" +#include "../mocks/blocks.hpp" +#include "../mocks/chunk_store.hpp" + +BOOST_FIXTURE_TEST_SUITE(query_extent_tests, test::directory_setup_fixture) + +BOOST_AUTO_TEST_CASE(query__is_full__chunk_store__false) +{ + const settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!query.is_full()); +} + +BOOST_AUTO_TEST_CASE(query_extent__body_sizes__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + + BOOST_REQUIRE_EQUAL(query.header_body_size(), schema::header::minrow); + BOOST_REQUIRE_EQUAL(query.output_body_size(), 81u); + BOOST_REQUIRE_EQUAL(query.input_body_size(), 79u); + BOOST_REQUIRE_EQUAL(query.ins_body_size(), schema::ins::minrow + schema::ins_sequence::minrow); + BOOST_REQUIRE_EQUAL(query.outs_body_size(), schema::outs::minrow); + BOOST_REQUIRE_EQUAL(query.txs_body_size(), schema::txs::minrow + one + schema::flags); + BOOST_REQUIRE_EQUAL(query.tx_body_size(), schema::transaction::minrow); + + BOOST_REQUIRE_EQUAL(query.candidate_body_size(), schema::height::minrow); + BOOST_REQUIRE_EQUAL(query.confirmed_body_size(), schema::height::minrow); + BOOST_REQUIRE_EQUAL(query.strong_tx_body_size(), schema::strong_tx::minrow); + BOOST_REQUIRE_EQUAL(query.ecdsa_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.schnorr_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.silent_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.duplicate_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.prevalid_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.prevout_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.validated_bk_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.validated_tx_body_size(), zero); + BOOST_REQUIRE_EQUAL(query.filter_bk_body_size(), schema::filter_bk::minrow); + BOOST_REQUIRE_EQUAL(query.filter_tx_body_size(), 5u); + BOOST_REQUIRE_EQUAL(query.address_body_size(), schema::address::minrow); +} + +BOOST_AUTO_TEST_CASE(query_extent__buckets__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + + BOOST_REQUIRE_EQUAL(query.header_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.ins_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.txs_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.tx_buckets(), 128u); + + BOOST_REQUIRE_EQUAL(query.strong_tx_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.duplicate_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.prevout_buckets(), 128); + BOOST_REQUIRE_EQUAL(query.validated_tx_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.validated_bk_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.filter_tx_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.filter_bk_buckets(), 128u); + BOOST_REQUIRE_EQUAL(query.address_buckets(), 128u); +} + +BOOST_AUTO_TEST_CASE(query_extent__records__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + + BOOST_REQUIRE_EQUAL(query.header_records(), one); + BOOST_REQUIRE_EQUAL(query.ins_records(), one); + BOOST_REQUIRE_EQUAL(query.tx_records(), one); + + BOOST_REQUIRE_EQUAL(query.candidate_records(), one); + BOOST_REQUIRE_EQUAL(query.confirmed_records(), one); + BOOST_REQUIRE_EQUAL(query.strong_tx_records(), one); + BOOST_REQUIRE_EQUAL(query.ecdsa_records(), zero); + BOOST_REQUIRE_EQUAL(query.schnorr_records(), zero); + BOOST_REQUIRE_EQUAL(query.silent_records(), zero); + BOOST_REQUIRE_EQUAL(query.duplicate_records(), zero); + BOOST_REQUIRE_EQUAL(query.prevalid_records(), zero); + BOOST_REQUIRE_EQUAL(query.filter_bk_records(), one); + BOOST_REQUIRE_EQUAL(query.address_records(), one); +} + +BOOST_AUTO_TEST_CASE(query_extent__input_output_count__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + + BOOST_REQUIRE_EQUAL(query.input_count(0), one); + BOOST_REQUIRE_EQUAL(query.output_count(0), one); + BOOST_REQUIRE_EQUAL(query.put_counts(0).first, one); + BOOST_REQUIRE_EQUAL(query.put_counts(0).second, one); + + BOOST_REQUIRE_EQUAL(query.input_count(1), zero); + BOOST_REQUIRE_EQUAL(query.output_count(1), zero); + BOOST_REQUIRE_EQUAL(query.put_counts(1).first, zero); + BOOST_REQUIRE_EQUAL(query.put_counts(1).second, zero); +} + +BOOST_AUTO_TEST_CASE(query_extent__optionals_enabled__default__true) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.address_enabled()); + BOOST_REQUIRE(query.filter_enabled()); +} + +BOOST_AUTO_TEST_CASE(query_extent__address_enabled__disabled__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + settings.address.buckets = 0; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(!query.address_enabled()); + BOOST_REQUIRE(query.filter_enabled()); +} + +BOOST_AUTO_TEST_CASE(query_extent__filter_enabled__disabled__false) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + settings.filter_tx.buckets = 0; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.address_enabled()); + BOOST_REQUIRE(!query.filter_enabled()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/navigate/navigate_reverse.cpp b/test/query/navigate/navigate_reverse.cpp index c02f9fdef..f29cb8760 100644 --- a/test/query/navigate/navigate_reverse.cpp +++ b/test/query/navigate/navigate_reverse.cpp @@ -1,448 +1,448 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "../../test.hpp" -#include "../../mocks/blocks.hpp" -#include "../../mocks/chunk_store.hpp" - -BOOST_FIXTURE_TEST_SUITE(query_navigate_tests, test::directory_setup_fixture) - -const auto& address0 = test::block1a_address0; -const auto& address1 = test::block1a_address1; - -// to_parent - -BOOST_AUTO_TEST_CASE(query_navigate__to_parent__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); - BOOST_REQUIRE_EQUAL(query.to_parent(0), header_link::terminal); - BOOST_REQUIRE_EQUAL(query.to_parent(1), 0u); - BOOST_REQUIRE_EQUAL(query.to_parent(2), 1u); - BOOST_REQUIRE_EQUAL(query.to_parent(3), 0u); - BOOST_REQUIRE_EQUAL(query.to_parent(4), 3u); - BOOST_REQUIRE_EQUAL(query.to_parent(5), header_link::terminal); -} - -// to_touched_txs1 - -BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs1__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links links{}; - BOOST_REQUIRE(!query.to_address_outputs(links, address1)); - - tx_links out{}; - BOOST_REQUIRE(!query.to_touched_txs(out, links)); - BOOST_REQUIRE_EQUAL(out.size(), 4u); - - // owners - BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1) - - // spenders of (0) - BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3) - BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4 - BOOST_REQUIRE_EQUAL(out.at(3), 6u); // block3a (tx6) -} - -// to_touched_txs2 - -BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs2__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links links{}; - const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, links, address0)); - - tx_links out{}; - BOOST_REQUIRE(!query.to_touched_txs(cancel, out, links)); - BOOST_REQUIRE_EQUAL(out.size(), 10u); - - // owners - BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1) - - // spenders of (0) - BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3) - BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4 - BOOST_REQUIRE_EQUAL(out.at(3), 5u); // tx5 - BOOST_REQUIRE_EQUAL(out.at(4), 6u); // block3a (tx6) - - // owners (unspent) - BOOST_REQUIRE_EQUAL(out.at(5), 2u); // block2a (tx2) [duplicate] - BOOST_REQUIRE_EQUAL(out.at(6), 3u); // block2a (tx3) - BOOST_REQUIRE_EQUAL(out.at(7), 4u); // tx4 [duplicate] - BOOST_REQUIRE_EQUAL(out.at(8), 5u); // tx5 [duplicate] - BOOST_REQUIRE_EQUAL(out.at(9), 6u); // block3a (tx6) [duplicate] -} - -// to_address_outputs1 - -BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs1__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links out{}; - BOOST_REQUIRE(!query.to_address_outputs(out, address1)); - - // There is 1 instance of the `script{ { { opcode::roll } } }` output. - BOOST_REQUIRE_EQUAL(out.size(), 1u); - BOOST_REQUIRE_EQUAL(out.front(), 88u); -} - -// to_address_outputs2 - -BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs2__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links out{}; - const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, out, address0)); - - // There are 6 instances of the `script{ { { opcode::pick } } }` output. - BOOST_REQUIRE_EQUAL(out.size(), 6u); - BOOST_REQUIRE_EQUAL(out.at(0), 123u); - BOOST_REQUIRE_EQUAL(out.at(1), 116u); - BOOST_REQUIRE_EQUAL(out.at(2), 109u); - BOOST_REQUIRE_EQUAL(out.at(3), 102u); - BOOST_REQUIRE_EQUAL(out.at(4), 95u); - BOOST_REQUIRE_EQUAL(out.at(5), 81u); -} - -// to_address_outputs3 - -BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__terminal__not_reduced) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links out{}; - address_link end{}; - const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, end, out, address0, max_size_t)); - - // There are 6 instances of the `script{ { { opcode::pick } } }` output. - BOOST_REQUIRE_EQUAL(out.size(), 6u); - BOOST_REQUIRE_EQUAL(out.at(0), 123u); - BOOST_REQUIRE_EQUAL(out.at(1), 116u); - BOOST_REQUIRE_EQUAL(out.at(2), 109u); - BOOST_REQUIRE_EQUAL(out.at(3), 102u); - BOOST_REQUIRE_EQUAL(out.at(4), 95u); - BOOST_REQUIRE_EQUAL(out.at(5), 81u); -} - -BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__limit__depth_limited) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links out{}; - address_link end{}; - const std::atomic_bool cancel{}; - BOOST_REQUIRE_EQUAL(query.to_address_outputs(cancel, end, out, address0, 4), error::depth_limited); - - // The limit is applied before deduplication. - // There are 6 instances of the `script{ { { opcode::pick } } }` output, limited to 4. - BOOST_REQUIRE_EQUAL(out.size(), 4u); - BOOST_REQUIRE_EQUAL(out.at(0), 123u); - BOOST_REQUIRE_EQUAL(out.at(1), 116u); - BOOST_REQUIRE_EQUAL(out.at(2), 109u); - BOOST_REQUIRE_EQUAL(out.at(3), 102u); - ////BOOST_REQUIRE_EQUAL(out.at(4), 95u); - ////BOOST_REQUIRE_EQUAL(out.at(5), 81u); -} - -BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_mismatch__populated_invalid_cursor) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links out{}; - address_link cursor{ 4242 }; - const std::atomic_bool cancel{}; - const auto ec = query.to_address_outputs(cancel, cursor, out, address0, max_size_t); - BOOST_REQUIRE_EQUAL(ec, error::invalid_cursor); - - // The end was not found but the full list is returned. - BOOST_REQUIRE_EQUAL(out.size(), 6u); - BOOST_REQUIRE_EQUAL(out.at(0), 123u); - BOOST_REQUIRE_EQUAL(out.at(1), 116u); - BOOST_REQUIRE_EQUAL(out.at(2), 109u); - BOOST_REQUIRE_EQUAL(out.at(3), 102u); - BOOST_REQUIRE_EQUAL(out.at(4), 95u); - BOOST_REQUIRE_EQUAL(out.at(5), 81u); -} - -BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_match__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - - output_links out{}; - address_link cursor{ 3 }; - const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); - BOOST_REQUIRE_EQUAL(cursor.value, 7u); - - // The stop was found so partial list is returned. - BOOST_REQUIRE_EQUAL(out.size(), 4u); - BOOST_REQUIRE_EQUAL(out.at(0), 123u); - BOOST_REQUIRE_EQUAL(out.at(1), 116u); - BOOST_REQUIRE_EQUAL(out.at(2), 109u); - BOOST_REQUIRE_EQUAL(out.at(3), 102u); -} - -BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__progression__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - - output_links out{}; - address_link cursor{}; - const std::atomic_bool cancel{}; - - // Add three unconfirmed blocks and two txs, with 7 outputs, 6 matching address. - BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); - BOOST_REQUIRE_EQUAL(cursor.value, 7u); - BOOST_REQUIRE_EQUAL(out.size(), 6u); - BOOST_REQUIRE_EQUAL(out.at(0), 123u); - BOOST_REQUIRE_EQUAL(out.at(1), 116u); - BOOST_REQUIRE_EQUAL(out.at(2), 109u); - BOOST_REQUIRE_EQUAL(out.at(3), 102u); - BOOST_REQUIRE_EQUAL(out.at(4), 95u); - BOOST_REQUIRE_EQUAL(out.at(5), 81u); - - // Add two unconfirmed blocks with 3 outputs, all matching address. - BOOST_REQUIRE(query.set(test::block1b, database::context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block2b, database::context{ 0, 2, 0 }, false, false)); - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); - BOOST_REQUIRE_EQUAL(cursor.value, 10u); - BOOST_REQUIRE_EQUAL(out.size(), 3u); - BOOST_REQUIRE_EQUAL(out.at(0), 144u); - BOOST_REQUIRE_EQUAL(out.at(1), 137u); - BOOST_REQUIRE_EQUAL(out.at(2), 130u); - - // Add one tx with one output, matching address. - BOOST_REQUIRE(query.set(test::tx2b)); - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); - BOOST_REQUIRE_EQUAL(cursor.value, 11u); - BOOST_REQUIRE_EQUAL(out.size(), 1u); - BOOST_REQUIRE_EQUAL(out.at(0), 151u); - - // No changes to this address since cursor. - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); - BOOST_REQUIRE_EQUAL(cursor.value, 11u); - BOOST_REQUIRE(out.empty()); -} - -// to_input_tx -// to_output_tx - -BOOST_AUTO_TEST_CASE(query_navigate__to_output_tx__to_output__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block3, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - - // All 5 blocks have one transaction with 1 output. - BOOST_REQUIRE_EQUAL(query.to_output_tx(0 * 0x51), 0u); - BOOST_REQUIRE_EQUAL(query.to_output_tx(1 * 0x51), 1u); - BOOST_REQUIRE_EQUAL(query.to_output_tx(2 * 0x51), 2u); - BOOST_REQUIRE_EQUAL(query.to_output_tx(3 * 0x51), 3u); - BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51), 4u); - BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 7u), 4u); - - BOOST_REQUIRE_EQUAL(query.to_output(0, 0), 0u * 0x51u); - BOOST_REQUIRE_EQUAL(query.to_output(1, 0), 1u * 0x51u); - BOOST_REQUIRE_EQUAL(query.to_output(2, 0), 2u * 0x51u); - BOOST_REQUIRE_EQUAL(query.to_output(3, 0), 3u * 0x51u); - BOOST_REQUIRE_EQUAL(query.to_output(4, 0), 4u * 0x51u); - BOOST_REQUIRE_EQUAL(query.to_output(4, 1), 4u * 0x51u + 7u); - - const output_links expected_outputs4{ 4 * 0x51, 4 * 0x51 + 7 }; - BOOST_REQUIRE_EQUAL(query.to_outputs(0), output_links{ 0 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_outputs(1), output_links{ 1 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_outputs(2), output_links{ 2 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_outputs(3), output_links{ 3 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_outputs(4), expected_outputs4); - - // All blocks have one transaction. - BOOST_REQUIRE_EQUAL(query.to_block_outputs(0), output_links{ 0 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_block_outputs(1), output_links{ 1 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_block_outputs(2), output_links{ 2 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_block_outputs(3), output_links{ 3 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_block_outputs(4), expected_outputs4); - - // No prevouts that exist. - const output_links expected_prevouts4{ output_link::terminal, output_link::terminal, output_link::terminal }; - BOOST_REQUIRE_EQUAL(query.to_prevouts(0), output_links{ output_link::terminal }); - BOOST_REQUIRE_EQUAL(query.to_prevouts(1), output_links{ output_link::terminal }); - BOOST_REQUIRE_EQUAL(query.to_prevouts(2), output_links{ output_link::terminal }); - BOOST_REQUIRE_EQUAL(query.to_prevouts(3), output_links{ output_link::terminal }); - BOOST_REQUIRE_EQUAL(query.to_prevouts(4), expected_prevouts4); - - // All blocks have one transaction. - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{}); - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{}); - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), output_links{}); - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(3), output_links{}); - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(4), output_links{}); - - // Past end. - BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 14), tx_link::terminal); - BOOST_REQUIRE_EQUAL(query.to_output(5, 0), output_link::terminal); - BOOST_REQUIRE(query.to_outputs(5).empty()); - BOOST_REQUIRE(query.to_block_outputs(5).empty()); -} - -// to_prevout_tx - -BOOST_AUTO_TEST_CASE(query_navigate__to_prevout_tx__to_prevout__expected) -{ - settings settings{}; - settings.tx_buckets = 3; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block2a, context{ 0, 1, 0 }, false, false)); - - // inputs in link order. - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(0), tx_link::terminal); - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(1), tx_link::terminal); - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(2), tx_link::terminal); - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(3), tx_link::terminal); - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(4), 1u); // block1a:0 (second serialized tx) - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(5), 1u); // block1a:0 (second serialized tx) - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(6), tx_link::terminal); - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(7), tx_link::terminal); - - const output_links expected_prevouts1{ output_link::terminal }; - const output_links expected_prevouts2{ output_link::terminal, output_link::terminal }; - const output_links expected_prevouts3{ output_link::terminal, output_link::terminal, output_link::terminal }; - const output_links expected_prevouts{ 0x51u, 0x51u + 7u }; - BOOST_REQUIRE_EQUAL(query.to_prevouts(0), expected_prevouts1); - BOOST_REQUIRE_EQUAL(query.to_prevouts(1), expected_prevouts3); - BOOST_REQUIRE_EQUAL(query.to_prevouts(2), expected_prevouts); - BOOST_REQUIRE_EQUAL(query.to_prevouts(3), expected_prevouts2); - - // First tx is coinbase, or tx has undefined prevouts. - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{}); - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{}); - BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), expected_prevouts2); - - // Past end. - BOOST_REQUIRE_EQUAL(query.to_prevout_tx(8), tx_link::terminal); -} - -// to_spenders1 -// to_spenders2 -// to_duplicates - -// to_block - -BOOST_AUTO_TEST_CASE(query_navigate__to_block__always__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - - class accessor - : public test::query_accessor - { - public: - using test::query_accessor::query_accessor; - using test::query_accessor::to_block; - }; - - accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false)); - - // Either not strong or not found, except genesis. - BOOST_REQUIRE(!query.to_block(0).is_terminal()); - BOOST_REQUIRE(query.to_block(1).is_terminal()); - BOOST_REQUIRE(query.to_block(2).is_terminal()); - BOOST_REQUIRE(query.to_block(3).is_terminal()); -} - -// to_input_index -// to_output_index - -BOOST_AUTO_TEST_SUITE_END() +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../../test.hpp" +#include "../../mocks/blocks.hpp" +#include "../../mocks/chunk_store.hpp" + +BOOST_FIXTURE_TEST_SUITE(query_navigate_tests, test::directory_setup_fixture) + +const auto& address0 = test::block1a_address0; +const auto& address1 = test::block1a_address1; + +// to_parent + +BOOST_AUTO_TEST_CASE(query_navigate__to_parent__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); + BOOST_REQUIRE_EQUAL(query.to_parent(0), header_link::terminal); + BOOST_REQUIRE_EQUAL(query.to_parent(1), 0u); + BOOST_REQUIRE_EQUAL(query.to_parent(2), 1u); + BOOST_REQUIRE_EQUAL(query.to_parent(3), 0u); + BOOST_REQUIRE_EQUAL(query.to_parent(4), 3u); + BOOST_REQUIRE_EQUAL(query.to_parent(5), header_link::terminal); +} + +// to_touched_txs1 + +BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs1__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links links{}; + BOOST_REQUIRE(!query.to_address_outputs(links, address1)); + + tx_links out{}; + BOOST_REQUIRE(!query.to_touched_txs(out, links)); + BOOST_REQUIRE_EQUAL(out.size(), 4u); + + // owners + BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1) + + // spenders of (0) + BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3) + BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4 + BOOST_REQUIRE_EQUAL(out.at(3), 6u); // block3a (tx6) +} + +// to_touched_txs2 + +BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs2__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links links{}; + const std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.to_address_outputs(cancel, links, address0)); + + tx_links out{}; + BOOST_REQUIRE(!query.to_touched_txs(cancel, out, links)); + BOOST_REQUIRE_EQUAL(out.size(), 10u); + + // owners + BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1) + + // spenders of (0) + BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3) + BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4 + BOOST_REQUIRE_EQUAL(out.at(3), 5u); // tx5 + BOOST_REQUIRE_EQUAL(out.at(4), 6u); // block3a (tx6) + + // owners (unspent) + BOOST_REQUIRE_EQUAL(out.at(5), 2u); // block2a (tx2) [duplicate] + BOOST_REQUIRE_EQUAL(out.at(6), 3u); // block2a (tx3) + BOOST_REQUIRE_EQUAL(out.at(7), 4u); // tx4 [duplicate] + BOOST_REQUIRE_EQUAL(out.at(8), 5u); // tx5 [duplicate] + BOOST_REQUIRE_EQUAL(out.at(9), 6u); // block3a (tx6) [duplicate] +} + +// to_address_outputs1 + +BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs1__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links out{}; + BOOST_REQUIRE(!query.to_address_outputs(out, address1)); + + // There is 1 instance of the `script{ { { opcode::roll } } }` output. + BOOST_REQUIRE_EQUAL(out.size(), 1u); + BOOST_REQUIRE_EQUAL(out.front(), 88u); +} + +// to_address_outputs2 + +BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs2__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links out{}; + const std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.to_address_outputs(cancel, out, address0)); + + // There are 6 instances of the `script{ { { opcode::pick } } }` output. + BOOST_REQUIRE_EQUAL(out.size(), 6u); + BOOST_REQUIRE_EQUAL(out.at(0), 123u); + BOOST_REQUIRE_EQUAL(out.at(1), 116u); + BOOST_REQUIRE_EQUAL(out.at(2), 109u); + BOOST_REQUIRE_EQUAL(out.at(3), 102u); + BOOST_REQUIRE_EQUAL(out.at(4), 95u); + BOOST_REQUIRE_EQUAL(out.at(5), 81u); +} + +// to_address_outputs3 + +BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__terminal__not_reduced) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links out{}; + address_link end{}; + const std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.to_address_outputs(cancel, end, out, address0, max_size_t)); + + // There are 6 instances of the `script{ { { opcode::pick } } }` output. + BOOST_REQUIRE_EQUAL(out.size(), 6u); + BOOST_REQUIRE_EQUAL(out.at(0), 123u); + BOOST_REQUIRE_EQUAL(out.at(1), 116u); + BOOST_REQUIRE_EQUAL(out.at(2), 109u); + BOOST_REQUIRE_EQUAL(out.at(3), 102u); + BOOST_REQUIRE_EQUAL(out.at(4), 95u); + BOOST_REQUIRE_EQUAL(out.at(5), 81u); +} + +BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__limit__depth_limited) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links out{}; + address_link end{}; + const std::atomic_bool cancel{}; + BOOST_REQUIRE_EQUAL(query.to_address_outputs(cancel, end, out, address0, 4), error::depth_limited); + + // The limit is applied before deduplication. + // There are 6 instances of the `script{ { { opcode::pick } } }` output, limited to 4. + BOOST_REQUIRE_EQUAL(out.size(), 4u); + BOOST_REQUIRE_EQUAL(out.at(0), 123u); + BOOST_REQUIRE_EQUAL(out.at(1), 116u); + BOOST_REQUIRE_EQUAL(out.at(2), 109u); + BOOST_REQUIRE_EQUAL(out.at(3), 102u); + ////BOOST_REQUIRE_EQUAL(out.at(4), 95u); + ////BOOST_REQUIRE_EQUAL(out.at(5), 81u); +} + +BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_mismatch__populated_invalid_cursor) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links out{}; + address_link cursor{ 4242 }; + const std::atomic_bool cancel{}; + const auto ec = query.to_address_outputs(cancel, cursor, out, address0, max_size_t); + BOOST_REQUIRE_EQUAL(ec, error::invalid_cursor); + + // The end was not found but the full list is returned. + BOOST_REQUIRE_EQUAL(out.size(), 6u); + BOOST_REQUIRE_EQUAL(out.at(0), 123u); + BOOST_REQUIRE_EQUAL(out.at(1), 116u); + BOOST_REQUIRE_EQUAL(out.at(2), 109u); + BOOST_REQUIRE_EQUAL(out.at(3), 102u); + BOOST_REQUIRE_EQUAL(out.at(4), 95u); + BOOST_REQUIRE_EQUAL(out.at(5), 81u); +} + +BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_match__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + + output_links out{}; + address_link cursor{ 3 }; + const std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); + BOOST_REQUIRE_EQUAL(cursor.value, 7u); + + // The stop was found so partial list is returned. + BOOST_REQUIRE_EQUAL(out.size(), 4u); + BOOST_REQUIRE_EQUAL(out.at(0), 123u); + BOOST_REQUIRE_EQUAL(out.at(1), 116u); + BOOST_REQUIRE_EQUAL(out.at(2), 109u); + BOOST_REQUIRE_EQUAL(out.at(3), 102u); +} + +BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__progression__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + + output_links out{}; + address_link cursor{}; + const std::atomic_bool cancel{}; + + // Add three unconfirmed blocks and two txs, with 7 outputs, 6 matching address. + BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); + BOOST_REQUIRE_EQUAL(cursor.value, 7u); + BOOST_REQUIRE_EQUAL(out.size(), 6u); + BOOST_REQUIRE_EQUAL(out.at(0), 123u); + BOOST_REQUIRE_EQUAL(out.at(1), 116u); + BOOST_REQUIRE_EQUAL(out.at(2), 109u); + BOOST_REQUIRE_EQUAL(out.at(3), 102u); + BOOST_REQUIRE_EQUAL(out.at(4), 95u); + BOOST_REQUIRE_EQUAL(out.at(5), 81u); + + // Add two unconfirmed blocks with 3 outputs, all matching address. + BOOST_REQUIRE(query.set(test::block1b, database::context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block2b, database::context{ 0, 2, 0 }, false, false)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); + BOOST_REQUIRE_EQUAL(cursor.value, 10u); + BOOST_REQUIRE_EQUAL(out.size(), 3u); + BOOST_REQUIRE_EQUAL(out.at(0), 144u); + BOOST_REQUIRE_EQUAL(out.at(1), 137u); + BOOST_REQUIRE_EQUAL(out.at(2), 130u); + + // Add one tx with one output, matching address. + BOOST_REQUIRE(query.set(test::tx2b)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); + BOOST_REQUIRE_EQUAL(cursor.value, 11u); + BOOST_REQUIRE_EQUAL(out.size(), 1u); + BOOST_REQUIRE_EQUAL(out.at(0), 151u); + + // No changes to this address since cursor. + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); + BOOST_REQUIRE_EQUAL(cursor.value, 11u); + BOOST_REQUIRE(out.empty()); +} + +// to_input_tx +// to_output_tx + +BOOST_AUTO_TEST_CASE(query_navigate__to_output_tx__to_output__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block3, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + + // All 5 blocks have one transaction with 1 output. + BOOST_REQUIRE_EQUAL(query.to_output_tx(0 * 0x51), 0u); + BOOST_REQUIRE_EQUAL(query.to_output_tx(1 * 0x51), 1u); + BOOST_REQUIRE_EQUAL(query.to_output_tx(2 * 0x51), 2u); + BOOST_REQUIRE_EQUAL(query.to_output_tx(3 * 0x51), 3u); + BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51), 4u); + BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 7u), 4u); + + BOOST_REQUIRE_EQUAL(query.to_output(0, 0), 0u * 0x51u); + BOOST_REQUIRE_EQUAL(query.to_output(1, 0), 1u * 0x51u); + BOOST_REQUIRE_EQUAL(query.to_output(2, 0), 2u * 0x51u); + BOOST_REQUIRE_EQUAL(query.to_output(3, 0), 3u * 0x51u); + BOOST_REQUIRE_EQUAL(query.to_output(4, 0), 4u * 0x51u); + BOOST_REQUIRE_EQUAL(query.to_output(4, 1), 4u * 0x51u + 7u); + + const output_links expected_outputs4{ 4 * 0x51, 4 * 0x51 + 7 }; + BOOST_REQUIRE_EQUAL(query.to_outputs(0), output_links{ 0 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(1), output_links{ 1 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(2), output_links{ 2 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(3), output_links{ 3 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(4), expected_outputs4); + + // All blocks have one transaction. + BOOST_REQUIRE_EQUAL(query.to_block_outputs(0), output_links{ 0 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_block_outputs(1), output_links{ 1 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_block_outputs(2), output_links{ 2 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_block_outputs(3), output_links{ 3 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_block_outputs(4), expected_outputs4); + + // No prevouts that exist. + const output_links expected_prevouts4{ output_link::terminal, output_link::terminal, output_link::terminal }; + BOOST_REQUIRE_EQUAL(query.to_prevouts(0), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(1), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(2), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(3), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(4), expected_prevouts4); + + // All blocks have one transaction. + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(3), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(4), output_links{}); + + // Past end. + BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 14), tx_link::terminal); + BOOST_REQUIRE_EQUAL(query.to_output(5, 0), output_link::terminal); + BOOST_REQUIRE(query.to_outputs(5).empty()); + BOOST_REQUIRE(query.to_block_outputs(5).empty()); +} + +// to_prevout_tx + +BOOST_AUTO_TEST_CASE(query_navigate__to_prevout_tx__to_prevout__expected) +{ + settings settings{}; + settings.tx.buckets = 3; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block2a, context{ 0, 1, 0 }, false, false)); + + // inputs in link order. + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(0), tx_link::terminal); + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(1), tx_link::terminal); + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(2), tx_link::terminal); + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(3), tx_link::terminal); + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(4), 1u); // block1a:0 (second serialized tx) + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(5), 1u); // block1a:0 (second serialized tx) + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(6), tx_link::terminal); + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(7), tx_link::terminal); + + const output_links expected_prevouts1{ output_link::terminal }; + const output_links expected_prevouts2{ output_link::terminal, output_link::terminal }; + const output_links expected_prevouts3{ output_link::terminal, output_link::terminal, output_link::terminal }; + const output_links expected_prevouts{ 0x51u, 0x51u + 7u }; + BOOST_REQUIRE_EQUAL(query.to_prevouts(0), expected_prevouts1); + BOOST_REQUIRE_EQUAL(query.to_prevouts(1), expected_prevouts3); + BOOST_REQUIRE_EQUAL(query.to_prevouts(2), expected_prevouts); + BOOST_REQUIRE_EQUAL(query.to_prevouts(3), expected_prevouts2); + + // First tx is coinbase, or tx has undefined prevouts. + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), expected_prevouts2); + + // Past end. + BOOST_REQUIRE_EQUAL(query.to_prevout_tx(8), tx_link::terminal); +} + +// to_spenders1 +// to_spenders2 +// to_duplicates + +// to_block + +BOOST_AUTO_TEST_CASE(query_navigate__to_block__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + + class accessor + : public test::query_accessor + { + public: + using test::query_accessor::query_accessor; + using test::query_accessor::to_block; + }; + + accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false)); + + // Either not strong or not found, except genesis. + BOOST_REQUIRE(!query.to_block(0).is_terminal()); + BOOST_REQUIRE(query.to_block(1).is_terminal()); + BOOST_REQUIRE(query.to_block(2).is_terminal()); + BOOST_REQUIRE(query.to_block(3).is_terminal()); +} + +// to_input_index +// to_output_index + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/properties_tx.cpp b/test/query/properties_tx.cpp index 8ec597eac..daf4b5b98 100644 --- a/test/query/properties_tx.cpp +++ b/test/query/properties_tx.cpp @@ -1,138 +1,138 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "../test.hpp" -#include "../mocks/blocks.hpp" -#include "../mocks/chunk_store.hpp" - -BOOST_FIXTURE_TEST_SUITE(query_properties_tx_tests, test::directory_setup_fixture) - -BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__invalid_link__unvalidated) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - - uint64_t fee{}; - size_t sigops{}; - BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated); - BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated); - BOOST_REQUIRE_EQUAL(fee, 0u); -} - -BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__unvalidated__unvalidated) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); - - uint64_t fee{}; - size_t sigops{}; - BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated); - BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated); - BOOST_REQUIRE_EQUAL(fee, 0u); -} - -BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_out_of_context__unvalidated) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - settings.validated_tx_buckets = 1; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); - BOOST_REQUIRE(query.set(test::block2, context{}, false, false)); - BOOST_REQUIRE(query.set(test::block3, context{}, false, false)); - - uint64_t fee{}; - size_t sigops{}; - constexpr context ctx{ 7, 8, 9 }; - - // Set a context which does not match ctx. - BOOST_REQUIRE(query.set_tx_connected(0, { 1, 5, 9 }, 0, 0)); - BOOST_REQUIRE(query.set_tx_connected(1, { 2, 6, 0 }, 0, 0)); - BOOST_REQUIRE(query.set_tx_connected(2, { 3, 7, 1 }, 0, 0)); - BOOST_REQUIRE(query.set_tx_connected(3, { 4, 8, 2 }, 0, 0)); - BOOST_REQUIRE_EQUAL(query.get_tx_state(1, ctx), error::unvalidated); - BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, ctx), error::unvalidated); - BOOST_REQUIRE_EQUAL(fee, 0u); - BOOST_REQUIRE_EQUAL(sigops, 0u); -} - -BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_connected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - settings.validated_tx_buckets = 1; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); - BOOST_REQUIRE(query.set(test::block2, context{}, false, false)); - BOOST_REQUIRE(query.set(test::block3, context{}, false, false)); - - uint64_t fee{}; - size_t sigops{}; - constexpr uint64_t expected_fee = 42; - constexpr size_t expected_sigops = 24; - constexpr context ctx{ 7, 8, 9 }; - BOOST_REQUIRE(query.set_tx_connected(0, ctx, 11, 12)); - BOOST_REQUIRE(query.set_tx_connected(1, ctx, 13, 14)); - BOOST_REQUIRE(query.set_tx_connected(2, ctx, expected_fee, expected_sigops)); - BOOST_REQUIRE(query.set_tx_connected(2, { 1, 5, 9 }, 15, 16)); - BOOST_REQUIRE(query.set_tx_connected(2, { 2, 6, 0 }, 17, 18)); - BOOST_REQUIRE(query.set_tx_connected(3, ctx, 19, 20)); - BOOST_REQUIRE_EQUAL(query.get_tx_state(2, ctx), error::tx_connected); - BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 2, ctx), error::tx_connected); - BOOST_REQUIRE_EQUAL(fee, expected_fee); - BOOST_REQUIRE_EQUAL(sigops, expected_sigops); -} - -BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_disconnected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); - BOOST_REQUIRE(query.set(test::block2, context{}, false, false)); - BOOST_REQUIRE(query.set(test::block3, context{}, false, false)); - - uint64_t fee{}; - size_t sigops{}; - constexpr context ctx{ 7, 8, 9 }; - BOOST_REQUIRE(query.set_tx_disconnected(4, ctx)); - BOOST_REQUIRE_EQUAL(query.get_tx_state(4, ctx), error::tx_disconnected); - BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 4, ctx), error::tx_disconnected); - BOOST_REQUIRE_EQUAL(fee, 0u); - BOOST_REQUIRE_EQUAL(sigops, 0u); -} - -BOOST_AUTO_TEST_SUITE_END() +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" +#include "../mocks/blocks.hpp" +#include "../mocks/chunk_store.hpp" + +BOOST_FIXTURE_TEST_SUITE(query_properties_tx_tests, test::directory_setup_fixture) + +BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__invalid_link__unvalidated) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + + uint64_t fee{}; + size_t sigops{}; + BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated); + BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated); + BOOST_REQUIRE_EQUAL(fee, 0u); +} + +BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__unvalidated__unvalidated) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); + + uint64_t fee{}; + size_t sigops{}; + BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated); + BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated); + BOOST_REQUIRE_EQUAL(fee, 0u); +} + +BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_out_of_context__unvalidated) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + settings.validated_tx.buckets = 1; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); + BOOST_REQUIRE(query.set(test::block2, context{}, false, false)); + BOOST_REQUIRE(query.set(test::block3, context{}, false, false)); + + uint64_t fee{}; + size_t sigops{}; + constexpr context ctx{ 7, 8, 9 }; + + // Set a context which does not match ctx. + BOOST_REQUIRE(query.set_tx_connected(0, { 1, 5, 9 }, 0, 0)); + BOOST_REQUIRE(query.set_tx_connected(1, { 2, 6, 0 }, 0, 0)); + BOOST_REQUIRE(query.set_tx_connected(2, { 3, 7, 1 }, 0, 0)); + BOOST_REQUIRE(query.set_tx_connected(3, { 4, 8, 2 }, 0, 0)); + BOOST_REQUIRE_EQUAL(query.get_tx_state(1, ctx), error::unvalidated); + BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, ctx), error::unvalidated); + BOOST_REQUIRE_EQUAL(fee, 0u); + BOOST_REQUIRE_EQUAL(sigops, 0u); +} + +BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_connected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + settings.validated_tx.buckets = 1; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); + BOOST_REQUIRE(query.set(test::block2, context{}, false, false)); + BOOST_REQUIRE(query.set(test::block3, context{}, false, false)); + + uint64_t fee{}; + size_t sigops{}; + constexpr uint64_t expected_fee = 42; + constexpr size_t expected_sigops = 24; + constexpr context ctx{ 7, 8, 9 }; + BOOST_REQUIRE(query.set_tx_connected(0, ctx, 11, 12)); + BOOST_REQUIRE(query.set_tx_connected(1, ctx, 13, 14)); + BOOST_REQUIRE(query.set_tx_connected(2, ctx, expected_fee, expected_sigops)); + BOOST_REQUIRE(query.set_tx_connected(2, { 1, 5, 9 }, 15, 16)); + BOOST_REQUIRE(query.set_tx_connected(2, { 2, 6, 0 }, 17, 18)); + BOOST_REQUIRE(query.set_tx_connected(3, ctx, 19, 20)); + BOOST_REQUIRE_EQUAL(query.get_tx_state(2, ctx), error::tx_connected); + BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 2, ctx), error::tx_connected); + BOOST_REQUIRE_EQUAL(fee, expected_fee); + BOOST_REQUIRE_EQUAL(sigops, expected_sigops); +} + +BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_disconnected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{}, false, false)); + BOOST_REQUIRE(query.set(test::block2, context{}, false, false)); + BOOST_REQUIRE(query.set(test::block3, context{}, false, false)); + + uint64_t fee{}; + size_t sigops{}; + constexpr context ctx{ 7, 8, 9 }; + BOOST_REQUIRE(query.set_tx_disconnected(4, ctx)); + BOOST_REQUIRE_EQUAL(query.get_tx_state(4, ctx), error::tx_disconnected); + BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 4, ctx), error::tx_disconnected); + BOOST_REQUIRE_EQUAL(fee, 0u); + BOOST_REQUIRE_EQUAL(sigops, 0u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/settings.cpp b/test/settings.cpp index 898ad3b9a..67376f95a 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -1,95 +1,95 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "test.hpp" - -BOOST_AUTO_TEST_SUITE(settings_tests) - -BOOST_AUTO_TEST_CASE(settings__construct__default__expected) -{ - database::settings configuration; - BOOST_REQUIRE_EQUAL(configuration.turbo, false); - BOOST_REQUIRE_EQUAL(configuration.mark_unconfirmable, true); - BOOST_REQUIRE_EQUAL(configuration.interval_depth, 255u); - BOOST_REQUIRE_EQUAL(configuration.fork_flags, 0u); - BOOST_REQUIRE_EQUAL(configuration.path, "bitcoin"); - - // Archives. - BOOST_REQUIRE_EQUAL(configuration.header_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.header_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.header_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.ins_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.ins_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.ins_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.input_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.input_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.output_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.output_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.outs_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.outs_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.tx_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.tx_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.tx_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.txs_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.txs_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.txs_rate, 50u); - - // Indexes. - BOOST_REQUIRE_EQUAL(configuration.candidate_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.candidate_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.confirmed_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.confirmed_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.strong_tx_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.strong_tx_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.strong_tx_rate, 50u); - - // Caches. - BOOST_REQUIRE_EQUAL(configuration.ecdsa_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.ecdsa_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.schnorr_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.schnorr_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.silent_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.silent_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.duplicate_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.duplicate_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.duplicate_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.prevalid_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.prevalid_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.prevout_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.prevout_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.prevout_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.validated_bk_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.validated_bk_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.validated_bk_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.validated_tx_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.validated_tx_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.validated_tx_rate, 50u); - - // Optionals. - BOOST_REQUIRE_EQUAL(configuration.address_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.address_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.address_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.filter_bk_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.filter_bk_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.filter_bk_rate, 50u); - BOOST_REQUIRE_EQUAL(configuration.filter_tx_buckets, 128u); - BOOST_REQUIRE_EQUAL(configuration.filter_tx_size, 1u); - BOOST_REQUIRE_EQUAL(configuration.filter_tx_rate, 50u); -} - -BOOST_AUTO_TEST_SUITE_END() +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "test.hpp" + +BOOST_AUTO_TEST_SUITE(settings_tests) + +BOOST_AUTO_TEST_CASE(settings__construct__default__expected) +{ + database::settings configuration; + BOOST_REQUIRE_EQUAL(configuration.turbo, false); + BOOST_REQUIRE_EQUAL(configuration.mark_unconfirmable, true); + BOOST_REQUIRE_EQUAL(configuration.interval_depth, 255u); + BOOST_REQUIRE_EQUAL(configuration.fork_flags, 0u); + BOOST_REQUIRE_EQUAL(configuration.path, "bitcoin"); + + // Archives. + BOOST_REQUIRE_EQUAL(configuration.header.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.header.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.header.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.ins.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.ins.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.ins.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.input.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.input.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.output.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.output.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.outs.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.outs.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.tx.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.tx.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.tx.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.txs.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.txs.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.txs.rate, 50u); + + // Indexes. + BOOST_REQUIRE_EQUAL(configuration.candidate.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.candidate.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.confirmed.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.confirmed.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.strong_tx.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.strong_tx.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.strong_tx.rate, 50u); + + // Caches. + BOOST_REQUIRE_EQUAL(configuration.ecdsa.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.ecdsa.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.schnorr.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.schnorr.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.silent.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.silent.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.duplicate.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.duplicate.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.duplicate.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.prevalid.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.prevalid.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.prevout.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.prevout.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.prevout.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.validated_bk.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.validated_bk.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.validated_bk.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.validated_tx.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.validated_tx.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.validated_tx.rate, 50u); + + // Optionals. + BOOST_REQUIRE_EQUAL(configuration.address.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.address.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.address.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.filter_bk.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.filter_bk.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.filter_bk.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.filter_tx.buckets, 128u); + BOOST_REQUIRE_EQUAL(configuration.filter_tx.size, 1u); + BOOST_REQUIRE_EQUAL(configuration.filter_tx.rate, 50u); +} + +BOOST_AUTO_TEST_SUITE_END() From 1241b13bebd55f1a0523ce3eb4ab6da556ce9355 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 20:04:13 -0400 Subject: [PATCH 2/3] Pass storage_settings through storage constructors. --- builds/gnu/Makefile.am | 1 + .../libbitcoin-database.vcxproj | 1 + .../libbitcoin-database.vcxproj.filters | 3 + include/bitcoin/database/impl/memory/mmap.ipp | 12 +-- include/bitcoin/database/impl/store/store.ipp | 87 ++++++++++--------- include/bitcoin/database/memory/mmap.hpp | 10 ++- include/bitcoin/database/memory/mmaps.hpp | 11 +-- include/bitcoin/database/memory/settings.hpp | 42 +++++++++ include/bitcoin/database/settings.hpp | 9 +- include/bitcoin/database/store.hpp | 4 + .../bitcoin/database/tables/caches/ecdsa.hpp | 7 +- .../database/tables/caches/schnorr.hpp | 7 +- .../bitcoin/database/tables/caches/silent.hpp | 7 +- src/define.cpp | 66 +++++++------- test/memory/mmap.cpp | 43 ++++----- test/mocks/chunk_storage.hpp | 13 +-- test/settings.cpp | 42 ++++----- 17 files changed, 213 insertions(+), 152 deletions(-) create mode 100644 include/bitcoin/database/memory/settings.hpp diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am index c7f10e158..b759cf5b8 100644 --- a/builds/gnu/Makefile.am +++ b/builds/gnu/Makefile.am @@ -227,6 +227,7 @@ include_bitcoin_database_memory_HEADERS = \ ${srcdir}/../../include/bitcoin/database/memory/mmaps.hpp \ ${srcdir}/../../include/bitcoin/database/memory/mstage.hpp \ ${srcdir}/../../include/bitcoin/database/memory/reader.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/settings.hpp \ ${srcdir}/../../include/bitcoin/database/memory/streamers.hpp \ ${srcdir}/../../include/bitcoin/database/memory/utilities.hpp diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj index 49769e11e..77b1138e2 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj @@ -161,6 +161,7 @@ + diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters index 85293bd77..134ca4e1f 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -197,6 +197,9 @@ include\bitcoin\database\memory + + include\bitcoin\database\memory + include\bitcoin\database\memory diff --git a/include/bitcoin/database/impl/memory/mmap.ipp b/include/bitcoin/database/impl/memory/mmap.ipp index b2cbc8ade..1c77a4d80 100644 --- a/include/bitcoin/database/impl/memory/mmap.ipp +++ b/include/bitcoin/database/impl/memory/mmap.ipp @@ -32,12 +32,12 @@ namespace database { // ---------------------------------------------------------------------------- TEMPLATE -CLASS::mmap(const path& filename, size_t minimum, size_t expansion, +CLASS::mmap(const path& filename, const storage_settings& settings, bool random, bool staged) NOEXCEPT requires (is_one(columns)) : filenames_{ filename }, - minimum_(to_rows(minimum)), - expansion_(expansion), + minimum_(to_rows(settings.size)), + expansion_(settings.rate), random_(random), staged_(staged), opened_{ file::invalid } @@ -45,12 +45,12 @@ CLASS::mmap(const path& filename, size_t minimum, size_t expansion, } TEMPLATE -CLASS::mmap(const paths& filenames, size_t minimum, size_t expansion, +CLASS::mmap(const paths& filenames, const storage_settings& settings, bool random, bool staged) NOEXCEPT requires (columns > one) : filenames_(filenames), - minimum_(to_rows(minimum)), - expansion_(expansion), + minimum_(to_rows(settings.size)), + expansion_(settings.rate), random_(random), staged_(staged), opened_{} diff --git a/include/bitcoin/database/impl/store/store.ipp b/include/bitcoin/database/impl/store/store.ipp index 36bcb78b4..315f077c0 100644 --- a/include/bitcoin/database/impl/store/store.ipp +++ b/include/bitcoin/database/impl/store/store.ipp @@ -31,79 +31,79 @@ CLASS::store(const settings& config) NOEXCEPT // Archive. // ------------------------------------------------------------------------ - header_head_(head(config.path / schema::dir::heads, schema::archive::header), 1, 0, random), - header_body_(body(config.path, schema::archive::header), config.header.size, config.header.rate, sequential, staged), + header_head_(head(config.path / schema::dir::heads, schema::archive::header), head_settings, random), + header_body_(body(config.path, schema::archive::header), config.header, sequential, staged), - input_head_(head(config.path / schema::dir::heads, schema::archive::input), 1, 0, sequential), - input_body_(body(config.path, schema::archive::input), config.input.size, config.input.rate, sequential, staged), + input_head_(head(config.path / schema::dir::heads, schema::archive::input), head_settings, sequential), + input_body_(body(config.path, schema::archive::input), config.input, sequential, staged), - output_head_(head(config.path / schema::dir::heads, schema::archive::output), 1, 0, sequential), - output_body_(body(config.path, schema::archive::output), config.output.size, config.output.rate, sequential, staged), + output_head_(head(config.path / schema::dir::heads, schema::archive::output), head_settings, sequential), + output_body_(body(config.path, schema::archive::output), config.output, sequential, staged), - ins_head_(head(config.path / schema::dir::heads, schema::archive::ins), 1, 0, random), - ins_body_(body(config.path, schema::archive::ins), config.ins.size, config.ins.rate, sequential, staged), + ins_head_(head(config.path / schema::dir::heads, schema::archive::ins), head_settings, random), + ins_body_(body(config.path, schema::archive::ins), config.ins, sequential, staged), - outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), 1, 0, sequential), - outs_body_(body(config.path, schema::archive::outs), config.outs.size, config.outs.rate, sequential, staged), + outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), head_settings, sequential), + outs_body_(body(config.path, schema::archive::outs), config.outs, sequential, staged), - tx_head_(head(config.path / schema::dir::heads, schema::archive::tx), 1, 0, random), - tx_body_(body(config.path, schema::archive::tx), config.tx.size, config.tx.rate, sequential, staged), + tx_head_(head(config.path / schema::dir::heads, schema::archive::tx), head_settings, random), + tx_body_(body(config.path, schema::archive::tx), config.tx, sequential, staged), - txs_head_(head(config.path / schema::dir::heads, schema::archive::txs), 1, 0, random), - txs_body_(body(config.path, schema::archive::txs), config.txs.size, config.txs.rate, sequential, staged), + txs_head_(head(config.path / schema::dir::heads, schema::archive::txs), head_settings, random), + txs_body_(body(config.path, schema::archive::txs), config.txs, sequential, staged), // Indexes. // ------------------------------------------------------------------------ - candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), 1, 0, sequential), - candidate_body_(body(config.path, schema::indexes::candidate), config.candidate.size, config.candidate.rate, sequential, staged), + candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), head_settings, sequential), + candidate_body_(body(config.path, schema::indexes::candidate), config.candidate, sequential, staged), - confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), 1, 0, sequential), - confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed.size, config.confirmed.rate, sequential, staged), + confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), head_settings, sequential), + confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed, sequential, staged), - strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx), 1, 0, random), - strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx.size, config.strong_tx.rate, sequential, staged), + strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx), head_settings, random), + strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx, sequential, staged), // Caches. // ------------------------------------------------------------------------ // TODO: body not random, but keep in memory. - ecdsa_head_(head(config.path / schema::dir::heads, schema::caches::ecdsa), 1, 0, sequential), - ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa.size, config.ecdsa.rate, sequential, staged), + ecdsa_head_(head(config.path / schema::dir::heads, schema::caches::ecdsa), head_settings, sequential), + ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa, sequential, staged), // TODO: body not random, but keep in memory. - schnorr_head_(head(config.path / schema::dir::heads, schema::caches::schnorr), 1, 0, sequential), - schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr.size, config.schnorr.rate, sequential, staged), + schnorr_head_(head(config.path / schema::dir::heads, schema::caches::schnorr), head_settings, sequential), + schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr, sequential, staged), - silent_head_(head(config.path / schema::dir::heads, schema::caches::silent), 1, 0, sequential), - silent_body_(body(config.path, schema::caches::silent), config.silent.size, config.silent.rate, sequential, staged), + silent_head_(head(config.path / schema::dir::heads, schema::caches::silent), head_settings, sequential), + silent_body_(body(config.path, schema::caches::silent), config.silent, sequential, staged), - duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate), 1, 0, random), - duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate.size, config.duplicate.rate, sequential, staged), + duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate), head_settings, random), + duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate, sequential, staged), - prevalid_head_(head(config.path / schema::dir::heads, schema::caches::prevalid), 1, 0, sequential), - prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid.size, config.prevalid.rate, sequential, staged), + prevalid_head_(head(config.path / schema::dir::heads, schema::caches::prevalid), head_settings, sequential), + prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid, sequential, staged), - prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout), 1, 0, random), - prevout_body_(body(config.path, schema::caches::prevout), config.prevout.size, config.prevout.rate, sequential, staged), + prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout), head_settings, random), + prevout_body_(body(config.path, schema::caches::prevout), config.prevout, sequential, staged), - validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk), 1, 0, random), - validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk.size, config.validated_bk.rate, sequential, staged), + validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk), head_settings, random), + validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk, sequential, staged), - validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx), 1, 0, random), - validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx.size, config.validated_tx.rate, sequential, staged), + validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx), head_settings, random), + validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx, sequential, staged), // Optionals. // ------------------------------------------------------------------------ - address_head_(head(config.path / schema::dir::heads, schema::optionals::address), 1, 0, random), - address_body_(body(config.path, schema::optionals::address), config.address.size, config.address.rate, sequential, staged), + address_head_(head(config.path / schema::dir::heads, schema::optionals::address), head_settings, random), + address_body_(body(config.path, schema::optionals::address), config.address, sequential, staged), - filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk), 1, 0, random), - filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk.size, config.filter_bk.rate, sequential, staged), + filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk), head_settings, random), + filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk, sequential, staged), - filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx), 1, 0, random), - filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx.size, config.filter_tx.rate, sequential, staged), + filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx), head_settings, random), + filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx, sequential, staged), // Locks. // ------------------------------------------------------------------------ @@ -129,7 +129,8 @@ CLASS::store(const settings& config) NOEXCEPT ecdsa(ecdsa_head_, ecdsa_body_), schnorr(schnorr_head_, schnorr_body_), silent(silent_head_, silent_body_), - duplicate(duplicate_head_, duplicate_body_, config.duplicate.buckets), + duplicate(duplicate_head_, duplicate_body_, system::possible_narrow_cast< + table::duplicate::link::integer>(config.duplicate.buckets)), prevalid(prevalid_head_, prevalid_body_), prevout(prevout_head_, prevout_body_, config.prevout.buckets), validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk.buckets), diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index 548c6d1b2..b8d286dfd 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace libbitcoin { namespace database { @@ -68,13 +69,16 @@ class mmap /// file only by explicit transfer, so no dirty file-backed page ever /// exists (no effect where the staging backend is not built). - /// Scalar construction (columns == 1): unchanged signature and codegen. - mmap(const path& filename, size_t minimum=1, size_t expansion=0, + /// Storage tuning is passed by settings (future tunables land there + /// without signature change); random and staged are structural. + + /// Scalar construction (columns == 1). + mmap(const path& filename, const storage_settings& settings={}, bool random=true, bool staged=false) NOEXCEPT requires (is_one(columns)); /// Aggregate construction (columns > 1): one file per column, shared guards. - mmap(const paths& filenames, size_t minimum=1, size_t expansion=0, + mmap(const paths& filenames, const storage_settings& settings={}, bool random=true, bool staged=false) NOEXCEPT requires (columns > one); diff --git a/include/bitcoin/database/memory/mmaps.hpp b/include/bitcoin/database/memory/mmaps.hpp index 8f42eceb3..daa7d1d70 100644 --- a/include/bitcoin/database/memory/mmaps.hpp +++ b/include/bitcoin/database/memory/mmaps.hpp @@ -51,17 +51,18 @@ class mmaps }... }; - mmaps(const path& base_path, size_t size, size_t rate, + mmaps(const path& base_path, const storage_settings& settings, bool random_access, bool staged=false) NOEXCEPT - : mmaps(base_path, size, rate, random_access, staged, sequence) + : mmaps(base_path, settings, random_access, staged, sequence) { } protected: template - mmaps(const path& base_path, size_t size, size_t rate, bool random_access, - bool staged, std::index_sequence) NOEXCEPT - : base(paths{ to_subpath(base_path, suffixes[Index])... }, size, rate, + mmaps(const path& base_path, const storage_settings& settings, + bool random_access, bool staged, + std::index_sequence) NOEXCEPT + : base(paths{ to_subpath(base_path, suffixes[Index])... }, settings, random_access, staged), base_path_{ base_path } { diff --git a/include/bitcoin/database/memory/settings.hpp b/include/bitcoin/database/memory/settings.hpp new file mode 100644 index 000000000..64bf2b94e --- /dev/null +++ b/include/bitcoin/database/memory/settings.hpp @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_MEMORY_SETTINGS_HPP +#define LIBBITCOIN_DATABASE_MEMORY_SETTINGS_HPP + +#include + +namespace libbitcoin { +namespace database { + +/// Storage tuning consumed by memory map construction: the base of table +/// configuration, as network settings bases are derived by server services. +/// Future staging tunables land here without constructor signature changes. +struct storage_settings +{ + /// Minimum body allocation (bytes). + uint64_t size{ 1 }; + + /// Body expansion rate (percentage). + uint16_t rate{ 5 }; +}; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/settings.hpp b/include/bitcoin/database/settings.hpp index 87f55e54e..545879273 100644 --- a/include/bitcoin/database/settings.hpp +++ b/include/bitcoin/database/settings.hpp @@ -20,6 +20,7 @@ #define LIBBITCOIN_DATABASE_SETTINGS_HPP #include +#include #include @@ -39,14 +40,10 @@ struct BCD_API settings /// Table settings. /// ----------------------------------------------------------------------- - /// Body storage sizing (all tables). + /// Body storage sizing (all tables), the storage construction base. struct simple_table + : public storage_settings { - /// Minimum body allocation (bytes). - uint64_t size{ 1 }; - - /// Body expansion rate (percentage). - uint16_t rate{ 50 }; }; /// Adds the head bucket count (hashmap hash space or arraymap length). diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp index 08c5ddfef..4ef68cebf 100644 --- a/include/bitcoin/database/store.hpp +++ b/include/bitcoin/database/store.hpp @@ -234,6 +234,10 @@ class store static constexpr bool random = true; static constexpr bool sequential = false; + // Heads are minimally allocated with no expansion (heads size to their + // configured buckets at table creation). + static constexpr storage_settings head_settings{ 1, 0 }; + // Bodies are append-only, so stage writes in anonymous memory where the // staging backend is built (heads update in place and remain resident). static constexpr bool staged = true; diff --git a/include/bitcoin/database/tables/caches/ecdsa.hpp b/include/bitcoin/database/tables/caches/ecdsa.hpp index 85d864090..02748e810 100644 --- a/include/bitcoin/database/tables/caches/ecdsa.hpp +++ b/include/bitcoin/database/tables/caches/ecdsa.hpp @@ -423,9 +423,10 @@ class ecdsa_storage : public ecdsa_files { public: - ecdsa_storage(const std::filesystem::path& path, size_t size, - size_t rate, bool random_access, bool staged=false) NOEXCEPT - : ecdsa_files(path, size, rate, random_access, staged) + ecdsa_storage(const std::filesystem::path& path, + const storage_settings& settings, bool random_access, + bool staged=false) NOEXCEPT + : ecdsa_files(path, settings, random_access, staged) { } }; diff --git a/include/bitcoin/database/tables/caches/schnorr.hpp b/include/bitcoin/database/tables/caches/schnorr.hpp index 9e0256ee6..9f7f4659a 100644 --- a/include/bitcoin/database/tables/caches/schnorr.hpp +++ b/include/bitcoin/database/tables/caches/schnorr.hpp @@ -250,9 +250,10 @@ class schnorr_storage : public schnorr_files { public: - schnorr_storage(const std::filesystem::path& path, size_t size, - size_t rate, bool random_access, bool staged=false) NOEXCEPT - : schnorr_files(path, size, rate, random_access, staged) + schnorr_storage(const std::filesystem::path& path, + const storage_settings& settings, bool random_access, + bool staged=false) NOEXCEPT + : schnorr_files(path, settings, random_access, staged) { } }; diff --git a/include/bitcoin/database/tables/caches/silent.hpp b/include/bitcoin/database/tables/caches/silent.hpp index 40005d97d..e27e3ae30 100644 --- a/include/bitcoin/database/tables/caches/silent.hpp +++ b/include/bitcoin/database/tables/caches/silent.hpp @@ -153,9 +153,10 @@ class silent_storage : public silent_files { public: - silent_storage(const std::filesystem::path& path, size_t size, - size_t rate, bool random_access, bool staged=false) NOEXCEPT - : silent_files(path, size, rate, random_access, staged) + silent_storage(const std::filesystem::path& path, + const storage_settings& settings, bool random_access, + bool staged=false) NOEXCEPT + : silent_files(path, settings, random_access, staged) { } }; diff --git a/src/define.cpp b/src/define.cpp index 52bd3c530..d1b6efd41 100644 --- a/src/define.cpp +++ b/src/define.cpp @@ -1,33 +1,33 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include - -// version : -// boost : version -// error : boost -// define : error -// settings : define -// /locks : define -// /file : define -// /memory : /file -// /primitives : /memory -// /tables : /primitives -// /types : /tables -// store : /types settings /locks -// query : /types settings +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include + +// version : +// boost : version +// error : boost +// define : error +// settings : define +// /locks : define +// /file : define +// /memory : /file settings +// /primitives : /memory +// /tables : /primitives +// /types : /tables +// store : /types settings /locks +// query : /types settings diff --git a/test/memory/mmap.cpp b/test/memory/mmap.cpp index c03692f2e..46a2e4bc2 100644 --- a/test/memory/mmap.cpp +++ b/test/memory/mmap.cpp @@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__loaded__expected_capacity) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, half_rate); + map instance(file, { minimum, half_rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE_EQUAL(instance.capacity(), minimum); @@ -325,7 +325,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__minimum_no_expansion__expected_capacity) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, 0); + map instance(file, { minimum, 0 }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE_EQUAL(instance.allocate(size), zero); @@ -346,7 +346,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__no_minimum_expansion__expected_capacity) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, rate); + map instance(file, { minimum, rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE_EQUAL(instance.allocate(size), zero); @@ -392,7 +392,7 @@ BOOST_AUTO_TEST_CASE(mmap__truncate__increase_capacity__failure) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, half_rate); + map instance(file, { minimum, half_rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE_EQUAL(instance.capacity(), minimum); @@ -410,7 +410,7 @@ BOOST_AUTO_TEST_CASE(mmap__truncate__unchanged__success_capacity_unchanged) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, half_rate); + map instance(file, { minimum, half_rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE_EQUAL(instance.size(), zero); @@ -431,7 +431,7 @@ BOOST_AUTO_TEST_CASE(mmap__truncate__decrease__success_logical_decreased_capacit const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, half_rate); + map instance(file, { minimum, half_rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE_EQUAL(instance.allocate(minimum), zero); @@ -466,7 +466,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__loaded__expected_capacity) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, half_rate); + map instance(file, { minimum, half_rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE_EQUAL(instance.capacity(), minimum); @@ -512,7 +512,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__minimum_no_expansion__expected_capacity) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, rate); + map instance(file, { minimum, rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE(instance.get_filled(offset, size, fill)); @@ -535,7 +535,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__no_minimum_expansion__expected_capacity) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, rate); + map instance(file, { minimum, rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); BOOST_REQUIRE(instance.get_filled(offset, size, fill)); @@ -560,7 +560,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__loaded__expected_fill) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, minimum, half_rate); + map instance(file, { minimum, half_rate }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -701,7 +701,8 @@ BOOST_AUTO_TEST_CASE(mmap__get_capacity__size__expected) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file); + // No expansion, so capacity equals the allocation. + map instance(file, { 1, 0 }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -826,7 +827,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__write_flush_write_reload__expected) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, 1, 50, true, true); + map instance(file, { 1, 50 }, true, true); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -862,7 +863,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__write_flush_write_reload__expected) BOOST_REQUIRE(!instance.get_fault()); // All content persists across close and reopen. - map reopened(file, 1, 50, true, true); + map reopened(file, { 1, 50 }, true, true); BOOST_REQUIRE(!reopened.open()); BOOST_REQUIRE(!reopened.load()); BOOST_REQUIRE_EQUAL(reopened.size(), first + second); @@ -887,7 +888,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__truncate_below_flush_rewrite__expected) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, 1, 50, true, true); + map instance(file, { 1, 50 }, true, true); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -920,7 +921,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__truncate_below_flush_rewrite__expected) BOOST_REQUIRE(!instance.get_fault()); // Retained content precedes replacement content across reopen. - map reopened(file, 1, 50, true, true); + map reopened(file, { 1, 50 }, true, true); BOOST_REQUIRE(!reopened.open()); BOOST_REQUIRE(!reopened.load()); BOOST_REQUIRE_EQUAL(reopened.size(), retained + appended); @@ -944,7 +945,7 @@ BOOST_AUTO_TEST_CASE(mmap__frontier__staged_out_of_order_completion__expected) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, 1, 50, true, true); + map instance(file, { 1, 50 }, true, true); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -971,7 +972,7 @@ BOOST_AUTO_TEST_CASE(mmap__frontier__staged_truncate__discards_extents) const std::string file = TEST_PATH; BOOST_REQUIRE(test::create(file)); - map instance(file, 1, 50, true, true); + map instance(file, { 1, 50 }, true, true); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -999,7 +1000,7 @@ BOOST_AUTO_TEST_CASE(mmap__unstaged__rewrite_below_flush__expected) BOOST_REQUIRE(test::create(file)); // Unstaged (default) retains in-place rewrite of flushed content (heads). - map instance(file, 1, 50, true); + map instance(file, { 1, 50 }, true); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -1031,7 +1032,7 @@ BOOST_AUTO_TEST_CASE(mmap__unstaged__rewrite_below_flush__expected) BOOST_REQUIRE(!instance.close()); BOOST_REQUIRE(!instance.get_fault()); - map reopened(file, 1, 50, true); + map reopened(file, { 1, 50 }, true); BOOST_REQUIRE(!reopened.open()); BOOST_REQUIRE(!reopened.load()); BOOST_REQUIRE_EQUAL(reopened.size(), size); @@ -1078,7 +1079,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__aggregate_remap__expected_geometry) const storage_t::paths files{ file + "_0", file + "_1" }; // minimum 1 byte -> 1 row; zero expansion makes capacity == rows exactly. - storage_t instance(files, 1, 0); + storage_t instance(files, { 1, 0 }); BOOST_REQUIRE(!instance.create()); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); @@ -1163,7 +1164,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__concurrent__unique_dense_claims) BOOST_REQUIRE(test::create(file)); // Tiny minimum and modest expansion force frequent slow-path remaps. - map instance(file, 1, 25); + map instance(file, { 1, 25 }); BOOST_REQUIRE(!instance.open()); BOOST_REQUIRE(!instance.load()); diff --git a/test/mocks/chunk_storage.hpp b/test/mocks/chunk_storage.hpp index c3a6d88e6..db87d72c0 100644 --- a/test/mocks/chunk_storage.hpp +++ b/test/mocks/chunk_storage.hpp @@ -34,9 +34,10 @@ class default_storage public: using path = std::filesystem::path; - default_storage(const path& filename="test", size_t minimum=1, - size_t expansion=0, bool random=true) NOEXCEPT - : Storage(filename, minimum, expansion, random) + default_storage(const path& filename="test", + const database::storage_settings& settings={}, + bool random=true) NOEXCEPT + : Storage(filename, settings, random) { } }; @@ -69,14 +70,16 @@ class chunk_storages { } - chunk_storages(const path& filename, size_t=1, size_t=0, bool=true, + chunk_storages(const path& filename, + const database::storage_settings& ={}, bool=true, bool=false) NOEXCEPT requires (is_one(columns)) : alias_{}, paths_{ filename }, logical_{} { } /// Aggregate construction (columns > 1): one backing per column. - chunk_storages(const paths& filenames, size_t=1, size_t=0, bool=true, + chunk_storages(const paths& filenames, + const database::storage_settings& ={}, bool=true, bool=false) NOEXCEPT requires (columns > one) : alias_{}, paths_{ filenames }, logical_{} { diff --git a/test/settings.cpp b/test/settings.cpp index 67376f95a..bb1c21f7c 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -32,64 +32,64 @@ BOOST_AUTO_TEST_CASE(settings__construct__default__expected) // Archives. BOOST_REQUIRE_EQUAL(configuration.header.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.header.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.header.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.header.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.ins.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.ins.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.ins.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.ins.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.input.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.input.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.input.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.output.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.output.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.output.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.outs.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.outs.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.outs.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.tx.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.tx.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.tx.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.tx.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.txs.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.txs.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.txs.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.txs.rate, 5u); // Indexes. BOOST_REQUIRE_EQUAL(configuration.candidate.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.candidate.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.candidate.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.confirmed.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.confirmed.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.confirmed.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.strong_tx.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.strong_tx.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.strong_tx.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.strong_tx.rate, 5u); // Caches. BOOST_REQUIRE_EQUAL(configuration.ecdsa.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.ecdsa.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.ecdsa.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.schnorr.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.schnorr.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.schnorr.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.silent.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.silent.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.silent.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.duplicate.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.duplicate.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.duplicate.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.duplicate.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.prevalid.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.prevalid.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.prevalid.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.prevout.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.prevout.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.prevout.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.prevout.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.validated_bk.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.validated_bk.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.validated_bk.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.validated_bk.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.validated_tx.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.validated_tx.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.validated_tx.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.validated_tx.rate, 5u); // Optionals. BOOST_REQUIRE_EQUAL(configuration.address.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.address.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.address.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.address.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.filter_bk.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.filter_bk.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.filter_bk.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.filter_bk.rate, 5u); BOOST_REQUIRE_EQUAL(configuration.filter_tx.buckets, 128u); BOOST_REQUIRE_EQUAL(configuration.filter_tx.size, 1u); - BOOST_REQUIRE_EQUAL(configuration.filter_tx.rate, 50u); + BOOST_REQUIRE_EQUAL(configuration.filter_tx.rate, 5u); } BOOST_AUTO_TEST_SUITE_END() From 35da21c6eee227c51d04d714a9e31c3a3f53ce90 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 21:09:23 -0400 Subject: [PATCH 3/3] Update duplicate link size to 3 bytes. --- include/bitcoin/database/impl/store/store.ipp | 3 +-- include/bitcoin/database/tables/schema.hpp | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/bitcoin/database/impl/store/store.ipp b/include/bitcoin/database/impl/store/store.ipp index 315f077c0..a9ee300d9 100644 --- a/include/bitcoin/database/impl/store/store.ipp +++ b/include/bitcoin/database/impl/store/store.ipp @@ -129,8 +129,7 @@ CLASS::store(const settings& config) NOEXCEPT ecdsa(ecdsa_head_, ecdsa_body_), schnorr(schnorr_head_, schnorr_body_), silent(silent_head_, silent_body_), - duplicate(duplicate_head_, duplicate_body_, system::possible_narrow_cast< - table::duplicate::link::integer>(config.duplicate.buckets)), + duplicate(duplicate_head_, duplicate_body_, config.duplicate.buckets), prevalid(prevalid_head_, prevalid_body_), prevout(prevout_head_, prevout_body_, config.prevout.buckets), validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk.buckets), diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp index 025b3bdd3..c71defd0a 100644 --- a/include/bitcoin/database/tables/schema.hpp +++ b/include/bitcoin/database/tables/schema.hpp @@ -52,7 +52,7 @@ constexpr size_t hash = system::hash_size; /// Primary keys. /// ----------------------------------------------------------------------- -constexpr size_t dup = 2; // ->duplicate [TODO: increase to 3] +constexpr size_t dup = 3; // ->duplicate constexpr size_t put = 5; // ->input/output slab. constexpr size_t ins_ = 4; // ->point|ins record. constexpr size_t outs_ = 4; // ->outs (puts) record. @@ -312,8 +312,8 @@ struct duplicate static constexpr size_t cell = sizeof(uint32_t); static constexpr link count() NOEXCEPT { return 1; } static_assert(minsize == 0u); - static_assert(minrow == 37u); - static_assert(link::size == 2u); + static_assert(minrow == 38u); + static_assert(link::size == 3u); static_assert(cell == 4u); };