diff options
Diffstat (limited to 'src/rpc.cpp')
| -rw-r--r-- | src/rpc.cpp | 72 |
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". | ||
| 549 | static 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. | ||
| 564 | static bool PayoutRoundIdentifiable(int height, const uint256& hash) | ||
| 565 | { | ||
| 566 | return hash != 0 || height != 0; | ||
| 567 | } | ||
| 568 | |||
| 533 | static bool PayoutFullyPaid(const PendingPayout& pp) | 569 | static 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. |
| 1286 | static bool SamePayment(const PayingRecord& a, const PayingRecord& b) | 1336 | static 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 | } |