Skip to content

Commit

Permalink
Use adjusted difficulty for trees in active transactions (#1810)
Browse files Browse the repository at this point in the history
* Use adjusted difficulty for trees in active transactions

* add adjusted difficulty calculation
* make list of dependent blocks in election

* Adjust difficulty for dependent on deleted blocks elections

* Potential overflow check

* Using reference to items in remaining_blocks

* correct words

* Replace push_back with emplace_back for deque of pairs
  • Loading branch information
SergiySW authored Mar 21, 2019
1 parent 930bcc2 commit 5670252
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 5 deletions.
96 changes: 96 additions & 0 deletions nano/core_test/conflicts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,99 @@ TEST (conflicts, reprioritize)
ASSERT_EQ (difficulty2, existing2->difficulty);
}
}

TEST (conflicts, dependency)
{
nano::system system (24000, 1);
auto & node1 (*system.nodes[0]);
nano::genesis genesis;
nano::keypair key1;
auto send1 (std::make_shared<nano::send_block> (genesis.hash (), key1.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
node1.work_generate_blocking (*send1);
ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code);
ASSERT_EQ (0, node1.active.size ());
node1.active.start (genesis.open);
node1.active.start (send1);
ASSERT_EQ (2, node1.active.size ());
// Check dependecy for genesis block
{
std::lock_guard<std::mutex> guard (node1.active.mutex);
auto existing1 (node1.active.roots.find (nano::uint512_union (genesis.open->previous (), genesis.open->root ())));
ASSERT_NE (node1.active.roots.end (), existing1);
auto election1 (existing1->election);
ASSERT_NE (nullptr, election1);
ASSERT_EQ (1, election1->dependent_blocks.size ());
ASSERT_NE (election1->dependent_blocks.end (), election1->dependent_blocks.find (send1->hash ()));
}
}

TEST (conflicts, adjusted_difficulty)
{
nano::system system (24000, 1);
auto & node1 (*system.nodes[0]);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
nano::keypair key3;
ASSERT_EQ (0, node1.active.size ());
node1.active.start (genesis.open);
auto send1 (std::make_shared<nano::send_block> (genesis.hash (), key1.pub, nano::genesis_amount - 2 * nano::xrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (genesis.hash ())));
node1.process_active (send1);
auto send2 (std::make_shared<nano::send_block> (send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 3 * nano::xrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send1->hash ())));
node1.process_active (send2);
auto receive1 (std::make_shared<nano::receive_block> (send2->hash (), send2->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send2->hash ())));
node1.process_active (receive1);
auto open1 (std::make_shared<nano::open_block> (send1->hash (), key1.pub, key1.pub, key1.prv, key1.pub, system.work.generate (key1.pub)));
node1.process_active (open1);
auto send3 (std::make_shared<nano::state_block> (key1.pub, open1->hash (), key1.pub, nano::xrb_ratio, key2.pub, key1.prv, key1.pub, system.work.generate (open1->hash ())));
node1.process_active (send3);
auto send4 (std::make_shared<nano::state_block> (key1.pub, send3->hash (), key1.pub, 0, key3.pub, key1.prv, key1.pub, system.work.generate (send3->hash ())));
node1.process_active (send4);
ASSERT_EQ (node1.ledger.epoch_signer, nano::test_genesis_key.pub);
auto open_epoch1 (std::make_shared<nano::state_block> (key2.pub, 0, 0, 0, node1.ledger.epoch_link, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (key2.pub)));
node1.process_active (open_epoch1);
auto receive2 (std::make_shared<nano::state_block> (key2.pub, open_epoch1->hash (), 0, nano::xrb_ratio, send3->hash (), key2.prv, key2.pub, system.work.generate (open_epoch1->hash ())));
node1.process_active (receive2);
auto open2 (std::make_shared<nano::state_block> (key3.pub, 0, key3.pub, nano::xrb_ratio, send4->hash (), key3.prv, key3.pub, system.work.generate (key3.pub)));
node1.process_active (open2);
auto change1 (std::make_shared<nano::state_block> (key3.pub, open2->hash (), nano::test_genesis_key.pub, nano::xrb_ratio, 0, key3.prv, key3.pub, system.work.generate (open2->hash ())));
node1.process_active (change1);
node1.block_processor.flush ();
ASSERT_EQ (11, node1.active.size ());
std::unordered_map<nano::block_hash, uint64_t> adjusted_difficulties;
{
std::lock_guard<std::mutex> guard (node1.active.mutex);
ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), genesis.hash ());
for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i)
{
adjusted_difficulties.insert (std::make_pair (i->election->status.winner->hash (), i->adjusted_difficulty));
}
}
// genesis
ASSERT_GT (adjusted_difficulties.find (genesis.hash ())->second, adjusted_difficulties.find (send1->hash ())->second);
ASSERT_GT (adjusted_difficulties.find (send1->hash ())->second, adjusted_difficulties.find (send2->hash ())->second);
ASSERT_GT (adjusted_difficulties.find (send2->hash ())->second, adjusted_difficulties.find (receive1->hash ())->second);
// key1
ASSERT_GT (adjusted_difficulties.find (send1->hash ())->second, adjusted_difficulties.find (open1->hash ())->second);
ASSERT_GT (adjusted_difficulties.find (open1->hash ())->second, adjusted_difficulties.find (send3->hash ())->second);
ASSERT_GT (adjusted_difficulties.find (send3->hash ())->second, adjusted_difficulties.find (send4->hash ())->second);
//key2
ASSERT_GT (adjusted_difficulties.find (send3->hash ())->second, adjusted_difficulties.find (receive2->hash ())->second);
ASSERT_GT (adjusted_difficulties.find (open_epoch1->hash ())->second, adjusted_difficulties.find (receive2->hash ())->second);
// key3
ASSERT_GT (adjusted_difficulties.find (send4->hash ())->second, adjusted_difficulties.find (open2->hash ())->second);
ASSERT_GT (adjusted_difficulties.find (open2->hash ())->second, adjusted_difficulties.find (change1->hash ())->second);
// Independent elections can have higher difficulty than adjusted tree
nano::keypair key4;
auto open_epoch2 (std::make_shared<nano::state_block> (key4.pub, 0, 0, 0, node1.ledger.epoch_link, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (key4.pub, adjusted_difficulties.find (genesis.hash ())->second)));
uint64_t difficulty;
ASSERT_FALSE (nano::work_validate (*open_epoch2, &difficulty));
ASSERT_GT (difficulty, adjusted_difficulties.find (genesis.hash ())->second);
node1.process_active (open_epoch2);
node1.block_processor.flush ();
ASSERT_EQ (12, node1.active.size ());
{
std::lock_guard<std::mutex> guard (node1.active.mutex);
ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), open_epoch2->hash ());
}
}
135 changes: 131 additions & 4 deletions nano/node/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2478,6 +2478,7 @@ announcements (0)
{
last_votes.insert (std::make_pair (node.network_params.ledger.not_an_account (), nano::vote_info{ std::chrono::steady_clock::now (), 0, block_a->hash () }));
blocks.insert (std::make_pair (block_a->hash (), block_a));
update_dependent ();
}

void nano::election::compute_rep_votes (nano::transaction const & transaction_a)
Expand Down Expand Up @@ -2564,6 +2565,8 @@ void nano::election::confirm_if_quorum (nano::transaction const & transaction_a)
auto node_l (node.shared ());
node_l->block_processor.force (block_l);
status.winner = block_l;
update_dependent ();
node_l->active.adjust_difficulty (block_l->hash ());
}
if (have_quorum (tally_l, sum))
{
Expand Down Expand Up @@ -2727,6 +2730,39 @@ size_t nano::election::last_votes_size ()
return last_votes.size ();
}

void nano::election::update_dependent ()
{
assert (!node.active.mutex.try_lock ());
std::vector<nano::block_hash> blocks_search;
auto hash (status.winner->hash ());
auto previous (status.winner->previous ());
if (!previous.is_zero ())
{
blocks_search.push_back (previous);
}
auto source (status.winner->source ());
if (!source.is_zero () && source != previous)
{
blocks_search.push_back (source);
}
auto link (status.winner->link ());
if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous)
{
blocks_search.push_back (link);
}
for (auto & block_search : blocks_search)
{
auto existing (node.active.blocks.find (block_search));
if (existing != node.active.blocks.end () && !existing->second->confirmed && !existing->second->stopped)
{
if (existing->second->dependent_blocks.find (hash) == existing->second->dependent_blocks.end ())
{
existing->second->dependent_blocks.insert (hash);
}
}
}
}

void nano::active_transactions::request_confirm (std::unique_lock<std::mutex> & lock_a)
{
std::unordered_set<nano::uint512_union> inactive;
Expand Down Expand Up @@ -2795,6 +2831,7 @@ void nano::active_transactions::request_confirm (std::unique_lock<std::mutex> &
}
}
}
election_l->update_dependent ();
}
}
if (election_l->announcements < announcement_long || election_l->announcements % announcement_long == 1)
Expand Down Expand Up @@ -2932,6 +2969,10 @@ void nano::active_transactions::request_confirm (std::unique_lock<std::mutex> &
(void)erased;
assert (erased == 1);
}
for (auto & dependent_block : root_it->election->dependent_blocks)
{
adjust_difficulty (dependent_block);
}
roots.erase (*i);
}
if (unconfirmed_count > 0)
Expand Down Expand Up @@ -2994,8 +3035,9 @@ bool nano::active_transactions::add (std::shared_ptr<nano::block> block_a, std::
uint64_t difficulty (0);
auto error (nano::work_validate (*block_a, &difficulty));
release_assert (!error);
roots.insert (nano::conflict_info{ root, difficulty, election });
roots.insert (nano::conflict_info{ root, difficulty, difficulty, election });
blocks.insert (std::make_pair (block_a->hash (), election));
adjust_difficulty (block_a->hash ());
}
error = existing != roots.end ();
}
Expand Down Expand Up @@ -3061,9 +3103,94 @@ void nano::active_transactions::update_difficulty (nano::block const & block_a)
uint64_t difficulty;
auto error (nano::work_validate (block_a, &difficulty));
assert (!error);
roots.modify (existing, [difficulty](nano::conflict_info & info_a) {
info_a.difficulty = difficulty;
});
if (difficulty > existing->difficulty)
{
roots.modify (existing, [difficulty](nano::conflict_info & info_a) {
info_a.difficulty = difficulty;
});
adjust_difficulty (block_a.hash ());
}
}
}

void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash_a)
{
assert (!mutex.try_lock ());
std::deque<std::pair<nano::block_hash, int64_t>> remaining_blocks;
remaining_blocks.emplace_back (hash_a, 0);
std::unordered_set<nano::block_hash> processed_blocks;
std::vector<std::pair<nano::uint512_union, int64_t>> elections_list;
uint128_t sum (0);
while (!remaining_blocks.empty ())
{
auto const & item (remaining_blocks.front ());
auto hash (item.first);
auto level (item.second);
if (processed_blocks.find (hash) == processed_blocks.end ())
{
auto existing (blocks.find (hash));
if (existing != blocks.end () && !existing->second->confirmed && !existing->second->stopped && existing->second->status.winner->hash () == hash)
{
auto previous (existing->second->status.winner->previous ());
if (!previous.is_zero ())
{
remaining_blocks.emplace_back (previous, level + 1);
}
auto source (existing->second->status.winner->source ());
if (!source.is_zero () && source != previous)
{
remaining_blocks.emplace_back (source, level + 1);
}
auto link (existing->second->status.winner->link ());
if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous)
{
remaining_blocks.emplace_back (link, level + 1);
}
for (auto & dependent_block : existing->second->dependent_blocks)
{
remaining_blocks.emplace_back (dependent_block, level - 1);
}
processed_blocks.insert (hash);
nano::uint512_union root (previous, existing->second->status.winner->root ());
auto existing_root (roots.find (root));
if (existing_root != roots.end ())
{
sum += existing_root->difficulty;
elections_list.emplace_back (root, level);
}
}
}
remaining_blocks.pop_front ();
}
if (elections_list.size () > 1)
{
uint64_t average (static_cast<uint64_t> (sum / elections_list.size ()));
// Potential overflow check
uint64_t divider (1);
if (elections_list.size () > 1000000 && (average - node.network_params.publish_threshold) > elections_list.size ())
{
divider = ((average - node.network_params.publish_threshold) / elections_list.size ()) + 1;
}
// Set adjusted difficulty
for (auto & item : elections_list)
{
auto existing_root (roots.find (item.first));
uint64_t difficulty_a (average + (item.second / divider));
roots.modify (existing_root, [difficulty_a](nano::conflict_info & info_a) {
info_a.adjusted_difficulty = difficulty_a;
});
}
}
// Set adjusted difficulty equals to difficulty
else if (elections_list.size () == 1)
{
auto existing_root (roots.find (elections_list.begin ()->first));
if (existing_root->difficulty != existing_root->adjusted_difficulty)
{
roots.modify (existing_root, [](nano::conflict_info & info_a) {
info_a.adjusted_difficulty = info_a.difficulty;
});
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion nano/node/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class election : public std::enable_shared_from_this<nano::election>
void log_votes (nano::tally_t const &);
bool publish (std::shared_ptr<nano::block> block_a);
size_t last_votes_size ();
void update_dependent ();
void stop ();
nano::node & node;
std::unordered_map<nano::account, nano::vote_info> last_votes;
Expand All @@ -91,12 +92,14 @@ class election : public std::enable_shared_from_this<nano::election>
bool stopped;
std::unordered_map<nano::block_hash, nano::uint128_t> last_tally;
unsigned announcements;
std::unordered_set<nano::block_hash> dependent_blocks;
};
class conflict_info
{
public:
nano::uint512_union root;
uint64_t difficulty;
uint64_t adjusted_difficulty;
std::shared_ptr<nano::election> election;
};
// Core class for determining consensus
Expand All @@ -117,6 +120,7 @@ class active_transactions
// Is the root of this block in the roots container
bool active (nano::block const &);
void update_difficulty (nano::block const &);
void adjust_difficulty (nano::block_hash const &);
std::deque<std::shared_ptr<nano::block>> list_blocks (bool = false);
void erase (nano::block const &);
bool empty ();
Expand All @@ -130,7 +134,7 @@ class active_transactions
boost::multi_index::hashed_unique<
boost::multi_index::member<nano::conflict_info, nano::uint512_union, &nano::conflict_info::root>>,
boost::multi_index::ordered_non_unique<
boost::multi_index::member<nano::conflict_info, uint64_t, &nano::conflict_info::difficulty>,
boost::multi_index::member<nano::conflict_info, uint64_t, &nano::conflict_info::adjusted_difficulty>,
std::greater<uint64_t>>>>
roots;
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> blocks;
Expand Down

0 comments on commit 5670252

Please sign in to comment.