mirror of https://github.com/oxen-io/oxen-core.git
Rotating Governance Schedule (#303)
* Add multiple governance keys * Batch governance payments to every interval blocks * Batching of gov rewards and fix breakages in tests * Add changes to api for working with core tests * Clean up left over debug stuff * more cleanup * Fix uninitialised batched governance value * Remove dependency on blockchain in tx utils * Remove redundant vector reserve
This commit is contained in:
parent
36b2d6b2de
commit
fad67eee46
|
@ -89,7 +89,7 @@ namespace cryptonote {
|
|||
return CRYPTONOTE_MAX_TX_SIZE;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version, uint64_t height) {
|
||||
bool get_base_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version, uint64_t height) {
|
||||
|
||||
//premine reward
|
||||
if (already_generated_coins == 0)
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace cryptonote {
|
|||
size_t get_min_block_weight(uint8_t version);
|
||||
size_t get_max_block_size();
|
||||
size_t get_max_tx_size();
|
||||
bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version, uint64_t height);
|
||||
bool get_base_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version, uint64_t height);
|
||||
uint8_t get_account_address_checksum(const public_address_outer_blob& bl);
|
||||
uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl);
|
||||
|
||||
|
|
|
@ -189,8 +189,12 @@ namespace config
|
|||
std::string const GENESIS_TX = "021e01ff000380808d93f5d771027c4fd4553bc9886f1f49e3f76d945bf71e8632a94e6c177b19cbc780e7e6bdb48080b4ccd4dfc60302c8b9f6461f58ef3f2107e577c7425d06af584a1c7482bf19060e84059c98b4c3808088fccdbcc32302732b53b0b0db706fcc3087074fb4b786da5ab72b2065699f9453448b0db27f892101ed71f2ce3fc70d7b2036f8a4e4b3fb75c66c12184b55a908e7d1a1d6995566cf00";
|
||||
uint32_t const GENESIS_NONCE = 1022201;
|
||||
|
||||
std::string const GOVERNANCE_WALLET_ADDRESS = "LCFxT37LAogDn1jLQKf4y7aAqfi21DjovX9qyijaLYQSdrxY1U5VGcnMJMjWrD9RhjeK5Lym67wZ73uh9AujXLQ1RKmXEyL";
|
||||
|
||||
uint64_t const GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = ((60 * 60 * 24 * 7) / DIFFICULTY_TARGET_V2);
|
||||
std::string const GOVERNANCE_WALLET_ADDRESS[] =
|
||||
{
|
||||
"LCFxT37LAogDn1jLQKf4y7aAqfi21DjovX9qyijaLYQSdrxY1U5VGcnMJMjWrD9RhjeK5Lym67wZ73uh9AujXLQ1RKmXEyL", // hardfork v7-10
|
||||
};
|
||||
|
||||
namespace testnet
|
||||
{
|
||||
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 156;
|
||||
|
@ -205,7 +209,13 @@ namespace config
|
|||
std::string const GENESIS_TX = "03011e001e01ff00018080c9db97f4fb270259b546996f69aa71abe4238995f41d780ab1abebcac9f00e808f147bdb9e3228420112573af8c309b69a1a646f41b5212ba7d9c4590bf86e04f36c486467cfef9d3d72000000000000000000000000000000000000000000000000000000000000000000";
|
||||
uint32_t const GENESIS_NONCE = 10001;
|
||||
|
||||
std::string const GOVERNANCE_WALLET_ADDRESS = "T6SUprTYE5rQpep9iQFxyPcKVd91DFR1fQ1Qsyqp5eYLiFc8XuYd3reRE71qDL8c3DXioUbDEpDFdaUpetnL37NS1R3rzoKxi";
|
||||
uint64_t const GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = 1000;
|
||||
std::string const GOVERNANCE_WALLET_ADDRESS[] =
|
||||
{
|
||||
"T6SUprTYE5rQpep9iQFxyPcKVd91DFR1fQ1Qsyqp5eYLiFc8XuYd3reRE71qDL8c3DXioUbDEpDFdaUpetnL37NS1R3rzoKxi", // hardfork v7-9
|
||||
"T6TzkJb5EiASaCkcH7idBEi1HSrpSQJE1Zq3aL65ojBMPZvqHNYPTL56i3dncGVNEYCG5QG5zrBmRiVwcg6b1cRM1SRNqbp44", // hardfork v10
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace stagenet
|
||||
|
@ -222,12 +232,26 @@ namespace config
|
|||
std::string const GENESIS_TX = "021e01ff000380808d93f5d771027e4490431900c66a6532917ad9e6a1de634a209b708f653097e7b48efc1238c68080b4ccd4dfc60302ba19a224e6474371f9161b2e6271a36d060cbdc2e479ad78f1be64c56576fa07808088fccdbcc32302bccf9c13ba1b5bb02638de6e557acdd46bf48953e42cf98a12d2ad2900cc316121018fc6728d9e3c062d3afae3b2317998d2abee1e12f51271ba1c0d3cdd236b81d200";
|
||||
uint32_t const GENESIS_NONCE = 10002;
|
||||
|
||||
std::string const GOVERNANCE_WALLET_ADDRESS = "59f7FCwYMiwMnFr8HwsnfJ2hK3DYB1tryhjsfmXqEBJojKyqKeNWoaDaZaauoZPiZHUYp2wJuy5s9H96qy4q9xUVCXXHmTU";
|
||||
uint64_t const GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = ((60 * 60 * 24 * 7) / DIFFICULTY_TARGET_V2);
|
||||
std::string const GOVERNANCE_WALLET_ADDRESS[] =
|
||||
{
|
||||
"59f7FCwYMiwMnFr8HwsnfJ2hK3DYB1tryhjsfmXqEBJojKyqKeNWoaDaZaauoZPiZHUYp2wJuy5s9H96qy4q9xUVCXXHmTU", // hardfork v7-9
|
||||
"59f7FCwYMiwMnFr8HwsnfJ2hK3DYB1tryhjsfmXqEBJojKyqKeNWoaDaZaauoZPiZHUYp2wJuy5s9H96qy4q9xUVCXXHmTU", // hardfork v10
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
enum network_version
|
||||
{
|
||||
network_version_7 = 7,
|
||||
network_version_8,
|
||||
network_version_9_service_nodes, // Proof Of Stake w/ Service Nodes
|
||||
network_version_10_bulletproofs, // Bulletproofs, Service Node Grace Registration Period, Batched Governance
|
||||
network_version_11_swarms,
|
||||
};
|
||||
|
||||
enum network_type : uint8_t
|
||||
{
|
||||
MAINNET = 0,
|
||||
|
@ -238,19 +262,21 @@ namespace cryptonote
|
|||
};
|
||||
struct config_t
|
||||
{
|
||||
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
|
||||
uint16_t const P2P_DEFAULT_PORT;
|
||||
uint16_t const RPC_DEFAULT_PORT;
|
||||
uint16_t const ZMQ_RPC_DEFAULT_PORT;
|
||||
boost::uuids::uuid const NETWORK_ID;
|
||||
std::string const GENESIS_TX;
|
||||
uint32_t const GENESIS_NONCE;
|
||||
uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
|
||||
uint16_t P2P_DEFAULT_PORT;
|
||||
uint16_t RPC_DEFAULT_PORT;
|
||||
uint16_t ZMQ_RPC_DEFAULT_PORT;
|
||||
boost::uuids::uuid NETWORK_ID;
|
||||
std::string GENESIS_TX;
|
||||
uint32_t GENESIS_NONCE;
|
||||
uint64_t GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS;
|
||||
std::string const *GOVERNANCE_WALLET_ADDRESS;
|
||||
};
|
||||
inline const config_t& get_config(network_type nettype)
|
||||
inline const config_t& get_config(network_type nettype, int hard_fork_version = 7)
|
||||
{
|
||||
static const config_t mainnet = {
|
||||
static config_t mainnet = {
|
||||
::config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
|
@ -259,9 +285,12 @@ namespace cryptonote
|
|||
::config::ZMQ_RPC_DEFAULT_PORT,
|
||||
::config::NETWORK_ID,
|
||||
::config::GENESIS_TX,
|
||||
::config::GENESIS_NONCE
|
||||
::config::GENESIS_NONCE,
|
||||
::config::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS,
|
||||
&::config::GOVERNANCE_WALLET_ADDRESS[0],
|
||||
};
|
||||
static const config_t testnet = {
|
||||
|
||||
static config_t testnet = {
|
||||
::config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
|
@ -270,9 +299,12 @@ namespace cryptonote
|
|||
::config::testnet::ZMQ_RPC_DEFAULT_PORT,
|
||||
::config::testnet::NETWORK_ID,
|
||||
::config::testnet::GENESIS_TX,
|
||||
::config::testnet::GENESIS_NONCE
|
||||
::config::testnet::GENESIS_NONCE,
|
||||
::config::testnet::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS,
|
||||
&::config::testnet::GOVERNANCE_WALLET_ADDRESS[0],
|
||||
};
|
||||
static const config_t stagenet = {
|
||||
|
||||
static config_t stagenet = {
|
||||
::config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
|
@ -281,14 +313,41 @@ namespace cryptonote
|
|||
::config::stagenet::ZMQ_RPC_DEFAULT_PORT,
|
||||
::config::stagenet::NETWORK_ID,
|
||||
::config::stagenet::GENESIS_TX,
|
||||
::config::stagenet::GENESIS_NONCE
|
||||
::config::stagenet::GENESIS_NONCE,
|
||||
::config::stagenet::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS,
|
||||
&::config::stagenet::GOVERNANCE_WALLET_ADDRESS[0],
|
||||
};
|
||||
|
||||
switch (nettype)
|
||||
{
|
||||
case MAINNET: return mainnet;
|
||||
case TESTNET: return testnet;
|
||||
case STAGENET: return stagenet;
|
||||
case FAKECHAIN: return mainnet;
|
||||
case MAINNET: case FAKECHAIN:
|
||||
{
|
||||
if (nettype == FAKECHAIN)
|
||||
mainnet.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = 100;
|
||||
|
||||
return mainnet;
|
||||
}
|
||||
|
||||
case TESTNET:
|
||||
{
|
||||
if (hard_fork_version <= network_version_9_service_nodes)
|
||||
testnet.GOVERNANCE_WALLET_ADDRESS = &::config::testnet::GOVERNANCE_WALLET_ADDRESS[0];
|
||||
else
|
||||
testnet.GOVERNANCE_WALLET_ADDRESS = &::config::testnet::GOVERNANCE_WALLET_ADDRESS[1];
|
||||
|
||||
return testnet;
|
||||
}
|
||||
|
||||
case STAGENET:
|
||||
{
|
||||
if (hard_fork_version <= network_version_9_service_nodes)
|
||||
stagenet.GOVERNANCE_WALLET_ADDRESS = &::config::stagenet::GOVERNANCE_WALLET_ADDRESS[0];
|
||||
else
|
||||
stagenet.GOVERNANCE_WALLET_ADDRESS = &::config::stagenet::GOVERNANCE_WALLET_ADDRESS[1];
|
||||
|
||||
return stagenet;
|
||||
}
|
||||
|
||||
default: throw std::runtime_error("Invalid network type");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -94,9 +94,9 @@ static const struct {
|
|||
time_t time;
|
||||
} mainnet_hard_forks[] = {
|
||||
// version 7 from the start of the blockchain, inhereted from Monero mainnet
|
||||
{ 7, 1, 0, 1503046577 },
|
||||
{ 8, 64324, 0, 1533006000 },
|
||||
{ 9, 101250, 0, 1537444800 },
|
||||
{ network_version_7, 1, 0, 1503046577 },
|
||||
{ network_version_8, 64324, 0, 1533006000 },
|
||||
{ network_version_9_service_nodes, 101250, 0, 1537444800 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
|
@ -106,9 +106,9 @@ static const struct {
|
|||
time_t time;
|
||||
} testnet_hard_forks[] = {
|
||||
// version 7 from the start of the blockchain, inhereted from Monero testnet
|
||||
{ 7, 1, 0, 1533631121 },
|
||||
{ 8, 2, 0, 1533631122 },
|
||||
{ 9, 3, 0, 1533631123 },
|
||||
{ network_version_7, 1, 0, 1533631121 },
|
||||
{ network_version_8, 2, 0, 1533631122 },
|
||||
{ network_version_9_service_nodes, 3, 0, 1533631123 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
|
@ -118,9 +118,9 @@ static const struct {
|
|||
time_t time;
|
||||
} stagenet_hard_forks[] = {
|
||||
// version 7 from the start of the blockchain, inhereted from Monero testnet
|
||||
{ 7, 1, 0, 1341378000 },
|
||||
{ 8, 64324, 0, 1533006000 },
|
||||
{ 9, 96210, 0, 1536840000 },
|
||||
{ network_version_7, 1, 0, 1341378000 },
|
||||
{ network_version_8, 64324, 0, 1533006000 },
|
||||
{ network_version_9_service_nodes, 96210, 0, 1536840000 },
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
@ -1125,84 +1125,68 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
|||
return false;
|
||||
}
|
||||
|
||||
if (version == 3) {
|
||||
for (auto &o: b.miner_tx.vout) {
|
||||
if (!is_valid_decomposed_amount(o.amount)) {
|
||||
MERROR_VER("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t height = cryptonote::get_block_height(b);
|
||||
std::vector<size_t> last_blocks_weights;
|
||||
get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version, cryptonote::get_block_height(b)))
|
||||
|
||||
loki_block_reward_context block_reward_context = {};
|
||||
block_reward_context.fee = fee;
|
||||
block_reward_context.height = height;
|
||||
if (!calc_batched_governance_reward(height, block_reward_context.batched_governance))
|
||||
{
|
||||
MERROR_VER("Failed to calculate batched governance reward");
|
||||
return false;
|
||||
}
|
||||
|
||||
block_reward_parts reward_parts;
|
||||
if (!get_loki_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, version, reward_parts, block_reward_context))
|
||||
{
|
||||
MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ValidateMinerTxHook* hook : m_validate_miner_tx_hooks)
|
||||
if (!hook->validate_miner_tx(b.prev_id, b.miner_tx, m_db->height(), get_current_hard_fork_version(), base_reward))
|
||||
return false;
|
||||
|
||||
if (already_generated_coins != 0)
|
||||
{
|
||||
uint64_t governance_reward = get_governance_reward(m_db->height(), base_reward);
|
||||
if (!hook->validate_miner_tx(b.prev_id, b.miner_tx, m_db->height(), version, reward_parts))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (b.miner_tx.vout.back().amount != governance_reward)
|
||||
if (already_generated_coins != 0 && block_has_governance_output(nettype(), b))
|
||||
{
|
||||
if (version >= network_version_10_bulletproofs && reward_parts.governance == 0)
|
||||
{
|
||||
MERROR("Governance reward amount incorrect. Should be: " << print_money(governance_reward) << ", is: " << print_money(b.miner_tx.vout.back().amount));
|
||||
MERROR("Governance reward should not be 0 after hardfork v10 if this height has a governance output because it is the batched payout height");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string governance_wallet_address_str;
|
||||
switch (m_nettype)
|
||||
if (b.miner_tx.vout.back().amount != reward_parts.governance)
|
||||
{
|
||||
case STAGENET:
|
||||
governance_wallet_address_str = ::config::stagenet::GOVERNANCE_WALLET_ADDRESS;
|
||||
break;
|
||||
case TESTNET:
|
||||
governance_wallet_address_str = ::config::testnet::GOVERNANCE_WALLET_ADDRESS;
|
||||
break;
|
||||
case FAKECHAIN: case MAINNET:
|
||||
governance_wallet_address_str = ::config::GOVERNANCE_WALLET_ADDRESS;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
MERROR("Governance reward amount incorrect. Should be: " << print_money(reward_parts.governance) << ", is: " << print_money(b.miner_tx.vout.back().amount));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validate_governance_reward_key(m_db->height(), governance_wallet_address_str, b.miner_tx.vout.size() - 1, boost::get<txout_to_key>(b.miner_tx.vout.back().target).key, m_nettype))
|
||||
if (!validate_governance_reward_key(m_db->height(), *cryptonote::get_config(m_nettype, version).GOVERNANCE_WALLET_ADDRESS, b.miner_tx.vout.size() - 1, boost::get<txout_to_key>(b.miner_tx.vout.back().target).key, m_nettype))
|
||||
{
|
||||
MERROR("Governance reward public key incorrect.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
base_reward = reward_parts.adjusted_base_reward;
|
||||
if(base_reward + fee < money_in_use)
|
||||
{
|
||||
MERROR_VER("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
|
||||
MERROR_VER("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
|
||||
return false;
|
||||
}
|
||||
// From hard fork 2, we allow a miner to claim less block reward than is allowed, in case a miner wants less dust
|
||||
if (m_hardfork->get_current_version() < 2)
|
||||
{
|
||||
if(base_reward + fee != money_in_use)
|
||||
{
|
||||
MDEBUG("coinbase transaction doesn't use full amount of block reward: spent: " << money_in_use << ", block reward " << base_reward + fee << "(" << base_reward << "+" << fee << ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// from hard fork 2, since a miner can claim less than the full block reward, we update the base_reward
|
||||
// to show the amount of coins that were actually generated, the remainder will be pushed back for later
|
||||
// emission. This modifies the emission curve very slightly.
|
||||
CHECK_AND_ASSERT_MES(money_in_use - fee <= base_reward, false, "base reward calculation bug");
|
||||
if(base_reward + fee != money_in_use)
|
||||
partial_block_reward = true;
|
||||
base_reward = money_in_use - fee;
|
||||
}
|
||||
|
||||
// since a miner can claim less than the full block reward, we update the base_reward
|
||||
// to show the amount of coins that were actually generated, the remainder will be pushed back for later
|
||||
// emission. This modifies the emission curve very slightly.
|
||||
CHECK_AND_ASSERT_MES(money_in_use - fee <= base_reward, false, "base reward calculation bug");
|
||||
if(base_reward != money_in_use)
|
||||
partial_block_reward = true;
|
||||
base_reward = money_in_use - fee;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
@ -1365,10 +1349,18 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
|
||||
uint8_t hf_version = m_hardfork->get_current_version();
|
||||
|
||||
crypto::public_key winner = m_service_node_list.select_winner(b.prev_id);
|
||||
std::vector<std::pair<account_public_address, uint64_t>> service_node_addresses = m_service_node_list.get_winner_addresses_and_portions(b.prev_id);
|
||||
loki_miner_tx_context miner_tx_context(m_nettype,
|
||||
m_service_node_list.select_winner(b.prev_id),
|
||||
m_service_node_list.get_winner_addresses_and_portions(b.prev_id));
|
||||
|
||||
if (!calc_batched_governance_reward(height, miner_tx_context.batched_governance))
|
||||
{
|
||||
LOG_ERROR("Failed to calculate batched governance reward");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, hf_version, miner_tx_context);
|
||||
|
||||
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, hf_version, m_nettype, winner, service_node_addresses);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
|
||||
size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
|
||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
|
@ -1377,7 +1369,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
#endif
|
||||
for (size_t try_count = 0; try_count != 10; ++try_count)
|
||||
{
|
||||
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, ex_nonce, hf_version, m_nettype, winner, service_node_addresses);
|
||||
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, ex_nonce, hf_version, miner_tx_context);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
|
||||
size_t coinbase_weight = get_transaction_weight(b.miner_tx);
|
||||
|
@ -3080,7 +3072,8 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
|
|||
{
|
||||
median = m_current_block_cumul_weight_limit / 2;
|
||||
already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
|
||||
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version, m_db->height()))
|
||||
|
||||
if (!get_base_block_reward(median, 1, already_generated_coins, base_reward, version, m_db->height()))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3143,8 +3136,9 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
|
|||
median = min_block_weight;
|
||||
|
||||
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
|
||||
|
||||
uint64_t base_reward;
|
||||
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version, m_db->height()))
|
||||
if (!get_base_block_reward(median, 1, already_generated_coins, base_reward, version, m_db->height()))
|
||||
{
|
||||
MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound");
|
||||
base_reward = BLOCK_REWARD_OVERESTIMATE;
|
||||
|
@ -4023,6 +4017,52 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
|
|||
return usable;
|
||||
}
|
||||
|
||||
bool Blockchain::calc_batched_governance_reward(uint64_t height, uint64_t &reward) const
|
||||
{
|
||||
reward = 0;
|
||||
int hard_fork_version = get_ideal_hard_fork_version(height);
|
||||
if (hard_fork_version <= network_version_9_service_nodes)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!height_has_governance_output(nettype(), hard_fork_version, height))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ignore governance reward and payout instead the last
|
||||
// GOVERNANCE_BLOCK_REWARD_INTERVAL number of blocks governance rewards. We
|
||||
// come back for this height's rewards in the next interval. The reward is
|
||||
// 0 if it's not time to pay out the batched payments
|
||||
|
||||
const cryptonote::config_t &network = cryptonote::get_config(nettype(), hard_fork_version);
|
||||
uint64_t num_blocks = network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS;
|
||||
uint64_t start_height = height - num_blocks;
|
||||
|
||||
if (height < num_blocks)
|
||||
{
|
||||
start_height = 0;
|
||||
num_blocks = height;
|
||||
}
|
||||
|
||||
std::vector<std::pair<cryptonote::blobdata, cryptonote::block>> blocks;
|
||||
if (!get_blocks(start_height, num_blocks, blocks))
|
||||
{
|
||||
LOG_ERROR("Unable to get historical blocks to calculated batched governance payment");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &it : blocks)
|
||||
{
|
||||
cryptonote::block const &block = it.second;
|
||||
if (block.major_version >= network_version_10_bulletproofs)
|
||||
reward += derive_governance_from_block_reward(nettype(), block);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// ND: Speedups:
|
||||
// 1. Thread long_hash computations if possible (m_max_prepare_blocks_threads = nthreads, default = 4)
|
||||
|
|
|
@ -81,14 +81,6 @@ namespace cryptonote
|
|||
class Blockchain
|
||||
{
|
||||
public:
|
||||
enum version
|
||||
{
|
||||
version_7 = 7,
|
||||
version_8,
|
||||
version_9,
|
||||
version_10_swarms,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Now-defunct (TODO: remove) struct from in-memory blockchain
|
||||
*/
|
||||
|
@ -133,7 +125,7 @@ namespace cryptonote
|
|||
class ValidateMinerTxHook
|
||||
{
|
||||
public:
|
||||
virtual bool validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, uint64_t base_reward) const = 0;
|
||||
virtual bool validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, block_reward_parts const &reward_parts) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -980,6 +972,8 @@ namespace cryptonote
|
|||
bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); }
|
||||
uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes);
|
||||
|
||||
bool calc_batched_governance_reward(uint64_t height, uint64_t &reward) const;
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
|
|
|
@ -43,13 +43,14 @@ using namespace epee;
|
|||
#include "ringct/rctSigs.h"
|
||||
#include "multisig/multisig.h"
|
||||
#include "common/int-util.h"
|
||||
#include "cryptonote_core/service_node_list.h"
|
||||
|
||||
using namespace crypto;
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress)
|
||||
static void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress)
|
||||
{
|
||||
num_stdaddresses = 0;
|
||||
num_subaddresses = 0;
|
||||
|
@ -101,32 +102,6 @@ namespace cryptonote
|
|||
return k;
|
||||
}
|
||||
|
||||
uint64_t get_governance_reward(uint64_t height, uint64_t base_reward)
|
||||
{
|
||||
return base_reward / 20;
|
||||
}
|
||||
|
||||
uint64_t get_service_node_reward(uint64_t height, uint64_t base_reward, int hard_fork_version)
|
||||
{
|
||||
return hard_fork_version >= 9 ? base_reward / 2 : 0;
|
||||
}
|
||||
|
||||
uint64_t get_portion_of_reward(uint64_t portions, uint64_t total_service_node_reward)
|
||||
{
|
||||
uint64_t hi, lo, rewardhi, rewardlo;
|
||||
lo = mul128(total_service_node_reward, portions, &hi);
|
||||
div128_64(hi, lo, STAKING_PORTIONS, &rewardhi, &rewardlo);
|
||||
return rewardlo;
|
||||
}
|
||||
|
||||
static uint64_t calculate_sum_of_portions(const std::vector<std::pair<cryptonote::account_public_address, uint64_t>>& portions, uint64_t total_service_node_reward)
|
||||
{
|
||||
uint64_t reward = 0;
|
||||
for (size_t i = 0; i < portions.size(); i++)
|
||||
reward += get_portion_of_reward(portions[i].second, total_service_node_reward);
|
||||
return reward;
|
||||
}
|
||||
|
||||
bool get_deterministic_output_key(const account_public_address& address, const keypair& tx_key, size_t output_index, crypto::public_key& output_key)
|
||||
{
|
||||
|
||||
|
@ -145,21 +120,7 @@ namespace cryptonote
|
|||
keypair gov_key = get_deterministic_keypair_from_height(height);
|
||||
|
||||
cryptonote::address_parse_info governance_wallet_address;
|
||||
switch (nettype)
|
||||
{
|
||||
case STAGENET:
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::STAGENET, governance_wallet_address_str);
|
||||
break;
|
||||
case TESTNET:
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::TESTNET, governance_wallet_address_str);
|
||||
break;
|
||||
case FAKECHAIN: case MAINNET:
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::MAINNET, governance_wallet_address_str);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, nettype, governance_wallet_address_str);
|
||||
crypto::public_key correct_key;
|
||||
|
||||
if (!get_deterministic_output_key(governance_wallet_address.address, gov_key, output_index, correct_key))
|
||||
|
@ -171,7 +132,105 @@ namespace cryptonote
|
|||
return correct_key == output_key;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
const int GOVERNANCE_BASE_REWARD_DIVISOR = 20;
|
||||
const int SERVICE_NODE_BASE_REWARD_DIVISOR = 2;
|
||||
uint64_t governance_reward_formula(uint64_t base_reward)
|
||||
{
|
||||
return base_reward / GOVERNANCE_BASE_REWARD_DIVISOR;
|
||||
}
|
||||
|
||||
bool block_has_governance_output(network_type nettype, cryptonote::block const &block)
|
||||
{
|
||||
bool result = height_has_governance_output(nettype, block.major_version, get_block_height(block));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool height_has_governance_output(network_type nettype, int hard_fork_version, uint64_t height)
|
||||
{
|
||||
if (height == 0)
|
||||
return false;
|
||||
|
||||
if (hard_fork_version <= network_version_9_service_nodes)
|
||||
return true;
|
||||
|
||||
const cryptonote::config_t &network = cryptonote::get_config(nettype, hard_fork_version);
|
||||
if (height % network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t derive_governance_from_block_reward(network_type nettype, const cryptonote::block &block)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
uint64_t height = get_block_height(block);
|
||||
uint64_t snode_reward = 0;
|
||||
uint64_t vout_end = block.miner_tx.vout.size();
|
||||
|
||||
if (block_has_governance_output(nettype, block))
|
||||
--vout_end; // skip the governance output, the governance may be the batched amount. we want the original base reward
|
||||
|
||||
for (size_t vout_index = 1; vout_index < vout_end; ++vout_index)
|
||||
{
|
||||
tx_out const &output = block.miner_tx.vout[vout_index];
|
||||
snode_reward += output.amount;
|
||||
}
|
||||
|
||||
static_assert(SERVICE_NODE_BASE_REWARD_DIVISOR == 2 &&
|
||||
GOVERNANCE_BASE_REWARD_DIVISOR == 20,
|
||||
"Anytime this changes, you should revisit this code and "
|
||||
"check, because we rely on the service node reward being 50\% "
|
||||
"of the base reward, and does not receive any fees. This isn't "
|
||||
"exactly intuitive and so changes to the reward structure may "
|
||||
"make this assumption invalid.");
|
||||
|
||||
uint64_t base_reward = snode_reward * SERVICE_NODE_BASE_REWARD_DIVISOR;
|
||||
uint64_t governance = governance_reward_formula(base_reward);
|
||||
uint64_t block_reward = base_reward - governance;
|
||||
|
||||
uint64_t actual_reward = 0; // sanity check
|
||||
for (tx_out const &output : block.miner_tx.vout) actual_reward += output.amount;
|
||||
|
||||
CHECK_AND_ASSERT_MES(block_reward <= actual_reward, false,
|
||||
"Rederiving the base block reward from the service node reward "
|
||||
"exceeded the actual amount paid in the block, derived block reward: "
|
||||
<< block_reward << ", actual reward: " << actual_reward);
|
||||
|
||||
result = governance;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t service_node_reward_formula(uint64_t base_reward, int hard_fork_version)
|
||||
{
|
||||
return hard_fork_version >= 9 ? (base_reward / SERVICE_NODE_BASE_REWARD_DIVISOR) : 0;
|
||||
}
|
||||
|
||||
uint64_t get_portion_of_reward(uint64_t portions, uint64_t total_service_node_reward)
|
||||
{
|
||||
uint64_t hi, lo, rewardhi, rewardlo;
|
||||
lo = mul128(total_service_node_reward, portions, &hi);
|
||||
div128_64(hi, lo, STAKING_PORTIONS, &rewardhi, &rewardlo);
|
||||
return rewardlo;
|
||||
}
|
||||
|
||||
static uint64_t calculate_sum_of_portions(const std::vector<std::pair<cryptonote::account_public_address, uint64_t>>& portions, uint64_t total_service_node_reward)
|
||||
{
|
||||
uint64_t reward = 0;
|
||||
for (size_t i = 0; i < portions.size(); i++)
|
||||
reward += get_portion_of_reward(portions[i].second, total_service_node_reward);
|
||||
return reward;
|
||||
}
|
||||
|
||||
loki_miner_tx_context::loki_miner_tx_context(network_type type, crypto::public_key winner, std::vector<std::pair<account_public_address, stake_portions>> winner_info)
|
||||
: nettype(type)
|
||||
, snode_winner_key(winner)
|
||||
, snode_winner_info(winner_info)
|
||||
, batched_governance(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool construct_miner_tx(
|
||||
size_t height,
|
||||
size_t median_weight,
|
||||
|
@ -182,15 +241,20 @@ namespace cryptonote
|
|||
transaction& tx,
|
||||
const blobdata& extra_nonce,
|
||||
uint8_t hard_fork_version,
|
||||
network_type nettype,
|
||||
const crypto::public_key& service_node_key,
|
||||
const std::vector<std::pair<account_public_address, uint64_t>>& service_node_info)
|
||||
const loki_miner_tx_context &miner_tx_context)
|
||||
{
|
||||
tx.vin.clear();
|
||||
tx.vout.clear();
|
||||
tx.extra.clear();
|
||||
tx.output_unlock_times.clear();
|
||||
tx.is_deregister = false;
|
||||
tx.version = (hard_fork_version >= network_version_9_service_nodes) ? transaction::version_3_per_output_unlock_times : transaction::version_2;
|
||||
|
||||
const network_type nettype = miner_tx_context.nettype;
|
||||
const crypto::public_key &service_node_key = miner_tx_context.snode_winner_key;
|
||||
const std::vector<std::pair<account_public_address, uint64_t>> &service_node_info =
|
||||
miner_tx_context.snode_winner_info.empty() ?
|
||||
service_nodes::null_winner : miner_tx_context.snode_winner_info;
|
||||
|
||||
keypair txkey = keypair::generate(hw::get_device("default"));
|
||||
add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
|
@ -198,7 +262,7 @@ namespace cryptonote
|
|||
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
|
||||
return false;
|
||||
|
||||
keypair gov_key = get_deterministic_keypair_from_height(height);
|
||||
keypair gov_key = get_deterministic_keypair_from_height(height); // NOTE: Always need since we use same key for service node
|
||||
if (already_generated_coins != 0)
|
||||
{
|
||||
add_tx_pub_key_to_extra(tx, gov_key.pub);
|
||||
|
@ -209,10 +273,16 @@ namespace cryptonote
|
|||
txin_gen in;
|
||||
in.height = height;
|
||||
|
||||
uint64_t block_reward;
|
||||
if(!get_block_reward(median_weight, current_block_weight, already_generated_coins, block_reward, hard_fork_version, height))
|
||||
loki_block_reward_context block_reward_context = {};
|
||||
block_reward_context.fee = fee;
|
||||
block_reward_context.height = height;
|
||||
block_reward_context.snode_winner_info = miner_tx_context.snode_winner_info;
|
||||
block_reward_context.batched_governance = miner_tx_context.batched_governance;
|
||||
|
||||
block_reward_parts reward_parts;
|
||||
if(!get_loki_block_reward(median_weight, current_block_weight, already_generated_coins, hard_fork_version, reward_parts, block_reward_context))
|
||||
{
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
LOG_PRINT_L0("Failed to calculate block reward");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -221,23 +291,8 @@ namespace cryptonote
|
|||
", fee " << fee);
|
||||
#endif
|
||||
|
||||
//TODO: declining governance reward schedule
|
||||
uint64_t governance_reward = 0;
|
||||
uint64_t total_service_node_reward = 0;
|
||||
uint64_t total_paid_service_node_reward = 0;
|
||||
if (already_generated_coins != 0)
|
||||
{
|
||||
governance_reward = get_governance_reward(height, block_reward);
|
||||
total_service_node_reward = get_service_node_reward(height, block_reward, hard_fork_version);
|
||||
total_paid_service_node_reward = calculate_sum_of_portions(service_node_info, total_service_node_reward);
|
||||
block_reward -= governance_reward;
|
||||
block_reward -= total_paid_service_node_reward;
|
||||
}
|
||||
|
||||
block_reward += fee;
|
||||
|
||||
uint64_t summary_amounts = 0;
|
||||
|
||||
// Miner Reward
|
||||
{
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
|
@ -251,15 +306,14 @@ namespace cryptonote
|
|||
tk.key = out_eph_public_key;
|
||||
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = block_reward;
|
||||
summary_amounts += out.amount = reward_parts.miner_reward();
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
tx.output_unlock_times.push_back(height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
||||
}
|
||||
|
||||
if (hard_fork_version >= 9)
|
||||
if (hard_fork_version >= network_version_9_service_nodes) // Service Node Reward
|
||||
{
|
||||
tx.version = transaction_prefix::version_3_per_output_unlock_times;
|
||||
for (size_t i = 0; i < service_node_info.size(); i++)
|
||||
{
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
|
@ -273,70 +327,112 @@ namespace cryptonote
|
|||
tk.key = out_eph_public_key;
|
||||
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = get_portion_of_reward(service_node_info[i].second, total_service_node_reward);
|
||||
summary_amounts += out.amount = get_portion_of_reward(service_node_info[i].second, reward_parts.service_node_total);
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
tx.output_unlock_times.push_back(height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tx.version = transaction_prefix::version_2;
|
||||
}
|
||||
|
||||
// Governance Distribution
|
||||
if (already_generated_coins != 0)
|
||||
{
|
||||
std::string governance_wallet_address_str;
|
||||
|
||||
cryptonote::address_parse_info governance_wallet_address;
|
||||
|
||||
switch (nettype)
|
||||
if (reward_parts.governance == 0)
|
||||
{
|
||||
case STAGENET:
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::STAGENET, ::config::stagenet::GOVERNANCE_WALLET_ADDRESS);
|
||||
break;
|
||||
case TESTNET:
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::TESTNET, ::config::testnet::GOVERNANCE_WALLET_ADDRESS);
|
||||
break;
|
||||
case FAKECHAIN: case MAINNET:
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::MAINNET, ::config::GOVERNANCE_WALLET_ADDRESS);
|
||||
break;
|
||||
default:
|
||||
CHECK_AND_ASSERT_MES(hard_fork_version >= network_version_10_bulletproofs, false, "Governance reward can NOT be 0 before hardfork 10, hard_fork_version: " << hard_fork_version);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string governance_wallet_address_str;
|
||||
|
||||
cryptonote::address_parse_info governance_wallet_address;
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, nettype, *cryptonote::get_config(nettype, hard_fork_version).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;
|
||||
}
|
||||
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = reward_parts.governance;
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
tx.output_unlock_times.push_back(height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = governance_reward;
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
tx.output_unlock_times.push_back(height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(summary_amounts == (block_reward + governance_reward + total_paid_service_node_reward), false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal total block_reward = " << (block_reward + governance_reward + total_paid_service_node_reward));
|
||||
uint64_t expected_amount = reward_parts.miner_reward() + reward_parts.governance + reward_parts.service_node_paid;
|
||||
CHECK_AND_ASSERT_MES(summary_amounts == expected_amount, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal total block_reward = " << expected_amount);
|
||||
|
||||
//lock
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
tx.vin.push_back(in);
|
||||
|
||||
tx.invalidate_hashes();
|
||||
|
||||
//LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee)
|
||||
// << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2);
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
|
||||
bool get_loki_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, int hard_fork_version, block_reward_parts &result, const loki_block_reward_context &loki_context)
|
||||
{
|
||||
result = {};
|
||||
uint64_t base_reward;
|
||||
if (!get_base_block_reward(median_weight, current_block_weight, already_generated_coins, base_reward, hard_fork_version, loki_context.height))
|
||||
{
|
||||
MERROR("Failed to calculate base block reward");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (base_reward == 0)
|
||||
{
|
||||
MERROR("Unexpected base reward of 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (already_generated_coins == 0)
|
||||
{
|
||||
result.original_base_reward = result.adjusted_base_reward = result.base_miner = base_reward;
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO: declining governance reward schedule
|
||||
result.original_base_reward = base_reward;
|
||||
result.service_node_total = service_node_reward_formula(base_reward, hard_fork_version);
|
||||
if (loki_context.snode_winner_info.empty()) result.service_node_paid = calculate_sum_of_portions(service_nodes::null_winner, result.service_node_total);
|
||||
else result.service_node_paid = calculate_sum_of_portions(loki_context.snode_winner_info, result.service_node_total);
|
||||
|
||||
// TODO(loki): Having the snode total and paid was probably just a sanity check? If so we can remove the field ..
|
||||
assert(result.service_node_total == result.service_node_paid);
|
||||
|
||||
result.adjusted_base_reward = result.original_base_reward;
|
||||
if (hard_fork_version >= network_version_10_bulletproofs)
|
||||
{
|
||||
// NOTE: After hardfork 10, remove the governance component in the base
|
||||
// reward as they are not included and batched into a later block. If we
|
||||
// calculated a (governance reward > 0), then this is the batched height,
|
||||
// add it to the adjusted base reward afterwards
|
||||
result.governance = loki_context.batched_governance;
|
||||
result.adjusted_base_reward -= governance_reward_formula(result.original_base_reward);
|
||||
|
||||
if (result.governance > 0)
|
||||
result.adjusted_base_reward += result.governance;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.governance = governance_reward_formula(result.original_base_reward);
|
||||
}
|
||||
|
||||
result.base_miner = result.adjusted_base_reward - (result.governance + result.service_node_paid);
|
||||
result.base_miner_fee = loki_context.fee;
|
||||
return true;
|
||||
}
|
||||
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::tx_destination_entry>& change_addr)
|
||||
{
|
||||
account_public_address addr = {null_pkey, null_pkey};
|
||||
|
|
|
@ -37,6 +37,32 @@
|
|||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
keypair get_deterministic_keypair_from_height(uint64_t height);
|
||||
bool get_deterministic_output_key (const account_public_address& address, const keypair& tx_key, size_t output_index, crypto::public_key& output_key);
|
||||
bool validate_governance_reward_key (uint64_t height, const std::string& governance_wallet_address_str, size_t output_index, const crypto::public_key& output_key, const cryptonote::network_type nettype);
|
||||
|
||||
uint64_t governance_reward_formula (uint64_t base_reward);
|
||||
bool block_has_governance_output (network_type nettype, cryptonote::block const &block);
|
||||
bool height_has_governance_output (network_type nettype, int hard_fork_version, uint64_t height);
|
||||
uint64_t derive_governance_from_block_reward (network_type nettype, const cryptonote::block &block);
|
||||
|
||||
uint64_t get_portion_of_reward (uint64_t portions, uint64_t total_service_node_reward);
|
||||
uint64_t service_node_reward_formula (uint64_t base_reward, int hard_fork_version);
|
||||
|
||||
struct loki_miner_tx_context // NOTE(loki): All the custom fields required by Loki to use construct_miner_tx
|
||||
{
|
||||
using stake_portions = uint64_t;
|
||||
|
||||
loki_miner_tx_context(network_type type = MAINNET,
|
||||
crypto::public_key winner = crypto::null_pkey,
|
||||
std::vector<std::pair<account_public_address, stake_portions>> winner_info = {});
|
||||
|
||||
network_type nettype;
|
||||
crypto::public_key snode_winner_key;
|
||||
std::vector<std::pair<account_public_address, stake_portions>> snode_winner_info; // NOTE: If empty we use service_nodes::null_winner
|
||||
uint64_t batched_governance; // NOTE: 0 until hardfork v10, then use blockchain::calc_batched_governance_reward
|
||||
};
|
||||
|
||||
bool construct_miner_tx(
|
||||
size_t height,
|
||||
size_t median_weight,
|
||||
|
@ -47,21 +73,51 @@ namespace cryptonote
|
|||
transaction& tx,
|
||||
const blobdata& extra_nonce = blobdata(),
|
||||
uint8_t hard_fork_version = 1,
|
||||
network_type nettype = MAINNET,
|
||||
const crypto::public_key& service_node_key = crypto::null_pkey,
|
||||
const std::vector<std::pair<account_public_address, uint64_t>>& service_node_info={ std::pair<account_public_address, uint64_t>({ crypto::null_pkey, crypto::null_pkey }, STAKING_PORTIONS) }
|
||||
);
|
||||
const loki_miner_tx_context &miner_context = {});
|
||||
|
||||
keypair get_deterministic_keypair_from_height(uint64_t height);
|
||||
struct block_reward_parts
|
||||
{
|
||||
// TODO(loki): There can be a difference between the total reward and the
|
||||
// reward paid out to the service node? This would mean that a user can
|
||||
// specify less portions but still contribute the full amount?
|
||||
// Or was this just a sanity check? I don't think the first case is possible
|
||||
uint64_t service_node_total;
|
||||
uint64_t service_node_paid;
|
||||
|
||||
uint64_t get_portion_of_reward(uint64_t portions, uint64_t total_service_node_reward);
|
||||
uint64_t governance;
|
||||
uint64_t base_miner;
|
||||
uint64_t base_miner_fee;
|
||||
|
||||
uint64_t get_governance_reward(uint64_t height, uint64_t base_reward);
|
||||
uint64_t get_service_node_reward(uint64_t height, uint64_t base_reward, int hard_fork_version);
|
||||
// NOTE: Post hardfork 10, adjusted base reward is the block reward with the
|
||||
// governance amount removed. We still need the original base reward, so
|
||||
// that we can calculate the 50% on the whole base amount, that should be
|
||||
// allocated for the service node and fees.
|
||||
|
||||
bool get_deterministic_output_key(const account_public_address& address, const keypair& tx_key, size_t output_index, crypto::public_key& output_key);
|
||||
// If this block contains the batched governance payment, this is
|
||||
// included in the adjusted base reward.
|
||||
|
||||
bool validate_governance_reward_key(uint64_t height, const std::string& governance_wallet_address_str, size_t output_index, const crypto::public_key& output_key, const cryptonote::network_type nettype);
|
||||
// Before hardfork 10, this is the same value as original_base_reward
|
||||
uint64_t adjusted_base_reward;
|
||||
uint64_t original_base_reward;
|
||||
|
||||
uint64_t miner_reward() { return base_miner + base_miner_fee; }
|
||||
};
|
||||
|
||||
struct loki_block_reward_context
|
||||
{
|
||||
using portions = uint64_t;
|
||||
uint64_t height;
|
||||
uint64_t fee;
|
||||
uint64_t batched_governance; // Optional: 0 hardfork v10, then must be calculated using blockchain::calc_batched_governance_reward
|
||||
std::vector<std::pair<account_public_address, portions>> snode_winner_info; // Optional: Check contributor portions add up, else set empty to use service_nodes::null_winner
|
||||
};
|
||||
|
||||
// NOTE(loki): I would combine this into get_base_block_reward, but
|
||||
// cryptonote_basic as a library is to be able to trivially link with
|
||||
// cryptonote_core since it would have a circular dependency on Blockchain
|
||||
|
||||
// NOTE: Block reward function that should be called after hard fork v10
|
||||
bool get_loki_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, int hard_fork_version, block_reward_parts &result, const loki_block_reward_context &loki_context);
|
||||
|
||||
struct tx_source_entry
|
||||
{
|
||||
|
|
|
@ -429,7 +429,7 @@ namespace service_nodes
|
|||
if (iter != m_service_nodes_infos.end())
|
||||
{
|
||||
int hard_fork_version = m_blockchain.get_hard_fork_version(block_height);
|
||||
if (hard_fork_version >= cryptonote::Blockchain::version_10_swarms)
|
||||
if (hard_fork_version >= cryptonote::network_version_10_bulletproofs)
|
||||
{
|
||||
service_node_info const &old_info = iter->second;
|
||||
uint64_t expiry_height = old_info.registration_height + get_staking_requirement_lock_blocks(m_blockchain.nettype());
|
||||
|
@ -672,13 +672,13 @@ namespace service_nodes
|
|||
int hard_fork_version = m_blockchain.get_hard_fork_version(block_height);
|
||||
|
||||
uint64_t lock_blocks = get_staking_requirement_lock_blocks(m_blockchain.nettype());
|
||||
if (hard_fork_version >= cryptonote::Blockchain::version_10_swarms)
|
||||
if (hard_fork_version >= cryptonote::network_version_10_bulletproofs)
|
||||
lock_blocks += STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS;
|
||||
|
||||
if (block_height < lock_blocks)
|
||||
return expired_nodes;
|
||||
|
||||
if (hard_fork_version >= cryptonote::Blockchain::version_10_swarms)
|
||||
if (hard_fork_version >= cryptonote::network_version_10_bulletproofs)
|
||||
{
|
||||
for (auto &it : m_service_nodes_infos)
|
||||
{
|
||||
|
@ -775,13 +775,18 @@ namespace service_nodes
|
|||
|
||||
/// validates the miner TX for the next block
|
||||
//
|
||||
bool service_node_list::validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, uint64_t base_reward) const
|
||||
bool service_node_list::validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, cryptonote::block_reward_parts const &reward_parts) const
|
||||
{
|
||||
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
|
||||
if (hard_fork_version < 9)
|
||||
return true;
|
||||
|
||||
uint64_t total_service_node_reward = cryptonote::get_service_node_reward(height, base_reward, hard_fork_version);
|
||||
// NOTE(loki): Service node reward distribution is calculated from the
|
||||
// original amount, i.e. 50% of the original base reward goes to service
|
||||
// nodes not 50% of the reward after removing the governance component (the
|
||||
// adjusted base reward post hardfork 10).
|
||||
uint64_t base_reward = reward_parts.original_base_reward;
|
||||
uint64_t total_service_node_reward = cryptonote::service_node_reward_formula(base_reward, hard_fork_version);
|
||||
|
||||
crypto::public_key winner = select_winner(prev_id);
|
||||
|
||||
|
@ -796,8 +801,7 @@ namespace service_nodes
|
|||
|
||||
for (size_t i = 0; i < addresses_and_portions.size(); i++)
|
||||
{
|
||||
size_t vout_index = miner_tx.vout.size() - 1 /* governance */ - addresses_and_portions.size() + i;
|
||||
|
||||
size_t vout_index = i + 1;
|
||||
uint64_t reward = cryptonote::get_portion_of_reward(addresses_and_portions[i].second, total_service_node_reward);
|
||||
|
||||
if (miner_tx.vout[vout_index].amount != reward)
|
||||
|
|
|
@ -125,7 +125,7 @@ namespace service_nodes
|
|||
void blockchain_detached(uint64_t height) override;
|
||||
void register_hooks(service_nodes::quorum_cop &quorum_cop);
|
||||
void init() override;
|
||||
bool validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, uint64_t base_reward) const override;
|
||||
bool validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, cryptonote::block_reward_parts const &base_reward) const override;
|
||||
std::vector<std::pair<cryptonote::account_public_address, uint64_t>> get_winner_addresses_and_portions(const crypto::hash& prev_id) const;
|
||||
crypto::public_key select_winner(const crypto::hash& prev_id) const;
|
||||
|
||||
|
@ -300,6 +300,8 @@ namespace service_nodes
|
|||
inline uint64_t get_min_node_contribution(uint64_t staking_requirement, uint64_t total_reserved) { return std::min(staking_requirement - total_reserved, staking_requirement / MAX_NUMBER_OF_CONTRIBUTORS); }
|
||||
|
||||
const static cryptonote::account_public_address null_address{ crypto::null_pkey, crypto::null_pkey };
|
||||
const static std::vector<std::pair<cryptonote::account_public_address, uint64_t>> null_winner =
|
||||
{std::pair<cryptonote::account_public_address, uint64_t>({null_address, STAKING_PORTIONS})};
|
||||
}
|
||||
|
||||
VARIANT_TAG(binary_archive, service_nodes::service_node_list::data_members_for_serialization, 0xa0);
|
||||
|
|
|
@ -1266,8 +1266,17 @@ namespace cryptonote
|
|||
fee = 0;
|
||||
|
||||
//baseline empty block
|
||||
get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version, height);
|
||||
loki_block_reward_context block_reward_context = {};
|
||||
block_reward_context.height = height;
|
||||
if (!m_blockchain.calc_batched_governance_reward(height, block_reward_context.batched_governance))
|
||||
{
|
||||
MERROR("Failed to calculated batched governance reward");
|
||||
return false;
|
||||
}
|
||||
|
||||
block_reward_parts reward_parts = {};
|
||||
get_loki_block_reward(median_weight, total_weight, already_generated_coins, version, reward_parts, block_reward_context);
|
||||
best_coinbase = reward_parts.base_miner;
|
||||
|
||||
size_t max_total_weight = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
std::unordered_set<crypto::key_image> k_images;
|
||||
|
@ -1297,14 +1306,15 @@ namespace cryptonote
|
|||
// start using the optimal filling algorithm from v5
|
||||
if (version >= 5)
|
||||
{
|
||||
// If we're getting lower coinbase tx,
|
||||
// stop including more tx
|
||||
uint64_t block_reward;
|
||||
if(!get_block_reward(median_weight, total_weight + meta.weight, already_generated_coins, block_reward, version, height))
|
||||
// If we're getting lower coinbase tx, stop including more tx
|
||||
block_reward_parts reward_parts_other = {};
|
||||
if(!get_loki_block_reward(median_weight, total_weight + meta.weight, already_generated_coins, version, reward_parts_other, block_reward_context))
|
||||
{
|
||||
LOG_PRINT_L2(" would exceed maximum block weight");
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t block_reward = reward_parts_other.base_miner;
|
||||
coinbase = block_reward + fee + meta.fee;
|
||||
if (coinbase < template_accept_threshold(best_coinbase))
|
||||
{
|
||||
|
|
|
@ -2074,7 +2074,7 @@ static void print_service_node_list_state(cryptonote::network_type nettype, int
|
|||
// Print Expiry Info
|
||||
{
|
||||
uint64_t expiry_height = entry.registration_height + service_nodes::get_staking_requirement_lock_blocks(nettype);
|
||||
if (hard_fork_version >= cryptonote::Blockchain::version_10_swarms)
|
||||
if (hard_fork_version >= cryptonote::network_version_10_bulletproofs)
|
||||
expiry_height += STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS;
|
||||
|
||||
if (curr_height)
|
||||
|
|
|
@ -5199,7 +5199,7 @@ bool simple_wallet::register_service_node_main(
|
|||
if (response.service_node_states.size() >= 1)
|
||||
{
|
||||
bool can_reregister = false;
|
||||
if (m_wallet->use_fork_rules(cryptonote::Blockchain::version_10_swarms, 0))
|
||||
if (m_wallet->use_fork_rules(cryptonote::network_version_10_bulletproofs, 0))
|
||||
{
|
||||
cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry const &node_info = response.service_node_states[0];
|
||||
uint64_t expiry_height = node_info.registration_height + staking_requirement_lock_blocks;
|
||||
|
|
|
@ -272,3 +272,69 @@ bool gen_block_reward::check_block_rewards(cryptonote::core& /*c*/, size_t /*ev_
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
gen_batched_governance_reward::gen_batched_governance_reward()
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(gen_batched_governance_reward, check_batched_governance_amount_matches);
|
||||
}
|
||||
|
||||
static uint64_t expected_total_governance_paid = 0;
|
||||
bool gen_batched_governance_reward::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const get_test_options<gen_batched_governance_reward> test_options = {};
|
||||
const config_t &network = cryptonote::get_config(cryptonote::FAKECHAIN, network_version_10_bulletproofs);
|
||||
|
||||
linear_chain_generator batched_governance_generator(events);
|
||||
{
|
||||
batched_governance_generator.rewind_until_version(test_options.hard_forks, network_version_10_bulletproofs);
|
||||
|
||||
uint64_t blocks_to_gen = network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS - batched_governance_generator.height();
|
||||
batched_governance_generator.rewind_blocks_n(blocks_to_gen);
|
||||
}
|
||||
|
||||
{
|
||||
// NOTE(loki): Since hard fork 8 we have an emissions curve change, so if
|
||||
// you don't atleast progress and generate blocks from hf8 you will run into
|
||||
// problems
|
||||
std::vector<test_event_entry> unused_events;
|
||||
linear_chain_generator no_batched_governance_generator(unused_events);
|
||||
no_batched_governance_generator.rewind_until_version(test_options.hard_forks, network_version_9_service_nodes);
|
||||
|
||||
while(no_batched_governance_generator.height() < batched_governance_generator.height())
|
||||
no_batched_governance_generator.create_block();
|
||||
|
||||
// NOTE(loki): Skip the last block as that is the batched payout height, we
|
||||
// don't include the governance reward of that height, that gets picked up
|
||||
// in the next batch.
|
||||
const std::vector<cryptonote::block>& blockchain = no_batched_governance_generator.blocks();
|
||||
for (size_t block_height = 1; block_height < blockchain.size() - 1; ++block_height)
|
||||
{
|
||||
const cryptonote::block &block = blockchain[block_height];
|
||||
expected_total_governance_paid += block.miner_tx.vout.back().amount;
|
||||
}
|
||||
}
|
||||
|
||||
DO_CALLBACK(events, "check_batched_governance_amount_matches");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_batched_governance_reward::check_batched_governance_amount_matches(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_batched_governance_reward::check_batched_governance_amount_matches");
|
||||
|
||||
uint64_t height = c.get_current_blockchain_height();
|
||||
std::vector<cryptonote::block> blockchain;
|
||||
if (!c.get_blocks((uint64_t)0, (size_t)height, blockchain))
|
||||
return false;
|
||||
|
||||
uint64_t governance = 0;
|
||||
for (size_t block_height = 1; block_height < blockchain.size(); ++block_height)
|
||||
{
|
||||
const cryptonote::block &block = blockchain[block_height];
|
||||
if (cryptonote::block_has_governance_output(cryptonote::FAKECHAIN, block))
|
||||
governance += block.miner_tx.vout.back().amount;
|
||||
}
|
||||
|
||||
CHECK_EQ(governance, expected_total_governance_paid);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -47,3 +47,19 @@ private:
|
|||
size_t m_invalid_block_index;
|
||||
std::vector<size_t> m_checked_blocks_indices;
|
||||
};
|
||||
|
||||
struct gen_batched_governance_reward : public test_chain_unit_base
|
||||
{
|
||||
gen_batched_governance_reward();
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
bool check_batched_governance_amount_matches(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
};
|
||||
|
||||
template<> struct get_test_options<gen_batched_governance_reward>
|
||||
{
|
||||
const std::vector<std::pair<uint8_t, uint64_t>> hard_forks = { std::make_pair(cryptonote::network_version_7, 0),
|
||||
std::make_pair(cryptonote::network_version_8, 1),
|
||||
std::make_pair(cryptonote::network_version_9_service_nodes, 2),
|
||||
std::make_pair(cryptonote::network_version_10_bulletproofs, 3) };
|
||||
const cryptonote::test_options test_options = { hard_forks };
|
||||
};
|
||||
|
|
|
@ -409,6 +409,72 @@ bool gen_block_miner_tx_has_no_out::generate(std::vector<test_event_entry>& even
|
|||
return true;
|
||||
}
|
||||
|
||||
static 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
|
||||
const int hard_fork_version = 7; // NOTE(loki): We know this test doesn't need the new block reward formula
|
||||
uint64_t block_reward;
|
||||
if (!get_base_block_reward(0, 0, already_generated_coins, block_reward, 1, 0)) {
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t governance_reward = 0;
|
||||
if (already_generated_coins != 0) {
|
||||
governance_reward = governance_reward_formula(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) {
|
||||
const cryptonote::network_type nettype = cryptonote::FAKECHAIN;
|
||||
cryptonote::address_parse_info governance_wallet_address;
|
||||
cryptonote::get_account_address_from_str(governance_wallet_address, nettype, *cryptonote::get_config(nettype, hard_fork_version).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 gen_block_miner_tx_has_out_to_alice::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
|
|
@ -64,7 +64,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
|
|||
for (size_t i = 0; i < NUM_MINERS; ++i)
|
||||
miner_accounts[i].generate();
|
||||
|
||||
generator.set_hf_version(8);
|
||||
generator.m_hf_version = 8;
|
||||
for (size_t n = 0; n < NUM_UNLOCKED_BLOCKS; ++n) {
|
||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n % NUM_MINERS],
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
|
||||
|
@ -193,7 +193,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
|
|||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(rct_txes);
|
||||
|
||||
generator.set_hf_version(10);
|
||||
generator.m_hf_version = 10;
|
||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes, blk_last, miner_account,
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version,
|
||||
10, 10, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||
|
@ -228,9 +228,25 @@ bool gen_bp_tx_validation_base::check_bp(const cryptonote::transaction &tx, size
|
|||
return true;
|
||||
}
|
||||
|
||||
// TODO(doyle): Revisit this. Is there some rule prohibiting a tx fee greater
|
||||
// than the block reward? Monero is unaffected because they have multiple
|
||||
// outputs of varying sizes in their miner tx, so the tx fee (inputs-outputs)
|
||||
// (because they don't use a change addr) doesn't eclipse the reward and doesn't
|
||||
// trigger the "base reward calculation bug" assert, whereas we do since we only
|
||||
// have 1 output. So my fix is to make it so we don't generate a tx that makes
|
||||
// too high of a fee from the change amount.
|
||||
|
||||
// Further addendum. In Loki hardfork 10, we also introduce batching governance
|
||||
// payments- so most block heights will remove the governance output from the
|
||||
// reward. So if we send less than the governance amount (~6ish loki from the
|
||||
// start of the chain), then we'll eclipse the reward again and overflow, so
|
||||
// most of these tests have again been modified to ensure that we use atleast
|
||||
// 6 loki from the block reward.
|
||||
// - 2018/10/29
|
||||
|
||||
bool gen_bp_tx_valid_1::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const uint64_t amounts_paid[] = {10, (uint64_t)-1};
|
||||
const uint64_t amounts_paid[] = {MK_COINS(10), (uint64_t)-1};
|
||||
const size_t bp_sizes[] = {1, (size_t)-1};
|
||||
const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof};
|
||||
return generate_with(events, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); });
|
||||
|
@ -245,7 +261,7 @@ bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) cons
|
|||
|
||||
bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const uint64_t amounts_paid[] = {5, 5, (uint64_t)-1};
|
||||
const uint64_t amounts_paid[] = {MK_COINS(5), MK_COINS(5), (uint64_t)-1};
|
||||
const size_t bp_sizes[] = {2, (size_t)-1};
|
||||
const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof};
|
||||
return generate_with(events, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); });
|
||||
|
@ -253,7 +269,8 @@ bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
|
|||
|
||||
bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const uint64_t amounts_paid[] = {50, 50, 50, (uint64_t)-1};
|
||||
// const uint64_t amounts_paid[] = {50, 50, 50, (uint64_t)-1};
|
||||
const uint64_t amounts_paid[] = {MK_COINS(28), MK_COINS(28), MK_COINS(28), (uint64_t)-1};
|
||||
const size_t bp_sizes[] = {4, (size_t)-1};
|
||||
const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof };
|
||||
return generate_with(events, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); });
|
||||
|
@ -261,7 +278,8 @@ bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const
|
|||
|
||||
bool gen_bp_tx_valid_16::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const uint64_t amounts_paid[] = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, (uint64_t)-1};
|
||||
// const uint64_t amounts_paid[] = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, (uint64_t)-1};
|
||||
const uint64_t amounts_paid[] = {MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), MK_COINS(1), (uint64_t)-1};
|
||||
const size_t bp_sizes[] = {16, (size_t)-1};
|
||||
const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof };
|
||||
return generate_with(events, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); });
|
||||
|
@ -283,14 +301,6 @@ bool gen_bp_tx_invalid_16_16::generate(std::vector<test_event_entry>& events) co
|
|||
|
||||
bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
// TODO(doyle): Revisit this. Is there some rule prohibiting a tx fee greater
|
||||
// than the block reward? Monero is unaffected because they have multiple
|
||||
// outputs of varying sizes, so the tx fee (inputs-outputs) (because they
|
||||
// don't use a change addr) doesn't eclipse the reward and doesn't trigger the
|
||||
// "base reward calculation bug" assert, whereas we do since we only have
|
||||
// 1 output. So my fix is to make it so we don't generate a tx that makes too
|
||||
// high of a fee from the change amount - 2018/09/09
|
||||
|
||||
//const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1};
|
||||
const uint64_t amounts_paid[] = {MK_COINS(50), MK_COINS(50), (size_t)-1, MK_COINS(50), MK_COINS(50), (uint64_t)-1};
|
||||
|
||||
|
|
|
@ -88,11 +88,35 @@ void linear_chain_generator::create_block(const std::vector<cryptonote::transact
|
|||
blocks_.push_back(blk);
|
||||
}
|
||||
|
||||
void linear_chain_generator::rewind_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version)
|
||||
{
|
||||
if (hard_forks.size() > 1)
|
||||
{
|
||||
gen_.m_hf_version = hard_forks[0].first;
|
||||
if (blocks_.size() == 0) create_genesis_block();
|
||||
|
||||
for (size_t i = 0; i < hard_forks.size() - 1 && gen_.m_hf_version != hard_fork_version; ++i)
|
||||
{
|
||||
uint64_t curr_fork_height = hard_forks[i].second;
|
||||
uint64_t next_fork_height = hard_forks[i + 1].second;
|
||||
assert(next_fork_height > curr_fork_height);
|
||||
|
||||
uint64_t blocks_till_next_hardfork = next_fork_height - curr_fork_height;
|
||||
rewind_blocks_n(blocks_till_next_hardfork - 1);
|
||||
gen_.m_hf_version = hard_forks[i + 1].first;
|
||||
create_block();
|
||||
}
|
||||
|
||||
assert(gen_.m_hf_version == hard_fork_version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void linear_chain_generator::rewind_until_v9()
|
||||
{
|
||||
gen_.set_hf_version(8);
|
||||
gen_.m_hf_version = 8;
|
||||
create_block();
|
||||
gen_.set_hf_version(9);
|
||||
gen_.m_hf_version = 9;
|
||||
create_block();
|
||||
}
|
||||
|
||||
|
@ -336,6 +360,25 @@ void test_generator::get_block_chain(std::vector<block_info>& blockchain, const
|
|||
std::reverse(blockchain.begin(), blockchain.end());
|
||||
}
|
||||
|
||||
// TODO(loki): Copypasta
|
||||
void test_generator::get_block_chain(std::vector<cryptonote::block>& blockchain, const crypto::hash& head, size_t n) const
|
||||
{
|
||||
crypto::hash curr = head;
|
||||
while (null_hash != curr && blockchain.size() < n)
|
||||
{
|
||||
auto it = m_blocks_info.find(curr);
|
||||
if (m_blocks_info.end() == it)
|
||||
{
|
||||
throw std::runtime_error("block hash wasn't found");
|
||||
}
|
||||
|
||||
blockchain.push_back(it->second.block);
|
||||
curr = it->second.prev_id;
|
||||
}
|
||||
|
||||
std::reverse(blockchain.begin(), blockchain.end());
|
||||
}
|
||||
|
||||
void test_generator::get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const
|
||||
{
|
||||
std::vector<block_info> blockchain;
|
||||
|
@ -365,9 +408,45 @@ uint64_t test_generator::get_already_generated_coins(const cryptonote::block& bl
|
|||
void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight, std::vector<size_t>& block_weights, uint64_t already_generated_coins)
|
||||
{
|
||||
const size_t block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
|
||||
|
||||
uint64_t block_reward;
|
||||
cryptonote::get_block_reward(misc_utils::median(block_weights), block_weight, already_generated_coins, block_reward, hf_version_, 0);
|
||||
m_blocks_info.insert({get_block_hash(blk), block_info(blk.prev_id, already_generated_coins + block_reward, block_weight)});
|
||||
cryptonote::get_base_block_reward(misc_utils::median(block_weights), block_weight, already_generated_coins, block_reward, m_hf_version, 0);
|
||||
|
||||
m_blocks_info.insert({get_block_hash(blk), block_info(blk.prev_id, already_generated_coins + block_reward, block_weight, blk)});
|
||||
}
|
||||
|
||||
static void manual_calc_batched_governance(const test_generator &generator, const crypto::hash &head, loki_miner_tx_context &miner_tx_context, int hard_fork_version, uint64_t height)
|
||||
{
|
||||
miner_tx_context.batched_governance = 0;
|
||||
|
||||
if (hard_fork_version >= cryptonote::network_version_10_bulletproofs &&
|
||||
cryptonote::height_has_governance_output(cryptonote::FAKECHAIN, hard_fork_version, height))
|
||||
{
|
||||
const cryptonote::config_t &network = cryptonote::get_config(cryptonote::FAKECHAIN, hard_fork_version);
|
||||
uint64_t num_blocks = network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS;
|
||||
uint64_t start_height = height - num_blocks;
|
||||
|
||||
if (height < num_blocks)
|
||||
{
|
||||
start_height = 0;
|
||||
num_blocks = height;
|
||||
}
|
||||
|
||||
std::vector<block> blockchain;
|
||||
blockchain.reserve(num_blocks);
|
||||
generator.get_block_chain(blockchain, head, num_blocks);
|
||||
|
||||
for (const block &entry : blockchain)
|
||||
{
|
||||
uint64_t block_height = cryptonote::get_block_height(entry);
|
||||
if (block_height < start_height)
|
||||
continue;
|
||||
|
||||
if (entry.major_version >= network_version_10_bulletproofs)
|
||||
miner_tx_context.batched_governance += cryptonote::derive_governance_from_block_reward(cryptonote::FAKECHAIN, entry);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
|
||||
|
@ -376,8 +455,8 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
|
|||
const crypto::public_key& sn_pub_key /* = crypto::null_key */, const std::vector<sn_contributor_t>& sn_infos)
|
||||
{
|
||||
/// a temporary workaround
|
||||
blk.major_version = hf_version_;
|
||||
blk.minor_version = hf_version_;
|
||||
blk.major_version = m_hf_version;
|
||||
blk.minor_version = m_hf_version;
|
||||
blk.timestamp = timestamp;
|
||||
blk.prev_id = prev_id;
|
||||
|
||||
|
@ -402,9 +481,13 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
|
|||
|
||||
blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx);
|
||||
size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
|
||||
|
||||
cryptonote::loki_miner_tx_context miner_tx_context(cryptonote::FAKECHAIN, sn_pub_key, sn_infos);
|
||||
manual_calc_batched_governance(*this, prev_id, miner_tx_context, m_hf_version, height);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), hf_version_, cryptonote::MAINNET, sn_pub_key, sn_infos))
|
||||
if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), m_hf_version, miner_tx_context))
|
||||
return false;
|
||||
|
||||
size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
|
||||
|
@ -504,9 +587,12 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
|
|||
}
|
||||
else
|
||||
{
|
||||
size_t current_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
|
||||
// TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
|
||||
if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, current_block_weight, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), hf_version_))
|
||||
cryptonote::loki_miner_tx_context miner_tx_context(cryptonote::FAKECHAIN);
|
||||
manual_calc_batched_governance(*this, prev_id, miner_tx_context, m_hf_version, height);
|
||||
|
||||
size_t current_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
|
||||
if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, current_block_weight, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), m_hf_version, miner_tx_context))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -975,127 +1061,17 @@ 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::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);
|
||||
crypto::derive_public_key(derivation, output_index, 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*/)
|
||||
{
|
||||
keypair txkey;
|
||||
txkey = keypair::generate(hw::get_device("default"));
|
||||
add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
|
||||
if (0 != p_txkey)
|
||||
*p_txkey = txkey;
|
||||
|
||||
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;
|
||||
}
|
||||
block_reward += fee;
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
|
||||
tx_out out;
|
||||
out.amount = block_reward;
|
||||
out.target = txout_to_key(out_eph_public_key);
|
||||
tx.vout.push_back(out);
|
||||
|
||||
tx.version = 1;
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head,
|
||||
const account_base& acc_from, const account_base& acc_to, uint64_t amount, uint64_t fee)
|
||||
{
|
||||
|
|
|
@ -174,16 +174,18 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_weight)
|
||||
block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_weight, cryptonote::block a_block)
|
||||
: prev_id(a_prev_id)
|
||||
, already_generated_coins(an_already_generated_coins)
|
||||
, block_weight(a_block_weight)
|
||||
, block(a_block)
|
||||
{
|
||||
}
|
||||
|
||||
crypto::hash prev_id;
|
||||
uint64_t already_generated_coins;
|
||||
size_t block_weight;
|
||||
cryptonote::block block;
|
||||
};
|
||||
|
||||
enum block_fields
|
||||
|
@ -199,7 +201,9 @@ public:
|
|||
bf_hf_version= 1 << 8
|
||||
};
|
||||
|
||||
void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const;
|
||||
void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const;
|
||||
void get_block_chain(std::vector<cryptonote::block>& blockchain, const crypto::hash& head, size_t n) const;
|
||||
|
||||
void get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const;
|
||||
uint64_t get_already_generated_coins(const crypto::hash& blk_id) const;
|
||||
uint64_t get_already_generated_coins(const cryptonote::block& blk) const;
|
||||
|
@ -222,13 +226,12 @@ 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) {}
|
||||
explicit test_generator(int hf_version = 7) : m_hf_version(hf_version) {}
|
||||
|
||||
void set_hf_version(uint8_t ver) { hf_version_ = ver; }
|
||||
int m_hf_version;
|
||||
|
||||
private:
|
||||
std::unordered_map<crypto::hash, block_info> m_blocks_info;
|
||||
uint8_t hf_version_;
|
||||
};
|
||||
|
||||
/// ------------ Service Nodes -----------
|
||||
|
@ -299,7 +302,8 @@ class linear_chain_generator
|
|||
: gen_(), events_(events)
|
||||
{ }
|
||||
|
||||
uint64_t height() const { return get_block_height(blocks_.back()); }
|
||||
uint64_t height() const { return get_block_height(blocks_.back()); }
|
||||
const std::vector<cryptonote::block>& blocks() const { return blocks_; }
|
||||
|
||||
cryptonote::account_base create_account();
|
||||
|
||||
|
@ -310,6 +314,7 @@ class linear_chain_generator
|
|||
cryptonote::block create_block_on_fork(const cryptonote::block& prev, const std::vector<cryptonote::transaction>& txs = {});
|
||||
|
||||
void rewind_until_v9();
|
||||
void rewind_until_version(const std::vector<std::pair<uint8_t, uint64_t>> &hard_forks, int hard_fork_version);
|
||||
void rewind_blocks_n(int n);
|
||||
void rewind_blocks();
|
||||
|
||||
|
@ -393,11 +398,7 @@ class dereg_tx_builder {
|
|||
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);
|
||||
crypto::public_key get_output_key(const cryptonote::keypair& txkey, const cryptonote::account_public_address& addr, size_t output_index);
|
||||
|
||||
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
|
||||
const cryptonote::account_public_address& miner_address, cryptonote::transaction& tx,
|
||||
|
@ -431,11 +432,11 @@ class TxBuilder {
|
|||
const cryptonote::account_base& m_from;
|
||||
const cryptonote::account_base& m_to;
|
||||
uint64_t m_amount;
|
||||
uint64_t m_fee;
|
||||
uint64_t m_unlock_time;
|
||||
std::vector<uint8_t> m_extra;
|
||||
|
||||
/// optional fields
|
||||
boost::optional<uint64_t> m_fee;
|
||||
boost::optional<std::vector<uint8_t>> m_extra;
|
||||
boost::optional<uint64_t> m_unlock_time;
|
||||
bool m_per_output_unlock = false;
|
||||
bool m_is_staking = false;
|
||||
|
||||
|
@ -455,6 +456,8 @@ public:
|
|||
, m_from(from)
|
||||
, m_to(to)
|
||||
, m_amount(amount)
|
||||
, m_fee(TESTS_DEFAULT_FEE)
|
||||
, m_unlock_time(0)
|
||||
{}
|
||||
|
||||
TxBuilder&& with_fee(uint64_t fee) {
|
||||
|
@ -497,24 +500,16 @@ public:
|
|||
std::vector<cryptonote::tx_destination_entry> destinations;
|
||||
uint64_t change_amount;
|
||||
|
||||
const auto fee = m_fee ? *m_fee : TESTS_DEFAULT_FEE;
|
||||
const auto nmix = 9;
|
||||
|
||||
fill_tx_sources_and_destinations(
|
||||
m_events, m_head, m_from, m_to, m_amount, fee, nmix, sources, destinations, &change_amount);
|
||||
m_events, m_head, m_from, m_to, m_amount, m_fee, nmix, sources, destinations, &change_amount);
|
||||
|
||||
const bool is_subaddr = false;
|
||||
|
||||
cryptonote::tx_destination_entry change_addr{ change_amount, m_from.get_keys().m_account_address, is_subaddr };
|
||||
|
||||
std::vector<uint8_t> extra;
|
||||
if (m_extra) extra = *m_extra;
|
||||
|
||||
const auto unlock_time = m_unlock_time ? *m_unlock_time : 0;
|
||||
|
||||
|
||||
return cryptonote::construct_tx(
|
||||
m_from.get_keys(), sources, destinations, change_addr, extra, m_tx, unlock_time, m_is_staking, m_per_output_unlock);
|
||||
m_from.get_keys(), sources, destinations, change_addr, m_extra, m_tx, m_unlock_time, m_is_staking, m_per_output_unlock);
|
||||
|
||||
}
|
||||
};
|
||||
|
|
|
@ -103,74 +103,6 @@ gen_simple_chain_001::gen_simple_chain_001()
|
|||
REGISTER_CALLBACK("verify_callback_2", gen_simple_chain_001::verify_callback_2);
|
||||
}
|
||||
|
||||
static void my_construct_tx(const eventV& events,
|
||||
cryptonote::transaction& tx,
|
||||
std::vector<cryptonote::block>& blocks,
|
||||
const cryptonote::account_base& from,
|
||||
const cryptonote::account_base& to,
|
||||
uint64_t amount)
|
||||
{
|
||||
|
||||
std::vector<tx_source_entry> sources;
|
||||
|
||||
{
|
||||
sources.resize(1);
|
||||
tx_source_entry& src = sources.back();
|
||||
|
||||
src.amount = blocks[0].miner_tx.vout[0].amount;
|
||||
src.rct = true;
|
||||
|
||||
const auto index_in_tx = 0;
|
||||
|
||||
for (int m = 0; m < 10; ++m) {
|
||||
const auto sender = boost::get<txout_to_key>(blocks[m].miner_tx.vout[index_in_tx].target).key;
|
||||
const auto tx_index = (m == 0) ? 0 : (1 + (m - 1) * 2);
|
||||
src.push_output(tx_index, sender, blocks[m].miner_tx.vout[index_in_tx].amount);
|
||||
}
|
||||
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[0].miner_tx);
|
||||
src.real_output = 0;
|
||||
src.mask = rct::identity();
|
||||
src.real_output_in_tx_index = index_in_tx;
|
||||
}
|
||||
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back({amount, to.get_keys().m_account_address, false /* is subaddr */});
|
||||
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
|
||||
/// not sure what this is
|
||||
tx_destination_entry change_addr{amount, from.get_keys().m_account_address, false /* is subaddr */ };
|
||||
|
||||
const auto spend_pk = from.get_keys().m_account_address.m_spend_public_key;
|
||||
uint64_t unlock_time = 0;
|
||||
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; // not sure what this is
|
||||
subaddresses[spend_pk] = { 0, 0 };
|
||||
|
||||
const bool rct = true;
|
||||
const bool staking = false;
|
||||
const bool per_output_unlock = false;
|
||||
|
||||
rct::multisig_out* msout = nullptr;
|
||||
|
||||
construct_tx_and_get_tx_key(from.get_keys(),
|
||||
subaddresses,
|
||||
sources,
|
||||
destinations,
|
||||
change_addr,
|
||||
{},
|
||||
tx,
|
||||
unlock_time,
|
||||
tx_key,
|
||||
additional_tx_keys,
|
||||
rct,
|
||||
rct::RangeProofBorromean,
|
||||
msout,
|
||||
staking,
|
||||
per_output_unlock);
|
||||
}
|
||||
|
||||
void make_rct_tx(eventV& events,
|
||||
std::vector<cryptonote::transaction>& txs,
|
||||
const cryptonote::block& blk_head,
|
||||
|
|
|
@ -109,6 +109,7 @@ int main(int argc, char* argv[])
|
|||
MLOG(el::Level::Info, "Running all tests\n");
|
||||
}
|
||||
|
||||
|
||||
if (run_all || command_line::get_arg(vm, arg_service_nodes))
|
||||
{
|
||||
GENERATE_AND_PLAY(gen_service_nodes);
|
||||
|
@ -122,6 +123,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
if (run_all)
|
||||
{
|
||||
GENERATE_AND_PLAY(gen_batched_governance_reward); // Loki Governance
|
||||
|
||||
GENERATE_AND_PLAY(gen_simple_chain_001);
|
||||
GENERATE_AND_PLAY(gen_simple_chain_split_1);
|
||||
GENERATE_AND_PLAY(gen_chain_switch_1);
|
||||
|
@ -280,7 +283,6 @@ int main(int argc, char* argv[])
|
|||
GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_2_many_inputs);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234_many_inputs);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "gtest/gtest.h"
|
||||
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
|
@ -42,17 +43,21 @@ namespace
|
|||
protected:
|
||||
static const size_t current_block_weight = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2;
|
||||
|
||||
bool m_block_not_too_big;
|
||||
union
|
||||
{
|
||||
bool m_block_not_too_big;
|
||||
bool m_block_reward_calc_success;
|
||||
};
|
||||
uint64_t m_block_reward;
|
||||
};
|
||||
|
||||
#define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \
|
||||
m_block_not_too_big = get_block_reward(0, current_block_weight, already_generated_coins, m_block_reward,7,0); \
|
||||
m_block_not_too_big = get_base_block_reward(0, current_block_weight, already_generated_coins, m_block_reward,7,0); \
|
||||
ASSERT_TRUE(m_block_not_too_big); \
|
||||
ASSERT_EQ(m_block_reward, expected_reward);
|
||||
|
||||
#define TEST_ALREADY_GENERATED_COINS_V2(already_generated_coins, expected_reward, h) \
|
||||
m_block_not_too_big = get_block_reward(0, current_block_weight, already_generated_coins, m_block_reward,8,h); \
|
||||
m_block_not_too_big = get_base_block_reward(0, current_block_weight, already_generated_coins, m_block_reward,8,h); \
|
||||
ASSERT_TRUE(m_block_not_too_big); \
|
||||
ASSERT_EQ(m_block_reward, expected_reward);
|
||||
|
||||
|
@ -86,20 +91,52 @@ namespace
|
|||
TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - ((1 << 20) - 1), UINT64_C(0));
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_already_generated_coins, reward_parity_between_orig_and_loki_algo)
|
||||
{
|
||||
uint64_t already_generated_coins = UINT64_C(22500000000000000)+116*720*90;
|
||||
m_block_reward_calc_success = true;
|
||||
|
||||
cryptonote::block_reward_parts reward_parts_v7 = {};
|
||||
cryptonote::loki_block_reward_context reward_context_v7 = {};
|
||||
m_block_reward_calc_success &= get_loki_block_reward(0, current_block_weight, already_generated_coins, 7, reward_parts_v7, reward_context_v7);
|
||||
|
||||
cryptonote::block_reward_parts reward_parts_v8 = {};
|
||||
cryptonote::loki_block_reward_context reward_context_v8 = {};
|
||||
m_block_reward_calc_success &= get_loki_block_reward(0, current_block_weight, already_generated_coins, 8, reward_parts_v8, reward_context_v8);
|
||||
|
||||
cryptonote::block_reward_parts reward_parts_v9 = {};
|
||||
cryptonote::loki_block_reward_context reward_context_v9 = {};
|
||||
m_block_reward_calc_success &= get_loki_block_reward(0, current_block_weight, already_generated_coins, 9, reward_parts_v9, reward_context_v9);
|
||||
|
||||
uint64_t reward_v7_orig = 0;
|
||||
m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v7_orig, 7, 0);
|
||||
|
||||
uint64_t reward_v8_orig = 0;
|
||||
m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v8_orig, 8, 0);
|
||||
|
||||
uint64_t reward_v9_orig = 0;
|
||||
m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v9_orig, 9, 0);
|
||||
|
||||
ASSERT_TRUE(m_block_reward_calc_success); \
|
||||
ASSERT_EQ(reward_parts_v7.adjusted_base_reward, reward_v7_orig);
|
||||
ASSERT_EQ(reward_parts_v8.adjusted_base_reward, reward_v8_orig);
|
||||
ASSERT_EQ(reward_parts_v9.adjusted_base_reward, reward_v9_orig);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
class block_reward_and_current_block_weight : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
m_block_not_too_big = get_block_reward(0, 0, already_generated_coins, m_standard_block_reward, 1, 0);
|
||||
m_block_not_too_big = get_base_block_reward(0, 0, already_generated_coins, m_standard_block_reward, 1, 0);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward);
|
||||
}
|
||||
|
||||
void do_test(size_t median_block_weight, size_t current_block_weight)
|
||||
{
|
||||
m_block_not_too_big = get_block_reward(median_block_weight, current_block_weight, already_generated_coins, m_block_reward, 1, 0);
|
||||
m_block_not_too_big = get_base_block_reward(median_block_weight, current_block_weight, already_generated_coins, m_block_reward, 1, 0);
|
||||
}
|
||||
|
||||
static const uint64_t already_generated_coins = UINT64_C(22500000000000000);
|
||||
|
@ -183,14 +220,14 @@ namespace
|
|||
|
||||
m_last_block_weights_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
|
||||
|
||||
m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), 0, already_generated_coins, m_standard_block_reward, 1, 0);
|
||||
m_block_not_too_big = get_base_block_reward(epee::misc_utils::median(m_last_block_weights), 0, already_generated_coins, m_standard_block_reward, 1, 0);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward);
|
||||
}
|
||||
|
||||
void do_test(size_t current_block_weight)
|
||||
{
|
||||
m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), current_block_weight, already_generated_coins, m_block_reward, 1, 0);
|
||||
m_block_not_too_big = get_base_block_reward(epee::misc_utils::median(m_last_block_weights), current_block_weight, already_generated_coins, m_block_reward, 1, 0);
|
||||
}
|
||||
|
||||
static const uint64_t already_generated_coins = UINT64_C(22500000000000000);
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace
|
|||
bool bulletproof,
|
||||
bool per_output_unlock)
|
||||
{
|
||||
|
||||
std::uint64_t source_amount = 0;
|
||||
std::vector<cryptonote::tx_source_entry> actual_sources;
|
||||
for (auto const& source : sources)
|
||||
|
|
Loading…
Reference in New Issue