Skip to content

Commit 5f65653

Browse files
fix: preserve legacy registrar state version
1 parent cae6ceb commit 5f65653

4 files changed

Lines changed: 164 additions & 2 deletions

File tree

src/evo/specialtxman.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,10 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul
454454
}
455455
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
456456
const uint16_t old_version{static_cast<uint16_t>(newState->nVersion)};
457-
uint16_t target_version{std::max<uint16_t>(old_version, opt_proTx->nVersion)};
458-
if (newState->pubKeyOperator != opt_proTx->pubKeyOperator) {
457+
const bool operator_changed{newState->pubKeyOperator != opt_proTx->pubKeyOperator};
458+
const uint16_t target_version{is_v24_deployed ? std::max<uint16_t>(old_version, opt_proTx->nVersion)
459+
: (operator_changed ? opt_proTx->nVersion : old_version)};
460+
if (operator_changed) {
459461
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
460462
newState->ResetOperatorFields();
461463
newState->BanIfNotBanned(nHeight);

src/test/evo_deterministicmns_tests.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,125 @@ void FuncV19Activation(TestChainSetup& setup)
419419
BOOST_REQUIRE(dummy_list == tip_list);
420420
};
421421

422+
void FuncProUpRegTxVersionHandlingBeforeV24(TestChainSetup& setup)
423+
{
424+
auto& chainman = *Assert(setup.m_node.chainman.get());
425+
auto& dmnman = *Assert(setup.m_node.dmnman);
426+
const CScript coinbase_pk = GetScriptForRawPubKey(setup.coinbaseKey.GetPubKey());
427+
428+
BOOST_REQUIRE(!DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman.GetConsensus(), Consensus::DEPLOYMENT_V19));
429+
BOOST_REQUIRE(bls::bls_legacy_scheme.load());
430+
431+
auto utxos = BuildSimpleUtxoMap(setup.m_coinbase_txns);
432+
CKey owner_key;
433+
CBLSSecretKey operator_key;
434+
auto tx_reg = CreateProRegTx(chainman.ActiveChain(), *(setup.m_node.mempool), utxos, 1, GenerateRandomAddress(),
435+
setup.coinbaseKey, owner_key, operator_key);
436+
const auto proTxHash = tx_reg.GetHash();
437+
setup.CreateAndProcessBlock({tx_reg}, coinbase_pk);
438+
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
439+
440+
auto dmn = dmnman.GetListAtChainTip().GetMN(proTxHash);
441+
BOOST_REQUIRE(dmn);
442+
BOOST_CHECK_EQUAL(dmn->pdmnState->nVersion, ProTxVersion::LegacyBLS);
443+
444+
while (!DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman.GetConsensus(), Consensus::DEPLOYMENT_V19)) {
445+
setup.CreateAndProcessBlock({}, coinbase_pk);
446+
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
447+
}
448+
BOOST_REQUIRE(!DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman, Consensus::DEPLOYMENT_V24));
449+
BOOST_REQUIRE(!bls::bls_legacy_scheme.load());
450+
451+
const auto payoutScript = GenerateRandomAddress();
452+
auto tx_upreg = CreateProUpRegTx(chainman.ActiveChain(), *(setup.m_node.mempool), utxos, proTxHash, owner_key,
453+
operator_key.GetPublicKey(), owner_key.GetPubKey().GetID(), payoutScript,
454+
setup.coinbaseKey);
455+
const auto opt_upreg = GetTxPayload<CProUpRegTx>(tx_upreg);
456+
BOOST_REQUIRE(opt_upreg);
457+
BOOST_CHECK_EQUAL(opt_upreg->nVersion, ProTxVersion::BasicBLS);
458+
459+
setup.CreateAndProcessBlock({tx_upreg}, coinbase_pk);
460+
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
461+
462+
dmn = dmnman.GetListAtChainTip().GetMN(proTxHash);
463+
BOOST_REQUIRE(dmn);
464+
BOOST_CHECK(dmn->pdmnState->scriptPayout == payoutScript);
465+
BOOST_CHECK_EQUAL(dmn->pdmnState->nVersion, ProTxVersion::LegacyBLS);
466+
BOOST_REQUIRE(!dmn->pdmnState->IsBanned());
467+
468+
CBLSSecretKey operator_key_new;
469+
operator_key_new.MakeNewKey();
470+
const auto payoutScript2 = GenerateRandomAddress();
471+
auto tx_upreg2 = CreateProUpRegTx(chainman.ActiveChain(), *(setup.m_node.mempool), utxos, proTxHash, owner_key,
472+
operator_key_new.GetPublicKey(), owner_key.GetPubKey().GetID(), payoutScript2,
473+
setup.coinbaseKey);
474+
setup.CreateAndProcessBlock({tx_upreg2}, coinbase_pk);
475+
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
476+
477+
dmn = dmnman.GetListAtChainTip().GetMN(proTxHash);
478+
BOOST_REQUIRE(dmn);
479+
BOOST_CHECK(dmn->pdmnState->scriptPayout == payoutScript2);
480+
BOOST_CHECK_EQUAL(dmn->pdmnState->nVersion, ProTxVersion::BasicBLS);
481+
BOOST_REQUIRE(dmn->pdmnState->IsBanned());
482+
};
483+
484+
void FuncProUpRegTxV4OnLegacyRejected(TestChainSetup& setup)
485+
{
486+
auto& chainman = *Assert(setup.m_node.chainman.get());
487+
auto& dmnman = *Assert(setup.m_node.dmnman);
488+
const CScript coinbase_pk = GetScriptForRawPubKey(setup.coinbaseKey.GetPubKey());
489+
490+
BOOST_REQUIRE(!DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman.GetConsensus(), Consensus::DEPLOYMENT_V19));
491+
BOOST_REQUIRE(!DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman, Consensus::DEPLOYMENT_V24));
492+
493+
auto utxos = BuildSimpleUtxoMap(setup.m_coinbase_txns);
494+
CKey owner_key;
495+
CBLSSecretKey operator_key;
496+
auto tx_reg = CreateProRegTx(chainman.ActiveChain(), *(setup.m_node.mempool), utxos, 1, GenerateRandomAddress(),
497+
setup.coinbaseKey, owner_key, operator_key);
498+
const auto proTxHash = tx_reg.GetHash();
499+
setup.CreateAndProcessBlock({tx_reg}, coinbase_pk);
500+
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
501+
502+
auto dmn = dmnman.GetListAtChainTip().GetMN(proTxHash);
503+
BOOST_REQUIRE(dmn);
504+
BOOST_CHECK_EQUAL(dmn->pdmnState->nVersion, ProTxVersion::LegacyBLS);
505+
506+
for (int i = 0; i < 2000 && !DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman, Consensus::DEPLOYMENT_V24); ++i) {
507+
setup.CreateAndProcessBlock({}, coinbase_pk);
508+
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
509+
}
510+
BOOST_REQUIRE(DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman.GetConsensus(), Consensus::DEPLOYMENT_V19));
511+
BOOST_REQUIRE(DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman, Consensus::DEPLOYMENT_V24));
512+
BOOST_REQUIRE(!bls::bls_legacy_scheme.load());
513+
BOOST_REQUIRE_EQUAL(dmnman.GetListAtChainTip().GetMN(proTxHash)->pdmnState->nVersion, ProTxVersion::LegacyBLS);
514+
515+
CProUpRegTx proTx;
516+
proTx.nVersion = ProTxVersion::MultiPayout;
517+
proTx.proTxHash = proTxHash;
518+
proTx.pubKeyOperator.Set(operator_key.GetPublicKey(), bls::bls_legacy_scheme.load());
519+
proTx.keyIDVoting = owner_key.GetPubKey().GetID();
520+
proTx.payouts = {{GenerateRandomAddress(), CMasternodePayoutShare::MAX_REWARD}};
521+
522+
CMutableTransaction tx;
523+
tx.nVersion = 3;
524+
tx.nType = TRANSACTION_PROVIDER_UPDATE_REGISTRAR;
525+
FundTransaction(chainman.ActiveChain(), tx, utxos, GetScriptForDestination(PKHash(setup.coinbaseKey.GetPubKey())),
526+
1 * COIN, setup.coinbaseKey);
527+
proTx.inputsHash = CalcTxInputsHash(CTransaction(tx));
528+
CHashSigner::SignHash(::SerializeHash(proTx), owner_key, proTx.vchSig);
529+
SetTxPayload(tx, proTx);
530+
SignTransaction(*(setup.m_node.mempool), tx, setup.coinbaseKey);
531+
532+
TxValidationState val_state;
533+
{
534+
LOCK(cs_main);
535+
BOOST_CHECK(!CheckProUpRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman,
536+
chainman.ActiveChainstate().CoinsTip(), chainman, val_state, /*check_sigs=*/true));
537+
}
538+
BOOST_CHECK_EQUAL(val_state.GetRejectReason(), "bad-protx-version-upgrade");
539+
};
540+
422541
void FuncDIP3Protx(TestChainSetup& setup)
423542
{
424543
auto& chainman = *Assert(setup.m_node.chainman.get());
@@ -976,6 +1095,20 @@ TestChainV19BeforeActivationSetup::TestChainV19BeforeActivationSetup() :
9761095
assert(!v19_active);
9771096
}
9781097

1098+
struct TestChainV24SignalBeforeV19Setup : public TestChainSetup {
1099+
TestChainV24SignalBeforeV19Setup() :
1100+
TestChainSetup(494, CBaseChainParams::REGTEST,
1101+
{"-testactivationheight=v19@500", "-testactivationheight=v20@500",
1102+
"-testactivationheight=mn_rr@500",
1103+
"-vbparams=v24:0:9999999999:0:500:400:300:5:0"})
1104+
{
1105+
assert(!DeploymentActiveAfter(m_node.chainman->ActiveChain().Tip(), m_node.chainman->GetConsensus(),
1106+
Consensus::DEPLOYMENT_V19));
1107+
assert(!DeploymentActiveAfter(m_node.chainman->ActiveChain().Tip(), *m_node.chainman,
1108+
Consensus::DEPLOYMENT_V24));
1109+
}
1110+
};
1111+
9791112
// DIP3 can only be activated with legacy scheme (v19 is activated later)
9801113
BOOST_AUTO_TEST_CASE(dip3_activation_legacy)
9811114
{
@@ -990,6 +1123,18 @@ BOOST_AUTO_TEST_CASE(v19_activation_legacy)
9901123
FuncV19Activation(setup);
9911124
}
9921125

1126+
BOOST_AUTO_TEST_CASE(proupreg_version_handling_before_v24)
1127+
{
1128+
TestChainV19BeforeActivationSetup setup;
1129+
FuncProUpRegTxVersionHandlingBeforeV24(setup);
1130+
}
1131+
1132+
BOOST_AUTO_TEST_CASE(proupreg_v4_on_legacy_rejected)
1133+
{
1134+
TestChainV24SignalBeforeV19Setup setup;
1135+
FuncProUpRegTxV4OnLegacyRejected(setup);
1136+
}
1137+
9931138
BOOST_AUTO_TEST_CASE(dip3_protx_legacy)
9941139
{
9951140
TestChainDIP3Setup setup;

src/test/evo_trivialvalidation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ BOOST_AUTO_TEST_CASE(multipayout_list_validation)
181181
CheckPayouts({{GetScriptForDestination(PKHash(owner_id)), 10000}}, owner_id, voting_id, "bad-protx-payee-reuse");
182182
CheckPayouts({{GetScriptForDestination(PKHash(voting_id)), 10000}}, owner_id, voting_id, "bad-protx-payee-reuse");
183183
CheckPayouts({{GetScriptForDestination(PKHash(CKeyID{})), 10000}}, CKeyID{}, voting_id, std::nullopt);
184+
CheckPayouts({{GetScriptForDestination(PKHash(voting_id)), 10000}}, CKeyID{}, voting_id, "bad-protx-payee-reuse");
184185

185186
TxValidationState state;
186187
BOOST_CHECK(!IsPayoutListKeySafe({{payout1, 10000}}, CTxDestination(PKHash(payout_key1.GetPubKey().GetID())),

test/functional/feature_masternode_payout_shares.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,20 @@ def shortpay_payout2(coinbase):
216216
assert_equal(payee["amount"], expected_amount)
217217
paid_owner_total += payee["amount"]
218218

219+
self.generate(node, 1, sync_fun=self.no_op)
220+
payments = node.masternode("payments")[0]["masternodes"][0]["payees"]
221+
payment_payees = [p for p in payments if p["script"] != "6a"]
222+
rpc_operator_payees = [p for p in payment_payees if p["address"] == operator_payout]
223+
assert_equal(len(rpc_operator_payees), 1)
224+
rpc_owner_payees = [p for p in payment_payees if p["address"] != operator_payout]
225+
assert_equal([p["address"] for p in rpc_owner_payees], [p["address"] for p in updated_payouts])
226+
assert_equal(rpc_operator_payees[0]["amount"], operator_amount)
227+
paid_owner_total = 0
228+
for i, payee in enumerate(rpc_owner_payees):
229+
expected_amount = owner_total - paid_owner_total if i == len(rpc_owner_payees) - 1 else owner_total * 1250 // 10000
230+
assert_equal(payee["amount"], expected_amount)
231+
paid_owner_total += payee["amount"]
232+
219233
node.sendtoaddress(mn.fundsAddr, 1)
220234
self.bump_mocktime(10 * 60 + 1)
221235
self.generate(node, 1, sync_fun=self.no_op)

0 commit comments

Comments
 (0)