mirror of https://github.com/oxen-io/oxen-core.git
Make hooks use exceptions on error rather than bool returns
bool returns suck in general, but in most cases here they are also a pain in the ass because *each* place that returns false is also issuing a log statement. If only there were a way to return error information to the common caller to have the common caller handle it... oh wait, there is!
This commit is contained in:
parent
f382c1971e
commit
9c9552380d
|
@ -167,11 +167,11 @@ namespace cryptonote
|
|||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::block_added(const block_added_info& info)
|
||||
void checkpoints::block_added(const block_added_info& info)
|
||||
{
|
||||
uint64_t const height = get_block_height(info.block);
|
||||
if (height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL || info.block.major_version < hf::hf12_checkpointing)
|
||||
return true;
|
||||
return;
|
||||
|
||||
uint64_t end_cull_height = 0;
|
||||
{
|
||||
|
@ -205,8 +205,6 @@ namespace cryptonote
|
|||
|
||||
if (info.checkpoint)
|
||||
update_checkpoint(*info.checkpoint);
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void checkpoints::blockchain_detached(uint64_t height)
|
||||
|
|
|
@ -113,7 +113,7 @@ namespace cryptonote
|
|||
class checkpoints
|
||||
{
|
||||
public:
|
||||
bool block_added(const block_added_info& info);
|
||||
void block_added(const block_added_info& info);
|
||||
void blockchain_detached(uint64_t height);
|
||||
|
||||
bool get_checkpoint(uint64_t height, checkpoint_t &checkpoint) const;
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace cryptonote {
|
|||
const std::vector<transaction>& txs;
|
||||
const checkpoint_t* const checkpoint;
|
||||
};
|
||||
using BlockAddedHook = std::function<bool(const block_added_info& info)>;
|
||||
using BlockAddedHook = std::function<void(const block_added_info& info)>;
|
||||
struct detached_info {
|
||||
uint64_t height;
|
||||
bool by_pop_blocks;
|
||||
|
@ -56,7 +56,7 @@ namespace cryptonote {
|
|||
const block_reward_parts& reward_parts;
|
||||
const std::vector<cryptonote::batch_sn_payment>& batched_sn_payments;
|
||||
};
|
||||
using ValidateMinerTxHook = std::function<bool(const miner_tx_info& info)>;
|
||||
using ValidateMinerTxHook = std::function<void(const miner_tx_info& info)>;
|
||||
|
||||
struct address_parse_info
|
||||
{
|
||||
|
|
|
@ -401,9 +401,10 @@ bool Blockchain::load_missing_blocks_into_oxen_subsystems()
|
|||
if (blk.major_version >= hf::hf13_enforce_checkpoints && get_checkpoint(block_height, checkpoint))
|
||||
checkpoint_ptr = &checkpoint;
|
||||
|
||||
if (!m_service_node_list.block_added(blk, txs, checkpoint_ptr))
|
||||
{
|
||||
MFATAL("Unable to process block for updating service node list: " << cryptonote::get_block_hash(blk));
|
||||
try {
|
||||
m_service_node_list.block_added(blk, txs, checkpoint_ptr);
|
||||
} catch (const std::exception& e) {
|
||||
MFATAL("Unable to process block {} for updating service node list: " << e.what());
|
||||
return false;
|
||||
}
|
||||
snl_iteration_duration += clock::now() - snl_start;
|
||||
|
@ -604,7 +605,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *ons_db, std::shared_ptr<crypton
|
|||
}
|
||||
|
||||
|
||||
hook_block_added([this] (const auto& info) { return m_checkpoints.block_added(info); });
|
||||
hook_block_added([this] (const auto& info) { m_checkpoints.block_added(info); });
|
||||
hook_blockchain_detached([this] (const auto& info) { m_checkpoints.blockchain_detached(info.height); });
|
||||
for (const auto& hook : m_init_hooks)
|
||||
hook();
|
||||
|
@ -1384,8 +1385,12 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
|||
miner_tx_info hook_data{b, reward_parts, batched_sn_payments};
|
||||
for (const auto& hook : m_validate_miner_tx_hooks)
|
||||
{
|
||||
if (!hook(hook_data))
|
||||
try {
|
||||
hook(hook_data);
|
||||
} catch (const std::exception& e) {
|
||||
MGINFO_RED("Miner tx failed validation: " << e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (already_generated_coins != 0 && block_has_governance_output(nettype(), b) && version < hf::hf19_reward_batching)
|
||||
|
@ -2100,8 +2105,12 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
block_added_info hook_data{b, txs, checkpoint};
|
||||
for (const auto& hook : m_alt_block_added_hooks)
|
||||
{
|
||||
if (!hook(hook_data))
|
||||
return false;
|
||||
try {
|
||||
hook(hook_data);
|
||||
} catch (const std::exception& e) {
|
||||
LOG_PRINT_L1("Failed to add alt block: " << e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4522,9 +4531,10 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
|
|||
for (std::pair<transaction, std::string> const &tx_pair : txs)
|
||||
only_txs.push_back(tx_pair.first);
|
||||
|
||||
if (!m_service_node_list.block_added(bl, only_txs, checkpoint))
|
||||
{
|
||||
MGINFO_RED("Failed to add block to Service Node List.");
|
||||
try {
|
||||
m_service_node_list.block_added(bl, only_txs, checkpoint);
|
||||
} catch (const std::exception& e) {
|
||||
MGINFO_RED("Failed to add block to Service Node List: " << e.what());
|
||||
bvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
@ -4551,9 +4561,10 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
|
|||
block_added_info hook_data{bl, only_txs, checkpoint};
|
||||
for (const auto& hook : m_block_added_hooks)
|
||||
{
|
||||
if (!hook(hook_data))
|
||||
{
|
||||
MGINFO_RED("Block added hook signalled failure");
|
||||
try {
|
||||
hook(hook_data);
|
||||
} catch (const std::exception& e) {
|
||||
MGINFO_RED("Block added hook failed with exception: " << e.what());
|
||||
bvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -978,23 +978,26 @@ namespace cryptonote
|
|||
void on_new_tx_from_block(const cryptonote::transaction &tx);
|
||||
|
||||
/**
|
||||
* @brief add a hook for processing new blocks and rollbacks for reorgs
|
||||
* @brief add a hook called during new block handling; should throw to abort adding the block.
|
||||
*/
|
||||
void hook_block_added(BlockAddedHook hook) {
|
||||
m_block_added_hooks.push_back(std::move(hook));
|
||||
}
|
||||
void hook_blockchain_detached(BlockchainDetachedHook hook) {
|
||||
m_blockchain_detached_hooks.push_back(std::move(hook));
|
||||
}
|
||||
void hook_init(InitHook hook) {
|
||||
m_init_hooks.push_back(std::move(hook));
|
||||
}
|
||||
void hook_validate_miner_tx(ValidateMinerTxHook hook) {
|
||||
m_validate_miner_tx_hooks.push_back(std::move(hook));
|
||||
}
|
||||
void hook_alt_block_added(BlockAddedHook hook) {
|
||||
m_alt_block_added_hooks.push_back(std::move(hook));
|
||||
}
|
||||
void hook_block_added(BlockAddedHook hook) { m_block_added_hooks.push_back(std::move(hook)); }
|
||||
/**
|
||||
* @brief add a hook called when blocks are removed from the chain.
|
||||
*/
|
||||
void hook_blockchain_detached(BlockchainDetachedHook hook) { m_blockchain_detached_hooks.push_back(std::move(hook)); }
|
||||
/**
|
||||
* @brief add a hook called during startup and re-initialization
|
||||
*/
|
||||
void hook_init(InitHook hook) { m_init_hooks.push_back(std::move(hook)); }
|
||||
/**
|
||||
* @brief add a hook to be called to validate miner txes; should throw if the miner tx is
|
||||
* invalid.
|
||||
*/
|
||||
void hook_validate_miner_tx(ValidateMinerTxHook hook) { m_validate_miner_tx_hooks.push_back(std::move(hook)); }
|
||||
/**
|
||||
* @brief add a hook to be called when adding an alt-chain block; should throw to abort adding.
|
||||
*/
|
||||
void hook_alt_block_added(BlockAddedHook hook) { m_alt_block_added_hooks.push_back(std::move(hook)); }
|
||||
|
||||
/**
|
||||
* @brief returns the timestamps of the last N blocks
|
||||
|
|
|
@ -768,14 +768,14 @@ namespace cryptonote
|
|||
m_service_node_list.set_quorum_history_storage(command_line::get_arg(vm, arg_store_quorum_history));
|
||||
|
||||
// NOTE: Implicit dependency. Service node list needs to be hooked before checkpoints.
|
||||
m_blockchain_storage.hook_blockchain_detached([this] (const auto& info) { return m_service_node_list.blockchain_detached(info.height); });
|
||||
m_blockchain_storage.hook_blockchain_detached([this] (const auto& info) { m_service_node_list.blockchain_detached(info.height); });
|
||||
m_blockchain_storage.hook_init([this] { m_service_node_list.init(); });
|
||||
m_blockchain_storage.hook_validate_miner_tx([this] (const auto& info) { return m_service_node_list.validate_miner_tx(info); });
|
||||
m_blockchain_storage.hook_alt_block_added([this] (const auto& info) { return m_service_node_list.alt_block_added(info); });
|
||||
m_blockchain_storage.hook_validate_miner_tx([this] (const auto& info) { m_service_node_list.validate_miner_tx(info); });
|
||||
m_blockchain_storage.hook_alt_block_added([this] (const auto& info) { m_service_node_list.alt_block_added(info); });
|
||||
|
||||
// NOTE: There is an implicit dependency on service node lists being hooked first!
|
||||
m_blockchain_storage.hook_init([this] { m_quorum_cop.init(); });
|
||||
m_blockchain_storage.hook_block_added([this] (const auto& info) { return m_quorum_cop.block_added(info.block, info.txs); });
|
||||
m_blockchain_storage.hook_block_added([this] (const auto& info) { m_quorum_cop.block_added(info.block, info.txs); });
|
||||
m_blockchain_storage.hook_blockchain_detached([this] (const auto& info) { m_quorum_cop.blockchain_detached(info.height, info.by_pop_blocks); });
|
||||
}
|
||||
|
||||
|
|
|
@ -1542,12 +1542,12 @@ namespace service_nodes
|
|||
}
|
||||
|
||||
|
||||
bool service_node_list::verify_block(const cryptonote::block &block, bool alt_block, cryptonote::checkpoint_t const *checkpoint)
|
||||
void service_node_list::verify_block(const cryptonote::block &block, bool alt_block, cryptonote::checkpoint_t const *checkpoint)
|
||||
{
|
||||
if (block.major_version < hf::hf9_service_nodes)
|
||||
return true;
|
||||
return;
|
||||
|
||||
std::string_view block_type = alt_block ? "alt block "sv : "block "sv;
|
||||
std::string_view block_type = alt_block ? "alt block"sv : "block"sv;
|
||||
|
||||
//
|
||||
// NOTE: Verify the checkpoint given on this height that locks in a block in the past.
|
||||
|
@ -1558,10 +1558,7 @@ namespace service_nodes
|
|||
std::shared_ptr<const quorum> quorum = get_quorum(quorum_type::checkpointing, checkpoint->height, false, alt_block ? &alt_quorums : nullptr);
|
||||
|
||||
if (!quorum)
|
||||
{
|
||||
MGINFO("Failed to get testing quorum checkpoint for " << block_type << cryptonote::get_block_hash(block));
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Failed to get testing quorum checkpoint for {} {}", block_type, cryptonote::get_block_hash(block))};
|
||||
|
||||
bool failed_checkpoint_verify = !service_nodes::verify_checkpoint(block.major_version, *checkpoint, *quorum);
|
||||
if (alt_block && failed_checkpoint_verify)
|
||||
|
@ -1577,10 +1574,7 @@ namespace service_nodes
|
|||
}
|
||||
|
||||
if (failed_checkpoint_verify)
|
||||
{
|
||||
MGINFO("Service node checkpoint failed verification for " << block_type << cryptonote::get_block_hash(block));
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Service node checkpoint failed verification for {} {}", block_type, cryptonote::get_block_hash(block))};
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1595,10 +1589,9 @@ namespace service_nodes
|
|||
{
|
||||
cryptonote::block prev_block;
|
||||
if (!find_block_in_db(m_blockchain.get_db(), block.prev_id, prev_block))
|
||||
{
|
||||
MGINFO("Alt block " << cryptonote::get_block_hash(block) << " references previous block " << block.prev_id << " not available in DB.");
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format(
|
||||
"Alt block {} references previous block {} not available in DB.",
|
||||
cryptonote::get_block_hash(block), block.prev_id)};
|
||||
|
||||
prev_timestamp = prev_block.timestamp;
|
||||
}
|
||||
|
@ -1609,10 +1602,9 @@ namespace service_nodes
|
|||
}
|
||||
|
||||
if (!pulse::get_round_timings(m_blockchain, height, prev_timestamp, timings))
|
||||
{
|
||||
MGINFO("Failed to query the block data for Pulse timings to validate incoming " << block_type << "at height " << height);
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format(
|
||||
"Failed to query the block data for Pulse timings to validate incoming {} at height {}",
|
||||
block_type, height)};
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1670,18 +1662,20 @@ namespace service_nodes
|
|||
alt_pulse_quorums);
|
||||
}
|
||||
|
||||
return result;
|
||||
if (!result)
|
||||
throw std::runtime_error{fmt::format("Failed to verify block components for incoming {} at height {}",
|
||||
block_type, height)};
|
||||
}
|
||||
|
||||
bool service_node_list::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs, cryptonote::checkpoint_t const *checkpoint)
|
||||
void service_node_list::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs, cryptonote::checkpoint_t const *checkpoint)
|
||||
{
|
||||
if (block.major_version < hf::hf9_service_nodes)
|
||||
return true;
|
||||
return;
|
||||
|
||||
std::lock_guard lock(m_sn_mutex);
|
||||
process_block(block, txs);
|
||||
bool result = verify_block(block, false /*alt_block*/, checkpoint);
|
||||
if (result && cryptonote::block_has_pulse_components(block))
|
||||
verify_block(block, false /*alt_block*/, checkpoint);
|
||||
if (cryptonote::block_has_pulse_components(block))
|
||||
{
|
||||
// NOTE: Only record participation if its a block we recently received.
|
||||
// Otherwise processing blocks in retrospect/re-loading on restart seeds
|
||||
|
@ -1697,10 +1691,8 @@ namespace service_nodes
|
|||
{
|
||||
std::shared_ptr<const quorum> quorum = get_quorum(quorum_type::pulse, block_height, false, nullptr);
|
||||
if (!quorum || quorum->validators.empty())
|
||||
{
|
||||
MFATAL("Unexpected Pulse error " << (quorum ? " quorum was not generated" : " quorum was empty"));
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format(
|
||||
"Unexpected Pulse error: {}", quorum ? " quorum was not generated" : " quorum was empty")};
|
||||
|
||||
for (size_t validator_index = 0; validator_index < service_nodes::PULSE_QUORUM_NUM_VALIDATORS; validator_index++)
|
||||
{
|
||||
|
@ -1710,7 +1702,6 @@ namespace service_nodes
|
|||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void service_node_list::reset_batching_to_latest_height()
|
||||
|
@ -2442,17 +2433,15 @@ namespace service_nodes
|
|||
}
|
||||
|
||||
// NOTE: Verify queued service node coinbase or pulse block producer rewards
|
||||
static bool verify_coinbase_tx_output(cryptonote::transaction const &miner_tx,
|
||||
static void verify_coinbase_tx_output(cryptonote::transaction const &miner_tx,
|
||||
uint64_t height,
|
||||
size_t output_index,
|
||||
cryptonote::account_public_address const &receiver,
|
||||
uint64_t reward)
|
||||
{
|
||||
if (output_index >= miner_tx.vout.size())
|
||||
{
|
||||
MGINFO_RED("Output Index: " << output_index << ", indexes out of bounds in vout array with size: " << miner_tx.vout.size());
|
||||
return false;
|
||||
}
|
||||
throw std::out_of_range{fmt::format("Output Index: {} , indexes out of bounds in vout array with size: ",
|
||||
output_index, miner_tx.vout.size())};
|
||||
|
||||
cryptonote::tx_out const &output = miner_tx.vout[output_index];
|
||||
|
||||
|
@ -2461,16 +2450,10 @@ namespace service_nodes
|
|||
// 1 ULP difference in the reward calculations.
|
||||
// TODO(oxen): eliminate all FP math from reward calculations
|
||||
if (!within_one(output.amount, reward))
|
||||
{
|
||||
MGINFO_RED("Service node reward amount incorrect. Should be " << cryptonote::print_money(reward) << ", is: " << cryptonote::print_money(output.amount));
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Service node reward amount incorrect. Should be {}, is: {}", cryptonote::print_money(reward), cryptonote::print_money(output.amount))};
|
||||
|
||||
if (!std::holds_alternative<cryptonote::txout_to_key>(output.target))
|
||||
{
|
||||
MGINFO_RED("Service node output target type should be txout_to_key");
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{"Service node output target type should be txout_to_key"};
|
||||
|
||||
// NOTE: Loki uses the governance key in the one-time ephemeral key
|
||||
// derivation for both Pulse Block Producer/Queued Service Node Winner rewards
|
||||
|
@ -2478,28 +2461,23 @@ namespace service_nodes
|
|||
crypto::public_key out_eph_public_key{};
|
||||
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(height);
|
||||
|
||||
bool r = crypto::generate_key_derivation(receiver.m_view_public_key, gov_key.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << receiver.m_view_public_key << ", " << gov_key.sec << ")");
|
||||
r = crypto::derive_public_key(derivation, output_index, receiver.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< receiver.m_spend_public_key << ")");
|
||||
if (!crypto::generate_key_derivation(receiver.m_view_public_key, gov_key.sec, derivation))
|
||||
throw std::runtime_error{"Failed to generate key derivation"};
|
||||
if (!crypto::derive_public_key(derivation, output_index, receiver.m_spend_public_key, out_eph_public_key))
|
||||
throw std::runtime_error{"Failed derive public key"};
|
||||
|
||||
if (var::get<cryptonote::txout_to_key>(output.target).key != out_eph_public_key)
|
||||
{
|
||||
MGINFO_RED("Invalid service node reward at output: " << output_index << ", output key, specifies wrong key");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
throw std::runtime_error{fmt::format("Invalid service node reward at output: {}, output key, specifies wrong key", output_index)};
|
||||
}
|
||||
|
||||
bool service_node_list::validate_miner_tx(const cryptonote::miner_tx_info& info) const
|
||||
void service_node_list::validate_miner_tx(const cryptonote::miner_tx_info& info) const
|
||||
{
|
||||
const auto& block = info.block;
|
||||
const auto& reward_parts = info.reward_parts;
|
||||
const auto& batched_sn_payments = info.batched_sn_payments;
|
||||
const auto hf_version = block.major_version;
|
||||
if (hf_version < hf::hf9_service_nodes)
|
||||
return true;
|
||||
return;
|
||||
|
||||
std::lock_guard lock(m_sn_mutex);
|
||||
uint64_t const height = cryptonote::get_block_height(block);
|
||||
|
@ -2514,10 +2492,7 @@ namespace service_nodes
|
|||
{
|
||||
auto const check_block_leader_pubkey = cryptonote::get_service_node_winner_from_tx_extra(miner_tx.extra);
|
||||
if (block_leader.key != check_block_leader_pubkey)
|
||||
{
|
||||
MGINFO_RED("Service node reward winner is incorrect! Expected " << block_leader.key << ", block has " << check_block_leader_pubkey);
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Service node reward winner is incorrect! Expected {}, block has {}", block_leader.key, check_block_leader_pubkey)};
|
||||
}
|
||||
|
||||
enum struct verify_mode
|
||||
|
@ -2539,20 +2514,14 @@ namespace service_nodes
|
|||
std::vector<crypto::hash> entropy = get_pulse_entropy_for_next_block(m_blockchain.get_db(), block.prev_id, block.pulse.round);
|
||||
quorum pulse_quorum = generate_pulse_quorum(m_blockchain.nettype(), block_leader.key, hf_version, m_state.active_service_nodes_infos(), entropy, block.pulse.round);
|
||||
if (!verify_pulse_quorum_sizes(pulse_quorum))
|
||||
{
|
||||
MGINFO_RED("Pulse block received but Pulse has insufficient nodes for quorum, block hash " << cryptonote::get_block_hash(block) << ", height " << height);
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Pulse block received but Pulse has insufficient nodes for quorum, block hash {}, height {}", cryptonote::get_block_hash(block), height)};
|
||||
|
||||
block_producer_key = pulse_quorum.workers[0];
|
||||
mode = (block_producer_key == block_leader.key) ? verify_mode::pulse_block_leader_is_producer
|
||||
: verify_mode::pulse_different_block_producer;
|
||||
|
||||
if (block.pulse.round == 0 && (mode == verify_mode::pulse_different_block_producer))
|
||||
{
|
||||
MGINFO_RED("The block producer in pulse round 0 should be the same node as the block leader: " << block_leader.key << ", actual producer: " << block_producer_key);
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("The block producer in pulse round 0 should be the same node as the block leader: {}, actual producer: {}", block_leader.key, block_producer_key)};
|
||||
}
|
||||
|
||||
// NOTE: Verify miner tx vout composition
|
||||
|
@ -2588,10 +2557,7 @@ namespace service_nodes
|
|||
{
|
||||
auto info_it = m_state.service_nodes_infos.find(block_producer_key);
|
||||
if (info_it == m_state.service_nodes_infos.end())
|
||||
{
|
||||
MGINFO_RED("The pulse block producer for round: " << +block.pulse.round << " is not currently a Service Node: " << block_producer_key);
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("The pulse block producer for round {:d} is not current a Service Node: {}", block.pulse.round, block_producer_key)};
|
||||
|
||||
block_producer = info_it->second;
|
||||
expected_vouts_size = mode == verify_mode::pulse_different_block_producer && reward_parts.miner_fee > 0
|
||||
|
@ -2613,20 +2579,16 @@ namespace service_nodes
|
|||
}
|
||||
|
||||
if (miner_tx.vout.size() != expected_vouts_size)
|
||||
{
|
||||
auto type =
|
||||
mode == verify_mode::miner ? "miner"sv :
|
||||
mode == verify_mode::batched_sn_rewards ? "batch reward"sv :
|
||||
mode == verify_mode::pulse_block_leader_is_producer ? "pulse"sv : "pulse alt round"sv;
|
||||
MGINFO_RED("Expected " << type << " block, the miner TX specifies a different amount of outputs vs the expected: " << expected_vouts_size << ", miner tx outputs: " << miner_tx.vout.size());
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Expected {} block, the miner TX specifies a different amount of outputs vs the expected: {}, miner tx outputs: {}",
|
||||
mode == verify_mode::miner ? "miner"sv :
|
||||
mode == verify_mode::batched_sn_rewards ? "batch reward"sv :
|
||||
mode == verify_mode::pulse_block_leader_is_producer ? "pulse"sv :
|
||||
"pulse alt round"sv,
|
||||
expected_vouts_size,
|
||||
miner_tx.vout.size())};
|
||||
|
||||
if (hf_version >= hf::hf16_pulse && reward_parts.base_miner != 0)
|
||||
{
|
||||
MGINFO_RED("Miner reward is incorrect expected 0 reward, block specified " << cryptonote::print_money(reward_parts.base_miner));
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Miner reward is incorrect expected 0 reward, block specified {}", cryptonote::print_money(reward_parts.base_miner))};
|
||||
|
||||
// NOTE: Verify Coinbase Amounts
|
||||
switch(mode)
|
||||
|
@ -2649,8 +2611,7 @@ namespace service_nodes
|
|||
const auto& payout = block_leader.payouts[i];
|
||||
if (split_rewards[i])
|
||||
{
|
||||
if (!verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]))
|
||||
return false;
|
||||
verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]);
|
||||
vout_index++;
|
||||
}
|
||||
}
|
||||
|
@ -2669,8 +2630,7 @@ namespace service_nodes
|
|||
const auto& payout = block_leader.payouts[i];
|
||||
if (split_rewards[i])
|
||||
{
|
||||
if (!verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]))
|
||||
return false;
|
||||
verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]);
|
||||
vout_index++;
|
||||
}
|
||||
}
|
||||
|
@ -2688,8 +2648,7 @@ namespace service_nodes
|
|||
const auto& payout = block_producer_payouts.payouts[i];
|
||||
if (split_rewards[i])
|
||||
{
|
||||
if (!verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]))
|
||||
return false;
|
||||
verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]);
|
||||
vout_index++;
|
||||
}
|
||||
}
|
||||
|
@ -2701,8 +2660,7 @@ namespace service_nodes
|
|||
const auto& payout = block_leader.payouts[i];
|
||||
if (split_rewards[i])
|
||||
{
|
||||
if (!verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]))
|
||||
return false;
|
||||
verify_coinbase_tx_output(miner_tx, height, vout_index, payout.address, split_rewards[i]);
|
||||
vout_index++;
|
||||
}
|
||||
}
|
||||
|
@ -2726,52 +2684,33 @@ namespace service_nodes
|
|||
const auto& batch_payment = batched_sn_payments[vout_index];
|
||||
|
||||
if (!std::holds_alternative<cryptonote::txout_to_key>(vout.target))
|
||||
{
|
||||
MGINFO_RED("Service node output target type should be txout_to_key");
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{"Service node output target type should be txout_to_key"};
|
||||
|
||||
constexpr uint64_t max_amount = std::numeric_limits<uint64_t>::max() / cryptonote::BATCH_REWARD_FACTOR;
|
||||
if (vout.amount > max_amount)
|
||||
{
|
||||
// We should never actually hit this limit unless someone is trying something nefarious
|
||||
MGINFO_RED("Batched reward payout invalid: exceeds maximum possible payout size");
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{"Batched reward payout invalid: exceeds maximum possible payout size"};
|
||||
|
||||
auto paid_amount = vout.amount * cryptonote::BATCH_REWARD_FACTOR;
|
||||
total_payout_in_vouts += paid_amount;
|
||||
if (paid_amount != batch_payment.amount)
|
||||
{
|
||||
MGINFO_RED(fmt::format("Batched reward payout incorrect: expected {}, not {}", batch_payment.amount, paid_amount));
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Batched reward payout incorrect: expected {}, not {}", batch_payment.amount, paid_amount)};
|
||||
|
||||
crypto::public_key out_eph_public_key{};
|
||||
if (!cryptonote::get_deterministic_output_key(batch_payment.address_info.address, deterministic_keypair, vout_index, out_eph_public_key))
|
||||
{
|
||||
MGINFO_RED("Failed to generate output one-time public key");
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{"Failed to generate output one-time public key"};
|
||||
|
||||
const auto& out_to_key = var::get<cryptonote::txout_to_key>(vout.target);
|
||||
if (tools::view_guts(out_to_key) != tools::view_guts(out_eph_public_key))
|
||||
{
|
||||
MGINFO_RED("Output Ephermeral Public Key does not match (payment to wrong recipient)");
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{"Output Ephermeral Public Key does not match (payment to wrong recipient)"};
|
||||
}
|
||||
if (total_payout_in_vouts != total_payout_in_our_db)
|
||||
{
|
||||
MGINFO_RED(fmt::format("Total service node reward amount incorrect: expected {}, not {}", total_payout_in_our_db, total_payout_in_vouts));
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Total service node reward amount incorrect: expected {}, not {}", total_payout_in_our_db, total_payout_in_vouts)};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool service_node_list::alt_block_added(const cryptonote::block_added_info& info)
|
||||
void service_node_list::alt_block_added(const cryptonote::block_added_info& info)
|
||||
{
|
||||
// NOTE: The premise is to search the main list and the alternative list for
|
||||
// the parent of the block we just received, generate the new Service Node
|
||||
|
@ -2784,14 +2723,14 @@ namespace service_nodes
|
|||
|
||||
auto& block = info.block;
|
||||
if (block.major_version < hf::hf9_service_nodes)
|
||||
return true;
|
||||
return;
|
||||
|
||||
uint64_t block_height = cryptonote::get_block_height(block);
|
||||
state_t const *starting_state = nullptr;
|
||||
crypto::hash const block_hash = get_block_hash(block);
|
||||
|
||||
auto it = m_transient.alt_state.find(block_hash);
|
||||
if (it != m_transient.alt_state.end()) return true; // NOTE: Already processed alt-state for this block
|
||||
if (it != m_transient.alt_state.end()) return; // NOTE: Already processed alt-state for this block
|
||||
|
||||
// NOTE: Check if alt block forks off some historical state on the canonical chain
|
||||
if (!starting_state)
|
||||
|
@ -2809,17 +2748,11 @@ namespace service_nodes
|
|||
}
|
||||
|
||||
if (!starting_state)
|
||||
{
|
||||
LOG_PRINT_L1("Received alt block but couldn't find parent state in historical state");
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{"Received alt block but couldn't find parent state in historical state"};
|
||||
|
||||
if (starting_state->block_hash != block.prev_id)
|
||||
{
|
||||
LOG_PRINT_L1("Unexpected state_t's hash: " << starting_state->block_hash
|
||||
<< ", does not match the block prev hash: " << block.prev_id);
|
||||
return false;
|
||||
}
|
||||
throw std::runtime_error{fmt::format("Unexpected state_t's hash: {}, does not match the block prev hash: {}",
|
||||
starting_state->block_hash, block.prev_id)};
|
||||
|
||||
// NOTE: Generate the next Service Node list state from this Alt block.
|
||||
state_t alt_state = *starting_state;
|
||||
|
@ -2830,7 +2763,7 @@ namespace service_nodes
|
|||
else
|
||||
m_transient.alt_state.emplace(block_hash, std::move(alt_state));
|
||||
|
||||
return verify_block(block, true /*alt_block*/, info.checkpoint);
|
||||
verify_block(block, true /*alt_block*/, info.checkpoint);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -455,15 +455,15 @@ namespace service_nodes
|
|||
service_node_list(const service_node_list &) = delete;
|
||||
service_node_list &operator=(const service_node_list &) = delete;
|
||||
|
||||
bool block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs, const cryptonote::checkpoint_t* checkpoint);
|
||||
void block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs, const cryptonote::checkpoint_t* checkpoint);
|
||||
void reset_batching_to_latest_height();
|
||||
bool state_history_exists(uint64_t height);
|
||||
bool process_batching_rewards(const cryptonote::block& block);
|
||||
bool pop_batching_rewards_block(const cryptonote::block& block);
|
||||
void blockchain_detached(uint64_t height);
|
||||
void init();
|
||||
bool validate_miner_tx(const cryptonote::miner_tx_info& info) const;
|
||||
bool alt_block_added(const cryptonote::block_added_info& info);
|
||||
void validate_miner_tx(const cryptonote::miner_tx_info& info) const;
|
||||
void alt_block_added(const cryptonote::block_added_info& info);
|
||||
payout get_block_leader() const { std::lock_guard lock{m_sn_mutex}; return m_state.get_block_leader(); }
|
||||
bool is_service_node(const crypto::public_key& pubkey, bool require_active = true) const;
|
||||
bool is_key_image_locked(crypto::key_image const &check_image, uint64_t *unlock_height = nullptr, service_node_info::contribution_t *the_locked_contribution = nullptr) const;
|
||||
|
@ -739,7 +739,7 @@ namespace service_nodes
|
|||
void record_pulse_participation(crypto::public_key const &pubkey, uint64_t height, uint8_t round, bool participated);
|
||||
|
||||
// Verify block against Service Node state that has just been called with 'state.update_from_block(block)'.
|
||||
bool verify_block(const cryptonote::block& block, bool alt_block, cryptonote::checkpoint_t const *checkpoint);
|
||||
void verify_block(const cryptonote::block& block, bool alt_block, cryptonote::checkpoint_t const *checkpoint);
|
||||
|
||||
void reset(bool delete_db_entry = false);
|
||||
bool load(uint64_t current_height);
|
||||
|
|
|
@ -510,7 +510,7 @@ namespace service_nodes
|
|||
}
|
||||
}
|
||||
|
||||
bool quorum_cop::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
|
||||
void quorum_cop::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
|
||||
{
|
||||
process_quorums(block);
|
||||
uint64_t const height = cryptonote::get_block_height(block) + 1; // chain height = new top block height + 1
|
||||
|
@ -520,8 +520,6 @@ namespace service_nodes
|
|||
// These feels out of place here because the hook system sucks: TODO replace it with
|
||||
// std::function hooks instead.
|
||||
m_core.update_omq_sns();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_obligations_vote(cryptonote::core &core, const quorum_vote_t& vote, const std::vector<pool_vote_entry>& votes, const quorum& quorum)
|
||||
|
|
|
@ -115,7 +115,7 @@ namespace service_nodes
|
|||
explicit quorum_cop(cryptonote::core& core);
|
||||
|
||||
void init();
|
||||
bool block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs);
|
||||
void block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs);
|
||||
void blockchain_detached(uint64_t height, bool by_pop_blocks);
|
||||
|
||||
void set_votes_relayed (std::vector<quorum_vote_t> const &relayed_votes);
|
||||
|
|
Loading…
Reference in New Issue