implementation of treasury block reward; various fixes for P2Pool and solo-xmrig miners

This commit is contained in:
Some Random Crypto Guy
2026-03-30 12:50:49 +01:00
parent c0862ea66e
commit c4e7f6e0f0
11 changed files with 56 additions and 77 deletions
+2 -1
View File
@@ -175,7 +175,8 @@ namespace cryptonote
uint64_t seed_height;
crypto::hash seed_hash;
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
crypto::public_key miner_reward_tx_key;
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash, miner_reward_tx_key))
{
LOG_ERROR("Failed to get_block_template(), stopping mining");
m_forced_stop = true;
+1 -1
View File
@@ -47,7 +47,7 @@ namespace cryptonote
struct i_miner_handler
{
virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) = 0;
protected:
~i_miner_handler(){};
};
+1 -1
View File
@@ -323,7 +323,7 @@ namespace config
const uint64_t STAKE_LOCK_PERIOD = 30*24*30;
const uint64_t TREASURY_SAL1_MINT_PERIOD = 30*24*30; // 1 month of blocks
std::string const TREASURY_ADDRESS_CARROT = "SC11sFBPrGmNuT8AiTPUW479BwkdPJwBxdjKEhZ96yDfFg3B4mawgcpE1YfCAa1zwzUiRTMP9eqB54av48ALhzUu1Q5QoPGUfh";
std::string const TREASURY_ADDRESS_CARROT = "SC11ksHLFhy7H1yMk9bUZvADG1Z9ZkR1T5QMknm3RbGBbgdPkyanB2WBb5TER3MsiwJC5BnyoiYs2DBcvAfAm6JQ537iNKdtvm";
std::string const TREASURY_ADDRESS = "SaLvdZR6w1A21sf2Wh6jYEh1wzY4GSbT7RX6FjyPsnLsffWLrzFQeXUXJcmBLRWDzZC2YXeYe5t7qKsnrg9FpmxmEcxPHsEYfqA";
// treasury payout {tx-key, output-key, anchor_enc, view_tag} tuples
+28 -38
View File
@@ -1560,7 +1560,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
treasury_index_in_tx_outputs = index_in_tx_outputs;
}
//validate reward
// Calculate reward being issued
uint64_t money_in_use = 0;
CHECK_AND_ASSERT_MES(b.miner_tx.amount_burnt > 0 || height == 0, false, "invalid tx.amount_burnt for miner_tx");
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt >= money_in_use, false, "miner transaction is overflowed by amount_burnt");
@@ -1576,6 +1576,25 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
}
partial_block_reward = false;
// Make sure the TOTAL REWARD is correct
uint64_t median_weight = m_current_block_cumul_weight_median;
if (!get_block_reward(median_weight, cumulative_block_weight, already_generated_coins, base_reward, version))
{
MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain");
return false;
}
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) << "), cumulative_block_weight " << cumulative_block_weight);
return false;
}
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;
}
// HF-specific additional checks
switch (version) {
case HF_VERSION_BULLETPROOF_PLUS:
case HF_VERSION_ENABLE_N_OUTS:
@@ -1596,12 +1615,6 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
// HF11: block reward split is 60% miner + 25% treasury + 15% staker (amount_burnt)
if (already_generated_coins != 0) {
// Make sure we have the correct number of outputs
if (treasury_payout_exists)
CHECK_AND_ASSERT_MES(b.miner_tx.vout.size() == 3, false, "Incorrect number of miner_tx outputs (expected 3)");
else
CHECK_AND_ASSERT_MES(b.miner_tx.vout.size() == 2, false, "Incorrect number of miner_tx outputs (expected 2)");
// Validate treasury share: one output must equal block_reward * 25 / 100
uint64_t expected_treasury_block_reward = money_in_use * BLOCK_REWARD_TREASURY_PCT / 100;
// Validate staker share: amount_burnt == block_reward * 15 / 100
@@ -1638,12 +1651,14 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
std::vector<crypto::public_key> additional_tx_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx.extra);
bool found_treasury_block_reward = false;
bool found_miner_block_reward = false;
for (size_t i = 0; i < b.miner_tx.vout.size(); i++) {
// Get the output
CHECK_AND_ASSERT_MES(b.miner_tx.vout[i].target.type() == typeid(txout_to_carrot_v1), false, "Output of miner_tx is not txout_to_carrot_V1");
const auto &output = boost::get<txout_to_carrot_v1>(b.miner_tx.vout[i].target);
// Check the output type is SAL1
CHECK_AND_ASSERT_MES(output.asset_type == "SAL1", false, "Output of miner_tx is not SAL1");
// Skip the premine remint
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) continue;
@@ -1677,17 +1692,8 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
found_treasury_block_reward = true;
continue;
}
if (b.miner_tx.vout[i].amount == expected_miner_block_reward) {
std::string miner_out_asset_type;
cryptonote::get_output_asset_type(b.miner_tx.vout[i], miner_out_asset_type);
CHECK_AND_ASSERT_MES(miner_out_asset_type == "SAL1", false,
"miner output has wrong asset_type: expected SAL1, got " << miner_out_asset_type);
found_miner_block_reward = true;
continue;
}
}
CHECK_AND_ASSERT_MES(found_treasury_block_reward, false, "miner_tx missing treasury output with expected amount " << expected_treasury_block_reward);
CHECK_AND_ASSERT_MES(found_miner_block_reward, false, "miner_tx missing miner output with expected amount " << expected_miner_block_reward);
}
break;
default:
@@ -1695,22 +1701,6 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
break;
}
uint64_t median_weight = m_current_block_cumul_weight_median;
if (!get_block_reward(median_weight, cumulative_block_weight, already_generated_coins, base_reward, version))
{
MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain");
return false;
}
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) << "), cumulative_block_weight " << cumulative_block_weight);
return false;
}
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;
}
return true;
}
//------------------------------------------------------------------
@@ -2041,7 +2031,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
// in a lot of places. That flag is not referenced in any of the code
// nor any of the makefiles, howeve. Need to look into whether or not it's
// necessary at all.
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_weight;
@@ -2449,7 +2439,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
uint8_t hf_version = b.major_version;
size_t max_outs = hf_version >= HF_VERSION_ENABLE_TOKENS ? 3 : (hf_version >= 4 ? 1 : 11);
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
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)
@@ -2458,7 +2448,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
#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, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
size_t coinbase_weight = get_transaction_weight(b.miner_tx);
@@ -2510,9 +2500,9 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
return false;
}
//------------------------------------------------------------------
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//------------------------------------------------------------------
bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
+2 -2
View File
@@ -394,8 +394,8 @@ namespace cryptonote
*
* @return true if block template filled in successfully, else false
*/
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
/**
* @brief gets data required to create a block template and start mining on it
+4 -4
View File
@@ -1542,14 +1542,14 @@ namespace cryptonote
notify_txpool_event(tx_blobs, epee::to_span(tx_hashes), epee::to_span(txs), just_broadcasted);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//-----------------------------------------------------------------------------------------------
bool core::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
+2 -2
View File
@@ -231,8 +231,8 @@ namespace cryptonote
*
* @note see Blockchain::create_block_template
*/
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) override;
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) override;
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
/**
* @copydoc Blockchain::get_miner_data
+6 -1
View File
@@ -469,7 +469,7 @@ namespace cryptonote
return treasury_anchor;
}
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, network_type nettype, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, crypto::public_key& miner_reward_tx_key, transaction& tx, network_type nettype, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
// Clear the TX contents
tx.set_null();
@@ -528,6 +528,9 @@ namespace cryptonote
std::vector<carrot::CarrotCoinbaseEnoteV1> enotes(num_enotes);
carrot::get_coinbase_output_proposal_v1(miner_proposal, height, enotes[0]);
// STORE THE MINER TX_PUB_KEY NOW
miner_reward_tx_key = carrot::raw_byte_convert<crypto::public_key>(enotes[0].enote_ephemeral_pubkey);
size_t enote_idx = 1;
// Add the treasury reward enote (25% of block reward) for HF11+
@@ -600,6 +603,8 @@ namespace cryptonote
keypair txkey = keypair::generate(hw::get_device("default"));
add_tx_pub_key_to_extra(tx, txkey.pub);
// STORE THE MINER TX_PUB_KEY NOW
miner_reward_tx_key = txkey.pub;
if(!extra_nonce.empty())
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
return false;
+1 -1
View File
@@ -79,7 +79,7 @@ namespace cryptonote
//---------------------------------------------------------------
carrot::janus_anchor_t get_deterministic_treasury_anchor_from_height(uint64_t height);
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, network_type nettype = network_type::FAKECHAIN, const std::vector<hardfork_t>& hardforks = {}, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, crypto::public_key& miner_reward_tx_key, transaction& tx, network_type nettype = network_type::FAKECHAIN, const std::vector<hardfork_t>& hardforks = {}, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
struct tx_source_entry
{
+2 -1
View File
@@ -149,7 +149,8 @@ void print_genesis_tx_hex(const cryptonote::network_type nettype) {
//Prepare genesis_tx
cryptonote::transaction tx_genesis;
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
crypto::public_key miner_reward_tx_key = crypto::null_pkey;
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, miner_reward_tx_key, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
std::cout << "Object:" << std::endl;
std::cout << obj_to_json_str(tx_genesis) << std::endl << std::endl;
+7 -25
View File
@@ -1871,40 +1871,22 @@ namespace cryptonote
bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
{
b = boost::value_initialized<cryptonote::block>();
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash))
crypto::public_key tx_pub_key = crypto::null_pkey;
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash, tx_pub_key))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to create block template");
return false;
}
blobdata block_blob = t_serializable_object_to_blob(b);
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
const std::vector<crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx);
if(tx_pub_key == crypto::null_pkey)
{
// Check for Carrot treasury payout
const uint8_t hf_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
if (hf_version >= HF_VERSION_CARROT && b.miner_tx.vout.size() == 2) {
const auto treasury_payout_data = get_config(nettype()).TREASURY_SAL1_MINT_OUTPUT_DATA;
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
if (!treasury_payout_exists) {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template (missing treasury payout)";
LOG_ERROR("Failed to get tx pub key in coinbase extra (missing treasury payout)");
return false;
}
tx_pub_key = additional_tx_pub_keys.back();
} else {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
}
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
}
blobdata block_blob = t_serializable_object_to_blob(b);
uint64_t next_height;
crypto::rx_seedheights(height, &seed_height, &next_height);
if (next_height != seed_height)