mirror of https://github.com/oxen-io/oxen-core.git
Version 0.4.0 Release Candidate (#216)
* core: submit uptime proof immediately after registering * Increase visibility of autostaking prompts * quorum_cop: changed uptime proof prune timeout to 2 hours 10 minutes * cleanup: removed scope limiting block * check_tx_inputs: fix deregister double spend test to include deregisters from other heights * config: new testnet network id, genesis tx, and version bump * wallet2: fix testnet wallet blockheight approximation * Fix change in address format in RPC which broke parsing and pooling contributors (#184) * Fix service node endpoints for RPC to also use stdout (#185) * fixed some further rct core tests (#180) * Fix service node state by calling detached hooks on failure to switch to alt chain (#188) * fixed block verification core tests (#186) * fixed block verification core tests * core tests: removed gen_block_miner_tx_out_is_small which is only relevant to hardfork version 1 * Don't consider expired deregistrations when filling block template * Add unit tests for getting staking requirement (#191) * First service node test (#190) * core_tests: added service node tests * core_tests: check balance after registration tx * Fix underflow for popping rollback events (#189) * Move deregistration age check into check_tx_inputs * Zero initialise rct_signatures member txnFee is a uint64_t and has uninit values * Enforce that deregisters must be 0 fee since we skip checks * Add unit tests for vote validation (#193) * Add unit tests for deregistration validation (#194) * Mainnet checkpoint 86535, testnet 3591, 4166 * Bump version number * Add print_sr for getting staking requirement (#198) * Misc bugfixes (#203) * removed unnecessary cast to double during txfee+coinbase calculation * simplewallet: increased autostaking interval from 2 minutes to 40 * Fix casting issues from uint to int (#204) * core_tests: check service node registration and expiration (#195) * core_tests: check service node registration and deregistration * core_tests for service nodes: - include service nodes rewards when calculating account's balance - check that service nodes rewards have been received * fixed namespace error; reduced the scope of staking requirement constants * On blockchain inc/dec mark deregisters relayble based on age (#201) * Service nodes restore only 1 rollback bug (#206) * Fix restore 1 rollback event, ensure prevent rollback is always added * Remove adding prevent_rollback event at init It gets called in on block added generic anyway. * Log db exception, fix relation operators for vote/deregister lifetime (#207) * Filter relayable deregisters w/ check_tx_inputs instead of blockchain callbacks * Bump version to 0.3.7-beta * fix build with GCC 8.1.0 (#211) * Add temp hardfork rule in testnet for deregister lifetimes (#210) * Update testnet, remove testnet forks, remove checkpoints, update blockheight estimate (#212) * Don't ban peers for a bad vote, just drop their connection (#213) * Update to version 0.3.0 release candidate (#215)
This commit is contained in:
parent
ad3344b5cf
commit
25292b3e86
|
@ -177,6 +177,7 @@ namespace cryptonote
|
|||
ADD_CHECKPOINT(10, "4a7cd8b9bff380d48d6f3533a5e0509f8589cc77d18218b3f7218846e77738fc");
|
||||
ADD_CHECKPOINT(100, "01b8d33a50713ff837f8ad7146021b8e3060e0316b5e4afc407e46cdb50b6760");
|
||||
ADD_CHECKPOINT(1000, "5e3b0a1f931885bc0ab1d6ecdc625816576feae29e2f9ac94c5ccdbedb1465ac");
|
||||
ADD_CHECKPOINT(86535, "52b7c5a60b97bf1efbf0d63a0aa1a313e8f0abe4627eb354b0c5a73cb1f4391e");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -158,7 +158,7 @@ DISABLE_VS_WARNINGS(4244 4345)
|
|||
void account_base::create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey)
|
||||
{
|
||||
crypto::secret_key fake;
|
||||
memset(&fake, 0, sizeof(fake));
|
||||
memset(&unwrap(fake), 0, sizeof(fake));
|
||||
create_from_keys(address, fake, viewkey);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
|
|
|
@ -350,6 +350,7 @@ namespace cryptonote
|
|||
vout.clear();
|
||||
extra.clear();
|
||||
signatures.clear();
|
||||
rct_signatures = {};
|
||||
rct_signatures.type = rct::RCTTypeNull;
|
||||
set_hash_valid(false);
|
||||
set_blob_size_valid(false);
|
||||
|
|
|
@ -50,8 +50,6 @@
|
|||
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10
|
||||
|
||||
#define STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS 20
|
||||
#define STAKING_REQUIREMENT_LOCK_BLOCKS (30*24*30)
|
||||
#define STAKING_REQUIREMENT_LOCK_BLOCKS_TESTNET (30*24*2)
|
||||
#define STAKING_PORTIONS UINT64_C(0xfffffffffffffffc)
|
||||
#define MAX_NUMBER_OF_CONTRIBUTORS 4
|
||||
#define MIN_PORTIONS (STAKING_PORTIONS / MAX_NUMBER_OF_CONTRIBUTORS)
|
||||
|
@ -67,7 +65,7 @@ static_assert(STAKING_PORTIONS % 3 == 0, "Use a multiple of three, so that it di
|
|||
|
||||
#define UPTIME_PROOF_BUFFER_IN_SECONDS (5*60)
|
||||
#define UPTIME_PROOF_FREQUENCY_IN_SECONDS (60*60)
|
||||
#define UPTIME_PROOF_MAX_TIME_IN_SECONDS (UPTIME_PROOF_FREQUENCY_IN_SECONDS + (2 * UPTIME_PROOF_BUFFER_IN_SECONDS))
|
||||
#define UPTIME_PROOF_MAX_TIME_IN_SECONDS (UPTIME_PROOF_FREQUENCY_IN_SECONDS * 2 + UPTIME_PROOF_BUFFER_IN_SECONDS)
|
||||
|
||||
// MONEY_SUPPLY - total number coins to be generated
|
||||
#define MONEY_SUPPLY ((uint64_t)(-1))
|
||||
|
@ -192,13 +190,13 @@ namespace config
|
|||
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 156;
|
||||
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 157;
|
||||
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 158;
|
||||
uint16_t const P2P_DEFAULT_PORT = 38150;
|
||||
uint16_t const RPC_DEFAULT_PORT = 38151;
|
||||
uint16_t const ZMQ_RPC_DEFAULT_PORT = 38152;
|
||||
uint16_t const P2P_DEFAULT_PORT = 38156;
|
||||
uint16_t const RPC_DEFAULT_PORT = 38157;
|
||||
uint16_t const ZMQ_RPC_DEFAULT_PORT = 38158;
|
||||
boost::uuids::uuid const NETWORK_ID = { {
|
||||
0x5e, 0x3a, 0x78, 0x65, 0xe1, 0x6f, 0xca, 0xb8, 0x02, 0xa1, 0xdc, 0x17, 0x61, 0x64, 0x15, 0xbc,
|
||||
0x5f, 0x3a, 0x78, 0x65, 0xe1, 0x6f, 0xca, 0xb8, 0x02, 0xa1, 0xdc, 0x17, 0x61, 0x64, 0x15, 0xbe,
|
||||
} }; // Bender's daydream
|
||||
std::string const GENESIS_TX = "03011e001e01ff00018080c9db97f4fb270286d1689b3935f334d26cd9ecb7fc7eb4d67fde89a702058e58ea6811740e40764201c3e91c848f253b0d0193c945787192e1a362f00c2e7f18a80064d9ed4ad88c0f72000000000000000000000000000000000000000000000000000000000000000000";
|
||||
std::string const GENESIS_TX = "03011e001e01ff00018080c9db97f4fb270259b546996f69aa71abe4238995f41d780ab1abebcac9f00e808f147bdb9e3228420112573af8c309b69a1a646f41b5212ba7d9c4590bf86e04f36c486467cfef9d3d72000000000000000000000000000000000000000000000000000000000000000000";
|
||||
uint32_t const GENESIS_NONCE = 10001;
|
||||
|
||||
std::string const GOVERNANCE_WALLET_ADDRESS = "T6SUprTYE5rQpep9iQFxyPcKVd91DFR1fQ1Qsyqp5eYLiFc8XuYd3reRE71qDL8c3DXioUbDEpDFdaUpetnL37NS1R3rzoKxi";
|
||||
|
|
|
@ -876,6 +876,10 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
|
|||
pop_block_from_blockchain();
|
||||
}
|
||||
|
||||
// Revert all changes from switching to the alt chain before adding the original chain back in
|
||||
for (BlockchainDetachedHook* hook : m_blockchain_detached_hooks)
|
||||
hook->blockchain_detached(rollback_height);
|
||||
|
||||
// make sure the hard fork object updates its current version
|
||||
m_hardfork->reorganize_from_chain_height(rollback_height);
|
||||
|
||||
|
@ -944,7 +948,6 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
|||
if(!r || !bvc.m_added_to_main_chain)
|
||||
{
|
||||
MERROR("Failed to switch to alternative blockchain");
|
||||
|
||||
// rollback_blockchain_switching should be moved to two different
|
||||
// functions: rollback and apply_chain, but for now we pretend it is
|
||||
// just the latter (because the rollback was done above).
|
||||
|
@ -1106,6 +1109,11 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
|||
money_in_use += o.amount;
|
||||
partial_block_reward = false;
|
||||
|
||||
if (b.miner_tx.vout.size() == 0) {
|
||||
MERROR_VER("miner tx has no outputs");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version == 3) {
|
||||
for (auto &o: b.miner_tx.vout) {
|
||||
if (!is_valid_decomposed_amount(o.amount)) {
|
||||
|
@ -3016,6 +3024,14 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
|||
|
||||
if (tx.is_deregister_tx())
|
||||
{
|
||||
if (tx.rct_signatures.txnFee != 0)
|
||||
{
|
||||
tvc.m_invalid_input = true;
|
||||
tvc.m_verifivation_failed = true;
|
||||
MERROR_VER("TX version deregister should have 0 fee!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the inputs (votes) of the transaction have not been already been
|
||||
// submitted to the blockchain under another transaction using a different
|
||||
// combination of votes.
|
||||
|
@ -3040,6 +3056,33 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check if deregister is too old or too new to hold onto
|
||||
{
|
||||
const uint64_t curr_height = get_current_blockchain_height();
|
||||
if (deregister.block_height >= curr_height)
|
||||
{
|
||||
LOG_PRINT_L1("Received deregister tx for height: " << deregister.block_height
|
||||
<< " and service node: " << deregister.service_node_index
|
||||
<< ", is newer than current height: " << curr_height
|
||||
<< " blocks and has been rejected.");
|
||||
tvc.m_vote_ctx.m_invalid_block_height = true;
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t delta_height = curr_height - deregister.block_height;
|
||||
if (delta_height >= loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT)
|
||||
{
|
||||
LOG_PRINT_L1("Received deregister tx for height: " << deregister.block_height
|
||||
<< " and service node: " << deregister.service_node_index
|
||||
<< ", is older than: " << loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT
|
||||
<< " blocks and has been rejected. The current height is: " << curr_height);
|
||||
tvc.m_vote_ctx.m_invalid_block_height = true;
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t height = deregister.block_height;
|
||||
const size_t num_blocks_to_check = loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT;
|
||||
|
||||
|
@ -3066,15 +3109,27 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (existing_deregister.block_height == deregister.block_height &&
|
||||
existing_deregister.service_node_index == deregister.service_node_index)
|
||||
std::shared_ptr<service_nodes::quorum_state> existing_deregister_quorum_state
|
||||
= m_service_node_list.get_quorum_state(existing_deregister.block_height);
|
||||
|
||||
if (!existing_deregister_quorum_state)
|
||||
{
|
||||
MERROR_VER("could not get quorum state for recent deregister tx");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existing_deregister_quorum_state->nodes_to_test[existing_deregister.service_node_index] ==
|
||||
quorum_state->nodes_to_test[deregister.service_node_index])
|
||||
{
|
||||
tvc.m_double_spend = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -3715,7 +3770,14 @@ leave:
|
|||
|
||||
// appears to be a NOP *and* is called elsewhere. wat?
|
||||
m_tx_pool.on_blockchain_inc(new_height, id);
|
||||
m_deregister_vote_pool.remove_expired_votes(new_height);
|
||||
|
||||
// New height is the height of the block we just mined. We want (new_height
|
||||
// + 1) because our age checks for deregister votes is now (age >=
|
||||
// DEREGISTER_VOTE_LIFETIME_BY_HEIGHT) where age is derived from
|
||||
// get_current_blockchain_height() which gives you the height that you are
|
||||
// currently mining for, i.e. (new_height + 1). Otherwise peers will silently
|
||||
// drop connection from each other when they go around P2Ping votes.
|
||||
m_deregister_vote_pool.remove_expired_votes(new_height + 1);
|
||||
m_deregister_vote_pool.remove_used_votes(txs);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1442,6 +1442,22 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
void core::do_uptime_proof_call()
|
||||
{
|
||||
std::vector<service_nodes::service_node_pubkey_info> states = get_service_node_list_state({ m_service_node_pubkey });
|
||||
|
||||
// wait one block before starting uptime proofs.
|
||||
if (!states.empty() && states[0].info.registration_height + 1 < get_current_blockchain_height())
|
||||
{
|
||||
m_submit_uptime_proof_interval.do_call(boost::bind(&core::submit_uptime_proof, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
// reset the interval so that we're ready when we register.
|
||||
m_submit_uptime_proof_interval = epee::math_helper::once_a_time_seconds<UPTIME_PROOF_FREQUENCY_IN_SECONDS, true>();
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::on_idle()
|
||||
{
|
||||
if(!m_starter_message_showed)
|
||||
|
@ -1468,11 +1484,9 @@ namespace cryptonote
|
|||
m_deregisters_auto_relayer.do_call(boost::bind(&core::relay_deregister_votes, this));
|
||||
m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
|
||||
m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this));
|
||||
if (m_service_node && m_service_node_list.is_service_node(m_service_node_pubkey))
|
||||
{
|
||||
m_submit_uptime_proof_interval.do_call(boost::bind(&core::submit_uptime_proof, this));
|
||||
m_uptime_proof_pruner.do_call(boost::bind(&service_nodes::quorum_cop::prune_uptime_proof, &m_quorum_cop));
|
||||
}
|
||||
if (m_service_node)
|
||||
do_uptime_proof_call();
|
||||
m_uptime_proof_pruner.do_call(boost::bind(&service_nodes::quorum_cop::prune_uptime_proof, &m_quorum_cop));
|
||||
|
||||
m_miner.on_idle();
|
||||
m_mempool.on_idle();
|
||||
|
@ -1689,32 +1703,30 @@ namespace cryptonote
|
|||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::add_deregister_vote(const loki::service_node_deregister::vote& vote, vote_verification_context &vvc)
|
||||
{
|
||||
uint64_t latest_block_height = std::max(get_current_blockchain_height(), get_target_blockchain_height());
|
||||
uint64_t delta_height = latest_block_height - vote.block_height;
|
||||
|
||||
if (vote.block_height < latest_block_height && delta_height >= loki::service_node_deregister::VOTE_LIFETIME_BY_HEIGHT)
|
||||
{
|
||||
uint64_t latest_block_height = std::max(get_current_blockchain_height(), get_target_blockchain_height());
|
||||
uint64_t delta_height = latest_block_height - vote.block_height;
|
||||
LOG_PRINT_L1("Received vote for height: " << vote.block_height
|
||||
<< " and service node: " << vote.service_node_index
|
||||
<< ", is older than: " << loki::service_node_deregister::VOTE_LIFETIME_BY_HEIGHT
|
||||
<< " blocks and has been rejected.");
|
||||
vvc.m_invalid_block_height = true;
|
||||
}
|
||||
else if (vote.block_height > latest_block_height)
|
||||
{
|
||||
LOG_PRINT_L1("Received vote for height: " << vote.block_height
|
||||
<< " and service node: " << vote.service_node_index
|
||||
<< ", is newer than: " << latest_block_height
|
||||
<< " (latest block height) and has been rejected.");
|
||||
vvc.m_invalid_block_height = true;
|
||||
}
|
||||
|
||||
if (vote.block_height < latest_block_height && delta_height > loki::service_node_deregister::VOTE_LIFETIME_BY_HEIGHT)
|
||||
{
|
||||
LOG_PRINT_L1("Received vote for height: " << vote.block_height
|
||||
<< " and service node: " << vote.service_node_index
|
||||
<< ", is older than: " << loki::service_node_deregister::VOTE_LIFETIME_BY_HEIGHT
|
||||
<< " blocks and has been rejected.");
|
||||
vvc.m_invalid_block_height = true;
|
||||
}
|
||||
else if (vote.block_height > latest_block_height)
|
||||
{
|
||||
LOG_PRINT_L1("Received vote for height: " << vote.block_height
|
||||
<< " and service node: " << vote.service_node_index
|
||||
<< ", is newer than: " << latest_block_height
|
||||
<< " (latest block height) and has been rejected.");
|
||||
vvc.m_invalid_block_height = true;
|
||||
}
|
||||
|
||||
if (vvc.m_invalid_block_height)
|
||||
{
|
||||
vvc.m_verification_failed = true;
|
||||
return false;
|
||||
}
|
||||
if (vvc.m_invalid_block_height)
|
||||
{
|
||||
vvc.m_verification_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::shared_ptr<service_nodes::quorum_state> quorum_state = m_service_node_list.get_quorum_state(vote.block_height);
|
||||
|
@ -1745,6 +1757,7 @@ namespace cryptonote
|
|||
|
||||
return result;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_service_node_keys(crypto::public_key &pub_key, crypto::secret_key &sec_key) const
|
||||
{
|
||||
if (m_service_node)
|
||||
|
|
|
@ -817,7 +817,7 @@ namespace cryptonote
|
|||
bool add_deregister_vote(const loki::service_node_deregister::vote& vote, vote_verification_context &vvc);
|
||||
|
||||
/**
|
||||
* @brief Return the account associated to this service node.
|
||||
* @brief Get the keypair for this service node.
|
||||
|
||||
* @param pub_key The public key for the service node, unmodified if not a service node
|
||||
|
||||
|
@ -1023,6 +1023,11 @@ namespace cryptonote
|
|||
*/
|
||||
bool init_service_node_key();
|
||||
|
||||
/**
|
||||
* @brief do the uptime proof logic and calls for idle loop.
|
||||
*/
|
||||
void do_uptime_proof_call();
|
||||
|
||||
bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
|
||||
|
||||
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
|
||||
|
|
|
@ -812,14 +812,14 @@ 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 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)
|
||||
{
|
||||
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, false, NULL);
|
||||
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, false, NULL, is_staking, per_output_unlock);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool generate_genesis_block(
|
||||
|
|
|
@ -118,7 +118,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 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_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, bool bulletproof = false, 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, bool bulletproof = false, rct::multisig_out *msout = NULL, bool is_staking_tx = false, bool per_output_unlock = false);
|
||||
|
||||
|
|
|
@ -122,20 +122,7 @@ namespace service_nodes
|
|||
cryptonote::vote_verification_context vvc = {};
|
||||
if (!m_core.add_deregister_vote(vote, vvc))
|
||||
{
|
||||
if (vvc.m_invalid_block_height)
|
||||
LOG_ERROR("block height was invalid: " << vote.block_height);
|
||||
|
||||
if (vvc.m_voters_quorum_index_out_of_bounds)
|
||||
LOG_ERROR("voters quorum index specified was out of bounds: " << vote.voters_quorum_index);
|
||||
|
||||
if (vvc.m_duplicate_voters)
|
||||
LOG_ERROR("voters index was duplicated: " << vote.voters_quorum_index);
|
||||
|
||||
if (vvc.m_service_node_index_out_of_bounds)
|
||||
LOG_ERROR("service node index specified out of bounds: " << vote.service_node_index);
|
||||
|
||||
if (vvc.m_signature_not_valid)
|
||||
LOG_ERROR("signature was not valid, was the signature signed properly?");
|
||||
LOG_ERROR("Failed to add deregister vote reason: " << print_vote_verification_context(vvc, &vote));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +159,7 @@ namespace service_nodes
|
|||
if (!crypto::check_signature(hash, pubkey, sig))
|
||||
return false;
|
||||
|
||||
m_uptime_proof_seen[pubkey] = timestamp;
|
||||
m_uptime_proof_seen[pubkey] = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ namespace loki
|
|||
if (deregister.service_node_index >= quorum_state.nodes_to_test.size())
|
||||
{
|
||||
vvc.m_service_node_index_out_of_bounds = true;
|
||||
LOG_PRINT_L1("Service node index in deregister vote was out of bounds: " << deregister.service_node_index << ", expected to be in range of: [0, " << quorum_state.nodes_to_test.size() << "]");
|
||||
LOG_PRINT_L1("Service node index in deregister vote was out of bounds: " << deregister.service_node_index << ", expected to be in range of: [0, " << quorum_state.nodes_to_test.size() << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ namespace loki
|
|||
if (vote.voters_quorum_index >= quorum.size())
|
||||
{
|
||||
vvc.m_voters_quorum_index_out_of_bounds = true;
|
||||
LOG_PRINT_L1("Voter's index in deregister vote was out of bounds: " << vote.voters_quorum_index << ", expected to be in range of: [0, " << quorum.size() << "]");
|
||||
LOG_PRINT_L1("Voter's index in deregister vote was out of bounds: " << vote.voters_quorum_index << ", expected to be in range of: [0, " << quorum.size() << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ namespace loki
|
|||
if (++quorum_set[vote.voters_quorum_index] > 1)
|
||||
{
|
||||
vvc.m_duplicate_voters = true;
|
||||
LOG_PRINT_L1("Voter quorum index is duplicated: " << vote.voters_quorum_index << ", expected to be in range of: [0, " << quorum.size() << "]");
|
||||
LOG_PRINT_L1("Voter quorum index is duplicated: " << vote.voters_quorum_index << ", expected to be in range of: [0, " << quorum.size() << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -106,8 +106,6 @@ namespace service_nodes
|
|||
block_added_generic(block, txs);
|
||||
}
|
||||
}
|
||||
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new prevent_rollback(current_height)));
|
||||
}
|
||||
|
||||
std::vector<crypto::public_key> service_node_list::get_service_nodes_pubkeys() const
|
||||
|
@ -187,7 +185,7 @@ namespace service_nodes
|
|||
if (tx.version >= cryptonote::transaction::version_3_per_output_unlock_times)
|
||||
unlock_time = tx.output_unlock_times[i];
|
||||
|
||||
return unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && unlock_time >= block_height + get_staking_requirement_lock_blocks();
|
||||
return unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && unlock_time >= block_height + get_staking_requirement_lock_blocks(m_blockchain.nettype());
|
||||
}
|
||||
|
||||
bool service_node_list::reg_tx_extract_fields(const cryptonote::transaction& tx, std::vector<cryptonote::account_public_address>& addresses, uint64_t& portions_for_operator, std::vector<uint64_t>& portions, uint64_t& expiration_timestamp, crypto::public_key& service_node_key, crypto::signature& signature, crypto::public_key& tx_pub_key) const
|
||||
|
@ -497,9 +495,15 @@ namespace service_nodes
|
|||
if (hard_fork_version < 9)
|
||||
return;
|
||||
|
||||
while (!m_rollback_events.empty() && m_rollback_events.front()->m_block_height < block_height - ROLLBACK_EVENT_EXPIRATION_BLOCKS)
|
||||
{
|
||||
m_rollback_events.pop_front();
|
||||
const size_t ROLLBACK_EVENT_EXPIRATION_BLOCKS = 30;
|
||||
uint64_t cull_height = (block_height < ROLLBACK_EVENT_EXPIRATION_BLOCKS) ? block_height : block_height - ROLLBACK_EVENT_EXPIRATION_BLOCKS;
|
||||
|
||||
while (!m_rollback_events.empty() && m_rollback_events.front()->m_block_height < cull_height)
|
||||
{
|
||||
m_rollback_events.pop_front();
|
||||
}
|
||||
m_rollback_events.push_front(std::unique_ptr<rollback_event>(new prevent_rollback(cull_height)));
|
||||
}
|
||||
|
||||
for (const crypto::public_key& pubkey : get_expired_nodes(block_height))
|
||||
|
@ -539,7 +543,8 @@ namespace service_nodes
|
|||
index++;
|
||||
}
|
||||
|
||||
const size_t QUORUM_LIFETIME = loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT;
|
||||
const size_t QUORUM_LIFETIME = (6 * loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT);
|
||||
// save six times the quorum lifetime, to be sure. also to help with debugging.
|
||||
const size_t cache_state_from_height = (block_height < QUORUM_LIFETIME) ? 0 : block_height - QUORUM_LIFETIME;
|
||||
|
||||
store_quorum_state_from_rewards_list(block_height);
|
||||
|
@ -576,7 +581,7 @@ namespace service_nodes
|
|||
{
|
||||
std::vector<crypto::public_key> expired_nodes;
|
||||
|
||||
const uint64_t lock_blocks = get_staking_requirement_lock_blocks();
|
||||
const uint64_t lock_blocks = get_staking_requirement_lock_blocks(m_blockchain.nettype());
|
||||
|
||||
if (block_height < lock_blocks)
|
||||
return expired_nodes;
|
||||
|
@ -796,11 +801,6 @@ namespace service_nodes
|
|||
}
|
||||
}
|
||||
|
||||
uint64_t service_node_list::get_staking_requirement_lock_blocks() const
|
||||
{
|
||||
return m_blockchain.nettype() == cryptonote::TESTNET || m_blockchain.nettype() == cryptonote::FAKECHAIN ? STAKING_REQUIREMENT_LOCK_BLOCKS_TESTNET : STAKING_REQUIREMENT_LOCK_BLOCKS;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service_node_list::rollback_event::rollback_event(uint64_t block_height, rollback_type type) : m_block_height(block_height), type(type)
|
||||
|
@ -955,7 +955,6 @@ namespace service_nodes
|
|||
i->m_info = from.m_info;
|
||||
i->type = rollback_event::change_type;
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(i));
|
||||
break;
|
||||
}
|
||||
else if (event.type() == typeid(rollback_new))
|
||||
{
|
||||
|
@ -965,7 +964,6 @@ namespace service_nodes
|
|||
i->m_key = from.m_key;
|
||||
i->type = rollback_event::new_type;
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(i));
|
||||
break;
|
||||
}
|
||||
else if (event.type() == typeid(prevent_rollback))
|
||||
{
|
||||
|
@ -974,10 +972,10 @@ namespace service_nodes
|
|||
i->m_block_height = from.m_block_height;
|
||||
i->type = rollback_event::prevent_type;
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(i));
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
MERROR("Unhandled rollback event type in restoring data to service node list.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1150,11 +1148,28 @@ namespace service_nodes
|
|||
cmd = stream.str();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t get_staking_requirement_lock_blocks(cryptonote::network_type nettype)
|
||||
{
|
||||
constexpr static uint32_t STAKING_REQUIREMENT_LOCK_BLOCKS = 30*24*30;
|
||||
constexpr static uint32_t STAKING_REQUIREMENT_LOCK_BLOCKS_TESTNET = 30*24*2;
|
||||
constexpr static uint32_t STAKING_REQUIREMENT_LOCK_BLOCKS_FAKENET = 30;
|
||||
|
||||
switch(nettype) {
|
||||
case cryptonote::TESTNET: return STAKING_REQUIREMENT_LOCK_BLOCKS_TESTNET;
|
||||
case cryptonote::FAKECHAIN: return STAKING_REQUIREMENT_LOCK_BLOCKS_FAKENET;
|
||||
default: return STAKING_REQUIREMENT_LOCK_BLOCKS;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t get_staking_requirement(cryptonote::network_type m_nettype, uint64_t height)
|
||||
{
|
||||
if (m_nettype == cryptonote::TESTNET || m_nettype == cryptonote::FAKECHAIN)
|
||||
return COIN * 100;
|
||||
|
||||
uint64_t hardfork_height = m_nettype == cryptonote::MAINNET ? 101250 : 96210 /* stagenet */;
|
||||
if (height < hardfork_height) height = hardfork_height;
|
||||
|
||||
uint64_t height_adjusted = height - hardfork_height;
|
||||
uint64_t base = 10000 * COIN;
|
||||
uint64_t variable = (35000.0 * COIN) / loki_exp2(height_adjusted/129600.0);
|
||||
|
@ -1162,5 +1177,13 @@ namespace service_nodes
|
|||
uint64_t flat = 15000 * COIN;
|
||||
return std::max(base + variable, height < 3628800 ? linear_up : flat);
|
||||
}
|
||||
|
||||
uint64_t portions_to_amount(uint64_t portions, uint64_t staking_requirement)
|
||||
{
|
||||
uint64_t hi, lo, resulthi, resultlo;
|
||||
lo = mul128(staking_requirement, portions, &hi);
|
||||
div128_64(hi, lo, STAKING_PORTIONS, &resulthi, &resultlo);
|
||||
return resultlo;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
#include <boost/variant.hpp>
|
||||
#include "serialization/serialization.h"
|
||||
|
||||
#define ROLLBACK_EVENT_EXPIRATION_BLOCKS 30
|
||||
|
||||
namespace service_nodes
|
||||
{
|
||||
const size_t QUORUM_SIZE = 10;
|
||||
|
@ -149,8 +147,6 @@ namespace service_nodes
|
|||
|
||||
std::vector<crypto::public_key> get_service_nodes_pubkeys() const;
|
||||
|
||||
uint64_t get_staking_requirement_lock_blocks() const;
|
||||
|
||||
template<typename T>
|
||||
void block_added_generic(const cryptonote::block& block, const T& txs);
|
||||
|
||||
|
@ -286,8 +282,12 @@ namespace service_nodes
|
|||
bool make_registration_cmd(cryptonote::network_type nettype, const std::vector<std::string> args, const crypto::public_key& service_node_pubkey,
|
||||
const crypto::secret_key service_node_key, std::string &cmd, bool make_friendly);
|
||||
|
||||
uint64_t get_staking_requirement_lock_blocks(cryptonote::network_type m_nettype);
|
||||
|
||||
uint64_t get_staking_requirement(cryptonote::network_type nettype, uint64_t height);
|
||||
|
||||
uint64_t portions_to_amount(uint64_t portions, uint64_t staking_requirement);
|
||||
|
||||
const static cryptonote::account_public_address null_address{ crypto::null_pkey, crypto::null_pkey };
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,10 @@ namespace cryptonote
|
|||
|
||||
uint64_t template_accept_threshold(uint64_t amount)
|
||||
{
|
||||
return amount * ACCEPT_THRESHOLD;
|
||||
// XXX: multiplying by ACCEPT_THRESHOLD here was removed because of a need
|
||||
// to accept 0 fee transactions correctly. the cast to float / double and
|
||||
// back again was causing issues estimating the effect of a zero fee tx
|
||||
return amount;
|
||||
}
|
||||
|
||||
uint64_t get_transaction_size_limit(uint8_t version)
|
||||
|
@ -249,40 +252,6 @@ namespace cryptonote
|
|||
}
|
||||
}
|
||||
|
||||
if (tx.is_deregister_tx())
|
||||
{
|
||||
tx_extra_service_node_deregister deregister;
|
||||
if (!get_service_node_deregister_from_tx_extra(tx.extra, deregister))
|
||||
{
|
||||
LOG_PRINT_L1("Could not get service node deregister from tx v3, possibly corrupt tx in your blockchain");
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t curr_height = m_blockchain.get_current_blockchain_height();
|
||||
if (deregister.block_height >= curr_height)
|
||||
{
|
||||
LOG_PRINT_L1("Received deregister tx for height: " << deregister.block_height
|
||||
<< " and service node: " << deregister.service_node_index
|
||||
<< ", is newer than current height: " << curr_height
|
||||
<< " blocks and has been rejected.");
|
||||
tvc.m_vote_ctx.m_invalid_block_height = true;
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t delta_height = curr_height - deregister.block_height;
|
||||
if (delta_height > loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT)
|
||||
{
|
||||
LOG_PRINT_L1("Received deregister tx for height: " << deregister.block_height
|
||||
<< " and service node: " << deregister.service_node_index
|
||||
<< ", is older than: " << loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT
|
||||
<< " blocks and has been rejected.");
|
||||
tvc.m_vote_ctx.m_invalid_block_height = true;
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_blockchain.check_tx_outputs(tx, tvc))
|
||||
{
|
||||
LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid output");
|
||||
|
@ -699,9 +668,22 @@ namespace cryptonote
|
|||
if (meta.fee == 0)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
if (cryptonote::parse_and_validate_tx_from_blob(bd, tx) && !tx.is_deregister_tx())
|
||||
if (!cryptonote::parse_and_validate_tx_from_blob(bd, tx))
|
||||
{
|
||||
return true;
|
||||
LOG_PRINT_L1("TX in pool could not be parsed from blob, txid: " << txid);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!tx.is_deregister_tx())
|
||||
return true;
|
||||
|
||||
tx_verification_context tvc;
|
||||
uint64_t max_used_block_height = 0;
|
||||
crypto::hash max_used_block_id = null_hash;
|
||||
if (!m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, /*kept_by_block*/ false))
|
||||
{
|
||||
LOG_PRINT_L1("TX deregister considered for relaying failed tx inputs check, txid: " << txid << ", reason: " << print_tx_verification_context(tvc, &tx));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -716,6 +698,7 @@ namespace cryptonote
|
|||
}
|
||||
return true;
|
||||
}, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -1094,6 +1077,7 @@ namespace cryptonote
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure
|
||||
if(m_blockchain.have_tx_keyimges_as_spent(tx))
|
||||
{
|
||||
|
|
|
@ -790,7 +790,7 @@ namespace cryptonote
|
|||
if (vvc.m_verification_failed)
|
||||
{
|
||||
LOG_PRINT_CCONTEXT_L1("Deregister vote verification failed, dropping connection");
|
||||
drop_connection(context, true /*add_fail*/, false /*flush_all_spans i.e. delete cached block data from this peer*/);
|
||||
drop_connection(context, false /*add_fail*/, false /*flush_all_spans i.e. delete cached block data from this peer*/);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,6 +150,25 @@ bool t_command_parser_executor::print_sn_key(const std::vector<std::string>& arg
|
|||
return result;
|
||||
}
|
||||
|
||||
bool t_command_parser_executor::print_sr(const std::vector<std::string>& args)
|
||||
{
|
||||
if (args.size() != 1)
|
||||
{
|
||||
std::cout << "expected 1 argument, <height>, received: " << args.size() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t height = 0;
|
||||
if(!epee::string_tools::get_xtype_from_string(height, args[0]))
|
||||
{
|
||||
std::cout << "wrong block height parameter" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = m_executor.print_sr(height);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool t_command_parser_executor::prepare_registration()
|
||||
{
|
||||
bool result = m_executor.prepare_registration();
|
||||
|
|
|
@ -79,6 +79,8 @@ public:
|
|||
|
||||
bool print_sn_key(const std::vector<std::string>& args);
|
||||
|
||||
bool print_sr(const std::vector<std::string>& args);
|
||||
|
||||
bool prepare_registration();
|
||||
|
||||
bool print_sn(const std::vector<std::string>& args);
|
||||
|
|
|
@ -107,6 +107,12 @@ t_command_server::t_command_server(
|
|||
, "print_sn_key"
|
||||
, "Print this daemon's service node key, if it is one and launched in service node mode."
|
||||
);
|
||||
m_command_lookup.set_handler(
|
||||
"print_sr"
|
||||
, std::bind(&t_command_parser_executor::print_sr, &m_parser, p::_1)
|
||||
, "print_sr <height>"
|
||||
, "Print the staking requirement for the height."
|
||||
);
|
||||
m_command_lookup.set_handler(
|
||||
"prepare_registration"
|
||||
, std::bind(&t_command_parser_executor::prepare_registration, &m_parser)
|
||||
|
|
|
@ -1041,7 +1041,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
|
|||
}
|
||||
else
|
||||
{
|
||||
memset(&res.pool_stats, 0, sizeof(res.pool_stats));
|
||||
res.pool_stats = {};
|
||||
if (!m_rpc_server->on_get_transaction_pool_stats(req, res, false) || res.status != CORE_RPC_STATUS_OK)
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, res.status);
|
||||
|
@ -2007,10 +2007,10 @@ bool t_rpc_command_executor::get_service_node_registration_cmd(const std::vector
|
|||
tools::fail_msg_writer() << make_error(fail_message, error_resp.message);
|
||||
return true;
|
||||
}
|
||||
|
||||
tools::success_msg_writer() << res.registration_cmd;
|
||||
}
|
||||
|
||||
tools::success_msg_writer() << res.registration_cmd;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2037,7 +2037,7 @@ static void print_service_node_list_state(cryptonote::network_type nettype, uint
|
|||
// Print Expiry Info
|
||||
{
|
||||
uint64_t expiry_height = entry.registration_height;
|
||||
expiry_height += (nettype == cryptonote::TESTNET) ? STAKING_REQUIREMENT_LOCK_BLOCKS_TESTNET : STAKING_REQUIREMENT_LOCK_BLOCKS;
|
||||
expiry_height += service_nodes::get_staking_requirement_lock_blocks(nettype);
|
||||
|
||||
if (curr_height)
|
||||
{
|
||||
|
@ -2097,92 +2097,104 @@ bool t_rpc_command_executor::print_sn(const std::vector<std::string> &args)
|
|||
epee::json_rpc::error error_resp;
|
||||
req.service_node_pubkeys = args;
|
||||
|
||||
cryptonote::COMMAND_RPC_GET_INFO::request get_info_req;
|
||||
cryptonote::COMMAND_RPC_GET_INFO::response get_info_res;
|
||||
|
||||
cryptonote::network_type nettype = cryptonote::UNDEFINED;
|
||||
uint64_t *curr_height = nullptr;
|
||||
if (m_is_rpc)
|
||||
{
|
||||
if (!m_rpc_client->json_rpc_request(req, res, "get_service_node", fail_message.c_str()))
|
||||
if (!m_rpc_client->rpc_request(get_info_req, get_info_res, "/getinfo", fail_message.c_str()))
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, res.status);
|
||||
return true;
|
||||
tools::fail_msg_writer() << make_error(fail_message, get_info_res.status);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_rpc_client->json_rpc_request(req, res, "get_service_nodes", fail_message.c_str()))
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, res.status);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (get_info_res.mainnet) nettype = cryptonote::MAINNET;
|
||||
else if (get_info_res.stagenet) nettype = cryptonote::STAGENET;
|
||||
else if (get_info_res.testnet) nettype = cryptonote::TESTNET;
|
||||
curr_height = &get_info_res.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_rpc_server->on_get_info(get_info_req, get_info_res) || get_info_res.status == CORE_RPC_STATUS_OK)
|
||||
curr_height = &get_info_res.height;
|
||||
|
||||
if (!m_rpc_server->on_get_service_nodes(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, error_resp.message);
|
||||
return true;
|
||||
}
|
||||
nettype = m_rpc_server->nettype();
|
||||
}
|
||||
|
||||
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *> unregistered;
|
||||
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *> registered;
|
||||
registered.reserve (res.service_node_states.size());
|
||||
unregistered.reserve(res.service_node_states.size() * 0.5f);
|
||||
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *> unregistered;
|
||||
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *> registered;
|
||||
registered.reserve (res.service_node_states.size());
|
||||
unregistered.reserve(res.service_node_states.size() * 0.5f);
|
||||
|
||||
for (auto &entry : res.service_node_states)
|
||||
for (auto &entry : res.service_node_states)
|
||||
{
|
||||
if (entry.total_contributed == entry.staking_requirement)
|
||||
{
|
||||
if (entry.total_contributed == entry.staking_requirement)
|
||||
registered.push_back(&entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
unregistered.push_back(&entry);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(unregistered.begin(), unregistered.end(),
|
||||
[](const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *a, const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *b) {
|
||||
uint64_t a_remaining = a->staking_requirement - a->total_reserved;
|
||||
uint64_t b_remaining = b->staking_requirement - b->total_reserved;
|
||||
|
||||
if (b_remaining == a_remaining)
|
||||
return b->portions_for_operator < a->portions_for_operator;
|
||||
|
||||
return b_remaining < a_remaining;
|
||||
});
|
||||
|
||||
std::stable_sort(registered.begin(), registered.end(),
|
||||
[](const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *a, const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *b) {
|
||||
if (a->last_reward_block_height == b->last_reward_block_height)
|
||||
return a->last_reward_transaction_index < b->last_reward_transaction_index;
|
||||
|
||||
return a->last_reward_block_height < b->last_reward_block_height;
|
||||
});
|
||||
|
||||
if (unregistered.size() > 0)
|
||||
{
|
||||
tools::msg_writer() << "Service Node Unregistered State[" << unregistered.size()<< "]";
|
||||
print_service_node_list_state(nettype, curr_height, unregistered);
|
||||
}
|
||||
|
||||
if (registered.size() > 0)
|
||||
{
|
||||
tools::msg_writer() << "Service Node Registration State[" << registered.size()<< "]";
|
||||
print_service_node_list_state(nettype, curr_height, registered);
|
||||
}
|
||||
|
||||
if (unregistered.size() == 0 && registered.size() == 0)
|
||||
{
|
||||
if (args.size() > 0)
|
||||
{
|
||||
tools::msg_writer() << "No service node is currently known on the network for: ";
|
||||
for (const std::string &arg : args)
|
||||
{
|
||||
registered.push_back(&entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
unregistered.push_back(&entry);
|
||||
tools::msg_writer() << arg;
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(unregistered.begin(), unregistered.end(),
|
||||
[](const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *a, const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *b) {
|
||||
uint64_t a_remaining = a->staking_requirement - a->total_reserved;
|
||||
uint64_t b_remaining = b->staking_requirement - b->total_reserved;
|
||||
|
||||
if (b_remaining == a_remaining)
|
||||
return b->portions_for_operator < a->portions_for_operator;
|
||||
|
||||
return b_remaining < a_remaining;
|
||||
});
|
||||
|
||||
std::stable_sort(registered.begin(), registered.end(),
|
||||
[](const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *a, const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry *b) {
|
||||
if (a->last_reward_block_height == b->last_reward_block_height)
|
||||
return a->last_reward_transaction_index < b->last_reward_transaction_index;
|
||||
|
||||
return a->last_reward_block_height < b->last_reward_block_height;
|
||||
});
|
||||
|
||||
uint64_t *curr_height = nullptr;
|
||||
cryptonote::COMMAND_RPC_GET_HEIGHT::request height_req = {};
|
||||
cryptonote::COMMAND_RPC_GET_HEIGHT::response height_res = {};
|
||||
m_rpc_server->on_get_height(height_req, height_res);
|
||||
|
||||
if (res.status == CORE_RPC_STATUS_OK)
|
||||
curr_height = &height_res.height;
|
||||
|
||||
if (unregistered.size() > 0)
|
||||
else
|
||||
{
|
||||
tools::msg_writer() << "Service Node Unregistered State[" << unregistered.size()<< "]";
|
||||
print_service_node_list_state(m_rpc_server->nettype(), curr_height, unregistered);
|
||||
}
|
||||
|
||||
if (registered.size() > 0)
|
||||
{
|
||||
tools::msg_writer() << "Service Node Registration State[" << registered.size()<< "]";
|
||||
print_service_node_list_state(m_rpc_server->nettype(), curr_height, registered);
|
||||
}
|
||||
|
||||
if (unregistered.size() == 0 && registered.size() == 0)
|
||||
{
|
||||
if (args.size() > 0)
|
||||
{
|
||||
tools::msg_writer() << "No service node is currently known on the network for: ";
|
||||
for (const std::string &arg : args)
|
||||
{
|
||||
tools::msg_writer() << arg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tools::msg_writer() << "No service node is currently known on the network";
|
||||
}
|
||||
tools::msg_writer() << "No service node is currently known on the network";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2220,6 +2232,36 @@ bool t_rpc_command_executor::print_sn_status()
|
|||
return result;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::print_sr(uint64_t height)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_GET_STAKING_REQUIREMENT::request req = {};
|
||||
cryptonote::COMMAND_RPC_GET_STAKING_REQUIREMENT::response res = {};
|
||||
std::string fail_message = "Unsuccessful";
|
||||
epee::json_rpc::error error_resp;
|
||||
req.height = height;
|
||||
|
||||
if (m_is_rpc)
|
||||
{
|
||||
if (!m_rpc_client->json_rpc_request(req, res, "get_staking_requirement", fail_message.c_str()))
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, res.status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
epee::json_rpc::error error_resp;
|
||||
if (!m_rpc_server->on_get_staking_requirement(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, error_resp.message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
tools::success_msg_writer() << "Staking Requirement: " << cryptonote::print_money(res.staking_requirement);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::print_sn_key()
|
||||
{
|
||||
cryptonote::COMMAND_RPC_GET_SERVICE_NODE_KEY::request req = {};
|
||||
|
|
|
@ -162,6 +162,8 @@ public:
|
|||
|
||||
bool print_sn_status();
|
||||
|
||||
bool print_sr(uint64_t height);
|
||||
|
||||
bool prepare_registration();
|
||||
|
||||
bool print_sn(const std::vector<std::string> &args);
|
||||
|
|
|
@ -372,7 +372,7 @@ namespace nodetool
|
|||
std::set<std::string> full_addrs;
|
||||
if (nettype == cryptonote::TESTNET)
|
||||
{
|
||||
full_addrs.insert("52.63.146.137:38150");
|
||||
full_addrs.insert("52.63.146.137:38156");
|
||||
}
|
||||
else if (nettype == cryptonote::STAGENET)
|
||||
{
|
||||
|
|
|
@ -2317,7 +2317,13 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
bool core_rpc_server::on_get_staking_requirement(const COMMAND_RPC_GET_STAKING_REQUIREMENT::request& req, COMMAND_RPC_GET_STAKING_REQUIREMENT::response& res, epee::json_rpc::error& error_resp)
|
||||
{
|
||||
PERF_TIMER(on_get_staking_requirement);
|
||||
res.staking_requirement = service_nodes::get_staking_requirement(nettype(), req.height);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
const command_line::arg_descriptor<std::string, false, true, 2> core_rpc_server::arg_rpc_bind_port = {
|
||||
"rpc-bind-port"
|
||||
|
|
|
@ -159,6 +159,7 @@ namespace cryptonote
|
|||
MAP_JON_RPC_WE("get_service_node_registration_cmd", on_get_service_node_registration_cmd, COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD)
|
||||
MAP_JON_RPC_WE("get_service_node_key", on_get_service_node_key, COMMAND_RPC_GET_SERVICE_NODE_KEY)
|
||||
MAP_JON_RPC_WE("get_service_nodes", on_get_service_nodes, COMMAND_RPC_GET_SERVICE_NODES)
|
||||
MAP_JON_RPC_WE("get_staking_requirement", on_get_staking_requirement, COMMAND_RPC_GET_STAKING_REQUIREMENT)
|
||||
END_JSON_RPC_MAP()
|
||||
END_URI_MAP2()
|
||||
|
||||
|
@ -226,6 +227,7 @@ namespace cryptonote
|
|||
bool on_get_service_node_registration_cmd(const COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD::request& req, COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD::response& res, epee::json_rpc::error& error_resp);
|
||||
bool on_get_service_node_key(const COMMAND_RPC_GET_SERVICE_NODE_KEY::request& req, COMMAND_RPC_GET_SERVICE_NODE_KEY::response& res, epee::json_rpc::error &error_resp);
|
||||
bool on_get_service_nodes(const COMMAND_RPC_GET_SERVICE_NODES::request& req, COMMAND_RPC_GET_SERVICE_NODES::response& res, epee::json_rpc::error& error_resp);
|
||||
bool on_get_staking_requirement(const COMMAND_RPC_GET_STAKING_REQUIREMENT::request& req, COMMAND_RPC_GET_STAKING_REQUIREMENT::response& res, epee::json_rpc::error& error_resp);
|
||||
//-----------------------
|
||||
|
||||
private:
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "cryptonote_basic/verification_context.h"
|
||||
#include "cryptonote_basic/difficulty.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "cryptonote_core/service_node_deregister.h"
|
||||
|
||||
namespace cryptonote
|
||||
|
@ -1581,6 +1582,8 @@ namespace cryptonote
|
|||
std::vector<txpool_histo> histo;
|
||||
uint32_t num_double_spends;
|
||||
|
||||
txpool_stats(): bytes_total(0), bytes_min(0), bytes_max(0), bytes_med(0), fee_total(0), oldest(0), txs_total(0), num_failing(0), num_10m(0), num_not_relayed(0), histo_98pc(0), num_double_spends(0) {}
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(bytes_total)
|
||||
KV_SERIALIZE(bytes_min)
|
||||
|
@ -2411,4 +2414,25 @@ namespace cryptonote
|
|||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_GET_STAKING_REQUIREMENT
|
||||
{
|
||||
struct request
|
||||
{
|
||||
uint64_t height;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(height)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
uint64_t staking_requirement;
|
||||
std::string status;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(staking_requirement)
|
||||
KV_SERIALIZE(status)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4699,14 +4699,6 @@ bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
|
|||
return sweep_main(0, true, args_);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
static cryptonote::account_public_address string_to_address(const std::string& s)
|
||||
{
|
||||
cryptonote::account_public_address address;
|
||||
if (!epee::string_tools::hex_to_pod(s, address))
|
||||
return service_nodes::null_address;
|
||||
return address;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::register_service_node_main(
|
||||
const std::vector<std::string>& service_node_key_as_str,
|
||||
uint64_t expiration_timestamp,
|
||||
|
@ -4748,7 +4740,7 @@ bool simple_wallet::register_service_node_main(
|
|||
return true;
|
||||
}
|
||||
|
||||
uint64_t staking_requirement_lock_blocks = (m_wallet->nettype() == cryptonote::TESTNET ? STAKING_REQUIREMENT_LOCK_BLOCKS_TESTNET : STAKING_REQUIREMENT_LOCK_BLOCKS);
|
||||
uint64_t staking_requirement_lock_blocks = service_nodes::get_staking_requirement_lock_blocks(m_wallet->nettype());
|
||||
uint64_t locked_blocks = staking_requirement_lock_blocks + STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS;
|
||||
|
||||
std::string err, err2;
|
||||
|
@ -5049,12 +5041,12 @@ bool simple_wallet::register_service_node(const std::vector<std::string> &args_)
|
|||
m_idle_thread.join();
|
||||
success_msg_writer(false) << please_wait_to_be_included_in_block_msg;
|
||||
#ifndef WIN32
|
||||
success_msg_writer() << tr("Entering autostaking mode, forking to background...");
|
||||
success_msg_writer(true /*color*/) << tr("Successfully entered autostaking mode, this wallet is moving into the background to automatically renew your service node every period.");
|
||||
tools::threadpool::getInstance().stop();
|
||||
posix::fork("");
|
||||
tools::threadpool::getInstance().start();
|
||||
#else
|
||||
success_msg_writer() << tr("Entering autostaking mode, please leave this wallet running.");
|
||||
success_msg_writer(true /*color*/) << tr("Successfully entered autostaking mode, please leave this wallet running to automatically renew your service node every period.");
|
||||
#endif
|
||||
m_idle_run.store(true, std::memory_order_relaxed);
|
||||
while (true)
|
||||
|
@ -5066,7 +5058,7 @@ bool simple_wallet::register_service_node(const std::vector<std::string> &args_)
|
|||
break;
|
||||
if (!m_idle_run.load(std::memory_order_relaxed))
|
||||
break;
|
||||
m_idle_cond.wait_for(lock, boost::chrono::seconds(120));
|
||||
m_idle_cond.wait_for(lock, boost::chrono::seconds(AUTOSTAKE_INTERVAL));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -5097,7 +5089,7 @@ bool simple_wallet::stake_main(
|
|||
uint64_t fetched_blocks;
|
||||
m_wallet->refresh(0, fetched_blocks);
|
||||
|
||||
uint64_t staking_requirement_lock_blocks = (m_wallet->nettype() == cryptonote::TESTNET ? STAKING_REQUIREMENT_LOCK_BLOCKS_TESTNET : STAKING_REQUIREMENT_LOCK_BLOCKS);
|
||||
uint64_t staking_requirement_lock_blocks = service_nodes::get_staking_requirement_lock_blocks(m_wallet->nettype());
|
||||
uint64_t locked_blocks = staking_requirement_lock_blocks + STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS;
|
||||
|
||||
std::string err, err2;
|
||||
|
@ -5168,7 +5160,11 @@ bool simple_wallet::stake_main(
|
|||
|
||||
for (const auto& contributor : snode_info.contributors)
|
||||
{
|
||||
if (string_to_address(contributor.address) == address)
|
||||
address_parse_info info;
|
||||
if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), contributor.address))
|
||||
info.address = service_nodes::null_address;
|
||||
|
||||
if (info.address == address)
|
||||
{
|
||||
uint64_t max_increase_reserve = snode_info.staking_requirement - snode_info.total_reserved;
|
||||
uint64_t max_increase_amount_to = contributor.reserved + max_increase_reserve;
|
||||
|
@ -5192,7 +5188,7 @@ bool simple_wallet::stake_main(
|
|||
if (amount > can_contrib_total)
|
||||
{
|
||||
success_msg_writer() << tr("You may only contribute up to ") << print_money(can_contrib_total) << tr(" more loki to this service node");
|
||||
success_msg_writer() << tr("Automatically staking ") << print_money(can_contrib_total);
|
||||
success_msg_writer() << tr("Reducing your stake from ") << print_money(amount) << tr(" to ") << print_money(can_contrib_total);
|
||||
amount = can_contrib_total;
|
||||
}
|
||||
if (amount < must_contrib_total)
|
||||
|
@ -5475,7 +5471,7 @@ bool simple_wallet::stake(const std::vector<std::string> &args_)
|
|||
break;
|
||||
if (!m_idle_run.load(std::memory_order_relaxed))
|
||||
break;
|
||||
m_idle_cond.wait_for(lock, boost::chrono::seconds(120));
|
||||
m_idle_cond.wait_for(lock, boost::chrono::seconds(AUTOSTAKE_INTERVAL));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
// Hardcode Monero's donation address (see #1447)
|
||||
constexpr const char MONERO_DONATION_ADDR[] = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A";
|
||||
|
||||
const int AUTOSTAKE_INTERVAL = 60 * 40; // once every 40 minutes.
|
||||
|
||||
/*!
|
||||
* \namespace cryptonote
|
||||
* \brief Holds cryptonote related classes and helpers.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#define DEF_LOKI_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_LOKI_VERSION "0.3.5"
|
||||
#define DEF_LOKI_VERSION "0.4.0"
|
||||
#define DEF_LOKI_RELEASE_NAME "Magic Mani"
|
||||
#define DEF_LOKI_VERSION_FULL DEF_LOKI_VERSION "-" DEF_LOKI_VERSION_TAG
|
||||
|
||||
|
|
|
@ -9163,7 +9163,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
|
|||
uint64_t wallet2::get_approximate_blockchain_height() const
|
||||
{
|
||||
const int seconds_per_block = DIFFICULTY_TARGET_V2;
|
||||
const time_t epochTimeMiningStarted = 1525067730 + (60 * 60 * 24 * 7); // 2018-04-30 ~3:55PM + 1 week to be conservative.
|
||||
const time_t epochTimeMiningStarted = (m_nettype == TESTNET || m_nettype == STAGENET ? 1536137083 : 1525067730) + (60 * 60 * 24 * 7); // 2018-04-30 ~3:55PM + 1 week to be conservative.
|
||||
const time_t currentTime = time(NULL);
|
||||
uint64_t approx_blockchain_height = (currentTime < epochTimeMiningStarted) ? 0 : (currentTime - epochTimeMiningStarted)/seconds_per_block;
|
||||
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
|
||||
|
|
|
@ -38,6 +38,7 @@ set(core_tests_sources
|
|||
integer_overflow.cpp
|
||||
multisig.cpp
|
||||
ring_signature_1.cpp
|
||||
service_nodes.cpp
|
||||
transaction_tests.cpp
|
||||
tx_validation.cpp
|
||||
v2_tests.cpp
|
||||
|
@ -55,6 +56,7 @@ set(core_tests_headers
|
|||
integer_overflow.h
|
||||
multisig.h
|
||||
ring_signature_1.h
|
||||
service_nodes.h
|
||||
transaction_tests.h
|
||||
tx_validation.h
|
||||
v2_tests.h
|
||||
|
|
|
@ -171,7 +171,7 @@ bool gen_block_invalid_nonce::generate(std::vector<test_event_entry>& events) co
|
|||
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> commulative_difficulties;
|
||||
if (!lift_up_difficulty(events, timestamps, commulative_difficulties, generator, 2, blk_0, miner_account))
|
||||
if (!lift_up_difficulty(events, timestamps, commulative_difficulties, generator, 4, blk_0, miner_account))
|
||||
return false;
|
||||
|
||||
// Create invalid nonce
|
||||
|
@ -331,29 +331,12 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events
|
|||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
|
||||
|
||||
GENERATE_ACCOUNT(alice);
|
||||
|
||||
tx_source_entry se;
|
||||
se.amount = blk_0.miner_tx.vout[0].amount;
|
||||
se.push_output(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key, se.amount);
|
||||
se.real_output = 0;
|
||||
se.rct = false;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx);
|
||||
se.real_output_in_tx_index = 0;
|
||||
std::vector<tx_source_entry> sources;
|
||||
sources.push_back(se);
|
||||
|
||||
tx_destination_entry de;
|
||||
de.addr = miner_account.get_keys().m_account_address;
|
||||
de.amount = se.amount;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back(de);
|
||||
|
||||
transaction tmp_tx;
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0))
|
||||
|
||||
if (!construct_tx_to_key(events, tmp_tx, blk_0r, miner_account, miner_account, blk_0.miner_tx.vout[0].amount))
|
||||
return false;
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0r);
|
||||
miner_tx.vin.push_back(tmp_tx.vin[0]);
|
||||
|
||||
block blk_1;
|
||||
|
@ -376,24 +359,8 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>
|
|||
|
||||
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
|
||||
|
||||
tx_source_entry se;
|
||||
se.amount = blk_1.miner_tx.vout[0].amount;
|
||||
se.push_output(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key, se.amount);
|
||||
se.real_output = 0;
|
||||
se.rct = false;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx);
|
||||
se.real_output_in_tx_index = 0;
|
||||
std::vector<tx_source_entry> sources;
|
||||
sources.push_back(se);
|
||||
|
||||
tx_destination_entry de;
|
||||
de.addr = miner_account.get_keys().m_account_address;
|
||||
de.amount = se.amount;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back(de);
|
||||
|
||||
transaction tmp_tx;
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0))
|
||||
if (!construct_tx_to_key(events, tmp_tx, blk_1r, miner_account, miner_account, blk_1.miner_tx.vout[0].amount))
|
||||
return false;
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_1);
|
||||
|
@ -408,22 +375,6 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>
|
|||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_is_small::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.vout[0].amount /= 2;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_is_big::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
@ -446,6 +397,7 @@ bool gen_block_miner_tx_has_no_out::generate(std::vector<test_event_entry>& even
|
|||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.vout.clear();
|
||||
miner_tx.version = 1;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
|
@ -462,19 +414,14 @@ bool gen_block_miner_tx_has_out_to_alice::generate(std::vector<test_event_entry>
|
|||
|
||||
GENERATE_ACCOUNT(alice);
|
||||
|
||||
keypair txkey;
|
||||
MAKE_MINER_TX_AND_KEY_MANUALLY(miner_tx, blk_0, &txkey);
|
||||
transaction miner_tx;
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::generate_key_derivation(alice.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::derive_public_key(derivation, 1, alice.get_keys().m_account_address.m_spend_public_key, out_eph_public_key);
|
||||
const auto height = get_block_height(blk_0);
|
||||
const auto coins = generator.get_already_generated_coins(blk_0);
|
||||
const auto& miner_address = miner_account.get_keys().m_account_address;
|
||||
const auto& alice_address = alice.get_keys().m_account_address;
|
||||
|
||||
tx_out out_to_alice;
|
||||
out_to_alice.amount = miner_tx.vout[0].amount / 2;
|
||||
miner_tx.vout[0].amount -= out_to_alice.amount;
|
||||
out_to_alice.target = txout_to_key(out_eph_public_key);
|
||||
miner_tx.vout.push_back(out_to_alice);
|
||||
construct_miner_tx_with_extra_output(miner_tx, miner_address, height+1, coins, alice_address);
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
|
@ -507,7 +454,9 @@ bool gen_block_is_too_big::generate(std::vector<test_event_entry>& events) const
|
|||
|
||||
// Creating a huge miner_tx, it will have a lot of outs
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.version = 1;
|
||||
static const size_t tx_out_count = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2;
|
||||
|
||||
uint64_t amount = get_outs_money_amount(miner_tx);
|
||||
uint64_t portion = amount / tx_out_count;
|
||||
uint64_t remainder = amount % tx_out_count;
|
||||
|
|
|
@ -110,7 +110,7 @@ struct gen_block_invalid_prev_id : public gen_block_verification_base<1>
|
|||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/);
|
||||
};
|
||||
|
||||
struct gen_block_invalid_nonce : public gen_block_verification_base<3>
|
||||
struct gen_block_invalid_nonce : public gen_block_verification_base<5>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
@ -165,11 +165,6 @@ struct gen_block_miner_tx_with_txin_to_key : public gen_block_verification_base<
|
|||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_is_small : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_is_big : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
|
|
@ -71,53 +71,68 @@ bool gen_chain_switch_1::generate(std::vector<test_event_entry>& events) const
|
|||
MAKE_ACCOUNT(events, recipient_account_2); // 2
|
||||
MAKE_ACCOUNT(events, recipient_account_3); // 3
|
||||
MAKE_ACCOUNT(events, recipient_account_4); // 4
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account) // <N blocks>
|
||||
MAKE_TX(events, tx_00, miner_account, recipient_account_1, MK_COINS(5), blk_0); // 5 + N
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, tx_00); // 6 + N
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 7 + N
|
||||
REWIND_BLOCKS(events, blk_0r0, blk_0, miner_account) // <N blocks>
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0r0, miner_account) // <N blocks>
|
||||
MAKE_TX(events, tx_00, miner_account, recipient_account_1, MK_COINS(5), blk_0r0); // 5 + 2N
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, tx_00); // 6 + 2N
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 7 + 2N
|
||||
REWIND_BLOCKS(events, blk_2r, blk_2, miner_account) // <N blocks>
|
||||
|
||||
// Transactions to test account balances after switch
|
||||
MAKE_TX_LIST_START(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(7), blk_2); // 8 + 2N
|
||||
MAKE_TX_LIST_START(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(11), blk_2); // 9 + 2N
|
||||
MAKE_TX_LIST_START(events, txs_blk_5, miner_account, recipient_account_4, MK_COINS(13), blk_2); // 10 + 2N
|
||||
MAKE_TX_LIST_START(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(7), blk_2); // 8 + 3N
|
||||
MAKE_TX_LIST_START(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(11), blk_2); // 9 + 3N
|
||||
MAKE_TX_LIST_START(events, txs_blk_5, miner_account, recipient_account_4, MK_COINS(13), blk_2); // 10 + 3N
|
||||
std::list<transaction> txs_blk_6;
|
||||
txs_blk_6.push_back(txs_blk_4.front());
|
||||
|
||||
// Transactions, that has different order in alt block chains
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(1), blk_2); // 11 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(1), blk_2); // 11 + 3N
|
||||
txs_blk_5.push_back(txs_blk_3.back());
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(2), blk_2); // 12 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(2), blk_2); // 12 + 3N
|
||||
txs_blk_6.push_back(txs_blk_3.back());
|
||||
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(1), blk_2); // 13 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(1), blk_2); // 13 + 3N
|
||||
txs_blk_5.push_back(txs_blk_3.back());
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_2, MK_COINS(2), blk_2); // 14 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_2, MK_COINS(2), blk_2); // 14 + 3N
|
||||
txs_blk_5.push_back(txs_blk_4.back());
|
||||
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_3, MK_COINS(1), blk_2); // 15 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_3, MK_COINS(1), blk_2); // 15 + 3N
|
||||
txs_blk_6.push_back(txs_blk_3.back());
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(2), blk_2); // 16 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(2), blk_2); // 16 + 3N
|
||||
txs_blk_5.push_back(txs_blk_4.back());
|
||||
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_4, MK_COINS(1), blk_2); // 17 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_4, MK_COINS(1), blk_2); // 17 + 3N
|
||||
txs_blk_5.push_back(txs_blk_4.back());
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_4, MK_COINS(2), blk_2); // 18 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_4, MK_COINS(2), blk_2); // 18 + 3N
|
||||
txs_blk_6.push_back(txs_blk_3.back());
|
||||
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_blk_3); // 19 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3, miner_account, txs_blk_4); // 20 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_blk_3); // 19 + 3N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3, miner_account, txs_blk_4); // 20 + 3N
|
||||
//split
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_5, blk_2r, miner_account, txs_blk_5); // 22 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_6, blk_5, miner_account, txs_blk_6); // 23 + 2N
|
||||
DO_CALLBACK(events, "check_split_not_switched"); // 21 + 2N
|
||||
MAKE_NEXT_BLOCK(events, blk_7, blk_6, miner_account); // 24 + 2N
|
||||
DO_CALLBACK(events, "check_split_switched"); // 25 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_5, blk_2r, miner_account, txs_blk_5); // 22 + 3N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_6, blk_5, miner_account, txs_blk_6); // 23 + 3N
|
||||
DO_CALLBACK(events, "check_split_not_switched"); // 21 + 3N
|
||||
MAKE_NEXT_BLOCK(events, blk_7, blk_6, miner_account); // 24 + 3N
|
||||
DO_CALLBACK(events, "check_split_switched"); // 25 + 3N
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t transferred_in_tx(const cryptonote::account_base& account, const cryptonote::transaction& tx) {
|
||||
|
||||
uint64_t total_amount = 0;
|
||||
|
||||
for (auto i = 0u; i < tx.vout.size(); ++i) {
|
||||
|
||||
if(is_out_to_acc(account.get_keys(), boost::get<txout_to_key>(tx.vout[i].target), get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), i)) {
|
||||
total_amount += get_amount(account, tx, i);
|
||||
}
|
||||
}
|
||||
|
||||
return total_amount;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
|
@ -131,8 +146,8 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev
|
|||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 10000, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(5 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size());
|
||||
CHECK_TEST_CONDITION(blocks.back() == boost::get<block>(events[20 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_4
|
||||
CHECK_EQ(5 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size());
|
||||
CHECK_TEST_CONDITION(blocks.back() == boost::get<block>(events[20 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_4
|
||||
|
||||
CHECK_EQ(2, c.get_alternative_blocks_count());
|
||||
|
||||
|
@ -151,9 +166,9 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev
|
|||
CHECK_EQ(1, tx_pool.size());
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
uint64_t transfered;
|
||||
lookup_acc_outs(m_recipient_account_4.get_keys(), tx_pool.front(), get_tx_pub_key_from_extra(tx_pool.front()), get_additional_tx_pub_keys_from_extra(tx_pool.front()), tx_outs, transfered);
|
||||
CHECK_EQ(MK_COINS(13), transfered);
|
||||
|
||||
const auto transferred = transferred_in_tx(m_recipient_account_4, tx_pool.front());
|
||||
CHECK_EQ(MK_COINS(13), transferred);
|
||||
|
||||
m_chain_1.swap(blocks);
|
||||
m_tx_pool.swap(tx_pool);
|
||||
|
@ -169,11 +184,11 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind
|
|||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 10000, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(6 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size());
|
||||
CHECK_EQ(6 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size());
|
||||
auto it = blocks.end();
|
||||
--it; --it; --it;
|
||||
CHECK_TEST_CONDITION(std::equal(blocks.begin(), it, m_chain_1.begin()));
|
||||
CHECK_TEST_CONDITION(blocks.back() == boost::get<block>(events[24 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_7
|
||||
CHECK_TEST_CONDITION(blocks.back() == boost::get<block>(events[24 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_7
|
||||
|
||||
std::list<block> alt_blocks;
|
||||
r = c.get_alternative_blocks(alt_blocks);
|
||||
|
@ -201,10 +216,8 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind
|
|||
CHECK_EQ(1, tx_pool.size());
|
||||
CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front()));
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
uint64_t transfered;
|
||||
lookup_acc_outs(m_recipient_account_2.get_keys(), tx_pool.front(), tx_outs, transfered);
|
||||
CHECK_EQ(MK_COINS(7), transfered);
|
||||
const auto transferred = transferred_in_tx(m_recipient_account_2, tx_pool.front());
|
||||
CHECK_EQ(MK_COINS(7), transferred);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@
|
|||
#include "include_base_utils.h"
|
||||
|
||||
#include "console_handler.h"
|
||||
#include "common/rules.h"
|
||||
|
||||
#include "p2p/net_node.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
|
@ -100,15 +101,17 @@ void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, st
|
|||
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx);
|
||||
uint64_t block_reward;
|
||||
cryptonote::get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version_, 0);
|
||||
m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size);
|
||||
m_blocks_info.insert({get_block_hash(blk), block_info(blk.prev_id, already_generated_coins + block_reward, block_size)});
|
||||
}
|
||||
|
||||
bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
|
||||
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
|
||||
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list)
|
||||
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list,
|
||||
const crypto::public_key& sn_pub_key /* = crypto::null_key */, const std::vector<sn_contributor_t>& sn_infos)
|
||||
{
|
||||
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
|
||||
blk.minor_version = CURRENT_BLOCK_MINOR_VERSION;
|
||||
/// a temporary workaround
|
||||
blk.major_version = hf_version_;
|
||||
blk.minor_version = hf_version_;
|
||||
blk.timestamp = timestamp;
|
||||
blk.prev_id = prev_id;
|
||||
|
||||
|
@ -135,7 +138,8 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
|
|||
size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
||||
while (true)
|
||||
{
|
||||
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), hf_version_))
|
||||
|
||||
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), hf_version_, cryptonote::MAINNET, sn_pub_key, sn_infos))
|
||||
return false;
|
||||
|
||||
size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
||||
|
@ -197,7 +201,8 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::a
|
|||
|
||||
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev,
|
||||
const cryptonote::account_base& miner_acc,
|
||||
const std::list<cryptonote::transaction>& tx_list/* = {}*/)
|
||||
const std::list<cryptonote::transaction>& tx_list/* = {}*/,
|
||||
const crypto::public_key& sn_pub_key /* = crypto::null_key */, const std::vector<sn_contributor_t>& sn_infos)
|
||||
{
|
||||
uint64_t height = boost::get<txin_gen>(blk_prev.miner_tx.vin.front()).height + 1;
|
||||
crypto::hash prev_id = get_block_hash(blk_prev);
|
||||
|
@ -207,7 +212,7 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::b
|
|||
std::vector<size_t> block_sizes;
|
||||
get_last_n_block_sizes(block_sizes, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
|
||||
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list);
|
||||
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list, sn_pub_key, sn_infos);
|
||||
}
|
||||
|
||||
bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc,
|
||||
|
@ -261,19 +266,21 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c
|
|||
struct output_index {
|
||||
const cryptonote::txout_target_v out;
|
||||
uint64_t amount;
|
||||
rct::key mask;
|
||||
size_t blk_height; // block height
|
||||
uint64_t unlock_time;
|
||||
size_t tx_no; // index of transaction in block
|
||||
size_t out_no; // index of out in transaction
|
||||
size_t idx;
|
||||
bool spent;
|
||||
bool is_sn_reward = false;
|
||||
const cryptonote::block *p_blk;
|
||||
const cryptonote::transaction *p_tx;
|
||||
|
||||
output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt)
|
||||
: out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { }
|
||||
output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, uint64_t ut, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt)
|
||||
: out(_out), amount(_a), blk_height(_h), unlock_time(ut), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { }
|
||||
|
||||
output_index(const output_index &other)
|
||||
: out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), out_no(other.out_no), idx(other.idx), spent(other.spent), p_blk(other.p_blk), p_tx(other.p_tx) { }
|
||||
output_index(const output_index &other) = default;
|
||||
|
||||
const std::string toString() const {
|
||||
std::stringstream ss;
|
||||
|
@ -282,6 +289,7 @@ struct output_index {
|
|||
<< " tx_no=" << tx_no
|
||||
<< " out_no=" << out_no
|
||||
<< " amount=" << amount
|
||||
<< " mask=" << mask
|
||||
<< " idx=" << idx
|
||||
<< " spent=" << spent
|
||||
<< "}";
|
||||
|
@ -392,16 +400,27 @@ bool init_output_indices(output_index_vec& outs, output_vec& outs_mine, const st
|
|||
|
||||
const auto height = boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height; /// replace with front?
|
||||
|
||||
outs.push_back({out.target, out.amount, height, i, j, &blk, vtx[i]});
|
||||
const auto unlock_time = (tx.version < 3) ? tx.unlock_time : tx.output_unlock_times[j];
|
||||
|
||||
outs.push_back({out.target, out.amount, height, unlock_time, i, j, &blk, vtx[i]});
|
||||
size_t tx_global_idx = outs.size() - 1;
|
||||
outs[tx_global_idx].idx = tx_global_idx;
|
||||
outs[tx_global_idx].mask = rct::zeroCommit(out.amount);
|
||||
// Is out to me?
|
||||
if (is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), j)) {
|
||||
outs_mine.push_back(tx_global_idx);
|
||||
auto& out = outs.back();
|
||||
if (out.amount == 0) {
|
||||
out.amount = get_amount(from, tx, j);
|
||||
const auto gov_key = cryptonote::get_deterministic_keypair_from_height(height);
|
||||
|
||||
const bool to_acc_regular = is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), j);
|
||||
const bool to_acc_sn_reward = to_acc_regular ? false : is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), gov_key.pub, {}, j);
|
||||
|
||||
if (to_acc_regular || to_acc_sn_reward) {
|
||||
outs_mine.push_back(tx_global_idx);
|
||||
auto& oi = outs.back();
|
||||
oi.is_sn_reward = to_acc_sn_reward;
|
||||
if (oi.amount == 0) {
|
||||
oi.amount = get_amount(from, tx, j);
|
||||
oi.mask = tx.rct_signatures.outPk[j].mask;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,10 +446,14 @@ bool init_spent_output_indices(output_index_vec& outs,
|
|||
crypto::public_key out_key = boost::get<txout_to_key>(oi.out).key;
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[from.get_keys().m_account_address.m_spend_public_key] = {0,0};
|
||||
|
||||
const auto tx_pk = oi.is_sn_reward ? get_deterministic_keypair_from_height(oi.blk_height).pub
|
||||
: get_tx_pub_key_from_extra(*oi.p_tx);
|
||||
|
||||
generate_key_image_helper(from.get_keys(),
|
||||
subaddresses,
|
||||
out_key,
|
||||
get_tx_pub_key_from_extra(*oi.p_tx),
|
||||
tx_pk,
|
||||
get_additional_tx_pub_keys_from_extra(*oi.p_tx),
|
||||
oi.out_no,
|
||||
in_ephemeral,
|
||||
|
@ -483,7 +506,7 @@ static bool fill_output_entries(const std::vector<output_index>& out_indices, si
|
|||
if (append)
|
||||
{
|
||||
const txout_to_key& otk = boost::get<txout_to_key>(oi.out);
|
||||
output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), rct::zeroCommit(oi.amount)})));
|
||||
output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), oi.mask})));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,18 +541,32 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te
|
|||
|
||||
cryptonote::tx_source_entry ts;
|
||||
|
||||
|
||||
const auto& tx = *oi.p_tx;
|
||||
ts.amount = oi.amount;
|
||||
ts.real_output_in_tx_index = oi.out_no;
|
||||
ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // incoming tx public key
|
||||
ts.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(*oi.p_tx);
|
||||
ts.real_out_tx_key = get_tx_pub_key_from_extra(tx); // incoming tx public key
|
||||
ts.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(tx);
|
||||
ts.mask = rct::identity();
|
||||
size_t realOutput;
|
||||
if (!fill_output_entries(outs, sender_out, nmix, realOutput, ts.outputs)) continue;
|
||||
|
||||
ts.real_output = realOutput;
|
||||
ts.rct = true;
|
||||
|
||||
/// Filling in the mask
|
||||
{
|
||||
crypto::key_derivation derivation;
|
||||
bool r = crypto::generate_key_derivation(ts.real_out_tx_key, from.get_keys().m_view_secret_key, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
|
||||
crypto::secret_key amount_key;
|
||||
crypto::derivation_to_scalar(derivation, oi.out_no, amount_key);
|
||||
if (tx.rct_signatures.type == rct::RCTTypeSimple || tx.rct_signatures.type == rct::RCTTypeSimpleBulletproof)
|
||||
rct::decodeRctSimple(
|
||||
tx.rct_signatures, rct::sk2rct(amount_key), oi.out_no, ts.mask, hw::get_device("default"));
|
||||
else if (tx.rct_signatures.type == rct::RCTTypeFull ||
|
||||
tx.rct_signatures.type == rct::RCTTypeFullBulletproof)
|
||||
rct::decodeRct(
|
||||
tx.rct_signatures, rct::sk2rct(amount_key), oi.out_no, ts.mask, hw::get_device("default"));
|
||||
}
|
||||
|
||||
if (!fill_output_entries(outs, sender_out, nmix, ts.real_output, ts.outputs)) continue;
|
||||
|
||||
sources.push_back(ts);
|
||||
|
||||
sources_amount += ts.amount;
|
||||
|
@ -550,7 +587,7 @@ bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_bas
|
|||
void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const block& blk_head,
|
||||
const cryptonote::account_base& from, const cryptonote::account_base& to,
|
||||
uint64_t amount, uint64_t fee, size_t nmix, std::vector<tx_source_entry>& sources,
|
||||
std::vector<tx_destination_entry>& destinations)
|
||||
std::vector<tx_destination_entry>& destinations, uint64_t *change_amount)
|
||||
{
|
||||
sources.clear();
|
||||
destinations.clear();
|
||||
|
@ -564,13 +601,15 @@ void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& event
|
|||
destinations.push_back(de);
|
||||
|
||||
tx_destination_entry de_change;
|
||||
uint64_t cache_back = get_inputs_amount(sources) - (amount + fee);
|
||||
if (0 < cache_back)
|
||||
uint64_t cash_back = get_inputs_amount(sources) - (amount + fee);
|
||||
if (0 < cash_back)
|
||||
{
|
||||
if (!fill_tx_destination(de_change, from, cache_back))
|
||||
if (!fill_tx_destination(de_change, from, cash_back))
|
||||
throw std::runtime_error("couldn't fill transaction cache back destination");
|
||||
destinations.push_back(de_change);
|
||||
}
|
||||
|
||||
if (change_amount) *change_amount = (cash_back > 0) ? cash_back : 0;
|
||||
}
|
||||
|
||||
void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height)
|
||||
|
@ -580,6 +619,86 @@ void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t
|
|||
blk.timestamp++;
|
||||
}
|
||||
|
||||
static crypto::public_key get_output_key(const keypair& txkey,
|
||||
const cryptonote::account_public_address& addr,
|
||||
size_t output_index)
|
||||
{
|
||||
crypto::key_derivation derivation;
|
||||
crypto::generate_key_derivation(addr.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::derive_public_key(derivation, 1, addr.m_spend_public_key, out_eph_public_key);
|
||||
return out_eph_public_key;
|
||||
}
|
||||
|
||||
bool construct_miner_tx_with_extra_output(cryptonote::transaction& tx,
|
||||
const cryptonote::account_public_address& miner_address,
|
||||
size_t height,
|
||||
uint64_t already_generated_coins,
|
||||
const cryptonote::account_public_address& extra_address)
|
||||
{
|
||||
|
||||
|
||||
keypair txkey = keypair::generate(hw::get_device("default"));
|
||||
add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
|
||||
keypair gov_key = get_deterministic_keypair_from_height(height);
|
||||
if (already_generated_coins != 0) {
|
||||
add_tx_pub_key_to_extra(tx, gov_key.pub);
|
||||
}
|
||||
|
||||
txin_gen in;
|
||||
in.height = height;
|
||||
tx.vin.push_back(in);
|
||||
|
||||
// This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
|
||||
uint64_t block_reward;
|
||||
if (!get_block_reward(0, 0, already_generated_coins, block_reward, 1, 0)) {
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int hard_fork_version = 7;
|
||||
|
||||
uint64_t governance_reward = 0;
|
||||
if (already_generated_coins != 0) {
|
||||
governance_reward = get_governance_reward(height, block_reward);
|
||||
block_reward -= governance_reward;
|
||||
}
|
||||
|
||||
tx.version = 1;
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
|
||||
/// half of the miner reward goes to the other account
|
||||
const auto miner_reward = block_reward / 2;
|
||||
|
||||
/// miner reward
|
||||
tx.vout.push_back({miner_reward, get_output_key(txkey, miner_address, 0)});
|
||||
|
||||
/// extra reward
|
||||
tx.vout.push_back({miner_reward, get_output_key(txkey, extra_address, 1)});
|
||||
|
||||
/// governance reward
|
||||
if (already_generated_coins != 0) {
|
||||
|
||||
cryptonote::address_parse_info governance_wallet_address;
|
||||
cryptonote::get_account_address_from_str(
|
||||
governance_wallet_address, cryptonote::MAINNET, ::config::GOVERNANCE_WALLET_ADDRESS);
|
||||
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
|
||||
if (!get_deterministic_output_key(
|
||||
governance_wallet_address.address, gov_key, tx.vout.size(), out_eph_public_key)) {
|
||||
MERROR("Failed to generate deterministic output key for governance wallet output creation");
|
||||
return false;
|
||||
}
|
||||
|
||||
tx.vout.push_back({governance_reward, out_eph_public_key});
|
||||
tx.output_unlock_times.push_back(height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
|
||||
const account_public_address& miner_address, transaction& tx, uint64_t fee,
|
||||
keypair* p_txkey/* = 0*/)
|
||||
|
@ -632,14 +751,47 @@ bool construct_tx_to_key(const std::vector<test_event_entry>& events,
|
|||
|
||||
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)
|
||||
uint64_t fee, size_t nmix, bool stake, boost::optional<const register_info> reg_info, uint64_t unlock_time)
|
||||
{
|
||||
vector<tx_source_entry> sources;
|
||||
vector<tx_destination_entry> destinations;
|
||||
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations);
|
||||
tx_destination_entry change_addr{ amount, from.get_keys().m_account_address, false /* is subaddr */ };
|
||||
|
||||
return cryptonote::construct_tx(from.get_keys(), sources, destinations, change_addr, {}, tx, 0);
|
||||
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 */ };
|
||||
|
||||
|
||||
std::vector<uint8_t> extra;
|
||||
|
||||
if (stake) {
|
||||
|
||||
if (!reg_info) {
|
||||
LOG_ERROR("Stake tx has not registration info");
|
||||
return false;
|
||||
}
|
||||
|
||||
add_service_node_pubkey_to_tx_extra(extra, reg_info->service_node_keypair.pub);
|
||||
|
||||
const uint64_t exp_timestamp = time(nullptr) + STAKING_AUTHORIZATION_EXPIRATION_WINDOW;
|
||||
|
||||
crypto::hash hash;
|
||||
bool hashed = cryptonote::get_registration_hash(reg_info->addresses, reg_info->operator_cut, reg_info->portions, exp_timestamp, hash);
|
||||
if (!hashed)
|
||||
{
|
||||
MERROR("Could not make registration hash from addresses and portions");
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto::signature signature;
|
||||
crypto::generate_signature(hash, reg_info->service_node_keypair.pub, reg_info->service_node_keypair.sec, signature);
|
||||
|
||||
add_service_node_register_to_tx_extra(extra, reg_info->addresses, reg_info->operator_cut, reg_info->portions, exp_timestamp, signature);
|
||||
add_service_node_contributor_to_tx_extra(extra, reg_info->addresses.at(0));
|
||||
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
|
@ -673,6 +825,32 @@ uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cry
|
|||
return res;
|
||||
}
|
||||
|
||||
uint64_t get_unlocked_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx) {
|
||||
|
||||
if (blockchain.empty()) return 0;
|
||||
|
||||
uint64_t res = 0;
|
||||
output_index_vec outs;
|
||||
output_vec outs_mine;
|
||||
|
||||
map_hash2tx_t confirmed_txs;
|
||||
get_confirmed_txs(blockchain, mtx, confirmed_txs);
|
||||
|
||||
if (!init_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
|
||||
return false;
|
||||
|
||||
if (!init_spent_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
|
||||
return false;
|
||||
|
||||
for (const size_t out_idx : outs_mine) {
|
||||
const auto unlocked = rules::is_output_unlocked(outs[out_idx].unlock_time, get_block_height(blockchain.back()));
|
||||
if (outs[out_idx].spent || !unlocked) continue;
|
||||
res += outs[out_idx].amount;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs)
|
||||
{
|
||||
std::unordered_set<crypto::hash> confirmed_hashes;
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "tests.core"
|
||||
|
||||
|
||||
#define TESTS_DEFAULT_FEE ((uint64_t)200000000) // 2 * pow(10, 8)
|
||||
|
||||
struct callback_entry
|
||||
{
|
||||
|
@ -106,10 +106,12 @@ struct event_visitor_settings
|
|||
{
|
||||
int valid_mask;
|
||||
bool txs_keeped_by_block;
|
||||
crypto::secret_key service_node_key;
|
||||
|
||||
enum settings
|
||||
{
|
||||
set_txs_keeped_by_block = 1 << 0
|
||||
set_txs_keeped_by_block = 1 << 0,
|
||||
set_service_node_key = 1 << 1
|
||||
};
|
||||
|
||||
event_visitor_settings(int a_valid_mask = 0, bool a_txs_keeped_by_block = false)
|
||||
|
@ -118,6 +120,13 @@ struct event_visitor_settings
|
|||
{
|
||||
}
|
||||
|
||||
static event_visitor_settings make_set_service_node_key(const crypto::secret_key& a_service_node_key)
|
||||
{
|
||||
event_visitor_settings settings(set_service_node_key);
|
||||
settings.service_node_key = a_service_node_key;
|
||||
return settings;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
|
||||
|
@ -126,6 +135,7 @@ private:
|
|||
{
|
||||
ar & valid_mask;
|
||||
ar & txs_keeped_by_block;
|
||||
ar & service_node_key;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -156,13 +166,6 @@ class test_generator
|
|||
public:
|
||||
struct block_info
|
||||
{
|
||||
block_info()
|
||||
: prev_id()
|
||||
, already_generated_coins(0)
|
||||
, block_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_size)
|
||||
: prev_id(a_prev_id)
|
||||
, already_generated_coins(an_already_generated_coins)
|
||||
|
@ -188,6 +191,8 @@ public:
|
|||
bf_hf_version= 1 << 8
|
||||
};
|
||||
|
||||
using sn_contributor_t = std::pair<cryptonote::account_public_address, uint64_t>;
|
||||
|
||||
void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const;
|
||||
void get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const;
|
||||
uint64_t get_already_generated_coins(const crypto::hash& blk_id) const;
|
||||
|
@ -196,10 +201,12 @@ public:
|
|||
void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins);
|
||||
bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
|
||||
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
|
||||
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list);
|
||||
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list, const crypto::public_key& sn_pub_key = crypto::null_pkey,
|
||||
const std::vector<sn_contributor_t>& = {{{crypto::null_pkey, crypto::null_pkey}, STAKING_PORTIONS}});
|
||||
bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp);
|
||||
bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc,
|
||||
const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>());
|
||||
const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>(), const crypto::public_key& sn_pub_key = crypto::null_pkey,
|
||||
const std::vector<sn_contributor_t>& = {{{crypto::null_pkey, crypto::null_pkey}, STAKING_PORTIONS}});
|
||||
|
||||
bool construct_block_manually(cryptonote::block& blk, const cryptonote::block& prev_block,
|
||||
const cryptonote::account_base& miner_acc, int actual_params = bf_none, uint8_t major_ver = 0,
|
||||
|
@ -209,14 +216,24 @@ public:
|
|||
bool construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
|
||||
const cryptonote::account_base& miner_acc, const std::vector<crypto::hash>& tx_hashes, size_t txs_size);
|
||||
|
||||
explicit test_generator(uint8_t hf_version = 7) : hf_version_(hf_version) {}
|
||||
|
||||
void set_hf_version(uint8_t ver) { hf_version_ = ver; }
|
||||
|
||||
private:
|
||||
std::unordered_map<crypto::hash, block_info> m_blocks_info;
|
||||
uint8_t hf_version_ = 7;
|
||||
uint8_t hf_version_;
|
||||
};
|
||||
|
||||
inline cryptonote::difficulty_type get_test_difficulty() {return 1;}
|
||||
void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height);
|
||||
|
||||
bool construct_miner_tx_with_extra_output(cryptonote::transaction& tx,
|
||||
const cryptonote::account_public_address& miner_address,
|
||||
size_t height,
|
||||
uint64_t already_generated_coins,
|
||||
const cryptonote::account_public_address& extra_address);
|
||||
|
||||
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);
|
||||
|
@ -226,9 +243,18 @@ bool construct_tx_to_key(const std::vector<test_event_entry>& events,
|
|||
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);
|
||||
uint64_t amount, uint64_t fee, size_t nmix, bool stake=false, boost::optional<const register_info> reg_info = boost::none, 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,
|
||||
uint64_t amount, uint64_t fee);
|
||||
|
@ -239,8 +265,12 @@ void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& event
|
|||
const cryptonote::account_base& from, const cryptonote::account_base& to,
|
||||
uint64_t amount, uint64_t fee, size_t nmix,
|
||||
std::vector<cryptonote::tx_source_entry>& sources,
|
||||
std::vector<cryptonote::tx_destination_entry>& destinations);
|
||||
std::vector<cryptonote::tx_destination_entry>& destinations, uint64_t *change_amount = nullptr);
|
||||
|
||||
/// 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);
|
||||
uint64_t get_unlocked_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
|
@ -578,7 +608,14 @@ inline bool do_replay_file(const std::string& filename)
|
|||
register_callback(#METHOD, boost::bind(&CLASS::METHOD, this, _1, _2, _3));
|
||||
|
||||
#define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS) \
|
||||
test_generator generator; \
|
||||
test_generator generator; \
|
||||
cryptonote::block BLK_NAME; \
|
||||
generator.construct_block(BLK_NAME, MINER_ACC, TS); \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
||||
/// TODO: use hf_ver from test options
|
||||
#define MAKE_GENESIS_BLOCK_WITH_HF_VERSION(VEC_EVENTS, BLK_NAME, MINER_ACC, TS, HF_VER) \
|
||||
test_generator generator(HF_VER); \
|
||||
cryptonote::block BLK_NAME; \
|
||||
generator.construct_block(BLK_NAME, MINER_ACC, TS); \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
@ -588,6 +625,11 @@ inline bool do_replay_file(const std::string& filename)
|
|||
generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC); \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
||||
#define MAKE_NEXT_BLOCK_V2(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, WINNER, SN_INFO) \
|
||||
cryptonote::block BLK_NAME; \
|
||||
generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, {}, WINNER, SN_INFO); \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
||||
#define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \
|
||||
cryptonote::block BLK_NAME; \
|
||||
{ \
|
||||
|
@ -614,8 +656,43 @@ inline bool do_replay_file(const std::string& filename)
|
|||
BLK_NAME = blk_last; \
|
||||
}
|
||||
|
||||
#define REWIND_BLOCKS_N_V2(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT, WINNER, SN_INFO) \
|
||||
cryptonote::block BLK_NAME; \
|
||||
{ \
|
||||
cryptonote::block blk_last = PREV_BLOCK; \
|
||||
for (size_t i = 0; i < COUNT; ++i) \
|
||||
{ \
|
||||
MAKE_NEXT_BLOCK_V2(VEC_EVENTS, blk, blk_last, MINER_ACC, WINNER, SN_INFO); \
|
||||
blk_last = blk; \
|
||||
} \
|
||||
BLK_NAME = blk_last; \
|
||||
}
|
||||
|
||||
#define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW)
|
||||
|
||||
inline cryptonote::transaction make_registration_tx(
|
||||
std::vector<test_event_entry>& events,
|
||||
const cryptonote::account_base& account,
|
||||
const cryptonote::keypair& service_node_keys,
|
||||
uint64_t operator_cut,
|
||||
const std::vector<cryptonote::account_public_address>& addresses,
|
||||
const std::vector<uint64_t>& portions,
|
||||
const cryptonote::block& head)
|
||||
{
|
||||
|
||||
const auto new_height = cryptonote::get_block_height(head) + 1;
|
||||
const auto staking_requirement = service_nodes::get_staking_requirement(cryptonote::FAKECHAIN, new_height);
|
||||
|
||||
uint64_t amount = service_nodes::portions_to_amount(portions[0], staking_requirement);
|
||||
|
||||
cryptonote::transaction tx;
|
||||
boost::optional<const register_info> reg_info = register_info{service_node_keys, portions, operator_cut, addresses};
|
||||
const auto unlock_time = new_height + service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN);
|
||||
construct_tx_to_key(events, tx, head, account, account, amount, TESTS_DEFAULT_FEE, 9, true /* staking */, reg_info, unlock_time);
|
||||
events.push_back(tx);
|
||||
return tx;
|
||||
}
|
||||
|
||||
#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); \
|
||||
|
@ -638,14 +715,12 @@ inline bool do_replay_file(const std::string& filename)
|
|||
std::list<cryptonote::transaction> SET_NAME; \
|
||||
MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD);
|
||||
|
||||
#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \
|
||||
transaction TX; \
|
||||
if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \
|
||||
miner_account.get_keys().m_account_address, TX, 0, KEY)) \
|
||||
#define MAKE_MINER_TX_MANUALLY(TX, BLK) \
|
||||
transaction TX; \
|
||||
if (!construct_miner_tx(get_block_height(BLK)+1, 0, generator.get_already_generated_coins(BLK), \
|
||||
0, 0, miner_account.get_keys().m_account_address, TX, {}, 7)) \
|
||||
return false;
|
||||
|
||||
#define MAKE_MINER_TX_MANUALLY(TX, BLK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, 0)
|
||||
|
||||
#define SET_EVENT_VISITOR_SETT(VEC_EVENTS, SETT, VAL) VEC_EVENTS.push_back(event_visitor_settings(SETT, VAL));
|
||||
|
||||
#define GENERATE(filename, genclass) \
|
||||
|
@ -716,4 +791,3 @@ inline bool do_replay_file(const std::string& filename)
|
|||
#define CHECK_EQ(v1, v2) CHECK_AND_ASSERT_MES(v1 == v2, false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " == " << QUOTEME(v2) << "\", " << v1 << " != " << v2)
|
||||
#define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2)
|
||||
#define MK_COINS(amount) (UINT64_C(amount) * COIN)
|
||||
#define TESTS_DEFAULT_FEE ((uint64_t)20000000000) // 2 * pow(10, 10)
|
||||
|
|
|
@ -91,6 +91,7 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
else if (command_line::get_arg(vm, arg_generate_and_play_test_data))
|
||||
{
|
||||
GENERATE_AND_PLAY(gen_service_nodes);
|
||||
GENERATE_AND_PLAY(gen_simple_chain_001);
|
||||
GENERATE_AND_PLAY(gen_simple_chain_split_1);
|
||||
GENERATE_AND_PLAY(one_block);
|
||||
|
@ -117,7 +118,6 @@ int main(int argc, char* argv[])
|
|||
GENERATE_AND_PLAY(gen_block_miner_tx_has_2_tx_gen_in);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_2_in);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_with_txin_to_key);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_is_small);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_is_big);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_no_out);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_out_to_alice);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "double_spend.h"
|
||||
#include "integer_overflow.h"
|
||||
#include "ring_signature_1.h"
|
||||
#include "service_nodes.h"
|
||||
#include "tx_validation.h"
|
||||
#include "v2_tests.h"
|
||||
#include "rct.h"
|
||||
|
|
|
@ -87,7 +87,7 @@ bool gen_ring_signature_1::generate(std::vector<test_event_entry>& events) const
|
|||
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, 2, blk_6); // 24 + 3N
|
||||
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
|
||||
|
||||
|
@ -144,8 +144,8 @@ gen_ring_signature_2::gen_ring_signature_2()
|
|||
}
|
||||
|
||||
/**
|
||||
* Bob has 4 inputs by 13 coins. He sends 4 * 13 coins to Alice, using ring signature with nmix = 3. Each Bob's input
|
||||
* is used as mix for 3 others.
|
||||
* Bob has 4 inputs by 13 coins. He sends 4 * 13 coins to Alice, using ring signature with nmix = 9. Each Bob's input
|
||||
* is used as mix for 9 others.
|
||||
*/
|
||||
bool gen_ring_signature_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
|
@ -159,18 +159,19 @@ bool gen_ring_signature_2::generate(std::vector<test_event_entry>& events) const
|
|||
MAKE_ACCOUNT(events, alice_account); // 2
|
||||
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account); // 3
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 4
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_account); // 5
|
||||
REWIND_BLOCKS(events, blk_2b, blk_2, miner_account); // <N blocks>
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2b, miner_account); // 5 + N
|
||||
REWIND_BLOCKS(events, blk_3r, blk_3, miner_account); // <N blocks>
|
||||
MAKE_TX_LIST_START(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 6 + N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 7 + N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 8 + N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 9 + N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_blk_4); // 10 + N
|
||||
DO_CALLBACK(events, "check_balances_1"); // 11 + N
|
||||
MAKE_TX_LIST_START(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 6 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 7 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 8 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 9 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_blk_4); // 10 + 2N
|
||||
DO_CALLBACK(events, "check_balances_1"); // 11 + 2N
|
||||
REWIND_BLOCKS(events, blk_4r, blk_4, miner_account); // <N blocks>
|
||||
MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(52) - TESTS_DEFAULT_FEE, 3, blk_4); // 12 + 2N
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4r, miner_account, tx_0); // 13 + 2N
|
||||
DO_CALLBACK(events, "check_balances_2"); // 14 + 2N
|
||||
MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(52) - TESTS_DEFAULT_FEE, 9, blk_4); // 12 + 3N
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4r, miner_account, tx_0); // 13 + 3N
|
||||
DO_CALLBACK(events, "check_balances_2"); // 14 + 3N
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -183,7 +184,7 @@ bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index
|
|||
m_alice_account = boost::get<account_base>(events[2]);
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
bool r = c.get_blocks(0, 100 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
|
@ -201,7 +202,7 @@ bool gen_ring_signature_2::check_balances_2(cryptonote::core& c, size_t ev_index
|
|||
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_2");
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
bool r = c.get_blocks(0, 100 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright (c) 2014-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "service_nodes.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
|
||||
gen_service_nodes::gen_service_nodes()
|
||||
{
|
||||
/// NOTE: we don't generate random keys here, because the verification will call its own constructor
|
||||
constexpr char pub_key_str[] = "cf6ae1d4e902f7a85af58d6069c29f09702e25fd07cf28d359e64401002db2a1";
|
||||
constexpr char sec_key_str[] = "ead4cc692c4237f62f9cefaf5e106995b2dda79a29002a546876f9ee7abcc203";
|
||||
|
||||
epee::string_tools::hex_to_pod(pub_key_str, m_alice_service_node_keys.pub);
|
||||
epee::string_tools::hex_to_pod(sec_key_str, m_alice_service_node_keys.sec);
|
||||
|
||||
REGISTER_CALLBACK("check_registered", gen_service_nodes::check_registered);
|
||||
REGISTER_CALLBACK("check_expired", gen_service_nodes::check_expired);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_service_nodes::generate(std::vector<test_event_entry> &events) const
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
|
||||
GENERATE_ACCOUNT(miner);
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner, ts_start); // 1
|
||||
MAKE_ACCOUNT(events, alice);
|
||||
|
||||
generator.set_hf_version(8);
|
||||
MAKE_NEXT_BLOCK(events, blk_a, blk_0, miner); // 2
|
||||
|
||||
generator.set_hf_version(9);
|
||||
MAKE_NEXT_BLOCK(events, blk_b, blk_a, miner); // 3
|
||||
|
||||
|
||||
REWIND_BLOCKS_N(events, blk_c, blk_b, miner, 10); // 13
|
||||
|
||||
REWIND_BLOCKS(events, blk_1, blk_c, miner); // 13 + N
|
||||
|
||||
MAKE_TX(events, tx_0, miner, alice, MK_COINS(101), blk_1);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1, miner, tx_0); // 14 + N
|
||||
|
||||
REWIND_BLOCKS(events, blk_3, blk_2, miner); // 14 + 2N
|
||||
|
||||
cryptonote::transaction alice_registration =
|
||||
make_registration_tx(events, alice, m_alice_service_node_keys, 0, { alice.get_keys().m_account_address }, { STAKING_PORTIONS }, blk_3);
|
||||
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner, alice_registration); // 15 + 2N
|
||||
|
||||
DO_CALLBACK(events, "check_registered");
|
||||
std::vector<test_generator::sn_contributor_t> sn_info = {{alice.get_keys().m_account_address, STAKING_PORTIONS}};
|
||||
REWIND_BLOCKS_N_V2(events, blk_5, blk_4, miner, service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN), m_alice_service_node_keys.pub, sn_info); // 15 + 2N + M
|
||||
DO_CALLBACK(events, "check_expired");
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_service_nodes::check_registered(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_service_nodes::check_registered");
|
||||
|
||||
cryptonote::account_base alice = boost::get<cryptonote::account_base>(events[1]);
|
||||
|
||||
std::list<block> block_list;
|
||||
bool r = c.get_blocks(0, 15 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
std::vector<block> blocks(block_list.begin(), block_list.end());
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
const uint64_t staking_requirement = MK_COINS(100);
|
||||
|
||||
CHECK_EQ(MK_COINS(101) - TESTS_DEFAULT_FEE - staking_requirement, get_unlocked_balance(alice, blocks, mtx));
|
||||
|
||||
/// check that alice is registered
|
||||
const auto info_v = c.get_service_node_list_state({m_alice_service_node_keys.pub});
|
||||
CHECK_EQ(info_v.empty(), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_service_nodes::check_expired(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_service_nodes::check_expired");
|
||||
|
||||
cryptonote::account_base alice = boost::get<cryptonote::account_base>(events[1]);
|
||||
|
||||
const auto stake_lock_time = service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN);
|
||||
|
||||
std::list<block> block_list;
|
||||
|
||||
bool r = c.get_blocks(0, 15 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + stake_lock_time, block_list);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
std::vector<block> blocks(block_list.begin(), block_list.end());
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
/// check that alice's registration expired
|
||||
const auto info_v = c.get_service_node_list_state({m_alice_service_node_keys.pub});
|
||||
CHECK_EQ(info_v.empty(), true);
|
||||
|
||||
/// check that alice received some service node rewards (TODO: check the balance precisely)
|
||||
CHECK_TEST_CONDITION(get_balance(alice, blocks, mtx) > MK_COINS(101) - TESTS_DEFAULT_FEE);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2014-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class gen_service_nodes : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_service_nodes();
|
||||
bool generate(std::vector<test_event_entry> &events) const;
|
||||
bool check_registered(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_expired(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
private:
|
||||
cryptonote::keypair m_alice_service_node_keys;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct get_test_options<gen_service_nodes> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(7, 0), std::make_pair(8, 1), std::make_pair(9, 2)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks
|
||||
};
|
||||
};
|
|
@ -56,6 +56,7 @@ set(unit_tests_sources
|
|||
multisig.cpp
|
||||
parse_amount.cpp
|
||||
serialization.cpp
|
||||
service_nodes.cpp
|
||||
sha256.cpp
|
||||
slow_memmem.cpp
|
||||
subaddress.cpp
|
||||
|
@ -69,7 +70,8 @@ set(unit_tests_sources
|
|||
ringct.cpp
|
||||
output_selection.cpp
|
||||
vercmp.cpp
|
||||
ringdb.cpp)
|
||||
ringdb.cpp
|
||||
)
|
||||
|
||||
set(unit_tests_headers
|
||||
unit_tests_utils.h)
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
// Copyright (c) 2018, The Loki Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "cryptonote_core/service_node_list.h"
|
||||
#include "cryptonote_core/service_node_deregister.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_config.h"
|
||||
|
||||
TEST(service_nodes, staking_requirement)
|
||||
{
|
||||
// TODO(loki): The current reference values here for the staking requirement
|
||||
// at certain heights has been derived from excel, so we have to use an
|
||||
// epsilon for dust amounts as amounts are off by a bit. When we switch to
|
||||
// integer math we can remove the need for this. Doyle - 2018-08-28
|
||||
|
||||
// NOTE: Thanks for the values @Sonofotis
|
||||
const uint64_t atomic_epsilon = config::DEFAULT_DUST_THRESHOLD;
|
||||
|
||||
// LHS of Equation
|
||||
// Try underflow
|
||||
{
|
||||
uint64_t height = 100;
|
||||
uint64_t mainnet_requirement = service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
uint64_t stagenet_requirement = service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
ASSERT_EQ(stagenet_requirement, (45000 * COIN));
|
||||
ASSERT_EQ(mainnet_requirement, (45000 * COIN));
|
||||
}
|
||||
|
||||
// Starting height for stagenet
|
||||
{
|
||||
uint64_t height = 96210;
|
||||
uint64_t stagenet_requirement = service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
ASSERT_EQ(stagenet_requirement, (45000 * COIN));
|
||||
}
|
||||
|
||||
// Starting height for mainnet
|
||||
{
|
||||
// NOTE: The maximum staking requirement is 50,000, in atomic units is 50,000,000,000,000 < int64 range (2^63-1)
|
||||
// so casting is safe.
|
||||
uint64_t height = 101250;
|
||||
int64_t mainnet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
int64_t stagenet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
|
||||
ASSERT_EQ(mainnet_requirement, (45000 * COIN));
|
||||
|
||||
int64_t stagenet_expected = (int64_t)((44069 * COIN) + 151880000);
|
||||
int64_t stagenet_delta = std::abs(stagenet_requirement - stagenet_expected);
|
||||
ASSERT_LT(stagenet_delta, atomic_epsilon);
|
||||
}
|
||||
|
||||
// Check the requirements are decreasing
|
||||
{
|
||||
uint64_t height = 250000;
|
||||
int64_t mainnet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
int64_t stagenet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
|
||||
int64_t mainnet_expected = (int64_t)((25796 * COIN) + 364642307);
|
||||
int64_t mainnet_delta = std::abs(mainnet_requirement - mainnet_expected);
|
||||
ASSERT_LT(mainnet_delta, atomic_epsilon);
|
||||
|
||||
int64_t stagenet_expected = (int64_t)((25376 * COIN) + 249888366);
|
||||
int64_t stagenet_delta = std::abs(stagenet_requirement - stagenet_expected);
|
||||
ASSERT_LT(stagenet_delta, atomic_epsilon);
|
||||
}
|
||||
|
||||
// Bottom of the curve, generally this should be the lowest the staking requirement will be
|
||||
{
|
||||
uint64_t height = 1036800;
|
||||
int64_t mainnet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
int64_t stagenet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
|
||||
int64_t mainnet_expected = (int64_t)((10234 * COIN) + 967482165);
|
||||
int64_t mainnet_delta = std::abs(mainnet_requirement - mainnet_expected);
|
||||
ASSERT_LT(mainnet_delta, atomic_epsilon);
|
||||
|
||||
int64_t stagenet_expected = (int64_t)((10228 * COIN) + 718366740);
|
||||
int64_t stagenet_delta = std::abs(stagenet_requirement - stagenet_expected);
|
||||
ASSERT_LT(stagenet_delta, atomic_epsilon);
|
||||
}
|
||||
|
||||
// RHS of Rewards Formula, 1st part
|
||||
// Where the two equations should meet and staking formula equalizes
|
||||
{
|
||||
uint64_t height = 1166400;
|
||||
uint64_t mainnet_requirement = service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
uint64_t stagenet_requirement = service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
ASSERT_EQ(mainnet_requirement, (10250 * COIN));
|
||||
ASSERT_EQ(stagenet_requirement, (10250 * COIN));
|
||||
}
|
||||
|
||||
// Checking the requirements still equal
|
||||
{
|
||||
uint64_t height = 1296000;
|
||||
uint64_t mainnet_requirement = service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
uint64_t stagenet_requirement = service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
|
||||
ASSERT_EQ(mainnet_requirement, (10500 * COIN));
|
||||
ASSERT_EQ(stagenet_requirement, (10500 * COIN));
|
||||
}
|
||||
|
||||
// Checking we are approaching 15000
|
||||
{
|
||||
uint64_t height = 3000000;
|
||||
int64_t mainnet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
int64_t stagenet_requirement = (int64_t)service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
|
||||
int64_t mainnet_expected = (int64_t)((13787 * COIN) + 37037037);
|
||||
int64_t mainnet_delta = std::abs(mainnet_requirement - mainnet_expected);
|
||||
ASSERT_LT(mainnet_delta, atomic_epsilon);
|
||||
|
||||
int64_t stagenet_expected = (int64_t)((13787 * COIN) + 37037037);
|
||||
int64_t stagenet_delta = std::abs(stagenet_requirement - stagenet_expected);
|
||||
ASSERT_LT(stagenet_delta, atomic_epsilon);
|
||||
}
|
||||
|
||||
// RHS of Rewards Formula, 2nd part
|
||||
// Last part of formula maxes out at 15000 if height > 3628800
|
||||
{
|
||||
uint64_t height = 3628800;
|
||||
uint64_t mainnet_requirement = service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
uint64_t stagenet_requirement = service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
|
||||
ASSERT_EQ(mainnet_requirement, (15000 * COIN));
|
||||
ASSERT_EQ(stagenet_requirement, (15000 * COIN));
|
||||
}
|
||||
|
||||
// Check we stay capped at 15000
|
||||
{
|
||||
uint64_t height = 4082400;
|
||||
uint64_t mainnet_requirement = service_nodes::get_staking_requirement(cryptonote::MAINNET, height);
|
||||
uint64_t stagenet_requirement = service_nodes::get_staking_requirement(cryptonote::STAGENET, height);
|
||||
|
||||
ASSERT_EQ(mainnet_requirement, (15000 * COIN));
|
||||
ASSERT_EQ(stagenet_requirement, (15000 * COIN));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(service_nodes, vote_validation)
|
||||
{
|
||||
// Generate a quorum and the voter
|
||||
cryptonote::keypair service_node_voter = cryptonote::keypair::generate(hw::get_device("default"));
|
||||
int voter_index = 0;
|
||||
|
||||
service_nodes::quorum_state state = {};
|
||||
{
|
||||
state.quorum_nodes.resize(10);
|
||||
state.nodes_to_test.resize(state.quorum_nodes.size());
|
||||
|
||||
for (size_t i = 0; i < state.quorum_nodes.size(); ++i)
|
||||
{
|
||||
state.quorum_nodes[i] = (i == voter_index) ? service_node_voter.pub : cryptonote::keypair::generate(hw::get_device("default")).pub;
|
||||
state.nodes_to_test[i] = cryptonote::keypair::generate(hw::get_device("default")).pub;
|
||||
}
|
||||
}
|
||||
|
||||
// Valid vote
|
||||
loki::service_node_deregister::vote valid_vote = {};
|
||||
{
|
||||
valid_vote.block_height = 10;
|
||||
valid_vote.service_node_index = 1;
|
||||
valid_vote.voters_quorum_index = voter_index;
|
||||
valid_vote.signature = loki::service_node_deregister::sign_vote(valid_vote.block_height, valid_vote.service_node_index, service_node_voter.pub, service_node_voter.sec);
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_vote(cryptonote::MAINNET, valid_vote, vvc, state);
|
||||
if (!result)
|
||||
printf("%s\n", cryptonote::print_vote_verification_context(vvc, &valid_vote));
|
||||
|
||||
ASSERT_TRUE(result);
|
||||
}
|
||||
|
||||
// Voters quorum index out of bounds
|
||||
{
|
||||
auto vote = valid_vote;
|
||||
vote.voters_quorum_index = state.quorum_nodes.size() + 10;
|
||||
vote.signature = loki::service_node_deregister::sign_vote(vote.block_height, vote.service_node_index, service_node_voter.pub, service_node_voter.sec);
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_vote(cryptonote::MAINNET, vote, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
|
||||
// Voters service node index out of bounds
|
||||
{
|
||||
auto vote = valid_vote;
|
||||
vote.service_node_index = state.nodes_to_test.size() + 10;
|
||||
vote.signature = loki::service_node_deregister::sign_vote(vote.block_height, vote.service_node_index, service_node_voter.pub, service_node_voter.sec);
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_vote(cryptonote::MAINNET, vote, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
|
||||
// Signature not valid
|
||||
{
|
||||
auto vote = valid_vote;
|
||||
cryptonote::keypair other_voter = cryptonote::keypair::generate(hw::get_device("default"));
|
||||
vote.signature = loki::service_node_deregister::sign_vote(vote.block_height, vote.service_node_index, other_voter.pub, other_voter.sec);
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_vote(cryptonote::MAINNET, vote, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(service_nodes, tx_extra_deregister_validation)
|
||||
{
|
||||
// Generate a quorum and the voter
|
||||
const size_t num_voters = 10;
|
||||
cryptonote::keypair voters[num_voters] = {};
|
||||
|
||||
service_nodes::quorum_state state = {};
|
||||
{
|
||||
state.quorum_nodes.resize(num_voters);
|
||||
state.nodes_to_test.resize(num_voters);
|
||||
|
||||
for (size_t i = 0; i < state.quorum_nodes.size(); ++i)
|
||||
{
|
||||
voters[i] = cryptonote::keypair::generate(hw::get_device("default"));
|
||||
state.quorum_nodes[i] = voters[i].pub;
|
||||
state.nodes_to_test[i] = cryptonote::keypair::generate(hw::get_device("default")).pub;
|
||||
}
|
||||
}
|
||||
|
||||
// Valid deregister
|
||||
cryptonote::tx_extra_service_node_deregister valid_deregister = {};
|
||||
{
|
||||
valid_deregister.block_height = 10;
|
||||
valid_deregister.service_node_index = 1;
|
||||
valid_deregister.votes.reserve(num_voters);
|
||||
for (size_t i = 0; i < num_voters; ++i)
|
||||
{
|
||||
cryptonote::keypair const *voter = voters + i;
|
||||
cryptonote::tx_extra_service_node_deregister::vote vote = {};
|
||||
|
||||
vote.voters_quorum_index = i;
|
||||
vote.signature = loki::service_node_deregister::sign_vote(valid_deregister.block_height, valid_deregister.service_node_index, voter->pub, voter->sec);
|
||||
valid_deregister.votes.push_back(vote);
|
||||
}
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_deregister(cryptonote::MAINNET, valid_deregister, vvc, state);
|
||||
if (!result)
|
||||
printf("%s\n", cryptonote::print_vote_verification_context(vvc));
|
||||
ASSERT_TRUE(result);
|
||||
}
|
||||
|
||||
// Deregister has insufficient votes
|
||||
{
|
||||
auto deregister = valid_deregister;
|
||||
while (deregister.votes.size() >= service_nodes::MIN_VOTES_TO_KICK_SERVICE_NODE)
|
||||
deregister.votes.pop_back();
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_deregister(cryptonote::MAINNET, deregister, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
|
||||
// Deregister has duplicated voter
|
||||
{
|
||||
auto deregister = valid_deregister;
|
||||
deregister.votes[0] = deregister.votes[1];
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_deregister(cryptonote::MAINNET, deregister, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
|
||||
// Deregister has one voter with invalid signature
|
||||
{
|
||||
auto deregister = valid_deregister;
|
||||
deregister.votes[0].signature = deregister.votes[1].signature;
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_deregister(cryptonote::MAINNET, deregister, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
|
||||
// Deregister has one voter with index out of bounds
|
||||
{
|
||||
auto deregister = valid_deregister;
|
||||
deregister.votes[0].voters_quorum_index = state.quorum_nodes.size() + 10;
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_deregister(cryptonote::MAINNET, deregister, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
|
||||
// Deregister service node index is out of bounds
|
||||
{
|
||||
auto deregister = valid_deregister;
|
||||
deregister.service_node_index = state.nodes_to_test.size() + 10;
|
||||
|
||||
cryptonote::vote_verification_context vvc = {};
|
||||
bool result = loki::service_node_deregister::verify_deregister(cryptonote::MAINNET, deregister, vvc, state);
|
||||
ASSERT_FALSE(result);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue