Skip to content

Commit ba2817f

Browse files
Merge #7342: fix(rpc): report inactive DIP3 for ProTx RPCs
f671673 fix: use token reject reason for inactive DIP3 (PastaClaw) 4d38776 fix(rpc): report inactive DIP3 for ProTx RPCs (PastaClaw) Pull request description: ## Issue being fixed or feature implemented ProTx RPCs can return a generic `bad-tx-type` error with RPC code `-1` when used before DIP0003 is active on regtest/devnet. The underlying problem is that special transaction validation rejects extra payload transactions before DIP3, but the RPC response does not explain that activation height is the blocker. This improves the Dash Core developer experience by returning an explicit inactive-DIP3 RPC verification error with activation height context. ## What was done? - Added an early DIP0003 activation check in the shared ProTx submission helper before special transaction validation. - Return `RPC_VERIFY_ERROR` with the DIP3 activation height, current chain height, next block height, and a regtest/devnet hint. - Added functional test coverage for both funded and external-collateral ProTx registration paths before DIP3 activation. ## How Has This Been Tested? Tested locally on macOS arm64 with the existing depends prefix and autotools build. - `make -j8 src/dashd src/dash-cli` - `test/functional/feature_dip3_deterministicmns.py --cachedir=/Users/claw/Projects/dash/worktrees/protx-dip3-error/test/cache` - Pre-PR review gate: `ship` ## Breaking Changes None. This only changes the RPC error returned before DIP0003 activation for ProTx submission paths. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK f671673 PastaPastaPasta: utACK f671673 Tree-SHA512: 0667d9d4860947c09849257bbf6585456ad4aeff7a6c6295de5ca1fc8c729909ac50af960697466697439952b54e1fd3ddaa972a31ef94a120a711b37faf8b25
2 parents dd95f96 + f671673 commit ba2817f

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

src/evo/specialtxman.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, llmq::CQuorumSn
110110
return true;
111111

112112
if (!DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) {
113-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type");
113+
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-dip3-inactive");
114114
}
115115

116116
try {

src/rpc/evo.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,25 @@ static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, CChainsta
354354
{
355355
LOCK(::cs_main);
356356

357+
const CBlockIndex* tip{chainman.ActiveChain().Tip()};
358+
const Consensus::Params& consensus_params{chainman.GetConsensus()};
359+
if (!DeploymentActiveAfter(tip, consensus_params, Consensus::DEPLOYMENT_DIP0003)) {
360+
const int current_height{tip ? tip->nHeight : -1};
361+
const int next_block_height{current_height + 1};
362+
const int activation_height{consensus_params.DIP0003Height};
363+
const int blocks_to_mine{
364+
activation_height > next_block_height ? activation_height - next_block_height : 0
365+
};
366+
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf(
367+
"DIP0003 is not active yet; ProTx transactions are valid starting at block height %d "
368+
"(current chain height %d, next block height %d). Mine %d more block%s or restart "
369+
"this regtest/devnet chain with DIP3 activation parameters that are already active.",
370+
activation_height, current_height, next_block_height, blocks_to_mine,
371+
blocks_to_mine == 1 ? "" : "s"));
372+
}
373+
357374
TxValidationState state;
358-
if (!chain_helper.special_tx->CheckSpecialTx(CTransaction(tx), chainman.ActiveChain().Tip(), chainman.ActiveChainstate().CoinsTip(), true, state)) {
375+
if (!chain_helper.special_tx->CheckSpecialTx(CTransaction(tx), tip, chainman.ActiveChainstate().CoinsTip(), true, state)) {
359376
throw std::runtime_error(state.ToString());
360377
}
361378
} // cs_main

test/functional/feature_dip3_deterministicmns.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,43 @@ def run_test(self):
6262
self.log.info("testing rejection of ProTx before dip3 activation")
6363
assert self.nodes[0].getblockchaininfo()['blocks'] < 135
6464

65+
def dip3_inactive_error():
66+
current_height = self.nodes[0].getblockcount()
67+
next_block_height = current_height + 1
68+
blocks_to_mine = 135 - next_block_height
69+
return (
70+
f"DIP0003 is not active yet; ProTx transactions are valid starting at block height 135 "
71+
f"(current chain height {current_height}, next block height {next_block_height}). "
72+
f"Mine {blocks_to_mine} more block{'s' if blocks_to_mine != 1 else ''} or restart "
73+
"this regtest/devnet chain with DIP3 activation parameters that are already active."
74+
)
75+
76+
before_activation_mn: MasternodeInfo = self.prepare_mn(
77+
self.nodes[0], self.num_initial_mn + 2, 'mn-before-dip3-activation'
78+
)
79+
self.nodes[0].sendtoaddress(
80+
before_activation_mn.fundsAddr, before_activation_mn.get_collateral_value() + 0.001
81+
)
82+
before_activation_mn.register_fund(
83+
self.nodes[0],
84+
submit=True,
85+
expected_assert_code=-25,
86+
expected_assert_msg=dip3_inactive_error(),
87+
)
88+
6589
mns: List[MasternodeInfo] = []
6690

6791
# prepare mn which should still be accepted later when dip3 activates
6892
self.log.info("creating collateral for mn-before-dip3")
6993
before_dip3_mn: MasternodeInfo = self.prepare_mn(self.nodes[0], 1, 'mn-before-dip3')
7094
self.create_mn_collateral(self.nodes[0], before_dip3_mn)
95+
self.nodes[0].sendtoaddress(before_dip3_mn.fundsAddr, 0.001)
96+
before_dip3_mn.register(
97+
self.nodes[0],
98+
submit=True,
99+
expected_assert_code=-25,
100+
expected_assert_msg=dip3_inactive_error(),
101+
)
71102
mns.append(before_dip3_mn)
72103

73104
# block 150 starts enforcing DIP3 MN payments

0 commit comments

Comments
 (0)