Skip to content

Commit aa9ea66

Browse files
committed
peerDAS: Decouple network subnets from das-core.
ethereum/consensus-specs#3832
1 parent 5f08559 commit aa9ea66

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+902
-684
lines changed

beacon-chain/blockchain/process_block.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -652,16 +652,20 @@ func uint64MapToSortedSlice(input map[uint64]bool) []uint64 {
652652
}
653653

654654
func (s *Service) areDataColumnsAvailable(ctx context.Context, root [32]byte, signed interfaces.ReadOnlySignedBeaconBlock) error {
655-
if signed.Version() < version.Deneb {
655+
if signed.Version() < version.Fulu {
656656
return nil
657657
}
658658

659659
block := signed.Block()
660660
if block == nil {
661661
return errors.New("invalid nil beacon block")
662662
}
663+
663664
// We are only required to check within MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS
664-
if !params.WithinDAPeriod(slots.ToEpoch(block.Slot()), slots.ToEpoch(s.CurrentSlot())) {
665+
blockSlot, currentSlot := block.Slot(), s.CurrentSlot()
666+
blockEpoch, currentEpoch := slots.ToEpoch(blockSlot), slots.ToEpoch(currentSlot)
667+
668+
if !params.WithinDAPeriod(blockEpoch, currentEpoch) {
665669
return nil
666670
}
667671

@@ -681,20 +685,26 @@ func (s *Service) areDataColumnsAvailable(ctx context.Context, root [32]byte, si
681685
}
682686

683687
// All columns to sample need to be available for the block to be considered available.
684-
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#subnet-sampling
688+
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/das-core.md#custody-sampling
685689
nodeID := s.cfg.P2P.NodeID()
686-
subnetSamplingSize := peerdas.SubnetSamplingSize()
690+
custodyGroupSamplingSize := peerdas.CustodyGroupSamplingSize()
687691

688-
colMap, err := peerdas.CustodyColumns(nodeID, subnetSamplingSize)
692+
custodyGroups, err := peerdas.CustodyGroups(nodeID, custodyGroupSamplingSize)
689693
if err != nil {
690-
return errors.Wrap(err, "custody columns")
694+
return errors.Wrap(err, "custody groups")
691695
}
692696

693-
// colMap represents the data columnns a node is expected to custody.
694-
if len(colMap) == 0 {
697+
// Exit early if the node is not expected to custody any data columns.
698+
if len(custodyGroups) == 0 {
695699
return nil
696700
}
697701

702+
// Get the custody columns from the groups.
703+
columnsMap, err := peerdas.CustodyColumns(custodyGroups)
704+
if err != nil {
705+
return errors.Wrap(err, "custody columns")
706+
}
707+
698708
// Subscribe to newsly data columns stored in the database.
699709
rootIndexChan := make(chan filesystem.RootIndexPair)
700710
subscription := s.blobStorage.DataColumnFeed.Subscribe(rootIndexChan)
@@ -715,7 +725,7 @@ func (s *Service) areDataColumnsAvailable(ctx context.Context, root [32]byte, si
715725
}
716726

717727
// Get a map of data column indices that are not currently available.
718-
missingMap, err := missingDataColumns(s.blobStorage, root, colMap)
728+
missingMap, err := missingDataColumns(s.blobStorage, root, columnsMap)
719729
if err != nil {
720730
return err
721731
}
@@ -743,10 +753,10 @@ func (s *Service) areDataColumnsAvailable(ctx context.Context, root [32]byte, si
743753
)
744754

745755
numberOfColumns := params.BeaconConfig().NumberOfColumns
746-
colMapCount := uint64(len(colMap))
756+
colMapCount := uint64(len(columnsMap))
747757

748758
if colMapCount < numberOfColumns {
749-
expected = uint64MapToSortedSlice(colMap)
759+
expected = uint64MapToSortedSlice(columnsMap)
750760
}
751761

752762
if missingMapCount < numberOfColumns {

beacon-chain/core/peerdas/helpers.go

Lines changed: 126 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -30,41 +30,41 @@ import (
3030
)
3131

3232
const (
33-
CustodySubnetCountEnrKey = "csc"
33+
CustodyGroupCountEnrKey = "cgc"
3434
)
3535

36-
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/p2p-interface.md#the-discovery-domain-discv5
37-
type Csc uint64
36+
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/p2p-interface.md#the-discovery-domain-discv5
37+
type Cgc uint64
3838

39-
func (Csc) ENRKey() string { return CustodySubnetCountEnrKey }
39+
func (Cgc) ENRKey() string { return CustodyGroupCountEnrKey }
4040

4141
var (
4242
// Custom errors
43-
errCustodySubnetCountTooLarge = errors.New("custody subnet count larger than data column sidecar subnet count")
44-
errIndexTooLarge = errors.New("column index is larger than the specified columns count")
45-
errMismatchLength = errors.New("mismatch in the length of the commitments and proofs")
46-
errRecordNil = errors.New("record is nil")
47-
errCannotLoadCustodySubnetCount = errors.New("cannot load the custody subnet count from peer")
43+
errCustodyGroupCountTooLarge = errors.New("custody group count too large")
44+
errWrongComputedCustodyGroupCount = errors.New("wrong computed custody group count, should never happen")
45+
errIndexTooLarge = errors.New("column index is larger than the specified columns count")
46+
errMismatchLength = errors.New("mismatch in the length of the commitments and proofs")
47+
errRecordNil = errors.New("record is nil")
48+
errCannotLoadCustodyGroupCount = errors.New("cannot load the custody group count from peer")
4849

4950
// maxUint256 is the maximum value of a uint256.
5051
maxUint256 = &uint256.Int{math.MaxUint64, math.MaxUint64, math.MaxUint64, math.MaxUint64}
5152
)
5253

53-
// CustodyColumnSubnets computes the subnets the node should participate in for custody.
54-
func CustodyColumnSubnets(nodeId enode.ID, custodySubnetCount uint64) (map[uint64]bool, error) {
55-
dataColumnSidecarSubnetCount := params.BeaconConfig().DataColumnSidecarSubnetCount
54+
// CustodyGroups computes the custody groups the node should participate in for custody.
55+
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/das-core.md#get_custody_groups
56+
func CustodyGroups(nodeId enode.ID, custodyGroupCount uint64) (map[uint64]bool, error) {
57+
numberOfCustodyGroup := params.BeaconConfig().NumberOfCustodyGroups
5658

57-
// Check if the custody subnet count is larger than the data column sidecar subnet count.
58-
if custodySubnetCount > dataColumnSidecarSubnetCount {
59-
return nil, errCustodySubnetCountTooLarge
59+
// Check if the custody group count is larger than the number of custody groups.
60+
if custodyGroupCount > numberOfCustodyGroup {
61+
return nil, errCustodyGroupCountTooLarge
6062
}
6163

62-
// First, compute the subnet IDs that the node should participate in.
63-
subnetIds := make(map[uint64]bool, custodySubnetCount)
64-
64+
custodyGroups := make(map[uint64]bool, custodyGroupCount)
6565
one := uint256.NewInt(1)
6666

67-
for currentId := new(uint256.Int).SetBytes(nodeId.Bytes()); uint64(len(subnetIds)) < custodySubnetCount; currentId.Add(currentId, one) {
67+
for currentId := new(uint256.Int).SetBytes(nodeId.Bytes()); uint64(len(custodyGroups)) < custodyGroupCount; currentId.Add(currentId, one) {
6868
// Convert to big endian bytes.
6969
currentIdBytesBigEndian := currentId.Bytes32()
7070

@@ -74,49 +74,112 @@ func CustodyColumnSubnets(nodeId enode.ID, custodySubnetCount uint64) (map[uint6
7474
// Hash the result.
7575
hashedCurrentId := hash.Hash(currentIdBytesLittleEndian)
7676

77-
// Get the subnet ID.
78-
subnetId := binary.LittleEndian.Uint64(hashedCurrentId[:8]) % dataColumnSidecarSubnetCount
77+
// Get the custody group ID.
78+
custodyGroupId := binary.LittleEndian.Uint64(hashedCurrentId[:8]) % numberOfCustodyGroup
7979

80-
// Add the subnet to the map.
81-
subnetIds[subnetId] = true
80+
// Add the custody group to the map.
81+
custodyGroups[custodyGroupId] = true
8282

8383
// Overflow prevention.
8484
if currentId.Cmp(maxUint256) == 0 {
8585
currentId = uint256.NewInt(0)
8686
}
8787
}
8888

89-
return subnetIds, nil
89+
// Final check.
90+
if uint64(len(custodyGroups)) != custodyGroupCount {
91+
return nil, errWrongComputedCustodyGroupCount
92+
}
93+
94+
return custodyGroups, nil
9095
}
9196

92-
// CustodyColumns computes the columns the node should custody.
93-
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#helper-functions
94-
func CustodyColumns(nodeId enode.ID, custodySubnetCount uint64) (map[uint64]bool, error) {
95-
dataColumnSidecarSubnetCount := params.BeaconConfig().DataColumnSidecarSubnetCount
97+
// ComputeColumnsForCustodyGroup computes the columns for a given custody group.
98+
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/das-core.md#compute_columns_for_custody_group
99+
func ComputeColumnsForCustodyGroup(custodyGroup uint64) ([]uint64, error) {
100+
beaconConfig := params.BeaconConfig()
101+
numberOfCustodyGroup := beaconConfig.NumberOfCustodyGroups
96102

97-
// Compute the custody subnets.
98-
subnetIds, err := CustodyColumnSubnets(nodeId, custodySubnetCount)
99-
if err != nil {
100-
return nil, errors.Wrap(err, "custody subnets")
103+
if custodyGroup > numberOfCustodyGroup {
104+
return nil, errCustodyGroupCountTooLarge
105+
}
106+
107+
numberOfColumns := beaconConfig.NumberOfColumns
108+
109+
columnsPerGroup := numberOfColumns / numberOfCustodyGroup
110+
111+
columns := make([]uint64, 0, columnsPerGroup)
112+
for i := range columnsPerGroup {
113+
column := numberOfCustodyGroup*i + custodyGroup
114+
columns = append(columns, column)
101115
}
102116

103-
columnsPerSubnet := fieldparams.NumberOfColumns / dataColumnSidecarSubnetCount
117+
return columns, nil
118+
}
119+
120+
// ComputeCustodyGroupForColumn computes the custody group for a given column.
121+
// It is the reciprocal function of ComputeColumnsForCustodyGroup.
122+
func ComputeCustodyGroupForColumn(columnIndex uint64) (uint64, error) {
123+
beaconConfig := params.BeaconConfig()
124+
numberOfColumns := beaconConfig.NumberOfColumns
125+
126+
if columnIndex >= numberOfColumns {
127+
return 0, errIndexTooLarge
128+
}
129+
130+
numberOfCustodyGroups := beaconConfig.NumberOfCustodyGroups
131+
columnsPerGroup := numberOfColumns / numberOfCustodyGroups
132+
133+
return columnIndex / columnsPerGroup, nil
134+
}
135+
136+
// ComputeSubnetForDataColumnSidecar computes the subnet for a data column sidecar.
137+
// https://github.com/ethereum/consensus-specs/blob/dev/specs/fulu/p2p-interface.md#compute_subnet_for_data_column_sidecar
138+
func ComputeSubnetForDataColumnSidecar(columnIndex uint64) uint64 {
139+
dataColumnSidecarSubnetCount := params.BeaconConfig().DataColumnSidecarSubnetCount
140+
return columnIndex % dataColumnSidecarSubnetCount
141+
}
104142

105-
// Knowing the subnet ID and the number of columns per subnet, select all the columns the node should custody.
106-
// Columns belonging to the same subnet are contiguous.
107-
columnIndices := make(map[uint64]bool, custodySubnetCount*columnsPerSubnet)
108-
for i := uint64(0); i < columnsPerSubnet; i++ {
109-
for subnetId := range subnetIds {
110-
columnIndex := dataColumnSidecarSubnetCount*i + subnetId
111-
columnIndices[columnIndex] = true
143+
// CustodyColumns computes the columns the node should custody.
144+
func CustodyColumns(custodyGroups map[uint64]bool) (map[uint64]bool, error) {
145+
numberOfCustodyGroups := params.BeaconConfig().NumberOfCustodyGroups
146+
147+
custodyGroupCount := len(custodyGroups)
148+
149+
// Compute the columns for each custody group.
150+
columns := make(map[uint64]bool, custodyGroupCount)
151+
for group := range custodyGroups {
152+
if group >= numberOfCustodyGroups {
153+
return nil, errCustodyGroupCountTooLarge
154+
}
155+
156+
groupColumns, err := ComputeColumnsForCustodyGroup(group)
157+
if err != nil {
158+
return nil, errors.Wrap(err, "compute columns for custody group")
159+
}
160+
161+
for _, column := range groupColumns {
162+
columns[column] = true
112163
}
113164
}
114165

115-
return columnIndices, nil
166+
return columns, nil
167+
}
168+
169+
// DataColumnSubnets computes the subnets for the data columns.
170+
func DataColumnSubnets(dataColumns map[uint64]bool) map[uint64]bool {
171+
subnets := make(map[uint64]bool, len(dataColumns))
172+
173+
for column := range dataColumns {
174+
subnet := ComputeSubnetForDataColumnSidecar(column)
175+
subnets[subnet] = true
176+
}
177+
178+
return subnets
116179
}
117180

118181
// DataColumnSidecars computes the data column sidecars from the signed block and blobs.
119-
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#recover_matrix
182+
// https://github.com/ethereum/consensus-specs/blob/dev/specs/fulu/das-core.md#get_data_column_sidecars
120183
func DataColumnSidecars(signedBlock interfaces.ReadOnlySignedBeaconBlock, blobs []kzg.Blob) ([]*ethpb.DataColumnSidecar, error) {
121184
startTime := time.Now()
122185
blobsCount := len(blobs)
@@ -454,39 +517,22 @@ func VerifyDataColumnsSidecarKZGProofs(sidecars []blocks.RODataColumn) (bool, er
454517
return verified, nil
455518
}
456519

457-
// CustodySubnetCount returns the number of subnets the node should participate in for custody.
458-
func CustodySubnetCount() uint64 {
520+
// CustodyGroupCount returns the number of groups the node should participate in for custody.
521+
func CustodyGroupCount() uint64 {
459522
if flags.Get().SubscribeToAllSubnets {
460-
return params.BeaconConfig().DataColumnSidecarSubnetCount
523+
return params.BeaconConfig().NumberOfCustodyGroups
461524
}
462525

463526
return params.BeaconConfig().CustodyRequirement
464527
}
465528

466-
// SubnetSamplingSize returns the number of subnets the node should sample from.
467-
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7594/das-core.md#subnet-sampling
468-
func SubnetSamplingSize() uint64 {
529+
// CustodyGroupSamplingSize returns the number of custody groups the node should sample from.
530+
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/das-core.md#custody-sampling
531+
func CustodyGroupSamplingSize() uint64 {
469532
samplesPerSlot := params.BeaconConfig().SamplesPerSlot
470-
custodySubnetCount := CustodySubnetCount()
471-
472-
return max(samplesPerSlot, custodySubnetCount)
473-
}
474-
475-
// CustodyColumnCount returns the number of columns the node should custody.
476-
func CustodyColumnCount() uint64 {
477-
// Get the number of subnets.
478-
dataColumnSidecarSubnetCount := params.BeaconConfig().DataColumnSidecarSubnetCount
479-
480-
// Compute the number of columns per subnet.
481-
columnsPerSubnet := fieldparams.NumberOfColumns / dataColumnSidecarSubnetCount
482-
483-
// Get the number of subnets we custody
484-
custodySubnetCount := CustodySubnetCount()
485-
486-
// Finally, compute the number of columns we should custody.
487-
custodyColumnCount := custodySubnetCount * columnsPerSubnet
533+
custodyGroupCount := CustodyGroupCount()
488534

489-
return custodyColumnCount
535+
return max(samplesPerSlot, custodyGroupCount)
490536
}
491537

492538
// HypergeomCDF computes the hypergeometric cumulative distribution function.
@@ -538,27 +584,27 @@ func ExtendedSampleCount(samplesPerSlot, allowedFailures uint64) uint64 {
538584
return sampleCount
539585
}
540586

541-
func CustodyCountFromRecord(record *enr.Record) (uint64, error) {
542-
// By default, we assume the peer custodies the minimum number of subnets.
587+
// CustodyGroupCountFromRecord extracts the custody group count from an ENR record.
588+
func CustodyGroupCountFromRecord(record *enr.Record) (uint64, error) {
543589
if record == nil {
544590
return 0, errRecordNil
545591
}
546592

547-
// Load the `custody_subnet_count`
548-
var csc Csc
549-
if err := record.Load(&csc); err != nil {
550-
return 0, errCannotLoadCustodySubnetCount
593+
// Load the `cgc`
594+
var cgc Cgc
595+
if cgc := record.Load(&cgc); cgc != nil {
596+
return 0, errCannotLoadCustodyGroupCount
551597
}
552598

553-
return uint64(csc), nil
599+
return uint64(cgc), nil
554600
}
555601

556-
func CanSelfReconstruct(numCol uint64) bool {
557-
total := params.BeaconConfig().NumberOfColumns
558-
// if total is odd, then we need total / 2 + 1 columns to reconstruct
559-
// if total is even, then we need total / 2 columns to reconstruct
560-
columnsNeeded := total/2 + total%2
561-
return numCol >= columnsNeeded
602+
func CanSelfReconstruct(custodyGroupCount uint64) bool {
603+
total := params.BeaconConfig().NumberOfCustodyGroups
604+
// If total is odd, then we need total / 2 + 1 columns to reconstruct.
605+
// If total is even, then we need total / 2 columns to reconstruct.
606+
custodyGroupsNeeded := total/2 + total%2
607+
return custodyGroupCount >= custodyGroupsNeeded
562608
}
563609

564610
// RecoverCellsAndProofs recovers the cells and proofs from the data column sidecars.

0 commit comments

Comments
 (0)