Skip to content
Merged
20 changes: 20 additions & 0 deletions doc/release-notes-7807.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Consensus (v24, not yet activated on mainnet or testnet)
---------------------------------------------------------

- EvoNodes cannot register with shared collateral or have more than one owner payout until
the new `evo_shares` deployment activates. An EvoNode ProRegTx with a share table is
rejected with `bad-protx-shares-evo`, and an EvoNode ProRegTx or ProUpRegTx with more than
one owner payout is rejected with `bad-protx-payouts-evo`. Regular masternodes are not
affected.
- `evo_shares` (version bit 14) is a masternode-activated deployment with the same parameters
as `v24`. This release follows it but its masternodes do not sign it, so it stays inactive
until a later release signs it once Platform supports EvoNode shares and payouts.
- The consent digest that share owners sign for a shared EvoNode registration also covers the
EvoNode's `platformNodeID`. The digest of a regular shared registration is unchanged.

New RPCs
--------

- `protx shared_register_prepare_evo` creates an unsigned shared EvoNode registration. It
takes the arguments of `protx shared_register_prepare` followed by `platformNodeID`,
`platformP2PAddrs` and `platformHTTPSAddrs`, and fails until `evo_shares` is active.
36 changes: 36 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,15 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V24].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_V24].useEHF = true;

consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].bit = 14;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; // TODO: same as DEPLOYMENT_V24
Comment thread
coderabbitai[bot] marked this conversation as resolved.
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; // TODO: same as DEPLOYMENT_V24
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdStart = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdMin = 2420; // 60% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].useEHF = true;

consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000ba7225adc3153945d14d"); // 2487500
consensus.defaultAssumeValid = uint256S("0x00000000000000119fe42827219e0686d3f7b494ae65f823194c740c5dbab492"); // 2487500

Expand Down Expand Up @@ -418,6 +427,15 @@ class CTestNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V24].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_V24].useEHF = true;

consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].bit = 14;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nStartTime = 1790208000; // Thursday, September 24, 2026 0:00:00
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdStart = 80; // 80% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].useEHF = true;

consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000036c8f738da818d2"); // 1400000
consensus.defaultAssumeValid = uint256S("0x000000541a23f9db7411cddbe50f9f1ebd4aa7108ebdcad62214753f648c0239"); // 1400000

Expand Down Expand Up @@ -591,6 +609,15 @@ class CDevNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V24].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_V24].useEHF = true;

consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].bit = 14;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nStartTime = 1751328000; // July 1, 2025
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nWindowSize = 120;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdStart = 96; // 80% of 120
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdMin = 72; // 60% of 120
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].useEHF = true;

consensus.nMinimumChainWork = uint256{};
consensus.defaultAssumeValid = uint256{};

Expand Down Expand Up @@ -826,6 +853,15 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V24].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_V24].useEHF = true;

consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].bit = 14;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nWindowSize = 250;
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdStart = 250 / 5 * 4; // 80% of window size
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nThresholdMin = 250 / 5 * 3; // 60% of window size
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_EVO_SHARES].useEHF = true;

consensus.nMinimumChainWork = uint256{};
consensus.defaultAssumeValid = uint256{};

Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_
enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_V24, // Deployment of doubling withdrawal limit, extended addresses
DEPLOYMENT_EVO_SHARES, // Deployment of EvoNode shared collateral and multiple owner payouts
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
Expand Down
4 changes: 4 additions & 0 deletions src/deploymentinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/"v24",
/*.gbt_force =*/true,
},
{
/*.name =*/"evo_shares",
/*.gbt_force =*/true,
},
};

std::string DeploymentName(Consensus::BuriedDeployment dep)
Expand Down
6 changes: 3 additions & 3 deletions src/evo/providertx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,6 @@ bool CProRegTx::IsTriviallyValid(TxValidationState& state) const
}

if (IsShared()) {
if (nType != MnType::Regular) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-shares-evo");
}
// The collateral must be internal; the funding inputs and outputs are covered by the consent digest
if (!collateralOutpoint.hash.IsNull()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-shares-external");
Expand Down Expand Up @@ -359,6 +356,9 @@ uint256 CProRegTx::MakeSharedRegConsentHash(const CTransaction& tx) const
hw << nMode;
hw << NetInfoSerWrapper(const_cast<std::shared_ptr<NetInfoInterface>&>(netInfo),
nVersion >= ProTxVersion::ExtAddr);
if (nType == MnType::Evo) {
hw << platformNodeID;
}
hw << keyIDVoting;
hw << CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(pubKeyOperator),
nVersion == ProTxVersion::LegacyBLS);
Expand Down
56 changes: 39 additions & 17 deletions src/evo/providertx_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ uint16_t CurrentProviderTxVersion(node::NodeContext& node, std::optional<bool> b
return DeploymentToProtxVersion(chainman.ActiveChain().Tip(), chainman, basic_override);
}

bool EvoSharesActive(node::NodeContext& node)
{
AssertLockNotHeld(::cs_main);
auto& chainman{*Assert(node.chainman)};
LOCK(::cs_main);
return DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman, Consensus::DEPLOYMENT_EVO_SHARES);
}

struct ChainSnapshot {
uint16_t provider_tx_version{0};
CDeterministicMNCPtr dmn;
Expand Down Expand Up @@ -255,6 +263,17 @@ std::optional<ProviderTxError> ValidateNetworkFields(const ProTx& payload, bool
return std::nullopt;
}

template <typename ProTx>
std::optional<ProviderTxError> ApplyPlatformNodeId(ProTx& payload, const std::optional<uint160>& platform_node_id)
{
if (payload.nType != MnType::Evo) return std::nullopt;
if (!platform_node_id) {
return Error(ProviderTxErrorCode::INVALID_PARAMETER, "platformNodeID must be specified for an EvoNode");
}
payload.platformNodeID = *platform_node_id;
return std::nullopt;
}

using PayoutResult = std::variant<MasternodePayoutShares, ProviderTxError>;

PayoutResult BuildPayouts(const std::vector<ProviderPayout>& payouts)
Expand Down Expand Up @@ -360,9 +379,8 @@ std::optional<ProviderTxError> Preflight(node::NodeContext& node, const CTransac
}

TxValidationState state;
const bool is_v24_active{DeploymentActiveAfter(tip, chainman, Consensus::DEPLOYMENT_V24)};
if (!chain_helper.special_tx->CheckSpecialTx(tx, tip, is_v24_active, chainman.ActiveChainstate().CoinsTip(), true,
state)) {
if (!chain_helper.special_tx->CheckSpecialTx(tx, tip, GetSpecialTxRules(tip, chainman),
chainman.ActiveChainstate().CoinsTip(), true, state)) {
return Error(ProviderTxErrorCode::CONSENSUS_REJECTED, state.ToString(), state.GetRejectReason());
}
return std::nullopt;
Expand Down Expand Up @@ -515,12 +533,7 @@ RegistrationResult BuildRegistration(node::NodeContext& node, Wallet& wallet,
return Error(ProviderTxErrorCode::INVALID_PARAMETER, "payouts array requires provider transaction version 3");
}

if (request.type == MnType::Evo) {
if (!request.platform_node_id) {
return Error(ProviderTxErrorCode::INVALID_PARAMETER, "platformNodeID must be specified for an EvoNode");
}
payload.platformNodeID = *request.platform_node_id;
}
if (auto error{ApplyPlatformNodeId(payload, request.platform_node_id)}) return *error;
if (auto error{ValidateNetworkFields(payload, /*allow_empty=*/true,
/*check_platform_node_id=*/true)}) {
return *error;
Expand Down Expand Up @@ -822,12 +835,7 @@ ProviderTxResult<ProviderTxSubmission> UpdateService(node::NodeContext& node, Wa
if (auto error{ApplyNetInfo(payload, request.net_info, request.type == MnType::Evo, /*optional=*/false)}) {
return *error;
}
if (request.type == MnType::Evo) {
if (!request.platform_node_id) {
return Error(ProviderTxErrorCode::INVALID_PARAMETER, "platformNodeID must be specified for an EvoNode");
}
payload.platformNodeID = *request.platform_node_id;
}
if (auto error{ApplyPlatformNodeId(payload, request.platform_node_id)}) return *error;
if (auto error{ValidateNetworkFields(payload, /*allow_empty=*/false,
/*check_platform_node_id=*/true)}) {
return *error;
Expand Down Expand Up @@ -992,12 +1000,20 @@ ProviderTxResult<ProviderTxSubmission> Revoke(node::NodeContext& node, Wallet& w
ProviderTxResult<PreparedSharedRegistration> PrepareSharedRegistration(node::NodeContext& node,
const SharedRegistrationRequest& request)
{
if (!IsValidMnType(request.type)) {
return Error(ProviderTxErrorCode::INVALID_PARAMETER, "invalid masternode type");
}
if (request.type == MnType::Evo && !EvoSharesActive(node)) {
return Error(ProviderTxErrorCode::INVALID_PARAMETER,
"EvoNode shared registration requires the evo_shares deployment to be active");
}

CMutableTransaction tx{request.funding_tx};
tx.nVersion = 3;
tx.nType = TRANSACTION_PROVIDER_REGISTER;

CProRegTx payload;
payload.nType = MnType::Regular;
payload.nType = request.type;
payload.nVersion = CurrentProviderTxVersion(node);
if (payload.nVersion < ProTxVersion::ExtAddr) {
return Error(ProviderTxErrorCode::INVALID_PARAMETER,
Expand All @@ -1023,7 +1039,13 @@ ProviderTxResult<PreparedSharedRegistration> PrepareSharedRegistration(node::Nod
share.reward ? GetScriptForDestination(*share.reward) : CScript{}, share.owner);
}

if (auto error{ApplyNetInfo(payload, request.net_info, /*platform=*/false, /*optional=*/true)}) return *error;
if (auto error{ApplyNetInfo(payload, request.net_info, /*platform=*/payload.nType == MnType::Evo, /*optional=*/true)}) {
return *error;
}
if (auto error{ApplyPlatformNodeId(payload, request.platform_node_id)}) return *error;
if (auto error{ValidateNetworkFields(payload, /*allow_empty=*/true, /*check_platform_node_id=*/true)}) {
return *error;
}

if (!request.operator_key.IsValid()) {
return Error(ProviderTxErrorCode::INVALID_PARAMETER, "operator BLS address must be a valid BLS public key");
Expand Down
Loading
Loading