diff options
Diffstat (limited to 'src/rpc.cpp')
| -rw-r--r-- | src/rpc.cpp | 161 |
1 files changed, 139 insertions, 22 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp index dbbf06d..310c0d4 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -1369,6 +1369,24 @@ static void MarkPoolRoundForfeited(const std::string& id, | |||
| 1369 | PublishPoolRounds(snap); | 1369 | PublishPoolRounds(snap); |
| 1370 | } | 1370 | } |
| 1371 | 1371 | ||
| 1372 | // Does this round have a published round proof? | ||
| 1373 | // | ||
| 1374 | // Existence answers a temporal question the legacy fallback below needs: | ||
| 1375 | // which builds could have paid this round. RecordPoolRound writes the proof | ||
| 1376 | // when the round is created, and structured payout metadata (upstream | ||
| 1377 | // cbdd06d) is an ancestor of round proofs (upstream 83ba822) -- no build | ||
| 1378 | // able to write a proof has ever sent a payment that only a comment can | ||
| 1379 | // identify. A round with a proof therefore cannot have a comment-only | ||
| 1380 | // payment; one that matches it is another round's, colliding on the height. | ||
| 1381 | static bool PoolRoundHasProof(const std::string& id) | ||
| 1382 | { | ||
| 1383 | std::lock_guard<std::mutex> lk(gPoolRoundsMutex); | ||
| 1384 | for (const PoolRoundProof& r : gPoolRounds) | ||
| 1385 | if (r.id == id) | ||
| 1386 | return true; | ||
| 1387 | return false; | ||
| 1388 | } | ||
| 1389 | |||
| 1372 | // Which wallet transaction, if any, already paid this? | 1390 | // Which wallet transaction, if any, already paid this? |
| 1373 | // | 1391 | // |
| 1374 | // Two kinds of evidence, in order of quality. | 1392 | // Two kinds of evidence, in order of quality. |
| @@ -1404,11 +1422,22 @@ static void MarkPoolRoundForfeited(const std::string& id, | |||
| 1404 | // (round, address) pair -- from the ledger's paidTxids, which is durable, | 1422 | // (round, address) pair -- from the ledger's paidTxids, which is durable, |
| 1405 | // and from what this process has already claimed. One legacy transaction | 1423 | // and from what this process has already claimed. One legacy transaction |
| 1406 | // can therefore settle at most one debt, so N transactions can never | 1424 | // can therefore settle at most one debt, so N transactions can never |
| 1407 | // satisfy more than N obligations. | 1425 | // satisfy more than N obligations; |
| 1426 | // | ||
| 1427 | // 4. it is not consulted at all for a round that has a round proof | ||
| 1428 | // (fAllowLegacy, decided by the caller from PoolRoundHasProof). Proofs | ||
| 1429 | // are written when a round is created, and only by builds whose every | ||
| 1430 | // payment names its round exactly -- so a comment-only transaction | ||
| 1431 | // cannot be a payment for such a round, only a same-height collision | ||
| 1432 | // from an older one, and skipping the fallback cannot hide a real | ||
| 1433 | // payment: the structured probe above already found any that exist. | ||
| 1408 | // | 1434 | // |
| 1409 | // What is left unresolved by construction is two pre-upgrade rounds at the | 1435 | // What is left unresolved by construction is a comment-only transaction and |
| 1410 | // SAME height paying the same address the same amount, since the comment | 1436 | // a PROOFLESS round at the same height paying the same address the same |
| 1411 | // carries only the height. That needs a reorg. | 1437 | // amount: two grandfathered zero-hash twins, or a twin pair from the builds |
| 1438 | // that keyed the ledger by hash before any metadata reached the wallet. The | ||
| 1439 | // comment carries only the height, so nothing ties the transaction to one | ||
| 1440 | // twin rather than the other. That needs a reorg. | ||
| 1412 | // | 1441 | // |
| 1413 | // This used to say the losing twin's ledger entry is voided by the orphan | 1442 | // This used to say the losing twin's ledger entry is voided by the orphan |
| 1414 | // check before it can be paid. That is wrong: PayoutBlockStillOnChain() | 1443 | // check before it can be paid. That is wrong: PayoutBlockStillOnChain() |
| @@ -1445,6 +1474,7 @@ static PayoutProbeResult FindRecordedPoolPayout(const std::string& id, | |||
| 1445 | const uint256& blockHash, | 1474 | const uint256& blockHash, |
| 1446 | const std::string& address, | 1475 | const std::string& address, |
| 1447 | int64 amount, | 1476 | int64 amount, |
| 1477 | bool fAllowLegacy, | ||
| 1448 | const std::set<std::string>& excluded, | 1478 | const std::set<std::string>& excluded, |
| 1449 | std::string& txidRet) | 1479 | std::string& txidRet) |
| 1450 | { | 1480 | { |
| @@ -1505,6 +1535,8 @@ static PayoutProbeResult FindRecordedPoolPayout(const std::string& id, | |||
| 1505 | return PAYOUT_PROBE_STRUCTURED; | 1535 | return PAYOUT_PROBE_STRUCTURED; |
| 1506 | } | 1536 | } |
| 1507 | 1537 | ||
| 1538 | if (!fAllowLegacy) | ||
| 1539 | continue; // comment-only evidence is inadmissible: rule 4 | ||
| 1508 | if (!fHaveScript) | 1540 | if (!fHaveScript) |
| 1509 | continue; | 1541 | continue; |
| 1510 | // The comment is written only by the payout path, so its presence | 1542 | // The comment is written only by the payout path, so its presence |
| @@ -2142,10 +2174,15 @@ static void PayoutThreadFn(void*) | |||
| 2142 | std::set<std::string> excluded; | 2174 | std::set<std::string> excluded; |
| 2143 | CollectForeignPayoutTxids(r.blockHeight, r.blockHash, r.address, | 2175 | CollectForeignPayoutTxids(r.blockHeight, r.blockHash, r.address, |
| 2144 | claimKey, excluded); | 2176 | claimKey, excluded); |
| 2177 | // A round with a published proof was created by a build whose | ||
| 2178 | // every payment names its round exactly, so only structured | ||
| 2179 | // evidence can settle it -- a comment match would be another | ||
| 2180 | // round's payment colliding on the height. | ||
| 2181 | const bool fAllowLegacy = !PoolRoundHasProof(id); | ||
| 2145 | r.fProbeWallet = false; | 2182 | r.fProbeWallet = false; |
| 2146 | PayoutProbeResult probe = FindRecordedPoolPayout( | 2183 | PayoutProbeResult probe = FindRecordedPoolPayout( |
| 2147 | id, r.blockHeight, r.blockHash, r.address, r.amount, | 2184 | id, r.blockHeight, r.blockHash, r.address, r.amount, |
| 2148 | excluded, recovered); | 2185 | fAllowLegacy, excluded, recovered); |
| 2149 | if (probe != PAYOUT_PROBE_NONE) { | 2186 | if (probe != PAYOUT_PROBE_NONE) { |
| 2150 | if (probe == PAYOUT_PROBE_LEGACY) { | 2187 | if (probe == PAYOUT_PROBE_LEGACY) { |
| 2151 | // Loud on purpose: this is the upgrade path, where the | 2188 | // Loud on purpose: this is the upgrade path, where the |
| @@ -3880,31 +3917,34 @@ int RunPayoutLedgerSelfTest() | |||
| 3880 | const std::string comment = LegacyPayoutComment(height); | 3917 | const std::string comment = LegacyPayoutComment(height); |
| 3881 | std::set<std::string> none; | 3918 | std::set<std::string> none; |
| 3882 | std::string found; | 3919 | std::string found; |
| 3920 | // No round proof exists for these rounds, so legacy evidence is | ||
| 3921 | // admissible -- computed exactly as the payout thread computes it. | ||
| 3922 | const bool fAllowLegacy = !PoolRoundHasProof(id); | ||
| 3883 | 3923 | ||
| 3884 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount, none, found) | 3924 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount, fAllowLegacy, none, found) |
| 3885 | == PAYOUT_PROBE_NONE, | 3925 | == PAYOUT_PROBE_NONE, |
| 3886 | "an empty wallet is not evidence that anything was paid"); | 3926 | "an empty wallet is not evidence that anything was paid"); |
| 3887 | 3927 | ||
| 3888 | const std::string legacyTxid = | 3928 | const std::string legacyTxid = |
| 3889 | PayoutTestAddWalletTx(comment, addrA, amount, 1, ""); | 3929 | PayoutTestAddWalletTx(comment, addrA, amount, 1, ""); |
| 3890 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount, none, found) | 3930 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount, fAllowLegacy, none, found) |
| 3891 | == PAYOUT_PROBE_LEGACY && found == legacyTxid, | 3931 | == PAYOUT_PROBE_LEGACY && found == legacyTxid, |
| 3892 | "a payment carrying only a pre-upgrade comment is recognised as" | 3932 | "a payment carrying only a pre-upgrade comment is recognised as" |
| 3893 | " already paid"); | 3933 | " already paid"); |
| 3894 | 3934 | ||
| 3895 | check(FindRecordedPoolPayout(id, height, 0, addrB, amount, none, found) | 3935 | check(FindRecordedPoolPayout(id, height, 0, addrB, amount, fAllowLegacy, none, found) |
| 3896 | == PAYOUT_PROBE_NONE, | 3936 | == PAYOUT_PROBE_NONE, |
| 3897 | "the legacy match requires the output to pay THIS recipient"); | 3937 | "the legacy match requires the output to pay THIS recipient"); |
| 3898 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount + COIN, none, found) | 3938 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount + COIN, fAllowLegacy, none, found) |
| 3899 | == PAYOUT_PROBE_NONE, | 3939 | == PAYOUT_PROBE_NONE, |
| 3900 | "the legacy match requires the output to pay exactly this amount"); | 3940 | "the legacy match requires the output to pay exactly this amount"); |
| 3901 | check(FindRecordedPoolPayout(id, height + 1, 0, addrA, amount, none, found) | 3941 | check(FindRecordedPoolPayout(id, height + 1, 0, addrA, amount, fAllowLegacy, none, found) |
| 3902 | == PAYOUT_PROBE_NONE, | 3942 | == PAYOUT_PROBE_NONE, |
| 3903 | "the legacy match requires the comment for this block height"); | 3943 | "the legacy match requires the comment for this block height"); |
| 3904 | 3944 | ||
| 3905 | std::set<std::string> spent; | 3945 | std::set<std::string> spent; |
| 3906 | spent.insert(legacyTxid); | 3946 | spent.insert(legacyTxid); |
| 3907 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount, spent, found) | 3947 | check(FindRecordedPoolPayout(id, height, 0, addrA, amount, fAllowLegacy, spent, found) |
| 3908 | == PAYOUT_PROBE_NONE, | 3948 | == PAYOUT_PROBE_NONE, |
| 3909 | "one legacy transaction cannot settle a second debt"); | 3949 | "one legacy transaction cannot settle a second debt"); |
| 3910 | 3950 | ||
| @@ -3913,7 +3953,7 @@ int RunPayoutLedgerSelfTest() | |||
| 3913 | // exactly what the structured metadata exists to prevent. | 3953 | // exactly what the structured metadata exists to prevent. |
| 3914 | const std::string otherId = PayoutIdForBlock(height, 0) + "-other"; | 3954 | const std::string otherId = PayoutIdForBlock(height, 0) + "-other"; |
| 3915 | PayoutTestAddWalletTx(comment, addrB, amount, 2, otherId); | 3955 | PayoutTestAddWalletTx(comment, addrB, amount, 2, otherId); |
| 3916 | check(FindRecordedPoolPayout(id, height, 0, addrB, amount, none, found) | 3956 | check(FindRecordedPoolPayout(id, height, 0, addrB, amount, fAllowLegacy, none, found) |
| 3917 | == PAYOUT_PROBE_NONE, | 3957 | == PAYOUT_PROBE_NONE, |
| 3918 | "another round's structured payment is not legacy evidence for" | 3958 | "another round's structured payment is not legacy evidence for" |
| 3919 | " this one"); | 3959 | " this one"); |
| @@ -3921,7 +3961,7 @@ int RunPayoutLedgerSelfTest() | |||
| 3921 | // The structured path still wins where it applies. | 3961 | // The structured path still wins where it applies. |
| 3922 | const std::string structuredTxid = | 3962 | const std::string structuredTxid = |
| 3923 | PayoutTestAddWalletTx(comment, addrB, amount, 3, id); | 3963 | PayoutTestAddWalletTx(comment, addrB, amount, 3, id); |
| 3924 | check(FindRecordedPoolPayout(id, height, 0, addrB, amount, none, found) | 3964 | check(FindRecordedPoolPayout(id, height, 0, addrB, amount, fAllowLegacy, none, found) |
| 3925 | == PAYOUT_PROBE_STRUCTURED && found == structuredTxid, | 3965 | == PAYOUT_PROBE_STRUCTURED && found == structuredTxid, |
| 3926 | "structured payout metadata is matched in preference to a comment"); | 3966 | "structured payout metadata is matched in preference to a comment"); |
| 3927 | 3967 | ||
| @@ -3935,7 +3975,7 @@ int RunPayoutLedgerSelfTest() | |||
| 3935 | PayoutTestClearBlockIndex(); | 3975 | PayoutTestClearBlockIndex(); |
| 3936 | const std::string addrC = PayoutTestAddress(0x33); | 3976 | const std::string addrC = PayoutTestAddress(0x33); |
| 3937 | PayoutTestAddWalletTx(comment, addrC, amount, 4, "", false); | 3977 | PayoutTestAddWalletTx(comment, addrC, amount, 4, "", false); |
| 3938 | check(FindRecordedPoolPayout(id, height, 0, addrC, amount, none, found) | 3978 | check(FindRecordedPoolPayout(id, height, 0, addrC, amount, fAllowLegacy, none, found) |
| 3939 | == PAYOUT_PROBE_NONE, | 3979 | == PAYOUT_PROBE_NONE, |
| 3940 | "a pre-upgrade payout that never confirmed is not read as" | 3980 | "a pre-upgrade payout that never confirmed is not read as" |
| 3941 | " payment"); | 3981 | " payment"); |
| @@ -3945,7 +3985,7 @@ int RunPayoutLedgerSelfTest() | |||
| 3945 | mapWallet.clear(); | 3985 | mapWallet.clear(); |
| 3946 | const std::string confirmedTxid = | 3986 | const std::string confirmedTxid = |
| 3947 | PayoutTestAddWalletTx(comment, addrC, amount, 4, "", true); | 3987 | PayoutTestAddWalletTx(comment, addrC, amount, 4, "", true); |
| 3948 | check(FindRecordedPoolPayout(id, height, 0, addrC, amount, none, found) | 3988 | check(FindRecordedPoolPayout(id, height, 0, addrC, amount, fAllowLegacy, none, found) |
| 3949 | == PAYOUT_PROBE_LEGACY && found == confirmedTxid, | 3989 | == PAYOUT_PROBE_LEGACY && found == confirmedTxid, |
| 3950 | "the same payout, confirmed, is"); | 3990 | "the same payout, confirmed, is"); |
| 3951 | 3991 | ||
| @@ -3956,7 +3996,7 @@ int RunPayoutLedgerSelfTest() | |||
| 3956 | PayoutTestAddWalletTx(comment, addrC, amount, 5, "", false); | 3996 | PayoutTestAddWalletTx(comment, addrC, amount, 5, "", false); |
| 3957 | std::set<std::string> spentFirst; | 3997 | std::set<std::string> spentFirst; |
| 3958 | spentFirst.insert(confirmedTxid); | 3998 | spentFirst.insert(confirmedTxid); |
| 3959 | check(FindRecordedPoolPayout(id, height, 0, addrC, amount, spentFirst, found) | 3999 | check(FindRecordedPoolPayout(id, height, 0, addrC, amount, fAllowLegacy, spentFirst, found) |
| 3960 | == PAYOUT_PROBE_LEGACY && found == alsoConfirmed, | 4000 | == PAYOUT_PROBE_LEGACY && found == alsoConfirmed, |
| 3961 | "an unconfirmed transaction does not hide a confirmed one behind" | 4001 | "an unconfirmed transaction does not hide a confirmed one behind" |
| 3962 | " it"); | 4002 | " it"); |
| @@ -4511,11 +4551,14 @@ int RunPayoutLedgerSelfTest() | |||
| 4511 | const std::string idB = PayoutIdForBlock(height, hashB); | 4551 | const std::string idB = PayoutIdForBlock(height, hashB); |
| 4512 | std::set<std::string> none; | 4552 | std::set<std::string> none; |
| 4513 | std::string found; | 4553 | std::string found; |
| 4554 | // Neither twin has a round proof here (section 15 covers the gate), | ||
| 4555 | // and their ids collide, so one answer serves both. | ||
| 4556 | const bool fAllowLegacy = !PoolRoundHasProof(idA); | ||
| 4514 | PayoutTestAddWalletTx(PayoutComment(height, hashA), addrA, amount, 1, ""); | 4557 | PayoutTestAddWalletTx(PayoutComment(height, hashA), addrA, amount, 1, ""); |
| 4515 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, none, found) | 4558 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, fAllowLegacy, none, found) |
| 4516 | == PAYOUT_PROBE_NONE, | 4559 | == PAYOUT_PROBE_NONE, |
| 4517 | "a new-format comment is never legacy evidence for a twin round"); | 4560 | "a new-format comment is never legacy evidence for a twin round"); |
| 4518 | check(FindRecordedPoolPayout(idA, height, hashA, addrA, amount, none, found) | 4561 | check(FindRecordedPoolPayout(idA, height, hashA, addrA, amount, fAllowLegacy, none, found) |
| 4519 | == PAYOUT_PROBE_NONE, | 4562 | == PAYOUT_PROBE_NONE, |
| 4520 | "nor a match channel for its own round -- structured metadata is"); | 4563 | "nor a match channel for its own round -- structured metadata is"); |
| 4521 | 4564 | ||
| @@ -4527,7 +4570,7 @@ int RunPayoutLedgerSelfTest() | |||
| 4527 | const std::string paidA = | 4570 | const std::string paidA = |
| 4528 | PayoutTestAddWalletTx(PayoutComment(height, hashA), addrA, amount, | 4571 | PayoutTestAddWalletTx(PayoutComment(height, hashA), addrA, amount, |
| 4529 | 2, idA); | 4572 | 2, idA); |
| 4530 | check(FindRecordedPoolPayout(idA, height, hashA, addrA, amount, none, found) | 4573 | check(FindRecordedPoolPayout(idA, height, hashA, addrA, amount, fAllowLegacy, none, found) |
| 4531 | == PAYOUT_PROBE_STRUCTURED && found == paidA, | 4574 | == PAYOUT_PROBE_STRUCTURED && found == paidA, |
| 4532 | "the winning twin's own payment is found by its structured id"); | 4575 | "the winning twin's own payment is found by its structured id"); |
| 4533 | 4576 | ||
| @@ -4539,7 +4582,7 @@ int RunPayoutLedgerSelfTest() | |||
| 4539 | check(idA == idB, | 4582 | check(idA == idB, |
| 4540 | "twin hashes differing only past the sixteenth hex digit collide" | 4583 | "twin hashes differing only past the sixteenth hex digit collide" |
| 4541 | " in id -- the reason the full hash must decide"); | 4584 | " in id -- the reason the full hash must decide"); |
| 4542 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, none, found) | 4585 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, fAllowLegacy, none, found) |
| 4543 | == PAYOUT_PROBE_STRUCTURED, | 4586 | == PAYOUT_PROBE_STRUCTURED, |
| 4544 | "a payment without the block key answers for a colliding twin --" | 4587 | "a payment without the block key answers for a colliding twin --" |
| 4545 | " the accepted residual for pre-key transactions"); | 4588 | " the accepted residual for pre-key transactions"); |
| @@ -4552,10 +4595,10 @@ int RunPayoutLedgerSelfTest() | |||
| 4552 | const std::string keyedA = | 4595 | const std::string keyedA = |
| 4553 | PayoutTestAddWalletTx(PayoutComment(height, hashA), addrA, amount, | 4596 | PayoutTestAddWalletTx(PayoutComment(height, hashA), addrA, amount, |
| 4554 | 3, idA, true, hashA.ToString()); | 4597 | 3, idA, true, hashA.ToString()); |
| 4555 | check(FindRecordedPoolPayout(idA, height, hashA, addrA, amount, none, found) | 4598 | check(FindRecordedPoolPayout(idA, height, hashA, addrA, amount, fAllowLegacy, none, found) |
| 4556 | == PAYOUT_PROBE_STRUCTURED && found == keyedA, | 4599 | == PAYOUT_PROBE_STRUCTURED && found == keyedA, |
| 4557 | "a payment carrying the full block hash settles its own round"); | 4600 | "a payment carrying the full block hash settles its own round"); |
| 4558 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, none, found) | 4601 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, fAllowLegacy, none, found) |
| 4559 | == PAYOUT_PROBE_NONE, | 4602 | == PAYOUT_PROBE_NONE, |
| 4560 | "and is no evidence for the twin, so the re-won height is paid"); | 4603 | "and is no evidence for the twin, so the re-won height is paid"); |
| 4561 | 4604 | ||
| @@ -4564,6 +4607,80 @@ int RunPayoutLedgerSelfTest() | |||
| 4564 | PayoutTestClearBlockIndex(); | 4607 | PayoutTestClearBlockIndex(); |
| 4565 | } | 4608 | } |
| 4566 | 4609 | ||
| 4610 | // -- 15. A proof-bearing round rejects comment-only evidence ------------- | ||
| 4611 | // | ||
| 4612 | // The surviving half of the same-height collision: a pre-upgrade | ||
| 4613 | // transaction carries only "Pool payout height H", so if H is won again | ||
| 4614 | // after a reorg, it matches the new round's (height, address, amount) | ||
| 4615 | // exactly and would settle a debt it never paid. The new round has a | ||
| 4616 | // round proof -- written at creation, and only by builds whose payments | ||
| 4617 | // all name their round -- so for it, comment-only evidence is | ||
| 4618 | // inadmissible and the miner is paid. A round without a proof keeps | ||
| 4619 | // today's bias: the evidence is admissible and doubt resolves to | ||
| 4620 | // "already paid". | ||
| 4621 | { | ||
| 4622 | PayoutTestResetState(); | ||
| 4623 | CRITICAL_BLOCK(cs_mapWallet) | ||
| 4624 | mapWallet.clear(); | ||
| 4625 | PayoutTestClearBlockIndex(); | ||
| 4626 | const std::string roundsPath = dir + "/pool_rounds_gate_selftest.json"; | ||
| 4627 | const std::string savedRoundsFile = strPoolRoundsFile; | ||
| 4628 | strPoolRoundsFile = roundsPath; | ||
| 4629 | remove(roundsPath.c_str()); | ||
| 4630 | |||
| 4631 | const int height = 7171; | ||
| 4632 | const int64 amount = 4 * COIN; | ||
| 4633 | uint256 hashB = 0; | ||
| 4634 | hashB.SetHex("00000000000000000000000000000000000000000000000000000000000000cc"); | ||
| 4635 | const std::string idB = PayoutIdForBlock(height, hashB); | ||
| 4636 | std::set<std::string> none; | ||
| 4637 | std::string found; | ||
| 4638 | |||
| 4639 | // The colliding pre-upgrade payment: confirmed, comment-only, same | ||
| 4640 | // height, same address, same amount -- the strongest wrong evidence | ||
| 4641 | // there is. | ||
| 4642 | PayoutTestAddWalletTx(LegacyPayoutComment(height), addrA, amount, 1, ""); | ||
| 4643 | |||
| 4644 | check(!PoolRoundHasProof(idB), | ||
| 4645 | "a round with no recorded proof does not claim one"); | ||
| 4646 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, | ||
| 4647 | !PoolRoundHasProof(idB), none, found) | ||
| 4648 | == PAYOUT_PROBE_LEGACY, | ||
| 4649 | "without a proof the doubt is genuine and still resolves to" | ||
| 4650 | " already-paid"); | ||
| 4651 | |||
| 4652 | PoolRoundProof round; | ||
| 4653 | round.id = idB; | ||
| 4654 | round.blockHeight = height; | ||
| 4655 | round.matureAtHeight = height + COINBASE_SPENDABLE_DEPTH; | ||
| 4656 | round.payouts[addrA] = amount; | ||
| 4657 | RecordPoolRound(round); | ||
| 4658 | check(PoolRoundHasProof(idB), | ||
| 4659 | "recording the round proof marks the round as this lineage's"); | ||
| 4660 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, | ||
| 4661 | !PoolRoundHasProof(idB), none, found) | ||
| 4662 | == PAYOUT_PROBE_NONE, | ||
| 4663 | "with a proof, a same-height comment-only transaction is not" | ||
| 4664 | " evidence, and the re-won height is paid"); | ||
| 4665 | |||
| 4666 | // Structured evidence still is: the round's own payment, recovered. | ||
| 4667 | const std::string paidB = | ||
| 4668 | PayoutTestAddWalletTx(PayoutComment(height, hashB), addrA, amount, | ||
| 4669 | 2, idB, true, hashB.ToString()); | ||
| 4670 | check(FindRecordedPoolPayout(idB, height, hashB, addrA, amount, | ||
| 4671 | !PoolRoundHasProof(idB), none, found) | ||
| 4672 | == PAYOUT_PROBE_STRUCTURED && found == paidB, | ||
| 4673 | "the gate does not blind the probe to the round's own structured" | ||
| 4674 | " payment"); | ||
| 4675 | |||
| 4676 | CRITICAL_BLOCK(cs_mapWallet) | ||
| 4677 | mapWallet.clear(); | ||
| 4678 | PayoutTestClearBlockIndex(); | ||
| 4679 | remove(roundsPath.c_str()); | ||
| 4680 | remove((roundsPath + ".tmp").c_str()); | ||
| 4681 | strPoolRoundsFile = savedRoundsFile; | ||
| 4682 | } | ||
| 4683 | |||
| 4567 | // -- clean up ------------------------------------------------------------ | 4684 | // -- clean up ------------------------------------------------------------ |
| 4568 | PayoutTestResetState(); | 4685 | PayoutTestResetState(); |
| 4569 | CRITICAL_BLOCK(cs_mapWallet) | 4686 | CRITICAL_BLOCK(cs_mapWallet) |