diff --git a/include/bitcoin/database/impl/query/archive_read.ipp b/include/bitcoin/database/impl/query/archive_read.ipp index 969a51b0..00495ed4 100644 --- a/include/bitcoin/database/impl/query/archive_read.ipp +++ b/include/bitcoin/database/impl/query/archive_read.ipp @@ -110,7 +110,7 @@ hashes CLASS::get_tx_keys(const header_link& link) const NOEXCEPT TEMPLATE size_t CLASS::get_tx_count(const header_link& link) const NOEXCEPT { - table::txs::get_tx_quantity txs{}; + table::txs::get_tx_count txs{}; if (!store_.txs.at(to_txs(link), txs)) return {}; @@ -175,6 +175,15 @@ bool CLASS::get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT return true; } +TEMPLATE +size_t CLASS::get_tx_size(const tx_link& link, + bool witness) const NOEXCEPT +{ + size_t light{}, heavy{}; + return get_tx_sizes(light, heavy, link) ? (witness ? heavy : light) : + max_uint64; +} + TEMPLATE bool CLASS::get_tx_sizes(size_t& light, size_t& heavy, const tx_link& link) const NOEXCEPT @@ -192,10 +201,25 @@ bool CLASS::get_tx_sizes(size_t& light, size_t& heavy, // ---------------------------------------------------------------------------- TEMPLATE -size_t CLASS::get_block_size(const header_link& link) const NOEXCEPT +size_t CLASS::get_block_size(const header_link& link, + bool witness) const NOEXCEPT +{ + size_t light{}, heavy{}; + return get_block_sizes(light, heavy, link) ? (witness ? heavy : light) : + max_uint64; +} + +TEMPLATE +bool CLASS::get_block_sizes(size_t& light, size_t& heavy, + const header_link& link) const NOEXCEPT { - table::txs::get_block_size txs{}; - return store_.txs.at(to_txs(link), txs) ? txs.wire : zero; + table::txs::get_sizes sizes{}; + if (!store_.txs.at(to_txs(link), sizes)) + return false; + + light = sizes.light; + heavy = sizes.heavy; + return true; } TEMPLATE diff --git a/include/bitcoin/database/impl/query/archive_write.ipp b/include/bitcoin/database/impl/query/archive_write.ipp index a35be377..014d0939 100644 --- a/include/bitcoin/database/impl/query/archive_write.ipp +++ b/include/bitcoin/database/impl/query/archive_write.ipp @@ -392,14 +392,18 @@ code CLASS::set_code(const block& block, const header_link& key, if ((ec = set_code(fk++, *tx, bypass))) return ec; - using bytes = linkage::integer; + // Optional hash, only has value on height intervals. auto interval = create_interval(key, height); - const auto size = block.serialized_size(true); - const auto wire = possible_narrow_cast(size); - // Depth is only used for genesis (is_zero(tx_fks[0])). + // Depth is only set by writer for genesis (is_zero(tx_fks[0])). const auto depth = store_.interval_depth(); + using bytes = linkage::integer; + const auto light = possible_narrow_cast( + block.serialized_size(false)); + const auto heavy = possible_narrow_cast + (block.serialized_size(true)); + // ======================================================================== const auto scope = store_.get_transactor(); constexpr auto positive = true; @@ -413,8 +417,9 @@ code CLASS::set_code(const block& block, const header_link& key, return store_.txs.put(to_txs(key), table::txs::put_group { {}, - wire, count, + light, + heavy, tx_fks, std::move(interval), depth diff --git a/include/bitcoin/database/impl/query/fees.ipp b/include/bitcoin/database/impl/query/fees.ipp index b6edb132..ca7f1844 100644 --- a/include/bitcoin/database/impl/query/fees.ipp +++ b/include/bitcoin/database/impl/query/fees.ipp @@ -27,14 +27,55 @@ #include #include +#define SLOW_FEES + namespace libbitcoin { namespace database { -// TODO: optimize. +// private +TEMPLATE +constexpr size_t CLASS::virtual_size(size_t light, size_t heavy) NOEXCEPT +{ + using namespace system; + using namespace system::chain; + constexpr auto scale = base_size_contribution + total_size_contribution; + + const auto weight = ceilinged_add( + ceilinged_multiply(base_size_contribution, light), + ceilinged_multiply(total_size_contribution, heavy)); + + // Block weight is 3 * nominal size * + 1 * witness size [bip141]. + return ceilinged_divide(weight, scale); +} + +TEMPLATE +bool CLASS::get_tx_virtual_size(uint64_t& out, + const tx_link& link) const NOEXCEPT +{ + size_t light{}, heavy{}; + if (!get_tx_sizes(light, heavy, link)) + return false; + + out = virtual_size(light, heavy); + return true; +} + +TEMPLATE +bool CLASS::get_block_virtual_size(uint64_t& out, + const header_link& link) const NOEXCEPT +{ + size_t light{}, heavy{}; + if (!get_block_sizes(light, heavy, link)) + return false; + + out = virtual_size(light, heavy); + return true; +} TEMPLATE uint64_t CLASS::get_tx_fee(const tx_link& link) const NOEXCEPT { +#if defined(SLOW_FEES) const auto tx = get_transaction(link, false); if (!tx) return max_uint64; @@ -43,6 +84,12 @@ uint64_t CLASS::get_tx_fee(const tx_link& link) const NOEXCEPT return zero; return populate_without_metadata(*tx) ? tx->fee() : max_uint64; +#else + // Get heavy and light sizes for the tx link and compute virtual size. + // Get and total value for each output of the tx link. + // Get all outputs links spent by the tx link (spends). + // Get and total value for each spend (todo: move value to outs). +#endif } TEMPLATE diff --git a/include/bitcoin/database/impl/query/height.ipp b/include/bitcoin/database/impl/query/height.ipp index fa2544ac..610868f8 100644 --- a/include/bitcoin/database/impl/query/height.ipp +++ b/include/bitcoin/database/impl/query/height.ipp @@ -38,7 +38,7 @@ 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)); + wire += get_block_size(to_candidate(height), true); return wire; } @@ -55,7 +55,7 @@ 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)); + wire += get_block_size(to_confirmed(height), true); return wire; } diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 7bac8d4f..62d65aa5 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -341,14 +341,19 @@ class query inline point_key get_point_key(const point_link& link) const NOEXCEPT; inline hash_digest get_point_hash(const point_link& link) const NOEXCEPT; - /// False implies not confirmed. - bool get_tx_height(size_t& out, const tx_link& link) const NOEXCEPT; + /// False position implies not confirmed (or fault). bool get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT; - bool get_tx_sizes(size_t& light, size_t& is_locked, + bool get_tx_height(size_t& out, const tx_link& link) const NOEXCEPT; + + /// Sizes. + size_t get_tx_size(const tx_link& link, bool witness) const NOEXCEPT; + size_t get_block_size(const header_link& link, bool witness) const NOEXCEPT; + bool get_block_sizes(size_t& light, size_t& heavy, + const header_link& link) const NOEXCEPT; + bool get_tx_sizes(size_t& light, size_t& heavy, const tx_link& link) const NOEXCEPT; /// Terminal implies not found, false implies fault. - size_t get_block_size(const header_link& link) const NOEXCEPT; height_link get_height(const hash_digest& key) const NOEXCEPT; height_link get_height(const header_link& link) const NOEXCEPT; bool get_height(size_t& out, const hash_digest& key) const NOEXCEPT; @@ -401,6 +406,12 @@ class query /// Fees. /// ----------------------------------------------------------------------- + /// Virtual size incorporates witnesses if archived. + bool get_tx_virtual_size(uint64_t& out, + const tx_link& link) const NOEXCEPT; + bool get_block_virtual_size(uint64_t& out, + const header_link& link) const NOEXCEPT; + /// Total fee value by tx or block. uint64_t get_tx_fee(const tx_link& link) const NOEXCEPT; uint64_t get_block_fee(const header_link& link) const NOEXCEPT; @@ -771,6 +782,7 @@ class query template static inline code parallel_address_transform(std::atomic_bool& cancel, outpoints& out, const output_links& links, Functor&& functor) NOEXCEPT; + static constexpr size_t virtual_size(size_t light, size_t heavy) NOEXCEPT; // Not thread safe. size_t get_fork_() const NOEXCEPT; diff --git a/include/bitcoin/database/tables/archives/transaction.hpp b/include/bitcoin/database/tables/archives/transaction.hpp index e7b6759d..8b825f31 100644 --- a/include/bitcoin/database/tables/archives/transaction.hpp +++ b/include/bitcoin/database/tables/archives/transaction.hpp @@ -29,6 +29,8 @@ namespace libbitcoin { namespace database { namespace table { +// TODO: merge coinbase bit (saves about 1GB). + /// Transaction is a canonical record hash table. struct transaction : public hash_map @@ -146,11 +148,14 @@ struct transaction inline bool to_data(finalizer& sink) const NOEXCEPT { using namespace system; + const auto light = possible_narrow_cast( + tx.serialized_size(false)); + const auto heavy = possible_narrow_cast + (tx.serialized_size(true)); + sink.write_byte(to_int(tx.is_coinbase())); - sink.write_little_endian( - possible_narrow_cast(tx.serialized_size(false))); - sink.write_little_endian( - possible_narrow_cast(tx.serialized_size(true))); + sink.write_little_endian(light); + sink.write_little_endian(heavy); sink.write_little_endian(tx.locktime()); sink.write_little_endian(tx.version()); sink.write_little_endian(ins_count); diff --git a/include/bitcoin/database/tables/archives/txs.hpp b/include/bitcoin/database/tables/archives/txs.hpp index 88217b6b..f2f9f6fa 100644 --- a/include/bitcoin/database/tables/archives/txs.hpp +++ b/include/bitcoin/database/tables/archives/txs.hpp @@ -31,6 +31,8 @@ namespace libbitcoin { namespace database { namespace table { +// TODO: make heavy field variable size. + /// Txs is a slab arraymap of tx fks (first is count), indexed by header.fk. struct txs : public array_map @@ -44,12 +46,21 @@ struct txs static constexpr auto offset = bytes::bits; static_assert(offset < to_bits(bytes::size)); - static constexpr bytes::integer merge(bool interval, - bytes::integer wire) NOEXCEPT + static constexpr bytes::integer merge(bool is_interval, + bytes::integer light) NOEXCEPT + { + BC_ASSERT_MSG(!system::get_right(light, offset), "overflow"); + return system::set_right(light, offset, is_interval); + } + + static constexpr bool is_interval(bytes::integer merged) NOEXCEPT { - using namespace system; - BC_ASSERT_MSG(!get_right(wire, offset), "overflow"); - return set_right(wire, offset, interval); + return system::get_right(merged, offset); + } + + static constexpr bytes::integer to_light(bytes::integer merged) NOEXCEPT + { + return system::set_right(merged, offset, false); } // Intervals are optional based on store configuration. @@ -64,7 +75,7 @@ struct txs inline link count() const NOEXCEPT { return system::possible_narrow_cast(ct::size + - bytes::size + tx::size * tx_fks.size() + + bytes::size + bytes::size + tx::size * tx_fks.size() + (interval.has_value() ? schema::hash : zero) + to_int(is_genesis())); } @@ -73,15 +84,17 @@ struct txs { using namespace system; tx_fks.resize(source.read_little_endian()); - const auto flagged = source.read_little_endian(); - wire = set_right(flagged, offset, false); + const auto merged = source.read_little_endian(); + heavy = source.read_little_endian(); std::for_each(tx_fks.begin(), tx_fks.end(), [&](auto& fk) NOEXCEPT { fk = source.read_little_endian(); }); - if (get_right(flagged, offset)) interval = source.read_hash(); - if (is_genesis()) depth = source.read_byte(); + light = to_light(merged); + interval.reset(); + if (is_interval(merged)) interval = source.read_hash(); + depth = is_genesis() ? source.read_byte() : zero; BC_ASSERT(!source || source.get_read_position() == count()); return source; } @@ -91,19 +104,19 @@ struct txs using namespace system; BC_ASSERT(tx_fks.size() < power2(to_bits(ct::size))); - const auto flag = interval.has_value(); - const auto flagged = merge(flag, wire); + const auto has_interval = interval.has_value(); + const auto merged = merge(has_interval, light); const auto fks = possible_narrow_cast(tx_fks.size()); - sink.write_little_endian(fks); - sink.write_little_endian(flagged); + sink.write_little_endian(merged); + sink.write_little_endian(heavy); std::for_each(tx_fks.begin(), tx_fks.end(), [&](const auto& fk) NOEXCEPT { sink.write_little_endian(fk); }); - if (flag) sink.write_bytes(interval.value()); + if (has_interval) sink.write_bytes(interval.value()); if (is_genesis()) sink.write_byte(depth); BC_ASSERT(!sink || sink.get_write_position() == count()); return sink; @@ -111,12 +124,15 @@ struct txs inline bool operator==(const slab& other) const NOEXCEPT { - return wire == other.wire + return light == other.light + && heavy == other.heavy && tx_fks == other.tx_fks - && interval == other.interval; + && interval == other.interval + && depth == other.depth; } - bytes::integer wire{}; + bytes::integer light{}; // block.serialized_size(false) + bytes::integer heavy{}; // block.serialized_size(true) keys tx_fks{}; hash interval{}; uint8_t depth{}; @@ -134,33 +150,35 @@ struct txs inline link count() const NOEXCEPT { return system::possible_narrow_cast(ct::size + - bytes::size + tx::size * number + + bytes::size + bytes::size + tx::size * number + (interval.has_value() ? schema::hash : zero) + to_int(is_zero(tx_fk))); } + // TODO: fks can instead be stored as a count and coinbase fk, + // TODO: but will need to be disambiguated from compact blocks. inline bool to_data(finalizer& sink) const NOEXCEPT { BC_ASSERT(number < system::power2(to_bits(ct::size))); - const auto flag = interval.has_value(); + const auto has_interval = interval.has_value(); + const auto merged = merge(has_interval, light); sink.write_little_endian(number); - sink.write_little_endian( - merge(flag, wire)); + sink.write_little_endian(merged); + sink.write_little_endian(heavy); - // TODO: this can instead be stored as a count and coinbase fk, - // TODO: but will need to be disambiguated from compact blocks. for (auto fk = tx_fk; fk < (tx_fk + number); ++fk) sink.write_little_endian(fk); - if (flag) sink.write_bytes(interval.value()); + if (has_interval) sink.write_bytes(interval.value()); if (is_genesis()) sink.write_byte(depth); BC_ASSERT(!sink || sink.get_write_position() == count()); return sink; } - bytes::integer wire{}; ct::integer number{}; + bytes::integer light{}; + bytes::integer heavy{}; tx::integer tx_fk{}; hash interval{}; uint8_t depth{}; @@ -179,9 +197,9 @@ struct txs { using namespace system; const auto number = source.read_little_endian(); - const auto flagged = source.read_little_endian(); - source.skip_bytes(tx::size * number); - if (get_right(flagged, offset)) interval = source.read_hash(); + const auto merged = source.read_little_endian(); + source.skip_bytes(bytes::size + tx::size * number); + if (is_interval(merged)) interval = source.read_hash(); return source; } @@ -202,10 +220,9 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { const auto number = source.read_little_endian(); - const auto flagged = source.read_little_endian(); - const auto skip = tx::size * number + - (system::get_right(flagged, offset) ? schema::hash : zero); - source.skip_bytes(skip); + const auto merged = source.read_little_endian(); + const auto skip_interval = is_interval(merged) ? schema::hash : zero; + source.skip_bytes(bytes::size + tx::size * number + skip_interval); depth = source.read_byte(); return source; } @@ -225,7 +242,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { const auto number = source.read_little_endian(); - source.skip_bytes(bytes::size); + source.skip_bytes(bytes::size + bytes::size); for (position = zero; position < number; ++position) if (source.read_little_endian() == tx_fk) return source; @@ -250,7 +267,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { const auto number = source.read_little_endian(); - source.skip_bytes(bytes::size); + source.skip_bytes(bytes::size + bytes::size); if (is_nonzero(number)) { coinbase_fk = source.read_little_endian(); @@ -276,7 +293,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { const auto number = source.read_little_endian(); - source.skip_bytes(bytes::size); + source.skip_bytes(bytes::size + bytes::size); if (number > position) { source.skip_bytes(position * tx::size); @@ -292,9 +309,14 @@ struct txs tx::integer tx_fk{}; }; - struct get_block_size + struct get_sizes : public schema::txs { + static constexpr bytes::integer to_size(bytes::integer merged) NOEXCEPT + { + return system::set_right(merged, offset, false); + } + inline link count() const NOEXCEPT { BC_ASSERT(false); @@ -304,12 +326,13 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { source.skip_bytes(ct::size); - wire = system::set_right(source.read_little_endian< - bytes::integer, bytes::size>(), offset, false); + light = to_size(source.read_little_endian()); + heavy = source.read_little_endian(); return source; } - bytes::integer wire{}; + bytes::integer light{}; + bytes::integer heavy{}; }; struct get_associated @@ -323,6 +346,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { + // Read is cheap but unnecessary, since 0 is never set here. associated = to_bool(source.read_little_endian()); return source; } @@ -342,7 +366,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { tx_fks.resize(source.read_little_endian()); - source.skip_bytes(bytes::size); + source.skip_bytes(bytes::size + bytes::size); std::for_each(tx_fks.begin(), tx_fks.end(), [&](auto& fk) NOEXCEPT { fk = source.read_little_endian(); @@ -370,7 +394,7 @@ struct txs return source; tx_fks.resize(sub1(number)); - source.skip_bytes(bytes::size + tx::size); + source.skip_bytes(bytes::size + bytes::size + tx::size); std::for_each(tx_fks.begin(), tx_fks.end(), [&](auto& fk) NOEXCEPT { fk = source.read_little_endian(); @@ -382,7 +406,7 @@ struct txs keys tx_fks{}; }; - struct get_tx_quantity + struct get_tx_count : public schema::txs { inline link count() const NOEXCEPT @@ -412,7 +436,7 @@ struct txs inline bool from_data(reader& source) NOEXCEPT { number = source.read_little_endian(); - source.skip_bytes(bytes::size); + source.skip_bytes(bytes::size + bytes::size); if (is_zero(number)) { diff --git a/include/bitcoin/database/tables/caches/prevout.hpp b/include/bitcoin/database/tables/caches/prevout.hpp index b0307220..268e4d0a 100644 --- a/include/bitcoin/database/tables/caches/prevout.hpp +++ b/include/bitcoin/database/tables/caches/prevout.hpp @@ -135,17 +135,17 @@ struct prevout return source; } - static inline bool coinbase(tx::integer spend) NOEXCEPT + static constexpr bool coinbase(tx::integer merged) NOEXCEPT { - // Inside are always reflected as coinbase. - return system::get_right(spend, offset); + // Inside spends are always reflected as coinbase. + return system::get_right(merged, offset); } - static inline tx::integer output_tx_fk(tx::integer spend) NOEXCEPT + static constexpr tx::integer output_tx_fk(tx::integer merged) NOEXCEPT { // Inside spends are mapped to terminal. - using namespace system; - return spend == tx::terminal ? spend : set_right(spend, offset, false); + return merged == tx::terminal ? merged : + system::set_right(merged, offset, false); } // Spend count is derived in confirmation from block.txs.ins. diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp index 07526527..15fb8a76 100644 --- a/include/bitcoin/database/tables/schema.hpp +++ b/include/bitcoin/database/tables/schema.hpp @@ -208,14 +208,15 @@ struct txs using link = linkage; static constexpr size_t minsize = count_ + // txs + schema::size + // block.serialized_size(false) schema::size + // block.serialized_size(true) schema::transaction::pk;// coinbase tx ////schema::bit + // is interval - merged bit into schema::size. ////0 | schema::hash + // electrum interval hash (each 2048th block). static constexpr size_t minrow = minsize; static constexpr size_t size = max_size_t; - static_assert(minsize == 10u); - static_assert(minrow == 10u); + static_assert(minsize == 13u); + static_assert(minrow == 13u); static_assert(link::size == 5u); }; diff --git a/test/query/archive_write.cpp b/test/query/archive_write.cpp index f3c7955a..249dec29 100644 --- a/test/query/archive_write.cpp +++ b/test/query/archive_write.cpp @@ -518,7 +518,7 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block__get_block__expected) "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script "00"); // witness const auto genesis_txs_head = system::base16_chunk( - "0b00000000" // slab size + "0e00000000" // slab size "0000000000" // pk-> "ffffffffff" "ffffffffff" @@ -537,7 +537,8 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block__get_block__expected) "ffffffffff"); const auto genesis_txs_body = system::base16_chunk( "010000" // txs count (1) - "1d0100" // txs wire (285) + "1d0100" // size light (285) + "1d0100" // size heavy (285) "00000000" // transaction[0] "ff"); // depth (255) @@ -670,7 +671,7 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block_txs__get_block__expected) "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script "00"); // witness const auto genesis_txs_head = system::base16_chunk( - "0b00000000" // slab size + "0e00000000" // slab size "0000000000" // pk-> "ffffffffff" "ffffffffff" @@ -689,7 +690,8 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block_txs__get_block__expected) "ffffffffff"); const auto genesis_txs_body = system::base16_chunk( "010000" // txs count (1) - "1d0100" // txs wire (285) + "1d0100" // size light (285) + "1d0100" // size heavy (285) "00000000" // transaction[0] "ff"); // depth (255) diff --git a/test/tables/archives/txs.cpp b/test/tables/archives/txs.cpp index 60cea7e6..d1179185 100644 --- a/test/tables/archives/txs.cpp +++ b/test/tables/archives/txs.cpp @@ -28,6 +28,7 @@ const table::txs::slab slab0 { {}, // schema::txs [all const static members] 0x000000, + 0x000000, { // tx fk 0 uniquely identifies genesis, resulting in depth storage. 0x00000000_u32 @@ -39,6 +40,7 @@ const table::txs::slab slab1 { {}, // schema::txs [all const static members] 0x0000ab, + 0x0000cc, { 0x56341211_u32 } @@ -47,6 +49,7 @@ const table::txs::slab slab2 { {}, // schema::txs [all const static members] 0x00a00b, + 0x00cc00, { 0x56341221_u32, 0x56341222_u32 @@ -56,6 +59,7 @@ const table::txs::slab slab3 { {}, // schema::txs [all const static members] 0x09000b, + 0xcc0000, { 0x56341231_u32, 0x56341232_u32, @@ -67,7 +71,10 @@ const data_chunk expected0 // slab0 (count) [1] 0x01, 0x00, 0x00, - // slab0 (wire) [0x00] + // slab0 (light) [0x00] + 0x00, 0x00, 0x00, + + // slab0 (heavy) [0x00] 0x00, 0x00, 0x00, // slab0 (txs) @@ -81,9 +88,12 @@ const data_chunk expected1 // slab1 (count) [1] 0x01, 0x00, 0x00, - // slab1 (wire) [0x0000ab] + // slab1 (light) [0x0000ab] 0xab, 0x00, 0x00, + // slab1 (heavy) [0x0000cc] + 0xcc, 0x00, 0x00, + // slab1 (txs) 0x11, 0x12, 0x34, 0x56 }; @@ -92,9 +102,12 @@ const data_chunk expected2 // slab2 (count) [2] 0x02, 0x00, 0x00, - // slab2 (wire) [0x00a00b] + // slab2 (light) [0x00a00b] 0x0b, 0xa0, 0x00, + // slab2 (heavy) [0x00cc00] + 0x00, 0xcc, 0x00, + // slab2 0x21, 0x12, 0x34, 0x56, 0x22, 0x12, 0x34, 0x56 @@ -104,9 +117,12 @@ const data_chunk expected3 // slab3 (count) [3] 0x03, 0x00, 0x00, - // slab3 (wire) [0x09000b] + // slab3 (light) [0x09000b] 0x0b, 0x00, 0x09, + // slab3 (heavy) [0xcc0000] + 0x00, 0x00, 0xcc, + // slab3 0x31, 0x12, 0x34, 0x56, 0x32, 0x12, 0x34, 0x56,