upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/rpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc.cpp')
-rw-r--r--src/rpc.cpp72
1 files changed, 65 insertions, 7 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp
index 76867ab..b6b26f1 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -530,6 +530,42 @@ static std::string PayoutIdForBlock(int blockHeight, const uint256& blockHash)
530 blockHash.ToString().substr(0, 16).c_str()); 530 blockHash.ToString().substr(0, 16).c_str());
531} 531}
532 532
533// Do these two references name the same payout round?
534//
535// The block hash alone does not decide it, and every ledger lookup used to
536// assume it did. Entries grandfathered in from a file written before payouts
537// were keyed by hash all carry hash 0, so under a hash-only comparison the
538// first zero-hash entry answers for every other one: RecordPayoutTxid writes
539// a txid against the wrong round (which is then erased as "fully paid" while
540// the real debt still stands), and ReconcilePayouts drops a live in-flight
541// record because a *different* zero-hash round happened to pay that address.
542//
543// A non-zero hash is unique and decides on its own -- that is the whole point
544// of keying on it. A zero hash falls back to the height, which
545// LoadPendingPayouts supplies for any entry that can name its own maturity.
546// Mixed hash-ness never matches: refusing to match is the safe direction for
547// every caller here, because each of them treats a match as "this debt is
548// already dealt with".
549static bool SamePayoutRound(int aHeight, const uint256& aHash,
550 int bHeight, const uint256& bHash)
551{
552 if (aHash != bHash)
553 return false;
554 if (aHash != 0)
555 return true;
556 return aHeight == bHeight;
557}
558
559// An entry with neither a hash nor a height cannot be told apart from any
560// other entry in the same state, so nothing may be recorded against it: a
561// txid written to the wrong one reports a debt as settled that is still owed.
562// Such an entry is never paid and is logged instead -- refusing to act is the
563// safe failure, and no file this tree has ever written can produce one.
564static bool PayoutRoundIdentifiable(int height, const uint256& hash)
565{
566 return hash != 0 || height != 0;
567}
568
533static bool PayoutFullyPaid(const PendingPayout& pp) 569static bool PayoutFullyPaid(const PendingPayout& pp)
534{ 570{
535 for (const auto& kv : pp.amounts) 571 for (const auto& kv : pp.amounts)
@@ -938,7 +974,9 @@ static void ReconcilePayouts()
938 for (auto it = gPaying.begin(); it != gPaying.end(); ) { 974 for (auto it = gPaying.begin(); it != gPaying.end(); ) {
939 bool paid = false; 975 bool paid = false;
940 for (const PendingPayout& pp : gPendingPayouts) 976 for (const PendingPayout& pp : gPendingPayouts)
941 if (pp.blockHash == it->blockHash && pp.paidTxids.count(it->address)) { 977 if (SamePayoutRound(pp.blockHeight, pp.blockHash,
978 it->blockHeight, it->blockHash)
979 && pp.paidTxids.count(it->address)) {
942 paid = true; 980 paid = true;
943 break; 981 break;
944 } 982 }
@@ -1237,6 +1275,18 @@ static void FlushMaturePayouts()
1237 continue; 1275 continue;
1238 if (PayoutFullyPaid(pp)) 1276 if (PayoutFullyPaid(pp))
1239 continue; 1277 continue;
1278 // An entry with neither a hash nor a height cannot be told apart
1279 // from another in the same state, so a txid recorded against it
1280 // could settle the wrong debt. Paying it is the unsafe move;
1281 // leaving it standing and saying so is not.
1282 if (!PayoutRoundIdentifiable(pp.blockHeight, pp.blockHash)) {
1283 LogPrint("payout", "[payout] WARNING: ledger entry maturing at"
1284 " height %d names neither a block hash nor a height --"
1285 " refusing to pay %zu recipient(s) that cannot be told"
1286 " apart from another round's\n",
1287 pp.matureAtHeight, pp.amounts.size());
1288 continue;
1289 }
1240 ready.push_back(pp); 1290 ready.push_back(pp);
1241 } 1291 }
1242 } 1292 }
@@ -1285,8 +1335,8 @@ static void FlushMaturePayouts()
1285// amounts is a map keyed by address, so (block, address) is already unique. 1335// amounts is a map keyed by address, so (block, address) is already unique.
1286static bool SamePayment(const PayingRecord& a, const PayingRecord& b) 1336static bool SamePayment(const PayingRecord& a, const PayingRecord& b)
1287{ 1337{
1288 return a.matureAtHeight == b.matureAtHeight 1338 return SamePayoutRound(a.blockHeight, a.blockHash, b.blockHeight, b.blockHash)
1289 && a.blockHash == b.blockHash 1339 && a.matureAtHeight == b.matureAtHeight
1290 && a.address == b.address 1340 && a.address == b.address
1291 && a.amount == b.amount; 1341 && a.amount == b.amount;
1292} 1342}
@@ -1309,7 +1359,11 @@ static void ForgetPayment(const PayingRecord& r)
1309 // standing). 1359 // standing).
1310 bool stillInFlight = false; 1360 bool stillInFlight = false;
1311 for (const auto& e : gPaying) 1361 for (const auto& e : gPaying)
1312 if (e.blockHash == r.blockHash) { stillInFlight = true; break; } 1362 if (SamePayoutRound(e.blockHeight, e.blockHash,
1363 r.blockHeight, r.blockHash)) {
1364 stillInFlight = true;
1365 break;
1366 }
1313 if (!stillInFlight) 1367 if (!stillInFlight)
1314 gQueuedPayouts.erase(PayoutIdForBlock(r.blockHeight, r.blockHash)); 1368 gQueuedPayouts.erase(PayoutIdForBlock(r.blockHeight, r.blockHash));
1315 1369
@@ -1355,7 +1409,8 @@ static void RecordPayoutTxid(const PayingRecord& r, const std::string& txid)
1355 { 1409 {
1356 std::lock_guard<std::mutex> lk(gPayoutLedgerMutex); 1410 std::lock_guard<std::mutex> lk(gPayoutLedgerMutex);
1357 for (auto it = gPendingPayouts.begin(); it != gPendingPayouts.end(); ++it) { 1411 for (auto it = gPendingPayouts.begin(); it != gPendingPayouts.end(); ++it) {
1358 if (it->blockHash != r.blockHash) 1412 if (!SamePayoutRound(it->blockHeight, it->blockHash,
1413 r.blockHeight, r.blockHash))
1359 continue; 1414 continue;
1360 it->paidTxids[r.address] = txid; 1415 it->paidTxids[r.address] = txid;
1361 it->retryAfter = 0; 1416 it->retryAfter = 0;
@@ -1389,7 +1444,8 @@ static void ForfeitPayoutRecipient(const PayingRecord& r)
1389 { 1444 {
1390 std::lock_guard<std::mutex> lk(gPayoutLedgerMutex); 1445 std::lock_guard<std::mutex> lk(gPayoutLedgerMutex);
1391 for (auto it = gPendingPayouts.begin(); it != gPendingPayouts.end(); ++it) { 1446 for (auto it = gPendingPayouts.begin(); it != gPendingPayouts.end(); ++it) {
1392 if (it->blockHash != r.blockHash) 1447 if (!SamePayoutRound(it->blockHeight, it->blockHash,
1448 r.blockHeight, r.blockHash))
1393 continue; 1449 continue;
1394 it->amounts.erase(r.address); 1450 it->amounts.erase(r.address);
1395 if (it->amounts.empty() || PayoutFullyPaid(*it)) 1451 if (it->amounts.empty() || PayoutFullyPaid(*it))
@@ -1443,7 +1499,9 @@ static void PayoutThreadFn(void*)
1443 { 1499 {
1444 std::lock_guard<std::mutex> lk(gPayoutLedgerMutex); 1500 std::lock_guard<std::mutex> lk(gPayoutLedgerMutex);
1445 for (const PendingPayout& pp : gPendingPayouts) 1501 for (const PendingPayout& pp : gPendingPayouts)
1446 if (pp.blockHash == r.blockHash && pp.paidTxids.count(r.address)) { 1502 if (SamePayoutRound(pp.blockHeight, pp.blockHash,
1503 r.blockHeight, r.blockHash)
1504 && pp.paidTxids.count(r.address)) {
1447 alreadyPaid = true; 1505 alreadyPaid = true;
1448 break; 1506 break;
1449 } 1507 }