Skip to content

Commit 4c00d98

Browse files
codablockUdjinM6
authored andcommitted
Allow re-signing of IS locks when performing retroactive signing (#3219)
* Implement re-signing of InstantSend inputs when TXs come in via blocks * Use GetAdjustedTime instead of GetTimeMillis in CSigSharesManager This allows use of mocktime in tests. * Expose verifiedProRegTxHash in getpeerinfo and implement wait_for_mnauth * Allow to wait for IS and CL to NOT happen * Bump timeout for wait_for_instantlock * Implement tests for retroactive signing of IS and CLs * Add wait_for_tx function to DashTestFramework * Add -whitelist=127.0.0.1 to node0 * Use node3 for isolated block generation * Don't test for non-receival of TXs on node4/node5
1 parent b4b9d34 commit 4c00d98

File tree

14 files changed

+307
-41
lines changed

14 files changed

+307
-41
lines changed

src/llmq/quorums_instantsend.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ void CInstantSendManager::InterruptWorkerThread()
371371
workInterrupt();
372372
}
373373

374-
bool CInstantSendManager::ProcessTx(const CTransaction& tx, const Consensus::Params& params)
374+
bool CInstantSendManager::ProcessTx(const CTransaction& tx, bool allowReSigning, const Consensus::Params& params)
375375
{
376376
if (!IsInstantSendEnabled()) {
377377
return true;
@@ -441,7 +441,7 @@ bool CInstantSendManager::ProcessTx(const CTransaction& tx, const Consensus::Par
441441
return false;
442442
}
443443
}
444-
if (alreadyVotedCount == ids.size()) {
444+
if (!allowReSigning && alreadyVotedCount == ids.size()) {
445445
LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s: already voted on all inputs, bailing out\n", __func__,
446446
tx.GetHash().ToString());
447447
return true;
@@ -454,9 +454,9 @@ bool CInstantSendManager::ProcessTx(const CTransaction& tx, const Consensus::Par
454454
auto& in = tx.vin[i];
455455
auto& id = ids[i];
456456
inputRequestIds.emplace(id);
457-
LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s: trying to vote on input %s with id %s\n", __func__,
458-
tx.GetHash().ToString(), in.prevout.ToStringShort(), id.ToString());
459-
if (quorumSigningManager->AsyncSignIfMember(llmqType, id, tx.GetHash())) {
457+
LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s: trying to vote on input %s with id %s. allowReSigning=%d\n", __func__,
458+
tx.GetHash().ToString(), in.prevout.ToStringShort(), id.ToString(), allowReSigning);
459+
if (quorumSigningManager->AsyncSignIfMember(llmqType, id, tx.GetHash(), allowReSigning)) {
460460
LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s: voted on input %s with id %s\n", __func__,
461461
tx.GetHash().ToString(), in.prevout.ToStringShort(), id.ToString());
462462
}
@@ -961,7 +961,7 @@ void CInstantSendManager::UpdateWalletTransaction(const CTransactionRef& tx, con
961961
mempool.AddTransactionsUpdated(1);
962962
}
963963

964-
void CInstantSendManager::ProcessNewTransaction(const CTransactionRef& tx, const CBlockIndex* pindex)
964+
void CInstantSendManager::ProcessNewTransaction(const CTransactionRef& tx, const CBlockIndex* pindex, bool allowReSigning)
965965
{
966966
if (!IsInstantSendEnabled()) {
967967
return;
@@ -989,7 +989,7 @@ void CInstantSendManager::ProcessNewTransaction(const CTransactionRef& tx, const
989989

990990
bool chainlocked = pindex && chainLocksHandler->HasChainLock(pindex->nHeight, pindex->GetBlockHash());
991991
if (islockHash.IsNull() && !chainlocked) {
992-
ProcessTx(*tx, Params().GetConsensus());
992+
ProcessTx(*tx, allowReSigning, Params().GetConsensus());
993993
}
994994

995995
LOCK(cs);
@@ -1004,7 +1004,7 @@ void CInstantSendManager::ProcessNewTransaction(const CTransactionRef& tx, const
10041004

10051005
void CInstantSendManager::TransactionAddedToMempool(const CTransactionRef& tx)
10061006
{
1007-
ProcessNewTransaction(tx, nullptr);
1007+
ProcessNewTransaction(tx, nullptr, false);
10081008
}
10091009

10101010
void CInstantSendManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted)
@@ -1021,7 +1021,7 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr<const CBlock>& pb
10211021
}
10221022

10231023
for (const auto& tx : pblock->vtx) {
1024-
ProcessNewTransaction(tx, pindex);
1024+
ProcessNewTransaction(tx, pindex, true);
10251025
}
10261026
}
10271027

@@ -1400,7 +1400,7 @@ bool CInstantSendManager::ProcessPendingRetryLockTxs()
14001400
tx->GetHash().ToString());
14011401
}
14021402

1403-
ProcessTx(*tx, Params().GetConsensus());
1403+
ProcessTx(*tx, false, Params().GetConsensus());
14041404
retryCount++;
14051405
}
14061406

src/llmq/quorums_instantsend.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class CInstantSendManager : public CRecoveredSigsListener
120120
void InterruptWorkerThread();
121121

122122
public:
123-
bool ProcessTx(const CTransaction& tx, const Consensus::Params& params);
123+
bool ProcessTx(const CTransaction& tx, bool allowReSigning, const Consensus::Params& params);
124124
bool CheckCanLock(const CTransaction& tx, bool printDebug, const Consensus::Params& params);
125125
bool CheckCanLock(const COutPoint& outpoint, bool printDebug, const uint256& txHash, CAmount* retValue, const Consensus::Params& params);
126126
bool IsLocked(const uint256& txHash);
@@ -141,7 +141,7 @@ class CInstantSendManager : public CRecoveredSigsListener
141141
void ProcessInstantSendLock(NodeId from, const uint256& hash, const CInstantSendLock& islock);
142142
void UpdateWalletTransaction(const CTransactionRef& tx, const CInstantSendLock& islock);
143143

144-
void ProcessNewTransaction(const CTransactionRef& tx, const CBlockIndex* pindex);
144+
void ProcessNewTransaction(const CTransactionRef& tx, const CBlockIndex* pindex, bool allowReSigning);
145145
void TransactionAddedToMempool(const CTransactionRef& tx);
146146
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted);
147147
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected);

src/llmq/quorums_signing.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ void CSigningManager::UnregisterRecoveredSigsListener(CRecoveredSigsListener* l)
762762
recoveredSigsListeners.erase(itRem, recoveredSigsListeners.end());
763763
}
764764

765-
bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
765+
bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash, bool allowReSign)
766766
{
767767
auto& params = Params().GetConsensus().llmqs.at(llmqType);
768768

@@ -773,24 +773,31 @@ bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint
773773
{
774774
LOCK(cs);
775775

776-
if (db.HasVotedOnId(llmqType, id)) {
776+
bool hasVoted = db.HasVotedOnId(llmqType, id);
777+
if (hasVoted) {
777778
uint256 prevMsgHash;
778779
db.GetVoteForId(llmqType, id, prevMsgHash);
779780
if (msgHash != prevMsgHash) {
780781
LogPrintf("CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting on conflicting msgHash=%s\n", __func__,
781782
id.ToString(), prevMsgHash.ToString(), msgHash.ToString());
783+
return false;
784+
} else if (allowReSign) {
785+
LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already voted for id=%s and msgHash=%s. Resigning!\n", __func__,
786+
id.ToString(), prevMsgHash.ToString());
782787
} else {
783788
LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting again.\n", __func__,
784789
id.ToString(), prevMsgHash.ToString());
790+
return false;
785791
}
786-
return false;
787792
}
788793

789794
if (db.HasRecoveredSigForId(llmqType, id)) {
790795
// no need to sign it if we already have a recovered sig
791796
return true;
792797
}
793-
db.WriteVoteForId(llmqType, id, msgHash);
798+
if (!hasVoted) {
799+
db.WriteVoteForId(llmqType, id, msgHash);
800+
}
794801
}
795802

796803
int tipHeight;
@@ -814,6 +821,10 @@ bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint
814821
return false;
815822
}
816823

824+
if (allowReSign) {
825+
// make us re-announce all known shares (other nodes might have run into a timeout)
826+
quorumSigSharesManager->ForceReAnnouncement(quorum, llmqType, id, msgHash);
827+
}
817828
quorumSigSharesManager->AsyncSign(quorum, id, msgHash);
818829

819830
return true;

src/llmq/quorums_signing.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class CSigningManager
170170
void RegisterRecoveredSigsListener(CRecoveredSigsListener* l);
171171
void UnregisterRecoveredSigsListener(CRecoveredSigsListener* l);
172172

173-
bool AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
173+
bool AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash, bool allowReSign = false);
174174
bool HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
175175
bool HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id);
176176
bool HasRecoveredSigForSession(const uint256& signHash);

src/llmq/quorums_signing_shares.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ void CSigSharesInv::Set(uint16_t quorumMember, bool v)
8282
inv[quorumMember] = v;
8383
}
8484

85+
void CSigSharesInv::SetAll(bool v)
86+
{
87+
for (size_t i = 0; i < inv.size(); i++) {
88+
inv[i] = v;
89+
}
90+
}
91+
8592
std::string CBatchedSigShares::ToInvString() const
8693
{
8794
CSigSharesInv inv;
@@ -678,7 +685,7 @@ void CSigSharesManager::ProcessSigShare(NodeId nodeId, const CSigShare& sigShare
678685
sigSharesToAnnounce.Add(sigShare.GetKey(), true);
679686

680687
// Update the time we've seen the last sigShare
681-
timeSeenForSessions[sigShare.GetSignHash()] = GetTimeMillis();
688+
timeSeenForSessions[sigShare.GetSignHash()] = GetAdjustedTime();
682689

683690
if (!quorumNodes.empty()) {
684691
// don't announce and wait for other nodes to request this share and directly send it to them
@@ -777,7 +784,7 @@ void CSigSharesManager::CollectSigSharesToRequest(std::unordered_map<NodeId, std
777784
{
778785
AssertLockHeld(cs);
779786

780-
int64_t now = GetTimeMillis();
787+
int64_t now = GetAdjustedTime();
781788
const size_t maxRequestsForNode = 32;
782789

783790
// avoid requesting from same nodes all the time
@@ -1143,8 +1150,8 @@ CSigShare CSigSharesManager::RebuildSigShare(const CSigSharesNodeState::SessionI
11431150

11441151
void CSigSharesManager::Cleanup()
11451152
{
1146-
int64_t now = GetTimeMillis();
1147-
if (now - lastCleanupTime < 5000) {
1153+
int64_t now = GetAdjustedTime();
1154+
if (now - lastCleanupTime < 5) {
11481155
return;
11491156
}
11501157

@@ -1265,7 +1272,7 @@ void CSigSharesManager::Cleanup()
12651272
nodeStates.erase(nodeId);
12661273
}
12671274

1268-
lastCleanupTime = GetTimeMillis();
1275+
lastCleanupTime = GetAdjustedTime();
12691276
}
12701277

12711278
void CSigSharesManager::RemoveSigSharesForSession(const uint256& signHash)
@@ -1426,6 +1433,31 @@ void CSigSharesManager::Sign(const CQuorumCPtr& quorum, const uint256& id, const
14261433
ProcessSigShare(-1, sigShare, *g_connman, quorum);
14271434
}
14281435

1436+
// causes all known sigShares to be re-announced
1437+
void CSigSharesManager::ForceReAnnouncement(const CQuorumCPtr& quorum, Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
1438+
{
1439+
LOCK(cs);
1440+
auto signHash = CLLMQUtils::BuildSignHash(llmqType, quorum->qc.quorumHash, id, msgHash);
1441+
auto sigs = sigShares.GetAllForSignHash(signHash);
1442+
if (sigs) {
1443+
for (auto& p : *sigs) {
1444+
// re-announce every sigshare to every node
1445+
sigSharesToAnnounce.Add(std::make_pair(signHash, p.first), true);
1446+
}
1447+
}
1448+
for (auto& p : nodeStates) {
1449+
CSigSharesNodeState& nodeState = p.second;
1450+
auto session = nodeState.GetSessionBySignHash(signHash);
1451+
if (!session) {
1452+
continue;
1453+
}
1454+
// pretend that the other node doesn't know about any shares so that we re-announce everything
1455+
session->knows.SetAll(false);
1456+
// we need to use a new session id as we don't know if the other node has run into a timeout already
1457+
session->sendSessionId = (uint32_t)-1;
1458+
}
1459+
}
1460+
14291461
void CSigSharesManager::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
14301462
{
14311463
LOCK(cs);

src/llmq/quorums_signing_shares.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class CSigSharesInv
104104
void Init(size_t size);
105105
bool IsSet(uint16_t quorumMember) const;
106106
void Set(uint16_t quorumMember, bool v);
107+
void SetAll(bool v);
107108
void Merge(const CSigSharesInv& inv2);
108109

109110
size_t CountSet() const;
@@ -328,8 +329,8 @@ class CSigSharesNodeState
328329

329330
class CSigSharesManager : public CRecoveredSigsListener
330331
{
331-
static const int64_t SESSION_NEW_SHARES_TIMEOUT = 60 * 1000;
332-
static const int64_t SIG_SHARE_REQUEST_TIMEOUT = 5 * 1000;
332+
static const int64_t SESSION_NEW_SHARES_TIMEOUT = 60;
333+
static const int64_t SIG_SHARE_REQUEST_TIMEOUT = 5;
333334

334335
// we try to keep total message size below 10k
335336
const size_t MAX_MSGS_CNT_QSIGSESANN = 100;
@@ -376,6 +377,7 @@ class CSigSharesManager : public CRecoveredSigsListener
376377

377378
void AsyncSign(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash);
378379
void Sign(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash);
380+
void ForceReAnnouncement(const CQuorumCPtr& quorum, Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
379381

380382
void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);
381383

src/net.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,11 @@ void CNode::copyStats(CNodeStats &stats)
716716
// Leave string empty if addrLocal invalid (not filled in yet)
717717
CService addrLocalUnlocked = GetAddrLocal();
718718
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";
719+
720+
{
721+
LOCK(cs_mnauth);
722+
X(verifiedProRegTxHash);
723+
}
719724
}
720725
#undef X
721726

src/net.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ class CNodeStats
694694
CAddress addr;
695695
// Bind address of our side of the connection
696696
CAddress addrBind;
697+
// In case this is a verified MN, this value is the proTx of the MN
698+
uint256 verifiedProRegTxHash;
697699
};
698700

699701

src/rpc/net.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
8080
" \"addrbind\":\"ip:port\", (string) Bind address of the connection to the peer\n"
8181
" \"addrlocal\":\"ip:port\", (string) Local address as reported by the peer\n"
8282
" \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n"
83+
" \"verified_proregtx_hash\": h, (hex) Only present when the peer is a masternode and succesfully\n"
84+
" autheticated via MNAUTH. In this case, this field contains the\n"
85+
" protx hash of the masternode\n"
8386
" \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n"
8487
" \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n"
8588
" \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n"
@@ -138,6 +141,9 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
138141
if (stats.addrBind.IsValid())
139142
obj.push_back(Pair("addrbind", stats.addrBind.ToString()));
140143
obj.push_back(Pair("services", strprintf("%016x", stats.nServices)));
144+
if (!stats.verifiedProRegTxHash.IsNull()) {
145+
obj.push_back(Pair("verified_proregtx_hash", stats.verifiedProRegTxHash.ToString()));
146+
}
141147
obj.push_back(Pair("relaytxes", stats.fRelayTxes));
142148
obj.push_back(Pair("lastsend", stats.nLastSend));
143149
obj.push_back(Pair("lastrecv", stats.nLastRecv));

test/functional/llmq-chainlocks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def run_test(self):
129129
# for the mined TXs, which will then allow the network to create a CLSIG
130130
self.log.info("Reenable network on first node and wait for chainlock")
131131
reconnect_isolated_node(self.nodes[0], 1)
132-
self.wait_for_chainlocked_block(self.nodes[0], self.nodes[0].getbestblockhash(), 30)
132+
self.wait_for_chainlocked_block(self.nodes[0], self.nodes[0].getbestblockhash(), timeout=30)
133133

134134
def create_chained_txs(self, node, amount):
135135
txid = node.sendtoaddress(node.getnewaddress(), amount)

0 commit comments

Comments
 (0)