Fix SN expirations and deregistrations in core tests (#493)

This commit is contained in:
Maxim Shishmarev 2019-03-18 10:48:50 +11:00 committed by Doyle
parent a2f5769a86
commit 4be45d29cf
6 changed files with 125 additions and 62 deletions

View File

@ -1313,6 +1313,10 @@ namespace service_nodes
}
else // Version 10 Bulletproofs
{
/// Note: this code exhibits a sublte unintended behaviour: a snode that
/// registered in hardfork 9 and was scheduled for deregistration in hardfork 10
/// will have its life is slightly prolonged by the "grace period", although it might
/// look like we use the registration height to determine the expiry height.
uint64_t node_expiry_height = info.registration_height + lock_blocks + STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS;
if (block_height > node_expiry_height)
expired_nodes.push_back(snode_key);

View File

@ -281,12 +281,12 @@ gen_batched_governance_reward::gen_batched_governance_reward()
static uint64_t expected_total_governance_paid = 0;
bool gen_batched_governance_reward::generate(std::vector<test_event_entry>& events) const
{
const get_test_options<gen_batched_governance_reward> test_options = {};
const config_t &network = cryptonote::get_config(cryptonote::FAKECHAIN, network_version_10_bulletproofs);
linear_chain_generator batched_governance_generator(events);
const get_test_options<gen_batched_governance_reward> test_options = {};
linear_chain_generator batched_governance_generator(events, test_options.hard_forks);
{
batched_governance_generator.rewind_until_version(test_options.hard_forks, network_version_10_bulletproofs);
batched_governance_generator.rewind_until_version(network_version_10_bulletproofs);
uint64_t blocks_to_gen = network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS - batched_governance_generator.height();
batched_governance_generator.rewind_blocks_n(blocks_to_gen);
@ -297,8 +297,8 @@ bool gen_batched_governance_reward::generate(std::vector<test_event_entry>& even
// you don't atleast progress and generate blocks from hf8 you will run into
// problems
std::vector<test_event_entry> unused_events;
linear_chain_generator no_batched_governance_generator(unused_events);
no_batched_governance_generator.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
linear_chain_generator no_batched_governance_generator(unused_events, test_options.hard_forks);
no_batched_governance_generator.rewind_until_version(network_version_9_service_nodes);
while(no_batched_governance_generator.height() < batched_governance_generator.height())
no_batched_governance_generator.create_block();

View File

@ -59,9 +59,9 @@ bool operator<(const last_reward_point& lhs, const last_reward_point& rhs) {
return lhs.priority < rhs.priority;
}
dereg_tx_builder linear_chain_generator::build_deregister(const crypto::public_key& pk)
dereg_tx_builder linear_chain_generator::build_deregister(const crypto::public_key& pk, bool commit)
{
return dereg_tx_builder(*this, pk);
return dereg_tx_builder(*this, pk, commit);
}
cryptonote::account_base linear_chain_generator::create_account()
@ -88,8 +88,21 @@ void linear_chain_generator::create_block(const std::vector<cryptonote::transact
blocks_.push_back(blk);
}
uint8_t linear_chain_generator::get_hf_version_at(uint64_t height) const {
void linear_chain_generator::rewind_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version)
uint8_t cur_hf_ver = 0;
for (auto i = 0u; i < hard_forks_.size(); ++i)
{
if (height < hard_forks_[i].second) break;
cur_hf_ver = hard_forks_[i].first;
}
assert(cur_hf_ver != 0);
return cur_hf_ver;
}
void linear_chain_generator::rewind_until_version(int hard_fork_version)
{
assert(gen_.m_hf_version < hard_fork_version);
@ -97,20 +110,20 @@ void linear_chain_generator::rewind_until_version(const std::vector<std::pair<ui
create_genesis_block();
size_t start_index;
for (start_index = 0; start_index < hard_forks.size(); ++start_index)
for (start_index = 0; start_index < hard_forks_.size(); ++start_index)
{
const uint8_t version = hard_forks[start_index].first;
const uint8_t version = hard_forks_[start_index].first;
if (version > gen_.m_hf_version) break;
}
for (size_t i = start_index; i < hard_forks.size() && gen_.m_hf_version < hard_fork_version; ++i)
for (size_t i = start_index; i < hard_forks_.size() && gen_.m_hf_version < hard_fork_version; ++i)
{
auto cur_height = blocks_.size();
uint64_t next_fork_height = hard_forks[i].second;
uint64_t next_fork_height = hard_forks_[i].second;
uint64_t blocks_till_next_hardfork = next_fork_height - cur_height;
rewind_blocks_n(blocks_till_next_hardfork);
gen_.m_hf_version = hard_forks[i].first;
gen_.m_hf_version = hard_forks_[i].first;
create_block();
}
@ -161,7 +174,18 @@ cryptonote::block linear_chain_generator::create_block_on_fork(const cryptonote:
/// now we can add sn from the buffer to be used in consequent nodes
sn_list_.add_registrations(registration_buffer_);
registration_buffer_.clear();
sn_list_.expire_old(height);
sn_list_.handle_deregistrations(deregistration_buffer_);
deregistration_buffer_.clear();
/// Note: depending on whether we check in hf9 or later, loki assignes different meaning to
/// "expiration height": in hf9 it expires nodes at their expiration height; after hf9 --
/// a the expiration height + 1.
if (get_hf_version() == network_version_9_service_nodes) {
sn_list_.expire_old(height);
} else {
sn_list_.expire_old(height - 1);
}
return blk;
}
@ -221,7 +245,15 @@ cryptonote::transaction linear_chain_generator::create_registration_tx(const cry
const cryptonote::keypair& sn_keys)
{
const sn_contributor_t contr = { acc.get_keys().m_account_address, STAKING_PORTIONS };
const uint32_t expires = height() + service_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN);
uint32_t expires = height() + service_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN);
/// Account for some inconsistency in service_nodes::staking_num_lock_blocks
/// on the boundary between hardforks 9 and 10
if (get_hf_version() == cryptonote::network_version_9_service_nodes &&
get_hf_version_at(expires) == cryptonote::network_version_10_bulletproofs)
{
expires += STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS;
}
const auto reg_idx = registration_buffer_.size();
registration_buffer_.push_back({ expires, sn_keys, contr, { height(), reg_idx } });
@ -238,7 +270,8 @@ cryptonote::transaction linear_chain_generator::create_registration_tx()
cryptonote::transaction linear_chain_generator::create_deregister_tx(const crypto::public_key& pk,
uint64_t height,
const std::vector<sn_idx>& voters,
uint64_t fee) const
uint64_t fee,
bool commit)
{
cryptonote::tx_extra_service_node_deregister deregister;
@ -265,6 +298,8 @@ cryptonote::transaction linear_chain_generator::create_deregister_tx(const crypt
deregister.votes.push_back({ signature, (uint32_t)voter.idx_in_quorum });
}
if (commit) deregistration_buffer_.push_back(pk);
const auto deregister_tx = make_deregistration_tx(events_, first_miner_, blocks_.back(), deregister, gen_.m_hf_version, fee);
events_.push_back(deregister_tx);
@ -312,11 +347,26 @@ inline void sn_list::add_registrations(const std::vector<sn_registration>& regs)
});
}
void sn_list::handle_deregistrations(const std::vector<crypto::public_key>& dereg_buffer)
{
const auto size_before = sn_owners_.size();
auto end_it = sn_owners_.end();
for (const auto pk : dereg_buffer) {
end_it = std::remove_if(sn_owners_.begin(), end_it, [&pk](const sn_registration& sn) {
return sn.keys.pub == pk;
});
}
sn_owners_.erase(end_it, sn_owners_.end());
assert(sn_owners_.size() == size_before - dereg_buffer.size());
}
inline void sn_list::expire_old(uint64_t height)
{
/// remove_if is stable, no need for re-sorting
const auto new_end = std::remove_if(
sn_owners_.begin(), sn_owners_.end(), [height](const sn_registration& reg) { return reg.valid_until < height; });
sn_owners_.begin(), sn_owners_.end(), [height](const sn_registration& reg) { return height > reg.valid_until; });
sn_owners_.erase(new_end, sn_owners_.end());
}

View File

@ -267,6 +267,9 @@ public:
void add_registrations(const std::vector<sn_registration>& regs);
void remove_node(const crypto::public_key& pk);
void handle_deregistrations(const std::vector<crypto::public_key>& dereg_buffer);
};
/// Service node and its index
@ -288,18 +291,23 @@ class linear_chain_generator
private:
test_generator gen_;
std::vector<test_event_entry>& events_;
const std::vector<std::pair<uint8_t, uint64_t>> hard_forks_;
std::vector<cryptonote::block> blocks_;
sn_list sn_list_;
/// keep new registrations here until the next block
/// keep new registrations and deregistrations here until the next block
std::vector<sn_registration> registration_buffer_;
std::vector<crypto::public_key> deregistration_buffer_;
cryptonote::account_base first_miner_;
/// Get hardfork version at specified height
uint8_t get_hf_version_at(uint64_t height) const;
public:
linear_chain_generator(std::vector<test_event_entry> &events)
: gen_(), events_(events)
linear_chain_generator(std::vector<test_event_entry> &events, const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks)
: gen_(), events_(events), hard_forks_(hard_forks)
{ }
uint64_t height() const { return get_block_height(blocks_.back()); }
@ -315,7 +323,7 @@ class linear_chain_generator
int get_hf_version() const;
void rewind_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version);
void rewind_until_version(int hard_fork_version);
void rewind_blocks_n(int n);
void rewind_blocks();
@ -342,9 +350,9 @@ class linear_chain_generator
QuorumState get_quorum_idxs(uint64_t height) const;
cryptonote::transaction create_deregister_tx(const crypto::public_key& pk, uint64_t height, const std::vector<sn_idx>& voters, uint64_t fee = 0) const;
cryptonote::transaction create_deregister_tx(const crypto::public_key& pk, uint64_t height, const std::vector<sn_idx>& voters, uint64_t fee, bool commit);
dereg_tx_builder build_deregister(const crypto::public_key& pk);
dereg_tx_builder build_deregister(const crypto::public_key& pk, bool commit = true);
crypto::public_key get_test_pk(uint32_t idx) const;
@ -366,9 +374,12 @@ class dereg_tx_builder {
boost::optional<const std::vector<sn_idx>&> voters_ = boost::none;
/// whether to actually remove SN from the list
bool commit_;
public:
dereg_tx_builder(linear_chain_generator& gen, const crypto::public_key& pk)
: gen_(gen), pk_(pk)
dereg_tx_builder(linear_chain_generator& gen, const crypto::public_key& pk, bool commit)
: gen_(gen), pk_(pk), commit_(commit)
{}
dereg_tx_builder&& with_height(uint64_t height) {
@ -391,7 +402,7 @@ class dereg_tx_builder {
{
const auto height = height_ ? *height_ : gen_.height();
const auto voters = voters_ ? *voters_ : gen_.get_quorum_idxs(height).voters;
return gen_.create_deregister_tx(pk_, height, voters, fee_.value_or(0));
return gen_.create_deregister_tx(pk_, height, voters, fee_.value_or(0), commit_);
}
};

View File

@ -54,8 +54,8 @@ namespace
bool gen_ring_signature_1::generate(std::vector<test_event_entry>& events) const
{
linear_chain_generator gen(events);
const get_test_options<gen_ring_signature_1> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const auto miner = gen.first_miner();

View File

@ -122,14 +122,14 @@ gen_service_nodes::gen_service_nodes()
bool gen_service_nodes::generate(std::vector<test_event_entry> &events) const
{
linear_chain_generator gen(events);
const get_test_options<gen_service_nodes> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const auto miner = gen.first_miner();
const auto alice = gen.create_account();
const get_test_options<gen_service_nodes> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
gen.rewind_until_version(network_version_9_service_nodes);
gen.rewind_blocks_n(10);
gen.rewind_blocks();
@ -219,15 +219,15 @@ test_prefer_deregisters::test_prefer_deregisters() {
//-----------------------------------------------------------------------------------------------------
bool test_prefer_deregisters::generate(std::vector<test_event_entry> &events)
{
linear_chain_generator gen(events);
const get_test_options<test_prefer_deregisters> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const auto miner = gen.first_miner();
const auto alice = gen.create_account();
const get_test_options<test_prefer_deregisters> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
gen.rewind_until_version(network_version_9_service_nodes);
/// give miner some outputs to spend and unlock them
gen.rewind_blocks_n(60);
@ -306,12 +306,12 @@ test_zero_fee_deregister::test_zero_fee_deregister() {
bool test_zero_fee_deregister::generate(std::vector<test_event_entry> &events)
{
linear_chain_generator gen(events);
const get_test_options<test_zero_fee_deregister> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const get_test_options<test_zero_fee_deregister> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
gen.rewind_until_version(network_version_9_service_nodes);
/// give miner some outputs to spend and unlock them
gen.rewind_blocks_n(20);
@ -347,14 +347,14 @@ bool test_deregister_safety_buffer::generate(std::vector<test_event_entry> &even
{
DEFINE_TESTS_ERROR_CONTEXT("test_deregister_safety_buffer::generate");
linear_chain_generator gen(events);
const get_test_options<test_deregister_safety_buffer> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const auto miner = gen.first_miner();
const get_test_options<test_deregister_safety_buffer> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
gen.rewind_until_version(network_version_9_service_nodes);
/// give miner some outputs to spend and unlock them
gen.rewind_blocks_n(40);
@ -418,7 +418,6 @@ bool test_deregister_safety_buffer::generate(std::vector<test_event_entry> &even
{
const auto dereg_tx = gen.build_deregister(pk).with_height(heightA).build();
gen.create_block({dereg_tx});
gen.deregister(pk);
}
/// Register the node again
@ -459,11 +458,11 @@ test_deregisters_on_split::test_deregisters_on_split()
//-----------------------------------------------------------------------------------------------------
bool test_deregisters_on_split::generate(std::vector<test_event_entry> &events)
{
linear_chain_generator gen(events);
const get_test_options<test_deregisters_on_split> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const get_test_options<test_deregisters_on_split> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
gen.rewind_until_version(network_version_9_service_nodes);
/// generate some outputs and unlock them
gen.rewind_blocks_n(20);
@ -492,10 +491,10 @@ bool test_deregisters_on_split::generate(std::vector<test_event_entry> &events)
const auto dereg_A = gen.build_deregister(pk).with_voters(quorumA).with_height(split_height).build();
/// create deregistration on alt chain (B)
auto quorumB = gen.get_quorum_idxs(split_height).voters;
auto quorumB = fork.get_quorum_idxs(split_height).voters;
quorumB.erase(quorumB.begin() + 1); /// remove second voter
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, true);
const auto dereg_B = gen.build_deregister(pk).with_voters(quorumB).with_height(split_height).build(); /// events[68]
const auto dereg_B = fork.build_deregister(pk).with_voters(quorumB).with_height(split_height).build(); /// events[68]
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, false);
/// continue main chain with deregister A
@ -504,9 +503,6 @@ bool test_deregisters_on_split::generate(std::vector<test_event_entry> &events)
/// continue alt chain with deregister B
fork.create_block({ dereg_B });
/// actually remove pk form the local service node list
fork.deregister(pk);
/// one more block on alt chain to switch
fork.create_block();
@ -565,11 +561,11 @@ deregister_too_old::deregister_too_old()
//-----------------------------------------------------------------------------------------------------
bool deregister_too_old::generate(std::vector<test_event_entry>& events)
{
linear_chain_generator gen(events);
const get_test_options<deregister_too_old> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const get_test_options<deregister_too_old> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
gen.rewind_until_version(network_version_9_service_nodes);
/// generate some outputs and unlock them
gen.rewind_blocks_n(20);
@ -585,9 +581,9 @@ bool deregister_too_old::generate(std::vector<test_event_entry>& events)
gen.create_block(reg_txs);
// create a deregister for this height
// create a deregister for this height (without committing)
const auto pk = gen.get_test_pk(0);
const auto dereg_tx = gen.build_deregister(pk).build();
const auto dereg_tx = gen.build_deregister(pk, false).build();
/// create enough block to make deregistrations invalid (60 - 1 blocks)
gen.rewind_blocks_n(service_nodes::deregister_vote::DEREGISTER_LIFETIME_BY_HEIGHT-1);
@ -610,11 +606,11 @@ sn_test_rollback::sn_test_rollback()
//-----------------------------------------------------------------------------------------------------
bool sn_test_rollback::generate(std::vector<test_event_entry>& events)
{
linear_chain_generator gen(events);
const get_test_options<sn_test_rollback> test_options = {};
linear_chain_generator gen(events, test_options.hard_forks);
gen.create_genesis_block();
const get_test_options<sn_test_rollback> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
gen.rewind_until_version(network_version_9_service_nodes);
/// generate some outputs and unlock them
gen.rewind_blocks_n(20);
@ -643,7 +639,6 @@ bool sn_test_rollback::generate(std::vector<test_event_entry>& events)
const auto pk = gen.get_test_pk(0);
const auto dereg_tx = gen.build_deregister(pk).build();
gen.create_block({dereg_tx});
gen.deregister(pk);
}
/// create a new service node (B) in the next block
@ -738,10 +733,10 @@ test_swarms_basic::test_swarms_basic() {
bool test_swarms_basic::generate(std::vector<test_event_entry>& events)
{
linear_chain_generator gen(events);
const get_test_options<test_swarms_basic> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
linear_chain_generator gen(events, test_options.hard_forks);
gen.rewind_until_version(network_version_9_service_nodes);
/// Create some service nodes before hf version 10
constexpr size_t init_sn_count = 16;
@ -767,7 +762,7 @@ bool test_swarms_basic::generate(std::vector<test_event_entry>& events)
return false;
}
gen.rewind_until_version(test_options.hard_forks, network_version_10_bulletproofs);
gen.rewind_until_version(network_version_10_bulletproofs);
/// test that we now have swarms
DO_CALLBACK(events, "test_initial_swarms");
@ -794,6 +789,9 @@ bool test_swarms_basic::generate(std::vector<test_event_entry>& events)
DO_CALLBACK(events, "test_after_deregisters");
/// test (implicitly) that deregistered nodes do not receive rewards
gen.rewind_blocks_n(5);
return true;
}