add core tests for swarms (#321)

This commit is contained in:
Maxim Shishmarev 2018-11-16 16:07:41 +11:00 committed by Doyle
parent 2ba498242e
commit 6d763041e0
7 changed files with 227 additions and 18 deletions

View File

@ -916,14 +916,17 @@ namespace cryptonote
return r;
}
//---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, bool is_staking, bool per_output_unlock)
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, uint8_t hf_version, bool is_staking)
{
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
std::vector<tx_destination_entry> destinations_copy = destinations;
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct::RangeProofBorromean, NULL, is_staking, per_output_unlock);
const rct::RangeProofType rp_type = (hf_version < network_version_10_bulletproofs) ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof;
const bool per_output_unlock = (hf_version >= network_version_9_service_nodes);
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rp_type, NULL, is_staking, per_output_unlock);
}
//---------------------------------------------------------------
bool generate_genesis_block(

View File

@ -170,7 +170,7 @@ namespace cryptonote
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, bool is_staking = false, bool per_output_unlock = false);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, uint8_t hf_version = cryptonote::network_version_7, bool is_staking = false);
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool per_output_unlock = false, bool shuffle_outs = true);
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool is_staking_tx = false, bool per_output_unlock = false);

View File

@ -88,6 +88,32 @@ void linear_chain_generator::create_block(const std::vector<cryptonote::transact
blocks_.push_back(blk);
}
void linear_chain_generator::continue_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version)
{
assert(gen_.m_hf_version < hard_fork_version);
for (auto i = 0u; i < hard_forks.size() - 1; ++i) {
const uint8_t ver = hard_forks[i].first;
const uint64_t height = hard_forks[i].second;
if (ver < get_hf_version()) continue;
auto cur_height = blocks_.size();
uint64_t next_fork_height = hard_forks[i + 1].second;
uint64_t blocks_till_next_hardfork = next_fork_height - cur_height;
rewind_blocks_n(blocks_till_next_hardfork);
gen_.m_hf_version = hard_forks[i + 1].first;
create_block();
}
assert(gen_.m_hf_version == hard_fork_version);
}
void linear_chain_generator::rewind_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version)
{
if (hard_forks.size() > 1)
@ -111,6 +137,10 @@ void linear_chain_generator::rewind_until_version(const std::vector<std::pair<ui
}
}
int linear_chain_generator::get_hf_version() const {
return gen_.m_hf_version;
}
void linear_chain_generator::rewind_until_v9()
{
@ -209,7 +239,7 @@ cryptonote::transaction linear_chain_generator::create_tx(const cryptonote::acco
uint64_t fee)
{
cryptonote::transaction t;
TxBuilder(events_, t, blocks_.back(), miner, acc, amount).with_fee(fee).build();
TxBuilder(events_, t, blocks_.back(), miner, acc, amount, gen_.m_hf_version).with_fee(fee).build();
events_.push_back(t);
return t;
}
@ -222,7 +252,7 @@ cryptonote::transaction linear_chain_generator::create_registration_tx(const cry
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());
return make_default_registration_tx(events_, acc, sn_keys, blocks_.back(), gen_.m_hf_version);
}
cryptonote::transaction linear_chain_generator::create_registration_tx()
@ -262,7 +292,7 @@ cryptonote::transaction linear_chain_generator::create_deregister_tx(const crypt
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);
const auto deregister_tx = make_deregistration_tx(events_, first_miner_, blocks_.back(), deregister, gen_.m_hf_version, fee);
events_.push_back(deregister_tx);
@ -619,7 +649,8 @@ cryptonote::transaction make_registration_tx(std::vector<test_event_entry>& even
uint64_t operator_cut,
const std::vector<cryptonote::account_public_address>& addresses,
const std::vector<uint64_t>& portions,
const cryptonote::block& head)
const cryptonote::block& head,
uint8_t hf_version)
{
const auto new_height = cryptonote::get_block_height(head) + 1;
const auto staking_requirement = service_nodes::get_staking_requirement(cryptonote::FAKECHAIN, new_height);
@ -647,7 +678,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));
TxBuilder(events, tx, head, account, account, amount).is_staking(true).with_extra(extra).with_unlock_time(unlock_time).with_per_output_unlock(true).build();
TxBuilder(events, tx, head, account, account, amount, hf_version).is_staking(true).with_extra(extra).with_unlock_time(unlock_time).with_per_output_unlock(true).build();
events.push_back(tx);
return tx;
}
@ -655,7 +686,9 @@ cryptonote::transaction make_registration_tx(std::vector<test_event_entry>& even
cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entry>& events,
const cryptonote::account_base& account,
const cryptonote::block& head,
const cryptonote::tx_extra_service_node_deregister& deregister, uint64_t fee)
const cryptonote::tx_extra_service_node_deregister& deregister,
uint8_t hf_version,
uint64_t fee)
{
cryptonote::transaction tx;
@ -669,7 +702,7 @@ cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entr
const uint64_t amount = 0;
if (fee) TxBuilder(events, tx, head, account, account, amount).with_fee(fee).with_extra(extra).with_per_output_unlock(true).build();
if (fee) TxBuilder(events, tx, head, account, account, amount, hf_version).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;
@ -680,9 +713,10 @@ cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entr
cryptonote::transaction make_default_registration_tx(std::vector<test_event_entry>& events,
const cryptonote::account_base& account,
const cryptonote::keypair& service_node_keys,
const cryptonote::block& head)
const cryptonote::block& head,
uint8_t hf_version)
{
return make_registration_tx(events, account, service_node_keys, 0, { account.get_keys().m_account_address }, { STAKING_PORTIONS }, head);
return make_registration_tx(events, account, service_node_keys, 0, { account.get_keys().m_account_address }, { STAKING_PORTIONS }, head, hf_version);
}
struct output_index {

View File

@ -313,7 +313,10 @@ class linear_chain_generator
cryptonote::block create_block_on_fork(const cryptonote::block& prev, const std::vector<cryptonote::transaction>& txs = {});
int get_hf_version() const;
void rewind_until_v9();
void continue_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version);
void rewind_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version);
void rewind_blocks_n(int n);
void rewind_blocks();
@ -427,6 +430,7 @@ class TxBuilder {
/// required fields
const std::vector<test_event_entry>& m_events;
const uint8_t m_hf_version;
cryptonote::transaction& m_tx;
const cryptonote::block& m_head;
const cryptonote::account_base& m_from;
@ -449,13 +453,15 @@ public:
const cryptonote::block& head,
const cryptonote::account_base& from,
const cryptonote::account_base& to,
uint64_t amount)
uint64_t amount,
uint8_t hf_version = cryptonote::network_version_9_service_nodes)
: m_events(events)
, m_tx(tx)
, m_head(head)
, m_from(from)
, m_to(to)
, m_amount(amount)
, m_hf_version(hf_version)
, m_fee(TESTS_DEFAULT_FEE)
, m_unlock_time(0)
{}
@ -509,7 +515,7 @@ public:
cryptonote::tx_destination_entry change_addr{ change_amount, m_from.get_keys().m_account_address, is_subaddr };
return cryptonote::construct_tx(
m_from.get_keys(), sources, destinations, change_addr, m_extra, m_tx, m_unlock_time, m_is_staking, m_per_output_unlock);
m_from.get_keys(), sources, destinations, change_addr, m_extra, m_tx, m_unlock_time, m_hf_version, m_is_staking);
}
};
@ -927,18 +933,20 @@ cryptonote::transaction make_registration_tx(std::vector<test_event_entry>& even
uint64_t operator_cut,
const std::vector<cryptonote::account_public_address>& addresses,
const std::vector<uint64_t>& portions,
const cryptonote::block& head);
const cryptonote::block& head,
uint8_t hf_version);
cryptonote::transaction make_default_registration_tx(std::vector<test_event_entry>& events,
const cryptonote::account_base& account,
const cryptonote::keypair& service_node_keys,
const cryptonote::block& head);
const cryptonote::block& head,
uint8_t hf_version);
cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entry>& events,
const cryptonote::account_base& account,
const cryptonote::block& head,
const cryptonote::tx_extra_service_node_deregister& deregister, uint64_t fee = 0);
const cryptonote::tx_extra_service_node_deregister& deregister, uint8_t hf_version, uint64_t fee);
#define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
cryptonote::transaction TX_NAME; \

View File

@ -119,6 +119,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(test_deregisters_on_split);
GENERATE_AND_PLAY(deregister_too_old);
GENERATE_AND_PLAY(sn_test_rollback);
GENERATE_AND_PLAY(test_swarms_basic);
}
if (run_all)

View File

@ -30,6 +30,7 @@
#include "chaingen.h"
#include "service_nodes.h"
#include "cryptonote_core/service_node_list.h"
using namespace std;
@ -91,6 +92,8 @@ static const std::pair<const char*, const char*> service_node_keys[] = {
static const auto SN_KEYS_COUNT = sizeof(service_node_keys) / sizeof(service_node_keys[0]);
static_assert(SN_KEYS_COUNT == 25, "Need to adjust the key count (for readability)");
cryptonote::keypair get_static_keys(size_t idx) {
if (idx >= SN_KEYS_COUNT) { MERROR("out of bounds"); throw std::exception(); }
@ -601,7 +604,8 @@ bool sn_test_rollback::generate(std::vector<test_event_entry>& events)
linear_chain_generator gen(events);
gen.create_genesis_block();
gen.rewind_until_v9();
const get_test_options<sn_test_rollback> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
/// generate some outputs and unlock them
gen.rewind_blocks_n(20);
@ -711,3 +715,139 @@ bool sn_test_rollback::test_registrations(cryptonote::core& c, size_t ev_index,
return true;
}
//-----------------------------------------------------------------------------------------------------
//------------------------------------- Test Swarm Basics ---------------------------------------------
//-----------------------------------------------------------------------------------------------------
test_swarms_basic::test_swarms_basic() {
REGISTER_CALLBACK("test_initial_swarms", test_swarms_basic::test_initial_swarms);
REGISTER_CALLBACK("test_with_more_sn", test_swarms_basic::test_with_more_sn);
REGISTER_CALLBACK("test_after_deregisters", test_swarms_basic::test_after_deregisters);
}
bool test_swarms_basic::generate(std::vector<test_event_entry>& events)
{
linear_chain_generator gen(events);
const get_test_options<test_swarms_basic> test_options = {};
gen.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
/// Create some service nodes before hf version 10
constexpr size_t init_sn_count = 16;
gen.rewind_blocks_n(100);
gen.rewind_blocks();
/// register some service nodes
std::vector<cryptonote::transaction> reg_txs;
for (auto i = 0u; i < init_sn_count; ++i) {
const auto sn = get_static_keys(i);
const auto tx = gen.create_registration_tx(gen.first_miner(), sn);
reg_txs.push_back(tx);
}
gen.create_block(reg_txs);
/// create a few blocks with active service nodes
gen.rewind_blocks_n(5);
if (gen.get_hf_version() != network_version_9_service_nodes) {
std::cerr << "wrong hf version\n";
return false;
}
gen.continue_until_version(test_options.hard_forks, network_version_10_bulletproofs);
/// test that we now have swarms
DO_CALLBACK(events, "test_initial_swarms");
/// rewind some blocks and register more service nodes
for (auto i = init_sn_count; i < SN_KEYS_COUNT; ++i) {
const auto sn = get_static_keys(i);
const auto tx = gen.create_registration_tx(gen.first_miner(), sn);
gen.create_block({tx});
}
/// test that another swarm has been created
DO_CALLBACK(events, "test_with_more_sn");
/// deregister a few snodes and test that both swarms are alive
std::vector<cryptonote::transaction> dereg_txs;
for (auto i = 0u; i < service_nodes::SWARM_BUFFER; ++i) {
const auto pk = gen.get_test_pk(i);
const auto tx = gen.build_deregister(pk).build();
dereg_txs.push_back(tx);
}
gen.create_block(dereg_txs);
DO_CALLBACK(events, "test_after_deregisters");
return true;
}
bool test_swarms_basic::test_initial_swarms(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
{
DEFINE_TESTS_ERROR_CONTEXT("test_swarms_basic::test_initial_swarms");
/// Check that there is one active swarm and the swarm queue is not empty
const auto sn_list = c.get_service_node_list_state({});
std::map<service_nodes::swarm_id_t, std::vector<crypto::public_key>> swarms;
for (const auto& entry : sn_list) {
const auto id = entry.info.swarm_id;
swarms[id].push_back(entry.pubkey);
}
/// One of the swarms represent a queue
CHECK_EQ(swarms.size(), 2);
const size_t queue_size = swarms.at(service_nodes::QUEUE_SWARM_ID).size();
/// No deregisters, so the swarms queue should be full
CHECK_TEST_CONDITION(queue_size > service_nodes::SWARM_BUFFER);
/// We shouldn't have too many nodes in the queue
CHECK_TEST_CONDITION(queue_size < service_nodes::SWARM_BUFFER + service_nodes::MAX_SWARM_SIZE);
return true;
}
bool test_swarms_basic::test_with_more_sn(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
{
DEFINE_TESTS_ERROR_CONTEXT("test_swarms_basic::test_with_more_sn");
const auto sn_list = c.get_service_node_list_state({});
std::map<service_nodes::swarm_id_t, std::vector<crypto::public_key>> swarms;
for (const auto& entry : sn_list) {
const auto id = entry.info.swarm_id;
swarms[id].push_back(entry.pubkey);
}
CHECK_EQ(swarms.size(), 3);
return true;
}
bool test_swarms_basic::test_after_deregisters(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
{
DEFINE_TESTS_ERROR_CONTEXT("test_swarms_basic::test_after_deregisters");
const auto sn_list = c.get_service_node_list_state({});
std::map<service_nodes::swarm_id_t, std::vector<crypto::public_key>> swarms;
for (const auto& entry : sn_list) {
const auto id = entry.info.swarm_id;
swarms[id].push_back(entry.pubkey);
}
/// The two swarms are still active, but the queue in now showing in the swarms
CHECK_TEST_CONDITION(swarms.find(service_nodes::QUEUE_SWARM_ID) == swarms.end());
CHECK_EQ(swarms.size(), 2);
return true;
}

View File

@ -191,3 +191,26 @@ public:
};
template<> struct get_test_options<sn_test_rollback>: public get_test_options<test_service_nodes_base> {};
//-------------------------------------------------------------------------------------------
class test_swarms_basic : public test_service_nodes_base
{
public:
test_swarms_basic();
bool generate(std::vector<test_event_entry>& events);
bool test_initial_swarms(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
bool test_with_more_sn(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
bool test_after_deregisters(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
};
template<>
struct get_test_options<test_swarms_basic>
{
const std::vector<std::pair<uint8_t, uint64_t>> hard_forks = { std::make_pair(7, 0),
std::make_pair(8, 1),
std::make_pair(9, 2),
std::make_pair(10, 150) };
const cryptonote::test_options test_options = { hard_forks };
};