Merge pull request #1369 from darcys22/1363-reason-field-deregistrations

1363 reason field deregistrations
This commit is contained in:
Jason Rhinelander 2021-03-28 22:07:49 -03:00 committed by GitHub
commit d524d560c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 213 additions and 54 deletions

View File

@ -774,7 +774,8 @@ namespace cryptonote
return false;
state_change = tx_extra_service_node_state_change{
service_nodes::new_state::deregister, dereg.block_height, dereg.service_node_index, dereg.votes.begin(), dereg.votes.end()};
tx_extra_service_node_state_change::version_t::v0,
service_nodes::new_state::deregister, dereg.block_height, dereg.service_node_index, 0, 0, {dereg.votes.begin(), dereg.votes.end()}};
return true;
}
//---------------------------------------------------------------

View File

@ -76,4 +76,26 @@ tx_extra_oxen_name_system tx_extra_oxen_name_system::make_update(
return result;
}
std::vector<std::string> readable_reasons(uint16_t decomm_reason) {
std::vector<std::string> results;
if (decomm_reason & missed_uptime_proof) results.push_back("Missed Uptime Proofs");
if (decomm_reason & missed_checkpoints) results.push_back("Missed Checkpoints");
if (decomm_reason & missed_pulse_participations) results.push_back("Missed Pulse Participation");
if (decomm_reason & storage_server_unreachable) results.push_back("Storage Server Unreachable");
if (decomm_reason & timestamp_response_unreachable) results.push_back("Unreachable for Timestamp Check");
if (decomm_reason & timesync_status_out_of_sync) results.push_back("Time out of sync");
return results;
}
std::vector<std::string> coded_reasons(uint16_t decomm_reason) {
std::vector<std::string> results;
if (decomm_reason & missed_uptime_proof) results.push_back("uptime");
if (decomm_reason & missed_checkpoints) results.push_back("checkpoints");
if (decomm_reason & missed_pulse_participations) results.push_back("pulse");
if (decomm_reason & storage_server_unreachable) results.push_back("storage");
if (decomm_reason & timestamp_response_unreachable) results.push_back("timecheck");
if (decomm_reason & timesync_status_out_of_sync) results.push_back("timesync");
return results;
}
}

View File

@ -1,21 +1,21 @@
// Copyright (c) 2014-2019, The Monero Project
//
//
// All rights reserved.
//
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@ -25,7 +25,7 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
@ -347,30 +347,91 @@ namespace cryptonote
END_SERIALIZE()
};
enum struct version_t : uint8_t { v0, v4_reasons = 4 };
version_t version;
service_nodes::new_state state;
uint64_t block_height;
uint32_t service_node_index;
std::vector<vote> votes;
uint64_t block_height;
uint32_t service_node_index;
uint16_t reason_consensus_all;
uint16_t reason_consensus_any;
std::vector<vote> votes;
tx_extra_service_node_state_change() = default;
template <typename... VotesArgs>
tx_extra_service_node_state_change(service_nodes::new_state state, uint64_t block_height, uint32_t service_node_index, VotesArgs &&...votes)
: state{state}, block_height{block_height}, service_node_index{service_node_index}, votes{std::forward<VotesArgs>(votes)...} {}
tx_extra_service_node_state_change(
version_t version,
service_nodes::new_state state,
uint64_t block_height,
uint32_t service_node_index,
uint16_t reason_all,
uint16_t reason_any,
std::vector<vote> votes) :
version{version},
state{state},
block_height{block_height},
service_node_index{service_node_index},
reason_consensus_all{reason_all},
reason_consensus_any{reason_any},
votes{std::move(votes)}
{}
// Compares equal if this represents a state change of the same SN (does *not* require equality of stored votes)
bool operator==(const tx_extra_service_node_state_change &sc) const {
return state == sc.state && block_height == sc.block_height && service_node_index == sc.service_node_index;
}
BEGIN_SERIALIZE()
ENUM_FIELD(state, state < service_nodes::new_state::_count);
VARINT_FIELD(block_height);
VARINT_FIELD(service_node_index);
FIELD(votes);
END_SERIALIZE()
template <class Archive>
void serialize_value(Archive& ar) {
// Retrofit a field version in here. Prior to adding reason fields, the first value (in
// binary serialization) was the `state` enum, which had a maximum acceptable value of 3: so
// if we get >= 4, that's a version, and otherwise we're implicitly version 0 (and there is no
// version 1-3).
if (Archive::is_serializer && version >= version_t::v4_reasons) {
field_varint(ar, "version", version);
} else if constexpr (Archive::is_deserializer) {
uint8_t ver;
field_varint(ar, "version", ver, [](auto v) { return v <= 4; });
if (ver < 4) { // Old record, so the "version" we read is actually the state value
version = version_t::v0;
state = static_cast<service_nodes::new_state>(ver);
} else {
version = static_cast<version_t>(ver);
}
}
if (Archive::is_serializer || version >= version_t::v4_reasons) {
field_varint(ar, "state", state, [](auto s) { return s < service_nodes::new_state::_count; });
}
field_varint(ar, "block_height", block_height);
field_varint(ar, "service_node_index", service_node_index);
field(ar, "votes", votes);
if (version >= version_t::v4_reasons)
{
field_varint(ar, "reason_consensus_all", reason_consensus_all);
field_varint(ar, "reason_consensus_any", reason_consensus_any);
}
}
};
// Describes the reason for a service node being decommissioned. Included in demerit votes and the decommission transaction itself.
enum Decommission_Reason : uint16_t {
missed_uptime_proof = 1 << 0,
missed_checkpoints = 1 << 1,
missed_pulse_participations = 1 << 2,
storage_server_unreachable = 1 << 3,
timestamp_response_unreachable = 1 << 4,
timesync_status_out_of_sync = 1 << 5
};
// Returns human-readable reason strings (e.g. "Missed Uptime Proofs") for the given reason bits
std::vector<std::string> readable_reasons(uint16_t decomm_reasons);
// Return reason code strings (e.g. "uptime") for the given reason bits; these are used for RPC
// where we want something in-between a bit field and a human-readable string.
std::vector<std::string> coded_reasons(uint16_t decomm_reasons);
// Pre-Heimdall service node deregistration data; it doesn't carry the state change (it is only
// used for deregistrations), and is stored slightly less efficiently in the tx extra data.
struct tx_extra_service_node_deregister_old

View File

@ -706,6 +706,8 @@ namespace service_nodes
info.active_since_height = -info.active_since_height;
info.last_decommission_height = block_height;
info.last_decommission_reason_consensus_all = state_change.reason_consensus_all;
info.last_decommission_reason_consensus_any = state_change.reason_consensus_any;
info.decommission_count++;
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints) {

View File

@ -210,6 +210,7 @@ namespace service_nodes
v4_noproofs,
v5_pulse_recomm_credit,
v6_reassign_sort_keys,
v7_decommission_reason,
_count
};
@ -271,6 +272,8 @@ namespace service_nodes
uint32_t decommission_count = 0; // How many times this service node has been decommissioned
int64_t active_since_height = 0; // if decommissioned: equal to the *negative* height at which you became active before the decommission
uint64_t last_decommission_height = 0; // The height at which the last (or current!) decommissioning started, or 0 if never decommissioned
uint16_t last_decommission_reason_consensus_all = 0; // The reason which the last (or current!) decommissioning occurred as voted by all SNs, or 0 if never decommissioned
uint16_t last_decommission_reason_consensus_any = 0; // The reason which the last (or current!) decommissioning occurred as voted by any of the SNs, or 0 if never decommissioned
int64_t recommission_credit = DECOMMISSION_INITIAL_CREDIT; // The number of blocks of credit you started with or kept when you were last activated (i.e. as of `active_since_height`)
std::vector<contributor_t> contributors;
uint64_t total_contributed = 0;
@ -331,6 +334,11 @@ namespace service_nodes
VARINT_FIELD(recommission_credit)
FIELD(pulse_sorter)
}
if (version >= version_t::v7_decommission_reason)
{
VARINT_FIELD(last_decommission_reason_consensus_all)
VARINT_FIELD(last_decommission_reason_consensus_any)
}
END_SERIALIZE()
};

View File

@ -369,6 +369,7 @@ namespace service_nodes
bool passed = test_results.passed();
new_state vote_for_state;
uint16_t reason = 0;
if (passed) {
if (info.is_decommissioned()) {
vote_for_state = new_state::recommission;
@ -385,6 +386,12 @@ namespace service_nodes
}
else {
if (!test_results.uptime_proved) reason |= cryptonote::Decommission_Reason::missed_uptime_proof;
if (!test_results.checkpoint_participation) reason |= cryptonote::Decommission_Reason::missed_checkpoints;
if (!test_results.pulse_participation) reason |= cryptonote::Decommission_Reason::missed_pulse_participations;
if (!test_results.storage_server_reachable) reason |= cryptonote::Decommission_Reason::storage_server_unreachable;
if (!test_results.timestamp_participation) reason |= cryptonote::Decommission_Reason::timestamp_response_unreachable;
if (!test_results.timesync_status) reason |= cryptonote::Decommission_Reason::timesync_status_out_of_sync;
int64_t credit = calculate_decommission_credit(info, latest_height);
if (info.is_decommissioned()) {
@ -415,7 +422,7 @@ namespace service_nodes
}
}
quorum_vote_t vote = service_nodes::make_state_change_vote(m_obligations_height, static_cast<uint16_t>(index_in_group), node_index, vote_for_state, my_keys);
quorum_vote_t vote = service_nodes::make_state_change_vote(m_obligations_height, static_cast<uint16_t>(index_in_group), node_index, vote_for_state, reason, my_keys);
cryptonote::vote_verification_context vvc;
if (!handle_vote(vote, vvc))
LOG_ERROR("Failed to add state change vote; reason: " << print_vote_verification_context(vvc, &vote));
@ -551,11 +558,24 @@ namespace service_nodes
return true;
}
cryptonote::tx_extra_service_node_state_change state_change{vote.state_change.state, vote.block_height, vote.state_change.worker_index};
using version_t = cryptonote::tx_extra_service_node_state_change::version_t;
auto ver = hf_version >= HF_VERSION_PROOF_BTENC ? version_t::v4_reasons : version_t::v0;
cryptonote::tx_extra_service_node_state_change state_change{
ver,
vote.state_change.state,
vote.block_height,
vote.state_change.worker_index,
vote.state_change.reason,
vote.state_change.reason,
{}};
state_change.votes.reserve(votes.size());
for (const auto &pool_vote : votes)
{
state_change.reason_consensus_any |= pool_vote.vote.state_change.reason;
state_change.reason_consensus_all &= pool_vote.vote.state_change.reason;
state_change.votes.emplace_back(pool_vote.vote.signature, pool_vote.vote.index_in_group);
}
cryptonote::transaction state_change_tx{};
if (cryptonote::add_service_node_state_change_to_tx_extra(state_change_tx.extra, state_change, hf_version))
@ -564,9 +584,7 @@ namespace service_nodes
state_change_tx.type = cryptonote::txtype::state_change;
cryptonote::tx_verification_context tvc{};
cryptonote::blobdata const tx_blob = cryptonote::tx_to_blob(state_change_tx);
bool result = core.handle_incoming_tx(tx_blob, tvc, cryptonote::tx_pool_options::new_tx());
bool result = core.handle_incoming_tx(cryptonote::tx_to_blob(state_change_tx), tvc, cryptonote::tx_pool_options::new_tx());
if (!result || tvc.m_verifivation_failed)
{
LOG_PRINT_L1("A full state change tx for height: " << vote.block_height <<

View File

@ -378,7 +378,7 @@ namespace service_nodes
return true;
}
quorum_vote_t make_state_change_vote(uint64_t block_height, uint16_t validator_index, uint16_t worker_index, new_state state, const service_node_keys &keys)
quorum_vote_t make_state_change_vote(uint64_t block_height, uint16_t validator_index, uint16_t worker_index, new_state state, uint16_t reason, const service_node_keys &keys)
{
quorum_vote_t result = {};
result.type = quorum_type::obligations;
@ -387,6 +387,7 @@ namespace service_nodes
result.index_in_group = validator_index;
result.state_change.worker_index = worker_index;
result.state_change.state = state;
result.state_change.reason = reason;
result.signature = make_signature_from_vote(result, keys);
return result;
}

View File

@ -52,7 +52,7 @@ namespace service_nodes
struct quorum;
struct checkpoint_vote { crypto::hash block_hash; };
struct state_change_vote { uint16_t worker_index; new_state state; };
struct state_change_vote { uint16_t worker_index; new_state state; uint16_t reason;};
enum struct quorum_type : uint8_t
{
@ -101,7 +101,7 @@ namespace service_nodes
struct service_node_keys;
quorum_vote_t make_state_change_vote(uint64_t block_height, uint16_t index_in_group, uint16_t worker_index, new_state state, const service_node_keys &keys);
quorum_vote_t make_state_change_vote(uint64_t block_height, uint16_t index_in_group, uint16_t worker_index, new_state state, uint16_t reason, const service_node_keys &keys);
quorum_vote_t make_checkpointing_vote(uint8_t hf_version, crypto::hash const &block_hash, uint64_t block_height, uint16_t index_in_quorum, const service_node_keys &keys);
cryptonote::checkpoint_t make_empty_service_node_checkpoint(crypto::hash const &block_hash, uint64_t height);

View File

@ -458,6 +458,7 @@ bt_dict serialize_vote(const quorum_vote_t &vote) {
else {
result["wi"] = vote.state_change.worker_index;
result["sc"] = static_cast<std::underlying_type_t<new_state>>(vote.state_change.state);
result["re"] = static_cast<uint16_t>(vote.state_change.reason);
}
return result;
}
@ -481,6 +482,7 @@ quorum_vote_t deserialize_vote(std::string_view v) {
} else {
vote.state_change.worker_index = get_int<uint16_t>(d.at("wi"));
vote.state_change.state = get_enum<new_state>(d, "sc");
vote.state_change.reason = get_int<uint16_t>(d.at("re"));
}
return vote;

View File

@ -469,6 +469,7 @@ bool rpc_command_executor::show_status() {
int64_t my_decomm_remaining = 0;
uint64_t my_sn_last_uptime = 0;
bool my_sn_registered = false, my_sn_staked = false, my_sn_active = false;
uint16_t my_reason_all = 0, my_reason_any = 0;
if (ires.service_node && *ires.service_node) {
GET_SERVICE_KEYS::response res{};
@ -488,6 +489,8 @@ bool rpc_command_executor::show_status() {
my_sn_active = entry.active;
my_decomm_remaining = entry.earned_downtime_blocks;
my_sn_last_uptime = entry.last_uptime_proof;
my_reason_all = entry.last_decommission_reason_consensus_all;
my_reason_any = entry.last_decommission_reason_consensus_any;
}
}
@ -567,6 +570,18 @@ bool rpc_command_executor::show_status() {
str << " (lokinet)";
tools::success_msg_writer() << str.str();
if (my_sn_registered && my_sn_staked && !my_sn_active && (my_reason_all | my_reason_any)) {
str.str("Decomm reasons: ");
if (auto reasons = cryptonote::readable_reasons(my_reason_all); !reasons.empty())
str << tools::join(", ", reasons);
if (auto reasons = cryptonote::readable_reasons(my_reason_any & ~my_reason_all); !reasons.empty()) {
for (auto& r : reasons)
r += "(some)";
str << (my_reason_all ? ", " : "") << tools::join(", ", reasons);
}
tools::fail_msg_writer() << str.str();
}
}
return true;
@ -1694,7 +1709,11 @@ static void append_printable_service_node_list_entry(cryptonote::network_type ne
if (entry.earned_downtime_blocks < service_nodes::DECOMMISSION_MINIMUM)
stream << " (Note: " << service_nodes::DECOMMISSION_MINIMUM << " blocks required to enable deregistration delay)";
} else {
stream << "Current Status: DECOMMISSIONED\n";
stream << "Current Status: DECOMMISSIONED - " ;
auto reason = cryptonote::readable_reasons(entry.last_decommission_reason_consensus_all);
for (auto i = reason.begin(); i != reason.end(); ++i)
stream << *i << ", ";
stream << "\n";
stream << indent2 << "Remaining Decommission Time Until DEREGISTRATION: " << entry.earned_downtime_blocks << " blocks";
}
stream << "\n";

View File

@ -753,6 +753,11 @@ namespace cryptonote { namespace rpc {
}
void operator()(const tx_extra_service_node_state_change& x) {
auto& sc = _state_change(x);
if (x.reason_consensus_all)
sc.reasons = cryptonote::coded_reasons(x.reason_consensus_all);
// If `any` has reasons not included in all then list the extra ones separately:
if (uint16_t reasons_maybe = x.reason_consensus_any & ~x.reason_consensus_all)
sc.reasons_maybe = cryptonote::coded_reasons(reasons_maybe);
switch (x.state)
{
case service_nodes::new_state::decommission: sc.type = "decom"; break;
@ -3037,6 +3042,8 @@ namespace cryptonote { namespace rpc {
? (info.is_decommissioned() ? info.last_decommission_height : info.active_since_height) : info.last_reward_block_height;
entry.earned_downtime_blocks = service_nodes::quorum_cop::calculate_decommission_credit(info, current_height);
entry.decommission_count = info.decommission_count;
entry.last_decommission_reason_consensus_all = info.last_decommission_reason_consensus_all;
entry.last_decommission_reason_consensus_any = info.last_decommission_reason_consensus_any;
m_core.get_service_node_list().access_proof(sn_info.pubkey, [&entry](const auto &proof) {
entry.service_node_version = proof.proof->version;

View File

@ -108,6 +108,8 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::state_change)
KV_SERIALIZE(height)
KV_SERIALIZE(index)
KV_SERIALIZE(voters)
KV_SERIALIZE(reasons);
KV_SERIALIZE(reasons_maybe);
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::lns_details)
KV_SERIALIZE(buy)

View File

@ -296,6 +296,8 @@ namespace rpc {
uint64_t height; // The voting block height for the changing service node and validators
uint32_t index; // The index of all tested nodes at the given height for which this state change applies
std::vector<uint32_t> voters; // The position of validators in the testing quorum who validated and voted for this state change. This typically contains just 7 required voter slots (of 10 eligible voters).
std::optional<std::vector<std::string>> reasons; // Reasons for the decommissioning/deregistration as reported by the voting quorum. This contains any reasons that all voters agreed on, one or more of: "uptime" (missing uptime proofs), "checkpoints" (missed checkpoint votes), "pulse" (missing pulse votes), "storage" (storage server pings failed), "timecheck" (time sync pings failed), "timesync" (time was out of sync)
std::optional<std::vector<std::string>> reasons_maybe; // If present, this contains any decomm/dereg reasons that were given by some but not all quorum voters
KV_MAP_SERIALIZABLE
};
struct lns_details
@ -2049,6 +2051,8 @@ namespace rpc {
bool funded;
bool state_height;
bool decommission_count;
bool last_decommission_reason_consensus_all;
bool last_decommission_reason_consensus_any;
bool earned_downtime_blocks;
bool service_node_version;
@ -2108,6 +2112,8 @@ namespace rpc {
bool funded; // True if the required stakes have been submitted to activate this Service Node
uint64_t state_height; // If active: the state at which the service node became active (i.e. fully staked height, or last recommissioning); if decommissioned: the decommissioning height; if awaiting: the last contribution (or registration) height
uint32_t decommission_count; // The number of times the Service Node has been decommissioned since registration
uint16_t last_decommission_reason_consensus_all; // The reason for the last decommission as voted by all SNs
uint16_t last_decommission_reason_consensus_any; // The reason for the last decommission as voted by any SNs
int64_t earned_downtime_blocks; // The number of blocks earned towards decommissioning, or the number of blocks remaining until deregistration if currently decommissioned
std::array<uint16_t, 3> service_node_version; // The major, minor, patch version of the Service Node respectively.
std::array<uint16_t, 3> lokinet_version; // The major, minor, patch version of the Service Node's lokinet router.

View File

@ -397,9 +397,9 @@ cryptonote::transaction oxen_chain_generator::create_and_add_big_tx(
return t;
}
cryptonote::transaction oxen_chain_generator::create_and_add_state_change_tx(service_nodes::new_state state, const crypto::public_key &pub_key, uint64_t height, const std::vector<uint64_t> &voters, uint64_t fee, bool kept_by_block)
cryptonote::transaction oxen_chain_generator::create_and_add_state_change_tx(service_nodes::new_state state, const crypto::public_key &pub_key, uint16_t reasons_all, uint16_t reasons_any, uint64_t height, const std::vector<uint64_t> &voters, uint64_t fee, bool kept_by_block)
{
cryptonote::transaction result = create_state_change_tx(state, pub_key, height, voters, fee);
cryptonote::transaction result = create_state_change_tx(state, pub_key, reasons_all, reasons_any, height, voters, fee);
add_tx(result, true /*can_be_added_to_blockchain*/, "" /*fail_msg*/, kept_by_block);
return result;
}
@ -517,11 +517,13 @@ cryptonote::transaction oxen_chain_generator::create_staking_tx(const crypto::pu
return result;
}
cryptonote::transaction oxen_chain_generator::create_state_change_tx(service_nodes::new_state state, const crypto::public_key &pub_key, uint64_t height, const std::vector<uint64_t>& voters, uint64_t fee) const
cryptonote::transaction oxen_chain_generator::create_state_change_tx(service_nodes::new_state state, const crypto::public_key &pub_key, uint16_t reasons_all, uint16_t reasons_any, uint64_t height, const std::vector<uint64_t>& voters, uint64_t fee) const
{
if (height == UINT64_MAX)
height = this->height();
auto hf_version = get_hf_version_at(height + 1);
service_nodes::quorum_manager const &quorums = quorum(height);
std::vector<crypto::public_key> const &validator_service_nodes = quorums.obligations->validators;
std::vector<crypto::public_key> const &worker_service_nodes = quorums.obligations->workers;
@ -534,13 +536,16 @@ cryptonote::transaction oxen_chain_generator::create_state_change_tx(service_nod
}
assert(worker_index < worker_service_nodes.size());
cryptonote::tx_extra_service_node_state_change state_change_extra(state, height, worker_index);
using scver = cryptonote::tx_extra_service_node_state_change::version_t;
cryptonote::tx_extra_service_node_state_change state_change_extra(
hf_version >= cryptonote::network_version_18 ? scver::v4_reasons : scver::v0,
state, height, worker_index, reasons_all, reasons_any, {});
if (voters.size())
{
for (const auto voter_index : voters)
{
auto voter_keys = get_cached_keys(validator_service_nodes[voter_index]);
service_nodes::quorum_vote_t vote = service_nodes::make_state_change_vote(state_change_extra.block_height, voter_index, state_change_extra.service_node_index, state, voter_keys);
service_nodes::quorum_vote_t vote = service_nodes::make_state_change_vote(state_change_extra.block_height, voter_index, state_change_extra.service_node_index, state, 0, voter_keys);
state_change_extra.votes.push_back({vote.signature, (uint32_t)voter_index});
}
}
@ -550,7 +555,7 @@ cryptonote::transaction oxen_chain_generator::create_state_change_tx(service_nod
{
auto voter_keys = get_cached_keys(validator_service_nodes[i]);
service_nodes::quorum_vote_t vote = service_nodes::make_state_change_vote(state_change_extra.block_height, i, state_change_extra.service_node_index, state, voter_keys);
service_nodes::quorum_vote_t vote = service_nodes::make_state_change_vote(state_change_extra.block_height, i, state_change_extra.service_node_index, state, 0, voter_keys);
state_change_extra.votes.push_back({vote.signature, (uint32_t)i});
}
}
@ -558,13 +563,18 @@ cryptonote::transaction oxen_chain_generator::create_state_change_tx(service_nod
cryptonote::transaction result;
{
std::vector<uint8_t> extra;
const bool full_tx_made = cryptonote::add_service_node_state_change_to_tx_extra(result.extra, state_change_extra, get_hf_version_at(height + 1));
const bool full_tx_made = cryptonote::add_service_node_state_change_to_tx_extra(result.extra, state_change_extra, hf_version);
assert(full_tx_made);
if (fee) oxen_tx_builder(events_, result, top().block, first_miner_, first_miner_.get_keys().m_account_address, 0 /*amount*/, get_hf_version_at(height + 1)).with_tx_type(cryptonote::txtype::state_change).with_fee(fee).with_extra(extra).build();
if (fee)
oxen_tx_builder(events_, result, top().block, first_miner_, first_miner_.get_keys().m_account_address, 0 /*amount*/, hf_version)
.with_tx_type(cryptonote::txtype::state_change)
.with_fee(fee)
.with_extra(extra)
.build();
else
{
result.type = cryptonote::txtype::state_change;
result.version = cryptonote::transaction::get_max_version_for_hf(get_hf_version_at(height + 1));
result.version = cryptonote::transaction::get_max_version_for_hf(hf_version);
}
}

View File

@ -1498,7 +1498,7 @@ struct oxen_chain_generator
cryptonote::transaction create_and_add_oxen_name_system_tx_update(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, lns::mapping_value const *value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, lns::generic_signature *signature = nullptr, bool kept_by_block = false);
cryptonote::transaction create_and_add_oxen_name_system_tx_renew(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, bool kept_by_block = false);
cryptonote::transaction create_and_add_tx (const cryptonote::account_base& src, const cryptonote::account_public_address& dest, uint64_t amount, uint64_t fee = TESTS_DEFAULT_FEE, bool kept_by_block = false);
cryptonote::transaction create_and_add_state_change_tx(service_nodes::new_state state, const crypto::public_key& pub_key, uint64_t height = -1, const std::vector<uint64_t>& voters = {}, uint64_t fee = 0, bool kept_by_block = false);
cryptonote::transaction create_and_add_state_change_tx(service_nodes::new_state state, const crypto::public_key& pub_key, uint16_t reasons_all, uint16_t reasons_any, uint64_t height = -1, const std::vector<uint64_t>& voters = {}, uint64_t fee = 0, bool kept_by_block = false);
cryptonote::transaction create_and_add_registration_tx(const cryptonote::account_base& src, const cryptonote::keypair& sn_keys = cryptonote::keypair{hw::get_device("default")}, bool kept_by_block = false);
cryptonote::transaction create_and_add_staking_tx (const crypto::public_key &pub_key, const cryptonote::account_base &src, uint64_t amount, bool kept_by_block = false);
oxen_blockchain_entry &create_and_add_next_block (const std::vector<cryptonote::transaction>& txs = {}, cryptonote::checkpoint_t const *checkpoint = nullptr, bool can_be_added_to_blockchain = true, std::string const &fail_msg = {});
@ -1514,7 +1514,7 @@ struct oxen_chain_generator
std::array<oxen_service_node_contribution, 3> const &contributors = {},
int num_contributors = 0) const;
cryptonote::transaction create_staking_tx (const crypto::public_key& pub_key, const cryptonote::account_base &src, uint64_t amount) const;
cryptonote::transaction create_state_change_tx(service_nodes::new_state state, const crypto::public_key& pub_key, uint64_t height = -1, const std::vector<uint64_t>& voters = {}, uint64_t fee = 0) const;
cryptonote::transaction create_state_change_tx(service_nodes::new_state state, const crypto::public_key& pub_key, uint16_t reasons_all, uint16_t reasons_any, uint64_t height = -1, const std::vector<uint64_t>& voters = {}, uint64_t fee = 0) const;
cryptonote::checkpoint_t create_service_node_checkpoint(uint64_t block_height, size_t num_votes) const;
// value: Takes the binary value NOT the human readable version, of the name->value mapping

View File

@ -791,8 +791,8 @@ bool oxen_core_test_deregister_preferred::generate(std::vector<test_event_entry>
/// generate two deregisters
const auto deregister_pub_key_1 = gen.top_quorum().obligations->workers[0];
const auto deregister_pub_key_2 = gen.top_quorum().obligations->workers[1];
gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key_1);
gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key_2);
gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key_1, 0, 0);
gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key_2, 0, 0);
oxen_register_callback(events, "check_prefer_deregisters", [&events, miner](cryptonote::core &c, size_t ev_index)
{
@ -857,7 +857,7 @@ bool oxen_core_test_deregister_safety_buffer::generate(std::vector<test_event_en
const auto deregister_pub_key = quorum_intersection[0];
{
const auto dereg_tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key, height_a);
const auto dereg_tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key, 0, 0, height_a);
gen.create_and_add_next_block({dereg_tx});
}
@ -870,7 +870,7 @@ bool oxen_core_test_deregister_safety_buffer::generate(std::vector<test_event_en
}
/// Try to deregister the node again for heightB (should fail)
const auto dereg_tx = gen.create_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key, height_b);
const auto dereg_tx = gen.create_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key, 0, 0, height_b);
gen.add_tx(dereg_tx, false /*can_be_added_to_blockchain*/, "After a Service Node has deregistered, it can NOT be deregistered from the result of a quorum preceeding the height that the Service Node re-registered as.");
return true;
@ -891,7 +891,7 @@ bool oxen_core_test_deregister_too_old::generate(std::vector<test_event_entry>&
gen.add_n_blocks(1);
const auto pk = gen.top_quorum().obligations->workers[0];
const auto dereg_tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk);
const auto dereg_tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, 0, 0);
gen.add_n_blocks(service_nodes::STATE_CHANGE_TX_LIFETIME_IN_BLOCKS); /// create enough blocks to make deregistrations invalid (60 blocks)
/// In the real world, this transaction should not make it into a block, but in this case we do try to add it (as in
@ -920,7 +920,7 @@ bool oxen_core_test_deregister_zero_fee::generate(std::vector<test_event_entry>
gen.create_and_add_next_block(reg_txs);
const auto deregister_pub_key = gen.top_quorum().obligations->workers[0];
cryptonote::transaction const invalid_deregister =
gen.create_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key, -1 /*height*/, {} /*voters*/, MK_COINS(1) /*fee*/);
gen.create_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key, 0, 0, -1 /*height*/, {} /*voters*/, MK_COINS(1) /*fee*/);
gen.add_tx(invalid_deregister, false /*can_be_added_to_blockchain*/, "Deregister transactions with non-zero fee can NOT be added to the blockchain");
return true;
}
@ -947,11 +947,11 @@ bool oxen_core_test_deregister_on_split::generate(std::vector<test_event_entry>
gen.add_event_msg("create deregistration A");
std::vector<uint64_t> const quorum_indexes = {1, 2, 3, 4, 5, 6, 7};
const auto dereg_a = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, split_height, quorum_indexes);
const auto dereg_a = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, 0, 0, split_height, quorum_indexes);
gen.add_event_msg("create deregistration on alt chain (B)");
std::vector<uint64_t> const fork_quorum_indexes = {1, 3, 4, 5, 6, 7, 8};
const auto dereg_b = fork.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, split_height, fork_quorum_indexes, 0 /*fee*/, true /*kept_by_block*/);
const auto dereg_b = fork.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, 0, 0, split_height, fork_quorum_indexes, 0 /*fee*/, true /*kept_by_block*/);
crypto::hash expected_tx_hash = cryptonote::get_transaction_hash(dereg_b);
size_t dereg_index = gen.event_index();
@ -1002,12 +1002,12 @@ bool oxen_core_test_state_change_ip_penalty_disallow_dupes::generate(std::vector
const auto pub_key = gen.top_quorum().obligations->workers[0];
std::vector<uint64_t> const quorum_indexes = {1, 2, 3, 4, 5, 6, 7};
const auto state_change_1 = gen.create_and_add_state_change_tx(service_nodes::new_state::ip_change_penalty, pub_key, gen.height(), quorum_indexes);
const auto state_change_1 = gen.create_and_add_state_change_tx(service_nodes::new_state::ip_change_penalty, pub_key, 0, 0, gen.height(), quorum_indexes);
// NOTE: Try duplicate state change with different quorum indexes
{
std::vector<uint64_t> const alt_quorum_indexes = {1, 3, 4, 5, 6, 7, 8};
const auto state_change_2 = gen.create_state_change_tx(service_nodes::new_state::ip_change_penalty, pub_key, gen.height(), alt_quorum_indexes);
const auto state_change_2 = gen.create_state_change_tx(service_nodes::new_state::ip_change_penalty, pub_key, 0, 0, gen.height(), alt_quorum_indexes);
gen.add_tx(state_change_2, false /*can_be_added_to_blockchain*/, "Can't add a state change with different permutation of votes than previously submitted");
// NOTE: Try same duplicate state change on a new height
@ -2768,7 +2768,7 @@ bool oxen_service_nodes_test_rollback::generate(std::vector<test_event_entry>& e
// deregister some node (A) on main
const auto pk = gen.top_quorum().obligations->workers[0];
const auto dereg_tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk);
const auto dereg_tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, 0, 0);
size_t deregister_index = gen.event_index();
gen.create_and_add_next_block({dereg_tx});
@ -2914,7 +2914,7 @@ bool oxen_service_nodes_test_swarms_basic::generate(std::vector<test_event_entry
for (size_t i = 0; i < excess; ++i)
{
const auto pk = top_quorum.obligations->workers[i];
const auto tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, cryptonote::get_block_height(gen.top().block));
const auto tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, 0, 0, cryptonote::get_block_height(gen.top().block));
dereg_txs.push_back(tx);
}
@ -2937,7 +2937,7 @@ bool oxen_service_nodes_test_swarms_basic::generate(std::vector<test_event_entry
dereg_txs.clear();
{
const auto pk = gen.top_quorum().obligations->workers[0];
const auto tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk);
const auto tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk, 0, 0);
dereg_txs.push_back(tx);
}
gen.create_and_add_next_block(dereg_txs);
@ -3204,7 +3204,7 @@ bool oxen_pulse_fallback_to_pow_and_back::generate(std::vector<test_event_entry>
{
const auto deregister_pub_key_1 = gen.top_quorum().obligations->workers[0];
cryptonote::transaction tx =
gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key_1);
gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, deregister_pub_key_1, 0, 0);
gen.create_and_add_next_block({tx});
}

View File

@ -160,7 +160,7 @@ TEST(service_nodes, vote_validation)
// Valid vote
uint64_t block_height = 70;
service_nodes::quorum_vote_t valid_vote = service_nodes::make_state_change_vote(block_height, voter_index, 1 /*worker_index*/, service_nodes::new_state::decommission, voter_keys);
service_nodes::quorum_vote_t valid_vote = service_nodes::make_state_change_vote(block_height, voter_index, 1 /*worker_index*/, service_nodes::new_state::decommission,0, voter_keys);
{
cryptonote::vote_verification_context vvc = {};
bool result = verify_vote(valid_vote, block_height, vvc, state);