From 376a80d3b4219d6f209eee4991e4fd2042c5da05 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Wed, 7 Nov 2018 15:07:56 -0600 Subject: [PATCH] Block uniquer (#1358) * Changing references to shared_ptr instead of unique_ptr in preparation for global uniquing. * More returning of shared_ptr instead of unique_ptr. * Removing unused declaration. * Converting more unique_ptr to shared_ptr * Removing unique_ptr cruft from when we handled blocks by unique_ptr instead of shared_ptr. * Adding block-uniquing class to reduce memory usage for blocks. * Using block_uniquer when deserializing messages. * Adding rai::block function full_hash which produces a hash covering every member of a block, including parts that are not represented in the block hash, such as the signature or block work. * Creating vote_uniquer class to save memory storing votes. * Hooking up vote uniquer. * Performing block-uniquing on local copy instead of a potentially shared copy at the end of the function. --- rai/core_test/block.cpp | 49 +++++++++++- rai/core_test/conflicts.cpp | 85 +++++++++++++++++++++ rai/core_test/message.cpp | 4 +- rai/core_test/message_parser.cpp | 22 ++++-- rai/core_test/network.cpp | 26 +++---- rai/core_test/node.cpp | 124 +++++++++++++++---------------- rai/lib/blocks.cpp | 83 +++++++++++++++++++-- rai/lib/blocks.hpp | 23 +++++- rai/node/bootstrap.cpp | 8 +- rai/node/bootstrap.hpp | 4 +- rai/node/common.cpp | 38 ++++++---- rai/node/common.hpp | 31 ++++---- rai/node/lmdb.cpp | 10 +-- rai/node/lmdb.hpp | 6 +- rai/node/node.cpp | 9 ++- rai/node/node.hpp | 5 +- rai/node/rpc.cpp | 8 +- rai/rai_node/entry.cpp | 2 +- rai/secure/blockstore.hpp | 4 +- rai/secure/common.cpp | 85 ++++++++++++++++++--- rai/secure/common.hpp | 25 ++++++- rai/secure/ledger.cpp | 10 +-- rai/secure/ledger.hpp | 4 +- 23 files changed, 492 insertions(+), 173 deletions(-) diff --git a/rai/core_test/block.cpp b/rai/core_test/block.cpp index 40e3e98f..51e3709b 100644 --- a/rai/core_test/block.cpp +++ b/rai/core_test/block.cpp @@ -294,8 +294,8 @@ TEST (block, publish_req_serialization) { rai::keypair key1; rai::keypair key2; - auto block (std::unique_ptr (new rai::send_block (0, key2.pub, 200, rai::keypair ().prv, 2, 3))); - rai::publish req (std::move (block)); + auto block (std::make_shared (0, key2.pub, 200, rai::keypair ().prv, 2, 3)); + rai::publish req (block); std::vector bytes; { rai::vectorstream stream (bytes); @@ -315,8 +315,8 @@ TEST (block, confirm_req_serialization) { rai::keypair key1; rai::keypair key2; - auto block (std::unique_ptr (new rai::send_block (0, key2.pub, 200, rai::keypair ().prv, 2, 3))); - rai::confirm_req req (std::move (block)); + auto block (std::make_shared (0, key2.pub, 200, rai::keypair ().prv, 2, 3)); + rai::confirm_req req (block); std::vector bytes; { rai::vectorstream stream (bytes); @@ -409,3 +409,44 @@ TEST (state_block, hashing) block.hashables.link.bytes[0] ^= 0x1; ASSERT_EQ (hash, block.hash ()); } + +TEST (block_uniquer, null) +{ + rai::block_uniquer uniquer; + ASSERT_EQ (nullptr, uniquer.unique (nullptr)); +} + +TEST (block_uniquer, single) +{ + rai::keypair key; + auto block1 (std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0)); + auto block2 (std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0)); + std::weak_ptr block3 (block2); + ASSERT_NE (nullptr, block3.lock ()); + rai::block_uniquer uniquer; + auto block4 (uniquer.unique (block1)); + ASSERT_EQ (block1, block4); + auto block5 (uniquer.unique (block2)); + ASSERT_EQ (block1, block5); + block2.reset (); + ASSERT_EQ (nullptr, block3.lock ()); +} + +TEST (block_uniquer, cleanup) +{ + rai::keypair key; + auto block1 (std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0)); + auto block2 (std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 1)); + rai::block_uniquer uniquer; + auto block3 (uniquer.unique (block1)); + auto block4 (uniquer.unique (block2)); + block2.reset (); + block4.reset (); + ASSERT_EQ (2, uniquer.size ()); + auto iterations (0); + while (uniquer.size () == 2) + { + auto block5 (uniquer.unique (block1)); + ASSERT_LT (iterations++, 200); + } +} diff --git a/rai/core_test/conflicts.cpp b/rai/core_test/conflicts.cpp index 2819d2d2..71eb6335 100644 --- a/rai/core_test/conflicts.cpp +++ b/rai/core_test/conflicts.cpp @@ -60,3 +60,88 @@ TEST (conflicts, add_two) node1.active.start (send2); ASSERT_EQ (2, node1.active.roots.size ()); } + +TEST (vote_uniquer, null) +{ + rai::block_uniquer block_uniquer; + rai::vote_uniquer uniquer (block_uniquer); + ASSERT_EQ (nullptr, uniquer.unique (nullptr)); +} + +// Show that an identical vote can be uniqued +TEST (vote_uniquer, same_vote) +{ + rai::block_uniquer block_uniquer; + rai::vote_uniquer uniquer (block_uniquer); + rai::keypair key; + auto vote1 (std::make_shared (key.pub, key.prv, 0, std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0))); + auto vote2 (std::make_shared (key.pub, key.prv, 0, std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0))); + ASSERT_EQ (vote1, uniquer.unique (vote1)); + ASSERT_EQ (vote1, uniquer.unique (vote2)); +} + +// Show that a different vote for the same block will have the block uniqued +TEST (vote_uniquer, same_block) +{ + rai::block_uniquer block_uniquer; + rai::vote_uniquer uniquer (block_uniquer); + rai::keypair key1; + rai::keypair key2; + auto vote1 (std::make_shared (key1.pub, key1.prv, 0, std::make_shared (0, 0, 0, 0, 0, key1.prv, key1.pub, 0))); + auto vote2 (std::make_shared (key2.pub, key2.prv, 0, std::make_shared (0, 0, 0, 0, 0, key1.prv, key1.pub, 0))); + ASSERT_EQ (vote1, uniquer.unique (vote1)); + ASSERT_EQ (vote2, uniquer.unique (vote2)); + ASSERT_NE (vote1, vote2); + ASSERT_EQ (boost::get> (vote1->blocks[0]), boost::get> (vote2->blocks[0])); +} + +TEST (vote_uniquer, vbh_one) +{ + rai::block_uniquer block_uniquer; + rai::vote_uniquer uniquer (block_uniquer); + rai::keypair key; + auto block (std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0)); + std::vector hashes; + hashes.push_back (block->hash ()); + auto vote1 (std::make_shared (key.pub, key.prv, 0, hashes)); + auto vote2 (std::make_shared (key.pub, key.prv, 0, hashes)); + ASSERT_EQ (vote1, uniquer.unique (vote1)); + ASSERT_EQ (vote1, uniquer.unique (vote2)); +} + +TEST (vote_uniquer, vbh_two) +{ + rai::block_uniquer block_uniquer; + rai::vote_uniquer uniquer (block_uniquer); + rai::keypair key; + auto block1 (std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0)); + std::vector hashes1; + hashes1.push_back (block1->hash ()); + auto block2 (std::make_shared (1, 0, 0, 0, 0, key.prv, key.pub, 0)); + std::vector hashes2; + hashes2.push_back (block2->hash ()); + auto vote1 (std::make_shared (key.pub, key.prv, 0, hashes1)); + auto vote2 (std::make_shared (key.pub, key.prv, 0, hashes2)); + ASSERT_EQ (vote1, uniquer.unique (vote1)); + ASSERT_EQ (vote2, uniquer.unique (vote2)); +} + +TEST (vote_uniquer, cleanup) +{ + rai::block_uniquer block_uniquer; + rai::vote_uniquer uniquer (block_uniquer); + rai::keypair key; + auto vote1 (std::make_shared (key.pub, key.prv, 0, std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0))); + auto vote2 (std::make_shared (key.pub, key.prv, 1, std::make_shared (0, 0, 0, 0, 0, key.prv, key.pub, 0))); + auto vote3 (uniquer.unique (vote1)); + auto vote4 (uniquer.unique (vote2)); + vote2.reset (); + vote4.reset (); + ASSERT_EQ (2, uniquer.size ()); + auto iterations (0); + while (uniquer.size () == 2) + { + auto vote5 (uniquer.unique (vote1)); + ASSERT_LT (iterations++, 200); + } +} diff --git a/rai/core_test/message.cpp b/rai/core_test/message.cpp index eef489cd..83fcc070 100644 --- a/rai/core_test/message.cpp +++ b/rai/core_test/message.cpp @@ -40,7 +40,7 @@ TEST (message, keepalive_deserialize) TEST (message, publish_serialization) { - rai::publish publish (std::unique_ptr (new rai::send_block (0, 1, 2, rai::keypair ().prv, 4, 5))); + rai::publish publish (std::make_shared (0, 1, 2, rai::keypair ().prv, 4, 5)); ASSERT_EQ (rai::block_type::send, publish.header.block_type ()); ASSERT_FALSE (publish.header.ipv4_only ()); publish.header.ipv4_only_set (true); @@ -72,7 +72,7 @@ TEST (message, publish_serialization) TEST (message, confirm_ack_serialization) { rai::keypair key1; - auto vote (std::make_shared (key1.pub, key1.prv, 0, std::unique_ptr (new rai::send_block (0, 1, 2, key1.prv, 4, 5)))); + auto vote (std::make_shared (key1.pub, key1.prv, 0, std::make_shared (0, 1, 2, key1.prv, 4, 5))); rai::confirm_ack con1 (vote); std::vector bytes; { diff --git a/rai/core_test/message_parser.cpp b/rai/core_test/message_parser.cpp index e5ae0db6..67575bb2 100644 --- a/rai/core_test/message_parser.cpp +++ b/rai/core_test/message_parser.cpp @@ -75,8 +75,10 @@ TEST (message_parser, exact_confirm_ack_size) { rai::system system (24000, 1); test_visitor visitor; - rai::message_parser parser (visitor, system.work); - auto block (std::unique_ptr (new rai::send_block (1, 1, 2, rai::keypair ().prv, 4, system.work.generate (1)))); + rai::block_uniquer block_uniquer; + rai::vote_uniquer vote_uniquer (block_uniquer); + rai::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + auto block (std::make_shared (1, 1, 2, rai::keypair ().prv, 4, system.work.generate (1))); auto vote (std::make_shared (0, rai::keypair ().prv, 0, std::move (block))); rai::confirm_ack message (vote); std::vector bytes; @@ -106,8 +108,10 @@ TEST (message_parser, exact_confirm_req_size) { rai::system system (24000, 1); test_visitor visitor; - rai::message_parser parser (visitor, system.work); - auto block (std::unique_ptr (new rai::send_block (1, 1, 2, rai::keypair ().prv, 4, system.work.generate (1)))); + rai::block_uniquer block_uniquer; + rai::vote_uniquer vote_uniquer (block_uniquer); + rai::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + auto block (std::make_shared (1, 1, 2, rai::keypair ().prv, 4, system.work.generate (1))); rai::confirm_req message (std::move (block)); std::vector bytes; { @@ -136,8 +140,10 @@ TEST (message_parser, exact_publish_size) { rai::system system (24000, 1); test_visitor visitor; - rai::message_parser parser (visitor, system.work); - auto block (std::unique_ptr (new rai::send_block (1, 1, 2, rai::keypair ().prv, 4, system.work.generate (1)))); + rai::block_uniquer block_uniquer; + rai::vote_uniquer vote_uniquer (block_uniquer); + rai::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + auto block (std::make_shared (1, 1, 2, rai::keypair ().prv, 4, system.work.generate (1))); rai::publish message (std::move (block)); std::vector bytes; { @@ -166,7 +172,9 @@ TEST (message_parser, exact_keepalive_size) { rai::system system (24000, 1); test_visitor visitor; - rai::message_parser parser (visitor, system.work); + rai::block_uniquer block_uniquer; + rai::vote_uniquer vote_uniquer (block_uniquer); + rai::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); rai::keepalive message; std::vector bytes; { diff --git a/rai/core_test/network.cpp b/rai/core_test/network.cpp index 05ef9249..09aea0da 100644 --- a/rai/core_test/network.cpp +++ b/rai/core_test/network.cpp @@ -201,7 +201,7 @@ TEST (network, send_valid_confirm_ack) rai::block_hash latest1 (system.nodes[0]->latest (rai::test_genesis_key.pub)); rai::send_block block2 (latest1, key2.pub, 50, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (latest1)); rai::block_hash latest2 (system.nodes[1]->latest (rai::test_genesis_key.pub)); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (block2))); + system.nodes[0]->process_active (std::make_shared (block2)); system.deadline_set (10s); // Keep polling until latest block changes while (system.nodes[1]->latest (rai::test_genesis_key.pub) == latest2) @@ -224,7 +224,7 @@ TEST (network, send_valid_publish) rai::send_block block2 (latest1, key2.pub, 50, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (latest1)); auto hash2 (block2.hash ()); rai::block_hash latest2 (system.nodes[1]->latest (rai::test_genesis_key.pub)); - system.nodes[1]->process_active (std::unique_ptr (new rai::send_block (block2))); + system.nodes[1]->process_active (std::make_shared (block2)); system.deadline_set (10s); while (system.nodes[0]->stats.count (rai::stat::type::message, rai::stat::detail::publish, rai::stat::dir::in) == 0) { @@ -242,7 +242,7 @@ TEST (network, send_valid_publish) TEST (network, send_insufficient_work) { rai::system system (24000, 2); - std::unique_ptr block (new rai::send_block (0, 1, 20, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0)); + auto block (std::make_shared (0, 1, 20, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0)); rai::publish publish (std::move (block)); std::shared_ptr> bytes (new std::vector); { @@ -584,8 +584,8 @@ TEST (bootstrap_processor, process_state) rai::genesis genesis; system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); auto node0 (system.nodes[0]); - std::unique_ptr block1 (new rai::state_block (rai::test_genesis_key.pub, node0->latest (rai::test_genesis_key.pub), rai::test_genesis_key.pub, rai::genesis_amount - 100, rai::test_genesis_key.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0)); - std::unique_ptr block2 (new rai::state_block (rai::test_genesis_key.pub, block1->hash (), rai::test_genesis_key.pub, rai::genesis_amount, block1->hash (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0)); + auto block1 (std::make_shared (rai::test_genesis_key.pub, node0->latest (rai::test_genesis_key.pub), rai::test_genesis_key.pub, rai::genesis_amount - 100, rai::test_genesis_key.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0)); + auto block2 (std::make_shared (rai::test_genesis_key.pub, block1->hash (), rai::test_genesis_key.pub, rai::genesis_amount, block1->hash (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0)); node0->work_generate_blocking (*block1); node0->work_generate_blocking (*block2); node0->process (*block1); @@ -636,13 +636,13 @@ TEST (bootstrap_processor, pull_diamond) { rai::system system (24000, 1); rai::keypair key; - std::unique_ptr send1 (new rai::send_block (system.nodes[0]->latest (rai::test_genesis_key.pub), key.pub, 0, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (system.nodes[0]->latest (rai::test_genesis_key.pub)))); + auto send1 (std::make_shared (system.nodes[0]->latest (rai::test_genesis_key.pub), key.pub, 0, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (system.nodes[0]->latest (rai::test_genesis_key.pub)))); ASSERT_EQ (rai::process_result::progress, system.nodes[0]->process (*send1).code); - std::unique_ptr open (new rai::open_block (send1->hash (), 1, key.pub, key.prv, key.pub, system.work.generate (key.pub))); + auto open (std::make_shared (send1->hash (), 1, key.pub, key.prv, key.pub, system.work.generate (key.pub))); ASSERT_EQ (rai::process_result::progress, system.nodes[0]->process (*open).code); - std::unique_ptr send2 (new rai::send_block (open->hash (), rai::test_genesis_key.pub, std::numeric_limits::max () - 100, key.prv, key.pub, system.work.generate (open->hash ()))); + auto send2 (std::make_shared (open->hash (), rai::test_genesis_key.pub, std::numeric_limits::max () - 100, key.prv, key.pub, system.work.generate (open->hash ()))); ASSERT_EQ (rai::process_result::progress, system.nodes[0]->process (*send2).code); - std::unique_ptr receive (new rai::receive_block (send1->hash (), send2->hash (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (send1->hash ()))); + auto receive (std::make_shared (send1->hash (), send2->hash (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (send1->hash ()))); ASSERT_EQ (rai::process_result::progress, system.nodes[0]->process (*receive).code); rai::node_init init1; auto node1 (std::make_shared (init1, system.service, 24002, rai::unique_path (), system.alarm, system.logging, system.work)); @@ -667,13 +667,13 @@ TEST (bootstrap_processor, push_diamond) auto wallet1 (node1->wallets.create (100)); wallet1->insert_adhoc (rai::test_genesis_key.prv); wallet1->insert_adhoc (key.prv); - std::unique_ptr send1 (new rai::send_block (system.nodes[0]->latest (rai::test_genesis_key.pub), key.pub, 0, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (system.nodes[0]->latest (rai::test_genesis_key.pub)))); + auto send1 (std::make_shared (system.nodes[0]->latest (rai::test_genesis_key.pub), key.pub, 0, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (system.nodes[0]->latest (rai::test_genesis_key.pub)))); ASSERT_EQ (rai::process_result::progress, node1->process (*send1).code); - std::unique_ptr open (new rai::open_block (send1->hash (), 1, key.pub, key.prv, key.pub, system.work.generate (key.pub))); + auto open (std::make_shared (send1->hash (), 1, key.pub, key.prv, key.pub, system.work.generate (key.pub))); ASSERT_EQ (rai::process_result::progress, node1->process (*open).code); - std::unique_ptr send2 (new rai::send_block (open->hash (), rai::test_genesis_key.pub, std::numeric_limits::max () - 100, key.prv, key.pub, system.work.generate (open->hash ()))); + auto send2 (std::make_shared (open->hash (), rai::test_genesis_key.pub, std::numeric_limits::max () - 100, key.prv, key.pub, system.work.generate (open->hash ()))); ASSERT_EQ (rai::process_result::progress, node1->process (*send2).code); - std::unique_ptr receive (new rai::receive_block (send1->hash (), send2->hash (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (send1->hash ()))); + auto receive (std::make_shared (send1->hash (), send2->hash (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (send1->hash ()))); ASSERT_EQ (rai::process_result::progress, node1->process (*receive).code); node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ()); system.deadline_set (10s); diff --git a/rai/core_test/node.cpp b/rai/core_test/node.cpp index 848bfe6b..5b1459c5 100644 --- a/rai/core_test/node.cpp +++ b/rai/core_test/node.cpp @@ -148,9 +148,9 @@ TEST (node, send_out_of_order) rai::send_block send1 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); rai::send_block send2 (send1.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (send1.hash ())); rai::send_block send3 (send2.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 3, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (send2.hash ())); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send3))); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send2))); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send1))); + system.nodes[0]->process_active (std::make_shared (send3)); + system.nodes[0]->process_active (std::make_shared (send2)); + system.nodes[0]->process_active (std::make_shared (send1)); system.deadline_set (10s); while (std::any_of (system.nodes.begin (), system.nodes.end (), [&](std::shared_ptr const & node_a) { return node_a->balance (rai::test_genesis_key.pub) != rai::genesis_amount - system.nodes[0]->config.receive_minimum.number () * 3; })) { @@ -1658,29 +1658,29 @@ TEST (node, vote_republish) rai::keypair key2; system.wallet (1)->insert_adhoc (key2.prv); rai::genesis genesis; - rai::send_block send1 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - rai::send_block send2 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send1))); + auto send1 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + auto send2 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + system.nodes[0]->process_active (send1); system.deadline_set (5s); - while (!system.nodes[1]->block (send1.hash ())) + while (!system.nodes[1]->block (send1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - system.nodes[0]->active.publish (std::unique_ptr (new rai::send_block (send2))); - auto vote (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, std::unique_ptr (new rai::send_block (send2)))); - ASSERT_TRUE (system.nodes[0]->active.active (send1)); - ASSERT_TRUE (system.nodes[1]->active.active (send1)); + system.nodes[0]->active.publish (send2); + auto vote (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, send2)); + ASSERT_TRUE (system.nodes[0]->active.active (*send1)); + ASSERT_TRUE (system.nodes[1]->active.active (*send1)); system.nodes[0]->vote_processor.vote (vote, system.nodes[0]->network.endpoint ()); - while (!system.nodes[0]->block (send2.hash ())) + while (!system.nodes[0]->block (send2->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - while (!system.nodes[1]->block (send2.hash ())) + while (!system.nodes[1]->block (send2->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (system.nodes[0]->block (send1.hash ())); - ASSERT_FALSE (system.nodes[1]->block (send1.hash ())); + ASSERT_FALSE (system.nodes[0]->block (send1->hash ())); + ASSERT_FALSE (system.nodes[1]->block (send1->hash ())); system.deadline_set (5s); while (system.nodes[1]->balance (key2.pub) != system.nodes[0]->config.receive_minimum.number () * 2) { @@ -1698,31 +1698,31 @@ TEST (node, vote_by_hash_republish) rai::keypair key2; system.wallet (1)->insert_adhoc (key2.prv); rai::genesis genesis; - rai::send_block send1 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - rai::send_block send2 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send1))); + auto send1 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + auto send2 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + system.nodes[0]->process_active (send1); system.deadline_set (5s); - while (!system.nodes[1]->block (send1.hash ())) + while (!system.nodes[1]->block (send1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - system.nodes[0]->active.publish (std::unique_ptr (new rai::send_block (send2))); + system.nodes[0]->active.publish (send2); std::vector vote_blocks; - vote_blocks.push_back (send2.hash ()); + vote_blocks.push_back (send2->hash ()); auto vote (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, vote_blocks)); - ASSERT_TRUE (system.nodes[0]->active.active (send1)); - ASSERT_TRUE (system.nodes[1]->active.active (send1)); + ASSERT_TRUE (system.nodes[0]->active.active (*send1)); + ASSERT_TRUE (system.nodes[1]->active.active (*send1)); system.nodes[0]->vote_processor.vote (vote, system.nodes[0]->network.endpoint ()); - while (!system.nodes[0]->block (send2.hash ())) + while (!system.nodes[0]->block (send2->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - while (!system.nodes[1]->block (send2.hash ())) + while (!system.nodes[1]->block (send2->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (system.nodes[0]->block (send1.hash ())); - ASSERT_FALSE (system.nodes[1]->block (send1.hash ())); + ASSERT_FALSE (system.nodes[0]->block (send1->hash ())); + ASSERT_FALSE (system.nodes[1]->block (send1->hash ())); system.deadline_set (5s); while (system.nodes[1]->balance (key2.pub) != system.nodes[0]->config.receive_minimum.number () * 2) { @@ -1743,31 +1743,31 @@ TEST (node, vote_by_hash_epoch_block_republish) system.nodes[0]->ledger.epoch_signer = epoch_signer.pub; system.nodes[1]->ledger.epoch_signer = epoch_signer.pub; rai::genesis genesis; - rai::send_block send1 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - rai::state_block epoch1 (rai::genesis_account, genesis.hash (), rai::genesis_account, rai::genesis_amount, system.nodes[0]->ledger.epoch_link, epoch_signer.prv, epoch_signer.pub, system.work.generate (genesis.hash ())); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send1))); + auto send1 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + auto epoch1 (std::make_shared (rai::genesis_account, genesis.hash (), rai::genesis_account, rai::genesis_amount, system.nodes[0]->ledger.epoch_link, epoch_signer.prv, epoch_signer.pub, system.work.generate (genesis.hash ()))); + system.nodes[0]->process_active (send1); system.deadline_set (5s); - while (!system.nodes[1]->block (send1.hash ())) + while (!system.nodes[1]->block (send1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - system.nodes[0]->active.publish (std::unique_ptr (new rai::state_block (epoch1))); + system.nodes[0]->active.publish (epoch1); std::vector vote_blocks; - vote_blocks.push_back (epoch1.hash ()); + vote_blocks.push_back (epoch1->hash ()); auto vote (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, vote_blocks)); - ASSERT_TRUE (system.nodes[0]->active.active (send1)); - ASSERT_TRUE (system.nodes[1]->active.active (send1)); + ASSERT_TRUE (system.nodes[0]->active.active (*send1)); + ASSERT_TRUE (system.nodes[1]->active.active (*send1)); system.nodes[0]->vote_processor.vote (vote, system.nodes[0]->network.endpoint ()); - while (!system.nodes[0]->block (epoch1.hash ())) + while (!system.nodes[0]->block (epoch1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - while (!system.nodes[1]->block (epoch1.hash ())) + while (!system.nodes[1]->block (epoch1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (system.nodes[0]->block (send1.hash ())); - ASSERT_FALSE (system.nodes[1]->block (send1.hash ())); + ASSERT_FALSE (system.nodes[0]->block (send1->hash ())); + ASSERT_FALSE (system.nodes[1]->block (send1->hash ())); } TEST (node, fork_invalid_block_signature) @@ -1775,30 +1775,30 @@ TEST (node, fork_invalid_block_signature) rai::system system (24000, 2); rai::keypair key2; rai::genesis genesis; - rai::send_block send1 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - rai::send_block send2 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - rai::send_block send2_corrupt (send2); - send2_corrupt.signature = rai::signature (123); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send1))); + auto send1 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + auto send2 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + auto send2_corrupt (std::make_shared (*send2)); + send2_corrupt->signature = rai::signature (123); + system.nodes[0]->process_active (send1); system.deadline_set (5s); - while (!system.nodes[0]->block (send1.hash ())) + while (!system.nodes[0]->block (send1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - auto vote (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, std::make_unique (send2))); - auto vote_corrupt (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, std::make_unique (send2_corrupt))); + auto vote (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, send2)); + auto vote_corrupt (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, send2_corrupt)); system.nodes[1]->network.republish_vote (vote_corrupt); ASSERT_NO_ERROR (system.poll ()); system.nodes[1]->network.republish_vote (vote); - while (system.nodes[0]->block (send1.hash ())) + while (system.nodes[0]->block (send1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - while (!system.nodes[0]->block (send2.hash ())) + while (!system.nodes[0]->block (send2->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (system.nodes[0]->block (send2.hash ())->block_signature (), send2.block_signature ()); + ASSERT_EQ (system.nodes[0]->block (send2->hash ())->block_signature (), send2->block_signature ()); } TEST (node, fork_invalid_block_signature_vote_by_hash) @@ -1806,35 +1806,35 @@ TEST (node, fork_invalid_block_signature_vote_by_hash) rai::system system (24000, 1); rai::keypair key2; rai::genesis genesis; - rai::send_block send1 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - rai::send_block send2 (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ())); - rai::send_block send2_corrupt (send2); - send2_corrupt.signature = rai::signature (123); - system.nodes[0]->process_active (std::unique_ptr (new rai::send_block (send1))); + auto send1 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + auto send2 (std::make_shared (genesis.hash (), key2.pub, std::numeric_limits::max () - system.nodes[0]->config.receive_minimum.number () * 2, rai::test_genesis_key.prv, rai::test_genesis_key.pub, system.work.generate (genesis.hash ()))); + auto send2_corrupt (std::make_shared (*send2)); + send2_corrupt->signature = rai::signature (123); + system.nodes[0]->process_active (send1); system.deadline_set (5s); - while (!system.nodes[0]->block (send1.hash ())) + while (!system.nodes[0]->block (send1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - system.nodes[0]->active.publish (std::make_unique (send2_corrupt)); + system.nodes[0]->active.publish (send2_corrupt); ASSERT_NO_ERROR (system.poll ()); - system.nodes[0]->active.publish (std::make_unique (send2)); + system.nodes[0]->active.publish (send2); std::vector vote_blocks; - vote_blocks.push_back (send2.hash ()); + vote_blocks.push_back (send2->hash ()); auto vote (std::make_shared (rai::test_genesis_key.pub, rai::test_genesis_key.prv, 0, vote_blocks)); { auto transaction (system.nodes[0]->store.tx_begin_read ()); system.nodes[0]->vote_processor.vote_blocking (transaction, vote, system.nodes[0]->network.endpoint ()); } - while (system.nodes[0]->block (send1.hash ())) + while (system.nodes[0]->block (send1->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - while (!system.nodes[0]->block (send2.hash ())) + while (!system.nodes[0]->block (send2->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (system.nodes[0]->block (send2.hash ())->block_signature (), send2.block_signature ()); + ASSERT_EQ (system.nodes[0]->block (send2->hash ())->block_signature (), send2->block_signature ()); } TEST (node, block_processor_signatures) diff --git a/rai/lib/blocks.cpp b/rai/lib/blocks.cpp index 206c77ac..601c4bb2 100644 --- a/rai/lib/blocks.cpp +++ b/rai/lib/blocks.cpp @@ -2,6 +2,8 @@ #include +#include + /** Compare blocks, first by type, then content. This is an optimization over dynamic_cast, which is very slow on some platforms. */ namespace { @@ -69,6 +71,20 @@ rai::block_hash rai::block::hash () const return result; } +rai::block_hash rai::block::full_hash () const +{ + rai::block_hash result; + blake2b_state state; + blake2b_init (&state, sizeof (result.bytes)); + blake2b_update (&state, hash ().bytes.data (), sizeof (hash ())); + auto signature (block_signature ()); + blake2b_update (&state, signature.bytes.data (), sizeof (signature)); + auto work (block_work ()); + blake2b_update (&state, &work, sizeof (work)); + blake2b_final (&state, result.bytes.data (), sizeof (result.bytes)); + return result; +} + void rai::send_block::visit (rai::block_visitor & visitor_a) const { visitor_a.send_block (*this); @@ -1145,9 +1161,9 @@ void rai::state_block::signature_set (rai::uint512_union const & signature_a) signature = signature_a; } -std::unique_ptr rai::deserialize_block_json (boost::property_tree::ptree const & tree_a) +std::shared_ptr rai::deserialize_block_json (boost::property_tree::ptree const & tree_a, rai::block_uniquer * uniquer_a) { - std::unique_ptr result; + std::shared_ptr result; try { auto type (tree_a.get ("type")); @@ -1200,14 +1216,18 @@ std::unique_ptr rai::deserialize_block_json (boost::property_tree::p catch (std::runtime_error const &) { } + if (uniquer_a != nullptr) + { + result = uniquer_a->unique (result); + } return result; } -std::unique_ptr rai::deserialize_block (rai::stream & stream_a) +std::shared_ptr rai::deserialize_block (rai::stream & stream_a, rai::block_uniquer * uniquer_a) { rai::block_type type; auto error (read (stream_a, type)); - std::unique_ptr result; + std::shared_ptr result; if (!error) { result = rai::deserialize_block (stream_a, type); @@ -1215,9 +1235,9 @@ std::unique_ptr rai::deserialize_block (rai::stream & stream_a) return result; } -std::unique_ptr rai::deserialize_block (rai::stream & stream_a, rai::block_type type_a) +std::shared_ptr rai::deserialize_block (rai::stream & stream_a, rai::block_type type_a, rai::block_uniquer * uniquer_a) { - std::unique_ptr result; + std::shared_ptr result; switch (type_a) { case rai::block_type::receive: @@ -1274,6 +1294,10 @@ std::unique_ptr rai::deserialize_block (rai::stream & stream_a, rai: assert (false); break; } + if (uniquer_a != nullptr) + { + result = uniquer_a->unique (result); + } return result; } @@ -1523,3 +1547,50 @@ void rai::receive_hashables::hash (blake2b_state & hash_a) const blake2b_update (&hash_a, previous.bytes.data (), sizeof (previous.bytes)); blake2b_update (&hash_a, source.bytes.data (), sizeof (source.bytes)); } + +std::shared_ptr rai::block_uniquer::unique (std::shared_ptr block_a) +{ + auto result (block_a); + if (result != nullptr) + { + rai::uint256_union key (block_a->full_hash ()); + std::lock_guard lock (mutex); + auto & existing (blocks[key]); + if (auto block_l = existing.lock ()) + { + result = block_l; + } + else + { + existing = block_a; + } + for (auto i (0); i < cleanup_count; ++i) + { + rai::uint256_union random; + rai::random_pool.GenerateBlock (random.bytes.data (), random.bytes.size ()); + auto existing (blocks.find (random)); + if (existing == blocks.end ()) + { + existing = blocks.begin (); + } + if (existing != blocks.end ()) + { + if (auto block_l = existing->second.lock ()) + { + // Still live + } + else + { + blocks.erase (existing); + } + } + } + } + return result; +} + +size_t rai::block_uniquer::size () +{ + std::lock_guard lock (mutex); + return blocks.size (); +} diff --git a/rai/lib/blocks.hpp b/rai/lib/blocks.hpp index 27e1f7a8..ab5374b4 100644 --- a/rai/lib/blocks.hpp +++ b/rai/lib/blocks.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace rai { @@ -44,6 +45,8 @@ class block public: // Return a digest of the hashables in this block. rai::block_hash hash () const; + // Return a digest of hashables and non-hashables in this block. + rai::block_hash full_hash () const; std::string to_json (); virtual void hash (blake2b_state &) const = 0; virtual uint64_t block_work () const = 0; @@ -302,8 +305,22 @@ public: virtual void state_block (rai::state_block const &) = 0; virtual ~block_visitor () = default; }; -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 &); +/** + * This class serves to find and return unique variants of a block in order to minimize memory usage + */ +class block_uniquer +{ +public: + std::shared_ptr unique (std::shared_ptr); + size_t size (); + +private: + std::mutex mutex; + std::unordered_map> blocks; + static unsigned constexpr cleanup_count = 2; +}; +std::shared_ptr deserialize_block (rai::stream &, rai::block_uniquer * = nullptr); +std::shared_ptr deserialize_block (rai::stream &, rai::block_type, rai::block_uniquer * = nullptr); +std::shared_ptr deserialize_block_json (boost::property_tree::ptree const &, rai::block_uniquer * = nullptr); void serialize_block (rai::stream &, rai::block const &); } diff --git a/rai/node/bootstrap.cpp b/rai/node/bootstrap.cpp index 7b4e7598..c834987b 100644 --- a/rai/node/bootstrap.cpp +++ b/rai/node/bootstrap.cpp @@ -604,7 +604,7 @@ void rai::bulk_push_client::start () void rai::bulk_push_client::push (rai::transaction const & transaction_a) { - std::unique_ptr block; + std::shared_ptr block; bool finished (false); while (block == nullptr && !finished) { @@ -1652,7 +1652,7 @@ void rai::bulk_pull_server::set_current_end () void rai::bulk_pull_server::send_next () { - std::unique_ptr block (get_next ()); + auto block (get_next ()); if (block != nullptr) { { @@ -1675,9 +1675,9 @@ void rai::bulk_pull_server::send_next () } } -std::unique_ptr rai::bulk_pull_server::get_next () +std::shared_ptr rai::bulk_pull_server::get_next () { - std::unique_ptr result; + std::shared_ptr result; bool send_current = false, set_current_to_end = false; /* diff --git a/rai/node/bootstrap.hpp b/rai/node/bootstrap.hpp index 5937d3bb..c6981ccd 100644 --- a/rai/node/bootstrap.hpp +++ b/rai/node/bootstrap.hpp @@ -238,7 +238,7 @@ class bulk_pull_server : public std::enable_shared_from_this const &, std::unique_ptr); void set_current_end (); - std::unique_ptr get_next (); + std::shared_ptr get_next (); void send_next (); void sent_action (boost::system::error_code const &, size_t); void send_finished (); @@ -276,7 +276,7 @@ class bulk_pull_blocks_server : public std::enable_shared_from_this const &, std::unique_ptr); void set_params (); - std::unique_ptr get_next (); + std::shared_ptr get_next (); void send_next (); void send_finished (); void no_block_sent (boost::system::error_code const &, size_t); diff --git a/rai/node/common.cpp b/rai/node/common.cpp index 4efb4c80..5132137b 100644 --- a/rai/node/common.cpp +++ b/rai/node/common.cpp @@ -146,7 +146,9 @@ std::string rai::message_parser::status_string () return "[unknown parse_status]"; } -rai::message_parser::message_parser (rai::message_visitor & visitor_a, rai::work_pool & pool_a) : +rai::message_parser::message_parser (rai::block_uniquer & block_uniquer_a, rai::vote_uniquer & vote_uniquer_a, rai::message_visitor & visitor_a, rai::work_pool & pool_a) : +block_uniquer (block_uniquer_a), +vote_uniquer (vote_uniquer_a), visitor (visitor_a), pool (pool_a), status (parse_status::success) @@ -237,7 +239,7 @@ void rai::message_parser::deserialize_keepalive (rai::stream & stream_a, rai::me void rai::message_parser::deserialize_publish (rai::stream & stream_a, rai::message_header const & header_a) { auto error (false); - rai::publish incoming (error, stream_a, header_a); + rai::publish incoming (error, stream_a, header_a, &block_uniquer); if (!error && at_end (stream_a)) { if (!rai::work_validate (*incoming.block)) @@ -258,7 +260,7 @@ void rai::message_parser::deserialize_publish (rai::stream & stream_a, rai::mess void rai::message_parser::deserialize_confirm_req (rai::stream & stream_a, rai::message_header const & header_a) { auto error (false); - rai::confirm_req incoming (error, stream_a, header_a); + rai::confirm_req incoming (error, stream_a, header_a, &block_uniquer); if (!error && at_end (stream_a)) { if (!rai::work_validate (*incoming.block)) @@ -279,7 +281,7 @@ void rai::message_parser::deserialize_confirm_req (rai::stream & stream_a, rai:: void rai::message_parser::deserialize_confirm_ack (rai::stream & stream_a, rai::message_header const & header_a) { auto error (false); - rai::confirm_ack incoming (error, stream_a, header_a); + rai::confirm_ack incoming (error, stream_a, header_a, &vote_uniquer); if (!error && at_end (stream_a)) { for (auto & vote_block : incoming.vote->blocks) @@ -387,12 +389,12 @@ bool rai::keepalive::operator== (rai::keepalive const & other_a) const return peers == other_a.peers; } -rai::publish::publish (bool & error_a, rai::stream & stream_a, rai::message_header const & header_a) : +rai::publish::publish (bool & error_a, rai::stream & stream_a, rai::message_header const & header_a, rai::block_uniquer * uniquer_a) : message (header_a) { if (!error_a) { - error_a = deserialize (stream_a); + error_a = deserialize (stream_a, uniquer_a); } } @@ -403,10 +405,10 @@ block (block_a) header.block_type_set (block->type ()); } -bool rai::publish::deserialize (rai::stream & stream_a) +bool rai::publish::deserialize (rai::stream & stream_a, rai::block_uniquer * uniquer_a) { assert (header.type == rai::message_type::publish); - block = rai::deserialize_block (stream_a, header.block_type ()); + block = rai::deserialize_block (stream_a, header.block_type (), uniquer_a); auto result (block == nullptr); return result; } @@ -428,12 +430,12 @@ bool rai::publish::operator== (rai::publish const & other_a) const return *block == *other_a.block; } -rai::confirm_req::confirm_req (bool & error_a, rai::stream & stream_a, rai::message_header const & header_a) : +rai::confirm_req::confirm_req (bool & error_a, rai::stream & stream_a, rai::message_header const & header_a, rai::block_uniquer * uniquer_a) : message (header_a) { if (!error_a) { - error_a = deserialize (stream_a); + error_a = deserialize (stream_a, uniquer_a); } } @@ -444,10 +446,10 @@ block (block_a) header.block_type_set (block->type ()); } -bool rai::confirm_req::deserialize (rai::stream & stream_a) +bool rai::confirm_req::deserialize (rai::stream & stream_a, rai::block_uniquer * uniquer_a) { assert (header.type == rai::message_type::confirm_req); - block = rai::deserialize_block (stream_a, header.block_type ()); + block = rai::deserialize_block (stream_a, header.block_type (), uniquer_a); auto result (block == nullptr); return result; } @@ -469,10 +471,14 @@ bool rai::confirm_req::operator== (rai::confirm_req const & other_a) const return *block == *other_a.block; } -rai::confirm_ack::confirm_ack (bool & error_a, rai::stream & stream_a, rai::message_header const & header_a) : +rai::confirm_ack::confirm_ack (bool & error_a, rai::stream & stream_a, rai::message_header const & header_a, rai::vote_uniquer * uniquer_a) : message (header_a), vote (std::make_shared (error_a, stream_a, header.block_type ())) { + if (uniquer_a) + { + vote = uniquer_a->unique (vote); + } } rai::confirm_ack::confirm_ack (std::shared_ptr vote_a) : @@ -490,10 +496,14 @@ vote (vote_a) } } -bool rai::confirm_ack::deserialize (rai::stream & stream_a) +bool rai::confirm_ack::deserialize (rai::stream & stream_a, rai::vote_uniquer * uniquer_a) { assert (header.type == rai::message_type::confirm_ack); auto result (vote->deserialize (stream_a)); + if (uniquer_a) + { + vote = uniquer_a->unique (vote); + } return result; } diff --git a/rai/node/common.hpp b/rai/node/common.hpp index 892848b1..932a20ae 100644 --- a/rai/node/common.hpp +++ b/rai/node/common.hpp @@ -191,7 +191,6 @@ public: message (rai::message_header const &); virtual ~message () = default; virtual void serialize (rai::stream &) = 0; - virtual bool deserialize (rai::stream &) = 0; virtual void visit (rai::message_visitor &) const = 0; rai::message_header header; }; @@ -214,7 +213,7 @@ public: invalid_magic, invalid_network }; - message_parser (rai::message_visitor &, rai::work_pool &); + message_parser (rai::block_uniquer &, rai::vote_uniquer &, rai::message_visitor &, rai::work_pool &); void deserialize_buffer (uint8_t const *, size_t); void deserialize_keepalive (rai::stream &, rai::message_header const &); void deserialize_publish (rai::stream &, rai::message_header const &); @@ -222,6 +221,8 @@ public: void deserialize_confirm_ack (rai::stream &, rai::message_header const &); void deserialize_node_id_handshake (rai::stream &, rai::message_header const &); bool at_end (rai::stream &); + rai::block_uniquer & block_uniquer; + rai::vote_uniquer & vote_uniquer; rai::message_visitor & visitor; rai::work_pool & pool; parse_status status; @@ -234,7 +235,7 @@ public: keepalive (bool &, rai::stream &, rai::message_header const &); keepalive (); void visit (rai::message_visitor &) const override; - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &); void serialize (rai::stream &) override; bool operator== (rai::keepalive const &) const; std::array peers; @@ -242,10 +243,10 @@ public: class publish : public message { public: - publish (bool &, rai::stream &, rai::message_header const &); + publish (bool &, rai::stream &, rai::message_header const &, rai::block_uniquer * = nullptr); publish (std::shared_ptr); void visit (rai::message_visitor &) const override; - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &, rai::block_uniquer * = nullptr); void serialize (rai::stream &) override; bool operator== (rai::publish const &) const; std::shared_ptr block; @@ -253,9 +254,9 @@ public: class confirm_req : public message { public: - confirm_req (bool &, rai::stream &, rai::message_header const &); + confirm_req (bool &, rai::stream &, rai::message_header const &, rai::block_uniquer * = nullptr); confirm_req (std::shared_ptr); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &, rai::block_uniquer * = nullptr); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; bool operator== (rai::confirm_req const &) const; @@ -264,9 +265,9 @@ public: class confirm_ack : public message { public: - confirm_ack (bool &, rai::stream &, rai::message_header const &); + confirm_ack (bool &, rai::stream &, rai::message_header const &, rai::vote_uniquer * = nullptr); confirm_ack (std::shared_ptr); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &, rai::vote_uniquer * = nullptr); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; bool operator== (rai::confirm_ack const &) const; @@ -277,7 +278,7 @@ class frontier_req : public message public: frontier_req (); frontier_req (bool &, rai::stream &, rai::message_header const &); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; bool operator== (rai::frontier_req const &) const; @@ -290,7 +291,7 @@ class bulk_pull : public message public: bulk_pull (); bulk_pull (bool &, rai::stream &, rai::message_header const &); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; rai::uint256_union start; @@ -301,7 +302,7 @@ class bulk_pull_account : public message public: bulk_pull_account (); bulk_pull_account (bool &, rai::stream &, rai::message_header const &); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; rai::uint256_union account; @@ -313,7 +314,7 @@ class bulk_pull_blocks : public message public: bulk_pull_blocks (); bulk_pull_blocks (bool &, rai::stream &, rai::message_header const &); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; rai::block_hash min_hash; @@ -326,7 +327,7 @@ class bulk_push : public message public: bulk_push (); bulk_push (rai::message_header const &); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; }; @@ -335,7 +336,7 @@ class node_id_handshake : public message public: node_id_handshake (bool &, rai::stream &, rai::message_header const &); node_id_handshake (boost::optional, boost::optional>); - bool deserialize (rai::stream &) override; + bool deserialize (rai::stream &); void serialize (rai::stream &) override; void visit (rai::message_visitor &) const override; bool operator== (rai::node_id_handshake const &) const; diff --git a/rai/node/lmdb.cpp b/rai/node/lmdb.cpp index 3e298faf..5dfdfe45 100644 --- a/rai/node/lmdb.cpp +++ b/rai/node/lmdb.cpp @@ -1226,7 +1226,7 @@ MDB_val rai::mdb_store::block_raw_get (rai::transaction const & transaction_a, r } template -std::unique_ptr rai::mdb_store::block_random (rai::transaction const & transaction_a, MDB_dbi database) +std::shared_ptr rai::mdb_store::block_random (rai::transaction const & transaction_a, MDB_dbi database) { rai::block_hash hash; rai::random_pool.GenerateBlock (hash.bytes.data (), hash.bytes.size ()); @@ -1240,11 +1240,11 @@ std::unique_ptr rai::mdb_store::block_random (rai::transaction const return block_get (transaction_a, rai::block_hash (existing->first)); } -std::unique_ptr rai::mdb_store::block_random (rai::transaction const & transaction_a) +std::shared_ptr rai::mdb_store::block_random (rai::transaction const & transaction_a) { auto count (block_count (transaction_a)); auto region (rai::random_pool.GenerateWord32 (0, count.sum () - 1)); - std::unique_ptr result; + std::shared_ptr result; if (region < count.send) { result = block_random (transaction_a, send_blocks); @@ -1315,11 +1315,11 @@ void rai::mdb_store::block_successor_clear (rai::transaction const & transaction block_put (transaction_a, hash_a, *block, 0, version); } -std::unique_ptr rai::mdb_store::block_get (rai::transaction const & transaction_a, rai::block_hash const & hash_a) +std::shared_ptr rai::mdb_store::block_get (rai::transaction const & transaction_a, rai::block_hash const & hash_a) { rai::block_type type; auto value (block_raw_get (transaction_a, hash_a, type)); - std::unique_ptr result; + std::shared_ptr result; if (value.mv_size != 0) { rai::bufferstream stream (reinterpret_cast (value.mv_data), value.mv_size); diff --git a/rai/node/lmdb.hpp b/rai/node/lmdb.hpp index 4d743a02..66b8ad81 100644 --- a/rai/node/lmdb.hpp +++ b/rai/node/lmdb.hpp @@ -155,8 +155,8 @@ public: void block_put (rai::transaction const &, rai::block_hash const &, rai::block const &, rai::block_hash const & = rai::block_hash (0), rai::epoch version = rai::epoch::epoch_0) override; rai::block_hash block_successor (rai::transaction const &, rai::block_hash const &) override; void block_successor_clear (rai::transaction const &, rai::block_hash const &) override; - std::unique_ptr block_get (rai::transaction const &, rai::block_hash const &) override; - std::unique_ptr block_random (rai::transaction const &) override; + std::shared_ptr block_get (rai::transaction const &, rai::block_hash const &) override; + std::shared_ptr block_random (rai::transaction const &) override; void block_del (rai::transaction const &, rai::block_hash const &) override; bool block_exists (rai::transaction const &, rai::block_hash const &) override; rai::block_counts block_count (rai::transaction const &) override; @@ -367,7 +367,7 @@ public: private: MDB_dbi block_database (rai::block_type, rai::epoch); template - std::unique_ptr block_random (rai::transaction const &, MDB_dbi); + std::shared_ptr block_random (rai::transaction const &, MDB_dbi); MDB_val block_raw_get (rai::transaction const &, rai::block_hash const &, rai::block_type &); void block_raw_put (rai::transaction const &, MDB_dbi, rai::block_hash const &, MDB_val); void clear (MDB_dbi); diff --git a/rai/node/node.cpp b/rai/node/node.cpp index bf267285..dbf59cfe 100644 --- a/rai/node/node.cpp +++ b/rai/node/node.cpp @@ -608,7 +608,7 @@ void rai::network::receive_action (rai::udp_data * data_a) if (!rai::reserved_address (data_a->endpoint, false) && data_a->endpoint != endpoint ()) { network_message_visitor visitor (node, data_a->endpoint); - rai::message_parser parser (visitor, node.work); + rai::message_parser parser (node.block_uniquer, node.vote_uniquer, visitor, node.work); parser.deserialize_buffer (data_a->buffer, data_a->size); if (parser.status != rai::message_parser::parse_status::success) { @@ -1287,7 +1287,8 @@ block_processor_thread ([this]() { this->block_processor.process_blocks (); }), online_reps (*this), -stats (config.stat_config) +stats (config.stat_config), +vote_uniquer (block_uniquer) { wallets.observer = [this](bool active) { observers.wallet.notify (active); @@ -1725,7 +1726,7 @@ rai::uint128_t rai::node::balance (rai::account const & account_a) return ledger.account_balance (transaction, account_a); } -std::unique_ptr rai::node::block (rai::block_hash const & hash_a) +std::shared_ptr rai::node::block (rai::block_hash const & hash_a) { auto transaction (store.tx_begin_read ()); return store.block_get (transaction, hash_a); @@ -2869,7 +2870,7 @@ void rai::active_transactions::announce_votes (std::unique_lock & lo if there are less than 100 active elections */ if (i->announcements % announcement_long == 1 && roots_size < 100) { - std::unique_ptr previous (nullptr); + std::shared_ptr previous; auto previous_hash (election_l->status.winner->previous ()); if (!previous_hash.is_zero ()) { diff --git a/rai/node/node.hpp b/rai/node/node.hpp index 3f676612..93f4fc7d 100644 --- a/rai/node/node.hpp +++ b/rai/node/node.hpp @@ -297,7 +297,6 @@ public: static unsigned const broadcast_interval_ms = (rai::rai_network == rai::rai_networks::rai_test_network) ? 10 : 50; void republish_block_batch (std::deque>, unsigned = broadcast_interval_ms); void republish (rai::block_hash const &, std::shared_ptr>, rai::endpoint); - void publish_broadcast (std::vector &, std::unique_ptr); void confirm_send (rai::confirm_ack const &, std::shared_ptr>, rai::endpoint const &); void merge_peers (std::array const &); void send_keepalive (rai::endpoint const &); @@ -425,7 +424,7 @@ public: void keepalive_preconfigured (std::vector const &); rai::block_hash latest (rai::account const &); rai::uint128_t balance (rai::account const &); - std::unique_ptr block (rai::block_hash const &); + std::shared_ptr block (rai::block_hash const &); std::pair balance_pending (rai::account const &); rai::uint128_t weight (rai::account const &); rai::account representative (rai::account const &); @@ -472,6 +471,8 @@ public: rai::online_reps online_reps; rai::stat stats; rai::keypair node_id; + rai::block_uniquer block_uniquer; + rai::vote_uniquer vote_uniquer; static double constexpr price_max = 16.0; static double constexpr free_cutoff = 1024.0; static std::chrono::seconds constexpr period = std::chrono::seconds (60); diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 328a8f18..d88ae019 100644 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -935,7 +935,7 @@ void rai::rpc_handler::blocks_info () if (source) { rai::block_hash source_hash (node.ledger.block_source (transaction, *block)); - std::unique_ptr block_a (node.store.block_get (transaction, source_hash)); + auto block_a (node.store.block_get (transaction, source_hash)); if (block_a != nullptr) { auto source_account (node.ledger.account (transaction, source_hash)); @@ -2649,7 +2649,7 @@ void rai::rpc_handler::republish () if (sources != 0) // Republish source chain { rai::block_hash source (node.ledger.block_source (transaction, *block)); - std::unique_ptr block_a (node.store.block_get (transaction, source)); + auto block_a (node.store.block_get (transaction, source)); std::vector hashes; while (block_a != nullptr && hashes.size () < sources) { @@ -2680,7 +2680,7 @@ void rai::rpc_handler::republish () if (!node.store.pending_exists (transaction, rai::pending_key (destination, hash))) { rai::block_hash previous (node.ledger.latest (transaction, destination)); - std::unique_ptr block_d (node.store.block_get (transaction, previous)); + auto block_d (node.store.block_get (transaction, previous)); rai::block_hash source; std::vector hashes; while (block_d != nullptr && hash != source) @@ -3426,7 +3426,7 @@ void rai::rpc_handler::wallet_republish () { rai::account account (i->first); auto latest (node.ledger.latest (transaction, account)); - std::unique_ptr block; + std::shared_ptr block; std::vector hashes; while (!latest.is_zero () && hashes.size () < count) { diff --git a/rai/rai_node/entry.cpp b/rai/rai_node/entry.cpp index f78c90b6..a4ae5f1e 100644 --- a/rai/rai_node/entry.cpp +++ b/rai/rai_node/entry.cpp @@ -139,7 +139,7 @@ int main (int argc, char * const * argv) { rai::account_info info (i->second); rai::block_hash rep_block (node.node->ledger.representative_calculated (transaction, info.head)); - std::unique_ptr block (node.node->store.block_get (transaction, rep_block)); + auto block (node.node->store.block_get (transaction, rep_block)); calculated[block->representative ()] += info.balance.number (); } total = 0; diff --git a/rai/secure/blockstore.hpp b/rai/secure/blockstore.hpp index 58495511..79a7c996 100644 --- a/rai/secure/blockstore.hpp +++ b/rai/secure/blockstore.hpp @@ -166,8 +166,8 @@ public: virtual void block_put (rai::transaction const &, rai::block_hash const &, rai::block const &, rai::block_hash const & = rai::block_hash (0), rai::epoch version = rai::epoch::epoch_0) = 0; virtual rai::block_hash block_successor (rai::transaction const &, rai::block_hash const &) = 0; virtual void block_successor_clear (rai::transaction const &, rai::block_hash const &) = 0; - virtual std::unique_ptr block_get (rai::transaction const &, rai::block_hash const &) = 0; - virtual std::unique_ptr block_random (rai::transaction const &) = 0; + virtual std::shared_ptr block_get (rai::transaction const &, rai::block_hash const &) = 0; + virtual std::shared_ptr block_random (rai::transaction const &) = 0; virtual void block_del (rai::transaction const &, rai::block_hash const &) = 0; virtual bool block_exists (rai::transaction const &, rai::block_hash const &) = 0; virtual rai::block_counts block_count (rai::transaction const &) = 0; diff --git a/rai/secure/common.cpp b/rai/secure/common.cpp index 579396fc..2b72a33d 100644 --- a/rai/secure/common.cpp +++ b/rai/secure/common.cpp @@ -404,12 +404,12 @@ signature (other_a.signature) { } -rai::vote::vote (bool & error_a, rai::stream & stream_a) +rai::vote::vote (bool & error_a, rai::stream & stream_a, rai::block_uniquer * uniquer_a) { - error_a = deserialize (stream_a); + error_a = deserialize (stream_a, uniquer_a); } -rai::vote::vote (bool & error_a, rai::stream & stream_a, rai::block_type type_a) +rai::vote::vote (bool & error_a, rai::stream & stream_a, rai::block_type type_a, rai::block_uniquer * uniquer_a) { if (!error_a) { @@ -435,7 +435,7 @@ rai::vote::vote (bool & error_a, rai::stream & stream_a, rai::block_type type_a) } else { - std::shared_ptr block (rai::deserialize_block (stream_a, type_a)); + std::shared_ptr block (rai::deserialize_block (stream_a, type_a, uniquer_a)); error_a = block == nullptr; if (!error_a) { @@ -511,6 +511,18 @@ rai::uint256_union rai::vote::hash () const return result; } +rai::uint256_union rai::vote::full_hash () const +{ + rai::uint256_union result; + blake2b_state state; + blake2b_init (&state, sizeof (result.bytes)); + blake2b_update (&state, hash ().bytes.data (), sizeof (hash ().bytes)); + blake2b_update (&state, account.bytes.data (), sizeof (account.bytes.data ())); + blake2b_update (&state, signature.bytes.data (), sizeof (signature.bytes.data ())); + blake2b_final (&state, result.bytes.data (), sizeof (result.bytes)); + return result; +} + void rai::vote::serialize (rai::stream & stream_a, rai::block_type type) { write (stream_a, account); @@ -556,7 +568,7 @@ void rai::vote::serialize (rai::stream & stream_a) } } -bool rai::vote::deserialize (rai::stream & stream_a) +bool rai::vote::deserialize (rai::stream & stream_a, rai::block_uniquer * uniquer_a) { auto result (read (stream_a, account)); if (!result) @@ -591,7 +603,7 @@ bool rai::vote::deserialize (rai::stream & stream_a) } else { - std::shared_ptr block (rai::deserialize_block (stream_a, type)); + std::shared_ptr block (rai::deserialize_block (stream_a, type, uniquer_a)); result = block == nullptr; if (!result) { @@ -636,14 +648,69 @@ boost::transform_iterator (blocks.end (), rai::iterate_vote_blocks_as_hash ()); } +rai::vote_uniquer::vote_uniquer (rai::block_uniquer & uniquer_a) : +uniquer (uniquer_a) +{ +} + +std::shared_ptr rai::vote_uniquer::unique (std::shared_ptr vote_a) +{ + auto result (vote_a); + if (result != nullptr) + { + if (!result->blocks[0].which ()) + { + result->blocks[0] = uniquer.unique (boost::get> (result->blocks[0])); + } + rai::uint256_union key (vote_a->full_hash ()); + std::lock_guard lock (mutex); + auto & existing (votes[key]); + if (auto block_l = existing.lock ()) + { + result = block_l; + } + else + { + existing = vote_a; + } + for (auto i (0); i < cleanup_count; ++i) + { + rai::uint256_union random; + rai::random_pool.GenerateBlock (random.bytes.data (), random.bytes.size ()); + auto existing (votes.find (random)); + if (existing == votes.end ()) + { + existing = votes.begin (); + } + if (existing != votes.end ()) + { + if (auto block_l = existing->second.lock ()) + { + // Still live + } + else + { + votes.erase (existing); + } + } + } + } + return result; +} + +size_t rai::vote_uniquer::size () +{ + std::lock_guard lock (mutex); + return votes.size (); +} + rai::genesis::genesis () { boost::property_tree::ptree tree; std::stringstream istream (rai::genesis_block); boost::property_tree::read_json (istream, tree); - auto block (rai::deserialize_block_json (tree)); - assert (dynamic_cast (block.get ()) != nullptr); - open.reset (static_cast (block.release ())); + open = rai::deserialize_block_json (tree); + assert (open != nullptr); } rai::block_hash rai::genesis::hash () const diff --git a/rai/secure/common.hpp b/rai/secure/common.hpp index 076b3b28..7d37a85f 100644 --- a/rai/secure/common.hpp +++ b/rai/secure/common.hpp @@ -146,17 +146,18 @@ class vote public: vote () = default; vote (rai::vote const &); - vote (bool &, rai::stream &); - vote (bool &, rai::stream &, rai::block_type); + vote (bool &, rai::stream &, rai::block_uniquer * = nullptr); + vote (bool &, rai::stream &, rai::block_type, rai::block_uniquer * = nullptr); vote (rai::account const &, rai::raw_key const &, uint64_t, std::shared_ptr); vote (rai::account const &, rai::raw_key const &, uint64_t, std::vector); std::string hashes_string () const; rai::uint256_union hash () const; + rai::uint256_union full_hash () const; bool operator== (rai::vote const &) const; bool operator!= (rai::vote const &) const; void serialize (rai::stream &, rai::block_type); void serialize (rai::stream &); - bool deserialize (rai::stream &); + bool deserialize (rai::stream &, rai::block_uniquer * = nullptr); bool validate (); boost::transform_iterator begin () const; boost::transform_iterator end () const; @@ -171,6 +172,22 @@ public: rai::signature signature; static const std::string hash_prefix; }; +/** + * This class serves to find and return unique variants of a vote in order to minimize memory usage + */ +class vote_uniquer +{ +public: + vote_uniquer (rai::block_uniquer &); + std::shared_ptr unique (std::shared_ptr); + size_t size (); + +private: + rai::block_uniquer & uniquer; + std::mutex mutex; + std::unordered_map> votes; + static unsigned constexpr cleanup_count = 2; +}; enum class vote_code { invalid, // Vote is not signed correctly @@ -229,6 +246,6 @@ class genesis public: explicit genesis (); rai::block_hash hash () const; - std::unique_ptr open; + std::shared_ptr open; }; } diff --git a/rai/secure/ledger.cpp b/rai/secure/ledger.cpp index 681e841f..78e050e3 100644 --- a/rai/secure/ledger.cpp +++ b/rai/secure/ledger.cpp @@ -798,7 +798,7 @@ rai::account rai::ledger::account (rai::transaction const & transaction_a, rai:: auto hash (hash_a); rai::block_hash successor (1); rai::block_info block_info; - std::unique_ptr block (store.block_get (transaction_a, hash)); + auto block (store.block_get (transaction_a, hash)); while (!successor.is_zero () && block->type () != rai::block_type::state && store.block_info_get (transaction_a, successor, block_info)) { successor = store.block_successor (transaction_a, hash); @@ -981,7 +981,7 @@ void rai::ledger::change_latest (rai::transaction const & transaction_a, rai::ac } } -std::unique_ptr rai::ledger::successor (rai::transaction const & transaction_a, rai::uint256_union const & root_a) +std::shared_ptr rai::ledger::successor (rai::transaction const & transaction_a, rai::uint256_union const & root_a) { rai::block_hash successor (0); if (store.account_exists (transaction_a, root_a)) @@ -995,7 +995,7 @@ std::unique_ptr rai::ledger::successor (rai::transaction const & tra { successor = store.block_successor (transaction_a, root_a); } - std::unique_ptr result; + std::shared_ptr result; if (!successor.is_zero ()) { result = store.block_get (transaction_a, successor); @@ -1004,12 +1004,12 @@ std::unique_ptr rai::ledger::successor (rai::transaction const & tra return result; } -std::unique_ptr rai::ledger::forked_block (rai::transaction const & transaction_a, rai::block const & block_a) +std::shared_ptr rai::ledger::forked_block (rai::transaction const & transaction_a, rai::block const & block_a) { assert (!store.block_exists (transaction_a, block_a.hash ())); auto root (block_a.root ()); assert (store.block_exists (transaction_a, root) || store.account_exists (transaction_a, root)); - std::unique_ptr result (store.block_get (transaction_a, store.block_successor (transaction_a, root))); + auto result (store.block_get (transaction_a, store.block_successor (transaction_a, root))); if (result == nullptr) { rai::account_info info; diff --git a/rai/secure/ledger.hpp b/rai/secure/ledger.hpp index 7563d3b0..13e253b2 100644 --- a/rai/secure/ledger.hpp +++ b/rai/secure/ledger.hpp @@ -25,8 +25,8 @@ public: rai::uint128_t account_balance (rai::transaction const &, rai::account const &); rai::uint128_t account_pending (rai::transaction const &, rai::account const &); rai::uint128_t weight (rai::transaction const &, rai::account const &); - std::unique_ptr successor (rai::transaction const &, rai::block_hash const &); - std::unique_ptr forked_block (rai::transaction const &, rai::block const &); + std::shared_ptr successor (rai::transaction const &, rai::block_hash const &); + std::shared_ptr forked_block (rai::transaction const &, rai::block const &); rai::block_hash latest (rai::transaction const &, rai::account const &); rai::block_hash latest_root (rai::transaction const &, rai::account const &); rai::block_hash representative (rai::transaction const &, rai::block_hash const &);