Skip to content

Commit c2335e9

Browse files
Merge #6815: refactor: follow-up dash#6761, implement review suggestions, ChainLock and InstantSend refactoring
34a90e6 chore: remove unused `IsInvInFilter` from `PeerManager` interface (Kittywhiskers Van Gogh) f3224ae refactor: consolidate `INPUTLOCK_REQUESTID_PREFIX` usage to `lock.cpp` (Kittywhiskers Van Gogh) 7b4ee6b trivial: document transaction confirmation safety threshold (Kittywhiskers Van Gogh) 25f05c1 refactor: make unknown block clsig flow easier to follow (Kittywhiskers Van Gogh) 9578146 refactor: document `pindex` assumptions in chainlocks code (Kittywhiskers Van Gogh) 4a744c7 refactor: use `std::chrono` for time variables, reduce resolution (Kittywhiskers Van Gogh) b051c22 refactor: consolidate `CLSIG_REQUESTID_PREFIX` usage to `clsig.cpp` (Kittywhiskers Van Gogh) 024b466 chore: move lock annotations in `chainlock.h` to the next line (Kittywhiskers Van Gogh) c6e99fb chore: apply most `clang-format` suggestions (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Depends on #6761 * Dependency for #6821 * Assumptions surrounding `pindex` usage have been documented in response to reviewer comments in [dash#6761](#6761) ([comment](#6761 (comment)), [comment](#6761 (comment))). * The internal structures of `CChainLocksHandler` (`txFirstSeenTime`, `seenChainLocks`, `lastCleanupTime`) have their time resolution reduced from milliseconds to seconds while migrating to `std::chrono`. * `CInstantSendManager::AskNodesForLockedTx()` was moved as `PeerManagerImpl::AskPeersForTransaction()` in [dash#6425](#6425) and with that, the sole external usage of `PeerManagerImpl::IsInvInFilter()` was moved internally, we can therefore safely remove it from the `PeerManager` interface. ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)** - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK 34a90e6 Tree-SHA512: 28b532cb5b8b5e6d7c1331c7c6c0ecd4d45b186922c279db6d2d3e8974d422ec8b67d75aeadce77986d409a8ed071e85359ee08609e0c2dde657e4520c546817
2 parents eb9d7d9 + 34a90e6 commit c2335e9

File tree

12 files changed

+146
-113
lines changed

12 files changed

+146
-113
lines changed

src/chainlock/chainlock.cpp

Lines changed: 70 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ using node::GetTransaction;
3232

3333
namespace llmq {
3434
namespace {
35-
static constexpr int64_t CLEANUP_INTERVAL{1000 * 30};
36-
static constexpr int64_t CLEANUP_SEEN_TIMEOUT{24 * 60 * 60 * 1000};
35+
static constexpr auto CLEANUP_INTERVAL{30s};
36+
static constexpr auto CLEANUP_SEEN_TIMEOUT{24h};
3737
//! How long to wait for islocks until we consider a block with non-islocked TXs to be safe to sign
38-
static constexpr int64_t WAIT_FOR_ISLOCK_TIMEOUT{10 * 60};
38+
static constexpr auto WAIT_FOR_ISLOCK_TIMEOUT{10min};
3939
} // anonymous namespace
4040

4141
bool AreChainLocksEnabled(const CSporkManager& sporkman)
@@ -71,15 +71,17 @@ void CChainLocksHandler::Start(const llmq::CInstantSendManager& isman)
7171
if (m_signer) {
7272
m_signer->Start();
7373
}
74-
scheduler->scheduleEvery([&]() {
75-
CheckActiveState();
76-
EnforceBestChainLock();
77-
Cleanup();
78-
// regularly retry signing the current chaintip as it might have failed before due to missing islocks
79-
if (m_signer) {
80-
m_signer->TrySignChainTip(isman);
81-
}
82-
}, std::chrono::seconds{5});
74+
scheduler->scheduleEvery(
75+
[&]() {
76+
CheckActiveState();
77+
EnforceBestChainLock();
78+
Cleanup();
79+
// regularly retry signing the current chaintip as it might have failed before due to missing islocks
80+
if (m_signer) {
81+
m_signer->TrySignChainTip(isman);
82+
}
83+
},
84+
std::chrono::seconds{5});
8385
}
8486

8587
void CChainLocksHandler::Stop()
@@ -131,7 +133,7 @@ MessageProcessingResult CChainLocksHandler::ProcessNewChainLock(const NodeId fro
131133

132134
{
133135
LOCK(cs);
134-
if (!seenChainLocks.emplace(hash, TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now())).second) {
136+
if (!seenChainLocks.emplace(hash, GetTime<std::chrono::seconds>()).second) {
135137
return {};
136138
}
137139

@@ -142,61 +144,59 @@ MessageProcessingResult CChainLocksHandler::ProcessNewChainLock(const NodeId fro
142144
}
143145

144146
if (const auto ret = VerifyChainLock(clsig); ret != VerifyRecSigStatus::Valid) {
145-
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- invalid CLSIG (%s), status=%d peer=%d\n", __func__, clsig.ToString(), ToUnderlying(ret), from);
147+
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- invalid CLSIG (%s), status=%d peer=%d\n", __func__,
148+
clsig.ToString(), ToUnderlying(ret), from);
146149
if (from != -1) {
147150
return MisbehavingError{10};
148151
}
149152
return {};
150153
}
151154

152155
const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(clsig.getBlockHash()));
156+
const CInv clsig_inv(MSG_CLSIG, hash);
153157

154158
{
155159
LOCK(cs);
156160
bestChainLockHash = hash;
157161
bestChainLock = clsig;
158162

159-
if (pindex != nullptr) {
160-
163+
if (pindex) {
161164
if (pindex->nHeight != clsig.getHeight()) {
162165
// Should not happen, same as the conflict check from above.
163166
LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
164-
__func__, clsig.ToString(), pindex->nHeight);
167+
__func__, clsig.ToString(), pindex->nHeight);
165168
// Note: not relaying clsig here
166169
return {};
167170
}
168-
169171
bestChainLockWithKnownBlock = bestChainLock;
170172
bestChainLockBlockIndex = pindex;
173+
} else {
174+
// We don't know the block/header for this CLSIG yet, so bail out for now and when the
175+
// block/header later comes in, we will enforce the correct chain. We still relay further.
176+
return clsig_inv;
171177
}
172-
// else if (pindex == nullptr)
173-
// Note: make sure to still relay clsig further.
174178
}
175179

176-
CInv clsigInv(MSG_CLSIG, hash);
177-
178-
if (pindex == nullptr) {
179-
// we don't know the block/header for this CLSIG yet, so bail out for now
180-
// when the block or the header later comes in, we will enforce the correct chain
181-
return clsigInv;
182-
}
180+
scheduler->scheduleFromNow(
181+
[&]() {
182+
CheckActiveState();
183+
EnforceBestChainLock();
184+
},
185+
std::chrono::seconds{0});
183186

184-
scheduler->scheduleFromNow([&]() {
185-
CheckActiveState();
186-
EnforceBestChainLock();
187-
}, std::chrono::seconds{0});
187+
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n", __func__,
188+
clsig.ToString(), from);
188189

189-
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
190-
__func__, clsig.ToString(), from);
191-
return clsigInv;
190+
return clsig_inv;
192191
}
193192

194193
void CChainLocksHandler::AcceptedBlockHeader(gsl::not_null<const CBlockIndex*> pindexNew)
195194
{
196195
LOCK(cs);
197196

198197
if (pindexNew->GetBlockHash() == bestChainLock.getBlockHash()) {
199-
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());
198+
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n",
199+
__func__, pindexNew->GetBlockHash().ToString());
200200

201201
if (bestChainLock.getHeight() != pindexNew->nHeight) {
202202
// Should not happen, same as the conflict check from ProcessNewChainLock.
@@ -220,15 +220,17 @@ void CChainLocksHandler::UpdatedBlockTip(const llmq::CInstantSendManager& isman)
220220
// EnforceBestChainLock switching chains.
221221
// atomic[If tryLockChainTipScheduled is false, do (set it to true] and schedule signing).
222222
if (bool expected = false; tryLockChainTipScheduled.compare_exchange_strong(expected, true)) {
223-
scheduler->scheduleFromNow([&]() {
224-
CheckActiveState();
225-
EnforceBestChainLock();
226-
Cleanup();
227-
if (m_signer) {
228-
m_signer->TrySignChainTip(isman);
229-
}
230-
tryLockChainTipScheduled = false;
231-
}, std::chrono::seconds{0});
223+
scheduler->scheduleFromNow(
224+
[&]() {
225+
CheckActiveState();
226+
EnforceBestChainLock();
227+
Cleanup();
228+
if (m_signer) {
229+
m_signer->TrySignChainTip(isman);
230+
}
231+
tryLockChainTipScheduled = false;
232+
},
233+
std::chrono::seconds{0});
232234
}
233235
}
234236

@@ -281,7 +283,8 @@ void CChainLocksHandler::BlockConnected(const std::shared_ptr<const CBlock>& pbl
281283
}
282284
}
283285

284-
void CChainLocksHandler::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, gsl::not_null<const CBlockIndex*> pindexDisconnected)
286+
void CChainLocksHandler::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock,
287+
gsl::not_null<const CBlockIndex*> pindexDisconnected)
285288
{
286289
if (m_signer) {
287290
m_signer->EraseFromBlockHashTxidMap(pindexDisconnected->GetBlockHash());
@@ -297,16 +300,16 @@ int32_t CChainLocksHandler::GetBestChainLockHeight() const
297300

298301
bool CChainLocksHandler::IsTxSafeForMining(const uint256& txid) const
299302
{
300-
int64_t txAge = 0;
303+
auto tx_age{0s};
301304
{
302305
LOCK(cs);
303306
auto it = txFirstSeenTime.find(txid);
304307
if (it != txFirstSeenTime.end()) {
305-
txAge = GetTime<std::chrono::seconds>().count() - it->second;
308+
tx_age = GetTime<std::chrono::seconds>() - it->second;
306309
}
307310
}
308311

309-
return txAge >= WAIT_FOR_ISLOCK_TIMEOUT;
312+
return tx_age >= WAIT_FOR_ISLOCK_TIMEOUT;
310313
}
311314

312315
// WARNING: cs_main and cs should not be held!
@@ -340,17 +343,21 @@ void CChainLocksHandler::EnforceBestChainLock()
340343
// Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
341344
// For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
342345
// and mark all of them as conflicting.
343-
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, pindex->GetBlockHash().ToString(), clsig->ToString());
346+
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__,
347+
pindex->GetBlockHash().ToString(), clsig->ToString());
344348
m_chainstate.EnforceBlock(dummy_state, pindex);
345349

346350

347-
if (/*activateNeeded =*/ WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight)) != currentBestChainLockBlockIndex) {
351+
if (/*activateNeeded =*/WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip()->GetAncestor(
352+
currentBestChainLockBlockIndex->nHeight)) !=
353+
currentBestChainLockBlockIndex) {
348354
if (!m_chainstate.ActivateBestChain(dummy_state)) {
349355
LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, dummy_state.ToString());
350356
return;
351357
}
352358
LOCK(::cs_main);
353-
if (m_chainstate.m_chain.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != currentBestChainLockBlockIndex) {
359+
if (m_chainstate.m_chain.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) !=
360+
currentBestChainLockBlockIndex) {
354361
return;
355362
}
356363
}
@@ -369,9 +376,10 @@ void CChainLocksHandler::EnforceBestChainLock()
369376
VerifyRecSigStatus CChainLocksHandler::VerifyChainLock(const chainlock::ChainLockSig& clsig) const
370377
{
371378
const auto llmqType = Params().GetConsensus().llmqTypeChainLocks;
372-
const uint256 nRequestId = ::SerializeHash(std::make_pair(chainlock::CLSIG_REQUESTID_PREFIX, clsig.getHeight()));
379+
const uint256 nRequestId = chainlock::GenSigRequestId(clsig.getHeight());
373380

374-
return llmq::VerifyRecoveredSig(llmqType, m_chainstate.m_chain, qman, clsig.getHeight(), nRequestId, clsig.getBlockHash(), clsig.getSig());
381+
return llmq::VerifyRecoveredSig(llmqType, m_chainstate.m_chain, qman, clsig.getHeight(), nRequestId,
382+
clsig.getBlockHash(), clsig.getSig());
375383
}
376384

377385
bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash) const
@@ -431,15 +439,15 @@ void CChainLocksHandler::Cleanup()
431439
return;
432440
}
433441

434-
if (TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now()) - lastCleanupTime < CLEANUP_INTERVAL) {
442+
if (GetTime<std::chrono::seconds>() - lastCleanupTime.load() < CLEANUP_INTERVAL) {
435443
return;
436444
}
437-
lastCleanupTime = TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now());
445+
lastCleanupTime = GetTime<std::chrono::seconds>();
438446

439447
{
440448
LOCK(cs);
441-
for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
442-
if (TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now()) - it->second >= CLEANUP_SEEN_TIMEOUT) {
449+
for (auto it = seenChainLocks.begin(); it != seenChainLocks.end();) {
450+
if (GetTime<std::chrono::seconds>() - it->second >= CLEANUP_SEEN_TIMEOUT) {
443451
it = seenChainLocks.erase(it);
444452
} else {
445453
++it;
@@ -459,15 +467,17 @@ void CChainLocksHandler::Cleanup()
459467

460468
LOCK(::cs_main);
461469
LOCK2(mempool.cs, cs); // need mempool.cs due to GetTransaction calls
462-
for (auto it = txFirstSeenTime.begin(); it != txFirstSeenTime.end(); ) {
470+
for (auto it = txFirstSeenTime.begin(); it != txFirstSeenTime.end();) {
463471
uint256 hashBlock;
464472
if (auto tx = GetTransaction(nullptr, &mempool, it->first, Params().GetConsensus(), hashBlock); !tx) {
465473
// tx has vanished, probably due to conflicts
466474
it = txFirstSeenTime.erase(it);
467475
} else if (!hashBlock.IsNull()) {
468476
const auto* pindex = m_chainstate.m_blockman.LookupBlockIndex(hashBlock);
469-
if (m_chainstate.m_chain.Tip()->GetAncestor(pindex->nHeight) == pindex && m_chainstate.m_chain.Height() - pindex->nHeight >= 6) {
470-
// tx got confirmed >= 6 times, so we can stop keeping track of it
477+
assert(pindex); // GetTransaction gave us that hashBlock, it should resolve to a valid block index
478+
if (m_chainstate.m_chain.Tip()->GetAncestor(pindex->nHeight) == pindex &&
479+
m_chainstate.m_chain.Height() - pindex->nHeight > chainlock::TX_CONFIRM_THRESHOLD) {
480+
// tx is sufficiently deep, we can stop tracking it
471481
it = txFirstSeenTime.erase(it);
472482
} else {
473483
++it;

src/chainlock/chainlock.h

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ class CChainLocksHandler final : public chainlock::ChainLockSignerParent
5757
chainlock::ChainLockSig bestChainLock GUARDED_BY(cs);
5858

5959
chainlock::ChainLockSig bestChainLockWithKnownBlock GUARDED_BY(cs);
60-
const CBlockIndex* bestChainLockBlockIndex GUARDED_BY(cs) {nullptr};
61-
const CBlockIndex* lastNotifyChainLockBlockIndex GUARDED_BY(cs) {nullptr};
60+
const CBlockIndex* bestChainLockBlockIndex GUARDED_BY(cs){nullptr};
61+
const CBlockIndex* lastNotifyChainLockBlockIndex GUARDED_BY(cs){nullptr};
6262

63-
std::unordered_map<uint256, int64_t, StaticSaltedHasher> txFirstSeenTime GUARDED_BY(cs);
63+
std::unordered_map<uint256, std::chrono::seconds, StaticSaltedHasher> txFirstSeenTime GUARDED_BY(cs);
6464

65-
std::map<uint256, int64_t> seenChainLocks GUARDED_BY(cs);
65+
std::map<uint256, std::chrono::seconds> seenChainLocks GUARDED_BY(cs);
6666

67-
std::atomic<int64_t> lastCleanupTime{0};
67+
std::atomic<std::chrono::seconds> lastCleanupTime{0s};
6868

6969
public:
7070
explicit CChainLocksHandler(CChainState& chainstate, CQuorumManager& _qman, CSigningManager& _sigman,
@@ -75,23 +75,32 @@ class CChainLocksHandler final : public chainlock::ChainLockSignerParent
7575
void Start(const llmq::CInstantSendManager& isman);
7676
void Stop();
7777

78-
bool AlreadyHave(const CInv& inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
79-
bool GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
80-
chainlock::ChainLockSig GetBestChainLock() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
78+
bool AlreadyHave(const CInv& inv) const
79+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
80+
bool GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const
81+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
82+
chainlock::ChainLockSig GetBestChainLock() const
83+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
8184
void UpdateTxFirstSeenMap(const std::unordered_set<uint256, StaticSaltedHasher>& tx, const int64_t& time) override
8285
EXCLUSIVE_LOCKS_REQUIRED(!cs);
8386

8487
[[nodiscard]] MessageProcessingResult ProcessNewChainLock(NodeId from, const chainlock::ChainLockSig& clsig,
8588
const uint256& hash) override
8689
EXCLUSIVE_LOCKS_REQUIRED(!cs);
8790

88-
void AcceptedBlockHeader(gsl::not_null<const CBlockIndex*> pindexNew) EXCLUSIVE_LOCKS_REQUIRED(!cs);
91+
void AcceptedBlockHeader(gsl::not_null<const CBlockIndex*> pindexNew)
92+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
8993
void UpdatedBlockTip(const llmq::CInstantSendManager& isman);
90-
void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) EXCLUSIVE_LOCKS_REQUIRED(!cs);
91-
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, gsl::not_null<const CBlockIndex*> pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs);
92-
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, gsl::not_null<const CBlockIndex*> pindexDisconnected) EXCLUSIVE_LOCKS_REQUIRED(!cs);
93-
void CheckActiveState() EXCLUSIVE_LOCKS_REQUIRED(!cs);
94-
void EnforceBestChainLock() EXCLUSIVE_LOCKS_REQUIRED(!cs);
94+
void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime)
95+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
96+
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, gsl::not_null<const CBlockIndex*> pindex)
97+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
98+
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, gsl::not_null<const CBlockIndex*> pindexDisconnected)
99+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
100+
void CheckActiveState()
101+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
102+
void EnforceBestChainLock()
103+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
95104

96105
bool HasChainLock(int nHeight, const uint256& blockHash) const override
97106
EXCLUSIVE_LOCKS_REQUIRED(!cs);
@@ -106,7 +115,8 @@ class CChainLocksHandler final : public chainlock::ChainLockSignerParent
106115
[[nodiscard]] bool IsEnabled() const override { return isEnabled; }
107116

108117
private:
109-
void Cleanup() EXCLUSIVE_LOCKS_REQUIRED(!cs);
118+
void Cleanup()
119+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
110120
};
111121

112122
bool AreChainLocksEnabled(const CSporkManager& sporkman);

src/chainlock/clsig.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
#include <tinyformat.h>
88

9+
#include <string_view>
10+
911
namespace chainlock {
10-
const std::string CLSIG_REQUESTID_PREFIX = "clsig";
12+
static constexpr std::string_view CLSIG_REQUESTID_PREFIX{"clsig"};
1113

1214
ChainLockSig::ChainLockSig() = default;
1315
ChainLockSig::~ChainLockSig() = default;
@@ -23,4 +25,9 @@ std::string ChainLockSig::ToString() const
2325
{
2426
return strprintf("ChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString());
2527
}
28+
29+
uint256 GenSigRequestId(const int32_t nHeight)
30+
{
31+
return ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, nHeight));
32+
}
2633
} // namespace chainlock

src/chainlock/clsig.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
#include <cstdint>
1414

1515
namespace chainlock {
16-
extern const std::string CLSIG_REQUESTID_PREFIX;
17-
1816
struct ChainLockSig {
1917
private:
2018
int32_t nHeight{-1};
@@ -38,6 +36,9 @@ struct ChainLockSig {
3836
READWRITE(obj.nHeight, obj.blockHash, obj.sig);
3937
}
4038
};
39+
40+
//! Generate clsig request ID with block height
41+
uint256 GenSigRequestId(const int32_t nHeight);
4142
} // namespace chainlock
4243

4344
#endif // BITCOIN_CHAINLOCK_CLSIG_H

0 commit comments

Comments
 (0)