diff --git a/history/history-network.md b/history/history-network.md index 27a01873..9ecd06cc 100644 --- a/history/history-network.md +++ b/history/history-network.md @@ -16,9 +16,9 @@ In addition, the chain history network provides individual epoch records for the - Block headers - Block bodies - - Transactions - - Ommers - - Withdrawals + - Transactions + - Ommers + - Withdrawals - Receipts - Header epoch records (pre-merge only) @@ -31,7 +31,7 @@ The network supports the following mechanisms for data retrieval: - Block receipts by block header hash - Header epoch record by epoch record hash -> The presence of the pre-merge header records provides an indirect way to lookup blocks by their number, but is restricted to pre-merge blocks. Retrieval of blocks by their number for post-merge blocks is not intrinsically supported within this network. +> The presence of the pre-merge header records provides an indirect way to lookup blocks by their number, but is restricted to pre-merge blocks. Retrieval of blocks by their number for post-merge blocks is not intrinsically supported within this network. > > This sub-protocol does **not** support retrieval of transactions by hash, only the full set of transactions for a given block. See the "Canonical Transaction Index" sub-protocol of the Portal Network for more information on how the portal network implements lookup of transactions by their individual hashes. @@ -79,7 +79,7 @@ The history network uses the standard routing table structure from the Portal Wi #### Data Radius -The history network includes one additional piece of node state that should be tracked. Nodes must track the `data_radius` from the Ping and Pong messages for other nodes in the network. This value is a 256 bit integer and represents the data that a node is "interested" in. We define the following function to determine whether node in the network should be interested in a piece of content. +The history network includes one additional piece of node state that should be tracked. Nodes must track the `data_radius` from the Ping and Pong messages for other nodes in the network. This value is a 256 bit integer and represents the data that a node is "interested" in. We define the following function to determine whether node in the network should be interested in a piece of content. ```python interested(node, content) = distance(node.id, content.id) <= node.radius @@ -112,7 +112,7 @@ MAX_RECEIPT_LENGTH = 2**27 # ~= 134 million # Maximum receipt length is logging a bunch of data out, currently at a cost of # 8 gas per byte. Since that is double the cost of 0 calldata bytes, the # maximum size is roughly half that of the transaction: 3.75 million bytes. -# But there is more reason for protocol devs to constrain the transaction length, +# But there is more reason for protocol developers to constrain the transaction length, # and it's not clear what the practical limits for receipts are, so we should add more buffer room. # Imagine the cost drops by 2x and the block gas limit goes up by 8x. So we add 2**4 = 16x buffer. @@ -181,10 +181,10 @@ content_key = selector + SSZ.serialize(block_header_key) ``` > **_Note:_** The `BlockHeaderProof` allows to provide headers without a proof (`None`). -For pre-merge headers, clients SHOULD NOT accept headers without a proof -as there is the `HistoricalHashesAccumulatorProof` solution available. -For post-merge headers, there is currently no proof solution and clients MAY -accept headers without a proof. +> For pre-merge headers, clients SHOULD NOT accept headers without a proof +> as there is the `HistoricalHashesAccumulatorProof` solution available. +> For post-merge headers, there is currently no proof solution and clients MAY +> accept headers without a proof. #### Block Body @@ -276,7 +276,7 @@ content_key = selector + SSZ.serialize(epoch_record_key) #### The "Historical Hashes Accumulator" -The "Historical Hashes Accumulator" is based on the [double-batched merkle log accumulator](https://ethresear.ch/t/double-batched-merkle-log-accumulator/571) that is currently used in the beacon chain. This data structure is designed to allow nodes in the network to "forget" the deeper history of the chain, while still being able to reliably receive historical headers with a proof that the received header is indeed from the canonical chain (as opposed to an uncle mined at the same block height). This data structure is only used for pre-merge blocks. +The "Historical Hashes Accumulator" is based on the [double-batched merkle log accumulator](https://ethresear.ch/t/double-batched-merkle-log-accumulator/571) that is currently used in the beacon chain. This data structure is designed to allow nodes in the network to "forget" the deeper history of the chain, while still being able to reliably receive historical headers with a proof that the received header is indeed from the canonical chain (as opposed to an uncle mined at the same block height). This data structure is only used for pre-merge blocks. The accumulator is defined as an [SSZ](https://ssz.dev/) data structure with the following schema: @@ -298,7 +298,6 @@ HistoricalHashesAccumulator = Container[ The algorithm for building the accumulator is as follows. - ```python def update_accumulator(accumulator: HistoricalHashesAccumulator, new_block_header: BlockHeader) -> None: # get the previous total difficulty @@ -322,9 +321,8 @@ def update_accumulator(accumulator: HistoricalHashesAccumulator, new_block_heade accumulator.current_epoch.append(header_record) ``` - The `HistoricalHashesAccumulator` is fully build and frozen when the last block before TheMerge/Paris fork is added and the last incomplete `EpochRecord` its `hash_tree_root` is added to the `historical_epochs`. -The network provides no mechanism for acquiring the fully build `HistoricalHashesAccumulator`. Clients are encouraged to solve this however they choose, with the suggestion that they include a frozen copy of the accumulator at the point of TheMerge within their client code, and provide a mechanism for users to override this value if they so choose. The `hash_tree_root` of the `HistoricalHashesAccumulator` is +The network provides no mechanism for acquiring the fully build `HistoricalHashesAccumulator`. Clients are encouraged to solve this however they choose, with the suggestion that they include a frozen copy of the accumulator at the point of TheMerge within their client code, and provide a mechanism for users to override this value if they so choose. The `hash_tree_root` of the `HistoricalHashesAccumulator` is defined in [EIP-7643](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7643.md). #### HistoricalHashesAccumulatorProof diff --git a/implementation-details-overlay.md b/implementation-details-overlay.md index dc252e13..adc8255b 100644 --- a/implementation-details-overlay.md +++ b/implementation-details-overlay.md @@ -198,7 +198,7 @@ Each bucket is limited to `K` total members ### D.3.d - Replacement cache -Each bucket maintains a set of additional nodes known to be at the appropriate distance. When a node is removed from the routing table it is replaced by a node from the replacement cache when one is available. The cache is managed such that it remains disjoint from the nodes in the corresponding bucket. +Each bucket maintains a set of additional nodes known to be at the appropriate distance. When a node is removed from the routing table it is replaced by a node from the replacement cache when one is available. The cache is managed such that it remains disjoint from the nodes in the corresponding bucket. ## D.4 - Retrieve nodes at specified log-distance @@ -218,7 +218,7 @@ The client uses a set of bootnodes to acquire an initial view of the network. ### E.1.a - Bootnodes -Each supported sub protocol can have its own set of bootnodes. These records can be either hard coded into the client or provided via client configuration. +Each supported sub protocol can have its own set of bootnodes. These records can be either hard coded into the client or provided via client configuration. ## E.2 - Population of routing table @@ -226,7 +226,7 @@ The client actively seeks to populate its routing table by performing [RFN](#TOD ## E.3 - Liveliness checks -The client tracks *liveliness* of nodes in its routing table and periodically checks the liveliness of the node in its routing table which was least recently checked. +The client tracks _liveliness_ of nodes in its routing table and periodically checks the liveliness of the node in its routing table which was least recently checked. ### E.3.a - Rate Limiting Liviliness Checks @@ -238,7 +238,7 @@ Management of stored content. ## F.1 - Content can be stored -Content can be stored in a persistent database. Databases are segmented by sub protocol. +Content can be stored in a persistent database. Databases are segmented by sub protocol. ## F.2 - Content can be retrieved by `content_id` @@ -248,12 +248,10 @@ Given a known `content_id` the corresponding content payload can be retrieved. Content can be removed. - ## F.4 - Query furthest by distance Retrieval of the content from the database which is furthest from a provided `node_id` using the custom distance function. - ## F.5 - Total size of stored content Retrieval of the total number of bytes stored. @@ -274,7 +272,7 @@ The ability to listening for an inbound connection from another node with a `con ## G.2 - Enforcement of maximum stored content size -When the total size of stored content exceeds the configured maximum content storage size the content which is furthest from the local `node_id` is evicted in a timely manner. This should also result in any "data radius" values relevant to this network being adjusted. +When the total size of stored content exceeds the configured maximum content storage size the content which is furthest from the local `node_id` is evicted in a timely manner. This should also result in any "data radius" values relevant to this network being adjusted. ## G.3 - Retrieval via FINDCONTENT/FOUNDCONTENT & uTP @@ -298,13 +296,13 @@ Support for receipt of content using the OFFER/ACCEPT messages and uTP sub proto ### G.4.a - Handle incoming gossip -Client can listen for incoming OFFER messages, responding with an ACCEPT message for any offered content which is of interest to the client. +Client can listen for incoming OFFER messages, responding with an ACCEPT message for any offered content which is of interest to the client. #### G.4.a.1 - Receipt via uTP After sending an ACCEPT response to an OFFER request the client listens for an inbound uTP stream with the `connection-id` that was sent with the ACCEPT response. -### G.4.b - Neighborhood Gossip Propogation +### G.4.b - Neighborhood Gossip Propagation Upon receiving and validating gossip content, the content should then be gossiped to some set of interested nearby peers. @@ -312,14 +310,12 @@ Upon receiving and validating gossip content, the content should then be gossipe Upon receiving an ACCEPT message in response to our own OFFER message the client can initiate a uTP stream with the other node and can send the content payload across the stream. - ## G.5 - Serving Content The client should listen for FINDCONTENT messages. When a FINDCONTENT message is received either the requested content or the nodes known to be closest to the content are returned via a FOUNDCONTENT message. - # H - JSON-RPC Endpoints that require for the portal network wire protocol. diff --git a/portal-wire-protocol.md b/portal-wire-protocol.md index 300602ff..abd6c68a 100644 --- a/portal-wire-protocol.md +++ b/portal-wire-protocol.md @@ -41,18 +41,17 @@ Currently defined `angelfood` protocol identifiers: ## Nodes and Node IDs -Nodes in the portal network are represented by their [EIP-778 Ethereum Node Record (ENR)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-778.md) from the Discovery v5 network. A node's `node-id` is derived according to the node's identity scheme, which is specified in the node's ENR. A node's `node-id` represents its address in the DHT. Node IDs are interchangeable between 32 byte identifiers and 256 bit integers. - +Nodes in the portal network are represented by their [EIP-778 Ethereum Node Record (ENR)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-778.md) from the Discovery v5 network. A node's `node-id` is derived according to the node's identity scheme, which is specified in the node's ENR. A node's `node-id` represents its address in the DHT. Node IDs are interchangeable between 32 byte identifiers and 256 bit integers. ## Content Keys and Content IDs Content keys are used to request or offer specific content data. As such the content key and content data can be thought of as a key-value pair with nodes in the network storing the content data and the content key being the identifier used to request and retrieve the data. -Each sub-protocol defines the set of supported content keys and the corresponding data payloads for each content key. The encoding of content keys is defined at the sub-protocol level. +Each sub-protocol defines the set of supported content keys and the corresponding data payloads for each content key. The encoding of content keys is defined at the sub-protocol level. Content keys are passed in their encoded format as byte strings to the messages defined in the Portal wire protocol. -Content IDs are derived from the content key. The Content ID can be represented interchangeably as either a 32 byte value or a 256 bit integer. The Content ID defines the address of the content in the DHT. The function for deriving the Content ID from a content key is defined at the sub-protocol level. +Content IDs are derived from the content key. The Content ID can be represented interchangeably as either a 32 byte value or a 256 bit integer. The Content ID defines the address of the content in the DHT. The function for deriving the Content ID from a content key is defined at the sub-protocol level. ### SHA256 Content ID Derivation Function @@ -66,8 +65,7 @@ content_id = sha256(encoded_content_key) The transmission of data that is too large to fit a single packet is done using [uTP](../assets/eip-7718/bep_0029-rst_post.pdf). -> The Portal wire protocol currently implements uTP over the `TALKREQ/TALKRESP` messages. Future plans are to move to the [sub-protocol data transmission](https://github.com/ethereum/devp2p/issues/229) in order to use a protocol native mechanism for establishing packet streams between clients. - +> The Portal wire protocol currently implements uTP over the `TALKREQ/TALKRESP` messages. Future plans are to move to the [sub-protocol data transmission](https://github.com/ethereum/devp2p/issues/229) in order to use a protocol native mechanism for establishing packet streams between clients. ## Request - Response Messages @@ -95,7 +93,7 @@ The transmission of `content` data that is too large to fit a single packet is d #### Ping (0x00) -Request message to check if a node is reachable, communicate basic information about our node, and request basic information about the recipient node. Additionally sub-protocol can define a schema for the `custom_payload` field to exchange additional information. +Request message to check if a node is reachable, communicate basic information about our node, and request basic information about the recipient node. Additionally sub-protocol can define a schema for the `custom_payload` field to exchange additional information. ``` selector = 0x00 @@ -127,8 +125,8 @@ find_nodes = Container(distances: List[uint16, limit=256]) ``` - `distances`: a sorted list of distances for which the node is requesting ENR records for. - - Each distance **MUST** be within the inclusive range `[0, 256]` - - Each distance in the list **MUST** be unique. + - Each distance **MUST** be within the inclusive range `[0, 256]` + - Each distance in the list **MUST** be unique. #### Nodes (0x03) @@ -141,9 +139,9 @@ nodes = Container(total: uint8, enrs: List[ByteList[2048], limit=32]) - `total`: The total number of `Nodes` response messages being sent. Currently fixed to only 1 response message. - `enrs`: List of byte strings, each of which is an RLP encoded ENR record. - * Individual ENR records **MUST** correspond to one of the requested distances. - * It is invalid to return multiple ENR records for the same `node_id`. - * The ENR record of the requesting node **SHOULD** be filtered out of the list. + - Individual ENR records **MUST** correspond to one of the requested distances. + - It is invalid to return multiple ENR records for the same `node_id`. + - The ENR record of the requesting node **SHOULD** be filtered out of the list. #### Find Content (0x04) @@ -160,7 +158,7 @@ find_content = Container(content_key: ByteList[2048]) Response message to Find Content (0x04). -This message can contain any of +This message can contain any of - a uTP connection ID - the requested content @@ -172,19 +170,19 @@ content = Union[connection_id: Bytes2, content: ByteList[2048], enrs: List[ ``` - `connection_id`: Connection ID to set up a uTP stream to transmit the requested data. - - Connection ID values **SHOULD** be randomly generated. + - Connection ID values **SHOULD** be randomly generated. - `content`: byte string of the requested content. - - This field **MUST** be used when the requested data can fit in this single response. + - This field **MUST** be used when the requested data can fit in this single response. - `enrs`: List of byte strings, each of which is an RLP encoded ENR record. - - The list of ENR records **MUST** be closest nodes to the requested content that the responding node has stored. - - The set of derived `node_id` values from the ENR records **MUST** be unique. - - The ENR record of the requesting & responding node **SHOULD** be filtered out of the list. + - The list of ENR records **MUST** be closest nodes to the requested content that the responding node has stored. + - The set of derived `node_id` values from the ENR records **MUST** be unique. + - The ENR record of the requesting & responding node **SHOULD** be filtered out of the list. If the node does not hold the requested content, and the node does not know of any nodes with eligible ENR values, then the node **MUST** return `enrs` as an empty list. -Upon *sending* this message with a `connection_id`, the sending node **SHOULD** *listen* for an incoming uTP stream with the generated `connection_id`. +Upon _sending_ this message with a `connection_id`, the sending node **SHOULD** _listen_ for an incoming uTP stream with the generated `connection_id`. -Upon *receiving* this message with a `connection_id`, the receiving node **SHOULD** *initiate* a uTP stream with the received `connection_id`. +Upon _receiving_ this message with a `connection_id`, the receiving node **SHOULD** _initiate_ a uTP stream with the received `connection_id`. ##### `content` Union Definition @@ -234,13 +232,13 @@ accept = Container(connection_id: Bytes2, content_keys: BitList[limit=64]] ``` - `connection_id`: Connection ID to set up a uTP stream to transmit the requested data. - - ConnectionID values **SHOULD** be randomly generated. + - ConnectionID values **SHOULD** be randomly generated. - `content_keys`: Signals which content keys are desired. - - A bit-list corresponding to the offered keys with the bits in the positions of the desired keys set to `1`. + - A bit-list corresponding to the offered keys with the bits in the positions of the desired keys set to `1`. -Upon *sending* this message, the requesting node **SHOULD** *listen* for an incoming uTP stream with the generated `connection_id`. +Upon _sending_ this message, the requesting node **SHOULD** _listen_ for an incoming uTP stream with the generated `connection_id`. -Upon *receiving* this message, the serving node **SHOULD** *initiate* a uTP stream with the received `connection_id`. +Upon _receiving_ this message, the serving node **SHOULD** _initiate_ a uTP stream with the received `connection_id`. ##### Content Encoding @@ -287,7 +285,6 @@ logdistance(a: uint256, b: uint256) = log2(distance(a, b)) A collection of test vectors for this specification can be found in the [Portal wire test vectors](./portal-wire-test-vectors.md) document. - ## Routing Table Sub-networks that use the Portal Wire Protocol will form an independent overlay DHT which requires nodes to maintain a separate routing table from the one used in the base Discv5 protocol. @@ -323,14 +320,12 @@ port := UDP port number ### Protocol Specific Node State -Sub protocols may define additional node state information which should be tracked in the node state database. This information will typically be transmitted in the `Ping.custom_data` and `Pong.custom_data` fields. - +Sub protocols may define additional node state information which should be tracked in the node state database. This information will typically be transmitted in the `Ping.custom_data` and `Pong.custom_data` fields. ## Algorithms Here we define a collection of generic algorithms which can be applied to a sub-protocol implementing the wire protocol. - ### Lookup The term lookup refers to the lookup algorithm described in section 2.3 of the Kademlia paper. @@ -359,9 +354,10 @@ To find a piece of content for `content-id`, a node performs a content lookup vi ### Storing Content -The concept of content storage is only applicable to sub-protocols that implement persistant storage of data. +The concept of content storage is only applicable to sub-protocols that implement persistent storage of data. Content will get stored by a node when: + - the node receives the content through the `Offer` - `Accept` message flow and the content falls within the node's radius - the node requests content through the `FindContent` - `Content` message flow and the content falls within the node's radius @@ -369,7 +365,7 @@ The network cannot make guarantees about the storage of particular content. A la ### Neighborhood Gossip -We use the term *neighborhood gossip* to refer to the process through which content is disseminated to all of the DHT nodes *near* the location in the DHT where the content is located. +We use the term _neighborhood gossip_ to refer to the process through which content is disseminated to all of the DHT nodes _near_ the location in the DHT where the content is located. The process works as follows: @@ -377,7 +373,7 @@ The process works as follows: - This DHT node checks their routing table for `k` nearby DHT nodes that should also be interested in the content. Those `k` nodes **SHOULD** not include the node that originally provided aformentioned content. - If the DHT node finds `n` or more DHT nodes interested it selects `n` of these nodes and offers the content to them. - If the DHT node finds less than `n` DHT nodes interested, it launches a node lookup with target `content-id` and it -offers the content to maximum `n` of the newly discovered nodes. + offers the content to maximum `n` of the newly discovered nodes. The process above should quickly saturate the area of the DHT where the content is located and naturally terminate as more nodes become aware of the content. diff --git a/portal-wire-test-vectors.md b/portal-wire-test-vectors.md index e6e0efc0..0fd78780 100644 --- a/portal-wire-test-vectors.md +++ b/portal-wire-test-vectors.md @@ -12,6 +12,7 @@ primarily verify the SSZ encoding and decoding of each protocol message. ### Ping Request #### Input Parameters + ``` enr_seq = 1 data_radius = 2^256 - 2 # Maximum value - 1 @@ -19,6 +20,7 @@ custom_payload = serialize(Container(data_radius)) ``` #### Expected Output + ``` message = 0x0001000000000000000c000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ``` @@ -26,6 +28,7 @@ message = 0x0001000000000000000c000000feffffffffffffffffffffffffffffffffffffffff ### Pong Response #### Input Parameters + ``` enr_seq = 1 data_radius = (2^256 - 1) / 2 # Maximum value / 2 @@ -33,6 +36,7 @@ custom_payload = serialize(Container(data_radius)) ``` #### Expected Output + ``` message = 0x0101000000000000000c000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f ``` @@ -40,11 +44,13 @@ message = 0x0101000000000000000c000000ffffffffffffffffffffffffffffffffffffffffff ### Find Nodes Request #### Input Parameters + ``` distances = [256, 255] ``` #### Expected Output + ``` message = 0x02040000000001ff00 ``` @@ -52,12 +58,14 @@ message = 0x02040000000001ff00 ### Nodes Response - Empty enrs #### Input Parameters + ``` total = 1 enrs = [] ``` #### Expected Output + ``` message = 0x030105000000 ``` @@ -65,6 +73,7 @@ message = 0x030105000000 ### Nodes Response - Multiple enrs #### Input Parameters + ``` enr1 = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg" @@ -75,6 +84,7 @@ enrs = [enr1, enr2] ``` #### Expected Output + ``` message = 0x030105000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235 ``` @@ -82,11 +92,13 @@ message = 0x030105000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc87191 ### Find Content Request #### Input Parameters + ``` content_key = 0x706f7274616c ``` #### Expected Output + ``` message = 0x0404000000706f7274616c ``` @@ -94,11 +106,13 @@ message = 0x0404000000706f7274616c ### Content Response - Connection id #### Input Parameters + ``` connection_id = [0x01, 0x02] ``` #### Expected Output + ``` message = 0x05000102 ``` @@ -106,11 +120,13 @@ message = 0x05000102 ### Content Response - Content payload #### Input Parameters + ``` content = 0x7468652063616b652069732061206c6965 ``` #### Expected Output + ``` message = 0x05017468652063616b652069732061206c6965 ``` @@ -118,6 +134,7 @@ message = 0x05017468652063616b652069732061206c6965 ### Content Response - Multiple enrs #### Input Parameters + ``` enr1 = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg" @@ -127,6 +144,7 @@ enrs = [enr1, enr2] ``` #### Expected Output + ``` message = 0x0502080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235 ``` @@ -134,12 +152,14 @@ message = 0x0502080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d3 ### Offer Request #### Input Parameters + ``` content_key1 = 0x010203 content_keys = [content_key1] ``` #### Expected Output + ``` message = 0x060400000004000000010203 ``` @@ -147,12 +167,14 @@ message = 0x060400000004000000010203 ### Accept Response #### Input Parameters + ``` connection_id = [0x01, 0x02] content_keys = [1, 0, 0, 0, 0, 0, 0, 0] # 8 bits bitlist, 0 bit set = byte 0x01 ``` #### Expected Output + ``` message = 0x070102060000000101 ``` diff --git a/state/state-network.md b/state/state-network.md index 410ca850..63539dff 100644 --- a/state/state-network.md +++ b/state/state-network.md @@ -6,7 +6,7 @@ This document is the specification for the sub-protocol that supports on-demand The execution state network is a [Kademlia](https://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf) DHT that uses the [Portal Wire Protocol](./portal-wire-protocol.md) to establish an overlay network on top of the [Discovery v5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire.md) protocol. -State data from the execution chain consists of all account data from the main storage trie, all contract storage data from all of the individual contract storage tries, and the individual bytecodes for all contracts across all historical state roots. This is traditionally referred to as an "archive node". +State data from the execution chain consists of all account data from the main storage trie, all contract storage data from all of the individual contract storage tries, and the individual bytecodes for all contracts across all historical state roots. This is traditionally referred to as an "archive node". ### Data @@ -19,7 +19,6 @@ The network stores the full execution layer state which encompasses the followin The network is implemented as an "archive" node meaning that it stores all tries for all historical blocks. - #### Retrieval - Account trie nodes by their node hash. @@ -34,7 +33,6 @@ tries for all historical blocks. The state network uses the stock XOR distance metric defined in the portal wire protocol specification. - ### Content ID Derivation Function The state network uses the SHA256 Content ID derivation function from the portal wire protocol specification. @@ -75,7 +73,7 @@ The execution state network uses the standard routing table structure from the P #### Data Radius -The execution state network includes one additional piece of node state that should be tracked. Nodes must track the `data_radius` from the Ping and Pong messages for other nodes in the network. This value is a 256 bit integer and represents the data that a node is "interested" in. We define the following function to determine whether node in the network should be interested in a piece of content. +The execution state network includes one additional piece of node state that should be tracked. Nodes must track the `data_radius` from the Ping and Pong messages for other nodes in the network. This value is a 256 bit integer and represents the data that a node is "interested" in. We define the following function to determine whether node in the network should be interested in a piece of content. ``` interested(node, content) = distance(node.id, content.id) <= node.radius @@ -91,16 +89,15 @@ A node should track their own radius value and provide this value in all Ping or The data payloads for many content types in the state network differ between OFFER/ACCEPT and FINDCONTENT/FOUNDCONTENT. -The OFFER/ACCEPT payloads need to be provable by their recipients. These proofs are useful during OFFER/ACCEPT because they verify that the offered data is indeed part of the canonical chain. - -The FINDCONTENT/FOUNDCONTENT payloads do not contain proofs because a piece of state can exist under many different state roots. All payloads can still be proved to be the correct requested data, however, it is the responsibility of the requesting party to anchor the returned data as canonical chain state data. +The OFFER/ACCEPT payloads need to be provable by their recipients. These proofs are useful during OFFER/ACCEPT because they verify that the offered data is indeed part of the canonical chain. +The FINDCONTENT/FOUNDCONTENT payloads do not contain proofs because a piece of state can exist under many different state roots. All payloads can still be proved to be the correct requested data, however, it is the responsibility of the requesting party to anchor the returned data as canonical chain state data. #### Helper Data Types ##### Paths (Nibbles) -A naive approach to storage of trie nodes would be to simply use the `node_hash` value of the trie node for storage. This scheme however results in stored data not being tied in any direct way to it's location in the trie. In a situation where a participant in the DHT wished to re-gossip data that they have stored, they would need to reconstruct a valid trie proof for that data in order to construct the appropriate OFFER/ACCEPT payload. We include the `path` metadata in state network content keys so that it is possible to reconstruct this proof. +A naive approach to storage of trie nodes would be to simply use the `node_hash` value of the trie node for storage. This scheme however results in stored data not being tied in any direct way to it's location in the trie. In a situation where a participant in the DHT wished to re-gossip data that they have stored, they would need to reconstruct a valid trie proof for that data in order to construct the appropriate OFFER/ACCEPT payload. We include the `path` metadata in state network content keys so that it is possible to reconstruct this proof. We define path as a sequences of "nibbles" which represent the path through the merkle patritia trie (MPT) to reach the trie node. @@ -141,7 +138,7 @@ AddressHash := Bytes32 Merkle Patricia Trie (MPT) proofs consist of a list of `TrieNode` objects that correspond to individual trie nodes from the MPT. Each node can be one of the different node types from the MPT -(e.g. branch, extension, leaf). When serialized, each `TrieNode` is represented as an RLP +(e.g. branch, extension, leaf). When serialized, each `TrieNode` is represented as an RLP serialized list of the component elements. The largest possible node type is the branch node, which should be up to 532 bytes (16 child nodes of `Bytes32` with extra encoding info), but we specify 1024 bytes to be on the safe side. @@ -164,11 +161,11 @@ All `TrieProof` objects are subject to the following validity requirements. ###### A: Lexical Ordering The sequence of nodes in the proof MUST represent the unbroken path in a trie, starting from the -root node and each node being the child of its predecesor. This results in the state root node -always occuring first and node being proven last in the list of trie nodes. +root node and each node being the child of its predecessor. This results in the state root node +always occurring first and node being proven last in the list of trie nodes. -> This validity condition is to ensure that verifcation of the proof can be done -in a single pass. +> This validity condition is to ensure that verification of the proof can be done +> in a single pass. ###### B: No Extraneous Nodes @@ -176,7 +173,6 @@ A proof MUST NOT contain any nodes that are not part of the set needed for provi > This validity condition is to protect against malicious or erroneous bloating of proof payloads. - #### Account Trie Node These data types represent a node from the main state trie. @@ -211,7 +207,6 @@ This type MUST be used when content retrieved from another node via FINDCONTENT/ content_for_retrieval := Container(node: TrieNode) ``` - #### Contract Trie Node These data types represent a node from an individual contract storage trie. @@ -248,13 +243,12 @@ This type MUST be used when content retrieved from another node via FINDCONTENT/ content_for_retrieval := Container(node: TrieNode) ``` - #### Contract Code These data types represent the bytecode for a contract. -> NOTE: Because CREATE2 opcode allows for redeployment of new code at an existing address_hash, we MUST randomly distribute contract code storage across the DHT keyspace to avoid hotspots developing in the network for any contract that has had many different code deployments. Were we to use the path based *high-bits* approach for computing the content-id, it would be possible for a single location in the network to accumulate a large number of contract code objects that all live in roughly the same space. -Problematic! +> NOTE: Because CREATE2 opcode allows for redeployment of new code at an existing address_hash, we MUST randomly distribute contract code storage across the DHT keyspace to avoid hotspots developing in the network for any contract that has had many different code deployments. Were we to use the path based _high-bits_ approach for computing the content-id, it would be possible for a single location in the network to accumulate a large number of contract code objects that all live in roughly the same space. +> Problematic! ``` contract_code_key := Container(address_hash: AddressHash, code_hash: Bytes32) @@ -282,7 +276,6 @@ This type MUST be used when content retrieved from another node via FINDCONTENT/ content_for_retrieval := Container(code: ByteList(32768)) ``` - ## Gossip As each new block is added to the chain, the updated state from that block must be gossiped into @@ -291,44 +284,44 @@ together with its proof. ### Terminology -The Merkle Patricia Trie (MPT) has three types of nodes: *"branch"*, *"extension"* and *"leaf"*. +The Merkle Patricia Trie (MPT) has three types of nodes: _"branch"_, _"extension"_ and _"leaf"_. The MPT also specifies the `nil` node, but it will never be sent or stored over network, so we will ignore it for this spec. Similarly to other tree structure, the `leaf` node is the lowest node on a certain path and it's where value is stored in the tree (strictly speaking, MPT allows value to be stored in `branch` -nodes as well, but Ethreum storage doesn't use this functionality). The `branch` and `extension` +nodes as well, but Ethereum storage doesn't use this functionality). The `branch` and `extension` nodes can be called `intermediate` nodes because there will always be a node that can only be reached by passing through them. -A *"merkle proof"* or *"proof"* is a collection of nodes from the trie sufficient to recompute the -state root and prove that *"target"* node is part of the trie defined by that state root. A proof is: +A _"merkle proof"_ or _"proof"_ is a collection of nodes from the trie sufficient to recompute the +state root and prove that _"target"_ node is part of the trie defined by that state root. A proof is: -- *"ordered"* - - the order of the nodes in the proof have to represent the path from root node to the target node - - first node must be the root node, followed by zero or more intermediate nodes, ending - with a target node - - it should be provable that any non-first node is part of the preceding node - - if root node is the target node, then the proof will only contain the root node - - the target node can be of any type (branch, extension or leaf) -- *"minimal"* - - it contains only the nodes from on a path from the root node to the target node +- _"ordered"_ + - the order of the nodes in the proof have to represent the path from root node to the target node + - first node must be the root node, followed by zero or more intermediate nodes, ending + with a target node + - it should be provable that any non-first node is part of the preceding node + - if root node is the target node, then the proof will only contain the root node + - the target node can be of any type (branch, extension or leaf) +- _"minimal"_ + - it contains only the nodes from on a path from the root node to the target node ### Overview At each block, the bridge is responsible for creating and gossiping all following data and their proofs: - account trie data: - - all of the new and modified trie nodes from the state trie + - all of the new and modified trie nodes from the state trie - contract storage trie data: - - all of the new and modified trie nodes from each modified contract storage trie - - proof has to include the proof for the account trie that corresponds to the same contract + - all of the new and modified trie nodes from each modified contract storage trie + - proof has to include the proof for the account trie that corresponds to the same contract - all contract bytecode for newly created contracts - - proof has to include the proof for the account trie that corresponds to the same contract + - proof has to include the proof for the account trie that corresponds to the same contract A bridge should compute the content-id values for all content key/value pairs. These content-ids -should be sorted by proximity to its own node-id. Beginning with the content that is *closest* to its -own node-id it should proceed to GOSSIP each individual content to nodes *interested* in that content. +should be sorted by proximity to its own node-id. Beginning with the content that is _closest_ to its +own node-id it should proceed to GOSSIP each individual content to nodes _interested_ in that content. #### Special case to consider @@ -340,7 +333,7 @@ something like this (numbers next to branches indicate the index in the branch n ``` branch (root) - /0 \1 + /0 \1 prefix: 123 prefix: 234 value: A value: B ``` diff --git a/transaction-gossip/transaction-gossip.md b/transaction-gossip/transaction-gossip.md index 084bf9e6..64d2fa26 100644 --- a/transaction-gossip/transaction-gossip.md +++ b/transaction-gossip/transaction-gossip.md @@ -12,7 +12,6 @@ The transaction gossip network is designed with the following requirements. - Under normal network conditions, nodes that are interested in observing the full transaction pool will reliably receive the full set of valid transactions currently being gossiped. - Participants are not required to process the full pool and can control the total percentage of the transaction pool they wish to process. - ## Wire Protocol The [Portal wire protocol](./portal-wire-protocol.md) is used as wire protocol for the transaction gossip network. @@ -21,7 +20,6 @@ As specified in the [Protocol identifiers](./portal-wire-protocol.md#protocol-id The network uses the PING/PONG/FINDNODES/FOUNDNODES/OFFER/ACCEPT messages from the [Portal Wire Protocol](./portal-wire-protocol.md). - ### Distance Function The network uses the standard XOR distance function. @@ -62,7 +60,7 @@ transaction := TODO Clients in the Transaction Gossip Network are expected to maintain both the standard routing table and a secondary routing table that is anchored to node radius values. -The secondary routing table is subject to all of the same maintenance and management rules as the primary table. Any node that is added to the secondary routing table must also satisfy the following validity condition: +The secondary routing table is subject to all of the same maintenance and management rules as the primary table. Any node that is added to the secondary routing table must also satisfy the following validity condition: ``` node.transaction_radius >= distance(node.node_id, self.node_id) @@ -70,14 +68,13 @@ node.transaction_radius >= distance(node.node_id, self.node_id) The additional validity rule aims to ensure that the table is populated with nodes will have shared interest in mostly the same transactions as us. - ## Gossip Algorithm The gossip mechanism for transactions is designed to allow DHT nodes to control what percentage of the transaction pool they wish to process. ### Radius -We use the term "radius" to refer to the mechanism through which a node may limit how much of the transaction pool they wish to process. The `radius` is a 256 bit integer. +We use the term "radius" to refer to the mechanism through which a node may limit how much of the transaction pool they wish to process. The `radius` is a 256 bit integer. A DHT node that wishes to process the full transaction pool would publis a radius value of `2**256-1`. We refer to such a DHT node as a "full radius node". @@ -85,15 +82,13 @@ A DHT node is expected to process transactions that satisfy the condition: `dist Each DHT node includes their current `radius` value in both PING and PONG messages. - ### Validation DHT nodes **should** drop transactions which do not pass the following validation rules - #### 2. Proof valid -The proof must be valid and against a *recent* state root. Individual implementations may choose the boundary for which a proof is considered stale. +The proof must be valid and against a _recent_ state root. Individual implementations may choose the boundary for which a proof is considered stale. > TODO: 8 blocks seems like a good simple starting point @@ -112,7 +107,6 @@ A DHT node should OFFER transactions to all of the DHT nodes present in their se A DHT node should only OFFER any individual transaction to a DHT node once. - ### Proof Updating A DHT Node which encounters a transaction with a proof that is outdated **may** update that proof. diff --git a/utp/discv5-utp.md b/utp/discv5-utp.md index 249ca68e..6000ffe2 100644 --- a/utp/discv5-utp.md +++ b/utp/discv5-utp.md @@ -6,8 +6,7 @@ This document specifies an implementation of the [uTP](https://www.bittorrent.or ## Motivation -The Discovery v5 protocol provides a simple and extensible UDP based protocol with robust encryption and resistance to deep packet inspection. The use of UDP however imposes a tight limit on packet sizes. [Sub-protocols](https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire.md#talkreq-request-0x05) which wish to implement functionality that requires transmission of data that exceeds this packet size are forced to implement their own solutions for splitting these payloads across multiple UDP packets. Packet loss makes this type of solution fragile. A generic solution that can be reused across different Discovery v5 sub-protocols will improve the overall security and robustness of sub-protocols. - +The Discovery v5 protocol provides a simple and extensible UDP based protocol with robust encryption and resistance to deep packet inspection. The use of UDP however imposes a tight limit on packet sizes. [Sub-protocols](https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire.md#talkreq-request-0x05) which wish to implement functionality that requires transmission of data that exceeds this packet size are forced to implement their own solutions for splitting these payloads across multiple UDP packets. Packet loss makes this type of solution fragile. A generic solution that can be reused across different Discovery v5 sub-protocols will improve the overall security and robustness of sub-protocols. ## Specification @@ -18,9 +17,9 @@ All uTP packets MUST be sent using the `TALKREQ` message. This protocol MUST NOT use the `TALKRESP` message for sending uTP packets. > Note: `TALKREQ` is part of a request-response mechanism and might cause Discovery v5 implementations -to invalidate peers when not receiving a `TALKRESP` response. This is an unresolved item in the specification. -Thus currently a `TALKRESP` message MAY be send as response on a `TALKREQ` message. -However, the response MUST be ignored by the uTP protocol. +> to invalidate peers when not receiving a `TALKRESP` response. This is an unresolved item in the specification. +> Thus currently a `TALKRESP` message MAY be send as response on a `TALKREQ` message. +> However, the response MUST be ignored by the uTP protocol. The payload passed to the `request` field of the `TALKREQ` message is the uTP packet as specified in BEP29. @@ -33,16 +32,18 @@ The uTP protocol as specified in BEP29 defines the packet structure and logic fo The main difference with BEP29 is that, instead of a raw UDP packet, the Discovery v5 `TALKREQ` message is used as transport. Additionally, following deviations from the uTP specification or reference implementation are applied: + - The `connection_id` is passed out of band (i.e. in a Portal wire protocol message), instead of randomly generated by the uTP connection intiator: This is required for integration with the Portal wire protocol. - To track incoming uTP streams, the IP address + port + Discovery v5 `NodeId` + `connection_id` is used, as opposed to IP address + port + `connection_id` in standard uTP. - It is allowed to send `ST_DATA` without receiving `ST_DATA` first from the initiator of the uTP connection. This is not specified in BEP29, but rather a deviation from the [uTP reference implementation](https://github.com/bittorrent/libutp). It was added in the reference implementation to counter a reflective DDoS. -Relevant paper: https://www.usenix.org/system/files/conference/woot15/woot15-paper-adamsky.pdf. -However, when using Discovery v5 as a transport, the DDoS becomes no longer applicable because a full Discovery v5 handshake is required which will not work with a spoofed IP address. + Relevant paper: https://www.usenix.org/system/files/conference/woot15/woot15-paper-adamsky.pdf. + However, when using Discovery v5 as a transport, the DDoS becomes no longer applicable because a full Discovery v5 handshake is required which will not work with a spoofed IP address. - The uTP reference implementation deviates from the uTP specification on the initialization of the `ack_nr` when receiving the `ACK` of a `SYN` packet. The reference implementation [initializes](https://github.com/bittorrent/libutp/blob/master/utp_internal.cpp#L1874) this as `c.ack_nr = pkt.seq_nr - 1` while the specification indicates `c.ack_nr = pkt.seq_nr`. The uTP over Discovery v5 specifications follows the uTP reference implementation: `c.ack_nr = pkt.seq_nr - 1`. ## Example Usage ### Data Request + Suppose we have a sub-protocol with the following messages: - `GetData` (request) @@ -56,7 +57,7 @@ so the `Data` response sent by Bob will contain a randomly generated Alice will then initiate a new uTP connection with Bob using this `connection_id`. Bob, upon sending the `Data` message containing the `connection_id` will -*listen* for a new incoming connection from Alice over the `utp` sub-protocol. +_listen_ for a new incoming connection from Alice over the `utp` sub-protocol. When this new connection is opened, Alice can then read the bytes from the stream until the connection closes. @@ -86,6 +87,7 @@ But Alice MAY also send a `ST_FIN` if Alice can conclude that it received all th data, and there are situations where this may happen (e.g. lost `ST_FIN` packet). ### Data Offer + Suppose we have a sub-protocol with the following messages: - `OfferData` (request) @@ -98,7 +100,7 @@ to the data. The `Accept` response sent by Bob will contain a randomly generated Alice will then initiate a new uTP connection with Bob using this `connection_id`. Bob, upon sending the `Accept` message containing the `connection_id` will -*listen* for a new incoming connection from Alice over the `utp` sub-protocol. +_listen_ for a new incoming connection from Alice over the `utp` sub-protocol. When this new connection is opened, Bob can then read the bytes from the stream until the connection closes. @@ -122,6 +124,7 @@ A typical message flow: Alice->>Bob: uTP ST_FIN ``` + The typical flow is that Alice sends the `ST_FIN` to terminate the uTP connection. But Bob MAY also send a `ST_FIN` if Bob can conclude that it received all the data, and there are situations where this may happen (e.g. lost `ST_FIN` packet). diff --git a/utp/utp-wire-test-vectors.md b/utp/utp-wire-test-vectors.md index de5098fc..72547979 100644 --- a/utp/utp-wire-test-vectors.md +++ b/utp/utp-wire-test-vectors.md @@ -10,14 +10,14 @@ uTP specification - https://www.bittorrent.org/beps/bep_0029.html. Each packet input parameters are - `(PacketHeader, List[Extension], payload)` -Altough spec defines only one extension called - `SelectiveAckExtension` +Although spec defines only one extension called - `SelectiveAckExtension` Taking that into account, packet inputs can be simplified to - `(PacketHeader, Option[SelectiveAckExtension], Payload)` Where: `payload` is an `ByteArray` -`PacketHeader` is object with input paramaters - +`PacketHeader` is object with input parameters - `(type, version, extension, connection_id, timestamp_microseconds, timestamp_difference_microseconds, wnd_size, seq_nr, ack_nr)` `Option[SelectiveAckExtension]` is either, bitmask of 32bits where each bit represents one packet in the send window @@ -45,6 +45,7 @@ Payload = [] ``` #### Expected Output + ``` packet = 0x41002741c9b699ba00000000001000002e6c0000 ``` @@ -71,11 +72,11 @@ Payload = [] ``` #### Expected Output + ``` packet = 0x21002741005e885e36a7e8830010000041a72e6d ``` - ### Ack Packet (with selective ack extension) #### Input Parameters @@ -98,11 +99,11 @@ Payload = [] ``` #### Expected Output + ``` packet = 0x21012741005e885e36a7e8830010000041a72e6d000401000080 ``` - ### DATA Packet #### Input Parameters @@ -125,11 +126,11 @@ Payload = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ``` #### Expected Output + ``` packet = 0x0100667d0f0cbacf0e710cbf00100000208e41a600010203040506070809 ``` - ### FIN Packet #### Input Parameters @@ -152,11 +153,11 @@ Payload = [] ``` #### Expected Output + ``` packet = 0x11004a3b1eb5be8f1e7c94d100100000a05a41a6 ``` - ### RESET Packet #### Input Parameters @@ -179,7 +180,7 @@ Payload = [] ``` #### Expected Output + ``` packet = 0x3100f34d2cc6cfbb0000000000000000d87541a7 ``` - diff --git a/verkle/verkle-state-network.md b/verkle/verkle-state-network.md index 64c127b8..eb313b15 100644 --- a/verkle/verkle-state-network.md +++ b/verkle/verkle-state-network.md @@ -19,14 +19,15 @@ To represent the trie node, the Verkle Trie uses Pedersen Commitment, which is c $$C = Commit(a_0, a_1, ..., a_{255}) = a_0B_0 + a_1B_1 + ... + a_{255}B_{255}$$ where: + - $B_i$ is basis of the Pedersen commitment - - already fixed Elliptic curve points on Banderwagon (a prime order subgroup over [Bandersnatch](https://ethresear.ch/t/introducing-bandersnatch-a-fast-elliptic-curve-built-over-the-bls12-381-scalar-field/9957)) curve. + - already fixed Elliptic curve points on Banderwagon (a prime order subgroup over [Bandersnatch](https://ethresear.ch/t/introducing-bandersnatch-a-fast-elliptic-curve-built-over-the-bls12-381-scalar-field/9957)) curve. - $a_i$ are values we are committing to - - value from elliptic curve's scalar field $F_r$ (maximum value is less than $2^{253}$) + - value from elliptic curve's scalar field $F_r$ (maximum value is less than $2^{253}$) - $C$ is the commitment of $a_i$ values, which on its own is a point on the elliptic curve - - in order to commit to another commitment, we map commitment $C$ to the scalar field $F_r$ and we call that **Pedersen Hash** or **hash commitment** - - these two values are frequently used interchangeably, but they are not one-to-one mapping - - in this document, we will use $C$ to indicate commitment expressed as elliptic point, and $c$ when it's mapped to scalar field (hash commitment) + - in order to commit to another commitment, we map commitment $C$ to the scalar field $F_r$ and we call that **Pedersen Hash** or **hash commitment** + - these two values are frequently used interchangeably, but they are not one-to-one mapping + - in this document, we will use $C$ to indicate commitment expressed as elliptic point, and $c$ when it's mapped to scalar field (hash commitment) #### Trie Node @@ -78,10 +79,9 @@ where: - $marker$ - currently only value $1$ is used - $stem$ - the path from the root of the trie (31 bytes) - $v_i^{low+access}$ - the lower 16 bytes of the value $v_i$ plus $2^{128}$ if value is modified - - note that if value is not modified, it will be equal to $0$ + - note that if value is not modified, it will be equal to $0$ - $v_i^{high}$ - the higher 16 bytes of the value $v_i$ - For optimization reasons, Portal Network splits leaf node into 2-layer mini network in the following way: ![Verkle leaf node](./../assets/verkle_leaf_node.png) @@ -116,7 +116,6 @@ $$ C = marker \cdot B_0 + stem \cdot B_1 + c_1B_2 + c_2B_3 $$ - ## Specification ### Protocol Identifier @@ -145,7 +144,7 @@ ScalarFieldValue := Bytes32 #### Bundle Commitment Proof -**⚠️ This section needs more reserach and detailed specifiction. ⚠️** +**⚠️ This section needs more reserach and detailed specification. ⚠️** In order to prevent bad actors from creating false data for the `bundle` nodes of the mini tries, we have to create and include proof that fragment commitments are correct. The exact proof schema is being researched. @@ -213,7 +212,7 @@ BranchFragmentNodeWithProof := Container( block_hash: Bytes32, path: Path, proof: Union[None, TrieProof], - ) + ) LeafBundleNode := Container( marker: uint32,