Skip to content

Commit

Permalink
chore: Encapsulate all kzg functionality for PeerDAS into the kzg pac…
Browse files Browse the repository at this point in the history
…kage (#14136)

* chore: move all ckzg related functionality into kzg package

* refactor code to match

* run: bazel run //:gazelle -- fix

* chore: add some docs and stop copying large objects when converting between types

* fixes

* manually add kzg.go dep to Build.Hazel

* move kzg methods to kzg.go

* chore: add RecoverCellsAndProofs method

* bazel run //:gazelle -- fix

* use BytesPerBlob constant

* chore: fix some deepsource issues

* one declaration for commans and blobs
  • Loading branch information
kevaundray committed Jul 3, 2024
1 parent 913e84d commit b8f43c0
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 68 deletions.
2 changes: 2 additions & 0 deletions beacon-chain/blockchain/kzg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"kzg.go",
"trusted_setup.go",
"validation.go",
],
Expand All @@ -14,6 +15,7 @@ go_library(
"@com_github_crate_crypto_go_kzg_4844//:go_default_library",
"@com_github_ethereum_c_kzg_4844//bindings/go:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//crypto/kzg4844:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)
Expand Down
156 changes: 156 additions & 0 deletions beacon-chain/blockchain/kzg/kzg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package kzg

import (
"errors"

ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
)

// Blob represents a serialized chunk of data.
type Blob [BytesPerBlob]byte

// Commitment represent a KZG commitment to a Blob.
type Commitment [48]byte

// Proof represents a KZG proof that attests to the validity of a Blob or parts of it.
type Proof [48]byte

// Bytes48 is a 48-byte array.
type Bytes48 = ckzg4844.Bytes48

// Bytes32 is a 32-byte array.
type Bytes32 = ckzg4844.Bytes32

// BytesPerCell is the number of bytes in a single cell.
const BytesPerCell = ckzg4844.FieldElementsPerCell * ckzg4844.BytesPerFieldElement

// BytesPerBlob is the number of bytes in a single blob.
const BytesPerBlob = ckzg4844.BytesPerBlob

// FieldElementsPerCell is the number of field elements in a single cell.
// TODO: This should not be exposed.
const FieldElementsPerCell = ckzg4844.FieldElementsPerCell

// CellsPerExtBlob is the number of cells that we generate for a single blob.
// This is equivalent to the number of columns in the data matrix.
const CellsPerExtBlob = ckzg4844.CellsPerExtBlob

// Cell represents a chunk of an encoded Blob.
// TODO: This is not correctly sized in c-kzg
// TODO: It should be a vector of bytes
// TODO: Note that callers of this package rely on `BytesPerCell`
type Cell ckzg4844.Cell

func BlobToKZGCommitment(blob *Blob) (Commitment, error) {
comm, err := kzg4844.BlobToCommitment(kzg4844.Blob(*blob))
if err != nil {
return Commitment{}, err
}
return Commitment(comm), nil
}

func ComputeBlobKZGProof(blob *Blob, commitment Commitment) (Proof, error) {
proof, err := kzg4844.ComputeBlobProof(kzg4844.Blob(*blob), kzg4844.Commitment(commitment))
if err != nil {
return [48]byte{}, err
}
return Proof(proof), nil
}

func ComputeCellsAndKZGProofs(blob *Blob) ([ckzg4844.CellsPerExtBlob]Cell, [ckzg4844.CellsPerExtBlob]Proof, error) {
ckzgBlob := ckzg4844.Blob(*blob)
_cells, _proofs, err := ckzg4844.ComputeCellsAndKZGProofs(&ckzgBlob)
if err != nil {
return [ckzg4844.CellsPerExtBlob]Cell{}, [ckzg4844.CellsPerExtBlob]Proof{}, err
}

// Convert Cells and Proofs to types defined in this package
var cells [ckzg4844.CellsPerExtBlob]Cell
for i := range _cells {
cells[i] = Cell(_cells[i])
}

var proofs [ckzg4844.CellsPerExtBlob]Proof
for i := range _proofs {
proofs[i] = Proof(_proofs[i])
}

return cells, proofs, nil
}

// VerifyCellKZGProof is unused. TODO: We can check when the batch size for `VerifyCellKZGProofBatch` is 1
// and call this, though I think its better if the cryptography library handles this.
func VerifyCellKZGProof(commitmentBytes Bytes48, cellId uint64, cell *Cell, proofBytes Bytes48) (bool, error) {
return ckzg4844.VerifyCellKZGProof(commitmentBytes, cellId, ckzg4844.Cell(*cell), proofBytes)
}

func VerifyCellKZGProofBatch(commitmentsBytes []Bytes48, rowIndices, columnIndices []uint64, _cells []Cell, proofsBytes []Bytes48) (bool, error) {
// Convert `Cell` type to `ckzg4844.Cell`
ckzgCells := make([]ckzg4844.Cell, len(_cells))
for i := range _cells {
ckzgCells[i] = ckzg4844.Cell(_cells[i])
}

return ckzg4844.VerifyCellKZGProofBatch(commitmentsBytes, rowIndices, columnIndices, ckzgCells, proofsBytes)
}

func RecoverAllCells(cellIds []uint64, _cells []Cell) ([ckzg4844.CellsPerExtBlob]Cell, error) {
// Convert `Cell` type to `ckzg4844.Cell`
ckzgCells := make([]ckzg4844.Cell, len(_cells))
for i := range _cells {
ckzgCells[i] = ckzg4844.Cell(_cells[i])
}

recoveredCells, err := ckzg4844.RecoverAllCells(cellIds, ckzgCells)
if err != nil {
return [ckzg4844.CellsPerExtBlob]Cell{}, err
}

// This should never happen, we return an error instead of panicking.
if len(recoveredCells) != ckzg4844.CellsPerExtBlob {
return [ckzg4844.CellsPerExtBlob]Cell{}, errors.New("recovered cells length is not equal to CellsPerExtBlob")
}

// Convert `ckzg4844.Cell` type to `Cell`
var ret [ckzg4844.CellsPerExtBlob]Cell
for i := range recoveredCells {
ret[i] = Cell(recoveredCells[i])
}
return ret, nil
}

// RecoverCellsAndKZGProofs recovers the cells and compute the KZG Proofs associated with the cells.
//
// This method will supersede the `RecoverAllCells` and `CellsToBlob` methods.
func RecoverCellsAndKZGProofs(cellIds []uint64, _cells []Cell) ([ckzg4844.CellsPerExtBlob]Cell, [ckzg4844.CellsPerExtBlob]Proof, error) {
// First recover all of the cells
recoveredCells, err := RecoverAllCells(cellIds, _cells)
if err != nil {
return [ckzg4844.CellsPerExtBlob]Cell{}, [ckzg4844.CellsPerExtBlob]Proof{}, err
}

// Extract the Blob from all of the Cells
blob, err := CellsToBlob(&recoveredCells)
if err != nil {
return [ckzg4844.CellsPerExtBlob]Cell{}, [ckzg4844.CellsPerExtBlob]Proof{}, err
}

// Compute all of the cells and KZG proofs
return ComputeCellsAndKZGProofs(&blob)
}

func CellsToBlob(_cells *[ckzg4844.CellsPerExtBlob]Cell) (Blob, error) {
// Convert `Cell` type to `ckzg4844.Cell`
var ckzgCells [ckzg4844.CellsPerExtBlob]ckzg4844.Cell
for i := range _cells {
ckzgCells[i] = ckzg4844.Cell(_cells[i])
}

blob, err := ckzg4844.CellsToBlob(ckzgCells)
if err != nil {
return Blob{}, err
}

return Blob(blob), nil
}
3 changes: 1 addition & 2 deletions beacon-chain/core/peerdas/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/peerdas",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/blockchain/kzg:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_ethereum_c_kzg_4844//bindings/go:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
Expand All @@ -32,7 +32,6 @@ go_test(
"//testing/util:go_default_library",
"@com_github_consensys_gnark_crypto//ecc/bls12-381/fr:go_default_library",
"@com_github_crate_crypto_go_kzg_4844//:go_default_library",
"@com_github_ethereum_c_kzg_4844//bindings/go:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)
61 changes: 29 additions & 32 deletions beacon-chain/core/peerdas/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
"math"
"math/big"

cKzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/holiman/uint256"
errors "github.com/pkg/errors"

"github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/kzg"
"github.com/prysmaticlabs/prysm/v5/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
Expand All @@ -20,11 +20,8 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)

// Bytes per cell
const (
CustodySubnetCountEnrKey = "csc"

bytesPerCell = cKzg4844.FieldElementsPerCell * cKzg4844.BytesPerFieldElement
)

// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/p2p-interface.md#the-discovery-domain-discv5
Expand Down Expand Up @@ -94,7 +91,7 @@ func CustodyColumns(nodeId enode.ID, custodySubnetCount uint64) (map[uint64]bool
return nil, errors.Wrap(err, "custody subnets")
}

columnsPerSubnet := cKzg4844.CellsPerExtBlob / dataColumnSidecarSubnetCount
columnsPerSubnet := kzg.CellsPerExtBlob / dataColumnSidecarSubnetCount

// Knowing the subnet ID and the number of columns per subnet, select all the columns the node should custody.
// Columns belonging to the same subnet are contiguous.
Expand All @@ -111,7 +108,7 @@ func CustodyColumns(nodeId enode.ID, custodySubnetCount uint64) (map[uint64]bool

// DataColumnSidecars computes the data column sidecars from the signed block and blobs.
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#recover_matrix
func DataColumnSidecars(signedBlock interfaces.ReadOnlySignedBeaconBlock, blobs []cKzg4844.Blob) ([]*ethpb.DataColumnSidecar, error) {
func DataColumnSidecars(signedBlock interfaces.ReadOnlySignedBeaconBlock, blobs []kzg.Blob) ([]*ethpb.DataColumnSidecar, error) {
blobsCount := len(blobs)
if blobsCount == 0 {
return nil, nil
Expand Down Expand Up @@ -140,12 +137,12 @@ func DataColumnSidecars(signedBlock interfaces.ReadOnlySignedBeaconBlock, blobs
}

// Compute cells and proofs.
cells := make([][cKzg4844.CellsPerExtBlob]cKzg4844.Cell, 0, blobsCount)
proofs := make([][cKzg4844.CellsPerExtBlob]cKzg4844.KZGProof, 0, blobsCount)
cells := make([][kzg.CellsPerExtBlob]kzg.Cell, 0, blobsCount)
proofs := make([][kzg.CellsPerExtBlob]kzg.Proof, 0, blobsCount)

for i := range blobs {
blob := &blobs[i]
blobCells, blobProofs, err := cKzg4844.ComputeCellsAndKZGProofs(blob)
blobCells, blobProofs, err := kzg.ComputeCellsAndKZGProofs(blob)
if err != nil {
return nil, errors.Wrap(err, "compute cells and KZG proofs")
}
Expand All @@ -155,10 +152,10 @@ func DataColumnSidecars(signedBlock interfaces.ReadOnlySignedBeaconBlock, blobs
}

// Get the column sidecars.
sidecars := make([]*ethpb.DataColumnSidecar, 0, cKzg4844.CellsPerExtBlob)
for columnIndex := uint64(0); columnIndex < cKzg4844.CellsPerExtBlob; columnIndex++ {
column := make([]cKzg4844.Cell, 0, blobsCount)
kzgProofOfColumn := make([]cKzg4844.KZGProof, 0, blobsCount)
sidecars := make([]*ethpb.DataColumnSidecar, 0, kzg.CellsPerExtBlob)
for columnIndex := uint64(0); columnIndex < kzg.CellsPerExtBlob; columnIndex++ {
column := make([]kzg.Cell, 0, blobsCount)
kzgProofOfColumn := make([]kzg.Proof, 0, blobsCount)

for rowIndex := 0; rowIndex < blobsCount; rowIndex++ {
cell := cells[rowIndex][columnIndex]
Expand All @@ -172,7 +169,7 @@ func DataColumnSidecars(signedBlock interfaces.ReadOnlySignedBeaconBlock, blobs
for i := range column {
cell := column[i]

cellBytes := make([]byte, 0, bytesPerCell)
cellBytes := make([]byte, 0, kzg.BytesPerCell)
for _, fieldElement := range cell {
copiedElem := fieldElement
cellBytes = append(cellBytes, copiedElem[:]...)
Expand Down Expand Up @@ -208,20 +205,20 @@ func DataColumnSidecarsForReconstruct(
blobKzgCommitments [][]byte,
signedBlockHeader *ethpb.SignedBeaconBlockHeader,
kzgCommitmentsInclusionProof [][]byte,
blobs []cKzg4844.Blob,
blobs []kzg.Blob,
) ([]*ethpb.DataColumnSidecar, error) {
blobsCount := len(blobs)
if blobsCount == 0 {
return nil, nil
}

// Compute cells and proofs.
cells := make([][cKzg4844.CellsPerExtBlob]cKzg4844.Cell, 0, blobsCount)
proofs := make([][cKzg4844.CellsPerExtBlob]cKzg4844.KZGProof, 0, blobsCount)
cells := make([][kzg.CellsPerExtBlob]kzg.Cell, 0, blobsCount)
proofs := make([][kzg.CellsPerExtBlob]kzg.Proof, 0, blobsCount)

for i := range blobs {
blob := &blobs[i]
blobCells, blobProofs, err := cKzg4844.ComputeCellsAndKZGProofs(blob)
blobCells, blobProofs, err := kzg.ComputeCellsAndKZGProofs(blob)
if err != nil {
return nil, errors.Wrap(err, "compute cells and KZG proofs")
}
Expand All @@ -231,10 +228,10 @@ func DataColumnSidecarsForReconstruct(
}

// Get the column sidecars.
sidecars := make([]*ethpb.DataColumnSidecar, 0, cKzg4844.CellsPerExtBlob)
for columnIndex := uint64(0); columnIndex < cKzg4844.CellsPerExtBlob; columnIndex++ {
column := make([]cKzg4844.Cell, 0, blobsCount)
kzgProofOfColumn := make([]cKzg4844.KZGProof, 0, blobsCount)
sidecars := make([]*ethpb.DataColumnSidecar, 0, kzg.CellsPerExtBlob)
for columnIndex := uint64(0); columnIndex < kzg.CellsPerExtBlob; columnIndex++ {
column := make([]kzg.Cell, 0, blobsCount)
kzgProofOfColumn := make([]kzg.Proof, 0, blobsCount)

for rowIndex := 0; rowIndex < blobsCount; rowIndex++ {
cell := cells[rowIndex][columnIndex]
Expand All @@ -248,7 +245,7 @@ func DataColumnSidecarsForReconstruct(
for i := range column {
cell := column[i]

cellBytes := make([]byte, 0, bytesPerCell)
cellBytes := make([]byte, 0, kzg.BytesPerCell)
for _, fieldElement := range cell {
copiedElem := fieldElement
cellBytes = append(cellBytes, copiedElem[:]...)
Expand Down Expand Up @@ -297,23 +294,23 @@ func VerifyDataColumnSidecarKZGProofs(sc *ethpb.DataColumnSidecar) (bool, error)
colI := sc.ColumnIndex
colIdx = append(colIdx, colI)
}
ckzgComms := make([]cKzg4844.Bytes48, 0, len(sc.KzgCommitments))
ckzgComms := make([]kzg.Bytes48, 0, len(sc.KzgCommitments))
for _, com := range sc.KzgCommitments {
ckzgComms = append(ckzgComms, cKzg4844.Bytes48(com))
ckzgComms = append(ckzgComms, kzg.Bytes48(com))
}
var cells []cKzg4844.Cell
var cells []kzg.Cell
for _, ce := range sc.DataColumn {
var newCell []cKzg4844.Bytes32
var newCell []kzg.Bytes32
for i := 0; i < len(ce); i += 32 {
newCell = append(newCell, cKzg4844.Bytes32(ce[i:i+32]))
newCell = append(newCell, kzg.Bytes32(ce[i:i+32]))
}
cells = append(cells, cKzg4844.Cell(newCell))
cells = append(cells, kzg.Cell(newCell))
}
var proofs []cKzg4844.Bytes48
var proofs []kzg.Bytes48
for _, p := range sc.KzgProof {
proofs = append(proofs, cKzg4844.Bytes48(p))
proofs = append(proofs, kzg.Bytes48(p))
}
return cKzg4844.VerifyCellKZGProofBatch(ckzgComms, rowIdx, colIdx, cells, proofs)
return kzg.VerifyCellKZGProofBatch(ckzgComms, rowIdx, colIdx, cells, proofs)
}

// CustodySubnetCount returns the number of subnets the node should participate in for custody.
Expand Down
Loading

0 comments on commit b8f43c0

Please sign in to comment.