From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Tue, 05 May 2026 08:40:03 -0700 Received: from mail-oa1-f58.google.com ([209.85.160.58]) by mail.fairlystable.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1wKHsQ-0004ta-7j for bitcoindev@gnusha.org; Tue, 05 May 2026 08:40:03 -0700 Received: by mail-oa1-f58.google.com with SMTP id 586e51a60fabf-42c0e460e4bsf11209950fac.1 for ; Tue, 05 May 2026 08:40:01 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1777995596; cv=pass; d=google.com; s=arc-20240605; b=acXVWL7blN/hY1zOvHqt7T3HfcSag3mrej+NGXzNJt4Vy78HAVRNKyKs07FI+Qd0pZ 0xvORBdxb8XQbD7z8XehvkTc8c1nHfKKeeuuUg71i0JUeln+mOEXjy47gGK8cV7ClJY/ hlBCNyDV2iN4gYqi2tvz21U6HcgnQIH3gUljHN3Z388qv/XMrjzjPeKhTlc71oQX2j9g A/q7gjmS9VMkE07RRCbiH+Tow0KNr7hdIkZVfF/o4QJTYDVHLRbJUpEuRy/lWq8t2E2J A542OEiSqgI3WzjeM17njdyBTOtV6bBLvDTU8dza3j7kp2V418mkInUQksJmy1QbcXe/ 0xBA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:reply-to:mime-version:feedback-id :message-id:subject:from:to:date:dkim-signature; bh=FnAoYqliSCV3wiMPG0gH/xF5ikIWETO19jn9byRY8r0=; fh=38cTJMclHTtq6QcNCSDR2cLL1fBDobsTW0sabeN80MM=; b=cfxpZyE4zGrmfNbkh1DDJxdHEjeI0+j94K732b+XztgGy//I4R69ldETSzRfYGPqs3 fn8awcVXtVJ9N1vsOe0w918TxFczCwJ2o60T0UtPsMsao5k0znmzRpb/c++RdG72X2uE cQvqugTceBdodzNeuac1BHFY1Mwa3pJq02405IwamBT/f7GJiJAbDQJmNjxkIq9X5J9y NBp4x/pw7dSLduqH+fKCNRFOou6oWxh/UVtVcJJhvQLM7XPfOIfgx0QOMQBv6iRGBXG+ PtOhRx3jPEwUbyYHWqeVsfKK8sAUZBMdd+MTsDj6DeQoT66hHku2yVq9hdCkQj9fc9uf Z6hw==; darn=gnusha.org ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@protonmail.com header.s=protonmail3 header.b=PRyCPUHT; spf=pass (google.com: domain of fjahr@protonmail.com designates 185.70.43.103 as permitted sender) smtp.mailfrom=fjahr@protonmail.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20251104; t=1777995596; x=1778600396; darn=gnusha.org; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:reply-to :x-original-authentication-results:x-original-sender:mime-version :feedback-id:message-id:subject:from:to:date:from:to:cc:subject:date :message-id:reply-to; bh=FnAoYqliSCV3wiMPG0gH/xF5ikIWETO19jn9byRY8r0=; b=bml/puxVVvw6qZxCu8nOb4ttypRX9tY/INUDPCtW04ghGf6TBQbp89E8y7a14h1Igd KWwDgGtVLlUsfq7IYCyvQW3Zv+UJ247z4SinvFghBzD3YsrUtBbXdqAfbNSbevpJHwjc 5S4KxXhOqQlPxPJtPNOaFlsoRrugiwb7iqKPD+uXBYM7y4T5w5YqpAKPIupLfnci7xYw d5TbMEcPzWEpQoxZd690zg7Atc0coQgopsmblVhsAYzCHUQj+tPS56TvS4bwIzZA2GxE GuJNzBVxY9Mqiu2luxK/Sv3mXBuSU3kzf0hsggWtYu9U8hzQEFWnKgZc+Ap2CUxUik1u Fs8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777995596; x=1778600396; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:reply-to :x-original-authentication-results:x-original-sender:mime-version :feedback-id:message-id:subject:from:to:date:x-beenthere :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FnAoYqliSCV3wiMPG0gH/xF5ikIWETO19jn9byRY8r0=; b=fA6r1tYqT5g7uXAU1JSUU4aiuRfNE1mCvgGIpj4Epu8siIHbtZnXBP32vTlw/4/Eub yELLPhNCqDjZYc2xKBb1/IHJ5rIabLIVR18CM31W06FyMy/yEgFCQXyPoaD0F0FtK6AI ch2i8LA2TO4Miel/Q+Htp1tXFy4OcJsgf9bLBVXmrwVASpqzoSGpT6WEv4f/0mC6PsuI FkF42IgeKB48APdxDAqjn3559JrWCwWsAWTnpCXEgTehX//P6x617qD7dOvZSG5hA8YA K9IMIE01aPDUAxAQ+HxzuQuzvX0eEEdoizE/rvzfTl0TVWPRfGt4UcQW9CI/lGNr4Icp Kwmw== X-Forwarded-Encrypted: i=2; AFNElJ/fsWskzu/Chx+PB1rDissOfZl0ukr82fWGkIZz+v+d2a7q0YTzRnBcuAbba6UNS0zVGFg2tOrwCkJf@gnusha.org X-Gm-Message-State: AOJu0YywAklDZ/7kN8gx7ZKVJOloDZXiNCmxPmcjIutefr2sY8W+cQl7 GxgrQisyobIrxiAO9OPI86+5N0FYF5BCgtNPdUcMyCgPeG0okxTTr7uG X-Received: by 2002:a05:6871:68c6:b0:433:ce4d:c214 with SMTP id 586e51a60fabf-43476003ed8mr7683570fac.6.1777995595990; Tue, 05 May 2026 08:39:55 -0700 (PDT) X-BeenThere: bitcoindev@googlegroups.com; h="AUV6zMMmq1G4I1SSAHM8Pmxtl7C958pIB3gWUv/vlGaLSLBL0Q==" Received: by 2002:a05:6870:8311:b0:417:5927:12e9 with SMTP id 586e51a60fabf-4342fa65ca5ls3772715fac.2.-pod-prod-01-us; Tue, 05 May 2026 08:39:50 -0700 (PDT) X-Received: by 2002:a05:6808:168b:b0:467:ca8:c396 with SMTP id 5614622812f47-47c88fc95e4mr7113883b6e.9.1777995589890; Tue, 05 May 2026 08:39:49 -0700 (PDT) Received: by 2002:a05:6402:5303:b0:67b:832d:aa4c with SMTP id 4fb4d7f45d1cf-67c137ca02bmsa12; Tue, 5 May 2026 08:36:11 -0700 (PDT) X-Received: by 2002:aa7:c51a:0:b0:672:be92:e913 with SMTP id 4fb4d7f45d1cf-67ccbbb8f33mr1309115a12.17.1777995369462; Tue, 05 May 2026 08:36:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1777995369; cv=none; d=google.com; s=arc-20240605; b=cdoCh/VuxHbAxqWE3YsSIxge7tQjawoTS1xhag94D+A7LJrDzpEpy0v0uKJTqK/hOI UOC4vaUvSPwpcfxa5z658x/KLTktilxZ6QoOgJAstB7XAikmDGtkI6uopCe/37em5bux vAR5F65bsBnjbdtsanWJIfccqo5Rgxulo1JO6TfkzHc2IJWfk7UgDAAhnaHxRjDRwDmo J+vPhIqO+/KR1Z31U/iWQ8S8tKzySZoFFTJAFWdwNHvkQRlAddw8GKRndukv7V/ZnswO J2VonD18X+zAChvLwYI6xqqlcwcAy+bZ6a9fbYH420PDfsj8nhGvezT4l8RIUEKbLwQl qagw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=mime-version:feedback-id:message-id:subject:from:to:date :dkim-signature; bh=UtGiVIVekCCB8S5y1bVPYtKsYacahmmHGh9JZrQOB40=; fh=lhFSo2W/mHC0QoJ9oNg3A35n0DTltt3CQl1/0RggJlk=; b=UCwDTpCNeeX56cqjFopZOoUhRz+tECk+P3jCBomazXEXO7bXK32G2+3YCbqArPwS8R ax1yRZtqyBLffq24ZKl7COiBilMWgb1VgodjNIgwPPKI1KYBOXkIBi6MZir9MHNXBE75 fCzXjmKk++2rN0bUYGSGdrlTcrKQkC3itk9K0ymBVTJX5OAlIux8OQHNxi1Xs6Z6N0AQ GS1HS9w+bXGSsLOK/qdbhJUNQBo7mABsrt6FCQDHyYp/9gMio0JKkPiNr6NZD1VswqSU ephMiZzBbRgLgSqtMRcYPWgSNgcc8vh5jGXxYNxVWFhxi9ik825sY5vGrfwICunMFign 27Cg==; dara=google.com ARC-Authentication-Results: i=1; gmr-mx.google.com; dkim=pass header.i=@protonmail.com header.s=protonmail3 header.b=PRyCPUHT; spf=pass (google.com: domain of fjahr@protonmail.com designates 185.70.43.103 as permitted sender) smtp.mailfrom=fjahr@protonmail.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com Received: from mail-43103.protonmail.ch (mail-43103.protonmail.ch. [185.70.43.103]) by gmr-mx.google.com with ESMTPS id 4fb4d7f45d1cf-67cd915e7aesi50507a12.8.2026.05.05.08.36.09 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2026 08:36:09 -0700 (PDT) Received-SPF: pass (google.com: domain of fjahr@protonmail.com designates 185.70.43.103 as permitted sender) client-ip=185.70.43.103; Date: Tue, 05 May 2026 15:36:04 +0000 To: "bitcoindev@googlegroups.com" From: "'Fabian' via Bitcoin Development Mailing List" Subject: [bitcoindev] [BIP Draft] P2P UTXO Set Sharing Message-ID: Feedback-ID: 5067558:user:proton X-Pm-Message-ID: b80eaf16d12f8171c0b966546959990d6771712f MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="b1=_x2wbsU61KNDvqIHhbCxYupZh5hf9jreoUaJxlQ8Rk" X-Original-Sender: fjahr@protonmail.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@protonmail.com header.s=protonmail3 header.b=PRyCPUHT; spf=pass (google.com: domain of fjahr@protonmail.com designates 185.70.43.103 as permitted sender) smtp.mailfrom=fjahr@protonmail.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com X-Original-From: Fabian Reply-To: Fabian Precedence: list Mailing-list: list bitcoindev@googlegroups.com; contact bitcoindev+owners@googlegroups.com List-ID: X-Google-Group-Id: 786775582512 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , X-Spam-Score: -1.0 (-) --b1=_x2wbsU61KNDvqIHhbCxYupZh5hf9jreoUaJxlQ8Rk Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Dear list, I am sharing a BIP draft for sharing the UTXO set over the P2P network, an = old idea that makes it possible to utilize AssumeUTXO without sourcing a UT= XO set dump from a third party source. You can find the full text below or = comment on the BIPs repository pull directly: https://github.com/bitcoin/bi= ps/pull/2137 Fabian ``` BIP: ? Layer: Peer Services Title: P2P UTXO Set Sharing Authors: Fabian Jahr Status: Draft Type: Specification Assigned: ? Discussion: ? Version: 0.2.0 License: BSD-2-Clause ``` ## Abstract This BIP defines a P2P protocol extension for sharing full UTXO sets betwee= n peers. It introduces a new service bit `NODE_UTXO_SET`, four new P2P messages (`getutxotree`, `u= txotree`, `getutxoset`, `utxoset`), and a chunk-hash list anchored to a Merkle root known to the re= questing node, enabling per-chunk verification. This allows nodes to bootstrap from a recent height= by obtaining the required UTXO set directly from the P2P network via mechanisms such as assu= meutxo. ## Motivation The assumeutxo feature (implemented in Bitcoin Core) allows nodes to begin = operating from a serialized UTXO set while validating historical blocks in the background. However, there is currently no canonic= al source for obtaining this data. Users must either generate one themselves from a fully synced node (u= sing `dumptxoutset` in Bitcoin Core), or download one from a third party. By enabling UTXO set sharing over the P2P network, new nodes can obtain the= data directly from peers, removing the dependency on external infrastructure. ## Specification The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in this= document are to be interpreted as described in RFC 2119. ### Service Bit | Name | Bit | Description | |------|-----|-------------| | `NODE_UTXO_SET` | 12 (0x1000) | The node can serve complete UTXO set data= for at least one height. | A node MUST NOT set this bit unless it has at least one full UTXO set avail= able to serve. A node signaling `NODE_UTXO_SET` MUST be capable of responding to `getutxot= ree` and `getutxoset` requests for every UTXO set it is willing to serve, including the full chun= k-hash list and every chunk of those sets. ### Data Structures #### Serialized UTXO Set The serialized UTXO set uses the format established by the Bitcoin Core RPC= `dumptxoutset` (as of Bitcoin Core v31). **Header (55 bytes):** | Field | Type | Size | Description | |-------|------|------|-------------| | `magic` | `bytes` | 5 | `0x7574786fff` (ASCII `utxo` + `0xff`). | | `version` | `uint16_t` | 2 | Format version. | | `network_magic` | `bytes` | 4 | Network message start bytes. | | `base_height` | `uint32_t` | 4 | Block height of the UTXO set. | | `base_blockhash` | `uint256` | 32 | Block hash of the UTXO set. | | `coins_count` | `uint64_t` | 8 | Total number of coins (UTXOs) in the set= . | **Body (coin data):** Coins are grouped by transaction hash. For each group: | Field | Type | Size | Description | |-------|------|------|-------------| | `txid` | `uint256` | 32 | Transaction hash. | | `num_coins` | `compact_size` | 1=E2=80=939 | Number of outputs for this t= xid. | For each coin in the group: | Field | Type | Size | Description | |-------|------|------|-------------| | `vout_index` | `compact_size` | 1=E2=80=939 | Output index. | | `coin` | `Coin` | variable | Serialized coin (varint-encoded code for hei= ght/coinbase, then compressed txout). | Coins are ordered lexicographically by outpoint (txid, then vout index), ma= tching the LevelDB iteration order of the coins database. #### Chunk Merkle Tree The serialized UTXO set (header + body) is split into chunks of exactly 3,9= 00,000 bytes (3.9 MB). The last chunk contains the remaining bytes and may be smaller. The leaf hash for each chunk is `SHA256d(chunk_data)`. The tree is built as= a balanced binary tree. When the number of nodes at a level is odd, the last node is duplicated before h= ashing the next level. Interior nodes are computed as `SHA256d(left_child || right_child)`. The leaves are delivered to the node in a single `utxotree` response. A nod= e that knows the Merkle root for a given UTXO set checks a received list of leaves by re= computing the root and comparing. The Merkle root is the sole trust input required to verify the i= ntegrity of the received UTXO set. `SHA256d` denotes double-SHA256: `SHA256d(x) =3D SHA256(SHA256(x))`. ### Messages #### `getutxotree` Sent to request the chunk-hash list for a specific UTXO set. | Field | Type | Size | Description | |-------|------|------|-------------| | `block_hash` | `uint256` | 32 | Block hash identifying the requested UTXO= set. | A node that has advertised `NODE_UTXO_SET` and can serve the requested UTXO= set MUST respond with `utxotree`. If the serving node cannot fulfill the request, it MUST NOT res= pond. The requesting node SHOULD apply a reasonable timeout and try another peer. #### `utxotree` Sent in response to `getutxotree`, delivering the full chunk-hash list alon= g with per-snapshot metadata. | Field | Type | Size | Description | |-------|------|------|-------------| | `block_hash` | `uint256` | 32 | Block hash this data corresponds to. | | `version` | `uint16_t` | 2 | Format version of the serialized UTXO set. | | `data_length` | `uint64_t` | 8 | Total size of the serialized UTXO set in= bytes (header + body). | | `num_chunks` | `compact_size` | 1=E2=80=939 | Number of chunks the serial= ized UTXO set is split into. | | `chunk_hashes` | `uint256[]` | 32 =C3=97 `num_chunks` | The ordered list = of chunk hashes. | Upon receiving a `utxotree` message, the node MUST recompute the Merkle roo= t from `chunk_hashes` and compare it against the Merkle root it knows for the corr= esponding UTXO set. If the roots do not match, the node MUST discard the response and MUST disconn= ect the peer. #### `getutxoset` Sent to request a single chunk of UTXO set data. The requesting node MUST h= ave received a `utxotree` for the corresponding UTXO set before sending this message. | Field | Type | Size | Description | |-------|------|------|-------------| | `block_hash` | `uint256` | 32 | Block hash identifying the requested UTXO= set. | | `chunk_index` | `uint32_t` | 4 | Zero-based index of the requested chunk.= | If the serving node cannot fulfill the request, it MUST NOT respond. The re= questing node SHOULD apply a reasonable timeout and try another peer. #### `utxoset` Sent in response to `getutxoset`, delivering one chunk. | Field | Type | Size | Description | |-------|------|------|-------------| | `block_hash` | `uint256` | 32 | Block hash this data corresponds to. | | `chunk_index` | `uint32_t` | 4 | Zero-based index of this chunk. | | `data` | `bytes` | variable | Chunk payload, exactly 3.9 MB except for th= e last chunk. | The transfer is receiver-driven: the requesting node sends one `getutxoset`= per chunk. Chunks MAY be requested in any order and from different peers. Upon receiving a `utxoset` message, the node MUST compute `SHA256d(data)` a= nd compare it against `chunk_hashes[chunk_index]` from the `utxotree` it accepted for this UTXO s= et. If the hashes do not match, the node MUST discard the chunk and MUST disconnect the peer. A node= SHOULD also disconnect a peer that sends a `utxoset` message with fields (`chunk_index`, `block_ha= sh`) that do not match the outstanding request. After all chunks have been received, the node SHOULD parse the reassembled = UTXO set against the serialized UTXO set format to confirm it is well-formed. ### Protocol Flow 1. The requesting node identifies peers advertising `NODE_UTXO_SET`. 2. The requesting node sends `getutxotree` for the desired block hash to on= e or more of these peers. 3. Each peer responds with `utxotree`. The requesting node verifies the res= ponse by recomputing the Merkle root against a value it knows for the given UTXO set, either fro= m a trusted source or by selecting a root with agreement among multiple peers. 4. The requesting node downloads chunks via `getutxoset`/`utxoset` exchange= s, verifying each chunk against its entry in the accepted `utxotree` on receipt. On verification fa= ilure the peer is disconnected and download continues from another peer without losing alread= y-verified chunks. 5. After all chunks are received, the node parses the reassembled UTXO set = against the serialized UTXO set format to confirm that it is well-formed. Serving nodes are free to limit the number of concurrent and repeated trans= fers per peer at their own discretion to manage resource consumption. ## Rationale **Usage of service bit 12:** Service bits allow selective peer discovery th= rough DNS seeds and addr relay. Bit 12 is chosen as the next unassigned bit after= `NODE_P2P_V2` (bit 11, BIP 324). **Direct request model:** Peers signal availability of UTXO sets via the `N= ODE_UTXO_SET` service bit; the requesting node identifies the desired UTXO set by block h= ash when sending `getutxotree`. The serving node responds only if it can serve that specific= UTXO set. **Per-chunk verification:** The chunk-hash list returned in `utxotree` enab= les each chunk to be verified by direct lookup against the accepted list as it arrives, allowing immediat= e detection of corrupt data, peer switching without data loss, and parallel download from multiple peers= . The list itself is small (~80 KB for a ~10 GB set). The specified serialization is deterministic, so= all honest nodes produce byte-identical output, guaranteeing Merkle root agreement. **3.9 MB chunk size:** The number balances round trips (~2,560 for a ~10 GB= set) against memory usage for buffering and verifying a single chunk. Smaller chunks would increase p= rotocol overhead; larger chunks would increase memory pressure on constrained devices commonly used = to run Bitcoin nodes. Together with the additional message overhead, the `utxoset` message includ= ing the chunk data also sits just below the theoretical maximum block size which means any implemen= tation should be able to handle messages of this size. **Reusing the `dumptxoutset` format:** Avoids introducing a new serializati= on format and ensures compatibility with UTXO sets already being generated and shared. **Relationship to BIP 64:** BIP 64 defined a protocol for querying individu= al UTXOs by outpoint and is now closed. This BIP addresses a different use case: bulk transfer of the e= ntire UTXO set for node bootstrapping. ## Reference Implementation [Bitcoin Core implementation pull request](https://github.com/bitcoin/bitco= in/pull/35054) ## Copyright This BIP is made available under the terms of the 2-clause BSD license. See https://opensource.org/license/BSD-2-Clause for more information. ## Changelog * __0.2.0__ (2026-05-04): * Dropped discovery before download approach, instead request the chunk-has= h list via `getutxotree`/`utxotree` * Dropped per-chunk Merkle proofs; chunks verified directly against the chu= nk-hash list * Dropped `height` from requests (`block_hash` is the sole identifier); add= ed format `version` to `utxotree` * Dropped references to the serialized hash; the Merkle root is the sole in= tegrity check * __0.1.0__ (2026-04-10): * Initial draft --=20 You received this message because you are subscribed to the Google Groups "= Bitcoin Development Mailing List" group. To unsubscribe from this group and stop receiving emails from it, send an e= mail to bitcoindev+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/= CmNC-9nAajMwaJi6dHTdlpgjzLZazPlMjGAxNT8DJXAnyw2sUKCygJJU4BLqaF8OYw3carG_pt1= Rriqu66OG3wQ8u2itVlJCFo1AhI3V4es%3D%40protonmail.com. --b1=_x2wbsU61KNDvqIHhbCxYupZh5hf9jreoUaJxlQ8Rk Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Dear list,

I am sharing a BIP draft for sharing the UTXO set over= the P2P network, an old idea that makes it possible to utilize AssumeUTXO = without sourcing a UTXO set dump from a third party source. You can find th= e full text below or comment on the BIPs repository pull directly: https://github.com/bitcoin/bips/pull/2= 137

Fabian


```
  BIP: ?
  = Layer: Peer Services
  Title: P2P UTXO Set Shar= ing
  Authors: Fabian Jahr <fjahr@protonmail.com>
  Status: Draf= t
  Type: Specification
=   Assigned: ?
  Discussion: ?
=
  Version: 0.2.0
  License: BSD= -2-Clause
```

= ## Abstract

This BIP defines a P2P pr= otocol extension for sharing full UTXO sets between peers. It introduces
a new service bit `NODE_UTXO_SET`, four new P2P messag= es (`getutxotree`, `utxotree`, `getutxoset`,
`utxose= t`), and a chunk-hash list anchored to a Merkle root known to the requestin= g node, enabling
per-chunk verification. This allows= nodes to bootstrap from a recent height by obtaining the
= required UTXO set directly from the P2P network via mechanisms such a= s assumeutxo.

## Motivation

The assumeutxo feature (implemented in Bitcoin= Core) allows nodes to begin operating from a serialized
<= span>UTXO set while validating
historical blocks in = the background. However, there is currently no canonical source for obtaini= ng this
data. Users must either generate one themsel= ves from a fully synced node (using `dumptxoutset` in
Bitcoin Core), or download one from a third party.

=
By enabling UTXO set sharing over the P2P network, new nod= es can obtain the data directly from
peers, removing= the dependency on external infrastructure.

## Specification

The key words= "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in this document are= to be
interpreted as described in RFC 2119.<= /div>

### Service Bit

<= div>| Name | Bit | Description |
|------|-----= |-------------|
| `NODE_UTXO_SET` | 12 (0x1000) | Th= e node can serve complete UTXO set data for at least one height. |

A node MUST NOT set this bit unless it has at= least one full UTXO set available to serve.
A node = signaling `NODE_UTXO_SET` MUST be capable of responding to `getutxotree` an= d `getutxoset`
requests for every UTXO set it is wil= ling to serve, including the full chunk-hash list and every
chunk of those sets.

### Data= Structures

#### Serialized UTXO Set<= /span>

The serialized UTXO set uses the form= at established by the Bitcoin Core RPC `dumptxoutset` (as of Bitcoin Core v= 31).

**Header (55 bytes):**

| Field | Type | Size | Description |
|-------|------|------|-------------|
| `magic` | `bytes` | 5 | `0x7574786fff` (ASCII `utxo` + `0xff`). |=
| `version` | `uint16_t` | 2 | Format version. |
| `network_magic` | `bytes` | 4 | Network message start bytes= . |
| `base_height` | `uint32_t` | 4 | Block height = of the UTXO set. |
| `base_blockhash` | `uint256` | = 32 | Block hash of the UTXO set. |
| `coins_count` |= `uint64_t` | 8 | Total number of coins (UTXOs) in the set. |
<= div>
**Body (coin data):**

<= div>Coins are grouped by transaction hash. For each group:

| Field | Type | Size | Description |
|-------|------|------|-------------|
= | `txid` | `uint256` | 32 | Transaction hash. |
| `n= um_coins` | `compact_size` | 1=E2=80=939 | Number of outputs for this txid.= |

For each coin in the group:=

| Field | Type | Size | Description |
|-------|------|------|-------------|
| `vout_index` | `compact_size` | 1=E2=80=939 | Output index. |<= /div>
| `coin` | `Coin` | variable | Serialized coin (varint-enco= ded code for height/coinbase, then compressed txout). |
Coins are ordered lexicographically by outpoint (txid, t= hen vout index), matching the LevelDB iteration
orde= r of the coins database.

#### Chunk M= erkle Tree

The serialized UTXO set (h= eader + body) is split into chunks of exactly 3,900,000 bytes (3.9 MB). The=
last chunk contains the remaining bytes and may be = smaller.

The leaf hash for each chunk= is `SHA256d(chunk_data)`. The tree is built as a balanced binary tree. Whe= n
the number of nodes at a level is odd, the last no= de is duplicated before hashing the next level.
Inte= rior nodes are computed as `SHA256d(left_child || right_child)`.

The leaves are delivered to the node in a singl= e `utxotree` response. A node that knows
the Merkle = root for a given UTXO set checks a received list of leaves by recomputing t= he root and
comparing. The Merkle root is the sole t= rust input required to verify the integrity of the received UTXO set.

`SHA256d` denotes double-SHA256: `SHA256d(= x) =3D SHA256(SHA256(x))`.

### Messag= es

#### `getutxotree`

Sent to request the chunk-hash list for a specific U= TXO set.

| Field | Type | Size | Desc= ription |
|-------|------|------|-------------|
| `block_hash` | `uint256` | 32 | Block hash identifying= the requested UTXO set. |

A node tha= t has advertised `NODE_UTXO_SET` and can serve the requested UTXO set MUST = respond with
`utxotree`. If the serving node cannot = fulfill the request, it MUST NOT respond. The requesting
<= span>node SHOULD apply a reasonable timeout and try another peer.

#### `utxotree`

Sent in response to `getutxotree`, delivering the full chunk-hash l= ist along with per-snapshot
metadata.

| Field | Type | Size | Description |
<= div>|-------|------|------|-------------|
| `b= lock_hash` | `uint256` | 32 | Block hash this data corresponds to. |=
| `version` | `uint16_t` | 2 | Format version of the seria= lized UTXO set. |
| `data_length` | `uint64_t` | 8 |= Total size of the serialized UTXO set in bytes (header + body). |
| `num_chunks` | `compact_size` | 1=E2=80=939 | Number of ch= unks the serialized UTXO set is split into. |
| `chu= nk_hashes` | `uint256[]` | 32 =C3=97 `num_chunks` | The ordered list of chu= nk hashes. |

Upon receiving a `utxotr= ee` message, the node MUST recompute the Merkle root from
= `chunk_hashes` and compare it against the Merkle root it knows for th= e corresponding UTXO set. If
the roots do not match,= the node MUST discard the response and MUST disconnect the peer.

#### `getutxoset`

<= div>Sent to request a single chunk of UTXO set data. The requesting n= ode MUST have received a `utxotree`
for the correspo= nding UTXO set before sending this message.

| Field | Type | Size | Description |
|------= -|------|------|-------------|
| `block_hash` | `uin= t256` | 32 | Block hash identifying the requested UTXO set. |
<= div>| `chunk_index` | `uint32_t` | 4 | Zero-based index of the reques= ted chunk. |

If the serving node cann= ot fulfill the request, it MUST NOT respond. The requesting node SHOULD app= ly
a reasonable timeout and try another peer.=

#### `utxoset`

<= div>Sent in response to `getutxoset`, delivering one chunk.

| Field | Type | Size | Description |
|-------|------|------|-------------|
| `block_hash` | `uint256` | 32 | Block hash this data corresponds to. |
| `chunk_index` | `uint32_t` | 4 | Zero-based index o= f this chunk. |
| `data` | `bytes` | variable | Chun= k payload, exactly 3.9 MB except for the last chunk. |
The transfer is receiver-driven: the requesting node send= s one `getutxoset` per chunk. Chunks MAY be
requeste= d in any order and from different peers.

Upon receiving a `utxoset` message, the node MUST compute `SHA256d(data= )` and compare it against
`chunk_hashes[chunk_index]= ` from the `utxotree` it accepted for this UTXO set. If the hashes do not
match, the node MUST discard the chunk and MUST disco= nnect the peer. A node SHOULD also disconnect
a peer= that sends a `utxoset` message with fields (`chunk_index`, `block_hash`) t= hat do not match
the outstanding request.

After all chunks have been received, the node S= HOULD parse the reassembled UTXO set against the
ser= ialized UTXO set format to confirm it is well-formed.

=
### Protocol Flow

1.= The requesting node identifies peers advertising `NODE_UTXO_SET`.
2. The requesting node sends `getutxotree` for the desired b= lock hash to one or more of these peers.
3. Each pee= r responds with `utxotree`. The requesting node verifies the response by re= computing
   the Merkle root against a val= ue it knows for the given UTXO set, either from a trusted source
   or by selecting a root with agreement among multi= ple peers.
4. The requesting node downloads chunks v= ia `getutxoset`/`utxoset` exchanges, verifying each chunk
=    against its entry in the accepted `utxotree` on receipt.= On verification failure the peer is
   di= sconnected and download continues from another peer without losing already-= verified chunks.
5. After all chunks are received, t= he node parses the reassembled UTXO set against the serialized
=
   UTXO set format to confirm that it is well-formed.<= /span>

Serving nodes are free to limit the n= umber of concurrent and repeated transfers per peer at their own
discretion to manage resource consumption.
## Rationale

**Usa= ge of service bit 12:** Service bits allow selective peer discovery through=
DNS seeds and addr relay. Bit 12 is chosen as the n= ext unassigned bit after `NODE_P2P_V2` (bit 11, BIP 324).
=
**Direct request model:** Peers signal availability of= UTXO sets via the `NODE_UTXO_SET`
service bit; the = requesting node identifies the desired UTXO set by block hash when sending<= /span>
`getutxotree`. The serving node responds only if it = can serve that specific UTXO set.

**P= er-chunk verification:** The chunk-hash list returned in `utxotree` enables= each chunk to be verified
by direct lookup against = the accepted list as it arrives, allowing immediate detection of corrupt da= ta,
peer switching without data loss, and parallel d= ownload from multiple peers. The list itself is small
(~80 KB for a ~10 GB set). The specified serialization is deterministic, = so all honest nodes produce
byte-identical output, g= uaranteeing Merkle root agreement.

**= 3.9 MB chunk size:** The number balances round trips (~2,560 for a ~10 GB s= et) against memory usage
for buffering and verifying= a single chunk. Smaller chunks would increase protocol overhead; larger
chunks would increase memory pressure on constrained d= evices commonly used to run Bitcoin nodes.
Together = with the additional message overhead, the `utxoset` message including the c= hunk data also
sits just below the theoretical maxim= um block size which means any implementation should be able to
=
handle messages of this size.

**Reusing the `dumptxoutset` format:** Avoids introducing a new seriali= zation format and ensures
compatibility with UTXO se= ts already being generated and shared.

**Relationship to BIP 64:** BIP 64 defined a protocol for querying indivi= dual UTXOs by outpoint and is
now closed. This BIP a= ddresses a different use case: bulk transfer of the entire UTXO set for nod= e
bootstrapping.

## Reference Implementation

[Bitco= in Core implementation pull request](= https://github.com/bitcoin/bitcoin/pull/35054)

## Copyright

This BIP i= s made available under the terms of the 2-clause BSD license. See

## Changelog

* __0.2.0__ (202= 6-05-04):
    * Dropped discovery before d= ownload approach, instead request the chunk-hash list via `getutxotree`/`ut= xotree`
    * Dropped per-chunk Merkle pro= ofs; chunks verified directly against the chunk-hash list
=     * Dropped `height` from requests (`block_hash` is the s= ole identifier); added format `version` to `utxotree`
    * Dropped references to the serialized hash; the Merkle roo= t is the sole integrity check
* __0.1.0__ (2026-04-1= 0):
    * Initial draft

--
You received this message because you are subscribed to the Google Groups &= quot;Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an e= mail to bitcoind= ev+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/= CmNC-9nAajMwaJi6dHTdlpgjzLZazPlMjGAxNT8DJXAnyw2sUKCygJJU4BLqaF8OYw3carG_pt1= Rriqu66OG3wQ8u2itVlJCFo1AhI3V4es%3D%40protonmail.com.
--b1=_x2wbsU61KNDvqIHhbCxYupZh5hf9jreoUaJxlQ8Rk--