mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
fixed some (previously working) core tests (#288)
This commit is contained in:
parent
87cfb05f55
commit
63337de3d9
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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); \
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue