{
 "number": 35315,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35315",
 "title": "refactor: Use NodeClock::time_point in more places",
 "author": "maflcko",
 "author_association": "MEMBER",
 "created_at": "2026-05-18T15:21:04Z",
 "updated_at": "2026-09-08T20:52:22Z",
 "age_days": 122,
 "draft": false,
 "labels": [
  "Refactoring",
  "Needs rebase"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
 "head_ref": "2603-net-less-GetTime",
 "head_repo": "maflcko/bitcoin-core",
 "head_history": [
  {
   "t": "2026-05-18T15:49:28Z",
   "sha": "fa2a20ca232c8b05162109bfb73adbfc75816eea"
  },
  {
   "t": "2026-05-18T15:52:56Z",
   "sha": "fa8b05f2b730648b9d31938803c727adc2a9cc47"
  },
  {
   "t": "2026-05-29T10:09:10Z",
   "sha": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da"
  },
  {
   "t": "2026-07-08T09:31:14Z",
   "sha": "fada25cfe60059c50dfb769faf97f9f9e6c07f25"
  },
  {
   "t": "2026-07-08T14:11:21Z",
   "sha": "fa314e433020667d1b1171b10083b5a53964d0f2"
  },
  {
   "t": "2026-07-09T15:12:39Z",
   "sha": "fa28d748c33726057f8e49630df5ad5bf52f8053"
  },
  {
   "t": "2026-07-09T16:10:11Z",
   "sha": "fa714df6625410bbcb25ae2588d46aae72a74c0a"
  },
  {
   "t": "2026-07-16T09:52:12Z",
   "sha": "fa146ffed41a5355d1374b4c4f912f3701e0beec"
  },
  {
   "t": "2026-07-21T07:42:31Z",
   "sha": "fa1f6e7f2292069a3c0042f8563b4510a0a03242"
  },
  {
   "t": "2026-07-21T13:39:18Z",
   "sha": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063"
  }
 ],
 "additions": 183,
 "deletions": 181,
 "changed_files": 24,
 "commit_count": 10,
 "size_bucket": "M",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "stickies-v",
      "url": "https://github.com/bitcoin/bitcoin/pull/35315#pullrequestreview-4764750338"
     },
     {
      "login": "ryanofsky",
      "url": "https://github.com/bitcoin/bitcoin/pull/35315#pullrequestreview-4800844081"
     },
     {
      "login": "jeanpablojp",
      "url": "https://github.com/bitcoin/bitcoin/pull/35315#pullrequestreview-5146886565"
     }
    ],
    "concept_ack": [
     {
      "login": "w0xlt",
      "url": "https://github.com/bitcoin/bitcoin/pull/35315#issuecomment-4700260474"
     }
    ],
    "stale_ack": [
     {
      "login": "sedited",
      "url": "https://github.com/bitcoin/bitcoin/pull/35315#pullrequestreview-4566138032"
     },
     {
      "login": "seduless",
      "url": "https://github.com/bitcoin/bitcoin/pull/35315#pullrequestreview-4566361081"
     }
    ]
   },
   "conflicts": [
    {
     "number": 35561,
     "title": "net: move some CNodeState fields to Peer",
     "author": "Crypt-iQ"
    },
    {
     "number": 35522,
     "title": "refactor: Extract per-message helpers from SendMessages() (move-only)",
     "author": "pablomartin4btc"
    },
    {
     "number": 35502,
     "title": "refactor: extract per-message helpers from ProcessMessage (move-only)",
     "author": "w0xlt"
    },
    {
     "number": 34824,
     "title": "net: encapsulate TxRelay state and replace recursive mutexes",
     "author": "w0xlt"
    },
    {
     "number": 34743,
     "title": "p2p: don't disconnect manual peers for block stalling",
     "author": "willcl-ark"
    },
    {
     "number": 34628,
     "title": "p2p: Replace per-peer transaction rate-limiting with global rate limits",
     "author": "ajtowns"
    },
    {
     "number": 34565,
     "title": "refactor: extract BlockDownloadManager from PeerManagerImpl",
     "author": "w0xlt"
    },
    {
     "number": 27052,
     "title": "test: rpc: add last block announcement time to getpeerinfo result",
     "author": "LarryRuane"
    }
   ]
  }
 },
 "acks_parsed": {
  "w0xlt": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-06-14T00:47:46Z",
   "stale": false
  },
  "sedited": {
   "kind": "ack",
   "hash": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da",
   "t": "2026-06-24T21:29:43Z",
   "stale": true
  },
  "seduless": {
   "kind": "ack",
   "hash": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da",
   "t": "2026-06-24T22:14:06Z",
   "stale": true
  },
  "ryanofsky": {
   "kind": "ack",
   "hash": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "t": "2026-07-28T18:53:41Z",
   "stale": false
  },
  "stickies-v": {
   "kind": "ack",
   "hash": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "t": "2026-07-23T18:09:58Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 2,
  "stale_ack": 2,
  "concept_ack": 1,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 8,
  "changes_requested": 0,
  "distinct_reviewers": [
   "jeanpablojp",
   "ryanofsky",
   "sedited",
   "seduless",
   "stickies-v",
   "w0xlt"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-07-21T14:36:42Z",
  "last_reviewer_activity": "2026-09-08T20:52:17Z",
  "last_reviewer": "jeanpablojp",
  "author_silent_days": 58,
  "waiting_on_author_days": 8,
  "days_since_update": 8
 },
 "refs": {
  "mentioned": [
   35557
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 35557,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "kernel, validation: Add btck_chainstate_manager_set_clock_time"
   }
  ],
  "conflicts": [
   35561,
   35522,
   35502,
   34824,
   34743,
   34628,
   34565,
   27052
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/addrman.cpp",
  "src/net.h",
  "src/net_processing.cpp",
  "src/net_processing.h",
  "src/node/eviction.h",
  "src/test/net_peer_eviction_tests.cpp",
  "src/test/txrequest_tests.cpp",
  "src/util/time.h"
 ],
 "body": "It is a bit confusing to have some code use the deprecated `GetTime`, which returns a duration and not a time point, and other code to use `NodeClock` time points.\n\nFix all places in `net_processing.cpp` to properly use time_point types.",
 "commits": [
  {
   "sha": "fa562e4c3d3af33844322cbcb7562ca77179dfca",
   "date": "2026-07-21T07:39:51Z",
   "message": "refactor: Allow NodeClock::epoch to be used in NodeSeconds context\n\nSome contexts have only second precision by definition, and it should be\nallowed to use NodeClock::epoch as an alias for zero.\n\nAlso, add a docstring."
  },
  {
   "sha": "fa208c5b24717fca45f7af10d27f2aeb5e068040",
   "date": "2026-07-21T07:39:58Z",
   "message": "refactor: Use NodeSeconds for m_best_block_time\n\nPreviously, it was using a duration type."
  },
  {
   "sha": "fafd1e3312fdf3410827940da88d3db2119614aa",
   "date": "2026-07-21T07:40:25Z",
   "message": "p2p: Use NodeClock::time_point for m_last_block_announcement\n\nPreviously, a raw i64 was used, which also required a cast to seconds.\n\nExternally, this is a refactor. Internally, this is a minimal behavior\nimprovement: The value tracks the last *new* block announcement. It\nshould be rare for more than one new block to be announced in the same\nsecond. Even if several new blocks were announced in the same second,\nthe value is only used in EvictExtraOutboundPeers. Changing the time\npoints to be more precise may change the sorting of the worst peers when\nthey all announced a block in the same second. In this case it should be\nfine and preferable, to use the more accurate sorting from the exact\nblock announce time for the worst peers, than to fall back to peer with\nthe highest id.\n\nNit note: The removed \\n char in the log is not needed (refactor)."
  },
  {
   "sha": "fa593c2e6e29864cb3b5e95bf37b790a0804ae38",
   "date": "2026-07-21T07:40:44Z",
   "message": "refactor: Use NodeClock::time_point in txdownloadman/txrequest\n\nThis may minimally increase the precision from \u00b5s to ns, but the\nbehavior does not change in this refactor."
  },
  {
   "sha": "fab33fdd92878de9e715b4c3214e939b51f08b6d",
   "date": "2026-07-21T13:33:17Z",
   "message": "p2p: Use NodeClock::time_point instead of std::chrono::seconds in node stats and eviction\n\nExternally, this is a refactor, because stats reported by RPC do not\nchange.\n\nInternally, this is a minimal behavior improvement: With the precision\nincrease from seconds to nanoseconds, SelectNodeToEvict will be more\nprecise picking the protected peers with CompareNodeBlockTime and\nCompareNodeTXTime."
  },
  {
   "sha": "fa4d22d06cbaa2339ad076d637d93e42bfa4fe93",
   "date": "2026-07-21T13:33:23Z",
   "message": "refactor: Drop HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER \"precision\"\n\nThe base timeout is 15 minutes, so trying to imply that a rounding error\nof 1ms is relevant seems confusing.\n\nFix the confusion by using the integer division truncation to round down\nto the next ms."
  },
  {
   "sha": "fa42ebd0d1336b7440283bc1a22632f03a3137a1",
   "date": "2026-07-21T13:33:25Z",
   "message": "refactor: Use NodeClock::time_point instead of std::chrono::microseconds in net_processing\n\nfor fields:\n\n* m_headers_sync_timeout\n* m_downloading_since\n* m_next_send_feefilter\n* m_next_addr_send\n* m_next_local_addr_send\n* m_next_inv_send_time\n* m_next_inv_to_inbounds_per_network_key\n* m_stalling_since\n\nThis patch may minimally increase the precision from \u00b5s to ns, but the\nbehavior does not change in this refactor."
  },
  {
   "sha": "fa8a149c2923d8075fc127dae39653142f6211fa",
   "date": "2026-07-21T13:33:38Z",
   "message": "refactor: Use time_point::max() for m_headers_sync_timeout\n\nThis refactor does not change any behavior. It uses a single natural\nspecial in-band sentinel values instead of two different ones:\nepoch-zero and time_point::max().\n\nReview help:\n\nfSyncStarted gates all uses of the field, so the zero-epoch default is\nirrelevant. time_point::max() used below is the meaningful sentinel\nhere, representing a disabled timeout after sync has started, so use\nthat value consistently."
  },
  {
   "sha": "fa58d8f4fb36e9028503f6bb9415b5c7c1c4dfa1",
   "date": "2026-07-21T13:34:21Z",
   "message": "refactor: Use time_point::min()/max() in net_processing\n\nThis refactor does not change any behavior. It uses a more natural\nspecial in-band sentinel values instead of epoch-zero.\n\nFields with time_point::min() as new sentinel value:\n* m_downloading_since\n* m_next_send_feefilter\n* m_next_addr_send\n* m_next_inv_send_time\n* m_next_local_addr_send\n\nFields with time_point::max() as new sentinel value:\n* m_stalling_since"
  },
  {
   "sha": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "date": "2026-07-21T13:35:18Z",
   "message": "refactor: Use NodeClock for last GetTime call in net_processing.cpp"
  }
 ],
 "timeline": [
  {
   "t": "2026-05-18T15:49:28Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa2a20ca232c8b05162109bfb73adbfc75816eea"
  },
  {
   "t": "2026-05-18T15:52:56Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa8b05f2b730648b9d31938803c727adc2a9cc47"
  },
  {
   "t": "2026-05-29T10:09:10Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da"
  },
  {
   "t": "2026-06-14T00:47:46Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK"
  },
  {
   "t": "2026-06-24T21:29:43Z",
   "kind": "review",
   "who": "sedited",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da",
   "text": "ACK fa14855ff1bd527cbbd56080eb2cfdcd74d780da"
  },
  {
   "t": "2026-06-24T22:14:06Z",
   "kind": "review",
   "who": "seduless",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da",
   "text": "Tested ACK fa14855ff1bd527cbbd56080eb2cfdcd74d780da\n\nIn commit fa1a22927adac877c46645b586ecda8ae1592002, `peerman_tests` still has a few straightforward migrations left. Non-blocking, happy to send a follow-up if you'd rather not fold it in here."
  },
  {
   "t": "2026-06-25T06:16:41Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Yeah, the goal here is mostly to fix net_processing fully. Not sure what the ideal size of a pull request is, but 7 commits and ~150 lines explicitly touched (and a few more implicitly) seems ok-ish.\n\nEdit: I think the next pull should just remove it fully, in all remaining places?"
  },
  {
   "t": "2026-06-25T15:50:33Z",
   "kind": "comment",
   "who": "seduless",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nPerhaps it's useful for the next follow-up to avoid clashing with the scoped clock proposed for the kernel in #35557?"
  },
  {
   "t": "2026-06-29T13:04:21Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Allow NodeClock::epoch to be used in NodeSeconds context\" (fa10abb29538fabb4ad97dd70a2e4c709f38fc16)\n\nThis change seems kludgy and would seem to encourage writing bad code.\n\nThe kludgy part is mixing up different `time_point` types inside a clock definition which should have one native time type.\n\nThe bad code part is encouraging code that shouldn't be tied to the unix epoch to reference it unnecessarily. For example the code using `NodeClock::epoch` in this commit is storing absolute times as durations which we do not want and should not encourage. And code in later commits uses `NodeClock::epoch` as an inappropriate sentinel value where `time_point::min`, `time_point::max`, or `std::nullopt` sentinels would be more appropriate.\n\nWould recommend dropping this commit, if not deleting the `NodeClock::epoch` constant entirely which seems nonstandard and not very helpful.\n\nIf default time values are needed, it's more direct and less verbose to call the default time point constructor. Having this second bitcoin-specific way of default initializing time variables just makes code less consistent and more confusing."
  },
  {
   "t": "2026-06-29T13:17:15Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa1a22927adac877c46645b586ecda8ae1592002",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeSeconds for m_best_block_time\" (fa1a22927adac877c46645b586ecda8ae1592002)\n\nThis seems unnecessarily confusing. NodeClock is an arbitrary precision system clock while NodeSeconds explicitly uses integer second values. It doesn't seem helpful to mix up these different types and it is also just shorter to write `std::atomic<NodeSeconds> m_best_block_time{}`."
  },
  {
   "t": "2026-06-29T13:24:01Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeClock::time_point for m_last_block_announcement\" (fa67c651510db5e571fa82fe692ac33e48c451a4)\n\nCan commit message be clarified to say whether there is any change in behavior here? Presumably if times were previously represented in seconds, and now higher precision times are used, now comparisons between the times may return different values and new bugs and corner cases could be exposed.\n\nChanging behavior should be ok and probably even an improvement, but commit message should clarify whether it is changing or not. Same comment applies to most other commits in this PR as well."
  },
  {
   "t": "2026-06-29T13:41:28Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/test/txrequest_tests.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeClock::time_point in txdownloadman/txrequest\" (fae925ff923f05f34c800b92039e5fd058262b74)\n\nNot important, but since this commit moves away from hardcoding microsecond types everywhere, it would be nice for test code here to stop hardcoding microseconds as well and switch to ticks (`NodeClock::duration`) instead."
  },
  {
   "t": "2026-06-29T13:59:02Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeClock::time_point instead of std::chrono::seconds\" (fa72cdf9563a39d6253570199a594b40d14c65dd)\n\nCommit title is very generic. Would be good to add \"in node stats\" to indicate where the replacement is happening."
  },
  {
   "t": "2026-06-29T14:15:16Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeClock::time_point instead of std::chrono::microseconds\" (cccc80ec090257ad614e54ca3975004282531bd9)\n\nWould seem good to mention net_processing in the commit title since otherwise it is unclear what part of the codebase this commit affects."
  },
  {
   "t": "2026-06-29T14:59:22Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "cccc80ec090257ad614e54ca3975004282531bd9",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeClock::time_point instead of std::chrono::microseconds\" (cccc80ec090257ad614e54ca3975004282531bd9)\n\nThe new code seems to make less sense semantically the old code.\n\nThe previous `if (state.m_stalling_since.count())` reads like \"if a stalling_since value is set\".\n\nThe new \"if (state.m_stalling_since != NodeClock::epoch)\" reads like \"if not stalling since january 1, 1970\"\n\nNumber of ways this could be improved:\n\n- Default `m_stalling_since` to `NodeClock::time_point::max` instead of `NodeClock::epoch`\n- Make `m_stalling_since` use `std::optional`.\n- Compare against the `NodeClock::time_point{}` default value instead referencing the unix epoch."
  },
  {
   "t": "2026-06-29T15:12:23Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fada25cfe60059c50dfb769faf97f9f9e6c07f25",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeClock::time_point instead of std::chrono::microseconds\" (cccc80ec090257ad614e54ca3975004282531bd9)\n\nWould seem more consistent to use `NodeClock::time_point::min()` here instead of `NodeClock::epoch` to indicate we are not syncing, given that `NodeClock::time_point::max()` is used immediately below to indicate we are done syncing. Epoch time should not be relevant."
  },
  {
   "t": "2026-06-29T16:29:44Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da",
   "text": "Code review ACK fa14855ff1bd527cbbd56080eb2cfdcd74d780da. But approach +0.5. `NodeClock::time_point` is an improvement over using integer or duration types to represent time points. But I don't think it's a good thing to be hardcoding `NodeClock::epoch` everywhere, especially for things like block times which don't come from the node/system clock. If default-initializing time variables, it seems better to do it the standard way by calling default constructors, instead of inviting inconsistency and preferring to use bitcoin-specific `NodeClock::epoch` constant. Also, if choosing sentinel time values, it seems better to use min or max values than to treat the epoch time as being special unnecessarily.\n\nI am also not sure it's good hardcode `NodeClock::time_point` types everywhere. It seems like a lost opportunity to choose to hardcode a platform-dependent clock type that doesn't have a standard precision or representation, when we could use application-specific type aliases like `using MempoolTime = NodeClock::time_point;`, `using NetworkTime = NodeClock::time_point;`, `using BlockTime = NodeSeconds;` to not be tied to the system clock and be able to intentionally chose which precision and representations to use in different areas of the code. For example, it would be nice to define standard ways of serializing each of these types without requiring them all to be serialized the same way.\n\nI left more detailed code review comments below, but I guess my main feedback is I would be happier to see most `NodeClock::epoch` uses dropped. And I also think it could be a good idea to replace most `NodeClock::time_point` references here with a networking specific `NetworkTime` alias."
  },
  {
   "t": "2026-06-30T15:21:45Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nNot sure. Those types are direct aliases, so they *are* tied to the (mockable) system clock (aka node clock). Also, given that they are type aliases, so anyone can use them interchangeably, which seems confusing.\n\nWhile I like my approach, I don't think it matters much and I am happy to switch to whatever reviewers prefer."
  },
  {
   "t": "2026-06-30T15:21:48Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "cccc80ec090257ad614e54ca3975004282531bd9",
   "in_reply_to": 3492644922,
   "text": "[quoted text omitted]\n\nOk, I'll think about those over the next few days, mostly wondering how to minimize review churn on this.\n\nThough, I think that it is nice to make the special value verbosely typed."
  },
  {
   "t": "2026-06-30T15:21:50Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492350338,
   "text": "(same). I think it is easy enough to call `git show --stat` to see the area of the codebase, but I am happy to consider changing it if there is a  push to this pull."
  },
  {
   "t": "2026-06-30T15:21:52Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492242685,
   "text": "I think that makes the commit message larger than the recommended 70chars, but I am happy to push this, if you think it is a blocker."
  },
  {
   "t": "2026-06-30T15:21:54Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/txrequest_tests.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492126975,
   "text": "I don't think this is allowed. Changing the fuzz test values to something else will likely change the fuzz input format, which is a behavior change.\n\nChanging the behavior is not allowed in refactor commits, according to `CONTRIBUTING.md:137`"
  },
  {
   "t": "2026-06-30T15:21:57Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492014147,
   "text": "[quoted text omitted]\n\nI use the keyword `refactor:` in the pull request title and in all commits to indicate that no behavior change is going on. If some weird or obscure behavior change was happening, it would be my burden to point them out in the commit message and reviewers are meant to be encouraged to report undocumented behavior changes.\n\nI like this notation, and I think it is brief and understandable. Also, it is explained in the docs:\n\n```\nCONTRIBUTING.md-126-### Creating the Pull Request\nCONTRIBUTING.md-127-\nCONTRIBUTING.md-128-The title of the pull request should be prefixed by the component or area that\nCONTRIBUTING.md-129-the pull request affects. Valid areas are:\nCONTRIBUTING.md-130-\n...\nCONTRIBUTING.md:137:  - `refactor` for structural changes that do not change behavior\n```\n\nHowever, if you think it is a blocker, I can modify all the commit messages to say:\n\n```\nThis refactor does not change any behavior.\n```"
  },
  {
   "t": "2026-06-30T15:22:00Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa1a22927adac877c46645b586ecda8ae1592002",
   "in_reply_to": 3491970807,
   "text": "[quoted text omitted]\n\nI don't understand why this is confusing. The epoch of a clock is always the same (a compile time constant), regardless of the the clock's time point duration.\n\n[quoted text omitted]\nYes, it is shorter, but also more confusing. `0s` or `epoch` is used as a sentinel value, so it seems good to be explicit about the special meaning.\n\nI know you mentioned that `std::optional` can be used instead, which would be cleaner. However, I don't really agree here, because it would make the code a lot more verbose and every call-site would have to safely unwrap the optional one way or another (nested `if`, `value_or`, ...)\n\nI like the current commit, so I think I'll keep it, but let me know if this is a blocker."
  },
  {
   "t": "2026-06-30T15:22:02Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3491892049,
   "text": "[quoted text omitted]\n\nIIUC addrman is using that to detect corrupt data and also using that as a sentinel. Also, it is used in the addrman serialization. The alternative to serializing just the duration would be to also serialize the type (epoch) somehow, but I don't think a full addrman rewrite with an addrman serialize change is the right call for a simple refactoring change.\n\nI am not sure if there is much value in removing the zero/echo stuff everywhere, but when epoch is used consistently, it is also easier to grep for it and find all places with a single call to `git grep`.\n\nI like the commit, and I don't think it matters much, so I'd prefer to keep it. But I am happy to drop it, if you think it is a blocker. Also, I am happy to review a different pull removing `epoch` (assuming that such a pull is doing some more important substantial changes as well)"
  },
  {
   "t": "2026-06-30T16:21:07Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3491892049,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3499890163\n\nI'm not suggesting changing the addrman serializaton format. That would be crazy. I am just asking to not increase usage of the unnecessary, nonstandard, undocumented NodeClock::epoch constant.\n\n[quoted text omitted]\nThere is no enforcement NodeClock::epoch is used consistently. Bitcoin core is adding a new, nonstandard clock class member to reference january 1, 1970, and encourage using it as a magical time point without disabling `time_point` default constructor which also sets this time. If this change included a lint check to prevent the default time_point constructor from being used that could make more sense. But this change provides no extra safety while encouraging bad duration-based code and magic-constant code to be written.\n\nThe other changes in this PR using time point more places seem great. But this change extending `NodeClock::epoch` to places where it doesn't actually make sense to treat January 1, 1970 as special is a step backwards. Better alternatives are using time_point::min, and time_point::max and std::optional in most cases.\n\n[quoted text omitted]\nI would definitely encourage dropping it but if you don't want to drop it I would like to see some explanation of why it is good to have. It seems like the only use-cases are bad code. `NodeClock::epoch` is not mentioned in the PR description and no other reviewer has commented on it. It's referenced 10 times before this PR and 44 times after. I gave a code review ACK and half-concept ACK on the PR so this is not a blocker for me, but I would definitely like to see it dropped from the PR or properly explained."
  },
  {
   "t": "2026-06-30T16:39:02Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa1a22927adac877c46645b586ecda8ae1592002",
   "in_reply_to": 3491970807,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3491970807\n\n[quoted text omitted]\nLooking at net_processing.cpp I see only one access in ApproximateBestBlockDepth which seems buggy and better off using std::optional.\n\nYou also claim that calling the default time point constructor (https://en.cppreference.com/cpp/chrono/time_point/time_point) would be \"more confusing\" without saying what is confusing. The default constructor is part of the standard and well-documented. NodeClock::epoch is nonstandard, undocumented, unjustified, and even more of an oddity after this PR because it now uses a different time type than the rest of the clock."
  },
  {
   "t": "2026-06-30T16:46:16Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492014147,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3499889472\n\n[quoted text omitted]\nYes, that's my intent here. Comparisons like `state->m_last_block_announcement < oldest_block_announcement` that could previously be false when times were seconds may now be true when times are nanoseconds (in case where times were equal). So this change does not seem like a pure refactoring and it would be good to point out any possible behavior changes like this in commit messages.\n\nA similar case where switching to more precise time types caused an observable change in behavior is 77043b0c856f195bda051a1feb2505347f0eddf3. There are also cases where changing time types could lead to overflows, but I don't think that is happening here."
  },
  {
   "t": "2026-06-30T16:50:03Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492350338,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3492350338\n\nThanks, anything seems fine. It is just nice to have to be have some idea about which code is changing when looking at git log --oneline output."
  },
  {
   "t": "2026-06-30T16:54:19Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "cccc80ec090257ad614e54ca3975004282531bd9",
   "in_reply_to": 3492644922,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3492644922\n\n[quoted text omitted]\nNote: of the 3 suggestions, comparing against `NodeClock::time_point{}` would be the smallest change, and would be a relative improvement because it would make code that is using an inappropriate sentinel value look like it using an inappropriate sentinel value. But other alternatives to use an appropriate value or use std::optional do not seem like much work either."
  },
  {
   "t": "2026-06-30T17:34:13Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da",
   "text": "Thanks for the replies. I still think expanded uses of NodeClock::epoch in this PR are bad, and haven't seen a positive case being made for them. So would like to see that addressed  by dropping them, improving them, or explaining benefits with some rationale.\n\nre: https://github.com/bitcoin/bitcoin/pull/35315#issuecomment-4845161113\n\n[quoted text omitted]\nNot sure what is confusing. Type aliases are meant to be interchangeable. They exist to express developer intent and make code more readable and maintainable. Using them would make time variables declarations more self-documenting, allow changing time types without causing churn, and allow adding more type constraints or features like serialization support in the future. I don't have a strong opinion on this. I just think it is a good idea without any downsides that I can see."
  },
  {
   "t": "2026-07-01T02:25:35Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3491892049,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3491892049\n\nAnother possible approach you can take if you are not comfortable with changing existing code or using the default [`time_point` constructor](https://en.cppreference.com/cpp/chrono/time_point/time_point) for its intended purpose would be to define a standalone constant like:\n\n```c++\n//! Default value assigned to a NodeSeconds time point variable if no explicit\n//! value is set. Since C++20 this is guaranteed to be the unix epoch time,\n//! 1970-01-01T00:00:00Z.\n//! Bitcoin Core code should generally avoid referencing this constant or\n//! treating this time point as special. If a time variable is unset it is\n//! usually preferable to initialize it with time_point::max or time_point::min\n//! values for more natural comparisons, or to use std::optional.\nconstexpr NodeSeconds NODE_UNSET_TIME{};\n```\n\nThis would be a drop-in replacement for the new `NodeClock::epoch` definition in this PR and avoid the problems I think `NodeClock::epoch` creates of encouraging incorrect and unsafe code, since it has a scarier  and usage comment. It would also avoid adding a nonstandard member to the clock class."
  },
  {
   "t": "2026-07-01T14:37:08Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3491892049,
   "text": "[quoted text omitted]\n\nOk, I just fail to see how to change addrman to avoid this magic value of zero. It is deeply embedded, so any change away from magic-zero means a larger re-write.\n\nI agree with you that epoch is unnecessary, but I think it is self-explanatory that it means the epoch (time-zero). Also there is a static_assert for documentation: `static_assert(NodeClock::epoch.time_since_epoch().count() == 0);`\n\nYour suggested docstring looks nice. I am happy to modify the first commit to add that docstring to `epoch`.\n\nI am less sure about moving this to a stand-alone constant. I can see your criticism, but I don't think `epoch` simply existing is *encouraging* incorrect and unsafe code (compared to the alternative of having no docstring and just a std-lib default constructor without any docstring/warning).  If moving to a stand-alone constant is important, maybe it can be done in a follow-up?\n\n[quoted text omitted]\nBtw, I agree. It is just that I don't agree the constant itself makes it worse. This is simply a years-old pre-existing code pattern, and I don't want to expand the scope here too much. (The changes are already 7 commits and 150+ lines changed)\n\nThe other reviewers didn't seem to have flagged it either?"
  },
  {
   "t": "2026-07-01T15:17:34Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492014147,
   "text": "[quoted text omitted]\n\nI don't think the bug was caused by switching to more precise time. The bug fixed there is a rare pre-existing bug, which was made more likely (almost deterministic) by using precise time.\n\n[quoted text omitted]\nCorrect. C++ duration types can deal with year ranges of roughly +-292 years, according to https://en.cppreference.com/cpp/chrono/duration.\n\nI'd presume, mostly there are only overflow issues, when using native time_point ::min() or ::max() in combination with untrusted input seconds (thus injecting a multiplication that overflows).\n\n[quoted text omitted]\nCorrect, but this change in behavior is not reliable or observable from outside. In fact, the tie-breaker behavior on the peer-id seems questionable to begin with. If block announcement happened on a \"second-boundary\" on master, e.g a block is announced by peer N in second 2.99, but by peer N+1 in second 3.01 (difference 0.02 seconds), then peer N is evicted. However, if the same difference of 0.02 seconds happens some other time (like peer_N announces at 5.20s and peer_N+1 at 5.22s), then peer N+1 is evicted.\n\nHappy to add this to the commit description, or happy to remove the code, but I wouldn't remove the `refactor:` label, as I don't consider this a change of behavior. Though, I can also add back the cast to seconds explicitly, if you think it makes sense."
  },
  {
   "t": "2026-07-01T15:18:12Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/txrequest_tests.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492126975,
   "text": "(closing thread due to thumbs up)"
  },
  {
   "t": "2026-07-01T15:26:04Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI mostly think this invites bike-shedding, because it is less clear where to draw the line without knowing any of the imaginary future plans. E.g. should network time be the same alias like p2p time, and mempool time, and validation time, or should even different fields in p2p have different named time aliases, ...? [Meta note: Generally it is best to provide each review topic in a new review thread, and not in the global thread. Otherwise, it is harder to follow the global thread, because it mixes different sub-threads]"
  },
  {
   "t": "2026-07-04T09:19:11Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI agree with @maflcko, and see similar things with other aliases that were introduced in the codebase. I think they are a poor tool for being the impetus for more in depth code changes. Sometimes they can help documenting intent, but for the reasons named here I don't think they really make it easier."
  },
  {
   "t": "2026-07-07T16:53:05Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3491892049,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3506795156\n\nI think I'm not sure what you are asking here. I have acked this PR. I just think this commit (fa1a22927adac877c46645b586ecda8ae1592002) is bad and unnecessary and suggested several alternatives that would be better:\n\n1. Not using magic zero value in runtime code but keeping it in serialized formats (runtime code might prefer `std::optional`, `time_point::min` or `time_point::max` values depending on the situation).\n2. Keeping magic zero value in runtime code but deleting (or at least not increasing use of) the nonstandard and error-prone `NodeClock::epoch` constant by using the standard C++ approach of calling the default `time_point{}` constructor.\n3. Adding a `NODE_UNSET_TIME` constant to make it clear that a magic value is being used that needs to be checked for separately, and also discourage code in the style from being written in the future."
  },
  {
   "t": "2026-07-07T17:09:48Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492014147,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3507087415\n\n[quoted text omitted]\nI don't know what happened here, but I'm definitely not asking you to remove refactoring label, or add casts to seconds.\n\nQuoting my original comment \"Can commit message be clarified\" \"commit message should clarify\" and \"Changing behavior should be ok and probably even an improvement\"... and I'm just saying commit messages in this PR would be better if they stated which commits change behavior of the code and which do not.\n\nA concrete reason for asking this is that while you and I have changed `time_point` code recently and are familiar with these edge-case behavior changes, other reviewers and readers may not be. If a commit only says it is a refactor and swaps out C++ types, reviewers might not know they should be looking out for these cases."
  },
  {
   "t": "2026-07-07T17:56:21Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fa14855ff1bd527cbbd56080eb2cfdcd74d780da",
   "text": "Thanks for the replies. To be clear my only objection to this PR is the expanded use of the `NodeClock::epoch` constant which I think will lead to bugs and encourage writing bad code. I suggested various specific alternatives in my comments, and also ACKed this PR so these comments can be ignored.\n\nMy [suggestion](https://github.com/bitcoin/bitcoin/pull/35315#pullrequestreview-4592059759) to use type aliases instead of hardcoding `NodeClock::time_point` is less important, because using `NodeClock::time_point` is definitely better than what current code does, so type aliases would just be an additional improvement. I did list specific reasons I think they are a good idea (making intent clearer by indicating which times need to have the same types and which time do not, making it possible to introduce module-specfic validation and serialization formats without needing to update many call sites) while objections seem more shallow and hand-wavy (\"invites bike-shedding\", \"they are a poor tool\") that list no technical downsides. Seems fine to agree to disagree on this, though."
  },
  {
   "t": "2026-07-08T09:31:14Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fada25cfe60059c50dfb769faf97f9f9e6c07f25"
  },
  {
   "t": "2026-07-08T09:31:45Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fada25cfe60059c50dfb769faf97f9f9e6c07f25",
   "in_reply_to": 3492729448,
   "text": "[quoted text omitted]\n\nSorry, I don't follow here. Using `epoch` or `min` is equally irrelevant and wrong here: The value isn't used and trying to imply that a timeout will happen in the past is confusing. Either this should be left as-is (which I've done in this pull request), or this should be rewritten from the ground up, but I think this pull is already large enough. Unless reviewers want me to rewrite this in a separate commit, I'll leave this as-is."
  },
  {
   "t": "2026-07-08T09:31:53Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "cccc80ec090257ad614e54ca3975004282531bd9",
   "in_reply_to": 3492644922,
   "text": "[quoted text omitted]\n\nThx done (in a new commit), and resolving thread"
  },
  {
   "t": "2026-07-08T09:32:01Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492350338,
   "text": "[quoted text omitted]\n\nthx, done, and resolving"
  },
  {
   "t": "2026-07-08T09:32:04Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492242685,
   "text": "[quoted text omitted]\n\nthx, done, and resolving"
  },
  {
   "t": "2026-07-08T09:32:07Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3492014147,
   "text": "[quoted text omitted]\n\nThx, done. Also used `::min` here over `::epoch`. Also, resolving thread."
  },
  {
   "t": "2026-07-08T09:32:22Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa1a22927adac877c46645b586ecda8ae1592002",
   "in_reply_to": 3491970807,
   "text": "[quoted text omitted]\n\nthx, done"
  },
  {
   "t": "2026-07-08T09:32:26Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3491892049,
   "text": "[quoted text omitted]\n\nThx, done in the commit msg and docstring."
  },
  {
   "t": "2026-07-08T09:38:28Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Thx for the review. Addressed/replied to all review threads while force pushing the rebase. The second-to-last commit was split up and rewritten from scratch."
  },
  {
   "t": "2026-07-08T14:11:21Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa314e433020667d1b1171b10083b5a53964d0f2"
  },
  {
   "t": "2026-07-08T14:13:01Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fada25cfe60059c50dfb769faf97f9f9e6c07f25",
   "in_reply_to": 3492729448,
   "text": "[quoted text omitted]\n\nOk, I went ahead and pushed a commit to use nullopt instead. The commit is separate, so that it explains why the change is correct and why epoch-zero or min (or any other value) is irrelevant."
  },
  {
   "t": "2026-07-09T01:38:43Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use std::optional<NodeClock::time_point> instead of std::chrono::microseconds in net_processing\" (facb9ef75ef9c8e264ba1238f864df5627493d13)\n\nSeems like logic for most of these variables would be simplified using min/max constants instead of optional. Like m_stalling_since would be simpler using max as its unset value, m_next_inv_send_time and m_next_local_addr_send would be simpler using min"
  },
  {
   "t": "2026-07-09T01:49:33Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fae47ceffe929bc83647174f9b4cc52814c9b55d",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use std::optional<NodeClock::time_point> instead of std::chrono::microseconds for m_headers_sync_timeout\" (fae47ceffe929bc83647174f9b4cc52814c9b55d)\n\nAlso seems like logic this commit would be simpler if it used `max` instead of std::optional"
  },
  {
   "t": "2026-07-09T02:02:30Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeSeconds for m_best_block_time\" (fa3d651b11a008ad6712824776e131b9510f4666)\n\nI'm surprised this is using the default constructor instead of setting NodeClock::epoch. I do think using default constructor is better than using epoch. But using std::optional here would seem better than either. Right now ApproximateBestBlockDepth returns a huge nonsense value when this is 0, but it would be better if ApproximateBestBlockDepth returned std::optional as well."
  },
  {
   "t": "2026-07-09T02:08:19Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/addrman.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Allow NodeClock::epoch to be used in NodeSeconds context\" (fa815059fc1a4eabbd81d586b8e515315eba5ec3)\n\nI need to catch up on your latest comments so sorry if this is explained somewhere, but I still do not understand the reasons behind the choice to prefer `NodeClock::epoch` over `NodeSeconds{0s}` and definitely think it would be good to explain why you think using the `epoch` constant is better in the commit message.\n\nI think using the epoch constant is bad because:\n\n- It makes code less less transparent. Previously it was obvious times were being set to 0, now it is less clear what is actually being set.\n- It make intent less clear. Previously it was clear that a completely arbitrary 0 value was being set and the time point it corresponds to is meaningless. Now it looks like a meaningful date is being set.\n- Now there are more uses of the `epoch` constant so it harder to remove the constant. The presence of this constant is bad because it encourages broken, nonstandard code to be written, as I think we both agree.\n\nIMO the PR would be improved dropping the code changes in this commit (I do think the comment added here is helpful)."
  },
  {
   "t": "2026-07-09T02:12:30Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fa314e433020667d1b1171b10083b5a53964d0f2",
   "text": "Code review ACK fa314e433020667d1b1171b10083b5a53964d0f2. IMO, this is much improved since the last version, and the last version was already a nice cleanup, so thanks for the updates! I still need to look over the latest review comments, but I reviewed all the code and left a few suggestions. Again feel free to ignore them."
  },
  {
   "t": "2026-07-09T02:21:12Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/node/eviction.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "Note: Some commits are still changing behavior without being labeled. For example in fa4e8efbb8b2e3f6460683ee6e1b3aaa3a662d3e logic deciding which peers to evict was based on seconds, now it is based on nanoseconds or whatever precision system_clock uses, which is a minor improvement, but also a potentially observable change.\n\nIMO, it would be better if commits explicitly said \"This commit is not changing any changing behavior\" \"This commit is changing behavior slightly...\" so intent behind the changes would be clear."
  },
  {
   "t": "2026-07-09T05:52:55Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3491892049,
   "text": "Closing thread for now, looks like there is a new thread on the same commit/topic: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3548400943"
  },
  {
   "t": "2026-07-09T05:54:02Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa1a22927adac877c46645b586ecda8ae1592002",
   "in_reply_to": 3491970807,
   "text": "Closing thread for now. Continued thread for same commit/topic is https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3548378616"
  },
  {
   "t": "2026-07-09T06:01:09Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fada25cfe60059c50dfb769faf97f9f9e6c07f25",
   "in_reply_to": 3492729448,
   "text": "Resolving thread for now. A new thread seems to have been started about `m_headers_sync_timeout` in https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3548321570"
  },
  {
   "t": "2026-07-09T06:41:49Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3548378616,
   "text": "[quoted text omitted]\nWhy are you surprised that I literally addressed your review feedback? https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3491970807 says:\n\n[quoted text omitted]\n---\n\n[quoted text omitted]\nIt is not allowed to call `ApproximateBestBlockDepth` when the value is not set. Also, there is no code path where it is not set. I don't think it makes sense to bubble up errors that can never happen. In any case, I am not changing any behavior in this commit, so changing `ApproximateBestBlockDepth`  seems out of scope.\n\n---\n\nI am not sure what the best way is to catch errors that can never happen here. Historically, in C++17, one could use uninitialized memory for atomic durations. However, in C++20, this is no longer possible after https://wg21.link/p0883r2. In any case, it was never possible with time_points, because they have a user-provided ctor. See also https://godbolt.org/z/a73hvs8rr"
  },
  {
   "t": "2026-07-09T06:42:28Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/addrman.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3548400943,
   "text": "[quoted text omitted]\n\nI am confident that everyone knows that epoch == zero == default ctor. If they don't, it would be faster to look it up, than it takes me to write this comment.\n\n[quoted text omitted]\nNo, the intent is not clearer, and zero is not arbitrary here. zero is embedded into the code and into the serialization format. Idk, I'd prefer to leave this as-is, but I can introduce an `ADDRMAN_MAGIC_ZERO = NodeSeconds{0s}; // used for internal corruption checks`.\n\n[quoted text omitted]\nNo, the docstring says that a magic zero should not be used, so the presence of the constant discourages use of the magic zero. Also, it is not harder to remove the constant: It is a 3-line scripted diff before and after the changes here to remove it, if anyone wanted to do that."
  },
  {
   "t": "2026-07-09T15:12:39Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa28d748c33726057f8e49630df5ad5bf52f8053"
  },
  {
   "t": "2026-07-09T15:43:28Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/node/eviction.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3548467817,
   "text": "Sure, modified the commit messages a bit."
  },
  {
   "t": "2026-07-09T15:43:32Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fae47ceffe929bc83647174f9b4cc52814c9b55d",
   "in_reply_to": 3548321570,
   "text": "[quoted text omitted]\n\nThx, done"
  },
  {
   "t": "2026-07-09T15:43:35Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": 3548274481,
   "text": "[quoted text omitted]\n\nSure, done."
  },
  {
   "t": "2026-07-09T15:44:01Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Thx for the review, replied to all threads and force pushed."
  },
  {
   "t": "2026-07-09T16:10:11Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa714df6625410bbcb25ae2588d46aae72a74c0a"
  },
  {
   "t": "2026-07-15T19:25:20Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "Approach ACK for improved type safety in time handling, this makes the code easier to read and harder to make mistakes.\n\n I think the `epoch` approach is sensible, as it is now documented that it should not be used in new code, and I personally find that (with the docstring) it makes things more clear (as someone not super familiar with p2p).\n\nmeta-nit: I think the current commit structure is reasonable, but I would have preferred having separate commits for refactoring changes (i.e. changing the type) and for behaviour changes (i.e. changing the default, using min/max etc). Not a blocker, it's doable as-is, but it would speed the (re-)review cycle up for me.\n\nmeta-nit: faba25132928b8bdce526957d3b311fca3e75faf should not have `refactor` in its title. This new behaviour looks benign to me too, but I think we should highlight that it has behaviour change."
  },
  {
   "t": "2026-07-16T09:52:12Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa146ffed41a5355d1374b4c4f912f3701e0beec"
  },
  {
   "t": "2026-07-16T09:57:47Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Thanks for the approach review! I've:\n\n* Restored the initial version of this pull request from [three weeks ago](https://github.com/bitcoin/bitcoin/pull/35315#pullrequestreview-4566138032), which had three code-acks.\n* I removed the minor log behavior change `Use time_point::min() over epoch-zero for m_last_block_announcement`. This pull is meant to be a refactor.\n* I've pushed the min/max refactoring commits on top. This should make (re-)review easier. Though, happy to drop them, and submit them later.\n* Replaced the overly broad \"refactor:\" with \"p2p:\" in the two seconds->nanoseconds changes."
  },
  {
   "t": "2026-07-16T12:27:53Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Allow NodeClock::epoch to be used in NodeSeconds context\" (fa3ed0cf09322553bb2f94e76744d1dd8081ea70)\n\nThis seems like bad advice and the previous comment in fa815059fc1a4eabbd81d586b8e515315eba5ec3 was better. The problem is:\n\n[quoted text omitted]\nAn unset variable should **not** be represented as zero. That is a footgun and a cause of bugs. If you think there are cases where this is appropriate I'd like to know what they are, and this comment should be specific about what they are.\n\nLog messages and RPCs should not be using this constant if they want to display or return zero when literal zeroes would be clearer. Also they are probably better off writing \"unset\" or `null` than using zeroes at all. C++ code is better off using `std::optional`, or min/max times when the goal is to represent the last time something happened, or the next time it should happen.\n\nI still believe that `epoch` is a non-standard, error-prone constant should be removed entirely. Zeroes are not a good way represent unset times. In cases where they can't be avoided, literal zeros can be used and would preferable for transparency, and to make intent clear because the internal clock epoch time should not be relevant to application logic. Alternately, if there is some reason we don't want to write zeroes literally, we could introduce a constant like `NODE_UNSET_TIME` which would also express intent more clearly and also make bad code stand out.\n\nSuggestion would be to drop this commit (preferred) or to improve the comment. I think earlier suggested comment could fit well here.\n\n```c++\n/// Default value assigned to a NodeSeconds time point variable if no explicit\n/// value is set. Since C++20, this is guaranteed to be the unix epoch time,\n/// 1970-01-01T00:00:00Z.\n/// Bitcoin Core code should generally avoid referencing this constant or\n/// treating this time point as special. If a time variable is unset it is\n/// usually preferable to initialize it with time_point::max or time_point::min\n/// values for more natural comparisons, or to use std::optional.\n```"
  },
  {
   "t": "2026-07-16T15:10:57Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"p2p: Use NodeClock::time_point for m_last_block_announcement\" (fa6f2e992785b88670a9c6d78078fc73c866e02e)\n\nNot important, but this is also dropping `\\n`, and I think it's good for commit messages to note when they make unrelated changes, so it's clear they are intentional. There is also another `\\n` kept immediately below."
  },
  {
   "t": "2026-07-16T15:17:52Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "In commit \"refactor: Use NodeClock::time_point instead of std::chrono::microseconds in net_processing\" (cccc523550951e5c4b0d635a5462851ff22c6806)\n\nCould mention this field in commit message"
  },
  {
   "t": "2026-07-16T15:34:32Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fada25cfe60059c50dfb769faf97f9f9e6c07f25",
   "in_reply_to": 3492729448,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3542813639\n\n[quoted text omitted]\nYes, it wasn't obvious the value was ignored and is gated on `fSyncStarted`.\n\nIt looked like the code was trying to represent 3 states: not syncing, syncing, and done syncing, but `fSyncStarted` means the first state is never actually checked.\n\nNext question might be why there is an `fSyncStarted` variable instead of using `std::optional`, but in any case use of `min` is probably not advisable here."
  },
  {
   "t": "2026-07-16T16:10:33Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3548378616,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3549457200\n\n[quoted text omitted]\nYes I was surprised you took the suggestion here and nowhere else.\n\n[quoted text omitted]\nThanks, I didn't realize there was no code path where `m_best_block_time` could be read before it is set. When I made my comment I was assuming that is was intended for `ApproximateBestBlockDepth` to return a really large value in this case so `NETWORK_LIMITED` would not be used.\n\nSuggestion: The fact that `m_best_block_time` does always get set to a different value is not obvious, and I think there should at least be a comment here like \"// default value is never used\" to be clear that it's a bug if this value does get used.\n\nI also think it could be safer to make this field `std::optional` and assert that it is set before it's used, but absent that, just having a comment would be helpful for making this not look like it is trying to return garbage."
  },
  {
   "t": "2026-07-16T16:44:16Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/addrman.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3548400943,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3549460422\n\n[quoted text omitted]\nI'm not sure how they would know that. `epoch` is a formerly undocumented, nonstandard constant that is set in one clock type but not others. And even though people can figure out `epoch` means 0, It is much more obvious that 0 means 0.\n\n[quoted text omitted]\nAgain using 0 to represent 0 would be recommended.\n\n[quoted text omitted]\nYeah we are talking past each other here. If 0 needs to be used, a literal 0 is a good way to represent it because it looks like a magic value and is a magic value.\n\nFortunately 0 does not need to be used most places, and new code should avoid using it by using min/max/optional instead. The one exception might be serialization code, but in that case the 0 can be written in the serialization logic and does not need to affect the way times are represented in memory.\n\nAn `epoch` constant makes bad code look less bad and encourages more code based on it to be written. More uses of the constant also make the constant harder to remove, even with a scripted diff.\n\nSuggestion: Obviously my preferred alternative would be to use `0` instead of `epoch` in the code, but I at least think you should update the commit to contain some positive explanation of why you think the constant is good to use, not just say it \"should be allowed\" and \"Code may reference this constant.\" When you are allowing the constant to be used more places, and using it more places, it seems like you should be able to describe what motivated you to do these things."
  },
  {
   "t": "2026-07-16T17:14:37Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa3c6bae01bdf472404612d492c81219f6c4f796",
   "in_reply_to": null,
   "text": "in fa3c6bae01bdf472404612d492c81219f6c4f796:\n\northogonal, but while touching, would be good to use `TicksSeconds` here to guard against errors when `m_best_block_time` changes its duration type?\n\ngit diff on fa3c6bae01\n\n```diff\ndiff --git a/src/net_processing.cpp b/src/net_processing.cpp\nindex bc7f807ea5..9efe02fa69 100644\n--- a/src/net_processing.cpp\n+++ b/src/net_processing.cpp\n@@ -1352,7 +1352,7 @@ bool PeerManagerImpl::TipMayBeStale()\n\n int64_t PeerManagerImpl::ApproximateBestBlockDepth() const\n {\n-    return (Now<NodeSeconds>() - m_best_block_time.load()).count() / m_chainparams.GetConsensus().nPowTargetSpacing;\n+    return TicksSeconds(Now<NodeSeconds>() - m_best_block_time.load()) / m_chainparams.GetConsensus().nPowTargetSpacing;\n }\n\n bool PeerManagerImpl::CanDirectFetch()\n\n```"
  },
  {
   "t": "2026-07-16T17:50:46Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fa146ffed41a5355d1374b4c4f912f3701e0beec",
   "text": "Code review ACK fa146ffed41a5355d1374b4c4f912f3701e0beec. Nice improvement using the time point type to represent times, instead of duration types or integers.\n\nUnfortunately, it looks like uses of the `epoch` constant are still increasing from `10` to `26` in this pull, but this is better than the `44` uses in previous versions.\n\nI left some new suggestions, but as always feel free to ignore them. Thanks for all the updates!"
  },
  {
   "t": "2026-07-16T19:45:34Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3548378616,
   "text": "I don't disagree, but this pull request is already too large, and this commit is a pure refactor (after compilation the asm will likely be identical before and after this commit), so I don't want to jam in more unrelated fixups.\n\nAlso, peerman isn't even used before connman is started, so simply constructing peerman with the correct value avoids the use of optional (and comment), but again, this pull is already too large to add more changes to it."
  },
  {
   "t": "2026-07-16T19:45:41Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/addrman.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3548400943,
   "text": "Looks like a third thread was started to bike-shed the color of zero. Closing this one for now, and let's move discussion there: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3595379854"
  },
  {
   "t": "2026-07-16T19:46:54Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3595379854,
   "text": "I think all other reviewers prefer the constant, so I won't be dropping it. I can add the words `Old code ...` and `New code ...`, or some other wording, if I have to re-touch.\n\nAbout the RPC/logs: Yes it is possible to change\n\n```cpp\nTicksSinceEpoch<seconds>(field_with_zero_default)\n```\n\nto\n\n```cpp\nfield_with_min_default == NodeClock::min() ? 0 : TicksSinceEpoch<seconds>(field_with_min_default)\n```\n\nor to\n\n```cpp\nTicksSinceEpoch<seconds>(opt_field.value_or(NodeClock::epoch))\n```\n\n, but this would have to be separate commit and this pull is already too large to include that here.\n\nAlso, breaking behavior changes like switching `0` to `null` or `\"unset\"` in RPC are not allowed in this refactor-only PR.\n\nI am happy to review a later pull, if someone creates one."
  },
  {
   "t": "2026-07-16T19:47:03Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3596646199,
   "text": "The `\\n` is not needed by the logger. May adjust the commit message if I have to re-touch."
  },
  {
   "t": "2026-07-16T19:47:20Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3596702443,
   "text": "May adjust the commit message if I have to re-touch."
  },
  {
   "t": "2026-07-20T16:36:55Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa146ffed41a5355d1374b4c4f912f3701e0beec",
   "in_reply_to": null,
   "text": "nit: \"maintain\" would now require nanoseconds. No practical implications either way, but might be slightly less confusion to not have a different duration here?\n\n```suggestion\n// Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to nanoseconds before scaling\n// to maintain precision\nstd::chrono::nanoseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *\n```"
  },
  {
   "t": "2026-07-20T20:48:04Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": 3548274481,
   "text": "[quoted text omitted]\n\nI find relying on sentinels (`if (State(staller)->m_stalling_since == NodeClock::time_point::max())`) to be an antipattern, and would have found it more clear if this were a `std::optional`.\n\ngit diff on fa146ffed4\n\n```diff\ndiff --git a/src/net_processing.cpp b/src/net_processing.cpp\nindex 78c6002ede..b4e6909ab2 100644\n--- a/src/net_processing.cpp\n+++ b/src/net_processing.cpp\n@@ -445,8 +445,8 @@ struct CNodeState {\n     const CBlockIndex* pindexBestHeaderSent{nullptr};\n     //! Whether we've started headers synchronization with this peer.\n     bool fSyncStarted{false};\n-    /// When this peer started stalling block download progress, or max() if not stalling.\n-    NodeClock::time_point m_stalling_since{NodeClock::time_point::max()};\n+    /// When this peer started stalling block download progress.\n+    std::optional<NodeClock::time_point> m_stalling_since{};\n     std::list<QueuedBlock> vBlocksInFlight;\n     //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.\n     NodeClock::time_point m_downloading_since{NodeClock::time_point::min()};\n@@ -1239,7 +1239,7 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node\n             // Last validated block on the queue for this peer was received.\n             m_peers_downloading_from--;\n         }\n-        state.m_stalling_since = NodeClock::time_point::max();\n+        state.m_stalling_since.reset();\n\n         range.first = mapBlocksInFlight.erase(range.first);\n     }\n@@ -6205,7 +6205,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)\n\n         // Detect whether we're stalling\n         auto stalling_timeout = m_block_stalling_timeout.load();\n-        if (state.m_stalling_since < now - stalling_timeout) {\n+        if (state.m_stalling_since && *state.m_stalling_since < now - stalling_timeout) {\n             // Stalling only triggers when the block download window cannot move. During normal steady state,\n             // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection\n             // should only happen during initial block download.\n@@ -6302,7 +6302,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)\n                     pindex->nHeight, node.GetId());\n             }\n             if (state.vBlocksInFlight.empty() && staller != -1) {\n-                if (State(staller)->m_stalling_since == NodeClock::time_point::max()) {\n+                if (!State(staller)->m_stalling_since) {\n                     State(staller)->m_stalling_since = now;\n                     LogDebug(BCLog::NET, \"Stall started peer=%d\\n\", staller);\n                 }\n\n```\n\nGenerally, I think `::min()` and `::max()` are natural choices when we can use them without relying on them as sentinel values. Once that no longer holds, I think `std::optional` is more clear.\n\ngit diff on fa146ffed4\n\n```diff\ndiff --git a/src/net_processing.cpp b/src/net_processing.cpp\nindex 78c6002ede..0c5214c2b0 100644\n--- a/src/net_processing.cpp\n+++ b/src/net_processing.cpp\n@@ -309,8 +309,8 @@ struct Peer {\n          *  NODE_BLOOM. See BIP35. */\n         bool m_send_mempool GUARDED_BY(m_tx_inventory_mutex){false};\n         /** The next time after which we will send an `inv` message containing\n-         *  transaction announcements to this peer. */\n-        NodeClock::time_point m_next_inv_send_time GUARDED_BY(m_tx_inventory_mutex){NodeClock::time_point::min()};\n+         *  transaction announcements to this peer. Unset until version handshake completes. */\n+        std::optional<NodeClock::time_point> m_next_inv_send_time GUARDED_BY(m_tx_inventory_mutex){};\n         /** The mempool sequence num at which we sent the last `inv` message to this peer.\n          *  Can relay txs with lower sequence numbers than this (see CTxMempool::info_for_relay). */\n         uint64_t m_last_inv_sequence GUARDED_BY(m_tx_inventory_mutex){1};\n@@ -366,8 +366,8 @@ struct Peer {\n     mutable Mutex m_addr_send_times_mutex;\n     /** Time point to send the next ADDR message to this peer. */\n     NodeClock::time_point m_next_addr_send GUARDED_BY(m_addr_send_times_mutex){NodeClock::time_point::min()};\n-    /** Time point to possibly re-announce our local address to this peer. */\n-    NodeClock::time_point m_next_local_addr_send GUARDED_BY(m_addr_send_times_mutex){NodeClock::time_point::min()};\n+    /** Time point to possibly re-announce our local address to this peer. Unset before first announcement. */\n+    std::optional<NodeClock::time_point> m_next_local_addr_send GUARDED_BY(m_addr_send_times_mutex){};\n     /** Whether the peer has signaled support for receiving ADDRv2 (BIP155)\n      *  messages, indicating a preference to receive ADDRv2 instead of ADDR ones. */\n     std::atomic_bool m_wants_addrv2{false};\n@@ -445,11 +445,11 @@ struct CNodeState {\n     const CBlockIndex* pindexBestHeaderSent{nullptr};\n     //! Whether we've started headers synchronization with this peer.\n     bool fSyncStarted{false};\n-    /// When this peer started stalling block download progress, or max() if not stalling.\n-    NodeClock::time_point m_stalling_since{NodeClock::time_point::max()};\n+    /// When this peer started stalling block download progress.\n+    std::optional<NodeClock::time_point> m_stalling_since{};\n     std::list<QueuedBlock> vBlocksInFlight;\n     //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.\n-    NodeClock::time_point m_downloading_since{NodeClock::time_point::min()};\n+    std::optional<NodeClock::time_point> m_downloading_since{};\n     //! Whether we consider this a preferred download peer.\n     bool fPreferredDownload{false};\n     /** Whether this peer wants invs or cmpctblocks (when possible) for block announcements. */\n@@ -1231,7 +1231,7 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node\n\n         if (state.vBlocksInFlight.begin() == list_it) {\n             // First block on the queue was received, update the start download time for the next one\n-            state.m_downloading_since = std::max(state.m_downloading_since, NodeClock::now());\n+            state.m_downloading_since = std::max(Assert(state.m_downloading_since).value(), NodeClock::now());\n         }\n         state.vBlocksInFlight.erase(list_it);\n\n@@ -1239,7 +1239,7 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node\n             // Last validated block on the queue for this peer was received.\n             m_peers_downloading_from--;\n         }\n-        state.m_stalling_since = NodeClock::time_point::max();\n+        state.m_stalling_since.reset();\n\n         range.first = mapBlocksInFlight.erase(range.first);\n     }\n@@ -2286,7 +2286,7 @@ void PeerManagerImpl::InitiateTxBroadcastToAll(const Txid& txid, const Wtxid& wt\n         // otherwise at risk of leaking to a spy, if the spy is able to\n         // distinguish transactions received during the handshake from the rest\n         // in the announcement.\n-        if (tx_relay->m_next_inv_send_time == NodeClock::time_point::min()) continue;\n+        if (!tx_relay->m_next_inv_send_time) continue;\n\n         const uint256& hash{peer.m_wtxid_relay ? wtxid.ToUint256() : txid.ToUint256()};\n         if (!tx_relay->m_tx_inventory_known_filter.contains(hash)) {\n@@ -3892,7 +3892,7 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string\n             Assume(WITH_LOCK(\n                 tx_relay->m_tx_inventory_mutex,\n                 return tx_relay->m_tx_inventory_to_send.empty() &&\n-                       tx_relay->m_next_inv_send_time == NodeClock::time_point::min()));\n+                       !tx_relay->m_next_inv_send_time));\n         }\n\n         if (pfrom.IsPrivateBroadcastConn()) {\n@@ -5500,19 +5500,19 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, NodeClock::time_poi\n     LOCK(peer.m_addr_send_times_mutex);\n     // Periodically advertise our local address to the peer.\n     if (fListen && !m_chainman.IsInitialBlockDownload() &&\n-        peer.m_next_local_addr_send < current_time) {\n+        (!peer.m_next_local_addr_send || *peer.m_next_local_addr_send < current_time)) {\n         // If we've sent before, clear the bloom filter for the peer, so that our\n         // self-announcement will actually go out.\n         // This might be unnecessary if the bloom filter has already rolled\n         // over since our last self-announcement, but there is only a small\n         // bandwidth cost that we can incur by doing this (which happens\n         // once a day on average).\n-        if (peer.m_next_local_addr_send != NodeClock::time_point::min()) {\n+        if (peer.m_next_local_addr_send) {\n             peer.m_addr_known->reset();\n         }\n         if (std::optional<CService> local_service = GetLocalAddrForPeer(node)) {\n             CAddress local_addr{*local_service, peer.m_our_services, Now<NodeSeconds>()};\n-            if (peer.m_next_local_addr_send == NodeClock::time_point::min()) {\n+            if (!peer.m_next_local_addr_send) {\n                 // Send the initial self-announcement in its own message. This makes sure\n                 // rate-limiting with limited start-tokens doesn't ignore it if the first\n                 // message ends up containing multiple addresses.\n@@ -6091,7 +6091,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)\n                 LOCK(tx_relay->m_tx_inventory_mutex);\n                 // Check whether periodic sends should happen\n                 bool fSendTrickle = node.HasPermission(NetPermissionFlags::NoBan);\n-                if (tx_relay->m_next_inv_send_time < now) {\n+                if (!tx_relay->m_next_inv_send_time || *tx_relay->m_next_inv_send_time < now) {\n                     fSendTrickle = true;\n                     if (node.IsInboundConn()) {\n                         tx_relay->m_next_inv_send_time = NextInvToInbounds(now, INBOUND_INVENTORY_BROADCAST_INTERVAL, node.m_network_key);\n@@ -6205,7 +6205,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)\n\n         // Detect whether we're stalling\n         auto stalling_timeout = m_block_stalling_timeout.load();\n-        if (state.m_stalling_since < now - stalling_timeout) {\n+        if (state.m_stalling_since && *state.m_stalling_since < now - stalling_timeout) {\n             // Stalling only triggers when the block download window cannot move. During normal steady state,\n             // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection\n             // should only happen during initial block download.\n@@ -6227,7 +6227,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)\n         if (state.vBlocksInFlight.size() > 0) {\n             QueuedBlock &queuedBlock = state.vBlocksInFlight.front();\n             int nOtherPeersWithValidatedDownloads = m_peers_downloading_from - 1;\n-            if (now > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {\n+            if (now > Assert(state.m_downloading_since).value() + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {\n                 LogInfo(\"Timeout downloading block %s, %s\", queuedBlock.pindex->GetBlockHash().ToString(), node.DisconnectMsg());\n                 node.fDisconnect = true;\n                 return true;\n@@ -6302,7 +6302,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)\n                     pindex->nHeight, node.GetId());\n             }\n             if (state.vBlocksInFlight.empty() && staller != -1) {\n-                if (State(staller)->m_stalling_since == NodeClock::time_point::max()) {\n+                if (!State(staller)->m_stalling_since) {\n                     State(staller)->m_stalling_since = now;\n                     LogDebug(BCLog::NET, \"Stall started peer=%d\\n\", staller);\n                 }\n\n```\n\nNot a blocker, I don't want these style preferences to get in the way of the real improvements."
  },
  {
   "t": "2026-07-20T20:57:48Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "fa146ffed41a5355d1374b4c4f912f3701e0beec",
   "text": "Reviewed fa146ffed41a5355d1374b4c4f912f3701e0beec\n\nCode LGTM but want to give it another review round to double check potential behaviour change. Left a few comments, but no blockers."
  },
  {
   "t": "2026-07-21T01:32:22Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": 3548274481,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35315#discussion_r3617453960\n\nI haven't checked recently but last time I looked min and max were not used as sentinels, but meaningful values that make comparisons simpler and bugs harder to introduce.\n\nIf the claim is that types like `optional` or `variant` should be used whenever an individual value is checked for anywhere in the code, even when the value is meaningful, and even when using these types would complicate other comparisons, I'd question that claim. Not to say it's wrong, but just to say it should be judged on how it simplifies code or prevents bugs or has some concrete benefit in the specific situation. Not just that it's \"more clear\" or avoids an \"antipattern\".\n\nIMO needing to add `Asserts` that would otherwise be unnecessary, and write fragile comparisons like `if (!x || *x < y)` and `if (x && x < y)` are reasons not to take this approach in this situation."
  },
  {
   "t": "2026-07-21T06:22:28Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": 3548274481,
   "text": "Right, `optional` was used in an earlier commit here (looks like GitHub purged facb9ef75ef9c8e264ba1238f864df5627493d13 already). The downsides were:\n\n* optional has \"converting\" compare operators (https://en.cppreference.com/cpp/utility/optional/operator_cmp), so it would be unclear how to write code: `if (!x || *x < y)` or `if (!x || x < y)` or `if (x<y)`. I mean, handling the nullopt sentinel value specifically seems clearer, but the compiler/stdlib doesn't enforce that, so maybe just use `if (x<y)`? But then, one may as well use `::min()`/`::max()` as equally good sentinel value without the code and logic overhead from optional?\n* dereferencing optional is UB, or calling `value()` on nullopt will throw. Sure, those are indications of software bugs, but in P2P code we seem to be using `Assume` instead of `Assert`. See for example the `Assume` on `m_next_inv_send_time`. So instead of trying to avoid nullptr-crash or a throw with optional and somehow still get `Assume` semanticts (crash in Debug build, fallback in Release), one might as well just use `::min()`/`::max()` as perfectly fine sentinel values that support `Assume` without a risk of crash/throw.\n* Existing code uses `epoch` as sentinel value, similar to `::min()`. Bitcoin Core doesn't really allow times at epoch, or before, so epoch-zero and `::min()` are equivalent sentinel values.\n* For the cases where epoch-zero as sentinel is slightly confusing (e.g. timeouts), it seems a smaller diff and suitable cleanup to just switch them to `::max()`."
  },
  {
   "t": "2026-07-21T07:42:31Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fa1f6e7f2292069a3c0042f8563b4510a0a03242"
  },
  {
   "t": "2026-07-21T07:43:14Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa146ffed41a5355d1374b4c4f912f3701e0beec",
   "in_reply_to": 3615896914,
   "text": "I mean this is pretty pointless, the base timeout is 15 minutes and the per-header timeout-diff is 1ms, so even with a 1 week-old best header, we are bike-shedding about 15min1.008sec vs 15min1.007sec.\n\nMy preference would be to just remove this useless \"precision\" code, or use `SecondsDouble` for \"precision\".\n\nedit: Miners can (and do?) \"wiggle\" the time here by more than one pow-target-spacing anyway, so implying a greater precision here seems confusing?"
  },
  {
   "t": "2026-07-21T07:43:25Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa3c6bae01bdf472404612d492c81219f6c4f796",
   "in_reply_to": 3597504622,
   "text": "Thx, done, but with a different patch"
  },
  {
   "t": "2026-07-21T07:43:34Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3596702443,
   "text": "thx, changed commit msg"
  },
  {
   "t": "2026-07-21T07:43:38Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3596646199,
   "text": "thx, changed commit msg"
  },
  {
   "t": "2026-07-21T07:43:42Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/util/time.h",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": 3595379854,
   "text": "[quoted text omitted]\n\nThx, minimally improved docstring."
  },
  {
   "t": "2026-07-21T10:35:43Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fa146ffed41a5355d1374b4c4f912f3701e0beec",
   "in_reply_to": 3615896914,
   "text": "[quoted text omitted]\n\nI agree, it seems unnecessary and confusing."
  },
  {
   "t": "2026-07-21T13:39:18Z",
   "kind": "force_push",
   "who": "maflcko",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063"
  },
  {
   "t": "2026-07-21T13:47:52Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": 3548274481,
   "text": "[quoted text omitted]\n\nThat's fair. In the case of `m_next_inv_send_time`, I think using `::min()` to indicate that we should send an inv asap, or `::max()` that we shouldn't send one is natural and clear and doesn't assign any special meaning to `::min()` or `::max()`. In `InitiateTxBroadcastToAll`, however, `::min()` does now not just guarantee that we're definitely at some later point, it also adds the additional meaning that a version handshake has not yet been completed. That has absolutely nothing to do with time. The reader now has to be aware of all the usage of the variable, and/or it has to be completely documented.\n\nWhen `m_next_inv_send_time` is `std::optional`, the type makes it perfectly clear that there is a discontinuity to be aware of. That's why I think it's more clear.\n\nNow, probably the most proper solution would be to just stop using `m_next_inv_send_time` as a proxy for a completed version handshake, but I haven't looked into how feasible that is here.\n\nFor other variables, like `m_stalling_since`, I think it is better to have a sensible (`std::nullopt`) vs a nonsensical (`::max()`: a \"since\" in the future can never make sense) value.\n\n[quoted text omitted]\nMy diff added 2 `Assert`s that could equally well be omitted, but they're just making the invariant explicit whereas the current code implicitly assumes them. I think explicit is better.\n\n[quoted text omitted]\nIt's more verbose (which can be improved with `value_or()`, but I'm not sure fragile is the appropriate term for making the discontinuity/semantic overload more explicit?\n\n---\n\n[quoted text omitted]\nPerhaps it's not suitable for the p2p code, but we could still adopt patterns like `Assume(some_var).value_or(::min())`? It's more verbose, but I think it is helpful to be more explicit?\n\n[quoted text omitted]\nI agree we can't enforce clean code here, but with `std::optional` we can at least allow it.\n\n[quoted text omitted]\nI agree it's not worse than `epoch`.\n\n[quoted text omitted]\nI agree, I think `::max()` for a timeout is very natural, even when it has not explicitly been set."
  },
  {
   "t": "2026-07-21T14:36:42Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": 3548274481,
   "text": "Not sure what to do here. This pull is already 10 commits, and switching to optional with `Assert` or  `Assume(some_var).value_or(::min()/max())` feels like it is going to explode the review scope here.\n\nI'd say the review scope here should restrict itself to compile-time type-changes only (modulo precision changes).\n\nchanging a few sentinel values from epoch-zero to min/max seems fine, but if reviewers can't agree on them for now, it seems best to postpone the two commits (fa8a149c2923d8075fc127dae39653142f6211fa & fa58d8f4fb36e9028503f6bb9415b5c7c1c4dfa1) and just fully restore the initial (and reviewed) version of this pull request  (https://github.com/bitcoin/bitcoin/pull/35315#issuecomment-4990553024)"
  },
  {
   "t": "2026-07-22T10:47:26Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "facb9ef75ef9c8e264ba1238f864df5627493d13",
   "in_reply_to": 3548274481,
   "text": "I'm happy to keep it as-is, it's not a regression vs master and the PR has too many other benefits to be spending too much time/energy on this rather small issue. I just wanted to share my view that I think we should ideally have less `::min()` `::max()` sentinels, not more."
  },
  {
   "t": "2026-07-23T13:52:30Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "Would be useful to document all sentinels:\n\ngit diff on fab4bd7f2b\n\n```diff\ndiff --git a/src/net_processing.cpp b/src/net_processing.cpp\nindex d1564766fb..903f6dadda 100644\n--- a/src/net_processing.cpp\n+++ b/src/net_processing.cpp\n@@ -309,7 +309,8 @@ struct Peer {\n          *  NODE_BLOOM. See BIP35. */\n         bool m_send_mempool GUARDED_BY(m_tx_inventory_mutex){false};\n         /** The next time after which we will send an `inv` message containing\n-         *  transaction announcements to this peer. */\n+         *  transaction announcements to this peer, or time_point::min() when\n+         *  the version handshake is not yet completed. */\n         NodeClock::time_point m_next_inv_send_time GUARDED_BY(m_tx_inventory_mutex){NodeClock::time_point::min()};\n         /** The mempool sequence num at which we sent the last `inv` message to this peer.\n          *  Can relay txs with lower sequence numbers than this (see CTxMempool::info_for_relay). */\n@@ -366,7 +367,8 @@ struct Peer {\n     mutable Mutex m_addr_send_times_mutex;\n     /** Time point to send the next ADDR message to this peer. */\n     NodeClock::time_point m_next_addr_send GUARDED_BY(m_addr_send_times_mutex){NodeClock::time_point::min()};\n-    /** Time point to possibly re-announce our local address to this peer. */\n+    /** Time point to possibly re-announce our local address to this peer, or\n+     *  time_point::min() if no self-announcement was sent to this peer yet. */\n     NodeClock::time_point m_next_local_addr_send GUARDED_BY(m_addr_send_times_mutex){NodeClock::time_point::min()};\n     /** Whether the peer has signaled support for receiving ADDRv2 (BIP155)\n      *  messages, indicating a preference to receive ADDRv2 instead of ADDR ones. */\n@@ -403,7 +405,8 @@ struct Peer {\n     /** Whether we've sent our peer a sendheaders message. **/\n     std::atomic<bool> m_sent_sendheaders{false};\n\n-    /** When to potentially disconnect peer for stalling headers download */\n+    /** When to potentially disconnect peer for stalling headers download, or\n+     *  time_point::max() if this peer is exempt from the timeout. */\n     NodeClock::time_point m_headers_sync_timeout GUARDED_BY(NetEventsInterface::g_msgproc_mutex){NodeClock::time_point::max()};\n\n     /** Whether this peer wants invs or headers (when possible) for block announcements */\n@@ -482,7 +485,8 @@ struct CNodeState {\n       * drop the outbound one that least recently announced us a new block.\n       */\n     struct ChainSyncTimeoutState {\n-        //! A timeout used for checking whether our peer has sufficiently synced\n+        //! A timeout used for checking whether our peer has sufficiently synced,\n+        //! or NodeClock::epoch when unset\n         NodeClock::time_point m_timeout{NodeClock::epoch};\n         //! A header with the work we require on our peer's chain\n         const CBlockIndex* m_work_header{nullptr};\n@@ -959,7 +963,7 @@ private:\n     typedef std::multimap<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator>> BlockDownloadMap;\n     BlockDownloadMap mapBlocksInFlight GUARDED_BY(cs_main);\n\n-    /** When our tip was last updated. */\n+    /** When our tip was last updated, or NodeClock::epoch for no update. */\n     std::atomic<NodeClock::time_point> m_last_tip_update{NodeClock::epoch};\n\n     /** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */\n\n```"
  },
  {
   "t": "2026-07-23T16:55:19Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "nit: any reason we use `Node<NodeSeconds>()` here instead of `NodeClock::now()`?"
  },
  {
   "t": "2026-07-23T18:09:58Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "text": "ACK fab4bd7f2bf8eb78efff00a6d1abcb28187e0063"
  },
  {
   "t": "2026-07-28T18:53:41Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "text": "Code review ACK fab4bd7f2bf8eb78efff00a6d1abcb28187e0063. Changes since last review: tweaking `epoch` comment and commit message as suggested, replacing nPowTargetSpacing with PowTargetSpacing a few places, simplifying HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER calculation making it slightly less precise but off by 1ms at most"
  },
  {
   "t": "2026-09-08T20:52:17Z",
   "kind": "review",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "text": "tACK fab4bd7f2bf8eb78efff00a6d1abcb28187e0063\n\nBuilt and ran the unit suites and the p2p functional tests.\n\nLeft 3 nits (feel free to ignore)"
  },
  {
   "t": "2026-09-08T20:52:17Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "nit: `MaybeSendAddr`, `GetRequestsToSend` and `MaybeSendFeefilter` all end up taking the `now` from the top of `SendMessages`, and `ConsiderEviction` is the one that still takes its own read. Worth passing `now` here too?\n\n```suggestion\n        ConsiderEviction(node, peer, now);\n```"
  },
  {
   "t": "2026-09-08T20:52:17Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/net_processing.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "nit: the rest of the file moved to `consensusParams.PowTargetSpacing()`, and this is the last duration still built by hand from `nPowTargetSpacing`.\n\n```suggestion\n            if (now > state.m_downloading_since + consensusParams.PowTargetSpacing() * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {\n```"
  },
  {
   "t": "2026-09-08T20:52:17Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/net_peer_eviction_tests.cpp",
   "commit": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
   "in_reply_to": null,
   "text": "nit: the eviction commit says the precision makes `SelectNodeToEvict` more precise through `CompareNodeBlockTime` and `CompareNodeTXTime`, and this fixture still hands it whole seconds. Casting both times back to seconds inside `SelectNodeToEvict` leaves `peer_eviction_test` green, and with these initialisations in milliseconds it fails. The other five in this test take the same change.\n\nThe announcement comparison in `EvictExtraOutboundPeers` has the same gap, worth one there too?\n\n```suggestion\n                            candidate.m_last_tx_time = NodeClock::time_point{std::chrono::milliseconds{number_of_nodes - candidate.id}};\n```"
  }
 ],
 "labels_log": [
  {
   "t": "2026-05-18T15:21:08Z",
   "action": "labeled",
   "label": "Refactoring",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-18T15:50:34Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-18T16:54:58Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-07T14:52:00Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-08T11:06:30Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-09T16:10:50Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-09T18:01:03Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-21T08:51:18Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-21T09:45:01Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-25T12:14:49Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 66749,
 "text_tokens_estimate": 16687,
 "changed_paths": [
  "src/addrman.cpp",
  "src/init.cpp",
  "src/net.h",
  "src/net_processing.cpp",
  "src/net_processing.h",
  "src/node/eviction.h",
  "src/node/txdownloadman.h",
  "src/node/txdownloadman_impl.cpp",
  "src/node/txdownloadman_impl.h",
  "src/qt/rpcconsole.cpp",
  "src/qt/rpcconsole.h",
  "src/rpc/net.cpp",
  "src/test/denialofservice_tests.cpp",
  "src/test/fuzz/node_eviction.cpp",
  "src/test/fuzz/txdownloadman.cpp",
  "src/test/fuzz/txrequest.cpp",
  "src/test/net_peer_eviction_tests.cpp",
  "src/test/peerman_tests.cpp",
  "src/test/txdownload_tests.cpp",
  "src/test/txrequest_tests.cpp",
  "src/test/util/net.cpp",
  "src/txrequest.cpp",
  "src/txrequest.h",
  "src/util/time.h"
 ],
 "files": [
  {
   "path": "src/addrman.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/init.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/net.h",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/net_processing.cpp",
   "add": 86,
   "del": 91
  },
  {
   "path": "src/net_processing.h",
   "add": 4,
   "del": 3
  },
  {
   "path": "src/node/eviction.h",
   "add": 3,
   "del": 2
  },
  {
   "path": "src/node/txdownloadman.h",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/node/txdownloadman_impl.cpp",
   "add": 6,
   "del": 6
  },
  {
   "path": "src/node/txdownloadman_impl.h",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/qt/rpcconsole.cpp",
   "add": 2,
   "del": 3
  },
  {
   "path": "src/qt/rpcconsole.h",
   "add": 0,
   "del": 5
  },
  {
   "path": "src/rpc/net.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/denialofservice_tests.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/fuzz/node_eviction.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/fuzz/txdownloadman.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/fuzz/txrequest.cpp",
   "add": 8,
   "del": 8
  },
  {
   "path": "src/test/net_peer_eviction_tests.cpp",
   "add": 6,
   "del": 6
  },
  {
   "path": "src/test/peerman_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/txdownload_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/txrequest_tests.cpp",
   "add": 11,
   "del": 11
  },
  {
   "path": "src/test/util/net.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/txrequest.cpp",
   "add": 12,
   "del": 12
  },
  {
   "path": "src/txrequest.h",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/util/time.h",
   "add": 12,
   "del": 1
  }
 ],
 "test_lines": 70,
 "git": {
  "head": "fab4bd7f2bf8eb78efff00a6d1abcb28187e0063",
  "head_matches_backup": true,
  "base": "a64df338e69b1736496e149c604e7ca928039b06",
  "commits": [
   {
    "sha": "fa562e4c3d",
    "subject": "refactor: Allow NodeClock::epoch to be used in NodeSeconds context",
    "files": 2,
    "add": 16,
    "del": 5
   },
   {
    "sha": "fa208c5b24",
    "subject": "refactor: Use NodeSeconds for m_best_block_time",
    "files": 4,
    "add": 12,
    "del": 11
   },
   {
    "sha": "fafd1e3312",
    "subject": "p2p: Use NodeClock::time_point for m_last_block_announcement",
    "files": 3,
    "add": 11,
    "del": 10
   },
   {
    "sha": "fa593c2e6e",
    "subject": "refactor: Use NodeClock::time_point in txdownloadman/txrequest",
    "files": 10,
    "add": 51,
    "del": 51
   },
   {
    "sha": "fab33fdd92",
    "subject": "p2p: Use NodeClock::time_point instead of std::chrono::seconds in node stats and eviction",
    "files": 10,
    "add": 47,
    "del": 53
   },
   {
    "sha": "fa4d22d06c",
    "subject": "refactor: Drop HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER \"precision\"",
    "files": 1,
    "add": 2,
    "del": 6
   },
   {
    "sha": "fa42ebd0d1",
    "subject": "refactor: Use NodeClock::time_point instead of std::chrono::microseconds in net_processing",
    "files": 1,
    "add": 41,
    "del": 42
   },
   {
    "sha": "fa8a149c29",
    "subject": "refactor: Use time_point::max() for m_headers_sync_timeout",
    "files": 1,
    "add": 3,
    "del": 3
   },
   {
    "sha": "fa58d8f4fb",
    "subject": "refactor: Use time_point::min()/max() in net_processing",
    "files": 1,
    "add": 16,
    "del": 16
   },
   {
    "sha": "fab4bd7f2b",
    "subject": "refactor: Use NodeClock for last GetTime call in net_processing.cpp",
    "files": 1,
    "add": 2,
    "del": 2
   }
  ],
  "patch_truncated": true
 },
 "input_hash": "2c5be12d14bfe6ae",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}