mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
service_node_list: add multiple stakers, reward shares, etc
This commit is contained in:
parent
fb66b7e00b
commit
003f0709b8
10 changed files with 382 additions and 276 deletions
|
@ -480,7 +480,6 @@ namespace cryptonote
|
|||
binary_archive<true> ar(oss);
|
||||
bool r = ::do_serialize(ar, field);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to serialize tx extra service node deregister");
|
||||
|
||||
std::string tx_extra_str = oss.str();
|
||||
size_t pos = tx_extra.size();
|
||||
tx_extra.resize(tx_extra.size() + tx_extra_str.size());
|
||||
|
@ -489,17 +488,46 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
void add_service_node_register_to_tx_extra(std::vector<uint8_t>& tx_extra, const tx_extra_service_node_register& registration)
|
||||
{
|
||||
add_data_to_tx_extra(tx_extra, reinterpret_cast<const char*>(®istration), sizeof(registration), TX_EXTRA_TAG_SERVICE_NODE_REGISTER);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool get_service_node_register_from_tx_extra(const std::vector<uint8_t>& tx_extra, tx_extra_service_node_register ®istration)
|
||||
{
|
||||
std::vector<tx_extra_field> tx_extra_fields;
|
||||
parse_tx_extra(tx_extra, tx_extra_fields);
|
||||
bool result = find_tx_extra_field_by_type(tx_extra_fields, registration);
|
||||
return result;
|
||||
return result && registration.m_public_spend_keys.size() == registration.m_public_view_keys.size();
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool add_service_node_register_to_tx_extra(std::vector<uint8_t>& tx_extra, const std::vector<cryptonote::account_public_address>& addresses, const std::vector<uint32_t>& shares, const crypto::public_key& service_node_key)
|
||||
{
|
||||
if (addresses.size() != shares.size())
|
||||
{
|
||||
LOG_ERROR("Tried to serialize registration with more addresses than shares, this should never happen");
|
||||
return false;
|
||||
}
|
||||
std::vector<crypto::public_key> public_view_keys(addresses.size());
|
||||
std::vector<crypto::public_key> public_spend_keys(addresses.size());
|
||||
for (size_t i = 0; i < addresses.size(); i++)
|
||||
{
|
||||
public_view_keys[i] = addresses[i].m_view_public_key;
|
||||
public_spend_keys[i] = addresses[i].m_spend_public_key;
|
||||
}
|
||||
// convert to variant
|
||||
tx_extra_field field = tx_extra_service_node_register{ public_spend_keys, public_view_keys, shares, service_node_key };
|
||||
// serialize
|
||||
std::ostringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
bool r = ::do_serialize(ar, field);
|
||||
CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra registration tx");
|
||||
// append
|
||||
std::string tx_extra_str = oss.str();
|
||||
size_t pos = tx_extra.size();
|
||||
tx_extra.resize(tx_extra.size() + tx_extra_str.size());
|
||||
memcpy(&tx_extra[pos], tx_extra_str.data(), tx_extra_str.size());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
void add_service_node_winner_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::public_key& winner)
|
||||
{
|
||||
add_data_to_tx_extra(tx_extra, reinterpret_cast<const char *>(&winner), sizeof(winner), TX_EXTRA_TAG_SERVICE_NODE_WINNER);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool get_service_node_deregister_from_tx_extra(const std::vector<uint8_t>& tx_extra, tx_extra_service_node_deregister &deregistration)
|
||||
|
@ -510,6 +538,18 @@ namespace cryptonote
|
|||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_service_node_winner_from_tx_extra(const std::vector<uint8_t>& tx_extra)
|
||||
{
|
||||
// parse
|
||||
std::vector<tx_extra_field> tx_extra_fields;
|
||||
parse_tx_extra(tx_extra, tx_extra_fields);
|
||||
// find corresponding field
|
||||
tx_extra_service_node_winner winner;
|
||||
if (!find_tx_extra_field_by_type(tx_extra_fields, winner))
|
||||
return crypto::null_pkey;
|
||||
return winner.m_service_node_key;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type)
|
||||
{
|
||||
if (tx_extra.empty())
|
||||
|
|
|
@ -71,9 +71,11 @@ namespace cryptonote
|
|||
void add_tx_pub_key_to_extra(transaction_prefix& tx, const crypto::public_key& tx_pub_key);
|
||||
void add_tx_pub_key_to_extra(std::vector<uint8_t>& tx_extra, const crypto::public_key& tx_pub_key);
|
||||
bool add_service_node_deregister_to_tx_extra(std::vector<uint8_t>& tx_extra, const tx_extra_service_node_deregister& deregistration);
|
||||
void add_service_node_register_to_tx_extra(std::vector<uint8_t>& tx_extra, const tx_extra_service_node_register& registration);
|
||||
bool get_service_node_register_from_tx_extra(const std::vector<uint8_t>& tx_extra, tx_extra_service_node_register& registration);
|
||||
bool get_service_node_deregister_from_tx_extra(const std::vector<uint8_t>& tx_extra, tx_extra_service_node_deregister& deregistration);
|
||||
bool add_service_node_register_to_tx_extra(std::vector<uint8_t>& tx_extra, const std::vector<cryptonote::account_public_address>& addresses, const std::vector<uint32_t>& shares, const crypto::public_key& service_node_key);
|
||||
void add_service_node_winner_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::public_key& winner);
|
||||
crypto::public_key get_service_node_winner_from_tx_extra(const std::vector<uint8_t>& tx_extra);
|
||||
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const std::vector<uint8_t>& tx_extra);
|
||||
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx);
|
||||
bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys);
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
|
||||
#define TX_EXTRA_TAG_SERVICE_NODE_REGISTER 0x70
|
||||
#define TX_EXTRA_TAG_SERVICE_NODE_DEREGISTER 0x71
|
||||
#define TX_EXTRA_TAG_SERVICE_NODE_WINNER 0x72
|
||||
#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE
|
||||
|
||||
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
|
||||
|
@ -180,16 +181,27 @@ namespace cryptonote
|
|||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_extra_service_node_register
|
||||
struct tx_extra_service_node_winner
|
||||
{
|
||||
crypto::public_key public_view_key;
|
||||
crypto::public_key public_spend_key;
|
||||
crypto::public_key service_node_key;
|
||||
crypto::public_key m_service_node_key;
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
FIELD(public_view_key)
|
||||
FIELD(public_spend_key)
|
||||
FIELD(service_node_key)
|
||||
FIELD(m_service_node_key)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_extra_service_node_register
|
||||
{
|
||||
std::vector<crypto::public_key> m_public_spend_keys;
|
||||
std::vector<crypto::public_key> m_public_view_keys;
|
||||
std::vector<uint32_t> m_shares;
|
||||
crypto::public_key m_service_node_key;
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
FIELD(m_public_spend_keys)
|
||||
FIELD(m_public_view_keys)
|
||||
FIELD(m_shares)
|
||||
FIELD(m_service_node_key)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
|
@ -223,6 +235,7 @@ namespace cryptonote
|
|||
tx_extra_additional_pub_keys,
|
||||
tx_extra_mysterious_minergate,
|
||||
tx_extra_service_node_register,
|
||||
tx_extra_service_node_winner,
|
||||
tx_extra_service_node_deregister> tx_extra_field;
|
||||
}
|
||||
|
||||
|
@ -236,3 +249,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXT
|
|||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_service_node_register, TX_EXTRA_TAG_SERVICE_NODE_REGISTER);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_service_node_deregister, TX_EXTRA_TAG_SERVICE_NODE_DEREGISTER);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_service_node_winner, TX_EXTRA_TAG_SERVICE_NODE_WINNER);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#define STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS 20
|
||||
#define STAKING_REQUIREMENT_LOCK_BLOCKS (30*24*31)
|
||||
#define STAKING_RELOCK_WINDOW_BLOCKS (30*6)
|
||||
#define STAKING_SHARES UINT32_MAX
|
||||
|
||||
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 11
|
||||
|
||||
|
|
|
@ -1324,9 +1324,10 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
uint8_t hf_version = m_hardfork->get_current_version();
|
||||
size_t max_outs = hf_version >= 4 ? 1 : 11;
|
||||
|
||||
account_public_address service_node_address = m_service_node_list.select_winner(b.prev_id);
|
||||
crypto::public_key winner = m_service_node_list.select_winner(b.prev_id);
|
||||
std::vector<std::pair<account_public_address, uint32_t>> service_node_addresses = m_service_node_list.get_winner_addresses_and_shares(b.prev_id);
|
||||
|
||||
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version, m_nettype, service_node_address);
|
||||
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version, m_nettype, winner, service_node_addresses);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
|
||||
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
|
||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
|
@ -1335,7 +1336,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_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version, m_nettype, service_node_address);
|
||||
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version, m_nettype, winner, service_node_addresses);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
|
||||
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
|
||||
|
|
|
@ -42,6 +42,7 @@ using namespace epee;
|
|||
#include "crypto/hash.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "multisig/multisig.h"
|
||||
#include "common/int-util.h"
|
||||
|
||||
using namespace crypto;
|
||||
|
||||
|
@ -103,7 +104,23 @@ namespace cryptonote
|
|||
|
||||
uint64_t get_service_node_reward(uint64_t height, uint64_t base_reward, int hard_fork_version)
|
||||
{
|
||||
return hard_fork_version >= 8 ? base_reward / 2 : 0;
|
||||
return hard_fork_version >= 9 ? base_reward / 2 : 0;
|
||||
}
|
||||
|
||||
uint64_t get_share_of_reward(uint32_t shares, uint64_t total_service_node_reward)
|
||||
{
|
||||
uint64_t hi, lo, rewardhi, rewardlo;
|
||||
lo = mul128(total_service_node_reward, shares, &hi);
|
||||
div128_32(hi, lo, STAKING_SHARES, &rewardhi, &rewardlo);
|
||||
return rewardlo;
|
||||
}
|
||||
|
||||
static uint64_t calculate_sum_of_shares(const std::vector<std::pair<cryptonote::account_public_address, uint32_t>>& shares, uint64_t total_service_node_reward)
|
||||
{
|
||||
uint64_t reward = 0;
|
||||
for (size_t i = 0; i < shares.size(); i++)
|
||||
reward += get_share_of_reward(shares[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)
|
||||
|
@ -151,7 +168,21 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs /* unused */, uint8_t hard_fork_version, network_type nettype, const account_public_address service_node_address) {
|
||||
bool construct_miner_tx(
|
||||
size_t height,
|
||||
size_t median_size,
|
||||
uint64_t already_generated_coins,
|
||||
size_t current_block_size,
|
||||
uint64_t fee,
|
||||
const account_public_address &miner_address,
|
||||
transaction& tx,
|
||||
const blobdata& extra_nonce,
|
||||
size_t max_outs /* unused */,
|
||||
uint8_t hard_fork_version,
|
||||
network_type nettype,
|
||||
const crypto::public_key& service_node_key,
|
||||
const std::vector<std::pair<account_public_address, uint32_t>>& service_node_info)
|
||||
{
|
||||
tx.vin.clear();
|
||||
tx.vout.clear();
|
||||
tx.extra.clear();
|
||||
|
@ -168,6 +199,8 @@ namespace cryptonote
|
|||
add_tx_pub_key_to_extra(tx, gov_key.pub);
|
||||
}
|
||||
|
||||
add_service_node_winner_to_tx_extra(tx.extra, service_node_key);
|
||||
|
||||
txin_gen in;
|
||||
in.height = height;
|
||||
|
||||
|
@ -185,13 +218,13 @@ namespace cryptonote
|
|||
|
||||
//TODO: declining governance reward schedule
|
||||
uint64_t governance_reward = 0;
|
||||
uint64_t service_node_reward = 0;
|
||||
uint64_t total_service_node_reward = 0;
|
||||
if (already_generated_coins != 0)
|
||||
{
|
||||
governance_reward = get_governance_reward(height, block_reward);
|
||||
service_node_reward = get_service_node_reward(height, block_reward, hard_fork_version);
|
||||
total_service_node_reward = get_service_node_reward(height, block_reward, hard_fork_version);
|
||||
block_reward -= governance_reward;
|
||||
block_reward -= service_node_reward;
|
||||
block_reward -= calculate_sum_of_shares(service_node_info, total_service_node_reward);
|
||||
}
|
||||
|
||||
block_reward += fee;
|
||||
|
@ -199,7 +232,7 @@ namespace cryptonote
|
|||
uint64_t summary_amounts = 0;
|
||||
|
||||
{
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")");
|
||||
|
@ -216,22 +249,25 @@ namespace cryptonote
|
|||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
if (hard_fork_version >= 8)
|
||||
if (hard_fork_version >= 9)
|
||||
{
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
bool r = crypto::generate_key_derivation(service_node_address.m_view_public_key, gov_key.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << service_node_address.m_view_public_key << ", " << gov_key.sec << ")");
|
||||
r = crypto::derive_public_key(derivation, 1, service_node_address.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 1 << ", "<< service_node_address.m_spend_public_key << ")");
|
||||
for (size_t i = 0; i < service_node_info.size(); i++)
|
||||
{
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
bool r = crypto::generate_key_derivation(service_node_info[i].first.m_view_public_key, gov_key.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << service_node_info[i].first.m_view_public_key << ", " << gov_key.sec << ")");
|
||||
r = crypto::derive_public_key(derivation, 1+i, service_node_info[i].first.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << (1+i) << ", "<< service_node_info[i].first.m_spend_public_key << ")");
|
||||
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = service_node_reward;
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = get_share_of_reward(service_node_info[i].second, total_service_node_reward);
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
}
|
||||
|
||||
if (already_generated_coins != 0)
|
||||
|
@ -273,7 +309,7 @@ namespace cryptonote
|
|||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(summary_amounts == (block_reward + governance_reward + service_node_reward), false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal total block_reward = " << (block_reward + governance_reward + service_node_reward));
|
||||
CHECK_AND_ASSERT_MES(summary_amounts == (block_reward + governance_reward + total_service_node_reward), false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal total block_reward = " << (block_reward + governance_reward + total_service_node_reward));
|
||||
|
||||
tx.version = 2;
|
||||
|
||||
|
|
|
@ -37,10 +37,26 @@
|
|||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1, network_type nettype = MAINNET, const account_public_address service_node_address=account_public_address());
|
||||
bool construct_miner_tx(
|
||||
size_t height,
|
||||
size_t median_size,
|
||||
uint64_t already_generated_coins,
|
||||
size_t current_block_size,
|
||||
uint64_t fee,
|
||||
const account_public_address &miner_address,
|
||||
transaction& tx,
|
||||
const blobdata& extra_nonce = blobdata(),
|
||||
size_t max_outs = 999,
|
||||
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, uint32_t>>& service_node_info={ std::pair<account_public_address, uint32_t>({ crypto::null_pkey, crypto::null_pkey }, STAKING_SHARES) }
|
||||
);
|
||||
|
||||
keypair get_deterministic_keypair_from_height(uint64_t height);
|
||||
|
||||
uint64_t get_share_of_reward(uint32_t shares, uint64_t total_service_node_reward);
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "wallet/wallet2.h"
|
||||
#include "cryptonote_tx_utils.h"
|
||||
#include "cryptonote_basic/tx_extra.h"
|
||||
#include "common/int-util.h"
|
||||
|
||||
#include "service_node_list.h"
|
||||
|
||||
|
@ -56,8 +57,7 @@ namespace service_nodes
|
|||
// TODO: Save this calculation, only do it if it's not here.
|
||||
LOG_PRINT_L0("Recalculating service nodes list, scanning last 30 days");
|
||||
|
||||
m_service_nodes_last_reward.clear();
|
||||
m_service_nodes_keys.clear();
|
||||
m_service_nodes_infos.clear();
|
||||
|
||||
while (!m_rollback_events.empty())
|
||||
{
|
||||
|
@ -101,8 +101,8 @@ namespace service_nodes
|
|||
std::vector<crypto::public_key> service_node_list::get_service_node_pubkeys() const
|
||||
{
|
||||
std::vector<crypto::public_key> result;
|
||||
for (const auto& iter : m_service_nodes_keys)
|
||||
result.push_back(iter.second);
|
||||
for (const auto& iter : m_service_nodes_infos)
|
||||
result.push_back(iter.first);
|
||||
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const crypto::public_key &a, const crypto::public_key &b) {
|
||||
|
@ -129,11 +129,7 @@ namespace service_nodes
|
|||
|
||||
bool service_node_list::is_service_node(const crypto::public_key& pubkey) const
|
||||
{
|
||||
// TODO: speed this up, it should just be a single lookup.
|
||||
for (const auto& iter : m_service_nodes_keys)
|
||||
if (iter.second == pubkey)
|
||||
return true;
|
||||
return false;
|
||||
return m_service_nodes_infos.find(pubkey) != m_service_nodes_infos.end();
|
||||
}
|
||||
|
||||
bool service_node_list::reg_tx_has_correct_unlock_time(const cryptonote::transaction& tx, uint64_t block_height) const
|
||||
|
@ -141,34 +137,36 @@ namespace service_nodes
|
|||
return tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && tx.unlock_time >= block_height + STAKING_REQUIREMENT_LOCK_BLOCKS;
|
||||
}
|
||||
|
||||
bool service_node_list::reg_tx_extract_fields(const cryptonote::transaction& tx, cryptonote::account_public_address& address, crypto::public_key& service_node_key, crypto::public_key& tx_pub_key) const
|
||||
bool service_node_list::reg_tx_extract_fields(const cryptonote::transaction& tx, std::vector<cryptonote::account_public_address>& addresses, std::vector<uint32_t>& shares, crypto::public_key& service_node_key, crypto::public_key& tx_pub_key) const
|
||||
{
|
||||
cryptonote::tx_extra_service_node_register register_;
|
||||
if (cryptonote::get_service_node_register_from_tx_extra(tx.extra, register_))
|
||||
cryptonote::tx_extra_service_node_register registration;
|
||||
if (get_service_node_register_from_tx_extra(tx.extra, registration))
|
||||
{
|
||||
address.m_spend_public_key = register_.public_spend_key;
|
||||
address.m_view_public_key = register_.public_view_key;
|
||||
service_node_key = register_.service_node_key;
|
||||
addresses.clear();
|
||||
addresses.reserve(registration.m_public_spend_keys.size());
|
||||
for (size_t i = 0; i < registration.m_public_spend_keys.size(); i++)
|
||||
addresses.push_back(cryptonote::account_public_address{ registration.m_public_spend_keys[i], registration.m_public_view_keys[i] });
|
||||
shares = registration.m_shares;
|
||||
service_node_key = registration.m_service_node_key;
|
||||
}
|
||||
else
|
||||
{
|
||||
address.m_spend_public_key = crypto::null_pkey;
|
||||
address.m_view_public_key = crypto::null_pkey;
|
||||
addresses.clear();
|
||||
shares.clear();
|
||||
service_node_key = crypto::null_pkey;
|
||||
}
|
||||
|
||||
tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx.extra);
|
||||
return address.m_spend_public_key != crypto::null_pkey &&
|
||||
address.m_view_public_key != crypto::null_pkey &&
|
||||
return !addresses.empty() &&
|
||||
tx_pub_key != crypto::null_pkey &&
|
||||
service_node_key != crypto::null_pkey;
|
||||
service_node_key != crypto::null_pkey &&
|
||||
!shares.empty();
|
||||
}
|
||||
|
||||
bool service_node_list::is_reg_tx_staking_output(const cryptonote::transaction& tx, int i, uint64_t block_height, crypto::key_derivation derivation, hw::device& hwdev) const
|
||||
uint64_t service_node_list::get_reg_tx_staking_output_contribution(const cryptonote::transaction& tx, int i, crypto::key_derivation derivation, hw::device& hwdev) const
|
||||
{
|
||||
if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
|
||||
{
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rct::key mask;
|
||||
|
@ -190,24 +188,22 @@ namespace service_nodes
|
|||
break;
|
||||
default:
|
||||
LOG_ERROR("Unsupported rct type: " << tx.rct_signatures.type);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR("Failed to decode input " << i);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return money_transferred >= m_blockchain.get_staking_requirement(block_height);
|
||||
return money_transferred;
|
||||
}
|
||||
|
||||
bool service_node_list::is_deregistration_tx(const cryptonote::transaction& tx, cryptonote::account_public_address &address) const
|
||||
bool service_node_list::is_deregistration_tx(const cryptonote::transaction& tx, crypto::public_key& key) const
|
||||
{
|
||||
if (tx.version != cryptonote::transaction::version_3_deregister_tx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
cryptonote::tx_extra_service_node_deregister deregister;
|
||||
if (!cryptonote::get_service_node_deregister_from_tx_extra(tx.extra, deregister))
|
||||
|
@ -216,72 +212,78 @@ namespace service_nodes
|
|||
return false;
|
||||
}
|
||||
|
||||
if (const std::shared_ptr<quorum_state> state = get_quorum_state(deregister.block_height))
|
||||
{
|
||||
if (deregister.service_node_index >= state->nodes_to_test.size())
|
||||
{
|
||||
LOG_ERROR("Service node index to vote off has become invalid, quorum rules have changed without a hardfork.");
|
||||
return false;
|
||||
}
|
||||
const std::shared_ptr<quorum_state> state = get_quorum_state(deregister.block_height);
|
||||
|
||||
const crypto::public_key &snode_pubkey = state->nodes_to_test[deregister.service_node_index];
|
||||
for (const auto& key_it : m_service_nodes_keys)
|
||||
{
|
||||
if (key_it.second == snode_pubkey)
|
||||
{
|
||||
address = key_it.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!state)
|
||||
{
|
||||
// TODO(loki): Not being able to find a quorum is fatal! We want better caching abilities.
|
||||
LOG_ERROR("Quorum state for height: " << deregister.block_height << ", was not stored by the daemon");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (deregister.service_node_index >= state->nodes_to_test.size())
|
||||
{
|
||||
LOG_ERROR("Service node index to vote off has become invalid, quorum rules have changed without a hardfork.");
|
||||
return false;
|
||||
}
|
||||
|
||||
key = state->nodes_to_test[deregister.service_node_index];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function takes a tx and returns true if it is a staking transaction.
|
||||
// It also sets the address argument to the public spendkey and pub viewkey of
|
||||
// the transaction.
|
||||
//
|
||||
bool service_node_list::is_registration_tx(const cryptonote::transaction& tx, uint64_t block_height, cryptonote::account_public_address& address, crypto::public_key& key) const
|
||||
bool service_node_list::is_registration_tx(const cryptonote::transaction& tx, uint64_t block_height, int index, crypto::public_key& key, service_node_info& info) const
|
||||
{
|
||||
if (!reg_tx_has_correct_unlock_time(tx, block_height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto::public_key tx_pub_key, service_node_key;
|
||||
cryptonote::account_public_address service_node_address;
|
||||
std::vector<cryptonote::account_public_address> service_node_addresses;
|
||||
std::vector<uint32_t> service_node_shares;
|
||||
|
||||
if (!reg_tx_extract_fields(tx, service_node_address, service_node_key, tx_pub_key))
|
||||
{
|
||||
if (!reg_tx_extract_fields(tx, service_node_addresses, service_node_shares, service_node_key, tx_pub_key))
|
||||
return false;
|
||||
|
||||
uint64_t total = 0;
|
||||
for (size_t i = 0; i < service_node_shares.size(); i++)
|
||||
total += service_node_shares[i];
|
||||
if (total > STAKING_SHARES)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_service_node(service_node_key))
|
||||
return false;
|
||||
|
||||
// TODO: check service_node_key has signed service node addresses and
|
||||
// service node shares and timestamp, and that timestamp is not old.
|
||||
|
||||
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(1);
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::generate_key_derivation(service_node_address.m_view_public_key, gov_key.sec, derivation);
|
||||
if (tx.vout.size() < service_node_addresses.size())
|
||||
return false;
|
||||
|
||||
hw::device& hwdev = hw::get_device("default");
|
||||
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
uint64_t transferred = 0;
|
||||
for (size_t i = 0; i < service_node_addresses.size(); i++)
|
||||
{
|
||||
if (is_reg_tx_staking_output(tx, i, block_height, derivation, hwdev))
|
||||
{
|
||||
address = service_node_address;
|
||||
key = service_node_key;
|
||||
return true;
|
||||
}
|
||||
crypto::key_derivation derivation;
|
||||
crypto::generate_key_derivation(service_node_addresses[i].m_view_public_key, gov_key.sec, derivation);
|
||||
|
||||
hw::device& hwdev = hw::get_device("default");
|
||||
|
||||
// TODO: check if output has correct unlock time here, when unlock time is done per output.
|
||||
transferred += get_reg_tx_staking_output_contribution(tx, i, derivation, hwdev);
|
||||
}
|
||||
return false;
|
||||
|
||||
if (transferred < m_blockchain.get_staking_requirement(block_height))
|
||||
return false;
|
||||
|
||||
key = service_node_key;
|
||||
|
||||
info.block_height = block_height;
|
||||
info.transaction_index = index;
|
||||
info.addresses = service_node_addresses;
|
||||
info.shares = service_node_shares;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void service_node_list::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
|
||||
|
@ -289,44 +291,6 @@ namespace service_nodes
|
|||
block_added_generic(block, txs);
|
||||
}
|
||||
|
||||
cryptonote::account_public_address service_node_list::find_service_node_from_miner_tx(const cryptonote::transaction& miner_tx, uint64_t height) const
|
||||
{
|
||||
if (miner_tx.vout.size() != 3)
|
||||
{
|
||||
MERROR("Miner tx should have 3 outputs");
|
||||
return null_address;
|
||||
}
|
||||
|
||||
if (miner_tx.vout[1].target.type() != typeid(cryptonote::txout_to_key))
|
||||
{
|
||||
MERROR("Service node output target type should be txout_to_key");
|
||||
return null_address;
|
||||
}
|
||||
|
||||
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(height);
|
||||
|
||||
for (const auto& address_blockheight : m_service_nodes_last_reward)
|
||||
{
|
||||
const cryptonote::account_public_address address = address_blockheight.first;
|
||||
const crypto::public_key& pub_spendkey = address.m_spend_public_key;
|
||||
const crypto::public_key& pub_viewkey = address.m_view_public_key;
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
bool r = crypto::generate_key_derivation(pub_viewkey, gov_key.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, null_address, "while creating outs: failed to generate_key_derivation(" << pub_viewkey << ", " << gov_key.sec << ")");
|
||||
r = crypto::derive_public_key(derivation, 1, pub_spendkey, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, null_address, "while creating outs: failed to derive_public_key(" << derivation << ", " << 1 << ", "<< pub_spendkey << ")");
|
||||
|
||||
if (boost::get<cryptonote::txout_to_key>(miner_tx.vout[1].target).key == out_eph_public_key)
|
||||
{
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
return null_address;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void service_node_list::block_added_generic(const cryptonote::block& block, const T& txs)
|
||||
|
@ -334,69 +298,64 @@ namespace service_nodes
|
|||
uint64_t block_height = cryptonote::get_block_height(block);
|
||||
int hard_fork_version = m_blockchain.get_hard_fork_version(block_height);
|
||||
|
||||
if (hard_fork_version < 8)
|
||||
if (hard_fork_version < 9)
|
||||
return;
|
||||
|
||||
assert(block.miner_tx.vout.size() == 3);
|
||||
|
||||
while (!m_rollback_events.empty() && m_rollback_events.front()->m_block_height < block_height - ROLLBACK_EVENT_EXPIRATION_BLOCKS)
|
||||
{
|
||||
m_rollback_events.pop_front();
|
||||
}
|
||||
|
||||
cryptonote::account_public_address winner_address = find_service_node_from_miner_tx(block.miner_tx, block_height);
|
||||
if (m_service_nodes_last_reward.count(winner_address) == 1)
|
||||
crypto::public_key winner_pubkey = cryptonote::get_service_node_winner_from_tx_extra(block.miner_tx.extra);
|
||||
if (m_service_nodes_infos.count(winner_pubkey) == 1)
|
||||
{
|
||||
m_rollback_events.push_back(
|
||||
std::unique_ptr<rollback_event>(
|
||||
new rollback_change(block_height, winner_address, m_service_nodes_last_reward[winner_address], m_service_nodes_keys[winner_address])
|
||||
new rollback_change(block_height, winner_pubkey, m_service_nodes_infos[winner_pubkey])
|
||||
)
|
||||
);
|
||||
m_service_nodes_last_reward[winner_address] = std::pair<uint64_t, size_t>(block_height, 0);
|
||||
// set the winner as though it was re-registering at transaction index=-1 for this block
|
||||
m_service_nodes_infos[winner_pubkey].block_height = 0;
|
||||
m_service_nodes_infos[winner_pubkey].transaction_index = -1;
|
||||
}
|
||||
|
||||
for (const cryptonote::account_public_address address : get_expired_nodes(block_height))
|
||||
for (const crypto::public_key& pubkey : get_expired_nodes(block_height))
|
||||
{
|
||||
auto i = m_service_nodes_last_reward.find(address);
|
||||
if (i != m_service_nodes_last_reward.end())
|
||||
auto i = m_service_nodes_infos.find(pubkey);
|
||||
if (i != m_service_nodes_infos.end())
|
||||
{
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, address, i->second, m_service_nodes_keys[winner_address])));
|
||||
m_service_nodes_last_reward.erase(i);
|
||||
m_service_nodes_keys.erase(address);
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, pubkey, i->second)));
|
||||
m_service_nodes_infos.erase(i);
|
||||
}
|
||||
// Service nodes may expire early if they double staked by accident, so
|
||||
// expiration doesn't mean the node is in the list.
|
||||
}
|
||||
|
||||
size_t index = 1;
|
||||
int index = 0;
|
||||
for (const cryptonote::transaction& tx : txs)
|
||||
{
|
||||
cryptonote::account_public_address address;
|
||||
crypto::public_key key;
|
||||
if (is_registration_tx(tx, block_height, address, key))
|
||||
service_node_info info;
|
||||
if (is_registration_tx(tx, block_height, index, key, info))
|
||||
{
|
||||
auto iter = m_service_nodes_last_reward.find(address);
|
||||
if (iter == m_service_nodes_last_reward.end())
|
||||
auto iter = m_service_nodes_infos.find(key);
|
||||
if (iter == m_service_nodes_infos.end())
|
||||
{
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_new(block_height, address)));
|
||||
m_service_nodes_last_reward[address] = std::pair<uint64_t, size_t>(block_height, index);
|
||||
m_service_nodes_keys[address] = key;
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_new(block_height, key)));
|
||||
m_service_nodes_infos[key] = info;
|
||||
}
|
||||
else
|
||||
{
|
||||
crypto::public_key& service_node_key = m_service_nodes_keys[address];
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, address, iter->second, service_node_key)));
|
||||
iter->second = std::pair<uint64_t, size_t>(block_height, index);
|
||||
service_node_key = key;
|
||||
MDEBUG("Detected stake using an existing service node key, funds were locked for no reward");
|
||||
}
|
||||
}
|
||||
else if (is_deregistration_tx(tx, address))
|
||||
else if (is_deregistration_tx(tx, key))
|
||||
{
|
||||
auto iter = m_service_nodes_last_reward.find(address);
|
||||
if (iter != m_service_nodes_last_reward.end())
|
||||
auto iter = m_service_nodes_infos.find(key);
|
||||
if (iter != m_service_nodes_infos.end())
|
||||
{
|
||||
crypto::public_key& service_node_key = m_service_nodes_keys[address];
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, address, iter->second, service_node_key)));
|
||||
m_service_nodes_last_reward.erase(iter);
|
||||
m_service_nodes_keys.erase(address);
|
||||
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, key, iter->second)));
|
||||
m_service_nodes_infos.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -426,7 +385,7 @@ namespace service_nodes
|
|||
{
|
||||
while (!m_rollback_events.empty() && m_rollback_events.back()->m_block_height >= height)
|
||||
{
|
||||
if (!m_rollback_events.back()->apply(m_service_nodes_last_reward, m_service_nodes_keys))
|
||||
if (!m_rollback_events.back()->apply(m_service_nodes_infos))
|
||||
{
|
||||
init();
|
||||
break;
|
||||
|
@ -440,9 +399,9 @@ namespace service_nodes
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<cryptonote::account_public_address> service_node_list::get_expired_nodes(uint64_t block_height) const
|
||||
std::vector<crypto::public_key> service_node_list::get_expired_nodes(uint64_t block_height) const
|
||||
{
|
||||
std::vector<cryptonote::account_public_address> expired_nodes;
|
||||
std::vector<crypto::public_key> expired_nodes;
|
||||
|
||||
if (block_height < STAKING_REQUIREMENT_LOCK_BLOCKS + STAKING_RELOCK_WINDOW_BLOCKS)
|
||||
return expired_nodes;
|
||||
|
@ -465,75 +424,108 @@ namespace service_nodes
|
|||
return expired_nodes;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (const cryptonote::transaction& tx : txs)
|
||||
{
|
||||
cryptonote::account_public_address address;
|
||||
crypto::public_key unused_key;
|
||||
if (is_registration_tx(tx, expired_nodes_block_height, address, unused_key))
|
||||
crypto::public_key key;
|
||||
service_node_info info;
|
||||
if (is_registration_tx(tx, expired_nodes_block_height, index, key, info))
|
||||
{
|
||||
expired_nodes.push_back(address);
|
||||
expired_nodes.push_back(key);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return expired_nodes;
|
||||
}
|
||||
|
||||
cryptonote::account_public_address service_node_list::select_winner(const crypto::hash& prev_id)
|
||||
std::vector<std::pair<cryptonote::account_public_address, uint32_t>> service_node_list::get_winner_addresses_and_shares(const crypto::hash& prev_id) const
|
||||
{
|
||||
auto lowest_height = std::pair<uint64_t, size_t>(std::numeric_limits<uint64_t>::max(), std::numeric_limits<size_t>::max());
|
||||
cryptonote::account_public_address address = null_address;
|
||||
for (const auto& spendkey_blockheight : m_service_nodes_last_reward)
|
||||
crypto::public_key key = select_winner(prev_id);
|
||||
if (key == crypto::null_pkey)
|
||||
return { std::make_pair(null_address, STAKING_SHARES) };
|
||||
std::vector<std::pair<cryptonote::account_public_address, uint32_t>> winners;
|
||||
for (size_t i = 0; i < m_service_nodes_infos.at(key).addresses.size(); i++)
|
||||
winners.push_back(std::make_pair(m_service_nodes_infos.at(key).addresses[i], m_service_nodes_infos.at(key).shares[i]));
|
||||
return winners;
|
||||
}
|
||||
|
||||
crypto::public_key service_node_list::select_winner(const crypto::hash& prev_id) const
|
||||
{
|
||||
auto oldest_waiting = std::pair<uint64_t, int>(std::numeric_limits<uint64_t>::max(), std::numeric_limits<int>::max());
|
||||
crypto::public_key key = crypto::null_pkey;
|
||||
for (const auto& info : m_service_nodes_infos)
|
||||
{
|
||||
if (spendkey_blockheight.second < lowest_height)
|
||||
auto waiting_since = std::make_pair(info.second.block_height, info.second.transaction_index);
|
||||
if (waiting_since < oldest_waiting)
|
||||
{
|
||||
lowest_height = spendkey_blockheight.second;
|
||||
address = spendkey_blockheight.first;
|
||||
waiting_since = oldest_waiting;
|
||||
key = info.first;
|
||||
}
|
||||
}
|
||||
return address;
|
||||
return key;
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
if (hard_fork_version < 8)
|
||||
if (hard_fork_version < 9)
|
||||
return true;
|
||||
|
||||
uint64_t service_node_reward = cryptonote::get_service_node_reward(height, base_reward, hard_fork_version);
|
||||
uint64_t total_service_node_reward = cryptonote::get_service_node_reward(height, base_reward, hard_fork_version);
|
||||
|
||||
if (miner_tx.vout.size() != 3)
|
||||
{
|
||||
MERROR("Miner TX should have exactly 3 outputs");
|
||||
crypto::public_key winner = select_winner(prev_id);
|
||||
|
||||
crypto::public_key check_winner_pubkey = cryptonote::get_service_node_winner_from_tx_extra(miner_tx.extra);
|
||||
if (check_winner_pubkey != winner)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (miner_tx.vout[1].amount != service_node_reward)
|
||||
{
|
||||
MERROR("Service node reward amount incorrect. Should be " << cryptonote::print_money(service_node_reward) << ", is: " << cryptonote::print_money(miner_tx.vout[1].amount));
|
||||
const std::vector<cryptonote::account_public_address> addresses =
|
||||
winner == crypto::null_pkey
|
||||
? std::vector<cryptonote::account_public_address>{ null_address }
|
||||
: m_service_nodes_infos.at(winner).addresses;
|
||||
|
||||
const std::vector<uint32_t> shares =
|
||||
winner == crypto::null_pkey
|
||||
? std::vector<uint32_t>{ STAKING_SHARES }
|
||||
: m_service_nodes_infos.at(winner).shares;
|
||||
|
||||
if (miner_tx.vout.size() - 1 < addresses.size())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (miner_tx.vout[1].target.type() != typeid(cryptonote::txout_to_key))
|
||||
for (size_t i = 0; i < addresses.size(); i++)
|
||||
{
|
||||
MERROR("Service node output target type should be txout_to_key");
|
||||
return false;
|
||||
}
|
||||
size_t vout_index = miner_tx.vout.size() - 1 /* governance */ - addresses.size() + i;
|
||||
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
cryptonote::account_public_address address = select_winner(prev_id);
|
||||
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(height);
|
||||
uint64_t reward = cryptonote::get_share_of_reward(shares[i], total_service_node_reward);
|
||||
|
||||
bool r = crypto::generate_key_derivation(address.m_view_public_key, gov_key.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << address.m_view_public_key << ", " << gov_key.sec << ")");
|
||||
r = crypto::derive_public_key(derivation, 1, address.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 1 << ", "<< address.m_spend_public_key << ")");
|
||||
if (miner_tx.vout[vout_index].amount != reward)
|
||||
{
|
||||
MERROR("Service node reward amount incorrect. Should be " << cryptonote::print_money(reward) << ", is: " << cryptonote::print_money(miner_tx.vout[vout_index].amount));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (boost::get<cryptonote::txout_to_key>(miner_tx.vout[1].target).key != out_eph_public_key)
|
||||
{
|
||||
MERROR("Invalid service node reward output");
|
||||
return false;
|
||||
if (miner_tx.vout[vout_index].target.type() != typeid(cryptonote::txout_to_key))
|
||||
{
|
||||
MERROR("Service node output target type should be txout_to_key");
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(height);
|
||||
|
||||
bool r = crypto::generate_key_derivation(addresses[i].m_view_public_key, gov_key.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << addresses[i].m_view_public_key << ", " << gov_key.sec << ")");
|
||||
r = crypto::derive_public_key(derivation, vout_index, addresses[i].m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << vout_index << ", "<< addresses[i].m_spend_public_key << ")");
|
||||
|
||||
if (boost::get<cryptonote::txout_to_key>(miner_tx.vout[vout_index].target).key != out_eph_public_key)
|
||||
{
|
||||
MERROR("Invalid service node reward output");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -606,45 +598,31 @@ namespace service_nodes
|
|||
{
|
||||
}
|
||||
|
||||
service_node_list::rollback_change::rollback_change(uint64_t block_height, const cryptonote::account_public_address& address, const std::pair<uint64_t, size_t>& height_index, const crypto::public_key& key)
|
||||
: service_node_list::rollback_event(block_height), m_address(address), m_height_index(height_index), m_key(key)
|
||||
service_node_list::rollback_change::rollback_change(uint64_t block_height, const crypto::public_key& key, const service_node_info& info)
|
||||
: service_node_list::rollback_event(block_height), m_key(key), m_info(info)
|
||||
{
|
||||
}
|
||||
|
||||
bool service_node_list::rollback_change::apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward, std::unordered_map<cryptonote::account_public_address, crypto::public_key>& service_nodes_keys) const
|
||||
bool service_node_list::rollback_change::apply(std::unordered_map<crypto::public_key, service_node_info>& service_nodes_infos) const
|
||||
{
|
||||
auto iter = service_nodes_last_reward.find(m_address);
|
||||
if (iter == service_nodes_last_reward.end())
|
||||
{
|
||||
MERROR("Could not find service node address in rollback change");
|
||||
return false;
|
||||
}
|
||||
service_nodes_last_reward[m_address] = m_height_index;
|
||||
service_nodes_keys[m_address] = m_key;
|
||||
service_nodes_infos[m_key] = m_info;
|
||||
return true;
|
||||
}
|
||||
|
||||
service_node_list::rollback_new::rollback_new(uint64_t block_height, cryptonote::account_public_address address)
|
||||
: service_node_list::rollback_event(block_height), m_address(address)
|
||||
service_node_list::rollback_new::rollback_new(uint64_t block_height, const crypto::public_key& key)
|
||||
: service_node_list::rollback_event(block_height), m_key(key)
|
||||
{
|
||||
}
|
||||
|
||||
bool service_node_list::rollback_new::apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward, std::unordered_map<cryptonote::account_public_address, crypto::public_key>& service_nodes_keys) const
|
||||
bool service_node_list::rollback_new::apply(std::unordered_map<crypto::public_key, service_node_info>& service_nodes_infos) const
|
||||
{
|
||||
auto iter = service_nodes_last_reward.find(m_address);
|
||||
auto iter2 = service_nodes_keys.find(m_address);
|
||||
if (iter == service_nodes_last_reward.end())
|
||||
{
|
||||
MERROR("Could not find service node address in rollback new");
|
||||
return false;
|
||||
}
|
||||
if (iter2 == service_nodes_keys.end())
|
||||
auto iter = service_nodes_infos.find(m_key);
|
||||
if (iter == service_nodes_infos.end())
|
||||
{
|
||||
MERROR("Could not find service node pubkey in rollback new");
|
||||
return false;
|
||||
}
|
||||
service_nodes_last_reward.erase(iter);
|
||||
service_nodes_keys.erase(iter2);
|
||||
service_nodes_infos.erase(iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -652,7 +630,7 @@ namespace service_nodes
|
|||
{
|
||||
}
|
||||
|
||||
bool service_node_list::prevent_rollback::apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward, std::unordered_map<cryptonote::account_public_address, crypto::public_key>& service_nodes_keys) const
|
||||
bool service_node_list::prevent_rollback::apply(std::unordered_map<crypto::public_key, service_node_info>& service_nodes_infos) const
|
||||
{
|
||||
MERROR("Unable to rollback any further!");
|
||||
return false;
|
||||
|
|
|
@ -59,26 +59,44 @@ namespace service_nodes
|
|||
void init();
|
||||
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);
|
||||
|
||||
std::vector<cryptonote::account_public_address> get_expired_nodes(uint64_t block_height) const;
|
||||
cryptonote::account_public_address select_winner(const crypto::hash& prev_id);
|
||||
std::vector<crypto::public_key> get_expired_nodes(uint64_t block_height) const;
|
||||
|
||||
std::vector<std::pair<cryptonote::account_public_address, uint32_t>> get_winner_addresses_and_shares(const crypto::hash& prev_id) const;
|
||||
crypto::public_key select_winner(const crypto::hash& prev_id) const;
|
||||
|
||||
bool is_service_node(const crypto::public_key& pubkey) const;
|
||||
const std::shared_ptr<quorum_state> get_quorum_state(uint64_t height) const;
|
||||
|
||||
private:
|
||||
|
||||
bool is_registration_tx(const cryptonote::transaction& tx, uint64_t block_height, cryptonote::account_public_address& address, crypto::public_key& key) const;
|
||||
bool is_deregistration_tx(const cryptonote::transaction& tx, cryptonote::account_public_address& address) const;
|
||||
struct service_node_info
|
||||
{
|
||||
// block_height and transaction_index are to record when the service node
|
||||
// is registered or when it last received a reward.
|
||||
//
|
||||
// set the winning service node as though it was re-registering at the
|
||||
// block height it wins on, with transaction index=-1
|
||||
// (hence transaction_index is signed)
|
||||
|
||||
uint64_t block_height;
|
||||
int transaction_index;
|
||||
std::vector<cryptonote::account_public_address> addresses;
|
||||
std::vector<uint32_t> shares;
|
||||
};
|
||||
|
||||
bool is_registration_tx(const cryptonote::transaction& tx, uint64_t block_height, int index, crypto::public_key& key, service_node_info& info) const;
|
||||
bool is_deregistration_tx(const cryptonote::transaction& tx, crypto::public_key& address) const;
|
||||
|
||||
std::vector<crypto::public_key> get_service_node_pubkeys() const;
|
||||
|
||||
template<typename T>
|
||||
void block_added_generic(const cryptonote::block& block, const T& txs);
|
||||
|
||||
bool reg_tx_has_correct_unlock_time(const cryptonote::transaction& tx, uint64_t block_height) const;
|
||||
bool reg_tx_extract_fields(const cryptonote::transaction& tx, cryptonote::account_public_address& address, crypto::public_key& service_node_key, crypto::public_key& tx_pub_key) const;
|
||||
bool is_reg_tx_staking_output(const cryptonote::transaction& tx, int i, uint64_t block_height, crypto::key_derivation derivation, hw::device& hwdev) const;
|
||||
bool reg_tx_extract_fields(const cryptonote::transaction& tx, std::vector<cryptonote::account_public_address>& addresses, std::vector<uint32_t>& shares, crypto::public_key& service_node_key, crypto::public_key& tx_pub_key) const;
|
||||
uint64_t get_reg_tx_staking_output_contribution(const cryptonote::transaction& tx, int i, crypto::key_derivation derivation, hw::device& hwdev) const;
|
||||
|
||||
cryptonote::account_public_address find_service_node_from_miner_tx(const cryptonote::transaction& miner_tx, uint64_t block_height) const;
|
||||
crypto::public_key find_service_node_from_miner_tx(const cryptonote::transaction& miner_tx, uint64_t block_height) const;
|
||||
|
||||
void store_quorum_state_from_rewards_list(uint64_t height);
|
||||
|
||||
|
@ -87,43 +105,37 @@ namespace service_nodes
|
|||
public:
|
||||
rollback_event(uint64_t block_height);
|
||||
virtual ~rollback_event() { }
|
||||
virtual bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward, std::unordered_map<cryptonote::account_public_address, crypto::public_key>& service_nodes_keys) const = 0;
|
||||
virtual bool apply(std::unordered_map<crypto::public_key, service_node_info>& service_nodes_infos) const = 0;
|
||||
uint64_t m_block_height;
|
||||
};
|
||||
|
||||
class rollback_change : public rollback_event
|
||||
{
|
||||
public:
|
||||
rollback_change(uint64_t block_height, const cryptonote::account_public_address& address, const std::pair<uint64_t, size_t>& height_index, const crypto::public_key& key);
|
||||
bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward, std::unordered_map<cryptonote::account_public_address, crypto::public_key>& service_nodes_keys) const;
|
||||
rollback_change(uint64_t block_height, const crypto::public_key& key, const service_node_info& info);
|
||||
bool apply(std::unordered_map<crypto::public_key, service_node_info>& service_nodes_infos) const;
|
||||
private:
|
||||
cryptonote::account_public_address m_address;
|
||||
std::pair<uint64_t, size_t> m_height_index;
|
||||
crypto::public_key m_key;
|
||||
service_node_info m_info;
|
||||
};
|
||||
|
||||
class rollback_new : public rollback_event
|
||||
{
|
||||
public:
|
||||
rollback_new(uint64_t block_height, cryptonote::account_public_address address);
|
||||
bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward, std::unordered_map<cryptonote::account_public_address, crypto::public_key>& service_nodes_keys) const;
|
||||
rollback_new(uint64_t block_height, const crypto::public_key& key);
|
||||
bool apply(std::unordered_map<crypto::public_key, service_node_info>& service_nodes_infos) const;
|
||||
private:
|
||||
cryptonote::account_public_address m_address;
|
||||
crypto::public_key m_key;
|
||||
};
|
||||
|
||||
class prevent_rollback : public rollback_event
|
||||
{
|
||||
public:
|
||||
prevent_rollback(uint64_t block_height);
|
||||
bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward, std::unordered_map<cryptonote::account_public_address, crypto::public_key>& service_nodes_keys) const;
|
||||
bool apply(std::unordered_map<crypto::public_key, service_node_info>& service_nodes_infos) const;
|
||||
};
|
||||
|
||||
// Service nodes are organized by time since last reward or registration
|
||||
// This value is given by block height, and differentiated by transaction index for
|
||||
// registrations that occured in the same block. index = 0 for block reward, 1 for first transaction, etc.
|
||||
// hence a std::pair<uint64_t, size_t> is used here for this value.
|
||||
std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>> m_service_nodes_last_reward;
|
||||
std::unordered_map<cryptonote::account_public_address, crypto::public_key> m_service_nodes_keys;
|
||||
std::unordered_map<crypto::public_key, service_node_info> m_service_nodes_infos;
|
||||
std::list<std::unique_ptr<rollback_event>> m_rollback_events;
|
||||
cryptonote::Blockchain& m_blockchain;
|
||||
|
||||
|
|
|
@ -4711,6 +4711,13 @@ bool simple_wallet::stake_all(const std::vector<std::string> &args_)
|
|||
return true;
|
||||
}
|
||||
|
||||
crypto::public_key service_node_key;
|
||||
if (!epee::string_tools::hex_to_pod(local_args[0], service_node_key))
|
||||
{
|
||||
fail_msg_writer() << tr("failed to parse service node pubkey");
|
||||
return true;
|
||||
}
|
||||
|
||||
priority = m_wallet->adjust_priority(priority);
|
||||
|
||||
size_t mixins = DEFAULT_MIX;
|
||||
|
@ -4729,17 +4736,16 @@ bool simple_wallet::stake_all(const std::vector<std::string> &args_)
|
|||
|
||||
cryptonote::account_public_address address = m_wallet->get_address();
|
||||
|
||||
std::vector<cryptonote::account_public_address> addresses = { address };
|
||||
std::vector<uint32_t> shares = { STAKING_SHARES };
|
||||
|
||||
std::vector<uint8_t> extra;
|
||||
|
||||
tx_extra_service_node_register register_;
|
||||
register_.public_view_key = address.m_view_public_key;
|
||||
register_.public_spend_key = address.m_spend_public_key;
|
||||
if (!epee::string_tools::hex_to_pod(local_args[0], register_.service_node_key))
|
||||
if (!add_service_node_register_to_tx_extra(extra, addresses, shares, service_node_key))
|
||||
{
|
||||
fail_msg_writer() << tr("failed to parse service node pubkey");
|
||||
fail_msg_writer() << tr("failed to serialize service node registration tx extra");
|
||||
return true;
|
||||
}
|
||||
add_service_node_register_to_tx_extra(extra, register_);
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
|
||||
|
|
Loading…
Reference in a new issue