diff --git a/include/bitcoin/database/impl/query/archive.ipp b/include/bitcoin/database/impl/query/archive.ipp index ef17088d..4bf01905 100644 --- a/include/bitcoin/database/impl/query/archive.ipp +++ b/include/bitcoin/database/impl/query/archive.ipp @@ -431,6 +431,45 @@ typename CLASS::transactions_ptr CLASS::get_transactions( return transactions; } +TEMPLATE +size_t CLASS::get_candidate_size() const NOEXCEPT +{ + return get_candidate_size(get_top_candidate()); +} + +TEMPLATE +size_t CLASS::get_candidate_size(size_t top) const NOEXCEPT +{ + size_t wire{}; + for (auto height = zero; height <= top; ++height) + wire += get_block_size(to_candidate(height)); + + return wire; +} + +TEMPLATE +size_t CLASS::get_confirmed_size() const NOEXCEPT +{ + return get_confirmed_size(get_top_confirmed()); +} + +TEMPLATE +size_t CLASS::get_confirmed_size(size_t top) const NOEXCEPT +{ + size_t wire{}; + for (auto height = zero; height <= top; ++height) + wire += get_block_size(to_confirmed(height)); + + return wire; +} + +TEMPLATE +size_t CLASS::get_block_size(const header_link& link) const NOEXCEPT +{ + table::txs::get_block_size txs{}; + return store_.txs.get(to_txs_link(link), txs) ? txs.wire : zero; +} + TEMPLATE typename CLASS::header::cptr CLASS::get_header( const header_link& link) const NOEXCEPT @@ -754,6 +793,20 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT return {}; } + ////// Commit addresses to search. + ////auto out_fk = puts.out_fks.begin(); + ////for (const auto& out: outs) + ////{ + //// if (!store_.address.put(address_hash(*out), table::address::record + //// { + //// {}, + //// *out_fk++ + //// })) + //// { + //// return {}; + //// } + ////} + // Commit tx to search. return store_.tx.commit_link(tx_fk, key); // ======================================================================== @@ -821,7 +874,8 @@ header_link CLASS::set_link(const block& block, const context& ctx) NOEXCEPT const auto header_fk = set_link(block.header(), ctx); // Returns txs::link so translate to header::link. - if (set_link(*block.transactions_ptr(), header_fk).is_terminal()) + if (set_link(*block.transactions_ptr(), header_fk, + block.serialized_size(true)).is_terminal()) return {}; return header_fk; @@ -836,7 +890,8 @@ header_link CLASS::set_link(const block& block) NOEXCEPT return {}; // Returns txs::link so translate to header::link. - if (set_link(*block.transactions_ptr(), header_fk).is_terminal()) + if (set_link(*block.transactions_ptr(), header_fk, + block.serialized_size(true)).is_terminal()) return {}; return header_fk; @@ -844,7 +899,7 @@ header_link CLASS::set_link(const block& block) NOEXCEPT TEMPLATE txs_link CLASS::set_link(const transactions& txs, - const header_link& link) NOEXCEPT + const header_link& link, size_t size) NOEXCEPT { if (link.is_terminal()) return{}; @@ -863,12 +918,14 @@ txs_link CLASS::set_link(const transactions& txs, if (!push_link_value(links, set_link(*tx))) return {}; + using bytes = linkage::integer; + const auto wire = system::possible_narrow_cast(size); const auto malleable = block::is_malleable64(txs); // ======================================================================== const auto scope = store_.get_transactor(); - return store_.txs.put_link(link, table::txs::slab{ {}, malleable, links }); + return store_.txs.put_link(link, table::txs::slab{ {}, malleable, wire, links }); // ======================================================================== } @@ -883,7 +940,7 @@ bool CLASS::dissasociate(const header_link& link) NOEXCEPT // ======================================================================== const auto scope = store_.get_transactor(); - return store_.txs.put_link(link, table::txs::slab{ {}, malleable, {} }); + return store_.txs.put_link(link, table::txs::slab{ {}, malleable, {}, {} }); // ======================================================================== } diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 3145a8e8..c6da48ad 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -262,6 +262,11 @@ class query outputs_ptr get_outputs(const tx_link& link) const NOEXCEPT; transactions_ptr get_transactions(const header_link& link) const NOEXCEPT; + size_t get_candidate_size() const NOEXCEPT; + size_t get_candidate_size(size_t top) const NOEXCEPT; + size_t get_confirmed_size() const NOEXCEPT; + size_t get_confirmed_size(size_t top) const NOEXCEPT; + size_t get_block_size(const header_link& link) const NOEXCEPT; header::cptr get_header(const header_link& link) const NOEXCEPT; block::cptr get_block(const header_link& link) const NOEXCEPT; transaction::cptr get_transaction(const tx_link& link) const NOEXCEPT; @@ -281,7 +286,8 @@ class query header_link set_link(const block& block, const chain_context& ctx) NOEXCEPT; header_link set_link(const block& block, const context& ctx) NOEXCEPT; header_link set_link(const block& block) NOEXCEPT; - txs_link set_link(const transactions& txs, const header_link& link) NOEXCEPT; + txs_link set_link(const transactions& txs, const header_link& link, + size_t size) NOEXCEPT; tx_link set_link(const transaction& tx) NOEXCEPT; bool dissasociate(const header_link& link) NOEXCEPT; @@ -474,7 +480,6 @@ class query const header_link& link, size_t height) const NOEXCEPT; private: - // for to_blocks using block_tx = table::strong_tx::record; using block_txs = std::vector; static inline header_links strong_only(const block_txs& strongs) NOEXCEPT; diff --git a/include/bitcoin/database/tables/archives/txs.hpp b/include/bitcoin/database/tables/archives/txs.hpp index f94caa5d..4dfebc25 100644 --- a/include/bitcoin/database/tables/archives/txs.hpp +++ b/include/bitcoin/database/tables/archives/txs.hpp @@ -36,6 +36,7 @@ struct txs { using tx = linkage; using keys = std_vector; + using bytes = linkage; using hash_map::hashmap; struct slab @@ -44,13 +45,14 @@ struct txs link count() const NOEXCEPT { return system::possible_narrow_cast(pk + sk + - schema::bit + schema::count_ + tx::size * tx_fks.size()); + schema::count_ + schema::bit + bytes::size +tx::size * tx_fks.size()); } inline bool from_data(reader& source) NOEXCEPT { tx_fks.resize(source.read_little_endian()); malleable = to_bool(source.read_byte()); + wire = source.read_little_endian(); std::for_each(tx_fks.begin(), tx_fks.end(), [&](auto& fk) NOEXCEPT { fk = source.read_little_endian(); @@ -67,6 +69,7 @@ struct txs sink.write_little_endian(fks); sink.write_byte(to_int(malleable)); + sink.write_little_endian(wire); std::for_each(tx_fks.begin(), tx_fks.end(), [&](const auto& fk) NOEXCEPT { sink.write_little_endian(fk); @@ -82,6 +85,7 @@ struct txs } bool malleable{ false }; + bytes::integer wire{}; // block.serialized_size(true) keys tx_fks{}; }; @@ -91,7 +95,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { const auto count = source.read_little_endian(); - source.skip_byte(); + source.skip_bytes(schema::bit + bytes::size); for (position = zero; position < count; ++position) if (source.read_little_endian() == link) return source; @@ -110,7 +114,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { const auto count = source.read_little_endian(); - source.skip_byte(); + source.skip_bytes(schema::bit + bytes::size); if (!is_zero(count)) { coinbase_fk = source.read_little_endian(); @@ -131,12 +135,26 @@ struct txs { source.skip_bytes(schema::count_); malleable = to_bool(source.read_byte()); + source.skip_bytes(bytes::size); return source; } bool malleable{}; }; + struct get_block_size + : public schema::txs + { + inline bool from_data(reader& source) NOEXCEPT + { + source.skip_bytes(schema::count_ + schema::bit); + wire = source.read_little_endian(); + return source; + } + + bytes::integer wire{}; + }; + struct get_associated : public schema::txs { diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp index 8ed39b48..92948143 100644 --- a/include/bitcoin/database/tables/schema.hpp +++ b/include/bitcoin/database/tables/schema.hpp @@ -111,7 +111,7 @@ namespace schema constexpr size_t puts_ = 5; // ->puts record. constexpr size_t spend_ = 4; // ->spend record. constexpr size_t point_ = 4; // ->point record. - constexpr size_t txs_ = 4; // ->txs slab. + constexpr size_t txs_ = 5; // ->txs slab. constexpr size_t tx = 4; // ->tx record. constexpr size_t block = 3; // ->header record. constexpr size_t bk_slab = 3; // ->validated_bk record. @@ -252,12 +252,13 @@ namespace schema static constexpr size_t sk = schema::header::pk; static constexpr size_t minsize = count_ + // txs - bit + // mutable + bit + // malleable (could compress with size) + schema::size + // block.serialized_size(true) (could be variable) transaction::pk; // coinbase static constexpr size_t minrow = pk + sk + minsize; static constexpr size_t size = max_size_t; - static_assert(minsize == 8u); - static_assert(minrow == 15u); + static_assert(minsize == 11u); + static_assert(minrow == 19u); }; /// Index tables. diff --git a/test/query/archive.cpp b/test/query/archive.cpp index d3c3aabb..34f825b0 100644 --- a/test/query/archive.cpp +++ b/test/query/archive.cpp @@ -536,22 +536,24 @@ BOOST_AUTO_TEST_CASE(query_archive__set_block__get_block__expected) "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script "00"); // witness const auto genesis_txs_head = system::base16_chunk( - "0f000000" // slabs size - "00000000" // pk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); + "1300000000" // slabs size + "0000000000" // pk-> + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff"); const auto genesis_txs_body = system::base16_chunk( - "ffffffff" // next-> - "000000" // header_fk - "01000000" // txs count (1) - "00000000"); // transaction[0] + "ffffffffff" // next-> + "000000" // header_fk + "010000" // txs count (1) + "00" // txs malleable (false) + "1d0100" // txs wire (285) + "00000000"); // transaction[0] settings settings{}; settings.header_buckets = 5; @@ -644,7 +646,7 @@ BOOST_AUTO_TEST_CASE(query_archive__set_block_txs__get_block__expected) "01000000" // version "010000" // ins_count "010000" // outs_count - "0000000000"); // puts_fk-> + "0000000000"); // puts_fk-> const auto genesis_puts_head = system::base16_chunk("0900000000"); const auto genesis_puts_body = system::base16_chunk( "00000000" // spend0_fk-> @@ -684,22 +686,24 @@ BOOST_AUTO_TEST_CASE(query_archive__set_block_txs__get_block__expected) "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script "00"); // witness const auto genesis_txs_head = system::base16_chunk( - "0f000000" // slabs size - "00000000" // pk-> - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff" - "ffffffff"); + "1300000000" // slabs size + "0000000000" // pk-> + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff" + "ffffffffff"); const auto genesis_txs_body = system::base16_chunk( - "ffffffff" // next-> - "000000" // header_fk - "01000000" // txs count (1) - "00000000"); // transaction[0] + "ffffffffff" // next-> + "000000" // header_fk + "010000" // txs count (1) + "00" // txs malleable (false) + "1d0100" // txs wire (285) + "00000000"); // transaction[0] settings settings{}; settings.header_buckets = 5; @@ -999,9 +1003,9 @@ BOOST_AUTO_TEST_CASE(query_archive__is_malleable__malleable__true) BOOST_REQUIRE(query.is_malleated(block3)); // Reassociate the same transaction sets (first(n), disassociated (0), second(n)) - BOOST_REQUIRE(!query.set_link(*block1.transactions_ptr(), 1).is_terminal()); - BOOST_REQUIRE(!query.set_link(*block2.transactions_ptr(), 2).is_terminal()); - BOOST_REQUIRE(!query.set_link(*block3.transactions_ptr(), 3).is_terminal()); + BOOST_REQUIRE(!query.set_link(*block1.transactions_ptr(), 1, block1.serialized_size(true)).is_terminal()); + BOOST_REQUIRE(!query.set_link(*block2.transactions_ptr(), 2, block2.serialized_size(false)).is_terminal()); + BOOST_REQUIRE(!query.set_link(*block3.transactions_ptr(), 3, block3.serialized_size(true)).is_terminal()); // Verify all 3 are reassociated. BOOST_REQUIRE(query.is_associated(1)); @@ -1017,6 +1021,29 @@ BOOST_AUTO_TEST_CASE(query_archive__is_malleable__malleable__true) BOOST_REQUIRE(query.is_malleated(block1)); BOOST_REQUIRE(query.is_malleated(block2)); BOOST_REQUIRE(query.is_malleated(block3)); + + // Verify stored block sizes. + BOOST_REQUIRE_EQUAL(query.get_block_size(0), test::genesis.serialized_size(true)); + BOOST_REQUIRE_EQUAL(query.get_block_size(1), block1.serialized_size(true)); + BOOST_REQUIRE_EQUAL(query.get_block_size(2), block2.serialized_size(false)); + BOOST_REQUIRE_EQUAL(query.get_block_size(3), block3.serialized_size(true)); + + // Verify confirmed wire size. + BOOST_REQUIRE_EQUAL(query.get_confirmed_size(), test::genesis.serialized_size(true)); + BOOST_REQUIRE_EQUAL(query.get_confirmed_size(0), test::genesis.serialized_size(true)); + BOOST_REQUIRE_EQUAL(query.get_confirmed_size(1), test::genesis.serialized_size(true)); + BOOST_REQUIRE_EQUAL(query.get_confirmed_size(42), test::genesis.serialized_size(true)); + + const auto candidate_size = test::genesis.serialized_size(true) + block1.serialized_size(true) + block2.serialized_size(false) + block3.serialized_size(true); + + // Verify candidate wire size. + BOOST_REQUIRE(query.push_candidate(query.to_header(block1.hash()))); + BOOST_REQUIRE(query.push_candidate(query.to_header(block2.hash()))); + BOOST_REQUIRE(query.push_candidate(query.to_header(block3.hash()))); + BOOST_REQUIRE_EQUAL(query.get_candidate_size(), candidate_size); + BOOST_REQUIRE_EQUAL(query.get_candidate_size(0), test::genesis.serialized_size(true)); + BOOST_REQUIRE_EQUAL(query.get_candidate_size(1), test::genesis.serialized_size(true) + block1.serialized_size(true)); + BOOST_REQUIRE_EQUAL(query.get_candidate_size(42), candidate_size); } BOOST_AUTO_TEST_CASE(query_archive__get_header__invalid_parent__expected) diff --git a/test/tables/archives/txs.cpp b/test/tables/archives/txs.cpp index 83215d46..cf1a4766 100644 --- a/test/tables/archives/txs.cpp +++ b/test/tables/archives/txs.cpp @@ -28,6 +28,7 @@ const table::txs::slab expected1 { {}, // schema::txs [all const static members] true, + 0x0000ab, std_vector { 0x56341211_u32 @@ -37,6 +38,7 @@ const table::txs::slab expected2 { {}, // schema::txs [all const static members] false, + 0x00a00b, std_vector { 0x56341221_u32, @@ -47,6 +49,7 @@ const table::txs::slab expected3 { {}, // schema::txs [all const static members] true, + 0xa0000b, std_vector { 0x56341231_u32, @@ -57,7 +60,7 @@ const table::txs::slab expected3 const data_chunk expected_file { // 00->terminal - 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, // key 0x11, 0x22, 0x33, @@ -68,12 +71,15 @@ const data_chunk expected_file // slab0 (malleable) [false] 0x00, + // slab0 (wire) [0x00] + 0x00, 0x00, 0x00, + // slab0 (txs) // -------------------------------------------------------------------------------------------- // 11->00 - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, // key 0x11, 0x22, 0x33, @@ -81,16 +87,19 @@ const data_chunk expected_file // slab1 (count) [1] 0x01, 0x00, 0x00, - // slab0 (malleable) [true] + // slab1 (malleable) [true] 0x01, + // slab1 (wire) [0x0000ab] + 0xab, 0x00, 0x00, + // slab1 (txs) 0x11, 0x12, 0x34, 0x56, // -------------------------------------------------------------------------------------------- // 26->11 - 0x0b, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, // key 0x11, 0x22, 0x33, @@ -98,9 +107,12 @@ const data_chunk expected_file // slab2 (count) [2] 0x02, 0x00, 0x00, - // slab0 (malleable) [false] + // slab2 (malleable) [false] 0x00, + // slab2 (wire) [0x00a00b] + 0x0b, 0xa0, 0x00, + // slab2 0x21, 0x12, 0x34, 0x56, 0x22, 0x12, 0x34, 0x56, @@ -108,7 +120,7 @@ const data_chunk expected_file // -------------------------------------------------------------------------------------------- // 45->26 - 0x1a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, // key 0x11, 0x22, 0x33, @@ -116,9 +128,12 @@ const data_chunk expected_file // slab3 (count) [3] 0x03, 0x00, 0x00, - // slab0 (malleable) [true] + // slab3 (malleable) [true] 0x01, + // slab3 (wire) [0xa0000b] + 0x0b, 0x00, 0xa0, + // slab3 0x31, 0x12, 0x34, 0x56, 0x32, 0x12, 0x34, 0x56, @@ -141,13 +156,13 @@ BOOST_AUTO_TEST_CASE(txs__put__get__expected) BOOST_REQUIRE(instance.get(0, slab)); BOOST_REQUIRE(slab == expected0); - BOOST_REQUIRE(instance.get(11, slab)); + BOOST_REQUIRE(instance.get(15, slab)); BOOST_REQUIRE(slab == expected1); - BOOST_REQUIRE(instance.get(26, slab)); + BOOST_REQUIRE(instance.get(34, slab)); BOOST_REQUIRE(slab == expected2); - BOOST_REQUIRE(instance.get(45, slab)); + BOOST_REQUIRE(instance.get(57, slab)); BOOST_REQUIRE(slab == expected3); }