Author: b10c
Source: https://b10c.me/observations/16-slow-block-propagation-validation-signet/
The original article was published in April 2026.
During last week's " Signet Tough Block Demo " event, I ran a custom P2P client connected to all (approximately 190) Signet nodes running on IPv4 and Tor. My goal was to measure the speed at which blocks propagate across the network and the speed at which each node verifies a block .
This article was originally published on bnoc.xyz .
The metrics show that block propagation speed was significantly impacted because nodes had to wait for peers that received the block earlier to complete their verification before receiving blocktxn response message and any missing transactions locally, allowing them to begin reconstructing, verifying, and forwarding the block. Faced with these far from worst-case scenario difficult blocks, verification speed decreased by approximately 160 times on the observable median peer node. This median node took 20 seconds (s) to verify a difficult block, while verifying a normal block on Signet took only 176 milliseconds (ms).
(Translator's note: "Signet" is a Bitcoin test network that uses a dedicated block signer. "Hardcore block" refers to a block that is particularly slow to verify.)
Methodology
To measure block propagation and verification speed, two types of announcement events from peer nodes are particularly important:
First, the BIP-152 high-bandwidth dense block announcement. After we request a high-bandwidth dense block from our peers using sendcmpct(1) message, the peers will send us a cmpctblock message as soon as possible after they have constructed the block and before they have verified it. When our client receives a cmpctblock message from a peer, it will request a transaction by sending a getblocktxn message. Once the peer has verified the block, it will respond with a blocktxn message. By recording the timestamps of the cmpctblock message and blocktxn message arriving locally, we can infer the verification time. The timestamp of cmpctblock also indicates the speed of block propagation (without verification time).
Secondly, the low-bandwidth dense block INV (or a similar BIP-130 block header) announcement also contains information. These occur after the node has verified the block.
Our custom P2P client also records ping pong round-trip time (RTT) of each node's Bitcoin protocol, thus enabling the timestamp of announcement messages to be adjusted for network and application layer latency.
The block propagation time is the difference between the timestamp of the announcement message and the timestamp of the first announcement of the block that we receive. Both timestamps are adjusted using RTT, with the adjustment term being $\frac{1}{2}$ RTT: $\textit{ts_adjusted} = \textit{ts_raw} - \frac{1}{2} \textit{RTT}$. We assume that RTT is symmetric to 1 .
The block verification time is the time difference between a peer node sending us a high-bandwidth dense block announcement and sending its corresponding blocktxn response information, expressed as 2. We don't know the timestamp of a peer node sending us a message, but we can calculate it from the timestamp of receiving the message and the RTT. The entire process follows this timeline:

This event sequence diagram shows the time period we measured.
- $t_0$: The peer node has completed block reconstruction and sent us a high-bandwidth dense block announcement.
- $t_1$: We received a high-bandwidth dense block announcement ($t_0$ followed by $\frac{1}{2} RTT$).
- $t_2$: We send a
getblocktxnrequest (assuming it's instantaneous). - $t_3$: The peer node received the
getblocktxnrequest ($t_2$ followed by $\frac{1}{2} RTT$). - $t_4$: The peer node completes block verification and sends
blocktxn(time uncertain). - $t_5$: We received
blocktxn($t_4$ followed by $\frac{1}{2} RTT$)
We want to measure the duration between two points in time, $t_0$ and $t_4$ ($= d$). For a peer node, we have $t_1$ (and $t_2$), $t_5$, and RTT. However, because a complete message round trip must be completed between $t_0$ and $t_4$, when the block verification time is shorter than the RTT, we can only obtain an upper bound on the verification duration. In this case, $d ≈ RTT$.
$$
\begin{align}
t_0 &= t_1 - \frac{1}{2} \textit{RTT} \\
t_2 &= t_1 \\
t_3 &= t_1 + \frac{1}{2} \textit{RTT} \\
t_4 &= t_5 - \frac{1}{2} \textit{RTT} \\
d &= t_4 - t_0 \\
\textit{longer-than-rtt} &= d > \textit{RTT}
\end{align}
$$
As for RTT itself, we use the median of the RTT recorded during the period of connection activity.
result
Block propagation
Regarding block propagation, we observed the time it took for a peer node to announce a block using a high-bandwidth dense block and the time it took to announce the block via an INV message. We also distinguished between ordinary blocks and specially constructed complex blocks.

- INV propagation speed of ordinary blocks and specially constructed difficult blocks, and ECDF propagation speed of dense blocks -
On both types of blocks—ordinary blocks and specially constructed complex blocks—high-bandwidth dense block announcements propagate faster than INV announcements. This is expected because high-bandwidth dense block announcements are sent before the block is verified, while INV announcements are sent only after the block has been verified.
The propagation time of regular blocks on Signet (green line) and dense block propagation time (blue line) are similar. Regular block channels on Signet do not contain many transactions and can be verified quickly. Nodes in the (first) 25th percentile will announce their blocks to us after 150ms; the median node is slightly slower, slightly over 200ms; the 75th percentile node is after 300ms; after 600ms, 90% of peer nodes have verified their blocks and sent us their announcements.
- (Difficult blocks only) Block propagation ECDF-
The propagation of difficult blocks differs significantly. While high-bandwidth dense block announcement messages (yellow lines) arrive before the INV announcement messages, nodes at the 25th percentile send us a high-bandwidth dense block announcement approximately 14 seconds later; their INV messages arrive after 27.5 seconds. The median node sends its dense block announcement approximately 26 seconds later, followed by its INV message after 31.7 seconds. The 75th percentile node sends its dense block announcement after 31.7 seconds and its INV message after 55 seconds. The 90th percentile peer node announces its difficult block as a dense block after 58 seconds and then announces it via an INV message after 114 seconds.
This indicates that the speed of block propagation depends on verification performance. Blocks that are easier to verify propagate faster, and blocks that are harder to verify propagate slower. This seems understandable, since nodes need to verify a block before propagating it. However, it's important to note that this slowdown only occurs when a node needs to request a transaction using getblocktxn . If a node doesn't need to request a transaction, for example, because its transaction pool happens to contain all the transactions in the block, it can immediately reconstruct the block and begin verifying it after receiving the cmpctblock message. Difficult transactions in slow blocks are non-standard and require getblocktxn to request. If a "dense block pre-filling" technique is implemented, as this post states, then difficult blocks might also propagate faster. In this demonstration, these difficult blocks all contained only a single difficult transaction of 999kvB in size. Pre-filling this transaction might not help, as it is simply too large.
Block verification
To measure block verification time, we only observe high-bandwidth dense block announcements and blocktxn responses. Specifically, our duration d is defined as d = t4 - t0 (as previously stated). When the actual verification time is lower than the RTT, the value we measure will be the upper limit of the true verification time. This means that the actual verification time may be shorter than the value we measure.

- Distribution of verification time for normal blocks and difficult blocks -
The graph above shows the verification time for regular blocks and specially constructed complex blocks. For some regular blocks, we only measure the upper bound; the actual verification time may be wider.
The verification time for normal blocks on Signet ranges from 10ms to 2s, while the verification time for troubled blocks ranges from 2.5s to 5 minutes.

- ECDF for block verification duration -
For regular blocks (including those where only the upper bound of the verification time is measured), the 25th percentile of the block verification time is 37ms; the 50th percentile is 176ms; the 75th percentile is 447ms; and the 90th percentile is 740ms. The vast majority of listening nodes on the Signet network can verify regular blocks within 1 second.
As for the difficult blocks, the 25th percentile value is 8.2s; the 50th percentile value is 19.7s; the 75th percentile value is 32s; and the 90th percentile value is 78.3s.
To get a visual sense of this verification speed, we can use the median verification time of ordinary blocks across peer nodes as a benchmark. Then, we compare it with the median verification time of difficult blocks for that node and calculate a multiplier.

- Slowing down verification time for complex blocks (ECDF) -
Among my peers, the 10th percentile was 13.5 times slower; the 25th percentile was 31 times slower; the 50th percentile was 159.4 times slower; the 75th percentile was 793 times slower; and the 90th percentile was 1156 times slower. There appears to be a significant difference in performance among listening nodes on the Signet network when verifying these complex blocks.
When interpreting this data, we must remember that these are just the difficult blocks that Antoine chose to disclose, and are far from the most difficult blocks that could be constructed to verify.
Accuracy of this measurement method
To verify the accuracy of our measurement method, we can examine the debug.log file of Bitcoin Core under -debug=bench -debug=validation -debug=net -logtimemicros=1 startup tab, and then compare the actual validation time with the measured validation time.
The screenshot of debug.log below shows the first hard block in the first round of the demo that my watch node experienced (see delvingbitcoin.org for details). I also added four markers ( [m1] [m2] [m3] [m4] ).
2026-04-08T14:05:12.292703Z [msghand] [cmpctblock] Successfully reconstructed block 0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b with 1 txn prefilled, 0 txn from mempool (incl at least 0 from extra pool) and 1 txn (999557 bytes) requested2026-04-08T14:05:12.292795Z [msghand] [cmpctblock] Reconstructed block 0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b required tx 1b44e7f59d39e4d53b4c4a77a650561de1871fe962fe6a17d1a302b877b2cb48[m1] 2026-04-08T14:05:12.422939Z [msghand] [validation] NewPoWValidBlock: block hash=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b2026-04-08T14:05:12.423680Z [msghand] [net] PeerManager::NewPoWValidBlock sending header-and-ids 0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b to peer=52026-04-08T14:05:12.423794Z [msghand] [net] sending cmpctblock (358 bytes) peer=52026-04-08T14:05:12.543864Z [msghand] [bench] - Using cached block2026-04-08T14:05:12.543926Z [msghand] [bench] - Load block from disk: 0.07ms2026-04-08T14:05:12.543962Z [msghand] [bench] - Sanity checks: 0.01ms [0.00s (0.01ms/blk)]2026-04-08T14:05:14.166485Z [msghand] [bench] - Fork checks: 1622.48ms [1.96s (93.29ms/blk)]2026-04-08T14:05:14.643331Z [msghand] [bench] - Connect 2 transactions: 476.84ms (238.419ms/tx, 1.189ms/txin) [0.69s (32.64ms/blk)]2026-04-08T14:06:33.383188Z [msghand] [bench] - Verify 401 txins: 79216.70ms (197.548ms/txin) [79.43s (3782.32ms/blk)]2026-04-08T14:06:33.385563Z [msghand] [bench] - Write undo data: 2.39ms [0.07s (3.19ms/blk)]2026-04-08T14:06:33.385643Z [msghand] [bench] - Index writing: 0.11ms [0.00s (0.08ms/blk)]2026-04-08T14:06:33.385754Z [msghand] [validation] BlockChecked: block hash=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b state=Valid2026-04-08T14:06:33.385862Z [msghand] [bench] - Connect total: 80841.94ms [81.46s (3879.22ms/blk)]2026-04-08T14:06:33.832999Z [msghand] [bench] - Flush: 447.07ms [0.51s (24.52ms/blk)]2026-04-08T14:06:33.833198Z [msghand] [bench] - Writing chainstate: 0.27ms [0.00s (0.19ms/blk)]2026-04-08T14:06:33.833668Z [msghand] [validation] Enqueuing MempoolTransactionsRemovedForBlock: block height=299177 txs removed=02026-04-08T14:06:33.833839Z [msghand] UpdateTip: new best=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b height=299177 version=0x20000000 log2_work=43.630312 tx=29147716 date='2026-04-08T14:03:39Z' progress=1.000000 cache=15.3MiB(114240txo)2026-04-08T14:06:33.833856Z [msghand] [bench] - Connect postprocess: 0.66ms [1.57s (74.55ms/blk)][m2] 2026-04-08T14:06:33.833868Z [msghand] [bench] - Connect block: 81290.01ms [83.55s (3978.55ms/blk)]2026-04-08T14:06:33.833926Z [msghand] [validation] Enqueuing BlockConnected: block hash=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b block height=2991772026-04-08T14:06:33.833962Z [msghand] [validation] Enqueuing UpdatedBlockTip: new block hash=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b fork block hash=0000000bc5d91380fa9188acfabd6a59244e8e6e744b0a0ef07064968027e256 (in IBD=false)2026-04-08T14:06:33.834043Z [msghand] [validation] ActiveTipChange: new block hash=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b block height=299177[m3] 2026-04-08T14:06:33.834887Z [msghand] [net] received: headers (82 bytes) peer=102026-04-08T14:06:33.835052Z [msghand] [net] sending ping (8 bytes) peer=102026-04-08T14:06:34.062891Z [scheduler] [validation] MempoolTransactionsRemovedForBlock: block height=299177 txs removed=02026-04-08T14:06:34.063548Z [scheduler] [validation] BlockConnected: block hash=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b block height=2991772026-04-08T14:06:34.063728Z [scheduler] [validation] UpdatedBlockTip: new block hash=0000000eb552c9f26e712d546c71297fd0623890299b40e7ada81d2dc32f5d0b fork block hash=0000000bc5d91380fa9188acfabd6a59244e8e6e744b0a0ef07064968027e256 (in IBD=false)[m4] 2026-04-08T14:06:34.461329Z [msghand] [net] received: headers (82 bytes) peer=42026-04-08T14:06:34.461786Z [msghand] [net] sending ping (8 bytes) peer=11... -> p2p communication continues-
[m1]: At2026-04-08T14:05:12.422939,NewPoWValidBlockmarks the start of validating this block. -
[m2]: At2026-04-08T14:06:33.833868, the branch log showsConnect block: 81290.01ms -
[m3]: At2026-04-08T14:06:33.834887, P2P communication was basically restored. -
[m4]: At2026-04-08T14:06:34.461329, we ended withUpdatedBlockTip(updating the top of the blockchain), and P2P communication was fully restored.
The difference between [m1] and [m4] is 82038ms, while the value we measured is 82049ms (only 11ms apart). Looking back at the first round, the difference between the two was not always this small.
| The value obtained by measurement | Actual value (from logs) | difference | Deviation rate | Block |
|---|---|---|---|---|
| 82049ms | 82038ms | 11ms | 0.013% | 0000000eb552c9f26e712d546c71297f.. |
| 82478ms | 81814ms | 664ms | 0.81% | 000000002b3a132836666c18f5e1a9d9.. |
| 76937ms | 76368ms | 569ms | 0.74% | 00000006d34037534a517f9e5809a347.. |
| 79382ms | 78667ms | 715ms | 0.90% | 00000014a4cae4501f98539b45c76059.. |
| 79894ms | 78571ms | 1323ms | 1.68% | 00000003220437cb8b5a2edef6be828c.. |
| 93556ms | 93541ms | 15ms | 0.016% | 000000143c97bf0134c5cf0881dfd4ef.. |
The higher measurement error occurs in the middle blocks, possibly because peer nodes have a large backlog of P2P messages that need to be processed before responding to our getblocktxn message or announcing the next dense block. Here, a 1% bias rate is acceptable. Also note that this does not mean our measurements for all peer nodes are accurate.
in conclusion
We successfully measured the propagation speed of high-bandwidth dense blocks and INV declarations among listening nodes on the Signet network. When difficult blocks appear, the block propagation speed is significantly reduced because they are broadcast simultaneously.
We also measured block verification time. Under normal Signet network conditions, block verification can sometimes be faster than the round-trip communication between nodes. In such cases, we can only measure the upper limit of verification time. For difficult blocks, we measured a 160x reduction in verification time for the 50th percentile node (using the difficult block shown in this demo). Difficult blocks require more than 20 seconds to verify.
Future work could involve exploring block propagation and verification times on the mainnet.
- - -
I have published the Jupyter notebook I used to create these charts here . Modifications and extensions to this research are welcome.
- - -
1. This might not hold true on Tor .
2. Sometimes, especially when critical blocks from multiple complex blocks arrive at peer nodes and are queued for verification, we only receive the getblocktxn message after all queued blocks have been verified. In this case, I use the time of the next dense block announcement. These are sent before the next block verification begins.
(over)



