{
 "number": 27052,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/27052",
 "title": "test: rpc: add last block announcement time to getpeerinfo result",
 "author": "LarryRuane",
 "author_association": "CONTRIBUTOR",
 "created_at": "2023-02-06T18:32:56Z",
 "updated_at": "2026-09-16T00:57:16Z",
 "age_days": 1318,
 "draft": false,
 "labels": [
  "Tests"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "13ce46ba1a79c99882cc1ac768db1df1f25de098",
 "head_ref": "2023-02-getpeerinfo",
 "head_repo": "LarryRuane/bitcoin",
 "head_history": [
  {
   "t": "2023-02-06T21:51:54Z",
   "sha": "becf38061b2625bcc629293fec0dce7c27292e14"
  },
  {
   "t": "2023-08-14T16:55:30Z",
   "sha": "efc2e70f1763c6bf3f6b246202adb9d5f1f798ee"
  },
  {
   "t": "2023-08-15T06:01:42Z",
   "sha": "16d2903b8a9e6e980e24e04ab2d86c7e778f5764"
  },
  {
   "t": "2023-08-15T06:17:37Z",
   "sha": "2e80e3bdcd9cac7878c0d14956ba9c57626da855"
  },
  {
   "t": "2023-08-15T06:34:30Z",
   "sha": "6c564aafb3d5f61fffa0d6311ad688c47b14014a"
  },
  {
   "t": "2023-08-16T19:25:15Z",
   "sha": "37e19bca9060fb7c8b89ad6411b1abe364efb152"
  },
  {
   "t": "2023-10-09T16:25:34Z",
   "sha": "790b6956a864e83c58a04217c36f85c0b78c3b19"
  },
  {
   "t": "2023-10-09T17:53:03Z",
   "sha": "1e11164d48de2e9e0ff844d4d92c2c6c9c28a05c"
  },
  {
   "t": "2023-10-10T18:11:18Z",
   "sha": "cbe4f67e3292bf45ea01aa94ecf798f12b6e16bc"
  },
  {
   "t": "2024-02-13T15:54:03Z",
   "sha": "b1c3826ec093386c2ddec1d30ca3e777f8860892"
  },
  {
   "t": "2024-03-21T17:47:44Z",
   "sha": "e47a5290108b0d427b5ff2c520fbd12654545a6c"
  },
  {
   "t": "2024-05-01T20:41:46Z",
   "sha": "1322a3a17189fd79a119cf368770e70ed6dda1ff"
  },
  {
   "t": "2024-07-26T18:28:13Z",
   "sha": "c49b1097b3fbd61b096b6878ac3617fb9a310f69"
  },
  {
   "t": "2024-09-09T18:46:15Z",
   "sha": "9f2879a7ec14cb38028bb9c0e979e815ce963957"
  },
  {
   "t": "2024-09-17T17:04:44Z",
   "sha": "7ce23d7e75398b65172613cf921f474d9f20d663"
  },
  {
   "t": "2024-11-02T21:22:11Z",
   "sha": "21ad9f83750350589898332458485a7c95bb875e"
  },
  {
   "t": "2025-03-31T22:45:41Z",
   "sha": "559b0913bfa74bce468ced66b8c0e96169d842f3"
  },
  {
   "t": "2025-04-29T04:58:15Z",
   "sha": "5bcf8491d15ac148507a4d172469e1fd9d12078e"
  },
  {
   "t": "2025-04-30T20:34:13Z",
   "sha": "f7788f66b4126be29f3599c01ca97a6c7f989f1c"
  },
  {
   "t": "2025-07-01T14:52:20Z",
   "sha": "cbe4603a90220f0ef0b21c4da68ee16791ad9034"
  },
  {
   "t": "2026-02-10T19:37:55Z",
   "sha": "95b673929b37d080017dc9f8f2d8df55f553f36d"
  },
  {
   "t": "2026-03-13T19:04:41Z",
   "sha": "d46a3a5cce4122c09a5b302dfec997e68ee9698d"
  },
  {
   "t": "2026-03-30T23:10:07Z",
   "sha": "e37ecc184ccb70c048e6102b422c786eb78d9390"
  },
  {
   "t": "2026-03-31T15:40:12Z",
   "sha": "2136973f373c7a4643d126e7bb73fa1fd232f3c4"
  },
  {
   "t": "2026-04-01T21:03:58Z",
   "sha": "036a87b8a9968c33960ede0c51d39722756ea979"
  },
  {
   "t": "2026-04-20T19:36:07Z",
   "sha": "136eed4a13c5c07c43675e27ddda14cb1a486661"
  },
  {
   "t": "2026-05-23T22:17:13Z",
   "sha": "4895b8f058b80749dc7ca62ef31787bbffb71b47"
  },
  {
   "t": "2026-05-24T02:41:29Z",
   "sha": "e422fc50f39b5ddd0333b232a7437f3f3dadb73c"
  },
  {
   "t": "2026-06-07T15:52:56Z",
   "sha": "252a6e72754147ad25f1bc1000cf958e3b350225"
  },
  {
   "t": "2026-07-24T15:50:57Z",
   "sha": "87bc3333063fe21ec3e16accb6bdc1035a5da865"
  },
  {
   "t": "2026-07-25T05:15:12Z",
   "sha": "13ce46ba1a79c99882cc1ac768db1df1f25de098"
  }
 ],
 "additions": 146,
 "deletions": 19,
 "changed_files": 8,
 "commit_count": 5,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "rkrux",
      "url": "https://github.com/bitcoin/bitcoin/pull/27052#pullrequestreview-5142316557"
     },
     {
      "login": "naiyoma",
      "url": "https://github.com/bitcoin/bitcoin/pull/27052#pullrequestreview-5166350604"
     }
    ],
    "concept_ack": [
     {
      "login": "satsie",
      "url": "https://github.com/bitcoin/bitcoin/pull/27052#issuecomment-1420228791"
     },
     {
      "login": "kristapsk",
      "url": "https://github.com/bitcoin/bitcoin/pull/27052#issuecomment-1680531024"
     }
    ]
   },
   "conflicts": [
    {
     "number": 19461,
     "title": "multiprocess: Add bitcoin-gui -ipcconnect option",
     "author": "ryanofsky"
    },
    {
     "number": 19460,
     "title": "multiprocess: Add bitcoin-wallet -ipcconnect option",
     "author": "ryanofsky"
    },
    {
     "number": 10102,
     "title": "Multiprocess bitcoin",
     "author": "ryanofsky"
    }
   ]
  }
 },
 "acks_parsed": {
  "satsie": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2023-02-07T05:39:52Z",
   "stale": false
  },
  "kristapsk": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2023-08-16T12:40:26Z",
   "stale": false
  },
  "rkrux": {
   "kind": "ack",
   "hash": null,
   "t": "2024-03-20T08:24:25Z",
   "stale": true
  },
  "naiyoma": {
   "kind": "ack",
   "hash": "13ce46ba1a79c99882cc1ac768db1df1f25de098",
   "t": "2026-09-10T11:12:08Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 1,
  "stale_ack": 1,
  "concept_ack": 2,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 1,
  "changes_requested": 0,
  "distinct_reviewers": [
   "danielabrozzoni",
   "dergoegge",
   "kristapsk",
   "luke-jr",
   "maflcko",
   "naiyoma",
   "rkrux",
   "satsie",
   "sedited",
   "theuni"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-07-25T05:17:16Z",
  "last_reviewer_activity": "2026-09-10T11:12:08Z",
  "last_reviewer": "naiyoma",
  "author_silent_days": 54,
  "waiting_on_author_days": 7,
  "days_since_update": 1
 },
 "refs": {
  "mentioned": [
   23352,
   25268,
   26172
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 26172,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2022-09-27",
    "title": "p2p: ProcessHeadersMessage(): fix received_new_header"
   },
   {
    "number": 23352,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "test: Extend stale_tip_peer_management test"
   },
   {
    "number": 25268,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "refactor: Introduce EvictionManager"
   }
  ],
  "conflicts": [
   19461,
   19460,
   10102
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "doc/release-notes-27052.md",
  "src/net.h",
  "src/net_processing.cpp",
  "src/test/denialofservice_tests.cpp",
  "test/functional/p2p_block_times.py"
 ],
 "body": "This PR adds `last_block_announcement` to the per-peer `getpeerinfo` RPC result. This is the most recent time that this peer was the first to notify our node of a new block (one that we didn't already know about), or zero if this peer has never been the first to notify us of a new block. This timestamp already exists internally and is used for stale-tip eviction logic; this PR exposes it at the RPC layer.\n\nThis PR started out as a suggestion for additional test coverage, see https://github.com/bitcoin/bitcoin/pull/26172#issuecomment-1259678260. It turned out that the easiest way to test (already-merged) #26172 is to add this field to `getpeerinfo` and have a functional test verify its value. But it may also be useful to have this result in its own right, similar to that RPC's existing `last_block` field -- it indicates something about the quality of our peers. It allows one to predict which peer will be evicted when the stale tip logic activates. (I'm not sure if that would be useful, but it may be.)\n\nThe functional test added here fails without #26172, which is the main goal.\n\nThis PR does not test the actual stale-tip eviction logic; that's difficult to do with a functional test. But it does test the correctness of the timestamp that the eviction logic depends on. #23352 is an attempt to test the eviction logic using a unit test, it's not ready to be merged yet. I think having both kinds of tests would be beneficial.",
 "commits": [
  {
   "sha": "bb8bca630a8ad772a15433d7a57cee4868d7d7cb",
   "date": "2026-07-25T05:03:08Z",
   "message": "change m_last_block_announcement type from int64_t to NodeClock::time_point\n\nThis non-functional change makes it clear that this variable is a\ntime_point (point-in-time) rather than a duration. (An int64_t can be\neither). This variable's use is modified in the following commits, so\nwe may as well modernize its type. This type is also mockable, which a\nlater test commit takes advantage of."
  },
  {
   "sha": "26a25162a48af567d819a3c59478ae924e4d21ce",
   "date": "2026-07-25T05:03:08Z",
   "message": "net: add m_last_block_announcement to CNodeStateStats\n\nCopy its value from CNodeState. Unused until the next commit."
  },
  {
   "sha": "7d3d04e925c70c0bb1df25a01feace9a2e3f009f",
   "date": "2026-07-25T05:03:08Z",
   "message": "rpc: add last_block_announcement to the getpeerinfo output\n\nThis is the most recent time that this peer was the first to notify our\nnode of a new block (one that we didn't already know about), or zero if\nthis peer has never been the first to notify us of a new block. This can\nbe used to evaluate the quality (performance) of this peer, similar to\nthe existing `last_block` field.\n\nThis timestamp already exists internally and is used for stale-tip\neviction logic; this PR exposes it at the RPC layer. This will also be\nused (in a later commit) to increase stale-tip test coverage."
  },
  {
   "sha": "5d5397d841085d024643927d952aa19bf36a8236",
   "date": "2026-07-25T05:14:29Z",
   "message": "test: add functional test for block announcement time tracking\n\nCo-authored-by: naiyoma <lankas.aurelia@gmail.com>"
  },
  {
   "sha": "13ce46ba1a79c99882cc1ac768db1df1f25de098",
   "date": "2026-07-25T05:14:29Z",
   "message": "doc: add release note for 27052\n\nThe `getpeerinfo` RPC now returns `last_block_announcement`."
  }
 ],
 "timeline": [
  {
   "t": "2023-02-06T21:51:54Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "becf38061b2625bcc629293fec0dce7c27292e14"
  },
  {
   "t": "2023-02-06T21:52:24Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "Force pushed to fix CI failure"
  },
  {
   "t": "2023-02-07T05:39:52Z",
   "kind": "comment",
   "who": "satsie",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK. I spent quite a bit of time trying to test #26172 (my findings are documented [here](https://github.com/satsie/bitcoin-notez/blob/master/bitcoin-core-development/testing-pr-26172.md)) and can confirm the difficulty of testing it with the current state of the test framework.\n\nI also support the suggestion to add `last_block_announcement` to `getpeerinfo` not only because it seems to be the easiest way to test 26172, but I agree it can be useful information to have when trying to learn more about one's peers, especially when this timestamp is already available internally."
  },
  {
   "t": "2023-02-07T09:26:04Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net.h",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": null,
   "text": "If you change the type, why not use NodeSeconds (a time point)? While `std::chrono::seconds` (a duration) is used widely in the codebase, because all durations that denote a time point are since epoch, using a dedicated type such as `NodeSeconds` might be clearer."
  },
  {
   "t": "2023-02-07T11:00:03Z",
   "kind": "review_comment",
   "who": "dergoegge",
   "assoc": "MEMBER",
   "path": "src/net.h",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": null,
   "text": "I don't think this belongs in `CNode`. It is used in our outbound eviction logic which is part of net processing and it isn't used in net at all (imo, the eviction logic should be encapsulated in its own module entirely #25268 \ud83d\ude09).\n\nAlso afaict, there is no need to move `m_last_block_announcement`, because you can just return it in `PeerManager::GetNodeStateStats`."
  },
  {
   "t": "2023-08-14T16:55:30Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "efc2e70f1763c6bf3f6b246202adb9d5f1f798ee"
  },
  {
   "t": "2023-08-15T06:01:42Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "16d2903b8a9e6e980e24e04ab2d86c7e778f5764"
  },
  {
   "t": "2023-08-15T06:17:37Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "2e80e3bdcd9cac7878c0d14956ba9c57626da855"
  },
  {
   "t": "2023-08-15T06:34:30Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "6c564aafb3d5f61fffa0d6311ad688c47b14014a"
  },
  {
   "t": "2023-08-15T06:55:13Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "Force pushed from becf38061b2625bcc629293fec0dce7c27292e14 to\n- efc2e70f1763c6bf3f6b246202adb9d5f1f798ee [diff](https://github.com/bitcoin/bitcoin/compare/becf38061b2625bcc629293fec0dce7c27292e14..efc2e70f1763c6bf3f6b246202adb9d5f1f798ee) -- needed rebase\n- 16d2903b8a9e6e980e24e04ab2d86c7e778f5764 [diff](https://github.com/bitcoin/bitcoin/compare/efc2e70f1763c6bf3f6b246202adb9d5f1f798ee..16d2903b8a9e6e980e24e04ab2d86c7e778f5764) -- make the suggested review changes\n- 2e80e3bdcd9cac7878c0d14956ba9c57626da855 [diff](https://github.com/bitcoin/bitcoin/compare/16d2903b8a9e6e980e24e04ab2d86c7e778f5764..2e80e3bdcd9cac7878c0d14956ba9c57626da855) -- fix CI failure\n- 6c564aafb3d5f61fffa0d6311ad688c47b14014a [diff](https://github.com/bitcoin/bitcoin/compare/2e80e3bdcd9cac7878c0d14956ba9c57626da855..6c564aafb3d5f61fffa0d6311ad688c47b14014a) -- add release notes, and small improvement to functional test\n\nI'll take this out of draft now since I think it's ready for review."
  },
  {
   "t": "2023-08-16T12:29:05Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": null,
   "text": "This is fragile, because when the type of `oldest_block_announcement` changes, the log output will change, too, without a compile error. It would be better to use `Ticks` or `TicksSinceEpoch`."
  },
  {
   "t": "2023-08-16T12:29:50Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": 1295821775,
   "text": "It is possible to write a clang-tidy check for this, but I am not sure if people want this. cc @theuni\n\n```diff\ndiff --git a/contrib/devtools/bitcoin-tidy/CMakeLists.txt b/contrib/devtools/bitcoin-tidy/CMakeLists.txt\nindex 35e60d1d87..b523692b12 100644\n--- a/contrib/devtools/bitcoin-tidy/CMakeLists.txt\n+++ b/contrib/devtools/bitcoin-tidy/CMakeLists.txt\n@@ -14,7 +14,10 @@ find_program(CLANG_TIDY_EXE NAMES \"clang-tidy-${LLVM_VERSION_MAJOR}\" \"clang-tidy\n message(STATUS \"Found LLVM ${LLVM_PACKAGE_VERSION}\")\n message(STATUS \"Found clang-tidy: ${CLANG_TIDY_EXE}\")\n\n-add_library(bitcoin-tidy MODULE bitcoin-tidy.cpp logprintf.cpp)\n+add_library(bitcoin-tidy MODULE bitcoin-tidy.cpp\n+  chrono.cpp\n+  logprintf.cpp\n+)\n target_include_directories(bitcoin-tidy SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS})\n\n # Disable RTTI and exceptions as necessary\n@@ -47,7 +50,10 @@ else()\n endif()\n\n # Create a dummy library that runs clang-tidy tests as a side-effect of building\n-add_library(bitcoin-tidy-tests OBJECT EXCLUDE_FROM_ALL example_logprintf.cpp)\n+add_library(bitcoin-tidy-tests OBJECT EXCLUDE_FROM_ALL\n+  example_chrono.cpp\n+  example_logprintf.cpp\n+)\n add_dependencies(bitcoin-tidy-tests bitcoin-tidy)\n\n set_target_properties(bitcoin-tidy-tests PROPERTIES CXX_CLANG_TIDY \"${CLANG_TIDY_COMMAND}\")\ndiff --git a/contrib/devtools/bitcoin-tidy/bitcoin-tidy.cpp b/contrib/devtools/bitcoin-tidy/bitcoin-tidy.cpp\nindex 0f34d37793..bc7c05ca45 100644\n--- a/contrib/devtools/bitcoin-tidy/bitcoin-tidy.cpp\n+++ b/contrib/devtools/bitcoin-tidy/bitcoin-tidy.cpp\n@@ -2,6 +2,7 @@\n // Distributed under the MIT software license, see the accompanying\n // file COPYING or http://www.opensource.org/licenses/mit-license.php.\n\n+#include \"chrono.h\"\n #include \"logprintf.h\"\n\n #include <clang-tidy/ClangTidyModule.h>\n@@ -12,6 +13,7 @@ class BitcoinModule final : public clang::tidy::ClangTidyModule\n public:\n     void addCheckFactories(clang::tidy::ClangTidyCheckFactories& CheckFactories) override\n     {\n+        CheckFactories.registerCheck<bitcoin::ChronoCheck>(\"bitcoin-chrono-count\");\n         CheckFactories.registerCheck<bitcoin::LogPrintfCheck>(\"bitcoin-unterminated-logprintf\");\n     }\n };\ndiff --git a/contrib/devtools/bitcoin-tidy/chrono.cpp b/contrib/devtools/bitcoin-tidy/chrono.cpp\nnew file mode 100644\nindex 0000000000..163cf0b9e8\n--- /dev/null\n+++ b/contrib/devtools/bitcoin-tidy/chrono.cpp\n@@ -0,0 +1,37 @@\n+// Copyright (c) The Bitcoin Core developers\n+// Distributed under the MIT software license, see the accompanying\n+// file COPYING or https://opensource.org/license/mit/.\n+\n+#include \"chrono.h\"\n+\n+#include <clang/AST/ASTContext.h>\n+#include <clang/ASTMatchers/ASTMatchFinder.h>\n+\n+namespace bitcoin {\n+\n+void ChronoCheck::registerMatchers(clang::ast_matchers::MatchFinder* finder)\n+{\n+    using namespace clang::ast_matchers;\n+\n+    finder->addMatcher(\n+        cxxMemberCallExpr(\n+            callee(cxxMethodDecl(hasName(\"count\"))),\n+            thisPointerType(qualType(hasDeclaration(cxxRecordDecl(\n+      hasName(\"std::chrono::duration\")\n+            )))),\n+\n+            expr().bind(\"count_call\")),\n+        this);\n+}\n+\n+void ChronoCheck::check(const clang::ast_matchers::MatchFinder::MatchResult& Result)\n+{\n+    if (const auto* call = Result.Nodes.getNodeAs<clang::Expr>(\"count_call\")) {\n+        const clang::ASTContext& ctx = *Result.Context;\n+        const auto user_diag = diag(call->getEndLoc(), \"Fragile call to std::chrono::duration::count(), Use Ticks or TicksSinceEpoch\");\n+        //const auto& loc = call->getLocationOfByte(call->getByteLength(), *Result.SourceManager, ctx.getLangOpts(), ctx.getTargetInfo());\n+        user_diag ;//<< clang::FixItHint::CreateInsertion(loc, \"\\\\n\");\n+    }\n+}\n+\n+} // namespace bitcoin\ndiff --git a/contrib/devtools/bitcoin-tidy/chrono.h b/contrib/devtools/bitcoin-tidy/chrono.h\nnew file mode 100644\nindex 0000000000..c455d786ed\n--- /dev/null\n+++ b/contrib/devtools/bitcoin-tidy/chrono.h\n@@ -0,0 +1,28 @@\n+// Copyright (c) The Bitcoin Core developers\n+// Distributed under the MIT software license, see the accompanying\n+// file COPYING or https://opensource.org/license/mit/.\n+\n+#ifndef CHRONO_CHECK_H\n+#define CHRONO_CHECK_H\n+\n+#include <clang-tidy/ClangTidyCheck.h>\n+\n+namespace bitcoin {\n+\n+class ChronoCheck final : public clang::tidy::ClangTidyCheck\n+{\n+public:\n+    ChronoCheck(clang::StringRef Name, clang::tidy::ClangTidyContext* Context)\n+        : clang::tidy::ClangTidyCheck(Name, Context) {}\n+\n+    bool isLanguageVersionSupported(const clang::LangOptions& LangOpts) const override\n+    {\n+        return LangOpts.CPlusPlus;\n+    }\n+    void registerMatchers(clang::ast_matchers::MatchFinder* Finder) override;\n+    void check(const clang::ast_matchers::MatchFinder::MatchResult& Result) override;\n+};\n+\n+} // namespace bitcoin\n+\n+#endif // CHRONO_CHECK_H\ndiff --git a/contrib/devtools/bitcoin-tidy/example_chrono.cpp b/contrib/devtools/bitcoin-tidy/example_chrono.cpp\nnew file mode 100644\nindex 0000000000..1bf2f0a353\n--- /dev/null\n+++ b/contrib/devtools/bitcoin-tidy/example_chrono.cpp\n@@ -0,0 +1,18 @@\n+// Copyright (c) The Bitcoin Core developers\n+// Distributed under the MIT software license, see the accompanying\n+// file COPYING or https://opensource.org/license/mit/.\n+\n+#include <chrono>\n+\n+void good_func()\n+{\n+std::chrono::seconds a{1};\n+++a;\n+a.zero();\n+}\n+void bad_func()\n+{\n+std::chrono::seconds{1}.count();\n+std::chrono::seconds a{1};\n+a.count();\n+}\ndiff --git a/src/.clang-tidy b/src/.clang-tidy\nindex b4d50135dd..4deb5a85a5 100644\n--- a/src/.clang-tidy\n+++ b/src/.clang-tidy\n@@ -1,6 +1,6 @@\n Checks: '\n -*,\n-bitcoin-unterminated-logprintf,\n+bitcoin-*,\n bugprone-argument-comment,\n bugprone-use-after-move,\n misc-unused-using-decls,"
  },
  {
   "t": "2023-08-16T12:40:26Z",
   "kind": "comment",
   "who": "kristapsk",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK"
  },
  {
   "t": "2023-08-16T19:25:15Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "37e19bca9060fb7c8b89ad6411b1abe364efb152"
  },
  {
   "t": "2023-08-16T21:11:24Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "Force-pushed to implement review suggestion to use `TicksSinceEpoch()` (thanks!)"
  },
  {
   "t": "2023-10-09T16:25:34Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "790b6956a864e83c58a04217c36f85c0b78c3b19"
  },
  {
   "t": "2023-10-09T17:53:03Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "1e11164d48de2e9e0ff844d4d92c2c6c9c28a05c"
  },
  {
   "t": "2023-10-09T20:30:11Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "Force pushed for needed rebase, then again for CI failure. @dergoegge can you take another look?"
  },
  {
   "t": "2023-10-10T12:34:25Z",
   "kind": "review_comment",
   "who": "dergoegge",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": null,
   "text": "This looks wrong to me, i think it should be:\n\n```suggestion\n            if (state->m_last_block_announcement < oldest_block_announcement.value_or(NodeSeconds::max()) || (state->m_last_block_announcement == *oldest_block_announcement && pnode->GetId() > worst_peer)) {\n```"
  },
  {
   "t": "2023-10-10T18:11:18Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "cbe4f67e3292bf45ea01aa94ecf798f12b6e16bc"
  },
  {
   "t": "2023-10-10T19:42:39Z",
   "kind": "review_comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "path": "src/net_processing.cpp",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": 1352377372,
   "text": "Thanks, good catch! I force-pushed to fix this in a slightly different way that seems more clear to me."
  },
  {
   "t": "2024-02-13T15:54:03Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "b1c3826ec093386c2ddec1d30ca3e777f8860892"
  },
  {
   "t": "2024-03-19T12:08:35Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "test/functional/p2p_block_times.py",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": null,
   "text": "[quoted text omitted]\n\nTypo closing parenthesis before ending quote"
  },
  {
   "t": "2024-03-20T08:09:02Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "doc/release-notes-27052.md",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": null,
   "text": "Would be helpful to mention which kind of eviction does this refer to."
  },
  {
   "t": "2024-03-20T08:15:36Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "doc/release-notes-27052.md",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": 1531649384,
   "text": "`stale-tip eviction logic`"
  },
  {
   "t": "2024-03-20T08:24:25Z",
   "kind": "review",
   "who": "rkrux",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "b1c3826ec093386c2ddec1d30ca3e777f8860892",
   "text": "ACK [b1c3826](https://github.com/bitcoin/bitcoin/pull/27052/commits/b1c3826ec093386c2ddec1d30ca3e777f8860892)\n\n### Testing Methodology\n1. Checked out PR\n2. Built core\n3. Ran `make check`\n4. Ran `test/functional/test_runner.py`.\n5. Ran `bitcoind`\n6. Ran `bcli getpeerinfo | jq '.[] | .last_block_announcement'` & got the following output that shows the presence of this new field `last_block_announcement` for nodes sending out new blocks to my node, 0 for the rest.\n```\n1710919881\n0\n1710919901\n1710920179\n0\n0\n0\n0\n0\n0\n```\n\nNote: This `functional/feature_anchors.py` test did fail one time before it passed on the re-run, which I guess is due to unrelated error. PFB the error output.\n```\nstdout:\n2024-03-20T07:07:27.561000Z TestFramework (INFO): PRNG seed is: 4633686694814387857\n2024-03-20T07:07:27.562000Z TestFramework (INFO): Initializing test directory /var/folders/6v/mpspb4bx4zzf3xr2z96hgq9h0000gn/T/test_runner_\u20bf_\ud83c\udfc3_20240320_122543/feature_anchors_23\n2024-03-20T07:07:28.673000Z TestFramework (INFO): When node starts, check if anchors.dat doesn't exist\n2024-03-20T07:07:28.673000Z TestFramework (INFO): Add 2 block-relay-only connections to node\n2024-03-20T07:07:28.942000Z TestFramework (INFO): Add 5 inbound connections to node\n2024-03-20T07:07:29.682000Z TestFramework (INFO): Check node connections\n2024-03-20T07:07:30.121000Z TestFramework (INFO): Check the addresses in anchors.dat\n2024-03-20T07:07:30.122000Z TestFramework (INFO): Perturb anchors.dat to test it doesn't throw an error during initialization\n2024-03-20T07:07:30.748000Z TestFramework (INFO): When node starts, check if anchors.dat doesn't exist anymore\n2024-03-20T07:07:30.748000Z TestFramework (INFO): Ensure addrv2 support\n2024-03-20T07:07:32.108000Z TestFramework (INFO): Add 256-bit-address block-relay-only connections to node\n2024-03-20T07:07:32.761000Z TestFramework (INFO): Check for addrv2 addresses in anchors.dat\n2024-03-20T07:07:32.845000Z TestFramework (INFO): Restarting node attempts to reconnect to anchors\n2024-03-20T07:07:34.928000Z TestFramework (ERROR): Assertion failed\n\nAssertionError: [node 0] Expected messages \"['Trying to make an anchor connection to pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333']\" does not partially match log:\n```"
  },
  {
   "t": "2024-03-21T17:47:44Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "e47a5290108b0d427b5ff2c520fbd12654545a6c"
  },
  {
   "t": "2024-03-21T17:48:20Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "@rkrux - Thanks for the review and the suggestions, force-pushed them."
  },
  {
   "t": "2024-05-01T20:41:46Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "1322a3a17189fd79a119cf368770e70ed6dda1ff"
  },
  {
   "t": "2024-07-26T18:28:13Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "c49b1097b3fbd61b096b6878ac3617fb9a310f69"
  },
  {
   "t": "2024-08-19T18:45:42Z",
   "kind": "comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "text": "Concept ACK , Adding  `last_block_announcement ` field to the `getpeerinfo` RPC result is a valuable addition and also helps us get more coverage for untested `last_block_announcement`."
  },
  {
   "t": "2024-08-29T19:11:26Z",
   "kind": "review_comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "path": "test/functional/p2p_block_times.py",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "in_reply_to": null,
   "text": "nit: I think it would be useful to add an assertion to check the initial value of last_block_announcement\n```suggestion\n   self.log.info(\"Check that last_block_announcement is initially zero\")\n        peerinfo = node.getpeerinfo()[0]\n        assert peerinfo['last_block_announcement'] == 0\n\n```"
  },
  {
   "t": "2024-08-30T12:01:18Z",
   "kind": "comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "text": "I tested to check if functional tests fail without the change from [https://github.com/bitcoin/bitcoin/pull/26172/](https://github.com/bitcoin/bitcoin/pull/26172/)\n\nReverted this commit [https://github.com/bitcoin/bitcoin/commit/9fcdb9f3a044330d3d7515fa35709102c98534d2](https://github.com/bitcoin/bitcoin/commit/9fcdb9f3a044330d3d7515fa35709102c98534d2)\nmake clean\n./configure\nmake\n\nThe test fails as expected\n\n```\nassert peerinfo['last_block_announcement'] == cur_time\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError\n```\nlast_block_announcement doesn't get updated instead value is always default 0"
  },
  {
   "t": "2024-09-09T18:46:15Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "9f2879a7ec14cb38028bb9c0e979e815ce963957"
  },
  {
   "t": "2024-09-17T17:04:44Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "7ce23d7e75398b65172613cf921f474d9f20d663"
  },
  {
   "t": "2024-11-02T21:22:11Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "21ad9f83750350589898332458485a7c95bb875e"
  },
  {
   "t": "2025-03-31T22:45:41Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "559b0913bfa74bce468ced66b8c0e96169d842f3"
  },
  {
   "t": "2025-03-31T23:02:06Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "Rebased for merge conflicts and to adopt @naiyoma's [review suggestion](https://github.com/bitcoin/bitcoin/pull/27052/files#r1736963290) (thank you!)."
  },
  {
   "t": "2025-04-29T04:58:15Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "5bcf8491d15ac148507a4d172469e1fd9d12078e"
  },
  {
   "t": "2025-04-30T20:34:13Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "f7788f66b4126be29f3599c01ca97a6c7f989f1c"
  },
  {
   "t": "2025-07-01T14:52:20Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034"
  },
  {
   "t": "2025-08-19T20:26:13Z",
   "kind": "review_comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "d46a3a5cce4122c09a5b302dfec997e68ee9698d",
   "in_reply_to": null,
   "text": "I'm not sure I understand why this is optional. Originally, it was set to a sentinel value.\nI don't see a situation where it wouldn't have a value.\nMy suggestion \u2192 `NodeSeconds oldest_block_announcement = NodeSeconds::max();`\nThat way `.has_value() `checks and `*oldest_block_announcement` can be avoided."
  },
  {
   "t": "2025-08-19T20:41:30Z",
   "kind": "review_comment",
   "who": "theuni",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "d46a3a5cce4122c09a5b302dfec997e68ee9698d",
   "in_reply_to": 2286253898,
   "text": "`optional` + `has_value()` communicates intent much more clearly than the sentinel imo. Though admittedly it's awkward as long as worst_peer uses a sentinel as well.\n\nI'd expect to see `std::optional<std::pair<NodeId, NodeSeconds>> worst_peer`, personally."
  },
  {
   "t": "2025-08-22T13:39:32Z",
   "kind": "review",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "cbe4603a90220f0ef0b21c4da68ee16791ad9034",
   "text": "Tested ACK cbe4603a90220f0ef0b21c4da68ee16791ad9034\n\nChanges since last review: rebased and addressed nit fixes.\n\nAlso tested with `msg_cmpctblock`  - test passed as expected."
  },
  {
   "t": "2026-02-10T19:37:55Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "95b673929b37d080017dc9f8f2d8df55f553f36d"
  },
  {
   "t": "2026-03-11T08:08:07Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "@rkrux @naiyoma can you take another look here?"
  },
  {
   "t": "2026-03-12T11:49:27Z",
   "kind": "comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nWill get around to it soon."
  },
  {
   "t": "2026-03-12T12:05:01Z",
   "kind": "comment",
   "who": "dergoegge",
   "assoc": "MEMBER",
   "text": "I think the commit messages for the last two commits should be more detailed"
  },
  {
   "t": "2026-03-13T19:04:41Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "d46a3a5cce4122c09a5b302dfec997e68ee9698d"
  },
  {
   "t": "2026-03-18T20:12:03Z",
   "kind": "review_comment",
   "who": "danielabrozzoni",
   "assoc": "MEMBER",
   "path": "test/functional/p2p_block_times.py",
   "commit": "b569561910f7f3faf9e3f35a80342cb9ed4f4b05",
   "in_reply_to": null,
   "text": "Here and below: prefer using `assert_equal` over `assert`, since it gives better error messages on failure.\n\nSee for example https://github.com/bitcoin/bitcoin/pull/34773 for some effort in that direction"
  },
  {
   "t": "2026-03-18T20:20:06Z",
   "kind": "review_comment",
   "who": "danielabrozzoni",
   "assoc": "MEMBER",
   "path": "test/functional/p2p_block_times.py",
   "commit": "b569561910f7f3faf9e3f35a80342cb9ed4f4b05",
   "in_reply_to": null,
   "text": "nit: we usually put the github link instead of the PR number, so it can be clicked directly:\n\n```\n\u276f git grep \"github.com/bitcoin/bitcoin/pull\" test/functional/\n\ntest/functional/README.md:  The CI linter job also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126).\ntest/functional/feature_assumevalid.py:valid (https://github.com/bitcoin/bitcoin/pull/9484)\ntest/functional/feature_notifications.py:            # https://github.com/bitcoin/bitcoin/pull/9371, it might be better\ntest/functional/rpc_decodescript.py:        # Sourced from https://github.com/bitcoin/bitcoin/pull/27037#issuecomment-1416151907.\ntest/functional/rpc_rawtransaction.py:        # Do this with a pruned chain, as a regression test for https://github.com/bitcoin/bitcoin/pull/29003\ntest/functional/test_runner.py:    # a hard link or a copy on any platform. See https://github.com/bitcoin/bitcoin/pull/27561.\ntest/functional/tool_bitcoin.py:        # https://github.com/bitcoin/bitcoin/pull/33229#issuecomment-3265524908\n```"
  },
  {
   "t": "2026-03-18T22:49:12Z",
   "kind": "review_comment",
   "who": "danielabrozzoni",
   "assoc": "MEMBER",
   "path": "src/net_processing.cpp",
   "commit": "56cd8d9160fe31c6cef15db6ef501f7bebdb36ac",
   "in_reply_to": null,
   "text": "In 56cd8d9160fe31c6cef15db6ef501f7bebdb36ac: I think this line is a cleanup that should be in the first commit, not in this one"
  },
  {
   "t": "2026-03-18T23:02:44Z",
   "kind": "review_comment",
   "who": "danielabrozzoni",
   "assoc": "MEMBER",
   "path": "test/functional/p2p_block_times.py",
   "commit": "b569561910f7f3faf9e3f35a80342cb9ed4f4b05",
   "in_reply_to": null,
   "text": "Why is this needed?"
  },
  {
   "t": "2026-03-18T23:04:37Z",
   "kind": "review_comment",
   "who": "danielabrozzoni",
   "assoc": "MEMBER",
   "path": "test/functional/p2p_block_times.py",
   "commit": "b569561910f7f3faf9e3f35a80342cb9ed4f4b05",
   "in_reply_to": null,
   "text": "nit: The pattern in other functional tests is to save the connection object and use it, instead of using p2ps:\n\n```diff\ndiff --git a/test/functional/p2p_block_times.py b/test/functional/p2p_block_times.py\nindex 6fc60f629e..7fba2ad212 100755\n--- a/test/functional/p2p_block_times.py\n+++ b/test/functional/p2p_block_times.py\n@@ -59,7 +59,7 @@ class P2PBlockTimes(BitcoinTestFramework):\n         self.generate(node, 1)\n\n         self.log.info(\"Create a full-outbound test framework peer\")\n-        node.add_outbound_p2p_connection(P2PDataStore(), p2p_idx=0)\n+        peer = node.add_outbound_p2p_connection(P2PDataStore(), p2p_idx=0)\n\n         self.log.info(\"Test framework peer generates a new block\")\n         tip = int(node.getbestblockhash(), 16)\n@@ -71,7 +71,7 @@ class P2PBlockTimes(BitcoinTestFramework):\n         assert peerinfo['last_block_announcement'] == 0\n\n         self.log.info(\"Test framework peer sends node the new block\")\n-        node.p2ps[0].send_blocks_and_test([block], node, success=True)\n+        peer.send_blocks_and_test([block], node, success=True)\n\n         self.log.info(\"Verify peerinfo block timestamps\")\n         peerinfo = node.getpeerinfo()[0]\n@@ -82,7 +82,7 @@ class P2PBlockTimes(BitcoinTestFramework):\n         node.setmocktime(cur_time+1)\n         headers_message = msg_headers()\n         headers_message.headers = [CBlockHeader(block)]\n-        node.p2ps[0].send_and_ping(headers_message)\n+        peer.send_and_ping(headers_message)\n\n         self.log.info(\"Verify that block announcement time isn't updated\")\n         peerinfo = node.getpeerinfo()[0]\n```\n\nHowever, some tests still use `node.p2ps`... If you like it better, feel free to keep it as is"
  },
  {
   "t": "2026-03-18T23:20:12Z",
   "kind": "review",
   "who": "danielabrozzoni",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "d46a3a5cce4122c09a5b302dfec997e68ee9698d",
   "text": "Code Review d46a3a5cce4122c09a5b302dfec997e68ee9698d\n\nI still haven't tested, I only looked at the code for now. The code looks ok, but there are some style improvements that I think should be picked up:\n\n- I would like to see https://github.com/bitcoin/bitcoin/pull/27052#discussion_r2286284964, I think it would make the code more consistent and easier to understand\n- In the first commit (e3ea5366cd5182525089cfd1a55dc675ff5faa46), it would be nice to have some more context in the commit message on why you're converting `m_last_block_announcement` to `NodeSeconds`. Something as easy as \"preparatory for next commit\" is fine, it's just to have some history in git as well\n- The PR description is good and gives a lot of insight as of why the PR is useful, but this context should be in the commit messages as well. I would add it to the commit message of the third commit (34b9122c2046f55901d3ccdcf9532f23e0c1326c)\n- The first three commits should have the prefixes `rpc:` or `net:` in the commit message."
  },
  {
   "t": "2026-03-20T13:05:57Z",
   "kind": "review",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "d46a3a5cce4122c09a5b302dfec997e68ee9698d",
   "text": "TestedACK d46a3a5cce4122c09a5b302dfec997e68ee9698d\n\nTested without the fix from PR #26172 by flipping the null check condition.\nThe test fails as expected, catching `last_block_announcement` not being updated\nwhen a peer announces a new block.\n\n```\nassert peerinfo['last_block_announcement'] == cur_time\nAssertionError\n```\n\nI have not been able to observe any  eviction logs, but I could see the last_block_announcement being set for my peers\nEvery 5.0s: bitcoin-cli  getpeerinfo | grep last_block_announcement                                                                                     ubuntu: Fri Mar 20 12:46:28 2026\n\n    \"last_block_announcement\": 1773995600,\n    \"last_block_announcement\": 0,\n    \"last_block_announcement\": 0,\n    \"last_block_announcement\": 1773999976,\n    \"last_block_announcement\": 1773999593,\n    \"last_block_announcement\": 0,\n    \"last_block_announcement\": 1773997866,\n    \"last_block_announcement\": 0,\n\nmicro nits: you can address this if you have to make another push\n\nConsider adding a test case for when a peer sends a header that is new to us but has less chain work than our tip  `last_block_announcement`."
  },
  {
   "t": "2026-03-30T23:10:07Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "e37ecc184ccb70c048e6102b422c786eb78d9390"
  },
  {
   "t": "2026-03-31T15:40:12Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "2136973f373c7a4643d126e7bb73fa1fd232f3c4"
  },
  {
   "t": "2026-04-01T21:03:58Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "036a87b8a9968c33960ede0c51d39722756ea979"
  },
  {
   "t": "2026-04-01T21:05:13Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "I think I've addressed (and accepted) all suggestions, thanks!"
  },
  {
   "t": "2026-04-02T04:52:13Z",
   "kind": "review_comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "path": "test/functional/p2p_block_times.py",
   "commit": "b569561910f7f3faf9e3f35a80342cb9ed4f4b05",
   "in_reply_to": 2956716628,
   "text": "You're correct, the last of those 3 lines (`self.disable_autoconnect = False`) doesn't appear to be needed; I don't remember where I got it from (another test, probably). Removed, thanks."
  },
  {
   "t": "2026-04-02T12:01:44Z",
   "kind": "comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nwill re-review\n\nI haven't been able to reproduce the test failing\n\n`458/458 - p2p_block_times.py failed, Duration: 2401 s`\n\nI think this is where the timeout is happening -> https://github.com/bitcoin/bitcoin/blob/master/test/functional/test_framework/p2p.py#L917"
  },
  {
   "t": "2026-04-20T07:52:00Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "@LarryRuane can you fix the CI failures here?"
  },
  {
   "t": "2026-04-20T19:36:07Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "136eed4a13c5c07c43675e27ddda14cb1a486661"
  },
  {
   "t": "2026-04-20T19:44:41Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "@naiyoma\n[quoted text omitted]\n\nI added this to the test, but could not get it to work (CI fails intermittently). I don't think it's very important, so I reverted it (force-pushed 136eed4a13c5c07c43675e27ddda14cb1a486661). In this very rare situation, if the timestamp gets updated (I don't think it will, but if it does), it's not much of a problem; it isn't critical that this timestamp is correct. If you know how to add this to the test, please explain; otherwise, I think this is okay to merge."
  },
  {
   "t": "2026-04-27T08:14:30Z",
   "kind": "comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nYeah, this is not important. I just thought it would be a good addition to the test, but it shouldn\u2019t block this PR from moving forward.\nI did find the CI failure a bit odd. I\u2019ll look into it. If I figure it out, I\u2019ll share it here and likely add it as a follow-up."
  },
  {
   "t": "2026-04-27T22:43:47Z",
   "kind": "comment",
   "who": "luke-jr",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nIf it isn't important, then why expose it to RPC at all?"
  },
  {
   "t": "2026-04-29T11:38:42Z",
   "kind": "comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI think I've been able to reproduce the CI failure.\nThe intermittent timeout depends on how fast the test runs.\n\nWhen the test runs quickly, the first and second blocks are identical because\nboth are created within the same second and get the same timestamp. So\n`blocks[-1].hash_hex == node.getbestblockhash()` is immediately True and\n`send_blocks_and_test(success=True)` resolves instantly.\n\nWhen there is a slight delay between the two create_block calls,\nthe blocks get different timestamps and are therefore different.\n`block.hash_hex != block.hash_hex`, so `wait_until` keeps polling for\n60 seconds waiting for the tip to change. This results in\na timeout.\n\nA second issue is that the test was not correclty checking the edge case i suggested.\nit was  passing because `received_new_header=0`\nwe already had the block. The chainwork condition was never\ntested.\n\nMy suggestion is to create a genuinely different block with an explicit timestamp:\n\n    block2 = create_block(tip, create_coinbase(2), block.nTime + 1)\n    block2.solve()\n    headers_message2 = msg_headers()\n    headers_message2.headers = [CBlockHeader(block2)]\n    peer.send_and_ping(headers_message2)\n\nThis ensures `received_new_header=1` never seen this header\nbut the condition still evaluates to false because `nChainWork` is equal not\ngreater:\n\n    if (received_new_header && last_header.nChainWork > m_chainman.ActiveChain().Tip()->nChainWork)\n    // True && (0006 > 0006) == False\n\nI did generate mutants for this condition\n\n```\nif (received_new_header && last_header.nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {\n        nodestate->m_last_block_announcement = Now<NodeSeconds>();\n    }\n```\n\n```\n\n<     if (received_new_header && last_header.nChainWork < m_chainman.ActiveChain().Tip()->nChainWork) {\n---\n[quoted text omitted]\n2899c2899\n<     if (received_new_header && last_header.nChainWork <= m_chainman.ActiveChain().Tip()->nChainWork) {\n---\n[quoted text omitted]\n2899c2899\n<     if (received_new_header || last_header.nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {\n---\n[quoted text omitted]\n\n```\nTesting this edge case does kill these two surviving mutants (> -> >= and && -> ||). In my opinion, this is worth testing, so maybe it can be included here or in a follow-up."
  },
  {
   "t": "2026-05-22T09:54:10Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "@LarryRuane do you plan to update your approach here with @naiyoma's suggestions?"
  },
  {
   "t": "2026-05-23T22:17:13Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "4895b8f058b80749dc7ca62ef31787bbffb71b47"
  },
  {
   "t": "2026-05-24T02:41:29Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "e422fc50f39b5ddd0333b232a7437f3f3dadb73c"
  },
  {
   "t": "2026-05-24T03:44:05Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "@naiyoma - thanks for the review and mutation testing!"
  },
  {
   "t": "2026-06-05T18:35:32Z",
   "kind": "review",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "e422fc50f39b5ddd0333b232a7437f3f3dadb73c",
   "text": "re-ACK e422fc50f39b5ddd0333b232a7437f3f3dadb73c\n\nnew test case added, test stylistic changes addressed, and commit messages prefixed, except for the first one, which could be net:"
  },
  {
   "t": "2026-06-07T15:52:56Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "252a6e72754147ad25f1bc1000cf958e3b350225"
  },
  {
   "t": "2026-06-07T15:55:12Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nThanks for catching that, force-pushed to fix that (just the commit message, no other changes)."
  },
  {
   "t": "2026-06-09T19:07:00Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/denialofservice_tests.cpp",
   "commit": "252a6e72754147ad25f1bc1000cf958e3b350225",
   "in_reply_to": null,
   "text": "nit in the first commit: This is not worse, but I don't think NodeSeconds is the right type here.\n\nIt seems more consistent to use the native clock type (and resolution), that is NodeClock::time_point.\n\nI've pushed a commit that does this to fa67c651510db5e571fa82fe692ac33e48c451a4 (https://github.com/bitcoin/bitcoin/pull/35315/commits)"
  },
  {
   "t": "2026-07-24T08:43:38Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "@LarryRuane do you want to address maflcko's nit?"
  },
  {
   "t": "2026-07-24T15:50:57Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "87bc3333063fe21ec3e16accb6bdc1035a5da865"
  },
  {
   "t": "2026-07-25T05:15:12Z",
   "kind": "force_push",
   "who": "LarryRuane",
   "commit": "13ce46ba1a79c99882cc1ac768db1df1f25de098"
  },
  {
   "t": "2026-07-25T05:17:16Z",
   "kind": "comment",
   "who": "LarryRuane",
   "assoc": "CONTRIBUTOR",
   "text": "CI failures should be fixed by rebasing (there was a hidden merge conflict); functional test framework's `create_block()`'s call signature changed to require the block time to be given as a keyword argument, rather than in position 3. The fix is to add the `ntime=` keyword to the one place that the new functional test, `p2p_block_times.py`, calls that function."
  },
  {
   "t": "2026-09-08T11:30:40Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "@naiyoma @rkrux can you follow up here?"
  },
  {
   "t": "2026-09-08T13:26:31Z",
   "kind": "review",
   "who": "rkrux",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "13ce46ba1a79c99882cc1ac768db1df1f25de098",
   "text": "lgtm ACK 13ce46ba1a79c99882cc1ac768db1df1f25de098"
  },
  {
   "t": "2026-09-10T11:12:08Z",
   "kind": "review",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "13ce46ba1a79c99882cc1ac768db1df1f25de098",
   "text": "ACK 13ce46ba1a79c99882cc1ac768db1df1f25de098"
  }
 ],
 "labels_log": [
  {
   "t": "2023-02-06T18:33:01Z",
   "action": "labeled",
   "label": "Tests",
   "who": "DrahtBot"
  },
  {
   "t": "2023-05-24T10:39:30Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-08-14T17:18:45Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-08-15T06:17:42Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-08-15T08:10:45Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-10-03T10:31:42Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-10-09T18:20:42Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-01-13T15:01:40Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-01-31T18:14:15Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-02-13T16:00:46Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-02-13T21:09:08Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-30T22:53:39Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-05-01T21:51:44Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-07-24T11:19:42Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-07-26T20:01:22Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-09-02T22:46:25Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-09-09T20:30:42Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-09-12T09:25:11Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-09-13T20:14:15Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-09-17T18:42:28Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-09-17T23:08:58Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-10-29T20:07:01Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-11-02T21:53:58Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-12-27T20:50:38Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-01-01T15:32:54Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-03-19T07:47:18Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-03-31T23:59:36Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-04-25T13:17:53Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-04-29T05:44:54Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-04-29T06:11:06Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-04-30T23:05:01Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-05-19T08:41:50Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-05-21T22:46:44Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-07T01:52:32Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-10T19:49:48Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-10T20:35:29Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-12T12:04:02Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-31T16:38:31Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-04-20T20:36:12Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-23T23:02:54Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-24T03:28:36Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-24T16:51:22Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-25T06:13:31Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2023-02-07T18:51:05Z",
   "kind": "convert_to_draft",
   "who": "LarryRuane"
  },
  {
   "t": "2023-08-15T06:55:18Z",
   "kind": "ready_for_review",
   "who": "LarryRuane"
  },
  {
   "t": "2026-02-12T11:05:22Z",
   "kind": "closed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-12T11:05:33Z",
   "kind": "reopened",
   "who": "DrahtBot"
  }
 ],
 "text_chars": 26539,
 "text_tokens_estimate": 6634,
 "changed_paths": [
  "doc/release-notes-27052.md",
  "src/net_processing.cpp",
  "src/net_processing.h",
  "src/rpc/net.cpp",
  "src/test/denialofservice_tests.cpp",
  "test/functional/p2p_block_times.py",
  "test/functional/rpc_net.py",
  "test/functional/test_runner.py"
 ],
 "files": [
  {
   "path": "doc/release-notes-27052.md",
   "add": 8,
   "del": 0
  },
  {
   "path": "src/net_processing.cpp",
   "add": 22,
   "del": 16
  },
  {
   "path": "src/net_processing.h",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/rpc/net.cpp",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/test/denialofservice_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "test/functional/p2p_block_times.py",
   "add": 105,
   "del": 0
  },
  {
   "path": "test/functional/rpc_net.py",
   "add": 5,
   "del": 1
  },
  {
   "path": "test/functional/test_runner.py",
   "add": 1,
   "del": 0
  }
 ],
 "test_lines": 114,
 "git": {
  "head": "13ce46ba1a79c99882cc1ac768db1df1f25de098",
  "head_matches_backup": true,
  "base": "6b059d9dbdbd4d5b6f52993f93bf291181bf3900",
  "commits": [
   {
    "sha": "bb8bca630a",
    "subject": "change m_last_block_announcement type from int64_t to NodeClock::time_point",
    "files": 3,
    "add": 23,
    "del": 18
   },
   {
    "sha": "26a25162a4",
    "subject": "net: add m_last_block_announcement to CNodeStateStats",
    "files": 2,
    "add": 2,
    "del": 0
   },
   {
    "sha": "7d3d04e925",
    "subject": "rpc: add last_block_announcement to the getpeerinfo output",
    "files": 2,
    "add": 7,
    "del": 1
   },
   {
    "sha": "5d5397d841",
    "subject": "test: add functional test for block announcement time tracking",
    "files": 2,
    "add": 106,
    "del": 0
   },
   {
    "sha": "13ce46ba1a",
    "subject": "doc: add release note for 27052",
    "files": 1,
    "add": 8,
    "del": 0
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "5ef8bae2e28f7683",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}