Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 35 additions & 34 deletions EIPS/eip-4844.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Compared to full data sharding, this EIP has a reduced cap on the number of thes
| `MAX_CALLDATA_SIZE` | `2**24` |
| `MAX_ACCESS_LIST_SIZE` | `2**24` |
| `MAX_ACCESS_LIST_STORAGE_KEYS` | `2**24` |
| `MAX_TX_WRAP_KZG_COMMITMENTS` | `2**12` |
| `MAX_TX_WRAP_COMMITMENTS` | `2**12` |
| `LIMIT_BLOBS_PER_TX` | `2**12` |
| `DATA_GAS_PER_BLOB` | `2**17` |
| `HASH_OPCODE_BYTE` | `Bytes1(0x49)` |
Expand All @@ -71,18 +71,18 @@ Compared to full data sharding, this EIP has a reduced cap on the number of thes

### Cryptographic Helpers

Throughout this proposal we use cryptographic methods and classes defined in the corresponding [consensus 4844 specs](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844).
Throughout this proposal we use cryptographic methods and classes defined in the corresponding [consensus 4844 specs](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb).

Specifically, we use the following methods from [`polynomial-commitments.md`](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844/polynomial-commitments.md):
Specifically, we use the following methods from [`polynomial-commitments.md`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb/polynomial-commitments.md):

- [`verify_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844/polynomial-commitments.md#verify_kzg_proof)
- [`verify_aggregate_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844/polynomial-commitments.md#verify_aggregate_kzg_proof)
- [`verify_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb/polynomial-commitments.md#verify_kzg_proof)
- [`verify_blob_kzg_proof_batch()`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb/polynomial-commitments.md#verify_blob_kzg_proof_batch)

### Helpers

```python
def kzg_to_versioned_hash(kzg: KZGCommitment) -> VersionedHash:
return BLOB_COMMITMENT_VERSION_KZG + sha256(kzg)[1:]
def kzg_to_versioned_hash(commitment: KZGCommitment) -> VersionedHash:
return BLOB_COMMITMENT_VERSION_KZG + sha256(commitment)[1:]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest renaming all BLOB_COMMITMENT_VERSION_KZG to VERSIONED_HASH_VERSION_KZG as CL specs.

Suggested change
return BLOB_COMMITMENT_VERSION_KZG + sha256(commitment)[1:]
return VERSIONED_HASH_VERSION_KZG + sha256(commitment)[1:]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

umm ... BLOB_COMMITMENT_VERSION_KZG makes more sense to me as its the version of the commitment scheme

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rationale was the version of VersionedHash. I'm not sure what's the scenario of changing VERSIONED_HASH_VERSION_* but not just changing the commitment scheme though.

It should be unified to one of the names anyway.

/cc @asn-d6 @dankrad any thoughts?


And kzg_to_versioned_hash was also renamed to kzg_commitment_to_versioned_hash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong opinion here. I think both variants are reasonable.

I would go with @hwwhww suggestions to make it the same with consensus-specs.

```

Approximates `factor * e ** (numerator / denominator)` using Taylor expansion:
Expand Down Expand Up @@ -121,7 +121,7 @@ class BlobTransaction(Container):
data: ByteList[MAX_CALLDATA_SIZE]
access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE]
max_fee_per_data_gas: uint256
blob_versioned_hashes: List[VersionedHash, MAX_VERSIONED_HASHES_LIST_SIZE]
versioned_hashes: List[VersionedHash, MAX_VERSIONED_HASHES_LIST_SIZE]

class AccessTuple(Container):
address: Address # Bytes20
Expand Down Expand Up @@ -150,15 +150,15 @@ The `TransactionNetworkPayload` wraps a `TransactionPayload` with additional dat
this wrapping data SHOULD be verified directly before or after signature verification.

When a blob transaction is passed through the network (see the [Networking](#networking) section below),
the `TransactionNetworkPayload` version of the transaction also includes `blobs` and `kzgs` (commitments list).
the `TransactionNetworkPayload` version of the transaction also includes `blobs`, `commitments` and `proofs`.
The execution layer verifies the wrapper validity against the inner `TransactionPayload` after signature verification as:

- `blob_versioned_hashes` must not be empty
- All hashes in `blob_versioned_hashes` must start with the byte `BLOB_COMMITMENT_VERSION_KZG`
- `versioned_hashes` must not be empty
- All hashes in `versioned_hashes` must start with the byte `BLOB_COMMITMENT_VERSION_KZG`
- There may be at most `MAX_DATA_GAS_PER_BLOCK // DATA_GAS_PER_BLOB` total blob commitments in a valid block.
- There is an equal amount of versioned hashes, kzg commitments and blobs.
- The KZG commitments hash to the versioned hashes, i.e. `kzg_to_versioned_hash(kzg[i]) == versioned_hash[i]`
- The KZG commitments match the blob contents. (Note: this can be optimized with additional data, using a proof for a
- There is an equal amount of versioned hashes, commitments, blobs and proofs.
- The commitments hash to the versioned hashes, i.e. `kzg_to_versioned_hash(commitment[i]) == versioned_hash[i]`
- The commitments match the blob contents. (Note: this can be optimized with additional data, using a proof for a
random evaluation at two points derived from the commitment and blob data)


Expand All @@ -182,7 +182,7 @@ def signed_tx_hash(tx: SignedBlobTransaction) -> Bytes32:
return keccak256(BLOB_TX_TYPE + ssz.serialize(tx))
```

Blob transactions with empty `blob_versioned_hashes` are also considered invalid during execution block verification, and must not be included in a valid block.
Blob transactions with empty `versioned_hashes` are also considered invalid during execution block verification, and must not be included in a valid block.

### Header extension

Expand Down Expand Up @@ -231,7 +231,7 @@ For the first post-fork block, `parent.excess_data_gas` is evaluated as `0`.
### Beacon chain validation

On the consensus-layer the blobs are now referenced, but not fully encoded, in the beacon block body.
Instead of embedding the full contents in the body, the contents of the blobs are propagated separately, as a "sidecar".
Instead of embedding the full contents in the body, the blobs are propagated separately, as a "sidecar".

This "sidecar" design provides forward compatibility for further data increases by black-boxing `is_data_available()`:
with full sharding `is_data_available()` can be replaced by data-availability-sampling (DAS) thus avoiding all blobs being downloaded by all beacon nodes on the network.
Expand All @@ -247,8 +247,8 @@ The `ethereum/consensus-specs` repository defines the following beacon-node chan
### Opcode to get versioned hashes

We add an opcode `DATAHASH` (with byte value `HASH_OPCODE_BYTE`) which reads `index` from the top of the stack
as big-endian `uint256`, and replaces it on the stack with `tx.message.blob_versioned_hashes[index]`
if `index < len(tx.message.blob_versioned_hashes)`, and otherwise with a zeroed `bytes32` value.
as big-endian `uint256`, and replaces it on the stack with `tx.message.versioned_hashes[index]`
if `index < len(tx.message.versioned_hashes)`, and otherwise with a zeroed `bytes32` value.
The opcode has a gas cost of `HASH_OPCODE_GAS`.

### Point evaluation precompile
Expand All @@ -270,13 +270,13 @@ def point_evaluation_precompile(input: Bytes) -> Bytes:
z = input[32:64]
y = input[64:96]
commitment = input[96:144]
kzg_proof = input[144:192]
proof = input[144:192]

# Verify commitment matches versioned_hash
assert kzg_to_versioned_hash(commitment) == versioned_hash

# Verify KZG proof
assert verify_kzg_proof(commitment, z, y, kzg_proof)
assert verify_kzg_proof(commitment, z, y, proof)

# Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values
return Bytes(U256(FIELD_ELEMENTS_PER_BLOB).to_be_bytes32() + U256(BLS_MODULUS).to_be_bytes32())
Expand All @@ -294,7 +294,7 @@ def calc_data_fee(tx: SignedBlobTransaction, parent: Header) -> int:
return get_total_data_gas(tx) * get_data_gasprice(parent)

def get_total_data_gas(tx: SignedBlobTransaction) -> int:
return DATA_GAS_PER_BLOB * len(tx.message.blob_versioned_hashes)
return DATA_GAS_PER_BLOB * len(tx.message.versioned_hashes)

def get_data_gasprice(header: Header) -> int:
return fake_exponential(
Expand All @@ -320,7 +320,7 @@ def validate_block(block: Block) -> None:
# ensure that the user was willing to at least pay the current data gasprice
assert tx.message.max_fee_per_data_gas >= get_data_gasprice(parent(block).header)

num_blobs += len(tx.message.blob_versioned_hashes)
num_blobs += len(tx.message.versioned_hashes)

# check that the excess data gas is correct
expected_edg = calc_excess_data_gas(parent(block).header, num_blobs)
Expand All @@ -341,26 +341,27 @@ the payload is a SSZ encoded container:
class BlobTransactionNetworkWrapper(Container):
tx: SignedBlobTransaction
# KZGCommitment = Bytes48
blob_kzgs: List[KZGCommitment, MAX_TX_WRAP_KZG_COMMITMENTS]
commitments: List[KZGCommitment, MAX_TX_WRAP_COMMITMENTS]
# BLSFieldElement = uint256
blobs: List[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB], LIMIT_BLOBS_PER_TX]
# KZGProof = Bytes48
kzg_aggregated_proof: KZGProof
proofs: List[KZGProof, MAX_TX_WRAP_COMMITMENTS]
```

We do network-level validation of `BlobTransactionNetworkWrapper` objects as follows:

```python
def validate_blob_transaction_wrapper(wrapper: BlobTransactionNetworkWrapper):
versioned_hashes = wrapper.tx.message.blob_versioned_hashes
commitments = wrapper.blob_kzgs
versioned_hashes = wrapper.tx.message.versioned_hashes
commitments = wrapper.commitments
blobs = wrapper.blobs
proofs = wrapper.proofs
# note: assert blobs are not malformatted
assert len(versioned_hashes) == len(commitments) == len(blobs)
assert len(versioned_hashes) == len(commitments) == len(blobs) == len(proofs)
assert len(versioned_hashes) > 0

# Verify that commitments match the blobs by checking the KZG proof
assert verify_aggregate_kzg_proof(blobs, commitments, wrapper.kzg_aggregated_proof)
# Verify that commitments match the blobs by checking the KZG proofs
assert verify_blob_kzg_proof_batch(blobs, commitments, proofs)

# Now that all commitments have been verified, check that versioned_hashes matches the commitments
for versioned_hash, commitment in zip(versioned_hashes, commitments):
Expand Down Expand Up @@ -399,7 +400,7 @@ The work that is already done in this EIP includes:

The work that remains to be done to get to full sharding includes:

- A low-degree extension of the `blob_kzgs` in the consensus layer to allow 2D sampling
- A low-degree extension of the `commitments` in the consensus layer to allow 2D sampling
- An actual implementation of data availability sampling
- PBS (proposer/builder separation), to avoid requiring individual validators to process 32 MB of data in one slot
- Proof of custody or similar in-protocol requirement for each validator to verify a particular part of the sharded data in each block
Expand All @@ -422,13 +423,13 @@ For each value it would provide a KZG proof and use the point evaluation precomp
and then perform the fraud proof verification on that data as is done today.

ZK rollups would provide two commitments to their transaction or state delta data:
the kzg in the blob and some commitment using whatever proof system the ZK rollup uses internally.
They would use a commitment proof of equivalence protocol, using the point evaluation precompile,
to prove that the kzg (which the protocol ensures points to available data) and the ZK rollup's own commitment refer to the same data.
the blob commitment (which the protocol ensures points to available data) and the ZK rollup's own commitment using whatever proof system the rollup uses internally.
They would use a proof of equivalence protocol, using the point evaluation precompile,
to prove that the two commitments refer to the same data.

### Versioned hashes & precompile return data

We use versioned hashes (rather than kzgs) as references to blobs in the execution layer to ensure forward compatibility with future changes.
We use versioned hashes (rather than commitments) as references to blobs in the execution layer to ensure forward compatibility with future changes.
For example, if we need to switch to Merkle trees + STARKs for quantum-safety reasons, then we would add a new version,
allowing the point evaluation precompile to work with the new format.
Rollups would not have to make any EVM-level changes to how they work;
Expand Down