mirror of https://github.com/oxen-io/oxen-core.git
Allow state changes if non conflicting states (#718)
* Only dupe check state changes against the latest valid change * Check the service node info for dupe state change * Gate dupe state changes behind HF12 * Actually properly gate dupe state change and revert breaking changes * Use is_decommissioned() to get service node state, change msg log level
This commit is contained in:
parent
5a13bb845e
commit
ca37156c38
|
@ -67,7 +67,7 @@ namespace service_nodes {
|
|||
decommission,
|
||||
recommission,
|
||||
ip_change_penalty,
|
||||
_count
|
||||
_count,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -3186,9 +3186,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
|||
|
||||
if (tx.type == txtype::state_change)
|
||||
{
|
||||
// Check the inputs (votes) of the transaction have not already been
|
||||
// submitted to the blockchain under another transaction using a different
|
||||
// combination of votes.
|
||||
tx_extra_service_node_state_change state_change;
|
||||
if (!get_service_node_state_change_from_tx_extra(tx.extra, state_change, hf_version))
|
||||
{
|
||||
|
@ -3216,51 +3213,77 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
|||
}
|
||||
|
||||
crypto::public_key const &state_change_service_node_pubkey = quorum->workers[state_change.service_node_index];
|
||||
const uint64_t height = state_change.block_height;
|
||||
constexpr size_t num_blocks_to_check = service_nodes::STATE_CHANGE_TX_LIFETIME_IN_BLOCKS;
|
||||
|
||||
std::vector<std::pair<cryptonote::blobdata,block>> blocks;
|
||||
std::vector<cryptonote::blobdata> txs;
|
||||
if (!get_blocks(height, num_blocks_to_check, blocks, txs))
|
||||
if (hf_version >= cryptonote::network_version_12_checkpointing)
|
||||
{
|
||||
MERROR_VER("Failed to get historical blocks to check against previous state changes for de-duplication");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (blobdata const &blob : txs)
|
||||
{
|
||||
transaction existing_tx;
|
||||
if (!parse_and_validate_tx_from_blob(blob, existing_tx))
|
||||
//
|
||||
// NOTE: Query the Service Node List for the in question Service Node the state change is for and disallow if conflicting
|
||||
//
|
||||
std::vector<service_nodes::service_node_pubkey_info> service_node_array = m_service_node_list.get_service_node_list_state({state_change_service_node_pubkey});
|
||||
if (service_node_array.empty())
|
||||
{
|
||||
MERROR_VER("tx could not be validated from blob, possibly corrupt blockchain");
|
||||
continue;
|
||||
LOG_PRINT_L2("Service Node no longer exists on the network, state change can be ignored");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (existing_tx.type != txtype::state_change)
|
||||
continue;
|
||||
|
||||
tx_extra_service_node_state_change existing_state_change;
|
||||
if (!get_service_node_state_change_from_tx_extra(existing_tx.extra, existing_state_change, hf_version))
|
||||
service_nodes::service_node_info const &service_node_info = service_node_array[0].info;
|
||||
if (!service_node_info.can_transition_to_state(state_change.state))
|
||||
{
|
||||
MERROR_VER("could not get service node state change from tx extra, possibly corrupt tx");
|
||||
continue;
|
||||
}
|
||||
|
||||
crypto::public_key existing_state_change_service_node_pubkey;
|
||||
if (!m_service_node_list.get_quorum_pubkey(quorum_type,
|
||||
service_nodes::quorum_group::worker,
|
||||
existing_state_change.block_height,
|
||||
existing_state_change.service_node_index,
|
||||
existing_state_change_service_node_pubkey))
|
||||
continue;
|
||||
|
||||
if (existing_state_change_service_node_pubkey == state_change_service_node_pubkey)
|
||||
{
|
||||
MERROR_VER("Already seen this state change tx (aka double spend)");
|
||||
LOG_PRINT_L2("State change trying to vote Service Node into the same state it already is in, (aka double spend)");
|
||||
tvc.m_double_spend = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check the inputs (votes) of the transaction have not already been
|
||||
// submitted to the blockchain under another transaction using a different
|
||||
// combination of votes.
|
||||
const uint64_t height = state_change.block_height;
|
||||
constexpr size_t num_blocks_to_check = service_nodes::STATE_CHANGE_TX_LIFETIME_IN_BLOCKS;
|
||||
|
||||
std::vector<std::pair<cryptonote::blobdata,block>> blocks;
|
||||
std::vector<cryptonote::blobdata> txs;
|
||||
if (!get_blocks(height, num_blocks_to_check, blocks, txs))
|
||||
{
|
||||
MERROR_VER("Failed to get historical blocks to check against previous state changes for de-duplication");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (blobdata const &blob : txs)
|
||||
{
|
||||
transaction existing_tx;
|
||||
if (!parse_and_validate_tx_from_blob(blob, existing_tx))
|
||||
{
|
||||
MERROR_VER("tx could not be validated from blob, possibly corrupt blockchain");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existing_tx.type != txtype::state_change)
|
||||
continue;
|
||||
|
||||
tx_extra_service_node_state_change existing_state_change;
|
||||
if (!get_service_node_state_change_from_tx_extra(existing_tx.extra, existing_state_change, hf_version))
|
||||
{
|
||||
MERROR_VER("could not get service node state change from tx extra, possibly corrupt tx");
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto existing_quorum = m_service_node_list.get_testing_quorum(quorum_type, existing_state_change.block_height);
|
||||
if (!existing_quorum)
|
||||
{
|
||||
MERROR_VER("Could not get obligations quorum for recent state change tx");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existing_quorum->workers[existing_state_change.service_node_index] == state_change_service_node_pubkey)
|
||||
{
|
||||
MERROR_VER("Already seen this state change tx (aka double spend)");
|
||||
tvc.m_double_spend = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (tx.type == txtype::key_image_unlock)
|
||||
{
|
||||
|
|
|
@ -2168,5 +2168,21 @@ namespace service_nodes
|
|||
cmd = stream.str();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool service_node_info::can_transition_to_state(new_state proposed_state) const
|
||||
{
|
||||
if (is_decommissioned())
|
||||
{
|
||||
return proposed_state != new_state::decommission &&
|
||||
proposed_state != new_state::ip_change_penalty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return proposed_state != new_state::recommission;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,8 @@ namespace service_nodes
|
|||
bool is_fully_funded() const { return total_contributed >= staking_requirement; }
|
||||
bool is_decommissioned() const { return active_since_height < 0; }
|
||||
bool is_active() const { return is_fully_funded() && !is_decommissioned(); }
|
||||
|
||||
bool can_transition_to_state(new_state proposed_state) const;
|
||||
size_t total_num_locked_contributions() const;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
|
|
|
@ -157,19 +157,28 @@ namespace cryptonote
|
|||
continue;
|
||||
}
|
||||
|
||||
crypto::public_key service_node_to_change_in_the_pool;
|
||||
if (service_node_list.get_quorum_pubkey(quorum_type, quorum_group, pool_tx_state_change.block_height, pool_tx_state_change.service_node_index, service_node_to_change_in_the_pool))
|
||||
if (hard_fork_version >= cryptonote::network_version_12_checkpointing)
|
||||
{
|
||||
if (service_node_to_change == service_node_to_change_in_the_pool)
|
||||
crypto::public_key service_node_to_change_in_the_pool;
|
||||
bool specifying_same_service_node = false;
|
||||
if (service_node_list.get_quorum_pubkey(quorum_type, quorum_group, pool_tx_state_change.block_height, pool_tx_state_change.service_node_index, service_node_to_change_in_the_pool))
|
||||
{
|
||||
specifying_same_service_node = (service_node_to_change == service_node_to_change_in_the_pool);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWARNING("Could not resolve the service node public key from the information in a pooled tx state change, falling back to primitive checking method");
|
||||
specifying_same_service_node = (state_change == pool_tx_state_change);
|
||||
}
|
||||
|
||||
if (specifying_same_service_node && pool_tx_state_change.state == state_change.state)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MWARNING("Could not resolve the service node public key from the information in a pooled tx state change, possibly corrupt tx in your blockchain, falling back to primitive checking method");
|
||||
if (state_change == pool_tx_state_change)
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (tx.type == txtype::key_image_unlock)
|
||||
|
|
Loading…
Reference in New Issue