Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: removed pre-MN_RR logic of validation of CL #6408

Merged
merged 3 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions src/evo/cbtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
}

bool CheckCbTxBestChainlock(const CBlock& block, const CBlockIndex* pindex,
const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state, const bool check_clhdiff)
const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state)
{
if (block.vtx[0]->nType != TRANSACTION_COINBASE) {
return true;
Expand All @@ -342,44 +342,43 @@ bool CheckCbTxBestChainlock(const CBlock& block, const CBlockIndex* pindex,
if (!opt_cbTx) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-payload");
}
const auto& cbTx = *opt_cbTx;

if (opt_cbTx->nVersion < CCbTx::Version::CLSIG_AND_BALANCE) {
if (cbTx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) {
return true;
}

auto best_clsig = chainlock_handler.GetBestChainLock();
if (best_clsig.getHeight() == pindex->nHeight - 1 && opt_cbTx->bestCLHeightDiff == 0 && opt_cbTx->bestCLSignature == best_clsig.getSig()) {
if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 && cbTx.bestCLSignature == best_clsig.getSig()) {
// matches our best clsig which still hold values for the previous block
return true;
}

if (check_clhdiff) {
auto prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
// If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null.
if (prevBlockCoinbaseChainlock.has_value()) {
// Previous block Coinbase has a non-null Chainlock: current block's Chainlock must be non-null and at least as new as the previous one
if (!opt_cbTx->bestCLSignature.IsValid()) {
// IsNull() doesn't exist for CBLSSignature: we assume that a non valid BLS sig is null
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-null-clsig");
}
if (opt_cbTx->bestCLHeightDiff > prevBlockCoinbaseChainlock.value().second + 1) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-older-clsig");
}
const auto prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
// If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null.
if (prevBlockCoinbaseChainlock.has_value()) {
// Previous block Coinbase has a non-null Chainlock: current block's Chainlock must be non-null and at least as new as the previous one
if (!cbTx.bestCLSignature.IsValid()) {
// IsNull() doesn't exist for CBLSSignature: we assume that a non valid BLS sig is null
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-null-clsig");
}
if (cbTx.bestCLHeightDiff > prevBlockCoinbaseChainlock.value().second + 1) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-older-clsig");
}
}

// IsNull() doesn't exist for CBLSSignature: we assume that a valid BLS sig is non-null
if (opt_cbTx->bestCLSignature.IsValid()) {
int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast<int>(opt_cbTx->bestCLHeightDiff) - 1;
if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == opt_cbTx->bestCLSignature) {
if (cbTx.bestCLSignature.IsValid()) {
int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast<int>(cbTx.bestCLHeightDiff) - 1;
if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == cbTx.bestCLSignature) {
// matches our best (but outdated) clsig, no need to verify it again
return true;
}
uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash();
if (chainlock_handler.VerifyChainLock(llmq::CChainLockSig(curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, opt_cbTx->bestCLSignature)) != llmq::VerifyRecSigStatus::Valid) {
if (chainlock_handler.VerifyChainLock(llmq::CChainLockSig(curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature)) != llmq::VerifyRecSigStatus::Valid) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig");
}
} else if (opt_cbTx->bestCLHeightDiff != 0) {
} else if (cbTx.bestCLHeightDiff != 0) {
// Null bestCLSignature is allowed only with bestCLHeightDiff = 0
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff");
}
Expand Down
2 changes: 1 addition & 1 deletion src/evo/cbtx.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
BlockValidationState& state);

bool CheckCbTxBestChainlock(const CBlock& block, const CBlockIndex* pindexPrev,
const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state, const bool check_clhdiff);
const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state);
bool CalcCbTxBestChainlock(const llmq::CChainLocksHandler& chainlock_handler, const CBlockIndex* pindexPrev,
uint32_t& bestCLHeightDiff, CBLSSignature& bestCLSignature);

Expand Down
2 changes: 1 addition & 1 deletion src/evo/specialtxman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(const CBlock& block, const CB
nTimeMerkle += nTime5 - nTime4;
LogPrint(BCLog::BENCHMARK, " - CheckCbTxMerkleRoots: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001);

if (fCheckCbTxMerkleRoots && !CheckCbTxBestChainlock(block, pindex, m_clhandler, state, DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_MN_RR))) {
if (fCheckCbTxMerkleRoots && !CheckCbTxBestChainlock(block, pindex, m_clhandler, state)) {
// pass the state returned by the function above
return false;
}
Expand Down
33 changes: 11 additions & 22 deletions test/functional/feature_llmq_chainlocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@

from test_framework.messages import CBlock, CCbTx
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, force_finish_mnsync, softfork_active
from test_framework.util import assert_equal, assert_raises_rpc_error, force_finish_mnsync


class LLMQChainLocksTest(DashTestFramework):
def set_test_params(self):
self.set_dash_test_params(5, 4, [["-testactivationheight=mn_rr@1100"]] * 5)
self.set_dash_test_params(5, 4)

def run_test(self):
# Connect all nodes to node1 so that we always have the whole network connected
Expand All @@ -31,8 +31,8 @@ def run_test(self):

self.test_coinbase_best_cl(self.nodes[0], expected_cl_in_cb=False)

self.activate_v20(expected_activation_height=900)
self.log.info("Activated v20 at height:" + str(self.nodes[0].getblockcount()))
self.activate_mn_rr(expected_activation_height=900)
self.log.info("Activated MN_RR at height:" + str(self.nodes[0].getblockcount()))

# v20 is active for the next block, not for the tip
self.test_coinbase_best_cl(self.nodes[0], expected_cl_in_cb=False)
Expand Down Expand Up @@ -243,14 +243,8 @@ def run_test(self):
assert_equal(tip_1['cbTx']['bestCLSignature'], tip_0['cbTx']['bestCLSignature'])
assert_equal(tip_1['cbTx']['bestCLHeightDiff'], tip_0['cbTx']['bestCLHeightDiff'] + 1)

self.log.info("Test that bestCLHeightDiff conditions are relaxed before mn_rr")
self.test_bestCLHeightDiff(False)

self.activate_mn_rr(expected_activation_height=1100)
self.log.info("Activated mn_rr at height:" + str(self.nodes[0].getblockcount()))

self.log.info("Test that bestCLHeightDiff conditions are stricter after mn_rr")
self.test_bestCLHeightDiff(True)
self.log.info("Test bestCLHeightDiff restrictions")
self.test_bestCLHeightDiff()

def create_chained_txs(self, node, amount):
txid = node.sendtoaddress(node.getnewaddress(), amount)
Expand Down Expand Up @@ -293,11 +287,10 @@ def test_coinbase_best_cl(self, node, expected_cl_in_cb=True, expected_null_cl=F
else:
assert "bestCLHeightDiff" not in cbtx and "bestCLSignature" not in cbtx

def test_bestCLHeightDiff(self, mn_rr_active):
def test_bestCLHeightDiff(self):
# We need 2 blocks we can grab clsigs from
for _ in range(2):
self.wait_for_chainlocked_block_all_nodes(self.generate(self.nodes[0], 1, sync_fun=self.no_op)[0])
assert_equal(softfork_active(self.nodes[1], "mn_rr"), mn_rr_active)
tip1_hash = self.nodes[1].getbestblockhash()

self.isolate_node(1)
Expand Down Expand Up @@ -339,23 +332,19 @@ def test_bestCLHeightDiff(self, mn_rr_active):
mal_block.hashMerkleRoot = mal_block.calc_merkle_root()
mal_block.solve()
result = self.nodes[1].submitblock(mal_block.serialize().hex())
assert_equal(result, "bad-cbtx-older-clsig" if mn_rr_active else "bad-cbtx-invalid-clsig")
assert_equal(result, "bad-cbtx-older-clsig")
assert_equal(self.nodes[1].getbestblockhash(), tip1_hash)

# Update the sig too and it should pass now when mn_rr is not active and fail otherwise
# Update the sig too and it should fail
old_blockhash = self.nodes[1].getblockhash(self.nodes[1].getblockcount() - 1)
cbtx.bestCLSignature = bytes.fromhex(self.nodes[1].getblock(old_blockhash, 2)["tx"][0]["cbTx"]["bestCLSignature"])
mal_block.vtx[0].vExtraPayload = cbtx.serialize()
mal_block.vtx[0].rehash()
mal_block.hashMerkleRoot = mal_block.calc_merkle_root()
mal_block.solve()
result = self.nodes[1].submitblock(mal_block.serialize().hex())
if mn_rr_active:
assert_equal(result, "bad-cbtx-older-clsig")
assert_equal(self.nodes[1].getbestblockhash(), tip1_hash)
else:
assert_equal(result, None)
assert not self.nodes[1].getbestblockhash() == tip1_hash
assert_equal(result, "bad-cbtx-older-clsig")
assert_equal(self.nodes[1].getbestblockhash(), tip1_hash)

self.reconnect_isolated_node(1, 0)
self.sync_all()
Expand Down
Loading