From e9a73774c62e30f7552c0c769beb349b3900a948 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 22 Oct 2024 10:44:33 +0200 Subject: [PATCH 1/4] EIP-7685: exclude empty requests data in commitment Here I am proposing to change the `requests_hash` commitment so that empty items are excluded. The point of this is to ensure a stable empty requests hash which is independent of fork. Having such a hash makes testing a lot easier, and it mirrors how other execution-layer commitments behave. --- EIPS/eip-7685.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/EIPS/eip-7685.md b/EIPS/eip-7685.md index 125cacdf2c40c0..0934ae18d5b7ca 100644 --- a/EIPS/eip-7685.md +++ b/EIPS/eip-7685.md @@ -45,21 +45,19 @@ Each request type will defines its own `requests` object using with its own #### Block Header -Extend the header with a new 32 byte value `requests_hash`. +Extend the header with a new 32 byte commitment value `requests_hash`. -The construction looks like: +A requests hash list is computed by hashing the elements of the block requests list, but +only ones where `request_data` is non-empty. The commitment is then computed as the sha256 +hash of the list of requests element hashes. -``` -sha256(sha256(requests_0) ++ sha256(requests_1) ++ ...) -``` - -Or in pseudocode: ```python -def compute_requests_hash(requests): +def compute_requests_hash(block_requests): m = sha256() - for r in requests: - m.update(sha256(r)) + for r in block_requests: + if len(r) > 1: + m.update(sha256(r)) return m.digest() block.header.requests_hash = compute_requests_hash(requests) @@ -67,8 +65,8 @@ block.header.requests_hash = compute_requests_hash(requests) ### Consensus Layer -Each proposal may choose how to extend the beacon chain types to include new EL -request types. +Each proposal may choose how to extend the beacon chain types to include new EL request +types. ## Rationale @@ -115,6 +113,12 @@ Within the same type, order is not defined. This is because the data of the request is opaque as far as this EIP is concerned. Therefore, it is to be determined by each request type individually. +### Removing empty requests in commitment + +We exclude empty requests elements from the `requests_hash` commitment in order to get a +stable 'empty' hash value that is independent of the blockchain fork. For a block with no +requests data, the `requests_hash` is simply `sha256("")`. + ## Backwards Compatibility No backward compatibility issues found. From d0631d7ccbfe92a3c0e04a06fd0c0e0d0702e2f5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 25 Nov 2024 17:08:52 +0100 Subject: [PATCH 2/4] EIP-7685: update --- EIPS/eip-7685.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-7685.md b/EIPS/eip-7685.md index 0934ae18d5b7ca..304c0e9d21710c 100644 --- a/EIPS/eip-7685.md +++ b/EIPS/eip-7685.md @@ -48,8 +48,10 @@ Each request type will defines its own `requests` object using with its own Extend the header with a new 32 byte commitment value `requests_hash`. A requests hash list is computed by hashing the elements of the block requests list, but -only ones where `request_data` is non-empty. The commitment is then computed as the sha256 -hash of the list of requests element hashes. +only ones where `request_data` is non-empty. Note we assume `request_type` has a size of +one byte. + +The commitment is then computed as the sha256 hash of the list of element hashes. ```python @@ -57,7 +59,7 @@ def compute_requests_hash(block_requests): m = sha256() for r in block_requests: if len(r) > 1: - m.update(sha256(r)) + m.update(sha256(r).digest()) return m.digest() block.header.requests_hash = compute_requests_hash(requests) From 9f80b684aeba7019c2e922ac37d521c3b59455c1 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 26 Nov 2024 12:12:13 +0100 Subject: [PATCH 3/4] Update EIPS/eip-7685.md Co-authored-by: Mikhail Kalinin --- EIPS/eip-7685.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7685.md b/EIPS/eip-7685.md index 304c0e9d21710c..7f22347ff0ec6d 100644 --- a/EIPS/eip-7685.md +++ b/EIPS/eip-7685.md @@ -55,7 +55,7 @@ The commitment is then computed as the sha256 hash of the list of element hashes ```python -def compute_requests_hash(block_requests): +def compute_requests_hash(block_requests: Sequence[bytes]): m = sha256() for r in block_requests: if len(r) > 1: From 11d158123373a1f0ffc0f130973893442a94c821 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 26 Nov 2024 13:36:37 +0100 Subject: [PATCH 4/4] EIP-7685: update commitment description --- EIPS/eip-7685.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/EIPS/eip-7685.md b/EIPS/eip-7685.md index 7f22347ff0ec6d..d7e2268fc65502 100644 --- a/EIPS/eip-7685.md +++ b/EIPS/eip-7685.md @@ -33,26 +33,30 @@ update on the execution block structure. #### Requests -A `requests` object consists of a `request_type` prepended to an opaque byte -array `request_data`. +A `requests` object consists of a `request_type` prepended to an opaque byte array +`request_data`. The `request_data` contains zero or more encoded request objects. ``` requests = request_type ++ request_data ``` -Each request type will defines its own `requests` object using with its own -`request_data` format. +Each request type will defines its own `requests` object with its own `request_data` format. #### Block Header Extend the header with a new 32 byte commitment value `requests_hash`. -A requests hash list is computed by hashing the elements of the block requests list, but -only ones where `request_data` is non-empty. Note we assume `request_type` has a size of -one byte. +While processing a block, multiple `requests` objects with different `request_type`s will +be produced by the system, and accumulated in the block requests list. -The commitment is then computed as the sha256 hash of the list of element hashes. +In order to compute the commitment, an intermediate hash list is first built by hashing +all non-empty requests elements of the block requests list. Items with empty +`request_data` are excluded, i.e. the intermediate list skips `requests` items which +contain only the `request_type` (1 byte) and nothing else. +Within the intermediate list, `requests` items must be ordered by `request_type` ascending. + +The final commitment is computed as the sha256 hash of the intermediate element hashes. ```python def compute_requests_hash(block_requests: Sequence[bytes]):