diff --git a/include/bitcoin/node/chase.hpp b/include/bitcoin/node/chase.hpp index aea98cb39..e356a0ed1 100644 --- a/include/bitcoin/node/chase.hpp +++ b/include/bitcoin/node/chase.hpp @@ -91,6 +91,9 @@ enum class chase /// Check/Identify. /// ----------------------------------------------------------------------- + /////// A set of blocks is being checked, top block provided (height_t). + ////checking, + /// A block has been downloaded, checked and stored (height_t). /// Issued by 'block_in_31800' or 'populate' and handled by 'connect'. /// Populate is bypassed for checkpoint/milestone blocks. diff --git a/include/bitcoin/node/chasers/chaser_check.hpp b/include/bitcoin/node/chasers/chaser_check.hpp index ae0166e12..32edcbc0d 100644 --- a/include/bitcoin/node/chasers/chaser_check.hpp +++ b/include/bitcoin/node/chasers/chaser_check.hpp @@ -68,6 +68,7 @@ class BCN_API chaser_check virtual void do_get_hashes(const map_handler& handler) NOEXCEPT; virtual void do_put_hashes(const map_ptr& map, const network::result_handler& handler) NOEXCEPT; + virtual void do_confirmable(height_t height) NOEXCEPT; private: typedef std::deque maps; @@ -89,6 +90,7 @@ class BCN_API chaser_check // These are protected by strand. size_t inventory_{}; size_t requested_{}; + size_t confirmed_{}; job::ptr job_{}; maps maps_{}; }; diff --git a/include/bitcoin/node/chasers/chaser_confirm.hpp b/include/bitcoin/node/chasers/chaser_confirm.hpp index 3dbd47fc5..875ad800c 100644 --- a/include/bitcoin/node/chasers/chaser_confirm.hpp +++ b/include/bitcoin/node/chasers/chaser_confirm.hpp @@ -50,25 +50,16 @@ class BCN_API chaser_confirm virtual bool handle_event(const code& ec, chase event_, event_value value) NOEXCEPT; + ////virtual void do_checking(height_t height) NOEXCEPT; + virtual void do_regressed(height_t branch_point) NOEXCEPT; virtual void do_validated(height_t height) NOEXCEPT; - virtual void do_reorganize(size_t height) NOEXCEPT; - virtual void do_organize(size_t height) NOEXCEPT; - - virtual void enqueue_block(const database::header_link& link) NOEXCEPT; - virtual void confirm_tx(const database::context& ctx, - const database::tx_link& link, const race::ptr& racer) NOEXCEPT; - virtual void handle_tx(const code& ec, const database::tx_link& tx, - const race::ptr& racer) NOEXCEPT; - virtual void handle_txs(const code& ec, const database::tx_link& tx, - const database::header_link& link, size_t height)NOEXCEPT; - virtual void confirm_block(const code& ec, - const database::header_link& link, size_t height) NOEXCEPT; - virtual void next_block(size_t height) NOEXCEPT; + virtual void do_bump(height_t branch_point) NOEXCEPT; -private: - void reset() NOEXCEPT; - bool busy() const NOEXCEPT; + ////virtual void do_reorganize(header_links& fork, size_t fork_point) NOEXCEPT; + ////virtual void do_organize(header_links& fork, const header_links& popped, + //// size_t fork_point) NOEXCEPT; +private: bool set_organized(const database::header_link& link, height_t height) NOEXCEPT; bool reset_organized(const database::header_link& link, @@ -77,8 +68,6 @@ class BCN_API chaser_confirm height_t height) NOEXCEPT; bool roll_back(const header_links& popped, size_t fork_point, size_t top) NOEXCEPT; - bool roll_back(const header_links& popped, size_t fork_point, - size_t top, const database::header_link& link) NOEXCEPT; bool get_fork_work(uint256_t& fork_work, header_links& fork, height_t fork_top) const NOEXCEPT; @@ -90,9 +79,8 @@ class BCN_API chaser_confirm // These are protected by strand. network::threadpool threadpool_; - header_links popped_{}; - header_links fork_{}; - size_t fork_point_{}; + network::asio::strand strand_; + bool mature_{}; }; } // namespace node diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index 90f6fe9f9..6119e10f6 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -73,8 +73,10 @@ class BCN_API chaser_validate // These are protected by strand. network::threadpool threadpool_; + network::asio::strand strand_; system::hash_digest neutrino_{}; size_t validation_backlog_{}; + bool mature_{}; }; } // namespace node diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index 9e7aa6cae..232d09b98 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -53,39 +53,52 @@ enum error_t : uint8_t suspended_service, /// blockchain - branch_error, orphan_block, orphan_header, duplicate_block, duplicate_header, - ////validation_bypass, - ////confirmation_bypass, - /// chasers - set_block_unconfirmable, - get_height, - get_branch_work, - get_is_strong, - invalid_branch_point, - pop_candidate, - push_candidate, - invalid_fork_point, - get_candidate_chain_state, - get_block, - get_unassociated, - get_fork_work, - to_confirmed, - pop_confirmed, - get_block_confirmable, - get_block_state, - set_strong, - set_unstrong, - set_organized, - set_block_confirmable, - set_block_valid, - node_confirm, - node_validate, - node_roll_back + /// faults (terminal, code error and store corruption assumed) + protocol1, + header1, + organize1, + organize2, + organize3, + organize4, + organize5, + organize6, + organize7, + organize8, + organize9, + organize10, + organize11, + organize12, + organize13, + organize14, + organize15, + validate1, + validate2, + validate3, + validate4, + validate5, + validate6, + confirm1, + confirm2, + confirm3, + confirm4, + confirm5, + confirm6, + confirm7, + confirm8, + confirm9, + confirm10, + confirm11, + confirm12, + confirm13, + confirm14, + confirm15, + confirm16, + confirm17 }; // No current need for error_code equivalence mapping. diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index 790399d00..a85cf4e34 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -57,8 +57,8 @@ code CLASS::start() NOEXCEPT if (!state_) { - fault(error::get_candidate_chain_state); - return error::get_candidate_chain_state; + fault(error::organize1); + return error::organize1; } LOGN("Candidate top [" << system::encode_hash(state_->hash()) << ":" @@ -205,14 +205,14 @@ void CLASS::do_organize(typename Block::cptr block, if (!get_branch_work(work, branch_point, tree_branch, store_branch, header)) { - handler(fault(error::get_branch_work), height); + handler(fault(error::organize2), height); return; } // branch_point is the highest tree-candidate common block. if (!get_is_strong(strong, work, branch_point)) { - handler(fault(error::get_is_strong), height); + handler(fault(error::organize3), height); return; } @@ -235,7 +235,7 @@ void CLASS::do_organize(typename Block::cptr block, const auto top_candidate = state_->height(); if (branch_point > top_candidate) { - handler(fault(error::invalid_branch_point), height); + handler(fault(error::organize4), height); return; } @@ -247,7 +247,7 @@ void CLASS::do_organize(typename Block::cptr block, if ((is_under_milestone(index) && !query.set_unstrong(candidate)) || !query.pop_candidate()) { - handler(fault(error::pop_candidate), height); + handler(fault(error::organize5), height); return; } @@ -264,7 +264,7 @@ void CLASS::do_organize(typename Block::cptr block, if ((is_under_milestone(index) && !query.set_strong(link)) || !query.push_candidate(link)) { - handler(fault(error::push_candidate), height); + handler(fault(error::organize6), height); return; } @@ -347,7 +347,7 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT size_t height{}; if (!query.get_height(height, link) || is_zero(height)) { - fault(error::get_height); + fault(error::organize7); return; } @@ -355,7 +355,7 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT const auto fork_point = query.get_fork(); if (height <= fork_point) { - fault(error::invalid_fork_point); + fault(error::organize8); return; } @@ -365,7 +365,7 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT auto state = query.get_candidate_chain_state(settings_, fork_point); if (!state) { - fault(error::get_candidate_chain_state); + fault(error::organize9); return; } @@ -378,7 +378,7 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT { if (!get_block(block, index)) { - fault(error::get_block); + fault(error::organize10); return; } @@ -398,7 +398,7 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT if ((is_under_milestone(index) && !query.set_unstrong(candidate)) || !query.pop_candidate()) { - fault(error::pop_candidate); + fault(error::organize11); return; } @@ -418,7 +418,7 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT // Confirmed are already set_strong and must stay that way. if (!query.push_candidate(query.to_confirmed(index))) { - fault(error::push_candidate); + fault(error::organize12); return; } @@ -428,7 +428,7 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT state = query.get_candidate_chain_state(settings_, top_confirmed); if (!state) { - fault(error::get_candidate_chain_state); + fault(error::organize13); return; } @@ -552,7 +552,7 @@ code CLASS::push_block(const Block& block, return ec; if (!query.push_candidate(link)) - return error::push_candidate; + return error::organize14; return ec; } @@ -562,7 +562,7 @@ code CLASS::push_block(const system::hash_digest& key) NOEXCEPT { const auto handle = tree_.extract(key); if (!handle) - return error::branch_error; + return error::organize15; const auto& value = handle.mapped(); return push_block(*value.block, value.state->context()); diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index c71b5d369..ea102d619 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -130,6 +130,12 @@ bool chaser_check::handle_event(const code&, chase event_, POST(do_headers, std::get(value)); break; } + case chase::confirmable: + { + BC_ASSERT(std::holds_alternative(value)); + POST(do_confirmable, std::get(value)); + break; + } case chase::stop: { return false; @@ -143,6 +149,15 @@ bool chaser_check::handle_event(const code&, chase event_, return true; } +void chaser_check::do_confirmable(height_t height) NOEXCEPT +{ + BC_ASSERT(stranded()); + confirmed_ = height; + + if (confirmed_ == requested_) + do_headers(height_t{}); +} + // regression // ---------------------------------------------------------------------------- @@ -223,7 +238,7 @@ void chaser_check::do_bump(height_t) NOEXCEPT query.to_candidate(add1(position())))) set_position(add1(position())); - set_unassociated(); + do_headers(height_t{}); } // add headers @@ -324,8 +339,10 @@ size_t chaser_check::set_unassociated() NOEXCEPT if (closed() || purging()) return {}; - // Defer new work issuance until all gaps are filled. - if (position() < requested_ || requested_ >= maximum_height_) + // Defer new work issuance until gaps filled and confirmation caught up. + if (position() < requested_ || + requested_ >= maximum_height_ + /*||confirmed_ < requested_*/) return {}; // Inventory size gets set only once. @@ -364,6 +381,7 @@ size_t chaser_check::set_unassociated() NOEXCEPT << count << ") last (" << requested_ << ")."); + ////notify(error::success, chase::checking, requested_); return count; } diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index 2794f3906..526dcc637 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -36,18 +37,20 @@ using namespace std::placeholders; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -// threads and priority_validation currently overloading validation settings. +// Single higher priority thread strand (base class strand uses network pool). +// Higher priority than validator ensures locality to validator reads. chaser_confirm::chaser_confirm(full_node& node) NOEXCEPT : chaser(node), concurrent_(node.config().node.concurrent_confirmation), - threadpool_(std::max(node.config().node.threads, 1_u32), - node.config().node.priority_validation ? - network::thread_priority::high : network::thread_priority::normal) + threadpool_(1_u32, node.config().node.priority_validation ? + network::thread_priority::highest : network::thread_priority::high), + strand_(threadpool_.service().get_executor()) { } code chaser_confirm::start() NOEXCEPT { + set_position(archive().get_fork()); SUBSCRIBE_EVENTS(handle_event, _1, _2, _3); return error::success; } @@ -77,6 +80,7 @@ bool chaser_confirm::handle_event(const code&, chase event_, if (closed()) return false; + // TODO: resume needs to ensure a bump. // Stop generating query during suspension. if (suspended()) return true; @@ -85,11 +89,28 @@ bool chaser_confirm::handle_event(const code&, chase event_, // These can come out of order, advance in order synchronously. switch (event_) { - case chase::blocks: + ////case chase::blocks: + ////{ + //// BC_ASSERT(std::holds_alternative(value)); + //// + //// if (concurrent_ || mature_) + //// { + //// // TODO: value is branch point. + //// POST(do_validated, std::get(value)); + //// } + //// + //// break; + ////} + case chase::start: + case chase::bump: { - // TODO: value is branch point. - BC_ASSERT(std::holds_alternative(value)); - POST(do_validated, std::get(value)); + if (concurrent_ || mature_) + { + ////POST(do_bump, height_t{}); + boost::asio::post(strand_, + BIND(do_bump, height_t{})); + } + break; } case chase::valid: @@ -97,15 +118,39 @@ bool chaser_confirm::handle_event(const code&, chase event_, // value is validated block height. BC_ASSERT(std::holds_alternative(value)); - // TODO: height may be premature due to concurrent download. - const auto height = std::get(value); - if (concurrent_ /*|| is_current(archive().to_candidate(height))*/) + if (concurrent_ || mature_) { - POST(do_validated, height); + ////POST(do_validated, std::get(value)); + boost::asio::post(strand_, + BIND(do_validated, std::get(value))); } break; } + ////case chase::checking: + ////{ + //// BC_ASSERT(std::holds_alternative(value)); + //// ////POST(do_checking, std::get(value)); + //// boost::asio::post(strand_, + //// BIND(do_checking, std::get(value))); + //// break; + ////} + case chase::regressed: + { + BC_ASSERT(std::holds_alternative(value)); + ////POST(do_regressed, std::get(value)); + boost::asio::post(strand_, + BIND(do_regressed, std::get(value))); + break; + } + case chase::disorganized: + { + BC_ASSERT(std::holds_alternative(value)); + ////POST(do_regressed, std::get(value)); + boost::asio::post(strand_, + BIND(do_regressed, std::get(value))); + break; + } case chase::stop: { return false; @@ -119,347 +164,354 @@ bool chaser_confirm::handle_event(const code&, chase event_, return true; } -// confirm -// ---------------------------------------------------------------------------- -// Blocks are either confirmed (blocks first) or validated/confirmed -// (headers first) here. Candidate chain reorganizations will result in -// reported heights moving in any direction. Each is treated as independent and -// only one representing a stronger chain is considered. +////void chaser_confirm::do_checking(height_t height) NOEXCEPT +////{ +////} -// Compute relative work, set fork_ and fork_point_. -void chaser_confirm::do_validated(height_t height) NOEXCEPT +void chaser_confirm::do_regressed(height_t branch_point) NOEXCEPT { BC_ASSERT(stranded()); + auto& query = archive(); - if (closed() || busy()) - return; - - bool strong{}; - uint256_t work{}; - - // Scan down from height to first confirmed, accumulate links and sum work. - if (!get_fork_work(work, fork_, height)) - { - fault(error::get_fork_work); - return; - } - - // No longer a candidate fork (heights are not candidates). - if (fork_.empty()) - return; - - // fork_point is the highest candidate-confirmed common block. - fork_point_ = height - fork_.size(); - if (!get_is_strong(strong, work, fork_point_)) - { - fault(error::get_is_strong); - return; - } - - // Not yet a strong fork (confirmed branch has at least as much work). - if (!strong) + for (auto height = position(); height > branch_point; --height) { - reset(); - return; + if (!query.set_unstrong(query.to_candidate(height))) + { + fault(error::confirm1); + return; + } } - do_reorganize(archive().get_top_confirmed()); + set_position(branch_point); } -// Pop confirmed chain from height down to above fork point, save popped_. -void chaser_confirm::do_reorganize(size_t height) NOEXCEPT +void chaser_confirm::do_validated(height_t height) NOEXCEPT { BC_ASSERT(stranded()); - if (height < fork_point_) - { - fault(error::invalid_fork_point); - return; - } - - popped_.clear(); - auto& query = archive(); - while (height > fork_point_) - { - const auto link = query.to_confirmed(height); - if (link.is_terminal()) - { - fault(error::to_confirmed); - return; - } - - popped_.push_back(link); - if (!query.set_unstrong(link)) - { - fault(error::set_unstrong); - return; - } - - if (!query.pop_confirmed()) - { - fault(error::pop_confirmed); - return; - } - - notify(error::success, chase::reorganized, popped_.back()); - fire(events::block_reorganized, height--); - } - - do_organize(add1(height)); + // Candidate block was validated at the given height, confirm/advance. + if (height == add1(position())) + do_bump(height); } -// Push candidate headers from above fork point to confirmed chain. -void chaser_confirm::do_organize(size_t height) NOEXCEPT +void chaser_confirm::do_bump(height_t) NOEXCEPT { BC_ASSERT(stranded()); - if (fork_.empty()) - return; - auto& query = archive(); - const auto& link = fork_.back(); - const auto bypass = is_under_checkpoint(height) || - query.is_milestone(link); - if (!bypass) + // Keep working past window if possible. + for (auto height = add1(position()); !closed(); ++height) { - const auto ec = query.get_block_state(link); + const auto link = query.to_candidate(height); + auto ec = query.get_block_state(link); - if (ec == database::error::block_valid) + // Don't report bypassed block is confirmable until associated. + if (ec == database::error::unassociated) + return; + + if (is_under_checkpoint(height) || query.is_milestone(link)) + { + notify(error::success, chase::confirmable, height); + fire(events::confirm_bypassed, height); + LOGV("Block confirmation bypassed: " << height); + ////return; + } + else if (ec == database::error::block_valid) { if (!query.set_strong(link)) { - fault(error::set_strong); + fault(error::confirm2); return; } - - enqueue_block(link); - return; + + if ((ec = query.block_confirmable(link))) + { + if (ec == database::error::integrity) + { + fault(error::confirm3); + return; + } + + if (!query.set_block_unconfirmable(link)) + { + fault(error::confirm4); + return; + } + + if (!query.set_unstrong(link)) + { + fault(error::confirm5); + return; + } + + // Blocks between link and fork point will be set_unstrong + // by header reorganization, picked up by do_regressed. + notify(ec, chase::unconfirmable, link); + fire(events::block_unconfirmable, height); + LOGR("Unconfirmable block [" << height << "] " + << ec.message()); + return; + } + + // TODO: fees. + if (!query.set_block_confirmable(link, {})) + { + fault(error::confirm6); + return; + } + + notify(error::success, chase::confirmable, height); + fire(events::block_confirmed, height); + LOGV("Block confirmed: " << height); + ////return; } else if (ec == database::error::block_confirmable) { - // Required of all confirmed, and before checking confirmable. - // Checked blocks are set at download, cannot be unset (reorged). - // Milestone blocks are set/unset strong by header organization. if (!query.set_strong(link)) { - fault(error::set_strong); + fault(error::confirm7); return; } - - // falls through (previously confirmable, reported as bypass) - } - else if (ec == database::error::block_unconfirmable) - { - notify(ec, chase::unconfirmable, link); - fire(events::block_unconfirmable, height); - - // Roll back previously confirmed blocks. - if (!roll_back(popped_, fork_point_, sub1(height))) - fault(error::node_roll_back); - - reset(); - return; + + notify(error::success, chase::confirmable, height); + fire(events::confirm_bypassed, height); + LOGV("Block previously confirmable: " << height); + ////return; } - else + else { // With or without an error code, shouldn't be here. // database::error::block_valid [canonical state ] // database::error::block_confirmable [resurrected state] - // database::error::block_unconfirmable [shouldn't be here] ? + // database::error::block_unconfirmable [shouldn't be here] // database::error::unknown_state [shouldn't be here] // database::error::unassociated [shouldn't be here] // database::error::unvalidated [shouldn't be here] - fault(error::node_confirm); return; } - } - notify(error::success, chase::confirmable, height); - fire(events::confirm_bypassed, height); - - if (!set_organized(link, height)) - { - fault(error::set_organized); - return; + set_position(height); } - - POST(next_block, add1(height)); - return; } -// DISTRUBUTE WORK UNITS -void chaser_confirm::enqueue_block(const header_link& link) NOEXCEPT -{ - BC_ASSERT(stranded()); - const auto& query = archive(); - - context ctx{}; - const auto txs = query.to_transactions(link); - if (txs.empty() || !query.get_context(ctx, link)) - { - POST(confirm_block, database::error::integrity, link, size_t{}); - return; - } - - code ec{}; - const auto height = ctx.height; - if ((ec = query.unspent_duplicates(txs.front(), ctx))) - { - POST(confirm_block, ec, link, height); - return; - } - - if (is_one(txs.size())) - { - POST(confirm_block, ec, link, height); - return; - } - - const auto racer = std::make_shared(sub1(txs.size())); - racer->start(BIND(handle_txs, _1, _2, link, height)); - fire(events::block_buffered, height); - - for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx) - boost::asio::post(threadpool_.service(), - BIND(confirm_tx, ctx, *tx, racer)); -} - -// START WORK UNIT -void chaser_confirm::confirm_tx(const context& ctx, const tx_link& link, - const race::ptr& racer) NOEXCEPT -{ - const auto ec = archive().tx_confirmable(link, ctx); - POST(handle_tx, ec, link, racer); -} - -// FINISH WORK UNIT -void chaser_confirm::handle_tx(const code& ec, const tx_link& tx, - const race::ptr& racer) NOEXCEPT -{ - BC_ASSERT(stranded()); - - // handle_txs will only get invoked once, with a first error code, so - // invoke fault here ensure that non-validation codes are not lost. - if (ec == database::error::integrity) - fault(error::node_confirm); - - // TODO: need to sort out bypass, validity, and fault codes. - // Always allow the racer to finish, invokes handle_txs exactly once. - racer->finish(ec, tx); -} - -// SYNCHRONIZE WORK UNITS -void chaser_confirm::handle_txs(const code& ec, const tx_link& tx, - const header_link& link, size_t height) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec) - { - // Log tx here as it's the first failed one. - LOGR("Error confirming tx [" << encode_hash(archive().get_tx_key(tx)) - << "] " << ec.message()); - } - - confirm_block(ec, link, height); -} - -// SUMMARIZE WORK -void chaser_confirm::confirm_block(const code& ec, const header_link& link, - size_t height) NOEXCEPT -{ - BC_ASSERT(stranded()); - auto& query = archive(); - - if (ec == database::error::integrity) - { - fault(error::node_confirm); - return; - } - - if (ec) - { - if (!query.set_block_unconfirmable(link)) - { - fault(error::set_block_unconfirmable); - return; - } - - LOGR("Unconfirmable block [" << height << "] " << ec.message()); - notify(ec, chase::unconfirmable, link); - fire(events::block_unconfirmable, height); - - // Roll back current and previously confirmed blocks. - if (!roll_back(popped_, fork_point_, height, link)) - { - fault(error::node_roll_back); - return; - } - - reset(); - return; - } - - // TODO: move fee setter to set_block_valid (transitory) and propagate to - // TODO: set_block_confirmable (final). Bypassed do not have the fee cache. - if (!query.set_block_confirmable(link, uint64_t{})) - { - fault(error::set_block_confirmable); - return; - } - - notify(error::success, chase::confirmable, height); - fire(events::block_confirmed, height); - if (!set_organized(link, height)) - { - fault(error::set_organized); - return; - } - - LOGV("Block confirmed and organized: " << height); - next_block(add1(height)); -} - -// SHARED ITERATE -void chaser_confirm::next_block(size_t height) NOEXCEPT -{ - BC_ASSERT(stranded()); - - // Continue until fork is empty. - fork_.pop_back(); - if (!fork_.empty()) - { - do_organize(height); - return; - } - - reset(); - const auto& query = archive(); - - // Prevent stall by bumping, as the event may have been missed. - const auto code = query.get_block_state(query.to_candidate(height)); - if ((code == database::error::block_valid) || - (code == database::error::block_confirmable)) - { - do_validated(height); - } -} +// confirm +// ---------------------------------------------------------------------------- +// Blocks are either confirmed (blocks first) or validated/confirmed (headers +// first) here. Candidate chain reorganizations will result in reported heights +// moving in any direction. Each is treated as independent and only one +// representing a stronger chain is considered. + +////// Compute relative work, set fork and fork_point, and invoke reorganize. +////void chaser_confirm::do_validated(height_t height) NOEXCEPT +////{ +//// BC_ASSERT(stranded()); +//// +//// if (closed()) +//// return; +//// +//// bool strong{}; +//// uint256_t work{}; +//// header_links fork{}; +//// +//// // Scan from validated height to first confirmed, save links, and sum work. +//// if (!get_fork_work(work, fork, height)) +//// { +//// fault(error::confirm1); +//// return; +//// } +//// +//// // No longer a candidate fork (may have been reorganized). +//// if (fork.empty()) +//// return; +//// +//// // fork_point is the highest candidate <-> confirmed common block. +//// const auto fork_point = height - fork.size(); +//// if (!get_is_strong(strong, work, fork_point)) +//// { +//// fault(error::confirm2); +//// return; +//// } +//// +//// // Fork is strong (has more work than confirmed branch). +//// if (strong) +//// do_reorganize(fork, fork_point); +////} +//// +////// Pop confirmed chain from top down to above fork point, save popped. +////void chaser_confirm::do_reorganize(header_links& fork, size_t fork_point) NOEXCEPT +////{ +//// auto& query = archive(); +//// +//// auto top = query.get_top_confirmed(); +//// if (top < fork_point) +//// { +//// fault(error::confirm3); +//// return; +//// } +//// +//// header_links popped{}; +//// while (top > fork_point) +//// { +//// const auto top_link = query.to_confirmed(top); +//// if (top_link.is_terminal()) +//// { +//// fault(error::confirm4); +//// return; +//// } +//// +//// popped.push_back(top_link); +//// if (!query.set_unstrong(top_link)) +//// { +//// fault(error::confirm5); +//// return; +//// } +//// +//// if (!query.pop_confirmed()) +//// { +//// fault(error::confirm6); +//// return; +//// } +//// +//// notify(error::success, chase::reorganized, top_link); +//// fire(events::block_reorganized, top--); +//// } +//// +//// // Top is now fork_point. +//// do_organize(fork, popped, fork_point); +////} +//// +////// Push candidate headers from above fork point to confirmed chain. +////void chaser_confirm::do_organize(header_links& fork, +//// const header_links& popped, size_t fork_point) NOEXCEPT +////{ +//// auto& query = archive(); +//// +//// // height tracks each fork link, starting at fork_point+1. +//// auto height = fork_point; +//// +//// while (!fork.empty()) +//// { +//// ++height; +//// const auto& link = fork.back(); +//// +//// if (is_under_checkpoint(height) || query.is_milestone(link)) +//// { +//// notify(error::success, chase::confirmable, height); +//// fire(events::confirm_bypassed, height); +//// LOGV("Block confirmation bypassed and organized: " << height); +//// +//// // Checkpoint and milestone are already set_strong. +//// if (!set_organized(link, height)) +//// { +//// fault(error::confirm7); +//// return; +//// } +//// } +//// else +//// { +//// auto ec = query.get_block_state(link); +//// if (ec == database::error::block_valid) +//// { +//// if (!query.set_strong(link)) +//// { +//// fault(error::confirm8); +//// return; +//// } +//// +//// // This spawns threads internally, blocking until complete. +//// if ((ec = query.block_confirmable(link))) +//// { +//// if (ec == database::error::integrity) +//// { +//// fault(error::confirm9); +//// return; +//// } +//// +//// notify(ec, chase::unconfirmable, link); +//// fire(events::block_unconfirmable, height); +//// LOGR("Unconfirmable block [" << height << "] " +//// << ec.message()); +//// +//// if (!query.set_unstrong(link)) +//// { +//// fault(error::confirm10); +//// return; +//// } +//// +//// if (!query.set_block_unconfirmable(link)) +//// { +//// fault(error::confirm11); +//// return; +//// } +//// +//// if (!roll_back(popped, fork_point, sub1(height))) +//// fault(error::confirm12); +//// +//// return; +//// } +//// +//// // TODO: fees. +//// if (!query.set_block_confirmable(link, {})) +//// { +//// fault(error::confirm13); +//// return; +//// } +//// +//// notify(ec, chase::confirmable, height); +//// fire(events::block_confirmed, height); +//// LOGV("Block confirmed and organized: " << height); +//// +//// if (!set_organized(link, height)) +//// fault(error::confirm14); +//// +//// return; +//// } +//// else if (ec == database::error::block_confirmable) +//// { +//// if (!query.set_strong(link)) +//// { +//// fault(error::confirm15); +//// return; +//// } +//// +//// notify(error::success, chase::confirmable, height); +//// fire(events::confirm_bypassed, height); +//// LOGV("Block previously confirmable organized: " << height); +//// +//// if (!set_organized(link, height)) +//// fault(error::confirm16); +//// +//// return; +//// } +//// else +//// { +//// /////////////////////////////////////////////////////////////// +//// // TODO: do not start branch unless block valid or confirmable. +//// /////////////////////////////////////////////////////////////// +//// +//// // With or without an error code, shouldn't be here. +//// // database::error::block_valid [canonical state ] +//// // database::error::block_confirmable [resurrected state] +//// // database::error::block_unconfirmable [shouldn't be here] +//// // database::error::unknown_state [shouldn't be here] +//// // database::error::unassociated [shouldn't be here] +//// // database::error::unvalidated [shouldn't be here] +//// ////fault(error::confirm17); +//// return; +//// } +//// } +//// +//// fork.pop_back(); +//// } +//// +//// /////////////////////////////////////////////////////////////////////////// +//// // TODO: terminal stall will happen here as validation completes. +//// /////////////////////////////////////////////////////////////////////////// +////} // Private // ---------------------------------------------------------------------------- -void chaser_confirm::reset() NOEXCEPT -{ - fork_.clear(); - popped_.clear(); - fork_point_ = zero; -} - -bool chaser_confirm::busy() const NOEXCEPT -{ - return !fork_.empty(); -} - bool chaser_confirm::set_organized(const header_link& link, height_t) NOEXCEPT { auto& query = archive(); @@ -495,13 +547,8 @@ bool chaser_confirm::set_reorganized(const header_link& link, return true; } -bool chaser_confirm::roll_back(const header_links& popped, - size_t fork_point, size_t top, const header_link& link) NOEXCEPT -{ - // The current block (top/link) is set_strong but is not confirmable. - return archive().set_unstrong(link) && roll_back(popped, fork_point, top); -} - +// Roll back to the specified new top. Header organization may then return the +// node to a previously-stronger branch - no need to do that here. bool chaser_confirm::roll_back(const header_links& popped, size_t fork_point, size_t top) NOEXCEPT { diff --git a/src/chasers/chaser_header.cpp b/src/chasers/chaser_header.cpp index d1fec76fd..81a9d2df2 100644 --- a/src/chasers/chaser_header.cpp +++ b/src/chasers/chaser_header.cpp @@ -37,7 +37,7 @@ chaser_header::chaser_header(full_node& node) NOEXCEPT code chaser_header::start() NOEXCEPT { if (!initialize_milestone()) - return fault(database::error::integrity); + return fault(error::header1); return chaser_organize
::start(); } diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index f119aa12d..12b8231fb 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -42,13 +42,8 @@ BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -// maximum_backlog is the limit of ASIO backlog in blocks. -// There is almost no cost the the backlog, as it's on an independent -// threadpool and contains only the header link value. It's better to avoid -// a costly query to get the tx count (for example) and allow the amount of -// actual work that is queued to vary significantly. The maximum_concurrency -// setting is overloaded for the purpose of limiting the backlog. - +// Multiple higher priority thread strand (base class strand uses network pool). +// Higher priority than downloader (net) ensures locality to downloader writes. chaser_validate::chaser_validate(full_node& node) NOEXCEPT : chaser(node), prepopulate_(node.config().node.prepopulate), @@ -58,7 +53,8 @@ chaser_validate::chaser_validate(full_node& node) NOEXCEPT subsidy_interval_(node.config().bitcoin.subsidy_interval_blocks), threadpool_(std::max(node.config().node.threads, 1_u32), node.config().node.priority_validation ? - network::thread_priority::high : network::thread_priority::normal) + network::thread_priority::high : network::thread_priority::normal), + strand_(threadpool_.service().get_executor()) { } @@ -91,23 +87,23 @@ bool chaser_validate::handle_event(const code&, chase event_, if (closed()) return false; + // TODO: resume needs to ensure a bump. // Stop generating query during suspension. if (suspended()) return true; // These come out of order, advance in order asynchronously. - // Asynchronous completion results in out of order notification. + // Asynchronous completion again results in out of order notification. switch (event_) { - // Track downloaded. - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ case chase::start: case chase::bump: { - // TODO: currency? - if (concurrent_ /*|| is_current(archive().to_candidate(height))*/) + if (concurrent_ || mature_) { - POST(do_bump, height_t{}); + ////POST(do_bump, height_t{}); + boost::asio::post(strand_, + BIND(do_bump, height_t{})); } break; @@ -117,11 +113,11 @@ bool chaser_validate::handle_event(const code&, chase event_, // value is checked block height. BC_ASSERT(std::holds_alternative(value)); - // TODO: height may be premature due to concurrent download. - const auto height = std::get(value); - if (concurrent_ /*|| is_current(archive().to_candidate(height))*/) + if (concurrent_ || mature_) { - POST(do_checked, height); + ////POST(do_checked, std::get(value)); + boost::asio::post(strand_, + BIND(do_checked, std::get(value))); } break; @@ -129,16 +125,19 @@ bool chaser_validate::handle_event(const code&, chase event_, case chase::regressed: { BC_ASSERT(std::holds_alternative(value)); - POST(do_regressed, std::get(value)); + ////POST(do_regressed, std::get(value)); + boost::asio::post(strand_, + BIND(do_regressed, std::get(value))); break; } case chase::disorganized: { BC_ASSERT(std::holds_alternative(value)); - POST(do_regressed, std::get(value)); + ////POST(do_regressed, std::get(value)); + boost::asio::post(strand_, + BIND(do_regressed, std::get(value))); break; } - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ case chase::stop: { return false; @@ -180,7 +179,6 @@ void chaser_validate::do_bump(height_t) NOEXCEPT BC_ASSERT(stranded()); const auto& query = archive(); - // Validate checked blocks starting immediately after last validated. // Bypass until next event if validation backlog is full. for (auto height = add1(position()); (validation_backlog_ < maximum_backlog_) && !closed(); ++height) @@ -188,16 +186,13 @@ void chaser_validate::do_bump(height_t) NOEXCEPT const auto link = query.to_candidate(height); const auto ec = query.get_block_state(link); - // TODO: Block until ungapped to current and then start at top validated. - - // Wait until the gap is filled at a current height. + // Wait until the gap is filled at a current height (or next top). if (ec == database::error::unassociated) return; // The size of the job is not relevant to the backlog cost. ++validation_backlog_; - // Causes a reorganization (should have been encountered by headers). if (ec == database::error::block_unconfirmable) { complete_block(ec, link, height); @@ -214,9 +209,9 @@ void chaser_validate::do_bump(height_t) NOEXCEPT continue; } - // Report backlog, should generally not exceed dowload window. - ////fire(events::block_buffered, validation_backlog_); - boost::asio::post(threadpool_.service(), BIND(validate_block, link)); + // concurrent by block + boost::asio::post(threadpool_.service(), + BIND(validate_block, link)); } } @@ -229,14 +224,18 @@ void chaser_validate::validate_block(const header_link& link) NOEXCEPT const auto block = query.get_block(link); if (!block) { - POST(complete_block, database::error::integrity, link, zero); + ////POST(complete_block, error::validate1, link, zero); + boost::asio::post(strand_, + BIND(complete_block, error::validate1, link, zero)); return; } chain::context ctx{}; if (!query.get_context(ctx, link)) { - POST(complete_block, database::error::integrity, link, zero); + ////POST(complete_block, error::validate2, link, zero); + boost::asio::post(strand_, + BIND(complete_block, error::validate2, link, zero)); return; } @@ -247,7 +246,9 @@ void chaser_validate::validate_block(const header_link& link) NOEXCEPT if (!query.populate(*block)) { // This could instead be a case of invalid milestone. - POST(complete_block, database::error::integrity, link, ctx.height); + ////POST(complete_block, error::validate3, link, ctx.height); + boost::asio::post(strand_, + BIND(complete_block, error::validate3, link, ctx.height)); return; } @@ -257,19 +258,21 @@ void chaser_validate::validate_block(const header_link& link) NOEXCEPT { if (!query.set_block_unconfirmable(link)) { - ec = database::error::integrity; + ec = error::validate4; } } else if (!query.set_block_valid(link)) { - ec = database::error::integrity; + ec = error::validate5; } else { fire(events::block_validated, ctx.height); } - POST(complete_block, ec, link, ctx.height); + ////POST(complete_block, ec, link, ctx.height); + boost::asio::post(strand_, + BIND(complete_block, ec, link, ctx.height)); } void chaser_validate::complete_block(const code& ec, const header_link& link, @@ -282,7 +285,7 @@ void chaser_validate::complete_block(const code& ec, const header_link& link, if (ec) { - if (ec == database::error::integrity) + if (ec == error::validate6) { fault(ec); return; @@ -303,6 +306,11 @@ void chaser_validate::complete_block(const code& ec, const header_link& link, // neutrino // ---------------------------------------------------------------------------- +// Since neutrino filters must be computed in block order, and require full +// block and previous output deserialization, configuration of this option +// implies either ordered validation or re-deserialization of 115% of the store +// in another chaser or populated block caching in that chaser (which implies a +// controlled/narrow window of concurrent validation). // Returns null_hash if not found, intended for genesis block. hash_digest chaser_validate::get_neutrino(size_t height) const NOEXCEPT diff --git a/src/error.cpp b/src/error.cpp index 81af74d12..71e5fa8ae 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -43,41 +43,52 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { suspended_service, "sacrificed service" }, // blockchain - { branch_error, "branch error" }, { orphan_block, "orphan block" }, { orphan_header, "orphan header" }, { duplicate_block, "duplicate block" }, { duplicate_header, "duplicate header" }, - ////{ validation_bypass, "validation bypass" }, - ////{ confirmation_bypass, "confirmation bypass" }, - /// query - { set_block_unconfirmable, "set_block_unconfirmable" }, - { get_height, "get_height" }, - { get_branch_work, "get_branch_work" }, - { get_is_strong, "get_is_strong" }, - { invalid_branch_point, "invalid_branch_point" }, - { pop_candidate, "pop_candidate" }, - { push_candidate, "push_candidate" }, - { invalid_fork_point, "invalid_fork_point" }, - { get_candidate_chain_state, "get_candidate_chain_state" }, - { get_block, "get_block" }, - { get_unassociated, "get_unassociated" }, - { get_fork_work, "get_fork_work" }, - { to_confirmed, "to_confirmed" }, - { pop_confirmed, "pop_confirmed" }, - { get_block_confirmable, "get_block_confirmable" }, - { get_block_state, "get_block_state" }, - { set_strong, "set_strong" }, - { set_unstrong, "set_unstrong" }, - { set_organized, "set_organized" }, - { set_block_confirmable, "set_block_confirmable" }, - { set_block_valid, "set_block_valid" }, - - /// query composite - { node_confirm, "node_confirm" }, - { node_validate, "node_validate" }, - { node_roll_back, "node_roll_back" } + /// faults + { protocol1, "protocol1" }, + { header1, "header1" }, + { organize1, "organize1" }, + { organize2, "organize2" }, + { organize3, "organize3" }, + { organize4, "organize4" }, + { organize5, "organize5" }, + { organize6, "organize6" }, + { organize7, "organize7" }, + { organize8, "organize8" }, + { organize9, "organize9" }, + { organize10, "organize10" }, + { organize11, "organize11" }, + { organize12, "organize12" }, + { organize13, "organize13" }, + { organize14, "organize14" }, + { organize15, "organize15" }, + { validate1, "validate1" }, + { validate2, "validate2" }, + { validate3, "validate3" }, + { validate4, "validate4" }, + { validate5, "validate5" }, + { validate6, "validate6" }, + { confirm1, "confirm1" }, + { confirm2, "confirm2" }, + { confirm3, "confirm3" }, + { confirm4, "confirm4" }, + { confirm5, "confirm5" }, + { confirm6, "confirm6" }, + { confirm7, "confirm7" }, + { confirm8, "confirm8" }, + { confirm9, "confirm9" }, + { confirm10, "confirm10" }, + { confirm11, "confirm11" }, + { confirm12, "confirm12" }, + { confirm13, "confirm13" }, + { confirm14, "confirm14" }, + { confirm15, "confirm15" }, + { confirm16, "confirm16" }, + { confirm17, "confirm17" } }; DEFINE_ERROR_T_CATEGORY(error, "node", "node code") diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index 1a3604b48..e48b7cc57 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -336,7 +336,7 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, { LOGF("Failure setting block unconfirmable [" << encode_hash(hash) << ":" << height << "] from [" << authority() << "]."); - fault(error::set_block_unconfirmable); + fault(error::protocol1); return false; } diff --git a/test/error.cpp b/test/error.cpp index c81805868..97881dad1 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -121,15 +121,6 @@ BOOST_AUTO_TEST_CASE(error_t__code__suspended_service__true_exected_message) // blockchain -BOOST_AUTO_TEST_CASE(error_t__code__branch_error__true_exected_message) -{ - constexpr auto value = error::branch_error; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "branch error"); -} - BOOST_AUTO_TEST_CASE(error_t__code__orphan_block__true_exected_message) { constexpr auto value = error::orphan_block; @@ -166,242 +157,57 @@ BOOST_AUTO_TEST_CASE(error_t__code__duplicate_header__true_exected_message) BOOST_REQUIRE_EQUAL(ec.message(), "duplicate header"); } -////BOOST_AUTO_TEST_CASE(error_t__code__validation_bypass__true_exected_message) -////{ -//// constexpr auto value = error::validation_bypass; -//// const auto ec = code(value); -//// BOOST_REQUIRE(ec); -//// BOOST_REQUIRE(ec == value); -//// BOOST_REQUIRE_EQUAL(ec.message(), "validation bypass"); -////} -//// -////BOOST_AUTO_TEST_CASE(error_t__code__confirmation_bypass__true_exected_message) -////{ -//// constexpr auto value = error::confirmation_bypass; -//// const auto ec = code(value); -//// BOOST_REQUIRE(ec); -//// BOOST_REQUIRE(ec == value); -//// BOOST_REQUIRE_EQUAL(ec.message(), "confirmation bypass"); -////} - -// query - -BOOST_AUTO_TEST_CASE(error_t__code__set_block_unconfirmable__true_exected_message) -{ - constexpr auto value = error::set_block_unconfirmable; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "set_block_unconfirmable"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__get_height__true_exected_message) -{ - constexpr auto value = error::get_height; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_height"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__get_branch_work__true_exected_message) -{ - constexpr auto value = error::get_branch_work; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_branch_work"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__get_is_strong__true_exected_message) -{ - constexpr auto value = error::get_is_strong; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_is_strong"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_branch_point__true_exected_message) -{ - constexpr auto value = error::invalid_branch_point; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_branch_point"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__pop_candidate__true_exected_message) -{ - constexpr auto value = error::pop_candidate; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "pop_candidate"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__push_candidate__true_exected_message) -{ - constexpr auto value = error::push_candidate; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "push_candidate"); -} +// faults -BOOST_AUTO_TEST_CASE(error_t__code__invalid_fork_point__true_exected_message) +BOOST_AUTO_TEST_CASE(error_t__code__protocol1__true_exected_message) { - constexpr auto value = error::invalid_fork_point; + constexpr auto value = error::protocol1; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_fork_point"); + BOOST_REQUIRE_EQUAL(ec.message(), "protocol1"); } -BOOST_AUTO_TEST_CASE(error_t__code__get_candidate_chain_state__true_exected_message) +BOOST_AUTO_TEST_CASE(error_t__code__header1__true_exected_message) { - constexpr auto value = error::get_candidate_chain_state; + constexpr auto value = error::header1; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_candidate_chain_state"); + BOOST_REQUIRE_EQUAL(ec.message(), "header1"); } -BOOST_AUTO_TEST_CASE(error_t__code__get_block__true_exected_message) +BOOST_AUTO_TEST_CASE(error_t__code__organize1__true_exected_message) { - constexpr auto value = error::get_block; + constexpr auto value = error::organize1; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_block"); + BOOST_REQUIRE_EQUAL(ec.message(), "organize1"); } -BOOST_AUTO_TEST_CASE(error_t__code__get_unassociated__true_exected_message) -{ - constexpr auto value = error::get_unassociated; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_unassociated"); -} +// TODO: organize2-organize15 -BOOST_AUTO_TEST_CASE(error_t__code__get_fork_work__true_exected_message) +BOOST_AUTO_TEST_CASE(error_t__code__validate1__true_exected_message) { - constexpr auto value = error::get_fork_work; + constexpr auto value = error::validate1; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_fork_work"); + BOOST_REQUIRE_EQUAL(ec.message(), "validate1"); } -BOOST_AUTO_TEST_CASE(error_t__code__to_confirmed__true_exected_message) -{ - constexpr auto value = error::to_confirmed; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "to_confirmed"); -} +// TODO: validate2-validate6 -BOOST_AUTO_TEST_CASE(error_t__code__pop_confirmed__true_exected_message) +BOOST_AUTO_TEST_CASE(error_t__code__confirm1__true_exected_message) { - constexpr auto value = error::pop_confirmed; + constexpr auto value = error::confirm1; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "pop_confirmed"); + BOOST_REQUIRE_EQUAL(ec.message(), "confirm1"); } -BOOST_AUTO_TEST_CASE(error_t__code__get_block_confirmable__true_exected_message) -{ - constexpr auto value = error::get_block_confirmable; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_block_confirmable"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__get_block_state__true_exected_message) -{ - constexpr auto value = error::get_block_state; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "get_block_state"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__set_strong__true_exected_message) -{ - constexpr auto value = error::set_strong; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "set_strong"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__set_unstrong__true_exected_message) -{ - constexpr auto value = error::set_unstrong; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "set_unstrong"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__set_organized__true_exected_message) -{ - constexpr auto value = error::set_organized; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "set_organized"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__set_block_confirmable__true_exected_message) -{ - constexpr auto value = error::set_block_confirmable; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "set_block_confirmable"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__set_block_valid__true_exected_message) -{ - constexpr auto value = error::set_block_valid; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "set_block_valid"); -} - -// query composite - -BOOST_AUTO_TEST_CASE(error_t__code__node_confirm__true_exected_message) -{ - constexpr auto value = error::node_confirm; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "node_confirm"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__node_validate__true_exected_message) -{ - constexpr auto value = error::node_validate; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "node_validate"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__node_roll_back__true_exected_message) -{ - constexpr auto value = error::node_roll_back; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "node_roll_back"); -} +// TODO: confirm2-confirm17 BOOST_AUTO_TEST_SUITE_END()