{
 "number": 36040,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36040",
 "title": "Wallet: Don't backdate locktime rbf",
 "author": "Bicaru20",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-08-20T15:28:02Z",
 "updated_at": "2026-09-16T14:32:49Z",
 "age_days": 28,
 "draft": false,
 "labels": [
  "Wallet"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
 "head_ref": "2026-dont-backdate-locktime-RBF",
 "head_repo": "Bicaru20/bitcoin",
 "head_history": [
  {
   "t": "2026-08-20T15:43:26Z",
   "sha": "f26586b91de16a03d4c265a7e8ee9fefb728dd65"
  },
  {
   "t": "2026-08-28T10:16:49Z",
   "sha": "f911ae8345dd22d2787801ed03c6b2a191584832"
  },
  {
   "t": "2026-09-08T21:27:38Z",
   "sha": "d26df478f02e7e076e8287bf43cecf64f3dcc3e8"
  },
  {
   "t": "2026-09-08T21:58:59Z",
   "sha": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e"
  },
  {
   "t": "2026-09-12T17:32:03Z",
   "sha": "dcda3c51e72552269445823bc65c35248fbdab88"
  },
  {
   "t": "2026-09-12T17:38:39Z",
   "sha": "b72a0533dac88b46755f9ee1051eb588b1934050"
  },
  {
   "t": "2026-09-15T12:28:17Z",
   "sha": "e34f9340d615976b1398e6ead0ab8d2bda24907c"
  }
 ],
 "additions": 153,
 "deletions": 8,
 "changed_files": 6,
 "commit_count": 2,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "molnard",
      "url": "https://github.com/bitcoin/bitcoin/pull/36040#issuecomment-5687931347"
     },
     {
      "login": "nervana21",
      "url": "https://github.com/bitcoin/bitcoin/pull/36040#pullrequestreview-5224058585"
     }
    ],
    "approach_ack": [
     {
      "login": "polespinasa",
      "url": "https://github.com/bitcoin/bitcoin/pull/36040#pullrequestreview-5018030362"
     }
    ]
   },
   "conflicts": []
  }
 },
 "acks_parsed": {
  "nervana21": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-08-21T00:34:49Z",
   "stale": false
  },
  "polespinasa": {
   "kind": "approach_ack",
   "hash": null,
   "t": "2026-08-25T14:25:01Z",
   "stale": false
  },
  "molnard": {
   "kind": "ack",
   "hash": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "t": "2026-09-15T20:53:22Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 1,
  "stale_ack": 0,
  "concept_ack": 1,
  "approach_ack": 1,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "danielabrozzoni",
   "molnard",
   "nervana21",
   "polespinasa"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-15T12:33:59Z",
  "last_reviewer_activity": "2026-09-16T14:30:54Z",
  "last_reviewer": "nervana21",
  "author_silent_days": 2,
  "waiting_on_author_days": 1,
  "days_since_update": 1
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/wallet/coincontrol.h",
  "src/wallet/feebumper.cpp",
  "src/wallet/spend.cpp",
  "src/wallet/spend.h",
  "src/wallet/test/feebumper_tests.cpp",
  "src/wallet/test/spend_tests.cpp",
  "test/functional/wallet_bumpfee.py"
 ],
 "body": "Closes https://github.com/bitcoin/bitcoin/issues/26526\n\nCurrently, in Bitcoin Core, given an original transaction A and its replacement B, we refer to **backdating** when the locktime of A is higher than the locktime of B (`A.locktime > B.locktime`). This can happen because Bitcoin Core enables **anti-fee-sniping** by default, which sets the transaction's locktime to the current block height. For privacy, 10% of the time, it instead sets a different locktime, randomly chosen between the current height and the current height minus 100 blocks. This can lead into having a replacement of a transaction with a locktime older than its original transaction. This is unrealistics and can be used as a wallet fingerprint.\n\nYou can find a functional test to reproduce the behaviour mentioned [here](https://gist.github.com/Bicaru20/9afcb878dd103369255cd81cffac86ba)\n\nThe approach proposed in this PR adds a new parameter to `CoinControl` to keep track of the previous locktime in the case of a `bumpfee`. We then pass this parameter to the DiscourageFeeSniping function through a new parameter, `minimum_height`, whose default value is set to 0.\n\nAlternative approach:\nSince the locktime of the new transaction is 0, we also considered setting it to the previous locktime value and then, inside `DiscourageFeeSniping`, saving the previous locktime and setting the transaction's locktime to the block height before applying any of the backdating logic. This way, we could avoid passing a new parameter to the function. We ultimately decided not to go with this approach, as it makes the code more difficult to follow.\n\nThe only RPC that is affected by this changes is `bumpfee`.\n\nThe pr also includes a functional test in `wallet_bumpfee.py` to test that when replacing a transation using `bumpfee` the locktime is not backdated.\n\nWe also conducted a small analysis to see how many the backdating in RBF transactions actually happen.\nWe have data on the replaced transactions from 2025-05-01 to 2026-06-01. With that we have been able to detect this many backdatings:\n\n_We took the date of the last transaction of the\nreplacement chain (A replacement chain are all the transactions that replace themselves)_\n\nIn total we have over 1,000,000 rbf transactions, but we see that on average there are only about 200-300 hundred replaccements backdating per week. Looking at the percentages we see that on average is less than 2% of all the transactions that have been replaced.\n\nSo, it is clear that backdating in replacement transactions is unusual. However, the few transactions that do exhibit backdating are easily fingerprintable as having been replaced using `bumpfee` in Bitcoin Core or Electrum.",
 "commits": [
  {
   "sha": "675448e26225d2d82b363a508048dc7819037219",
   "date": "2026-09-12T17:17:52Z",
   "message": "Wallet: Do not allow bumpfee to backdate the replacement transaction locktime\n\nThis changes the behavior of bumpfee so when used, the replacement\ntransaction doesn't have an older lockitme than the original transaction.\nNow the replacement transaction will have a locktime between the block_height\nand the locktime of the original transaction, except when the minimum_height\nis greter than block_height. This could happen if the original transaction\nhas a high locktime and a sequence number that disable locktime validation.\nIn this case the tx is valid because the locktime is not used.\n\nCo-Authored-By: danielabrozzoni <danielabrozzoni@protonmail.com>"
  },
  {
   "sha": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "date": "2026-09-15T12:27:21Z",
   "message": "Test: bumpfee does not backdate the locktime\n\nUnit test to check that the DiscourageFeeSniping function behaves as expected\nwhen the minimum_height parameter is passed.\nWe test that the backdating occurs between the block_height and minimum_height\nrange.\nWe test that when the transaction doesn't pass IsCurrentForAntiFeeSniping the\nlocktime is set to minimum_height if block_height > minimum_height,\nor to 0 if block_height < minimum_height.\n\nCo-Authored-By: danielabrozzoni <danielabrozzoni@protonmail.com>"
  }
 ],
 "timeline": [
  {
   "t": "2026-08-20T15:43:26Z",
   "kind": "force_push",
   "who": "Bicaru20",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65"
  },
  {
   "t": "2026-08-21T00:34:49Z",
   "kind": "comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK"
  },
  {
   "t": "2026-08-23T17:07:13Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/feebumper.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": null,
   "text": "b7935c708edc225e20dcab0820dff774f841736b: Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\n```suggestion\n    if (!new_coin_control.m_locktime.has_value()){\n```\n\nnit"
  },
  {
   "t": "2026-08-23T17:08:09Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": null,
   "text": "b7935c708edc225e20dcab0820dff774f841736b: Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\n```suggestion\n            const int tip_floor_distance = block_height - static_cast<int>(minimum_height);\n            if (tip_floor_distance > 0) {\n                const int bound = minimum_height > 0\n                    ? tip_floor_distance + 1\n                    : std::min(100, tip_floor_distance + 1);\n                const int back = rng_fast.randrange(bound);\n                tx.nLockTime = block_height - back;\n            }\n```\n\nThere's an OBO in this logic. In the inclusive range `[minimum_height, block_height]` there are `(block_height - minimum_height) + 1` possible values. `randrange(block_height - minimum_height)` only draws `block_height - minimum_height` possible values and can never pick the `minimum_height` value.\n\nAlso, when `minimum_height == 0`, we retain the prior behavior and backdate at most 99 blocks from tip. When `minimum_height > 0`, we should use the full inclusive window without the 100 block cap."
  },
  {
   "t": "2026-08-23T17:10:10Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": null,
   "text": "b7935c708edc225e20dcab0820dff774f841736b: Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\n```suggestion\n        // the privacy of high-latency transactions. Use minimum_height so new\n        // sends still get a constant 0 fingerprint, while bumpfee keeps the prior\n        // height and does not make the replacement older than the original.\n        // If that height is ahead of the local tip, use 0 so the tx stays final\n        // and we do not fingerprint the lagging tip.\n        if (minimum_height <= static_cast<uint32_t>(block_height)) {\n            tx.nLockTime = minimum_height;\n        } else {\n            tx.nLockTime = 0;\n```\n\nIn the case where we're fee bumping an already-nLockTimed tx, we should not reset its `nLockTime` back below that established `minimum_height`. Otherwise we fingerprint the tx. `minimum_height` defaults to 0, so new sends on a stale chain still retain the prior behavior of `nLockTime = 0`.\n\nIn the case where the `minimum_height` is ahead of the local tip, set `nLockTime = 0`"
  },
  {
   "t": "2026-08-23T17:12:20Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.h",
   "commit": "f911ae8345dd22d2787801ed03c6b2a191584832",
   "in_reply_to": null,
   "text": "b7935c708edc225e20dcab0820dff774f841736b: Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\n```suggestion\n * current chain tip unless we are not synced with the current chain).\n * The locktime is occasionally backdated, but never below minimum_height.\n * When minimum_height is 0, backdating is capped at 99 blocks from tip.\n * When minimum_height is set (bumpfee), backdating is uniform on [minimum_height, tip].\n * If the chain is not current and minimum_height is above the local tip, locktime is 0.\n```"
  },
  {
   "t": "2026-08-23T17:15:37Z",
   "kind": "review",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "text": "b7935c708edc225e20dcab0820dff774f841736b: Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\nnit:\nCommit subject and body say \"lockitme\". Should be \"locktime\".\n\nCommit body says \"This changes change the behavior\". It should be \"This changes the behavior\"."
  },
  {
   "t": "2026-08-25T10:52:17Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/wallet/spend.cpp",
   "commit": "b7935c708edc225e20dcab0820dff774f841736b",
   "in_reply_to": null,
   "text": "in b7935c708edc225e20dcab0820dff774f841736b Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\nIs this if needed? can we have a `block_height` smaller than the `minimum_height`? I think at least we will always be at the `minimum_height`, I can only think of a re-org, but then we must swich to a chain with more PoW so the height will likely be higher too.\n\nAlso I think this can be simplified into:\n```diff\n$ git diff\ndiff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp\nindex 40eeb78f17..a34c95fcb3 100644\n--- a/src/wallet/spend.cpp\n+++ b/src/wallet/spend.cpp\n@@ -1023,15 +1023,11 @@ void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng_fast,\n         // that transactions that are delayed after signing for whatever reason,\n         // e.g. high-latency mix networks and some CoinJoin implementations, have\n         // better privacy.\n-        if (rng_fast.randrange(10) == 0) {\n+        if (rng_fast.randrange(10) == 0 && tx.nLockTime > minimum_height) {\n             // If a previous locktime is passed (like in the bump fee case), the\n             // backdating is limited between the current height and the previous locktime\n-            if (static_cast<uint32_t>(block_height) >= minimum_height) {\n-                int locktime_range = std::min(100, int(block_height - minimum_height));\n-                if (locktime_range > 0){\n-                    tx.nLockTime = std::max(int(minimum_height), int(tx.nLockTime) - int(rng_fast.randrange(locktime_range)));\n-                }\n-            }\n+            int range = std::min(100, int(block_height - minimum_height + 1));\n+            tx.nLockTime = std::max(int(minimum_height), int(tx.nLockTime) - int(rng_fast.randrange(range)));\n         }\n     } else {\n         // If our chain is lagging behind, we can't discourage fee sniping nor help\nsliv3r@sliv3r-tuxedo:~/Documentos/Projectes/BitcoinCore/bitcoin$\n\n```\n\nThis version I propose has several improvements:\n1. Removes some redundant if conditions.\n2. Fixes a missing + 1 case that you are missing by just taking `block_height - minimum_height`.\n3. Keeps the old behavior for the default case. Before this PR, short chains with less than a 100blocks would backdate locktime to 0, while this commit clamps it at 1."
  },
  {
   "t": "2026-08-25T14:03:53Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/wallet/feebumper.cpp",
   "commit": "b7935c708edc225e20dcab0820dff774f841736b",
   "in_reply_to": null,
   "text": "in b7935c708edc225e20dcab0820dff774f841736b Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\nnit: missing space between ) and {"
  },
  {
   "t": "2026-08-25T14:04:27Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/wallet/spend.cpp",
   "commit": "b7935c708edc225e20dcab0820dff774f841736b",
   "in_reply_to": null,
   "text": "in b7935c7 Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\nnit: again, space between ) and {"
  },
  {
   "t": "2026-08-25T14:05:15Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/wallet/spend.cpp",
   "commit": "b7935c708edc225e20dcab0820dff774f841736b",
   "in_reply_to": null,
   "text": "in b7935c7 Wallet: Do not allow bumpfee to backdate the replacement transaction lockitme\n\nnit: this is over indented, there are 8 spaces instead of 4."
  },
  {
   "t": "2026-08-25T14:07:30Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_bumpfee.py",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": null,
   "text": "in f26586b91de16a03d4c265a7e8ee9fefb728dd65 Test: bumpfee does not backdate the locktime\n\nMissing commas between arguments, probably True con go with `verbose=True` so it is easier to understand."
  },
  {
   "t": "2026-08-25T14:11:02Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_bumpfee.py",
   "commit": "f911ae8345dd22d2787801ed03c6b2a191584832",
   "in_reply_to": null,
   "text": "in f26586b Test: bumpfee does not backdate the locktime\n\nThe test is weak, if the backdating code is broken and it does not backdate, this loop would run forever and never fail. Probably should add a big max num of tries so if after N iterations it did not pass, we can assume the code is wrong.\n\nI would suggest a unit test for the function anyway, that way we can just test the backdating function."
  },
  {
   "t": "2026-08-25T14:25:01Z",
   "kind": "review",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "text": "Approach ACK\n\nreviewed f26586b91de16a03d4c265a7e8ee9fefb728dd65\n\nin f26586b91de16a03d4c265a7e8ee9fefb728dd65 there as a typo in the commit message. It ends with - should be a ."
  },
  {
   "t": "2026-08-26T08:13:54Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": null,
   "text": "Suppose the original tx `nLockTime` is 96400. The node goes offline for more than 8 hours, and the user calls `bumpfee`. The PR passes 9640 into `DiscourageFeeSniping()` but the chain considered stale, so the execution goes directly to `tx.nLockTime = 0;`.\nTherefore:\n\n```\noriginal:    900,000\nreplacement:       0\n```"
  },
  {
   "t": "2026-08-26T08:24:47Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": null,
   "text": "When `minimum_height` is greater, nothing happens. The transaction keeps the already assigned `block_height,`, which is below the declared minimum.\n\nThis can happen, for example, if:\n- The chain has moved backward after a reorganization or manual `invalidateblock`.\n- The original wallet transaction was dropped or never broadcast.\n- The original transaction was created with a future height locktime."
  },
  {
   "t": "2026-08-26T08:35:48Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": null,
   "text": "A true lower-bound implementation would retain the original distribution.\n\nExample: `rng_fast.randrange(50)` => 0...49.\n\nTherefore, the subtraction can never reach or cross `minimum_height`."
  },
  {
   "t": "2026-08-26T08:46:21Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/coincontrol.h",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": null,
   "text": "`CCoinControl `describes how a new transaction should be constructed - but `m_previous_locktime` is different. A normal transaction has no previous transaction. The concept exists only because `CreateRateBumpTransaction()` is constructing an RBF replacement.\n\nThere is a hidden coupling now. The generic transaction builder must now understand a fee-bumping detail => maintenance complexity later.\n\nWhat if we try to find better ownership for this fee-bump-specific improvement? The fee-bumper already possesses both values, so it could enforce the relationship locally:\n\n```\nmtx = CMutableTransaction(*txr.tx);\n\nif (!coin_control.m_locktime &&\n    tx->nLockTime < LOCKTIME_THRESHOLD) {\n    mtx.nLockTime =\n        std::max(mtx.nLockTime, tx->nLockTime);\n}\n```\n\nThe generic builder creates a normal transaction. The fee-bumper then applies the replacement-specific invariant before signing."
  },
  {
   "t": "2026-08-26T08:56:21Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "test/functional/wallet_bumpfee.py",
   "commit": "f911ae8345dd22d2787801ed03c6b2a191584832",
   "in_reply_to": null,
   "text": "Small chance to that the test can pass with the unpatched code, when the first nonzero random backdate is only one or two blocks."
  },
  {
   "t": "2026-08-26T09:07:54Z",
   "kind": "review",
   "who": "molnard",
   "assoc": "NONE",
   "state": "COMMENTED",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "text": "Concept ACK.\n\nI think this can be simplified by enforcing the replacement-specific invariant in `CreateRateBumpTransaction()` (feebumper.cpp), after creation and before signing. It avoids adding state to CCoinControl, preserves the existing random distribution, and covers stale and `previous_locktime > block_height` cases that the current implementation misses.\n\nThe test appears flaky: unpatched implementation still has about a chance of passing. Could this regression be tested deterministically, perhaps at the unit level with controlled randomness?"
  },
  {
   "t": "2026-08-28T10:16:49Z",
   "kind": "force_push",
   "who": "Bicaru20",
   "commit": "f911ae8345dd22d2787801ed03c6b2a191584832"
  },
  {
   "t": "2026-08-28T10:18:07Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3839087577,
   "text": "[quoted text omitted]\n\nThe idea here is to avoid backdating when using `bumpfee`, without making these changes distinguishable from other transactions that use antifee sniping. If we were to use only `minimum_height` as the inclusive window, we could end up with replacements with a locktime more than 100 blocks behind the current tip. That would be easily fingerprintable as coming from Core, which is exactly what we want to avoid."
  },
  {
   "t": "2026-08-28T10:20:55Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3839092014,
   "text": "[quoted text omitted]\n\nThe only time when this can happen is if the node is still downloading blocks or if the last block is from more than 8 horus ago. This males the `block_height` unrelaibale to compre and it could happen that `minimum_height` is greater than the `block_height` beacuse we don't have blockchain up to date. This could lead to setting a transaction with a locktime to 0 because even though `minimum_height` is less than the current block tip becasue the blockhain is not up to date. Example: We have the originstl transaction `txA` with locktime 12. The current tip is 15 but the node has only downloaded till 10. If we replace the transaction, that would set the locktime to 0 (since `minimum_height` is 12 and `current_height` is 10), and with that logic this would tell that the node is not up to date. Plus I do not belive that setting the rpelacement set to 0 fingerprints the transactions as there are lots of wallets than when bumping fee set the locktime to 0."
  },
  {
   "t": "2026-08-28T10:21:48Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3839092014,
   "text": "[quoted text omitted]\n\n~~But that would disable the antifee sniping completly, wouldn't it be beeter to leave the locktime in the `current_tip`? That way we are still antifee sniping.~~"
  },
  {
   "t": "2026-08-28T10:22:36Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "b7935c708edc225e20dcab0820dff774f841736b",
   "in_reply_to": 3852235914,
   "text": "[quoted text omitted]\n\nIf the original transaction has a final sequence number and a locktime greater than the current tip, the locktime is not enforced because of the sequence number, so the transaction is valid with a locktime far greater than the current tip. This would make `minimum_height` in the replacement greater than the current tip. This is the only case I can think of. The other solution would be to check the sequence number of the original transaction."
  },
  {
   "t": "2026-08-28T10:23:33Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/coincontrol.h",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3861075004,
   "text": "I'm not sure if doing it like this is a good idea. When backdating, if we apply this, the Discuragefeesniping will give a locktime between the `[current_height, current_height-100]`, and most of the times this locktime will be older than the previous locktime since most of replacements are done after a few blocks. That means that we will have either a replacement transaction with a locktime set on the current_height or on the previous lockitme most of the time. If we keep the current logic, we'll have a more diverse distribution of locktimes for replacement transactions."
  },
  {
   "t": "2026-08-28T10:24:08Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3861000931,
   "text": "Fixed in c72998bc4e"
  },
  {
   "t": "2026-08-28T10:24:41Z",
   "kind": "comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "text": "Thanks for the reviews!\n- c72998bc4e: Fix the nits. Also applied Pol suggestion to simplify the code in `Discuragefeesniping` and at the same time fixing the OBO problem.\n- f911ae8345: Add `verbose=True`\n\nFor now putting the pr as a draft. Seeing some of the feedback I see now that the functional test is weak and that a unit test is better. Once the unit test is completed I will open the pr again.\nDuring this time, feel free to respond to my replies to your comments so we can agree on the best approach."
  },
  {
   "t": "2026-08-28T10:26:20Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3860846452,
   "text": "I think that is how it should be. See my previous answer: https://github.com/bitcoin/bitcoin/pull/36040#discussion_r3879830139"
  },
  {
   "t": "2026-08-28T10:29:07Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3839092014,
   "text": "[quoted text omitted]\n\nYou're right about this one, otherwise we would be setting a locktime older than the original transaction."
  },
  {
   "t": "2026-08-28T10:29:42Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3860922521,
   "text": "Yes, you're rigth. I will fix it."
  },
  {
   "t": "2026-08-28T14:14:09Z",
   "kind": "review_comment",
   "who": "danielabrozzoni",
   "assoc": "MEMBER",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3839092014,
   "text": "[quoted text omitted]\n\nI think this makes sense. For example:\n- Current block height is 15, we create `txA` with `locktime = 15`\n- A few more blocks come in, `txA` is not included in any, but we fall behind the tip\n- We want to replace txA with txB; `minimum_height = 15`. Here we could either: a) set txB's locktime to `0`, or b) set txB's locktime to `minimum_height`\n\nIf we go with a), an observer would say that we are either:\n1. using a walelt that does anti-feesniping, but sets nlocktime to 0 when feebumping (assume there are any, I'm not sure)\n2. using Bitcoin Core or equivalent, but we're not synced to the tip.\nIn the imaginary future where we all use the same exact anti-fee sniping strategy, 1. doesn't exist, and the observer would notice we're not synced to the tip\n\nIf we go with b), an observer would say that we are either:\n1. using a wallet that does anti-fee sniping on txs and replacements\n2. using some protocol based on presigned transactions that had a locktime set (and there's no fee sniping at all)\n\nThe reason 2. wouldn't exist in case a) is that it's pretty weird that in a protocol that uses locktime, a txA has a locktime, but its replacement doesn't."
  },
  {
   "t": "2026-09-01T11:26:17Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/spend.cpp",
   "commit": "f26586b91de16a03d4c265a7e8ee9fefb728dd65",
   "in_reply_to": 3839092014,
   "text": "I agree with the axiom of `replacement.nLockTime >= original.nLockTime` in any case. Good privacy strategy: do not reveal more than you already had, if possible.\n\nFollowing that, the solution of setting `replacement.nLockTime = original.nLockTime` preserves the information already visible in the original, while other solutions introduce a new, unusual transition - since we cannot do better because our chain is stale."
  },
  {
   "t": "2026-09-01T11:30:40Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/coincontrol.h",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3861075004,
   "text": "Fair point\u2014the `std::max()` example may not be the right implementation.\n\nMy main point was architectural rather than about that exact implementation. What do you think about keeping this replacement-specific behavior in `CreateRateBumpTransaction()`? The fee-bumper owns both the original and replacement transactions and therefore knows the invariant it must enforce.\n\nWe could still generate the locktime using the desired distribution, possibly through a shared helper, while avoiding `m_previous_locktime` in the generic `CCoinControl` and keeping the generic transaction builder unaware of RBF history."
  },
  {
   "t": "2026-09-08T21:18:17Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/coincontrol.h",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3861075004,
   "text": "I'd rather keep all the anti fee sniping logic inside the `Discuragefeesniping`. I think this way it is easier to follow the code and not delegate part of this logic outside the function because of the RBF case. I feel that otherwise we would be duplicating code when the current approach just needs the `m_previous_locktime` in `CCoinControl`."
  },
  {
   "t": "2026-09-08T21:27:38Z",
   "kind": "force_push",
   "who": "Bicaru20",
   "commit": "d26df478f02e7e076e8287bf43cecf64f3dcc3e8"
  },
  {
   "t": "2026-09-08T21:58:59Z",
   "kind": "force_push",
   "who": "Bicaru20",
   "commit": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e"
  },
  {
   "t": "2026-09-08T22:06:27Z",
   "kind": "comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "text": "Changes made:\n\n-   cc8f4784cf9ba77fb22209adbfa9c2176b82228a: I adpoted @nervana21  suggestion. Now we don't reset the `nLocktime` below the established `minimum_height` even if we are on a stal chain. The only case when we reset the `nlocktime` to 0 is if the `minimum_height` is higher than the current `block_height`. In this case, we set it to 0.\n\n-  903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e: I've eliminated the functional test and added a unit test in the `spend_tests.cpp` of the wallet. I added the following cases:\n\n    1. First, the case to check that the backdating when having a minimum_height occurs between the expected range (`[minimum_height, block_height]`).\n    2. Second, the case to check that the backdating can reach the `minimum_height`. This way we prove that the OBO error is not there and also check that when calculating the `range` in `spend.cpp` this is not 0 as this would cause an error later on `rng_fast.randrange(range)`.\n    3. Third, the case where the `minimum_height` > `block_height`. In this case we set the locktime to 0.\n    4. Fourth, the case where we have a stale chain and `minimum_height` <= `block_height`, in this case we set the locktime to `minimum_height`\n    5. Finally, the case where we have a stale chain and `minimum_height` > `block_height`, in this case we set the locktime to 0."
  },
  {
   "t": "2026-09-09T10:45:37Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/coincontrol.h",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3861075004,
   "text": "I looked more closely at the approach I suggested. Although it separates the replacement-specific behavior a bit more cleanly and avoids adding a field to `CCoinControl`, it introduces complexity elsewhere.\n\nYour approach fits the existing code structure better, and I think the extra complexity of the separation outweighs its benefits here.\n\nThanks for taking the time to consider it."
  },
  {
   "t": "2026-09-09T11:48:01Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": null,
   "text": "Could you add a test covering the full modified path, starting with passing the original locktime through `CCoinControl`? The current tests call `DiscourageFeeSniping()` directly, so they would still pass if the original locktime were no longer forwarded.\n\nA wallet unit test that calls `CreateRateBumpTransaction()` should be enough. Using a stale tip and an original height-based locktime below the tip would make the check deterministic: the replacement should retain the original locktime rather than fall back to zero."
  },
  {
   "t": "2026-09-09T11:51:09Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": null,
   "text": "Could we use an explicitly fixed RNG seed here and verify that the resulting sequence reaches the minimum within the iteration limit? The test can fail even with correct code if none of the 200 attempts succeed (even though the chances are low - thinking long term)."
  },
  {
   "t": "2026-09-09T11:54:04Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": null,
   "text": "Could you add a case with `minimum_height` greater than 99 blocks below the tip? The current tests use gaps of only 1 and 10 blocks, so they wouldn't catch the removal of the 100-value cap. On a current chain, the locktime should still stay within `[block_height - 99, block_height]`."
  },
  {
   "t": "2026-09-09T12:04:50Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/spend.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": null,
   "text": "nit:\n\nWith `minimum_height == 0`, this also changes the locktime distribution for ordinary sends when the chain height is below 99, short chains (like regtest).\n\nFor example, at height 2, the old code samples an offset from `[0, 99]` and clamps negative results to zero. Within the 10% random branch, offset 0 gives locktime 2, offset 1 gives locktime 1, and offsets 2\u201399 all give locktime 0. Zero therefore has a 98% probability within that branch.\n\nThe new code samples an offset from `[0, 2]`, so locktimes 0, 1, and 2 each have a one-third probability within the random branch. The possible locktimes are the same, but their probabilities are different. The 90% branch that uses the current height is unchanged.\n\nIs this intentional? I think this is good as it is, I just wanted to point out the change."
  },
  {
   "t": "2026-09-09T12:11:47Z",
   "kind": "review",
   "who": "molnard",
   "assoc": "NONE",
   "state": "COMMENTED",
   "commit": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e",
   "text": "Concept ACK.\n\nI ran all new and modified tests locally, and they passed. I've left my test-related comments inline."
  },
  {
   "t": "2026-09-09T20:07:43Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e",
   "in_reply_to": null,
   "text": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e: Test: bumpfee does not backdate the locktime\n\nnit: these should be alphabetized with the rest of the includes"
  },
  {
   "t": "2026-09-09T20:20:03Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": null,
   "text": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e: Test: bumpfee does not backdate the locktime\n\n```suggestion\n    bool backdated{false};\n    bool saw_tip_locktime{false};\n    for (int i{0}; i < 200; ++i) {\n        DiscourageFeeSniping(mtx, rng_fast, chain, block_hash, block_height, block_height - 10);\n        BOOST_CHECK_LE(mtx.nLockTime, static_cast<uint32_t>(block_height));\n        BOOST_CHECK_GE(mtx.nLockTime, static_cast<uint32_t>(block_height - 10));\n        if (mtx.nLockTime == static_cast<uint32_t>(block_height)) {\n            saw_tip_locktime = true;\n        } else {\n            backdated = true;\n        }\n        if (backdated && saw_tip_locktime) break;\n    }\n    BOOST_CHECK(backdated);\n    BOOST_CHECK(saw_tip_locktime);\n```\n\nWould it make sense to strengthen this test to check not only that we backdated but also check that we left the locktime unchanged?"
  },
  {
   "t": "2026-09-09T20:23:41Z",
   "kind": "comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "text": "Thanks for taking the suggestions. I've left just a few more comments."
  },
  {
   "t": "2026-09-09T22:14:48Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e",
   "in_reply_to": null,
   "text": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e: Test: bumpfee does not backdate the locktime\n```suggestion\n    // Verify DiscourageFeeSniping on a stale tip when minimum_height is set.\n```\n\nnit: wording"
  },
  {
   "t": "2026-09-12T17:32:03Z",
   "kind": "force_push",
   "who": "Bicaru20",
   "commit": "dcda3c51e72552269445823bc65c35248fbdab88"
  },
  {
   "t": "2026-09-12T17:33:13Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3968112246,
   "text": "[quoted text omitted]\n\nI didn't though of that case, but I don't think it matters since in new chains you can start spending the UTXOs in block 101, and by then this is not a problem anymore."
  },
  {
   "t": "2026-09-12T17:34:09Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3967982041,
   "text": "Done! Added a test in feebumper_test.cpp which bumps a transaction directly through `CreateRateBumpTransaction`."
  },
  {
   "t": "2026-09-12T17:34:24Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3968007511,
   "text": "Yes, I think it makes sense to use a fixed rng seed. Thanks for pointing it out!"
  },
  {
   "t": "2026-09-12T17:34:47Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3968028894,
   "text": "Done! I modified the `discourage_fee_sniping_backdating_bounds` first test so the `minimum_height` is greater than the 100 block limit."
  },
  {
   "t": "2026-09-12T17:35:08Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": 3972652355,
   "text": "Yes, I think it doesnt hurt and makes the test more robust."
  },
  {
   "t": "2026-09-12T17:35:19Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "903d2bfa675f1acfc24e21fbbeff9ceb8b0e019e",
   "in_reply_to": 3973490576,
   "text": "Done!"
  },
  {
   "t": "2026-09-12T17:38:39Z",
   "kind": "force_push",
   "who": "Bicaru20",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050"
  },
  {
   "t": "2026-09-12T17:39:38Z",
   "kind": "comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "text": "Thanks again for the reviews!\n\n- In b72a0533dac88b46755f9ee1051eb588b1934050:\n  - I added a test in `feebumper_test.cpp` (since we use feebumper logic) which bumps a transaction directly through `CreateRateBumpTransaction`. As suggested by @molnard, we do it on a stale chain so we can check if the previous lockitme is propagated to the new tx.\n  - In the test `discourage_fee_sniping_backdating_bounds` I added a fixed rng seed and modified the first test so the `minimum_height` is greater than the 100 block limit.\n  - Also added `saw_tip_locktime` check suggested by @nervana21"
  },
  {
   "t": "2026-09-14T09:48:19Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": null,
   "text": "Shouldn't this be `[block_height - 99, block_height]`? `randrange(100)` returns values from 0 to 99, so the maximum backdate is 99 blocks. Including the current height, that gives 100 possible locktimes.\n\nThe `BOOST_CHECK_GE` below should also use `block_height - 99`; otherwise it allows a 100-block backdate."
  },
  {
   "t": "2026-09-14T09:58:51Z",
   "kind": "review_comment",
   "who": "molnard",
   "assoc": "NONE",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": null,
   "text": "Could we remove the `break` and run all 200 iterations? This would check more generated locktimes against both bounds and could catch regressions from future changes that the early exit would miss. We can still check `backdated` and `saw_tip_locktime` after the loop.\n\nFor example, with the current fixed seed, the first four locktimes are `111, 111, 111, 17`, even if we remove the `std::min(100, ...)` cap. The loop exits there and misses the regression. In a standalone reproduction using Core's RNG, continuing the loop catches a violation of the 99-block bound on call 37."
  },
  {
   "t": "2026-09-14T10:00:55Z",
   "kind": "review",
   "who": "molnard",
   "assoc": "NONE",
   "state": "COMMENTED",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "text": "ACK b72a0533dac88b46755f9ee1051eb588b1934050 with nits.\n\nThe code looks good. I've left a couple of minor comments on the tests."
  },
  {
   "t": "2026-09-14T16:55:49Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/feebumper_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": null,
   "text": "b72a0533dac88b46755f9ee1051eb588b1934050: Test: bumpfee does not backdate the locktime\n\n```suggestion\n    // Mine one more block so one coinbase is mature, then sync a spendable wallet.\n    CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));\n    auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), coinbaseKey);\n\n    std::vector<CRecipient> recipients{{*Assert(wallet->GetNewDestination(OutputType::BECH32, \"dummy\")),\n                                        /*nAmount=*/40 * COIN, /*fSubtractFeeFromAmount=*/true}};\n\n    auto res = CreateTransaction(*wallet, recipients, /*change_pos=*/std::nullopt, CCoinControl{});\n```\n\nSince we're only dealing with a single coinbase coin, I think this can be simplified."
  },
  {
   "t": "2026-09-14T16:57:01Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/feebumper_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": null,
   "text": "b72a0533dac88b46755f9ee1051eb588b1934050: Test: bumpfee does not backdate the locktime\n\n```suggestion\n    // Rate bump with a stale tip: keep the original transaction's locktime.\n```\n\nnit"
  },
  {
   "t": "2026-09-14T16:57:58Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/feebumper_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": null,
   "text": "b72a0533dac88b46755f9ee1051eb588b1934050: Test: bumpfee does not backdate the locktime\n\n```suggestion\n    auto bump_res = feebumper::CreateRateBumpTransaction(*wallet, txr.tx->GetHash(), CCoinControl{}, errors, old_fee, new_fee, mtx, /*require_mine=*/false, /*outputs=*/{});\n```\n\nas above"
  },
  {
   "t": "2026-09-15T11:46:35Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": 4003982107,
   "text": "True, I got confussed when writing the comments. Thnaks!"
  },
  {
   "t": "2026-09-15T11:46:38Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/spend_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": 4004056194,
   "text": "Agreed, it makes sense."
  },
  {
   "t": "2026-09-15T11:58:04Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/feebumper_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": 4007525331,
   "text": "True, thisway is better. Thanks!"
  },
  {
   "t": "2026-09-15T12:28:17Z",
   "kind": "force_push",
   "who": "Bicaru20",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c"
  },
  {
   "t": "2026-09-15T12:30:57Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/feebumper_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": 4007541909,
   "text": "Done!"
  },
  {
   "t": "2026-09-15T12:31:04Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/test/feebumper_tests.cpp",
   "commit": "b72a0533dac88b46755f9ee1051eb588b1934050",
   "in_reply_to": 4007534628,
   "text": "Done!"
  },
  {
   "t": "2026-09-15T12:33:59Z",
   "kind": "comment",
   "who": "Bicaru20",
   "assoc": "CONTRIBUTOR",
   "text": "Changes made:\n- Reduced the `bump_transaction_fee_sniping_check` test in feebumper_test as suggested by @nervana21\n- In `discourage_fee_sniping_backdating_bounds` I eliminated the breaks and change the 100 for a 99 as @molnard suggested."
  },
  {
   "t": "2026-09-15T20:53:22Z",
   "kind": "comment",
   "who": "molnard",
   "assoc": "NONE",
   "text": "ACK e34f9340d615976b1398e6ead0ab8d2bda24907c\n\nChecked the code and ran the tests - all good."
  },
  {
   "t": "2026-09-16T14:28:14Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.h",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "in_reply_to": null,
   "text": "675448e26225d2d82b363a508048dc7819037219: Wallet: Do not allow bumpfee to backdate the replacement transaction locktime\n\n```suggestion\n * The locktime is occasionally backdated, but never below minimum_height\n * and at most 99 blocks below the tip.\n```\n\nnit"
  },
  {
   "t": "2026-09-16T14:30:06Z",
   "kind": "comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "text": "675448e26225d2d82b363a508048dc7819037219: Wallet: Do not allow bumpfee to backdate the replacement transaction locktime\n\nFor this commit message:\nlockitme -> locktime\ngreter -> greater"
  },
  {
   "t": "2026-09-16T14:30:54Z",
   "kind": "review",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
   "text": "tACK e34f9340d615976b1398e6ead0ab8d2bda24907c\n\nleft minor, non-blocking nits"
  }
 ],
 "labels_log": [
  {
   "t": "2026-08-20T15:28:06Z",
   "action": "labeled",
   "label": "Wallet",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-20T15:44:30Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-20T17:02:46Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-08T22:00:18Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-08T23:01:44Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-12T17:39:18Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-12T18:40:00Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-08-28T10:24:55Z",
   "kind": "convert_to_draft",
   "who": "Bicaru20"
  },
  {
   "t": "2026-09-08T22:06:45Z",
   "kind": "ready_for_review",
   "who": "Bicaru20"
  }
 ],
 "text_chars": 28518,
 "text_tokens_estimate": 7129,
 "changed_paths": [
  "src/wallet/coincontrol.h",
  "src/wallet/feebumper.cpp",
  "src/wallet/spend.cpp",
  "src/wallet/spend.h",
  "src/wallet/test/feebumper_tests.cpp",
  "src/wallet/test/spend_tests.cpp"
 ],
 "files": [
  {
   "path": "src/wallet/coincontrol.h",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/wallet/feebumper.cpp",
   "add": 4,
   "del": 0
  },
  {
   "path": "src/wallet/spend.cpp",
   "add": 20,
   "del": 6
  },
  {
   "path": "src/wallet/spend.h",
   "add": 6,
   "del": 2
  },
  {
   "path": "src/wallet/test/feebumper_tests.cpp",
   "add": 31,
   "del": 0
  },
  {
   "path": "src/wallet/test/spend_tests.cpp",
   "add": 90,
   "del": 0
  }
 ],
 "test_lines": 121,
 "git": {
  "head": "e34f9340d615976b1398e6ead0ab8d2bda24907c",
  "head_matches_backup": true,
  "base": "59fd053dac83c989dcb18f6d92e0d17eb7ecb227",
  "commits": [
   {
    "sha": "675448e262",
    "subject": "Wallet: Do not allow bumpfee to backdate the replacement transaction locktime",
    "files": 4,
    "add": 32,
    "del": 8
   },
   {
    "sha": "e34f9340d6",
    "subject": "Test: bumpfee does not backdate the locktime",
    "files": 2,
    "add": 121,
    "del": 0
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "5c44aaebed67ef02",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}