Skip to content

Commit

Permalink
Remove recovered sigs from the LLMQ db when corresponding IS locks ge…
Browse files Browse the repository at this point in the history
…t 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
  • Loading branch information
codablock authored and panleone committed Nov 10, 2024
1 parent 4da73ff commit d06d7db
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 21 deletions.
76 changes: 55 additions & 21 deletions src/llmq/quorums_signing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,15 @@ void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
{
CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);

uint32_t curTime = GetAdjustedTime();

// we put these close to each other to leverage leveldb's key compaction
// this way, the second key can be used for fast HasRecoveredSig checks while the first key stores the recSig
auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
batch.Write(k1, recSig);
batch.Write(k2, (uint8_t)1);
// 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
batch.Write(k2, curTime);

// store by object hash
auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
Expand All @@ -228,7 +231,7 @@ void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
batch.Write(k4, (uint8_t)1);

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

db.WriteBatch(batch);
Expand All @@ -243,6 +246,50 @@ void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
}
}

void CRecoveredSigsDb::RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteTimeKey)
{
AssertLockHeld(cs);

CRecoveredSig recSig;
if (!ReadRecoveredSig(llmqType, id, recSig)) {
return;
}

auto signHash = llmq::utils::BuildSignHash(recSig);

auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
auto k4 = std::make_tuple(std::string("rs_s"), signHash);
batch.Erase(k1);
batch.Erase(k2);
batch.Erase(k3);
batch.Erase(k4);

if (deleteTimeKey) {
CDataStream writeTimeDs(SER_DISK, CLIENT_VERSION);
// TODO remove the size() == sizeof(uint32_t) in a future version (when we stop supporting upgrades from < 0.14.1)
if (db.ReadDataStream(k2, writeTimeDs) && writeTimeDs.size() == sizeof(uint32_t)) {
uint32_t writeTime;
writeTimeDs >> writeTime;
auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32(writeTime), recSig.llmqType, recSig.id);
batch.Erase(k5);
}
}

hasSigForIdCache.erase(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id));
hasSigForSessionCache.erase(signHash);
hasSigForHashCache.erase(recSig.GetHash());
}

void CRecoveredSigsDb::RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
{
LOCK(cs);
CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
RemoveRecoveredSig(batch, llmqType, id, true);
db.WriteBatch(batch);
}

void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
{
std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
Expand Down Expand Up @@ -279,25 +326,7 @@ void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
{
LOCK(cs);
for (auto& e : toDelete) {
CRecoveredSig recSig;
if (!ReadRecoveredSig(e.first, e.second, recSig)) {
continue;
}

auto signHash = llmq::utils::BuildSignHash(recSig);

auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
auto k4 = std::make_tuple(std::string("rs_s"), signHash);
batch.Erase(k1);
batch.Erase(k2);
batch.Erase(k3);
batch.Erase(k4);

hasSigForIdCache.erase(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id));
hasSigForSessionCache.erase(signHash);
hasSigForHashCache.erase(recSig.GetHash());
RemoveRecoveredSig(batch, e.first, e.second, false);

if (batch.SizeEstimate() >= (1 << 24)) {
db.WriteBatch(batch);
Expand Down Expand Up @@ -641,6 +670,11 @@ void CSigningManager::ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& re
}
}

void CSigningManager::RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
{
db.RemoveRecoveredSig(llmqType, id);
}

void CSigningManager::Cleanup()
{
int64_t now = GetTimeMillis();
Expand Down
6 changes: 6 additions & 0 deletions src/llmq/quorums_signing.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class CRecoveredSigsDb
bool GetRecoveredSigByHash(const uint256& hash, CRecoveredSig& ret);
bool GetRecoveredSigById(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret);
void WriteRecoveredSig(const CRecoveredSig& recSig);
void RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);

void CleanupOldRecoveredSigs(int64_t maxAge);

Expand All @@ -89,6 +90,7 @@ class CRecoveredSigsDb

private:
bool ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret);
void RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteTimeKey);
};

class CRecoveredSigsListener
Expand Down Expand Up @@ -132,6 +134,10 @@ class CSigningManager

void ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);

// This is called when a recovered signature can be safely removed from the DB. This is only safe when some other
// mechanism prevents possible conflicts. As an example, ChainLocks prevent conflicts in confirmed TXs InstantSend votes
void RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);

private:
void ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman);
bool PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan);
Expand Down

0 comments on commit d06d7db

Please sign in to comment.