Compare commits

..

38 Commits

Author SHA1 Message Date
Potuz
31404e9961 add larger trusted setup 2025-05-13 14:12:25 -05:00
Potuz
093ba20ffc Fix genesis block proposal (#15084)
* Fix genesis block proposal

* fix test

* fix test 2
2025-05-13 14:12:25 -05:00
Potuz
36140006ad ignore some chunks and blocks earlier 2025-05-13 14:12:25 -05:00
Potuz
a5536cfe94 delay block proposal 2025-05-13 14:12:25 -05:00
Potuz
34515abf9e better logs 2025-05-13 14:12:25 -05:00
Potuz
a418358d41 Add feature flag to delay block broadcast 2025-05-13 14:12:25 -05:00
Potuz
3627f7d787 do not verify block signatures over RPC 2025-05-13 14:12:25 -05:00
Potuz
bfec40b784 create new context to process blocks 2025-05-13 14:12:25 -05:00
Potuz
981036953d send block feed when received via chunks 2025-05-13 14:12:25 -05:00
nisdas
35a760c34b Use SignedBeaconBlock 2025-05-13 14:12:25 -05:00
Potuz
4a7de3a2db Fix chunking 2025-05-13 14:12:25 -05:00
nisdas
09985f2d8e Build Reproducible Test 2025-05-13 14:12:25 -05:00
nisdas
7941c053de Fix Interface Assertion 2025-05-13 14:12:25 -05:00
Potuz
c8c0fd8a5a broadcast chunks on gossip 2025-05-13 14:12:25 -05:00
Potuz
bc8d82ba4a add log when decoding a block 2025-05-13 14:12:25 -05:00
Potuz
5efb241c3e Broadcast chunked blocks 2025-05-13 14:12:25 -05:00
Potuz
3663d11212 fix block encoding when chunking 2025-05-13 14:12:25 -05:00
nisdas
843c2222ec Fix Validator Config 2025-05-13 14:12:25 -05:00
Nishant Das
d29a4c3514 Implement RLNC Broadcasting (#14830)
Add a pubsub API to broadcast chunks
2025-05-13 14:12:25 -05:00
Potuz
13c2f2b20b Do not check blob signature 2025-05-13 14:12:25 -05:00
Potuz
7437db4b0b Add the signatures over the commitments to the block before processing the state transition 2025-05-13 14:12:25 -05:00
Potuz
8133706dba Add endpoint to propose blocks
The implementation of Broadcast is blocked as it needs to be changed
2025-05-13 14:12:25 -05:00
Potuz
5113c482c8 remove signature check 2025-05-13 14:12:25 -05:00
Potuz
d728ff223c Add Ristretto trusted setup
Load the setup from a file and use it across all clients.
2025-05-13 14:12:25 -05:00
Potuz
276e1743af Add chunk broadcasting
Also make `ReadOnlyBeaconBlockChunk` actually read only and copy the
return values.
2025-05-13 14:12:25 -05:00
Potuz
ee25ce628e Handle the block when it can be recovered
Still TODO

- Handle chunks for pending blocks
- Handle state transition logic
- Handle rebroadcasting
- Handle block production
2025-05-13 14:12:25 -05:00
Potuz
883cc55bab prune the chunk cache every 10 minutes 2025-05-13 14:12:25 -05:00
Potuz
c78a190c6c Add a cache for incoming chunks
- Finish validation of each incoming chunk including signature
  verification when needed.

Still TODO:
- Handle chunks for pending blocks
- Convert the decoded block to an actual block and send it to the
  blockchain package.
2025-05-13 14:12:25 -05:00
Potuz
3475c7e23a Handle incoming chunks on the P2P layer
- Add hooks for the new topic "beacon_block_chunk"
- Add protos for the new messages
- Handle the sync package logic of signature validation
- Create native types for the chunks to be handled in the sync package

Still TODO:
- Handle chunks for pending blocks.
- Handle nodes with incoming chunks, avoid verifying signature twice
- Decode the block and send it to the blockchain package
2025-05-13 14:12:25 -05:00
Potuz
e5215bfc44 Add feature flag 2025-05-13 14:12:25 -05:00
Potuz
1d6ebc6d18 Add decoding and tests. 2025-05-13 14:12:25 -05:00
Potuz
b7aa76a013 Add node, receive and prepare message functions 2025-05-13 14:12:25 -05:00
Potuz
ec41d7693b Add echelon form and addRow 2025-05-13 14:12:25 -05:00
Potuz
cd36340638 Add chunks and message verifying 2025-05-13 14:12:25 -05:00
Potuz
3ec6cf6c4f add committer
Fix conflicts
2025-05-13 14:12:25 -05:00
Preston Van Loon
6f9a93ac89 spectests: Fix sha256 (#15278)
* spectests: Fix sha256

* Changelog fragment
2025-05-13 19:01:20 +00:00
terence
93a5fdd8f3 Remove unused variables, functions and more (#15264) 2025-05-12 13:25:26 +00:00
Potuz
0251fd78e9 Remove unused fieldparams (#15263)
Fixes #15262
2025-05-11 13:08:59 +00:00
126 changed files with 70435 additions and 4339 deletions

View File

@@ -271,7 +271,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-JljxS/if/t0qvGWcf5CgsX+72fj90yGTg/uEgC56y7U=",
integrity = "sha256-cI+DJe3BXlZ0lr28w3USi2lnYOUUfdi/YZ3nJuRiiYU=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version,
)
@@ -287,7 +287,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-NRba2h4zqb2LAXyDPglHTtkT4gVyuwpY708XmwXKXV8=",
integrity = "sha256-eBLWqO/RdcqsANmA/rwkJ4kI+LCL+Q0RmIDq6z85lYQ=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version,
)
@@ -303,7 +303,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-hpbtKUbc3NHtVcUPk/Zm+Hn57G2ijI9qvXJwl9hc/tM=",
integrity = "sha256-ab0H0WTzhSwYJ2a+GHVbUMoNRActJw18EmX3o5hhDi0",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
)

View File

@@ -14,22 +14,10 @@ import (
)
const (
EventHead = "head"
EventBlock = "block"
EventAttestation = "attestation"
EventVoluntaryExit = "voluntary_exit"
EventBlsToExecutionChange = "bls_to_execution_change"
EventProposerSlashing = "proposer_slashing"
EventAttesterSlashing = "attester_slashing"
EventFinalizedCheckpoint = "finalized_checkpoint"
EventChainReorg = "chain_reorg"
EventContributionAndProof = "contribution_and_proof"
EventLightClientFinalityUpdate = "light_client_finality_update"
EventLightClientOptimisticUpdate = "light_client_optimistic_update"
EventPayloadAttributes = "payload_attributes"
EventBlobSidecar = "blob_sidecar"
EventError = "error"
EventConnectionError = "connection_error"
EventHead = "head"
EventError = "error"
EventConnectionError = "connection_error"
)
var (

View File

@@ -70,6 +70,7 @@ go_library(
"//beacon-chain/startup:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/sync/rlnc:go_default_library",
"//beacon-chain/verification:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",

View File

@@ -17,6 +17,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
)
@@ -70,6 +71,14 @@ func WithDepositCache(c cache.DepositCache) Option {
}
}
// WithChunkCommitter for chunk committer.
func WithChunkCommitter(c *rlnc.Committer) Option {
return func(s *Service) error {
s.cfg.ChunkCommitter = c
return nil
}
}
// WithPayloadIDCache for payload ID cache.
func WithPayloadIDCache(c *cache.PayloadIDCache) Option {
return func(s *Service) error {

View File

@@ -743,24 +743,6 @@ func TestOnBlock_NilBlock(t *testing.T) {
require.Equal(t, true, IsInvalidBlock(err))
}
func TestOnBlock_InvalidSignature(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
gs, keys := util.DeterministicGenesisState(t, 32)
require.NoError(t, service.saveGenesisData(ctx, gs))
blk, err := util.GenerateFullBlock(gs, keys, util.DefaultBlockGenConfig(), 1)
require.NoError(t, err)
blk.Signature = []byte{'a'} // Mutate the signature.
wsb, err := consensusblocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
preState, err := service.getBlockPreState(ctx, wsb.Block())
require.NoError(t, err)
_, err = service.validateStateTransition(ctx, preState, wsb)
require.Equal(t, true, IsInvalidBlock(err))
}
func TestOnBlock_CallNewPayloadAndForkchoiceUpdated(t *testing.T) {
params.SetupTestConfigCleanup(t)
config := params.BeaconConfig()

View File

@@ -74,6 +74,10 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
log.WithField("blockRoot", fmt.Sprintf("%#x", blockRoot)).Debug("Ignoring already synced block")
return nil
}
if s.BlockBeingSynced(blockRoot) {
log.WithField("blockRoot", fmt.Sprintf("%#x", blockRoot)).Debug("Ignoring already syncing block")
return nil
}
receivedTime := time.Now()
s.blockBeingSynced.set(blockRoot)
defer s.blockBeingSynced.unset(blockRoot)

View File

@@ -30,6 +30,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
@@ -93,6 +94,7 @@ type config struct {
FinalizedStateAtStartUp state.BeaconState
ExecutionEngineCaller execution.EngineCaller
SyncChecker Checker
ChunkCommitter *rlnc.Committer
}
// Checker is an interface used to determine if a node is in initial sync

View File

@@ -52,6 +52,11 @@ func (mb *mockBroadcaster) Broadcast(_ context.Context, _ proto.Message) error {
return nil
}
func (mb *mockBroadcaster) BroadcastBlockChunks(_ context.Context, _ []*ethpb.BeaconBlockChunk) error {
mb.broadcastCalled = true
return nil
}
func (mb *mockBroadcaster) BroadcastAttestation(_ context.Context, _ uint64, _ ethpb.Att) error {
mb.broadcastCalled = true
return nil

View File

@@ -291,52 +291,3 @@ func TestProcessBlockHeader_OK(t *testing.T) {
}
assert.Equal(t, true, proto.Equal(nsh, expected), "Expected %v, received %v", expected, nsh)
}
func TestBlockSignatureSet_OK(t *testing.T) {
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
PublicKey: make([]byte, 32),
WithdrawalCredentials: make([]byte, 32),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: true,
}
}
state, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, state.SetValidators(validators))
require.NoError(t, state.SetSlot(10))
require.NoError(t, state.SetLatestBlockHeader(util.HydrateBeaconHeader(&ethpb.BeaconBlockHeader{
Slot: 9,
ProposerIndex: 0,
})))
latestBlockSignedRoot, err := state.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
currentEpoch := time.CurrentEpoch(state)
priv, err := bls.RandKey()
require.NoError(t, err)
pID, err := helpers.BeaconProposerIndex(context.Background(), state)
require.NoError(t, err)
block := util.NewBeaconBlock()
block.Block.Slot = 10
block.Block.ProposerIndex = pID
block.Block.Body.RandaoReveal = bytesutil.PadTo([]byte{'A', 'B', 'C'}, 96)
block.Block.ParentRoot = latestBlockSignedRoot[:]
block.Signature, err = signing.ComputeDomainAndSign(state, currentEpoch, block.Block, params.BeaconConfig().DomainBeaconProposer, priv)
require.NoError(t, err)
proposerIdx, err := helpers.BeaconProposerIndex(context.Background(), state)
require.NoError(t, err)
validators[proposerIdx].Slashed = false
validators[proposerIdx].PublicKey = priv.PublicKey().Marshal()
err = state.UpdateValidatorAtIndex(proposerIdx, validators[proposerIdx])
require.NoError(t, err)
set, err := blocks.BlockSignatureBatch(state, block.Block.ProposerIndex, block.Signature, block.Block.HashTreeRoot)
require.NoError(t, err)
verified, err := set.Verify()
require.NoError(t, err)
assert.Equal(t, true, verified, "Block signature set returned a set which was unable to be verified")
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"github.com/OffchainLabs/prysm/v6/time/slots"
"github.com/pkg/errors"
@@ -240,16 +239,3 @@ func verifyBlobCommitmentCount(slot primitives.Slot, body interfaces.ReadOnlyBea
}
return nil
}
// GetBlockPayloadHash returns the hash of the execution payload of the block
func GetBlockPayloadHash(blk interfaces.ReadOnlyBeaconBlock) ([32]byte, error) {
var payloadHash [32]byte
if IsPreBellatrixVersion(blk.Version()) {
return payloadHash, nil
}
payload, err := blk.Body().Execution()
if err != nil {
return payloadHash, err
}
return bytesutil.ToBytes32(payload.BlockHash()), nil
}

View File

@@ -120,24 +120,6 @@ func VerifyBlockSignatureUsingCurrentFork(beaconState state.ReadOnlyBeaconState,
})
}
// BlockSignatureBatch retrieves the block signature batch from the provided block and its corresponding state.
func BlockSignatureBatch(beaconState state.ReadOnlyBeaconState,
proposerIndex primitives.ValidatorIndex,
sig []byte,
rootFunc func() ([32]byte, error)) (*bls.SignatureBatch, error) {
currentEpoch := slots.ToEpoch(beaconState.Slot())
domain, err := signing.Domain(beaconState.Fork(), currentEpoch, params.BeaconConfig().DomainBeaconProposer, beaconState.GenesisValidatorsRoot())
if err != nil {
return nil, err
}
proposer, err := beaconState.ValidatorAtIndex(proposerIndex)
if err != nil {
return nil, err
}
proposerPubKey := proposer.PublicKey
return signing.BlockSignatureBatch(proposerPubKey, sig, domain, rootFunc)
}
// RandaoSignatureBatch retrieves the relevant randao specific signature batch object
// from a block and its corresponding state.
func RandaoSignatureBatch(

View File

@@ -0,0 +1,17 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["signature.go"],
importpath = "github.com/OffchainLabs/prysm/v6/beacon-chain/core/chunks",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//network/forks:go_default_library",
"//time/slots:go_default_library",
],
)

View File

@@ -0,0 +1,53 @@
package chunks
import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/signing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/network/forks"
"github.com/OffchainLabs/prysm/v6/time/slots"
)
// VerifyChunkSignature verifies the proposer signature of a beacon block chunk.
func VerifyChunkSignature(beaconState state.ReadOnlyBeaconState,
proposerIndex primitives.ValidatorIndex,
sig []byte,
rootFunc func() ([32]byte, error)) error {
currentEpoch := slots.ToEpoch(beaconState.Slot())
domain, err := signing.Domain(beaconState.Fork(), currentEpoch, params.BeaconConfig().DomainBeaconProposer, beaconState.GenesisValidatorsRoot())
if err != nil {
return err
}
proposer, err := beaconState.ValidatorAtIndex(proposerIndex)
if err != nil {
return err
}
proposerPubKey := proposer.PublicKey
return signing.VerifyBlockSigningRoot(proposerPubKey, sig, domain, rootFunc)
}
// VerifyChunkSignatureUsingCurrentFork verifies the proposer signature of a beacon block chunk. This differs
// from the above method by not using fork data from the state and instead retrieving it
// via the respective epoch.
func VerifyChunkSignatureUsingCurrentFork(beaconState state.ReadOnlyBeaconState, chunk interfaces.ReadOnlyBeaconBlockChunk) error {
currentEpoch := slots.ToEpoch(chunk.Slot())
fork, err := forks.Fork(currentEpoch)
if err != nil {
return err
}
domain, err := signing.Domain(fork, currentEpoch, params.BeaconConfig().DomainBeaconProposer, beaconState.GenesisValidatorsRoot())
if err != nil {
return err
}
proposer, err := beaconState.ValidatorAtIndex(chunk.ProposerIndex())
if err != nil {
return err
}
proposerPubKey := proposer.PublicKey
sig := chunk.Signature()
return signing.VerifyBlockSigningRoot(proposerPubKey, sig[:], domain, func() ([32]byte, error) {
return chunk.HeaderRoot(), nil
})
}

View File

@@ -180,12 +180,6 @@ func ProcessBlockNoVerifyAnySig(
return nil, nil, err
}
sig := signed.Signature()
bSet, err := b.BlockSignatureBatch(st, blk.ProposerIndex(), sig[:], blk.HashTreeRoot)
if err != nil {
tracing.AnnotateError(span, err)
return nil, nil, errors.Wrap(err, "could not retrieve block signature set")
}
randaoReveal := signed.Block().Body().RandaoReveal()
rSet, err := b.RandaoSignatureBatch(ctx, st, randaoReveal[:])
if err != nil {
@@ -199,7 +193,7 @@ func ProcessBlockNoVerifyAnySig(
// Merge beacon block, randao and attestations signatures into a set.
set := bls.NewSet()
set.Join(bSet).Join(rSet).Join(aSet)
set.Join(rSet).Join(aSet)
if blk.Version() >= version.Capella {
changes, err := signed.Block().Body().BLSToExecutionChanges()

View File

@@ -158,9 +158,8 @@ func TestProcessBlockNoVerify_SigSetContainsDescriptions(t *testing.T) {
set, _, err := transition.ProcessBlockNoVerifyAnySig(context.Background(), beaconState, wsb)
require.NoError(t, err)
assert.Equal(t, len(set.Signatures), len(set.Descriptions), "Signatures and descriptions do not match up")
assert.Equal(t, "block signature", set.Descriptions[0])
assert.Equal(t, "randao signature", set.Descriptions[1])
assert.Equal(t, "attestation signature", set.Descriptions[2])
assert.Equal(t, "randao signature", set.Descriptions[0])
assert.Equal(t, "attestation signature", set.Descriptions[1])
}
func TestProcessOperationsNoVerifyAttsSigs_OK(t *testing.T) {

View File

@@ -41,8 +41,6 @@ type ReadOnlyDatabase interface {
StateSummary(ctx context.Context, blockRoot [32]byte) (*ethpb.StateSummary, error)
HasStateSummary(ctx context.Context, blockRoot [32]byte) bool
HighestSlotStatesBelow(ctx context.Context, slot primitives.Slot) ([]state.ReadOnlyBeaconState, error)
// StateDiff related methods.
StateDiff(ctx context.Context, slot primitives.Slot) (state.BeaconState, error)
// Checkpoint operations.
JustifiedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error)
FinalizedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error)
@@ -85,8 +83,6 @@ type NoHeadAccessDatabase interface {
DeleteStates(ctx context.Context, blockRoots [][32]byte) error
SaveStateSummary(ctx context.Context, summary *ethpb.StateSummary) error
SaveStateSummaries(ctx context.Context, summaries []*ethpb.StateSummary) error
// Statediff related methods.
SaveStateDiff(ctx context.Context, state state.ReadOnlyBeaconState) error
// Checkpoint operations.
SaveJustifiedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error

View File

@@ -25,7 +25,6 @@ go_library(
"migration_state_validators.go",
"schema.go",
"state.go",
"state_diff.go",
"state_summary.go",
"state_summary_cache.go",
"utils.go",
@@ -45,7 +44,6 @@ go_library(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/hdiff:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/light-client:go_default_library",
"//consensus-types/primitives:go_default_library",
@@ -96,7 +94,6 @@ go_test(
"migration_archived_index_test.go",
"migration_block_slot_index_test.go",
"migration_state_validators_test.go",
"state_diff_test.go",
"state_summary_test.go",
"state_test.go",
"utils_test.go",
@@ -119,7 +116,6 @@ go_test(
"//consensus-types/light-client:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/dbval:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -16,9 +16,6 @@ var ErrNotFoundOriginBlockRoot = errors.Wrap(ErrNotFound, "OriginBlockRoot")
// ErrNotFoundGenesisBlockRoot means no genesis block root was found, indicating the db was not initialized with genesis
var ErrNotFoundGenesisBlockRoot = errors.Wrap(ErrNotFound, "OriginGenesisRoot")
// ErrNotFoundBackfillBlockRoot is an error specifically for the origin block root getter
var ErrNotFoundBackfillBlockRoot = errors.Wrap(ErrNotFound, "BackfillBlockRoot")
// ErrNotFoundFeeRecipient is a not found error specifically for the fee recipient getter
var ErrNotFoundFeeRecipient = errors.Wrap(ErrNotFound, "fee recipient")

View File

@@ -112,7 +112,6 @@ var Buckets = [][]byte{
lightClientUpdatesBucket,
lightClientBootstrapBucket,
lightClientSyncCommitteeBucket,
stateDiffBucket,
// Indices buckets.
blockSlotIndicesBucket,
stateSlotIndicesBucket,

View File

@@ -16,7 +16,6 @@ var (
stateValidatorsBucket = []byte("state-validators")
feeRecipientBucket = []byte("fee-recipient")
registrationBucket = []byte("registration")
stateDiffBucket = []byte("state-diff")
// Light Client Updates Bucket
lightClientUpdatesBucket = []byte("light-client-updates")

View File

@@ -1,272 +0,0 @@
package kv
import (
"context"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/hdiff"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/math"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing/trace"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
)
/*
We use a level-based approach to save state diffs. The levels are 0-6, where each level corresponds to an exponent of 2 (exponents[lvl]).
The data at level 0 is saved every 2**exponent[0] slots and always contains a full state snapshot that is used as a base for the delta saved at other levels.
*/
var (
anchorCache = make(map[int]state.ReadOnlyBeaconState, len(params.StateHierarchyExponents())) // cache full states at the last node at each level
)
// SaveStateDiff takes a state and decides between saving a full state snapshot or a diff.
func (s *Store) SaveStateDiff(ctx context.Context, st state.ReadOnlyBeaconState) error {
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveStateDiff")
defer span.End()
slot := st.Slot()
offset, err := s.loadOrInitOffset(slot)
if err != nil {
return err
}
if uint64(slot) < offset {
return ErrSlotBeforeOffset
}
// Find the level to save the state.
lvl := computeLevel(offset, slot)
if lvl == -1 {
return nil
}
// Save full state if level is 0.
if lvl == 0 {
return s.saveFullSnapshot(lvl, st)
}
// Get anchor state to compute the diff from.
anchorState, err := s.getAnchorState(offset, lvl, slot)
if err != nil {
return err
}
err = s.saveHdiff(lvl, anchorState, st)
if err != nil {
return err
}
return nil
}
// StateDiff retrieves the full state for a given slot.
func (s *Store) StateDiff(ctx context.Context, slot primitives.Slot) (state.BeaconState, error) {
offset, err := s.getOffset()
if err != nil {
return nil, err
}
if uint64(slot) < offset {
return nil, ErrSlotBeforeOffset
}
snapshot, diffChain, err := s.getBaseAndDiffChain(offset, slot)
// TODO: apply the diff chain to the snapshot and return the final state.
return nil, nil
}
// SaveHdiff computes the diff between the anchor state and the current state and saves it to the database.
func (s *Store) saveHdiff(lvl int, anchor, st state.ReadOnlyBeaconState) error {
slot := uint64(st.Slot())
key := makeKey(lvl, slot)
// TODO: compute actual diff
diff := hdiff.HdiffSerialized{}
err := s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
// TODO: save the diff bytes per field with suffix
buf := make([]byte, len(key)+len("_s"))
copy(buf, key)
copy(buf[len(key):], "_s")
if err := bucket.Put(buf, diff); err != nil {
return err
}
copy(buf[len(key):], "_v")
if err := bucket.Put(buf, diff); err != nil {
return err
}
copy(buf[len(key):], "_b")
if err := bucket.Put(buf, diff); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Save the full state to the cache (if not the last level).
if lvl != len(params.StateHierarchyExponents())-1 {
anchorCache[lvl] = st
}
return nil
}
// SaveFullSnapshot saves the full level 0 state snapshot to the database.
func (s *Store) saveFullSnapshot(lvl int, st state.ReadOnlyBeaconState) error {
slot := uint64(st.Slot())
key := makeKey(lvl, slot)
stateBytes, err := st.MarshalSSZ()
// add version key to value
enc, err := addKey(st.Version(), stateBytes)
if err != nil {
return err
}
err = s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
if err := bucket.Put(key, enc); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Save the full state to the cache.
anchorCache[lvl] = st
return nil
}
func addKey(v int, bytes []byte) ([]byte, error) {
key, err := keyForSnapshot(v)
if err != nil {
return nil, err
}
enc := make([]byte, len(key)+len(bytes))
copy(enc, key)
copy(enc[len(key):], bytes)
return enc, nil
}
func (s *Store) getBaseAndDiffChain(offset uint64, slot primitives.Slot) (state.BeaconState, []*hdiff.HdiffSerialized, error) {
rel := uint64(slot) - offset
lvl := computeLevel(offset, slot)
if lvl == -1 {
return nil, nil, errors.New("slot not in tree")
}
exponents := params.StateHierarchyExponents()
baseSpan := math.PowerOf2(exponents[0])
baseAnchorSlot := (rel / baseSpan * baseSpan) + offset
var diffChainIndices []uint64
for i := 1; i < lvl; i++ {
span := math.PowerOf2(exponents[i])
diffSlot := rel / span * span
if diffSlot == baseAnchorSlot {
continue
}
diffChainIndices = appendUnique(diffChainIndices, diffSlot+offset)
}
baseSnapshot, err := s.getFullSnapshot(lvl, baseAnchorSlot)
if err != nil {
return nil, nil, err
}
diffChain := make([]*hdiff.HdiffSerialized, len(diffChainIndices))
for _, diffSlot := range diffChainIndices {
diff, err := s.getDiff(computeLevel(offset, primitives.Slot(diffSlot)), diffSlot)
if err != nil {
return nil, nil, err
}
diffChain = append(diffChain, diff)
}
return baseSnapshot, diffChain, nil
}
func (s *Store) getDiff(lvl int, slot uint64) (*hdiff.HdiffSerialized, error) {
key := makeKey(lvl, slot)
var stateDiff []byte
var validatorDiff []byte
var balancesDiff []byte
err := s.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
buf := make([]byte, len(key)+len("_s"))
copy(buf, key)
copy(buf[len(key):], "_s")
stateDiff = bucket.Get(buf)
if stateDiff == nil {
return errors.New("state diff not found")
}
copy(buf[len(key):], "_v")
validatorDiff = bucket.Get(buf)
if validatorDiff == nil {
return errors.New("validator diff not found")
}
copy(buf[len(key):], "_b")
balancesDiff = bucket.Get(buf)
if balancesDiff == nil {
return errors.New("balances diff not found")
}
return nil
})
if err != nil {
return nil, err
}
return &hdiff.HdiffSerialized{}, nil
}
func (s *Store) getFullSnapshot(lvl int, slot uint64) (state.BeaconState, error) {
key := makeKey(lvl, slot)
var enc []byte
err := s.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
enc = bucket.Get(key)
if enc == nil {
return errors.New("state not found")
}
return nil
})
if err != nil {
return nil, err
}
return s.decodeStateSnapshot(enc)
}
func appendUnique(s []uint64, v uint64) []uint64 {
for _, x := range s {
if x == v {
return s
}
}
return append(s, v)
}

View File

@@ -1,167 +0,0 @@
package kv
import (
"context"
"encoding/binary"
"errors"
"fmt"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
state_native "github.com/OffchainLabs/prysm/v6/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/math"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"go.etcd.io/bbolt"
)
var (
offsetKey = []byte("offset")
ErrSlotBeforeOffset = errors.New("slot is before root offset")
)
func makeKey(level int, slot uint64) []byte {
buf := make([]byte, 1+8)
buf[0] = byte(level)
binary.BigEndian.PutUint64(buf[1:], slot)
return buf
}
func (s *Store) getAnchorState(offset uint64, lvl int, slot primitives.Slot) (anchor state.ReadOnlyBeaconState, err error) {
if lvl == 0 {
return nil, errors.New("no anchor for level 0")
}
relSlot := uint64(slot) - offset
prevExp := params.StateHierarchyExponents()[lvl-1]
span := math.PowerOf2(prevExp)
anchorSlot := primitives.Slot((relSlot / span * span) + offset)
anchorLvl := computeLevel(offset, anchorSlot)
if anchorLvl == -1 {
return nil, errors.New("could not compute anchor level")
}
// Check if we have the anchor in cache.
anchor, ok := anchorCache[anchorLvl]
if ok {
return anchor, nil
}
// If not, load it from the database.
anchor, err = s.StateDiff(context.Background(), anchorSlot)
if err != nil {
return nil, err
}
// Save it in the cache.
anchorCache[anchorLvl] = anchor
return anchor, nil
}
// ComputeLevel computes the level in the diff tree. Returns -1 in case slot should not be in tree.
func computeLevel(offset uint64, slot primitives.Slot) int {
rel := uint64(slot) - offset
for i, exp := range params.StateHierarchyExponents() {
span := math.PowerOf2(exp)
if rel%span == 0 {
return i
}
}
// If rel isnt on any of the boundaries, we should ignore saving it.
return -1
}
func (s *Store) loadOrInitOffset(slot primitives.Slot) (offset uint64, err error) {
return offset, s.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := bucket.Get(offsetKey)
if offsetBytes != nil {
offset = binary.BigEndian.Uint64(offsetBytes)
return nil
}
offset = uint64(slot)
offsetBytes = make([]byte, 8)
binary.BigEndian.PutUint64(offsetBytes, offset)
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
return err
}
return nil
})
}
func (s *Store) getOffset() (offset uint64, err error) {
return offset, s.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := bucket.Get(offsetKey)
if offsetBytes != nil {
offset = binary.BigEndian.Uint64(offsetBytes)
return nil
}
return bbolt.ErrIncompatibleValue
})
}
func keyForSnapshot(v int) ([]byte, error) {
switch v {
case version.Electra:
return ElectraKey, nil
case version.Deneb:
return denebKey, nil
case version.Capella:
return capellaKey, nil
case version.Bellatrix:
return bellatrixKey, nil
case version.Altair:
return altairKey, nil
default:
return nil, fmt.Errorf("unsupported version %s", version.String(v))
}
}
func (s *Store) decodeStateSnapshot(enc []byte) (state.BeaconState, error) {
switch {
case HasElectraKey(enc):
var electraState ethpb.BeaconStateElectra
if err := electraState.UnmarshalSSZ(enc[len(ElectraKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoElectra(&electraState)
case hasDenebKey(enc):
var denebState ethpb.BeaconStateDeneb
if err := denebState.UnmarshalSSZ(enc[len(denebKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoDeneb(&denebState)
case hasCapellaKey(enc):
var capellaState ethpb.BeaconStateCapella
if err := capellaState.UnmarshalSSZ(enc[len(capellaKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoCapella(&capellaState)
case hasBellatrixKey(enc):
var bellatrixState ethpb.BeaconStateBellatrix
if err := bellatrixState.UnmarshalSSZ(enc[len(bellatrixKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoBellatrix(&bellatrixState)
case hasAltairKey(enc):
var altairState ethpb.BeaconStateAltair
if err := altairState.UnmarshalSSZ(enc[len(altairKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoAltair(&altairState)
default:
return nil, fmt.Errorf("unsupported encoding %x", enc)
}
}

View File

@@ -1,163 +0,0 @@
package kv
import (
"testing"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/math"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/testing/util"
"go.etcd.io/bbolt"
)
func TestStateDiff_LoadOrInitOffset(t *testing.T) {
db := setupDB(t)
offset, err := loadOrInitOffset(db, 10)
require.NoError(t, err)
require.Equal(t, uint64(10), offset)
offset, err = loadOrInitOffset(db, 20)
require.NoError(t, err)
require.Equal(t, uint64(10), offset)
offset, err = loadOrInitOffset(db, 5)
require.NoError(t, err)
require.Equal(t, uint64(10), offset)
}
func TestStateDiff_ComputeLevel(t *testing.T) {
db := setupDB(t)
offset, err := loadOrInitOffset(db, 0)
require.NoError(t, err)
require.Equal(t, uint64(0), offset)
// 2 ** 21
lvl, shouldSave := computeLevel(math.PowerOf2(21))
require.Equal(t, 0, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 21 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(21) * 3)
require.Equal(t, 0, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 18
lvl, shouldSave = computeLevel(math.PowerOf2(18))
require.Equal(t, 1, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 18 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(18) * 3)
require.Equal(t, 1, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 16
lvl, shouldSave = computeLevel(math.PowerOf2(16))
require.Equal(t, 2, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 16 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(16) * 3)
require.Equal(t, 2, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 13
lvl, shouldSave = computeLevel(math.PowerOf2(13))
require.Equal(t, 3, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 13 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(13) * 3)
require.Equal(t, 3, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 11
lvl, shouldSave = computeLevel(math.PowerOf2(11))
require.Equal(t, 4, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 11 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(11) * 3)
require.Equal(t, 4, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 9
lvl, shouldSave = computeLevel(math.PowerOf2(9))
require.Equal(t, 5, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 9 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(9) * 3)
require.Equal(t, 5, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 5
lvl, shouldSave = computeLevel(math.PowerOf2(5))
require.Equal(t, 6, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 5 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(5) * 3)
require.Equal(t, 6, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 7
lvl, shouldSave = computeLevel(math.PowerOf2(7))
require.Equal(t, 6, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 5 + 1
lvl, shouldSave = computeLevel(math.PowerOf2(5) + 1)
require.Equal(t, false, shouldSave)
// 2 ** 5 + 16
lvl, shouldSave = computeLevel(math.PowerOf2(5) + 16)
require.Equal(t, false, shouldSave)
// 2 ** 5 + 32
lvl, shouldSave = computeLevel(math.PowerOf2(5) + 32)
require.Equal(t, true, shouldSave)
require.Equal(t, 6, lvl)
}
func TestStateDiff_SaveFullSnapshot(t *testing.T) {
db := setupDB(t)
// Set offset to zero
offset, err := loadOrInitOffset(db, 0)
require.NoError(t, err)
require.Equal(t, uint64(0), offset)
// Create state with slot 2**21 * 3
st, err := util.NewBeaconStateElectra()
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(21))
err = st.SetSlot(slot)
require.NoError(t, err)
stssz, err := st.MarshalSSZ()
require.NoError(t, err)
err = db.SaveStateDiff(nil, st)
require.NoError(t, err)
err = db.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
for i := 6; i >= 0; i-- {
s := bucket.Get(makeKey(i, uint64(slot)))
require.NotNil(t, s, "key not found")
if i > 0 {
require.DeepEqual(t, EmptyNodeMarker, s, "node not marked as empty")
} else {
require.DeepSSZEqual(t, stssz, s, "retrieved state does not match saved state")
}
}
return nil
})
require.NoError(t, err)
}

View File

@@ -51,6 +51,7 @@ go_library(
"//beacon-chain/sync/checkpoint:go_default_library",
"//beacon-chain/sync/genesis:go_default_library",
"//beacon-chain/sync/initial-sync:go_default_library",
"//beacon-chain/sync/rlnc:go_default_library",
"//beacon-chain/verification:go_default_library",
"//cmd:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",

View File

@@ -53,6 +53,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/checkpoint"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/genesis"
initialsync "github.com/OffchainLabs/prysm/v6/beacon-chain/sync/initial-sync"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/beacon-chain/verification"
"github.com/OffchainLabs/prysm/v6/cmd"
"github.com/OffchainLabs/prysm/v6/cmd/beacon-chain/flags"
@@ -124,6 +125,7 @@ type BeaconNode struct {
syncChecker *initialsync.SyncChecker
slasherEnabled bool
lcStore *lightclient.Store
chunkCommitter *rlnc.Committer
}
// New creates a new node instance, sets up configuration options, and registers
@@ -139,6 +141,11 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
registry := runtime.NewServiceRegistry()
ctx := cliCtx.Context
committer, err := rlnc.LoadTrustedSetup()
if err != nil {
return nil, errors.Wrap(err, "could not load the committer trusted setup")
}
beacon := &BeaconNode{
cliCtx: cliCtx,
ctx: ctx,
@@ -163,6 +170,7 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
syncChecker: &initialsync.SyncChecker{},
slasherEnabled: cliCtx.Bool(flags.SlasherFlag.Name),
lcStore: &lightclient.Store{},
chunkCommitter: committer,
}
for _, opt := range opts {
@@ -777,6 +785,7 @@ func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *st
blockchain.WithBlobStorage(b.BlobStorage),
blockchain.WithTrackedValidatorsCache(b.trackedValidatorsCache),
blockchain.WithPayloadIDCache(b.payloadIDCache),
blockchain.WithChunkCommitter(b.chunkCommitter),
blockchain.WithSyncChecker(b.syncChecker),
blockchain.WithSlasherEnabled(b.slasherEnabled),
blockchain.WithLightClientStore(b.lcStore),
@@ -866,6 +875,7 @@ func (b *BeaconNode) registerSyncService(initialSyncComplete chan struct{}, bFil
regularsync.WithAvailableBlocker(bFillStore),
regularsync.WithSlasherEnabled(b.slasherEnabled),
regularsync.WithLightClientStore(b.lcStore),
regularsync.WithChunkCommitter(b.chunkCommitter),
)
return b.services.RegisterService(rs)
}
@@ -1013,6 +1023,7 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error {
TrackedValidatorsCache: b.trackedValidatorsCache,
PayloadIDCache: b.payloadIDCache,
LCStore: b.lcStore,
ChunkCommitter: b.chunkCommitter,
})
return b.services.RegisterService(rpcService)

View File

@@ -17,6 +17,7 @@ import (
"github.com/OffchainLabs/prysm/v6/network/forks"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/time/slots"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/pkg/errors"
ssz "github.com/prysmaticlabs/fastssz"
"github.com/sirupsen/logrus"
@@ -230,6 +231,36 @@ func (s *Service) BroadcastBlob(ctx context.Context, subnet uint64, blob *ethpb.
return nil
}
// BroadcastBlockChunks sends the passed messages to the pubsub topic
func (s *Service) BroadcastBlockChunks(ctx context.Context, chunks []*ethpb.BeaconBlockChunk) error {
ctx, span := trace.StartSpan(ctx, "p2p.BroadcastBlob")
defer span.End()
forkDigest, err := s.currentForkDigest()
if err != nil {
err := errors.Wrap(err, "could not retrieve fork digest")
tracing.AnnotateError(span, err)
return err
}
topic := RLNCTopicFormat
topic = fmt.Sprintf(topic, forkDigest)
multipleMessages := make([][]byte, len(chunks))
for i, c := range chunks {
buf := new(bytes.Buffer)
if _, err := s.Encoding().EncodeGossip(buf, c); err != nil {
err := errors.Wrap(err, "could not encode message")
tracing.AnnotateError(span, err)
return err
}
multipleMessages[i] = buf.Bytes()
}
if err := s.PublishMultipleToTopic(ctx, topic+s.Encoding().ProtocolSuffix(), multipleMessages, pubsub.WithRandomPublishing()); err != nil {
err := errors.Wrap(err, "could not publish message")
tracing.AnnotateError(span, err)
return err
}
return nil
}
func (s *Service) internalBroadcastBlob(ctx context.Context, subnet uint64, blobSidecar *ethpb.BlobSidecar, forkDigest [4]byte) {
_, span := trace.StartSpan(ctx, "p2p.internalBroadcastBlob")
defer span.End()

View File

@@ -12,6 +12,7 @@ go_library(
visibility = [
"//beacon-chain:__subpackages__",
"//cmd:__subpackages__",
"//validator:__subpackages__",
],
deps = [
"//config/params:go_default_library",

View File

@@ -24,6 +24,7 @@ var gossipTopicMappings = map[string]func() proto.Message{
BlobSubnetTopicFormat: func() proto.Message { return &ethpb.BlobSidecar{} },
LightClientOptimisticUpdateTopicFormat: func() proto.Message { return &ethpb.LightClientOptimisticUpdateAltair{} },
LightClientFinalityUpdateTopicFormat: func() proto.Message { return &ethpb.LightClientFinalityUpdateAltair{} },
RLNCTopicFormat: func() proto.Message { return &ethpb.BeaconBlockChunk{} },
}
// GossipTopicMappings is a function to return the assigned data type
@@ -140,6 +141,7 @@ func init() {
GossipTypeMapping[reflect.TypeOf(&ethpb.AttesterSlashingElectra{})] = AttesterSlashingSubnetTopicFormat
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedAggregateAttestationAndProofElectra{})] = AggregateAndProofSubnetTopicFormat
GossipTypeMapping[reflect.TypeOf(&ethpb.LightClientFinalityUpdateElectra{})] = LightClientFinalityUpdateTopicFormat
GossipTypeMapping[reflect.TypeOf(&ethpb.BeaconBlockChunk{})] = RLNCTopicFormat
// Specially handle Fulu objects.
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBeaconBlockFulu{})] = BlockSubnetTopicFormat

View File

@@ -34,6 +34,7 @@ type P2P interface {
// Broadcaster broadcasts messages to peers over the p2p pubsub protocol.
type Broadcaster interface {
Broadcast(context.Context, proto.Message) error
BroadcastBlockChunks(context.Context, []*ethpb.BeaconBlockChunk) error
BroadcastAttestation(ctx context.Context, subnet uint64, att ethpb.Att) error
BroadcastSyncCommitteeMessage(ctx context.Context, subnet uint64, sMsg *ethpb.SyncCommitteeMessage) error
BroadcastBlob(ctx context.Context, subnet uint64, blob *ethpb.BlobSidecar) error

View File

@@ -100,6 +100,26 @@ func (s *Service) PublishToTopic(ctx context.Context, topic string, data []byte,
}
}
func (s *Service) PublishMultipleToTopic(ctx context.Context, topic string, msgs [][]byte, opts ...pubsub.PubOpt) error {
topicHandle, err := s.JoinTopic(topic)
if err != nil {
return err
}
// Wait for at least 1 peer to be available to receive the published message.
for {
if len(topicHandle.ListPeers()) > 0 || flags.Get().MinimumSyncPeers == 0 {
return topicHandle.PublishMultiple(ctx, msgs, opts...)
}
select {
case <-ctx.Done():
return errors.Wrapf(ctx.Err(), "unable to find requisite number of peers for topic %s, 0 peers found to publish to", topic)
default:
time.Sleep(100 * time.Millisecond)
}
}
}
// SubscribeToTopic joins (if necessary) and subscribes to PubSub topic.
func (s *Service) SubscribeToTopic(topic string, opts ...pubsub.SubOpt) (*pubsub.Subscription, error) {
s.awaitStateInitialized() // Genesis time and genesis validators root are required to subscribe.

View File

@@ -134,6 +134,10 @@ func (*FakeP2P) Broadcast(_ context.Context, _ proto.Message) error {
return nil
}
func (*FakeP2P) BroadcastBlockChunks(_ context.Context, _ []*ethpb.BeaconBlockChunk) error {
return nil
}
// BroadcastAttestation -- fake.
func (*FakeP2P) BroadcastAttestation(_ context.Context, _ uint64, _ ethpb.Att) error {
return nil

View File

@@ -28,6 +28,11 @@ func (m *MockBroadcaster) Broadcast(_ context.Context, msg proto.Message) error
return nil
}
func (m *MockBroadcaster) BroadcastBlockChunks(_ context.Context, _ []*ethpb.BeaconBlockChunk) error {
m.BroadcastCalled.Store(true)
return nil
}
// BroadcastAttestation records a broadcast occurred.
func (m *MockBroadcaster) BroadcastAttestation(_ context.Context, _ uint64, a ethpb.Att) error {
m.BroadcastCalled.Store(true)

View File

@@ -190,6 +190,11 @@ func (p *TestP2P) Broadcast(_ context.Context, _ proto.Message) error {
return nil
}
func (p *TestP2P) BroadcastBlockChunks(_ context.Context, _ []*ethpb.BeaconBlockChunk) error {
p.BroadcastCalled.Store(true)
return nil
}
// BroadcastAttestation broadcasts an attestation.
func (p *TestP2P) BroadcastAttestation(_ context.Context, _ uint64, _ ethpb.Att) error {
p.BroadcastCalled.Store(true)

View File

@@ -34,6 +34,8 @@ const (
GossipLightClientFinalityUpdateMessage = "light_client_finality_update"
// GossipLightClientOptimisticUpdateMessage is the name for the light client optimistic update message type.
GossipLightClientOptimisticUpdateMessage = "light_client_optimistic_update"
// GossipBlockChunkMessage is the name for the block chunk message type.
GossipBlockChunkMessage = "beacon_block_chunk"
// Topic Formats
//
// AttestationSubnetTopicFormat is the topic format for the attestation subnet.
@@ -60,4 +62,6 @@ const (
LightClientFinalityUpdateTopicFormat = GossipProtocolAndDigest + GossipLightClientFinalityUpdateMessage
// LightClientOptimisticUpdateTopicFormat is the topic format for the light client optimistic update subnet.
LightClientOptimisticUpdateTopicFormat = GossipProtocolAndDigest + GossipLightClientOptimisticUpdateMessage
// RLNCTopicFormat is the topic format for the RLNC subnet.
RLNCTopicFormat = GossipProtocolAndDigest + GossipBlockChunkMessage
)

View File

@@ -52,6 +52,7 @@ go_library(
"//beacon-chain/startup:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/sync:go_default_library",
"//beacon-chain/sync/rlnc:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//io/logs:go_default_library",

View File

@@ -13,6 +13,7 @@ import (
"github.com/OffchainLabs/prysm/v6/api"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/beacon-chain/cache/depositsnapshot"
coreblocks "github.com/OffchainLabs/prysm/v6/beacon-chain/core/blocks"
corehelpers "github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/transition"
"github.com/OffchainLabs/prysm/v6/beacon-chain/db/filters"
@@ -1005,6 +1006,13 @@ func (s *Server) validateConsensus(ctx context.Context, b *eth.GenericSignedBeac
if err != nil {
return errors.Wrap(err, "could not get parent state")
}
blockRoot, err := blk.Block().HashTreeRoot()
if err != nil {
return errors.Wrap(err, "could not hash block")
}
if err := coreblocks.VerifyBlockSignatureUsingCurrentFork(parentState, blk, blockRoot); err != nil {
return errors.Wrap(err, "could not verify block signature")
}
_, err = transition.ExecuteStateTransition(ctx, parentState, blk)
if err != nil {
return errors.Wrap(err, "could not execute state transition")

View File

@@ -678,187 +678,6 @@ const (
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}`
// BadBlindedBellatrixBlock contains wrong data to create a block that does not pass ToConsensus conversion
// "parent_root" length too short
// "block_hash" length too short
// "state_root" length too short
BadBlindedBellatrixBlock = `{
"message": {
"slot": "1",
"proposer_index": "1",
"parent_root": "0xcf8e0d4e95872",
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"body": {
"randao_reveal": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"eth1_data": {
"deposit_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"deposit_count": "1",
"block_hash": "0xcf8e0d4e95872"
},
"graffiti": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"proposer_slashings": [
{
"signed_header_1": {
"message": {
"slot": "1",
"proposer_index": "1",
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"state_root": "0xcf8e0d4e9580f2",
"body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
},
"signed_header_2": {
"message": {
"slot": "1",
"proposer_index": "1",
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
}
],
"attester_slashings": [
{
"attestation_1": {
"attesting_indices": [
"1"
],
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"data": {
"slot": "1",
"index": "1",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"target": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}
}
},
"attestation_2": {
"attesting_indices": [
"1"
],
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"data": {
"slot": "1",
"index": "1",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"target": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}
}
}
}
],
"attestations": [
{
"aggregation_bits": "0xffffffffffffffffffffffffffffffffff3f",
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"data": {
"slot": "1",
"index": "1",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"target": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}
}
}
],
"deposits": [
{
"proof": [
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
],
"data": {
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
"withdrawal_credentials": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"amount": "1",
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
}
],
"voluntary_exits": [
{
"message": {
"epoch": "1",
"validator_index": "1"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
],
"sync_aggregate": {
"sync_committee_bits": "0x6451e9f951ebf05edc01de67e593484b672877054f055903ff0df1a1a945cf30ca26bb4d4b154f94a1bc776bcf5d0efb3603e1f9b8ee2499ccdcfe2a18cef458",
"sync_committee_signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
},
"execution_payload_header": {
"parent_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"receipts_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prev_randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"block_number": "1",
"gas_limit": "1",
"gas_used": "1",
"timestamp": "1",
"extra_data": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"base_fee_per_gas": "1",
"block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"transactions_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}
}
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}`
CapellaBlock = `{
"message": {
"slot": "1",
@@ -1056,208 +875,7 @@ const (
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}`
// BadCapellaBlock contains wrong data to create a block that does not pass ToConsensus conversion
// "state_root" length too short
// "block_hash" length too short
// "graffiti" length too short
// "state_root" length too short
BadCapellaBlock = `{
"message": {
"slot": "1",
"proposer_index": "1",
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"state_root": "0xcf8e0d4e957e8208d920f2",
"body": {
"randao_reveal": "0x1b66ac1fb663c9baf888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"eth1_data": {
"deposit_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"deposit_count": "1",
"block_hash": "0xcf8e0d4e95873691884560367e8208d920f2"
},
"graffiti": "0xcf8e0d4e9587369b230120f2",
"proposer_slashings": [
{
"signed_header_1": {
"message": {
"slot": "1",
"proposer_index": "1",
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"state_root": "0xcf8e0d4e9587369b2301d208d920f2",
"body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
},
"signed_header_2": {
"message": {
"slot": "1",
"proposer_index": "1",
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
}
],
"attester_slashings": [
{
"attestation_1": {
"attesting_indices": [
"1"
],
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"data": {
"slot": "1",
"index": "1",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"target": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}
}
},
"attestation_2": {
"attesting_indices": [
"1"
],
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"data": {
"slot": "1",
"index": "1",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"target": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}
}
}
}
],
"attestations": [
{
"aggregation_bits": "0xffffffffffffffffffffffffffffffffff3f",
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
"data": {
"slot": "1",
"index": "1",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
},
"target": {
"epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}
}
}
],
"deposits": [
{
"proof": [
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
],
"data": {
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
"withdrawal_credentials": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"amount": "1",
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
}
],
"voluntary_exits": [
{
"message": {
"epoch": "1",
"validator_index": "1"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
],
"sync_aggregate": {
"sync_committee_bits": "0x6451e9f951ebf05edc01de67e593484b672877054f055903ff0df1a1a945cf30ca26bb4d4b154f94a1bc776bcf5d0efb3603e1f9b8ee2499ccdcfe2a18cef458",
"sync_committee_signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
},
"execution_payload": {
"parent_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"receipts_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prev_randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"block_number": "1",
"gas_limit": "1",
"gas_used": "1",
"timestamp": "1",
"extra_data": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"base_fee_per_gas": "14074904626401341155369551180448584754667373453244490859944217516317499064576",
"block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"transactions": [
"0x02f878831469668303f51d843b9ac9f9843b9aca0082520894c93269b73096998db66be0441e836d873535cb9c8894a19041886f000080c001a031cc29234036afbf9a1fb9476b463367cb1f957ac0b919b69bbc798436e604aaa018c4e9c3914eb27aadd0b91e10b18655739fcf8c1fc398763a9f1beecb8ddc86"
],
"withdrawals": [
{
"index": "1",
"validator_index": "1",
"address": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
"amount": "1"
}
]
},
"bls_to_execution_changes": [
{
"message": {
"validator_index": "1",
"from_bls_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
"to_execution_address": "0xabcf8e0d4e9587369b2301d0790347320302cc09"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
]
}
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}`
BlindedCapellaBlock = `{
"message": {
"slot": "1",

View File

@@ -65,6 +65,7 @@ go_library(
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/sync:go_default_library",
"//beacon-chain/sync/rlnc:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",

View File

@@ -18,6 +18,8 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/transition"
"github.com/OffchainLabs/prysm/v6/beacon-chain/db/kv"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/config/features"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
@@ -43,6 +45,7 @@ var eth1DataNotification bool
const (
eth1dataTimeout = 2 * time.Second
defaultBuilderBoostFactor = primitives.Gwei(100)
meshSize = 40 // The number of peers to broadcast block chunks
)
// Deprecated: The gRPC API will remain the default and fully supported through v8 (expected in 2026) but will be eventually removed in favor of REST API.
@@ -271,6 +274,60 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
return vs.constructGenericBeaconBlock(sBlk, bundle, winningBid)
}
// ProposeChunkedBlock handles the proposal of chunked beacon blocks
func (vs *Server) ProposeChunkedBlock(ctx context.Context, req *ethpb.ChunkedBeaconBlock) (*ethpb.ProposeResponse, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.ProposeChunkedBlock")
defer span.End()
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}
block, err := blocks.NewSignedBeaconBlock(req.Block.Block)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "%s: %v", "decode block failed", err)
}
var sidecars []*ethpb.BlobSidecar
if block.IsBlinded() {
block, sidecars, err = vs.handleBlindedBlock(ctx, block)
} else if block.Version() >= version.Deneb {
sidecars, err = vs.blobSidecarsFromUnblindedBlock(block, req.Block)
}
if err != nil {
return nil, status.Errorf(codes.Internal, "%s: %v", "handle block failed", err)
}
root, err := block.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not hash tree root: %v", err)
}
var wg sync.WaitGroup
errChan := make(chan error, 1)
wg.Add(1)
go func() {
defer wg.Done()
if err := vs.broadcastReceiveChunkedBlock(ctx, req, root); err != nil {
errChan <- errors.Wrap(err, "broadcast/receive block failed")
return
}
errChan <- nil
}()
if err := vs.broadcastAndReceiveBlobs(ctx, sidecars, root); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast/receive blobs: %v", err)
}
wg.Wait()
if err := <-errChan; err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast/receive block: %v", err)
}
return &ethpb.ProposeResponse{BlockRoot: root[:]}, nil
}
// Deprecated: The gRPC API will remain the default and fully supported through v8 (expected in 2026) but will be eventually removed in favor of REST API.
//
// ProposeBeaconBlock handles the proposal of beacon blocks.
@@ -366,12 +423,60 @@ func (vs *Server) blobSidecarsFromUnblindedBlock(block interfaces.SignedBeaconBl
return BuildBlobSidecars(block, rawBlobs, proofs)
}
// broadcastReceiveChunkedBlock broadcasts a chunked block and handles its reception.
func (vs *Server) broadcastReceiveChunkedBlock(ctx context.Context, req *ethpb.ChunkedBeaconBlock, root [32]byte) error {
block, err := blocks.NewSignedBeaconBlock(req.Block.Block)
if err != nil {
return errors.Wrap(err, "block construction failed")
}
messages, err := vs.constructChunkMessages(req)
if err != nil {
return errors.Wrap(err, "could not construct messages")
}
if err := slots.WaitUntil(ctx, vs.TimeFetcher.GenesisTime(), block.Block().Slot(), features.Get().DelayBlockBroadcast); err != nil {
return errors.Wrap(err, "could not wait until broadcast time")
}
if err := vs.P2P.BroadcastBlockChunks(ctx, messages); err != nil {
return errors.Wrap(err, "broadcast failed")
}
vs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: block},
})
return vs.BlockReceiver.ReceiveBlock(ctx, block, root, nil)
}
func (s *Server) constructChunkMessages(cBlk *ethpb.ChunkedBeaconBlock) ([]*ethpb.BeaconBlockChunk, error) {
node, err := rlnc.NewNodeFromChunkedBlock(s.ChunkCommitter, cBlk)
if err != nil {
return nil, errors.Wrap(err, "could not construct node")
}
multipleMessages := make([]*ethpb.BeaconBlockChunk, 0, meshSize)
for i := 0; i < meshSize; i++ {
msg, err := node.PrepareMessage()
if err != nil {
return nil, errors.Wrap(err, "could not prepare message")
}
chunk := &ethpb.BeaconBlockChunk{
Data: msg.Data(),
Coefficients: msg.Coefficients(),
Header: cBlk.Header,
Signature: cBlk.Signature,
}
multipleMessages = append(multipleMessages, chunk)
}
return multipleMessages, nil
}
// broadcastReceiveBlock broadcasts a block and handles its reception.
func (vs *Server) broadcastReceiveBlock(ctx context.Context, block interfaces.SignedBeaconBlock, root [32]byte) error {
protoBlock, err := block.Proto()
if err != nil {
return errors.Wrap(err, "protobuf conversion failed")
}
if err := slots.WaitUntil(ctx, vs.TimeFetcher.GenesisTime(), block.Block().Slot(), features.Get().DelayBlockBroadcast); err != nil {
return errors.Wrap(err, "could not wait until broadcast time")
}
if err := vs.P2P.Broadcast(ctx, protoBlock); err != nil {
return errors.Wrap(err, "broadcast failed")
}

View File

@@ -27,6 +27,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
"github.com/OffchainLabs/prysm/v6/network/forks"
@@ -80,6 +81,7 @@ type Server struct {
ClockWaiter startup.ClockWaiter
CoreService *core.Service
AttestationStateFetcher blockchain.AttestationStateFetcher
ChunkCommitter *rlnc.Committer
}
// Deprecated: The gRPC API will remain the default and fully supported through v8 (expected in 2026) but will be eventually removed in favor of REST API.

View File

@@ -36,6 +36,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/stategen"
chainSync "github.com/OffchainLabs/prysm/v6/beacon-chain/sync"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/config/features"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/io/logs"
@@ -123,6 +124,7 @@ type Config struct {
TrackedValidatorsCache *cache.TrackedValidatorsCache
PayloadIDCache *cache.PayloadIDCache
LCStore *lightClient.Store
ChunkCommitter *rlnc.Committer
}
// NewService instantiates a new RPC service instance that will
@@ -252,6 +254,7 @@ func NewService(ctx context.Context, cfg *Config) *Service {
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
PayloadIDCache: s.cfg.PayloadIDCache,
AttestationStateFetcher: s.cfg.AttestationReceiver,
ChunkCommitter: s.cfg.ChunkCommitter,
}
s.validatorServer = validatorServer
nodeServer := &nodev1alpha1.Server{

View File

@@ -35,6 +35,7 @@ go_library(
"subscriber_beacon_aggregate_proof.go",
"subscriber_beacon_attestation.go",
"subscriber_beacon_blocks.go",
"subscriber_beacon_blocks_chunks.go",
"subscriber_blob_sidecar.go",
"subscriber_bls_to_execution_change.go",
"subscriber_handlers.go",
@@ -68,6 +69,7 @@ go_library(
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/chunks:go_default_library",
"//beacon-chain/core/feed:go_default_library",
"//beacon-chain/core/feed/block:go_default_library",
"//beacon-chain/core/feed/operation:go_default_library",
@@ -95,6 +97,7 @@ go_library(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/sync/backfill/coverage:go_default_library",
"//beacon-chain/sync/rlnc:go_default_library",
"//beacon-chain/sync/verify:go_default_library",
"//beacon-chain/verification:go_default_library",
"//cache/lru:go_default_library",
@@ -103,6 +106,7 @@ go_library(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/chunks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/light-client:go_default_library",
"//consensus-types/primitives:go_default_library",

View File

@@ -19,6 +19,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/backfill/coverage"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/beacon-chain/verification"
)
@@ -165,6 +166,14 @@ func WithStateNotifier(n statefeed.Notifier) Option {
}
}
// WithChunkCommitter gives the sync package direct access to the Ristretto trusted setup.
func WithChunkCommitter(c *rlnc.Committer) Option {
return func(s *Service) error {
s.cfg.chunkCommitter = c
return nil
}
}
// WithBlobStorage gives the sync package direct access to BlobStorage.
func WithBlobStorage(b *filesystem.BlobStorage) Option {
return func(s *Service) error {

View File

@@ -0,0 +1,45 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"block_chunk_cache.go",
"committer.go",
"errors.go",
"matrix.go",
"message.go",
"node.go",
"trusted_setup.go",
],
embedsrcs = ["trusted_setup.json"],
importpath = "github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc",
visibility = ["//visibility:public"],
deps = [
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/rand:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_gtank_ristretto255//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"block_chunk_cache_test.go",
"committer_test.go",
"matrix_test.go",
"message_test.go",
"node_test.go",
"trusted_setup_test.go",
],
embed = [":go_default_library"],
deps = [
"//consensus-types/chunks:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"@com_github_gtank_ristretto255//:go_default_library",
],
)

View File

@@ -0,0 +1,110 @@
package rlnc
import (
"sync"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/pkg/errors"
)
var numChunks = uint(10)
var maxChunkSize = uint(65536) // 2MB for 10 chunks.
type BlockChunkCache struct {
sync.Mutex
committer *Committer
nodes map[primitives.Slot]map[primitives.ValidatorIndex]*Node
}
func NewBlockChunkCache(committer *Committer) *BlockChunkCache {
return &BlockChunkCache{
committer: committer,
nodes: make(map[primitives.Slot]map[primitives.ValidatorIndex]*Node),
}
}
func (b *BlockChunkCache) AddChunk(chunk interfaces.ReadOnlyBeaconBlockChunk) error {
b.Lock()
defer b.Unlock()
m, err := newMessage(chunk)
if err != nil {
return errors.Wrap(err, "failed to create new message")
}
if _, ok := b.nodes[chunk.Slot()]; !ok {
b.nodes[chunk.Slot()] = make(map[primitives.ValidatorIndex]*Node)
} else if n, ok := b.nodes[chunk.Slot()][chunk.ProposerIndex()]; ok {
return n.receive(m)
}
node := NewNode(b.committer, numChunks)
if err := node.receive(m); err != nil {
return errors.Wrap(err, "failed to receive message")
}
b.nodes[chunk.Slot()][chunk.ProposerIndex()] = node
return ErrSignatureNotVerified
}
// GetBlockData returns the block for the given slot and proposer index if all the chunks are present.
func (b *BlockChunkCache) GetBlockData(slot primitives.Slot, proposerIndex primitives.ValidatorIndex) ([]byte, error) {
b.Lock()
defer b.Unlock()
if _, ok := b.nodes[slot]; !ok {
return nil, ErrNoData
}
if _, ok := b.nodes[slot][proposerIndex]; !ok {
return nil, ErrNoData
}
node := b.nodes[slot][proposerIndex]
return node.decode() // Only error is ErrNoData when the node is not full.
}
// Prune removes all nodes from before the given slot.
func (b *BlockChunkCache) Prune(slot primitives.Slot) {
b.Lock()
defer b.Unlock()
for s := range b.nodes {
if s < slot {
delete(b.nodes, s)
}
}
}
// RemoveNode removes the node that has the given chunk.
func (b *BlockChunkCache) RemoveNode(chunk interfaces.ReadOnlyBeaconBlockChunk) {
b.Lock()
defer b.Unlock()
if _, ok := b.nodes[chunk.Slot()]; !ok {
return
}
delete(b.nodes[chunk.Slot()], chunk.ProposerIndex())
}
// PrepareMessage prepares a message to broadcast after receiving the given chunk.
func (b *BlockChunkCache) PrepareMessage(chunk interfaces.ReadOnlyBeaconBlockChunk) (*ethpb.BeaconBlockChunk, error) {
b.Lock()
defer b.Unlock()
if _, ok := b.nodes[chunk.Slot()]; !ok {
return nil, ErrNoData
}
if _, ok := b.nodes[chunk.Slot()][chunk.ProposerIndex()]; !ok {
return nil, ErrNoData
}
node := b.nodes[chunk.Slot()][chunk.ProposerIndex()]
msg, err := node.PrepareMessage()
if err != nil {
return nil, errors.Wrap(err, "failed to prepare message")
}
signature := chunk.Signature()
return &ethpb.BeaconBlockChunk{
Data: msg.Data(),
Coefficients: msg.Coefficients(),
Header: chunk.Header(),
Signature: signature[:],
}, nil
}

View File

@@ -0,0 +1,76 @@
package rlnc
import (
"crypto/rand"
"testing"
"github.com/OffchainLabs/prysm/v6/consensus-types/chunks"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/testing/require"
)
func TestBlockChunkCache(t *testing.T) {
// Create a new block chunk cache.
committer := newCommitter(10)
cache := NewBlockChunkCache(committer)
require.NotNil(t, cache)
require.Equal(t, 0, len(cache.nodes))
chunkSize := uint(4)
block := make([]byte, numChunks*chunkSize*31)
_, err := rand.Read(block)
require.NoError(t, err)
node, err := NewSource(committer, numChunks, block)
require.NoError(t, err)
// Prepare a message
msg, err := node.PrepareMessage()
require.NoError(t, err)
require.NotNil(t, msg)
chunkProto := &ethpb.BeaconBlockChunk{
Data: msg.Data(),
Coefficients: msg.Coefficients(),
Header: &ethpb.BeaconBlockChunkHeader{
Slot: 1,
ProposerIndex: 1,
ParentRoot: make([]byte, 32),
Commitments: msg.Commitments(),
},
Signature: make([]byte, 96),
}
chunk, err := chunks.NewBlockChunk(chunkProto)
require.NoError(t, err)
// Add the chunk to the cache.
require.ErrorIs(t, ErrSignatureNotVerified, cache.AddChunk(chunk))
require.Equal(t, 1, len(cache.nodes))
// Prepare a second message
msg, err = node.PrepareMessage()
require.NoError(t, err)
require.NotNil(t, msg)
chunkProto = &ethpb.BeaconBlockChunk{
Data: msg.Data(),
Coefficients: msg.Coefficients(),
Header: &ethpb.BeaconBlockChunkHeader{
Slot: 1,
ProposerIndex: 1,
ParentRoot: make([]byte, 32),
Commitments: msg.Commitments(),
},
Signature: make([]byte, 96),
}
chunk, err = chunks.NewBlockChunk(chunkProto)
require.NoError(t, err)
// Add the chunk to the cache
require.NoError(t, cache.AddChunk(chunk)) // No error this time as the signature was verified before
require.Equal(t, 1, len(cache.nodes)) // No new node, same block chunk
cachedNode := cache.nodes[1][1]
require.Equal(t, 2, len(cachedNode.chunks))
message, err := cache.PrepareMessage(chunk)
require.NoError(t, err)
require.DeepEqual(t, message.Header.Commitments, chunkProto.Header.Commitments)
}

View File

@@ -0,0 +1,50 @@
package rlnc
import (
"errors"
"github.com/OffchainLabs/prysm/v6/crypto/rand"
ristretto "github.com/gtank/ristretto255"
)
// Committer is a structure that holds the Ristretto generators.
type Committer struct {
generators []*ristretto.Element
}
// newCommitter creates a new committer with the number of generators.
// TODO: read the generators from the config file.
func newCommitter(n uint) *Committer {
generators := make([]*ristretto.Element, n)
for i := range generators {
generators[i] = randomElement()
if generators[i] == nil {
return nil
}
}
return &Committer{generators}
}
func (c *Committer) commit(scalars []*ristretto.Scalar) (*ristretto.Element, error) {
if len(scalars) > len(c.generators) {
return nil, errors.New("too many scalars")
}
result := &ristretto.Element{}
return result.VarTimeMultiScalarMult(scalars, c.generators[:len(scalars)]), nil
}
func (c *Committer) num() int {
return len(c.generators)
}
func randomElement() (ret *ristretto.Element) {
buf := make([]byte, 64)
gen := rand.NewGenerator()
_, err := gen.Read(buf)
if err != nil {
return nil
}
ret = &ristretto.Element{}
ret.FromUniformBytes(buf)
return
}

View File

@@ -0,0 +1,35 @@
package rlnc
import (
"crypto/rand"
"testing"
"github.com/OffchainLabs/prysm/v6/testing/require"
ristretto "github.com/gtank/ristretto255"
)
func TestCommit(t *testing.T) {
n := 5
c := newCommitter(uint(n))
require.NotNil(t, c)
require.Equal(t, n, len(c.generators))
scalars := make([]*ristretto.Scalar, n+1)
randomBytes := make([]byte, 64)
for i := range scalars {
_, err := rand.Read(randomBytes)
require.NoError(t, err)
scalars[i] = &ristretto.Scalar{}
scalars[i].FromUniformBytes(randomBytes)
}
msm := &ristretto.Element{}
msm.VarTimeMultiScalarMult(scalars[:n], c.generators)
_, err := c.commit(scalars[:n-1])
require.NoError(t, err)
_, err = c.commit(scalars)
require.NotNil(t, err)
committment, err := c.commit(scalars[:n])
require.NoError(t, err)
require.Equal(t, 1, committment.Equal(msm))
}

View File

@@ -0,0 +1,12 @@
package rlnc
import "errors"
var ErrInvalidSize = errors.New("invalid size")
var ErrNoData = errors.New("no data")
var ErrIncorrectCommitments = errors.New("incorrect commitments")
var ErrInvalidMessage = errors.New("invalid message")
var ErrLinearlyDependentMessage = errors.New("linearly dependent message")
var ErrInvalidScalar = errors.New("invalid scalar encoding")
var ErrInvalidElement = errors.New("invalid element encoding")
var ErrSignatureNotVerified = errors.New("signature not verified") // ErrSignatureNotVerified is returned when the signature of a chunk is not yet verified.

View File

@@ -0,0 +1,202 @@
package rlnc
import (
"errors"
ristretto "github.com/gtank/ristretto255"
)
var zeroVector = ristretto.NewScalar()
func scalarLC(coeffs []*ristretto.Scalar, data [][]*ristretto.Scalar) (ret []*ristretto.Scalar, err error) {
if len(coeffs) != len(data) {
return nil, errors.New("different number of coefficients and vectors")
}
if len(data) == 0 {
return nil, nil
}
prod := ristretto.Scalar{}
ret = make([]*ristretto.Scalar, len(data[0]))
for i := range ret {
ret[i] = ristretto.NewScalar()
for j, c := range coeffs {
ret[i].Add(ret[i], prod.Multiply(c, data[j][i]))
}
}
return
}
// echelon is a struct that holds the echelon form of a matrix of coefficients and the
// corresponding transformation matrix to get it to that form. That is we are guaranteed to have
// transform * coefficients = triangular
type echelon struct {
coefficients [][]*ristretto.Scalar
triangular [][]*ristretto.Scalar
transform [][]*ristretto.Scalar
}
// newEchelon returns a new echelon struct.
func newEchelon(size uint) *echelon {
transform := make([][]*ristretto.Scalar, size)
for i := range transform {
transform[i] = make([]*ristretto.Scalar, size)
for j := range transform[i] {
if j != i {
transform[i][j] = ristretto.NewScalar()
} else {
transform[i][i] = scalarOne()
}
}
}
return &echelon{
coefficients: make([][]*ristretto.Scalar, 0),
triangular: make([][]*ristretto.Scalar, 0),
transform: transform,
}
}
func identityMatrix(size uint) [][]*ristretto.Scalar {
coefficients := make([][]*ristretto.Scalar, size)
for i := range coefficients {
coefficients[i] = make([]*ristretto.Scalar, size)
for j := range coefficients[i] {
if j == i {
coefficients[i][i] = scalarOne()
} else {
coefficients[i][j] = ristretto.NewScalar()
}
}
}
return coefficients
}
// newIdentityEchelon returns a new echelon struct with the identity coefficients.
func newIdentityEchelon(size uint) *echelon {
return &echelon{
coefficients: identityMatrix(size),
triangular: identityMatrix(size),
transform: identityMatrix(size),
}
}
func (e *echelon) isFull() bool {
if len(e.coefficients) == 0 {
return false
}
return len(e.coefficients) == len(e.coefficients[0])
}
// copyVector returns a copy of a vector.
func copyVector(v []*ristretto.Scalar) []*ristretto.Scalar {
ret := make([]*ristretto.Scalar, len(v))
for i, s := range v {
ret[i] = ristretto.NewScalar()
*ret[i] = *s
}
return ret
}
// firstEntry returns the index of the first non-zero entry in a vector. It returns -1 if the vector is all zeros.
func firstEntry(v []*ristretto.Scalar) int {
for i, s := range v {
if s.Equal(zeroVector) == 0 {
return i
}
}
return -1
}
// isZeroVector returns true if the vector is all zeros.
func isZeroVector(v []*ristretto.Scalar) bool {
return firstEntry(v) == -1
}
func (e *echelon) addRow(row []*ristretto.Scalar) bool {
// do not add malformed rows. This assumes transform is never nil.
if len(row) != len(e.transform[0]) {
return false
}
if isZeroVector(row) {
return false
}
// do not add anything if we have the full data.
if e.isFull() {
return false
}
// Add any incoming row if we are empty.
currentSize := len(e.coefficients)
if currentSize == 0 {
e.coefficients = append(e.coefficients, row)
e.triangular = append(e.triangular, row)
return true
}
// currentSize is the index we are about to add.
tr := copyVector(e.transform[currentSize])
newEchelonRow := copyVector(row)
i := 0
for ; i < currentSize; i++ {
j := firstEntry(e.triangular[i])
k := firstEntry(newEchelonRow)
if k == -1 {
return false
}
if j < k {
continue
}
if j > k {
break
}
pivot := *e.triangular[i][j]
f := *newEchelonRow[j]
for l := range newEchelonRow {
newEchelonRow[l].Multiply(newEchelonRow[l], &pivot)
newEchelonRow[l].Subtract(newEchelonRow[l], ristretto.NewScalar().Multiply(&f, e.triangular[i][l]))
tr[l].Multiply(tr[l], &pivot)
tr[l].Subtract(tr[l], ristretto.NewScalar().Multiply(&f, e.transform[i][l]))
}
}
if isZeroVector(newEchelonRow) {
return false
}
e.triangular = append(e.triangular[:i], append([][]*ristretto.Scalar{newEchelonRow}, e.triangular[i:]...)...)
e.coefficients = append(e.coefficients, row)
if i < currentSize {
e.transform = append(e.transform[:currentSize], e.transform[currentSize+1:]...)
e.transform = append(e.transform[:i], append([][]*ristretto.Scalar{tr}, e.transform[i:]...)...)
return true
}
e.transform[i] = tr
return true
}
func (e *echelon) inverse() (ret [][]*ristretto.Scalar, err error) {
if !e.isFull() {
return nil, ErrNoData
}
ret = make([][]*ristretto.Scalar, len(e.transform))
for i := range e.transform {
ret[i] = make([]*ristretto.Scalar, len(e.transform))
for j := range e.transform {
ret[i][j] = &ristretto.Scalar{}
*ret[i][j] = *e.transform[i][j]
}
}
pivot := &ristretto.Scalar{}
diff := &ristretto.Scalar{}
normalizedDiff := &ristretto.Scalar{}
for i := len(e.triangular) - 1; i >= 0; i-- {
pivot = pivot.Invert(e.triangular[i][i])
for j := 0; j < len(ret); j++ {
ret[i][j] = ret[i][j].Multiply(ret[i][j], pivot)
}
for j := i + 1; j < len(e.triangular); j++ {
diff = diff.Multiply(e.triangular[i][j], pivot)
for k := 0; k < len(ret); k++ {
normalizedDiff = normalizedDiff.Multiply(ret[j][k], diff)
ret[i][k] = ret[i][k].Subtract(ret[i][k], normalizedDiff)
}
}
}
return
}

View File

@@ -0,0 +1,141 @@
package rlnc
import (
"testing"
"github.com/OffchainLabs/prysm/v6/testing/require"
ristretto "github.com/gtank/ristretto255"
)
func TestMatrixMul(t *testing.T) {
// chunks have length 3 and there are two chunks
s11 := randomScalar()
s12 := randomScalar()
s13 := randomScalar()
scalars1 := []*ristretto.Scalar{s11, s12, s13}
s21 := randomScalar()
s22 := randomScalar()
s23 := randomScalar()
scalars2 := []*ristretto.Scalar{s21, s22, s23}
data := [][]*ristretto.Scalar{scalars1, scalars2}
coeff1 := randomScalar()
coeff2 := randomScalar()
coeff3 := randomScalar()
badCofficients := []*ristretto.Scalar{coeff1, coeff2, coeff3}
coefficients := badCofficients[:2]
// Bad number of coefficients
lc, err := scalarLC(badCofficients, data)
require.NotNil(t, err)
require.IsNil(t, lc)
lc, err = scalarLC(coefficients, data)
require.NoError(t, err)
require.Equal(t, len(scalars1), len(lc))
require.NotNil(t, lc[0])
require.Equal(t, 1, ristretto.NewScalar().Add(
ristretto.NewScalar().Multiply(coeff1, s11),
ristretto.NewScalar().Multiply(coeff2, s21)).Equal(lc[0]))
require.Equal(t, 1, ristretto.NewScalar().Add(
ristretto.NewScalar().Multiply(coeff1, s12),
ristretto.NewScalar().Multiply(coeff2, s22)).Equal(lc[1]))
require.Equal(t, 1, ristretto.NewScalar().Add(
ristretto.NewScalar().Multiply(coeff1, s13),
ristretto.NewScalar().Multiply(coeff2, s23)).Equal(lc[2]))
}
func Test_isZeroVector(t *testing.T) {
zero := ristretto.NewScalar()
nonZero := randomScalar()
require.Equal(t, true, isZeroVector([]*ristretto.Scalar{zero, zero, zero}))
require.Equal(t, false, isZeroVector([]*ristretto.Scalar{zero, zero, nonZero}))
}
func Test_scalarOne(t *testing.T) {
scalar := scalarOne()
other := randomScalar()
third := ristretto.NewScalar()
third.Multiply(scalar, other)
require.Equal(t, 1, third.Equal(other))
}
func TestAddRow(t *testing.T) {
e := newEchelon(3)
row := []*ristretto.Scalar{randomScalar(), randomScalar()}
require.Equal(t, false, e.addRow(row))
require.Equal(t, 0, len(e.coefficients))
row = append(row, randomScalar())
require.Equal(t, true, e.addRow(row))
require.Equal(t, 1, len(e.coefficients))
require.Equal(t, false, e.addRow(row))
require.Equal(t, 1, len(e.coefficients))
row = []*ristretto.Scalar{randomScalar(), randomScalar(), randomScalar()}
require.Equal(t, true, e.addRow(row))
require.Equal(t, 2, len(e.coefficients))
require.Equal(t, 1, e.triangular[1][0].Equal(ristretto.NewScalar()))
row = []*ristretto.Scalar{randomScalar(), randomScalar(), randomScalar()}
require.Equal(t, true, e.addRow(row))
require.Equal(t, 3, len(e.coefficients))
require.Equal(t, 1, e.triangular[2][0].Equal(ristretto.NewScalar()))
require.Equal(t, 1, e.triangular[2][1].Equal(ristretto.NewScalar()))
row = []*ristretto.Scalar{randomScalar(), randomScalar(), randomScalar()}
require.Equal(t, false, e.addRow(row))
// Check that the transform * coefficients = triangular
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
prod := ristretto.NewScalar()
for k := 0; k < 3; k++ {
prod = prod.Add(prod, ristretto.NewScalar().Multiply(e.transform[i][k], e.coefficients[k][j]))
}
require.Equal(t, 1, prod.Equal(e.triangular[i][j]))
}
}
}
func TestInverse(t *testing.T) {
e := newEchelon(3)
_, err := e.inverse()
require.ErrorIs(t, ErrNoData, err)
row := []*ristretto.Scalar{randomScalar(), randomScalar(), randomScalar()}
require.Equal(t, true, e.addRow(row))
row = []*ristretto.Scalar{randomScalar(), randomScalar(), randomScalar()}
require.Equal(t, true, e.addRow(row))
row = []*ristretto.Scalar{randomScalar(), randomScalar(), randomScalar()}
require.Equal(t, true, e.addRow(row))
inv, err := e.inverse()
require.NoError(t, err)
require.Equal(t, 3, len(inv))
require.Equal(t, 3, len(inv[0]))
require.Equal(t, 3, len(inv[1]))
require.Equal(t, 3, len(inv[2]))
// Check that the inverse * coefficients = identity
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
prod := ristretto.NewScalar()
for k := 0; k < 3; k++ {
prod = prod.Add(prod, ristretto.NewScalar().Multiply(inv[i][k], e.coefficients[k][j]))
}
if j != i {
require.Equal(t, 1, prod.Equal(ristretto.NewScalar()))
} else {
require.Equal(t, 1, prod.Equal(scalarOne()))
}
}
}
}

View File

@@ -0,0 +1,141 @@
package rlnc
import (
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v6/crypto/rand"
ristretto "github.com/gtank/ristretto255"
"github.com/sirupsen/logrus"
)
type chunk struct {
data []*ristretto.Scalar
coefficients []*ristretto.Scalar
}
type Message struct {
chunk chunk
commitments []*ristretto.Element
}
// NewMessage creates a new message from a chunk interface.
func newMessage(c interfaces.ReadOnlyBeaconBlockChunk) (*Message, error) {
data, err := dataToVector(c.Data())
if err != nil {
return nil, err
}
coefficients, err := dataToVector(c.Coefficients())
if err != nil {
return nil, err
}
chunk := chunk{
data: data,
coefficients: coefficients,
}
commitments, err := dataToElements(c.Commitments())
if err != nil {
return nil, err
}
return &Message{
chunk: chunk,
commitments: commitments,
}, nil
}
// Verify verifies that the message is compatible with the signed committmments
func (m *Message) Verify(c *Committer) bool {
// We should get the same number of coefficients as commitments.
if len(m.chunk.coefficients) != len(m.commitments) {
return false
}
msm, err := c.commit(m.chunk.data)
if err != nil {
return false
}
if len(m.chunk.data) > c.num() {
return false
}
com := ristretto.NewElement().VarTimeMultiScalarMult(m.chunk.coefficients, m.commitments)
return msm.Equal(com) == 1
}
var scalarOneBytes = [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
func scalarOne() (ret *ristretto.Scalar) {
ret = &ristretto.Scalar{}
if err := ret.Decode(scalarOneBytes[:]); err != nil {
logrus.WithError(err).Error("failed to decode scalar one")
}
return
}
func randomScalar() (ret *ristretto.Scalar) {
buf := make([]byte, 64)
gen := rand.NewGenerator()
_, err := gen.Read(buf)
if err != nil {
return nil
}
ret = &ristretto.Scalar{}
ret.FromUniformBytes(buf)
return
}
// dataToVector converts a slice of scalars encoded as bytes to a slice of scalars.
func dataToVector(data [][]byte) ([]*ristretto.Scalar, error) {
ret := make([]*ristretto.Scalar, len(data))
for i, d := range data {
if len(d) != 32 {
return nil, ErrInvalidScalar
}
ret[i] = &ristretto.Scalar{}
if err := ret[i].Decode(d); err != nil {
return nil, ErrInvalidScalar
}
}
return ret, nil
}
// dataToElements converts a slice of scalars encoded as bytes to a slice of elements.
func dataToElements(data [][]byte) ([]*ristretto.Element, error) {
ret := make([]*ristretto.Element, len(data))
for i, d := range data {
if len(d) != 32 {
return nil, ErrInvalidElement
}
ret[i] = &ristretto.Element{}
if err := ret[i].Decode(d); err != nil {
return nil, ErrInvalidElement
}
}
return ret, nil
}
// Data returns the data of the message as serialized bytes
func (m *Message) Data() [][]byte {
data := make([][]byte, len(m.chunk.data))
for i, d := range m.chunk.data {
data[i] = d.Encode(nil)
}
return data
}
// Coefficients returns the coefficients of the message as serialized bytes
func (m *Message) Coefficients() [][]byte {
coefficients := make([][]byte, len(m.chunk.coefficients))
for i, c := range m.chunk.coefficients {
coefficients[i] = c.Encode(nil)
}
return coefficients
}
// Commitments returns the commitments of the message as serialized bytes
func (m *Message) Commitments() [][]byte {
commitments := make([][]byte, len(m.commitments))
for i, c := range m.commitments {
commitments[i] = make([]byte, 0)
commitments[i] = c.Encode(nil)
}
return commitments
}

View File

@@ -0,0 +1,100 @@
package rlnc
import (
"testing"
"github.com/OffchainLabs/prysm/v6/testing/require"
ristretto "github.com/gtank/ristretto255"
)
func TestVerify(t *testing.T) {
// chunks have length 3 and there are two chunks
s11 := randomScalar()
s12 := randomScalar()
s13 := randomScalar()
scalars1 := []*ristretto.Scalar{s11, s12, s13}
s21 := randomScalar()
s22 := randomScalar()
s23 := randomScalar()
scalars2 := []*ristretto.Scalar{s21, s22, s23}
data := [][]*ristretto.Scalar{scalars1, scalars2}
coeff1 := randomScalar()
coeff2 := randomScalar()
coeff3 := randomScalar()
goodCoefficients := []*ristretto.Scalar{coeff1, coeff2}
badCoefficients := []*ristretto.Scalar{coeff1, coeff3}
committer := newCommitter(3)
c1, err := committer.commit(scalars1)
require.NoError(t, err)
c2, err := committer.commit(scalars2)
require.NoError(t, err)
goodCommitments := []*ristretto.Element{c1, c2}
badCommitments := []*ristretto.Element{c1, c1}
goodData, err := scalarLC(goodCoefficients, data)
require.NoError(t, err)
badData, err := scalarLC(badCoefficients, data)
require.NoError(t, err)
tests := []struct {
name string
message *Message
expected bool
}{
{
name: "valid message",
message: &Message{
chunk: chunk{
data: goodData,
coefficients: goodCoefficients,
},
commitments: goodCommitments,
},
expected: true,
},
{
name: "invalid coefficients",
message: &Message{
chunk: chunk{
data: goodData,
coefficients: badCoefficients,
},
commitments: goodCommitments,
},
expected: false,
},
{
name: "invalid data",
message: &Message{
chunk: chunk{
data: badData,
coefficients: goodCoefficients,
},
commitments: goodCommitments,
},
expected: false,
},
{
name: "invalid commitments",
message: &Message{
chunk: chunk{
data: goodData,
coefficients: goodCoefficients,
},
commitments: badCommitments,
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.message.Verify(committer)
require.Equal(t, tt.expected, result)
})
}
}

View File

@@ -0,0 +1,342 @@
package rlnc
import (
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
ristretto "github.com/gtank/ristretto255"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// rlncBlockSuffix is a byte that is added to the end of the block to mark it's end.
var rlncBlockSuffix = byte(0xfe)
// Node represents a node in the RLNC network. It keeps the data it holds as a matrix of scalars
// as well as the commitments to the data. The coefficients and a partial inversion of the corresponding
// matrix is kept in the echelon object. The committer keeps the trusted setup generators
type Node struct {
chunks [][]*ristretto.Scalar
commitments []*ristretto.Element
echelon *echelon
committer *Committer
slot primitives.Slot // repeated but convenient for the validator client
proposerIndex primitives.ValidatorIndex // repeated but convenient for the validator client
parentRoot []byte // repeated but convenient for the validator client
signature []byte // repeated but convenient for the validator client
}
func NewNode(committer *Committer, size uint) *Node {
return &Node{
chunks: make([][]*ristretto.Scalar, 0),
echelon: newEchelon(size),
committer: committer,
}
}
func NewNodeFromChunkedBlock(committer *Committer, blk *ethpb.ChunkedBeaconBlock) (*Node, error) {
size := uint(len(blk.Chunks))
chunks := make([][]*ristretto.Scalar, size)
var err error
for i, c := range blk.Chunks {
chunks[i], err = dataToVector(c.Data)
if err != nil {
return nil, errors.Wrap(err, "could not convert chunk data to scalar")
}
}
commitments, err := dataToElements(blk.Header.Commitments)
if err != nil {
return nil, errors.Wrap(err, "could not convert chunk commitments to Ristretto elements")
}
return &Node{
chunks: chunks,
commitments: commitments,
echelon: newIdentityEchelon(size),
committer: committer,
slot: blk.Header.Slot,
proposerIndex: blk.Header.ProposerIndex,
parentRoot: blk.Header.ParentRoot,
signature: blk.Signature,
}, nil
}
func (n *Node) GetChunkedBlock(blk *ethpb.GenericSignedBeaconBlock) *ethpb.ChunkedBeaconBlock {
chunks := make([]*ethpb.BeaconBlockChunkData, len(n.chunks))
for i, c := range n.Data() {
chunks[i] = &ethpb.BeaconBlockChunkData{
Data: c,
}
}
header := &ethpb.BeaconBlockChunkHeader{
Slot: n.Slot(),
ProposerIndex: n.ProposerIndex(),
ParentRoot: n.ParentRoot(),
Commitments: n.Commitments(),
}
return &ethpb.ChunkedBeaconBlock{
Header: header,
Chunks: chunks,
Signature: n.Signature(),
Block: blk,
}
}
// NewSource creates a new node that holds all the data already chunked and committed.
// It is called by a broadcasting node starting the RLNC process.
func NewSource(committer *Committer, size uint, data []byte) (*Node, error) {
chunks := blockToChunks(size, data)
commitments, err := computeCommitments(committer, chunks)
if err != nil {
return nil, err
}
return &Node{
chunks: chunks,
commitments: commitments,
echelon: newIdentityEchelon(size),
committer: committer,
}, nil
}
// SetSlot sets the slot of the node.
func (n *Node) SetSlot(slot primitives.Slot) {
n.slot = slot
}
// SetProposerIndex sets the proposer index of the node.
func (n *Node) SetProposerIndex(proposerIndex primitives.ValidatorIndex) {
n.proposerIndex = proposerIndex
}
// SetSignature sets the signature of the node.
func (n *Node) SetSignature(signature []byte) {
n.signature = signature
}
// SetParentRoot sets the parent root of the node.
func (n *Node) SetParentRoot(parentRoot []byte) {
n.parentRoot = parentRoot
}
// Slot returns the slot of the node.
func (n *Node) Slot() primitives.Slot {
return n.slot
}
// ProposerIndex returns the proposer index of the node.
func (n *Node) ProposerIndex() primitives.ValidatorIndex {
return n.proposerIndex
}
// Signature returns the signature of the node.
func (n *Node) Signature() []byte {
return n.signature
}
// ParentRoot returns the parent root of the node.
func (n *Node) ParentRoot() []byte {
return n.parentRoot
}
// computeCommitments computes the commitments of the data in the node.
func computeCommitments(c *Committer, data [][]*ristretto.Scalar) (commitments []*ristretto.Element, err error) {
if len(data) == 0 {
return nil, nil
}
commitments = make([]*ristretto.Element, len(data))
for i, d := range data {
commitments[i], err = c.commit(d)
if err != nil {
return nil, err
}
}
return commitments, nil
}
func blockToChunks(numChunks uint, data []byte) [][]*ristretto.Scalar {
chunks := make([][]*ristretto.Scalar, numChunks)
chunkSize := ((uint(len(data))+numChunks-1)/numChunks + 30) / 31 * 31
for i := uint(0); i < numChunks; i++ {
start := i * chunkSize
end := start + chunkSize
if end > uint(len(data)) {
end = uint(len(data))
}
// The last chunk can actually be fully zeroes.
if start > uint(len(data)) {
start = end
}
chunk := data[start:end]
// Pad the chunk with zeroes if necessary
if uint(len(chunk)) < chunkSize {
paddedChunk := make([]byte, chunkSize)
copy(paddedChunk, chunk)
chunk = paddedChunk
}
chunks[i] = bytesToVector(chunk)
}
return chunks
}
// bytesToVector converts a byte slice to a vector of scalars. The input is expected to be a multiple of 31 bytes.
// We take the very naive approach of decoding each 31 bytes of data to a scalar of 32 bytes,
// this way we are guaranteed to get a valid little Endian encoding of the scalar.
// TODO: The actual network encoding should take care of adding 5 zero bits every 256 bits so that
// we can avoid copying memory every time here. This is a temporary solution.
func bytesToVector(data []byte) []*ristretto.Scalar {
ret := make([]*ristretto.Scalar, len(data)/31)
buf := [32]byte{}
for i := 0; i < len(data); i += 31 {
ret[i/31] = ristretto.NewScalar()
copy(buf[:], data[i:i+31])
if err := ret[i/31].Decode(buf[:]); err != nil {
logrus.WithError(err).Error("failed to decode scalar")
}
}
return ret
}
func (n *Node) generateRandomCoeffs() []*ristretto.Scalar {
coeffs := make([]*ristretto.Scalar, len(n.chunks))
for i := 0; i < len(n.chunks); i++ {
coeffs[i] = randomScalar()
}
return coeffs
}
func (n *Node) chunkLC(scalars []*ristretto.Scalar) (chunk, error) {
if len(scalars) != len(n.chunks) {
return chunk{}, ErrInvalidSize
}
data, err := scalarLC(scalars, n.chunks)
if err != nil {
return chunk{}, err
}
coefficients, err := scalarLC(scalars, n.echelon.coefficients)
if err != nil {
return chunk{}, err
}
return chunk{
data: data,
coefficients: coefficients,
}, nil
}
func (n *Node) PrepareMessage() (*Message, error) {
if len(n.chunks) == 0 {
return nil, ErrNoData
}
scalars := n.generateRandomCoeffs()
chunk, err := n.chunkLC(scalars)
if err != nil {
return nil, err
}
return &Message{
chunk: chunk,
commitments: n.commitments,
}, nil
}
// checkExistingCommitments returns true if the commitments are the same as the ones in the node or the node didn't have any.
func (n *Node) checkExistingCommitments(c []*ristretto.Element) bool {
if len(n.commitments) == 0 {
return true
}
if len(c) != len(n.commitments) {
return false
}
for i := 0; i < len(c); i++ {
if c[i].Equal(n.commitments[i]) != 1 {
return false
}
}
return true
}
// checkExistingChunks checks that the incoming chunk has the same size as the preexisting ones if any.
func (n *Node) checkExistingChunks(c []*ristretto.Scalar) bool {
if len(n.chunks) == 0 {
return true
}
return len(c) == len(n.chunks[0])
}
func (n *Node) receive(message *Message) error {
if !n.checkExistingCommitments(message.commitments) {
return ErrIncorrectCommitments
}
if !n.checkExistingChunks(message.chunk.data) {
return ErrInvalidSize
}
if len(message.chunk.coefficients) != len(message.commitments) {
return ErrInvalidSize
}
if !message.Verify(n.committer) {
return ErrInvalidMessage
}
if !n.echelon.addRow(message.chunk.coefficients) {
return ErrLinearlyDependentMessage
}
n.chunks = append(n.chunks, message.chunk.data)
if len(n.commitments) == 0 {
n.commitments = make([]*ristretto.Element, len(message.commitments))
for i, c := range message.commitments {
n.commitments[i] = &ristretto.Element{}
*n.commitments[i] = *c
}
}
return nil
}
// stripSuffix returns the slice before the last appearance of suffix, returns the whole data if suffix is not found
func stripSuffix(data []byte, suffix byte) []byte {
for i := len(data) - 1; i > 0; i-- {
if data[i] == suffix {
return data[:i]
}
}
return data
}
func (n *Node) decode() ([]byte, error) {
inverse, err := n.echelon.inverse()
if err != nil {
return nil, err
}
ret := make([]byte, 0, len(n.chunks)*len(n.chunks[0])*31+1)
prod := &ristretto.Scalar{}
for i := 0; i < len(inverse); i++ {
for k := 0; k < len(n.chunks[0]); k++ {
entry := ristretto.NewScalar()
for j := 0; j < len(inverse); j++ {
prod = prod.Multiply(inverse[i][j], n.chunks[j][k])
entry = entry.Add(entry, prod)
}
ret = entry.Encode(ret)[:len(ret)+31] // len(ret) is computed before the append.
}
}
return stripSuffix(ret, rlncBlockSuffix), nil
}
// Data returns the data of the chunks in a node as serialized bytes
func (m *Node) Data() [][][]byte {
ret := make([][][]byte, len(m.chunks))
for j, c := range m.chunks {
ret[j] = make([][]byte, len(c))
for i, d := range c {
ret[j][i] = d.Encode(nil)
}
}
return ret
}
// Commitments returns the commitments of the chunks in a node as serialized bytes
func (m *Node) Commitments() [][]byte {
ret := make([][]byte, len(m.commitments))
for j, c := range m.commitments {
ret[j] = c.Encode(nil)
}
return ret
}

View File

@@ -0,0 +1,121 @@
package rlnc
import (
"crypto/rand"
"encoding/hex"
"testing"
"github.com/OffchainLabs/prysm/v6/testing/require"
ristretto "github.com/gtank/ristretto255"
)
func TestPrepareMessage(t *testing.T) {
numChunks := uint(10)
chunkSize := uint(100)
committer := newCommitter(chunkSize)
block := make([]byte, numChunks*chunkSize*31)
_, err := rand.Read(block)
require.NoError(t, err)
node, err := NewSource(committer, numChunks, block)
require.NoError(t, err)
message, err := node.PrepareMessage()
require.NoError(t, err)
require.NotNil(t, message)
require.Equal(t, true, message.Verify(committer))
emptyNode := NewNode(committer, numChunks)
_, err = emptyNode.PrepareMessage()
require.ErrorIs(t, ErrNoData, err)
}
func TestReceive(t *testing.T) {
numChunks := uint(2)
chunkSize := uint(100)
committer := newCommitter(chunkSize)
block := make([]byte, numChunks*chunkSize*31)
_, err := rand.Read(block)
require.NoError(t, err)
node, err := NewSource(committer, numChunks, block)
require.NoError(t, err)
// Send one message
message, err := node.PrepareMessage()
require.NoError(t, err)
require.NotNil(t, message)
receiver := NewNode(committer, numChunks)
require.NoError(t, receiver.receive(message))
require.Equal(t, 1, len(receiver.chunks))
// Send another message
message, err = node.PrepareMessage()
require.NoError(t, err)
require.NotNil(t, message)
require.NoError(t, receiver.receive(message))
require.Equal(t, 2, len(receiver.chunks))
// The third one should fail
message, err = node.PrepareMessage()
require.NoError(t, err)
require.NotNil(t, message)
require.ErrorIs(t, ErrLinearlyDependentMessage, receiver.receive(message))
require.Equal(t, 2, len(receiver.chunks))
}
func TestDecode(t *testing.T) {
numChunks := uint(3)
chunkSize := uint(10)
committer := newCommitter(chunkSize)
block := make([]byte, numChunks*chunkSize*31)
_, err := rand.Read(block)
block[len(block)-1] = 0xfe // termination byte
require.NoError(t, err)
node, err := NewSource(committer, numChunks, block)
require.NoError(t, err)
emptyNode := NewNode(committer, numChunks)
for i := 0; i < int(numChunks); i++ {
_, err = emptyNode.decode()
require.ErrorIs(t, ErrNoData, err)
message, err := node.PrepareMessage()
require.NoError(t, err)
require.NoError(t, emptyNode.receive(message))
}
decoded, err := emptyNode.decode()
require.NoError(t, err)
require.DeepEqual(t, block[:len(block)-1], decoded)
}
func TestDecodeLarge(t *testing.T) {
numChunks := uint(10)
blockHex := "bc0e0401000901007b0907f0f500c1441f3332515f27b5371829016890ec25b7b6464ce1781f863a2d50bca20d13f464e4da6b4ee0f208dad1d890023d3bef4be7084148d9e709d06a727e5133e454000000a63c7a7a1e189e8b7ccb767bd808b97a6ef7e7bf445ea5b663f882102031d0f05cbdcf7cb691dd0769ca73771b0353a20a38e2e2872b1d034caf1b4d392e49d11730939b544208663c99b4eca4e6684bc0b52ad8da7d5b19399e4badd867fe0fd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e00000000000000006d58a19c0870ceeaa225e8edf1fcbca5ee993c26c3d8a81adc2d2903c857128d54616b6f79616b69000dfc3e010008440100150408ec04000504f079ffffffff8d0941132d8b818e3688d3fedaf35f06607329a11609c8988bc9beaba7620280eb1b638500f1b0be7851eeb718bd8c150fb7a0d93de240ec9633ef61ab0cb299fc2d7c9ebf7b257b708b2f09c880ceff9f56f57ba61b31b25aa7d837b57571ddec04000010000000f6000000dc010000c2020000e400199e00020d0b7eac010d27a201007e5000f0658c61ab8ddf9bca9e5f197b4c109ba711c47bd3ce1ee1817c34a62b0878c3d3d0743605ad2c921c1e374f17fc3e056c300e90274033765e377682d42ddf3803bd245566f7fcb05ee5152b318c7132df9e2f1909162f52d6ce5c3258ed1a4fd731560fe400000011af00030d097e96000d27a201007e5000f061823821d2cfa82d8d780ed0876c4d0c318f1b3fd4f1fd737786d0a21bdcef7c606bbad4131d8506ac553bf1abd1a187f911310bbb4a4ce878cf60b75540d5187b0a1ef67624cb1d3e39c66dbf932f1b0916d97d7f1b20ee2bf0cbc963f5e2efab9e062ee60011017e960011289e01007e5000f06192395c50082d15bc3c4e7fe88b105805813975c19a458fca626098365c255f377d9e245037c1b621750d263c565166510a43fa90e4478fb0cce3dde050db2b0f81ce2b20e8615defccab99423795f2d5edc31b2844602f913e416bd764f81abaf0062ee60091667e96009ede0011017e5000f065878aa8c6899a9ed25b66fcaee48033b6c1f16d23e1565327fcea6f9cc42b5731a46d9402a64d50314fca6e08b72d62a718c8324d00a2365190093221e51a109dadaed0f38b7e8ee402b557c7d4ba5b5775858a2190ac997dbc8bd9c336371f58520c6d58a19c6e6404f055ab4de8ffccf7b19aa6d7d4ccc4c82f091ebd5715b978681cf010a0b67a38242ae933d7238681678b4e14f1598387b70d3beaf7c956e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42100003100fe0100fe0100fe0100d601007e7401516e0880c3c9110b05010c7b66986701091cfc010000c0702734010c5e01008c114188951f5052ea700e7c99d32aabb6c5ba121b04d8d1ec722bb7d9d58306ccfc010000fe"
block, err := hex.DecodeString(blockHex)
require.NoError(t, err)
chunkSize := ((uint(len(block))+numChunks-1)/numChunks + 30) / 31 * 31
committer := newCommitter(chunkSize)
node, err := NewSource(committer, numChunks, block)
require.NoError(t, err)
emptyNode := NewNode(committer, numChunks)
for i := 0; i < int(numChunks); i++ {
_, err = emptyNode.decode()
require.ErrorIs(t, ErrNoData, err)
message, err := node.PrepareMessage()
require.NoError(t, err)
require.NoError(t, emptyNode.receive(message))
}
decoded, err := emptyNode.decode()
require.NoError(t, err)
// Remove termination byte
require.DeepEqual(t, block[:len(block)-1], decoded)
}
func TestOneScalar(t *testing.T) {
scalarHex := "1f5052ea700e7c99d32aabb6c5ba121b04d8d1ec722bb7d9d58306ccfc010000"
stringBytes := []byte{0x1f, 0x50, 0x52, 0xea, 0x70, 0xe, 0x7c, 0x99, 0xd3, 0x2a, 0xab, 0xb6, 0xc5, 0xba, 0x12, 0x1b, 0x4, 0xd8, 0xd1, 0xec, 0x72, 0x2b, 0xb7, 0xd9, 0xd5, 0x83, 0x6, 0xcc, 0xfc, 0x1, 0x0, 0x0}
scalarBytes, err := hex.DecodeString(scalarHex)
require.DeepEqual(t, stringBytes, scalarBytes)
require.NoError(t, err)
scalar := &ristretto.Scalar{}
require.NoError(t, scalar.Decode(scalarBytes))
obtained := scalar.Encode(nil)
require.DeepEqual(t, scalarBytes, obtained)
}

View File

@@ -0,0 +1,23 @@
package rlnc
import (
_ "embed"
"encoding/json"
ristretto "github.com/gtank/ristretto255"
"github.com/pkg/errors"
)
var (
//go:embed trusted_setup.json
embeddedTrustedSetup []byte // 311KB
)
func LoadTrustedSetup() (*Committer, error) {
var elements []*ristretto.Element
err := json.Unmarshal(embeddedTrustedSetup, &elements)
if err != nil {
return nil, errors.Wrap(err, "could not parse trusted setup JSON")
}
return &Committer{elements}, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
package rlnc
import (
"testing"
"github.com/OffchainLabs/prysm/v6/testing/require"
)
func TestTrustedSetup(t *testing.T) {
// Load the trusted setup.
committer, err := LoadTrustedSetup()
require.NoError(t, err)
require.NotNil(t, committer)
require.Equal(t, maxChunkSize, uint(committer.num()))
}

View File

@@ -9,15 +9,6 @@ import (
"sync"
"time"
lightClient "github.com/OffchainLabs/prysm/v6/beacon-chain/core/light-client"
lru "github.com/hashicorp/golang-lru"
pubsub "github.com/libp2p/go-libp2p-pubsub"
libp2pcore "github.com/libp2p/go-libp2p/core"
"github.com/libp2p/go-libp2p/core/peer"
gcache "github.com/patrickmn/go-cache"
"github.com/pkg/errors"
"github.com/trailofbits/go-mutexasserts"
"github.com/OffchainLabs/prysm/v6/async"
"github.com/OffchainLabs/prysm/v6/async/abool"
"github.com/OffchainLabs/prysm/v6/async/event"
@@ -26,6 +17,7 @@ import (
blockfeed "github.com/OffchainLabs/prysm/v6/beacon-chain/core/feed/block"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/feed/operation"
statefeed "github.com/OffchainLabs/prysm/v6/beacon-chain/core/feed/state"
lightClient "github.com/OffchainLabs/prysm/v6/beacon-chain/core/light-client"
"github.com/OffchainLabs/prysm/v6/beacon-chain/db"
"github.com/OffchainLabs/prysm/v6/beacon-chain/db/filesystem"
"github.com/OffchainLabs/prysm/v6/beacon-chain/execution"
@@ -38,6 +30,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/backfill/coverage"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/beacon-chain/verification"
lruwrpr "github.com/OffchainLabs/prysm/v6/cache/lru"
"github.com/OffchainLabs/prysm/v6/config/params"
@@ -48,6 +41,13 @@ import (
"github.com/OffchainLabs/prysm/v6/runtime"
prysmTime "github.com/OffchainLabs/prysm/v6/time"
"github.com/OffchainLabs/prysm/v6/time/slots"
lru "github.com/hashicorp/golang-lru"
pubsub "github.com/libp2p/go-libp2p-pubsub"
libp2pcore "github.com/libp2p/go-libp2p/core"
"github.com/libp2p/go-libp2p/core/peer"
gcache "github.com/patrickmn/go-cache"
"github.com/pkg/errors"
"github.com/trailofbits/go-mutexasserts"
)
var _ runtime.Service = (*Service)(nil)
@@ -103,6 +103,7 @@ type config struct {
clock *startup.Clock
stateNotifier statefeed.Notifier
blobStorage *filesystem.BlobStorage
chunkCommitter *rlnc.Committer
}
// This defines the interface for interacting with block chain service
@@ -167,6 +168,7 @@ type Service struct {
ctxMap ContextByteVersions
slasherEnabled bool
lcStore *lightClient.Store
blockChunkCache *rlnc.BlockChunkCache
}
// NewService initializes new regular sync service.
@@ -213,6 +215,7 @@ func NewService(ctx context.Context, opts ...Option) *Service {
r.subHandler = newSubTopicHandler()
r.rateLimiter = newRateLimiter(r.cfg.p2p)
r.initCaches()
async.RunEvery(ctx, 10*time.Minute, r.startChunkPruner)
return r
}
@@ -294,6 +297,7 @@ func (s *Service) initCaches() {
s.seenAttesterSlashingCache = make(map[uint64]bool)
s.seenProposerSlashingCache = lruwrpr.New(seenProposerSlashingSize)
s.badBlockCache = lruwrpr.New(badBlockSize)
s.blockChunkCache = rlnc.NewBlockChunkCache(s.cfg.chunkCommitter)
}
func (s *Service) waitForChainStart() {

View File

@@ -79,12 +79,21 @@ func (s *Service) activeSyncSubnetIndices(currentSlot primitives.Slot) []uint64
// Register PubSub subscribers
func (s *Service) registerSubscribers(epoch primitives.Epoch, digest [4]byte) {
s.subscribe(
p2p.BlockSubnetTopicFormat,
s.validateBeaconBlockPubSub,
s.beaconBlockSubscriber,
digest,
)
if features.Get().UseRLNC {
s.subscribe(
p2p.RLNCTopicFormat,
s.validateBeaconBlockChunkPubSub,
s.beaconBlockChunkSubscriber,
digest,
)
} else {
s.subscribe(
p2p.BlockSubnetTopicFormat,
s.validateBeaconBlockPubSub,
s.beaconBlockSubscriber,
digest,
)
}
s.subscribe(
p2p.AggregateAndProofSubnetTopicFormat,
s.validateAggregateAndProof,

View File

@@ -0,0 +1,246 @@
package sync
import (
"context"
"fmt"
"time"
"github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain"
core_chunks "github.com/OffchainLabs/prysm/v6/beacon-chain/core/chunks"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/feed"
blockfeed "github.com/OffchainLabs/prysm/v6/beacon-chain/core/feed/block"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/encoder"
"github.com/OffchainLabs/prysm/v6/beacon-chain/sync/rlnc"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v6/consensus-types/chunks"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing/trace"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/time/slots"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
fastssz "github.com/prysmaticlabs/fastssz"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
const meshSize = 40 // number of peers to send chunks
// beaconBlockChunkSubscriber is a noop since all syncing happens at the validation step
func (s *Service) beaconBlockChunkSubscriber(_ context.Context, _ proto.Message) error {
return nil
}
func (s *Service) validateBeaconBlockChunkPubSub(ctx context.Context, pid peer.ID, msg *pubsub.Message) (pubsub.ValidationResult, error) {
if pid == s.cfg.p2p.PeerID() {
return pubsub.ValidationAccept, nil
}
if s.cfg.initialSync.Syncing() {
return pubsub.ValidationIgnore, nil
}
ctx, span := trace.StartSpan(ctx, "sync.validateBeaconBlockChunkPubSub")
defer span.End()
m, err := s.decodePubsubMessage(msg)
if err != nil {
tracing.AnnotateError(span, err)
return pubsub.ValidationReject, errors.Wrap(err, "Could not decode message")
}
wrappedChunk, err := chunks.NewBlockChunk(m)
if err != nil {
tracing.AnnotateError(span, err)
return pubsub.ValidationReject, errors.Wrap(err, "Could not create chunk object")
}
// It's fine to use the same lock for both block and chunk validation
s.validateBlockLock.Lock()
defer s.validateBlockLock.Unlock()
chunk := interfaces.ReadOnlyBeaconBlockChunk(wrappedChunk)
if chunk.IsNil() {
return pubsub.ValidationReject, errors.New("chunk is nil")
}
// Ignore the chunk if we have seen the block
if s.hasSeenBlockIndexSlot(chunk.Slot(), chunk.ProposerIndex()) {
return pubsub.ValidationIgnore, nil
}
// Check if parent is a bad block and then reject the chunk.
if s.hasBadBlock(chunk.ParentRoot()) {
err := fmt.Errorf("received chunk that has an invalid parent %#x", chunk.ParentRoot())
log.WithError(err).Debug("Received block with an invalid parent")
return pubsub.ValidationReject, err
}
// Be lenient in handling early blocks. Instead of discarding blocks arriving later than
// MAXIMUM_GOSSIP_CLOCK_DISPARITY in future, we tolerate blocks arriving at max two slots
// earlier (SECONDS_PER_SLOT * 2 seconds). Queue such blocks and process them at the right slot.
genesisTime := uint64(s.cfg.clock.GenesisTime().Unix())
if err := slots.VerifyTime(genesisTime, chunk.Slot(), earlyBlockProcessingTolerance); err != nil {
log.WithError(err).Debug("Ignored chunk: could not verify slot time")
return pubsub.ValidationIgnore, nil
}
cp := s.cfg.chain.FinalizedCheckpt()
startSlot, err := slots.EpochStart(cp.Epoch)
if err != nil {
log.WithError(err).Debug("Ignored block: could not calculate epoch start slot")
return pubsub.ValidationIgnore, nil
}
if startSlot >= chunk.Slot() {
err := fmt.Errorf("finalized slot %d greater or equal to block slot %d", startSlot, chunk.Slot())
log.Debug(err)
return pubsub.ValidationIgnore, err
}
if !s.cfg.chain.HasBlock(ctx, chunk.ParentRoot()) {
// TODO: implement pending chunk storage
return pubsub.ValidationIgnore, err
}
// We ignore messages instead of accepting them to avoid rebroadcasting by gossipsub.
err = s.validateBeaconBlockChunk(ctx, chunk)
if errors.Is(err, rlnc.ErrLinearlyDependentMessage) {
log.Debug("ignoring linearly dependent message")
return pubsub.ValidationIgnore, nil
} else if err != nil {
// TODO: cook up a slashing object if the error is ErrIncorrectCommitments
log.WithError(err).Debug("Could not validate beacon block chunk")
return pubsub.ValidationReject, err
}
pr := chunk.ParentRoot()
logFields := logrus.Fields{
"chunkSlot": chunk.Slot(),
"proposerIndex": chunk.ProposerIndex(),
"parentRoot": fmt.Sprintf("%#x", pr[:8]),
}
log.WithFields(logFields).Debug("Received block chunk")
// If the block can be recovered, send it to the blockchain package
go s.reconstructBlockFromChunk(ctx, chunk)
go s.broadcastBlockChunk(ctx, chunk)
return pubsub.ValidationIgnore, nil
}
func (s *Service) validateBeaconBlockChunk(ctx context.Context, chunk interfaces.ReadOnlyBeaconBlockChunk) error {
if !s.cfg.chain.InForkchoice(chunk.ParentRoot()) {
return blockchain.ErrNotDescendantOfFinalized
}
err := s.blockChunkCache.AddChunk(chunk)
if err == nil {
return nil
}
if errors.Is(err, rlnc.ErrSignatureNotVerified) {
parentState, err := s.cfg.stateGen.StateByRoot(ctx, chunk.ParentRoot())
if err != nil {
s.blockChunkCache.RemoveNode(chunk) // Node is guaranteed to have a single chunk
return err
}
if err := core_chunks.VerifyChunkSignatureUsingCurrentFork(parentState, chunk); err != nil {
s.blockChunkCache.RemoveNode(chunk) // Node is guaranteed to have a single chunk
return err
}
return nil
}
return err
}
// startChunkPruner starts a goroutine that prunes the block chunk cache every epoch.
func (s *Service) startChunkPruner() {
cp := s.cfg.chain.FinalizedCheckpt()
fSlot, err := slots.EpochStart(cp.Epoch)
if err != nil {
log.WithError(err).Debug("could not prune the chunk cache: could not calculate epoch start slot")
} else {
s.blockChunkCache.Prune(fSlot)
}
}
func (s *Service) logReceivedBlock(blk interfaces.ReadOnlySignedBeaconBlock) {
pr := blk.Block().ParentRoot()
parentRoot := fmt.Sprintf("%#x", pr[:8])
since := time.Since(slots.StartTime(uint64(s.cfg.chain.GenesisTime().Unix()), blk.Block().Slot()))
log.WithFields(logrus.Fields{
"slot": blk.Block().Slot(),
"parentRoot": parentRoot,
"sinceSlotStart": since,
}).Debug("Received block")
}
func (s *Service) reconstructBlockFromChunk(ctx context.Context, chunk interfaces.ReadOnlyBeaconBlockChunk) {
data, err := s.blockChunkCache.GetBlockData(chunk.Slot(), chunk.ProposerIndex())
if err != nil {
return
}
msg := p2p.GossipTopicMappings(p2p.BlockSubnetTopicFormat, slots.ToEpoch(chunk.Slot()))
e := &encoder.SszNetworkEncoder{}
msgSSZ, ok := msg.(fastssz.Unmarshaler)
if !ok {
logrus.Error("Could not convert message to fastssz.Unmarshaler")
return
}
if err := e.DecodeGossip(data, msgSSZ); err != nil {
logrus.WithError(err).Error("Could not decode block data")
return
}
// We overwrite the signature in the block to keep it to be the original one in the database
// Unfortunately to do this without reflection we make extra copies of the full block. We could switch over the type instead.
blk, err := blocks.NewSignedBeaconBlock(msg)
if err != nil {
logrus.WithError(err).Error("Could not create signed beacon block")
return
}
// return early if we have already synced the block, this can happen by contention with the chunk syncing
if s.hasSeenBlockIndexSlot(chunk.Slot(), chunk.ProposerIndex()) {
return
}
sig := chunk.Signature()
blk.SetSignature(sig[:])
protoBlock, err := blk.Proto()
if err != nil {
logrus.WithError(err).Error("Could not convert block to protomessage")
return
}
logrus.WithFields(logrus.Fields{"slot": chunk.Slot(), "proposerIndex": chunk.ProposerIndex()}).Info("decoded beacon block")
// Broadcast the block on a feed to notify other services in the beacon node
// of a received block (even if it does not process correctly through a state transition).
s.cfg.blockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{
SignedBlock: blk,
},
})
// log the received block data
s.logReceivedBlock(blk)
// create a new context to process the block
go func() {
slotCtx, cancel := context.WithTimeout(context.Background(), time.Duration(params.BeaconConfig().SecondsPerSlot)*time.Second)
defer cancel()
if err := s.beaconBlockSubscriber(slotCtx, protoBlock); err != nil {
logrus.WithError(err).Error("Could not handle p2p pubsub")
}
}()
}
func (s *Service) broadcastBlockChunk(ctx context.Context, chunk interfaces.ReadOnlyBeaconBlockChunk) {
messages := make([]*ethpb.BeaconBlockChunk, 0, meshSize)
for i := 0; i < meshSize; i++ {
msg, err := s.blockChunkCache.PrepareMessage(chunk)
if err != nil {
log.WithError(err).Error("could not prepare message")
return
}
messages = append(messages, msg)
}
if err := s.cfg.p2p.BroadcastBlockChunks(ctx, messages); err != nil {
log.WithError(err).Error("chunk broadcast failed")
}
}

View File

@@ -293,8 +293,11 @@ func (s *Service) validatePhase0Block(ctx context.Context, blk interfaces.ReadOn
return nil, err
}
if err := blocks.VerifyBlockSignatureUsingCurrentFork(parentState, blk, blockRoot); err != nil {
return nil, err
if !features.Get().UseRLNC {
// TODO: verify the signature when using RLNC.
if err := blocks.VerifyBlockSignatureUsingCurrentFork(parentState, blk, blockRoot); err != nil {
return nil, err
}
}
// In the event the block is more than an epoch ahead from its
// parent state, we have to advance the state forward.

View File

@@ -92,11 +92,14 @@ func (s *Service) validateBlob(ctx context.Context, pid peer.ID, msg *pubsub.Mes
return pubsub.ValidationIgnore, err
}
if err := vf.SidecarParentValid(s.hasBadBlock); err != nil {
return pubsub.ValidationReject, err
// TODO: verify the proposer signature here by fetching it from the chunks cache, or putting the blob in a queue
if !features.Get().UseRLNC {
if err := vf.ValidProposerSignature(ctx); err != nil {
return pubsub.ValidationReject, err
}
}
if err := vf.ValidProposerSignature(ctx); err != nil {
if err := vf.SidecarParentValid(s.hasBadBlock); err != nil {
return pubsub.ValidationReject, err
}

View File

@@ -0,0 +1,3 @@
### Fixed
- Fixed proposing at genesis when starting post Bellatrix

View File

@@ -1,3 +0,0 @@
### Added
- Add native state diff type and marshalling functions

View File

@@ -0,0 +1,3 @@
### Ignored
- Removed unusued fieldparams

3
changelog/potuz_rlnc.md Normal file
View File

@@ -0,0 +1,3 @@
### Added
- Use random linear network coding for block propagation

View File

@@ -0,0 +1,3 @@
### Ignored
- Updated sha256 hashes for spectests at version v1.5.0. This was due to maintainers re-issuing the tar files to omit various empty directories.

3
changelog/tt_sandwich.md Normal file
View File

@@ -0,0 +1,3 @@
### Ignored
- Remove unused.

View File

@@ -50,6 +50,7 @@ type Flags struct {
EnableHistoricalSpaceRepresentation bool // EnableHistoricalSpaceRepresentation enables the saving of registry validators in separate buckets to save space
EnableBeaconRESTApi bool // EnableBeaconRESTApi enables experimental usage of the beacon REST API by the validator when querying a beacon node
EnableExperimentalAttestationPool bool // EnableExperimentalAttestationPool enables an experimental attestation pool design.
UseRLNC bool // UseRLNC enables the use of random linear network coding for gossiping.
// Logging related toggles.
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
EnableFullSSZDataLogging bool // Enables logging for full ssz data on rejected gossip messages
@@ -85,7 +86,8 @@ type Flags struct {
KeystoreImportDebounceInterval time.Duration
// AggregateIntervals specifies the time durations at which we aggregate attestations preparing for forkchoice.
AggregateIntervals [3]time.Duration
AggregateIntervals [3]time.Duration
DelayBlockBroadcast time.Duration // DelayBlockBroadcast is the time duration to delay block broadcast.
// Feature related flags (alignment forced in the end)
ForceHead string // ForceHead forces the head block to be a specific block root, the last head block, or the last finalized block.
@@ -274,6 +276,15 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logEnabled(enableExperimentalAttestationPool)
cfg.EnableExperimentalAttestationPool = true
}
if ctx.IsSet(useRLNC.Name) {
logEnabled(useRLNC)
cfg.UseRLNC = true
}
if ctx.IsSet(delayBlockBroadcast.Name) {
logEnabled(delayBlockBroadcast)
cfg.DelayBlockBroadcast = ctx.Duration(delayBlockBroadcast.Name)
}
if ctx.IsSet(forceHeadFlag.Name) {
logEnabled(forceHeadFlag)
cfg.ForceHead = ctx.String(forceHeadFlag.Name)
@@ -334,6 +345,10 @@ func ConfigureValidator(ctx *cli.Context) error {
logEnabled(EnableBeaconRESTApi)
cfg.EnableBeaconRESTApi = true
}
if ctx.IsSet(useRLNC.Name) {
logEnabled(useRLNC)
cfg.UseRLNC = true
}
cfg.KeystoreImportDebounceInterval = ctx.Duration(dynamicKeyReloadDebounceInterval.Name)
Init(cfg)
return nil

View File

@@ -176,6 +176,11 @@ var (
Name: "enable-experimental-attestation-pool",
Usage: "Enables an experimental attestation pool design.",
}
useRLNC = &cli.BoolFlag{
Name: "use-rlnc",
Usage: "Experimental: enables the use of random linear network coding for gossiping.",
Hidden: true,
}
// forceHeadFlag is a flag to force the head of the beacon chain to a specific block.
forceHeadFlag = &cli.StringFlag{
Name: "sync-from",
@@ -188,11 +193,18 @@ var (
Name: "blacklist-roots",
Usage: "A comma-separatted list of 0x-prefixed hexstrings. Declares blocks with the given blockroots to be invalid. It downscores peers that send these blocks.",
}
// delayBlockBroadcast is a flag for delaying the block broadcast to the network.
delayBlockBroadcast = &cli.DurationFlag{
Name: "delay-block-broadcast",
Usage: "Delays the block broadcast to the network.",
Value: 0,
}
)
// devModeFlags holds list of flags that are set when development mode is on.
var devModeFlags = []cli.Flag{
backfill.EnableExperimentalBackfill,
useRLNC,
}
// ValidatorFlags contains a list of all the feature flags that apply to the validator client.
@@ -208,11 +220,13 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
EnableMinimalSlashingProtection,
enableDoppelGangerProtection,
EnableBeaconRESTApi,
useRLNC,
}...)
// E2EValidatorFlags contains a list of the validator feature flags to be tested in E2E.
var E2EValidatorFlags = []string{
"--enable-doppelganger",
"--use-rlnc",
}
// BeaconChainFlags contains a list of all the feature flags that apply to the beacon-chain client.
@@ -245,6 +259,8 @@ var BeaconChainFlags = combinedFlags([]cli.Flag{
DisableQUIC,
EnableDiscoveryReboot,
enableExperimentalAttestationPool,
useRLNC,
delayBlockBroadcast,
forceHeadFlag,
blacklistRoots,
}, deprecatedBeaconFlags, deprecatedFlags, upcomingDeprecation)

View File

@@ -40,17 +40,7 @@ const (
PendingDepositsLimit = 134217728 // Maximum number of pending balance deposits in the beacon state.
PendingPartialWithdrawalsLimit = 134217728 // Maximum number of pending partial withdrawals in the beacon state.
PendingConsolidationsLimit = 262144 // Maximum number of pending consolidations in the beacon state.
MaxDepositRequestsPerPayload = 8192 // Maximum number of deposit requests in an execution payload.
MaxWithdrawalRequestsPerPayload = 16 // Maximum number of execution layer withdrawal requests in an execution payload.
MaxConsolidationRequestsPerPayload = 1 // Maximum number of consolidation requests in an execution payload.
MaxProposerSlashings = 16 // Maximum number of proposer slashings in a block.
MaxAttesterSlashings = 2 // Maximum number of attester slashings in a block.
MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block.
MaxAttestations = 128 // Maximum number of attestations in a block.
MaxAttestationsElectra = 8 // Maximum number of attestations in a block.
MaxDeposits = 16 // Maximum number of deposits in a block.
MaxVoluntaryExits = 16 // Maximum number of voluntary exits in a block.
MaxBlsToExecutionChanges = 16 // Maximum number of bls to execution changes in a block.
MaxRandomByte = uint64(1<<8 - 1) // MaxRandomByte defines max for a random byte using for proposer and sync committee sampling.
MaxRandomValueElectra = uint64(1<<16 - 1) // MaxRandomValueElectra defines max for a random value using for proposer and sync committee sampling.
)

View File

@@ -40,17 +40,7 @@ const (
PendingDepositsLimit = 134217728 // Maximum number of pending balance deposits in the beacon state.
PendingPartialWithdrawalsLimit = 64 // Maximum number of pending partial withdrawals in the beacon state.
PendingConsolidationsLimit = 64 // Maximum number of pending consolidations in the beacon state.
MaxDepositRequestsPerPayload = 4 // Maximum number of deposit requests in an execution payload.
MaxWithdrawalRequestsPerPayload = 2 // Maximum number of execution layer withdrawal requests in an execution payload.
MaxConsolidationRequestsPerPayload = 1 // Maximum number of consolidation requests in an execution payload.
MaxProposerSlashings = 16 // Maximum number of proposer slashings in a block.
MaxAttesterSlashings = 2 // Maximum number of attester slashings in a block.
MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block.
MaxAttestations = 128 // Maximum number of attestations in a block.
MaxAttestationsElectra = 8 // Maximum number of attestations in a block.
MaxDeposits = 16 // Maximum number of deposits in a block.
MaxVoluntaryExits = 16 // Maximum number of voluntary exits in a block.
MaxBlsToExecutionChanges = 16 // Maximum number of bls to execution changes in a block.
MaxRandomByte = uint64(1<<8 - 1) // Maximum value for a random value using for proposer and sync committee sampling.
MaxRandomValueElectra = uint64(1<<16 - 1) // Maximum value for a random value using for proposer and sync committee sampling.
)

View File

@@ -1,9 +0,0 @@
package params
var (
stateHierarchyExponents = []uint64{21, 18, 16, 13, 11, 9, 5}
)
func StateHierarchyExponents() []uint64 {
return stateHierarchyExponents
}

View File

@@ -40,12 +40,6 @@ func NewWrappedExecutionData(v proto.Message) (interfaces.ExecutionData, error)
case *enginev1.ExecutionBundleElectra:
// note: no payload changes in electra so using deneb
return WrappedExecutionPayloadDeneb(pbStruct.Payload)
case *enginev1.ExecutionPayloadHeader:
return WrappedExecutionPayloadHeader(pbStruct)
case *enginev1.ExecutionPayloadHeaderCapella:
return WrappedExecutionPayloadHeaderCapella(pbStruct)
case *enginev1.ExecutionPayloadHeaderDeneb:
return WrappedExecutionPayloadHeaderDeneb(pbStruct)
default:
return nil, ErrUnsupportedVersion
}

View File

@@ -18,7 +18,6 @@ import (
const (
payloadFieldIndex = 9
bodyFieldIndex = 4
)
func ComputeBlockBodyFieldRoots(ctx context.Context, blockBody *BeaconBlockBody) ([][]byte, error) {
@@ -196,45 +195,6 @@ func ComputeBlockBodyFieldRoots(ctx context.Context, blockBody *BeaconBlockBody)
return fieldRoots, nil
}
func ComputeBlockFieldRoots(ctx context.Context, block interfaces.ReadOnlyBeaconBlock) ([][]byte, error) {
_, span := trace.StartSpan(ctx, "blocks.ComputeBlockFieldRoots")
defer span.End()
if block == nil {
return nil, errNilBlock
}
fieldRoots := make([][]byte, 5)
for i := range fieldRoots {
fieldRoots[i] = make([]byte, 32)
}
// Slot
slotRoot := ssz.Uint64Root(uint64(block.Slot()))
copy(fieldRoots[0], slotRoot[:])
// Proposer Index
proposerRoot := ssz.Uint64Root(uint64(block.ProposerIndex()))
copy(fieldRoots[1], proposerRoot[:])
// Parent Root
parentRoot := block.ParentRoot()
copy(fieldRoots[2], parentRoot[:])
// State Root
stateRoot := block.StateRoot()
copy(fieldRoots[3], stateRoot[:])
// block body Root
blockBodyRoot, err := block.Body().HashTreeRoot()
if err != nil {
return nil, err
}
copy(fieldRoots[4], blockBodyRoot[:])
return fieldRoots, nil
}
func PayloadProof(ctx context.Context, block interfaces.ReadOnlyBeaconBlock) ([][]byte, error) {
i := block.Body()
blockBody, ok := i.(*BeaconBlockBody)

View File

@@ -0,0 +1,19 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"beacon_block_chunk.go",
"error.go",
],
importpath = "github.com/OffchainLabs/prysm/v6/consensus-types/chunks",
visibility = ["//visibility:public"],
deps = [
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
],
)

View File

@@ -0,0 +1,182 @@
package chunks
import (
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"github.com/OffchainLabs/prysm/v6/time/slots"
)
var _ interfaces.BeaconBlockChunk = &BeaconBlockChunk{}
type BeaconBlockChunk struct {
chunk *ethpb.BeaconBlockChunk
headerRoot [32]byte
version int
}
// Slot returns the slot of the beacon block chunk.
func (b *BeaconBlockChunk) Slot() primitives.Slot {
return b.chunk.Header.Slot
}
// ProposerIndex returns the proposer index of the beacon block chunk.
func (b *BeaconBlockChunk) ProposerIndex() primitives.ValidatorIndex {
return b.chunk.Header.ProposerIndex
}
// ParentRoot returns the parent root of the beacon block chunk.
func (b *BeaconBlockChunk) ParentRoot() [32]byte {
return [32]byte(b.chunk.Header.ParentRoot)
}
// Commitments returns the commitments of the beacon block chunk.
func (b *BeaconBlockChunk) Commitments() [][]byte {
cmts := make([][]byte, len(b.chunk.Header.Commitments))
for i, cmt := range b.chunk.Header.Commitments {
cmts[i] = make([]byte, len(cmt))
copy(cmts[i], cmt)
}
return cmts
}
// Signature returns the signature of the beacon block chunk.
func (b *BeaconBlockChunk) Signature() [96]byte {
return [96]byte(b.chunk.Signature)
}
// IsNil returns true if the beacon block chunk is nil.
func (b *BeaconBlockChunk) IsNil() bool {
if b == nil || b.chunk == nil || b.chunk.Header == nil {
return true
}
if b.chunk.Header.ParentRoot == nil {
return true
}
if b.chunk.Header.Commitments == nil {
return true
}
if b.chunk.Data == nil {
return true
}
return b.chunk.Signature == nil
}
// Data returns the data of the beacon block chunk.
func (b *BeaconBlockChunk) Data() [][]byte {
data := make([][]byte, len(b.chunk.Data))
for i, d := range b.chunk.Data {
data[i] = make([]byte, len(d))
copy(data[i], d)
}
return data
}
// Coefficients returns the coefficients of the beacon block chunk.
func (b *BeaconBlockChunk) Coefficients() [][]byte {
coefficients := make([][]byte, len(b.chunk.Coefficients))
for i, c := range b.chunk.Coefficients {
coefficients[i] = make([]byte, len(c))
copy(coefficients[i], c)
}
return coefficients
}
// Version returns the version of the beacon block chunk.
func (b *BeaconBlockChunk) Version() int {
return b.version
}
// HeaderRoot returns the root of the beacon block chunk header
func (b *BeaconBlockChunk) HeaderRoot() [32]byte {
return b.headerRoot
}
// Header returns a copy of the header of the beacon block chunk.
func (b *BeaconBlockChunk) Header() *ethpb.BeaconBlockChunkHeader {
root := b.ParentRoot()
return &ethpb.BeaconBlockChunkHeader{
Slot: b.chunk.Header.Slot,
ProposerIndex: b.chunk.Header.ProposerIndex,
ParentRoot: root[:],
Commitments: b.Commitments(),
}
}
func NewBlockChunk(i interface{}) (*BeaconBlockChunk, error) {
switch b := i.(type) {
case nil:
return nil, ErrNilObject
case *ethpb.BeaconBlockChunk:
root, err := b.Header.HashTreeRoot()
if err != nil {
return nil, err
}
return &BeaconBlockChunk{chunk: b, headerRoot: root, version: slotToVersion(b.Header.Slot)}, nil
default:
return nil, ErrInvalidType
}
}
func slotToVersion(slot primitives.Slot) int {
epoch := slots.ToEpoch(slot)
cfg := params.BeaconConfig()
if epoch < cfg.AltairForkEpoch {
return version.Phase0
}
if epoch < cfg.BellatrixForkEpoch {
return version.Altair
}
if epoch < cfg.CapellaForkEpoch {
return version.Bellatrix
}
if epoch < cfg.DenebForkEpoch {
return version.Capella
}
if epoch < cfg.ElectraForkEpoch {
return version.Deneb
}
return version.Electra
}
// SetParentRoot sets the parent root of the beacon block chunk.
func (b *BeaconBlockChunk) SetParentRoot(root []byte) {
b.chunk.Header.ParentRoot = root
}
// SetCommitments sets the commitments of the beacon block chunk.
func (b *BeaconBlockChunk) SetCommitments(commitments [][]byte) {
b.chunk.Header.Commitments = commitments
}
// SetCoefficients sets the coefficients of the beacon block chunk.
func (b *BeaconBlockChunk) SetCoefficients(coefficients [][]byte) {
b.chunk.Coefficients = coefficients
}
// SetData sets the data of the beacon block chunk.
func (b *BeaconBlockChunk) SetData(data [][]byte) {
b.chunk.Data = data
}
// SetSignature sets the signature of the beacon block chunk.
func (b *BeaconBlockChunk) SetSignature(signature [96]byte) {
b.chunk.Signature = signature[:]
}
// SetSlot sets the slot of the beacon block chunk.
func (b *BeaconBlockChunk) SetSlot(slot primitives.Slot) {
b.chunk.Header.Slot = slot
}
// SetProposerIndex sets the proposer index of the beacon block chunk.
func (b *BeaconBlockChunk) SetProposerIndex(index primitives.ValidatorIndex) {
b.chunk.Header.ProposerIndex = index
}
// SetVersion sets the version of the beacon block chunk.
func (b *BeaconBlockChunk) SetVersion(version int) {
b.version = version
}

View File

@@ -0,0 +1,6 @@
package chunks
import "errors"
var ErrNilObject = errors.New("nil object") // ErrNilObject is returned when a nil object is received instead of a valid chunk
var ErrInvalidType = errors.New("invalid type") // ErrInvalidType is returned when an invalid type is received instead of a valid chunk

View File

@@ -1,33 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["state_diff.go"],
importpath = "github.com/OffchainLabs/prysm/v6/consensus-types/hdiff",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/state:go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/helpers:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["state_diff_test.go"],
embed = [":go_default_library"],
deps = [
"//testing/require:go_default_library",
"//testing/util:go_default_library",
],
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,60 +0,0 @@
package hdiff
import (
"testing"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/testing/util"
)
func Test_diffToState(t *testing.T) {
source, _ := util.DeterministicGenesisStateElectra(t, 256)
target := source.Copy()
require.NoError(t, target.SetSlot(source.Slot()+1))
hdiff, err := diffToState(source, target)
require.NoError(t, err)
require.Equal(t, hdiff.slot, target.Slot())
require.Equal(t, hdiff.targetVersion, target.Version())
}
func Test_kmpIndex(t *testing.T) {
intSlice := make([]*int, 10)
for i := 0; i < len(intSlice); i++ {
intSlice[i] = new(int)
*intSlice[i] = i
}
integerEquals := func(a, b *int) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
return *a == *b
}
t.Run("integer entries match", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[2], intSlice[3], intSlice[4]}
target := []*int{intSlice[2], intSlice[3], intSlice[4], intSlice[5], intSlice[6], intSlice[7], nil}
target = append(target, source...)
require.Equal(t, 2, kmpIndex(len(source), target, integerEquals))
})
t.Run("integer entries skipped", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[2], intSlice[3], intSlice[4]}
target := []*int{intSlice[2], intSlice[3], intSlice[4], intSlice[0], intSlice[5], nil}
target = append(target, source...)
require.Equal(t, 2, kmpIndex(len(source), target, integerEquals))
})
t.Run("integer entries repetitions", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[0], intSlice[0], intSlice[0]}
target := []*int{intSlice[0], intSlice[0], intSlice[1], intSlice[2], intSlice[5], nil}
target = append(target, source...)
require.Equal(t, 3, kmpIndex(len(source), target, integerEquals))
})
t.Run("integer entries no match", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[2], intSlice[3]}
target := []*int{intSlice[4], intSlice[5], intSlice[6], nil}
target = append(target, source...)
require.Equal(t, len(source), kmpIndex(len(source), target, integerEquals))
})
}

View File

@@ -1,9 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["comparisons.go"],
importpath = "github.com/OffchainLabs/prysm/v6/consensus-types/helpers",
visibility = ["//visibility:public"],
deps = ["//proto/prysm/v1alpha1:go_default_library"],
)

View File

@@ -1,109 +0,0 @@
package helpers
import (
"bytes"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
)
func ForksEqual(s, t *ethpb.Fork) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if s.Epoch != t.Epoch {
return false
}
if !bytes.Equal(s.PreviousVersion, t.PreviousVersion) {
return false
}
return bytes.Equal(s.CurrentVersion, t.CurrentVersion)
}
func BlockHeadersEqual(s, t *ethpb.BeaconBlockHeader) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if s.Slot != t.Slot {
return false
}
if s.ProposerIndex != t.ProposerIndex {
return false
}
if !bytes.Equal(s.ParentRoot, t.ParentRoot) {
return false
}
if !bytes.Equal(s.StateRoot, t.StateRoot) {
return false
}
return bytes.Equal(s.BodyRoot, t.BodyRoot)
}
func Eth1DataEqual(s, t *ethpb.Eth1Data) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if !bytes.Equal(s.DepositRoot, t.DepositRoot) {
return false
}
if s.DepositCount != t.DepositCount {
return false
}
return bytes.Equal(s.BlockHash, t.BlockHash)
}
func PendingDepositsEqual(s, t *ethpb.PendingDeposit) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if !bytes.Equal(s.PublicKey, t.PublicKey) {
return false
}
if !bytes.Equal(s.WithdrawalCredentials, t.WithdrawalCredentials) {
return false
}
if s.Amount != t.Amount {
return false
}
if !bytes.Equal(s.Signature, t.Signature) {
return false
}
return s.Slot == t.Slot
}
func PendingPartialWithdrawalsEqual(s, t *ethpb.PendingPartialWithdrawal) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if s.Index != t.Index {
return false
}
if s.Amount != t.Amount {
return false
}
return s.WithdrawableEpoch == t.WithdrawableEpoch
}
func PendingConsolidationsEqual(s, t *ethpb.PendingConsolidation) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
return s.SourceIndex == t.SourceIndex && s.TargetIndex == t.TargetIndex
}

View File

@@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"beacon_block.go",
"beacon_block_chunk.go",
"error.go",
"light_client.go",
"utils.go",

View File

@@ -0,0 +1,35 @@
package interfaces
import (
field_params "github.com/OffchainLabs/prysm/v6/config/fieldparams"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
)
// ReadOnlyBeaconBlockChunk is an interface describing the method set of
// a signed beacon block chunk
type ReadOnlyBeaconBlockChunk interface {
IsNil() bool
Version() int
Slot() primitives.Slot
ProposerIndex() primitives.ValidatorIndex
ParentRoot() [field_params.RootLength]byte
Commitments() [][]byte
Signature() [field_params.BLSSignatureLength]byte
Header() *ethpb.BeaconBlockChunkHeader
HeaderRoot() [field_params.RootLength]byte
Data() [][]byte
Coefficients() [][]byte
}
type BeaconBlockChunk interface {
ReadOnlyBeaconBlockChunk
SetParentRoot([]byte)
SetProposerIndex(idx primitives.ValidatorIndex)
SetSlot(slot primitives.Slot)
SetSignature(sig [96]byte)
SetVersion(version int)
SetCommitments(commitments [][]byte)
SetData(data [][]byte)
SetCoefficients(coefficients [][]byte)
}

View File

@@ -373,8 +373,8 @@ def prysm_deps():
go_repository(
name = "com_github_cespare_cp",
importpath = "github.com/cespare/cp",
sum = "h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=",
version = "v1.1.1",
sum = "h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=",
version = "v0.1.0",
)
go_repository(
name = "com_github_cespare_xxhash",
@@ -1395,6 +1395,12 @@ def prysm_deps():
sum = "h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=",
version = "v2.25.1",
)
go_repository(
name = "com_github_gtank_ristretto255",
importpath = "github.com/gtank/ristretto255",
sum = "h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=",
version = "v0.1.2",
)
go_repository(
name = "com_github_guptarohit_asciigraph",
importpath = "github.com/guptarohit/asciigraph",
@@ -1999,8 +2005,9 @@ def prysm_deps():
name = "com_github_libp2p_go_libp2p_pubsub",
build_file_proto_mode = "disable_global",
importpath = "github.com/libp2p/go-libp2p-pubsub",
sum = "h1:RmFQ2XAy3zQtbt2iNPy7Tt0/3fwTnHpCQSSnmGnt1Ps=",
version = "v0.13.0",
replace = "github.com/nisdas/go-libp2p-pubsub",
sum = "h1:eqQDbjtvXnkZcpC7BayY5FeIzpSGHeDyn53EZ9qVha0=",
version = "v0.3.3-0.20250127130457-2becfc6d889e",
)
go_repository(
name = "com_github_libp2p_go_libp2p_testing",

View File

@@ -153,13 +153,6 @@ func WithdrawalRequestsSliceRoot(withdrawalRequests []*enginev1.WithdrawalReques
return SliceRoot(withdrawalRequests, limit)
}
// ConsolidationRequestsSliceRoot computes the HTR of a slice of consolidation requests from the EL.
// The limit parameter is used as input to the bitwise merkleization algorithm.
func ConsolidationRequestsSliceRoot(consolidationRequests []*enginev1.ConsolidationRequest, limit uint64) ([32]byte, error) {
return SliceRoot(consolidationRequests, limit)
}
// ByteSliceRoot is a helper func to merkleize an arbitrary List[Byte, N]
// this func runs Chunkify + MerkleizeVector
// max length is dividable by 32 ( root length )

4
go.mod
View File

@@ -30,6 +30,7 @@ require (
github.com/gostaticanalysis/comment v1.4.2
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/gtank/ristretto255 v0.1.2
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/herumi/bls-eth-go-binary v1.31.0
github.com/holiman/uint256 v1.3.2
@@ -112,7 +113,6 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.17.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/cp v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/cockroachdb/errors v1.11.3 // indirect
@@ -289,3 +289,5 @@ require (
)
replace github.com/json-iterator/go => github.com/prestonvanloon/go v1.1.7-0.20190722034630-4f2e55fcf87b
replace github.com/libp2p/go-libp2p-pubsub => github.com/nisdas/go-libp2p-pubsub v0.3.3-0.20250127130457-2becfc6d889e

10
go.sum
View File

@@ -110,8 +110,8 @@ github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -452,6 +452,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpg
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -589,8 +591,6 @@ github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl9
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-mplex v0.9.0 h1:R58pDRAmuBXkYugbSSXR9wrTX3+1pFM1xP2bLuodIq8=
github.com/libp2p/go-libp2p-mplex v0.9.0/go.mod h1:ro1i4kuwiFT+uMPbIDIFkcLs1KRbNp0QwnUXM+P64Og=
github.com/libp2p/go-libp2p-pubsub v0.13.0 h1:RmFQ2XAy3zQtbt2iNPy7Tt0/3fwTnHpCQSSnmGnt1Ps=
github.com/libp2p/go-libp2p-pubsub v0.13.0/go.mod h1:m0gpUOyrXKXdE7c8FNQ9/HLfWbxaEw7xku45w+PaqZo=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
@@ -733,6 +733,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nisdas/go-libp2p-pubsub v0.3.3-0.20250127130457-2becfc6d889e h1:eqQDbjtvXnkZcpC7BayY5FeIzpSGHeDyn53EZ9qVha0=
github.com/nisdas/go-libp2p-pubsub v0.3.3-0.20250127130457-2becfc6d889e/go.mod h1:m0gpUOyrXKXdE7c8FNQ9/HLfWbxaEw7xku45w+PaqZo=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=

View File

@@ -28,7 +28,7 @@ message ExecutionPayload {
bytes parent_hash = 1 [ (ethereum.eth.ext.ssz_size) = "32" ];
bytes fee_recipient = 2 [ (ethereum.eth.ext.ssz_size) = "20" ];
bytes state_root = 3 [ (ethereum.eth.ext.ssz_size) = "32" ];
bytes receipts_root = 4 [ (ethereum.eth.ext.ssz_size) = "32" ];
bytes receipts_root = 4 [ (ethereum.eth.ext.ssz_size) = "32" ];
bytes logs_bloom = 5 [ (ethereum.eth.ext.ssz_size) = "logs_bloom.size" ];
bytes prev_randao = 6 [ (ethereum.eth.ext.ssz_size) = "32" ];
uint64 block_number = 7;
@@ -249,10 +249,10 @@ message BlobsBundle {
];
}
// BlobsBundleV2 is retrieved through engine-api from the execution layer client.
// It consists of the necessary components for constructing data column sidecars
// objects to gossip through p2p.
// It is introduced in Fulu network upgrade.
// BlobsBundleV2 is retrieved through engine-api from the execution layer
// client. It consists of the necessary components for constructing data column
// sidecars objects to gossip through p2p. It is introduced in Fulu network
// upgrade.
message BlobsBundleV2 {
repeated bytes kzg_commitments = 1 [
(ethereum.eth.ext.ssz_size) = "?,48",
@@ -261,7 +261,8 @@ message BlobsBundleV2 {
repeated bytes proofs = 2 [
(ethereum.eth.ext.ssz_size) = "?,48",
(ethereum.eth.ext.ssz_max) = "max_cell_proofs_length.size" // Changed in EIP-7594
(ethereum.eth.ext.ssz_max) =
"max_cell_proofs_length.size" // Changed in EIP-7594
];
repeated bytes blobs = 3 [
@@ -276,17 +277,18 @@ message Blob {
bytes data = 1 [ (ethereum.eth.ext.ssz_size) = "blob.size" ];
}
// BlobAndProofV2 consists of the blob and the cell proofs for each cell in the blob.
// BlobAndProofV2 consists of the blob and the cell proofs for each cell in the
// blob.
message BlobAndProof {
bytes blob = 1 [ (ethereum.eth.ext.ssz_size) = "blob.size" ];
bytes kzg_proof = 2 [ (ethereum.eth.ext.ssz_size) = "48" ];
}
// BlobAndProofV2 consists of the blob and the cell proofs for each cell in the blob.
// It is introduced in Fulu network upgrade.
// BlobAndProofV2 consists of the blob and the cell proofs for each cell in the
// blob. It is introduced in Fulu network upgrade.
message BlobAndProofV2 {
bytes blob = 1 [ (ethereum.eth.ext.ssz_size) = "blob.size" ];
repeated bytes kzg_proofs = 2 [
repeated bytes kzg_proofs = 2 [
(ethereum.eth.ext.ssz_size) = "48",
(ethereum.eth.ext.ssz_max) = "max_cell_proofs_length.size"
];

View File

@@ -129,16 +129,16 @@ var file_proto_engine_v1_fulu_proto_rawDesc = []byte{
0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03,
0x28, 0x0c, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0x8e, 0x01, 0x0a, 0x16, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74,
0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0x8d, 0x01, 0x0a, 0x16, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31,
0x42, 0x0c, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79,
0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d,
0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65,
0x2f, 0x76, 0x31, 0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x76, 0x31, 0xaa, 0x02, 0x12, 0x45,
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x56,
0x31, 0xca, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x6e, 0x67,
0x69, 0x6e, 0x65, 0x5c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66,
0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f,
0x76, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f,
0x76, 0x31, 0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x76, 0x31, 0xaa, 0x02, 0x12, 0x45, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31,
0xca, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x6e, 0x67, 0x69,
0x6e, 0x65, 0x5c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -6,7 +6,7 @@ import "proto/eth/ext/options.proto";
import "proto/engine/v1/execution_engine.proto";
option csharp_namespace = "Ethereum.Engine.V1";
option go_package = "github.com/prysmaticlabs/prysm/v5/proto/engine/v1;enginev1";
option go_package = "github.com/OffchainLabs/prysm/v6/proto/engine/v1;enginev1";
option java_multiple_files = true;
option java_outer_classname = "ElectraProto";
option java_package = "org.ethereum.engine.v1";

View File

@@ -19,18 +19,28 @@ import "proto/eth/ext/options.proto";
import "proto/prysm/v1alpha1/beacon_block.proto";
option csharp_namespace = "Ethereum.Eth.v1alpha1";
option go_package = "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1;eth";
option go_package = "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1;eth";
option java_multiple_files = true;
option java_outer_classname = "DataColumnsProto";
option java_package = "org.ethereum.eth.v1alpha1";
option php_namespace = "Ethereum\\Eth\\v1alpha1";
message DataColumnSidecar {
uint64 index = 1;
repeated bytes column = 2 [(ethereum.eth.ext.ssz_size) = "?,bytes_per_cell.size", (ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"];
repeated bytes kzg_commitments = 3 [(ethereum.eth.ext.ssz_size) = "?,48", (ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"];
repeated bytes kzg_proofs = 4 [(ethereum.eth.ext.ssz_size) = "?,48", (ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"];
repeated bytes column = 2 [
(ethereum.eth.ext.ssz_size) = "?,bytes_per_cell.size",
(ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"
];
repeated bytes kzg_commitments = 3 [
(ethereum.eth.ext.ssz_size) = "?,48",
(ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"
];
repeated bytes kzg_proofs = 4 [
(ethereum.eth.ext.ssz_size) = "?,48",
(ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"
];
SignedBeaconBlockHeader signed_block_header = 5;
repeated bytes kzg_commitments_inclusion_proof = 6 [(ethereum.eth.ext.ssz_size) = "kzg_commitments_inclusion_proof_depth.size,32"];
repeated bytes kzg_commitments_inclusion_proof = 6
[ (ethereum.eth.ext.ssz_size) =
"kzg_commitments_inclusion_proof_depth.size,32" ];
}

View File

@@ -5,46 +5,6 @@ import (
ethpbalpha "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
)
// V1Alpha1SignedHeaderToV1 converts a v1alpha1 signed beacon block header to v1.
func V1Alpha1SignedHeaderToV1(v1alpha1Hdr *ethpbalpha.SignedBeaconBlockHeader) *ethpbv1.SignedBeaconBlockHeader {
if v1alpha1Hdr == nil || v1alpha1Hdr.Header == nil {
return &ethpbv1.SignedBeaconBlockHeader{}
}
return &ethpbv1.SignedBeaconBlockHeader{
Message: V1Alpha1HeaderToV1(v1alpha1Hdr.Header),
Signature: v1alpha1Hdr.Signature,
}
}
// V1Alpha1HeaderToV1 converts a v1alpha1 beacon block header to v1.
func V1Alpha1HeaderToV1(v1alpha1Hdr *ethpbalpha.BeaconBlockHeader) *ethpbv1.BeaconBlockHeader {
if v1alpha1Hdr == nil {
return &ethpbv1.BeaconBlockHeader{}
}
return &ethpbv1.BeaconBlockHeader{
Slot: v1alpha1Hdr.Slot,
ProposerIndex: v1alpha1Hdr.ProposerIndex,
ParentRoot: v1alpha1Hdr.ParentRoot,
StateRoot: v1alpha1Hdr.StateRoot,
BodyRoot: v1alpha1Hdr.BodyRoot,
}
}
// V1HeaderToV1Alpha1 converts a v1 beacon block header to v1alpha1.
func V1HeaderToV1Alpha1(v1Header *ethpbv1.BeaconBlockHeader) *ethpbalpha.BeaconBlockHeader {
if v1Header == nil {
return &ethpbalpha.BeaconBlockHeader{}
}
return &ethpbalpha.BeaconBlockHeader{
Slot: v1Header.Slot,
ProposerIndex: v1Header.ProposerIndex,
ParentRoot: v1Header.ParentRoot,
StateRoot: v1Header.StateRoot,
BodyRoot: v1Header.BodyRoot,
}
}
// V1ValidatorToV1Alpha1 converts a v1 validator to v1alpha1.
func V1ValidatorToV1Alpha1(v1Validator *ethpbv1.Validator) *ethpbalpha.Validator {
if v1Validator == nil {

View File

@@ -51,7 +51,9 @@ ssz_phase0_objs = [
"AttestationData",
"AttesterSlashing",
"BeaconBlock",
"BeaconBlockChunk",
"BeaconBlockHeader",
"BeaconBlockChunkHeader",
"BeaconState",
"Checkpoint",
"Deposit",
@@ -390,6 +392,7 @@ ssz_proto_files(
srcs = [
"attestation.proto",
"beacon_block.proto",
"beacon_block_chunk.proto",
"beacon_state.proto",
"blobs.proto",
"data_columns.proto",

View File

@@ -129,7 +129,8 @@ message AggregateAttestationAndProofElectra {
// The aggregated attestation that was submitted.
AttestationElectra aggregate = 2;
// 96 byte selection proof signed by the aggregator, which is the signature of the slot to aggregate.
// 96 byte selection proof signed by the aggregator, which is the signature of
// the slot to aggregate.
bytes selection_proof = 3 [ (ethereum.eth.ext.ssz_size) = "96" ];
}

View File

@@ -941,16 +941,19 @@ message BuilderBidDeneb {
}
message BuilderBidElectra {
ethereum.engine.v1.ExecutionPayloadHeaderDeneb header = 1;
repeated bytes blob_kzg_commitments = 2 [(ethereum.eth.ext.ssz_size) = "?,48", (ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"];
ethereum.engine.v1.ExecutionRequests execution_requests = 3;
bytes value = 4 [(ethereum.eth.ext.ssz_size) = "32"];
bytes pubkey = 5 [(ethereum.eth.ext.ssz_size) = "48"];
ethereum.engine.v1.ExecutionPayloadHeaderDeneb header = 1;
repeated bytes blob_kzg_commitments = 2 [
(ethereum.eth.ext.ssz_size) = "?,48",
(ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"
];
ethereum.engine.v1.ExecutionRequests execution_requests = 3;
bytes value = 4 [ (ethereum.eth.ext.ssz_size) = "32" ];
bytes pubkey = 5 [ (ethereum.eth.ext.ssz_size) = "48" ];
}
message SignedBuilderBidElectra {
BuilderBidElectra message = 1 ;
bytes signature = 2 [(ethereum.eth.ext.ssz_size) = "96"];
BuilderBidElectra message = 1;
bytes signature = 2 [ (ethereum.eth.ext.ssz_size) = "96" ];
}
message BlobSidecars {

472
proto/prysm/v1alpha1/beacon_block_chunk.pb.go generated Executable file
View File

@@ -0,0 +1,472 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc v3.21.7
// source: proto/prysm/v1alpha1/beacon_block_chunk.proto
package eth
import (
reflect "reflect"
sync "sync"
github_com_OffchainLabs_prysm_v6_consensus_types_primitives "github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
_ "github.com/OffchainLabs/prysm/v6/proto/eth/ext"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type BeaconBlockChunk struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Data [][]byte `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty" ssz-max:"32768" ssz-size:"?,32"`
Coefficients [][]byte `protobuf:"bytes,2,rep,name=coefficients,proto3" json:"coefficients,omitempty" ssz-max:"128" ssz-size:"?,32"`
Header *BeaconBlockChunkHeader `protobuf:"bytes,3,opt,name=header,proto3" json:"header,omitempty"`
Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"`
}
func (x *BeaconBlockChunk) Reset() {
*x = BeaconBlockChunk{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BeaconBlockChunk) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BeaconBlockChunk) ProtoMessage() {}
func (x *BeaconBlockChunk) ProtoReflect() protoreflect.Message {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BeaconBlockChunk.ProtoReflect.Descriptor instead.
func (*BeaconBlockChunk) Descriptor() ([]byte, []int) {
return file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescGZIP(), []int{0}
}
func (x *BeaconBlockChunk) GetData() [][]byte {
if x != nil {
return x.Data
}
return nil
}
func (x *BeaconBlockChunk) GetCoefficients() [][]byte {
if x != nil {
return x.Coefficients
}
return nil
}
func (x *BeaconBlockChunk) GetHeader() *BeaconBlockChunkHeader {
if x != nil {
return x.Header
}
return nil
}
func (x *BeaconBlockChunk) GetSignature() []byte {
if x != nil {
return x.Signature
}
return nil
}
type BeaconBlockChunkHeader struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Slot github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot `protobuf:"varint,1,opt,name=slot,proto3" json:"slot,omitempty" cast-type:"github.com/OffchainLabs/prysm/v6/consensus-types/primitives.Slot"`
ProposerIndex github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex `protobuf:"varint,2,opt,name=proposer_index,json=proposerIndex,proto3" json:"proposer_index,omitempty" cast-type:"github.com/OffchainLabs/prysm/v6/consensus-types/primitives.ValidatorIndex"`
ParentRoot []byte `protobuf:"bytes,3,opt,name=parent_root,json=parentRoot,proto3" json:"parent_root,omitempty" ssz-size:"32"`
Commitments [][]byte `protobuf:"bytes,4,rep,name=commitments,proto3" json:"commitments,omitempty" ssz-max:"128" ssz-size:"?,32"`
}
func (x *BeaconBlockChunkHeader) Reset() {
*x = BeaconBlockChunkHeader{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BeaconBlockChunkHeader) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BeaconBlockChunkHeader) ProtoMessage() {}
func (x *BeaconBlockChunkHeader) ProtoReflect() protoreflect.Message {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BeaconBlockChunkHeader.ProtoReflect.Descriptor instead.
func (*BeaconBlockChunkHeader) Descriptor() ([]byte, []int) {
return file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescGZIP(), []int{1}
}
func (x *BeaconBlockChunkHeader) GetSlot() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot {
if x != nil {
return x.Slot
}
return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot(0)
}
func (x *BeaconBlockChunkHeader) GetProposerIndex() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex {
if x != nil {
return x.ProposerIndex
}
return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex(0)
}
func (x *BeaconBlockChunkHeader) GetParentRoot() []byte {
if x != nil {
return x.ParentRoot
}
return nil
}
func (x *BeaconBlockChunkHeader) GetCommitments() [][]byte {
if x != nil {
return x.Commitments
}
return nil
}
type BeaconBlockChunkData struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Data [][]byte `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty" ssz-max:"32768" ssz-size:"?,32"`
}
func (x *BeaconBlockChunkData) Reset() {
*x = BeaconBlockChunkData{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BeaconBlockChunkData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BeaconBlockChunkData) ProtoMessage() {}
func (x *BeaconBlockChunkData) ProtoReflect() protoreflect.Message {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BeaconBlockChunkData.ProtoReflect.Descriptor instead.
func (*BeaconBlockChunkData) Descriptor() ([]byte, []int) {
return file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescGZIP(), []int{2}
}
func (x *BeaconBlockChunkData) GetData() [][]byte {
if x != nil {
return x.Data
}
return nil
}
type ChunkedBeaconBlock struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Header *BeaconBlockChunkHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Chunks []*BeaconBlockChunkData `protobuf:"bytes,2,rep,name=chunks,proto3" json:"chunks,omitempty"`
Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"`
Block *GenericSignedBeaconBlock `protobuf:"bytes,4,opt,name=block,proto3" json:"block,omitempty"`
}
func (x *ChunkedBeaconBlock) Reset() {
*x = ChunkedBeaconBlock{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ChunkedBeaconBlock) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChunkedBeaconBlock) ProtoMessage() {}
func (x *ChunkedBeaconBlock) ProtoReflect() protoreflect.Message {
mi := &file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChunkedBeaconBlock.ProtoReflect.Descriptor instead.
func (*ChunkedBeaconBlock) Descriptor() ([]byte, []int) {
return file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescGZIP(), []int{3}
}
func (x *ChunkedBeaconBlock) GetHeader() *BeaconBlockChunkHeader {
if x != nil {
return x.Header
}
return nil
}
func (x *ChunkedBeaconBlock) GetChunks() []*BeaconBlockChunkData {
if x != nil {
return x.Chunks
}
return nil
}
func (x *ChunkedBeaconBlock) GetSignature() []byte {
if x != nil {
return x.Signature
}
return nil
}
func (x *ChunkedBeaconBlock) GetBlock() *GenericSignedBeaconBlock {
if x != nil {
return x.Block
}
return nil
}
var File_proto_prysm_v1alpha1_beacon_block_chunk_proto protoreflect.FileDescriptor
var file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDesc = []byte{
0x0a, 0x2d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31,
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x15, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31,
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74,
0x68, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d,
0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e,
0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdb, 0x01, 0x0a,
0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x75, 0x6e,
0x6b, 0x12, 0x25, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x42,
0x11, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x33, 0x32, 0x92, 0xb5, 0x18, 0x05, 0x33, 0x32, 0x37,
0x36, 0x38, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, 0x0c, 0x63, 0x6f, 0x65, 0x66,
0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x0f,
0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x33, 0x32, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52,
0x0c, 0x63, 0x6f, 0x65, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x45, 0x0a,
0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e,
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52,
0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xc5, 0x02, 0x0a, 0x16, 0x42,
0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x48,
0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73,
0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e,
0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74,
0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12,
0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65,
0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e,
0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f,
0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72,
0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65,
0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18,
0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12,
0x31, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04,
0x20, 0x03, 0x28, 0x0c, 0x42, 0x0f, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x33, 0x32, 0x92, 0xb5,
0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e,
0x74, 0x73, 0x22, 0x3d, 0x0a, 0x14, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x44, 0x61, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x04, 0x64, 0x61,
0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x11, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c,
0x33, 0x32, 0x92, 0xb5, 0x18, 0x05, 0x33, 0x32, 0x37, 0x36, 0x38, 0x52, 0x04, 0x64, 0x61, 0x74,
0x61, 0x22, 0x8d, 0x02, 0x0a, 0x12, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x65, 0x61,
0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x45, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64,
0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x75, 0x6e,
0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12,
0x43, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c,
0x6f, 0x63, 0x6b, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x63, 0x68,
0x75, 0x6e, 0x6b, 0x73, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52,
0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65,
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42,
0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x42, 0x9f, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42,
0x15, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x75, 0x6e,
0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62,
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b,
0x65, 0x74, 0x68, 0xaa, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45,
0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70,
0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescOnce sync.Once
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescData = file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDesc
)
func file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescGZIP() []byte {
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescOnce.Do(func() {
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescData)
})
return file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDescData
}
var file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_proto_prysm_v1alpha1_beacon_block_chunk_proto_goTypes = []interface{}{
(*BeaconBlockChunk)(nil), // 0: ethereum.eth.v1alpha1.BeaconBlockChunk
(*BeaconBlockChunkHeader)(nil), // 1: ethereum.eth.v1alpha1.BeaconBlockChunkHeader
(*BeaconBlockChunkData)(nil), // 2: ethereum.eth.v1alpha1.BeaconBlockChunkData
(*ChunkedBeaconBlock)(nil), // 3: ethereum.eth.v1alpha1.ChunkedBeaconBlock
(*GenericSignedBeaconBlock)(nil), // 4: ethereum.eth.v1alpha1.GenericSignedBeaconBlock
}
var file_proto_prysm_v1alpha1_beacon_block_chunk_proto_depIdxs = []int32{
1, // 0: ethereum.eth.v1alpha1.BeaconBlockChunk.header:type_name -> ethereum.eth.v1alpha1.BeaconBlockChunkHeader
1, // 1: ethereum.eth.v1alpha1.ChunkedBeaconBlock.header:type_name -> ethereum.eth.v1alpha1.BeaconBlockChunkHeader
2, // 2: ethereum.eth.v1alpha1.ChunkedBeaconBlock.chunks:type_name -> ethereum.eth.v1alpha1.BeaconBlockChunkData
4, // 3: ethereum.eth.v1alpha1.ChunkedBeaconBlock.block:type_name -> ethereum.eth.v1alpha1.GenericSignedBeaconBlock
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_proto_prysm_v1alpha1_beacon_block_chunk_proto_init() }
func file_proto_prysm_v1alpha1_beacon_block_chunk_proto_init() {
if File_proto_prysm_v1alpha1_beacon_block_chunk_proto != nil {
return
}
file_proto_prysm_v1alpha1_beacon_block_proto_init()
if !protoimpl.UnsafeEnabled {
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BeaconBlockChunk); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BeaconBlockChunkHeader); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BeaconBlockChunkData); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ChunkedBeaconBlock); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proto_prysm_v1alpha1_beacon_block_chunk_proto_goTypes,
DependencyIndexes: file_proto_prysm_v1alpha1_beacon_block_chunk_proto_depIdxs,
MessageInfos: file_proto_prysm_v1alpha1_beacon_block_chunk_proto_msgTypes,
}.Build()
File_proto_prysm_v1alpha1_beacon_block_chunk_proto = out.File
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_rawDesc = nil
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_goTypes = nil
file_proto_prysm_v1alpha1_beacon_block_chunk_proto_depIdxs = nil
}

View File

@@ -0,0 +1,68 @@
// Copyright 2024 Prysmatic Labs.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package ethereum.eth.v1alpha1;
import "proto/eth/ext/options.proto";
import "proto/prysm/v1alpha1/beacon_block.proto";
option csharp_namespace = "Ethereum.Eth.v1alpha1";
option go_package = "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1;eth";
option java_multiple_files = true;
option java_outer_classname = "BeaconBlockChunkProto";
option java_package = "org.ethereum.eth.v1alpha1";
option php_namespace = "Ethereum\\Eth\\v1alpha1";
message BeaconBlockChunk {
repeated bytes data = 1 [
(ethereum.eth.ext.ssz_size) = "?,32",
(ethereum.eth.ext.ssz_max) = "32768"
];
repeated bytes coefficients = 2 [
(ethereum.eth.ext.ssz_size) = "?,32",
(ethereum.eth.ext.ssz_max) = "128"
];
BeaconBlockChunkHeader header = 3;
bytes signature = 4 [ (ethereum.eth.ext.ssz_size) = "96" ];
}
message BeaconBlockChunkHeader {
uint64 slot = 1 [
(ethereum.eth.ext.cast_type) =
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives.Slot"
];
uint64 proposer_index = 2 [ (ethereum.eth.ext.cast_type) =
"github.com/OffchainLabs/prysm/v6/"
"consensus-types/primitives.ValidatorIndex" ];
bytes parent_root = 3 [ (ethereum.eth.ext.ssz_size) = "32" ];
repeated bytes commitments = 4 [
(ethereum.eth.ext.ssz_size) = "?,32",
(ethereum.eth.ext.ssz_max) = "128"
];
}
message BeaconBlockChunkData {
repeated bytes data = 1 [
(ethereum.eth.ext.ssz_size) = "?,32",
(ethereum.eth.ext.ssz_max) = "32768"
];
}
message ChunkedBeaconBlock {
BeaconBlockChunkHeader header = 1;
repeated BeaconBlockChunkData chunks = 2;
bytes signature = 3 [ (ethereum.eth.ext.ssz_size) = "96" ];
GenericSignedBeaconBlock block = 4;
}

Some files were not shown because too many files have changed in this diff Show More