Vote processed event
This commit is contained in:
		
					parent
					
						
							
								0fa894f19d
							
						
					
				
			
			
				commit
				
					
						7d2fbd8026
					
				
			
		
					 8 changed files with 78 additions and 41 deletions
				
			
		|  | @ -365,7 +365,7 @@ TEST (inactive_votes_cache, existing_vote) | |||
| 	ASSERT_EQ (send->hash (), last_vote1.hash); | ||||
| 	ASSERT_EQ (nano::vote::timestamp_min * 1, last_vote1.timestamp); | ||||
| 	// Attempt to change vote with inactive_votes_cache
 | ||||
| 	node.vote_cache.vote (vote1); | ||||
| 	node.vote_cache.insert (vote1); | ||||
| 	auto cached = node.vote_cache.find (send->hash ()); | ||||
| 	ASSERT_EQ (1, cached.size ()); | ||||
| 	for (auto const & cached_vote : cached) | ||||
|  | @ -1535,7 +1535,7 @@ TEST (active_transactions, allow_limited_overflow) | |||
| 	{ | ||||
| 		// Non-final vote, so it stays in the AEC without getting confirmed
 | ||||
| 		auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); | ||||
| 		node.vote_cache.vote (vote); | ||||
| 		node.vote_cache.insert (vote); | ||||
| 	} | ||||
| 
 | ||||
| 	// Ensure active elections overfill AEC only up to normal + hinted limit
 | ||||
|  | @ -1573,7 +1573,7 @@ TEST (active_transactions, allow_limited_overflow_adapt) | |||
| 	{ | ||||
| 		// Non-final vote, so it stays in the AEC without getting confirmed
 | ||||
| 		auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); | ||||
| 		node.vote_cache.vote (vote); | ||||
| 		node.vote_cache.insert (vote); | ||||
| 	} | ||||
| 
 | ||||
| 	// Ensure hinted election amount is bounded by hinted limit
 | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ TEST (vote_cache, insert_one_hash) | |||
| 	auto rep1 = create_rep (7); | ||||
| 	auto hash1 = nano::test::random_hash (); | ||||
| 	auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	ASSERT_EQ (1, vote_cache.size ()); | ||||
| 
 | ||||
| 	auto peek1 = vote_cache.find (hash1); | ||||
|  | @ -88,9 +88,9 @@ TEST (vote_cache, insert_one_hash_many_votes) | |||
| 	auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); | ||||
| 	auto vote2 = nano::test::make_vote (rep2, { hash1 }, 2 * 1024 * 1024); | ||||
| 	auto vote3 = nano::test::make_vote (rep3, { hash1 }, 3 * 1024 * 1024); | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.vote (vote2); | ||||
| 	vote_cache.vote (vote3); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	vote_cache.insert (vote2); | ||||
| 	vote_cache.insert (vote3); | ||||
| 
 | ||||
| 	ASSERT_EQ (1, vote_cache.size ()); | ||||
| 	auto peek1 = vote_cache.find (hash1); | ||||
|  | @ -132,9 +132,9 @@ TEST (vote_cache, insert_many_hashes_many_votes) | |||
| 	auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024); | ||||
| 	auto vote4 = nano::test::make_vote (rep4, { hash1 }, 1024 * 1024); | ||||
| 	// Insert first 3 votes in cache
 | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.vote (vote2); | ||||
| 	vote_cache.vote (vote3); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	vote_cache.insert (vote2); | ||||
| 	vote_cache.insert (vote3); | ||||
| 	// Ensure all of those are properly inserted
 | ||||
| 	ASSERT_EQ (3, vote_cache.size ()); | ||||
| 	ASSERT_EQ (1, vote_cache.find (hash1).size ()); | ||||
|  | @ -152,7 +152,7 @@ TEST (vote_cache, insert_many_hashes_many_votes) | |||
| 	ASSERT_EQ (peek1.front (), vote3); | ||||
| 
 | ||||
| 	// Now add a vote from rep4 with the highest voting weight
 | ||||
| 	vote_cache.vote (vote4); | ||||
| 	vote_cache.insert (vote4); | ||||
| 
 | ||||
| 	// Ensure that the first entry in queue is now the one for hash1 (rep1 + rep4 tally weight)
 | ||||
| 	auto tops2 = vote_cache.top (0); | ||||
|  | @ -195,8 +195,8 @@ TEST (vote_cache, insert_duplicate) | |||
| 	auto rep1 = create_rep (9); | ||||
| 	auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); | ||||
| 	auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.vote (vote2); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	vote_cache.insert (vote2); | ||||
| 	ASSERT_EQ (1, vote_cache.size ()); | ||||
| } | ||||
| 
 | ||||
|  | @ -212,12 +212,12 @@ TEST (vote_cache, insert_newer) | |||
| 	auto hash1 = nano::test::random_hash (); | ||||
| 	auto rep1 = create_rep (9); | ||||
| 	auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	auto peek1 = vote_cache.find (hash1); | ||||
| 	ASSERT_EQ (peek1.size (), 1); | ||||
| 	ASSERT_EQ (peek1.front (), vote1); | ||||
| 	auto vote2 = nano::test::make_final_vote (rep1, { hash1 }); | ||||
| 	vote_cache.vote (vote2); | ||||
| 	vote_cache.insert (vote2); | ||||
| 	auto peek2 = vote_cache.find (hash1); | ||||
| 	ASSERT_EQ (peek2.size (), 1); | ||||
| 	ASSERT_EQ (peek2.front (), vote2); // vote2 should replace vote1 as it has a higher timestamp
 | ||||
|  | @ -235,12 +235,12 @@ TEST (vote_cache, insert_older) | |||
| 	auto hash1 = nano::test::random_hash (); | ||||
| 	auto rep1 = create_rep (9); | ||||
| 	auto vote1 = nano::test::make_vote (rep1, { hash1 }, 2 * 1024 * 1024); | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	auto peek1 = vote_cache.find (hash1); | ||||
| 	ASSERT_EQ (peek1.size (), 1); | ||||
| 	ASSERT_EQ (peek1.front (), vote1); | ||||
| 	auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); | ||||
| 	vote_cache.vote (vote2); | ||||
| 	vote_cache.insert (vote2); | ||||
| 	auto peek2 = vote_cache.find (hash1); | ||||
| 	ASSERT_EQ (peek2.size (), 1); | ||||
| 	ASSERT_EQ (peek2.front (), vote1); // vote1 should still be in cache as it has a higher timestamp
 | ||||
|  | @ -265,9 +265,9 @@ TEST (vote_cache, erase) | |||
| 	auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); | ||||
| 	auto vote2 = nano::test::make_vote (rep2, { hash2 }, 1024 * 1024); | ||||
| 	auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024); | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.vote (vote2); | ||||
| 	vote_cache.vote (vote3); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	vote_cache.insert (vote2); | ||||
| 	vote_cache.insert (vote3); | ||||
| 	ASSERT_EQ (3, vote_cache.size ()); | ||||
| 	ASSERT_FALSE (vote_cache.empty ()); | ||||
| 	ASSERT_FALSE (vote_cache.find (hash1).empty ()); | ||||
|  | @ -304,7 +304,7 @@ TEST (vote_cache, overfill) | |||
| 		auto rep1 = create_rep (count - n); | ||||
| 		auto hash1 = nano::test::random_hash (); | ||||
| 		auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); | ||||
| 		vote_cache.vote (vote1); | ||||
| 		vote_cache.insert (vote1); | ||||
| 	} | ||||
| 	ASSERT_LT (vote_cache.size (), count); | ||||
| 	// Check that oldest votes are dropped first
 | ||||
|  | @ -328,7 +328,7 @@ TEST (vote_cache, overfill_entry) | |||
| 	{ | ||||
| 		auto rep1 = create_rep (9); | ||||
| 		auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); | ||||
| 		vote_cache.vote (vote1); | ||||
| 		vote_cache.insert (vote1); | ||||
| 	} | ||||
| 	ASSERT_EQ (1, vote_cache.size ()); | ||||
| } | ||||
|  | @ -344,7 +344,7 @@ TEST (vote_cache, age_cutoff) | |||
| 	auto hash1 = nano::test::random_hash (); | ||||
| 	auto rep1 = create_rep (9); | ||||
| 	auto vote1 = nano::test::make_vote (rep1, { hash1 }, 3); | ||||
| 	vote_cache.vote (vote1); | ||||
| 	vote_cache.insert (vote1); | ||||
| 	ASSERT_EQ (1, vote_cache.size ()); | ||||
| 	ASSERT_FALSE (vote_cache.find (hash1).empty ()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -419,13 +419,9 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p | |||
| 
 | ||||
| 	if (result.inserted) | ||||
| 	{ | ||||
| 		release_assert (result.election); | ||||
| 		debug_assert (result.election); | ||||
| 
 | ||||
| 		auto cached = node.vote_cache.find (hash); | ||||
| 		for (auto const & cached_vote : cached) | ||||
| 		{ | ||||
| 			vote (cached_vote); | ||||
| 		} | ||||
| 		trigger_vote_cache (hash); | ||||
| 
 | ||||
| 		node.observers.active_started.notify (hash); | ||||
| 		vacancy_update (); | ||||
|  | @ -442,8 +438,18 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p | |||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| bool nano::active_transactions::trigger_vote_cache (nano::block_hash hash) | ||||
| { | ||||
| 	auto cached = node.vote_cache.find (hash); | ||||
| 	for (auto const & cached_vote : cached) | ||||
| 	{ | ||||
| 		vote (cached_vote, nano::vote_source::cache); | ||||
| 	} | ||||
| 	return !cached.empty (); | ||||
| } | ||||
| 
 | ||||
| // Validate a vote and apply it to the current election if one exists
 | ||||
| std::unordered_map<nano::block_hash, nano::vote_code> nano::active_transactions::vote (std::shared_ptr<nano::vote> const & vote) | ||||
| std::unordered_map<nano::block_hash, nano::vote_code> nano::active_transactions::vote (std::shared_ptr<nano::vote> const & vote, nano::vote_source source) | ||||
| { | ||||
| 	std::unordered_map<nano::block_hash, nano::vote_code> results; | ||||
| 	std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> process; | ||||
|  | @ -480,7 +486,7 @@ std::unordered_map<nano::block_hash, nano::vote_code> nano::active_transactions: | |||
| 
 | ||||
| 		for (auto const & [block_hash, election] : process) | ||||
| 		{ | ||||
| 			auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash); | ||||
| 			auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash, source); | ||||
| 			results[block_hash] = vote_result; | ||||
| 
 | ||||
| 			processed |= (vote_result == nano::vote_code::vote); | ||||
|  | @ -502,6 +508,8 @@ std::unordered_map<nano::block_hash, nano::vote_code> nano::active_transactions: | |||
| 		return results.find (hash) != results.end (); | ||||
| 	})); | ||||
| 
 | ||||
| 	vote_processed.notify (vote, source, results); | ||||
| 
 | ||||
| 	return results; | ||||
| } | ||||
| 
 | ||||
|  | @ -612,11 +620,7 @@ bool nano::active_transactions::publish (std::shared_ptr<nano::block> const & bl | |||
| 			blocks.emplace (block_a->hash (), election); | ||||
| 			lock.unlock (); | ||||
| 
 | ||||
| 			auto cached = node.vote_cache.find (block_a->hash ()); | ||||
| 			for (auto const & cached_vote : cached) | ||||
| 			{ | ||||
| 				vote (cached_vote); | ||||
| 			} | ||||
| 			trigger_vote_cache (block_a->hash ()); | ||||
| 
 | ||||
| 			node.stats.inc (nano::stat::type::active, nano::stat::detail::election_block_conflict); | ||||
| 		} | ||||
|  |  | |||
|  | @ -152,7 +152,7 @@ public: | |||
| 	 */ | ||||
| 	nano::election_insertion_result insert (std::shared_ptr<nano::block> const &, nano::election_behavior = nano::election_behavior::normal); | ||||
| 	// Distinguishes replay votes, cannot be determined if the block is not in any election
 | ||||
| 	std::unordered_map<nano::block_hash, nano::vote_code> vote (std::shared_ptr<nano::vote> const &); | ||||
| 	std::unordered_map<nano::block_hash, nano::vote_code> vote (std::shared_ptr<nano::vote> const &, nano::vote_source = nano::vote_source::live); | ||||
| 	// Is the root of this block in the roots container
 | ||||
| 	bool active (nano::block const &) const; | ||||
| 	bool active (nano::qualified_root const &) const; | ||||
|  | @ -189,6 +189,10 @@ public: | |||
| 	void add_election_winner_details (nano::block_hash const &, std::shared_ptr<nano::election> const &); | ||||
| 	std::shared_ptr<nano::election> remove_election_winner_details (nano::block_hash const &); | ||||
| 
 | ||||
| public: // Events
 | ||||
| 	using vote_processed_event_t = nano::observer_set<std::shared_ptr<nano::vote> const &, nano::vote_source, std::unordered_map<nano::block_hash, nano::vote_code> const &>; | ||||
| 	vote_processed_event_t vote_processed; | ||||
| 
 | ||||
| private: | ||||
| 	// Erase elections if we're over capacity
 | ||||
| 	void trim (); | ||||
|  | @ -201,6 +205,7 @@ private: | |||
| 	std::vector<std::shared_ptr<nano::election>> list_active_impl (std::size_t) const; | ||||
| 	void activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block); | ||||
| 	void notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector<nano::vote_with_weight_info> const & votes); | ||||
| 	bool trigger_vote_cache (nano::block_hash); | ||||
| 
 | ||||
| private: // Dependencies
 | ||||
| 	nano::node & node; | ||||
|  |  | |||
|  | @ -217,6 +217,10 @@ nano::node::node (std::shared_ptr<boost::asio::io_context> io_ctx_a, std::filesy | |||
| 		scheduler.optimistic.activate (account, account_info, conf_info); | ||||
| 	}); | ||||
| 
 | ||||
| 	active.vote_processed.add ([this] (std::shared_ptr<nano::vote> const & vote, nano::vote_source source, std::unordered_map<nano::block_hash, nano::vote_code> const & results) { | ||||
| 		vote_cache.observe (vote, source, results); | ||||
| 	}); | ||||
| 
 | ||||
| 	if (!init_error ()) | ||||
| 	{ | ||||
| 		// Notify election schedulers when AEC frees election slot
 | ||||
|  |  | |||
|  | @ -128,7 +128,25 @@ nano::vote_cache::vote_cache (vote_cache_config const & config_a, nano::stats & | |||
| { | ||||
| } | ||||
| 
 | ||||
| void nano::vote_cache::vote (std::shared_ptr<nano::vote> const & vote, std::function<bool (nano::block_hash const &)> const & filter) | ||||
| void nano::vote_cache::observe (const std::shared_ptr<nano::vote> & vote, nano::vote_source source, std::unordered_map<nano::block_hash, nano::vote_code> results) | ||||
| { | ||||
| 	if (source == nano::vote_source::live) | ||||
| 	{ | ||||
| 		insert (vote, [&results] (nano::block_hash const & hash) { | ||||
| 			// This filters which hashes should be included in the vote cache
 | ||||
| 			if (auto it = results.find (hash); it != results.end ()) | ||||
| 			{ | ||||
| 				auto result = it->second; | ||||
| 				// Cache votes with a corresponding active election (indicated by `vote_code::vote`) in case that election gets dropped
 | ||||
| 				return result == nano::vote_code::vote || result == nano::vote_code::indeterminate; | ||||
| 			} | ||||
| 			debug_assert (false); | ||||
| 			return false; | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void nano::vote_cache::insert (std::shared_ptr<nano::vote> const & vote, std::function<bool (nano::block_hash const &)> filter) | ||||
| { | ||||
| 	auto const representative = vote->account; | ||||
| 	auto const timestamp = vote->timestamp (); | ||||
|  | @ -138,6 +156,7 @@ void nano::vote_cache::vote (std::shared_ptr<nano::vote> const & vote, std::func | |||
| 
 | ||||
| 	for (auto const & hash : vote->hashes) | ||||
| 	{ | ||||
| 		// Using filter callback here to avoid unnecessary relocking when processing large votes
 | ||||
| 		if (!filter (hash)) | ||||
| 		{ | ||||
| 			continue; | ||||
|  |  | |||
|  | @ -106,9 +106,14 @@ public: | |||
| 	/**
 | ||||
| 	 * Adds a new vote to cache | ||||
| 	 */ | ||||
| 	void vote ( | ||||
| 	void insert ( | ||||
| 	std::shared_ptr<nano::vote> const & vote, | ||||
| 	std::function<bool (nano::block_hash const &)> const & filter = [] (nano::block_hash const &) { return true; }); | ||||
| 	std::function<bool (nano::block_hash const &)> filter = [] (nano::block_hash const &) { return true; }); | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * Should be called for every processed vote, filters which votes should be added to cache | ||||
| 	 */ | ||||
| 	void observe (std::shared_ptr<nano::vote> const & vote, nano::vote_source source, std::unordered_map<nano::block_hash, nano::vote_code>); | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * Tries to find an entry associated with block hash | ||||
|  |  | |||
|  | @ -176,7 +176,7 @@ nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr<nano::vote> | |||
| 		} | ||||
| 		result = replay ? nano::vote_code::replay : (processed ? nano::vote_code::vote : nano::vote_code::indeterminate); | ||||
| 
 | ||||
| 		observers.vote.notify (vote_a, channel_a, result); | ||||
| 		observers.vote.notify (vote, channel, result); | ||||
| 	} | ||||
| 
 | ||||
| 	stats.inc (nano::stat::type::vote, to_stat_detail (result)); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Piotr Wójcik
				Piotr Wójcik