diff --git a/include/bitcoin/database/error.hpp b/include/bitcoin/database/error.hpp index a2e11072..6625149d 100644 --- a/include/bitcoin/database/error.hpp +++ b/include/bitcoin/database/error.hpp @@ -45,6 +45,8 @@ enum error_t : uint8_t integrity6, integrity7, integrity8, + integrity9, + integrity10, /// memory map open_open, diff --git a/include/bitcoin/database/impl/primitives/hashmap.ipp b/include/bitcoin/database/impl/primitives/hashmap.ipp index 8223dad9..3c5a9802 100644 --- a/include/bitcoin/database/impl/primitives/hashmap.ipp +++ b/include/bitcoin/database/impl/primitives/hashmap.ipp @@ -146,7 +146,6 @@ inline bool CLASS::exists(const Key& key) const NOEXCEPT TEMPLATE inline Link CLASS::first(const Key& key) const NOEXCEPT { - ////return it(key).self(); return first(get_memory(), head_.top(key), key); } diff --git a/include/bitcoin/database/impl/primitives/iterator.ipp b/include/bitcoin/database/impl/primitives/iterator.ipp index 67776bde..6601133e 100644 --- a/include/bitcoin/database/impl/primitives/iterator.ipp +++ b/include/bitcoin/database/impl/primitives/iterator.ipp @@ -21,12 +21,19 @@ #include #include +#include #include #include namespace libbitcoin { namespace database { +TEMPLATE +CLASS::iterator(const memory_ptr& data, const Link& start, Key&& key) NOEXCEPT + : memory_(data), key_(std::forward(key)), link_(to_match(start)) +{ +} + TEMPLATE CLASS::iterator(const memory_ptr& data, const Link& start, const Key& key) NOEXCEPT @@ -40,6 +47,12 @@ inline bool CLASS::advance() NOEXCEPT return !((link_ = to_next(link_))).is_terminal(); } +TEMPLATE +inline const Key& CLASS::key() const NOEXCEPT +{ + return key_; +} + TEMPLATE inline const Link& CLASS::self() const NOEXCEPT { @@ -52,6 +65,13 @@ inline const memory_ptr& CLASS::get() const NOEXCEPT return memory_; } +TEMPLATE +inline void CLASS::reset() NOEXCEPT +{ + link_ = Link::terminal; + memory_.reset(); +} + TEMPLATE inline CLASS::operator bool() const NOEXCEPT { @@ -66,14 +86,14 @@ Link CLASS::to_match(Link link) const NOEXCEPT { // Because of this !link_.is_terminal() subsequently guards both. if (!memory_) - return {}; + return Link::terminal; while (!link.is_terminal()) { // get element offset (fault) const auto offset = memory_->offset(manager::link_to_position(link)); if (is_null(offset)) - return {}; + return Link::terminal; // element key matches (found) const auto key_ptr = std::next(offset, Link::size); @@ -95,7 +115,7 @@ Link CLASS::to_next(Link link) const NOEXCEPT // get element offset (fault) auto offset = memory_->offset(manager::link_to_position(link)); if (is_null(offset)) - return {}; + return Link::terminal; // set next element link (loop) link = { system::unsafe_array_cast(offset) }; @@ -105,7 +125,7 @@ Link CLASS::to_next(Link link) const NOEXCEPT // get next element offset (fault) offset = memory_->offset(manager::link_to_position(link)); if (is_null(offset)) - return {}; + return Link::terminal; // next element key matches (found) const auto key_ptr = std::next(offset, Link::size); diff --git a/include/bitcoin/database/impl/query/confirm.ipp b/include/bitcoin/database/impl/query/confirm.ipp index 0812d74c..0fcbb8ce 100644 --- a/include/bitcoin/database/impl/query/confirm.ipp +++ b/include/bitcoin/database/impl/query/confirm.ipp @@ -247,26 +247,51 @@ TEMPLATE error::error_t CLASS::spent_prevout(const hash_digest& point_hash, index point_index, const tx_link& self) const NOEXCEPT { - // Search key must be passed as an l-value as it is held by reference. - const auto key = table::spend::compose(point_hash, point_index); - - // Iterate all spends of the point to find double spends. - auto it = store_.spend.it(key); + ////if (table::spend::null_point(point_hash)) + //// return error::integrity6; + //// + ////if (table::spend::null_point(point_index)) + //// return error::integrity7; + + // TODO: pass comparitor to iterator construct to preclude composition copy. + auto it = store_.spend.it(table::spend::compose(point_hash, point_index)); if (!it) - return self.is_terminal() ? error::success : error::integrity3; + { + if (self.is_terminal()) + return error::success; + + if (!it.get()) + return error::integrity8; + + if (it.self().is_terminal()) + return error::integrity9; + + if (!store_.spend.exists(it.key())) + return error::integrity10; + + return error::integrity3; + } - table::spend::get_parent spend{}; + // Get all txs that spend the point (non-terminal self must be a spender). + tx_links spenders{}; do { + table::spend::get_parent spend{}; if (!store_.spend.get(it, spend)) return error::integrity4; - // is_strong_tx (search) only called in the case of duplicate. - // Other parent tx of spend is strong (confirmed spent prevout). - if ((spend.parent_fk != self) && is_strong_tx(spend.parent_fk)) - return error::confirmed_double_spend; + // Exclude self from strong_tx search. + if (spend.parent_fk != self) + spenders.push_back(spend.parent_fk); } while (it.advance()); + it.reset(); + + // Find a confirmed spending tx. + for (const auto& spender: spenders) + if (is_strong_tx(spender)) + return error::confirmed_double_spend; + return error::success; } @@ -349,6 +374,8 @@ inline tx_links CLASS::get_strong_txs(const tx_link& link) const NOEXCEPT } while (it.advance()); + it.reset(); + return strong_only(pairs); } @@ -367,18 +394,14 @@ code CLASS::unspent_duplicates(const header_link& link, const auto cb = to_coinbase(link); if (cb.is_terminal()) return error::integrity; - - // Get the coinbase tx hash. - const auto tx_hash = get_tx_key(cb); - if (tx_hash == system::null_hash) - return error::integrity; + // TODO: deadlock risk. // Iterate all strong records for each tx link of the same hash. // The same link may be the coinbase for more than one block. // Distinct links may be the coinbase for independent blocks. // Duplicate instances of a tx (including cb) may exist because of a race. // Strong records for a link may be reorganized and again organized. - auto it = store_.tx.it(tx_hash); + auto it = store_.tx.it(get_tx_key(cb)); if (!it) return error::integrity; @@ -387,13 +410,11 @@ code CLASS::unspent_duplicates(const header_link& link, tx_links coinbases{}; do { - //////////////////////////////////////////////// - // TODO: deadlock. - //////////////////////////////////////////////// for (const auto& tx: get_strong_txs(it.self())) coinbases.push_back(tx); } while (it.advance()); + it.reset(); if (prevout_enabled()) { @@ -428,7 +449,7 @@ code CLASS::unspent_duplicates(const header_link& link, TEMPLATE bool CLASS::get_spend_set(spend_set& set, const tx_link& link) const NOEXCEPT { - table::transaction::get_version_puts tx{}; + table::transaction::get_version_inputs tx{}; if (!store_.tx.get(link, tx)) return false; @@ -437,22 +458,19 @@ bool CLASS::get_spend_set(spend_set& set, const tx_link& link) const NOEXCEPT if (!store_.puts.get(tx.puts_fk, puts)) return false; - set.tx = link; + set.in_tx = link; set.version = tx.version; - set.spends.clear(); - set.spends.reserve(tx.ins_count); - table::spend::get_point_sequence spend{}; + set.spends.reserve(puts.spend_fks.size()); const auto ptr = store_.spend.get_memory(); // This is not concurrent because get_spend_sets is (by tx). for (const auto& spend_fk: puts.spend_fks) { + table::spend::get_point_sequence spend{}; if (!store_.spend.get(ptr, spend_fk, spend)) return false; - // Translate result set to public struct. - set.spends.emplace_back(spend.point_hash, spend.point_index, - spend.sequence, table::prevout::tx::integer{}, bool{}); + set.spends.push_back(std::move(spend.value)); } return true; @@ -467,40 +485,44 @@ bool CLASS::get_spend_sets(spend_sets& sets, // Coinbase tx does not spend so is not retrieved. const auto txs = to_spending_transactions(link); if (txs.empty()) - { - sets.clear(); return true; - } + + sets.resize(txs.size()); + ////auto set = sets.begin(); + ////auto spend_count = zero; + ////for (const auto& tx: txs) + ////{ + //// if (!get_spend_set(*set, tx)) + //// return false; + //// + //// spend_count += (set++)->spends.size(); + ////} std::atomic success{ true }; - const auto to_set = [this, &success](const auto& tx) NOEXCEPT + std::atomic count{ zero }; + const auto to_set = [this, &success, &count](const auto& tx) NOEXCEPT { spend_set set{}; - if (!get_spend_set(set, tx)) success.store(false); + if (!get_spend_set(set, tx)) + success.store(false); + + count += set.spends.size(); return set; }; - sets.resize(txs.size()); - // C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++. std_transform(bc::par_unseq, txs.begin(), txs.end(), sets.begin(), to_set); - return success && (prevout_enabled() ? populate_prevouts(sets, link) : + return success && (prevout_enabled() ? populate_prevouts(sets, count, link) : populate_prevouts(sets)); } TEMPLATE -bool CLASS::populate_prevouts(spend_sets& sets, +bool CLASS::populate_prevouts(spend_sets& sets, size_t spend_count, const header_link& link) const NOEXCEPT { - const auto sets_size = std::accumulate(sets.begin(), sets.end(), zero, - [](size_t total, const auto& set) NOEXCEPT - { - return system::ceilinged_add(total, set.spends.size()); - }); - table::prevout::record_get prevouts{}; - prevouts.values.resize(sets_size); + prevouts.values.resize(spend_count); if (!store_.prevout.at(link, prevouts)) return false; @@ -508,8 +530,8 @@ bool CLASS::populate_prevouts(spend_sets& sets, for (auto& set: sets) for (auto& spend: set.spends) { - spend.coinbase = prevouts.coinbase(index); - spend.tx_fk = prevouts.output_tx_fk(index++); + spend.out_tx = prevouts.output_tx_fk(index); + spend.coinbase = prevouts.coinbase(index++); } return true; @@ -523,11 +545,10 @@ bool CLASS::populate_prevouts(spend_sets& sets) const NOEXCEPT for (auto& set: sets) for (auto& spend: set.spends) { - // This should be converted when the spend set is generated. - spend.tx_fk = to_tx(spend.hash); - - spend.coinbase = is_coinbase(spend.tx_fk); - if (spend.tx_fk == table::prevout::tx::terminal) + // TODO: could be converted when the spend set is generated. + spend.out_tx = to_tx(spend.hash); + spend.coinbase = is_coinbase(spend.out_tx); + if (spend.out_tx == table::prevout::tx::terminal) return false; } @@ -542,8 +563,8 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT return error::integrity1; code ec{}; - if ((ec = unspent_duplicates(link, ctx))) - return ec; + ////if ((ec = unspent_duplicates(link, ctx))) + //// return ec; spend_sets sets{}; if (!get_spend_sets(sets, link)) @@ -558,9 +579,9 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT { error::error_t ec{}; for (const auto& spend: set.spends) - if ((spend.tx_fk != table::prevout::tx::terminal) && + if ((spend.out_tx != table::prevout::tx::terminal) && ((ec = unspendable_prevout(spend.sequence, spend.coinbase, - spend.tx_fk, set.version, ctx)))) + spend.out_tx, set.version, ctx)))) result.store(ec); return result != error::success; @@ -570,8 +591,8 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT { error::error_t ec{}; for (const auto& spend: set.spends) - if ((spend.tx_fk != table::prevout::tx::terminal) && - ((ec = spent_prevout(spend.hash, spend.index, set.tx)))) + if ((spend.out_tx != table::prevout::tx::terminal) && + ((ec = spent_prevout(spend.hash, spend.index, set.in_tx)))) result.store(ec); return result != error::success; diff --git a/include/bitcoin/database/impl/query/optional.ipp b/include/bitcoin/database/impl/query/optional.ipp index ff9578dd..ab8c423e 100644 --- a/include/bitcoin/database/impl/query/optional.ipp +++ b/include/bitcoin/database/impl/query/optional.ipp @@ -39,6 +39,7 @@ bool CLASS::get_confirmed_balance(uint64_t& out, if (!it) return false; + // TODO: deadlock risk. out = zero; do { @@ -69,6 +70,7 @@ bool CLASS::to_address_outputs(output_links& out, if (!it) return false; + // TODO: deadlock risk. out.clear(); do { @@ -94,6 +96,7 @@ bool CLASS::to_unspent_outputs(output_links& out, if (!it) return false; + // TODO: deadlock risk. out.clear(); do { @@ -120,6 +123,7 @@ bool CLASS::to_minimum_unspent_outputs(output_links& out, if (!it) return false; + // TODO: deadlock risk. out.clear(); do { diff --git a/include/bitcoin/database/impl/query/translate.ipp b/include/bitcoin/database/impl/query/translate.ipp index 01b95a13..a51ee8c4 100644 --- a/include/bitcoin/database/impl/query/translate.ipp +++ b/include/bitcoin/database/impl/query/translate.ipp @@ -230,6 +230,7 @@ inline strong_pair CLASS::to_strong(const hash_digest& tx_hash) const NOEXCEPT if (!it) return strong; + // TODO: deadlock risk. do { // Only top block (strong) association for given tx is considered. @@ -318,8 +319,8 @@ TEMPLATE spend_links CLASS::to_spenders(const hash_digest& tx_hash, uint32_t output_index) const NOEXCEPT { - const auto point = table::spend::compose(tx_hash, output_index); - auto it = store_.spend.it(point); + // TODO: pass comparitor to iterator construct to preclude composition copy. + auto it = store_.spend.it(table::spend::compose(tx_hash, output_index)); if (!it) return {}; @@ -342,7 +343,7 @@ spend_links CLASS::to_spenders(const hash_digest& tx_hash, return fault; // Only one input of a given tx may spend an output. - if (spend.key == point) + if (spend.key == it.key()) { spenders.push_back(spend_fk); found = true; diff --git a/include/bitcoin/database/primitives/iterator.hpp b/include/bitcoin/database/primitives/iterator.hpp index 73c44a92..af475615 100644 --- a/include/bitcoin/database/primitives/iterator.hpp +++ b/include/bitcoin/database/primitives/iterator.hpp @@ -44,13 +44,15 @@ class iterator DEFAULT_COPY_MOVE_DESTRUCT(iterator); /// This advances to first match (or terminal). - /// Key must be passed as an l-value as it is held by reference. - iterator(const memory_ptr& data, const Link& start, - const Key& key) NOEXCEPT; + iterator(const memory_ptr& data, const Link& start, Key&& key) NOEXCEPT; + iterator(const memory_ptr& data, const Link& start, const Key& key) NOEXCEPT; /// Advance to and return next iterator. inline bool advance() NOEXCEPT; + /// Expose the search key. + inline const Key& key() const NOEXCEPT; + /// Advance to next match and return false if terminal (not found). inline const Link& self() const NOEXCEPT; @@ -58,6 +60,9 @@ class iterator // TODO: for use by hashmap, make exclusive via friend. inline const memory_ptr& get() const NOEXCEPT; + /// Release the memory pointer, invalidates iterator. + inline void reset() NOEXCEPT; + /// True if the iterator is not terminal. inline operator bool() const NOEXCEPT; @@ -71,7 +76,7 @@ class iterator // This is not thread safe, but it's object is not modified here and the // memory that it refers to is not addressable until written, and writes // are guarded by allocator, which is protected by mutex. - const memory_ptr memory_; + memory_ptr memory_; // This is thread safe. const Key key_; diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 6617b040..5b9dccf9 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -52,36 +52,6 @@ struct strong_pair { header_link block{}; tx_link tx{}; }; using spend_key = table::spend::search_key; using two_counts = std::pair; -struct spend_set -{ - struct spend - { - inline table::spend::search_key prevout() const NOEXCEPT - { - return table::spend::compose(hash, index); - } - - inline bool is_null() const NOEXCEPT - { - return table::spend::null_point(index); - } - - // From spend table. - hash_digest hash{}; - table::spend::ix::integer index{}; - uint32_t sequence{}; - - // From prevouts table. - table::prevout::tx::integer tx_fk{}; - bool coinbase{}; - }; - - tx_link tx{}; - uint32_t version{}; - std::vector spends{}; -}; -using spend_sets = std::vector; - // Writers (non-const) are only: push_, pop_, set_ and initialize. template class query @@ -569,7 +539,8 @@ class query // Critical path bool populate_prevouts(spend_sets& sets) const NOEXCEPT; - bool populate_prevouts(spend_sets& sets, const header_link& link) const NOEXCEPT; + bool populate_prevouts(spend_sets& sets, size_t set_count, + const header_link& link) const NOEXCEPT; bool get_spend_set(spend_set& set, const tx_link& link) const NOEXCEPT; bool get_spend_sets(spend_sets& set, const header_link& link) const NOEXCEPT; bool is_spent_prevout(const hash_digest& point_hash, index point_index, diff --git a/include/bitcoin/database/tables/archives/spend.hpp b/include/bitcoin/database/tables/archives/spend.hpp index 446c0339..3e8bac1a 100644 --- a/include/bitcoin/database/tables/archives/spend.hpp +++ b/include/bitcoin/database/tables/archives/spend.hpp @@ -44,13 +44,18 @@ struct spend return index == system::chain::point::null_index; } + static constexpr bool null_point(const hash_digest& hash) NOEXCEPT + { + return hash == system::null_hash; + } + static inline search_key compose(const hash_digest& point_hash, ix::integer point_index) NOEXCEPT { using namespace system; search_key key{}; - array_cast(key) = point_hash; + std::copy_n(point_hash.begin(), schema::hash, key.begin()); key.at(schema::hash + 0) = byte<0>(point_index); key.at(schema::hash + 1) = byte<1>(point_index); key.at(schema::hash + 2) = byte<2>(point_index); @@ -193,24 +198,22 @@ struct spend inline bool from_data(reader& source) NOEXCEPT { source.rewind_bytes(sk); - point_hash = source.read_hash(); - point_index = source.read_little_endian(); - if (point_index == ix::terminal) - point_index = system::chain::point::null_index; + value.hash = source.read_hash(); + value.index = source.read_little_endian(); + if (value.index == ix::terminal) + value.index = system::chain::point::null_index; source.skip_bytes(tx::size); - sequence = source.read_little_endian(); + value.sequence = source.read_little_endian(); return source; } inline bool is_null() const NOEXCEPT { - return null_point(point_index); + return null_point(value.index); } - hash_digest point_hash{}; - ix::integer point_index{}; - uint32_t sequence{}; + spend_set::spend value{}; }; struct get_parent diff --git a/include/bitcoin/database/tables/archives/transaction.hpp b/include/bitcoin/database/tables/archives/transaction.hpp index 59bff6ec..ecd9dea0 100644 --- a/include/bitcoin/database/tables/archives/transaction.hpp +++ b/include/bitcoin/database/tables/archives/transaction.hpp @@ -214,7 +214,7 @@ struct transaction puts::integer puts_fk{}; }; - struct get_version_puts + struct get_version_inputs : public schema::transaction { inline puts::integer outs_fk() const NOEXCEPT @@ -226,15 +226,14 @@ struct transaction { source.skip_bytes(skip_to_version); version = source.read_little_endian(); - ins_count = source.read_little_endian(); - outs_count = source.read_little_endian(); - puts_fk = source.read_little_endian(); + ins_count = source.read_little_endian(); + source.skip_bytes(ix::size); + puts_fk = source.read_little_endian(); return source; } uint32_t version{}; ix::integer ins_count{}; - ix::integer outs_count{}; puts::integer puts_fk{}; }; diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp index 35acc2f6..8d6ac057 100644 --- a/include/bitcoin/database/tables/schema.hpp +++ b/include/bitcoin/database/tables/schema.hpp @@ -398,6 +398,27 @@ namespace schema ////}; } +struct spend_set +{ + // TODO: index, sequence, out_tx, in_tx not derived. + struct spend + { + // From spend table. + hash_digest hash{}; + uint32_t index{}; + uint32_t sequence{}; + + // From prevouts table. + uint32_t out_tx{}; + bool coinbase{}; + }; + + uint32_t in_tx{}; + uint32_t version{}; + std::vector spends{}; +}; +using spend_sets = std::vector; + } // namespace database } // namespace libbitcoin diff --git a/src/error.cpp b/src/error.cpp index b76e2ec6..1793e403 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -38,6 +38,8 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { integrity6, "store corrupted6" }, { integrity7, "store corrupted7" }, { integrity8, "store corrupted8" }, + { integrity9, "store corrupted9" }, + { integrity10, "store corrupted10" }, // memory map { open_open, "opening open file" }, diff --git a/test/primitives/iterator.cpp b/test/primitives/iterator.cpp index aed29d9a..b02f0b76 100644 --- a/test/primitives/iterator.cpp +++ b/test/primitives/iterator.cpp @@ -34,6 +34,7 @@ BOOST_AUTO_TEST_CASE(iterator__self__empty__terminal) const slab_iterate iterator{ file.get(), start, key0 }; BOOST_REQUIRE(iterator.self().is_terminal()); BOOST_REQUIRE(!iterator); + BOOST_REQUIRE_EQUAL(iterator.key(), key0); } BOOST_AUTO_TEST_CASE(iterator__self__overflow__terminal) @@ -53,6 +54,7 @@ BOOST_AUTO_TEST_CASE(iterator__self__overflow__terminal) const slab_iterate iterator{ file.get(), start, key0 }; BOOST_REQUIRE(iterator.self().is_terminal()); BOOST_REQUIRE(!iterator); + BOOST_REQUIRE_EQUAL(iterator.key(), key0); } BOOST_AUTO_TEST_CASE(iterator__advance__record__expected) @@ -78,6 +80,7 @@ BOOST_AUTO_TEST_CASE(iterator__advance__record__expected) BOOST_REQUIRE(!iterator.advance()); BOOST_REQUIRE(!iterator); BOOST_REQUIRE_EQUAL(iterator.self(), link::terminal); + BOOST_REQUIRE_EQUAL(iterator.key(), key2); } BOOST_AUTO_TEST_CASE(iterator__advance__slab__expected) @@ -104,6 +107,34 @@ BOOST_AUTO_TEST_CASE(iterator__advance__slab__expected) BOOST_REQUIRE(!iterator.advance()); BOOST_REQUIRE(!iterator); BOOST_REQUIRE_EQUAL(iterator.self(), link::terminal); + BOOST_REQUIRE_EQUAL(iterator.key(), key2); + + iterator.reset(); + BOOST_REQUIRE_EQUAL(iterator.self(), link::terminal); +} + +BOOST_AUTO_TEST_CASE(iterator__reset__always__sets_terminal_retains_key) +{ + using link = linkage<1>; + using key = data_array<2>; + using slab_iterate = iterator; + + constexpr auto start = 0; + constexpr key key2{ 0x1a, 0x2a }; + data_chunk data + { + 0x03, 0x1a, 0x2a, + 0x07, 0x1a, 0x2a, 0xee, + 0xff, 0xcc, 0xcc, 0xee, 0xee + }; + test::chunk_storage file{ data }; + slab_iterate iterator{ file.get(), start, key2 }; + BOOST_REQUIRE(iterator); + BOOST_REQUIRE_EQUAL(iterator.self(), 0x00u); + + iterator.reset(); + BOOST_REQUIRE_EQUAL(iterator.self(), link::terminal); + BOOST_REQUIRE_EQUAL(iterator.key(), key2); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/translate.cpp b/test/query/translate.cpp index 76c53297..5dc951b8 100644 --- a/test/query/translate.cpp +++ b/test/query/translate.cpp @@ -187,10 +187,12 @@ class accessor using test::query_accessor::query_accessor; bool get_spend_set_(spend_set& set, const tx_link& link) const NOEXCEPT { + set.spends.clear(); return test::query_accessor::get_spend_set(set, link); } bool get_spend_sets_(spend_sets& sets, const header_link& link) const NOEXCEPT { + sets.clear(); return test::query_accessor::get_spend_sets(sets, link); } }; @@ -247,36 +249,36 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_tx__to_spend__expected) spend_set set{}; BOOST_REQUIRE(query.get_spend_set_(set, 0)); - BOOST_REQUIRE_EQUAL(set.tx, 0u); + BOOST_REQUIRE_EQUAL(set.in_tx, 0u); BOOST_REQUIRE_EQUAL(set.spends.size(), 1u); - BOOST_REQUIRE(set.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(set.spends.front().hash, system::null_hash); BOOST_REQUIRE_EQUAL(set.version, test::genesis.transactions_ptr()->front()->version()); BOOST_REQUIRE(query.get_spend_set_(set, 1)); - BOOST_REQUIRE_EQUAL(set.tx, 1u); + BOOST_REQUIRE_EQUAL(set.in_tx, 1u); BOOST_REQUIRE_EQUAL(set.spends.size(), 1u); - BOOST_REQUIRE(set.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(set.spends.front().hash, system::null_hash); BOOST_REQUIRE_EQUAL(set.version, test::block1.transactions_ptr()->front()->version()); BOOST_REQUIRE(query.get_spend_set_(set, 2)); - BOOST_REQUIRE_EQUAL(set.tx, 2u); + BOOST_REQUIRE_EQUAL(set.in_tx, 2u); BOOST_REQUIRE_EQUAL(set.spends.size(), 1u); - BOOST_REQUIRE(set.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(set.spends.front().hash, system::null_hash); BOOST_REQUIRE_EQUAL(set.version, test::block2.transactions_ptr()->front()->version()); BOOST_REQUIRE(query.get_spend_set_(set, 3)); - BOOST_REQUIRE_EQUAL(set.tx, 3u); + BOOST_REQUIRE_EQUAL(set.in_tx, 3u); BOOST_REQUIRE_EQUAL(set.spends.size(), 1u); - BOOST_REQUIRE(set.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(set.spends.front().hash, system::null_hash); BOOST_REQUIRE_EQUAL(set.version, test::block3.transactions_ptr()->front()->version()); // block1a has no first coinbase. BOOST_REQUIRE(query.get_spend_set_(set, 4)); - BOOST_REQUIRE_EQUAL(set.tx, 4u); + BOOST_REQUIRE_EQUAL(set.in_tx, 4u); BOOST_REQUIRE_EQUAL(set.spends.size(), 3u); - BOOST_REQUIRE(!set.spends[0].is_null()); - BOOST_REQUIRE(!set.spends[1].is_null()); - BOOST_REQUIRE(!set.spends[2].is_null()); + BOOST_REQUIRE_NE(set.spends[0].hash, system::null_hash); + BOOST_REQUIRE_NE(set.spends[1].hash, system::null_hash); + BOOST_REQUIRE_NE(set.spends[2].hash, system::null_hash); BOOST_REQUIRE_EQUAL(set.spends[0].sequence, 42u); BOOST_REQUIRE_EQUAL(set.spends[1].sequence, 24u); BOOST_REQUIRE_EQUAL(set.spends[2].sequence, 25u); @@ -531,12 +533,12 @@ BOOST_AUTO_TEST_CASE(query_translate__get_spend_sets__prevout_populated__expecte // get_spend_sets keys on first-tx-ness as coinbase, so only one input despite two points. BOOST_REQUIRE(query.get_spend_sets_(sets, 2)); BOOST_REQUIRE_EQUAL(sets.size(), 1u); - BOOST_REQUIRE_EQUAL(sets[0].tx, 3u); + BOOST_REQUIRE_EQUAL(sets[0].in_tx, 3u); BOOST_REQUIRE_EQUAL(sets[0].version, 0xb2u); BOOST_REQUIRE_EQUAL(sets[0].spends.size(), 1u); // Internal spend is always terminal/coinbase (defaulted, to be skipped). - BOOST_REQUIRE_EQUAL(sets[0].spends[0].tx_fk, tx_link::terminal); + BOOST_REQUIRE_EQUAL(sets[0].spends[0].out_tx, tx_link::terminal); BOOST_REQUIRE_EQUAL(sets[0].spends[0].coinbase, true); BOOST_REQUIRE_EQUAL(sets[0].spends[0].index, 0u); BOOST_REQUIRE_EQUAL(sets[0].spends[0].hash, test::tx2b.hash(false)); @@ -590,12 +592,12 @@ BOOST_AUTO_TEST_CASE(query_translate__get_spend_sets__no_prevout_populated__expe // get_spend_sets keys on first-tx-ness as coinbase, so only one input despite two points. BOOST_REQUIRE(query.get_spend_sets_(sets, 2)); BOOST_REQUIRE_EQUAL(sets.size(), 1u); - BOOST_REQUIRE_EQUAL(sets[0].tx, 3u); + BOOST_REQUIRE_EQUAL(sets[0].in_tx, 3u); BOOST_REQUIRE_EQUAL(sets[0].version, 0xb2u); BOOST_REQUIRE_EQUAL(sets[0].spends.size(), 1u); // All spends are accounted for (internal spends are not skipped). - BOOST_REQUIRE_EQUAL(sets[0].spends[0].tx_fk, 2u); + BOOST_REQUIRE_EQUAL(sets[0].spends[0].out_tx, 2u); BOOST_REQUIRE_EQUAL(sets[0].spends[0].coinbase, false); BOOST_REQUIRE_EQUAL(sets[0].spends[0].index, 0u); BOOST_REQUIRE_EQUAL(sets[0].spends[0].hash, test::tx2b.hash(false));