Skip to content

Commit 8c49d9b

Browse files
committed
Remove recovered sigs from the LLMQ db when corresponding IS locks get confirmed (dashpay#3048)
* Remove unused overload of RemoveInstantSendLock * Move deletion of recovered sigs into own method * Remove recovered sigs for fully confirmed IS locks * Also remove rs_t entries when removing recovered sigs from the outside CleanupOldRecoveredSigs already does this as the last step, but when RemoveRecoveredSig is called from the outside (e.g. from InstantSend), these keys are not removed. This PR fixes this by storing the write time into rs_r and later uses it to remove the rs_t entry. Old entries will be incompatible with this (1 byte written in the past, 4 bytes written now). This checked by comparing the data size with sizeof(uint32_t). * Add TODO
1 parent 2e0cf8a commit 8c49d9b

File tree

4 files changed

+70
-29
lines changed

4 files changed

+70
-29
lines changed

src/llmq/quorums_instantsend.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@ void CInstantSendDb::WriteNewInstantSendLock(const uint256& hash, const CInstant
6161
}
6262
}
6363

64-
void CInstantSendDb::RemoveInstantSendLock(const uint256& hash, CInstantSendLockPtr islock)
65-
{
66-
CDBBatch batch(db);
67-
RemoveInstantSendLock(batch, hash, islock);
68-
db.WriteBatch(batch);
69-
}
70-
7164
void CInstantSendDb::RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock)
7265
{
7366
if (!islock) {
@@ -1106,6 +1099,8 @@ void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew)
11061099

11071100
void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex)
11081101
{
1102+
auto& consensusParams = Params().GetConsensus();
1103+
11091104
std::unordered_map<uint256, CInstantSendLockPtr> removeISLocks;
11101105
{
11111106
LOCK(cs);
@@ -1123,7 +1118,14 @@ void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex)
11231118
for (auto& in : islock->inputs) {
11241119
auto inputRequestId = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in));
11251120
inputRequestIds.erase(inputRequestId);
1121+
1122+
// no need to keep recovered sigs for fully confirmed IS locks, as there is no chance for conflicts
1123+
// from now on. All inputs are spent now and can't be spend in any other TX.
1124+
quorumSigningManager->RemoveRecoveredSig(consensusParams.llmqForInstantSend, inputRequestId);
11261125
}
1126+
1127+
// same as in the loop
1128+
quorumSigningManager->RemoveRecoveredSig(consensusParams.llmqForInstantSend, islock->GetRequestId());
11271129
}
11281130

11291131
// Find all previously unlocked TXs that got locked by this fully confirmed (ChainLock) block and remove them

src/llmq/quorums_instantsend.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ class CInstantSendDb
5353
CInstantSendDb(CDBWrapper& _db) : db(_db) {}
5454

5555
void WriteNewInstantSendLock(const uint256& hash, const CInstantSendLock& islock);
56-
void RemoveInstantSendLock(const uint256& hash, CInstantSendLockPtr islock);
5756
void RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock);
5857

5958
void WriteInstantSendLockMined(const uint256& hash, int nHeight);

src/llmq/quorums_signing.cpp

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,15 @@ void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
224224
{
225225
CDBBatch batch(db);
226226

227+
uint32_t curTime = GetAdjustedTime();
228+
227229
// we put these close to each other to leverage leveldb's key compaction
228230
// this way, the second key can be used for fast HasRecoveredSig checks while the first key stores the recSig
229231
auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
230232
auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
231233
batch.Write(k1, recSig);
232-
batch.Write(k2, (uint8_t)1);
234+
// this key is also used to store the current time, so that we can easily get to the "rs_t" key when we have the id
235+
batch.Write(k2, curTime);
233236

234237
// store by object hash
235238
auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
@@ -241,7 +244,7 @@ void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
241244
batch.Write(k4, (uint8_t)1);
242245

243246
// store by current time. Allows fast cleanup of old recSigs
244-
auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32(GetAdjustedTime()), recSig.llmqType, recSig.id);
247+
auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32(curTime), recSig.llmqType, recSig.id);
245248
batch.Write(k5, (uint8_t)1);
246249

247250
db.WriteBatch(batch);
@@ -256,6 +259,50 @@ void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
256259
}
257260
}
258261

262+
void CRecoveredSigsDb::RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteTimeKey)
263+
{
264+
AssertLockHeld(cs);
265+
266+
CRecoveredSig recSig;
267+
if (!ReadRecoveredSig(llmqType, id, recSig)) {
268+
return;
269+
}
270+
271+
auto signHash = CLLMQUtils::BuildSignHash(recSig);
272+
273+
auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
274+
auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
275+
auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
276+
auto k4 = std::make_tuple(std::string("rs_s"), signHash);
277+
batch.Erase(k1);
278+
batch.Erase(k2);
279+
batch.Erase(k3);
280+
batch.Erase(k4);
281+
282+
if (deleteTimeKey) {
283+
CDataStream writeTimeDs(SER_DISK, CLIENT_VERSION);
284+
// TODO remove the size() == sizeof(uint32_t) in a future version (when we stop supporting upgrades from < 0.14.1)
285+
if (db.ReadDataStream(k2, writeTimeDs) && writeTimeDs.size() == sizeof(uint32_t)) {
286+
uint32_t writeTime;
287+
writeTimeDs >> writeTime;
288+
auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t) htobe32(writeTime), recSig.llmqType, recSig.id);
289+
batch.Erase(k5);
290+
}
291+
}
292+
293+
hasSigForIdCache.erase(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id));
294+
hasSigForSessionCache.erase(signHash);
295+
hasSigForHashCache.erase(recSig.GetHash());
296+
}
297+
298+
void CRecoveredSigsDb::RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
299+
{
300+
LOCK(cs);
301+
CDBBatch batch(db);
302+
RemoveRecoveredSig(batch, llmqType, id, true);
303+
db.WriteBatch(batch);
304+
}
305+
259306
void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
260307
{
261308
std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
@@ -292,25 +339,7 @@ void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
292339
{
293340
LOCK(cs);
294341
for (auto& e : toDelete) {
295-
CRecoveredSig recSig;
296-
if (!ReadRecoveredSig(e.first, e.second, recSig)) {
297-
continue;
298-
}
299-
300-
auto signHash = CLLMQUtils::BuildSignHash(recSig);
301-
302-
auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
303-
auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
304-
auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
305-
auto k4 = std::make_tuple(std::string("rs_s"), signHash);
306-
batch.Erase(k1);
307-
batch.Erase(k2);
308-
batch.Erase(k3);
309-
batch.Erase(k4);
310-
311-
hasSigForIdCache.erase(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id));
312-
hasSigForSessionCache.erase(signHash);
313-
hasSigForHashCache.erase(recSig.GetHash());
342+
RemoveRecoveredSig(batch, e.first, e.second, false);
314343

315344
if (batch.SizeEstimate() >= (1 << 24)) {
316345
db.WriteBatch(batch);
@@ -680,6 +709,11 @@ void CSigningManager::PushReconstructedRecoveredSig(const llmq::CRecoveredSig& r
680709
pendingReconstructedRecoveredSigs.emplace_back(recoveredSig, quorum);
681710
}
682711

712+
void CSigningManager::RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
713+
{
714+
db.RemoveRecoveredSig(llmqType, id);
715+
}
716+
683717
void CSigningManager::Cleanup()
684718
{
685719
int64_t now = GetTimeMillis();

src/llmq/quorums_signing.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class CRecoveredSigsDb
8484
bool GetRecoveredSigByHash(const uint256& hash, CRecoveredSig& ret);
8585
bool GetRecoveredSigById(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret);
8686
void WriteRecoveredSig(const CRecoveredSig& recSig);
87+
void RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);
8788

8889
void CleanupOldRecoveredSigs(int64_t maxAge);
8990

@@ -96,6 +97,7 @@ class CRecoveredSigsDb
9697

9798
private:
9899
bool ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret);
100+
void RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteTimeKey);
99101
};
100102

101103
class CRecoveredSigsListener
@@ -144,6 +146,10 @@ class CSigningManager
144146
// This is the case for example when a signature appears as part of InstantSend or ChainLocks
145147
void PushReconstructedRecoveredSig(const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum);
146148

149+
// This is called when a recovered signature can be safely removed from the DB. This is only safe when some other
150+
// mechanism prevents possible conflicts. As an example, ChainLocks prevent conflicts in confirmed TXs InstantSend votes
151+
void RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);
152+
147153
private:
148154
void ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman);
149155
bool PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan);

0 commit comments

Comments
 (0)