diff --git a/rai/core_test/block_store.cpp b/rai/core_test/block_store.cpp index dac1f4be..7b8ba037 100644 --- a/rai/core_test/block_store.cpp +++ b/rai/core_test/block_store.cpp @@ -525,4 +525,19 @@ TEST (block_store, frontier) ASSERT_EQ (account, store.frontier_get (transaction, hash)); store.frontier_del (transaction, hash); ASSERT_TRUE (store.frontier_get (transaction, hash).is_zero ()); +} + +TEST (block_store, block_replace) +{ + bool init (false); + rai::block_store store (init, rai::unique_path ()); + ASSERT_TRUE (!init); + rai::send_block send1 (0, 0, 0, 0, 0, 1); + rai::send_block send2 (0, 0, 0, 0, 0, 2); + rai::transaction transaction (store.environment, nullptr, true); + store.block_put (transaction, 0, send1); + store.block_put (transaction, 0, send2); + auto block3 (store.block_get (transaction, 0)); + ASSERT_NE (nullptr, block3); + ASSERT_EQ (2, block3->block_work ()); } \ No newline at end of file diff --git a/rai/core_test/node.cpp b/rai/core_test/node.cpp index a6e5a7ab..6ecbcfa0 100644 --- a/rai/core_test/node.cpp +++ b/rai/core_test/node.cpp @@ -401,4 +401,40 @@ TEST (node_config, random_rep) rai::node_config config1 (100, logging1); auto rep (config1.random_representative ()); ASSERT_NE (config1.preconfigured_representatives.end (), std::find (config1.preconfigured_representatives.begin (), config1.preconfigured_representatives.end (), rep)); +} + +TEST (node, block_replace) +{ + rai::system system (24000, 2); + system.wallet (0)->insert (rai::test_genesis_key.prv); + ASSERT_FALSE (system.wallet (0)->send_sync (rai::test_genesis_key.pub, 0, 1000)); + std::unique_ptr block1; + { + rai::transaction transaction (system.nodes [0]->store.environment, nullptr, false); + block1 = system.nodes [0]->store.block_get (transaction, system.nodes [0]->ledger.latest (transaction, rai::test_genesis_key.pub)); + } + ASSERT_NE (nullptr, block1); + auto initial_work (block1->block_work ()); + while (rai::work_value (block1->root (), block1->block_work ()) <= rai::work_value (block1->root (), initial_work)) + { + system.work.generate (*block1); + } + system.nodes [1]->network.republish_block (block1->clone (), 0); + auto iterations1 (0); + std::unique_ptr block2; + while (block2 == nullptr) + { + system.poll (); + ++iterations1; + ASSERT_LT (iterations1, 200); + rai::transaction transaction (system.nodes [0]->store.environment, nullptr, false); + auto block (system.nodes [0]->store.block_get (transaction, system.nodes [0]->ledger.latest (transaction, rai::test_genesis_key.pub))); + if (block->block_work () != initial_work) + { + block2 = std::move (block); + } + } + ASSERT_NE (initial_work, block1->block_work ()); + ASSERT_EQ (block1->block_work (), block2->block_work ()); + ASSERT_GT (rai::work_value (block2->root (), block2->block_work ()), rai::work_value (block1->root (), initial_work)); } \ No newline at end of file diff --git a/rai/node.cpp b/rai/node.cpp index 07ae04e2..75f9cc11 100644 --- a/rai/node.cpp +++ b/rai/node.cpp @@ -2309,6 +2309,24 @@ rai::process_return rai::node::process_receive (rai::block const & block_a) } case rai::process_result::old: { + { + auto root (block_a.root ()); + auto hash (block_a.hash ()); + rai::transaction transaction (store.environment, nullptr, true); + auto existing (store.block_get (transaction, hash)); + if (existing != nullptr) + { + // Replace block with one that has higher work value + if (rai::work_value (root, block_a.block_work ()) > rai::work_value (root, existing->block_work ())) + { + store.block_put (transaction, hash, block_a); + } + } + else + { + // Could have been rolled back, maybe + } + } if (config.logging.ledger_duplicate_logging ()) { BOOST_LOG (log) << boost::str (boost::format ("Old for: %1%") % block_a.hash ().to_string ()); diff --git a/rai/secure.cpp b/rai/secure.cpp index d8c84be3..ceb26b53 100644 --- a/rai/secure.cpp +++ b/rai/secure.cpp @@ -564,7 +564,7 @@ void rai::serialize_block (rai::stream & stream_a, rai::block const & block_a) block_a.serialize (stream_a); } -bool rai::work_validate (rai::block_hash const & root_a, uint64_t work_a) +uint64_t rai::work_value (rai::block_hash const & root_a, uint64_t work_a) { uint64_t result; blake2b_state hash; @@ -572,7 +572,13 @@ bool rai::work_validate (rai::block_hash const & root_a, uint64_t work_a) blake2b_update (&hash, reinterpret_cast (&work_a), sizeof (work_a)); blake2b_update (&hash, root_a.bytes.data (), root_a.bytes.size ()); blake2b_final (&hash, reinterpret_cast (&result), sizeof (result)); - return result < rai::block::publish_threshold; + return result; +} + +bool rai::work_validate (rai::block_hash const & root_a, uint64_t work_a) +{ + auto result (rai::work_value (root_a, work_a) < rai::block::publish_threshold); + return result; } bool rai::work_validate (rai::block & block_a) diff --git a/rai/secure.hpp b/rai/secure.hpp index 49f06f76..5d872d90 100644 --- a/rai/secure.hpp +++ b/rai/secure.hpp @@ -96,6 +96,7 @@ std::unique_ptr deserialize_block (rai::stream &); std::unique_ptr deserialize_block (rai::stream &, rai::block_type); std::unique_ptr deserialize_block_json (boost::property_tree::ptree const &); void serialize_block (rai::stream &, rai::block const &); +uint64_t work_value (rai::block_hash const &, uint64_t); bool work_validate (rai::block &); bool work_validate (rai::block_hash const &, uint64_t); class send_hashables