fixed some (previously working) core tests (#288)

This commit is contained in:
Maxim Shishmarev 2018-10-15 12:23:28 +11:00 committed by Doyle
parent 87cfb05f55
commit 63337de3d9
8 changed files with 608 additions and 529 deletions

View file

@ -329,11 +329,12 @@ bool gen_block_miner_tx_has_2_tx_gen_in::generate(std::vector<test_event_entry>&
bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events) const
{
BLOCK_VALIDATION_INIT_GENERATE();
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
REWIND_BLOCKS_N(events, blk_0a, blk_0, miner_account, 10);
REWIND_BLOCKS(events, blk_0r, blk_0a, miner_account);
transaction tmp_tx;
if (!construct_tx_to_key(events, tmp_tx, blk_0r, miner_account, miner_account, blk_0.miner_tx.vout[0].amount))
if (!TxBuilder(events, tmp_tx, blk_0r, miner_account, miner_account, blk_0.miner_tx.vout[0].amount).build())
return false;
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0r);
@ -360,7 +361,7 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
transaction tmp_tx;
if (!construct_tx_to_key(events, tmp_tx, blk_1r, miner_account, miner_account, blk_1.miner_tx.vout[0].amount))
if (!TxBuilder(events, tmp_tx, blk_1r, miner_account, miner_account, blk_1.miner_tx.vout[0].amount).build())
return false;
MAKE_MINER_TX_MANUALLY(miner_tx, blk_1);

View file

@ -155,7 +155,7 @@ struct gen_block_miner_tx_has_2_tx_gen_in : public gen_block_verification_base<1
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_block_miner_tx_has_2_in : public gen_block_verification_base<CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + 1>
struct gen_block_miner_tx_has_2_in : public gen_block_verification_base<CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + 11>
{
bool generate(std::vector<test_event_entry>& events) const;
};

View file

@ -80,7 +80,8 @@ bool gen_simple_chain_split_1::generate(std::vector<test_event_entry> &events) c
GENERATE_ACCOUNT(first_miner_account);
// events index
MAKE_GENESIS_BLOCK(events, blk_0, first_miner_account, ts_start); // 0
MAKE_NEXT_BLOCK(events, blk_1, blk_0, first_miner_account); // 1
REWIND_BLOCKS_N(events, blk_1a, blk_0, first_miner_account, 20);
MAKE_NEXT_BLOCK(events, blk_1, blk_1a, first_miner_account); // 1
MAKE_NEXT_BLOCK(events, blk_2, blk_1, first_miner_account); // 2
MAKE_NEXT_BLOCK(events, blk_3, blk_2, first_miner_account); // 3
MAKE_NEXT_BLOCK(events, blk_4, blk_3, first_miner_account); // 4
@ -178,10 +179,11 @@ bool gen_simple_chain_split_1::check_split_not_switched(cryptonote::core& c, siz
{
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched");
//check height
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9);
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9);
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[8])));
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 2);
CHECK_EQ(c.get_current_blockchain_height(), 29);
CHECK_EQ(c.get_blockchain_total_transactions(), 29);
CHECK_EQ(c.get_tail_id(), get_block_hash(boost::get<cryptonote::block>(events[28])));
CHECK_EQ(c.get_alternative_blocks_count(), 2);
return true;
}
//-----------------------------------------------------------------------------------------------------
@ -189,10 +191,10 @@ bool gen_simple_chain_split_1::check_split_not_switched2(cryptonote::core& c, si
{
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched2");
//check height
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9);
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9);
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[8])));
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3);
CHECK_EQ(c.get_current_blockchain_height(), 29);
CHECK_EQ(c.get_blockchain_total_transactions(), 29);
CHECK_EQ(c.get_tail_id(), get_block_hash(boost::get<cryptonote::block>(events[28])));
CHECK_EQ(c.get_alternative_blocks_count(), 3);
return true;
}
//-----------------------------------------------------------------------------------------------------
@ -201,10 +203,10 @@ bool gen_simple_chain_split_1::check_split_switched(cryptonote::core& c, size_t
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched");
//check height
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 10);
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 10);
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[14])));
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3);
CHECK_EQ(c.get_current_blockchain_height(), 30);
CHECK_EQ(c.get_blockchain_total_transactions(), 30);
CHECK_EQ(c.get_tail_id(), get_block_hash(boost::get<cryptonote::block>(events[34])));
CHECK_EQ(c.get_alternative_blocks_count(), 3);
return true;
}
//-----------------------------------------------------------------------------------------------------
@ -212,10 +214,10 @@ bool gen_simple_chain_split_1::check_split_not_switched_back(cryptonote::core& c
{
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched_back");
//check height
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 14);
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 14);
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[19])));
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8);
CHECK_EQ(c.get_current_blockchain_height(), 34);
CHECK_EQ(c.get_blockchain_total_transactions(), 34);
CHECK_EQ(c.get_tail_id(), get_block_hash(boost::get<cryptonote::block>(events[39])));
CHECK_EQ(c.get_alternative_blocks_count(), 8);
return true;
}
@ -225,10 +227,10 @@ bool gen_simple_chain_split_1::check_split_switched_back_1(cryptonote::core& c,
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched_back_1");
//check height
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 15);
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 15);
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[26])));
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8);
CHECK_EQ(c.get_current_blockchain_height(), 35);
CHECK_EQ(c.get_blockchain_total_transactions(), 35);
CHECK_EQ(c.get_tail_id(), get_block_hash(boost::get<cryptonote::block>(events[46])));
CHECK_EQ(c.get_alternative_blocks_count(), 8);
return true;
}//-----------------------------------------------------------------------------------------------------
@ -237,10 +239,10 @@ bool gen_simple_chain_split_1::check_split_switched_back_2(cryptonote::core& c,
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched_back_2");
//check height
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 16);
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 16);
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[28])));
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8);
CHECK_EQ(c.get_current_blockchain_height(), 36);
CHECK_EQ(c.get_blockchain_total_transactions(), 36);
CHECK_EQ(c.get_tail_id(), get_block_hash(boost::get<cryptonote::block>(events[48])));
CHECK_EQ(c.get_alternative_blocks_count(), 8);
return true;
}
//-----------------------------------------------------------------------------------------------------

View file

@ -51,7 +51,273 @@ using namespace epee;
using namespace crypto;
using namespace cryptonote;
bool operator<(const last_reward_point& lhs, const last_reward_point& rhs) {
if (lhs.height != rhs.height) {
return lhs.height < rhs.height;
}
return lhs.priority < rhs.priority;
}
dereg_tx_builder linear_chain_generator::build_deregister(const crypto::public_key& pk)
{
return dereg_tx_builder(*this, pk);
}
cryptonote::account_base linear_chain_generator::create_account()
{
cryptonote::account_base account;
account.generate();
events_.push_back(account);
return account;
}
void linear_chain_generator::create_genesis_block()
{
constexpr uint64_t ts_start = 1338224400;
first_miner_.generate();
cryptonote::block gen_block;
gen_.construct_block(gen_block, first_miner_, ts_start);
events_.push_back(gen_block);
blocks_.push_back(gen_block);
}
void linear_chain_generator::create_block(const std::vector<cryptonote::transaction>& txs)
{
const auto blk = create_block_on_fork(blocks_.back(), txs);
blocks_.push_back(blk);
}
void linear_chain_generator::rewind_until_v9()
{
gen_.set_hf_version(8);
create_block();
gen_.set_hf_version(9);
create_block();
}
void linear_chain_generator::rewind_blocks_n(int n)
{
for (auto i = 0; i < n; ++i) {
create_block();
}
}
void linear_chain_generator::rewind_blocks()
{
rewind_blocks_n(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
}
cryptonote::block linear_chain_generator::create_block_on_fork(const cryptonote::block& prev,
const std::vector<cryptonote::transaction>& txs)
{
const auto height = get_block_height(prev) + 1;
const auto& winner_pk = sn_list_.get_winner_pk(height);
const auto& sn_pk = winner_pk ? *winner_pk : crypto::null_pkey;
std::vector<sn_contributor_t> contribs = { { { crypto::null_pkey, crypto::null_pkey }, STAKING_PORTIONS } };
if (winner_pk) {
const auto& reg = sn_list_.find_registration(*winner_pk);
if (reg) {
contribs = { reg->contribution };
}
}
cryptonote::block blk;
gen_.construct_block(blk, prev, first_miner_, { txs.begin(), txs.end() }, sn_pk, contribs);
events_.push_back(blk);
/// 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);
return blk;
}
QuorumState linear_chain_generator::get_quorum_idxs(const cryptonote::block& block) const
{
if (sn_list_.size() <= service_nodes::QUORUM_SIZE) {
std::cerr << "Not enough service nodes\n";
return {};
}
std::vector<size_t> pub_keys_indexes;
{
uint64_t seed = 0;
const crypto::hash block_hash = cryptonote::get_block_hash(block);
std::memcpy(&seed, block_hash.data, std::min(sizeof(seed), sizeof(block_hash.data)));
pub_keys_indexes.resize(sn_list_.size());
for (size_t i = 0; i < pub_keys_indexes.size(); i++) {
pub_keys_indexes[i] = i;
}
service_nodes::loki_shuffle(pub_keys_indexes, seed);
}
QuorumState quorum;
for (auto i = 0u; i < service_nodes::QUORUM_SIZE; ++i) {
quorum.voters.push_back({ sn_list_.at(pub_keys_indexes[i]).keys.pub, i });
}
for (auto i = service_nodes::QUORUM_SIZE; i < pub_keys_indexes.size(); ++i) {
quorum.to_test.push_back({ sn_list_.at(pub_keys_indexes[i]).keys.pub, i });
}
return quorum;
}
QuorumState linear_chain_generator::get_quorum_idxs(uint64_t height) const
{
const auto block = blocks_.at(height);
return get_quorum_idxs(block);
}
cryptonote::transaction linear_chain_generator::create_tx(const cryptonote::account_base& miner,
const cryptonote::account_base& acc,
uint64_t amount,
uint64_t fee)
{
cryptonote::transaction t;
TxBuilder(events_, t, blocks_.back(), miner, acc, amount).with_fee(fee).build();
events_.push_back(t);
return t;
}
cryptonote::transaction linear_chain_generator::create_registration_tx(const cryptonote::account_base& acc,
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::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN);
const auto reg_idx = registration_buffer_.size();
registration_buffer_.push_back({ expires, sn_keys, contr, { height(), reg_idx } });
return make_default_registration_tx(events_, acc, sn_keys, blocks_.back());
}
cryptonote::transaction linear_chain_generator::create_registration_tx()
{
const auto sn_keys = keypair::generate(hw::get_device("default"));
return create_registration_tx(first_miner_, sn_keys);
}
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
{
cryptonote::tx_extra_service_node_deregister deregister;
deregister.block_height = height;
const auto idx = get_idx_in_tested(pk, height);
if (!idx) { MERROR("service node could not be found in the servcie node list"); throw std::exception(); }
deregister.service_node_index = *idx; /// idx inside nodes to test
/// need to create MIN_VOTES_TO_KICK_SERVICE_NODE (7) votes
for (const auto voter : voters) {
const auto reg = sn_list_.find_registration(voter.sn_pk);
if (!reg) return {};
const auto pk = reg->keys.pub;
const auto sk = reg->keys.sec;
const auto signature =
loki::service_node_deregister::sign_vote(deregister.block_height, deregister.service_node_index, pk, sk);
deregister.votes.push_back({ signature, (uint32_t)voter.idx_in_quorum });
}
const auto deregister_tx = make_deregistration_tx(events_, first_miner_, blocks_.back(), deregister, fee);
events_.push_back(deregister_tx);
return deregister_tx;
}
crypto::public_key linear_chain_generator::get_test_pk(uint32_t idx) const
{
const auto& to_test = get_quorum_idxs(height()).to_test;
return to_test.at(idx).sn_pk;
}
boost::optional<uint32_t> linear_chain_generator::get_idx_in_tested(const crypto::public_key& pk, uint64_t height) const
{
const auto& to_test = get_quorum_idxs(height).to_test;
for (const auto& sn : to_test) {
if (sn.sn_pk == pk) return sn.idx_in_quorum - service_nodes::QUORUM_SIZE;
}
return boost::none;
}
void linear_chain_generator::deregister(const crypto::public_key& pk) {
sn_list_.remove_node(pk);
}
inline void sn_list::remove_node(const crypto::public_key& pk)
{
const auto it =
std::find_if(sn_owners_.begin(), sn_owners_.end(), [pk](const sn_registration& sn) { return sn.keys.pub == pk; });
if (it != sn_owners_.end()) sn_owners_.erase(it); else abort();
}
inline void sn_list::add_registrations(const std::vector<sn_registration>& regs)
{
sn_owners_.insert(sn_owners_.begin(), regs.begin(), regs.end());
std::sort(sn_owners_.begin(), sn_owners_.end(),
[](const sn_registration &a, const sn_registration &b) {
return memcmp(reinterpret_cast<const void*>(&a.keys.pub), reinterpret_cast<const void*>(&b.keys.pub),
sizeof(a.keys.pub)) < 0;
});
}
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_.erase(new_end, sn_owners_.end());
}
inline const boost::optional<sn_registration> sn_list::find_registration(const crypto::public_key& pk) const
{
const auto it =
std::find_if(sn_owners_.begin(), sn_owners_.end(), [pk](const sn_registration& sn) { return sn.keys.pub == pk; });
if (it == sn_owners_.end()) return boost::none;
return *it;
}
inline const boost::optional<crypto::public_key> sn_list::get_winner_pk(uint64_t height)
{
if (sn_owners_.empty()) return boost::none;
auto it =
std::min_element(sn_owners_.begin(), sn_owners_.end(), [](const sn_registration& lhs, const sn_registration& rhs) {
return lhs.last_reward < rhs.last_reward;
});
it->last_reward.height = height;
return it->keys.pub;
}
/// --------------------------------------------------------------
void test_generator::get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const
{
crypto::hash curr = head;
@ -296,8 +562,7 @@ cryptonote::transaction make_registration_tx(std::vector<test_event_entry>& even
add_service_node_register_to_tx_extra(extra, addresses, operator_cut, portions, exp_timestamp, signature);
add_service_node_contributor_to_tx_extra(extra, addresses.at(0));
construct_tx_to_key(
events, tx, head, account, account, amount, TESTS_DEFAULT_FEE, 9, true /* staking */, extra, unlock_time);
TxBuilder(events, tx, head, account, account, amount).is_staking(true).with_extra(extra).with_unlock_time(unlock_time).with_per_output_unlock(true).build();
events.push_back(tx);
return tx;
}
@ -317,10 +582,9 @@ cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entr
return {};
}
const uint64_t unlock_time = 0;
const uint64_t amount = 0;
if (fee) construct_tx_to_key(events, tx, head, account, account, amount, fee, 9, false /* staking */, extra, unlock_time);
if (fee) TxBuilder(events, tx, head, account, account, amount).with_fee(fee).with_extra(extra).with_per_output_unlock(true).build();
tx.version = cryptonote::transaction::version_3_per_output_unlock_times;
tx.is_deregister = true;
@ -817,35 +1081,12 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins
return true;
}
bool construct_tx_to_key(const std::vector<test_event_entry>& events,
cryptonote::transaction& tx,
const block& blk_head,
const cryptonote::account_base& from,
const cryptonote::account_base& to,
uint64_t amount)
{
return construct_tx_to_key(events, tx, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 9);
}
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const block& blk_head,
const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount,
uint64_t fee, size_t nmix, bool stake, const std::vector<uint8_t>& extra, uint64_t unlock_time)
{
vector<tx_source_entry> sources;
vector<tx_destination_entry> destinations;
uint64_t change_amount;
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations, &change_amount);
tx_destination_entry change_addr{change_amount, from.get_keys().m_account_address, false /* is subaddr */ };
return cryptonote::construct_tx(from.get_keys(), sources, destinations, change_addr, extra, tx, unlock_time, stake, true);
}
transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head,
const account_base& acc_from, const account_base& acc_to, uint64_t amount, uint64_t fee)
{
transaction tx;
construct_tx_to_key(events, tx, blk_head, acc_from, acc_to, amount, fee, 9);
TxBuilder(events, tx, blk_head, acc_from, acc_to, amount).with_fee(fee).build();
events.push_back(tx);
return tx;
}

View file

@ -224,6 +224,165 @@ private:
uint8_t hf_version_;
};
/// ------------ Service Nodes -----------
struct last_reward_point {
uint64_t height;
uint64_t priority;
};
struct sn_registration {
uint64_t valid_until; /// block height
cryptonote::keypair keys;
sn_contributor_t contribution;
last_reward_point last_reward;
};
class sn_list
{
std::vector<sn_registration> sn_owners_;
public:
const sn_registration& at(size_t idx) const { return sn_owners_.at(idx); }
const boost::optional<sn_registration> find_registration(const crypto::public_key& pk) const;
void expire_old(uint64_t height);
const boost::optional<crypto::public_key> get_winner_pk(uint64_t height);
size_t size() const { return sn_owners_.size(); }
void add_registrations(const std::vector<sn_registration>& regs);
void remove_node(const crypto::public_key& pk);
};
/// Service node and its index
struct sn_idx {
crypto::public_key sn_pk;
/// index in the sorted list of service nodes for a particular block
size_t idx_in_quorum;
};
struct QuorumState {
std::vector<sn_idx> voters;
std::vector<sn_idx> to_test;
};
class dereg_tx_builder;
class linear_chain_generator
{
private:
test_generator gen_;
std::vector<test_event_entry>& events_;
std::vector<cryptonote::block> blocks_;
sn_list sn_list_;
/// keep new registrations here until the next block
std::vector<sn_registration> registration_buffer_;
cryptonote::account_base first_miner_;
public:
linear_chain_generator(std::vector<test_event_entry> &events)
: gen_(), events_(events)
{ }
uint64_t height() const { return get_block_height(blocks_.back()); }
cryptonote::account_base create_account();
void create_genesis_block();
void create_block(const std::vector<cryptonote::transaction>& txs = {});
cryptonote::block create_block_on_fork(const cryptonote::block& prev, const std::vector<cryptonote::transaction>& txs = {});
void rewind_until_v9();
void rewind_blocks_n(int n);
void rewind_blocks();
cryptonote::transaction create_tx(const cryptonote::account_base& miner,
const cryptonote::account_base& acc,
uint64_t amount,
uint64_t fee = TESTS_DEFAULT_FEE);
cryptonote::transaction create_registration_tx(const cryptonote::account_base& acc, const cryptonote::keypair& sn_keys);
cryptonote::transaction create_registration_tx();
const cryptonote::account_base& first_miner() const { return first_miner_; }
/// Note: should be carefull with returing a reference to vector elements
const cryptonote::block& chain_head() const { return blocks_.back(); }
/// get a copy of the service node list
sn_list get_sn_list() const { return sn_list_; }
void set_sn_list(const sn_list& list) { sn_list_ = list; }
QuorumState get_quorum_idxs(const cryptonote::block& block) const;
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;
dereg_tx_builder build_deregister(const crypto::public_key& pk);
crypto::public_key get_test_pk(uint32_t idx) const;
boost::optional<uint32_t> get_idx_in_tested(const crypto::public_key& pk, uint64_t height) const;
void deregister(const crypto::public_key& pk);
};
class dereg_tx_builder {
linear_chain_generator& gen_;
const crypto::public_key& pk_;
/// the height at which `pk_` is to be found
boost::optional<uint64_t> height_ = boost::none;
boost::optional<uint64_t> fee_ = boost::none;
boost::optional<const std::vector<sn_idx>&> voters_ = boost::none;
public:
dereg_tx_builder(linear_chain_generator& gen, const crypto::public_key& pk)
: gen_(gen), pk_(pk)
{}
dereg_tx_builder&& with_height(uint64_t height) {
height_ = height;
return std::move(*this);
}
dereg_tx_builder&& with_fee(uint64_t fee) {
fee_ = fee;
return std::move(*this);
}
dereg_tx_builder&& with_voters(const std::vector<sn_idx>& voters)
{
voters_ = voters;
return std::move(*this);
}
cryptonote::transaction build()
{
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));
}
};
inline cryptonote::difficulty_type get_test_difficulty() {return 1;}
void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height);
@ -236,23 +395,6 @@ bool construct_miner_tx_with_extra_output(cryptonote::transaction& tx,
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
const cryptonote::account_public_address& miner_address, cryptonote::transaction& tx,
uint64_t fee, cryptonote::keypair* p_txkey = 0);
bool construct_tx_to_key(const std::vector<test_event_entry>& events,
cryptonote::transaction& tx,
const cryptonote::block& blk_head,
const cryptonote::account_base& from,
const cryptonote::account_base& to,
uint64_t amount);
struct register_info {
const cryptonote::keypair& service_node_keypair;
const std::vector<uint64_t>& portions;
uint64_t operator_cut;
const std::vector<cryptonote::account_public_address>& addresses;
};
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx,
const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to,
uint64_t amount, uint64_t fee, size_t nmix, bool stake=false, const std::vector<uint8_t>& extra = {}, uint64_t unlock_time=0);
cryptonote::transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const cryptonote::block& blk_head,
const cryptonote::account_base& acc_from, const cryptonote::account_base& acc_to,
@ -266,6 +408,103 @@ void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& event
std::vector<cryptonote::tx_source_entry>& sources,
std::vector<cryptonote::tx_destination_entry>& destinations, uint64_t *change_amount = nullptr);
class TxBuilder {
/// required fields
const std::vector<test_event_entry>& m_events;
cryptonote::transaction& m_tx;
const cryptonote::block& m_head;
const cryptonote::account_base& m_from;
const cryptonote::account_base& m_to;
uint64_t m_amount;
/// optional fields
boost::optional<uint64_t> m_fee;
boost::optional<std::vector<uint8_t>> m_extra;
boost::optional<uint64_t> m_unlock_time;
bool m_per_output_unlock = false;
bool m_is_staking = false;
/// this makes sure we didn't forget to build it
bool m_finished = false;
public:
TxBuilder(const std::vector<test_event_entry>& events,
cryptonote::transaction& tx,
const cryptonote::block& head,
const cryptonote::account_base& from,
const cryptonote::account_base& to,
uint64_t amount)
: m_events(events)
, m_tx(tx)
, m_head(head)
, m_from(from)
, m_to(to)
, m_amount(amount)
{}
TxBuilder&& with_fee(uint64_t fee) {
m_fee = fee;
return std::move(*this);
}
TxBuilder&& with_extra(const std::vector<uint8_t>& extra) {
m_extra = extra;
return std::move(*this);
}
TxBuilder&& is_staking(bool val) {
m_is_staking = val;
return std::move(*this);
}
TxBuilder&& with_unlock_time(uint64_t val) {
m_unlock_time = val;
return std::move(*this);
}
TxBuilder&& with_per_output_unlock(bool val) {
m_per_output_unlock = val;
return std::move(*this);
}
~TxBuilder() {
if (!m_finished) {
std::cerr << "Tx building not finished\n";
abort();
}
}
bool build()
{
m_finished = true;
std::vector<cryptonote::tx_source_entry> sources;
std::vector<cryptonote::tx_destination_entry> destinations;
uint64_t change_amount;
const auto fee = m_fee ? *m_fee : TESTS_DEFAULT_FEE;
const auto nmix = 9;
fill_tx_sources_and_destinations(
m_events, m_head, m_from, m_to, m_amount, fee, nmix, sources, destinations, &change_amount);
const bool is_subaddr = false;
cryptonote::tx_destination_entry change_addr{ change_amount, m_from.get_keys().m_account_address, is_subaddr };
std::vector<uint8_t> extra;
if (m_extra) extra = *m_extra;
const auto unlock_time = m_unlock_time ? *m_unlock_time : 0;
return cryptonote::construct_tx(
m_from.get_keys(), sources, destinations, change_addr, extra, m_tx, unlock_time, m_is_staking, m_per_output_unlock);
}
};
/// Get the amount transferred to `account` in `tx` as output `i`
uint64_t get_amount(const cryptonote::account_base& account, const cryptonote::transaction& tx, int i);
uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx);
@ -688,8 +927,8 @@ cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entr
const cryptonote::tx_extra_service_node_deregister& deregister, uint64_t fee = 0);
#define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
cryptonote::transaction TX_NAME; \
construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \
cryptonote::transaction TX_NAME; \
TxBuilder(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT).build(); \
VEC_EVENTS.push_back(TX_NAME);
#define MAKE_TX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, 9, HEAD)
@ -697,7 +936,7 @@ cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entr
#define MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
{ \
cryptonote::transaction t; \
construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \
TxBuilder(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT).build(); \
SET_NAME.push_back(t); \
VEC_EVENTS.push_back(t); \
}

View file

@ -180,7 +180,8 @@ void make_rct_tx(eventV& events,
uint64_t amount)
{
txs.emplace_back();
bool success = construct_tx_to_key(events, txs.back(), blk_head, from, to, amount);
bool success = TxBuilder(events, txs.back(), blk_head, from, to, amount).build();
/// TODO: beter error message
if (!success) throw std::exception();
events.push_back(txs.back());
@ -225,7 +226,7 @@ bool gen_simple_chain_001::generate(eventV& events)
events.push_back(chain.back());
/// Note: to create N RingCT transactions need at least 10 + N outputs
while (chain.size() < 8) {
while (chain.size() < 10) {
chain.emplace_back();
const auto idx = chain.size() - 1;
generator.construct_block(chain[idx], chain[idx-1], miner);

View file

@ -54,42 +54,46 @@ namespace
bool gen_ring_signature_1::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
linear_chain_generator gen(events);
gen.create_genesis_block();
// events
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); // 0
MAKE_ACCOUNT(events, some_account_1); // 1
MAKE_ACCOUNT(events, some_account_2); // 2
MAKE_ACCOUNT(events, bob_account); // 3
MAKE_ACCOUNT(events, alice_account); // 4
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account); // 5
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 6
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_account); // 7
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_account); // 8
REWIND_BLOCKS(events, blk_5, blk_4, miner_account); // <N blocks>
REWIND_BLOCKS(events, blk_5r, blk_5, miner_account); // <N blocks>
MAKE_TX_LIST_START(events, txs_blk_6, miner_account, bob_account, MK_COINS(1), blk_5); // 9 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5); // 10 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5); // 11 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(20) + rnd_20, blk_5); // 12 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 13 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 14 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 15 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 16 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 17 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 18 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 19 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(20) + rnd_20, blk_5); // 20 + 2N
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_2, MK_COINS(20) + rnd_20, blk_5); // 21 + 2N
MAKE_NEXT_BLOCK_TX_LIST(events, blk_6, blk_5r, miner_account, txs_blk_6); // 22 + 2N
DO_CALLBACK(events, "check_balances_1"); // 23 + 2N
REWIND_BLOCKS(events, blk_6r, blk_6, miner_account); // <N blocks>
// 129 = 11 + 11 + 20 + 29 + 29 + 29
MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - TESTS_DEFAULT_FEE, 9, blk_6); // 24 + 3N
MAKE_NEXT_BLOCK_TX1(events, blk_7, blk_6r, miner_account, tx_0); // 25 + 3N
DO_CALLBACK(events, "check_balances_2"); // 26 + 3N
const auto miner = gen.first_miner();
const auto bob = gen.create_account(); /// event 1
const auto alice = gen.create_account(); /// event 2
const auto some_account_1 = gen.create_account();
const auto some_account_2 = gen.create_account();
/// give the miner some outputs to spend and ulock them
gen.rewind_blocks_n(20);
gen.rewind_blocks();
std::vector<cryptonote::transaction> txs;
txs.push_back( gen.create_tx(miner, bob, MK_COINS(1)) );
txs.push_back( gen.create_tx(miner, bob, MK_COINS(11) + rnd_11) );
txs.push_back( gen.create_tx(miner, bob, MK_COINS(11) + rnd_11) );
txs.push_back( gen.create_tx(miner, bob, MK_COINS(20) + rnd_20) );
txs.push_back( gen.create_tx(miner, bob, MK_COINS(29) + rnd_29) );
txs.push_back( gen.create_tx(miner, bob, MK_COINS(29) + rnd_29) );
txs.push_back( gen.create_tx(miner, bob, MK_COINS(29) + rnd_29) );
txs.push_back( gen.create_tx(miner, some_account_1, MK_COINS(11) + rnd_11) );
txs.push_back( gen.create_tx(miner, some_account_1, MK_COINS(11) + rnd_11) );
txs.push_back( gen.create_tx(miner, some_account_1, MK_COINS(11) + rnd_11) );
txs.push_back( gen.create_tx(miner, some_account_1, MK_COINS(11) + rnd_11) );
txs.push_back( gen.create_tx(miner, some_account_1, MK_COINS(20) + rnd_20) );
txs.push_back( gen.create_tx(miner, some_account_2, MK_COINS(20) + rnd_20) );
gen.create_block(txs);
gen.rewind_blocks();
DO_CALLBACK(events, "check_balances_1");
auto tx = gen.create_tx(bob, alice, MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - TESTS_DEFAULT_FEE);
gen.create_block({tx});
DO_CALLBACK(events, "check_balances_2");
return true;
}
@ -98,11 +102,11 @@ bool gen_ring_signature_1::check_balances_1(cryptonote::core& c, size_t ev_index
{
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_1");
m_bob_account = boost::get<account_base>(events[3]);
m_alice_account = boost::get<account_base>(events[4]);
m_bob_account = boost::get<account_base>(events[1]);
m_alice_account = boost::get<account_base>(events[2]);
std::vector<block> blocks;
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
bool r = c.get_blocks(0, 1000, blocks);
CHECK_TEST_CONDITION(r);
std::vector<cryptonote::block> chain;
@ -120,7 +124,7 @@ bool gen_ring_signature_1::check_balances_2(cryptonote::core& c, size_t ev_index
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_2");
std::vector<block> blocks;
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
bool r = c.get_blocks(0, 1000, blocks);
CHECK_TEST_CONDITION(r);
std::vector<cryptonote::block> chain;

View file

@ -103,415 +103,6 @@ cryptonote::keypair get_static_keys(size_t idx) {
return keypair;
}
struct last_reward_point {
uint64_t height;
uint64_t priority;
};
bool operator<(const last_reward_point& lhs, const last_reward_point& rhs) {
if (lhs.height != rhs.height) {
return lhs.height < rhs.height;
}
return lhs.priority < rhs.priority;
}
struct sn_registration {
uint64_t valid_until; /// block height
cryptonote::keypair keys;
sn_contributor_t contribution;
last_reward_point last_reward;
};
/// Service node and its index
struct sn_idx {
crypto::public_key sn_pk;
/// index in the sorted list of service nodes for a particular block
size_t idx_in_quorum;
};
struct QuorumState {
std::vector<sn_idx> voters;
std::vector<sn_idx> to_test;
};
class sn_list
{
std::vector<sn_registration> sn_owners_;
public:
const sn_registration& at(size_t idx) const { return sn_owners_.at(idx); }
const boost::optional<sn_registration> find_registration(const crypto::public_key& pk) const;
void expire_old(uint64_t height);
const boost::optional<crypto::public_key> get_winner_pk(uint64_t height);
size_t size() const { return sn_owners_.size(); }
void add_registrations(const std::vector<sn_registration>& regs);
void remove_node(const crypto::public_key& pk);
};
inline void sn_list::remove_node(const crypto::public_key& pk)
{
const auto it =
std::find_if(sn_owners_.begin(), sn_owners_.end(), [pk](const sn_registration& sn) { return sn.keys.pub == pk; });
if (it != sn_owners_.end()) sn_owners_.erase(it); else abort();
}
inline void sn_list::add_registrations(const std::vector<sn_registration>& regs)
{
sn_owners_.insert(sn_owners_.begin(), regs.begin(), regs.end());
std::sort(sn_owners_.begin(), sn_owners_.end(),
[](const sn_registration &a, const sn_registration &b) {
return memcmp(reinterpret_cast<const void*>(&a.keys.pub), reinterpret_cast<const void*>(&b.keys.pub),
sizeof(a.keys.pub)) < 0;
});
}
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_.erase(new_end, sn_owners_.end());
}
inline const boost::optional<sn_registration> sn_list::find_registration(const crypto::public_key& pk) const
{
const auto it =
std::find_if(sn_owners_.begin(), sn_owners_.end(), [pk](const sn_registration& sn) { return sn.keys.pub == pk; });
if (it == sn_owners_.end()) return boost::none;
return *it;
}
inline const boost::optional<crypto::public_key> sn_list::get_winner_pk(uint64_t height)
{
if (sn_owners_.empty()) return boost::none;
auto it =
std::min_element(sn_owners_.begin(), sn_owners_.end(), [](const sn_registration& lhs, const sn_registration& rhs) {
return lhs.last_reward < rhs.last_reward;
});
it->last_reward.height = height;
return it->keys.pub;
}
class dereg_tx_builder;
class linear_chain_generator
{
private:
test_generator gen_;
std::vector<test_event_entry>& events_;
std::vector<cryptonote::block> blocks_;
sn_list sn_list_;
/// keep new registrations here until the next block
std::vector<sn_registration> registration_buffer_;
cryptonote::account_base first_miner_;
public:
linear_chain_generator(std::vector<test_event_entry> &events)
: gen_(), events_(events)
{ }
uint64_t height() const { return get_block_height(blocks_.back()); }
cryptonote::account_base create_account();
void create_genesis_block();
void create_block(const std::vector<cryptonote::transaction>& txs = {});
void rewind_until_v9();
void rewind_blocks_n(int n);
void rewind_blocks();
cryptonote::transaction create_tx(const cryptonote::account_base& miner,
const cryptonote::account_base& acc,
uint64_t amount,
uint64_t fee = TESTS_DEFAULT_FEE);
cryptonote::transaction create_registration_tx(const cryptonote::account_base& acc, const cryptonote::keypair& sn_keys);
cryptonote::transaction create_registration_tx();
const cryptonote::account_base& first_miner() const { return first_miner_; }
/// Note: should be carefull with returing a reference to vector elements
const cryptonote::block& chain_head() const { return blocks_.back(); }
QuorumState get_quorum_idxs(const cryptonote::block& block) const;
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;
dereg_tx_builder build_deregister(const crypto::public_key& pk);
crypto::public_key get_test_pk(uint32_t idx) const;
boost::optional<uint32_t> get_idx_in_tested(const crypto::public_key& pk, uint64_t height) const;
void remove_node(const crypto::public_key& pk) {
sn_list_.remove_node(pk);
}
};
class dereg_tx_builder {
linear_chain_generator& gen_;
const crypto::public_key& pk_;
/// the height at which `pk_` is to be found
boost::optional<uint64_t> height_ = boost::none;
boost::optional<uint64_t> fee_ = boost::none;
boost::optional<const std::vector<sn_idx>&> voters_ = boost::none;
public:
dereg_tx_builder(linear_chain_generator& gen, const crypto::public_key& pk)
: gen_(gen), pk_(pk)
{}
dereg_tx_builder&& with_height(uint64_t height) {
height_ = height;
return std::move(*this);
}
dereg_tx_builder&& with_fee(uint64_t fee) {
fee_ = fee;
return std::move(*this);
}
dereg_tx_builder&& with_voters(const std::vector<sn_idx>& voters)
{
voters_ = voters;
return std::move(*this);
}
cryptonote::transaction build()
{
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));
}
};
dereg_tx_builder linear_chain_generator::build_deregister(const crypto::public_key& pk)
{
return dereg_tx_builder(*this, pk);
}
cryptonote::account_base linear_chain_generator::create_account()
{
cryptonote::account_base account;
account.generate();
events_.push_back(account);
return account;
}
void linear_chain_generator::create_genesis_block()
{
constexpr uint64_t ts_start = 1338224400;
first_miner_.generate();
cryptonote::block gen_block;
gen_.construct_block(gen_block, first_miner_, ts_start);
events_.push_back(gen_block);
blocks_.push_back(gen_block);
}
void linear_chain_generator::create_block(const std::vector<cryptonote::transaction>& txs)
{
const auto prev = blocks_.back();
const auto height = get_block_height(prev) + 1;
const auto& winner_pk = sn_list_.get_winner_pk(height);
const auto& sn_pk = winner_pk ? *winner_pk : crypto::null_pkey;
std::vector<sn_contributor_t> contribs = { { { crypto::null_pkey, crypto::null_pkey }, STAKING_PORTIONS } };
if (winner_pk) {
const auto& reg = sn_list_.find_registration(*winner_pk);
if (reg) {
contribs = { reg->contribution };
}
}
cryptonote::block blk;
gen_.construct_block(blk, prev, first_miner_, { txs.begin(), txs.end() }, sn_pk, contribs);
events_.push_back(blk);
/// 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);
blocks_.push_back(blk);
}
void linear_chain_generator::rewind_until_v9()
{
gen_.set_hf_version(8);
create_block();
gen_.set_hf_version(9);
create_block();
}
void linear_chain_generator::rewind_blocks_n(int n)
{
for (auto i = 0; i < n; ++i) {
create_block();
}
}
void linear_chain_generator::rewind_blocks()
{
rewind_blocks_n(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
}
QuorumState linear_chain_generator::get_quorum_idxs(const cryptonote::block& block) const
{
if (sn_list_.size() <= service_nodes::QUORUM_SIZE) {
std::cerr << "Not enough service nodes\n";
return {};
}
std::vector<size_t> pub_keys_indexes;
{
uint64_t seed = 0;
const crypto::hash block_hash = cryptonote::get_block_hash(block);
std::memcpy(&seed, block_hash.data, std::min(sizeof(seed), sizeof(block_hash.data)));
pub_keys_indexes.resize(sn_list_.size());
for (size_t i = 0; i < pub_keys_indexes.size(); i++) {
pub_keys_indexes[i] = i;
}
service_nodes::loki_shuffle(pub_keys_indexes, seed);
}
QuorumState quorum;
for (auto i = 0u; i < service_nodes::QUORUM_SIZE; ++i) {
quorum.voters.push_back({ sn_list_.at(pub_keys_indexes[i]).keys.pub, i });
}
for (auto i = service_nodes::QUORUM_SIZE; i < pub_keys_indexes.size(); ++i) {
quorum.to_test.push_back({ sn_list_.at(pub_keys_indexes[i]).keys.pub, i });
}
return quorum;
}
QuorumState linear_chain_generator::get_quorum_idxs(uint64_t height) const
{
const auto block = blocks_.at(height);
return get_quorum_idxs(block);
}
cryptonote::transaction linear_chain_generator::create_tx(const cryptonote::account_base& miner,
const cryptonote::account_base& acc,
uint64_t amount,
uint64_t fee)
{
cryptonote::transaction t;
construct_tx_to_key(events_, t, blocks_.back(), miner, acc, amount, fee, 9);
events_.push_back(t);
return t;
}
cryptonote::transaction linear_chain_generator::create_registration_tx(const cryptonote::account_base& acc,
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::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN);
const auto reg_idx = registration_buffer_.size();
registration_buffer_.push_back({ expires, sn_keys, contr, { height(), reg_idx } });
return make_default_registration_tx(events_, acc, sn_keys, blocks_.back());
}
cryptonote::transaction linear_chain_generator::create_registration_tx()
{
const auto sn_keys = keypair::generate(hw::get_device("default"));
return create_registration_tx(first_miner_, sn_keys);
}
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
{
cryptonote::tx_extra_service_node_deregister deregister;
deregister.block_height = height;
const auto idx = get_idx_in_tested(pk, height);
if (!idx) { MERROR("service node could not be found in the servcie node list"); throw std::exception(); }
deregister.service_node_index = *idx; /// idx inside nodes to test
/// need to create MIN_VOTES_TO_KICK_SERVICE_NODE (7) votes
for (const auto voter : voters) {
const auto reg = sn_list_.find_registration(voter.sn_pk);
if (!reg) return {};
const auto pk = reg->keys.pub;
const auto sk = reg->keys.sec;
const auto signature =
loki::service_node_deregister::sign_vote(deregister.block_height, deregister.service_node_index, pk, sk);
deregister.votes.push_back({ signature, (uint32_t)voter.idx_in_quorum });
}
const auto deregister_tx = make_deregistration_tx(events_, first_miner_, blocks_.back(), deregister, fee);
events_.push_back(deregister_tx);
return deregister_tx;
}
crypto::public_key linear_chain_generator::get_test_pk(uint32_t idx) const
{
const auto& to_test = get_quorum_idxs(height()).to_test;
return to_test.at(idx).sn_pk;
}
boost::optional<uint32_t> linear_chain_generator::get_idx_in_tested(const crypto::public_key& pk, uint64_t height) const
{
const auto& to_test = get_quorum_idxs(height).to_test;
for (const auto& sn : to_test) {
if (sn.sn_pk == pk) return sn.idx_in_quorum - service_nodes::QUORUM_SIZE;
}
return boost::none;
}
//-----------------------------------------------------------------------------------------------------
//---------------------------------- Generate Service Nodes -------------------------------------------
@ -815,7 +406,7 @@ 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.remove_node(pk);
gen.deregister(pk);
}
/// Register the node again
@ -900,7 +491,7 @@ bool test_deregisters_on_split::generate(std::vector<test_event_entry> &events)
fork.create_block({ dereg_B });
/// actually remove pk form the local service node list
fork.remove_node(pk);
fork.deregister(pk);
/// one more block on alt chain to switch
fork.create_block();
@ -1036,7 +627,7 @@ 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.remove_node(pk);
gen.deregister(pk);
}
/// create a new service node (B) in the next block