Skip to content

Commit

Permalink
Peer das core (#13877)
Browse files Browse the repository at this point in the history
* Bump `c-kzg-4844` lib to the `das` branch.

* Implement `MerkleProofKZGCommitments`.

* Implement `das-core.md`.

* Use `peerdas.CustodyColumnSubnets` and `peerdas.CustodyColumns`.

* `CustodyColumnSubnets`: Include `i` in the for loop.

* Remove `computeSubscribedColumnSubnet`.

* Remove `peerdas.CustodyColumns` out of the for loop.
  • Loading branch information
nalepae committed Aug 27, 2024
1 parent 98e2aee commit 57f6c2e
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 71 deletions.
21 changes: 21 additions & 0 deletions beacon-chain/core/peerdas/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load("@prysm//tools/go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["helpers.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/peerdas",
visibility = ["//visibility:public"],
deps = [
"//config/fieldparams: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_holiman_uint256//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)
245 changes: 245 additions & 0 deletions beacon-chain/core/peerdas/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
package peerdas

import (
"encoding/binary"

cKzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
"github.com/holiman/uint256"

"github.com/ethereum/go-ethereum/p2p/enode"
errors "github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)

const (
// Number of field elements per extended blob
fieldElementsPerExtBlob = 2 * cKzg4844.FieldElementsPerBlob

// Bytes per cell
bytesPerCell = cKzg4844.FieldElementsPerCell * cKzg4844.BytesPerFieldElement

// Number of cells in the extended matrix
extendedMatrixSize = fieldparams.MaxBlobsPerBlock * cKzg4844.CellsPerExtBlob
)

type (
extendedMatrix []cKzg4844.Cell

cellCoordinate struct {
blobIndex uint64
cellID uint64
}
)

var (
errCustodySubnetCountTooLarge = errors.New("custody subnet count larger than data column sidecar subnet count")
errCellNotFound = errors.New("cell not found (should never happen)")
errCurveOrder = errors.New("could not set bls curve order as big int")
errBlsFieldElementNil = errors.New("bls field element is nil")
errBlsFieldElementBiggerThanCurveOrder = errors.New("bls field element higher than curve order")
errBlsFieldElementDoesNotFit = errors.New("bls field element does not fit in BytesPerFieldElement")
)

// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#helper-functions
func CustodyColumns(nodeId enode.ID, custodySubnetCount uint64) (map[uint64]bool, error) {
dataColumnSidecarSubnetCount := params.BeaconConfig().DataColumnSidecarSubnetCount

// Compute the custodied subnets.
subnetIds, err := CustodyColumnSubnets(nodeId, custodySubnetCount)
if err != nil {
return nil, errors.Wrap(err, "custody subnets")
}

columnsPerSubnet := cKzg4844.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.
columnIndices := make(map[uint64]bool, custodySubnetCount*columnsPerSubnet)
for i := uint64(0); i < columnsPerSubnet; i++ {
for subnetId := range subnetIds {
columnIndex := dataColumnSidecarSubnetCount*i + subnetId
columnIndices[columnIndex] = true
}
}

return columnIndices, nil
}

func CustodyColumnSubnets(nodeId enode.ID, custodySubnetCount uint64) (map[uint64]bool, error) {
dataColumnSidecarSubnetCount := params.BeaconConfig().DataColumnSidecarSubnetCount

// Check if the custody subnet count is larger than the data column sidecar subnet count.
if custodySubnetCount > dataColumnSidecarSubnetCount {
return nil, errCustodySubnetCountTooLarge
}

// First, compute the subnet IDs that the node should participate in.
subnetIds := make(map[uint64]bool, custodySubnetCount)

for i := uint64(0); uint64(len(subnetIds)) < custodySubnetCount; i++ {
nodeIdUInt256, nextNodeIdUInt256 := new(uint256.Int), new(uint256.Int)
nodeIdUInt256.SetBytes(nodeId.Bytes())
nextNodeIdUInt256.Add(nodeIdUInt256, uint256.NewInt(i))
nextNodeIdUInt64 := nextNodeIdUInt256.Uint64()
nextNodeId := bytesutil.Uint64ToBytesLittleEndian(nextNodeIdUInt64)

hashedNextNodeId := hash.Hash(nextNodeId)
subnetId := binary.LittleEndian.Uint64(hashedNextNodeId[:8]) % dataColumnSidecarSubnetCount

if _, exists := subnetIds[subnetId]; !exists {
subnetIds[subnetId] = true
}
}

return subnetIds, nil
}

// computeExtendedMatrix computes the extended matrix from the blobs.
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#compute_extended_matrix
func computeExtendedMatrix(blobs []cKzg4844.Blob) (extendedMatrix, error) {
matrix := make(extendedMatrix, 0, extendedMatrixSize)

for i := range blobs {
// Chunk a non-extended blob into cells representing the corresponding extended blob.
blob := &blobs[i]
cells, err := cKzg4844.ComputeCells(blob)
if err != nil {
return nil, errors.Wrap(err, "compute cells for blob")
}

matrix = append(matrix, cells[:]...)
}

return matrix, nil
}

// recoverMatrix recovers the extended matrix from some cells.
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#recover_matrix
func recoverMatrix(cellFromCoordinate map[cellCoordinate]cKzg4844.Cell, blobCount uint64) (extendedMatrix, error) {
matrix := make(extendedMatrix, 0, extendedMatrixSize)

for blobIndex := uint64(0); blobIndex < blobCount; blobIndex++ {
// Filter all cells that belong to the current blob.
cellIds := make([]uint64, 0, cKzg4844.CellsPerExtBlob)
for coordinate := range cellFromCoordinate {
if coordinate.blobIndex == blobIndex {
cellIds = append(cellIds, coordinate.cellID)
}
}

// Retrieve cells corresponding to all `cellIds`.
cellIdsCount := len(cellIds)

cells := make([]cKzg4844.Cell, 0, cellIdsCount)
for _, cellId := range cellIds {
coordinate := cellCoordinate{blobIndex: blobIndex, cellID: cellId}
cell, ok := cellFromCoordinate[coordinate]
if !ok {
return matrix, errCellNotFound
}

cells = append(cells, cell)
}

// Recover all cells.
allCellsForRow, err := cKzg4844.RecoverAllCells(cellIds, cells)
if err != nil {
return matrix, errors.Wrap(err, "recover all cells")
}

matrix = append(matrix, allCellsForRow[:]...)
}

return matrix, nil
}

// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#recover_matrix
func dataColumnSidecars(signedBlock interfaces.SignedBeaconBlock, blobs []cKzg4844.Blob) ([]ethpb.DataColumnSidecar, error) {
blobsCount := len(blobs)

// Get the signed block header.
signedBlockHeader, err := signedBlock.Header()
if err != nil {
return nil, errors.Wrap(err, "signed block header")
}

// Get the block body.
block := signedBlock.Block()
blockBody := block.Body()

// Get the blob KZG commitments.
blobKzgCommitments, err := blockBody.BlobKzgCommitments()
if err != nil {
return nil, errors.Wrap(err, "blob KZG commitments")
}

// Compute the KZG commitments inclusion proof.
kzgCommitmentsInclusionProof, err := blocks.MerkleProofKZGCommitments(blockBody)
if err != nil {
return nil, errors.Wrap(err, "merkle proof ZKG commitments")
}

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

for i := range blobs {
blob := &blobs[i]
blobCells, blobProofs, err := cKzg4844.ComputeCellsAndProofs(blob)
if err != nil {
return nil, errors.Wrap(err, "compute cells and proofs")
}

cells = append(cells, blobCells)
proofs = append(proofs, blobProofs)
}

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

for rowIndex := 0; rowIndex < blobsCount; rowIndex++ {
cell := cells[rowIndex][columnIndex]
column = append(column, cell)

kzgProof := proofs[rowIndex][columnIndex]
kzgProofOfColumn = append(kzgProofOfColumn, kzgProof)
}

columnBytes := make([][]byte, 0, blobsCount)
for i := range column {
cell := column[i]

cellBytes := make([]byte, 0, bytesPerCell)
for _, fieldElement := range cell {
cellBytes = append(cellBytes, fieldElement[:]...)
}

columnBytes = append(columnBytes, cellBytes)
}

kzgProofOfColumnBytes := make([][]byte, 0, blobsCount)
for _, kzgProof := range kzgProofOfColumn {
kzgProofOfColumnBytes = append(kzgProofOfColumnBytes, kzgProof[:])
}

sidecars = append(sidecars, ethpb.DataColumnSidecar{
ColumnIndex: columnIndex,
DataColumn: columnBytes,
KzgCommitments: blobKzgCommitments,
KzgProof: kzgProofOfColumnBytes,
SignedBlockHeader: signedBlockHeader,
KzgCommitmentsInclusionProof: kzgCommitmentsInclusionProof,
})
}

return sidecars, nil
}
2 changes: 1 addition & 1 deletion beacon-chain/p2p/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ go_library(
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/feed/state:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/peerdas:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/p2p/encoder:go_default_library",
Expand All @@ -60,7 +61,6 @@ go_library(
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/leaky-bucket:go_default_library",
"//container/slice:go_default_library",
"//crypto/ecdsa:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
Expand Down
60 changes: 8 additions & 52 deletions beacon-chain/p2p/subnets.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import (
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/peerdas"
"github.com/prysmaticlabs/prysm/v5/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/v5/container/slice"
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
mathutil "github.com/prysmaticlabs/prysm/v5/math"
Expand Down Expand Up @@ -212,10 +212,16 @@ func initializePersistentColumnSubnets(id enode.ID) error {
if ok && expTime.After(time.Now()) {
return nil
}
subs, err := computeSubscribedColumnSubnets(id)
subsMap, err := peerdas.CustodyColumnSubnets(id, params.BeaconConfig().CustodyRequirement)
if err != nil {
return err
}

subs := make([]uint64, 0, len(subsMap))
for sub := range subsMap {
subs = append(subs, sub)
}

cache.ColumnSubnetIDs.AddColumnSubnets(subs)
return nil
}
Expand All @@ -239,46 +245,6 @@ func computeSubscribedSubnets(nodeID enode.ID, epoch primitives.Epoch) ([]uint64
return subs, nil
}

func ComputeCustodyColumns(nodeID enode.ID) ([]uint64, error) {
subs, err := computeSubscribedColumnSubnets(nodeID)
if err != nil {
return nil, err
}
colsPerSub := params.BeaconConfig().NumberOfColumns / params.BeaconConfig().DataColumnSidecarSubnetCount
colIdxs := []uint64{}
for _, sub := range subs {
for i := uint64(0); i < colsPerSub; i++ {
colId := params.BeaconConfig().DataColumnSidecarSubnetCount*i + sub
colIdxs = append(colIdxs, colId)
}
}
return colIdxs, nil
}

func computeSubscribedColumnSubnets(nodeID enode.ID) ([]uint64, error) {
subnetsPerNode := params.BeaconConfig().CustodyRequirement
subs := make([]uint64, 0, subnetsPerNode)

for i := uint64(0); i < subnetsPerNode; i++ {
sub, err := computeSubscribedColumnSubnet(nodeID, i)
if err != nil {
return nil, err
}
if slice.IsInUint64(sub, subs) {
continue
}
subs = append(subs, sub)
}
isubnetsPerNode, err := mathutil.Int(subnetsPerNode)
if err != nil {
return nil, err
}
if len(subs) != isubnetsPerNode {
return nil, errors.Errorf("inconsistent subnet assignment: %d vs %d", len(subs), isubnetsPerNode)
}
return subs, nil
}

// Spec pseudocode definition:
//
// def compute_subscribed_subnet(node_id: NodeID, epoch: Epoch, index: int) -> SubnetID:
Expand All @@ -304,16 +270,6 @@ func computeSubscribedSubnet(nodeID enode.ID, epoch primitives.Epoch, index uint
return subnet, nil
}

func computeSubscribedColumnSubnet(nodeID enode.ID, index uint64) (uint64, error) {
num := uint256.NewInt(0).SetBytes(nodeID.Bytes())
num = num.Add(num, uint256.NewInt(index))
num64bit := num.Uint64()
byteNum := bytesutil.Uint64ToBytesLittleEndian(num64bit)
hashedObj := hash.Hash(byteNum)
subnetID := bytesutil.FromBytes8(hashedObj[:8]) % params.BeaconConfig().DataColumnSidecarSubnetCount
return subnetID, nil
}

func computeSubscriptionExpirationTime(nodeID enode.ID, epoch primitives.Epoch) time.Duration {
nodeOffset, _ := computeOffsetAndPrefix(nodeID)
pastEpochs := (nodeOffset + uint64(epoch)) % params.BeaconConfig().EpochsPerSubnetSubscription
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/sync/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ go_library(
"//beacon-chain/core/feed/operation:go_default_library",
"//beacon-chain/core/feed/state:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/peerdas:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/core/transition/interop:go_default_library",
Expand Down
Loading

0 comments on commit 57f6c2e

Please sign in to comment.