Skip to content

Commit c873e55

Browse files
committed
refactor: move CL code validation to break circular dependencies over CbTx
1 parent 1011f6b commit c873e55

File tree

5 files changed

+130
-131
lines changed

5 files changed

+130
-131
lines changed

src/evo/cbtx.cpp

Lines changed: 0 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <evo/cbtx.h>
77
#include <evo/specialtx.h>
88
#include <llmq/blockprocessor.h>
9-
#include <llmq/chainlocks.h>
109
#include <llmq/commitment.h>
1110
#include <llmq/options.h>
1211
#include <llmq/quorums.h>
@@ -217,129 +216,6 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
217216
return true;
218217
}
219218

220-
bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex,
221-
const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state)
222-
{
223-
if (cbTx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) {
224-
return true;
225-
}
226-
227-
static Mutex cached_mutex;
228-
static const CBlockIndex* cached_pindex GUARDED_BY(cached_mutex){nullptr};
229-
static std::optional<std::pair<CBLSSignature, uint32_t>> cached_chainlock GUARDED_BY(cached_mutex){std::nullopt};
230-
231-
auto best_clsig = chainlock_handler.GetBestChainLock();
232-
if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 && cbTx.bestCLSignature == best_clsig.getSig()) {
233-
// matches our best clsig which still hold values for the previous block
234-
LOCK(cached_mutex);
235-
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
236-
cached_pindex = pindex;
237-
return true;
238-
}
239-
240-
std::optional<std::pair<CBLSSignature, uint32_t>> prevBlockCoinbaseChainlock{std::nullopt};
241-
if (LOCK(cached_mutex); cached_pindex == pindex->pprev) {
242-
prevBlockCoinbaseChainlock = cached_chainlock;
243-
}
244-
if (!prevBlockCoinbaseChainlock.has_value()) {
245-
prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
246-
}
247-
// If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null.
248-
if (prevBlockCoinbaseChainlock.has_value()) {
249-
// 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
250-
if (!cbTx.bestCLSignature.IsValid()) {
251-
// IsNull() doesn't exist for CBLSSignature: we assume that a non valid BLS sig is null
252-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-null-clsig");
253-
}
254-
if (cbTx.bestCLHeightDiff > prevBlockCoinbaseChainlock.value().second + 1) {
255-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-older-clsig");
256-
}
257-
}
258-
259-
// IsNull() doesn't exist for CBLSSignature: we assume that a valid BLS sig is non-null
260-
if (cbTx.bestCLSignature.IsValid()) {
261-
int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast<int>(cbTx.bestCLHeightDiff) - 1;
262-
if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == cbTx.bestCLSignature) {
263-
// matches our best (but outdated) clsig, no need to verify it again
264-
LOCK(cached_mutex);
265-
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
266-
cached_pindex = pindex;
267-
return true;
268-
}
269-
uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash();
270-
if (chainlock_handler.VerifyChainLock(llmq::CChainLockSig(curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature)) != llmq::VerifyRecSigStatus::Valid) {
271-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig");
272-
}
273-
LOCK(cached_mutex);
274-
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
275-
cached_pindex = pindex;
276-
} else if (cbTx.bestCLHeightDiff != 0) {
277-
// Null bestCLSignature is allowed only with bestCLHeightDiff = 0
278-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff");
279-
}
280-
281-
return true;
282-
}
283-
284-
bool CalcCbTxBestChainlock(const llmq::CChainLocksHandler& chainlock_handler, const CBlockIndex* pindexPrev,
285-
uint32_t& bestCLHeightDiff, CBLSSignature& bestCLSignature)
286-
{
287-
auto best_clsig = chainlock_handler.GetBestChainLock();
288-
if (best_clsig.getHeight() < Params().GetConsensus().DeploymentHeight(Consensus::DEPLOYMENT_V19)) {
289-
// We don't want legacy BLS ChainLocks in CbTx (can happen on regtest/devenets)
290-
best_clsig = llmq::CChainLockSig{};
291-
}
292-
if (best_clsig.getHeight() == pindexPrev->nHeight) {
293-
// Our best CL is the newest one possible
294-
bestCLHeightDiff = 0;
295-
bestCLSignature = best_clsig.getSig();
296-
return true;
297-
}
298-
299-
auto prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindexPrev);
300-
if (prevBlockCoinbaseChainlock.has_value()) {
301-
// Previous block Coinbase contains a non-null CL: We must insert the same sig or a better (newest) one
302-
if (best_clsig.IsNull()) {
303-
// We don't know any CL, therefore inserting the CL of the previous block
304-
bestCLHeightDiff = prevBlockCoinbaseChainlock->second + 1;
305-
bestCLSignature = prevBlockCoinbaseChainlock->first;
306-
return true;
307-
}
308-
309-
// We check if our best CL is newer than the one from previous block Coinbase
310-
int curCLHeight = best_clsig.getHeight();
311-
int prevCLHeight = pindexPrev->nHeight - static_cast<int>(prevBlockCoinbaseChainlock->second) - 1;
312-
if (curCLHeight < prevCLHeight) {
313-
// Our best CL isn't newer: inserting CL from previous block
314-
bestCLHeightDiff = prevBlockCoinbaseChainlock->second + 1;
315-
bestCLSignature = prevBlockCoinbaseChainlock->first;
316-
}
317-
else {
318-
// Our best CL is newer
319-
bestCLHeightDiff = pindexPrev->nHeight - best_clsig.getHeight();
320-
bestCLSignature = best_clsig.getSig();
321-
}
322-
323-
return true;
324-
}
325-
else {
326-
// Previous block Coinbase has no CL. We can either insert null or any valid CL
327-
if (best_clsig.IsNull()) {
328-
// We don't know any CL, therefore inserting a null CL
329-
bestCLHeightDiff = 0;
330-
bestCLSignature.Reset();
331-
return false;
332-
}
333-
334-
// Inserting our best CL
335-
bestCLHeightDiff = pindexPrev->nHeight - best_clsig.getHeight();
336-
bestCLSignature = chainlock_handler.GetBestChainLock().getSig();
337-
338-
return true;
339-
}
340-
}
341-
342-
343219
std::string CCbTx::ToString() const
344220
{
345221
return strprintf("CCbTx(nVersion=%d, nHeight=%d, merkleRootMNList=%s, merkleRootQuorums=%s, bestCLHeightDiff=%d, bestCLSig=%s, creditPoolBalance=%d.%08d)",

src/evo/cbtx.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class CBlock;
1616
class CBlockIndex;
1717
class TxValidationState;
1818
namespace llmq {
19-
class CChainLocksHandler;
2019
class CQuorumBlockProcessor;
2120
}// namespace llmq
2221

@@ -71,11 +70,6 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
7170
const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet,
7271
BlockValidationState& state);
7372

74-
bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindexPrev,
75-
const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state);
76-
bool CalcCbTxBestChainlock(const llmq::CChainLocksHandler& chainlock_handler, const CBlockIndex* pindexPrev,
77-
uint32_t& bestCLHeightDiff, CBLSSignature& bestCLSignature);
78-
7973
std::optional<std::pair<CBLSSignature, uint32_t>> GetNonNullCoinbaseChainlock(const CBlockIndex* pindex);
8074

8175
#endif // BITCOIN_EVO_CBTX_H

src/evo/specialtxman.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,80 @@
1717
#include <evo/simplifiedmns.h>
1818
#include <hash.h>
1919
#include <llmq/blockprocessor.h>
20+
#include <llmq/chainlocks.h>
21+
#include <llmq/clsig.h>
2022
#include <llmq/commitment.h>
23+
#include <llmq/quorums.h>
2124
#include <primitives/block.h>
2225
#include <validation.h>
2326

27+
static bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex,
28+
const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state)
29+
{
30+
if (cbTx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) {
31+
return true;
32+
}
33+
34+
static Mutex cached_mutex;
35+
static const CBlockIndex* cached_pindex GUARDED_BY(cached_mutex){nullptr};
36+
static std::optional<std::pair<CBLSSignature, uint32_t>> cached_chainlock GUARDED_BY(cached_mutex){std::nullopt};
37+
38+
auto best_clsig = chainlock_handler.GetBestChainLock();
39+
if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 &&
40+
cbTx.bestCLSignature == best_clsig.getSig()) {
41+
// matches our best clsig which still hold values for the previous block
42+
LOCK(cached_mutex);
43+
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
44+
cached_pindex = pindex;
45+
return true;
46+
}
47+
48+
std::optional<std::pair<CBLSSignature, uint32_t>> prevBlockCoinbaseChainlock{std::nullopt};
49+
if (LOCK(cached_mutex); cached_pindex == pindex->pprev) {
50+
prevBlockCoinbaseChainlock = cached_chainlock;
51+
}
52+
if (!prevBlockCoinbaseChainlock.has_value()) {
53+
prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
54+
}
55+
// If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null.
56+
if (prevBlockCoinbaseChainlock.has_value()) {
57+
// 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
58+
if (!cbTx.bestCLSignature.IsValid()) {
59+
// IsNull() doesn't exist for CBLSSignature: we assume that a non valid BLS sig is null
60+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-null-clsig");
61+
}
62+
if (cbTx.bestCLHeightDiff > prevBlockCoinbaseChainlock.value().second + 1) {
63+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-older-clsig");
64+
}
65+
}
66+
67+
// IsNull() doesn't exist for CBLSSignature: we assume that a valid BLS sig is non-null
68+
if (cbTx.bestCLSignature.IsValid()) {
69+
int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast<int>(cbTx.bestCLHeightDiff) - 1;
70+
if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == cbTx.bestCLSignature) {
71+
// matches our best (but outdated) clsig, no need to verify it again
72+
LOCK(cached_mutex);
73+
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
74+
cached_pindex = pindex;
75+
return true;
76+
}
77+
uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash();
78+
if (chainlock_handler.VerifyChainLock(
79+
llmq::CChainLockSig(curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature)) !=
80+
llmq::VerifyRecSigStatus::Valid) {
81+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig");
82+
}
83+
LOCK(cached_mutex);
84+
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
85+
cached_pindex = pindex;
86+
} else if (cbTx.bestCLHeightDiff != 0) {
87+
// Null bestCLSignature is allowed only with bestCLHeightDiff = 0
88+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff");
89+
}
90+
91+
return true;
92+
}
93+
2494
static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, llmq::CQuorumSnapshotManager& qsnapman,
2595
const ChainstateManager& chainman, const llmq::CQuorumManager& qman,
2696
const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view,

src/node/miner.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,66 @@ void BlockAssembler::resetBlock()
121121
nFees = 0;
122122
}
123123

124+
// Helper to calculate best chainlock
125+
static bool CalcCbTxBestChainlock(const llmq::CChainLocksHandler& chainlock_handler, const CBlockIndex* pindexPrev,
126+
uint32_t& bestCLHeightDiff, CBLSSignature& bestCLSignature)
127+
{
128+
auto best_clsig = chainlock_handler.GetBestChainLock();
129+
if (best_clsig.getHeight() < Params().GetConsensus().DeploymentHeight(Consensus::DEPLOYMENT_V19)) {
130+
// We don't want legacy BLS ChainLocks in CbTx (can happen on regtest/devenets)
131+
best_clsig = llmq::CChainLockSig{};
132+
}
133+
if (best_clsig.getHeight() == pindexPrev->nHeight) {
134+
// Our best CL is the newest one possible
135+
bestCLHeightDiff = 0;
136+
bestCLSignature = best_clsig.getSig();
137+
return true;
138+
}
139+
140+
auto prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindexPrev);
141+
if (prevBlockCoinbaseChainlock.has_value()) {
142+
// Previous block Coinbase contains a non-null CL: We must insert the same sig or a better (newest) one
143+
if (best_clsig.IsNull()) {
144+
// We don't know any CL, therefore inserting the CL of the previous block
145+
bestCLHeightDiff = prevBlockCoinbaseChainlock->second + 1;
146+
bestCLSignature = prevBlockCoinbaseChainlock->first;
147+
return true;
148+
}
149+
150+
// We check if our best CL is newer than the one from previous block Coinbase
151+
int curCLHeight = best_clsig.getHeight();
152+
int prevCLHeight = pindexPrev->nHeight - static_cast<int>(prevBlockCoinbaseChainlock->second) - 1;
153+
if (curCLHeight < prevCLHeight) {
154+
// Our best CL isn't newer: inserting CL from previous block
155+
bestCLHeightDiff = prevBlockCoinbaseChainlock->second + 1;
156+
bestCLSignature = prevBlockCoinbaseChainlock->first;
157+
}
158+
else {
159+
// Our best CL is newer
160+
bestCLHeightDiff = pindexPrev->nHeight - best_clsig.getHeight();
161+
bestCLSignature = best_clsig.getSig();
162+
}
163+
164+
return true;
165+
}
166+
else {
167+
// Previous block Coinbase has no CL. We can either insert null or any valid CL
168+
if (best_clsig.IsNull()) {
169+
// We don't know any CL, therefore inserting a null CL
170+
bestCLHeightDiff = 0;
171+
bestCLSignature.Reset();
172+
return false;
173+
}
174+
175+
// Inserting our best CL
176+
bestCLHeightDiff = pindexPrev->nHeight - best_clsig.getHeight();
177+
bestCLSignature = chainlock_handler.GetBestChainLock().getSig();
178+
179+
return true;
180+
}
181+
}
182+
183+
124184
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
125185
{
126186
int64_t nTimeStart = GetTimeMicros();

test/lint/lint-circular-dependencies.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
"evo/netinfo -> evo/providertx -> evo/netinfo",
4848
"evo/simplifiedmns -> llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns",
4949
"core_io -> evo/assetlocktx -> llmq/signing -> net_processing -> evo/simplifiedmns -> core_io",
50-
"evo/cbtx -> llmq/chainlocks -> llmq/instantsend -> net_processing -> evo/simplifiedmns -> evo/cbtx",
5150
"evo/specialtxman -> validation -> evo/specialtxman",
5251
"governance/governance -> governance/object -> governance/governance",
5352
"governance/governance -> masternode/sync -> governance/governance",

0 commit comments

Comments
 (0)