feat: added kzg specs to gossip validation rules, fixed peerdas from C API

This commit is contained in:
Agnish Ghosh 2024-05-29 14:02:56 +05:30
parent 4c390323d3
commit 26519f68a0
No known key found for this signature in database
GPG Key ID: 7E927C221EBA4F6E
3 changed files with 117 additions and 57 deletions

View File

@ -13,7 +13,7 @@ import
results, results,
# Internals # Internals
../spec/[ ../spec/[
beaconstate, state_transition_block, forks, helpers, network, signatures], beaconstate, state_transition_block, forks, helpers, network, signatures, eip7594_helpers],
../consensus_object_pools/[ ../consensus_object_pools/[
attestation_pool, blockchain_dag, blob_quarantine, block_quarantine, attestation_pool, blockchain_dag, blob_quarantine, block_quarantine,
spec_cache, light_client_pool, sync_committee_msg_pool, spec_cache, light_client_pool, sync_committee_msg_pool,
@ -207,6 +207,18 @@ func check_blob_sidecar_inclusion_proof(
ok() ok()
func check_data_column_sidecar_inclusion_proof(
data_column_sidecar: DataColumnSidecar): Result[void, ValidationError] =
let res = data_column_sidecar.verify_data_column_sidecar_inclusion_proof()
if res.isErr:
return errReject(res.error)
proc check_data_column_sidecar_kzg_proofs(
data_column_sidecar: DataColumnSidecar): Result[void, ValidationError] =
let res = data_column_sidecar.verify_data_column_sidecar_kzg_proofs()
if res.isErr:
return errReject(res.error)
# Gossip Validation # Gossip Validation
# ---------------------------------------------------------------- # ----------------------------------------------------------------
@ -502,11 +514,19 @@ proc validateDataColumnSidecar*(
if not (block_header.slot > dag.finalizedHead.slot): if not (block_header.slot > dag.finalizedHead.slot):
return errIgnore("DataColumnSidecar: slot already finalized") return errIgnore("DataColumnSidecar: slot already finalized")
# TODO: [REJECT] The sidecar's `kzg_commitments` inclusion proof is valid as verified by # [REJECT] The sidecar's `kzg_commitments` inclusion proof is valid as verified by
# `verify_data_column_sidecar_inclusion_proof(sidecar)`. # `verify_data_column_sidecar_inclusion_proof(sidecar)`.
block:
let v = check_data_column_sidecar_inclusion_proof(data_column_sidecar)
if v.isErr:
return dag.checkedReject(v.error)
# TODO: [REJECT] The sidecar's column data is valid as # [REJECT] The sidecar's column data is valid as
# verified by `verify_data_column_kzg_proofs(sidecar)` # verified by `verify_data_column_kzg_proofs(sidecar)`
block:
let r = check_data_column_sidecar_kzg_proofs(data_column_sidecar)
if r.isErr:
return dag.checkedReject(r.error)
# [IGNORE] The sidecar is the first sidecar for the tuple # [IGNORE] The sidecar is the first sidecar for the tuple
# (block_header.slot, block_header.proposer_index, blob_sidecar.index) # (block_header.slot, block_header.proposer_index, blob_sidecar.index)

View File

@ -20,6 +20,8 @@ type
Coset* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement] Coset* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement]
CosetEvals* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement] CosetEvals* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement]
Cell* = KzgCell Cell* = KzgCell
Cells* = KzgCells
CellsAndProofs* = KzgCellsAndKzgProofs
CellID* = uint64 CellID* = uint64
RowIndex* = uint64 RowIndex* = uint64
ColumnIndex* = uint64 ColumnIndex* = uint64
@ -34,8 +36,8 @@ const
TARGET_NUMBER_OF_PEERS* = 70 TARGET_NUMBER_OF_PEERS* = 70
type type
DataColumn* = List[Cell, Limit(MAX_BLOB_COMMITMENTS_PER_BLOCK)] DataColumn* = List[KzgCell, Limit(MAX_BLOB_COMMITMENTS_PER_BLOCK)]
ExtendedMatrix* = List[Cell, Limit(MAX_CELLS_IN_EXTENDED_MATRIX)] ExtendedMatrix* = List[KzgCell, Limit(MAX_CELLS_IN_EXTENDED_MATRIX)]
DataColumnSidecar* = object DataColumnSidecar* = object
index*: ColumnIndex # Index of column in extended matrix index*: ColumnIndex # Index of column in extended matrix
@ -44,7 +46,7 @@ type
kzg_proofs*: List[KzgProof, Limit(MAX_BLOB_COMMITMENTS_PER_BLOCK)] kzg_proofs*: List[KzgProof, Limit(MAX_BLOB_COMMITMENTS_PER_BLOCK)]
signed_block_header*: SignedBeaconBlockHeader signed_block_header*: SignedBeaconBlockHeader
kzg_commitments_inclusion_proof*: kzg_commitments_inclusion_proof*:
array[KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, KzgBytes32] array[KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, Eth2Digest]
func shortLog*(v: DataColumnSidecar): auto = func shortLog*(v: DataColumnSidecar): auto =
( (

View File

@ -8,22 +8,21 @@
{.push raises: [].} {.push raises: [].}
# Uncategorized helper functions from the spec # Uncategorized helper functions from the spec
import import
tables, tables,
algorithm, algorithm,
std/macros, std/macros,
results, stew/results,
stew/assign2, ssz_serialization/proofs,
nim-ssz-serialization/ssz_serialization/proofs,
chronicles, chronicles,
std/sequtils,
./[beacon_time, crypto], ./[beacon_time, crypto],
eth/p2p/discoveryv5/[node], eth/p2p/discoveryv5/[node],
./helpers, ./helpers,
./datatypes/[eip7594, deneb] ./datatypes/[eip7594, deneb]
var ctx: KzgCtx
proc sortedColumnIndices*(columnsPerSubnet: ColumnIndex, subnetIds: HashSet[uint64]): seq[ColumnIndex] = proc sortedColumnIndices*(columnsPerSubnet: ColumnIndex, subnetIds: HashSet[uint64]): seq[ColumnIndex] =
var res: seq[ColumnIndex] = @[] var res: seq[ColumnIndex] = @[]
for i in 0 ..< columnsPerSubnet: for i in 0 ..< columnsPerSubnet:
@ -33,6 +32,7 @@ proc sortedColumnIndices*(columnsPerSubnet: ColumnIndex, subnetIds: HashSet[uint
res.sort() res.sort()
res res
# https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/specs/_features/eip7594/das-core.md#get_custody_columns
proc get_custody_columns*(node_id: NodeId, custody_subnet_count: uint64): Result[seq[ColumnIndex], cstring] = proc get_custody_columns*(node_id: NodeId, custody_subnet_count: uint64): Result[seq[ColumnIndex], cstring] =
# assert custody_subnet_count <= DATA_COLUMN_SIDECAR_SUBNET_COUNT # assert custody_subnet_count <= DATA_COLUMN_SIDECAR_SUBNET_COUNT
@ -65,18 +65,23 @@ proc get_custody_columns*(node_id: NodeId, custody_subnet_count: uint64): Result
ok(sortedColumnIndices(ColumnIndex(columns_per_subnet), subnet_ids)) ok(sortedColumnIndices(ColumnIndex(columns_per_subnet), subnet_ids))
# https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/specs/_features/eip7594/das-core.md#compute_extended_matrix
# #### `compute_extended_matrix`
proc compute_extended_matrix* (blobs: seq[KzgBlob]): Result[ExtendedMatrix, cstring] = proc compute_extended_matrix* (blobs: seq[KzgBlob]): Result[ExtendedMatrix, cstring] =
# This helper demonstrates the relationship between blobs and `ExtendedMatrix` # This helper demonstrates the relationship between blobs and `ExtendedMatrix`
var extended_matrix: ExtendedMatrix var extended_matrix: ExtendedMatrix
for blob in blobs: for blob in blobs:
let computed_cell = computeCellsAndKzgProofs(blob) let res = computeCells(ctx, blob)
discard extended_matrix.add(computed_cell)
if res.isErr:
return err("Error computing kzg cells and kzg proofs")
discard extended_matrix.add(res.get())
ok(extended_matrix) ok(extended_matrix)
proc recover_matrix*(cells_dict: Table[(BlobIndex, CellID), Cell], blobCount: uint64): Result[ExtendedMatrix, cstring] = # https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/specs/_features/eip7594/das-core.md#recover_matrix
proc recover_matrix*(cells_dict: Table[(BlobIndex, CellID), KzgCell], blobCount: uint64): Result[ExtendedMatrix, cstring] =
# This helper demonstrates how to apply recover_all_cells # This helper demonstrates how to apply recover_all_cells
# The data structure for storing cells is implementation-dependent # The data structure for storing cells is implementation-dependent
@ -92,7 +97,7 @@ proc recover_matrix*(cells_dict: Table[(BlobIndex, CellID), Cell], blobCount: ui
if blIdx == blobIndex: if blIdx == blobIndex:
cellIds.add(cellId) cellIds.add(cellId)
var cells: seq[Cell] = @[] var cells: seq[KzgCell] = @[]
for cellId in cellIds: for cellId in cellIds:
var interim_key = (BlobIndex(blobIndex), cellId) var interim_key = (BlobIndex(blobIndex), cellId)
@ -102,79 +107,91 @@ proc recover_matrix*(cells_dict: Table[(BlobIndex, CellID), Cell], blobCount: ui
cells.add(cell) cells.add(cell)
except: except:
debug "DataColumn: Key not found in Cell Dictionary", interim_key debug "DataColumn: Key not found in Cell Dictionary", interim_key
var allCellsForRow: Cells
allCellsForRow = recoverAllCells(cellIds, cells) let allCellsForRow = recoverAllCells(ctx, cellIds, cells)
discard extended_matrix.add(allCellsForRow) discard extended_matrix.add(allCellsForRow.get())
ok(extended_matrix) ok(extended_matrix)
proc get_data_column_sidecars*(signed_block: deneb.SignedBeaconBlock, blobs: seq[KzgBlob]): Result[seq[DataColumnSidecar]] = # https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/specs/_features/eip7594/das-core.md#get_data_column_sidecars
proc get_data_column_sidecars*(signed_block: deneb.SignedBeaconBlock, blobs: seq[KzgBlob]): Result[seq[DataColumnSidecar], cstring] =
# #### `get_data_column_sidecars`
var sidecar: DataColumnSidecar
var signed_block_header: deneb.SignedBeaconBlockHeader var signed_block_header: deneb.SignedBeaconBlockHeader
var blck = signed_block.message var blck = signed_block.message
let
kzgCommitmentInclusionProof = build_proof(blck.body, 32'u64)
if kzgCommitmentInclusionProof.isErr(): var cellsAndProofs: seq[KzgCellsAndKzgProofs] = @[]
fatal "EIP7549: Could not compute Merkle proof"
var cellsAndProofs: seq[CellsAndProofs]
for blob in blobs: for blob in blobs:
let let
computed_cell = computeCellsAndKzgProofs(blob) computed_cell = computeCellsAndKzgProofs(ctx, blob)
if computed_cell.isErr(): if computed_cell.isErr():
fatal "EIP7549: Could not compute cells" fatal "EIP7549: Could not compute cells"
cellsAndProofs.add(computed_cell) cellsAndProofs.add(computed_cell.get())
let blobCount = blobs.len let blobCount = blobs.len
var cells: seq[seq[Cell]] = @[] var
var proofs: seq[seq[KzgProof]] = @[] cells: seq[seq[KzgCell]]
proofs: seq[seq[KzgProof]]
for i in 0..<blobCount: for i in 0..<blobCount:
cells.add(cellsAndProofs.cells) cells[i].add(cellsAndProofs[i].cells[0])
proofs.add(cellsAndProofs.proofs) proofs[i].add(cellsAndProofs[i].proofs[1])
var sidecars: seq[DataColumnSidecar] = @[] var sidecars: seq[DataColumnSidecar] = @[]
for columnIndex in 0..<NUMBER_OF_COLUMNS: for columnIndex in 0..<NUMBER_OF_COLUMNS:
var column: DataColumn var column: DataColumn
var cellsForColumn: seq[Cell] = @[]
for rowIndex in 0..<blobCount: for rowIndex in 0..<blobCount:
cellsForColumn.add(cells[rowIndex][columnIndex]) column[rowIndex] = cells[rowIndex][columnIndex]
column = DataColumn(cellsForColumn)
var kzgProofOfColumn: seq[KzgProof] = @[] var kzgProofOfColumn: List[KzgProof, Limit(MAX_BLOB_COMMITMENTS_PER_BLOCK)]
for rowIndex in 0..<blobCount: for rowIndex in 0..<blobCount:
kzgProofOfColumn.add(proofs[rowIndex][columnIndex]) kzgProofOfColumn[rowIndex] = proofs[rowIndex][columnIndex]
var sidecar = DataColumnSidecar( sidecar = DataColumnSidecar(
index: columnIndex, index: uint64(columnIndex),
column: column, column: column,
kzgCommitments: blck.body.blob_kzg_commitments, kzgCommitments: blck.body.blob_kzg_commitments,
kzgProofs: kzgProofOfColumn, kzgProofs: kzgProofOfColumn,
signed_block_header: signed_block_header, signed_block_header: signed_block_header
kzg_commitments_inclusion_proof: kzgCommitmentInclusionProof
) )
blck.body.build_proof(
kzg_commitment_inclusion_proof_gindex(BlobIndex(columnIndex)),
sidecar.kzg_commitments_inclusion_proof).expect("Valid gindex")
sidecars.add(sidecar) sidecars.add(sidecar)
ok(sidecars) ok(sidecars)
# Helper function to `verifyCellKzgProofBatch` at https://github.com/ethereum/c-kzg-4844/blob/das/bindings/nim/kzg_ex.nim#L170
proc validate_data_column_sidecar*(
expected_commitments: seq[KzgCommitment], rowIndex: seq[RowIndex], columnIndex: seq[ColumnIndex], column: seq[KzgCell],
proofs: seq[KzgProof]): Result[void, string] =
proc verify_data_column_sidecar_kzg_proofs* (sidecar: DataColumnSidecar): Result[bool, cstring] = let res = verifyCellKzgProofBatch(expected_commitments, rowIndex, columnIndex, column, proofs).valueOr:
return err("DataColumnSidecar: Proof verification error: " & error())
if not res:
return err("DataColumnSidecar: Proof verification failed")
ok()
# https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/specs/_features/eip7594/p2p-interface.md#verify_data_column_sidecar_kzg_proofs
proc verify_data_column_sidecar_kzg_proofs*(sidecar: DataColumnSidecar): Result[void, string] =
# Verifying if the KZG proofs are correct # Verifying if the KZG proofs are correct
# Check if the data column sidecar index < NUMBER_OF_COLUMNS # Check if the data column sidecar index < NUMBER_OF_COLUMNS
if not (sidecar.index < NUMBER_OF_COLUMNS): if not (sidecar.index < NUMBER_OF_COLUMNS):
return err("EIP7549: Data column sidecar index exceeds the NUMBER_OF_COLUMNS") return err("EIP7594: Data column sidecar index exceeds the NUMBER_OF_COLUMNS")
# Check is the sidecar column length == sidecar.kzg_commitments length == sidecar.kzg_proofs mixInLength # Check is the sidecar column length == sidecar.kzg_commitments length == sidecar.kzg_proofs mixInLength
if not (sidecar.column.len == sidecar.kzg_commitments.len and sidecar.kzg_commitments.len == sidecar.kzg_proofs.len): if not (sidecar.column.len == sidecar.kzg_commitments.len):
return err("EIP7549: Data column sidecar column length does not match the kzg_commitments length or kzg_proofs length") return err("EIP7594: Data column sidecar length is not equal to the kzg_commitments length")
if not (sidecar.kzg_commitments.len == sidecar.kzg_proofs.len):
return err("EIP7594: Data column sidecar kzg_commitments length is not equal to the kzg_proofs length")
# Iterate through the row indices # Iterate through the row indices
var rowIndices: seq[RowIndex] = @[] var rowIndices: seq[RowIndex] = @[]
@ -184,15 +201,36 @@ proc verify_data_column_sidecar_kzg_proofs* (sidecar: DataColumnSidecar): Result
# Iterate through the column indices # Iterate through the column indices
var colIndices: seq[ColumnIndex] = @[] var colIndices: seq[ColumnIndex] = @[]
for i in 0..<sidecar.column.len: for i in 0..<sidecar.column.len:
colIndices.add(ColumnIndex(i)) colIndices.add(sidecar.index * uint64(sidecar.column.len))
let kzgCommits = sidecar.kzg_commitments.asSeq
let sidecarCol = sidecar.column.asSeq
let kzgProofs = sidecar.kzg_proofs.asSeq
# KZG batch verifies that the cells match the corresponding commitments and KZG proofs # KZG batch verifies that the cells match the corresponding commitments and KZG proofs
var res = verifyCellKzgProofBatch( let res = validate_data_column_sidecar(
sidecar.kzg_commitments, kzgCommits,
rowIndices, rowIndices,
colIndices, colIndices,
sidecar.column, sidecarCol,
sidecar.kzg_proofs kzgProofs)
)
ok(res) if res.isErr():
return err("DataColumnSidecar: validation failed")
ok()
# https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/specs/_features/eip7594/p2p-interface.md#verify_data_column_sidecar_inclusion_proof
proc verify_data_column_sidecar_inclusion_proof*(sidecar: DataColumnSidecar): Result[void, string] =
# Verify if the given KZG commitments are included in the beacon block
let gindex = kzg_commitment_inclusion_proof_gindex(sidecar.index)
if not is_valid_merkle_branch(
hash_tree_root(sidecar.kzg_commitments),
sidecar.kzg_commitments_inclusion_proof,
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH,
get_subtree_index(gindex),
sidecar.signed_block_header.message.body_root):
return err("DataColumnSidecar: inclusion proof not valid")
ok()