Skip to content

Commit 92d2fc1

Browse files
authored
Implement validator custody (#14948)
* Node info: Rename cache and mutex. * Add `VALIDATOR_CUSTODY_REQUIREMENT` and `BALANCE_PER_ADDITIONAL_CUSTODY_GROUP`. * Implement `ValidatorsCustodyRequirement`. * Sync service: Add tracked validators cache. * `dataColumnSidecarByRootRPCHandler`: Remove custody columns in logs. * `dataColumnSidecarByRangeRPCHandler`: Remove custody columns in logs. * `blobsFromStoredDataColumns`: Simplify. Do not make any more a difference between "can theoretically reconstruct" and "can actually reconstruct". * Implement validator custody. * Fix Nishant's comment. * Fix Nishant's commit.
1 parent 8996000 commit 92d2fc1

Some content is hidden

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

49 files changed

+854
-280
lines changed

beacon-chain/blockchain/options.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/prysmaticlabs/prysm/v5/async/event"
55
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
66
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
7+
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/peerdas"
78
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
89
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem"
910
"github.com/prysmaticlabs/prysm/v5/beacon-chain/execution"
@@ -213,3 +214,10 @@ func WithSyncChecker(checker Checker) Option {
213214
return nil
214215
}
215216
}
217+
218+
func WithCustodyInfo(custodyInfo *peerdas.CustodyInfo) Option {
219+
return func(s *Service) error {
220+
s.cfg.CustodyInfo = custodyInfo
221+
return nil
222+
}
223+
}

beacon-chain/blockchain/process_block.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,8 +682,12 @@ func (s *Service) areDataColumnsAvailable(ctx context.Context, root [32]byte, si
682682
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/das-core.md#custody-sampling
683683
nodeID := s.cfg.P2P.NodeID()
684684

685+
// Prevent custody group count to change during the rest of the function.
686+
s.cfg.CustodyInfo.Mut.RLock()
687+
defer s.cfg.CustodyInfo.Mut.RUnlock()
688+
685689
// Get the custody group sampling size for the node.
686-
custodyGroupSamplingSize := peerdas.CustodyGroupSamplingSize()
690+
custodyGroupSamplingSize := s.cfg.CustodyInfo.CustodyGroupSamplingSize(peerdas.Actual)
687691
peerInfo, _, err := peerdas.Info(nodeID, custodyGroupSamplingSize)
688692
if err != nil {
689693
return errors.Wrap(err, "peer info")

beacon-chain/blockchain/service.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed"
1818
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
1919
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
20+
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/peerdas"
2021
coreTime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
2122
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
2223
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
@@ -93,6 +94,7 @@ type config struct {
9394
FinalizedStateAtStartUp state.BeaconState
9495
ExecutionEngineCaller execution.EngineCaller
9596
SyncChecker Checker
97+
CustodyInfo *peerdas.CustodyInfo
9698
}
9799

98100
// Checker is an interface used to determine if a node is in initial sync

beacon-chain/core/peerdas/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ go_library(
1414
visibility = ["//visibility:public"],
1515
deps = [
1616
"//beacon-chain/blockchain/kzg:go_default_library",
17+
"//beacon-chain/state:go_default_library",
1718
"//cmd/beacon-chain/flags:go_default_library",
1819
"//config/fieldparams:go_default_library",
1920
"//config/params:go_default_library",
2021
"//consensus-types/blocks:go_default_library",
2122
"//consensus-types/interfaces:go_default_library",
23+
"//consensus-types/primitives:go_default_library",
2224
"//crypto/hash:go_default_library",
2325
"//encoding/bytesutil:go_default_library",
2426
"//proto/prysm/v1alpha1:go_default_library",
@@ -46,10 +48,12 @@ go_test(
4648
deps = [
4749
":go_default_library",
4850
"//beacon-chain/blockchain/kzg:go_default_library",
51+
"//beacon-chain/state/state-native:go_default_library",
4952
"//cmd/beacon-chain/flags:go_default_library",
5053
"//config/fieldparams:go_default_library",
5154
"//config/params:go_default_library",
5255
"//consensus-types/blocks:go_default_library",
56+
"//consensus-types/primitives:go_default_library",
5357
"//proto/prysm/v1alpha1:go_default_library",
5458
"//testing/require:go_default_library",
5559
"//testing/util:go_default_library",

beacon-chain/core/peerdas/das_core.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import (
1111
"github.com/holiman/uint256"
1212
"github.com/pkg/errors"
1313
"github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/kzg"
14+
beaconState "github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
1415
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
1516
"github.com/prysmaticlabs/prysm/v5/config/params"
1617
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
1718
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
19+
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
1820
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
1921
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
2022
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
@@ -30,6 +32,13 @@ var (
3032
maxUint256 = &uint256.Int{math.MaxUint64, math.MaxUint64, math.MaxUint64, math.MaxUint64}
3133
)
3234

35+
type CustodyType int
36+
37+
const (
38+
Target CustodyType = iota
39+
Actual
40+
)
41+
3342
// CustodyGroups computes the custody groups the node should participate in for custody.
3443
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/das-core.md#get_custody_groups
3544
func CustodyGroups(nodeId enode.ID, custodyGroupCount uint64) (map[uint64]bool, error) {
@@ -193,10 +202,14 @@ func DataColumnSidecars(signedBlock interfaces.ReadOnlySignedBeaconBlock, blobs
193202

194203
// CustodyGroupSamplingSize returns the number of custody groups the node should sample from.
195204
// https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.10/specs/fulu/das-core.md#custody-sampling
196-
func CustodyGroupSamplingSize() uint64 {
197-
samplesPerSlot := params.BeaconConfig().SamplesPerSlot
198-
custodyGroupCount := CustodyGroupCount()
205+
func (custodyInfo *CustodyInfo) CustodyGroupSamplingSize(ct CustodyType) uint64 {
206+
custodyGroupCount := custodyInfo.TargetGroupCount.Get()
199207

208+
if ct == Actual {
209+
custodyGroupCount = custodyInfo.ActualGroupCount()
210+
}
211+
212+
samplesPerSlot := params.BeaconConfig().SamplesPerSlot
200213
return max(samplesPerSlot, custodyGroupCount)
201214
}
202215

@@ -226,6 +239,28 @@ func CustodyColumns(custodyGroups map[uint64]bool) (map[uint64]bool, error) {
226239
return columns, nil
227240
}
228241

242+
// ValidatorsCustodyRequirement returns the number of custody groups regarding the validator indices attached to the beacon node.
243+
// https://github.com/ethereum/consensus-specs/blob/dev/specs/fulu/das-core.md#validator-custody
244+
func ValidatorsCustodyRequirement(state beaconState.ReadOnlyBeaconState, validatorsIndex map[primitives.ValidatorIndex]bool) (uint64, error) {
245+
totalNodeBalance := uint64(0)
246+
for index := range validatorsIndex {
247+
balance, err := state.BalanceAtIndex(index)
248+
if err != nil {
249+
return 0, errors.Wrapf(err, "balance at index for validator index %v", index)
250+
}
251+
252+
totalNodeBalance += balance
253+
}
254+
255+
beaconConfig := params.BeaconConfig()
256+
numberOfCustodyGroup := beaconConfig.NumberOfCustodyGroups
257+
validatorCustodyRequirement := beaconConfig.ValidatorCustodyRequirement
258+
balancePerAdditionalCustodyGroup := beaconConfig.BalancePerAdditionalCustodyGroup
259+
260+
count := totalNodeBalance / balancePerAdditionalCustodyGroup
261+
return min(max(count, validatorCustodyRequirement), numberOfCustodyGroup), nil
262+
}
263+
229264
// Blobs extract blobs from `dataColumnsSidecar`.
230265
// This can be seen as the reciprocal function of DataColumnSidecars.
231266
// `dataColumnsSidecar` needs to contain the datacolumns corresponding to the non-extended matrix,

beacon-chain/core/peerdas/das_core_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import (
66
"github.com/pkg/errors"
77
"github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/kzg"
88
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/peerdas"
9+
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
910
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
1011
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
12+
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
1113
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
1214
"github.com/prysmaticlabs/prysm/v5/testing/require"
1315
"github.com/prysmaticlabs/prysm/v5/testing/util"
@@ -147,3 +149,99 @@ func TestDataColumnsSidecarsBlobsRoundtrip(t *testing.T) {
147149
// Check that the blobs are the same.
148150
require.DeepSSZEqual(t, verifiedROBlobs, roundtripBlobs)
149151
}
152+
153+
func TestValidatorsCustodyRequirement(t *testing.T) {
154+
testCases := []struct {
155+
name string
156+
count uint64
157+
expected uint64
158+
}{
159+
{name: "0 validators", count: 0, expected: 8},
160+
{name: "1 validator", count: 1, expected: 8},
161+
{name: "8 validators", count: 8, expected: 8},
162+
{name: "9 validators", count: 9, expected: 9},
163+
{name: "100 validators", count: 100, expected: 100},
164+
{name: "128 validators", count: 128, expected: 128},
165+
{name: "129 validators", count: 129, expected: 128},
166+
{name: "1000 validators", count: 1000, expected: 128},
167+
}
168+
169+
const balance = uint64(32_000_000_000)
170+
171+
for _, tc := range testCases {
172+
t.Run(tc.name, func(t *testing.T) {
173+
balances := make([]uint64, 0, tc.count)
174+
for range tc.count {
175+
balances = append(balances, balance)
176+
}
177+
178+
validatorsIndex := make(map[primitives.ValidatorIndex]bool)
179+
for i := range tc.count {
180+
validatorsIndex[primitives.ValidatorIndex(i)] = true
181+
}
182+
183+
beaconState, err := state_native.InitializeFromProtoFulu(&ethpb.BeaconStateElectra{Balances: balances})
184+
require.NoError(t, err)
185+
186+
actual, err := peerdas.ValidatorsCustodyRequirement(beaconState, validatorsIndex)
187+
require.NoError(t, err)
188+
require.Equal(t, tc.expected, actual)
189+
})
190+
}
191+
}
192+
193+
func TestCustodyGroupSamplingSize(t *testing.T) {
194+
testCases := []struct {
195+
name string
196+
custodyType peerdas.CustodyType
197+
validatorsCustodyRequirement uint64
198+
toAdvertiseCustodyGroupCount uint64
199+
expected uint64
200+
}{
201+
{
202+
name: "target, lower than samples per slot",
203+
custodyType: peerdas.Target,
204+
validatorsCustodyRequirement: 2,
205+
expected: 8,
206+
},
207+
{
208+
name: "target, higher than samples per slot",
209+
custodyType: peerdas.Target,
210+
validatorsCustodyRequirement: 100,
211+
expected: 100,
212+
},
213+
{
214+
name: "actual, lower than samples per slot",
215+
custodyType: peerdas.Actual,
216+
validatorsCustodyRequirement: 3,
217+
toAdvertiseCustodyGroupCount: 4,
218+
expected: 8,
219+
},
220+
{
221+
name: "actual, higher than samples per slot",
222+
custodyType: peerdas.Actual,
223+
validatorsCustodyRequirement: 100,
224+
toAdvertiseCustodyGroupCount: 101,
225+
expected: 100,
226+
},
227+
}
228+
229+
for _, tc := range testCases {
230+
t.Run(tc.name, func(t *testing.T) {
231+
// Create a custody info.
232+
custodyInfo := peerdas.CustodyInfo{}
233+
234+
// Set the validators custody requirement for target custody group count.
235+
custodyInfo.TargetGroupCount.SetValidatorsCustodyRequirement(tc.validatorsCustodyRequirement)
236+
237+
// Set the to advertise custody group count.
238+
custodyInfo.ToAdvertiseGroupCount.Set(tc.toAdvertiseCustodyGroupCount)
239+
240+
// Compute the custody group sampling size.
241+
actual := custodyInfo.CustodyGroupSamplingSize(tc.custodyType)
242+
243+
// Check the result.
244+
require.Equal(t, tc.expected, actual)
245+
})
246+
}
247+
}

0 commit comments

Comments
 (0)