From 9c61117b71ab6258d817445425294e62f25706d4 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Fri, 11 Oct 2024 08:58:34 -0500 Subject: [PATCH 01/26] update batch deposit message verification for better readability (#14526) * reversing boolean return for better readability * skips more reversals * changelog --- CHANGELOG.md | 1 + beacon-chain/core/altair/deposit.go | 12 +++---- beacon-chain/core/altair/deposit_test.go | 2 +- beacon-chain/core/blocks/deposit.go | 5 ++- beacon-chain/core/blocks/deposit_test.go | 39 ++++++++++++++++++++-- beacon-chain/core/electra/deposits.go | 22 ++++++------ beacon-chain/core/electra/deposits_test.go | 6 ++-- 7 files changed, 61 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1c2ed847a91..cbf8ed3f725b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Updated Sepolia bootnodes. - Make committee aware packing the default by deprecating `--enable-committee-aware-packing`. - Moved `ConvertKzgCommitmentToVersionedHash` to the `primitives` package. +- reversed the boolean return on `BatchVerifyDepositsSignatures`, from need verification, to all keys successfully verified ### Deprecated - `--disable-grpc-gateway` flag is deprecated due to grpc gateway removal. diff --git a/beacon-chain/core/altair/deposit.go b/beacon-chain/core/altair/deposit.go index 9f3a0736c581..084643324e40 100644 --- a/beacon-chain/core/altair/deposit.go +++ b/beacon-chain/core/altair/deposit.go @@ -37,7 +37,7 @@ func ProcessDeposits( beaconState state.BeaconState, deposits []*ethpb.Deposit, ) (state.BeaconState, error) { - batchVerified, err := blocks.BatchVerifyDepositsSignatures(ctx, deposits) + allSignaturesVerified, err := blocks.BatchVerifyDepositsSignatures(ctx, deposits) if err != nil { return nil, err } @@ -46,7 +46,7 @@ func ProcessDeposits( if deposit == nil || deposit.Data == nil { return nil, errors.New("got a nil deposit in block") } - beaconState, err = ProcessDeposit(beaconState, deposit, batchVerified) + beaconState, err = ProcessDeposit(beaconState, deposit, allSignaturesVerified) if err != nil { return nil, errors.Wrapf(err, "could not process deposit from %#x", bytesutil.Trunc(deposit.Data.PublicKey)) } @@ -81,7 +81,7 @@ func ProcessDeposits( // amount=deposit.data.amount, // signature=deposit.data.signature, // ) -func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verifySignature bool) (state.BeaconState, error) { +func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, allSignaturesVerified bool) (state.BeaconState, error) { if err := blocks.VerifyDeposit(beaconState, deposit); err != nil { if deposit == nil || deposit.Data == nil { return nil, err @@ -92,7 +92,7 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif return nil, err } - return ApplyDeposit(beaconState, deposit.Data, verifySignature) + return ApplyDeposit(beaconState, deposit.Data, allSignaturesVerified) } // ApplyDeposit @@ -115,13 +115,13 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif // # Increase balance by deposit amount // index = ValidatorIndex(validator_pubkeys.index(pubkey)) // increase_balance(state, index, amount) -func ApplyDeposit(beaconState state.BeaconState, data *ethpb.Deposit_Data, verifySignature bool) (state.BeaconState, error) { +func ApplyDeposit(beaconState state.BeaconState, data *ethpb.Deposit_Data, allSignaturesVerified bool) (state.BeaconState, error) { pubKey := data.PublicKey amount := data.Amount withdrawalCredentials := data.WithdrawalCredentials index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) if !ok { - if verifySignature { + if !allSignaturesVerified { valid, err := blocks.IsValidDepositSignature(data) if err != nil { return nil, err diff --git a/beacon-chain/core/altair/deposit_test.go b/beacon-chain/core/altair/deposit_test.go index dda8b468d635..34dda460890f 100644 --- a/beacon-chain/core/altair/deposit_test.go +++ b/beacon-chain/core/altair/deposit_test.go @@ -199,7 +199,7 @@ func TestProcessDeposit_SkipsInvalidDeposit(t *testing.T) { }, }) require.NoError(t, err) - newState, err := altair.ProcessDeposit(beaconState, dep[0], true) + newState, err := altair.ProcessDeposit(beaconState, dep[0], false) require.NoError(t, err, "Expected invalid block deposit to be ignored without error") if newState.Eth1DepositIndex() != 1 { diff --git a/beacon-chain/core/blocks/deposit.go b/beacon-chain/core/blocks/deposit.go index 3b8b715ca1a8..1a860b9dd4e7 100644 --- a/beacon-chain/core/blocks/deposit.go +++ b/beacon-chain/core/blocks/deposit.go @@ -55,12 +55,11 @@ func BatchVerifyDepositsSignatures(ctx context.Context, deposits []*ethpb.Deposi return false, err } - verified := false if err := verifyDepositDataWithDomain(ctx, deposits, domain); err != nil { log.WithError(err).Debug("Failed to batch verify deposits signatures, will try individual verify") - verified = true + return false, nil } - return verified, nil + return true, nil } // IsValidDepositSignature returns whether deposit_data is valid diff --git a/beacon-chain/core/blocks/deposit_test.go b/beacon-chain/core/blocks/deposit_test.go index 31ba22f23d17..4414d849abab 100644 --- a/beacon-chain/core/blocks/deposit_test.go +++ b/beacon-chain/core/blocks/deposit_test.go @@ -17,6 +17,41 @@ import ( ) func TestBatchVerifyDepositsSignatures_Ok(t *testing.T) { + sk, err := bls.RandKey() + require.NoError(t, err) + domain, err := signing.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil) + require.NoError(t, err) + deposit := ðpb.Deposit{ + Data: ðpb.Deposit_Data{ + PublicKey: sk.PublicKey().Marshal(), + WithdrawalCredentials: make([]byte, 32), + Amount: 3000, + }, + } + sr, err := signing.ComputeSigningRoot(ðpb.DepositMessage{ + PublicKey: deposit.Data.PublicKey, + WithdrawalCredentials: deposit.Data.WithdrawalCredentials, + Amount: 3000, + }, domain) + require.NoError(t, err) + sig := sk.Sign(sr[:]) + deposit.Data.Signature = sig.Marshal() + leaf, err := deposit.Data.HashTreeRoot() + require.NoError(t, err) + // We then create a merkle branch for the test. + depositTrie, err := trie.GenerateTrieFromItems([][]byte{leaf[:]}, params.BeaconConfig().DepositContractTreeDepth) + require.NoError(t, err, "Could not generate trie") + proof, err := depositTrie.MerkleProof(0) + require.NoError(t, err, "Could not generate proof") + + deposit.Proof = proof + require.NoError(t, err) + verified, err := blocks.BatchVerifyDepositsSignatures(context.Background(), []*ethpb.Deposit{deposit}) + require.NoError(t, err) + require.Equal(t, true, verified) +} + +func TestBatchVerifyDepositsSignatures_InvalidSignature(t *testing.T) { deposit := ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: bytesutil.PadTo([]byte{1, 2, 3}, 48), @@ -34,9 +69,9 @@ func TestBatchVerifyDepositsSignatures_Ok(t *testing.T) { deposit.Proof = proof require.NoError(t, err) - ok, err := blocks.BatchVerifyDepositsSignatures(context.Background(), []*ethpb.Deposit{deposit}) + verified, err := blocks.BatchVerifyDepositsSignatures(context.Background(), []*ethpb.Deposit{deposit}) require.NoError(t, err) - require.Equal(t, true, ok) + require.Equal(t, false, verified) } func TestVerifyDeposit_MerkleBranchFailsVerification(t *testing.T) { diff --git a/beacon-chain/core/electra/deposits.go b/beacon-chain/core/electra/deposits.go index e9d753b5a3b9..1dc3d30d603c 100644 --- a/beacon-chain/core/electra/deposits.go +++ b/beacon-chain/core/electra/deposits.go @@ -38,7 +38,7 @@ func ProcessDeposits( defer span.End() // Attempt to verify all deposit signatures at once, if this fails then fall back to processing // individual deposits with signature verification enabled. - batchVerified, err := blocks.BatchVerifyDepositsSignatures(ctx, deposits) + allSignaturesVerified, err := blocks.BatchVerifyDepositsSignatures(ctx, deposits) if err != nil { return nil, errors.Wrap(err, "could not verify deposit signatures in batch") } @@ -47,7 +47,7 @@ func ProcessDeposits( if d == nil || d.Data == nil { return nil, errors.New("got a nil deposit in block") } - beaconState, err = ProcessDeposit(beaconState, d, batchVerified) + beaconState, err = ProcessDeposit(beaconState, d, allSignaturesVerified) if err != nil { return nil, errors.Wrapf(err, "could not process deposit from %#x", bytesutil.Trunc(d.Data.PublicKey)) } @@ -82,7 +82,7 @@ func ProcessDeposits( // amount=deposit.data.amount, // signature=deposit.data.signature, // ) -func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verifySignature bool) (state.BeaconState, error) { +func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, allSignaturesVerified bool) (state.BeaconState, error) { if err := blocks.VerifyDeposit(beaconState, deposit); err != nil { if deposit == nil || deposit.Data == nil { return nil, err @@ -92,7 +92,7 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif if err := beaconState.SetEth1DepositIndex(beaconState.Eth1DepositIndex() + 1); err != nil { return nil, err } - return ApplyDeposit(beaconState, deposit.Data, verifySignature) + return ApplyDeposit(beaconState, deposit.Data, allSignaturesVerified) } // ApplyDeposit @@ -116,13 +116,13 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif // and is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature) // ): // switch_to_compounding_validator(state, index) -func ApplyDeposit(beaconState state.BeaconState, data *ethpb.Deposit_Data, verifySignature bool) (state.BeaconState, error) { +func ApplyDeposit(beaconState state.BeaconState, data *ethpb.Deposit_Data, allSignaturesVerified bool) (state.BeaconState, error) { pubKey := data.PublicKey amount := data.Amount withdrawalCredentials := data.WithdrawalCredentials index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) if !ok { - if verifySignature { + if !allSignaturesVerified { valid, err := IsValidDepositSignature(data) if err != nil { return nil, errors.Wrap(err, "could not verify deposit signature") @@ -144,7 +144,7 @@ func ApplyDeposit(beaconState state.BeaconState, data *ethpb.Deposit_Data, verif return nil, err } if helpers.IsCompoundingWithdrawalCredential(withdrawalCredentials) && helpers.HasETH1WithdrawalCredential(val) { - if verifySignature { + if !allSignaturesVerified { valid, err := IsValidDepositSignature(data) if err != nil { return nil, errors.Wrap(err, "could not verify deposit signature") @@ -325,12 +325,12 @@ func ProcessDepositRequests(ctx context.Context, beaconState state.BeaconState, }, }) } - batchVerified, err := blocks.BatchVerifyDepositsSignatures(ctx, deposits) + allSignaturesVerified, err := blocks.BatchVerifyDepositsSignatures(ctx, deposits) if err != nil { return nil, errors.Wrap(err, "could not verify deposit signatures in batch") } for _, receipt := range requests { - beaconState, err = processDepositRequest(beaconState, receipt, batchVerified) + beaconState, err = processDepositRequest(beaconState, receipt, allSignaturesVerified) if err != nil { return nil, errors.Wrap(err, "could not apply deposit request") } @@ -352,7 +352,7 @@ func ProcessDepositRequests(ctx context.Context, beaconState state.BeaconState, // amount=deposit_request.amount, // signature=deposit_request.signature, // ) -func processDepositRequest(beaconState state.BeaconState, request *enginev1.DepositRequest, verifySignature bool) (state.BeaconState, error) { +func processDepositRequest(beaconState state.BeaconState, request *enginev1.DepositRequest, allSignaturesVerified bool) (state.BeaconState, error) { requestsStartIndex, err := beaconState.DepositRequestsStartIndex() if err != nil { return nil, errors.Wrap(err, "could not get deposit requests start index") @@ -367,5 +367,5 @@ func processDepositRequest(beaconState state.BeaconState, request *enginev1.Depo Amount: request.Amount, WithdrawalCredentials: bytesutil.SafeCopyBytes(request.WithdrawalCredentials), Signature: bytesutil.SafeCopyBytes(request.Signature), - }, verifySignature) + }, allSignaturesVerified) } diff --git a/beacon-chain/core/electra/deposits_test.go b/beacon-chain/core/electra/deposits_test.go index e498ea613119..e44d04bde18d 100644 --- a/beacon-chain/core/electra/deposits_test.go +++ b/beacon-chain/core/electra/deposits_test.go @@ -322,7 +322,7 @@ func TestProcessDeposit_SkipsInvalidDeposit(t *testing.T) { }, }) require.NoError(t, err) - newState, err := electra.ProcessDeposit(beaconState, dep[0], true) + newState, err := electra.ProcessDeposit(beaconState, dep[0], false) require.NoError(t, err, "Expected invalid block deposit to be ignored without error") if newState.Eth1DepositIndex() != 1 { @@ -359,7 +359,7 @@ func TestApplyDeposit_TopUps_WithBadSignature(t *testing.T) { vals[0].PublicKey = sk.PublicKey().Marshal() vals[0].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte require.NoError(t, st.SetValidators(vals)) - adSt, err := electra.ApplyDeposit(st, depositData, true) + adSt, err := electra.ApplyDeposit(st, depositData, false) require.NoError(t, err) pbd, err := adSt.PendingBalanceDeposits() require.NoError(t, err) @@ -390,7 +390,7 @@ func TestApplyDeposit_Electra_SwitchToCompoundingValidator(t *testing.T) { require.NoError(t, err) sig := sk.Sign(sr[:]) depositData.Signature = sig.Marshal() - adSt, err := electra.ApplyDeposit(st, depositData, false) + adSt, err := electra.ApplyDeposit(st, depositData, true) require.NoError(t, err) pbd, err := adSt.PendingBalanceDeposits() require.NoError(t, err) From 8a0545c3d767a02c522aeffda580d5221a470239 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Sun, 13 Oct 2024 20:21:42 -0500 Subject: [PATCH 02/26] Eip6110 queue deposit requests (#14430) * wip * updating types and wip on functions * renaming to MAX_PENDING_DEPOSITS_PER_EPOCH * fixing linting and conversions * adding queue deposit changes * fixing test and cloning * removing unneeded test based on update * gaz * wip apply pending deposit * fixing replay test and adding apply pending deposit * fixing setters test * updating transition test * changelog * updating pending deposits * fixing ProcessPendingDeposit unit tests * gaz * fixing cyclic dependencies * fix visiblity * missed adding the right signature verification * adding point to infinity topup test * adding apply pending deposit test * making changes based on eip6110 changes * fixing ineffassign * gaz * adding batched verifications sigs * fixing broken type * fixing proto * updated consensus spec tests and fixed consensus bug tests * testing readability improvement by avoiding ApplyPendingDeposit * removing the boolean from apply pending deposit * improve naming * review comments and fixing a small bug using wrong variable * fixing tests and skipping a test * adding some test skips * fixing bugs terence found * adding test for batchProcessNewPendingDeposits * gaz * adding churn test * updating spec tests to alpha.8 * adding pr to changelog * addressing terence's comments * Update beacon-chain/core/electra/validator.go Co-authored-by: terence * adding tests for batch verify and rename some variables * skipping tests , add them back in later * skipping one more test --------- Co-authored-by: terence --- CHANGELOG.md | 2 + WORKSPACE | 10 +- api/server/structs/conversions.go | 13 +- api/server/structs/conversions_state.go | 4 +- api/server/structs/other.go | 9 +- api/server/structs/state.go | 2 +- beacon-chain/core/blocks/deposit.go | 56 ++ beacon-chain/core/blocks/deposit_test.go | 51 ++ beacon-chain/core/electra/BUILD.bazel | 7 +- .../core/electra/consolidations_test.go | 32 -- beacon-chain/core/electra/deposits.go | 510 +++++++++++++----- beacon-chain/core/electra/deposits_test.go | 329 ++++++++--- beacon-chain/core/electra/export_test.go | 3 + beacon-chain/core/electra/transition.go | 4 +- beacon-chain/core/electra/transition_test.go | 15 +- beacon-chain/core/electra/upgrade.go | 4 +- beacon-chain/core/electra/upgrade_test.go | 6 +- beacon-chain/core/electra/validator.go | 147 ++--- beacon-chain/core/electra/validator_test.go | 29 +- beacon-chain/p2p/encoder/ssz_test.go | 8 +- beacon-chain/rpc/eth/config/handlers_test.go | 9 +- beacon-chain/state/interfaces.go | 6 +- beacon-chain/state/state-native/BUILD.bazel | 8 +- .../state/state-native/beacon_state.go | 6 +- ...alance_deposits.go => getters_deposits.go} | 14 +- ...osits_test.go => getters_deposits_test.go} | 33 +- .../state/state-native/getters_state.go | 4 +- beacon-chain/state/state-native/hasher.go | 6 +- ...alance_deposits.go => setters_deposits.go} | 32 +- ...osits_test.go => setters_deposits_test.go} | 34 +- beacon-chain/state/state-native/state_trie.go | 12 +- .../state/state-native/types/types.go | 8 +- beacon-chain/state/stategen/BUILD.bazel | 3 + beacon-chain/state/stategen/replay_test.go | 30 +- beacon-chain/state/stateutil/BUILD.bazel | 2 +- ...osits_root.go => pending_deposits_root.go} | 4 +- beacon-chain/state/testing/BUILD.bazel | 10 +- beacon-chain/state/testing/generators.go | 44 ++ config/fieldparams/mainnet.go | 2 +- config/fieldparams/minimal.go | 2 +- config/params/config.go | 3 +- config/params/loader_test.go | 2 + config/params/mainnet_config.go | 3 +- config/params/minimal_config.go | 2 + proto/prysm/v1alpha1/BUILD.bazel | 4 +- proto/prysm/v1alpha1/beacon_block.go | 11 - .../prysm/v1alpha1/beacon_block_fuzz_test.go | 2 +- proto/prysm/v1alpha1/beacon_state.pb.go | 113 ++-- proto/prysm/v1alpha1/beacon_state.proto | 2 +- proto/prysm/v1alpha1/cloners_test.go | 46 -- proto/prysm/v1alpha1/eip_7251.pb.go | 166 +++--- proto/prysm/v1alpha1/eip_7251.proto | 14 +- proto/prysm/v1alpha1/eip_7521.go | 16 + proto/prysm/v1alpha1/eip_7521_fuzz_test.go | 1 + proto/prysm/v1alpha1/electra.ssz.go | 146 +++-- proto/ssz_proto_library.bzl | 4 +- .../electra/epoch_processing/BUILD.bazel | 2 +- .../pending_balance_updates_test.go | 11 - .../pending_consolidations_test.go | 1 + .../pending_deposits_updates_test.go | 11 + .../electra/operations/consolidation_test.go | 1 + .../electra/operations/withdrawals_test.go | 1 + .../mainnet/electra/sanity/blocks_test.go | 1 + .../electra/epoch_processing/BUILD.bazel | 2 +- .../pending_balance_updates_test.go | 11 - .../pending_consolidations_test.go | 1 + .../pending_deposits_updates_test.go | 11 + .../electra/operations/consolidation_test.go | 1 + .../electra/operations/withdrawals_test.go | 1 + .../minimal/electra/sanity/blocks_test.go | 1 + .../electra/epoch_processing/BUILD.bazel | 2 +- ..._updates.go => pending_deposit_updates.go} | 10 +- .../shared/electra/ssz_static/ssz_static.go | 4 +- testing/util/electra_state.go | 2 +- 74 files changed, 1368 insertions(+), 751 deletions(-) create mode 100644 beacon-chain/core/electra/export_test.go rename beacon-chain/state/state-native/{getters_balance_deposits.go => getters_deposits.go} (66%) rename beacon-chain/state/state-native/{getters_balance_deposits_test.go => getters_deposits_test.go} (56%) rename beacon-chain/state/state-native/{setters_balance_deposits.go => setters_deposits.go} (55%) rename beacon-chain/state/state-native/{setters_balance_deposits_test.go => setters_deposits_test.go} (61%) rename beacon-chain/state/stateutil/{pending_balance_deposits_root.go => pending_deposits_root.go} (57%) create mode 100644 beacon-chain/state/testing/generators.go delete mode 100644 testing/spectest/mainnet/electra/epoch_processing/pending_balance_updates_test.go create mode 100644 testing/spectest/mainnet/electra/epoch_processing/pending_deposits_updates_test.go delete mode 100644 testing/spectest/minimal/electra/epoch_processing/pending_balance_updates_test.go create mode 100644 testing/spectest/minimal/electra/epoch_processing/pending_deposits_updates_test.go rename testing/spectest/shared/electra/epoch_processing/{pending_balance_updates.go => pending_deposit_updates.go} (71%) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbf8ed3f725b..878a96a0b17d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Light client support: Implement `ComputeFieldRootsForBlockBody`. - Light client support: Add light client database changes. - Light client support: Implement capella and deneb changes. +- Electra EIP6110: Queue deposit [pr](https://github.com/prysmaticlabs/prysm/pull/14430) - Light client support: Implement `BlockToLightClientHeader` function. - Light client support: Consensus types. - GetBeaconStateV2: add Electra case. @@ -44,6 +45,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - `grpc-gateway-corsdomain` is renamed to http-cors-domain. The old name can still be used as an alias. - `api-timeout` is changed from int flag to duration flag, default value updated. - Light client support: abstracted out the light client headers with different versions. +- Electra EIP6110: Queue deposit requests changes from consensus spec pr #3818 - `ApplyToEveryValidator` has been changed to prevent misuse bugs, it takes a closure that takes a `ReadOnlyValidator` and returns a raw pointer to a `Validator`. - Removed gorilla mux library and replaced it with net/http updates in go 1.22. - Clean up `ProposeBlock` for validator client to reduce cognitive scoring and enable further changes. diff --git a/WORKSPACE b/WORKSPACE index d7e10683fdb2..c9906164adc5 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -227,7 +227,7 @@ filegroup( url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz", ) -consensus_spec_version = "v1.5.0-alpha.6" +consensus_spec_version = "v1.5.0-alpha.8" bls_test_version = "v0.1.1" @@ -243,7 +243,7 @@ filegroup( visibility = ["//visibility:public"], ) """, - integrity = "sha256-M7u/Ot/Vzorww+dFbHp0cxLyM2mezJjijCzq+LY3uvs=", + integrity = "sha256-BsGIbEyJuYrzhShGl0tHhR4lP5Qwno8R3k8a6YBR/DA=", url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version, ) @@ -259,7 +259,7 @@ filegroup( visibility = ["//visibility:public"], ) """, - integrity = "sha256-deOSeLRsmHXvkRp8n2bs3HXdkGUJWWqu8KFM/QABbZg=", + integrity = "sha256-DkdvhPP2KiqUOpwFXQIFDCWCwsUDIC/xhTBD+TZevm0=", url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version, ) @@ -275,7 +275,7 @@ filegroup( visibility = ["//visibility:public"], ) """, - integrity = "sha256-Zz7YCf6XVf57nzSEGq9ToflJFHM0lAGwhd18l9Rf3hA=", + integrity = "sha256-vkZqV0HB8A2Uc56C1Us/p5G57iaHL+zw2No93Xt6M/4=", url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version, ) @@ -290,7 +290,7 @@ filegroup( visibility = ["//visibility:public"], ) """, - integrity = "sha256-BoXckDxXnDcEmAjg/dQgf/tLiJsb6CT0aZvmWHFijrY=", + integrity = "sha256-D/HPAW61lKqjoWwl7N0XvhdX+67dCEFAy8JxVzqBGtU=", strip_prefix = "consensus-specs-" + consensus_spec_version[1:], url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version, ) diff --git a/api/server/structs/conversions.go b/api/server/structs/conversions.go index d735910929a6..9ed1f50e4e67 100644 --- a/api/server/structs/conversions.go +++ b/api/server/structs/conversions.go @@ -1476,12 +1476,15 @@ func DepositSnapshotFromConsensus(ds *eth.DepositSnapshot) *DepositSnapshot { } } -func PendingBalanceDepositsFromConsensus(ds []*eth.PendingBalanceDeposit) []*PendingBalanceDeposit { - deposits := make([]*PendingBalanceDeposit, len(ds)) +func PendingDepositsFromConsensus(ds []*eth.PendingDeposit) []*PendingDeposit { + deposits := make([]*PendingDeposit, len(ds)) for i, d := range ds { - deposits[i] = &PendingBalanceDeposit{ - Index: fmt.Sprintf("%d", d.Index), - Amount: fmt.Sprintf("%d", d.Amount), + deposits[i] = &PendingDeposit{ + Pubkey: hexutil.Encode(d.PublicKey), + WithdrawalCredentials: hexutil.Encode(d.WithdrawalCredentials), + Amount: fmt.Sprintf("%d", d.Amount), + Signature: hexutil.Encode(d.Signature), + Slot: fmt.Sprintf("%d", d.Slot), } } return deposits diff --git a/api/server/structs/conversions_state.go b/api/server/structs/conversions_state.go index 5c982c1d606b..043b8e5a6517 100644 --- a/api/server/structs/conversions_state.go +++ b/api/server/structs/conversions_state.go @@ -722,7 +722,7 @@ func BeaconStateElectraFromConsensus(st beaconState.BeaconState) (*BeaconStateEl if err != nil { return nil, err } - pbd, err := st.PendingBalanceDeposits() + pbd, err := st.PendingDeposits() if err != nil { return nil, err } @@ -770,7 +770,7 @@ func BeaconStateElectraFromConsensus(st beaconState.BeaconState) (*BeaconStateEl EarliestExitEpoch: fmt.Sprintf("%d", eee), ConsolidationBalanceToConsume: fmt.Sprintf("%d", cbtc), EarliestConsolidationEpoch: fmt.Sprintf("%d", ece), - PendingBalanceDeposits: PendingBalanceDepositsFromConsensus(pbd), + PendingDeposits: PendingDepositsFromConsensus(pbd), PendingPartialWithdrawals: PendingPartialWithdrawalsFromConsensus(ppw), PendingConsolidations: PendingConsolidationsFromConsensus(pc), }, nil diff --git a/api/server/structs/other.go b/api/server/structs/other.go index 0a91b69ee752..e81789cb208c 100644 --- a/api/server/structs/other.go +++ b/api/server/structs/other.go @@ -257,9 +257,12 @@ type ConsolidationRequest struct { TargetPubkey string `json:"target_pubkey"` } -type PendingBalanceDeposit struct { - Index string `json:"index"` - Amount string `json:"amount"` +type PendingDeposit struct { + Pubkey string `json:"pubkey"` + WithdrawalCredentials string `json:"withdrawal_credentials"` + Amount string `json:"amount"` + Signature string `json:"signature"` + Slot string `json:"slot"` } type PendingPartialWithdrawal struct { diff --git a/api/server/structs/state.go b/api/server/structs/state.go index 56a15e0c98e7..9704a75d4013 100644 --- a/api/server/structs/state.go +++ b/api/server/structs/state.go @@ -176,7 +176,7 @@ type BeaconStateElectra struct { EarliestExitEpoch string `json:"earliest_exit_epoch"` ConsolidationBalanceToConsume string `json:"consolidation_balance_to_consume"` EarliestConsolidationEpoch string `json:"earliest_consolidation_epoch"` - PendingBalanceDeposits []*PendingBalanceDeposit `json:"pending_balance_deposits"` + PendingDeposits []*PendingDeposit `json:"pending_deposits"` PendingPartialWithdrawals []*PendingPartialWithdrawal `json:"pending_partial_withdrawals"` PendingConsolidations []*PendingConsolidation `json:"pending_consolidations"` } diff --git a/beacon-chain/core/blocks/deposit.go b/beacon-chain/core/blocks/deposit.go index 1a860b9dd4e7..af7a45b67b84 100644 --- a/beacon-chain/core/blocks/deposit.go +++ b/beacon-chain/core/blocks/deposit.go @@ -62,6 +62,21 @@ func BatchVerifyDepositsSignatures(ctx context.Context, deposits []*ethpb.Deposi return true, nil } +// BatchVerifyPendingDepositsSignatures batch verifies pending deposit signatures. +func BatchVerifyPendingDepositsSignatures(ctx context.Context, deposits []*ethpb.PendingDeposit) (bool, error) { + var err error + domain, err := signing.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil) + if err != nil { + return false, err + } + + if err := verifyPendingDepositDataWithDomain(ctx, deposits, domain); err != nil { + log.WithError(err).Debug("Failed to batch verify deposits signatures, will try individual verify") + return false, nil + } + return true, nil +} + // IsValidDepositSignature returns whether deposit_data is valid // def is_valid_deposit_signature(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64, signature: BLSSignature) -> bool: // @@ -158,3 +173,44 @@ func verifyDepositDataWithDomain(ctx context.Context, deps []*ethpb.Deposit, dom } return nil } + +func verifyPendingDepositDataWithDomain(ctx context.Context, deps []*ethpb.PendingDeposit, domain []byte) error { + if len(deps) == 0 { + return nil + } + pks := make([]bls.PublicKey, len(deps)) + sigs := make([][]byte, len(deps)) + msgs := make([][32]byte, len(deps)) + for i, dep := range deps { + if ctx.Err() != nil { + return ctx.Err() + } + if dep == nil { + return errors.New("nil deposit") + } + dpk, err := bls.PublicKeyFromBytes(dep.PublicKey) + if err != nil { + return err + } + pks[i] = dpk + sigs[i] = dep.Signature + depositMessage := ðpb.DepositMessage{ + PublicKey: dep.PublicKey, + WithdrawalCredentials: dep.WithdrawalCredentials, + Amount: dep.Amount, + } + sr, err := signing.ComputeSigningRoot(depositMessage, domain) + if err != nil { + return err + } + msgs[i] = sr + } + verify, err := bls.VerifyMultipleSignatures(sigs, msgs, pks) + if err != nil { + return errors.Errorf("could not verify multiple signatures: %v", err) + } + if !verify { + return errors.New("one or more deposit signatures did not verify") + } + return nil +} diff --git a/beacon-chain/core/blocks/deposit_test.go b/beacon-chain/core/blocks/deposit_test.go index 4414d849abab..480b4f5c1890 100644 --- a/beacon-chain/core/blocks/deposit_test.go +++ b/beacon-chain/core/blocks/deposit_test.go @@ -128,3 +128,54 @@ func TestIsValidDepositSignature_Ok(t *testing.T) { require.NoError(t, err) require.Equal(t, true, valid) } + +func TestBatchVerifyPendingDepositsSignatures_Ok(t *testing.T) { + sk, err := bls.RandKey() + require.NoError(t, err) + domain, err := signing.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil) + require.NoError(t, err) + pendingDeposit := ðpb.PendingDeposit{ + PublicKey: sk.PublicKey().Marshal(), + WithdrawalCredentials: make([]byte, 32), + Amount: 3000, + } + sr, err := signing.ComputeSigningRoot(ðpb.DepositMessage{ + PublicKey: pendingDeposit.PublicKey, + WithdrawalCredentials: pendingDeposit.WithdrawalCredentials, + Amount: 3000, + }, domain) + require.NoError(t, err) + sig := sk.Sign(sr[:]) + pendingDeposit.Signature = sig.Marshal() + + sk2, err := bls.RandKey() + require.NoError(t, err) + pendingDeposit2 := ðpb.PendingDeposit{ + PublicKey: sk2.PublicKey().Marshal(), + WithdrawalCredentials: make([]byte, 32), + Amount: 4000, + } + sr2, err := signing.ComputeSigningRoot(ðpb.DepositMessage{ + PublicKey: pendingDeposit2.PublicKey, + WithdrawalCredentials: pendingDeposit2.WithdrawalCredentials, + Amount: 4000, + }, domain) + require.NoError(t, err) + sig2 := sk2.Sign(sr2[:]) + pendingDeposit2.Signature = sig2.Marshal() + + verified, err := blocks.BatchVerifyPendingDepositsSignatures(context.Background(), []*ethpb.PendingDeposit{pendingDeposit, pendingDeposit2}) + require.NoError(t, err) + require.Equal(t, true, verified) +} + +func TestBatchVerifyPendingDepositsSignatures_InvalidSignature(t *testing.T) { + pendingDeposit := ðpb.PendingDeposit{ + PublicKey: bytesutil.PadTo([]byte{1, 2, 3}, 48), + WithdrawalCredentials: make([]byte, 32), + Signature: make([]byte, 96), + } + verified, err := blocks.BatchVerifyPendingDepositsSignatures(context.Background(), []*ethpb.PendingDeposit{pendingDeposit}) + require.NoError(t, err) + require.Equal(t, false, verified) +} diff --git a/beacon-chain/core/electra/BUILD.bazel b/beacon-chain/core/electra/BUILD.bazel index 748f01272611..35ef921f3ef0 100644 --- a/beacon-chain/core/electra/BUILD.bazel +++ b/beacon-chain/core/electra/BUILD.bazel @@ -32,11 +32,13 @@ go_library( "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", "//contracts/deposit:go_default_library", + "//crypto/bls/common:go_default_library", "//encoding/bytesutil:go_default_library", "//math:go_default_library", "//monitoring/tracing/trace:go_default_library", "//proto/engine/v1:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//runtime/version:go_default_library", "//time/slots:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_pkg_errors//:go_default_library", @@ -52,24 +54,27 @@ go_test( "deposit_fuzz_test.go", "deposits_test.go", "effective_balance_updates_test.go", + "export_test.go", "registry_updates_test.go", "transition_test.go", "upgrade_test.go", "validator_test.go", "withdrawals_test.go", ], + embed = [":go_default_library"], deps = [ - ":go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/signing:go_default_library", "//beacon-chain/core/time:go_default_library", "//beacon-chain/state:go_default_library", "//beacon-chain/state/state-native:go_default_library", + "//beacon-chain/state/testing:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", "//consensus-types/primitives:go_default_library", "//crypto/bls:go_default_library", + "//crypto/bls/common:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/engine/v1:go_default_library", "//proto/prysm/v1alpha1:go_default_library", diff --git a/beacon-chain/core/electra/consolidations_test.go b/beacon-chain/core/electra/consolidations_test.go index 98378fed3ca1..97ff9e981bab 100644 --- a/beacon-chain/core/electra/consolidations_test.go +++ b/beacon-chain/core/electra/consolidations_test.go @@ -201,38 +201,6 @@ func TestProcessPendingConsolidations(t *testing.T) { } } -func stateWithActiveBalanceETH(t *testing.T, balETH uint64) state.BeaconState { - gwei := balETH * 1_000_000_000 - balPerVal := params.BeaconConfig().MinActivationBalance - numVals := gwei / balPerVal - - vals := make([]*eth.Validator, numVals) - bals := make([]uint64, numVals) - for i := uint64(0); i < numVals; i++ { - wc := make([]byte, 32) - wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte - wc[31] = byte(i) - vals[i] = ð.Validator{ - ActivationEpoch: 0, - ExitEpoch: params.BeaconConfig().FarFutureEpoch, - EffectiveBalance: balPerVal, - WithdrawalCredentials: wc, - } - bals[i] = balPerVal - } - st, err := state_native.InitializeFromProtoUnsafeElectra(ð.BeaconStateElectra{ - Slot: 10 * params.BeaconConfig().SlotsPerEpoch, - Validators: vals, - Balances: bals, - Fork: ð.Fork{ - CurrentVersion: params.BeaconConfig().ElectraForkVersion, - }, - }) - require.NoError(t, err) - - return st -} - func TestProcessConsolidationRequests(t *testing.T) { tests := []struct { name string diff --git a/beacon-chain/core/electra/deposits.go b/beacon-chain/core/electra/deposits.go index 1dc3d30d603c..431ab79fe60b 100644 --- a/beacon-chain/core/electra/deposits.go +++ b/beacon-chain/core/electra/deposits.go @@ -2,7 +2,6 @@ package electra import ( "context" - "fmt" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" @@ -17,6 +16,7 @@ import ( enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" log "github.com/sirupsen/logrus" ) @@ -95,32 +95,44 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, allSi return ApplyDeposit(beaconState, deposit.Data, allSignaturesVerified) } -// ApplyDeposit -// def apply_deposit(state: BeaconState, pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64, signature: BLSSignature) -> None: -// validator_pubkeys = [v.pubkey for v in state.validators] -// if pubkey not in validator_pubkeys: +// ApplyDeposit adds the incoming deposit as a pending deposit on the state // -// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract -// if is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature): -// add_validator_to_registry(state, pubkey, withdrawal_credentials, amount) -// -// else: -// -// # Increase balance by deposit amount -// index = ValidatorIndex(validator_pubkeys.index(pubkey)) -// state.pending_balance_deposits.append(PendingBalanceDeposit(index=index, amount=amount)) # [Modified in Electra:EIP-7251] -// # Check if valid deposit switch to compounding credentials -// -// if ( is_compounding_withdrawal_credential(withdrawal_credentials) and has_eth1_withdrawal_credential(state.validators[index]) +// Spec pseudocode definition: +// def apply_deposit(state: BeaconState, // -// and is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature) -// ): -// switch_to_compounding_validator(state, index) +// pubkey: BLSPubkey, +// withdrawal_credentials: Bytes32, +// amount: uint64, +// signature: BLSSignature) -> None: +// validator_pubkeys = [v.pubkey for v in state.validators] +// if pubkey not in validator_pubkeys: +// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract +// if is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature): +// add_validator_to_registry(state, pubkey, withdrawal_credentials, Gwei(0)) # [Modified in Electra:EIP7251] +// # [New in Electra:EIP7251] +// state.pending_deposits.append(PendingDeposit( +// pubkey=pubkey, +// withdrawal_credentials=withdrawal_credentials, +// amount=amount, +// signature=signature, +// slot=GENESIS_SLOT, # Use GENESIS_SLOT to distinguish from a pending deposit request +// )) +// else: +// # Increase balance by deposit amount +// # [Modified in Electra:EIP7251] +// state.pending_deposits.append(PendingDeposit( +// pubkey=pubkey, +// withdrawal_credentials=withdrawal_credentials, +// amount=amount, +// signature=signature, +// slot=GENESIS_SLOT # Use GENESIS_SLOT to distinguish from a pending deposit request +// )) func ApplyDeposit(beaconState state.BeaconState, data *ethpb.Deposit_Data, allSignaturesVerified bool) (state.BeaconState, error) { pubKey := data.PublicKey amount := data.Amount withdrawalCredentials := data.WithdrawalCredentials - index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) + signature := data.Signature + _, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) if !ok { if !allSignaturesVerified { valid, err := IsValidDepositSignature(data) @@ -131,32 +143,20 @@ func ApplyDeposit(beaconState state.BeaconState, data *ethpb.Deposit_Data, allSi return beaconState, nil } } - if err := AddValidatorToRegistry(beaconState, pubKey, withdrawalCredentials, amount); err != nil { + + if err := AddValidatorToRegistry(beaconState, pubKey, withdrawalCredentials, 0); err != nil { // # [Modified in Electra:EIP7251] return nil, errors.Wrap(err, "could not add validator to registry") } - } else { - // no validation on top-ups (phase0 feature). no validation before state change - if err := beaconState.AppendPendingBalanceDeposit(index, amount); err != nil { - return nil, err - } - val, err := beaconState.ValidatorAtIndex(index) - if err != nil { - return nil, err - } - if helpers.IsCompoundingWithdrawalCredential(withdrawalCredentials) && helpers.HasETH1WithdrawalCredential(val) { - if !allSignaturesVerified { - valid, err := IsValidDepositSignature(data) - if err != nil { - return nil, errors.Wrap(err, "could not verify deposit signature") - } - if !valid { - return beaconState, nil - } - } - if err := SwitchToCompoundingValidator(beaconState, index); err != nil { - return nil, errors.Wrap(err, "could not switch to compound validator") - } - } + } + // no validation on top-ups (phase0 feature). no validation before state change + if err := beaconState.AppendPendingDeposit(ðpb.PendingDeposit{ + PublicKey: pubKey, + WithdrawalCredentials: withdrawalCredentials, + Amount: amount, + Signature: signature, + Slot: params.BeaconConfig().GenesisSlot, + }); err != nil { + return nil, err } return beaconState, nil } @@ -185,152 +185,364 @@ func verifyDepositDataSigningRoot(obj *ethpb.Deposit_Data, domain []byte) error return deposit.VerifyDepositSignature(obj, domain) } -// ProcessPendingBalanceDeposits implements the spec definition below. This method mutates the state. +// ProcessPendingDeposits implements the spec definition below. This method mutates the state. +// Iterating over `pending_deposits` queue this function runs the following checks before applying pending deposit: +// 1. All Eth1 bridge deposits are processed before the first deposit request gets processed. +// 2. Deposit position in the queue is finalized. +// 3. Deposit does not exceed the `MAX_PENDING_DEPOSITS_PER_EPOCH` limit. +// 4. Deposit does not exceed the activation churn limit. // // Spec definition: // -// def process_pending_balance_deposits(state: BeaconState) -> None: -// available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state) -// processed_amount = 0 -// next_deposit_index = 0 -// deposits_to_postpone = [] +// def process_pending_deposits(state: BeaconState) -> None: // -// for deposit in state.pending_balance_deposits: -// validator = state.validators[deposit.index] -// # Validator is exiting, postpone the deposit until after withdrawable epoch -// if validator.exit_epoch < FAR_FUTURE_EPOCH: -// if get_current_epoch(state) <= validator.withdrawable_epoch: -// deposits_to_postpone.append(deposit) -// # Deposited balance will never become active. Increase balance but do not consume churn -// else: -// increase_balance(state, deposit.index, deposit.amount) -// # Validator is not exiting, attempt to process deposit -// else: -// # Deposit does not fit in the churn, no more deposit processing in this epoch. -// if processed_amount + deposit.amount > available_for_processing: -// break -// # Deposit fits in the churn, process it. Increase balance and consume churn. -// else: -// increase_balance(state, deposit.index, deposit.amount) -// processed_amount += deposit.amount -// # Regardless of how the deposit was handled, we move on in the queue. -// next_deposit_index += 1 +// next_epoch = Epoch(get_current_epoch(state) + 1) +// available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state) +// processed_amount = 0 +// next_deposit_index = 0 +// deposits_to_postpone = [] +// is_churn_limit_reached = False +// finalized_slot = compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) +// +// for deposit in state.pending_deposits: +// # Do not process deposit requests if Eth1 bridge deposits are not yet applied. +// if ( +// # Is deposit request +// deposit.slot > GENESIS_SLOT and +// # There are pending Eth1 bridge deposits +// state.eth1_deposit_index < state.deposit_requests_start_index +// ): +// break // -// state.pending_balance_deposits = state.pending_balance_deposits[next_deposit_index:] +// # Check if deposit has been finalized, otherwise, stop processing. +// if deposit.slot > finalized_slot: +// break // -// if len(state.pending_balance_deposits) == 0: -// state.deposit_balance_to_consume = Gwei(0) +// # Check if number of processed deposits has not reached the limit, otherwise, stop processing. +// if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH: +// break +// +// # Read validator state +// is_validator_exited = False +// is_validator_withdrawn = False +// validator_pubkeys = [v.pubkey for v in state.validators] +// if deposit.pubkey in validator_pubkeys: +// validator = state.validators[ValidatorIndex(validator_pubkeys.index(deposit.pubkey))] +// is_validator_exited = validator.exit_epoch < FAR_FUTURE_EPOCH +// is_validator_withdrawn = validator.withdrawable_epoch < next_epoch +// +// if is_validator_withdrawn: +// # Deposited balance will never become active. Increase balance but do not consume churn +// apply_pending_deposit(state, deposit) +// elif is_validator_exited: +// # Validator is exiting, postpone the deposit until after withdrawable epoch +// deposits_to_postpone.append(deposit) // else: -// state.deposit_balance_to_consume = available_for_processing - processed_amount +// # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. +// is_churn_limit_reached = processed_amount + deposit.amount > available_for_processing +// if is_churn_limit_reached: +// break // -// state.pending_balance_deposits += deposits_to_postpone -func ProcessPendingBalanceDeposits(ctx context.Context, st state.BeaconState, activeBalance primitives.Gwei) error { - _, span := trace.StartSpan(ctx, "electra.ProcessPendingBalanceDeposits") +// # Consume churn and apply deposit. +// processed_amount += deposit.amount +// apply_pending_deposit(state, deposit) +// +// # Regardless of how the deposit was handled, we move on in the queue. +// next_deposit_index += 1 +// +// state.pending_deposits = state.pending_deposits[next_deposit_index:] + deposits_to_postpone +// +// # Accumulate churn only if the churn limit has been hit. +// if is_churn_limit_reached: +// state.deposit_balance_to_consume = available_for_processing - processed_amount +// else: +// state.deposit_balance_to_consume = Gwei(0) +func ProcessPendingDeposits(ctx context.Context, st state.BeaconState, activeBalance primitives.Gwei) error { + _, span := trace.StartSpan(ctx, "electra.ProcessPendingDeposits") defer span.End() if st == nil || st.IsNil() { return errors.New("nil state") } + // constants & initializations + nextEpoch := slots.ToEpoch(st.Slot()) + 1 + processedAmount := uint64(0) + nextDepositIndex := uint64(0) + isChurnLimitReached := false + + var pendingDepositsToBatchVerify []*ethpb.PendingDeposit + var pendingDepositsToPostpone []*eth.PendingDeposit + depBalToConsume, err := st.DepositBalanceToConsume() if err != nil { - return err + return errors.Wrap(err, "could not get deposit balance to consume") } availableForProcessing := depBalToConsume + helpers.ActivationExitChurnLimit(activeBalance) - processedAmount := uint64(0) - nextDepositIndex := 0 - var depositsToPostpone []*eth.PendingBalanceDeposit - deposits, err := st.PendingBalanceDeposits() + finalizedSlot, err := slots.EpochStart(st.FinalizedCheckpoint().Epoch) + if err != nil { + return errors.Wrap(err, "could not get finalized slot") + } + + startIndex, err := st.DepositRequestsStartIndex() + if err != nil { + return errors.Wrap(err, "could not get starting pendingDeposit index") + } + + pendingDeposits, err := st.PendingDeposits() if err != nil { return err } + for _, pendingDeposit := range pendingDeposits { + // Do not process pendingDeposit requests if Eth1 bridge deposits are not yet applied. + if pendingDeposit.Slot > params.BeaconConfig().GenesisSlot && st.Eth1DepositIndex() < startIndex { + break + } - // constants - ffe := params.BeaconConfig().FarFutureEpoch - nextEpoch := slots.ToEpoch(st.Slot()) + 1 + // Check if pendingDeposit has been finalized, otherwise, stop processing. + if pendingDeposit.Slot > finalizedSlot { + break + } - for _, balanceDeposit := range deposits { - v, err := st.ValidatorAtIndexReadOnly(balanceDeposit.Index) - if err != nil { - return fmt.Errorf("failed to fetch validator at index: %w", err) + // Check if number of processed deposits has not reached the limit, otherwise, stop processing. + if nextDepositIndex >= params.BeaconConfig().MaxPendingDepositsPerEpoch { + break } - // If the validator is currently exiting, postpone the deposit until after the withdrawable - // epoch. - if v.ExitEpoch() < ffe { - if nextEpoch <= v.WithdrawableEpoch() { - depositsToPostpone = append(depositsToPostpone, balanceDeposit) - } else { - // The deposited balance will never become active. Therefore, we increase the balance but do - // not consume the churn. - if err := helpers.IncreaseBalance(st, balanceDeposit.Index, balanceDeposit.Amount); err != nil { - return err - } + var isValidatorExited bool + var isValidatorWithdrawn bool + index, found := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(pendingDeposit.PublicKey)) + if found { + val, err := st.ValidatorAtIndexReadOnly(index) + if err != nil { + return errors.Wrap(err, "could not get validator") } + isValidatorExited = val.ExitEpoch() < params.BeaconConfig().FarFutureEpoch + isValidatorWithdrawn = val.WithdrawableEpoch() < nextEpoch + } + + if isValidatorWithdrawn { + // note: the validator will never be active, just increase the balance + if err := helpers.IncreaseBalance(st, index, pendingDeposit.Amount); err != nil { + return errors.Wrap(err, "could not increase balance") + } + } else if isValidatorExited { + pendingDepositsToPostpone = append(pendingDepositsToPostpone, pendingDeposit) } else { - // Validator is not exiting, attempt to process deposit. - if primitives.Gwei(processedAmount+balanceDeposit.Amount) > availableForProcessing { + isChurnLimitReached = primitives.Gwei(processedAmount+pendingDeposit.Amount) > availableForProcessing + if isChurnLimitReached { break } - // Deposit fits in churn, process it. Increase balance and consume churn. - if err := helpers.IncreaseBalance(st, balanceDeposit.Index, balanceDeposit.Amount); err != nil { - return err + processedAmount += pendingDeposit.Amount + + // note: the following code deviates from the spec in order to perform batch signature verification + if found { + if err := helpers.IncreaseBalance(st, index, pendingDeposit.Amount); err != nil { + return errors.Wrap(err, "could not increase balance") + } + } else { + // Collect deposit for batch signature verification + pendingDepositsToBatchVerify = append(pendingDepositsToBatchVerify, pendingDeposit) } - processedAmount += balanceDeposit.Amount } - // Regardless of how the deposit was handled, we move on in the queue. + // Regardless of how the pendingDeposit was handled, we move on in the queue. nextDepositIndex++ } + // Perform batch signature verification on pending deposits that require validator registration + if err = batchProcessNewPendingDeposits(ctx, st, pendingDepositsToBatchVerify); err != nil { + return errors.Wrap(err, "could not process pending deposits with new public keys") + } + // Combined operation: - // - state.pending_balance_deposits = state.pending_balance_deposits[next_deposit_index:] - // - state.pending_balance_deposits += deposits_to_postpone - // However, the number of remaining deposits must be maintained to properly update the deposit + // - state.pending_deposits = state.pending_deposits[next_deposit_index:] + // - state.pending_deposits += deposits_to_postpone + // However, the number of remaining deposits must be maintained to properly update the pendingDeposit // balance to consume. - numRemainingDeposits := len(deposits[nextDepositIndex:]) - deposits = append(deposits[nextDepositIndex:], depositsToPostpone...) - if err := st.SetPendingBalanceDeposits(deposits); err != nil { + pendingDeposits = append(pendingDeposits[nextDepositIndex:], pendingDepositsToPostpone...) + if err := st.SetPendingDeposits(pendingDeposits); err != nil { + return errors.Wrap(err, "could not set pending deposits") + } + // Accumulate churn only if the churn limit has been hit. + if isChurnLimitReached { + return st.SetDepositBalanceToConsume(availableForProcessing - primitives.Gwei(processedAmount)) + } + return st.SetDepositBalanceToConsume(0) +} + +// batchProcessNewPendingDeposits should only be used to process new deposits that require validator registration +func batchProcessNewPendingDeposits(ctx context.Context, state state.BeaconState, pendingDeposits []*ethpb.PendingDeposit) error { + // Return early if there are no deposits to process + if len(pendingDeposits) == 0 { + return nil + } + + // Try batch verification of all deposit signatures + allSignaturesVerified, err := blocks.BatchVerifyPendingDepositsSignatures(ctx, pendingDeposits) + if err != nil { + return errors.Wrap(err, "batch signature verification failed") + } + + // Process each deposit individually + for _, pendingDeposit := range pendingDeposits { + validSignature := allSignaturesVerified + + // If batch verification failed, check the individual deposit signature + if !allSignaturesVerified { + validSignature, err = blocks.IsValidDepositSignature(ðpb.Deposit_Data{ + PublicKey: bytesutil.SafeCopyBytes(pendingDeposit.PublicKey), + WithdrawalCredentials: bytesutil.SafeCopyBytes(pendingDeposit.WithdrawalCredentials), + Amount: pendingDeposit.Amount, + Signature: bytesutil.SafeCopyBytes(pendingDeposit.Signature), + }) + if err != nil { + return errors.Wrap(err, "individual deposit signature verification failed") + } + } + + // Add validator to the registry if the signature is valid + if validSignature { + err = AddValidatorToRegistry(state, pendingDeposit.PublicKey, pendingDeposit.WithdrawalCredentials, pendingDeposit.Amount) + if err != nil { + return errors.Wrap(err, "failed to add validator to registry") + } + } + } + + return nil +} + +// ApplyPendingDeposit implements the spec definition below. +// Note : This function is NOT used by ProcessPendingDeposits due to simplified logic for more readable batch processing +// +// Spec Definition: +// +// def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> None: +// +// """ +// Applies ``deposit`` to the ``state``. +// """ +// validator_pubkeys = [v.pubkey for v in state.validators] +// if deposit.pubkey not in validator_pubkeys: +// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract +// if is_valid_deposit_signature( +// deposit.pubkey, +// deposit.withdrawal_credentials, +// deposit.amount, +// deposit.signature +// ): +// add_validator_to_registry(state, deposit.pubkey, deposit.withdrawal_credentials, deposit.amount) +// else: +// validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) +// # Increase balance +// increase_balance(state, validator_index, deposit.amount) +func ApplyPendingDeposit(ctx context.Context, st state.BeaconState, deposit *ethpb.PendingDeposit) error { + _, span := trace.StartSpan(ctx, "electra.ApplyPendingDeposit") + defer span.End() + index, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(deposit.PublicKey)) + if !ok { + verified, err := blocks.IsValidDepositSignature(ðpb.Deposit_Data{ + PublicKey: bytesutil.SafeCopyBytes(deposit.PublicKey), + WithdrawalCredentials: bytesutil.SafeCopyBytes(deposit.WithdrawalCredentials), + Amount: deposit.Amount, + Signature: bytesutil.SafeCopyBytes(deposit.Signature), + }) + if err != nil { + return errors.Wrap(err, "could not verify deposit signature") + } + + if verified { + if err := AddValidatorToRegistry(st, deposit.PublicKey, deposit.WithdrawalCredentials, deposit.Amount); err != nil { + return errors.Wrap(err, "could not add validator to registry") + } + } + return nil + } + return helpers.IncreaseBalance(st, index, deposit.Amount) +} + +// AddValidatorToRegistry updates the beacon state with validator information +// def add_validator_to_registry(state: BeaconState, pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> None: +// +// index = get_index_for_new_validator(state) +// validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount) # [Modified in Electra:EIP7251] +// set_or_append_list(state.validators, index, validator) +// set_or_append_list(state.balances, index, amount) +// set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000)) +// set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000)) +// set_or_append_list(state.inactivity_scores, index, uint64(0)) +func AddValidatorToRegistry(beaconState state.BeaconState, pubKey []byte, withdrawalCredentials []byte, amount uint64) error { + val := GetValidatorFromDeposit(pubKey, withdrawalCredentials, amount) + if err := beaconState.AppendValidator(val); err != nil { + return err + } + if err := beaconState.AppendBalance(amount); err != nil { return err } - if numRemainingDeposits == 0 { - return st.SetDepositBalanceToConsume(0) - } else { - return st.SetDepositBalanceToConsume(availableForProcessing - primitives.Gwei(processedAmount)) + // only active in altair and only when it's a new validator (after append balance) + if beaconState.Version() >= version.Altair { + if err := beaconState.AppendInactivityScore(0); err != nil { + return err + } + if err := beaconState.AppendPreviousParticipationBits(0); err != nil { + return err + } + if err := beaconState.AppendCurrentParticipationBits(0); err != nil { + return err + } + } + return nil +} + +// GetValidatorFromDeposit gets a new validator object with provided parameters +// +// def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator: +// +// validator = Validator( +// pubkey=pubkey, +// withdrawal_credentials=withdrawal_credentials, +// activation_eligibility_epoch=FAR_FUTURE_EPOCH, +// activation_epoch=FAR_FUTURE_EPOCH, +// exit_epoch=FAR_FUTURE_EPOCH, +// withdrawable_epoch=FAR_FUTURE_EPOCH, +// effective_balance=Gwei(0), +// ) +// +// # [Modified in Electra:EIP7251] +// max_effective_balance = get_max_effective_balance(validator) +// validator.effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance) +// +// return validator +func GetValidatorFromDeposit(pubKey []byte, withdrawalCredentials []byte, amount uint64) *ethpb.Validator { + validator := ðpb.Validator{ + PublicKey: pubKey, + WithdrawalCredentials: withdrawalCredentials, + ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, + ActivationEpoch: params.BeaconConfig().FarFutureEpoch, + ExitEpoch: params.BeaconConfig().FarFutureEpoch, + WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, + EffectiveBalance: 0, } + maxEffectiveBalance := helpers.ValidatorMaxEffectiveBalance(validator) + validator.EffectiveBalance = min(amount-(amount%params.BeaconConfig().EffectiveBalanceIncrement), maxEffectiveBalance) + return validator } // ProcessDepositRequests is a function as part of electra to process execution layer deposits func ProcessDepositRequests(ctx context.Context, beaconState state.BeaconState, requests []*enginev1.DepositRequest) (state.BeaconState, error) { - ctx, span := trace.StartSpan(ctx, "electra.ProcessDepositRequests") + _, span := trace.StartSpan(ctx, "electra.ProcessDepositRequests") defer span.End() if len(requests) == 0 { return beaconState, nil } - deposits := make([]*ethpb.Deposit, 0) - for _, req := range requests { - if req == nil { - return nil, errors.New("got a nil DepositRequest") - } - deposits = append(deposits, ðpb.Deposit{ - Data: ðpb.Deposit_Data{ - PublicKey: req.Pubkey, - WithdrawalCredentials: req.WithdrawalCredentials, - Amount: req.Amount, - Signature: req.Signature, - }, - }) - } - allSignaturesVerified, err := blocks.BatchVerifyDepositsSignatures(ctx, deposits) - if err != nil { - return nil, errors.Wrap(err, "could not verify deposit signatures in batch") - } + var err error for _, receipt := range requests { - beaconState, err = processDepositRequest(beaconState, receipt, allSignaturesVerified) + beaconState, err = processDepositRequest(beaconState, receipt) if err != nil { return nil, errors.Wrap(err, "could not apply deposit request") } @@ -342,30 +554,38 @@ func ProcessDepositRequests(ctx context.Context, beaconState state.BeaconState, // def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None: // // # Set deposit request start index -// if state.deposit_requests_start_index == UNSET_DEPOSIT_REQUEST_START_INDEX: +// if state.deposit_requests_start_index == UNSET_DEPOSIT_REQUESTS_START_INDEX: // state.deposit_requests_start_index = deposit_request.index // -// apply_deposit( -// state=state, +// # Create pending deposit +// state.pending_deposits.append(PendingDeposit( // pubkey=deposit_request.pubkey, // withdrawal_credentials=deposit_request.withdrawal_credentials, // amount=deposit_request.amount, // signature=deposit_request.signature, -// ) -func processDepositRequest(beaconState state.BeaconState, request *enginev1.DepositRequest, allSignaturesVerified bool) (state.BeaconState, error) { +// slot=state.slot, +// )) +func processDepositRequest(beaconState state.BeaconState, request *enginev1.DepositRequest) (state.BeaconState, error) { requestsStartIndex, err := beaconState.DepositRequestsStartIndex() if err != nil { return nil, errors.Wrap(err, "could not get deposit requests start index") } if requestsStartIndex == params.BeaconConfig().UnsetDepositRequestsStartIndex { + if request == nil { + return nil, errors.New("nil deposit request") + } if err := beaconState.SetDepositRequestsStartIndex(request.Index); err != nil { return nil, errors.Wrap(err, "could not set deposit requests start index") } } - return ApplyDeposit(beaconState, ðpb.Deposit_Data{ + if err := beaconState.AppendPendingDeposit(ðpb.PendingDeposit{ PublicKey: bytesutil.SafeCopyBytes(request.Pubkey), Amount: request.Amount, WithdrawalCredentials: bytesutil.SafeCopyBytes(request.WithdrawalCredentials), Signature: bytesutil.SafeCopyBytes(request.Signature), - }, allSignaturesVerified) + Slot: beaconState.Slot(), + }); err != nil { + return nil, errors.Wrap(err, "could not append deposit request") + } + return beaconState, nil } diff --git a/beacon-chain/core/electra/deposits_test.go b/beacon-chain/core/electra/deposits_test.go index e44d04bde18d..d94500278e13 100644 --- a/beacon-chain/core/electra/deposits_test.go +++ b/beacon-chain/core/electra/deposits_test.go @@ -9,10 +9,12 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native" + stateTesting "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/testing" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/crypto/bls" + "github.com/prysmaticlabs/prysm/v5/crypto/bls/common" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" @@ -20,7 +22,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/testing/util" ) -func TestProcessPendingBalanceDeposits(t *testing.T) { +func TestProcessPendingDeposits(t *testing.T) { tests := []struct { name string state state.BeaconState @@ -48,17 +50,10 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { { name: "more deposits than balance to consume processes partial deposits", state: func() state.BeaconState { - st := stateWithActiveBalanceETH(t, 1_000) - require.NoError(t, st.SetDepositBalanceToConsume(100)) amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9) - deps := make([]*eth.PendingBalanceDeposit, 20) - for i := 0; i < len(deps); i += 1 { - deps[i] = ð.PendingBalanceDeposit{ - Amount: uint64(amountAvailForProcessing) / 10, - Index: primitives.ValidatorIndex(i), - } - } - require.NoError(t, st.SetPendingBalanceDeposits(deps)) + depositAmount := uint64(amountAvailForProcessing) / 10 + st := stateWithPendingDeposits(t, 1_000, 20, depositAmount) + require.NoError(t, st.SetDepositBalanceToConsume(100)) return st }(), check: func(t *testing.T, st state.BeaconState) { @@ -74,25 +69,45 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { } // Half of the balance deposits should have been processed. - remaining, err := st.PendingBalanceDeposits() + remaining, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, 10, len(remaining)) }, }, { - name: "less deposits than balance to consume processes all deposits", + name: "withdrawn validators should not consume churn", state: func() state.BeaconState { - st := stateWithActiveBalanceETH(t, 1_000) - require.NoError(t, st.SetDepositBalanceToConsume(0)) amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9) - deps := make([]*eth.PendingBalanceDeposit, 5) - for i := 0; i < len(deps); i += 1 { - deps[i] = ð.PendingBalanceDeposit{ - Amount: uint64(amountAvailForProcessing) / 5, - Index: primitives.ValidatorIndex(i), - } + depositAmount := uint64(amountAvailForProcessing) + // set the pending deposits to the maximum churn limit + st := stateWithPendingDeposits(t, 1_000, 2, depositAmount) + vals := st.Validators() + vals[1].WithdrawableEpoch = 0 + require.NoError(t, st.SetValidators(vals)) + return st + }(), + check: func(t *testing.T, st state.BeaconState) { + amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9) + // Validators 0..9 should have their balance increased + for i := primitives.ValidatorIndex(0); i < 2; i++ { + b, err := st.BalanceAtIndex(i) + require.NoError(t, err) + require.Equal(t, params.BeaconConfig().MinActivationBalance+uint64(amountAvailForProcessing), b) } - require.NoError(t, st.SetPendingBalanceDeposits(deps)) + + // All pending deposits should have been processed + remaining, err := st.PendingDeposits() + require.NoError(t, err) + require.Equal(t, 0, len(remaining)) + }, + }, + { + name: "less deposits than balance to consume processes all deposits", + state: func() state.BeaconState { + amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9) + depositAmount := uint64(amountAvailForProcessing) / 5 + st := stateWithPendingDeposits(t, 1_000, 5, depositAmount) + require.NoError(t, st.SetDepositBalanceToConsume(0)) return st }(), check: func(t *testing.T, st state.BeaconState) { @@ -108,7 +123,74 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { } // All of the balance deposits should have been processed. - remaining, err := st.PendingBalanceDeposits() + remaining, err := st.PendingDeposits() + require.NoError(t, err) + require.Equal(t, 0, len(remaining)) + }, + }, + { + name: "process pending deposit for unknown key, activates new key", + state: func() state.BeaconState { + st := stateWithActiveBalanceETH(t, 0) + sk, err := bls.RandKey() + require.NoError(t, err) + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(0) + dep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0) + require.NoError(t, st.SetPendingDeposits([]*eth.PendingDeposit{dep})) + require.Equal(t, 0, len(st.Validators())) + require.Equal(t, 0, len(st.Balances())) + return st + }(), + check: func(t *testing.T, st state.BeaconState) { + res, err := st.DepositBalanceToConsume() + require.NoError(t, err) + require.Equal(t, primitives.Gwei(0), res) + b, err := st.BalanceAtIndex(0) + require.NoError(t, err) + require.Equal(t, params.BeaconConfig().MinActivationBalance, b) + + // All of the balance deposits should have been processed. + remaining, err := st.PendingDeposits() + require.NoError(t, err) + require.Equal(t, 0, len(remaining)) + + // validator becomes active + require.Equal(t, 1, len(st.Validators())) + require.Equal(t, 1, len(st.Balances())) + }, + }, + { + name: "process excess balance that uses a point to infinity signature, processed as a topup", + state: func() state.BeaconState { + excessBalance := uint64(100) + st := stateWithActiveBalanceETH(t, 32) + validators := st.Validators() + sk, err := bls.RandKey() + require.NoError(t, err) + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(0) + validators[0].PublicKey = sk.PublicKey().Marshal() + validators[0].WithdrawalCredentials = wc + dep := stateTesting.GeneratePendingDeposit(t, sk, excessBalance, bytesutil.ToBytes32(wc), 0) + dep.Signature = common.InfiniteSignature[:] + require.NoError(t, st.SetValidators(validators)) + st.SaveValidatorIndices() + require.NoError(t, st.SetPendingDeposits([]*eth.PendingDeposit{dep})) + return st + }(), + check: func(t *testing.T, st state.BeaconState) { + res, err := st.DepositBalanceToConsume() + require.NoError(t, err) + require.Equal(t, primitives.Gwei(0), res) + b, err := st.BalanceAtIndex(0) + require.NoError(t, err) + require.Equal(t, params.BeaconConfig().MinActivationBalance+uint64(100), b) + + // All of the balance deposits should have been processed. + remaining, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, 0, len(remaining)) }, @@ -116,17 +198,10 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { { name: "exiting validator deposit postponed", state: func() state.BeaconState { - st := stateWithActiveBalanceETH(t, 1_000) - require.NoError(t, st.SetDepositBalanceToConsume(0)) amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9) - deps := make([]*eth.PendingBalanceDeposit, 5) - for i := 0; i < len(deps); i += 1 { - deps[i] = ð.PendingBalanceDeposit{ - Amount: uint64(amountAvailForProcessing) / 5, - Index: primitives.ValidatorIndex(i), - } - } - require.NoError(t, st.SetPendingBalanceDeposits(deps)) + depositAmount := uint64(amountAvailForProcessing) / 5 + st := stateWithPendingDeposits(t, 1_000, 5, depositAmount) + require.NoError(t, st.SetDepositBalanceToConsume(0)) v, err := st.ValidatorAtIndex(0) require.NoError(t, err) v.ExitEpoch = 10 @@ -148,7 +223,7 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { // All of the balance deposits should have been processed, except validator index 0 was // added back to the pending deposits queue. - remaining, err := st.PendingBalanceDeposits() + remaining, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, 1, len(remaining)) }, @@ -156,15 +231,7 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { { name: "exited validator balance increased", state: func() state.BeaconState { - st := stateWithActiveBalanceETH(t, 1_000) - deps := make([]*eth.PendingBalanceDeposit, 1) - for i := 0; i < len(deps); i += 1 { - deps[i] = ð.PendingBalanceDeposit{ - Amount: 1_000_000, - Index: primitives.ValidatorIndex(i), - } - } - require.NoError(t, st.SetPendingBalanceDeposits(deps)) + st := stateWithPendingDeposits(t, 1_000, 1, 1_000_000) v, err := st.ValidatorAtIndex(0) require.NoError(t, err) v.ExitEpoch = 2 @@ -182,7 +249,7 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { require.Equal(t, uint64(1_100_000), b) // All of the balance deposits should have been processed. - remaining, err := st.PendingBalanceDeposits() + remaining, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, 0, len(remaining)) }, @@ -199,7 +266,7 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { tab, err = helpers.TotalActiveBalance(tt.state) } require.NoError(t, err) - err = electra.ProcessPendingBalanceDeposits(context.TODO(), tt.state, primitives.Gwei(tab)) + err = electra.ProcessPendingDeposits(context.TODO(), tt.state, primitives.Gwei(tab)) require.Equal(t, tt.wantErr, err != nil, "wantErr=%v, got err=%s", tt.wantErr, err) if tt.check != nil { tt.check(t, tt.state) @@ -208,6 +275,27 @@ func TestProcessPendingBalanceDeposits(t *testing.T) { } } +func TestBatchProcessNewPendingDeposits(t *testing.T) { + t.Run("invalid batch initiates correct individual validation", func(t *testing.T) { + st := stateWithActiveBalanceETH(t, 0) + require.Equal(t, 0, len(st.Validators())) + require.Equal(t, 0, len(st.Balances())) + sk, err := bls.RandKey() + require.NoError(t, err) + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(0) + validDep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0) + invalidDep := ð.PendingDeposit{} + // have a combination of valid and invalid deposits + deps := []*eth.PendingDeposit{validDep, invalidDep} + require.NoError(t, electra.BatchProcessNewPendingDeposits(context.Background(), st, deps)) + // successfully added to register + require.Equal(t, 1, len(st.Validators())) + require.Equal(t, 1, len(st.Balances())) + }) +} + func TestProcessDepositRequests(t *testing.T) { st, _ := util.DeterministicGenesisStateElectra(t, 1) sk, err := bls.RandKey() @@ -220,7 +308,7 @@ func TestProcessDepositRequests(t *testing.T) { }) t.Run("nil request errors", func(t *testing.T) { _, err = electra.ProcessDepositRequests(context.Background(), st, []*enginev1.DepositRequest{nil}) - require.ErrorContains(t, "got a nil DepositRequest", err) + require.ErrorContains(t, "nil deposit request", err) }) vals := st.Validators() @@ -230,7 +318,7 @@ func TestProcessDepositRequests(t *testing.T) { bals := st.Balances() bals[0] = params.BeaconConfig().MinActivationBalance + 2000 require.NoError(t, st.SetBalances(bals)) - require.NoError(t, st.SetPendingBalanceDeposits(make([]*eth.PendingBalanceDeposit, 0))) // reset pbd as the determinitstic state populates this already + require.NoError(t, st.SetPendingDeposits(make([]*eth.PendingDeposit, 0))) // reset pbd as the determinitstic state populates this already withdrawalCred := make([]byte, 32) withdrawalCred[0] = params.BeaconConfig().CompoundingWithdrawalPrefixByte depositMessage := ð.DepositMessage{ @@ -255,11 +343,10 @@ func TestProcessDepositRequests(t *testing.T) { st, err = electra.ProcessDepositRequests(context.Background(), st, requests) require.NoError(t, err) - pbd, err := st.PendingBalanceDeposits() + pbd, err := st.PendingDeposits() require.NoError(t, err) - require.Equal(t, 2, len(pbd)) + require.Equal(t, 1, len(pbd)) require.Equal(t, uint64(1000), pbd[0].Amount) - require.Equal(t, uint64(2000), pbd[1].Amount) } func TestProcessDeposit_Electra_Simple(t *testing.T) { @@ -286,7 +373,7 @@ func TestProcessDeposit_Electra_Simple(t *testing.T) { require.NoError(t, err) pdSt, err := electra.ProcessDeposits(context.Background(), st, deps) require.NoError(t, err) - pbd, err := pdSt.PendingBalanceDeposits() + pbd, err := pdSt.PendingDeposits() require.NoError(t, err) require.Equal(t, params.BeaconConfig().MinActivationBalance, pbd[2].Amount) require.Equal(t, 3, len(pbd)) @@ -361,40 +448,128 @@ func TestApplyDeposit_TopUps_WithBadSignature(t *testing.T) { require.NoError(t, st.SetValidators(vals)) adSt, err := electra.ApplyDeposit(st, depositData, false) require.NoError(t, err) - pbd, err := adSt.PendingBalanceDeposits() + pbd, err := adSt.PendingDeposits() require.NoError(t, err) require.Equal(t, 1, len(pbd)) require.Equal(t, topUpAmount, pbd[0].Amount) } -func TestApplyDeposit_Electra_SwitchToCompoundingValidator(t *testing.T) { - st, _ := util.DeterministicGenesisStateElectra(t, 3) - sk, err := bls.RandKey() +// stateWithActiveBalanceETH generates a mock beacon state given a balance in eth +func stateWithActiveBalanceETH(t *testing.T, balETH uint64) state.BeaconState { + gwei := balETH * 1_000_000_000 + balPerVal := params.BeaconConfig().MinActivationBalance + numVals := gwei / balPerVal + + vals := make([]*eth.Validator, numVals) + bals := make([]uint64, numVals) + for i := uint64(0); i < numVals; i++ { + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(i) + vals[i] = ð.Validator{ + ActivationEpoch: 0, + ExitEpoch: params.BeaconConfig().FarFutureEpoch, + EffectiveBalance: balPerVal, + WithdrawalCredentials: wc, + WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, + } + bals[i] = balPerVal + } + st, err := state_native.InitializeFromProtoUnsafeElectra(ð.BeaconStateElectra{ + Slot: 10 * params.BeaconConfig().SlotsPerEpoch, + Validators: vals, + Balances: bals, + Fork: ð.Fork{ + CurrentVersion: params.BeaconConfig().ElectraForkVersion, + }, + }) require.NoError(t, err) - withdrawalCred := make([]byte, 32) - withdrawalCred[0] = params.BeaconConfig().CompoundingWithdrawalPrefixByte - depositData := ð.Deposit_Data{ - PublicKey: sk.PublicKey().Marshal(), - Amount: 1000, - WithdrawalCredentials: withdrawalCred, - Signature: make([]byte, fieldparams.BLSSignatureLength), + // set some fake finalized checkpoint + require.NoError(t, st.SetFinalizedCheckpoint(ð.Checkpoint{ + Epoch: 0, + Root: make([]byte, 32), + })) + return st +} + +// stateWithPendingDeposits with pending deposits and existing ethbalance +func stateWithPendingDeposits(t *testing.T, balETH uint64, numDeposits, amount uint64) state.BeaconState { + st := stateWithActiveBalanceETH(t, balETH) + deps := make([]*eth.PendingDeposit, numDeposits) + validators := st.Validators() + for i := 0; i < len(deps); i += 1 { + sk, err := bls.RandKey() + require.NoError(t, err) + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(i) + validators[i].PublicKey = sk.PublicKey().Marshal() + validators[i].WithdrawalCredentials = wc + deps[i] = stateTesting.GeneratePendingDeposit(t, sk, amount, bytesutil.ToBytes32(wc), 0) } - vals := st.Validators() - vals[0].PublicKey = sk.PublicKey().Marshal() - vals[0].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte - require.NoError(t, st.SetValidators(vals)) - bals := st.Balances() - bals[0] = params.BeaconConfig().MinActivationBalance + 2000 - require.NoError(t, st.SetBalances(bals)) - sr, err := signing.ComputeSigningRoot(depositData, bytesutil.ToBytes(3, 32)) + require.NoError(t, st.SetValidators(validators)) + st.SaveValidatorIndices() + require.NoError(t, st.SetPendingDeposits(deps)) + return st +} + +func TestApplyPendingDeposit_TopUp(t *testing.T) { + excessBalance := uint64(100) + st := stateWithActiveBalanceETH(t, 32) + validators := st.Validators() + sk, err := bls.RandKey() require.NoError(t, err) - sig := sk.Sign(sr[:]) - depositData.Signature = sig.Marshal() - adSt, err := electra.ApplyDeposit(st, depositData, true) + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(0) + validators[0].PublicKey = sk.PublicKey().Marshal() + validators[0].WithdrawalCredentials = wc + dep := stateTesting.GeneratePendingDeposit(t, sk, excessBalance, bytesutil.ToBytes32(wc), 0) + dep.Signature = common.InfiniteSignature[:] + require.NoError(t, st.SetValidators(validators)) + st.SaveValidatorIndices() + + require.NoError(t, electra.ApplyPendingDeposit(context.Background(), st, dep)) + + b, err := st.BalanceAtIndex(0) require.NoError(t, err) - pbd, err := adSt.PendingBalanceDeposits() + require.Equal(t, params.BeaconConfig().MinActivationBalance+uint64(excessBalance), b) +} + +func TestApplyPendingDeposit_UnknownKey(t *testing.T) { + st := stateWithActiveBalanceETH(t, 0) + sk, err := bls.RandKey() require.NoError(t, err) - require.Equal(t, 2, len(pbd)) - require.Equal(t, uint64(1000), pbd[0].Amount) - require.Equal(t, uint64(2000), pbd[1].Amount) + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(0) + dep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0) + require.Equal(t, 0, len(st.Validators())) + require.NoError(t, electra.ApplyPendingDeposit(context.Background(), st, dep)) + // activates new validator + require.Equal(t, 1, len(st.Validators())) + b, err := st.BalanceAtIndex(0) + require.NoError(t, err) + require.Equal(t, params.BeaconConfig().MinActivationBalance, b) +} + +func TestApplyPendingDeposit_InvalidSignature(t *testing.T) { + st := stateWithActiveBalanceETH(t, 0) + + sk, err := bls.RandKey() + require.NoError(t, err) + wc := make([]byte, 32) + wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + wc[31] = byte(0) + dep := ð.PendingDeposit{ + PublicKey: sk.PublicKey().Marshal(), + WithdrawalCredentials: wc, + Amount: 100, + } + require.Equal(t, 0, len(st.Validators())) + require.NoError(t, electra.ApplyPendingDeposit(context.Background(), st, dep)) + // no validator added + require.Equal(t, 0, len(st.Validators())) + // no topup either + require.Equal(t, 0, len(st.Balances())) } diff --git a/beacon-chain/core/electra/export_test.go b/beacon-chain/core/electra/export_test.go new file mode 100644 index 000000000000..599926f2a4cd --- /dev/null +++ b/beacon-chain/core/electra/export_test.go @@ -0,0 +1,3 @@ +package electra + +var BatchProcessNewPendingDeposits = batchProcessNewPendingDeposits diff --git a/beacon-chain/core/electra/transition.go b/beacon-chain/core/electra/transition.go index 78be77ce467e..0f0111d54022 100644 --- a/beacon-chain/core/electra/transition.go +++ b/beacon-chain/core/electra/transition.go @@ -44,7 +44,7 @@ var ( // process_registry_updates(state) // process_slashings(state) // process_eth1_data_reset(state) -// process_pending_balance_deposits(state) # New in EIP7251 +// process_pending_deposits(state) # New in EIP7251 // process_pending_consolidations(state) # New in EIP7251 // process_effective_balance_updates(state) // process_slashings_reset(state) @@ -94,7 +94,7 @@ func ProcessEpoch(ctx context.Context, state state.BeaconState) error { return err } - if err = ProcessPendingBalanceDeposits(ctx, state, primitives.Gwei(bp.ActiveCurrentEpoch)); err != nil { + if err = ProcessPendingDeposits(ctx, state, primitives.Gwei(bp.ActiveCurrentEpoch)); err != nil { return err } if err = ProcessPendingConsolidations(ctx, state); err != nil { diff --git a/beacon-chain/core/electra/transition_test.go b/beacon-chain/core/electra/transition_test.go index b94fb21611ec..e03ee7ab9e22 100644 --- a/beacon-chain/core/electra/transition_test.go +++ b/beacon-chain/core/electra/transition_test.go @@ -57,14 +57,17 @@ func TestProcessEpoch_CanProcessElectra(t *testing.T) { require.NoError(t, st.SetSlot(10*params.BeaconConfig().SlotsPerEpoch)) require.NoError(t, st.SetDepositBalanceToConsume(100)) amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9) - deps := make([]*ethpb.PendingBalanceDeposit, 20) + validators := st.Validators() + deps := make([]*ethpb.PendingDeposit, 20) for i := 0; i < len(deps); i += 1 { - deps[i] = ðpb.PendingBalanceDeposit{ - Amount: uint64(amountAvailForProcessing) / 10, - Index: primitives.ValidatorIndex(i), + deps[i] = ðpb.PendingDeposit{ + PublicKey: validators[i].PublicKey, + WithdrawalCredentials: validators[i].WithdrawalCredentials, + Amount: uint64(amountAvailForProcessing) / 10, + Slot: 0, } } - require.NoError(t, st.SetPendingBalanceDeposits(deps)) + require.NoError(t, st.SetPendingDeposits(deps)) require.NoError(t, st.SetPendingConsolidations([]*ethpb.PendingConsolidation{ { SourceIndex: 2, @@ -108,7 +111,7 @@ func TestProcessEpoch_CanProcessElectra(t *testing.T) { require.Equal(t, primitives.Gwei(100), res) // Half of the balance deposits should have been processed. - remaining, err := st.PendingBalanceDeposits() + remaining, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, 10, len(remaining)) diff --git a/beacon-chain/core/electra/upgrade.go b/beacon-chain/core/electra/upgrade.go index 214a0fa5ac9a..2364db01f436 100644 --- a/beacon-chain/core/electra/upgrade.go +++ b/beacon-chain/core/electra/upgrade.go @@ -101,7 +101,7 @@ import ( // earliest_exit_epoch=earliest_exit_epoch, // consolidation_balance_to_consume=0, // earliest_consolidation_epoch=compute_activation_exit_epoch(get_current_epoch(pre)), -// pending_balance_deposits=[], +// pending_deposits=[], // pending_partial_withdrawals=[], // pending_consolidations=[], // ) @@ -272,7 +272,7 @@ func UpgradeToElectra(beaconState state.BeaconState) (state.BeaconState, error) EarliestExitEpoch: earliestExitEpoch, ConsolidationBalanceToConsume: helpers.ConsolidationChurnLimit(primitives.Gwei(tab)), EarliestConsolidationEpoch: helpers.ActivationExitEpoch(slots.ToEpoch(beaconState.Slot())), - PendingBalanceDeposits: make([]*ethpb.PendingBalanceDeposit, 0), + PendingDeposits: make([]*ethpb.PendingDeposit, 0), PendingPartialWithdrawals: make([]*ethpb.PendingPartialWithdrawal, 0), PendingConsolidations: make([]*ethpb.PendingConsolidation, 0), } diff --git a/beacon-chain/core/electra/upgrade_test.go b/beacon-chain/core/electra/upgrade_test.go index 2fa8d1ba43f9..40f2f754c5a7 100644 --- a/beacon-chain/core/electra/upgrade_test.go +++ b/beacon-chain/core/electra/upgrade_test.go @@ -169,10 +169,10 @@ func TestUpgradeToElectra(t *testing.T) { require.NoError(t, err) require.Equal(t, helpers.ActivationExitEpoch(slots.ToEpoch(preForkState.Slot())), earliestConsolidationEpoch) - pendingBalanceDeposits, err := mSt.PendingBalanceDeposits() + pendingDeposits, err := mSt.PendingDeposits() require.NoError(t, err) - require.Equal(t, 2, len(pendingBalanceDeposits)) - require.Equal(t, uint64(1000), pendingBalanceDeposits[1].Amount) + require.Equal(t, 2, len(pendingDeposits)) + require.Equal(t, uint64(1000), pendingDeposits[1].Amount) numPendingPartialWithdrawals, err := mSt.NumPendingPartialWithdrawals() require.NoError(t, err) diff --git a/beacon-chain/core/electra/validator.go b/beacon-chain/core/electra/validator.go index 82b41fd8d7b4..451d6df8bfd7 100644 --- a/beacon-chain/core/electra/validator.go +++ b/beacon-chain/core/electra/validator.go @@ -7,83 +7,20 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" + "github.com/prysmaticlabs/prysm/v5/crypto/bls/common" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) -// AddValidatorToRegistry updates the beacon state with validator information -// def add_validator_to_registry(state: BeaconState, -// -// pubkey: BLSPubkey, -// withdrawal_credentials: Bytes32, -// amount: uint64) -> None: -// index = get_index_for_new_validator(state) -// validator = get_validator_from_deposit(pubkey, withdrawal_credentials) -// set_or_append_list(state.validators, index, validator) -// set_or_append_list(state.balances, index, 0) # [Modified in Electra:EIP7251] -// set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000)) -// set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000)) -// set_or_append_list(state.inactivity_scores, index, uint64(0)) -// state.pending_balance_deposits.append(PendingBalanceDeposit(index=index, amount=amount)) # [New in Electra:EIP7251] -func AddValidatorToRegistry(beaconState state.BeaconState, pubKey []byte, withdrawalCredentials []byte, amount uint64) error { - val := ValidatorFromDeposit(pubKey, withdrawalCredentials) - if err := beaconState.AppendValidator(val); err != nil { - return err - } - index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) - if !ok { - return errors.New("could not find validator in registry") - } - if err := beaconState.AppendBalance(0); err != nil { - return err - } - if err := beaconState.AppendPendingBalanceDeposit(index, amount); err != nil { - return err - } - if err := beaconState.AppendInactivityScore(0); err != nil { - return err - } - if err := beaconState.AppendPreviousParticipationBits(0); err != nil { - return err - } - return beaconState.AppendCurrentParticipationBits(0) -} - -// ValidatorFromDeposit gets a new validator object with provided parameters -// -// def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes32) -> Validator: -// -// return Validator( -// pubkey=pubkey, -// withdrawal_credentials=withdrawal_credentials, -// activation_eligibility_epoch=FAR_FUTURE_EPOCH, -// activation_epoch=FAR_FUTURE_EPOCH, -// exit_epoch=FAR_FUTURE_EPOCH, -// withdrawable_epoch=FAR_FUTURE_EPOCH, -// effective_balance=0, # [Modified in Electra:EIP7251] -// -// ) -func ValidatorFromDeposit(pubKey []byte, withdrawalCredentials []byte) *ethpb.Validator { - return ðpb.Validator{ - PublicKey: pubKey, - WithdrawalCredentials: withdrawalCredentials, - ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, - ActivationEpoch: params.BeaconConfig().FarFutureEpoch, - ExitEpoch: params.BeaconConfig().FarFutureEpoch, - WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, - EffectiveBalance: 0, // [Modified in Electra:EIP7251] - } -} - // SwitchToCompoundingValidator // // Spec definition: // -// def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None: -// validator = state.validators[index] -// if has_eth1_withdrawal_credential(validator): -// validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] -// queue_excess_active_balance(state, index) +// def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None: +// +// validator = state.validators[index] +// if has_eth1_withdrawal_credential(validator): +// validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] +// queue_excess_active_balance(state, index) func SwitchToCompoundingValidator(s state.BeaconState, idx primitives.ValidatorIndex) error { v, err := s.ValidatorAtIndex(idx) if err != nil { @@ -102,18 +39,24 @@ func SwitchToCompoundingValidator(s state.BeaconState, idx primitives.ValidatorI return nil } -// QueueExcessActiveBalance queues validators with balances above the min activation balance and adds to pending balance deposit. +// QueueExcessActiveBalance queues validators with balances above the min activation balance and adds to pending deposit. // // Spec definition: // -// def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> None: -// balance = state.balances[index] -// if balance > MIN_ACTIVATION_BALANCE: -// excess_balance = balance - MIN_ACTIVATION_BALANCE -// state.balances[index] = MIN_ACTIVATION_BALANCE -// state.pending_balance_deposits.append( -// PendingBalanceDeposit(index=index, amount=excess_balance) -// ) +// def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> None: +// +// balance = state.balances[index] +// if balance > MIN_ACTIVATION_BALANCE: +// excess_balance = balance - MIN_ACTIVATION_BALANCE +// state.balances[index] = MIN_ACTIVATION_BALANCE +// validator = state.validators[index] +// state.pending_deposits.append(PendingDeposit( +// pubkey=validator.pubkey, +// withdrawal_credentials=validator.withdrawal_credentials, +// amount=excess_balance, +// signature=bls.G2_POINT_AT_INFINITY, +// slot=GENESIS_SLOT, +// )) func QueueExcessActiveBalance(s state.BeaconState, idx primitives.ValidatorIndex) error { bal, err := s.BalanceAtIndex(idx) if err != nil { @@ -121,11 +64,21 @@ func QueueExcessActiveBalance(s state.BeaconState, idx primitives.ValidatorIndex } if bal > params.BeaconConfig().MinActivationBalance { - excessBalance := bal - params.BeaconConfig().MinActivationBalance if err := s.UpdateBalancesAtIndex(idx, params.BeaconConfig().MinActivationBalance); err != nil { return err } - return s.AppendPendingBalanceDeposit(idx, excessBalance) + excessBalance := bal - params.BeaconConfig().MinActivationBalance + val, err := s.ValidatorAtIndex(idx) + if err != nil { + return err + } + return s.AppendPendingDeposit(ðpb.PendingDeposit{ + PublicKey: val.PublicKey, + WithdrawalCredentials: val.WithdrawalCredentials, + Amount: excessBalance, + Signature: common.InfiniteSignature[:], + Slot: params.BeaconConfig().GenesisSlot, + }) } return nil } @@ -134,15 +87,21 @@ func QueueExcessActiveBalance(s state.BeaconState, idx primitives.ValidatorIndex // // Spec definition: // -// def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None: -// balance = state.balances[index] -// state.balances[index] = 0 -// validator = state.validators[index] -// validator.effective_balance = 0 -// validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH -// state.pending_balance_deposits.append( -// PendingBalanceDeposit(index=index, amount=balance) -// ) +// def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None: +// +// balance = state.balances[index] +// state.balances[index] = 0 +// validator = state.validators[index] +// validator.effective_balance = 0 +// validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH +// state.pending_deposits.append(PendingDeposit( +// pubkey=validator.pubkey, +// withdrawal_credentials=validator.withdrawal_credentials, +// amount=balance, +// signature=bls.G2_POINT_AT_INFINITY, +// slot=GENESIS_SLOT, +// +// )) // //nolint:dupword func QueueEntireBalanceAndResetValidator(s state.BeaconState, idx primitives.ValidatorIndex) error { @@ -166,5 +125,11 @@ func QueueEntireBalanceAndResetValidator(s state.BeaconState, idx primitives.Val return err } - return s.AppendPendingBalanceDeposit(idx, bal) + return s.AppendPendingDeposit(ðpb.PendingDeposit{ + PublicKey: v.PublicKey, + WithdrawalCredentials: v.WithdrawalCredentials, + Amount: bal, + Signature: common.InfiniteSignature[:], + Slot: params.BeaconConfig().GenesisSlot, + }) } diff --git a/beacon-chain/core/electra/validator_test.go b/beacon-chain/core/electra/validator_test.go index a1fa8308ba22..8cdfa2c53406 100644 --- a/beacon-chain/core/electra/validator_test.go +++ b/beacon-chain/core/electra/validator_test.go @@ -6,7 +6,6 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra" state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native" - fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" @@ -14,20 +13,6 @@ import ( "github.com/prysmaticlabs/prysm/v5/testing/util" ) -func TestAddValidatorToRegistry(t *testing.T) { - st, err := state_native.InitializeFromProtoElectra(ð.BeaconStateElectra{}) - require.NoError(t, err) - require.NoError(t, electra.AddValidatorToRegistry(st, make([]byte, fieldparams.BLSPubkeyLength), make([]byte, fieldparams.RootLength), 100)) - balances := st.Balances() - require.Equal(t, 1, len(balances)) - require.Equal(t, uint64(0), balances[0]) - pbds, err := st.PendingBalanceDeposits() - require.NoError(t, err) - require.Equal(t, 1, len(pbds)) - require.Equal(t, uint64(100), pbds[0].Amount) - require.Equal(t, primitives.ValidatorIndex(0), pbds[0].Index) -} - func TestSwitchToCompoundingValidator(t *testing.T) { s, err := state_native.InitializeFromProtoElectra(ð.BeaconStateElectra{ Validators: []*eth.Validator{ @@ -60,7 +45,7 @@ func TestSwitchToCompoundingValidator(t *testing.T) { b, err := s.BalanceAtIndex(1) require.NoError(t, err) require.Equal(t, params.BeaconConfig().MinActivationBalance, b, "balance was changed") - pbd, err := s.PendingBalanceDeposits() + pbd, err := s.PendingDeposits() require.NoError(t, err) require.Equal(t, 0, len(pbd), "pending balance deposits should be empty") @@ -69,11 +54,10 @@ func TestSwitchToCompoundingValidator(t *testing.T) { b, err = s.BalanceAtIndex(2) require.NoError(t, err) require.Equal(t, params.BeaconConfig().MinActivationBalance, b, "balance was not changed") - pbd, err = s.PendingBalanceDeposits() + pbd, err = s.PendingDeposits() require.NoError(t, err) require.Equal(t, 1, len(pbd), "pending balance deposits should have one element") require.Equal(t, uint64(100_000), pbd[0].Amount, "pending balance deposit amount is incorrect") - require.Equal(t, primitives.ValidatorIndex(2), pbd[0].Index, "pending balance deposit index is incorrect") } func TestQueueEntireBalanceAndResetValidator(t *testing.T) { @@ -97,11 +81,10 @@ func TestQueueEntireBalanceAndResetValidator(t *testing.T) { require.NoError(t, err) require.Equal(t, uint64(0), v.EffectiveBalance, "effective balance was not reset") require.Equal(t, params.BeaconConfig().FarFutureEpoch, v.ActivationEligibilityEpoch, "activation eligibility epoch was not reset") - pbd, err := s.PendingBalanceDeposits() + pbd, err := s.PendingDeposits() require.NoError(t, err) require.Equal(t, 1, len(pbd), "pending balance deposits should have one element") require.Equal(t, params.BeaconConfig().MinActivationBalance+100_000, pbd[0].Amount, "pending balance deposit amount is incorrect") - require.Equal(t, primitives.ValidatorIndex(0), pbd[0].Index, "pending balance deposit index is incorrect") } func TestSwitchToCompoundingValidator_Ok(t *testing.T) { @@ -114,7 +97,7 @@ func TestSwitchToCompoundingValidator_Ok(t *testing.T) { require.NoError(t, st.SetBalances(bals)) require.NoError(t, electra.SwitchToCompoundingValidator(st, 0)) - pbd, err := st.PendingBalanceDeposits() + pbd, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, uint64(1010), pbd[0].Amount) // appends it at the end val, err := st.ValidatorAtIndex(0) @@ -132,7 +115,7 @@ func TestQueueExcessActiveBalance_Ok(t *testing.T) { err := electra.QueueExcessActiveBalance(st, 0) require.NoError(t, err) - pbd, err := st.PendingBalanceDeposits() + pbd, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, uint64(1000), pbd[0].Amount) // appends it at the end @@ -149,7 +132,7 @@ func TestQueueEntireBalanceAndResetValidator_Ok(t *testing.T) { err := electra.QueueEntireBalanceAndResetValidator(st, 0) require.NoError(t, err) - pbd, err := st.PendingBalanceDeposits() + pbd, err := st.PendingDeposits() require.NoError(t, err) require.Equal(t, 1, len(pbd)) require.Equal(t, params.BeaconConfig().MinActivationBalance-1000, pbd[0].Amount) diff --git a/beacon-chain/p2p/encoder/ssz_test.go b/beacon-chain/p2p/encoder/ssz_test.go index 17c93286fc2b..6e12af4e7014 100644 --- a/beacon-chain/p2p/encoder/ssz_test.go +++ b/beacon-chain/p2p/encoder/ssz_test.go @@ -110,7 +110,7 @@ type BeaconStateElectraCreator struct{} type PowBlockCreator struct{} type HistoricalSummaryCreator struct{} type BlobIdentifierCreator struct{} -type PendingBalanceDepositCreator struct{} +type PendingDepositCreator struct{} type PendingPartialWithdrawalCreator struct{} type PendingConsolidationCreator struct{} type StatusCreator struct{} @@ -279,8 +279,8 @@ func (BeaconStateElectraCreator) Create() MarshalerProtoMessage { return ðpb. func (PowBlockCreator) Create() MarshalerProtoMessage { return ðpb.PowBlock{} } func (HistoricalSummaryCreator) Create() MarshalerProtoMessage { return ðpb.HistoricalSummary{} } func (BlobIdentifierCreator) Create() MarshalerProtoMessage { return ðpb.BlobIdentifier{} } -func (PendingBalanceDepositCreator) Create() MarshalerProtoMessage { - return ðpb.PendingBalanceDeposit{} +func (PendingDepositCreator) Create() MarshalerProtoMessage { + return ðpb.PendingDeposit{} } func (PendingPartialWithdrawalCreator) Create() MarshalerProtoMessage { return ðpb.PendingPartialWithdrawal{} @@ -397,7 +397,7 @@ var creators = []MarshalerProtoCreator{ PowBlockCreator{}, HistoricalSummaryCreator{}, BlobIdentifierCreator{}, - PendingBalanceDepositCreator{}, + PendingDepositCreator{}, PendingPartialWithdrawalCreator{}, PendingConsolidationCreator{}, StatusCreator{}, diff --git a/beacon-chain/rpc/eth/config/handlers_test.go b/beacon-chain/rpc/eth/config/handlers_test.go index f273f948133e..8284e095bb4b 100644 --- a/beacon-chain/rpc/eth/config/handlers_test.go +++ b/beacon-chain/rpc/eth/config/handlers_test.go @@ -138,7 +138,7 @@ func TestGetSpec(t *testing.T) { config.WhistleBlowerRewardQuotientElectra = 79 config.PendingPartialWithdrawalsLimit = 80 config.MinActivationBalance = 81 - config.PendingBalanceDepositLimit = 82 + config.PendingDepositLimit = 82 config.MaxPendingPartialsPerWithdrawalsSweep = 83 config.PendingConsolidationsLimit = 84 config.MaxPartialWithdrawalsPerPayload = 85 @@ -150,6 +150,7 @@ func TestGetSpec(t *testing.T) { config.MaxCellsInExtendedMatrix = 91 config.UnsetDepositRequestsStartIndex = 92 config.MaxDepositRequestsPerPayload = 93 + config.MaxPendingDepositsPerEpoch = 94 var dbp [4]byte copy(dbp[:], []byte{'0', '0', '0', '1'}) @@ -188,7 +189,7 @@ func TestGetSpec(t *testing.T) { data, ok := resp.Data.(map[string]interface{}) require.Equal(t, true, ok) - assert.Equal(t, 154, len(data)) + assert.Equal(t, 155, len(data)) for k, v := range data { t.Run(k, func(t *testing.T) { switch k { @@ -499,7 +500,7 @@ func TestGetSpec(t *testing.T) { assert.Equal(t, "80", v) case "MIN_ACTIVATION_BALANCE": assert.Equal(t, "81", v) - case "PENDING_BALANCE_DEPOSITS_LIMIT": + case "PENDING_DEPOSITS_LIMIT": assert.Equal(t, "82", v) case "MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP": assert.Equal(t, "83", v) @@ -523,6 +524,8 @@ func TestGetSpec(t *testing.T) { assert.Equal(t, "92", v) case "MAX_DEPOSIT_REQUESTS_PER_PAYLOAD": assert.Equal(t, "93", v) + case "MAX_PENDING_DEPOSITS_PER_EPOCH": + assert.Equal(t, "94", v) default: t.Errorf("Incorrect key: %s", k) } diff --git a/beacon-chain/state/interfaces.go b/beacon-chain/state/interfaces.go index ba7d00fdac88..5c9fa1f8e3da 100644 --- a/beacon-chain/state/interfaces.go +++ b/beacon-chain/state/interfaces.go @@ -225,7 +225,7 @@ type ReadOnlySyncCommittee interface { type ReadOnlyDeposits interface { DepositBalanceToConsume() (primitives.Gwei, error) DepositRequestsStartIndex() (uint64, error) - PendingBalanceDeposits() ([]*ethpb.PendingBalanceDeposit, error) + PendingDeposits() ([]*ethpb.PendingDeposit, error) } type ReadOnlyConsolidations interface { @@ -331,8 +331,8 @@ type WriteOnlyConsolidations interface { } type WriteOnlyDeposits interface { - AppendPendingBalanceDeposit(index primitives.ValidatorIndex, amount uint64) error + AppendPendingDeposit(pd *ethpb.PendingDeposit) error SetDepositRequestsStartIndex(index uint64) error - SetPendingBalanceDeposits(val []*ethpb.PendingBalanceDeposit) error + SetPendingDeposits(val []*ethpb.PendingDeposit) error SetDepositBalanceToConsume(primitives.Gwei) error } diff --git a/beacon-chain/state/state-native/BUILD.bazel b/beacon-chain/state/state-native/BUILD.bazel index 5404444d5a27..b687240a46b3 100644 --- a/beacon-chain/state/state-native/BUILD.bazel +++ b/beacon-chain/state/state-native/BUILD.bazel @@ -7,11 +7,11 @@ go_library( "doc.go", "error.go", "getters_attestation.go", - "getters_balance_deposits.go", "getters_block.go", "getters_checkpoint.go", "getters_consolidation.go", "getters_deposit_requests.go", + "getters_deposits.go", "getters_eth1.go", "getters_exit.go", "getters_misc.go", @@ -27,12 +27,12 @@ go_library( "proofs.go", "readonly_validator.go", "setters_attestation.go", - "setters_balance_deposits.go", "setters_block.go", "setters_checkpoint.go", "setters_churn.go", "setters_consolidation.go", "setters_deposit_requests.go", + "setters_deposits.go", "setters_eth1.go", "setters_misc.go", "setters_participation.go", @@ -91,11 +91,11 @@ go_test( name = "go_default_test", srcs = [ "getters_attestation_test.go", - "getters_balance_deposits_test.go", "getters_block_test.go", "getters_checkpoint_test.go", "getters_consolidation_test.go", "getters_deposit_requests_test.go", + "getters_deposits_test.go", "getters_exit_test.go", "getters_participation_test.go", "getters_test.go", @@ -107,10 +107,10 @@ go_test( "readonly_validator_test.go", "references_test.go", "setters_attestation_test.go", - "setters_balance_deposits_test.go", "setters_churn_test.go", "setters_consolidation_test.go", "setters_deposit_requests_test.go", + "setters_deposits_test.go", "setters_eth1_test.go", "setters_misc_test.go", "setters_participation_test.go", diff --git a/beacon-chain/state/state-native/beacon_state.go b/beacon-chain/state/state-native/beacon_state.go index 916746b5916e..d8b1b2785da5 100644 --- a/beacon-chain/state/state-native/beacon_state.go +++ b/beacon-chain/state/state-native/beacon_state.go @@ -65,7 +65,7 @@ type BeaconState struct { earliestExitEpoch primitives.Epoch consolidationBalanceToConsume primitives.Gwei earliestConsolidationEpoch primitives.Epoch - pendingBalanceDeposits []*ethpb.PendingBalanceDeposit // pending_balance_deposits: List[PendingBalanceDeposit, PENDING_BALANCE_DEPOSITS_LIMIT] + pendingDeposits []*ethpb.PendingDeposit // pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] pendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal // pending_partial_withdrawals: List[PartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] pendingConsolidations []*ethpb.PendingConsolidation // pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] @@ -121,7 +121,7 @@ type beaconStateMarshalable struct { EarliestExitEpoch primitives.Epoch `json:"earliest_exit_epoch" yaml:"earliest_exit_epoch"` ConsolidationBalanceToConsume primitives.Gwei `json:"consolidation_balance_to_consume" yaml:"consolidation_balance_to_consume"` EarliestConsolidationEpoch primitives.Epoch `json:"earliest_consolidation_epoch" yaml:"earliest_consolidation_epoch"` - PendingBalanceDeposits []*ethpb.PendingBalanceDeposit `json:"pending_balance_deposits" yaml:"pending_balance_deposits"` + PendingDeposits []*ethpb.PendingDeposit `json:"pending_deposits" yaml:"pending_deposits"` PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal `json:"pending_partial_withdrawals" yaml:"pending_partial_withdrawals"` PendingConsolidations []*ethpb.PendingConsolidation `json:"pending_consolidations" yaml:"pending_consolidations"` } @@ -190,7 +190,7 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) { EarliestExitEpoch: b.earliestExitEpoch, ConsolidationBalanceToConsume: b.consolidationBalanceToConsume, EarliestConsolidationEpoch: b.earliestConsolidationEpoch, - PendingBalanceDeposits: b.pendingBalanceDeposits, + PendingDeposits: b.pendingDeposits, PendingPartialWithdrawals: b.pendingPartialWithdrawals, PendingConsolidations: b.pendingConsolidations, } diff --git a/beacon-chain/state/state-native/getters_balance_deposits.go b/beacon-chain/state/state-native/getters_deposits.go similarity index 66% rename from beacon-chain/state/state-native/getters_balance_deposits.go rename to beacon-chain/state/state-native/getters_deposits.go index b31b872f082e..99e8ee6eb5cb 100644 --- a/beacon-chain/state/state-native/getters_balance_deposits.go +++ b/beacon-chain/state/state-native/getters_deposits.go @@ -18,22 +18,22 @@ func (b *BeaconState) DepositBalanceToConsume() (primitives.Gwei, error) { return b.depositBalanceToConsume, nil } -// PendingBalanceDeposits is a non-mutating call to the beacon state which returns a deep copy of +// PendingDeposits is a non-mutating call to the beacon state which returns a deep copy of // the pending balance deposit slice. This method requires access to the RLock on the state and // only applies in electra or later. -func (b *BeaconState) PendingBalanceDeposits() ([]*ethpb.PendingBalanceDeposit, error) { +func (b *BeaconState) PendingDeposits() ([]*ethpb.PendingDeposit, error) { if b.version < version.Electra { - return nil, errNotSupported("PendingBalanceDeposits", b.version) + return nil, errNotSupported("PendingDeposits", b.version) } b.lock.RLock() defer b.lock.RUnlock() - return b.pendingBalanceDepositsVal(), nil + return b.pendingDepositsVal(), nil } -func (b *BeaconState) pendingBalanceDepositsVal() []*ethpb.PendingBalanceDeposit { - if b.pendingBalanceDeposits == nil { +func (b *BeaconState) pendingDepositsVal() []*ethpb.PendingDeposit { + if b.pendingDeposits == nil { return nil } - return ethpb.CopySlice(b.pendingBalanceDeposits) + return ethpb.CopySlice(b.pendingDeposits) } diff --git a/beacon-chain/state/state-native/getters_balance_deposits_test.go b/beacon-chain/state/state-native/getters_deposits_test.go similarity index 56% rename from beacon-chain/state/state-native/getters_balance_deposits_test.go rename to beacon-chain/state/state-native/getters_deposits_test.go index ab1738e0b754..4deaf1a53f6d 100644 --- a/beacon-chain/state/state-native/getters_balance_deposits_test.go +++ b/beacon-chain/state/state-native/getters_deposits_test.go @@ -25,21 +25,40 @@ func TestDepositBalanceToConsume(t *testing.T) { require.ErrorContains(t, "not supported", err) } -func TestPendingBalanceDeposits(t *testing.T) { +func TestPendingDeposits(t *testing.T) { s, err := state_native.InitializeFromProtoElectra(ð.BeaconStateElectra{ - PendingBalanceDeposits: []*eth.PendingBalanceDeposit{ - {Index: 1, Amount: 2}, - {Index: 3, Amount: 4}, + PendingDeposits: []*eth.PendingDeposit{ + { + PublicKey: []byte{1, 2, 3}, + WithdrawalCredentials: []byte{4, 5, 6}, + Amount: 2, + Signature: []byte{7, 8, 9}, + Slot: 1, + }, + { + PublicKey: []byte{11, 22, 33}, + WithdrawalCredentials: []byte{44, 55, 66}, + Amount: 4, + Signature: []byte{77, 88, 99}, + Slot: 2, + }, }, }) require.NoError(t, err) - pbd, err := s.PendingBalanceDeposits() + pbd, err := s.PendingDeposits() require.NoError(t, err) require.Equal(t, 2, len(pbd)) - require.Equal(t, primitives.ValidatorIndex(1), pbd[0].Index) + require.DeepEqual(t, []byte{1, 2, 3}, pbd[0].PublicKey) + require.DeepEqual(t, []byte{4, 5, 6}, pbd[0].WithdrawalCredentials) require.Equal(t, uint64(2), pbd[0].Amount) - require.Equal(t, primitives.ValidatorIndex(3), pbd[1].Index) + require.DeepEqual(t, []byte{7, 8, 9}, pbd[0].Signature) + require.Equal(t, primitives.Slot(1), pbd[0].Slot) + + require.DeepEqual(t, []byte{11, 22, 33}, pbd[1].PublicKey) + require.DeepEqual(t, []byte{44, 55, 66}, pbd[1].WithdrawalCredentials) require.Equal(t, uint64(4), pbd[1].Amount) + require.DeepEqual(t, []byte{77, 88, 99}, pbd[1].Signature) + require.Equal(t, primitives.Slot(2), pbd[1].Slot) // Fails for older than electra state s, err = state_native.InitializeFromProtoDeneb(ð.BeaconStateDeneb{}) diff --git a/beacon-chain/state/state-native/getters_state.go b/beacon-chain/state/state-native/getters_state.go index c55ad70e4b1f..29519afc7cee 100644 --- a/beacon-chain/state/state-native/getters_state.go +++ b/beacon-chain/state/state-native/getters_state.go @@ -208,7 +208,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { EarliestExitEpoch: b.earliestExitEpoch, ConsolidationBalanceToConsume: b.consolidationBalanceToConsume, EarliestConsolidationEpoch: b.earliestConsolidationEpoch, - PendingBalanceDeposits: b.pendingBalanceDeposits, + PendingDeposits: b.pendingDeposits, PendingPartialWithdrawals: b.pendingPartialWithdrawals, PendingConsolidations: b.pendingConsolidations, } @@ -414,7 +414,7 @@ func (b *BeaconState) ToProto() interface{} { EarliestExitEpoch: b.earliestExitEpoch, ConsolidationBalanceToConsume: b.consolidationBalanceToConsume, EarliestConsolidationEpoch: b.earliestConsolidationEpoch, - PendingBalanceDeposits: b.pendingBalanceDepositsVal(), + PendingDeposits: b.pendingDepositsVal(), PendingPartialWithdrawals: b.pendingPartialWithdrawalsVal(), PendingConsolidations: b.pendingConsolidationsVal(), } diff --git a/beacon-chain/state/state-native/hasher.go b/beacon-chain/state/state-native/hasher.go index 60422707be72..96303cde4fef 100644 --- a/beacon-chain/state/state-native/hasher.go +++ b/beacon-chain/state/state-native/hasher.go @@ -296,12 +296,12 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b eceRoot := ssz.Uint64Root(uint64(state.earliestConsolidationEpoch)) fieldRoots[types.EarliestConsolidationEpoch.RealPosition()] = eceRoot[:] - // PendingBalanceDeposits root. - pbdRoot, err := stateutil.PendingBalanceDepositsRoot(state.pendingBalanceDeposits) + // PendingDeposits root. + pbdRoot, err := stateutil.PendingDepositsRoot(state.pendingDeposits) if err != nil { return nil, errors.Wrap(err, "could not compute pending balance deposits merkleization") } - fieldRoots[types.PendingBalanceDeposits.RealPosition()] = pbdRoot[:] + fieldRoots[types.PendingDeposits.RealPosition()] = pbdRoot[:] // PendingPartialWithdrawals root. ppwRoot, err := stateutil.PendingPartialWithdrawalsRoot(state.pendingPartialWithdrawals) diff --git a/beacon-chain/state/state-native/setters_balance_deposits.go b/beacon-chain/state/state-native/setters_deposits.go similarity index 55% rename from beacon-chain/state/state-native/setters_balance_deposits.go rename to beacon-chain/state/state-native/setters_deposits.go index 1be44219bc5a..d4ea73ccd9ce 100644 --- a/beacon-chain/state/state-native/setters_balance_deposits.go +++ b/beacon-chain/state/state-native/setters_deposits.go @@ -8,43 +8,43 @@ import ( "github.com/prysmaticlabs/prysm/v5/runtime/version" ) -// AppendPendingBalanceDeposit is a mutating call to the beacon state to create and append a pending +// AppendPendingDeposit is a mutating call to the beacon state to create and append a pending // balance deposit object on to the state. This method requires access to the Lock on the state and // only applies in electra or later. -func (b *BeaconState) AppendPendingBalanceDeposit(index primitives.ValidatorIndex, amount uint64) error { +func (b *BeaconState) AppendPendingDeposit(pd *ethpb.PendingDeposit) error { if b.version < version.Electra { - return errNotSupported("AppendPendingBalanceDeposit", b.version) + return errNotSupported("AppendPendingDeposit", b.version) } b.lock.Lock() defer b.lock.Unlock() - b.sharedFieldReferences[types.PendingBalanceDeposits].MinusRef() - b.sharedFieldReferences[types.PendingBalanceDeposits] = stateutil.NewRef(1) + b.sharedFieldReferences[types.PendingDeposits].MinusRef() + b.sharedFieldReferences[types.PendingDeposits] = stateutil.NewRef(1) - b.pendingBalanceDeposits = append(b.pendingBalanceDeposits, ðpb.PendingBalanceDeposit{Index: index, Amount: amount}) + b.pendingDeposits = append(b.pendingDeposits, pd) - b.markFieldAsDirty(types.PendingBalanceDeposits) - b.rebuildTrie[types.PendingBalanceDeposits] = true + b.markFieldAsDirty(types.PendingDeposits) + b.rebuildTrie[types.PendingDeposits] = true return nil } -// SetPendingBalanceDeposits is a mutating call to the beacon state which replaces the pending +// SetPendingDeposits is a mutating call to the beacon state which replaces the pending // balance deposit slice with the provided value. This method requires access to the Lock on the // state and only applies in electra or later. -func (b *BeaconState) SetPendingBalanceDeposits(val []*ethpb.PendingBalanceDeposit) error { +func (b *BeaconState) SetPendingDeposits(val []*ethpb.PendingDeposit) error { if b.version < version.Electra { - return errNotSupported("SetPendingBalanceDeposits", b.version) + return errNotSupported("SetPendingDeposits", b.version) } b.lock.Lock() defer b.lock.Unlock() - b.sharedFieldReferences[types.PendingBalanceDeposits].MinusRef() - b.sharedFieldReferences[types.PendingBalanceDeposits] = stateutil.NewRef(1) + b.sharedFieldReferences[types.PendingDeposits].MinusRef() + b.sharedFieldReferences[types.PendingDeposits] = stateutil.NewRef(1) - b.pendingBalanceDeposits = val + b.pendingDeposits = val - b.markFieldAsDirty(types.PendingBalanceDeposits) - b.rebuildTrie[types.PendingBalanceDeposits] = true + b.markFieldAsDirty(types.PendingDeposits) + b.rebuildTrie[types.PendingDeposits] = true return nil } diff --git a/beacon-chain/state/state-native/setters_balance_deposits_test.go b/beacon-chain/state/state-native/setters_deposits_test.go similarity index 61% rename from beacon-chain/state/state-native/setters_balance_deposits_test.go rename to beacon-chain/state/state-native/setters_deposits_test.go index 594943b712a9..ac2e31f12d34 100644 --- a/beacon-chain/state/state-native/setters_balance_deposits_test.go +++ b/beacon-chain/state/state-native/setters_deposits_test.go @@ -9,40 +9,52 @@ import ( "github.com/prysmaticlabs/prysm/v5/testing/require" ) -func TestAppendPendingBalanceDeposit(t *testing.T) { +func TestAppendPendingDeposit(t *testing.T) { s, err := state_native.InitializeFromProtoElectra(ð.BeaconStateElectra{}) require.NoError(t, err) - pbd, err := s.PendingBalanceDeposits() + pbd, err := s.PendingDeposits() require.NoError(t, err) require.Equal(t, 0, len(pbd)) - require.NoError(t, s.AppendPendingBalanceDeposit(1, 10)) - pbd, err = s.PendingBalanceDeposits() + creds := []byte{0xFA, 0xCC} + pubkey := []byte{0xAA, 0xBB} + sig := []byte{0xCC, 0xDD} + require.NoError(t, s.AppendPendingDeposit(ð.PendingDeposit{ + PublicKey: pubkey, + WithdrawalCredentials: creds, + Amount: 10, + Signature: sig, + Slot: 1, + })) + pbd, err = s.PendingDeposits() require.NoError(t, err) require.Equal(t, 1, len(pbd)) - require.Equal(t, primitives.ValidatorIndex(1), pbd[0].Index) + require.DeepEqual(t, pubkey, pbd[0].PublicKey) require.Equal(t, uint64(10), pbd[0].Amount) + require.DeepEqual(t, creds, pbd[0].WithdrawalCredentials) + require.Equal(t, primitives.Slot(1), pbd[0].Slot) + require.DeepEqual(t, sig, pbd[0].Signature) // Fails for versions older than electra s, err = state_native.InitializeFromProtoDeneb(ð.BeaconStateDeneb{}) require.NoError(t, err) - require.ErrorContains(t, "not supported", s.AppendPendingBalanceDeposit(1, 1)) + require.ErrorContains(t, "not supported", s.AppendPendingDeposit(ð.PendingDeposit{})) } -func TestSetPendingBalanceDeposits(t *testing.T) { +func TestSetPendingDeposits(t *testing.T) { s, err := state_native.InitializeFromProtoElectra(ð.BeaconStateElectra{}) require.NoError(t, err) - pbd, err := s.PendingBalanceDeposits() + pbd, err := s.PendingDeposits() require.NoError(t, err) require.Equal(t, 0, len(pbd)) - require.NoError(t, s.SetPendingBalanceDeposits([]*eth.PendingBalanceDeposit{{}, {}, {}})) - pbd, err = s.PendingBalanceDeposits() + require.NoError(t, s.SetPendingDeposits([]*eth.PendingDeposit{{}, {}, {}})) + pbd, err = s.PendingDeposits() require.NoError(t, err) require.Equal(t, 3, len(pbd)) // Fails for versions older than electra s, err = state_native.InitializeFromProtoDeneb(ð.BeaconStateDeneb{}) require.NoError(t, err) - require.ErrorContains(t, "not supported", s.SetPendingBalanceDeposits([]*eth.PendingBalanceDeposit{{}, {}, {}})) + require.ErrorContains(t, "not supported", s.SetPendingDeposits([]*eth.PendingDeposit{{}, {}, {}})) } func TestSetDepositBalanceToConsume(t *testing.T) { diff --git a/beacon-chain/state/state-native/state_trie.go b/beacon-chain/state/state-native/state_trie.go index f995688bb082..6536cc9eef45 100644 --- a/beacon-chain/state/state-native/state_trie.go +++ b/beacon-chain/state/state-native/state_trie.go @@ -106,7 +106,7 @@ var electraFields = append( types.EarliestExitEpoch, types.ConsolidationBalanceToConsume, types.EarliestConsolidationEpoch, - types.PendingBalanceDeposits, + types.PendingDeposits, types.PendingPartialWithdrawals, types.PendingConsolidations, ) @@ -755,7 +755,7 @@ func InitializeFromProtoUnsafeElectra(st *ethpb.BeaconStateElectra) (state.Beaco earliestExitEpoch: st.EarliestExitEpoch, consolidationBalanceToConsume: st.ConsolidationBalanceToConsume, earliestConsolidationEpoch: st.EarliestConsolidationEpoch, - pendingBalanceDeposits: st.PendingBalanceDeposits, + pendingDeposits: st.PendingDeposits, pendingPartialWithdrawals: st.PendingPartialWithdrawals, pendingConsolidations: st.PendingConsolidations, @@ -820,7 +820,7 @@ func InitializeFromProtoUnsafeElectra(st *ethpb.BeaconStateElectra) (state.Beaco b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) b.sharedFieldReferences[types.LatestExecutionPayloadHeaderDeneb] = stateutil.NewRef(1) // New in Electra. b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) // New in Capella. - b.sharedFieldReferences[types.PendingBalanceDeposits] = stateutil.NewRef(1) // New in Electra. + b.sharedFieldReferences[types.PendingDeposits] = stateutil.NewRef(1) // New in Electra. b.sharedFieldReferences[types.PendingPartialWithdrawals] = stateutil.NewRef(1) // New in Electra. b.sharedFieldReferences[types.PendingConsolidations] = stateutil.NewRef(1) // New in Electra. if !features.Get().EnableExperimentalState { @@ -898,7 +898,7 @@ func (b *BeaconState) Copy() state.BeaconState { currentEpochParticipation: b.currentEpochParticipation, inactivityScores: b.inactivityScores, inactivityScoresMultiValue: b.inactivityScoresMultiValue, - pendingBalanceDeposits: b.pendingBalanceDeposits, + pendingDeposits: b.pendingDeposits, pendingPartialWithdrawals: b.pendingPartialWithdrawals, pendingConsolidations: b.pendingConsolidations, @@ -1301,8 +1301,8 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) return ssz.Uint64Root(uint64(b.consolidationBalanceToConsume)), nil case types.EarliestConsolidationEpoch: return ssz.Uint64Root(uint64(b.earliestConsolidationEpoch)), nil - case types.PendingBalanceDeposits: - return stateutil.PendingBalanceDepositsRoot(b.pendingBalanceDeposits) + case types.PendingDeposits: + return stateutil.PendingDepositsRoot(b.pendingDeposits) case types.PendingPartialWithdrawals: return stateutil.PendingPartialWithdrawalsRoot(b.pendingPartialWithdrawals) case types.PendingConsolidations: diff --git a/beacon-chain/state/state-native/types/types.go b/beacon-chain/state/state-native/types/types.go index 0fc23cfd363d..1a93c58cb2e6 100644 --- a/beacon-chain/state/state-native/types/types.go +++ b/beacon-chain/state/state-native/types/types.go @@ -106,8 +106,8 @@ func (f FieldIndex) String() string { return "consolidationBalanceToConsume" case EarliestConsolidationEpoch: return "earliestConsolidationEpoch" - case PendingBalanceDeposits: - return "pendingBalanceDeposits" + case PendingDeposits: + return "pendingDeposits" case PendingPartialWithdrawals: return "pendingPartialWithdrawals" case PendingConsolidations: @@ -189,7 +189,7 @@ func (f FieldIndex) RealPosition() int { return 32 case EarliestConsolidationEpoch: return 33 - case PendingBalanceDeposits: + case PendingDeposits: return 34 case PendingPartialWithdrawals: return 35 @@ -256,7 +256,7 @@ const ( EarliestExitEpoch // Electra: EIP-7251 ConsolidationBalanceToConsume // Electra: EIP-7251 EarliestConsolidationEpoch // Electra: EIP-7251 - PendingBalanceDeposits // Electra: EIP-7251 + PendingDeposits // Electra: EIP-7251 PendingPartialWithdrawals // Electra: EIP-7251 PendingConsolidations // Electra: EIP-7251 ) diff --git a/beacon-chain/state/stategen/BUILD.bazel b/beacon-chain/state/stategen/BUILD.bazel index 5905a0a5bdc2..d6d90003346c 100644 --- a/beacon-chain/state/stategen/BUILD.bazel +++ b/beacon-chain/state/stategen/BUILD.bazel @@ -72,18 +72,21 @@ go_test( "//beacon-chain/forkchoice/doubly-linked-tree:go_default_library", "//beacon-chain/state:go_default_library", "//beacon-chain/state/state-native:go_default_library", + "//beacon-chain/state/testing:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", "//consensus-types/blocks/testing:go_default_library", "//consensus-types/interfaces:go_default_library", "//consensus-types/mock:go_default_library", "//consensus-types/primitives:go_default_library", + "//crypto/bls:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//runtime/version:go_default_library", "//testing/assert:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", diff --git a/beacon-chain/state/stategen/replay_test.go b/beacon-chain/state/stategen/replay_test.go index 768b84e59a52..0eebec3ce32e 100644 --- a/beacon-chain/state/stategen/replay_test.go +++ b/beacon-chain/state/stategen/replay_test.go @@ -4,15 +4,18 @@ import ( "context" "testing" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/db" testDB "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree" + stateTesting "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/testing" "github.com/prysmaticlabs/prysm/v5/config/params" consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v5/crypto/bls" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" @@ -209,13 +212,30 @@ func TestReplayBlocks_ProcessEpoch_Electra(t *testing.T) { beaconState, _ := util.DeterministicGenesisStateElectra(t, 1) require.NoError(t, beaconState.SetDepositBalanceToConsume(100)) amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9) - require.NoError(t, beaconState.SetPendingBalanceDeposits([]*ethpb.PendingBalanceDeposit{ + genesisBlock := util.NewBeaconBlockElectra() + + sk, err := bls.RandKey() + require.NoError(t, err) + ethAddress, err := hexutil.Decode("0x967646dCD8d34F4E02204faeDcbAe0cC96fB9245") + require.NoError(t, err) + newCredentials := make([]byte, 12) + newCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + withdrawalCredentials := append(newCredentials, ethAddress...) + ffe := params.BeaconConfig().FarFutureEpoch + require.NoError(t, beaconState.SetValidators([]*ethpb.Validator{ { - Amount: uint64(amountAvailForProcessing) / 10, - Index: primitives.ValidatorIndex(0), + PublicKey: sk.PublicKey().Marshal(), + WithdrawalCredentials: withdrawalCredentials, + ExitEpoch: ffe, + EffectiveBalance: params.BeaconConfig().MinActivationBalance, }, })) - genesisBlock := util.NewBeaconBlockElectra() + beaconState.SaveValidatorIndices() + + require.NoError(t, beaconState.SetPendingDeposits([]*ethpb.PendingDeposit{ + stateTesting.GeneratePendingDeposit(t, sk, uint64(amountAvailForProcessing)/10, bytesutil.ToBytes32(withdrawalCredentials), genesisBlock.Block.Slot), + })) + bodyRoot, err := genesisBlock.Block.HashTreeRoot() require.NoError(t, err) err = beaconState.SetLatestBlockHeader(ðpb.BeaconBlockHeader{ @@ -238,7 +258,7 @@ func TestReplayBlocks_ProcessEpoch_Electra(t *testing.T) { require.NoError(t, err) require.Equal(t, primitives.Gwei(0), res) - remaining, err := newState.PendingBalanceDeposits() + remaining, err := newState.PendingDeposits() require.NoError(t, err) require.Equal(t, 0, len(remaining)) diff --git a/beacon-chain/state/stateutil/BUILD.bazel b/beacon-chain/state/stateutil/BUILD.bazel index 05d2c998151a..b9270e81c7e8 100644 --- a/beacon-chain/state/stateutil/BUILD.bazel +++ b/beacon-chain/state/stateutil/BUILD.bazel @@ -12,8 +12,8 @@ go_library( "historical_summaries_root.go", "participation_bit_root.go", "pending_attestation_root.go", - "pending_balance_deposits_root.go", "pending_consolidations_root.go", + "pending_deposits_root.go", "pending_partial_withdrawals_root.go", "reference.go", "sync_committee.root.go", diff --git a/beacon-chain/state/stateutil/pending_balance_deposits_root.go b/beacon-chain/state/stateutil/pending_deposits_root.go similarity index 57% rename from beacon-chain/state/stateutil/pending_balance_deposits_root.go rename to beacon-chain/state/stateutil/pending_deposits_root.go index 04d9b23f10b4..6d79759e4599 100644 --- a/beacon-chain/state/stateutil/pending_balance_deposits_root.go +++ b/beacon-chain/state/stateutil/pending_deposits_root.go @@ -6,6 +6,6 @@ import ( ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) -func PendingBalanceDepositsRoot(slice []*ethpb.PendingBalanceDeposit) ([32]byte, error) { - return ssz.SliceRoot(slice, fieldparams.PendingBalanceDepositsLimit) +func PendingDepositsRoot(slice []*ethpb.PendingDeposit) ([32]byte, error) { + return ssz.SliceRoot(slice, fieldparams.PendingDepositsLimit) } diff --git a/beacon-chain/state/testing/BUILD.bazel b/beacon-chain/state/testing/BUILD.bazel index 3a2585f11847..0a053d670a2b 100644 --- a/beacon-chain/state/testing/BUILD.bazel +++ b/beacon-chain/state/testing/BUILD.bazel @@ -4,17 +4,25 @@ go_library( name = "go_default_library", testonly = True, srcs = [ + "generators.go", "getters.go", "getters_block.go", "getters_checkpoint.go", "getters_validator.go", ], importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/testing", - visibility = ["//beacon-chain/state:__subpackages__"], + visibility = [ + "//beacon-chain/core:__subpackages__", + "//beacon-chain/state:__subpackages__", + ], deps = [ + "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/signing:go_default_library", "//beacon-chain/state:go_default_library", "//config/fieldparams:go_default_library", + "//config/params:go_default_library", "//consensus-types/primitives:go_default_library", + "//crypto/bls/common:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//testing/assert:go_default_library", diff --git a/beacon-chain/state/testing/generators.go b/beacon-chain/state/testing/generators.go new file mode 100644 index 000000000000..51dfb5c2d209 --- /dev/null +++ b/beacon-chain/state/testing/generators.go @@ -0,0 +1,44 @@ +package testing + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" + "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v5/crypto/bls/common" + "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/testing/require" +) + +// GeneratePendingDeposit is used for testing and producing a signed pending deposit +func GeneratePendingDeposit(t *testing.T, key common.SecretKey, amount uint64, withdrawalCredentials [32]byte, slot primitives.Slot) *ethpb.PendingDeposit { + dm := ðpb.DepositMessage{ + PublicKey: key.PublicKey().Marshal(), + WithdrawalCredentials: withdrawalCredentials[:], + Amount: amount, + } + domain, err := signing.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil) + require.NoError(t, err) + sr, err := signing.ComputeSigningRoot(dm, domain) + require.NoError(t, err) + sig := key.Sign(sr[:]) + depositData := ðpb.Deposit_Data{ + PublicKey: bytesutil.SafeCopyBytes(dm.PublicKey), + WithdrawalCredentials: bytesutil.SafeCopyBytes(dm.WithdrawalCredentials), + Amount: dm.Amount, + Signature: sig.Marshal(), + } + valid, err := blocks.IsValidDepositSignature(depositData) + require.NoError(t, err) + require.Equal(t, true, valid) + return ðpb.PendingDeposit{ + PublicKey: bytesutil.SafeCopyBytes(dm.PublicKey), + WithdrawalCredentials: bytesutil.SafeCopyBytes(dm.WithdrawalCredentials), + Amount: dm.Amount, + Signature: sig.Marshal(), + Slot: slot, + } +} diff --git a/config/fieldparams/mainnet.go b/config/fieldparams/mainnet.go index db16f6f6edf2..3eb5e90b53ee 100644 --- a/config/fieldparams/mainnet.go +++ b/config/fieldparams/mainnet.go @@ -37,7 +37,7 @@ const ( SyncCommitteeBranchDepth = 5 // SyncCommitteeBranchDepth defines the number of leaves in a merkle proof of a sync committee. SyncCommitteeBranchDepthElectra = 6 // SyncCommitteeBranchDepthElectra defines the number of leaves in a merkle proof of a sync committee. FinalityBranchDepth = 6 // FinalityBranchDepth defines the number of leaves in a merkle proof of the finalized checkpoint root. - PendingBalanceDepositsLimit = 134217728 // Maximum number of pending balance deposits in the beacon state. + 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. diff --git a/config/fieldparams/minimal.go b/config/fieldparams/minimal.go index 1eeadbb1b0a5..db99c2dd91b4 100644 --- a/config/fieldparams/minimal.go +++ b/config/fieldparams/minimal.go @@ -37,7 +37,7 @@ const ( SyncCommitteeBranchDepth = 5 // SyncCommitteeBranchDepth defines the number of leaves in a merkle proof of a sync committee. SyncCommitteeBranchDepthElectra = 6 // SyncCommitteeBranchDepthElectra defines the number of leaves in a merkle proof of a sync committee. FinalityBranchDepth = 6 // FinalityBranchDepth defines the number of leaves in a merkle proof of the finalized checkpoint root. - PendingBalanceDepositsLimit = 134217728 // Maximum number of pending balance deposits in the beacon state. + 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. diff --git a/config/params/config.go b/config/params/config.go index 7ed09964a168..a1e59c1033a6 100644 --- a/config/params/config.go +++ b/config/params/config.go @@ -244,11 +244,12 @@ type BeaconChainConfig struct { MaxEffectiveBalanceElectra uint64 `yaml:"MAX_EFFECTIVE_BALANCE_ELECTRA" spec:"true"` // MaxEffectiveBalanceElectra is the maximal amount of Gwei that is effective for staking, increased in electra. MinSlashingPenaltyQuotientElectra uint64 `yaml:"MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA" spec:"true"` // MinSlashingPenaltyQuotientElectra is used to calculate the minimum penalty to prevent DoS attacks, modified for electra. WhistleBlowerRewardQuotientElectra uint64 `yaml:"WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA" spec:"true"` // WhistleBlowerRewardQuotientElectra is used to calculate whistle blower reward, modified in electra. - PendingBalanceDepositLimit uint64 `yaml:"PENDING_BALANCE_DEPOSITS_LIMIT" spec:"true"` // PendingBalanceDepositLimit is the maximum number of pending balance deposits allowed in the beacon state. + PendingDepositLimit uint64 `yaml:"PENDING_DEPOSITS_LIMIT" spec:"true"` // PendingDepositLimit is the maximum number of pending balance deposits allowed in the beacon state. PendingPartialWithdrawalsLimit uint64 `yaml:"PENDING_PARTIAL_WITHDRAWALS_LIMIT" spec:"true"` // PendingPartialWithdrawalsLimit is the maximum number of pending partial withdrawals allowed in the beacon state. PendingConsolidationsLimit uint64 `yaml:"PENDING_CONSOLIDATIONS_LIMIT" spec:"true"` // PendingConsolidationsLimit is the maximum number of pending validator consolidations allowed in the beacon state. MaxConsolidationsRequestsPerPayload uint64 `yaml:"MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD" spec:"true"` // MaxConsolidationsRequestsPerPayload is the maximum number of consolidations in a block. MaxPendingPartialsPerWithdrawalsSweep uint64 `yaml:"MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP" spec:"true"` // MaxPendingPartialsPerWithdrawalsSweep is the maximum number of pending partial withdrawals to process per payload. + MaxPendingDepositsPerEpoch uint64 `yaml:"MAX_PENDING_DEPOSITS_PER_EPOCH" spec:"true"` // MaxPendingDepositsPerEpoch is the maximum number of pending deposits per epoch processing. FullExitRequestAmount uint64 `yaml:"FULL_EXIT_REQUEST_AMOUNT" spec:"true"` // FullExitRequestAmount is the amount of Gwei required to request a full exit. MaxWithdrawalRequestsPerPayload uint64 `yaml:"MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD" spec:"true"` // MaxWithdrawalRequestsPerPayload is the maximum number of execution layer withdrawal requests in each payload. MaxDepositRequestsPerPayload uint64 `yaml:"MAX_DEPOSIT_REQUESTS_PER_PAYLOAD" spec:"true"` // MaxDepositRequestsPerPayload is the maximum number of execution layer deposits in each payload diff --git a/config/params/loader_test.go b/config/params/loader_test.go index 1760f0bf223a..8cd166d46725 100644 --- a/config/params/loader_test.go +++ b/config/params/loader_test.go @@ -180,6 +180,7 @@ func TestModifiedE2E(t *testing.T) { func TestLoadConfigFile(t *testing.T) { t.Run("mainnet", func(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") mn := params.MainnetConfig().Copy() mainnetPresetsFiles := presetsFilePath(t, "mainnet") var err error @@ -198,6 +199,7 @@ func TestLoadConfigFile(t *testing.T) { }) t.Run("minimal", func(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") min := params.MinimalSpecConfig().Copy() minimalPresetsFiles := presetsFilePath(t, "minimal") var err error diff --git a/config/params/mainnet_config.go b/config/params/mainnet_config.go index 012a98f8d592..39449260b83f 100644 --- a/config/params/mainnet_config.go +++ b/config/params/mainnet_config.go @@ -282,12 +282,13 @@ var mainnetBeaconConfig = &BeaconChainConfig{ MaxEffectiveBalanceElectra: 2048_000_000_000, MinSlashingPenaltyQuotientElectra: 4096, WhistleBlowerRewardQuotientElectra: 4096, - PendingBalanceDepositLimit: 134_217_728, + PendingDepositLimit: 134_217_728, PendingPartialWithdrawalsLimit: 134_217_728, PendingConsolidationsLimit: 262_144, MinActivationBalance: 32_000_000_000, MaxConsolidationsRequestsPerPayload: 1, MaxPendingPartialsPerWithdrawalsSweep: 8, + MaxPendingDepositsPerEpoch: 16, FullExitRequestAmount: 0, MaxWithdrawalRequestsPerPayload: 16, MaxDepositRequestsPerPayload: 8192, // 2**13 (= 8192) diff --git a/config/params/minimal_config.go b/config/params/minimal_config.go index d5c3e4b0ada7..234ec1b2a0a2 100644 --- a/config/params/minimal_config.go +++ b/config/params/minimal_config.go @@ -111,6 +111,8 @@ func MinimalSpecConfig() *BeaconChainConfig { minimalConfig.MaxDepositRequestsPerPayload = 4 minimalConfig.PendingPartialWithdrawalsLimit = 64 minimalConfig.MaxPendingPartialsPerWithdrawalsSweep = 1 + minimalConfig.PendingDepositLimit = 134217728 + minimalConfig.MaxPendingDepositsPerEpoch = 16 // Ethereum PoW parameters. minimalConfig.DepositChainID = 5 // Chain ID of eth1 goerli. diff --git a/proto/prysm/v1alpha1/BUILD.bazel b/proto/prysm/v1alpha1/BUILD.bazel index e1245051e02f..d7ff67576a7a 100644 --- a/proto/prysm/v1alpha1/BUILD.bazel +++ b/proto/prysm/v1alpha1/BUILD.bazel @@ -159,8 +159,8 @@ ssz_electra_objs = [ "BlindedBeaconBlockElectra", "Consolidation", "IndexedAttestationElectra", - "PendingBalanceDeposit", - "PendingBalanceDeposits", + "PendingDeposit", + "PendingDeposits", "PendingConsolidation", "PendingPartialWithdrawal", "SignedAggregateAttestationAndProofElectra", diff --git a/proto/prysm/v1alpha1/beacon_block.go b/proto/prysm/v1alpha1/beacon_block.go index be4366b710d2..1f14849e79f4 100644 --- a/proto/prysm/v1alpha1/beacon_block.go +++ b/proto/prysm/v1alpha1/beacon_block.go @@ -625,14 +625,3 @@ func (summary *HistoricalSummary) Copy() *HistoricalSummary { StateSummaryRoot: bytesutil.SafeCopyBytes(summary.StateSummaryRoot), } } - -// Copy -- -func (pbd *PendingBalanceDeposit) Copy() *PendingBalanceDeposit { - if pbd == nil { - return nil - } - return &PendingBalanceDeposit{ - Index: pbd.Index, - Amount: pbd.Amount, - } -} diff --git a/proto/prysm/v1alpha1/beacon_block_fuzz_test.go b/proto/prysm/v1alpha1/beacon_block_fuzz_test.go index 043728777455..6570a73ea6d9 100644 --- a/proto/prysm/v1alpha1/beacon_block_fuzz_test.go +++ b/proto/prysm/v1alpha1/beacon_block_fuzz_test.go @@ -62,5 +62,5 @@ func TestCopyBeaconBlockFields_Fuzz(t *testing.T) { fuzzCopies(t, ð.SignedBLSToExecutionChange{}) fuzzCopies(t, ð.BLSToExecutionChange{}) fuzzCopies(t, ð.HistoricalSummary{}) - fuzzCopies(t, ð.PendingBalanceDeposit{}) + fuzzCopies(t, ð.PendingDeposit{}) } diff --git a/proto/prysm/v1alpha1/beacon_state.pb.go b/proto/prysm/v1alpha1/beacon_state.pb.go index 33b8d1f9bd10..a9bd001c7244 100755 --- a/proto/prysm/v1alpha1/beacon_state.pb.go +++ b/proto/prysm/v1alpha1/beacon_state.pb.go @@ -1873,7 +1873,7 @@ type BeaconStateElectra struct { EarliestExitEpoch github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch `protobuf:"varint,12004,opt,name=earliest_exit_epoch,json=earliestExitEpoch,proto3" json:"earliest_exit_epoch,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"` ConsolidationBalanceToConsume github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Gwei `protobuf:"varint,12005,opt,name=consolidation_balance_to_consume,json=consolidationBalanceToConsume,proto3" json:"consolidation_balance_to_consume,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Gwei"` EarliestConsolidationEpoch github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch `protobuf:"varint,12006,opt,name=earliest_consolidation_epoch,json=earliestConsolidationEpoch,proto3" json:"earliest_consolidation_epoch,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"` - PendingBalanceDeposits []*PendingBalanceDeposit `protobuf:"bytes,12007,rep,name=pending_balance_deposits,json=pendingBalanceDeposits,proto3" json:"pending_balance_deposits,omitempty" ssz-max:"134217728"` + PendingDeposits []*PendingDeposit `protobuf:"bytes,12007,rep,name=pending_deposits,json=pendingDeposits,proto3" json:"pending_deposits,omitempty" ssz-max:"134217728"` PendingPartialWithdrawals []*PendingPartialWithdrawal `protobuf:"bytes,12008,rep,name=pending_partial_withdrawals,json=pendingPartialWithdrawals,proto3" json:"pending_partial_withdrawals,omitempty" ssz-max:"134217728"` PendingConsolidations []*PendingConsolidation `protobuf:"bytes,12009,rep,name=pending_consolidations,json=pendingConsolidations,proto3" json:"pending_consolidations,omitempty" ssz-max:"262144"` } @@ -2148,9 +2148,9 @@ func (x *BeaconStateElectra) GetEarliestConsolidationEpoch() github_com_prysmati return github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch(0) } -func (x *BeaconStateElectra) GetPendingBalanceDeposits() []*PendingBalanceDeposit { +func (x *BeaconStateElectra) GetPendingDeposits() []*PendingDeposit { if x != nil { - return x.PendingBalanceDeposits + return x.PendingDeposits } return nil } @@ -3010,7 +3010,7 @@ var file_proto_prysm_v1alpha1_beacon_state_proto_rawDesc = []byte{ 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x42, 0x0c, 0x92, 0xb5, 0x18, 0x08, 0x31, 0x36, 0x37, 0x37, 0x37, 0x32, 0x31, 0x36, 0x52, 0x13, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x69, 0x65, 0x73, 0x22, 0xcf, 0x19, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, + 0x72, 0x69, 0x65, 0x73, 0x22, 0xb9, 0x19, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12, @@ -3192,57 +3192,56 @@ var file_proto_prysm_v1alpha1_beacon_state_proto_rawDesc = []byte{ 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, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x1a, 0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x76, 0x0a, 0x18, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0xe7, 0x5d, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x0d, 0x92, - 0xb5, 0x18, 0x09, 0x31, 0x33, 0x34, 0x32, 0x31, 0x37, 0x37, 0x32, 0x38, 0x52, 0x16, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x73, 0x12, 0x7f, 0x0a, 0x1b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, - 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x61, 0x6c, 0x73, 0x18, 0xe8, 0x5d, 0x20, 0x03, 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, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, - 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x42, 0x0d, 0x92, 0xb5, 0x18, - 0x09, 0x31, 0x33, 0x34, 0x32, 0x31, 0x37, 0x37, 0x32, 0x38, 0x52, 0x19, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x73, 0x12, 0x6f, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0xe9, 0x5d, 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, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x92, 0xb5, 0x18, 0x06, 0x32, 0x36, 0x32, 0x31, 0x34, 0x34, 0x52, - 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x77, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x12, 0x25, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, - 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, - 0x61, 0x73, 0x68, 0x12, 0x31, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x66, - 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, - 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, - 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x22, 0x7f, 0x0a, 0x11, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x12, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x6f, 0x6f, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, - 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x6f, 0x6f, - 0x74, 0x12, 0x34, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, - 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x6f, 0x6f, 0x74, 0x42, 0x9b, 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, 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 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, 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, 0x56, 0x31, 0x41, 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, + 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x60, 0x0a, 0x10, + 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x18, 0xe7, 0x5d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x0d, + 0x92, 0xb5, 0x18, 0x09, 0x31, 0x33, 0x34, 0x32, 0x31, 0x37, 0x37, 0x32, 0x38, 0x52, 0x0f, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x7f, + 0x0a, 0x1b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, + 0x6c, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x18, 0xe8, 0x5d, + 0x20, 0x03, 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, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31, 0x33, 0x34, 0x32, 0x31, + 0x37, 0x37, 0x32, 0x38, 0x52, 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x12, + 0x6f, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x6f, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe9, 0x5d, 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, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x92, + 0xb5, 0x18, 0x06, 0x32, 0x36, 0x32, 0x31, 0x34, 0x34, 0x52, 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x8d, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x25, 0x0a, + 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x31, 0x0a, + 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, + 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, + 0x22, 0x7f, 0x0a, 0x11, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x12, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x34, 0x0a, 0x12, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x6f, 0x6f, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, + 0x10, 0x73, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x6f, 0x6f, + 0x74, 0x42, 0x9b, 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, + 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 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, 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, 0x56, + 0x31, 0x41, 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 ( @@ -3285,7 +3284,7 @@ var file_proto_prysm_v1alpha1_beacon_state_proto_goTypes = []interface{}{ (*v1.ExecutionPayloadHeader)(nil), // 23: ethereum.engine.v1.ExecutionPayloadHeader (*v1.ExecutionPayloadHeaderCapella)(nil), // 24: ethereum.engine.v1.ExecutionPayloadHeaderCapella (*v1.ExecutionPayloadHeaderDeneb)(nil), // 25: ethereum.engine.v1.ExecutionPayloadHeaderDeneb - (*PendingBalanceDeposit)(nil), // 26: ethereum.eth.v1alpha1.PendingBalanceDeposit + (*PendingDeposit)(nil), // 26: ethereum.eth.v1alpha1.PendingDeposit (*PendingPartialWithdrawal)(nil), // 27: ethereum.eth.v1alpha1.PendingPartialWithdrawal (*PendingConsolidation)(nil), // 28: ethereum.eth.v1alpha1.PendingConsolidation } @@ -3359,7 +3358,7 @@ var file_proto_prysm_v1alpha1_beacon_state_proto_depIdxs = []int32{ 10, // 66: ethereum.eth.v1alpha1.BeaconStateElectra.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee 25, // 67: ethereum.eth.v1alpha1.BeaconStateElectra.latest_execution_payload_header:type_name -> ethereum.engine.v1.ExecutionPayloadHeaderDeneb 17, // 68: ethereum.eth.v1alpha1.BeaconStateElectra.historical_summaries:type_name -> ethereum.eth.v1alpha1.HistoricalSummary - 26, // 69: ethereum.eth.v1alpha1.BeaconStateElectra.pending_balance_deposits:type_name -> ethereum.eth.v1alpha1.PendingBalanceDeposit + 26, // 69: ethereum.eth.v1alpha1.BeaconStateElectra.pending_deposits:type_name -> ethereum.eth.v1alpha1.PendingDeposit 27, // 70: ethereum.eth.v1alpha1.BeaconStateElectra.pending_partial_withdrawals:type_name -> ethereum.eth.v1alpha1.PendingPartialWithdrawal 28, // 71: ethereum.eth.v1alpha1.BeaconStateElectra.pending_consolidations:type_name -> ethereum.eth.v1alpha1.PendingConsolidation 72, // [72:72] is the sub-list for method output_type diff --git a/proto/prysm/v1alpha1/beacon_state.proto b/proto/prysm/v1alpha1/beacon_state.proto index d907c9941f20..ae184e0864c4 100644 --- a/proto/prysm/v1alpha1/beacon_state.proto +++ b/proto/prysm/v1alpha1/beacon_state.proto @@ -402,7 +402,7 @@ message BeaconStateElectra { uint64 earliest_exit_epoch = 12004 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"]; uint64 consolidation_balance_to_consume = 12005 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Gwei"]; uint64 earliest_consolidation_epoch = 12006 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"]; - repeated PendingBalanceDeposit pending_balance_deposits = 12007 [(ethereum.eth.ext.ssz_max) = "pending_balance_deposits_limit"]; + repeated PendingDeposit pending_deposits = 12007 [(ethereum.eth.ext.ssz_max) = "pending_deposits_limit"]; repeated PendingPartialWithdrawal pending_partial_withdrawals = 12008 [(ethereum.eth.ext.ssz_max) = "pending_partial_withdrawals_limit"]; repeated PendingConsolidation pending_consolidations = 12009 [(ethereum.eth.ext.ssz_max) = "pending_consolidations_limit"]; } diff --git a/proto/prysm/v1alpha1/cloners_test.go b/proto/prysm/v1alpha1/cloners_test.go index 294443afff8b..eb6e9cdd6a46 100644 --- a/proto/prysm/v1alpha1/cloners_test.go +++ b/proto/prysm/v1alpha1/cloners_test.go @@ -1152,49 +1152,3 @@ func genConsolidationRequest() *enginev1.ConsolidationRequest { TargetPubkey: bytes(48), } } - -func genPendingPartialWithdrawals(num int) []*v1alpha1.PendingPartialWithdrawal { - ppws := make([]*v1alpha1.PendingPartialWithdrawal, num) - for i := 0; i < num; i++ { - ppws[i] = genPendingPartialWithdrawal() - } - return ppws -} - -func genPendingPartialWithdrawal() *v1alpha1.PendingPartialWithdrawal { - return &v1alpha1.PendingPartialWithdrawal{ - Index: 123456, - Amount: 55555, - WithdrawableEpoch: 444444, - } -} - -func genPendingConsolidations(num int) []*v1alpha1.PendingConsolidation { - pcs := make([]*v1alpha1.PendingConsolidation, num) - for i := 0; i < num; i++ { - pcs[i] = genPendingConsolidation() - } - return pcs -} - -func genPendingConsolidation() *v1alpha1.PendingConsolidation { - return &v1alpha1.PendingConsolidation{ - SourceIndex: 1, - TargetIndex: 2, - } -} - -func genPendingBalanceDeposits(num int) []*v1alpha1.PendingBalanceDeposit { - pbds := make([]*v1alpha1.PendingBalanceDeposit, num) - for i := 0; i < num; i++ { - pbds[i] = genPendingBalanceDeposit() - } - return pbds -} - -func genPendingBalanceDeposit() *v1alpha1.PendingBalanceDeposit { - return &v1alpha1.PendingBalanceDeposit{ - Index: 123456, - Amount: 55555, - } -} diff --git a/proto/prysm/v1alpha1/eip_7251.pb.go b/proto/prysm/v1alpha1/eip_7251.pb.go index 569c74533b18..21b2dafc44f3 100755 --- a/proto/prysm/v1alpha1/eip_7251.pb.go +++ b/proto/prysm/v1alpha1/eip_7251.pb.go @@ -23,17 +23,20 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type PendingBalanceDeposit struct { +type PendingDeposit struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Index github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.ValidatorIndex `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.ValidatorIndex"` - Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` + PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" spec-name:"pubkey" ssz-size:"48"` + WithdrawalCredentials []byte `protobuf:"bytes,2,opt,name=withdrawal_credentials,json=withdrawalCredentials,proto3" json:"withdrawal_credentials,omitempty" ssz-size:"32"` + Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` + Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"` + Slot github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot `protobuf:"varint,5,opt,name=slot,proto3" json:"slot,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"` } -func (x *PendingBalanceDeposit) Reset() { - *x = PendingBalanceDeposit{} +func (x *PendingDeposit) Reset() { + *x = PendingDeposit{} if protoimpl.UnsafeEnabled { mi := &file_proto_prysm_v1alpha1_eip_7251_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -41,13 +44,13 @@ func (x *PendingBalanceDeposit) Reset() { } } -func (x *PendingBalanceDeposit) String() string { +func (x *PendingDeposit) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PendingBalanceDeposit) ProtoMessage() {} +func (*PendingDeposit) ProtoMessage() {} -func (x *PendingBalanceDeposit) ProtoReflect() protoreflect.Message { +func (x *PendingDeposit) ProtoReflect() protoreflect.Message { mi := &file_proto_prysm_v1alpha1_eip_7251_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -59,25 +62,46 @@ func (x *PendingBalanceDeposit) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PendingBalanceDeposit.ProtoReflect.Descriptor instead. -func (*PendingBalanceDeposit) Descriptor() ([]byte, []int) { +// Deprecated: Use PendingDeposit.ProtoReflect.Descriptor instead. +func (*PendingDeposit) Descriptor() ([]byte, []int) { return file_proto_prysm_v1alpha1_eip_7251_proto_rawDescGZIP(), []int{0} } -func (x *PendingBalanceDeposit) GetIndex() github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.ValidatorIndex { +func (x *PendingDeposit) GetPublicKey() []byte { if x != nil { - return x.Index + return x.PublicKey } - return github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.ValidatorIndex(0) + return nil } -func (x *PendingBalanceDeposit) GetAmount() uint64 { +func (x *PendingDeposit) GetWithdrawalCredentials() []byte { + if x != nil { + return x.WithdrawalCredentials + } + return nil +} + +func (x *PendingDeposit) GetAmount() uint64 { if x != nil { return x.Amount } return 0 } +func (x *PendingDeposit) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +func (x *PendingDeposit) GetSlot() github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot { + if x != nil { + return x.Slot + } + return github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot(0) +} + type PendingPartialWithdrawal struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -204,60 +228,68 @@ var file_proto_prysm_v1alpha1_eip_7251_proto_rawDesc = []byte{ 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, 0x22, 0x96, 0x01, 0x0a, 0x15, 0x50, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x12, 0x65, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x4f, 0x82, 0xb5, 0x18, 0x4b, 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, 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, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x22, 0x90, 0x02, 0x0a, 0x18, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, - 0x72, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x12, - 0x65, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4f, - 0x82, 0xb5, 0x18, 0x4b, 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, 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, - 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x75, - 0x0a, 0x12, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x65, - 0x70, 0x6f, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x46, 0x82, 0xb5, 0x18, 0x42, - 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, 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, 0x45, 0x70, 0x6f, - 0x63, 0x68, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x62, 0x6c, 0x65, - 0x45, 0x70, 0x6f, 0x63, 0x68, 0x22, 0xfe, 0x01, 0x0a, 0x14, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x72, - 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x4f, 0x82, 0xb5, 0x18, 0x4b, 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, 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, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x72, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4f, 0x82, 0xb5, 0x18, 0x4b, 0x67, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x02, 0x0a, 0x0e, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x0a, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, + 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x3d, 0x0a, + 0x16, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x5f, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x15, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 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, 0x12, 0x59, 0x0a, 0x04, 0x73, 0x6c, + 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x97, 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, 0x0c, 0x45, 0x49, 0x50, 0x37, 0x32, 0x35, 0x31, 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, 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, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, + 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x22, 0x90, 0x02, 0x0a, 0x18, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x12, 0x65, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x4f, 0x82, 0xb5, 0x18, 0x4b, 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, 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, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x75, 0x0a, 0x12, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x46, 0x82, + 0xb5, 0x18, 0x42, 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, 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, + 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x62, 0x6c, 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x22, 0xfe, 0x01, 0x0a, 0x14, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x72, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4f, 0x82, 0xb5, 0x18, 0x4b, 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, 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, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x72, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4f, 0x82, 0xb5, 0x18, + 0x4b, 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, 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, 0x0b, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x97, 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, 0x0c, 0x45, 0x49, 0x50, 0x37, 0x32, 0x35, 0x31, + 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, 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 ( @@ -274,7 +306,7 @@ func file_proto_prysm_v1alpha1_eip_7251_proto_rawDescGZIP() []byte { var file_proto_prysm_v1alpha1_eip_7251_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_proto_prysm_v1alpha1_eip_7251_proto_goTypes = []interface{}{ - (*PendingBalanceDeposit)(nil), // 0: ethereum.eth.v1alpha1.PendingBalanceDeposit + (*PendingDeposit)(nil), // 0: ethereum.eth.v1alpha1.PendingDeposit (*PendingPartialWithdrawal)(nil), // 1: ethereum.eth.v1alpha1.PendingPartialWithdrawal (*PendingConsolidation)(nil), // 2: ethereum.eth.v1alpha1.PendingConsolidation } @@ -293,7 +325,7 @@ func file_proto_prysm_v1alpha1_eip_7251_proto_init() { } if !protoimpl.UnsafeEnabled { file_proto_prysm_v1alpha1_eip_7251_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PendingBalanceDeposit); i { + switch v := v.(*PendingDeposit); i { case 0: return &v.state case 1: diff --git a/proto/prysm/v1alpha1/eip_7251.proto b/proto/prysm/v1alpha1/eip_7251.proto index 723743b70181..1fc2bf3cdffa 100644 --- a/proto/prysm/v1alpha1/eip_7251.proto +++ b/proto/prysm/v1alpha1/eip_7251.proto @@ -24,12 +24,18 @@ option java_outer_classname = "EIP7251Proto"; option java_package = "org.ethereum.eth.v1alpha1"; option php_namespace = "Ethereum\\Eth\\v1alpha1"; -message PendingBalanceDeposit { - // Validator index for the deposit. - uint64 index = 1 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.ValidatorIndex"]; +message PendingDeposit { + // 48 byte BLS public key of the validator. + bytes public_key = 1 [(ethereum.eth.ext.ssz_size) = "48", (ethereum.eth.ext.spec_name) = "pubkey"]; + + // A 32 byte hash of the withdrawal address public key. + bytes withdrawal_credentials = 2 [(ethereum.eth.ext.ssz_size) = "32"]; // The amount of the deposit (gwei). - uint64 amount = 2; + uint64 amount = 3; + // 96 byte BLS signature from the validator that produced this block. + bytes signature = 4 [(ethereum.eth.ext.ssz_size) = "96"]; + uint64 slot = 5 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; } message PendingPartialWithdrawal { diff --git a/proto/prysm/v1alpha1/eip_7521.go b/proto/prysm/v1alpha1/eip_7521.go index 473ab2de9882..92c9453f3f51 100644 --- a/proto/prysm/v1alpha1/eip_7521.go +++ b/proto/prysm/v1alpha1/eip_7521.go @@ -1,5 +1,21 @@ package eth +import "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" + +// Copy -- +func (pd *PendingDeposit) Copy() *PendingDeposit { + if pd == nil { + return nil + } + return &PendingDeposit{ + PublicKey: bytesutil.SafeCopyBytes(pd.PublicKey), + WithdrawalCredentials: bytesutil.SafeCopyBytes(pd.WithdrawalCredentials), + Amount: pd.Amount, + Signature: bytesutil.SafeCopyBytes(pd.Signature), + Slot: pd.Slot, + } +} + // Copy -- func (pw *PendingPartialWithdrawal) Copy() *PendingPartialWithdrawal { if pw == nil { diff --git a/proto/prysm/v1alpha1/eip_7521_fuzz_test.go b/proto/prysm/v1alpha1/eip_7521_fuzz_test.go index 5123be544d75..7a0e36c98a3d 100644 --- a/proto/prysm/v1alpha1/eip_7521_fuzz_test.go +++ b/proto/prysm/v1alpha1/eip_7521_fuzz_test.go @@ -7,6 +7,7 @@ import ( ) func TestCopyEip7521Types_Fuzz(t *testing.T) { + fuzzCopies(t, ð.PendingDeposit{}) fuzzCopies(t, ð.PendingPartialWithdrawal{}) fuzzCopies(t, ð.PendingConsolidation{}) } diff --git a/proto/prysm/v1alpha1/electra.ssz.go b/proto/prysm/v1alpha1/electra.ssz.go index 15aabf461d03..4088848f7244 100644 --- a/proto/prysm/v1alpha1/electra.ssz.go +++ b/proto/prysm/v1alpha1/electra.ssz.go @@ -3083,9 +3083,9 @@ func (b *BeaconStateElectra) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Field (33) 'EarliestConsolidationEpoch' dst = ssz.MarshalUint64(dst, uint64(b.EarliestConsolidationEpoch)) - // Offset (34) 'PendingBalanceDeposits' + // Offset (34) 'PendingDeposits' dst = ssz.WriteOffset(dst, offset) - offset += len(b.PendingBalanceDeposits) * 16 + offset += len(b.PendingDeposits) * 192 // Offset (35) 'PendingPartialWithdrawals' dst = ssz.WriteOffset(dst, offset) @@ -3178,13 +3178,13 @@ func (b *BeaconStateElectra) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (34) 'PendingBalanceDeposits' - if size := len(b.PendingBalanceDeposits); size > 134217728 { - err = ssz.ErrListTooBigFn("--.PendingBalanceDeposits", size, 134217728) + // Field (34) 'PendingDeposits' + if size := len(b.PendingDeposits); size > 134217728 { + err = ssz.ErrListTooBigFn("--.PendingDeposits", size, 134217728) return } - for ii := 0; ii < len(b.PendingBalanceDeposits); ii++ { - if dst, err = b.PendingBalanceDeposits[ii].MarshalSSZTo(dst); err != nil { + for ii := 0; ii < len(b.PendingDeposits); ii++ { + if dst, err = b.PendingDeposits[ii].MarshalSSZTo(dst); err != nil { return } } @@ -3416,7 +3416,7 @@ func (b *BeaconStateElectra) UnmarshalSSZ(buf []byte) error { // Field (33) 'EarliestConsolidationEpoch' b.EarliestConsolidationEpoch = github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch(ssz.UnmarshallUint64(buf[2736693:2736701])) - // Offset (34) 'PendingBalanceDeposits' + // Offset (34) 'PendingDeposits' if o34 = ssz.ReadOffset(buf[2736701:2736705]); o34 > size || o27 > o34 { return ssz.ErrOffset } @@ -3562,19 +3562,19 @@ func (b *BeaconStateElectra) UnmarshalSSZ(buf []byte) error { } } - // Field (34) 'PendingBalanceDeposits' + // Field (34) 'PendingDeposits' { buf = tail[o34:o35] - num, err := ssz.DivideInt2(len(buf), 16, 134217728) + num, err := ssz.DivideInt2(len(buf), 192, 134217728) if err != nil { return err } - b.PendingBalanceDeposits = make([]*PendingBalanceDeposit, num) + b.PendingDeposits = make([]*PendingDeposit, num) for ii := 0; ii < num; ii++ { - if b.PendingBalanceDeposits[ii] == nil { - b.PendingBalanceDeposits[ii] = new(PendingBalanceDeposit) + if b.PendingDeposits[ii] == nil { + b.PendingDeposits[ii] = new(PendingDeposit) } - if err = b.PendingBalanceDeposits[ii].UnmarshalSSZ(buf[ii*16 : (ii+1)*16]); err != nil { + if err = b.PendingDeposits[ii].UnmarshalSSZ(buf[ii*192 : (ii+1)*192]); err != nil { return err } } @@ -3652,8 +3652,8 @@ func (b *BeaconStateElectra) SizeSSZ() (size int) { // Field (27) 'HistoricalSummaries' size += len(b.HistoricalSummaries) * 64 - // Field (34) 'PendingBalanceDeposits' - size += len(b.PendingBalanceDeposits) * 16 + // Field (34) 'PendingDeposits' + size += len(b.PendingDeposits) * 192 // Field (35) 'PendingPartialWithdrawals' size += len(b.PendingPartialWithdrawals) * 24 @@ -3952,15 +3952,15 @@ func (b *BeaconStateElectra) HashTreeRootWith(hh *ssz.Hasher) (err error) { // Field (33) 'EarliestConsolidationEpoch' hh.PutUint64(uint64(b.EarliestConsolidationEpoch)) - // Field (34) 'PendingBalanceDeposits' + // Field (34) 'PendingDeposits' { subIndx := hh.Index() - num := uint64(len(b.PendingBalanceDeposits)) + num := uint64(len(b.PendingDeposits)) if num > 134217728 { err = ssz.ErrIncorrectListSize return } - for _, elem := range b.PendingBalanceDeposits { + for _, elem := range b.PendingDeposits { if err = elem.HashTreeRootWith(hh); err != nil { return } @@ -4004,62 +4004,122 @@ func (b *BeaconStateElectra) HashTreeRootWith(hh *ssz.Hasher) (err error) { return } -// MarshalSSZ ssz marshals the PendingBalanceDeposit object -func (p *PendingBalanceDeposit) MarshalSSZ() ([]byte, error) { +// MarshalSSZ ssz marshals the PendingDeposit object +func (p *PendingDeposit) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(p) } -// MarshalSSZTo ssz marshals the PendingBalanceDeposit object to a target array -func (p *PendingBalanceDeposit) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the PendingDeposit object to a target array +func (p *PendingDeposit) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - // Field (0) 'Index' - dst = ssz.MarshalUint64(dst, uint64(p.Index)) + // Field (0) 'PublicKey' + if size := len(p.PublicKey); size != 48 { + err = ssz.ErrBytesLengthFn("--.PublicKey", size, 48) + return + } + dst = append(dst, p.PublicKey...) - // Field (1) 'Amount' + // Field (1) 'WithdrawalCredentials' + if size := len(p.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("--.WithdrawalCredentials", size, 32) + return + } + dst = append(dst, p.WithdrawalCredentials...) + + // Field (2) 'Amount' dst = ssz.MarshalUint64(dst, p.Amount) + // Field (3) 'Signature' + if size := len(p.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("--.Signature", size, 96) + return + } + dst = append(dst, p.Signature...) + + // Field (4) 'Slot' + dst = ssz.MarshalUint64(dst, uint64(p.Slot)) + return } -// UnmarshalSSZ ssz unmarshals the PendingBalanceDeposit object -func (p *PendingBalanceDeposit) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the PendingDeposit object +func (p *PendingDeposit) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size != 16 { + if size != 192 { return ssz.ErrSize } - // Field (0) 'Index' - p.Index = github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.ValidatorIndex(ssz.UnmarshallUint64(buf[0:8])) + // Field (0) 'PublicKey' + if cap(p.PublicKey) == 0 { + p.PublicKey = make([]byte, 0, len(buf[0:48])) + } + p.PublicKey = append(p.PublicKey, buf[0:48]...) - // Field (1) 'Amount' - p.Amount = ssz.UnmarshallUint64(buf[8:16]) + // Field (1) 'WithdrawalCredentials' + if cap(p.WithdrawalCredentials) == 0 { + p.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) + } + p.WithdrawalCredentials = append(p.WithdrawalCredentials, buf[48:80]...) + + // Field (2) 'Amount' + p.Amount = ssz.UnmarshallUint64(buf[80:88]) + + // Field (3) 'Signature' + if cap(p.Signature) == 0 { + p.Signature = make([]byte, 0, len(buf[88:184])) + } + p.Signature = append(p.Signature, buf[88:184]...) + + // Field (4) 'Slot' + p.Slot = github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot(ssz.UnmarshallUint64(buf[184:192])) return err } -// SizeSSZ returns the ssz encoded size in bytes for the PendingBalanceDeposit object -func (p *PendingBalanceDeposit) SizeSSZ() (size int) { - size = 16 +// SizeSSZ returns the ssz encoded size in bytes for the PendingDeposit object +func (p *PendingDeposit) SizeSSZ() (size int) { + size = 192 return } -// HashTreeRoot ssz hashes the PendingBalanceDeposit object -func (p *PendingBalanceDeposit) HashTreeRoot() ([32]byte, error) { +// HashTreeRoot ssz hashes the PendingDeposit object +func (p *PendingDeposit) HashTreeRoot() ([32]byte, error) { return ssz.HashWithDefaultHasher(p) } -// HashTreeRootWith ssz hashes the PendingBalanceDeposit object with a hasher -func (p *PendingBalanceDeposit) HashTreeRootWith(hh *ssz.Hasher) (err error) { +// HashTreeRootWith ssz hashes the PendingDeposit object with a hasher +func (p *PendingDeposit) HashTreeRootWith(hh *ssz.Hasher) (err error) { indx := hh.Index() - // Field (0) 'Index' - hh.PutUint64(uint64(p.Index)) + // Field (0) 'PublicKey' + if size := len(p.PublicKey); size != 48 { + err = ssz.ErrBytesLengthFn("--.PublicKey", size, 48) + return + } + hh.PutBytes(p.PublicKey) - // Field (1) 'Amount' + // Field (1) 'WithdrawalCredentials' + if size := len(p.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("--.WithdrawalCredentials", size, 32) + return + } + hh.PutBytes(p.WithdrawalCredentials) + + // Field (2) 'Amount' hh.PutUint64(p.Amount) + // Field (3) 'Signature' + if size := len(p.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("--.Signature", size, 96) + return + } + hh.PutBytes(p.Signature) + + // Field (4) 'Slot' + hh.PutUint64(uint64(p.Slot)) + hh.Merkleize(indx) return } diff --git a/proto/ssz_proto_library.bzl b/proto/ssz_proto_library.bzl index 982be54cc022..820cad3ff7ce 100644 --- a/proto/ssz_proto_library.bzl +++ b/proto/ssz_proto_library.bzl @@ -32,7 +32,7 @@ mainnet = { "max_committees_per_slot.size": "64", "committee_bits.size": "8", "committee_bits.type": "github.com/prysmaticlabs/go-bitfield.Bitvector64", - "pending_balance_deposits_limit": "134217728", + "pending_deposits_limit": "134217728", "pending_partial_withdrawals_limit": "134217728", "pending_consolidations_limit": "262144", "max_consolidation_requests_per_payload.size": "1", @@ -64,7 +64,7 @@ minimal = { "max_committees_per_slot.size": "4", "committee_bits.size": "1", "committee_bits.type": "github.com/prysmaticlabs/go-bitfield.Bitvector4", - "pending_balance_deposits_limit": "134217728", + "pending_deposits_limit": "134217728", "pending_partial_withdrawals_limit": "64", "pending_consolidations_limit": "64", "max_consolidation_requests_per_payload.size": "1", diff --git a/testing/spectest/mainnet/electra/epoch_processing/BUILD.bazel b/testing/spectest/mainnet/electra/epoch_processing/BUILD.bazel index 9315ef295420..0c7f666d06ab 100644 --- a/testing/spectest/mainnet/electra/epoch_processing/BUILD.bazel +++ b/testing/spectest/mainnet/electra/epoch_processing/BUILD.bazel @@ -9,8 +9,8 @@ go_test( "inactivity_updates_test.go", "justification_and_finalization_test.go", "participation_flag_updates_test.go", - "pending_balance_updates_test.go", "pending_consolidations_test.go", + "pending_deposits_updates_test.go", "randao_mixes_reset_test.go", "registry_updates_test.go", "rewards_and_penalties_test.go", diff --git a/testing/spectest/mainnet/electra/epoch_processing/pending_balance_updates_test.go b/testing/spectest/mainnet/electra/epoch_processing/pending_balance_updates_test.go deleted file mode 100644 index 7c47df8e8282..000000000000 --- a/testing/spectest/mainnet/electra/epoch_processing/pending_balance_updates_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package epoch_processing - -import ( - "testing" - - "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing" -) - -func TestMainnet_Electra_EpochProcessing_PendingBalanceDeposits(t *testing.T) { - epoch_processing.RunPendingBalanceDepositsTests(t, "mainnet") -} diff --git a/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go b/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go index 9dbc628d062f..ba4972fba70e 100644 --- a/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go +++ b/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go @@ -7,5 +7,6 @@ import ( ) func TestMainnet_Electra_EpochProcessing_PendingConsolidations(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") epoch_processing.RunPendingConsolidationsTests(t, "mainnet") } diff --git a/testing/spectest/mainnet/electra/epoch_processing/pending_deposits_updates_test.go b/testing/spectest/mainnet/electra/epoch_processing/pending_deposits_updates_test.go new file mode 100644 index 000000000000..374a8a175230 --- /dev/null +++ b/testing/spectest/mainnet/electra/epoch_processing/pending_deposits_updates_test.go @@ -0,0 +1,11 @@ +package epoch_processing + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing" +) + +func TestMainnet_Electra_EpochProcessing_PendingDeposits(t *testing.T) { + epoch_processing.RunPendingDepositsTests(t, "mainnet") +} diff --git a/testing/spectest/mainnet/electra/operations/consolidation_test.go b/testing/spectest/mainnet/electra/operations/consolidation_test.go index 3afe9874ec60..05978c80aa03 100644 --- a/testing/spectest/mainnet/electra/operations/consolidation_test.go +++ b/testing/spectest/mainnet/electra/operations/consolidation_test.go @@ -7,5 +7,6 @@ import ( ) func TestMainnet_Electra_Operations_Consolidation(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") operations.RunConsolidationTest(t, "mainnet") } diff --git a/testing/spectest/mainnet/electra/operations/withdrawals_test.go b/testing/spectest/mainnet/electra/operations/withdrawals_test.go index d57e1f115bc8..0f5a1331d4b7 100644 --- a/testing/spectest/mainnet/electra/operations/withdrawals_test.go +++ b/testing/spectest/mainnet/electra/operations/withdrawals_test.go @@ -7,5 +7,6 @@ import ( ) func TestMainnet_Electra_Operations_Withdrawals(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") operations.RunWithdrawalsTest(t, "mainnet") } diff --git a/testing/spectest/mainnet/electra/sanity/blocks_test.go b/testing/spectest/mainnet/electra/sanity/blocks_test.go index 8e3fc1bcb93e..be34cffdcfef 100644 --- a/testing/spectest/mainnet/electra/sanity/blocks_test.go +++ b/testing/spectest/mainnet/electra/sanity/blocks_test.go @@ -7,5 +7,6 @@ import ( ) func TestMainnet_Electra_Sanity_Blocks(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") sanity.RunBlockProcessingTest(t, "mainnet", "sanity/blocks/pyspec_tests") } diff --git a/testing/spectest/minimal/electra/epoch_processing/BUILD.bazel b/testing/spectest/minimal/electra/epoch_processing/BUILD.bazel index 2e85ef9c939c..3415b173c1b7 100644 --- a/testing/spectest/minimal/electra/epoch_processing/BUILD.bazel +++ b/testing/spectest/minimal/electra/epoch_processing/BUILD.bazel @@ -9,8 +9,8 @@ go_test( "inactivity_updates_test.go", "justification_and_finalization_test.go", "participation_flag_updates_test.go", - "pending_balance_updates_test.go", "pending_consolidations_test.go", + "pending_deposits_updates_test.go", "randao_mixes_reset_test.go", "registry_updates_test.go", "rewards_and_penalties_test.go", diff --git a/testing/spectest/minimal/electra/epoch_processing/pending_balance_updates_test.go b/testing/spectest/minimal/electra/epoch_processing/pending_balance_updates_test.go deleted file mode 100644 index a2cc73b1a3ca..000000000000 --- a/testing/spectest/minimal/electra/epoch_processing/pending_balance_updates_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package epoch_processing - -import ( - "testing" - - "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing" -) - -func TestMinimal_Electra_EpochProcessing_PendingBalanceDeposits(t *testing.T) { - epoch_processing.RunPendingBalanceDepositsTests(t, "minimal") -} diff --git a/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go b/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go index 0fcbb76608d0..4253043d319b 100644 --- a/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go +++ b/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go @@ -7,5 +7,6 @@ import ( ) func TestMinimal_Electra_EpochProcessing_PendingConsolidations(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") epoch_processing.RunPendingConsolidationsTests(t, "minimal") } diff --git a/testing/spectest/minimal/electra/epoch_processing/pending_deposits_updates_test.go b/testing/spectest/minimal/electra/epoch_processing/pending_deposits_updates_test.go new file mode 100644 index 000000000000..960ffbf8536e --- /dev/null +++ b/testing/spectest/minimal/electra/epoch_processing/pending_deposits_updates_test.go @@ -0,0 +1,11 @@ +package epoch_processing + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing" +) + +func TestMinimal_Electra_EpochProcessing_PendingDeposits(t *testing.T) { + epoch_processing.RunPendingDepositsTests(t, "minimal") +} diff --git a/testing/spectest/minimal/electra/operations/consolidation_test.go b/testing/spectest/minimal/electra/operations/consolidation_test.go index cc46d13998d2..6ec83f5a1fd7 100644 --- a/testing/spectest/minimal/electra/operations/consolidation_test.go +++ b/testing/spectest/minimal/electra/operations/consolidation_test.go @@ -7,5 +7,6 @@ import ( ) func TestMinimal_Electra_Operations_Consolidation(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") operations.RunConsolidationTest(t, "minimal") } diff --git a/testing/spectest/minimal/electra/operations/withdrawals_test.go b/testing/spectest/minimal/electra/operations/withdrawals_test.go index 4b93287a4188..dcbc0672e396 100644 --- a/testing/spectest/minimal/electra/operations/withdrawals_test.go +++ b/testing/spectest/minimal/electra/operations/withdrawals_test.go @@ -7,5 +7,6 @@ import ( ) func TestMinimal_Electra_Operations_Withdrawals(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") operations.RunWithdrawalsTest(t, "minimal") } diff --git a/testing/spectest/minimal/electra/sanity/blocks_test.go b/testing/spectest/minimal/electra/sanity/blocks_test.go index 5d2e27fcd24e..56e1905a5998 100644 --- a/testing/spectest/minimal/electra/sanity/blocks_test.go +++ b/testing/spectest/minimal/electra/sanity/blocks_test.go @@ -7,5 +7,6 @@ import ( ) func TestMinimal_Electra_Sanity_Blocks(t *testing.T) { + t.Skip("TODO: add back in after all spec test features are in.") sanity.RunBlockProcessingTest(t, "minimal", "sanity/blocks/pyspec_tests") } diff --git a/testing/spectest/shared/electra/epoch_processing/BUILD.bazel b/testing/spectest/shared/electra/epoch_processing/BUILD.bazel index b82d6a6ba643..c65c52dd9f69 100644 --- a/testing/spectest/shared/electra/epoch_processing/BUILD.bazel +++ b/testing/spectest/shared/electra/epoch_processing/BUILD.bazel @@ -11,8 +11,8 @@ go_library( "inactivity_updates.go", "justification_and_finalization.go", "participation_flag_updates.go", - "pending_balance_updates.go", "pending_consolidations.go", + "pending_deposit_updates.go", "randao_mixes_reset.go", "registry_updates.go", "rewards_and_penalties.go", diff --git a/testing/spectest/shared/electra/epoch_processing/pending_balance_updates.go b/testing/spectest/shared/electra/epoch_processing/pending_deposit_updates.go similarity index 71% rename from testing/spectest/shared/electra/epoch_processing/pending_balance_updates.go rename to testing/spectest/shared/electra/epoch_processing/pending_deposit_updates.go index 89a734373199..c630a4e80849 100644 --- a/testing/spectest/shared/electra/epoch_processing/pending_balance_updates.go +++ b/testing/spectest/shared/electra/epoch_processing/pending_deposit_updates.go @@ -13,23 +13,23 @@ import ( "github.com/prysmaticlabs/prysm/v5/testing/spectest/utils" ) -func RunPendingBalanceDepositsTests(t *testing.T, config string) { +func RunPendingDepositsTests(t *testing.T, config string) { require.NoError(t, utils.SetConfig(t, config)) - testFolders, testsFolderPath := utils.TestFolders(t, config, "electra", "epoch_processing/pending_balance_deposits/pyspec_tests") + testFolders, testsFolderPath := utils.TestFolders(t, config, "electra", "epoch_processing/pending_deposits/pyspec_tests") for _, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { folderPath := path.Join(testsFolderPath, folder.Name()) - RunEpochOperationTest(t, folderPath, processPendingBalanceDeposits) + RunEpochOperationTest(t, folderPath, processPendingDeposits) }) } } -func processPendingBalanceDeposits(t *testing.T, st state.BeaconState) (state.BeaconState, error) { +func processPendingDeposits(t *testing.T, st state.BeaconState) (state.BeaconState, error) { // The caller of this method would normally have the precompute balance values for total // active balance for this epoch. For ease of test setup, we will compute total active // balance from the given state. tab, err := helpers.TotalActiveBalance(st) require.NoError(t, err) - return st, electra.ProcessPendingBalanceDeposits(context.TODO(), st, primitives.Gwei(tab)) + return st, electra.ProcessPendingDeposits(context.TODO(), st, primitives.Gwei(tab)) } diff --git a/testing/spectest/shared/electra/ssz_static/ssz_static.go b/testing/spectest/shared/electra/ssz_static/ssz_static.go index d1060c922e44..ee60e997db4f 100644 --- a/testing/spectest/shared/electra/ssz_static/ssz_static.go +++ b/testing/spectest/shared/electra/ssz_static/ssz_static.go @@ -141,8 +141,8 @@ func UnmarshalledSSZ(t *testing.T, serializedBytes []byte, folderName string) (i obj = ðpb.BLSToExecutionChange{} case "SignedBLSToExecutionChange": obj = ðpb.SignedBLSToExecutionChange{} - case "PendingBalanceDeposit": - obj = ðpb.PendingBalanceDeposit{} + case "PendingDeposit": + obj = ðpb.PendingDeposit{} case "PendingPartialWithdrawal": obj = ðpb.PendingPartialWithdrawal{} case "PendingConsolidation": diff --git a/testing/util/electra_state.go b/testing/util/electra_state.go index afb4497afd4c..551af5536508 100644 --- a/testing/util/electra_state.go +++ b/testing/util/electra_state.go @@ -209,7 +209,7 @@ func buildGenesisBeaconStateElectra(genesisTime uint64, preState state.BeaconSta ExitBalanceToConsume: helpers.ActivationExitChurnLimit(primitives.Gwei(tab)), EarliestConsolidationEpoch: helpers.ActivationExitEpoch(slots.ToEpoch(preState.Slot())), ConsolidationBalanceToConsume: helpers.ConsolidationChurnLimit(primitives.Gwei(tab)), - PendingBalanceDeposits: make([]*ethpb.PendingBalanceDeposit, 0), + PendingDeposits: make([]*ethpb.PendingDeposit, 0), PendingPartialWithdrawals: make([]*ethpb.PendingPartialWithdrawal, 0), PendingConsolidations: make([]*ethpb.PendingConsolidation, 0), } From 80cafaa6dffe12c18091390e8f6b55036db3af67 Mon Sep 17 00:00:00 2001 From: terence Date: Mon, 14 Oct 2024 06:40:50 -0700 Subject: [PATCH 03/26] Fix partial withdrawals (#14509) --- CHANGELOG.md | 1 + .../state/state-native/getters_withdrawal.go | 3 ++- .../state-native/getters_withdrawal_test.go | 20 ++++++++++++++++++- .../electra/operations/withdrawals_test.go | 1 - .../electra/operations/withdrawals_test.go | 1 - 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 878a96a0b17d..a73e15b33318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Testing: added custom matcher for better push settings testing. - Registered `GetDepositSnapshot` Beacon API endpoint. - Fixed mesh size by appending `gParams.Dhi = gossipSubDhi` +- Fix skipping partial withdrawals count. ### Security diff --git a/beacon-chain/state/state-native/getters_withdrawal.go b/beacon-chain/state/state-native/getters_withdrawal.go index 5014f45db0cc..d1b9e4057dcb 100644 --- a/beacon-chain/state/state-native/getters_withdrawal.go +++ b/beacon-chain/state/state-native/getters_withdrawal.go @@ -113,6 +113,7 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, err epoch := slots.ToEpoch(b.slot) // Electra partial withdrawals functionality. + var partialWithdrawalsCount uint64 if b.version >= version.Electra { for _, w := range b.pendingPartialWithdrawals { if w.WithdrawableEpoch > epoch || len(withdrawals) >= int(params.BeaconConfig().MaxPendingPartialsPerWithdrawalsSweep) { @@ -139,9 +140,9 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, err }) withdrawalIndex++ } + partialWithdrawalsCount++ } } - partialWithdrawalsCount := uint64(len(withdrawals)) validatorsLen := b.validatorsLen() bound := mathutil.Min(uint64(validatorsLen), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep) diff --git a/beacon-chain/state/state-native/getters_withdrawal_test.go b/beacon-chain/state/state-native/getters_withdrawal_test.go index 2b247c255bf8..f6a6158f318c 100644 --- a/beacon-chain/state/state-native/getters_withdrawal_test.go +++ b/beacon-chain/state/state-native/getters_withdrawal_test.go @@ -343,10 +343,28 @@ func TestExpectedWithdrawals(t *testing.T) { require.NoError(t, pb.UnmarshalSSZ(serializedSSZ)) s, err := state_native.InitializeFromProtoElectra(pb) require.NoError(t, err) - t.Log(s.NumPendingPartialWithdrawals()) expected, partialWithdrawalsCount, err := s.ExpectedWithdrawals() require.NoError(t, err) require.Equal(t, 8, len(expected)) require.Equal(t, uint64(8), partialWithdrawalsCount) }) + + t.Run("electra some pending partial withdrawals", func(t *testing.T) { + // Load a serialized Electra state from disk. + // This spectest has a fully hydrated beacon state with partial pending withdrawals. + serializedBytes, err := util.BazelFileBytes("tests/mainnet/electra/operations/withdrawal_request/pyspec_tests/pending_withdrawals_consume_all_excess_balance/pre.ssz_snappy") + require.NoError(t, err) + serializedSSZ, err := snappy.Decode(nil /* dst */, serializedBytes) + require.NoError(t, err) + pb := ðpb.BeaconStateElectra{} + require.NoError(t, pb.UnmarshalSSZ(serializedSSZ)) + s, err := state_native.InitializeFromProtoElectra(pb) + require.NoError(t, err) + p, err := s.PendingPartialWithdrawals() + require.NoError(t, err) + require.NoError(t, s.UpdateBalancesAtIndex(p[0].Index, 0)) // This should still count as partial withdrawal. + _, partialWithdrawalsCount, err := s.ExpectedWithdrawals() + require.NoError(t, err) + require.Equal(t, uint64(10), partialWithdrawalsCount) + }) } diff --git a/testing/spectest/mainnet/electra/operations/withdrawals_test.go b/testing/spectest/mainnet/electra/operations/withdrawals_test.go index 0f5a1331d4b7..d57e1f115bc8 100644 --- a/testing/spectest/mainnet/electra/operations/withdrawals_test.go +++ b/testing/spectest/mainnet/electra/operations/withdrawals_test.go @@ -7,6 +7,5 @@ import ( ) func TestMainnet_Electra_Operations_Withdrawals(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") operations.RunWithdrawalsTest(t, "mainnet") } diff --git a/testing/spectest/minimal/electra/operations/withdrawals_test.go b/testing/spectest/minimal/electra/operations/withdrawals_test.go index dcbc0672e396..4b93287a4188 100644 --- a/testing/spectest/minimal/electra/operations/withdrawals_test.go +++ b/testing/spectest/minimal/electra/operations/withdrawals_test.go @@ -7,6 +7,5 @@ import ( ) func TestMinimal_Electra_Operations_Withdrawals(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") operations.RunWithdrawalsTest(t, "minimal") } From 7238848d81c0da24427b57153ed06a0b24efeb71 Mon Sep 17 00:00:00 2001 From: Sammy Rosso <15244892+saolyn@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:33:20 +0200 Subject: [PATCH 04/26] Fix exchange capabilities (#14533) * add proto marshal and unmarshal * changelog * Use strings * fix missing error handling * fix test --------- Co-authored-by: terence tsao --- CHANGELOG.md | 1 + beacon-chain/execution/engine_client.go | 9 +- beacon-chain/execution/engine_client_test.go | 8 +- proto/engine/v1/execution_engine.pb.go | 91 +++----------------- proto/engine/v1/execution_engine.proto | 3 - 5 files changed, 22 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a73e15b33318..79ddbe0d2a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Make committee aware packing the default by deprecating `--enable-committee-aware-packing`. - Moved `ConvertKzgCommitmentToVersionedHash` to the `primitives` package. - reversed the boolean return on `BatchVerifyDepositsSignatures`, from need verification, to all keys successfully verified +- Fix `engine_exchangeCapabilities` implementation. ### Deprecated - `--disable-grpc-gateway` flag is deprecated due to grpc gateway removal. diff --git a/beacon-chain/execution/engine_client.go b/beacon-chain/execution/engine_client.go index 07075ffa7d37..a278d23e30a9 100644 --- a/beacon-chain/execution/engine_client.go +++ b/beacon-chain/execution/engine_client.go @@ -297,13 +297,16 @@ func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) { ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeCapabilities") defer span.End() - result := &pb.ExchangeCapabilities{} + var result []string err := s.rpcClient.CallContext(ctx, &result, ExchangeCapabilities, supportedEngineEndpoints) + if err != nil { + return nil, handleRPCError(err) + } var unsupported []string for _, s1 := range supportedEngineEndpoints { supported := false - for _, s2 := range result.SupportedMethods { + for _, s2 := range result { if s1 == s2 { supported = true break @@ -316,7 +319,7 @@ func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) { if len(unsupported) != 0 { log.Warnf("Please update client, detected the following unsupported engine methods: %s", unsupported) } - return result.SupportedMethods, handleRPCError(err) + return result, handleRPCError(err) } // GetTerminalBlockHash returns the valid terminal block hash based on total difficulty. diff --git a/beacon-chain/execution/engine_client_test.go b/beacon-chain/execution/engine_client_test.go index d0ec2a48feb4..7bee80ebdb9d 100644 --- a/beacon-chain/execution/engine_client_test.go +++ b/beacon-chain/execution/engine_client_test.go @@ -1996,11 +1996,10 @@ func Test_ExchangeCapabilities(t *testing.T) { defer func() { require.NoError(t, r.Body.Close()) }() - exchangeCapabilities := &pb.ExchangeCapabilities{} resp := map[string]interface{}{ "jsonrpc": "2.0", "id": 1, - "result": exchangeCapabilities, + "result": []string{}, } err := json.NewEncoder(w).Encode(resp) require.NoError(t, err) @@ -2029,14 +2028,11 @@ func Test_ExchangeCapabilities(t *testing.T) { defer func() { require.NoError(t, r.Body.Close()) }() - exchangeCapabilities := &pb.ExchangeCapabilities{ - SupportedMethods: []string{"A", "B", "C"}, - } resp := map[string]interface{}{ "jsonrpc": "2.0", "id": 1, - "result": exchangeCapabilities, + "result": []string{"A", "B", "C"}, } err := json.NewEncoder(w).Encode(resp) require.NoError(t, err) diff --git a/proto/engine/v1/execution_engine.pb.go b/proto/engine/v1/execution_engine.pb.go index 8fb7f54a6a35..141fc09baf2d 100755 --- a/proto/engine/v1/execution_engine.pb.go +++ b/proto/engine/v1/execution_engine.pb.go @@ -1697,53 +1697,6 @@ func (x *Blob) GetData() []byte { return nil } -type ExchangeCapabilities struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SupportedMethods []string `protobuf:"bytes,1,rep,name=supported_methods,json=supportedMethods,proto3" json:"supported_methods,omitempty"` -} - -func (x *ExchangeCapabilities) Reset() { - *x = ExchangeCapabilities{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExchangeCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExchangeCapabilities) ProtoMessage() {} - -func (x *ExchangeCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[16] - 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 ExchangeCapabilities.ProtoReflect.Descriptor instead. -func (*ExchangeCapabilities) Descriptor() ([]byte, []int) { - return file_proto_engine_v1_execution_engine_proto_rawDescGZIP(), []int{16} -} - -func (x *ExchangeCapabilities) GetSupportedMethods() []string { - if x != nil { - return x.SupportedMethods - } - return nil -} - var File_proto_engine_v1_execution_engine_proto protoreflect.FileDescriptor var file_proto_engine_v1_execution_engine_proto_rawDesc = []byte{ @@ -2119,22 +2072,17 @@ var file_proto_engine_v1_execution_engine_proto_rawDesc = []byte{ 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x26, 0x0a, 0x04, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a, 0x8a, 0xb5, 0x18, 0x06, 0x31, - 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x43, 0x0a, 0x14, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, - 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, - 0x42, 0x96, 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, 0x14, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 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, + 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x96, 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, 0x14, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 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, } var ( @@ -2150,7 +2098,7 @@ func file_proto_engine_v1_execution_engine_proto_rawDescGZIP() []byte { } var file_proto_engine_v1_execution_engine_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_engine_v1_execution_engine_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_proto_engine_v1_execution_engine_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_proto_engine_v1_execution_engine_proto_goTypes = []interface{}{ (PayloadStatus_Status)(0), // 0: ethereum.engine.v1.PayloadStatus.Status (*ExecutionPayload)(nil), // 1: ethereum.engine.v1.ExecutionPayload @@ -2169,7 +2117,6 @@ var file_proto_engine_v1_execution_engine_proto_goTypes = []interface{}{ (*Withdrawal)(nil), // 14: ethereum.engine.v1.Withdrawal (*BlobsBundle)(nil), // 15: ethereum.engine.v1.BlobsBundle (*Blob)(nil), // 16: ethereum.engine.v1.Blob - (*ExchangeCapabilities)(nil), // 17: ethereum.engine.v1.ExchangeCapabilities } var file_proto_engine_v1_execution_engine_proto_depIdxs = []int32{ 14, // 0: ethereum.engine.v1.ExecutionPayloadCapella.withdrawals:type_name -> ethereum.engine.v1.Withdrawal @@ -2385,18 +2332,6 @@ func file_proto_engine_v1_execution_engine_proto_init() { return nil } } - file_proto_engine_v1_execution_engine_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExchangeCapabilities); 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{ @@ -2404,7 +2339,7 @@ func file_proto_engine_v1_execution_engine_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_engine_v1_execution_engine_proto_rawDesc, NumEnums: 1, - NumMessages: 17, + NumMessages: 16, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/engine/v1/execution_engine.proto b/proto/engine/v1/execution_engine.proto index 050848c0fb48..17ac3b258248 100644 --- a/proto/engine/v1/execution_engine.proto +++ b/proto/engine/v1/execution_engine.proto @@ -224,6 +224,3 @@ message Blob { bytes data = 1 [(ethereum.eth.ext.ssz_size) = "blob.size"]; } -message ExchangeCapabilities { - repeated string supported_methods = 1; -} From dc91c963b9e896ae87f0a6d11f82229dab5dc928 Mon Sep 17 00:00:00 2001 From: Rupam Dey <117000803+rupam-04@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:23:16 +0530 Subject: [PATCH 05/26] feat: (light client)add new consensus types for Electra (#14527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add `LightClientBootstrapElectra` to proto * add `LightClientUpdateElectra` to proto * implement `bootstrapElectra` * add ssz support for `LightClientBootstrapElectra` * remove unused type * update `CHANGELOG.md` * implement `updateElectra` * refactor: remove `CurrentSyncCommitteeBranchElectra()` from `LightClientBootstrap` * remove `NewWrappedHeaderElectra` * Update consensus-types/light-client/bootstrap.go Co-authored-by: RadosÅ‚aw Kapka * Update consensus-types/light-client/update.go Co-authored-by: RadosÅ‚aw Kapka * add `CurrentSyncCommitteeBranchElectra()` to `LightClientBootstrap` * add `NextSyncCommitteeBranchElectra()` to `LightClientUpdate` * revert changes to unrelated pb/ssz files * Revert "revert changes to unrelated pb/ssz files" This reverts commit 5ceaaf5ba6333961f950809f534ca396f3b6f3a2. * more refactors * even more refactors --------- Co-authored-by: RadosÅ‚aw Kapka --- CHANGELOG.md | 1 + consensus-types/interfaces/light_client.go | 7 +- consensus-types/light-client/bootstrap.go | 90 ++- consensus-types/light-client/update.go | 120 +++- proto/prysm/v1alpha1/BUILD.bazel | 2 + proto/prysm/v1alpha1/electra.ssz.go | 408 +++++++++++- proto/prysm/v1alpha1/light_client.pb.go | 702 ++++++++++++++------- proto/prysm/v1alpha1/light_client.proto | 16 + 8 files changed, 1102 insertions(+), 244 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79ddbe0d2a61..99a8acb9be60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Add Bellatrix tests for light client functions. - Add Discovery Rebooter Feature. - Added GetBlockAttestationsV2 endpoint. +- Light client support: Consensus types for Electra ### Changed diff --git a/consensus-types/interfaces/light_client.go b/consensus-types/interfaces/light_client.go index a0719b3a3729..217a086497d4 100644 --- a/consensus-types/interfaces/light_client.go +++ b/consensus-types/interfaces/light_client.go @@ -9,6 +9,7 @@ import ( type LightClientExecutionBranch = [fieldparams.ExecutionBranchDepth][fieldparams.RootLength]byte type LightClientSyncCommitteeBranch = [fieldparams.SyncCommitteeBranchDepth][fieldparams.RootLength]byte +type LightClientSyncCommitteeBranchElectra = [fieldparams.SyncCommitteeBranchDepthElectra][fieldparams.RootLength]byte type LightClientFinalityBranch = [fieldparams.FinalityBranchDepth][fieldparams.RootLength]byte type LightClientHeader interface { @@ -24,7 +25,8 @@ type LightClientBootstrap interface { Version() int Header() LightClientHeader CurrentSyncCommittee() *pb.SyncCommittee - CurrentSyncCommitteeBranch() LightClientSyncCommitteeBranch + CurrentSyncCommitteeBranch() (LightClientSyncCommitteeBranch, error) + CurrentSyncCommitteeBranchElectra() (LightClientSyncCommitteeBranchElectra, error) } type LightClientUpdate interface { @@ -32,7 +34,8 @@ type LightClientUpdate interface { Version() int AttestedHeader() LightClientHeader NextSyncCommittee() *pb.SyncCommittee - NextSyncCommitteeBranch() LightClientSyncCommitteeBranch + NextSyncCommitteeBranch() (LightClientSyncCommitteeBranch, error) + NextSyncCommitteeBranchElectra() (LightClientSyncCommitteeBranchElectra, error) FinalizedHeader() LightClientHeader FinalityBranch() LightClientFinalityBranch SyncAggregate() *pb.SyncAggregate diff --git a/consensus-types/light-client/bootstrap.go b/consensus-types/light-client/bootstrap.go index ec97de605877..33d8b2c78f70 100644 --- a/consensus-types/light-client/bootstrap.go +++ b/consensus-types/light-client/bootstrap.go @@ -22,6 +22,8 @@ func NewWrappedBootstrap(m proto.Message) (interfaces.LightClientBootstrap, erro return NewWrappedBootstrapCapella(t) case *pb.LightClientBootstrapDeneb: return NewWrappedBootstrapDeneb(t) + case *pb.LightClientBootstrapElectra: + return NewWrappedBootstrapElectra(t) default: return nil, fmt.Errorf("cannot construct light client bootstrap from type %T", t) } @@ -83,8 +85,12 @@ func (h *bootstrapAltair) CurrentSyncCommittee() *pb.SyncCommittee { return h.p.CurrentSyncCommittee } -func (h *bootstrapAltair) CurrentSyncCommitteeBranch() interfaces.LightClientSyncCommitteeBranch { - return h.currentSyncCommitteeBranch +func (h *bootstrapAltair) CurrentSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return h.currentSyncCommitteeBranch, nil +} + +func (h *bootstrapAltair) CurrentSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return [6][32]byte{}, consensustypes.ErrNotSupported("CurrentSyncCommitteeBranchElectra", version.Altair) } type bootstrapCapella struct { @@ -143,8 +149,12 @@ func (h *bootstrapCapella) CurrentSyncCommittee() *pb.SyncCommittee { return h.p.CurrentSyncCommittee } -func (h *bootstrapCapella) CurrentSyncCommitteeBranch() interfaces.LightClientSyncCommitteeBranch { - return h.currentSyncCommitteeBranch +func (h *bootstrapCapella) CurrentSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return h.currentSyncCommitteeBranch, nil +} + +func (h *bootstrapCapella) CurrentSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return [6][32]byte{}, consensustypes.ErrNotSupported("CurrentSyncCommitteeBranchElectra", version.Capella) } type bootstrapDeneb struct { @@ -203,6 +213,74 @@ func (h *bootstrapDeneb) CurrentSyncCommittee() *pb.SyncCommittee { return h.p.CurrentSyncCommittee } -func (h *bootstrapDeneb) CurrentSyncCommitteeBranch() interfaces.LightClientSyncCommitteeBranch { - return h.currentSyncCommitteeBranch +func (h *bootstrapDeneb) CurrentSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return h.currentSyncCommitteeBranch, nil +} + +func (h *bootstrapDeneb) CurrentSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return [6][32]byte{}, consensustypes.ErrNotSupported("CurrentSyncCommitteeBranchElectra", version.Deneb) +} + +type bootstrapElectra struct { + p *pb.LightClientBootstrapElectra + header interfaces.LightClientHeader + currentSyncCommitteeBranch interfaces.LightClientSyncCommitteeBranchElectra +} + +var _ interfaces.LightClientBootstrap = &bootstrapElectra{} + +func NewWrappedBootstrapElectra(p *pb.LightClientBootstrapElectra) (interfaces.LightClientBootstrap, error) { + if p == nil { + return nil, consensustypes.ErrNilObjectWrapped + } + header, err := NewWrappedHeaderDeneb(p.Header) + if err != nil { + return nil, err + } + branch, err := createBranch[interfaces.LightClientSyncCommitteeBranchElectra]( + "sync committee", + p.CurrentSyncCommitteeBranch, + fieldparams.SyncCommitteeBranchDepthElectra, + ) + if err != nil { + return nil, err + } + + return &bootstrapElectra{ + p: p, + header: header, + currentSyncCommitteeBranch: branch, + }, nil +} + +func (h *bootstrapElectra) MarshalSSZTo(dst []byte) ([]byte, error) { + return h.p.MarshalSSZTo(dst) +} + +func (h *bootstrapElectra) MarshalSSZ() ([]byte, error) { + return h.p.MarshalSSZ() +} + +func (h *bootstrapElectra) SizeSSZ() int { + return h.p.SizeSSZ() +} + +func (h *bootstrapElectra) Version() int { + return version.Electra +} + +func (h *bootstrapElectra) Header() interfaces.LightClientHeader { + return h.header +} + +func (h *bootstrapElectra) CurrentSyncCommittee() *pb.SyncCommittee { + return h.p.CurrentSyncCommittee +} + +func (h *bootstrapElectra) CurrentSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return [5][32]byte{}, consensustypes.ErrNotSupported("CurrentSyncCommitteeBranch", version.Electra) +} + +func (h *bootstrapElectra) CurrentSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return h.currentSyncCommitteeBranch, nil } diff --git a/consensus-types/light-client/update.go b/consensus-types/light-client/update.go index 00430e393a98..66a3403f1896 100644 --- a/consensus-types/light-client/update.go +++ b/consensus-types/light-client/update.go @@ -100,8 +100,12 @@ func (u *updateAltair) NextSyncCommittee() *pb.SyncCommittee { return u.p.NextSyncCommittee } -func (u *updateAltair) NextSyncCommitteeBranch() interfaces.LightClientSyncCommitteeBranch { - return u.nextSyncCommitteeBranch +func (u *updateAltair) NextSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return u.nextSyncCommitteeBranch, nil +} + +func (u *updateAltair) NextSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return [6][32]byte{}, consensustypes.ErrNotSupported("NextSyncCommitteeBranchElectra", version.Altair) } func (u *updateAltair) FinalizedHeader() interfaces.LightClientHeader { @@ -192,8 +196,12 @@ func (u *updateCapella) NextSyncCommittee() *pb.SyncCommittee { return u.p.NextSyncCommittee } -func (u *updateCapella) NextSyncCommitteeBranch() interfaces.LightClientSyncCommitteeBranch { - return u.nextSyncCommitteeBranch +func (u *updateCapella) NextSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return u.nextSyncCommitteeBranch, nil +} + +func (u *updateCapella) NextSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return [6][32]byte{}, consensustypes.ErrNotSupported("NextSyncCommitteeBranchElectra", version.Capella) } func (u *updateCapella) FinalizedHeader() interfaces.LightClientHeader { @@ -284,8 +292,12 @@ func (u *updateDeneb) NextSyncCommittee() *pb.SyncCommittee { return u.p.NextSyncCommittee } -func (u *updateDeneb) NextSyncCommitteeBranch() interfaces.LightClientSyncCommitteeBranch { - return u.nextSyncCommitteeBranch +func (u *updateDeneb) NextSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return u.nextSyncCommitteeBranch, nil +} + +func (u *updateDeneb) NextSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return [6][32]byte{}, consensustypes.ErrNotSupported("NextSyncCommitteeBranchElectra", version.Deneb) } func (u *updateDeneb) FinalizedHeader() interfaces.LightClientHeader { @@ -303,3 +315,99 @@ func (u *updateDeneb) SyncAggregate() *pb.SyncAggregate { func (u *updateDeneb) SignatureSlot() primitives.Slot { return u.p.SignatureSlot } + +type updateElectra struct { + p *pb.LightClientUpdateElectra + attestedHeader interfaces.LightClientHeader + nextSyncCommitteeBranch interfaces.LightClientSyncCommitteeBranchElectra + finalizedHeader interfaces.LightClientHeader + finalityBranch interfaces.LightClientFinalityBranch +} + +var _ interfaces.LightClientUpdate = &updateElectra{} + +func NewWrappedUpdateElectra(p *pb.LightClientUpdateElectra) (interfaces.LightClientUpdate, error) { + if p == nil { + return nil, consensustypes.ErrNilObjectWrapped + } + attestedHeader, err := NewWrappedHeaderDeneb(p.AttestedHeader) + if err != nil { + return nil, err + } + finalizedHeader, err := NewWrappedHeaderDeneb(p.FinalizedHeader) + if err != nil { + return nil, err + } + scBranch, err := createBranch[interfaces.LightClientSyncCommitteeBranchElectra]( + "sync committee", + p.NextSyncCommitteeBranch, + fieldparams.SyncCommitteeBranchDepthElectra, + ) + if err != nil { + return nil, err + } + finalityBranch, err := createBranch[interfaces.LightClientFinalityBranch]( + "finality", + p.FinalityBranch, + fieldparams.FinalityBranchDepth, + ) + if err != nil { + return nil, err + } + + return &updateElectra{ + p: p, + attestedHeader: attestedHeader, + nextSyncCommitteeBranch: scBranch, + finalizedHeader: finalizedHeader, + finalityBranch: finalityBranch, + }, nil +} + +func (u *updateElectra) MarshalSSZTo(dst []byte) ([]byte, error) { + return u.p.MarshalSSZTo(dst) +} + +func (u *updateElectra) MarshalSSZ() ([]byte, error) { + return u.p.MarshalSSZ() +} + +func (u *updateElectra) SizeSSZ() int { + return u.p.SizeSSZ() +} + +func (u *updateElectra) Version() int { + return version.Electra +} + +func (u *updateElectra) AttestedHeader() interfaces.LightClientHeader { + return u.attestedHeader +} + +func (u *updateElectra) NextSyncCommittee() *pb.SyncCommittee { + return u.p.NextSyncCommittee +} + +func (u *updateElectra) NextSyncCommitteeBranch() (interfaces.LightClientSyncCommitteeBranch, error) { + return [5][32]byte{}, consensustypes.ErrNotSupported("NextSyncCommitteeBranch", version.Electra) +} + +func (u *updateElectra) NextSyncCommitteeBranchElectra() (interfaces.LightClientSyncCommitteeBranchElectra, error) { + return u.nextSyncCommitteeBranch, nil +} + +func (u *updateElectra) FinalizedHeader() interfaces.LightClientHeader { + return u.finalizedHeader +} + +func (u *updateElectra) FinalityBranch() interfaces.LightClientFinalityBranch { + return u.finalityBranch +} + +func (u *updateElectra) SyncAggregate() *pb.SyncAggregate { + return u.p.SyncAggregate +} + +func (u *updateElectra) SignatureSlot() primitives.Slot { + return u.p.SignatureSlot +} diff --git a/proto/prysm/v1alpha1/BUILD.bazel b/proto/prysm/v1alpha1/BUILD.bazel index d7ff67576a7a..e79cd7856734 100644 --- a/proto/prysm/v1alpha1/BUILD.bazel +++ b/proto/prysm/v1alpha1/BUILD.bazel @@ -159,6 +159,8 @@ ssz_electra_objs = [ "BlindedBeaconBlockElectra", "Consolidation", "IndexedAttestationElectra", + "LightClientBootstrapElectra", + "LightClientUpdateElectra", "PendingDeposit", "PendingDeposits", "PendingConsolidation", diff --git a/proto/prysm/v1alpha1/electra.ssz.go b/proto/prysm/v1alpha1/electra.ssz.go index 4088848f7244..772aec1ecb90 100644 --- a/proto/prysm/v1alpha1/electra.ssz.go +++ b/proto/prysm/v1alpha1/electra.ssz.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 3a2dbf56ebf4e81fbf961840a4cd2addac9047b17c12bad04e60879df5b69277 +// Hash: 5ca1c2c4e61b47b1f8185b3e9c477ae280f82e1483b88d4e11fa214452da5117 package eth import ( @@ -4252,3 +4252,409 @@ func (p *PendingConsolidation) HashTreeRootWith(hh *ssz.Hasher) (err error) { hh.Merkleize(indx) return } + +// MarshalSSZ ssz marshals the LightClientBootstrapElectra object +func (l *LightClientBootstrapElectra) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(l) +} + +// MarshalSSZTo ssz marshals the LightClientBootstrapElectra object to a target array +func (l *LightClientBootstrapElectra) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(24820) + + // Offset (0) 'Header' + dst = ssz.WriteOffset(dst, offset) + if l.Header == nil { + l.Header = new(LightClientHeaderDeneb) + } + offset += l.Header.SizeSSZ() + + // Field (1) 'CurrentSyncCommittee' + if l.CurrentSyncCommittee == nil { + l.CurrentSyncCommittee = new(SyncCommittee) + } + if dst, err = l.CurrentSyncCommittee.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'CurrentSyncCommitteeBranch' + if size := len(l.CurrentSyncCommitteeBranch); size != 6 { + err = ssz.ErrVectorLengthFn("--.CurrentSyncCommitteeBranch", size, 6) + return + } + for ii := 0; ii < 6; ii++ { + if size := len(l.CurrentSyncCommitteeBranch[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("--.CurrentSyncCommitteeBranch[ii]", size, 32) + return + } + dst = append(dst, l.CurrentSyncCommitteeBranch[ii]...) + } + + // Field (0) 'Header' + if dst, err = l.Header.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the LightClientBootstrapElectra object +func (l *LightClientBootstrapElectra) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 24820 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Header' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 24820 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'CurrentSyncCommittee' + if l.CurrentSyncCommittee == nil { + l.CurrentSyncCommittee = new(SyncCommittee) + } + if err = l.CurrentSyncCommittee.UnmarshalSSZ(buf[4:24628]); err != nil { + return err + } + + // Field (2) 'CurrentSyncCommitteeBranch' + l.CurrentSyncCommitteeBranch = make([][]byte, 6) + for ii := 0; ii < 6; ii++ { + if cap(l.CurrentSyncCommitteeBranch[ii]) == 0 { + l.CurrentSyncCommitteeBranch[ii] = make([]byte, 0, len(buf[24628:24820][ii*32:(ii+1)*32])) + } + l.CurrentSyncCommitteeBranch[ii] = append(l.CurrentSyncCommitteeBranch[ii], buf[24628:24820][ii*32:(ii+1)*32]...) + } + + // Field (0) 'Header' + { + buf = tail[o0:] + if l.Header == nil { + l.Header = new(LightClientHeaderDeneb) + } + if err = l.Header.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the LightClientBootstrapElectra object +func (l *LightClientBootstrapElectra) SizeSSZ() (size int) { + size = 24820 + + // Field (0) 'Header' + if l.Header == nil { + l.Header = new(LightClientHeaderDeneb) + } + size += l.Header.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the LightClientBootstrapElectra object +func (l *LightClientBootstrapElectra) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(l) +} + +// HashTreeRootWith ssz hashes the LightClientBootstrapElectra object with a hasher +func (l *LightClientBootstrapElectra) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Header' + if err = l.Header.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'CurrentSyncCommittee' + if err = l.CurrentSyncCommittee.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'CurrentSyncCommitteeBranch' + { + if size := len(l.CurrentSyncCommitteeBranch); size != 6 { + err = ssz.ErrVectorLengthFn("--.CurrentSyncCommitteeBranch", size, 6) + return + } + subIndx := hh.Index() + for _, i := range l.CurrentSyncCommitteeBranch { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + hh.Merkleize(subIndx) + } + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the LightClientUpdateElectra object +func (l *LightClientUpdateElectra) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(l) +} + +// MarshalSSZTo ssz marshals the LightClientUpdateElectra object to a target array +func (l *LightClientUpdateElectra) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(25184) + + // Offset (0) 'AttestedHeader' + dst = ssz.WriteOffset(dst, offset) + if l.AttestedHeader == nil { + l.AttestedHeader = new(LightClientHeaderDeneb) + } + offset += l.AttestedHeader.SizeSSZ() + + // Field (1) 'NextSyncCommittee' + if l.NextSyncCommittee == nil { + l.NextSyncCommittee = new(SyncCommittee) + } + if dst, err = l.NextSyncCommittee.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'NextSyncCommitteeBranch' + if size := len(l.NextSyncCommitteeBranch); size != 6 { + err = ssz.ErrVectorLengthFn("--.NextSyncCommitteeBranch", size, 6) + return + } + for ii := 0; ii < 6; ii++ { + if size := len(l.NextSyncCommitteeBranch[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("--.NextSyncCommitteeBranch[ii]", size, 32) + return + } + dst = append(dst, l.NextSyncCommitteeBranch[ii]...) + } + + // Offset (3) 'FinalizedHeader' + dst = ssz.WriteOffset(dst, offset) + if l.FinalizedHeader == nil { + l.FinalizedHeader = new(LightClientHeaderDeneb) + } + offset += l.FinalizedHeader.SizeSSZ() + + // Field (4) 'FinalityBranch' + if size := len(l.FinalityBranch); size != 6 { + err = ssz.ErrVectorLengthFn("--.FinalityBranch", size, 6) + return + } + for ii := 0; ii < 6; ii++ { + if size := len(l.FinalityBranch[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("--.FinalityBranch[ii]", size, 32) + return + } + dst = append(dst, l.FinalityBranch[ii]...) + } + + // Field (5) 'SyncAggregate' + if l.SyncAggregate == nil { + l.SyncAggregate = new(SyncAggregate) + } + if dst, err = l.SyncAggregate.MarshalSSZTo(dst); err != nil { + return + } + + // Field (6) 'SignatureSlot' + dst = ssz.MarshalUint64(dst, uint64(l.SignatureSlot)) + + // Field (0) 'AttestedHeader' + if dst, err = l.AttestedHeader.MarshalSSZTo(dst); err != nil { + return + } + + // Field (3) 'FinalizedHeader' + if dst, err = l.FinalizedHeader.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the LightClientUpdateElectra object +func (l *LightClientUpdateElectra) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 25184 { + return ssz.ErrSize + } + + tail := buf + var o0, o3 uint64 + + // Offset (0) 'AttestedHeader' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 25184 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'NextSyncCommittee' + if l.NextSyncCommittee == nil { + l.NextSyncCommittee = new(SyncCommittee) + } + if err = l.NextSyncCommittee.UnmarshalSSZ(buf[4:24628]); err != nil { + return err + } + + // Field (2) 'NextSyncCommitteeBranch' + l.NextSyncCommitteeBranch = make([][]byte, 6) + for ii := 0; ii < 6; ii++ { + if cap(l.NextSyncCommitteeBranch[ii]) == 0 { + l.NextSyncCommitteeBranch[ii] = make([]byte, 0, len(buf[24628:24820][ii*32:(ii+1)*32])) + } + l.NextSyncCommitteeBranch[ii] = append(l.NextSyncCommitteeBranch[ii], buf[24628:24820][ii*32:(ii+1)*32]...) + } + + // Offset (3) 'FinalizedHeader' + if o3 = ssz.ReadOffset(buf[24820:24824]); o3 > size || o0 > o3 { + return ssz.ErrOffset + } + + // Field (4) 'FinalityBranch' + l.FinalityBranch = make([][]byte, 6) + for ii := 0; ii < 6; ii++ { + if cap(l.FinalityBranch[ii]) == 0 { + l.FinalityBranch[ii] = make([]byte, 0, len(buf[24824:25016][ii*32:(ii+1)*32])) + } + l.FinalityBranch[ii] = append(l.FinalityBranch[ii], buf[24824:25016][ii*32:(ii+1)*32]...) + } + + // Field (5) 'SyncAggregate' + if l.SyncAggregate == nil { + l.SyncAggregate = new(SyncAggregate) + } + if err = l.SyncAggregate.UnmarshalSSZ(buf[25016:25176]); err != nil { + return err + } + + // Field (6) 'SignatureSlot' + l.SignatureSlot = github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot(ssz.UnmarshallUint64(buf[25176:25184])) + + // Field (0) 'AttestedHeader' + { + buf = tail[o0:o3] + if l.AttestedHeader == nil { + l.AttestedHeader = new(LightClientHeaderDeneb) + } + if err = l.AttestedHeader.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (3) 'FinalizedHeader' + { + buf = tail[o3:] + if l.FinalizedHeader == nil { + l.FinalizedHeader = new(LightClientHeaderDeneb) + } + if err = l.FinalizedHeader.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the LightClientUpdateElectra object +func (l *LightClientUpdateElectra) SizeSSZ() (size int) { + size = 25184 + + // Field (0) 'AttestedHeader' + if l.AttestedHeader == nil { + l.AttestedHeader = new(LightClientHeaderDeneb) + } + size += l.AttestedHeader.SizeSSZ() + + // Field (3) 'FinalizedHeader' + if l.FinalizedHeader == nil { + l.FinalizedHeader = new(LightClientHeaderDeneb) + } + size += l.FinalizedHeader.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the LightClientUpdateElectra object +func (l *LightClientUpdateElectra) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(l) +} + +// HashTreeRootWith ssz hashes the LightClientUpdateElectra object with a hasher +func (l *LightClientUpdateElectra) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'AttestedHeader' + if err = l.AttestedHeader.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'NextSyncCommittee' + if err = l.NextSyncCommittee.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'NextSyncCommitteeBranch' + { + if size := len(l.NextSyncCommitteeBranch); size != 6 { + err = ssz.ErrVectorLengthFn("--.NextSyncCommitteeBranch", size, 6) + return + } + subIndx := hh.Index() + for _, i := range l.NextSyncCommitteeBranch { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + hh.Merkleize(subIndx) + } + + // Field (3) 'FinalizedHeader' + if err = l.FinalizedHeader.HashTreeRootWith(hh); err != nil { + return + } + + // Field (4) 'FinalityBranch' + { + if size := len(l.FinalityBranch); size != 6 { + err = ssz.ErrVectorLengthFn("--.FinalityBranch", size, 6) + return + } + subIndx := hh.Index() + for _, i := range l.FinalityBranch { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + hh.Merkleize(subIndx) + } + + // Field (5) 'SyncAggregate' + if err = l.SyncAggregate.HashTreeRootWith(hh); err != nil { + return + } + + // Field (6) 'SignatureSlot' + hh.PutUint64(uint64(l.SignatureSlot)) + + hh.Merkleize(indx) + return +} diff --git a/proto/prysm/v1alpha1/light_client.pb.go b/proto/prysm/v1alpha1/light_client.pb.go index 8e342b43342b..e0bf066767c8 100755 --- a/proto/prysm/v1alpha1/light_client.pb.go +++ b/proto/prysm/v1alpha1/light_client.pb.go @@ -7,14 +7,13 @@ package eth import ( - reflect "reflect" - sync "sync" - github_com_prysmaticlabs_prysm_v5_consensus_types_primitives "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" _ "github.com/prysmaticlabs/prysm/v5/proto/eth/ext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( @@ -386,6 +385,69 @@ func (x *LightClientBootstrapDeneb) GetCurrentSyncCommitteeBranch() [][]byte { return nil } +type LightClientBootstrapElectra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Header *LightClientHeaderDeneb `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` + CurrentSyncCommittee *SyncCommittee `protobuf:"bytes,2,opt,name=current_sync_committee,json=currentSyncCommittee,proto3" json:"current_sync_committee,omitempty"` + CurrentSyncCommitteeBranch [][]byte `protobuf:"bytes,3,rep,name=current_sync_committee_branch,json=currentSyncCommitteeBranch,proto3" json:"current_sync_committee_branch,omitempty" ssz-size:"6,32"` +} + +func (x *LightClientBootstrapElectra) Reset() { + *x = LightClientBootstrapElectra{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LightClientBootstrapElectra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LightClientBootstrapElectra) ProtoMessage() {} + +func (x *LightClientBootstrapElectra) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[6] + 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 LightClientBootstrapElectra.ProtoReflect.Descriptor instead. +func (*LightClientBootstrapElectra) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{6} +} + +func (x *LightClientBootstrapElectra) GetHeader() *LightClientHeaderDeneb { + if x != nil { + return x.Header + } + return nil +} + +func (x *LightClientBootstrapElectra) GetCurrentSyncCommittee() *SyncCommittee { + if x != nil { + return x.CurrentSyncCommittee + } + return nil +} + +func (x *LightClientBootstrapElectra) GetCurrentSyncCommitteeBranch() [][]byte { + if x != nil { + return x.CurrentSyncCommitteeBranch + } + return nil +} + type LightClientUpdateAltair struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -403,7 +465,7 @@ type LightClientUpdateAltair struct { func (x *LightClientUpdateAltair) Reset() { *x = LightClientUpdateAltair{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[6] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -416,7 +478,7 @@ func (x *LightClientUpdateAltair) String() string { func (*LightClientUpdateAltair) ProtoMessage() {} func (x *LightClientUpdateAltair) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[6] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -429,7 +491,7 @@ func (x *LightClientUpdateAltair) ProtoReflect() protoreflect.Message { // Deprecated: Use LightClientUpdateAltair.ProtoReflect.Descriptor instead. func (*LightClientUpdateAltair) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{6} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{7} } func (x *LightClientUpdateAltair) GetAttestedHeader() *LightClientHeaderAltair { @@ -498,7 +560,7 @@ type LightClientUpdateCapella struct { func (x *LightClientUpdateCapella) Reset() { *x = LightClientUpdateCapella{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[7] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -511,7 +573,7 @@ func (x *LightClientUpdateCapella) String() string { func (*LightClientUpdateCapella) ProtoMessage() {} func (x *LightClientUpdateCapella) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[7] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -524,7 +586,7 @@ func (x *LightClientUpdateCapella) ProtoReflect() protoreflect.Message { // Deprecated: Use LightClientUpdateCapella.ProtoReflect.Descriptor instead. func (*LightClientUpdateCapella) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{7} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{8} } func (x *LightClientUpdateCapella) GetAttestedHeader() *LightClientHeaderCapella { @@ -593,7 +655,7 @@ type LightClientUpdateDeneb struct { func (x *LightClientUpdateDeneb) Reset() { *x = LightClientUpdateDeneb{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[8] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -606,7 +668,7 @@ func (x *LightClientUpdateDeneb) String() string { func (*LightClientUpdateDeneb) ProtoMessage() {} func (x *LightClientUpdateDeneb) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[8] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -619,7 +681,7 @@ func (x *LightClientUpdateDeneb) ProtoReflect() protoreflect.Message { // Deprecated: Use LightClientUpdateDeneb.ProtoReflect.Descriptor instead. func (*LightClientUpdateDeneb) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{8} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{9} } func (x *LightClientUpdateDeneb) GetAttestedHeader() *LightClientHeaderDeneb { @@ -671,6 +733,101 @@ func (x *LightClientUpdateDeneb) GetSignatureSlot() github_com_prysmaticlabs_pry return github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot(0) } +type LightClientUpdateElectra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AttestedHeader *LightClientHeaderDeneb `protobuf:"bytes,1,opt,name=attested_header,json=attestedHeader,proto3" json:"attested_header,omitempty"` + NextSyncCommittee *SyncCommittee `protobuf:"bytes,2,opt,name=next_sync_committee,json=nextSyncCommittee,proto3" json:"next_sync_committee,omitempty"` + NextSyncCommitteeBranch [][]byte `protobuf:"bytes,3,rep,name=next_sync_committee_branch,json=nextSyncCommitteeBranch,proto3" json:"next_sync_committee_branch,omitempty" ssz-size:"6,32"` + FinalizedHeader *LightClientHeaderDeneb `protobuf:"bytes,4,opt,name=finalized_header,json=finalizedHeader,proto3" json:"finalized_header,omitempty"` + FinalityBranch [][]byte `protobuf:"bytes,5,rep,name=finality_branch,json=finalityBranch,proto3" json:"finality_branch,omitempty" ssz-size:"6,32"` + SyncAggregate *SyncAggregate `protobuf:"bytes,6,opt,name=sync_aggregate,json=syncAggregate,proto3" json:"sync_aggregate,omitempty"` + SignatureSlot github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot `protobuf:"varint,7,opt,name=signature_slot,json=signatureSlot,proto3" json:"signature_slot,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"` +} + +func (x *LightClientUpdateElectra) Reset() { + *x = LightClientUpdateElectra{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LightClientUpdateElectra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LightClientUpdateElectra) ProtoMessage() {} + +func (x *LightClientUpdateElectra) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[10] + 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 LightClientUpdateElectra.ProtoReflect.Descriptor instead. +func (*LightClientUpdateElectra) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{10} +} + +func (x *LightClientUpdateElectra) GetAttestedHeader() *LightClientHeaderDeneb { + if x != nil { + return x.AttestedHeader + } + return nil +} + +func (x *LightClientUpdateElectra) GetNextSyncCommittee() *SyncCommittee { + if x != nil { + return x.NextSyncCommittee + } + return nil +} + +func (x *LightClientUpdateElectra) GetNextSyncCommitteeBranch() [][]byte { + if x != nil { + return x.NextSyncCommitteeBranch + } + return nil +} + +func (x *LightClientUpdateElectra) GetFinalizedHeader() *LightClientHeaderDeneb { + if x != nil { + return x.FinalizedHeader + } + return nil +} + +func (x *LightClientUpdateElectra) GetFinalityBranch() [][]byte { + if x != nil { + return x.FinalityBranch + } + return nil +} + +func (x *LightClientUpdateElectra) GetSyncAggregate() *SyncAggregate { + if x != nil { + return x.SyncAggregate + } + return nil +} + +func (x *LightClientUpdateElectra) GetSignatureSlot() github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot { + if x != nil { + return x.SignatureSlot + } + return github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot(0) +} + type LightClientFinalityUpdateAltair struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -686,7 +843,7 @@ type LightClientFinalityUpdateAltair struct { func (x *LightClientFinalityUpdateAltair) Reset() { *x = LightClientFinalityUpdateAltair{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[9] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -699,7 +856,7 @@ func (x *LightClientFinalityUpdateAltair) String() string { func (*LightClientFinalityUpdateAltair) ProtoMessage() {} func (x *LightClientFinalityUpdateAltair) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[9] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -712,7 +869,7 @@ func (x *LightClientFinalityUpdateAltair) ProtoReflect() protoreflect.Message { // Deprecated: Use LightClientFinalityUpdateAltair.ProtoReflect.Descriptor instead. func (*LightClientFinalityUpdateAltair) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{9} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{11} } func (x *LightClientFinalityUpdateAltair) GetAttestedHeader() *LightClientHeaderAltair { @@ -765,7 +922,7 @@ type LightClientFinalityUpdateCapella struct { func (x *LightClientFinalityUpdateCapella) Reset() { *x = LightClientFinalityUpdateCapella{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[10] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -778,7 +935,7 @@ func (x *LightClientFinalityUpdateCapella) String() string { func (*LightClientFinalityUpdateCapella) ProtoMessage() {} func (x *LightClientFinalityUpdateCapella) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[10] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -791,7 +948,7 @@ func (x *LightClientFinalityUpdateCapella) ProtoReflect() protoreflect.Message { // Deprecated: Use LightClientFinalityUpdateCapella.ProtoReflect.Descriptor instead. func (*LightClientFinalityUpdateCapella) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{10} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{12} } func (x *LightClientFinalityUpdateCapella) GetAttestedHeader() *LightClientHeaderCapella { @@ -844,7 +1001,7 @@ type LightClientFinalityUpdateDeneb struct { func (x *LightClientFinalityUpdateDeneb) Reset() { *x = LightClientFinalityUpdateDeneb{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[11] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -857,7 +1014,7 @@ func (x *LightClientFinalityUpdateDeneb) String() string { func (*LightClientFinalityUpdateDeneb) ProtoMessage() {} func (x *LightClientFinalityUpdateDeneb) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[11] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -870,7 +1027,7 @@ func (x *LightClientFinalityUpdateDeneb) ProtoReflect() protoreflect.Message { // Deprecated: Use LightClientFinalityUpdateDeneb.ProtoReflect.Descriptor instead. func (*LightClientFinalityUpdateDeneb) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{11} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{13} } func (x *LightClientFinalityUpdateDeneb) GetAttestedHeader() *LightClientHeaderDeneb { @@ -921,7 +1078,7 @@ type LightClientOptimisticUpdateAltair struct { func (x *LightClientOptimisticUpdateAltair) Reset() { *x = LightClientOptimisticUpdateAltair{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[12] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -934,7 +1091,7 @@ func (x *LightClientOptimisticUpdateAltair) String() string { func (*LightClientOptimisticUpdateAltair) ProtoMessage() {} func (x *LightClientOptimisticUpdateAltair) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[12] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -947,7 +1104,7 @@ func (x *LightClientOptimisticUpdateAltair) ProtoReflect() protoreflect.Message // Deprecated: Use LightClientOptimisticUpdateAltair.ProtoReflect.Descriptor instead. func (*LightClientOptimisticUpdateAltair) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{12} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{14} } func (x *LightClientOptimisticUpdateAltair) GetAttestedHeader() *LightClientHeaderAltair { @@ -984,7 +1141,7 @@ type LightClientOptimisticUpdateCapella struct { func (x *LightClientOptimisticUpdateCapella) Reset() { *x = LightClientOptimisticUpdateCapella{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[13] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -997,7 +1154,7 @@ func (x *LightClientOptimisticUpdateCapella) String() string { func (*LightClientOptimisticUpdateCapella) ProtoMessage() {} func (x *LightClientOptimisticUpdateCapella) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[13] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1010,7 +1167,7 @@ func (x *LightClientOptimisticUpdateCapella) ProtoReflect() protoreflect.Message // Deprecated: Use LightClientOptimisticUpdateCapella.ProtoReflect.Descriptor instead. func (*LightClientOptimisticUpdateCapella) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{13} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{15} } func (x *LightClientOptimisticUpdateCapella) GetAttestedHeader() *LightClientHeaderCapella { @@ -1047,7 +1204,7 @@ type LightClientOptimisticUpdateDeneb struct { func (x *LightClientOptimisticUpdateDeneb) Reset() { *x = LightClientOptimisticUpdateDeneb{} if protoimpl.UnsafeEnabled { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[14] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1060,7 +1217,7 @@ func (x *LightClientOptimisticUpdateDeneb) String() string { func (*LightClientOptimisticUpdateDeneb) ProtoMessage() {} func (x *LightClientOptimisticUpdateDeneb) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[14] + mi := &file_proto_prysm_v1alpha1_light_client_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1073,7 +1230,7 @@ func (x *LightClientOptimisticUpdateDeneb) ProtoReflect() protoreflect.Message { // Deprecated: Use LightClientOptimisticUpdateDeneb.ProtoReflect.Descriptor instead. func (*LightClientOptimisticUpdateDeneb) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{14} + return file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP(), []int{16} } func (x *LightClientOptimisticUpdateDeneb) GetAttestedHeader() *LightClientHeaderDeneb { @@ -1198,6 +1355,23 @@ var file_proto_prysm_v1alpha1_light_client_proto_rawDesc = []byte{ 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x35, 0x2c, 0x33, 0x32, 0x52, 0x1a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x74, 0x65, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x22, 0x8d, 0x02, 0x0a, 0x1b, 0x4c, + 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, + 0x72, 0x61, 0x70, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x12, 0x5a, 0x0a, 0x16, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x79, 0x6e, + 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x52, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x12, 0x4b, 0x0a, + 0x1d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x36, 0x2c, 0x33, 0x32, 0x52, 0x1a, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x22, 0xd8, 0x04, 0x0a, 0x17, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x57, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, @@ -1311,161 +1485,199 @@ var file_proto_prysm_v1alpha1_light_client_proto_rawDesc = []byte{ 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 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, 0x0d, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xc3, 0x03, 0x0a, 0x1f, - 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, - 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, - 0x57, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x59, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xd7, 0x04, 0x0a, 0x18, + 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x56, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x5f, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, + 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x12, 0x54, 0x0a, 0x13, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x52, 0x11, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x12, 0x45, 0x0a, 0x1a, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, + 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x62, 0x72, + 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, + 0x36, 0x2c, 0x33, 0x32, 0x52, 0x17, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x58, 0x0a, + 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x18, 0x04, 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, + 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, + 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x36, 0x2c, 0x33, 0x32, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, + 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xc3, 0x03, 0x0a, 0x1f, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x57, 0x0a, 0x0f, 0x61, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x6c, 0x74, 0x61, - 0x69, 0x72, 0x52, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, - 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, - 0x18, 0x04, 0x36, 0x2c, 0x33, 0x32, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, + 0x69, 0x72, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x59, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x0f, 0x66, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, + 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x36, 0x2c, 0x33, 0x32, + 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, + 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, + 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xc6, 0x03, 0x0a, 0x20, + 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, + 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x18, 0x01, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x5a, 0x0a, 0x10, 0x66, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, + 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, 0x4c, 0x69, 0x67, + 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, + 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x74, 0x79, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, + 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x36, 0x2c, 0x33, 0x32, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, + 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, + 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xc0, 0x03, 0x0a, 0x1e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x56, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x65, 0x64, 0x5f, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, + 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, + 0x58, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x18, 0x02, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x0f, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x36, 0x2c, 0x33, 0x32, 0x52, 0x0e, 0x66, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x4b, 0x0a, 0x0e, + 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, + 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xb7, 0x02, 0x0a, 0x21, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, + 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x57, 0x0a, + 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, + 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, + 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, - 0x74, 0x22, 0xc6, 0x03, 0x0a, 0x20, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, - 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x12, 0x5a, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x0f, 0x66, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x0f, - 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x36, 0x2c, 0x33, 0x32, 0x52, - 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, - 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, - 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xc0, 0x03, 0x0a, 0x1e, 0x4c, - 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x56, 0x0a, - 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 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, 0x4c, - 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x0f, - 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, - 0x31, 0x0a, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x36, 0x2c, - 0x33, 0x32, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, + 0x74, 0x22, 0xb9, 0x02, 0x0a, 0x22, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, + 0x6c, 0x61, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xb7, 0x02, - 0x0a, 0x21, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, - 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x74, - 0x61, 0x69, 0x72, 0x12, 0x57, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, - 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x0e, 0x61, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0e, - 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, - 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xb9, 0x02, 0x0a, 0x22, 0x4c, 0x69, 0x67, 0x68, - 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, - 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x58, - 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x18, 0x01, 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, - 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, - 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, - 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, - 0x6c, 0x6f, 0x74, 0x22, 0xb5, 0x02, 0x0a, 0x20, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x56, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x65, 0x64, 0x5f, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, - 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, - 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, - 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x42, 0x99, 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, 0x10, 0x4c, 0x69, 0x67, 0x68, 0x74, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x38, 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, 0x74, 0x68, 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, 0x56, 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, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0xb5, 0x02, + 0x0a, 0x20, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, + 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6e, + 0x65, 0x62, 0x12, 0x56, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 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, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x0e, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, + 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x45, 0x82, 0xb5, 0x18, 0x41, 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, 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, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x42, 0x99, 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, 0x10, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x38, 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, 0x74, 0x68, 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, 0x56, 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 ( @@ -1480,7 +1692,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_rawDescGZIP() []byte { return file_proto_prysm_v1alpha1_light_client_proto_rawDescData } -var file_proto_prysm_v1alpha1_light_client_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_proto_prysm_v1alpha1_light_client_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_proto_prysm_v1alpha1_light_client_proto_goTypes = []interface{}{ (*LightClientHeaderAltair)(nil), // 0: ethereum.eth.v1alpha1.LightClientHeaderAltair (*LightClientHeaderCapella)(nil), // 1: ethereum.eth.v1alpha1.LightClientHeaderCapella @@ -1488,65 +1700,73 @@ var file_proto_prysm_v1alpha1_light_client_proto_goTypes = []interface{}{ (*LightClientBootstrapAltair)(nil), // 3: ethereum.eth.v1alpha1.LightClientBootstrapAltair (*LightClientBootstrapCapella)(nil), // 4: ethereum.eth.v1alpha1.LightClientBootstrapCapella (*LightClientBootstrapDeneb)(nil), // 5: ethereum.eth.v1alpha1.LightClientBootstrapDeneb - (*LightClientUpdateAltair)(nil), // 6: ethereum.eth.v1alpha1.LightClientUpdateAltair - (*LightClientUpdateCapella)(nil), // 7: ethereum.eth.v1alpha1.LightClientUpdateCapella - (*LightClientUpdateDeneb)(nil), // 8: ethereum.eth.v1alpha1.LightClientUpdateDeneb - (*LightClientFinalityUpdateAltair)(nil), // 9: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair - (*LightClientFinalityUpdateCapella)(nil), // 10: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella - (*LightClientFinalityUpdateDeneb)(nil), // 11: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb - (*LightClientOptimisticUpdateAltair)(nil), // 12: ethereum.eth.v1alpha1.LightClientOptimisticUpdateAltair - (*LightClientOptimisticUpdateCapella)(nil), // 13: ethereum.eth.v1alpha1.LightClientOptimisticUpdateCapella - (*LightClientOptimisticUpdateDeneb)(nil), // 14: ethereum.eth.v1alpha1.LightClientOptimisticUpdateDeneb - (*BeaconBlockHeader)(nil), // 15: ethereum.eth.v1alpha1.BeaconBlockHeader - (*v1.ExecutionPayloadHeaderCapella)(nil), // 16: ethereum.engine.v1.ExecutionPayloadHeaderCapella - (*v1.ExecutionPayloadHeaderDeneb)(nil), // 17: ethereum.engine.v1.ExecutionPayloadHeaderDeneb - (*SyncCommittee)(nil), // 18: ethereum.eth.v1alpha1.SyncCommittee - (*SyncAggregate)(nil), // 19: ethereum.eth.v1alpha1.SyncAggregate + (*LightClientBootstrapElectra)(nil), // 6: ethereum.eth.v1alpha1.LightClientBootstrapElectra + (*LightClientUpdateAltair)(nil), // 7: ethereum.eth.v1alpha1.LightClientUpdateAltair + (*LightClientUpdateCapella)(nil), // 8: ethereum.eth.v1alpha1.LightClientUpdateCapella + (*LightClientUpdateDeneb)(nil), // 9: ethereum.eth.v1alpha1.LightClientUpdateDeneb + (*LightClientUpdateElectra)(nil), // 10: ethereum.eth.v1alpha1.LightClientUpdateElectra + (*LightClientFinalityUpdateAltair)(nil), // 11: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair + (*LightClientFinalityUpdateCapella)(nil), // 12: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella + (*LightClientFinalityUpdateDeneb)(nil), // 13: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb + (*LightClientOptimisticUpdateAltair)(nil), // 14: ethereum.eth.v1alpha1.LightClientOptimisticUpdateAltair + (*LightClientOptimisticUpdateCapella)(nil), // 15: ethereum.eth.v1alpha1.LightClientOptimisticUpdateCapella + (*LightClientOptimisticUpdateDeneb)(nil), // 16: ethereum.eth.v1alpha1.LightClientOptimisticUpdateDeneb + (*BeaconBlockHeader)(nil), // 17: ethereum.eth.v1alpha1.BeaconBlockHeader + (*v1.ExecutionPayloadHeaderCapella)(nil), // 18: ethereum.engine.v1.ExecutionPayloadHeaderCapella + (*v1.ExecutionPayloadHeaderDeneb)(nil), // 19: ethereum.engine.v1.ExecutionPayloadHeaderDeneb + (*SyncCommittee)(nil), // 20: ethereum.eth.v1alpha1.SyncCommittee + (*SyncAggregate)(nil), // 21: ethereum.eth.v1alpha1.SyncAggregate } var file_proto_prysm_v1alpha1_light_client_proto_depIdxs = []int32{ - 15, // 0: ethereum.eth.v1alpha1.LightClientHeaderAltair.beacon:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader - 15, // 1: ethereum.eth.v1alpha1.LightClientHeaderCapella.beacon:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader - 16, // 2: ethereum.eth.v1alpha1.LightClientHeaderCapella.execution:type_name -> ethereum.engine.v1.ExecutionPayloadHeaderCapella - 15, // 3: ethereum.eth.v1alpha1.LightClientHeaderDeneb.beacon:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader - 17, // 4: ethereum.eth.v1alpha1.LightClientHeaderDeneb.execution:type_name -> ethereum.engine.v1.ExecutionPayloadHeaderDeneb + 17, // 0: ethereum.eth.v1alpha1.LightClientHeaderAltair.beacon:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader + 17, // 1: ethereum.eth.v1alpha1.LightClientHeaderCapella.beacon:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader + 18, // 2: ethereum.eth.v1alpha1.LightClientHeaderCapella.execution:type_name -> ethereum.engine.v1.ExecutionPayloadHeaderCapella + 17, // 3: ethereum.eth.v1alpha1.LightClientHeaderDeneb.beacon:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader + 19, // 4: ethereum.eth.v1alpha1.LightClientHeaderDeneb.execution:type_name -> ethereum.engine.v1.ExecutionPayloadHeaderDeneb 0, // 5: ethereum.eth.v1alpha1.LightClientBootstrapAltair.header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair - 18, // 6: ethereum.eth.v1alpha1.LightClientBootstrapAltair.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 20, // 6: ethereum.eth.v1alpha1.LightClientBootstrapAltair.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee 1, // 7: ethereum.eth.v1alpha1.LightClientBootstrapCapella.header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella - 18, // 8: ethereum.eth.v1alpha1.LightClientBootstrapCapella.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 20, // 8: ethereum.eth.v1alpha1.LightClientBootstrapCapella.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee 2, // 9: ethereum.eth.v1alpha1.LightClientBootstrapDeneb.header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb - 18, // 10: ethereum.eth.v1alpha1.LightClientBootstrapDeneb.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee - 0, // 11: ethereum.eth.v1alpha1.LightClientUpdateAltair.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair - 18, // 12: ethereum.eth.v1alpha1.LightClientUpdateAltair.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee - 0, // 13: ethereum.eth.v1alpha1.LightClientUpdateAltair.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair - 19, // 14: ethereum.eth.v1alpha1.LightClientUpdateAltair.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 1, // 15: ethereum.eth.v1alpha1.LightClientUpdateCapella.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella - 18, // 16: ethereum.eth.v1alpha1.LightClientUpdateCapella.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee - 1, // 17: ethereum.eth.v1alpha1.LightClientUpdateCapella.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella - 19, // 18: ethereum.eth.v1alpha1.LightClientUpdateCapella.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 2, // 19: ethereum.eth.v1alpha1.LightClientUpdateDeneb.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb - 18, // 20: ethereum.eth.v1alpha1.LightClientUpdateDeneb.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee - 2, // 21: ethereum.eth.v1alpha1.LightClientUpdateDeneb.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb - 19, // 22: ethereum.eth.v1alpha1.LightClientUpdateDeneb.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 0, // 23: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair - 0, // 24: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair - 19, // 25: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 1, // 26: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella - 1, // 27: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella - 19, // 28: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 2, // 29: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb - 2, // 30: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb - 19, // 31: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 0, // 32: ethereum.eth.v1alpha1.LightClientOptimisticUpdateAltair.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair - 19, // 33: ethereum.eth.v1alpha1.LightClientOptimisticUpdateAltair.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 1, // 34: ethereum.eth.v1alpha1.LightClientOptimisticUpdateCapella.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella - 19, // 35: ethereum.eth.v1alpha1.LightClientOptimisticUpdateCapella.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 2, // 36: ethereum.eth.v1alpha1.LightClientOptimisticUpdateDeneb.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb - 19, // 37: ethereum.eth.v1alpha1.LightClientOptimisticUpdateDeneb.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 38, // [38:38] is the sub-list for method output_type - 38, // [38:38] is the sub-list for method input_type - 38, // [38:38] is the sub-list for extension type_name - 38, // [38:38] is the sub-list for extension extendee - 0, // [0:38] is the sub-list for field type_name + 20, // 10: ethereum.eth.v1alpha1.LightClientBootstrapDeneb.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 2, // 11: ethereum.eth.v1alpha1.LightClientBootstrapElectra.header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 20, // 12: ethereum.eth.v1alpha1.LightClientBootstrapElectra.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 0, // 13: ethereum.eth.v1alpha1.LightClientUpdateAltair.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair + 20, // 14: ethereum.eth.v1alpha1.LightClientUpdateAltair.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 0, // 15: ethereum.eth.v1alpha1.LightClientUpdateAltair.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair + 21, // 16: ethereum.eth.v1alpha1.LightClientUpdateAltair.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 1, // 17: ethereum.eth.v1alpha1.LightClientUpdateCapella.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella + 20, // 18: ethereum.eth.v1alpha1.LightClientUpdateCapella.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 1, // 19: ethereum.eth.v1alpha1.LightClientUpdateCapella.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella + 21, // 20: ethereum.eth.v1alpha1.LightClientUpdateCapella.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 2, // 21: ethereum.eth.v1alpha1.LightClientUpdateDeneb.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 20, // 22: ethereum.eth.v1alpha1.LightClientUpdateDeneb.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 2, // 23: ethereum.eth.v1alpha1.LightClientUpdateDeneb.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 21, // 24: ethereum.eth.v1alpha1.LightClientUpdateDeneb.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 2, // 25: ethereum.eth.v1alpha1.LightClientUpdateElectra.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 20, // 26: ethereum.eth.v1alpha1.LightClientUpdateElectra.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 2, // 27: ethereum.eth.v1alpha1.LightClientUpdateElectra.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 21, // 28: ethereum.eth.v1alpha1.LightClientUpdateElectra.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 0, // 29: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair + 0, // 30: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair + 21, // 31: ethereum.eth.v1alpha1.LightClientFinalityUpdateAltair.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 1, // 32: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella + 1, // 33: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella + 21, // 34: ethereum.eth.v1alpha1.LightClientFinalityUpdateCapella.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 2, // 35: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 2, // 36: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb.finalized_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 21, // 37: ethereum.eth.v1alpha1.LightClientFinalityUpdateDeneb.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 0, // 38: ethereum.eth.v1alpha1.LightClientOptimisticUpdateAltair.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderAltair + 21, // 39: ethereum.eth.v1alpha1.LightClientOptimisticUpdateAltair.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 1, // 40: ethereum.eth.v1alpha1.LightClientOptimisticUpdateCapella.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderCapella + 21, // 41: ethereum.eth.v1alpha1.LightClientOptimisticUpdateCapella.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 2, // 42: ethereum.eth.v1alpha1.LightClientOptimisticUpdateDeneb.attested_header:type_name -> ethereum.eth.v1alpha1.LightClientHeaderDeneb + 21, // 43: ethereum.eth.v1alpha1.LightClientOptimisticUpdateDeneb.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 44, // [44:44] is the sub-list for method output_type + 44, // [44:44] is the sub-list for method input_type + 44, // [44:44] is the sub-list for extension type_name + 44, // [44:44] is the sub-list for extension extendee + 0, // [0:44] is the sub-list for field type_name } func init() { file_proto_prysm_v1alpha1_light_client_proto_init() } @@ -1630,7 +1850,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientUpdateAltair); i { + switch v := v.(*LightClientBootstrapElectra); i { case 0: return &v.state case 1: @@ -1642,7 +1862,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientUpdateCapella); i { + switch v := v.(*LightClientUpdateAltair); i { case 0: return &v.state case 1: @@ -1654,7 +1874,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientUpdateDeneb); i { + switch v := v.(*LightClientUpdateCapella); i { case 0: return &v.state case 1: @@ -1666,7 +1886,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientFinalityUpdateAltair); i { + switch v := v.(*LightClientUpdateDeneb); i { case 0: return &v.state case 1: @@ -1678,7 +1898,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientFinalityUpdateCapella); i { + switch v := v.(*LightClientUpdateElectra); i { case 0: return &v.state case 1: @@ -1690,7 +1910,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientFinalityUpdateDeneb); i { + switch v := v.(*LightClientFinalityUpdateAltair); i { case 0: return &v.state case 1: @@ -1702,7 +1922,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientOptimisticUpdateAltair); i { + switch v := v.(*LightClientFinalityUpdateCapella); i { case 0: return &v.state case 1: @@ -1714,7 +1934,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightClientOptimisticUpdateCapella); i { + switch v := v.(*LightClientFinalityUpdateDeneb); i { case 0: return &v.state case 1: @@ -1726,6 +1946,30 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { } } file_proto_prysm_v1alpha1_light_client_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LightClientOptimisticUpdateAltair); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_prysm_v1alpha1_light_client_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LightClientOptimisticUpdateCapella); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_prysm_v1alpha1_light_client_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LightClientOptimisticUpdateDeneb); i { case 0: return &v.state @@ -1744,7 +1988,7 @@ func file_proto_prysm_v1alpha1_light_client_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_prysm_v1alpha1_light_client_proto_rawDesc, NumEnums: 0, - NumMessages: 15, + NumMessages: 17, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/prysm/v1alpha1/light_client.proto b/proto/prysm/v1alpha1/light_client.proto index 6e3ca511ba60..52b5e967c53c 100644 --- a/proto/prysm/v1alpha1/light_client.proto +++ b/proto/prysm/v1alpha1/light_client.proto @@ -61,6 +61,12 @@ message LightClientBootstrapDeneb { repeated bytes current_sync_committee_branch = 3 [(ethereum.eth.ext.ssz_size) = "5,32"]; } +message LightClientBootstrapElectra { + LightClientHeaderDeneb header = 1; + SyncCommittee current_sync_committee = 2; + repeated bytes current_sync_committee_branch = 3 [(ethereum.eth.ext.ssz_size) = "6,32"]; +} + message LightClientUpdateAltair { LightClientHeaderAltair attested_header = 1; SyncCommittee next_sync_committee = 2; @@ -91,6 +97,16 @@ message LightClientUpdateDeneb { uint64 signature_slot = 7 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; } +message LightClientUpdateElectra { + LightClientHeaderDeneb attested_header = 1; + SyncCommittee next_sync_committee = 2; + repeated bytes next_sync_committee_branch = 3 [(ethereum.eth.ext.ssz_size) = "6,32"]; + LightClientHeaderDeneb finalized_header = 4; + repeated bytes finality_branch = 5 [(ethereum.eth.ext.ssz_size) = "6,32"]; + SyncAggregate sync_aggregate = 6; + uint64 signature_slot = 7 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; +} + message LightClientFinalityUpdateAltair { LightClientHeaderAltair attested_header = 1; LightClientHeaderAltair finalized_header = 2; From f307a369a58e9d62cec75f4e9108f24eb1ded918 Mon Sep 17 00:00:00 2001 From: Sammy Rosso <15244892+saolyn@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:27:22 +0200 Subject: [PATCH 06/26] Add POST `/eth/v2/beacon/pool/attester_slashings` (#14480) * add endpoint * changelog * add required version header * fix return values * broken test * fix test * linter * Fix * Proto update * fix test * fix test * Radek' review * remove duplicate tests --- CHANGELOG.md | 1 + beacon-chain/rpc/endpoints.go | 14 +- beacon-chain/rpc/endpoints_test.go | 1 + beacon-chain/rpc/eth/beacon/handlers_pool.go | 73 +- .../rpc/eth/beacon/handlers_pool_test.go | 681 +++++++++--------- testing/mock/beacon_service_mock.go | 4 +- 6 files changed, 410 insertions(+), 364 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a8acb9be60..4738828c4bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Add Discovery Rebooter Feature. - Added GetBlockAttestationsV2 endpoint. - Light client support: Consensus types for Electra +- Added SubmitPoolAttesterSlashingV2 endpoint. ### Changed diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 09358123e6f5..ac51e8b25865 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -690,12 +690,22 @@ func (s *Service) beaconEndpoints( }, { template: "/eth/v1/beacon/pool/attester_slashings", - name: namespace + ".SubmitAttesterSlashing", + name: namespace + ".SubmitAttesterSlashings", middleware: []middleware.Middleware{ middleware.ContentTypeHandler([]string{api.JsonMediaType}), middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), }, - handler: server.SubmitAttesterSlashing, + handler: server.SubmitAttesterSlashings, + methods: []string{http.MethodPost}, + }, + { + template: "/eth/v2/beacon/pool/attester_slashings", + name: namespace + ".SubmitAttesterSlashingsV2", + middleware: []middleware.Middleware{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitAttesterSlashingsV2, methods: []string{http.MethodPost}, }, { diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index b2f286cb5fb0..185182ff49c9 100644 --- a/beacon-chain/rpc/endpoints_test.go +++ b/beacon-chain/rpc/endpoints_test.go @@ -42,6 +42,7 @@ func Test_endpoints(t *testing.T) { "/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet}, "/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost}, "/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost}, + "/eth/v2/beacon/pool/attester_slashings": {http.MethodPost}, "/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost}, "/eth/v1/beacon/pool/sync_committees": {http.MethodPost}, "/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost}, diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 9af9a0320f85..0d92b94b0bb3 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server" "github.com/prysmaticlabs/prysm/v5/api/server/structs" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" @@ -482,10 +483,10 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) } -// SubmitAttesterSlashing submits an attester slashing object to node's pool and +// SubmitAttesterSlashings submits an attester slashing object to node's pool and // if passes validation node MUST broadcast it to network. -func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request) { - ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashing") +func (s *Server) SubmitAttesterSlashings(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashings") defer span.End() var req structs.AttesterSlashing @@ -504,16 +505,80 @@ func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request) httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) return } + s.submitAttesterSlashing(w, ctx, slashing) +} + +// SubmitAttesterSlashingsV2 submits an attester slashing object to node's pool and +// if passes validation node MUST broadcast it to network. +func (s *Server) SubmitAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashingsV2") + defer span.End() + + versionHeader := r.Header.Get(api.VersionHeader) + if versionHeader == "" { + httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest) + } + v, err := version.FromString(versionHeader) + if err != nil { + httputil.HandleError(w, "Invalid version: "+err.Error(), http.StatusBadRequest) + return + } + + if v >= version.Electra { + var req structs.AttesterSlashingElectra + err := json.NewDecoder(r.Body).Decode(&req) + switch { + case errors.Is(err, io.EOF): + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + return + case err != nil: + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + return + } + + slashing, err := req.ToConsensus() + if err != nil { + httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) + return + } + s.submitAttesterSlashing(w, ctx, slashing) + } else { + var req structs.AttesterSlashing + err := json.NewDecoder(r.Body).Decode(&req) + switch { + case errors.Is(err, io.EOF): + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + return + case err != nil: + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + return + } + + slashing, err := req.ToConsensus() + if err != nil { + httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) + return + } + s.submitAttesterSlashing(w, ctx, slashing) + } +} + +func (s *Server) submitAttesterSlashing( + w http.ResponseWriter, + ctx context.Context, + slashing eth.AttSlashing, +) { headState, err := s.ChainInfoFetcher.HeadState(ctx) if err != nil { httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) return } - headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slashing.Attestation_1.Data.Slot) + headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slashing.FirstAttestation().GetData().Slot) if err != nil { httputil.HandleError(w, "Could not process slots: "+err.Error(), http.StatusInternalServerError) return } + err = blocks.VerifyAttesterSlashing(ctx, headState, slashing) if err != nil { httputil.HandleError(w, "Invalid attester slashing: "+err.Error(), http.StatusBadRequest) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index d2ccfbfde25c..593eb8a3d854 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server" "github.com/prysmaticlabs/prysm/v5/api/server/structs" blockchainmock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing" @@ -37,6 +38,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/encoding/ssz" "github.com/prysmaticlabs/prysm/v5/network/httputil" ethpbv1alpha1 "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" @@ -1142,383 +1144,350 @@ func TestGetProposerSlashings(t *testing.T) { assert.Equal(t, 2, len(resp.Data)) } -func TestSubmitAttesterSlashing_Ok(t *testing.T) { +func TestSubmitAttesterSlashings(t *testing.T) { ctx := context.Background() transition.SkipSlotCache.Disable() defer transition.SkipSlotCache.Enable() - _, keys, err := util.DeterministicDepositsAndKeys(1) - require.NoError(t, err) - validator := ðpbv1alpha1.Validator{ - PublicKey: keys[0].PublicKey().Marshal(), + attestationData1 := ðpbv1alpha1.AttestationData{ + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), + }, + } + attestationData2 := ðpbv1alpha1.AttestationData{ + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, } - bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error { - state.Validators = []*ethpbv1alpha1.Validator{validator} - return nil - }) - require.NoError(t, err) - slashing := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + t.Run("V1", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + attestationData1.Slot = 1 + attestationData2.Slot = 1 + slashing := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot1"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), }, - }, - Signature: make([]byte, 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + } + + _, keys, err := util.DeterministicDepositsAndKeys(1) + require.NoError(t, err) + validator := ðpbv1alpha1.Validator{ + PublicKey: keys[0].PublicKey().Marshal(), + } + + bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error { + state.Validators = []*ethpbv1alpha1.Validator{validator} + return nil + }) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(bs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + chainmock := &blockchainmock.ChainService{State: bs} + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashings(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) + assert.Equal(t, true, ok) + }) + t.Run("accross-fork", func(t *testing.T) { + attestationData1.Slot = params.BeaconConfig().SlotsPerEpoch + attestationData2.Slot = params.BeaconConfig().SlotsPerEpoch + slashing := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), }, - }, - Signature: make([]byte, 96), - }, - } - - for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { - sb, err := signing.ComputeDomainAndSign(bs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - att.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashing(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) - assert.Equal(t, true, ok) -} - -func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 1 - params.OverrideBeaconConfig(config) - - bs, keys := util.DeterministicGenesisState(t, 1) - - slashing := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: params.BeaconConfig().SlotsPerEpoch, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + } + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 1 + params.OverrideBeaconConfig(config) + + bs, keys := util.DeterministicGenesisState(t, 1) + newBs := bs.Copy() + newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + broadcaster := &p2pMock.MockBroadcaster{} + chainmock := &blockchainmock.ChainService{State: bs} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashings(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) + assert.Equal(t, true, ok) + }) + t.Run("invalid-slashing", func(t *testing.T) { + bs, err := util.NewBeaconState() + require.NoError(t, err) + + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + } + + var body bytes.Buffer + _, err = body.WriteString(invalidAttesterSlashing) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashings(writer, request) + require.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.StringContains(t, "Invalid attester slashing", e.Message) + }) + }) + t.Run("V2", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + attestationData1.Slot = 1 + attestationData2.Slot = 1 + electraSlashing := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot1"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), }, - }, - Signature: make([]byte, 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: params.BeaconConfig().SlotsPerEpoch, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + } + + _, keys, err := util.DeterministicDepositsAndKeys(1) + require.NoError(t, err) + validator := ðpbv1alpha1.Validator{ + PublicKey: keys[0].PublicKey().Marshal(), + } + + ebs, err := util.NewBeaconStateElectra(func(state *ethpbv1alpha1.BeaconStateElectra) error { + state.Validators = []*ethpbv1alpha1.Validator{validator} + return nil + }) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{electraSlashing.Attestation_1, electraSlashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(ebs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + chainmock := &blockchainmock.ChainService{State: ebs} + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{electraSlashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_electras", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, ebs, true) + require.Equal(t, 1, len(pendingSlashings)) + require.Equal(t, 1, broadcaster.NumMessages()) + assert.DeepEqual(t, electraSlashing, pendingSlashings[0]) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) + assert.Equal(t, true, ok) + }) + t.Run("accross-fork", func(t *testing.T) { + attestationData1.Slot = params.BeaconConfig().SlotsPerEpoch + attestationData2.Slot = params.BeaconConfig().SlotsPerEpoch + slashing := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), }, - }, - Signature: make([]byte, 96), - }, - } - - newBs := bs.Copy() - newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) - require.NoError(t, err) - - for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { - sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - att.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashing(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) - assert.Equal(t, true, ok) -} - -func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) { - bs, err := util.NewBeaconState() - require.NoError(t, err) - - broadcaster := &p2pMock.MockBroadcaster{} - s := &Server{ - ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - } - - var body bytes.Buffer - _, err = body.WriteString(invalidAttesterSlashing) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashing(writer, request) - require.Equal(t, http.StatusBadRequest, writer.Code) - e := &httputil.DefaultJsonError{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) - assert.Equal(t, http.StatusBadRequest, e.Code) - assert.StringContains(t, "Invalid attester slashing", e.Message) -} - -func TestSubmitProposerSlashing_Ok(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - _, keys, err := util.DeterministicDepositsAndKeys(1) - require.NoError(t, err) - validator := ðpbv1alpha1.Validator{ - PublicKey: keys[0].PublicKey().Marshal(), - WithdrawableEpoch: primitives.Epoch(1), - } - bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error { - state.Validators = []*ethpbv1alpha1.Validator{validator} - return nil + } + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 1 + params.OverrideBeaconConfig(config) + + bs, keys := util.DeterministicGenesisState(t, 1) + newBs := bs.Copy() + newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + broadcaster := &p2pMock.MockBroadcaster{} + chainmock := &blockchainmock.ChainService{State: bs} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) + assert.Equal(t, true, ok) + }) }) - require.NoError(t, err) - - slashing := ðpbv1alpha1.ProposerSlashing{ - Header_1: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot1"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot1"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot1"), 32), - }, - Signature: make([]byte, 96), - }, - Header_2: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot2"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot2"), 32), - }, - Signature: make([]byte, 96), - }, - } - - for _, h := range []*ethpbv1alpha1.SignedBeaconBlockHeader{slashing.Header_1, slashing.Header_2} { - sb, err := signing.ComputeDomainAndSign( - bs, - slots.ToEpoch(h.Header.Slot), - h.Header, - params.BeaconConfig().DomainBeaconProposer, - keys[0], - ) + t.Run("invalid-slashing", func(t *testing.T) { + bs, err := util.NewBeaconStateElectra() require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - h.Signature = sig.Marshal() - } - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/proposer_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitProposerSlashing(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing) - assert.Equal(t, true, ok) -} - -func TestSubmitProposerSlashing_AcrossFork(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 1 - params.OverrideBeaconConfig(config) - - bs, keys := util.DeterministicGenesisState(t, 1) - - slashing := ðpbv1alpha1.ProposerSlashing{ - Header_1: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: params.BeaconConfig().SlotsPerEpoch, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot1"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot1"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot1"), 32), - }, - Signature: make([]byte, 96), - }, - Header_2: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: params.BeaconConfig().SlotsPerEpoch, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot2"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot2"), 32), - }, - Signature: make([]byte, 96), - }, - } - - newBs := bs.Copy() - newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) - require.NoError(t, err) + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + } - for _, h := range []*ethpbv1alpha1.SignedBeaconBlockHeader{slashing.Header_1, slashing.Header_2} { - sb, err := signing.ComputeDomainAndSign( - newBs, - slots.ToEpoch(h.Header.Slot), - h.Header, - params.BeaconConfig().DomainBeaconProposer, - keys[0], - ) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) + var body bytes.Buffer + _, err = body.WriteString(invalidAttesterSlashing) require.NoError(t, err) - h.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/proposer_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.SubmitProposerSlashing(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing) - assert.Equal(t, true, ok) + s.SubmitAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.StringContains(t, "Invalid attester slashing", e.Message) + }) } func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) { diff --git a/testing/mock/beacon_service_mock.go b/testing/mock/beacon_service_mock.go index f5e506ba34d2..49b8b66d46d9 100644 --- a/testing/mock/beacon_service_mock.go +++ b/testing/mock/beacon_service_mock.go @@ -429,7 +429,7 @@ func (m *MockBeaconChainClient) SubmitAttesterSlashing(arg0 context.Context, arg for _, a := range arg2 { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "SubmitAttesterSlashing", varargs...) + ret := m.ctrl.Call(m, "SubmitAttesterSlashings", varargs...) ret0, _ := ret[0].(*eth.SubmitSlashingResponse) ret1, _ := ret[1].(error) return ret0, ret1 @@ -439,7 +439,7 @@ func (m *MockBeaconChainClient) SubmitAttesterSlashing(arg0 context.Context, arg func (mr *MockBeaconChainClientMockRecorder) SubmitAttesterSlashing(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttesterSlashing", reflect.TypeOf((*MockBeaconChainClient)(nil).SubmitAttesterSlashing), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttesterSlashings", reflect.TypeOf((*MockBeaconChainClient)(nil).SubmitAttesterSlashing), varargs...) } // SubmitAttesterSlashingElectra mocks base method. From 0a4ed8279b5fd776c05aa5a769aff30ef54dffbf Mon Sep 17 00:00:00 2001 From: terence Date: Tue, 15 Oct 2024 08:11:57 -0700 Subject: [PATCH 07/26] Switch to compounding when consolidating with source==target (#14511) * Switch to compounding when consolidating with source==target * Feedback --- CHANGELOG.md | 1 + beacon-chain/core/electra/consolidations.go | 205 ++++++++++++++---- .../core/electra/consolidations_test.go | 89 +++++++- beacon-chain/core/electra/validator.go | 18 +- beacon-chain/core/helpers/BUILD.bazel | 1 + .../pending_consolidations_test.go | 1 - .../electra/operations/consolidation_test.go | 1 - .../mainnet/electra/sanity/blocks_test.go | 1 - .../pending_consolidations_test.go | 1 - .../electra/operations/consolidation_test.go | 1 - .../minimal/electra/sanity/blocks_test.go | 1 - 11 files changed, 255 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4738828c4bb7..bf0342e40acb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Moved `ConvertKzgCommitmentToVersionedHash` to the `primitives` package. - reversed the boolean return on `BatchVerifyDepositsSignatures`, from need verification, to all keys successfully verified - Fix `engine_exchangeCapabilities` implementation. +- Switch to compounding when consolidating with source==target. ### Deprecated - `--disable-grpc-gateway` flag is deprecated due to grpc gateway removal. diff --git a/beacon-chain/core/electra/consolidations.go b/beacon-chain/core/electra/consolidations.go index 70d3ba4ee17d..7b33f62fc2c0 100644 --- a/beacon-chain/core/electra/consolidations.go +++ b/beacon-chain/core/electra/consolidations.go @@ -15,6 +15,7 @@ import ( enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/time/slots" + log "github.com/sirupsen/logrus" ) // ProcessPendingConsolidations implements the spec definition below. This method makes mutating @@ -22,25 +23,28 @@ import ( // // Spec definition: // -// def process_pending_consolidations(state: BeaconState) -> None: -// next_pending_consolidation = 0 -// for pending_consolidation in state.pending_consolidations: -// source_validator = state.validators[pending_consolidation.source_index] -// if source_validator.slashed: -// next_pending_consolidation += 1 -// continue -// if source_validator.withdrawable_epoch > get_current_epoch(state): -// break -// -// # Churn any target excess active balance of target and raise its max -// switch_to_compounding_validator(state, pending_consolidation.target_index) -// # Move active balance to target. Excess balance is withdrawable. -// active_balance = get_active_balance(state, pending_consolidation.source_index) -// decrease_balance(state, pending_consolidation.source_index, active_balance) -// increase_balance(state, pending_consolidation.target_index, active_balance) +// def process_pending_consolidations(state: BeaconState) -> None: +// +// next_epoch = Epoch(get_current_epoch(state) + 1) +// next_pending_consolidation = 0 +// for pending_consolidation in state.pending_consolidations: +// source_validator = state.validators[pending_consolidation.source_index] +// if source_validator.slashed: // next_pending_consolidation += 1 +// continue +// if source_validator.withdrawable_epoch > next_epoch: +// break +// +// # Calculate the consolidated balance +// max_effective_balance = get_max_effective_balance(source_validator) +// source_effective_balance = min(state.balances[pending_consolidation.source_index], max_effective_balance) +// +// # Move active balance to target. Excess balance is withdrawable. +// decrease_balance(state, pending_consolidation.source_index, source_effective_balance) +// increase_balance(state, pending_consolidation.target_index, source_effective_balance) +// next_pending_consolidation += 1 // -// state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:] +// state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:] func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState) error { _, span := trace.StartSpan(ctx, "electra.ProcessPendingConsolidations") defer span.End() @@ -51,12 +55,11 @@ func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState) err nextEpoch := slots.ToEpoch(st.Slot()) + 1 - var nextPendingConsolidation uint64 pendingConsolidations, err := st.PendingConsolidations() if err != nil { return err } - + var nextPendingConsolidation uint64 for _, pc := range pendingConsolidations { sourceValidator, err := st.ValidatorAtIndex(pc.SourceIndex) if err != nil { @@ -70,18 +73,16 @@ func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState) err break } - if err := SwitchToCompoundingValidator(st, pc.TargetIndex); err != nil { - return err - } - - activeBalance, err := st.ActiveBalanceAtIndex(pc.SourceIndex) + validatorBalance, err := st.BalanceAtIndex(pc.SourceIndex) if err != nil { return err } - if err := helpers.DecreaseBalance(st, pc.SourceIndex, activeBalance); err != nil { + b := min(validatorBalance, helpers.ValidatorMaxEffectiveBalance(sourceValidator)) + + if err := helpers.DecreaseBalance(st, pc.SourceIndex, b); err != nil { return err } - if err := helpers.IncreaseBalance(st, pc.TargetIndex, activeBalance); err != nil { + if err := helpers.IncreaseBalance(st, pc.TargetIndex, b); err != nil { return err } nextPendingConsolidation++ @@ -101,6 +102,16 @@ func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState) err // state: BeaconState, // consolidation_request: ConsolidationRequest // ) -> None: +// if is_valid_switch_to_compounding_request(state, consolidation_request): +// validator_pubkeys = [v.pubkey for v in state.validators] +// request_source_pubkey = consolidation_request.source_pubkey +// source_index = ValidatorIndex(validator_pubkeys.index(request_source_pubkey)) +// switch_to_compounding_validator(state, source_index) +// return +// +// # Verify that source != target, so a consolidation cannot be used as an exit. +// if consolidation_request.source_pubkey == consolidation_request.target_pubkey: +// return // # If the pending consolidations queue is full, consolidation requests are ignored // if len(state.pending_consolidations) == PENDING_CONSOLIDATIONS_LIMIT: // return @@ -121,10 +132,6 @@ func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState) err // source_validator = state.validators[source_index] // target_validator = state.validators[target_index] // -// # Verify that source != target, so a consolidation cannot be used as an exit. -// if source_index == target_index: -// return -// // # Verify source withdrawal credentials // has_correct_credential = has_execution_withdrawal_credential(source_validator) // is_correct_source_address = ( @@ -160,19 +167,14 @@ func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState) err // source_index=source_index, // target_index=target_index // )) +// +// # Churn any target excess active balance of target and raise its max +// if has_eth1_withdrawal_credential(target_validator): +// switch_to_compounding_validator(state, target_index) func ProcessConsolidationRequests(ctx context.Context, st state.BeaconState, reqs []*enginev1.ConsolidationRequest) error { if len(reqs) == 0 || st == nil { return nil } - - activeBal, err := helpers.TotalActiveBalance(st) - if err != nil { - return err - } - churnLimit := helpers.ConsolidationChurnLimit(primitives.Gwei(activeBal)) - if churnLimit <= primitives.Gwei(params.BeaconConfig().MinActivationBalance) { - return nil - } curEpoch := slots.ToEpoch(st.Slot()) ffe := params.BeaconConfig().FarFutureEpoch minValWithdrawDelay := params.BeaconConfig().MinValidatorWithdrawabilityDelay @@ -182,22 +184,44 @@ func ProcessConsolidationRequests(ctx context.Context, st state.BeaconState, req if ctx.Err() != nil { return fmt.Errorf("cannot process consolidation requests: %w", ctx.Err()) } + if IsValidSwitchToCompoundingRequest(st, cr) { + srcIdx, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(cr.SourcePubkey)) + if !ok { + log.Error("failed to find source validator index") + continue + } + if err := SwitchToCompoundingValidator(st, srcIdx); err != nil { + log.WithError(err).Error("failed to switch to compounding validator") + } + continue + } + sourcePubkey := bytesutil.ToBytes48(cr.SourcePubkey) + targetPubkey := bytesutil.ToBytes48(cr.TargetPubkey) + if sourcePubkey == targetPubkey { + continue + } + if npc, err := st.NumPendingConsolidations(); err != nil { return fmt.Errorf("failed to fetch number of pending consolidations: %w", err) // This should never happen. } else if npc >= pcLimit { return nil } - srcIdx, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(cr.SourcePubkey)) - if !ok { - continue + activeBal, err := helpers.TotalActiveBalance(st) + if err != nil { + return err + } + churnLimit := helpers.ConsolidationChurnLimit(primitives.Gwei(activeBal)) + if churnLimit <= primitives.Gwei(params.BeaconConfig().MinActivationBalance) { + return nil } - tgtIdx, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(cr.TargetPubkey)) + + srcIdx, ok := st.ValidatorIndexByPubkey(sourcePubkey) if !ok { continue } - - if srcIdx == tgtIdx { + tgtIdx, ok := st.ValidatorIndexByPubkey(targetPubkey) + if !ok { continue } @@ -237,7 +261,8 @@ func ProcessConsolidationRequests(ctx context.Context, st state.BeaconState, req // Initiate the exit of the source validator. exitEpoch, err := ComputeConsolidationEpochAndUpdateChurn(ctx, st, primitives.Gwei(srcV.EffectiveBalance)) if err != nil { - return fmt.Errorf("failed to compute consolidaiton epoch: %w", err) + log.WithError(err).Error("failed to compute consolidation epoch") + continue } srcV.ExitEpoch = exitEpoch srcV.WithdrawableEpoch = exitEpoch + minValWithdrawDelay @@ -248,7 +273,95 @@ func ProcessConsolidationRequests(ctx context.Context, st state.BeaconState, req if err := st.AppendPendingConsolidation(ð.PendingConsolidation{SourceIndex: srcIdx, TargetIndex: tgtIdx}); err != nil { return fmt.Errorf("failed to append pending consolidation: %w", err) // This should never happen. } + + if helpers.HasETH1WithdrawalCredential(tgtV) { + if err := SwitchToCompoundingValidator(st, tgtIdx); err != nil { + log.WithError(err).Error("failed to switch to compounding validator") + continue + } + } } return nil } + +// IsValidSwitchToCompoundingRequest returns true if the given consolidation request is valid for switching to compounding. +// +// Spec code: +// +// def is_valid_switch_to_compounding_request( +// +// state: BeaconState, +// consolidation_request: ConsolidationRequest +// +// ) -> bool: +// +// # Switch to compounding requires source and target be equal +// if consolidation_request.source_pubkey != consolidation_request.target_pubkey: +// return False +// +// # Verify pubkey exists +// source_pubkey = consolidation_request.source_pubkey +// validator_pubkeys = [v.pubkey for v in state.validators] +// if source_pubkey not in validator_pubkeys: +// return False +// +// source_validator = state.validators[ValidatorIndex(validator_pubkeys.index(source_pubkey))] +// +// # Verify request has been authorized +// if source_validator.withdrawal_credentials[12:] != consolidation_request.source_address: +// return False +// +// # Verify source withdrawal credentials +// if not has_eth1_withdrawal_credential(source_validator): +// return False +// +// # Verify the source is active +// current_epoch = get_current_epoch(state) +// if not is_active_validator(source_validator, current_epoch): +// return False +// +// # Verify exit for source has not been initiated +// if source_validator.exit_epoch != FAR_FUTURE_EPOCH: +// return False +// +// return True +func IsValidSwitchToCompoundingRequest(st state.BeaconState, req *enginev1.ConsolidationRequest) bool { + if req.SourcePubkey == nil || req.TargetPubkey == nil { + return false + } + + if !bytes.Equal(req.SourcePubkey, req.TargetPubkey) { + return false + } + + srcIdx, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(req.SourcePubkey)) + if !ok { + return false + } + // As per the consensus specification, this error is not considered an assertion. + // Therefore, if the source_pubkey is not found in validator_pubkeys, we simply return false. + srcV, err := st.ValidatorAtIndexReadOnly(srcIdx) + if err != nil { + return false + } + sourceAddress := req.SourceAddress + withdrawalCreds := srcV.GetWithdrawalCredentials() + if len(withdrawalCreds) != 32 || len(sourceAddress) != 20 || !bytes.HasSuffix(withdrawalCreds, sourceAddress) { + return false + } + + if !helpers.HasETH1WithdrawalCredential(srcV) { + return false + } + + curEpoch := slots.ToEpoch(st.Slot()) + if !helpers.IsActiveValidatorUsingTrie(srcV, curEpoch) { + return false + } + + if srcV.ExitEpoch() != params.BeaconConfig().FarFutureEpoch { + return false + } + return true +} diff --git a/beacon-chain/core/electra/consolidations_test.go b/beacon-chain/core/electra/consolidations_test.go index 97ff9e981bab..8d065d914848 100644 --- a/beacon-chain/core/electra/consolidations_test.go +++ b/beacon-chain/core/electra/consolidations_test.go @@ -13,6 +13,7 @@ import ( enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/require" + "github.com/prysmaticlabs/prysm/v5/testing/util" ) func TestProcessPendingConsolidations(t *testing.T) { @@ -80,10 +81,10 @@ func TestProcessPendingConsolidations(t *testing.T) { require.NoError(t, err) require.Equal(t, uint64(0), num) - // v1 is switched to compounding validator. + // v1 withdrawal credentials should not be updated. v1, err := st.ValidatorAtIndex(1) require.NoError(t, err) - require.Equal(t, params.BeaconConfig().CompoundingWithdrawalPrefixByte, v1.WithdrawalCredentials[0]) + require.Equal(t, params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, v1.WithdrawalCredentials[0]) }, wantErr: false, }, @@ -396,3 +397,87 @@ func TestProcessConsolidationRequests(t *testing.T) { }) } } + +func TestIsValidSwitchToCompoundingRequest(t *testing.T) { + st, _ := util.DeterministicGenesisStateElectra(t, 1) + t.Run("nil source pubkey", func(t *testing.T) { + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + SourcePubkey: nil, + TargetPubkey: []byte{'a'}, + }) + require.Equal(t, false, ok) + }) + t.Run("nil target pubkey", func(t *testing.T) { + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + TargetPubkey: nil, + SourcePubkey: []byte{'a'}, + }) + require.Equal(t, false, ok) + }) + t.Run("different source and target pubkey", func(t *testing.T) { + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + TargetPubkey: []byte{'a'}, + SourcePubkey: []byte{'b'}, + }) + require.Equal(t, false, ok) + }) + t.Run("source validator not found in state", func(t *testing.T) { + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + SourceAddress: make([]byte, 20), + TargetPubkey: []byte{'a'}, + SourcePubkey: []byte{'a'}, + }) + require.Equal(t, false, ok) + }) + t.Run("incorrect source address", func(t *testing.T) { + v, err := st.ValidatorAtIndex(0) + require.NoError(t, err) + pubkey := v.PublicKey + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + SourceAddress: make([]byte, 20), + TargetPubkey: pubkey, + SourcePubkey: pubkey, + }) + require.Equal(t, false, ok) + }) + t.Run("incorrect eth1 withdrawal credential", func(t *testing.T) { + v, err := st.ValidatorAtIndex(0) + require.NoError(t, err) + pubkey := v.PublicKey + wc := v.WithdrawalCredentials + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + SourceAddress: wc[12:], + TargetPubkey: pubkey, + SourcePubkey: pubkey, + }) + require.Equal(t, false, ok) + }) + t.Run("is valid compounding request", func(t *testing.T) { + v, err := st.ValidatorAtIndex(0) + require.NoError(t, err) + pubkey := v.PublicKey + wc := v.WithdrawalCredentials + v.WithdrawalCredentials[0] = 1 + require.NoError(t, st.UpdateValidatorAtIndex(0, v)) + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + SourceAddress: wc[12:], + TargetPubkey: pubkey, + SourcePubkey: pubkey, + }) + require.Equal(t, true, ok) + }) + t.Run("already has an exit epoch", func(t *testing.T) { + v, err := st.ValidatorAtIndex(0) + require.NoError(t, err) + pubkey := v.PublicKey + wc := v.WithdrawalCredentials + v.ExitEpoch = 100 + require.NoError(t, st.UpdateValidatorAtIndex(0, v)) + ok := electra.IsValidSwitchToCompoundingRequest(st, &enginev1.ConsolidationRequest{ + SourceAddress: wc[12:], + TargetPubkey: pubkey, + SourcePubkey: pubkey, + }) + require.Equal(t, false, ok) + }) +} diff --git a/beacon-chain/core/electra/validator.go b/beacon-chain/core/electra/validator.go index 451d6df8bfd7..31959bb2a26f 100644 --- a/beacon-chain/core/electra/validator.go +++ b/beacon-chain/core/electra/validator.go @@ -3,7 +3,6 @@ package electra import ( "errors" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" @@ -18,9 +17,8 @@ import ( // def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None: // // validator = state.validators[index] -// if has_eth1_withdrawal_credential(validator): -// validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] -// queue_excess_active_balance(state, index) +// validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] +// queue_excess_active_balance(state, index) func SwitchToCompoundingValidator(s state.BeaconState, idx primitives.ValidatorIndex) error { v, err := s.ValidatorAtIndex(idx) if err != nil { @@ -29,14 +27,12 @@ func SwitchToCompoundingValidator(s state.BeaconState, idx primitives.ValidatorI if len(v.WithdrawalCredentials) == 0 { return errors.New("validator has no withdrawal credentials") } - if helpers.HasETH1WithdrawalCredential(v) { - v.WithdrawalCredentials[0] = params.BeaconConfig().CompoundingWithdrawalPrefixByte - if err := s.UpdateValidatorAtIndex(idx, v); err != nil { - return err - } - return QueueExcessActiveBalance(s, idx) + + v.WithdrawalCredentials[0] = params.BeaconConfig().CompoundingWithdrawalPrefixByte + if err := s.UpdateValidatorAtIndex(idx, v); err != nil { + return err } - return nil + return QueueExcessActiveBalance(s, idx) } // QueueExcessActiveBalance queues validators with balances above the min activation balance and adds to pending deposit. diff --git a/beacon-chain/core/helpers/BUILD.bazel b/beacon-chain/core/helpers/BUILD.bazel index b0cbd9c16061..090a57458a6d 100644 --- a/beacon-chain/core/helpers/BUILD.bazel +++ b/beacon-chain/core/helpers/BUILD.bazel @@ -63,6 +63,7 @@ go_test( "validators_test.go", "weak_subjectivity_test.go", ], + data = glob(["testdata/**"]), embed = [":go_default_library"], shard_count = 2, tags = ["CI_race_detection"], diff --git a/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go b/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go index ba4972fba70e..9dbc628d062f 100644 --- a/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go +++ b/testing/spectest/mainnet/electra/epoch_processing/pending_consolidations_test.go @@ -7,6 +7,5 @@ import ( ) func TestMainnet_Electra_EpochProcessing_PendingConsolidations(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") epoch_processing.RunPendingConsolidationsTests(t, "mainnet") } diff --git a/testing/spectest/mainnet/electra/operations/consolidation_test.go b/testing/spectest/mainnet/electra/operations/consolidation_test.go index 05978c80aa03..3afe9874ec60 100644 --- a/testing/spectest/mainnet/electra/operations/consolidation_test.go +++ b/testing/spectest/mainnet/electra/operations/consolidation_test.go @@ -7,6 +7,5 @@ import ( ) func TestMainnet_Electra_Operations_Consolidation(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") operations.RunConsolidationTest(t, "mainnet") } diff --git a/testing/spectest/mainnet/electra/sanity/blocks_test.go b/testing/spectest/mainnet/electra/sanity/blocks_test.go index be34cffdcfef..8e3fc1bcb93e 100644 --- a/testing/spectest/mainnet/electra/sanity/blocks_test.go +++ b/testing/spectest/mainnet/electra/sanity/blocks_test.go @@ -7,6 +7,5 @@ import ( ) func TestMainnet_Electra_Sanity_Blocks(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") sanity.RunBlockProcessingTest(t, "mainnet", "sanity/blocks/pyspec_tests") } diff --git a/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go b/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go index 4253043d319b..0fcbb76608d0 100644 --- a/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go +++ b/testing/spectest/minimal/electra/epoch_processing/pending_consolidations_test.go @@ -7,6 +7,5 @@ import ( ) func TestMinimal_Electra_EpochProcessing_PendingConsolidations(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") epoch_processing.RunPendingConsolidationsTests(t, "minimal") } diff --git a/testing/spectest/minimal/electra/operations/consolidation_test.go b/testing/spectest/minimal/electra/operations/consolidation_test.go index 6ec83f5a1fd7..cc46d13998d2 100644 --- a/testing/spectest/minimal/electra/operations/consolidation_test.go +++ b/testing/spectest/minimal/electra/operations/consolidation_test.go @@ -7,6 +7,5 @@ import ( ) func TestMinimal_Electra_Operations_Consolidation(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") operations.RunConsolidationTest(t, "minimal") } diff --git a/testing/spectest/minimal/electra/sanity/blocks_test.go b/testing/spectest/minimal/electra/sanity/blocks_test.go index 56e1905a5998..5d2e27fcd24e 100644 --- a/testing/spectest/minimal/electra/sanity/blocks_test.go +++ b/testing/spectest/minimal/electra/sanity/blocks_test.go @@ -7,6 +7,5 @@ import ( ) func TestMinimal_Electra_Sanity_Blocks(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") sanity.RunBlockProcessingTest(t, "minimal", "sanity/blocks/pyspec_tests") } From de094b0078b2da1d3bd1146bef253f68b1ad70f9 Mon Sep 17 00:00:00 2001 From: Jorrian <48367297+Jor-Tech@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:14:45 +0200 Subject: [PATCH 08/26] Update default scrape-interval to 2 minutes for Beaconcha.in API rate limit (#14537) * Update default scrape-interval to 2 minutes for Beaconcha.in API rate limit * Update changelog for scrape-interval change --------- Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com> --- CHANGELOG.md | 1 + cmd/client-stats/flags/flags.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf0342e40acb..658d1b9ac1cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Moved `ConvertKzgCommitmentToVersionedHash` to the `primitives` package. - reversed the boolean return on `BatchVerifyDepositsSignatures`, from need verification, to all keys successfully verified - Fix `engine_exchangeCapabilities` implementation. +- Updated the default `scrape-interval` in `Client-stats` to 2 minutes to accommodate Beaconcha.in API rate limits. - Switch to compounding when consolidating with source==target. ### Deprecated diff --git a/cmd/client-stats/flags/flags.go b/cmd/client-stats/flags/flags.go index 605c9494b466..95ef697e4200 100644 --- a/cmd/client-stats/flags/flags.go +++ b/cmd/client-stats/flags/flags.go @@ -28,6 +28,6 @@ var ( ScrapeIntervalFlag = &cli.DurationFlag{ Name: "scrape-interval", Usage: "Frequency of scraping expressed as a duration, eg 2m or 1m5s.", - Value: 60 * time.Second, + Value: 120 * time.Second, } ) From f776b968ad99b3f40ea010a7a58d2e51b0c51e1e Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:41:58 -0500 Subject: [PATCH 09/26] Remove finalized validator index to pubkey cache (#14497) * remove the cache * linting * changelog * fixing unit tests --- CHANGELOG.md | 5 +- beacon-chain/blockchain/receive_block.go | 1 - beacon-chain/blockchain/service.go | 2 - beacon-chain/core/electra/deposits_test.go | 3 - beacon-chain/state/interfaces.go | 1 - beacon-chain/state/state-native/BUILD.bazel | 2 - .../state/state-native/beacon_state.go | 1 - .../state/state-native/getters_validator.go | 4 - .../state/state-native/readonly_validator.go | 5 - .../state/state-native/setters_misc.go | 16 --- beacon-chain/state/state-native/state_trie.go | 69 ++++++------ .../state-native/validator_index_cache.go | 96 ---------------- .../validator_index_cache_test.go | 105 ------------------ beacon-chain/state/stategen/replay_test.go | 1 - 14 files changed, 34 insertions(+), 277 deletions(-) delete mode 100644 beacon-chain/state/state-native/validator_index_cache.go delete mode 100644 beacon-chain/state/state-native/validator_index_cache_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 658d1b9ac1cd..28b57a07ee49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,9 +71,10 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve ### Removed -- removed gRPC Gateway -- Removed unused blobs bundle cache +- Removed gRPC Gateway. +- Removed unused blobs bundle cache. - Removed consolidation signing domain from params. The Electra design changed such that EL handles consolidation signature verification. +- Removed finalized validator index cache, no longer needed. ### Fixed diff --git a/beacon-chain/blockchain/receive_block.go b/beacon-chain/blockchain/receive_block.go index 200d22207414..241c0ca9ced9 100644 --- a/beacon-chain/blockchain/receive_block.go +++ b/beacon-chain/blockchain/receive_block.go @@ -273,7 +273,6 @@ func (s *Service) reportPostBlockProcessing( func (s *Service) executePostFinalizationTasks(ctx context.Context, finalizedState state.BeaconState) { finalized := s.cfg.ForkChoiceStore.FinalizedCheckpoint() go func() { - finalizedState.SaveValidatorIndices() // used to handle Validator index invariant from EIP6110 s.sendNewFinalizedEvent(ctx, finalizedState) }() depCtx, cancel := context.WithTimeout(context.Background(), depositDeadline) diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index 23106e032a79..925d22a6e796 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -329,8 +329,6 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error { return errors.Wrap(err, "failed to initialize blockchain service") } - saved.SaveValidatorIndices() // used to handle Validator index invariant from EIP6110 - return nil } diff --git a/beacon-chain/core/electra/deposits_test.go b/beacon-chain/core/electra/deposits_test.go index d94500278e13..0cef53f329ea 100644 --- a/beacon-chain/core/electra/deposits_test.go +++ b/beacon-chain/core/electra/deposits_test.go @@ -177,7 +177,6 @@ func TestProcessPendingDeposits(t *testing.T) { dep := stateTesting.GeneratePendingDeposit(t, sk, excessBalance, bytesutil.ToBytes32(wc), 0) dep.Signature = common.InfiniteSignature[:] require.NoError(t, st.SetValidators(validators)) - st.SaveValidatorIndices() require.NoError(t, st.SetPendingDeposits([]*eth.PendingDeposit{dep})) return st }(), @@ -508,7 +507,6 @@ func stateWithPendingDeposits(t *testing.T, balETH uint64, numDeposits, amount u deps[i] = stateTesting.GeneratePendingDeposit(t, sk, amount, bytesutil.ToBytes32(wc), 0) } require.NoError(t, st.SetValidators(validators)) - st.SaveValidatorIndices() require.NoError(t, st.SetPendingDeposits(deps)) return st } @@ -527,7 +525,6 @@ func TestApplyPendingDeposit_TopUp(t *testing.T) { dep := stateTesting.GeneratePendingDeposit(t, sk, excessBalance, bytesutil.ToBytes32(wc), 0) dep.Signature = common.InfiniteSignature[:] require.NoError(t, st.SetValidators(validators)) - st.SaveValidatorIndices() require.NoError(t, electra.ApplyPendingDeposit(context.Background(), st, dep)) diff --git a/beacon-chain/state/interfaces.go b/beacon-chain/state/interfaces.go index 5c9fa1f8e3da..8724e5a3cf0a 100644 --- a/beacon-chain/state/interfaces.go +++ b/beacon-chain/state/interfaces.go @@ -105,7 +105,6 @@ type WriteOnlyBeaconState interface { AppendHistoricalRoots(root [32]byte) error AppendHistoricalSummaries(*ethpb.HistoricalSummary) error SetLatestExecutionPayloadHeader(payload interfaces.ExecutionData) error - SaveValidatorIndices() } // ReadOnlyValidator defines a struct which only has read access to validator methods. diff --git a/beacon-chain/state/state-native/BUILD.bazel b/beacon-chain/state/state-native/BUILD.bazel index b687240a46b3..9a662fc22404 100644 --- a/beacon-chain/state/state-native/BUILD.bazel +++ b/beacon-chain/state/state-native/BUILD.bazel @@ -46,7 +46,6 @@ go_library( "ssz.go", "state_trie.go", "types.go", - "validator_index_cache.go", ], importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native", visibility = ["//visibility:public"], @@ -121,7 +120,6 @@ go_test( "state_test.go", "state_trie_test.go", "types_test.go", - "validator_index_cache_test.go", ], data = glob(["testdata/**"]) + [ "@consensus_spec_tests_mainnet//:test_data", diff --git a/beacon-chain/state/state-native/beacon_state.go b/beacon-chain/state/state-native/beacon_state.go index d8b1b2785da5..576a6e2e7d06 100644 --- a/beacon-chain/state/state-native/beacon_state.go +++ b/beacon-chain/state/state-native/beacon_state.go @@ -76,7 +76,6 @@ type BeaconState struct { stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie rebuildTrie map[types.FieldIndex]bool valMapHandler *stateutil.ValidatorMapHandler - validatorIndexCache *finalizedValidatorIndexCache merkleLayers [][][]byte sharedFieldReferences map[types.FieldIndex]*stateutil.Reference } diff --git a/beacon-chain/state/state-native/getters_validator.go b/beacon-chain/state/state-native/getters_validator.go index 6acac6100040..bc0ec49dfdd4 100644 --- a/beacon-chain/state/state-native/getters_validator.go +++ b/beacon-chain/state/state-native/getters_validator.go @@ -180,10 +180,6 @@ func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]by b.lock.RLock() defer b.lock.RUnlock() - if b.Version() >= version.Electra { - return b.getValidatorIndex(key) - } - var numOfVals int if features.Get().EnableExperimentalState { numOfVals = b.validatorsMultiValue.Len(b) diff --git a/beacon-chain/state/state-native/readonly_validator.go b/beacon-chain/state/state-native/readonly_validator.go index c26b212ba820..19ae4e5cc4ea 100644 --- a/beacon-chain/state/state-native/readonly_validator.go +++ b/beacon-chain/state/state-native/readonly_validator.go @@ -70,11 +70,6 @@ func (v readOnlyValidator) PublicKey() [fieldparams.BLSPubkeyLength]byte { return pubkey } -// publicKeySlice returns the public key in the slice form for the read only validator. -func (v readOnlyValidator) publicKeySlice() []byte { - return v.validator.PublicKey -} - // WithdrawalCredentials returns the withdrawal credentials of the // read only validator. func (v readOnlyValidator) GetWithdrawalCredentials() []byte { diff --git a/beacon-chain/state/state-native/setters_misc.go b/beacon-chain/state/state-native/setters_misc.go index affd7ef520be..e5c4b4e65a43 100644 --- a/beacon-chain/state/state-native/setters_misc.go +++ b/beacon-chain/state/state-native/setters_misc.go @@ -107,22 +107,6 @@ func (b *BeaconState) SetHistoricalRoots(val [][]byte) error { return nil } -// SaveValidatorIndices save validator indices of beacon chain to cache -func (b *BeaconState) SaveValidatorIndices() { - if b.Version() < version.Electra { - return - } - - b.lock.Lock() - defer b.lock.Unlock() - - if b.validatorIndexCache == nil { - b.validatorIndexCache = newFinalizedValidatorIndexCache() - } - - b.saveValidatorIndices() -} - // AppendHistoricalRoots for the beacon state. Appends the new value // to the end of list. func (b *BeaconState) AppendHistoricalRoots(root [32]byte) error { diff --git a/beacon-chain/state/state-native/state_trie.go b/beacon-chain/state/state-native/state_trie.go index 6536cc9eef45..a5270f533424 100644 --- a/beacon-chain/state/state-native/state_trie.go +++ b/beacon-chain/state/state-native/state_trie.go @@ -189,12 +189,11 @@ func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, id: types.Enumerator.Inc(), - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), - validatorIndexCache: newFinalizedValidatorIndexCache(), + dirtyFields: make(map[types.FieldIndex]bool, fieldCount), + dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), + stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), + rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), + valMapHandler: stateutil.NewValMapHandler(st.Validators), } if features.Get().EnableExperimentalState { @@ -296,12 +295,11 @@ func InitializeFromProtoUnsafeAltair(st *ethpb.BeaconStateAltair) (state.BeaconS id: types.Enumerator.Inc(), - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), - validatorIndexCache: newFinalizedValidatorIndexCache(), + dirtyFields: make(map[types.FieldIndex]bool, fieldCount), + dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), + stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), + rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), + valMapHandler: stateutil.NewValMapHandler(st.Validators), } if features.Get().EnableExperimentalState { @@ -407,12 +405,11 @@ func InitializeFromProtoUnsafeBellatrix(st *ethpb.BeaconStateBellatrix) (state.B id: types.Enumerator.Inc(), - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), - validatorIndexCache: newFinalizedValidatorIndexCache(), + dirtyFields: make(map[types.FieldIndex]bool, fieldCount), + dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), + stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), + rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), + valMapHandler: stateutil.NewValMapHandler(st.Validators), } if features.Get().EnableExperimentalState { @@ -522,12 +519,11 @@ func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.Beaco id: types.Enumerator.Inc(), - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), - validatorIndexCache: newFinalizedValidatorIndexCache(), + dirtyFields: make(map[types.FieldIndex]bool, fieldCount), + dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), + stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), + rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), + valMapHandler: stateutil.NewValMapHandler(st.Validators), } if features.Get().EnableExperimentalState { @@ -636,12 +632,11 @@ func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconSta nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex, historicalSummaries: st.HistoricalSummaries, - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), - validatorIndexCache: newFinalizedValidatorIndexCache(), + dirtyFields: make(map[types.FieldIndex]bool, fieldCount), + dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), + stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), + rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), + valMapHandler: stateutil.NewValMapHandler(st.Validators), } if features.Get().EnableExperimentalState { @@ -759,12 +754,11 @@ func InitializeFromProtoUnsafeElectra(st *ethpb.BeaconStateElectra) (state.Beaco pendingPartialWithdrawals: st.PendingPartialWithdrawals, pendingConsolidations: st.PendingConsolidations, - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), - validatorIndexCache: newFinalizedValidatorIndexCache(), //only used in post-electra and only populates when finalizing, otherwise it falls back to processing the full validator set + dirtyFields: make(map[types.FieldIndex]bool, fieldCount), + dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), + stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), + rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), + valMapHandler: stateutil.NewValMapHandler(st.Validators), } if features.Get().EnableExperimentalState { @@ -925,8 +919,7 @@ func (b *BeaconState) Copy() state.BeaconState { stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), // Share the reference to validator index map. - valMapHandler: b.valMapHandler, - validatorIndexCache: b.validatorIndexCache, + valMapHandler: b.valMapHandler, } if features.Get().EnableExperimentalState { diff --git a/beacon-chain/state/state-native/validator_index_cache.go b/beacon-chain/state/state-native/validator_index_cache.go deleted file mode 100644 index 83115fef110a..000000000000 --- a/beacon-chain/state/state-native/validator_index_cache.go +++ /dev/null @@ -1,96 +0,0 @@ -package state_native - -import ( - "bytes" - "sync" - - "github.com/prysmaticlabs/prysm/v5/config/features" - fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" - "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" - ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" -) - -// finalizedValidatorIndexCache maintains a mapping from validator public keys to their indices within the beacon state. -// It includes a lastFinalizedIndex to track updates up to the last finalized validator index, -// and uses a mutex for concurrent read/write access to the cache. -type finalizedValidatorIndexCache struct { - indexMap map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex // Maps finalized BLS public keys to validator indices. - sync.RWMutex -} - -// newFinalizedValidatorIndexCache initializes a new validator index cache with an empty index map. -func newFinalizedValidatorIndexCache() *finalizedValidatorIndexCache { - return &finalizedValidatorIndexCache{ - indexMap: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - } -} - -// getValidatorIndex retrieves the validator index for a given public key from the cache. -// If the public key is not found in the cache, it searches through the state starting from the last finalized index. -func (b *BeaconState) getValidatorIndex(pubKey [fieldparams.BLSPubkeyLength]byte) (primitives.ValidatorIndex, bool) { - b.validatorIndexCache.RLock() - defer b.validatorIndexCache.RUnlock() - index, found := b.validatorIndexCache.indexMap[pubKey] - if found { - return index, true - } - - validatorCount := len(b.validatorIndexCache.indexMap) - vals := b.validatorsReadOnlySinceIndex(validatorCount) - for i, val := range vals { - if bytes.Equal(bytesutil.PadTo(val.publicKeySlice(), 48), pubKey[:]) { - index := primitives.ValidatorIndex(validatorCount + i) - return index, true - } - } - return 0, false -} - -// saveValidatorIndices updates the validator index cache with new indices. -// It processes validator indices starting after the last finalized index and updates the tracker. -func (b *BeaconState) saveValidatorIndices() { - b.validatorIndexCache.Lock() - defer b.validatorIndexCache.Unlock() - - validatorCount := len(b.validatorIndexCache.indexMap) - vals := b.validatorsReadOnlySinceIndex(validatorCount) - for i, val := range vals { - b.validatorIndexCache.indexMap[val.PublicKey()] = primitives.ValidatorIndex(validatorCount + i) - } -} - -// validatorsReadOnlySinceIndex constructs a list of read only validator references after a specified index. -// The indices in the returned list correspond to their respective validator indices in the state. -// It returns nil if the specified index is out of bounds. This function is read-only and does not use locks. -func (b *BeaconState) validatorsReadOnlySinceIndex(index int) []readOnlyValidator { - totalValidators := b.validatorsLen() - if index >= totalValidators { - return nil - } - - var v []*ethpb.Validator - if features.Get().EnableExperimentalState { - if b.validatorsMultiValue == nil { - return nil - } - v = b.validatorsMultiValue.Value(b) - } else { - if b.validators == nil { - return nil - } - v = b.validators - } - - result := make([]readOnlyValidator, totalValidators-index) - for i := 0; i < len(result); i++ { - val := v[i+index] - if val == nil { - continue - } - result[i] = readOnlyValidator{ - validator: val, - } - } - return result -} diff --git a/beacon-chain/state/state-native/validator_index_cache_test.go b/beacon-chain/state/state-native/validator_index_cache_test.go deleted file mode 100644 index 85177f89ceeb..000000000000 --- a/beacon-chain/state/state-native/validator_index_cache_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package state_native - -import ( - "testing" - - fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" - "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" - "github.com/prysmaticlabs/prysm/v5/testing/require" -) - -func Test_FinalizedValidatorIndexCache(t *testing.T) { - c := newFinalizedValidatorIndexCache() - b := &BeaconState{validatorIndexCache: c} - - // What happens if you call getValidatorIndex with a public key that is not in the cache and state? - // The function will return 0 and false. - i, exists := b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{0}) - require.Equal(t, primitives.ValidatorIndex(0), i) - require.Equal(t, false, exists) - - // Validators are added to the state. They are [0, 1, 2] - b.validators = []*ethpb.Validator{ - {PublicKey: []byte{1}}, - {PublicKey: []byte{2}}, - {PublicKey: []byte{3}}, - } - // We should be able to retrieve these validators by public key even when they are not in the cache - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{1}) - require.Equal(t, primitives.ValidatorIndex(0), i) - require.Equal(t, true, exists) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{2}) - require.Equal(t, primitives.ValidatorIndex(1), i) - require.Equal(t, true, exists) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{3}) - require.Equal(t, primitives.ValidatorIndex(2), i) - require.Equal(t, true, exists) - - // State is finalized. We save [0, 1, 2 ] to the cache. - b.saveValidatorIndices() - require.Equal(t, 3, len(b.validatorIndexCache.indexMap)) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{1}) - require.Equal(t, primitives.ValidatorIndex(0), i) - require.Equal(t, true, exists) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{2}) - require.Equal(t, primitives.ValidatorIndex(1), i) - require.Equal(t, true, exists) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{3}) - require.Equal(t, primitives.ValidatorIndex(2), i) - require.Equal(t, true, exists) - - // New validators are added to the state. They are [4, 5] - b.validators = []*ethpb.Validator{ - {PublicKey: []byte{1}}, - {PublicKey: []byte{2}}, - {PublicKey: []byte{3}}, - {PublicKey: []byte{4}}, - {PublicKey: []byte{5}}, - } - // We should be able to retrieve these validators by public key even when they are not in the cache - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{4}) - require.Equal(t, primitives.ValidatorIndex(3), i) - require.Equal(t, true, exists) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{5}) - require.Equal(t, primitives.ValidatorIndex(4), i) - require.Equal(t, true, exists) - - // State is finalized. We save [4, 5] to the cache. - b.saveValidatorIndices() - require.Equal(t, 5, len(b.validatorIndexCache.indexMap)) - - // New validators are added to the state. They are [6] - b.validators = []*ethpb.Validator{ - {PublicKey: []byte{1}}, - {PublicKey: []byte{2}}, - {PublicKey: []byte{3}}, - {PublicKey: []byte{4}}, - {PublicKey: []byte{5}}, - {PublicKey: []byte{6}}, - } - // We should be able to retrieve these validators by public key even when they are not in the cache - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{6}) - require.Equal(t, primitives.ValidatorIndex(5), i) - require.Equal(t, true, exists) - - // State is finalized. We save [6] to the cache. - b.saveValidatorIndices() - require.Equal(t, 6, len(b.validatorIndexCache.indexMap)) - - // Save a few more times. - b.saveValidatorIndices() - b.saveValidatorIndices() - require.Equal(t, 6, len(b.validatorIndexCache.indexMap)) - - // Can still retrieve the validators from the cache - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{1}) - require.Equal(t, primitives.ValidatorIndex(0), i) - require.Equal(t, true, exists) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{2}) - require.Equal(t, primitives.ValidatorIndex(1), i) - require.Equal(t, true, exists) - i, exists = b.getValidatorIndex([fieldparams.BLSPubkeyLength]byte{3}) - require.Equal(t, primitives.ValidatorIndex(2), i) - require.Equal(t, true, exists) -} diff --git a/beacon-chain/state/stategen/replay_test.go b/beacon-chain/state/stategen/replay_test.go index 0eebec3ce32e..bc03583d89e1 100644 --- a/beacon-chain/state/stategen/replay_test.go +++ b/beacon-chain/state/stategen/replay_test.go @@ -230,7 +230,6 @@ func TestReplayBlocks_ProcessEpoch_Electra(t *testing.T) { EffectiveBalance: params.BeaconConfig().MinActivationBalance, }, })) - beaconState.SaveValidatorIndices() require.NoError(t, beaconState.SetPendingDeposits([]*ethpb.PendingDeposit{ stateTesting.GeneratePendingDeposit(t, sk, uint64(amountAvailForProcessing)/10, bytesutil.ToBytes32(withdrawalCredentials), genesisBlock.Block.Slot), From 30fcf5366a4cccad2d3fa1e5c49c67aa2ff64df1 Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Tue, 15 Oct 2024 15:53:47 -0500 Subject: [PATCH 10/26] Update CHANGELOG.md for v5.1.1 (#14541) --- CHANGELOG.md | 65 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28b57a07ee49..e0aa7f00f6ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,53 @@ All notable changes to this project will be documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. -## [Unreleased](https://github.com/prysmaticlabs/prysm/compare/v5.1.0...HEAD) +## [Unreleased](https://github.com/prysmaticlabs/prysm/compare/v5.1.1...HEAD) + +### Added + +- Electra EIP6110: Queue deposit [pr](https://github.com/prysmaticlabs/prysm/pull/14430) +- Add Bellatrix tests for light client functions. +- Add Discovery Rebooter Feature. +- Added GetBlockAttestationsV2 endpoint. +- Light client support: Consensus types for Electra +- Added SubmitPoolAttesterSlashingV2 endpoint. + +### Changed + +- Electra EIP6110: Queue deposit requests changes from consensus spec pr #3818 +- reversed the boolean return on `BatchVerifyDepositsSignatures`, from need verification, to all keys successfully verified +- Fix `engine_exchangeCapabilities` implementation. +- Updated the default `scrape-interval` in `Client-stats` to 2 minutes to accommodate Beaconcha.in API rate limits. +- Switch to compounding when consolidating with source==target. + +### Deprecated + +- `/eth/v1alpha1/validator/activation/stream` grpc wait for activation stream is deprecated. [pr](https://github.com/prysmaticlabs/prysm/pull/14514) + +### Removed + +- Removed finalized validator index cache, no longer needed. + +### Fixed + +- Fixed mesh size by appending `gParams.Dhi = gossipSubDhi` +- Fix skipping partial withdrawals count. + +### Security + + +## [v5.1.1](https://github.com/prysmaticlabs/prysm/compare/v5.1.0...v5.1.1) - 2024-10-15 + +This release has a number of features and improvements. Most notably, the feature flag +`--enable-experimental-state` has been flipped to "opt out" via `--disable-experimental-state`. +The experimental state management design has shown significant improvements in memory usage at +runtime. Updates to libp2p's gossipsub have some bandwidith stability improvements with support for +IDONTWANT control messages. + +The gRPC gateway has been deprecated from Prysm in this release. If you need JSON data, consider the +standardized beacon-APIs. + +Updating to this release is recommended at your convenience. ### Added @@ -13,7 +59,6 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Light client support: Implement `ComputeFieldRootsForBlockBody`. - Light client support: Add light client database changes. - Light client support: Implement capella and deneb changes. -- Electra EIP6110: Queue deposit [pr](https://github.com/prysmaticlabs/prysm/pull/14430) - Light client support: Implement `BlockToLightClientHeader` function. - Light client support: Consensus types. - GetBeaconStateV2: add Electra case. @@ -24,11 +69,6 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Add Electra support and tests for light client functions. - fastssz version bump (better error messages). - SSE implementation that sheds stuck clients. [pr](https://github.com/prysmaticlabs/prysm/pull/14413) -- Add Bellatrix tests for light client functions. -- Add Discovery Rebooter Feature. -- Added GetBlockAttestationsV2 endpoint. -- Light client support: Consensus types for Electra -- Added SubmitPoolAttesterSlashingV2 endpoint. ### Changed @@ -47,7 +87,6 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - `grpc-gateway-corsdomain` is renamed to http-cors-domain. The old name can still be used as an alias. - `api-timeout` is changed from int flag to duration flag, default value updated. - Light client support: abstracted out the light client headers with different versions. -- Electra EIP6110: Queue deposit requests changes from consensus spec pr #3818 - `ApplyToEveryValidator` has been changed to prevent misuse bugs, it takes a closure that takes a `ReadOnlyValidator` and returns a raw pointer to a `Validator`. - Removed gorilla mux library and replaced it with net/http updates in go 1.22. - Clean up `ProposeBlock` for validator client to reduce cognitive scoring and enable further changes. @@ -59,22 +98,16 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Updated Sepolia bootnodes. - Make committee aware packing the default by deprecating `--enable-committee-aware-packing`. - Moved `ConvertKzgCommitmentToVersionedHash` to the `primitives` package. -- reversed the boolean return on `BatchVerifyDepositsSignatures`, from need verification, to all keys successfully verified -- Fix `engine_exchangeCapabilities` implementation. -- Updated the default `scrape-interval` in `Client-stats` to 2 minutes to accommodate Beaconcha.in API rate limits. -- Switch to compounding when consolidating with source==target. ### Deprecated - `--disable-grpc-gateway` flag is deprecated due to grpc gateway removal. - `--enable-experimental-state` flag is deprecated. This feature is now on by default. Opt-out with `--disable-experimental-state`. -- `/eth/v1alpha1/validator/activation/stream` grpc wait for activation stream is deprecated. [pr](https://github.com/prysmaticlabs/prysm/pull/14514) ### Removed - Removed gRPC Gateway. - Removed unused blobs bundle cache. - Removed consolidation signing domain from params. The Electra design changed such that EL handles consolidation signature verification. -- Removed finalized validator index cache, no longer needed. ### Fixed @@ -93,11 +126,11 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Light client support: fix light client attested header execution fields' wrong version bug. - Testing: added custom matcher for better push settings testing. - Registered `GetDepositSnapshot` Beacon API endpoint. -- Fixed mesh size by appending `gParams.Dhi = gossipSubDhi` -- Fix skipping partial withdrawals count. ### Security +No notable security updates. + ## [v5.1.0](https://github.com/prysmaticlabs/prysm/compare/v5.0.4...v5.1.0) - 2024-08-20 This release contains 171 new changes and many of these are related to Electra! Along side the Electra changes, there From c8d3ed02cb677b58134d66ca2dfa9f8b8a47313d Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:34:41 -0500 Subject: [PATCH 11/26] unskip load config tests (#14539) * uncommenting lode config tests * fixing minimal config --- config/params/loader_test.go | 2 -- config/params/minimal_config.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/config/params/loader_test.go b/config/params/loader_test.go index 8cd166d46725..1760f0bf223a 100644 --- a/config/params/loader_test.go +++ b/config/params/loader_test.go @@ -180,7 +180,6 @@ func TestModifiedE2E(t *testing.T) { func TestLoadConfigFile(t *testing.T) { t.Run("mainnet", func(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") mn := params.MainnetConfig().Copy() mainnetPresetsFiles := presetsFilePath(t, "mainnet") var err error @@ -199,7 +198,6 @@ func TestLoadConfigFile(t *testing.T) { }) t.Run("minimal", func(t *testing.T) { - t.Skip("TODO: add back in after all spec test features are in.") min := params.MinimalSpecConfig().Copy() minimalPresetsFiles := presetsFilePath(t, "minimal") var err error diff --git a/config/params/minimal_config.go b/config/params/minimal_config.go index 234ec1b2a0a2..e4c33d220acc 100644 --- a/config/params/minimal_config.go +++ b/config/params/minimal_config.go @@ -110,7 +110,7 @@ func MinimalSpecConfig() *BeaconChainConfig { minimalConfig.MaxWithdrawalRequestsPerPayload = 2 minimalConfig.MaxDepositRequestsPerPayload = 4 minimalConfig.PendingPartialWithdrawalsLimit = 64 - minimalConfig.MaxPendingPartialsPerWithdrawalsSweep = 1 + minimalConfig.MaxPendingPartialsPerWithdrawalsSweep = 2 minimalConfig.PendingDepositLimit = 134217728 minimalConfig.MaxPendingDepositsPerEpoch = 16 From 5a5193c59d0b8e7b0337143ba607904e7dd514d1 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:16:43 -0500 Subject: [PATCH 12/26] Execution API Electra: removing bodies v2 logic (#14538) * removing bodies v2 logic * changelog * fixing unit test --- CHANGELOG.md | 1 + beacon-chain/execution/engine_client.go | 6 --- beacon-chain/execution/mock_test.go | 16 ------- beacon-chain/execution/payload_body.go | 11 +---- beacon-chain/execution/payload_body_test.go | 50 +-------------------- 5 files changed, 4 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0aa7f00f6ec..bbbb1e7f3035 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ Updating to this release is recommended at your convenience. - Removed gRPC Gateway. - Removed unused blobs bundle cache. - Removed consolidation signing domain from params. The Electra design changed such that EL handles consolidation signature verification. +- Remove engine_getPayloadBodiesBy{Hash|Range}V2 ### Fixed diff --git a/beacon-chain/execution/engine_client.go b/beacon-chain/execution/engine_client.go index a278d23e30a9..039707d8fca6 100644 --- a/beacon-chain/execution/engine_client.go +++ b/beacon-chain/execution/engine_client.go @@ -44,8 +44,6 @@ var ( GetPayloadMethodV4, GetPayloadBodiesByHashV1, GetPayloadBodiesByRangeV1, - GetPayloadBodiesByHashV2, - GetPayloadBodiesByRangeV2, } ) @@ -77,12 +75,8 @@ const ( BlockByNumberMethod = "eth_getBlockByNumber" // GetPayloadBodiesByHashV1 is the engine_getPayloadBodiesByHashX JSON-RPC method for pre-Electra payloads. GetPayloadBodiesByHashV1 = "engine_getPayloadBodiesByHashV1" - // GetPayloadBodiesByHashV2 is the engine_getPayloadBodiesByHashX JSON-RPC method introduced by Electra. - GetPayloadBodiesByHashV2 = "engine_getPayloadBodiesByHashV2" // GetPayloadBodiesByRangeV1 is the engine_getPayloadBodiesByRangeX JSON-RPC method for pre-Electra payloads. GetPayloadBodiesByRangeV1 = "engine_getPayloadBodiesByRangeV1" - // GetPayloadBodiesByRangeV2 is the engine_getPayloadBodiesByRangeX JSON-RPC method introduced by Electra. - GetPayloadBodiesByRangeV2 = "engine_getPayloadBodiesByRangeV2" // ExchangeCapabilities request string for JSON-RPC. ExchangeCapabilities = "engine_exchangeCapabilities" // Defines the seconds before timing out engine endpoints with non-block execution semantics. diff --git a/beacon-chain/execution/mock_test.go b/beacon-chain/execution/mock_test.go index 046ccaaa5255..16f61f870855 100644 --- a/beacon-chain/execution/mock_test.go +++ b/beacon-chain/execution/mock_test.go @@ -3,7 +3,6 @@ package execution import ( "context" "encoding/json" - "math" "net/http" "net/http/httptest" "testing" @@ -131,21 +130,10 @@ func TestParseRequest(t *testing.T) { strToHexBytes(t, "0x66756c6c00000000000000000000000000000000000000000000000000000000"), }, }, - { - method: GetPayloadBodiesByHashV2, - byteArgs: []hexutil.Bytes{ - strToHexBytes(t, "0x656d707479000000000000000000000000000000000000000000000000000000"), - strToHexBytes(t, "0x66756c6c00000000000000000000000000000000000000000000000000000000"), - }, - }, { method: GetPayloadBodiesByRangeV1, hexArgs: []string{hexutil.EncodeUint64(0), hexutil.EncodeUint64(1)}, }, - { - method: GetPayloadBodiesByRangeV2, - hexArgs: []string{hexutil.EncodeUint64(math.MaxUint64), hexutil.EncodeUint64(1)}, - }, } for _, c := range cases { t.Run(c.method, func(t *testing.T) { @@ -191,9 +179,7 @@ func TestParseRequest(t *testing.T) { func TestCallCount(t *testing.T) { methods := []string{ GetPayloadBodiesByHashV1, - GetPayloadBodiesByHashV2, GetPayloadBodiesByRangeV1, - GetPayloadBodiesByRangeV2, } cases := []struct { method string @@ -201,10 +187,8 @@ func TestCallCount(t *testing.T) { }{ {method: GetPayloadBodiesByHashV1, count: 1}, {method: GetPayloadBodiesByHashV1, count: 2}, - {method: GetPayloadBodiesByHashV2, count: 1}, {method: GetPayloadBodiesByRangeV1, count: 1}, {method: GetPayloadBodiesByRangeV1, count: 2}, - {method: GetPayloadBodiesByRangeV2, count: 1}, } for _, c := range cases { t.Run(c.method, func(t *testing.T) { diff --git a/beacon-chain/execution/payload_body.go b/beacon-chain/execution/payload_body.go index 763cfd214501..17aba3329e0f 100644 --- a/beacon-chain/execution/payload_body.go +++ b/beacon-chain/execution/payload_body.go @@ -12,7 +12,6 @@ import ( "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" pb "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" - "github.com/prysmaticlabs/prysm/v5/runtime/version" "google.golang.org/protobuf/proto" ) @@ -87,10 +86,7 @@ func (r *blindedBlockReconstructor) addToBatch(b interfaces.ReadOnlySignedBeacon return nil } -func payloadBodyMethodForBlock(b interface{ Version() int }) string { - if b.Version() > version.Deneb { - return GetPayloadBodiesByHashV2 - } +func payloadBodyMethodForBlock(_ interface{ Version() int }) string { return GetPayloadBodiesByHashV1 } @@ -243,9 +239,6 @@ func (r *blindedBlockReconstructor) unblinded() ([]interfaces.SignedBeaconBlock, return unblinded, nil } -func rangeMethodForHashMethod(method string) string { - if method == GetPayloadBodiesByHashV2 { - return GetPayloadBodiesByRangeV2 - } +func rangeMethodForHashMethod(_ string) string { return GetPayloadBodiesByRangeV1 } diff --git a/beacon-chain/execution/payload_body_test.go b/beacon-chain/execution/payload_body_test.go index b4485499603a..f233ca63be5b 100644 --- a/beacon-chain/execution/payload_body_test.go +++ b/beacon-chain/execution/payload_body_test.go @@ -25,33 +25,6 @@ func (v versioner) Version() int { return v.version } -func TestPayloadBodyMethodForBlock(t *testing.T) { - cases := []struct { - versions []int - want string - }{ - { - versions: []int{version.Phase0, version.Altair, version.Bellatrix, version.Capella, version.Deneb}, - want: GetPayloadBodiesByHashV1, - }, - { - versions: []int{version.Electra}, - want: GetPayloadBodiesByHashV2, - }, - } - for _, c := range cases { - for _, v := range c.versions { - t.Run(version.String(v), func(t *testing.T) { - v := versioner{version: v} - require.Equal(t, c.want, payloadBodyMethodForBlock(v)) - }) - } - } - t.Run("post-electra", func(t *testing.T) { - require.Equal(t, GetPayloadBodiesByHashV2, payloadBodyMethodForBlock(versioner{version: version.Electra + 1})) - }) -} - func payloadToBody(t *testing.T, ed interfaces.ExecutionData) *pb.ExecutionPayloadBody { body := &pb.ExecutionPayloadBody{} txs, err := ed.Transactions() @@ -347,22 +320,6 @@ func TestReconstructBlindedBlockBatchFallbackToRange(t *testing.T) { } mockWriteResult(t, w, msg, executionPayloadBodies) }) - // separate methods for the electra block - srv.register(GetPayloadBodiesByHashV2, func(msg *jsonrpcMessage, w http.ResponseWriter, r *http.Request) { - executionPayloadBodies := []*pb.ExecutionPayloadBody{nil} - mockWriteResult(t, w, msg, executionPayloadBodies) - }) - srv.register(GetPayloadBodiesByRangeV2, func(msg *jsonrpcMessage, w http.ResponseWriter, r *http.Request) { - p := mockParseUintList(t, msg.Params) - require.Equal(t, 2, len(p)) - start, count := p[0], p[1] - require.Equal(t, fx.electra.blinded.header.BlockNumber(), start) - require.Equal(t, uint64(1), count) - executionPayloadBodies := []*pb.ExecutionPayloadBody{ - payloadToBody(t, fx.electra.blinded.header), - } - mockWriteResult(t, w, msg, executionPayloadBodies) - }) blind := []interfaces.ReadOnlySignedBeaconBlock{ fx.denebBlock.blinded.block, fx.emptyDenebBlock.blinded.block, @@ -381,13 +338,8 @@ func TestReconstructBlindedBlockBatchDenebAndElectra(t *testing.T) { t.Run("deneb and electra", func(t *testing.T) { cli, srv := newMockEngine(t) fx := testBlindedBlockFixtures(t) - // The reconstructed should make separate calls for the deneb (v1) and electra (v2) blocks. srv.register(GetPayloadBodiesByHashV1, func(msg *jsonrpcMessage, w http.ResponseWriter, r *http.Request) { - executionPayloadBodies := []*pb.ExecutionPayloadBody{payloadToBody(t, fx.denebBlock.blinded.header)} - mockWriteResult(t, w, msg, executionPayloadBodies) - }) - srv.register(GetPayloadBodiesByHashV2, func(msg *jsonrpcMessage, w http.ResponseWriter, r *http.Request) { - executionPayloadBodies := []*pb.ExecutionPayloadBody{payloadToBody(t, fx.electra.blinded.header)} + executionPayloadBodies := []*pb.ExecutionPayloadBody{payloadToBody(t, fx.denebBlock.blinded.header), payloadToBody(t, fx.electra.blinded.header)} mockWriteResult(t, w, msg, executionPayloadBodies) }) blinded := []interfaces.ReadOnlySignedBeaconBlock{ From 2afa63b44202064043b57a5eb83d2441d6b8444c Mon Sep 17 00:00:00 2001 From: Sammy Rosso <15244892+saolyn@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:23:23 +0200 Subject: [PATCH 13/26] Add GET `/eth/v2/beacon/pool/attester_slashings` (#14479) * add endpoint * changelog * correct resp with both attestationSlashings types * fix and comment * fix test * fix version check * review + fixes * fix * James' review * Review items * Radek' review * Radek' review --- CHANGELOG.md | 1 + api/server/structs/endpoints_beacon.go | 3 +- beacon-chain/rpc/endpoints.go | 9 + beacon-chain/rpc/endpoints_test.go | 2 +- beacon-chain/rpc/eth/beacon/handlers_pool.go | 64 ++++++- .../rpc/eth/beacon/handlers_pool_test.go | 179 ++++++++++++++++-- 6 files changed, 229 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbbb1e7f3035..1f36fa51b638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ Updating to this release is recommended at your convenience. - Add Electra support and tests for light client functions. - fastssz version bump (better error messages). - SSE implementation that sheds stuck clients. [pr](https://github.com/prysmaticlabs/prysm/pull/14413) +- Added GetPoolAttesterSlashingsV2 endpoint. ### Changed diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index 9073dbc6456f..aff21aad55a7 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -176,7 +176,8 @@ type BLSToExecutionChangesPoolResponse struct { } type GetAttesterSlashingsResponse struct { - Data []*AttesterSlashing `json:"data"` + Version string `json:"version,omitempty"` + Data json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types } type GetProposerSlashingsResponse struct { diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index ac51e8b25865..f6b47009582b 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -688,6 +688,15 @@ func (s *Service) beaconEndpoints( handler: server.GetAttesterSlashings, methods: []string{http.MethodGet}, }, + { + template: "/eth/v2/beacon/pool/attester_slashings", + name: namespace + ".GetAttesterSlashingsV2", + middleware: []middleware.Middleware{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetAttesterSlashingsV2, + methods: []string{http.MethodGet}, + }, { template: "/eth/v1/beacon/pool/attester_slashings", name: namespace + ".SubmitAttesterSlashings", diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index 185182ff49c9..d77dc4257b59 100644 --- a/beacon-chain/rpc/endpoints_test.go +++ b/beacon-chain/rpc/endpoints_test.go @@ -42,7 +42,7 @@ func Test_endpoints(t *testing.T) { "/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet}, "/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost}, "/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost}, - "/eth/v2/beacon/pool/attester_slashings": {http.MethodPost}, + "/eth/v2/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost}, "/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost}, "/eth/v1/beacon/pool/sync_committees": {http.MethodPost}, "/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost}, diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 0d92b94b0bb3..0b5612bae65d 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -468,19 +468,65 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { return } sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) - ss := make([]*eth.AttesterSlashing, 0, len(sourceSlashings)) - for _, slashing := range sourceSlashings { - s, ok := slashing.(*eth.AttesterSlashing) - if ok { - ss = append(ss, s) - } else { - httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) + slashings := make([]*structs.AttesterSlashing, len(sourceSlashings)) + for i, slashing := range sourceSlashings { + as, ok := slashing.(*eth.AttesterSlashing) + if !ok { + httputil.HandleError(w, fmt.Sprintf("Unable to convert slashing of type %T", slashing), http.StatusInternalServerError) return } + slashings[i] = structs.AttesterSlashingFromConsensus(as) } - slashings := structs.AttesterSlashingsFromConsensus(ss) + attBytes, err := json.Marshal(slashings) + if err != nil { + httputil.HandleError(w, fmt.Sprintf("Failed to marshal slashings: %v", err), http.StatusInternalServerError) + return + } + httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: attBytes}) +} - httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) +// GetAttesterSlashingsV2 retrieves attester slashings known by the node but +// not necessarily incorporated into any block, supporting both AttesterSlashing and AttesterSlashingElectra. +func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.GetAttesterSlashingsV2") + defer span.End() + + headState, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx) + if err != nil { + httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) + return + } + var attStructs []interface{} + sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) + for _, slashing := range sourceSlashings { + if slashing.Version() >= version.Electra { + a, ok := slashing.(*eth.AttesterSlashingElectra) + if !ok { + httputil.HandleError(w, fmt.Sprintf("Unable to convert electra slashing of type %T to an Electra slashing", slashing), http.StatusInternalServerError) + return + } + attStruct := structs.AttesterSlashingElectraFromConsensus(a) + attStructs = append(attStructs, attStruct) + } else { + a, ok := slashing.(*eth.AttesterSlashing) + if !ok { + httputil.HandleError(w, fmt.Sprintf("Unable to convert slashing of type %T to a Phase0 slashing", slashing), http.StatusInternalServerError) + return + } + attStruct := structs.AttesterSlashingFromConsensus(a) + attStructs = append(attStructs, attStruct) + } + } + attBytes, err := json.Marshal(attStructs) + if err != nil { + httputil.HandleError(w, fmt.Sprintf("Failed to marshal slashing: %v", err), http.StatusInternalServerError) + return + } + resp := &structs.GetAttesterSlashingsResponse{ + Version: version.String(sourceSlashings[0].Version()), + Data: attBytes, + } + httputil.WriteJson(w, resp) } // SubmitAttesterSlashings submits an attester slashing object to node's pool and diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 593eb8a3d854..9b549c1e45fe 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -985,9 +985,7 @@ func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) { } func TestGetAttesterSlashings(t *testing.T) { - bs, err := util.NewBeaconState() - require.NoError(t, err) - slashing1 := ðpbv1alpha1.AttesterSlashing{ + slashing1PreElectra := ðpbv1alpha1.AttesterSlashing{ Attestation_1: ðpbv1alpha1.IndexedAttestation{ AttestingIndices: []uint64{1, 10}, Data: ðpbv1alpha1.AttestationData{ @@ -1023,7 +1021,7 @@ func TestGetAttesterSlashings(t *testing.T) { Signature: bytesutil.PadTo([]byte("signature2"), 96), }, } - slashing2 := ðpbv1alpha1.AttesterSlashing{ + slashing2PreElectra := ðpbv1alpha1.AttesterSlashing{ Attestation_1: ðpbv1alpha1.IndexedAttestation{ AttestingIndices: []uint64{3, 30}, Data: ðpbv1alpha1.AttestationData{ @@ -1059,23 +1057,168 @@ func TestGetAttesterSlashings(t *testing.T) { Signature: bytesutil.PadTo([]byte("signature4"), 96), }, } - - s := &Server{ - ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, - SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, + slashing1PostElectra := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature1"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{2, 20}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 2, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 2, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 20, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature2"), 96), + }, + } + slashing2PostElectra := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{3, 30}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 3, + CommitteeIndex: 3, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 3, + Root: bytesutil.PadTo([]byte("sourceroot3"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 30, + Root: bytesutil.PadTo([]byte("targetroot3"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature3"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{4, 40}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 4, + CommitteeIndex: 4, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 4, + Root: bytesutil.PadTo([]byte("sourceroot4"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 40, + Root: bytesutil.PadTo([]byte("targetroot4"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature4"), 96), + }, } - request := httptest.NewRequest(http.MethodGet, "http://example.com/beacon/pool/attester_slashings", nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + t.Run("V1", func(t *testing.T) { + bs, err := util.NewBeaconState() + require.NoError(t, err) - s.GetAttesterSlashings(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - resp := &structs.GetAttesterSlashingsResponse{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) - require.NotNil(t, resp) - require.NotNil(t, resp.Data) - assert.Equal(t, 2, len(resp.Data)) + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1PreElectra, slashing2PreElectra}}, + } + + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetAttesterSlashings(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + + var slashings []*structs.AttesterSlashing + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + + ss, err := structs.AttesterSlashingsToConsensus(slashings) + require.NoError(t, err) + + require.DeepEqual(t, slashing1PreElectra, ss[0]) + require.DeepEqual(t, slashing2PreElectra, ss[1]) + }) + t.Run("V2-post-electra", func(t *testing.T) { + bs, err := util.NewBeaconStateElectra() + require.NoError(t, err) + + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1PostElectra, slashing2PostElectra}}, + } + + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + assert.Equal(t, "electra", resp.Version) + + // Unmarshal resp.Data into a slice of slashings + var slashings []*structs.AttesterSlashingElectra + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + + ss, err := structs.AttesterSlashingsElectraToConsensus(slashings) + require.NoError(t, err) + + require.DeepEqual(t, slashing1PostElectra, ss[0]) + require.DeepEqual(t, slashing2PostElectra, ss[1]) + }) + t.Run("V2-pre-electra", func(t *testing.T) { + bs, err := util.NewBeaconState() + require.NoError(t, err) + + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1PreElectra, slashing2PreElectra}}, + } + + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + + var slashings []*structs.AttesterSlashing + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + + ss, err := structs.AttesterSlashingsToConsensus(slashings) + require.NoError(t, err) + + require.DeepEqual(t, slashing1PreElectra, ss[0]) + require.DeepEqual(t, slashing2PreElectra, ss[1]) + }) } func TestGetProposerSlashings(t *testing.T) { From 1086bdf2b39946cc587660d0c165eec29cec34de Mon Sep 17 00:00:00 2001 From: kasey <489222+kasey@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:14:20 -0500 Subject: [PATCH 14/26] recover from panics when writing the event stream (#14545) * recover from panics when writing the event stream * changelog --------- Co-authored-by: Kasey Kirkham --- CHANGELOG.md | 1 + beacon-chain/rpc/eth/events/events.go | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f36fa51b638..3c1e04d74da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Fixed mesh size by appending `gParams.Dhi = gossipSubDhi` - Fix skipping partial withdrawals count. +- recover from panics when writing the event stream [pr](https://github.com/prysmaticlabs/prysm/pull/14545) ### Security diff --git a/beacon-chain/rpc/eth/events/events.go b/beacon-chain/rpc/eth/events/events.go index 28e31fc9840f..7698d2fb9017 100644 --- a/beacon-chain/rpc/eth/events/events.go +++ b/beacon-chain/rpc/eth/events/events.go @@ -71,6 +71,7 @@ var ( errSlowReader = errors.New("client failed to read fast enough to keep outgoing buffer below threshold") errNotRequested = errors.New("event not requested by client") errUnhandledEventData = errors.New("unable to represent event data in the event stream") + errWriterUnusable = errors.New("http response writer is unusable") ) // StreamingResponseWriter defines a type that can be used by the eventStreamer. @@ -309,10 +310,21 @@ func (es *eventStreamer) outboxWriteLoop(ctx context.Context, cancel context.Can } } +func writeLazyReaderWithRecover(w StreamingResponseWriter, lr lazyReader) (err error) { + defer func() { + if r := recover(); r != nil { + log.WithField("panic", r).Error("Recovered from panic while writing event to client.") + err = errWriterUnusable + } + }() + _, err = io.Copy(w, lr()) + return err +} + func (es *eventStreamer) writeOutbox(ctx context.Context, w StreamingResponseWriter, first lazyReader) error { needKeepAlive := true if first != nil { - if _, err := io.Copy(w, first()); err != nil { + if err := writeLazyReaderWithRecover(w, first); err != nil { return err } needKeepAlive = false @@ -325,13 +337,13 @@ func (es *eventStreamer) writeOutbox(ctx context.Context, w StreamingResponseWri case <-ctx.Done(): return ctx.Err() case rf := <-es.outbox: - if _, err := io.Copy(w, rf()); err != nil { + if err := writeLazyReaderWithRecover(w, rf); err != nil { return err } needKeepAlive = false default: if needKeepAlive { - if _, err := io.Copy(w, newlineReader()); err != nil { + if err := writeLazyReaderWithRecover(w, newlineReader); err != nil { return err } } From d6c5692dc0853eb55411561cc8cfd32021e60aae Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:42:29 -0500 Subject: [PATCH 15/26] Execution API Electra: requests as a sidecar (#14492) * wip * gaz * rename field * sammy review * updating execution api request and reverting response back * fixing linting * changelog * changelog * adding in serialization of requests * code cleanup * adding some happy path tests and fixing mock * mock still broken * fixing linting * updating name on proto * missed naming * placeholder fix for TestClient_HTTP * removing duplicate change log * adding in test for get payloadv4 as well as some tests * added tests for execution client testing, fixed encode type * adding comment for placeholder test * fixing test and addressing feedback * feedback * flipping the test names, was used in reverse * feedback from kasey * reverting switch back to if statements to fix bug * lint --- CHANGELOG.md | 1 + beacon-chain/blockchain/execution_engine.go | 18 +- beacon-chain/execution/engine_client.go | 26 +- beacon-chain/execution/engine_client_test.go | 368 +++++++++++++++++- .../execution/testing/mock_engine_client.go | 4 +- .../v1alpha1/validator/proposer_bellatrix.go | 8 + consensus-types/blocks/execution.go | 3 + consensus-types/blocks/get_payload.go | 15 +- consensus-types/blocks/setters.go | 10 + consensus-types/interfaces/beacon_block.go | 1 + proto/engine/v1/BUILD.bazel | 4 +- proto/engine/v1/electra.go | 114 ++++++ proto/engine/v1/electra.pb.go | 256 ++++++++---- proto/engine/v1/electra.proto | 14 +- proto/engine/v1/electra_test.go | 56 +++ proto/engine/v1/export_test.go | 8 + proto/engine/v1/json_marshal_unmarshal.go | 139 +++++++ .../engine/v1/json_marshal_unmarshal_test.go | 3 + .../shared/common/forkchoice/service.go | 2 +- 19 files changed, 946 insertions(+), 104 deletions(-) create mode 100644 proto/engine/v1/electra_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c1e04d74da8..f71d58c6ff6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ Updating to this release is recommended at your convenience. - GetBeaconStateV2: add Electra case. - Implement [consensus-specs/3875](https://github.com/ethereum/consensus-specs/pull/3875). - Tests to ensure sepolia config matches the official upstream yaml. +- `engine_newPayloadV4`,`engine_getPayloadV4` used for electra payload communication with execution client. [pr](https://github.com/prysmaticlabs/prysm/pull/14492) - HTTP endpoint for PublishBlobs. - GetBlockV2, GetBlindedBlock, ProduceBlockV2, ProduceBlockV3: add Electra case. - Add Electra support and tests for light client functions. diff --git a/beacon-chain/blockchain/execution_engine.go b/beacon-chain/blockchain/execution_engine.go index 9e7e440718cf..0317098b0923 100644 --- a/beacon-chain/blockchain/execution_engine.go +++ b/beacon-chain/blockchain/execution_engine.go @@ -216,17 +216,25 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, } var lastValidHash []byte + var parentRoot *common.Hash + var versionedHashes []common.Hash + var requests *enginev1.ExecutionRequests if blk.Version() >= version.Deneb { - var versionedHashes []common.Hash versionedHashes, err = kzgCommitmentsToVersionedHashes(blk.Block().Body()) if err != nil { return false, errors.Wrap(err, "could not get versioned hashes to feed the engine") } - pr := common.Hash(blk.Block().ParentRoot()) - lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, versionedHashes, &pr) - } else { - lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, []common.Hash{}, &common.Hash{} /*empty version hashes and root before Deneb*/) + prh := common.Hash(blk.Block().ParentRoot()) + parentRoot = &prh } + if blk.Version() >= version.Electra { + requests, err = blk.Block().Body().ExecutionRequests() + if err != nil { + return false, errors.Wrap(err, "could not get execution requests") + } + } + lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, versionedHashes, parentRoot, requests) + switch { case err == nil: newPayloadValidNodeCount.Inc() diff --git a/beacon-chain/execution/engine_client.go b/beacon-chain/execution/engine_client.go index 039707d8fca6..2dfc479f4214 100644 --- a/beacon-chain/execution/engine_client.go +++ b/beacon-chain/execution/engine_client.go @@ -108,7 +108,7 @@ type PayloadReconstructor interface { // EngineCaller defines a client that can interact with an Ethereum // execution node's engine service via JSON-RPC. type EngineCaller interface { - NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes []common.Hash, parentBlockRoot *common.Hash) ([]byte, error) + NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes []common.Hash, parentBlockRoot *common.Hash, executionRequests *pb.ExecutionRequests) ([]byte, error) ForkchoiceUpdated( ctx context.Context, state *pb.ForkchoiceState, attrs payloadattribute.Attributer, ) (*pb.PayloadIDBytes, []byte, error) @@ -119,8 +119,8 @@ type EngineCaller interface { var ErrEmptyBlockHash = errors.New("Block hash is empty 0x0000...") -// NewPayload calls the engine_newPayloadVX method via JSON-RPC. -func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes []common.Hash, parentBlockRoot *common.Hash) ([]byte, error) { +// NewPayload request calls the engine_newPayloadVX method via JSON-RPC. +func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes []common.Hash, parentBlockRoot *common.Hash, executionRequests *pb.ExecutionRequests) ([]byte, error) { ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.NewPayload") defer span.End() start := time.Now() @@ -157,9 +157,20 @@ func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionDa if !ok { return nil, errors.New("execution data must be a Deneb execution payload") } - err := s.rpcClient.CallContext(ctx, result, NewPayloadMethodV3, payloadPb, versionedHashes, parentBlockRoot) - if err != nil { - return nil, handleRPCError(err) + if executionRequests == nil { + err := s.rpcClient.CallContext(ctx, result, NewPayloadMethodV3, payloadPb, versionedHashes, parentBlockRoot) + if err != nil { + return nil, handleRPCError(err) + } + } else { + flattenedRequests, err := pb.EncodeExecutionRequests(executionRequests) + if err != nil { + return nil, errors.Wrap(err, "failed to encode execution requests") + } + err = s.rpcClient.CallContext(ctx, result, NewPayloadMethodV4, payloadPb, versionedHashes, parentBlockRoot, flattenedRequests) + if err != nil { + return nil, handleRPCError(err) + } } default: return nil, errors.New("unknown execution data type") @@ -253,6 +264,9 @@ func (s *Service) ForkchoiceUpdated( func getPayloadMethodAndMessage(slot primitives.Slot) (string, proto.Message) { pe := slots.ToEpoch(slot) + if pe >= params.BeaconConfig().ElectraForkEpoch { + return GetPayloadMethodV4, &pb.ExecutionBundleElectra{} + } if pe >= params.BeaconConfig().DenebForkEpoch { return GetPayloadMethodV3, &pb.ExecutionPayloadDenebWithValueAndBlobsBundle{} } diff --git a/beacon-chain/execution/engine_client_test.go b/beacon-chain/execution/engine_client_test.go index 7bee80ebdb9d..0e2ecb48bd2c 100644 --- a/beacon-chain/execution/engine_client_test.go +++ b/beacon-chain/execution/engine_client_test.go @@ -123,7 +123,7 @@ func TestClient_IPC(t *testing.T) { require.Equal(t, true, ok) wrappedPayload, err := blocks.WrappedExecutionPayload(req) require.NoError(t, err) - latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.NoError(t, err) require.DeepEqual(t, bytesutil.ToBytes32(want.LatestValidHash), bytesutil.ToBytes32(latestValidHash)) }) @@ -134,7 +134,7 @@ func TestClient_IPC(t *testing.T) { require.Equal(t, true, ok) wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(req) require.NoError(t, err) - latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.NoError(t, err) require.DeepEqual(t, bytesutil.ToBytes32(want.LatestValidHash), bytesutil.ToBytes32(latestValidHash)) }) @@ -163,7 +163,7 @@ func TestClient_HTTP(t *testing.T) { cfg := params.BeaconConfig().Copy() cfg.CapellaForkEpoch = 1 cfg.DenebForkEpoch = 2 - cfg.ElectraForkEpoch = 2 + cfg.ElectraForkEpoch = 3 params.OverrideBeaconConfig(cfg) t.Run(GetPayloadMethod, func(t *testing.T) { @@ -320,6 +320,89 @@ func TestClient_HTTP(t *testing.T) { blobs := [][]byte{bytesutil.PadTo([]byte("a"), fieldparams.BlobLength), bytesutil.PadTo([]byte("b"), fieldparams.BlobLength)} require.DeepEqual(t, blobs, resp.BlobsBundle.Blobs) }) + t.Run(GetPayloadMethodV4, func(t *testing.T) { + payloadId := [8]byte{1} + want, ok := fix["ExecutionBundleElectra"].(*pb.GetPayloadV4ResponseJson) + require.Equal(t, true, ok) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + defer func() { + require.NoError(t, r.Body.Close()) + }() + enc, err := io.ReadAll(r.Body) + require.NoError(t, err) + jsonRequestString := string(enc) + + reqArg, err := json.Marshal(pb.PayloadIDBytes(payloadId)) + require.NoError(t, err) + + // We expect the JSON string RPC request contains the right arguments. + require.Equal(t, true, strings.Contains( + jsonRequestString, string(reqArg), + )) + resp := map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": want, + } + err = json.NewEncoder(w).Encode(resp) + require.NoError(t, err) + })) + defer srv.Close() + + rpcClient, err := rpc.DialHTTP(srv.URL) + require.NoError(t, err) + defer rpcClient.Close() + + client := &Service{} + client.rpcClient = rpcClient + + // We call the RPC method via HTTP and expect a proper result. + resp, err := client.GetPayload(ctx, payloadId, 3*params.BeaconConfig().SlotsPerEpoch) + require.NoError(t, err) + require.Equal(t, true, resp.OverrideBuilder) + g, err := resp.ExecutionData.ExcessBlobGas() + require.NoError(t, err) + require.DeepEqual(t, uint64(3), g) + g, err = resp.ExecutionData.BlobGasUsed() + require.NoError(t, err) + require.DeepEqual(t, uint64(2), g) + + commitments := [][]byte{bytesutil.PadTo([]byte("commitment1"), fieldparams.BLSPubkeyLength), bytesutil.PadTo([]byte("commitment2"), fieldparams.BLSPubkeyLength)} + require.DeepEqual(t, commitments, resp.BlobsBundle.KzgCommitments) + proofs := [][]byte{bytesutil.PadTo([]byte("proof1"), fieldparams.BLSPubkeyLength), bytesutil.PadTo([]byte("proof2"), fieldparams.BLSPubkeyLength)} + require.DeepEqual(t, proofs, resp.BlobsBundle.Proofs) + blobs := [][]byte{bytesutil.PadTo([]byte("a"), fieldparams.BlobLength), bytesutil.PadTo([]byte("b"), fieldparams.BlobLength)} + require.DeepEqual(t, blobs, resp.BlobsBundle.Blobs) + requests := &pb.ExecutionRequests{ + Deposits: []*pb.DepositRequest{ + { + Pubkey: bytesutil.PadTo([]byte{byte('a')}, fieldparams.BLSPubkeyLength), + WithdrawalCredentials: bytesutil.PadTo([]byte{byte('b')}, fieldparams.RootLength), + Amount: params.BeaconConfig().MinActivationBalance, + Signature: bytesutil.PadTo([]byte{byte('c')}, fieldparams.BLSSignatureLength), + Index: 0, + }, + }, + Withdrawals: []*pb.WithdrawalRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('d')}, common.AddressLength), + ValidatorPubkey: bytesutil.PadTo([]byte{byte('e')}, fieldparams.BLSPubkeyLength), + Amount: params.BeaconConfig().MinActivationBalance, + }, + }, + Consolidations: []*pb.ConsolidationRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('f')}, common.AddressLength), + SourcePubkey: bytesutil.PadTo([]byte{byte('g')}, fieldparams.BLSPubkeyLength), + TargetPubkey: bytesutil.PadTo([]byte{byte('h')}, fieldparams.BLSPubkeyLength), + }, + }, + } + + require.DeepEqual(t, requests, resp.ExecutionRequests) + }) + t.Run(ForkchoiceUpdatedMethod+" VALID status", func(t *testing.T) { forkChoiceState := &pb.ForkchoiceState{ HeadBlockHash: []byte("head"), @@ -470,7 +553,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.NoError(t, err) require.DeepEqual(t, want.LatestValidHash, resp) }) @@ -484,7 +567,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.NoError(t, err) require.DeepEqual(t, want.LatestValidHash, resp) }) @@ -498,7 +581,46 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, nil) + require.NoError(t, err) + require.DeepEqual(t, want.LatestValidHash, resp) + }) + t.Run(NewPayloadMethodV4+" VALID status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayloadDeneb"].(*pb.ExecutionPayloadDeneb) + require.Equal(t, true, ok) + want, ok := fix["ValidPayloadStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + + // We call the RPC method via HTTP and expect a proper result. + wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) + require.NoError(t, err) + requests := &pb.ExecutionRequests{ + Deposits: []*pb.DepositRequest{ + { + Pubkey: bytesutil.PadTo([]byte{byte('a')}, fieldparams.BLSPubkeyLength), + WithdrawalCredentials: bytesutil.PadTo([]byte{byte('b')}, fieldparams.RootLength), + Amount: params.BeaconConfig().MinActivationBalance, + Signature: bytesutil.PadTo([]byte{byte('c')}, fieldparams.BLSSignatureLength), + Index: 0, + }, + }, + Withdrawals: []*pb.WithdrawalRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('d')}, common.AddressLength), + ValidatorPubkey: bytesutil.PadTo([]byte{byte('e')}, fieldparams.BLSPubkeyLength), + Amount: params.BeaconConfig().MinActivationBalance, + }, + }, + Consolidations: []*pb.ConsolidationRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('f')}, common.AddressLength), + SourcePubkey: bytesutil.PadTo([]byte{byte('g')}, fieldparams.BLSPubkeyLength), + TargetPubkey: bytesutil.PadTo([]byte{byte('h')}, fieldparams.BLSPubkeyLength), + }, + }, + } + client := newPayloadV4Setup(t, want, execPayload, requests) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, requests) require.NoError(t, err) require.DeepEqual(t, want.LatestValidHash, resp) }) @@ -512,7 +634,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err) require.DeepEqual(t, []uint8(nil), resp) }) @@ -526,7 +648,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err) require.DeepEqual(t, []uint8(nil), resp) }) @@ -540,7 +662,46 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, nil) + require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err) + require.DeepEqual(t, []uint8(nil), resp) + }) + t.Run(NewPayloadMethodV4+" SYNCING status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayloadDeneb"].(*pb.ExecutionPayloadDeneb) + require.Equal(t, true, ok) + want, ok := fix["SyncingStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + + // We call the RPC method via HTTP and expect a proper result. + wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) + require.NoError(t, err) + requests := &pb.ExecutionRequests{ + Deposits: []*pb.DepositRequest{ + { + Pubkey: bytesutil.PadTo([]byte{byte('a')}, fieldparams.BLSPubkeyLength), + WithdrawalCredentials: bytesutil.PadTo([]byte{byte('b')}, fieldparams.RootLength), + Amount: params.BeaconConfig().MinActivationBalance, + Signature: bytesutil.PadTo([]byte{byte('c')}, fieldparams.BLSSignatureLength), + Index: 0, + }, + }, + Withdrawals: []*pb.WithdrawalRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('d')}, common.AddressLength), + ValidatorPubkey: bytesutil.PadTo([]byte{byte('e')}, fieldparams.BLSPubkeyLength), + Amount: params.BeaconConfig().MinActivationBalance, + }, + }, + Consolidations: []*pb.ConsolidationRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('f')}, common.AddressLength), + SourcePubkey: bytesutil.PadTo([]byte{byte('g')}, fieldparams.BLSPubkeyLength), + TargetPubkey: bytesutil.PadTo([]byte{byte('h')}, fieldparams.BLSPubkeyLength), + }, + }, + } + client := newPayloadV4Setup(t, want, execPayload, requests) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, requests) require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err) require.DeepEqual(t, []uint8(nil), resp) }) @@ -554,7 +715,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err) require.DeepEqual(t, []uint8(nil), resp) }) @@ -568,7 +729,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err) require.DeepEqual(t, []uint8(nil), resp) }) @@ -582,7 +743,45 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, nil) + require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err) + require.DeepEqual(t, []uint8(nil), resp) + }) + t.Run(NewPayloadMethodV4+" INVALID_BLOCK_HASH status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayloadDeneb"].(*pb.ExecutionPayloadDeneb) + require.Equal(t, true, ok) + want, ok := fix["InvalidBlockHashStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + // We call the RPC method via HTTP and expect a proper result. + wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) + require.NoError(t, err) + requests := &pb.ExecutionRequests{ + Deposits: []*pb.DepositRequest{ + { + Pubkey: bytesutil.PadTo([]byte{byte('a')}, fieldparams.BLSPubkeyLength), + WithdrawalCredentials: bytesutil.PadTo([]byte{byte('b')}, fieldparams.RootLength), + Amount: params.BeaconConfig().MinActivationBalance, + Signature: bytesutil.PadTo([]byte{byte('c')}, fieldparams.BLSSignatureLength), + Index: 0, + }, + }, + Withdrawals: []*pb.WithdrawalRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('d')}, common.AddressLength), + ValidatorPubkey: bytesutil.PadTo([]byte{byte('e')}, fieldparams.BLSPubkeyLength), + Amount: params.BeaconConfig().MinActivationBalance, + }, + }, + Consolidations: []*pb.ConsolidationRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('f')}, common.AddressLength), + SourcePubkey: bytesutil.PadTo([]byte{byte('g')}, fieldparams.BLSPubkeyLength), + TargetPubkey: bytesutil.PadTo([]byte{byte('h')}, fieldparams.BLSPubkeyLength), + }, + }, + } + client := newPayloadV4Setup(t, want, execPayload, requests) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, requests) require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err) require.DeepEqual(t, []uint8(nil), resp) }) @@ -596,7 +795,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.ErrorIs(t, ErrInvalidPayloadStatus, err) require.DeepEqual(t, want.LatestValidHash, resp) }) @@ -610,7 +809,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.ErrorIs(t, ErrInvalidPayloadStatus, err) require.DeepEqual(t, want.LatestValidHash, resp) }) @@ -624,7 +823,46 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, nil) + require.ErrorIs(t, ErrInvalidPayloadStatus, err) + require.DeepEqual(t, want.LatestValidHash, resp) + }) + t.Run(NewPayloadMethodV4+" INVALID status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayloadDeneb"].(*pb.ExecutionPayloadDeneb) + require.Equal(t, true, ok) + want, ok := fix["InvalidStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + + // We call the RPC method via HTTP and expect a proper result. + wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload) + require.NoError(t, err) + requests := &pb.ExecutionRequests{ + Deposits: []*pb.DepositRequest{ + { + Pubkey: bytesutil.PadTo([]byte{byte('a')}, fieldparams.BLSPubkeyLength), + WithdrawalCredentials: bytesutil.PadTo([]byte{byte('b')}, fieldparams.RootLength), + Amount: params.BeaconConfig().MinActivationBalance, + Signature: bytesutil.PadTo([]byte{byte('c')}, fieldparams.BLSSignatureLength), + Index: 0, + }, + }, + Withdrawals: []*pb.WithdrawalRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('d')}, common.AddressLength), + ValidatorPubkey: bytesutil.PadTo([]byte{byte('e')}, fieldparams.BLSPubkeyLength), + Amount: params.BeaconConfig().MinActivationBalance, + }, + }, + Consolidations: []*pb.ConsolidationRequest{ + { + SourceAddress: bytesutil.PadTo([]byte{byte('f')}, common.AddressLength), + SourcePubkey: bytesutil.PadTo([]byte{byte('g')}, fieldparams.BLSPubkeyLength), + TargetPubkey: bytesutil.PadTo([]byte{byte('h')}, fieldparams.BLSPubkeyLength), + }, + }, + } + client := newPayloadV4Setup(t, want, execPayload, requests) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{'a'}, requests) require.ErrorIs(t, ErrInvalidPayloadStatus, err) require.DeepEqual(t, want.LatestValidHash, resp) }) @@ -638,7 +876,7 @@ func TestClient_HTTP(t *testing.T) { // We call the RPC method via HTTP and expect a proper result. wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload) require.NoError(t, err) - resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}) + resp, err := client.NewPayload(ctx, wrappedPayload, []common.Hash{}, &common.Hash{}, nil) require.ErrorIs(t, ErrUnknownPayloadStatus, err) require.DeepEqual(t, []uint8(nil), resp) }) @@ -1297,6 +1535,7 @@ func fixtures() map[string]interface{} { "ExecutionPayloadDeneb": s.ExecutionPayloadDeneb, "ExecutionPayloadCapellaWithValue": s.ExecutionPayloadWithValueCapella, "ExecutionPayloadDenebWithValue": s.ExecutionPayloadWithValueDeneb, + "ExecutionBundleElectra": s.ExecutionBundleElectra, "ValidPayloadStatus": s.ValidPayloadStatus, "InvalidBlockHashStatus": s.InvalidBlockHashStatus, "AcceptedStatus": s.AcceptedStatus, @@ -1483,6 +1722,53 @@ func fixturesStruct() *payloadFixtures { Blobs: []hexutil.Bytes{{'a'}, {'b'}}, }, } + + depositRequestBytes, err := hexutil.Decode("0x610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "620000000000000000000000000000000000000000000000000000000000000000" + + "4059730700000063000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000") + if err != nil { + panic("failed to decode deposit request") + } + withdrawalRequestBytes, err := hexutil.Decode("0x6400000000000000000000000000000000000000" + + "6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040597307000000") + if err != nil { + panic("failed to decode withdrawal request") + } + consolidationRequestBytes, err := hexutil.Decode("0x6600000000000000000000000000000000000000" + + "670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + if err != nil { + panic("failed to decode consolidation request") + } + executionBundleFixtureElectra := &pb.GetPayloadV4ResponseJson{ + ShouldOverrideBuilder: true, + ExecutionPayload: &pb.ExecutionPayloadDenebJSON{ + ParentHash: &common.Hash{'a'}, + FeeRecipient: &common.Address{'b'}, + StateRoot: &common.Hash{'c'}, + ReceiptsRoot: &common.Hash{'d'}, + LogsBloom: &hexutil.Bytes{'e'}, + PrevRandao: &common.Hash{'f'}, + BaseFeePerGas: "0x123", + BlockHash: &common.Hash{'g'}, + Transactions: []hexutil.Bytes{{'h'}}, + Withdrawals: []*pb.Withdrawal{}, + BlockNumber: &hexUint, + GasLimit: &hexUint, + GasUsed: &hexUint, + Timestamp: &hexUint, + BlobGasUsed: &bgu, + ExcessBlobGas: &ebg, + }, + BlockValue: "0x11fffffffff", + BlobsBundle: &pb.BlobBundleJSON{ + Commitments: []hexutil.Bytes{[]byte("commitment1"), []byte("commitment2")}, + Proofs: []hexutil.Bytes{[]byte("proof1"), []byte("proof2")}, + Blobs: []hexutil.Bytes{{'a'}, {'b'}}, + }, + ExecutionRequests: []hexutil.Bytes{depositRequestBytes, withdrawalRequestBytes, consolidationRequestBytes}, + } parent := bytesutil.PadTo([]byte("parentHash"), fieldparams.RootLength) sha3Uncles := bytesutil.PadTo([]byte("sha3Uncles"), fieldparams.RootLength) miner := bytesutil.PadTo([]byte("miner"), fieldparams.FeeRecipientLength) @@ -1576,6 +1862,7 @@ func fixturesStruct() *payloadFixtures { EmptyExecutionPayloadDeneb: emptyExecutionPayloadDeneb, ExecutionPayloadWithValueCapella: executionPayloadWithValueFixtureCapella, ExecutionPayloadWithValueDeneb: executionPayloadWithValueFixtureDeneb, + ExecutionBundleElectra: executionBundleFixtureElectra, ValidPayloadStatus: validStatus, InvalidBlockHashStatus: inValidBlockHashStatus, AcceptedStatus: acceptedStatus, @@ -1599,6 +1886,7 @@ type payloadFixtures struct { ExecutionPayloadDeneb *pb.ExecutionPayloadDeneb ExecutionPayloadWithValueCapella *pb.GetPayloadV2ResponseJson ExecutionPayloadWithValueDeneb *pb.GetPayloadV3ResponseJson + ExecutionBundleElectra *pb.GetPayloadV4ResponseJson ValidPayloadStatus *pb.PayloadStatus InvalidBlockHashStatus *pb.PayloadStatus AcceptedStatus *pb.PayloadStatus @@ -1957,6 +2245,54 @@ func newPayloadV3Setup(t *testing.T, status *pb.PayloadStatus, payload *pb.Execu return service } +func newPayloadV4Setup(t *testing.T, status *pb.PayloadStatus, payload *pb.ExecutionPayloadDeneb, requests *pb.ExecutionRequests) *Service { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + defer func() { + require.NoError(t, r.Body.Close()) + }() + enc, err := io.ReadAll(r.Body) + require.NoError(t, err) + jsonRequestString := string(enc) + require.Equal(t, true, strings.Contains( + jsonRequestString, string("engine_newPayloadV4"), + )) + + reqPayload, err := json.Marshal(payload) + require.NoError(t, err) + + // We expect the JSON string RPC request contains the right arguments. + require.Equal(t, true, strings.Contains( + jsonRequestString, string(reqPayload), + )) + + reqRequests, err := pb.EncodeExecutionRequests(requests) + require.NoError(t, err) + + jsonRequests, err := json.Marshal(reqRequests) + require.NoError(t, err) + + require.Equal(t, true, strings.Contains( + jsonRequestString, string(jsonRequests), + )) + + resp := map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": status, + } + err = json.NewEncoder(w).Encode(resp) + require.NoError(t, err) + })) + + rpcClient, err := rpc.DialHTTP(srv.URL) + require.NoError(t, err) + + service := &Service{} + service.rpcClient = rpcClient + return service +} + func TestReconstructBlindedBlockBatch(t *testing.T) { t.Run("empty response works", func(t *testing.T) { ctx := context.Background() diff --git a/beacon-chain/execution/testing/mock_engine_client.go b/beacon-chain/execution/testing/mock_engine_client.go index 5fd5c41bd25c..397384fb6d25 100644 --- a/beacon-chain/execution/testing/mock_engine_client.go +++ b/beacon-chain/execution/testing/mock_engine_client.go @@ -39,7 +39,7 @@ type EngineClient struct { } // NewPayload -- -func (e *EngineClient) NewPayload(_ context.Context, _ interfaces.ExecutionData, _ []common.Hash, _ *common.Hash) ([]byte, error) { +func (e *EngineClient) NewPayload(_ context.Context, _ interfaces.ExecutionData, _ []common.Hash, _ *common.Hash, _ *pb.ExecutionRequests) ([]byte, error) { return e.NewPayloadResp, e.ErrNewPayload } @@ -54,7 +54,7 @@ func (e *EngineClient) ForkchoiceUpdated( } // GetPayload -- -func (e *EngineClient) GetPayload(_ context.Context, _ [8]byte, s primitives.Slot) (*blocks.GetPayloadResponse, error) { +func (e *EngineClient) GetPayload(_ context.Context, _ [8]byte, _ primitives.Slot) (*blocks.GetPayloadResponse, error) { return e.GetPayloadResponse, e.ErrGetPayload } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go index 5380eb374a90..e9d74693cb91 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go @@ -77,6 +77,7 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc log.WithError(err).Warn("Proposer: failed to retrieve header from BuilderBid") return local.Bid, local.BlobsBundle, setLocalExecution(blk, local) } + //TODO: add builder execution requests here. if bid.Version() >= version.Deneb { builderKzgCommitments, err = bid.BlobKzgCommitments() if err != nil { @@ -353,12 +354,19 @@ func setLocalExecution(blk interfaces.SignedBeaconBlock, local *blocks.GetPayloa if local.BlobsBundle != nil { kzgCommitments = local.BlobsBundle.KzgCommitments } + if local.ExecutionRequests != nil { + if err := blk.SetExecutionRequests(local.ExecutionRequests); err != nil { + return errors.Wrap(err, "could not set execution requests") + } + } + return setExecution(blk, local.ExecutionData, false, kzgCommitments) } // setBuilderExecution sets the execution context for a builder's beacon block. // It delegates to setExecution for the actual work. func setBuilderExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData, builderKzgCommitments [][]byte) error { + // TODO #14344: add execution requests for electra return setExecution(blk, execution, true, builderKzgCommitments) } diff --git a/consensus-types/blocks/execution.go b/consensus-types/blocks/execution.go index ee74f18011b4..0c75f1d43128 100644 --- a/consensus-types/blocks/execution.go +++ b/consensus-types/blocks/execution.go @@ -37,6 +37,9 @@ func NewWrappedExecutionData(v proto.Message) (interfaces.ExecutionData, error) return WrappedExecutionPayloadDeneb(pbStruct) case *enginev1.ExecutionPayloadDenebWithValueAndBlobsBundle: return WrappedExecutionPayloadDeneb(pbStruct.Payload) + case *enginev1.ExecutionBundleElectra: + // note: no payload changes in electra so using deneb + return WrappedExecutionPayloadDeneb(pbStruct.Payload) default: return nil, ErrUnsupportedVersion } diff --git a/consensus-types/blocks/get_payload.go b/consensus-types/blocks/get_payload.go index c12e84a3acd0..9ee52ccfc7e9 100644 --- a/consensus-types/blocks/get_payload.go +++ b/consensus-types/blocks/get_payload.go @@ -14,7 +14,8 @@ type GetPayloadResponse struct { BlobsBundle *pb.BlobsBundle OverrideBuilder bool // todo: should we convert this to Gwei up front? - Bid primitives.Wei + Bid primitives.Wei + ExecutionRequests *pb.ExecutionRequests } // bundleGetter is an interface satisfied by get payload responses that have a blobs bundle. @@ -31,6 +32,10 @@ type shouldOverrideBuilderGetter interface { GetShouldOverrideBuilder() bool } +type executionRequestsGetter interface { + GetDecodedExecutionRequests() (*pb.ExecutionRequests, error) +} + func NewGetPayloadResponse(msg proto.Message) (*GetPayloadResponse, error) { r := &GetPayloadResponse{} bundleGetter, hasBundle := msg.(bundleGetter) @@ -38,6 +43,7 @@ func NewGetPayloadResponse(msg proto.Message) (*GetPayloadResponse, error) { r.BlobsBundle = bundleGetter.GetBlobsBundle() } bidValueGetter, hasBid := msg.(bidValueGetter) + executionRequestsGetter, hasExecutionRequests := msg.(executionRequestsGetter) wei := primitives.ZeroWei() if hasBid { // The protobuf types that engine api responses unmarshal into store their values in little endian form. @@ -56,5 +62,12 @@ func NewGetPayloadResponse(msg proto.Message) (*GetPayloadResponse, error) { return nil, err } r.ExecutionData = ed + if hasExecutionRequests { + requests, err := executionRequestsGetter.GetDecodedExecutionRequests() + if err != nil { + return nil, err + } + r.ExecutionRequests = requests + } return r, nil } diff --git a/consensus-types/blocks/setters.go b/consensus-types/blocks/setters.go index 3650903404d0..a2f376c76c0f 100644 --- a/consensus-types/blocks/setters.go +++ b/consensus-types/blocks/setters.go @@ -6,6 +6,7 @@ import ( consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" ) @@ -172,3 +173,12 @@ func (b *SignedBeaconBlock) SetBlobKzgCommitments(c [][]byte) error { b.block.body.blobKzgCommitments = c return nil } + +// SetExecutionRequests sets the execution requests in the block. +func (b *SignedBeaconBlock) SetExecutionRequests(req *enginev1.ExecutionRequests) error { + if b.version < version.Electra { + return consensus_types.ErrNotSupported("SetExecutionRequests", b.version) + } + b.block.body.executionRequests = req + return nil +} diff --git a/consensus-types/interfaces/beacon_block.go b/consensus-types/interfaces/beacon_block.go index ff3e83e61fef..1e9c704d2e7c 100644 --- a/consensus-types/interfaces/beacon_block.go +++ b/consensus-types/interfaces/beacon_block.go @@ -91,6 +91,7 @@ type SignedBeaconBlock interface { SetProposerIndex(idx primitives.ValidatorIndex) SetSlot(slot primitives.Slot) SetSignature(sig []byte) + SetExecutionRequests(er *enginev1.ExecutionRequests) error Unblind(e ExecutionData) error } diff --git a/proto/engine/v1/BUILD.bazel b/proto/engine/v1/BUILD.bazel index f496c41b1814..fad2e889531d 100644 --- a/proto/engine/v1/BUILD.bazel +++ b/proto/engine/v1/BUILD.bazel @@ -48,7 +48,7 @@ ssz_gen_marshal( "WithdrawalRequest", "DepositRequest", "ConsolidationRequest", - "ExecutionRequests", + "ExecutionRequests", ], ) @@ -74,7 +74,7 @@ go_proto_library( go_library( name = "go_default_library", srcs = [ - "electra.go", + "electra.go", "execution_engine.go", "json_marshal_unmarshal.go", ":ssz_generated_files", # keep diff --git a/proto/engine/v1/electra.go b/proto/engine/v1/electra.go index 386cce1bfe16..90d003ec2a7d 100644 --- a/proto/engine/v1/electra.go +++ b/proto/engine/v1/electra.go @@ -1,4 +1,118 @@ package enginev1 +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" +) + type ExecutionPayloadElectra = ExecutionPayloadDeneb type ExecutionPayloadHeaderElectra = ExecutionPayloadHeaderDeneb + +var ( + drExample = &DepositRequest{} + drSize = drExample.SizeSSZ() + wrExample = &WithdrawalRequest{} + wrSize = wrExample.SizeSSZ() + crExample = &ConsolidationRequest{} + crSize = crExample.SizeSSZ() +) + +const LenExecutionRequestsElectra = 3 + +func (ebe *ExecutionBundleElectra) GetDecodedExecutionRequests() (*ExecutionRequests, error) { + requests := &ExecutionRequests{} + + if len(ebe.ExecutionRequests) != LenExecutionRequestsElectra /* types of requests */ { + return nil, errors.Errorf("invalid execution request size: %d", len(ebe.ExecutionRequests)) + } + + // deposit requests + drs, err := unmarshalItems(ebe.ExecutionRequests[0], drSize, func() *DepositRequest { return &DepositRequest{} }) + if err != nil { + return nil, err + } + requests.Deposits = drs + + // withdrawal requests + wrs, err := unmarshalItems(ebe.ExecutionRequests[1], wrSize, func() *WithdrawalRequest { return &WithdrawalRequest{} }) + if err != nil { + return nil, err + } + requests.Withdrawals = wrs + + // consolidation requests + crs, err := unmarshalItems(ebe.ExecutionRequests[2], crSize, func() *ConsolidationRequest { return &ConsolidationRequest{} }) + if err != nil { + return nil, err + } + requests.Consolidations = crs + + return requests, nil +} + +func EncodeExecutionRequests(requests *ExecutionRequests) ([]hexutil.Bytes, error) { + if requests == nil { + return nil, errors.New("invalid execution requests") + } + + drBytes, err := marshalItems(requests.Deposits) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal deposit requests") + } + + wrBytes, err := marshalItems(requests.Withdrawals) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal withdrawal requests") + } + + crBytes, err := marshalItems(requests.Consolidations) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal consolidation requests") + } + return []hexutil.Bytes{drBytes, wrBytes, crBytes}, nil +} + +type sszUnmarshaler interface { + UnmarshalSSZ([]byte) error +} + +type sszMarshaler interface { + MarshalSSZTo(buf []byte) ([]byte, error) + SizeSSZ() int +} + +func marshalItems[T sszMarshaler](items []T) ([]byte, error) { + if len(items) == 0 { + return []byte{}, nil + } + size := items[0].SizeSSZ() + buf := make([]byte, 0, size*len(items)) + var err error + for i, item := range items { + buf, err = item.MarshalSSZTo(buf) + if err != nil { + return nil, fmt.Errorf("failed to marshal item at index %d: %w", i, err) + } + } + return buf, nil +} + +// Generic function to unmarshal items +func unmarshalItems[T sszUnmarshaler](data []byte, itemSize int, newItem func() T) ([]T, error) { + if len(data)%itemSize != 0 { + return nil, fmt.Errorf("invalid data length: data size (%d) is not a multiple of item size (%d)", len(data), itemSize) + } + numItems := len(data) / itemSize + items := make([]T, numItems) + for i := range items { + itemBytes := data[i*itemSize : (i+1)*itemSize] + item := newItem() + if err := item.UnmarshalSSZ(itemBytes); err != nil { + return nil, fmt.Errorf("failed to unmarshal item at index %d: %w", i, err) + } + items[i] = item + } + return items, nil +} diff --git a/proto/engine/v1/electra.pb.go b/proto/engine/v1/electra.pb.go index a23e9a678154..aa0e0809d7a1 100755 --- a/proto/engine/v1/electra.pb.go +++ b/proto/engine/v1/electra.pb.go @@ -290,6 +290,85 @@ func (x *ExecutionRequests) GetConsolidations() []*ConsolidationRequest { return nil } +type ExecutionBundleElectra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload *ExecutionPayloadDeneb `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + BlobsBundle *BlobsBundle `protobuf:"bytes,3,opt,name=blobs_bundle,json=blobsBundle,proto3" json:"blobs_bundle,omitempty"` + ShouldOverrideBuilder bool `protobuf:"varint,4,opt,name=should_override_builder,json=shouldOverrideBuilder,proto3" json:"should_override_builder,omitempty"` + ExecutionRequests [][]byte `protobuf:"bytes,5,rep,name=execution_requests,json=executionRequests,proto3" json:"execution_requests,omitempty"` +} + +func (x *ExecutionBundleElectra) Reset() { + *x = ExecutionBundleElectra{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_engine_v1_electra_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecutionBundleElectra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecutionBundleElectra) ProtoMessage() {} + +func (x *ExecutionBundleElectra) ProtoReflect() protoreflect.Message { + mi := &file_proto_engine_v1_electra_proto_msgTypes[4] + 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 ExecutionBundleElectra.ProtoReflect.Descriptor instead. +func (*ExecutionBundleElectra) Descriptor() ([]byte, []int) { + return file_proto_engine_v1_electra_proto_rawDescGZIP(), []int{4} +} + +func (x *ExecutionBundleElectra) GetPayload() *ExecutionPayloadDeneb { + if x != nil { + return x.Payload + } + return nil +} + +func (x *ExecutionBundleElectra) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (x *ExecutionBundleElectra) GetBlobsBundle() *BlobsBundle { + if x != nil { + return x.BlobsBundle + } + return nil +} + +func (x *ExecutionBundleElectra) GetShouldOverrideBuilder() bool { + if x != nil { + return x.ShouldOverrideBuilder + } + return false +} + +func (x *ExecutionBundleElectra) GetExecutionRequests() [][]byte { + if x != nil { + return x.ExecutionRequests + } + return nil +} + var File_proto_engine_v1_electra_proto protoreflect.FileDescriptor var file_proto_engine_v1_electra_proto_rawDesc = []byte{ @@ -298,64 +377,85 @@ var file_proto_engine_v1_electra_proto_rawDesc = []byte{ 0x12, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 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, - 0x22, 0x8d, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x31, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0xc3, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x12, 0x3d, 0x0a, 0x16, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x15, 0x77, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 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, - 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x9f, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x73, 0x6f, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2d, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, - 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, - 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0c, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0d, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x87, 0x02, 0x0a, 0x11, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x48, - 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x42, 0x08, 0x92, 0xb5, 0x18, 0x04, 0x38, 0x31, 0x39, 0x32, 0x52, 0x08, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x4f, 0x0a, 0x0b, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0b, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x12, 0x57, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, - 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x05, 0x92, 0xb5, 0x18, - 0x01, 0x31, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x42, 0x8e, 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, + 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, + 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, + 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0d, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x31, 0x0a, + 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, + 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc3, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x06, 0x70, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, + 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x3d, 0x0a, 0x16, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, + 0x02, 0x33, 0x32, 0x52, 0x15, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 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, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x9f, + 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x75, + 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, + 0x34, 0x38, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x22, 0x87, 0x02, 0x0a, 0x11, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x48, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x08, 0x92, 0xb5, + 0x18, 0x04, 0x38, 0x31, 0x39, 0x32, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x12, 0x4f, 0x0a, 0x0b, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x06, 0x92, 0xb5, + 0x18, 0x02, 0x31, 0x36, 0x52, 0x0b, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x73, 0x12, 0x57, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x73, + 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x9e, 0x02, 0x0a, 0x16, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x6c, + 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, 0x65, + 0x62, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x42, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x62, + 0x73, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x42, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x6f, + 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x4f, 0x76, 0x65, + 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 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, 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, } var ( @@ -370,22 +470,27 @@ func file_proto_engine_v1_electra_proto_rawDescGZIP() []byte { return file_proto_engine_v1_electra_proto_rawDescData } -var file_proto_engine_v1_electra_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_proto_engine_v1_electra_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_proto_engine_v1_electra_proto_goTypes = []interface{}{ - (*WithdrawalRequest)(nil), // 0: ethereum.engine.v1.WithdrawalRequest - (*DepositRequest)(nil), // 1: ethereum.engine.v1.DepositRequest - (*ConsolidationRequest)(nil), // 2: ethereum.engine.v1.ConsolidationRequest - (*ExecutionRequests)(nil), // 3: ethereum.engine.v1.ExecutionRequests + (*WithdrawalRequest)(nil), // 0: ethereum.engine.v1.WithdrawalRequest + (*DepositRequest)(nil), // 1: ethereum.engine.v1.DepositRequest + (*ConsolidationRequest)(nil), // 2: ethereum.engine.v1.ConsolidationRequest + (*ExecutionRequests)(nil), // 3: ethereum.engine.v1.ExecutionRequests + (*ExecutionBundleElectra)(nil), // 4: ethereum.engine.v1.ExecutionBundleElectra + (*ExecutionPayloadDeneb)(nil), // 5: ethereum.engine.v1.ExecutionPayloadDeneb + (*BlobsBundle)(nil), // 6: ethereum.engine.v1.BlobsBundle } var file_proto_engine_v1_electra_proto_depIdxs = []int32{ 1, // 0: ethereum.engine.v1.ExecutionRequests.deposits:type_name -> ethereum.engine.v1.DepositRequest 0, // 1: ethereum.engine.v1.ExecutionRequests.withdrawals:type_name -> ethereum.engine.v1.WithdrawalRequest 2, // 2: ethereum.engine.v1.ExecutionRequests.consolidations:type_name -> ethereum.engine.v1.ConsolidationRequest - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 5, // 3: ethereum.engine.v1.ExecutionBundleElectra.payload:type_name -> ethereum.engine.v1.ExecutionPayloadDeneb + 6, // 4: ethereum.engine.v1.ExecutionBundleElectra.blobs_bundle:type_name -> ethereum.engine.v1.BlobsBundle + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_proto_engine_v1_electra_proto_init() } @@ -393,6 +498,7 @@ func file_proto_engine_v1_electra_proto_init() { if File_proto_engine_v1_electra_proto != nil { return } + file_proto_engine_v1_execution_engine_proto_init() if !protoimpl.UnsafeEnabled { file_proto_engine_v1_electra_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WithdrawalRequest); i { @@ -442,6 +548,18 @@ func file_proto_engine_v1_electra_proto_init() { return nil } } + file_proto_engine_v1_electra_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecutionBundleElectra); 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{ @@ -449,7 +567,7 @@ func file_proto_engine_v1_electra_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_engine_v1_electra_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/engine/v1/electra.proto b/proto/engine/v1/electra.proto index 5b77f682d475..9ac48bf0d478 100644 --- a/proto/engine/v1/electra.proto +++ b/proto/engine/v1/electra.proto @@ -16,6 +16,7 @@ syntax = "proto3"; package ethereum.engine.v1; 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"; @@ -60,7 +61,16 @@ message ConsolidationRequest { // ExecutionRequests is a container that contains all the requests from the execution layer to be included in a block message ExecutionRequests { - repeated DepositRequest deposits = 1 [(ethereum.eth.ext.ssz_max) = "max_deposit_requests_per_payload.size"]; + repeated DepositRequest deposits = 1 [(ethereum.eth.ext.ssz_max) = "max_deposit_requests_per_payload.size"]; repeated WithdrawalRequest withdrawals = 2 [(ethereum.eth.ext.ssz_max) = "max_withdrawal_requests_per_payload.size"]; - repeated ConsolidationRequest consolidations = 3 [(ethereum.eth.ext.ssz_max) = "max_consolidation_requests_per_payload.size"]; + repeated ConsolidationRequest consolidations = 3 [(ethereum.eth.ext.ssz_max) = "max_consolidation_requests_per_payload.size"]; +} + +// ExecutionBundleElectra is a container that builds on Payload V4 and includes execution requests sidecar needed post Electra +message ExecutionBundleElectra { + ExecutionPayloadDeneb payload = 1; + bytes value = 2; + BlobsBundle blobs_bundle = 3; + bool should_override_builder = 4; + repeated bytes execution_requests = 5; } diff --git a/proto/engine/v1/electra_test.go b/proto/engine/v1/electra_test.go new file mode 100644 index 000000000000..2d4c9903b98f --- /dev/null +++ b/proto/engine/v1/electra_test.go @@ -0,0 +1,56 @@ +package enginev1_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" + enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" + "github.com/prysmaticlabs/prysm/v5/testing/require" +) + +var depositRequestsSSZHex = "0x706b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077630000000000000000000000000000000000000000000000000000000000007b00000000000000736967000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c801000000000000706b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000776300000000000000000000000000000000000000000000000000000000000090010000000000007369670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000" + +func TestUnmarshalItems_OK(t *testing.T) { + drb, err := hexutil.Decode(depositRequestsSSZHex) + require.NoError(t, err) + exampleRequest := &enginev1.DepositRequest{} + depositRequests, err := enginev1.UnmarshalItems(drb, exampleRequest.SizeSSZ(), func() *enginev1.DepositRequest { return &enginev1.DepositRequest{} }) + require.NoError(t, err) + + exampleRequest1 := &enginev1.DepositRequest{ + Pubkey: bytesutil.PadTo([]byte("pk"), 48), + WithdrawalCredentials: bytesutil.PadTo([]byte("wc"), 32), + Amount: 123, + Signature: bytesutil.PadTo([]byte("sig"), 96), + Index: 456, + } + exampleRequest2 := &enginev1.DepositRequest{ + Pubkey: bytesutil.PadTo([]byte("pk"), 48), + WithdrawalCredentials: bytesutil.PadTo([]byte("wc"), 32), + Amount: 400, + Signature: bytesutil.PadTo([]byte("sig"), 96), + Index: 32, + } + require.DeepEqual(t, depositRequests, []*enginev1.DepositRequest{exampleRequest1, exampleRequest2}) +} + +func TestMarshalItems_OK(t *testing.T) { + exampleRequest1 := &enginev1.DepositRequest{ + Pubkey: bytesutil.PadTo([]byte("pk"), 48), + WithdrawalCredentials: bytesutil.PadTo([]byte("wc"), 32), + Amount: 123, + Signature: bytesutil.PadTo([]byte("sig"), 96), + Index: 456, + } + exampleRequest2 := &enginev1.DepositRequest{ + Pubkey: bytesutil.PadTo([]byte("pk"), 48), + WithdrawalCredentials: bytesutil.PadTo([]byte("wc"), 32), + Amount: 400, + Signature: bytesutil.PadTo([]byte("sig"), 96), + Index: 32, + } + drbs, err := enginev1.MarshalItems([]*enginev1.DepositRequest{exampleRequest1, exampleRequest2}) + require.NoError(t, err) + require.DeepEqual(t, depositRequestsSSZHex, hexutil.Encode(drbs)) +} diff --git a/proto/engine/v1/export_test.go b/proto/engine/v1/export_test.go index 17f4cde64607..2466b7fa44d1 100644 --- a/proto/engine/v1/export_test.go +++ b/proto/engine/v1/export_test.go @@ -1,3 +1,11 @@ package enginev1 type Copier[T any] copier[T] + +func MarshalItems[T sszMarshaler](items []T) ([]byte, error) { + return marshalItems(items) +} + +func UnmarshalItems[T sszUnmarshaler](data []byte, itemSize int, newItem func() T) ([]T, error) { + return unmarshalItems(data, itemSize, newItem) +} diff --git a/proto/engine/v1/json_marshal_unmarshal.go b/proto/engine/v1/json_marshal_unmarshal.go index 13ed85726cad..c06b937136d0 100644 --- a/proto/engine/v1/json_marshal_unmarshal.go +++ b/proto/engine/v1/json_marshal_unmarshal.go @@ -297,6 +297,14 @@ type GetPayloadV3ResponseJson struct { ShouldOverrideBuilder bool `json:"shouldOverrideBuilder"` } +type GetPayloadV4ResponseJson struct { + ExecutionPayload *ExecutionPayloadDenebJSON `json:"executionPayload"` + BlockValue string `json:"blockValue"` + BlobsBundle *BlobBundleJSON `json:"blobsBundle"` + ShouldOverrideBuilder bool `json:"shouldOverrideBuilder"` + ExecutionRequests []hexutil.Bytes `json:"executionRequests"` +} + // ExecutionPayloadBody represents the engine API ExecutionPayloadV1 or ExecutionPayloadV2 type. type ExecutionPayloadBody struct { Transactions []hexutil.Bytes `json:"transactions"` @@ -1112,6 +1120,137 @@ func (e *ExecutionPayloadDenebWithValueAndBlobsBundle) UnmarshalJSON(enc []byte) return nil } +func (e *ExecutionBundleElectra) UnmarshalJSON(enc []byte) error { + dec := GetPayloadV4ResponseJson{} + if err := json.Unmarshal(enc, &dec); err != nil { + return err + } + + if dec.ExecutionPayload.ParentHash == nil { + return errors.New("missing required field 'parentHash' for ExecutionPayload") + } + if dec.ExecutionPayload.FeeRecipient == nil { + return errors.New("missing required field 'feeRecipient' for ExecutionPayload") + } + if dec.ExecutionPayload.StateRoot == nil { + return errors.New("missing required field 'stateRoot' for ExecutionPayload") + } + if dec.ExecutionPayload.ReceiptsRoot == nil { + return errors.New("missing required field 'receiptsRoot' for ExecutableDataV1") + } + if dec.ExecutionPayload.LogsBloom == nil { + return errors.New("missing required field 'logsBloom' for ExecutionPayload") + } + if dec.ExecutionPayload.PrevRandao == nil { + return errors.New("missing required field 'prevRandao' for ExecutionPayload") + } + if dec.ExecutionPayload.ExtraData == nil { + return errors.New("missing required field 'extraData' for ExecutionPayload") + } + if dec.ExecutionPayload.BlockHash == nil { + return errors.New("missing required field 'blockHash' for ExecutionPayload") + } + if dec.ExecutionPayload.Transactions == nil { + return errors.New("missing required field 'transactions' for ExecutionPayload") + } + if dec.ExecutionPayload.BlockNumber == nil { + return errors.New("missing required field 'blockNumber' for ExecutionPayload") + } + if dec.ExecutionPayload.Timestamp == nil { + return errors.New("missing required field 'timestamp' for ExecutionPayload") + } + if dec.ExecutionPayload.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for ExecutionPayload") + } + if dec.ExecutionPayload.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for ExecutionPayload") + } + if dec.ExecutionPayload.BlobGasUsed == nil { + return errors.New("missing required field 'blobGasUsed' for ExecutionPayload") + } + if dec.ExecutionPayload.ExcessBlobGas == nil { + return errors.New("missing required field 'excessBlobGas' for ExecutionPayload") + } + + *e = ExecutionBundleElectra{Payload: &ExecutionPayloadDeneb{}} + e.Payload.ParentHash = dec.ExecutionPayload.ParentHash.Bytes() + e.Payload.FeeRecipient = dec.ExecutionPayload.FeeRecipient.Bytes() + e.Payload.StateRoot = dec.ExecutionPayload.StateRoot.Bytes() + e.Payload.ReceiptsRoot = dec.ExecutionPayload.ReceiptsRoot.Bytes() + e.Payload.LogsBloom = *dec.ExecutionPayload.LogsBloom + e.Payload.PrevRandao = dec.ExecutionPayload.PrevRandao.Bytes() + e.Payload.BlockNumber = uint64(*dec.ExecutionPayload.BlockNumber) + e.Payload.GasLimit = uint64(*dec.ExecutionPayload.GasLimit) + e.Payload.GasUsed = uint64(*dec.ExecutionPayload.GasUsed) + e.Payload.Timestamp = uint64(*dec.ExecutionPayload.Timestamp) + e.Payload.ExtraData = dec.ExecutionPayload.ExtraData + baseFee, err := hexutil.DecodeBig(dec.ExecutionPayload.BaseFeePerGas) + if err != nil { + return err + } + e.Payload.BaseFeePerGas = bytesutil.PadTo(bytesutil.ReverseByteOrder(baseFee.Bytes()), fieldparams.RootLength) + + e.Payload.ExcessBlobGas = uint64(*dec.ExecutionPayload.ExcessBlobGas) + e.Payload.BlobGasUsed = uint64(*dec.ExecutionPayload.BlobGasUsed) + + e.Payload.BlockHash = dec.ExecutionPayload.BlockHash.Bytes() + transactions := make([][]byte, len(dec.ExecutionPayload.Transactions)) + for i, tx := range dec.ExecutionPayload.Transactions { + transactions[i] = tx + } + e.Payload.Transactions = transactions + if dec.ExecutionPayload.Withdrawals == nil { + dec.ExecutionPayload.Withdrawals = make([]*Withdrawal, 0) + } + e.Payload.Withdrawals = dec.ExecutionPayload.Withdrawals + + v, err := hexutil.DecodeBig(dec.BlockValue) + if err != nil { + return err + } + e.Value = bytesutil.PadTo(bytesutil.ReverseByteOrder(v.Bytes()), fieldparams.RootLength) + + if dec.BlobsBundle == nil { + return nil + } + e.BlobsBundle = &BlobsBundle{} + + commitments := make([][]byte, len(dec.BlobsBundle.Commitments)) + for i, kzg := range dec.BlobsBundle.Commitments { + k := kzg + commitments[i] = bytesutil.PadTo(k[:], fieldparams.BLSPubkeyLength) + } + e.BlobsBundle.KzgCommitments = commitments + + proofs := make([][]byte, len(dec.BlobsBundle.Proofs)) + for i, proof := range dec.BlobsBundle.Proofs { + p := proof + proofs[i] = bytesutil.PadTo(p[:], fieldparams.BLSPubkeyLength) + } + e.BlobsBundle.Proofs = proofs + + blobs := make([][]byte, len(dec.BlobsBundle.Blobs)) + for i, blob := range dec.BlobsBundle.Blobs { + b := make([]byte, fieldparams.BlobLength) + copy(b, blob) + blobs[i] = b + } + e.BlobsBundle.Blobs = blobs + + e.ShouldOverrideBuilder = dec.ShouldOverrideBuilder + + requests := make([][]byte, len(dec.ExecutionRequests)) + for i, request := range dec.ExecutionRequests { + r := make([]byte, len(request)) + copy(r, request) + requests[i] = r + } + + e.ExecutionRequests = requests + + return nil +} + // RecastHexutilByteSlice converts a []hexutil.Bytes to a [][]byte func RecastHexutilByteSlice(h []hexutil.Bytes) [][]byte { r := make([][]byte, len(h)) diff --git a/proto/engine/v1/json_marshal_unmarshal_test.go b/proto/engine/v1/json_marshal_unmarshal_test.go index 9ce48ac0b680..a3104f42fef9 100644 --- a/proto/engine/v1/json_marshal_unmarshal_test.go +++ b/proto/engine/v1/json_marshal_unmarshal_test.go @@ -607,6 +607,9 @@ func TestJsonMarshalUnmarshal(t *testing.T) { // Expect no transaction objects in the unmarshaled data. require.Equal(t, 0, len(payloadPb.Transactions)) }) + t.Run("execution bundle electra with deneb payload, blob data, and execution requests", func(t *testing.T) { + // TODO #14351: update this test when geth updates + }) } func TestPayloadIDBytes_MarshalUnmarshalJSON(t *testing.T) { diff --git a/testing/spectest/shared/common/forkchoice/service.go b/testing/spectest/shared/common/forkchoice/service.go index 6395907b9da2..648f6bd42523 100644 --- a/testing/spectest/shared/common/forkchoice/service.go +++ b/testing/spectest/shared/common/forkchoice/service.go @@ -103,7 +103,7 @@ func (m *engineMock) ForkchoiceUpdated(context.Context, *pb.ForkchoiceState, pay return nil, m.latestValidHash, m.payloadStatus } -func (m *engineMock) NewPayload(context.Context, interfaces.ExecutionData, []common.Hash, *common.Hash) ([]byte, error) { +func (m *engineMock) NewPayload(context.Context, interfaces.ExecutionData, []common.Hash, *common.Hash, *pb.ExecutionRequests) ([]byte, error) { return m.latestValidHash, m.payloadStatus } From ffc443b5f276ad06e7516c5b912616d32a650d07 Mon Sep 17 00:00:00 2001 From: terence Date: Wed, 16 Oct 2024 15:02:52 -0700 Subject: [PATCH 16/26] Update correlation penalty for EIP-7251 (#14456) * Update correlation penalty for EIP-7251 * Potuz and James feedback --- CHANGELOG.md | 1 + beacon-chain/core/epoch/epoch_processing.go | 23 +++++- .../core/epoch/epoch_processing_test.go | 72 +++++++++++++++++++ .../epoch_processing/slashings_test.go | 1 - .../epoch_processing/slashings_test.go | 1 - 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f71d58c6ff6e..c4463e28efb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,6 +101,7 @@ Updating to this release is recommended at your convenience. - Updated Sepolia bootnodes. - Make committee aware packing the default by deprecating `--enable-committee-aware-packing`. - Moved `ConvertKzgCommitmentToVersionedHash` to the `primitives` package. +- Updated correlation penalty for EIP-7251. ### Deprecated - `--disable-grpc-gateway` flag is deprecated due to grpc gateway removal. diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index e1adcc390237..19464abaa91d 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -147,11 +147,17 @@ func ProcessRegistryUpdates(ctx context.Context, st state.BeaconState) (state.Be // epoch = get_current_epoch(state) // total_balance = get_total_active_balance(state) // adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance) +// if state.version == electra: +// increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from total balance to avoid uint64 overflow +// penalty_per_effective_balance_increment = adjusted_total_slashing_balance // (total_balance // increment) // for index, validator in enumerate(state.validators): // if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch: // increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow // penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance // penalty = penalty_numerator // total_balance * increment +// if state.version == electra: +// effective_balance_increments = validator.effective_balance // increment +// penalty = penalty_per_effective_balance_increment * effective_balance_increments // decrease_balance(state, ValidatorIndex(index), penalty) func ProcessSlashings(st state.BeaconState, slashingMultiplier uint64) (state.BeaconState, error) { currentEpoch := time.CurrentEpoch(st) @@ -177,13 +183,26 @@ func ProcessSlashings(st state.BeaconState, slashingMultiplier uint64) (state.Be // below equally. increment := params.BeaconConfig().EffectiveBalanceIncrement minSlashing := math.Min(totalSlashing*slashingMultiplier, totalBalance) + + // Modified in Electra:EIP7251 + var penaltyPerEffectiveBalanceIncrement uint64 + if st.Version() >= version.Electra { + penaltyPerEffectiveBalanceIncrement = minSlashing / (totalBalance / increment) + } + bals := st.Balances() changed := false err = st.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error { correctEpoch := (currentEpoch + exitLength/2) == val.WithdrawableEpoch() if val.Slashed() && correctEpoch { - penaltyNumerator := val.EffectiveBalance() / increment * minSlashing - penalty := penaltyNumerator / totalBalance * increment + var penalty uint64 + if st.Version() >= version.Electra { + effectiveBalanceIncrements := val.EffectiveBalance() / increment + penalty = penaltyPerEffectiveBalanceIncrement * effectiveBalanceIncrements + } else { + penaltyNumerator := val.EffectiveBalance() / increment * minSlashing + penalty = penaltyNumerator / totalBalance * increment + } bals[idx] = helpers.DecreaseBalanceWithVal(bals[idx], penalty) changed = true } diff --git a/beacon-chain/core/epoch/epoch_processing_test.go b/beacon-chain/core/epoch/epoch_processing_test.go index caeee1a1b25d..f9bb3b594923 100644 --- a/beacon-chain/core/epoch/epoch_processing_test.go +++ b/beacon-chain/core/epoch/epoch_processing_test.go @@ -448,3 +448,75 @@ func TestProcessHistoricalDataUpdate(t *testing.T) { }) } } + +func TestProcessSlashings_SlashedElectra(t *testing.T) { + tests := []struct { + state *ethpb.BeaconStateElectra + want uint64 + }{ + { + state: ðpb.BeaconStateElectra{ + Validators: []*ethpb.Validator{ + {Slashed: true, + WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2, + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + {ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}}, + Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance}, + Slashings: []uint64{0, 1e9}, + }, + want: uint64(29000000000), + }, + { + state: ðpb.BeaconStateElectra{ + Validators: []*ethpb.Validator{ + {Slashed: true, + WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2, + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + {ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + {ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + }, + Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance}, + Slashings: []uint64{0, 1e9}, + }, + want: uint64(30500000000), + }, + { + state: ðpb.BeaconStateElectra{ + Validators: []*ethpb.Validator{ + {Slashed: true, + WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2, + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra}, + {ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra}, + {ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra}, + }, + Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance * 10, params.BeaconConfig().MaxEffectiveBalance * 20}, + Slashings: []uint64{0, 2 * 1e9}, + }, + want: uint64(317000001536), + }, + { + state: ðpb.BeaconStateElectra{ + Validators: []*ethpb.Validator{ + {Slashed: true, + WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2, + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra - params.BeaconConfig().EffectiveBalanceIncrement}, + {ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra - params.BeaconConfig().EffectiveBalanceIncrement}}, + Balances: []uint64{params.BeaconConfig().MaxEffectiveBalanceElectra - params.BeaconConfig().EffectiveBalanceIncrement, params.BeaconConfig().MaxEffectiveBalanceElectra - params.BeaconConfig().EffectiveBalanceIncrement}, + Slashings: []uint64{0, 1e9}, + }, + want: uint64(2044000000727), + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprint(i), func(t *testing.T) { + original := proto.Clone(tt.state) + s, err := state_native.InitializeFromProtoElectra(tt.state) + require.NoError(t, err) + helpers.ClearCache() + newState, err := epoch.ProcessSlashings(s, params.BeaconConfig().ProportionalSlashingMultiplierBellatrix) + require.NoError(t, err) + assert.Equal(t, tt.want, newState.Balances()[0], "ProcessSlashings({%v}) = newState; newState.Balances[0] = %d", original, newState.Balances()[0]) + }) + } +} diff --git a/testing/spectest/mainnet/electra/epoch_processing/slashings_test.go b/testing/spectest/mainnet/electra/epoch_processing/slashings_test.go index 22ebd4911e35..f5d3d2f7b95a 100644 --- a/testing/spectest/mainnet/electra/epoch_processing/slashings_test.go +++ b/testing/spectest/mainnet/electra/epoch_processing/slashings_test.go @@ -7,6 +7,5 @@ import ( ) func TestMainnet_Electra_EpochProcessing_Slashings(t *testing.T) { - t.Skip("slashing processing missing") epoch_processing.RunSlashingsTests(t, "mainnet") } diff --git a/testing/spectest/minimal/electra/epoch_processing/slashings_test.go b/testing/spectest/minimal/electra/epoch_processing/slashings_test.go index 4e46fb557cdb..2271afdf262a 100644 --- a/testing/spectest/minimal/electra/epoch_processing/slashings_test.go +++ b/testing/spectest/minimal/electra/epoch_processing/slashings_test.go @@ -7,6 +7,5 @@ import ( ) func TestMinimal_Electra_EpochProcessing_Slashings(t *testing.T) { - t.Skip("slashing processing missing") epoch_processing.RunSlashingsTests(t, "minimal") } From 4aa54107e4267b9d984efb4d3f7c90668b78b165 Mon Sep 17 00:00:00 2001 From: Sammy Rosso <15244892+saolyn@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:05:57 +0200 Subject: [PATCH 17/26] Add `/eth/v2/validator/aggregate_and_proofs` (#14490) * fix endpoint * changelog + gaz * add to endpoints test * Radek' review * James' review * fix duplication a bit * fix changelog --- CHANGELOG.md | 1 + api/server/structs/endpoints_validator.go | 2 +- beacon-chain/rpc/endpoints.go | 10 + beacon-chain/rpc/endpoints_test.go | 1 + beacon-chain/rpc/eth/validator/BUILD.bazel | 1 + beacon-chain/rpc/eth/validator/handlers.go | 117 ++++- .../rpc/eth/validator/handlers_test.go | 400 +++++++++++++++--- 7 files changed, 448 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4463e28efb4..a01a870a659d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Added GetBlockAttestationsV2 endpoint. - Light client support: Consensus types for Electra - Added SubmitPoolAttesterSlashingV2 endpoint. +- Added SubmitAggregateAndProofsRequestV2 endpoint. ### Changed diff --git a/api/server/structs/endpoints_validator.go b/api/server/structs/endpoints_validator.go index dfb94daea20a..8fc4b7d83fae 100644 --- a/api/server/structs/endpoints_validator.go +++ b/api/server/structs/endpoints_validator.go @@ -15,7 +15,7 @@ type SubmitContributionAndProofsRequest struct { } type SubmitAggregateAndProofsRequest struct { - Data []*SignedAggregateAttestationAndProof `json:"data"` + Data []json.RawMessage `json:"data"` } type SubmitSyncCommitteeSubscriptionsRequest struct { diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index f6b47009582b..2d61aee6173c 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -219,6 +219,16 @@ func (s *Service) validatorEndpoints( handler: server.SubmitAggregateAndProofs, methods: []string{http.MethodPost}, }, + { + template: "/eth/v2/validator/aggregate_and_proofs", + name: namespace + ".SubmitAggregateAndProofsV2", + middleware: []middleware.Middleware{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitAggregateAndProofsV2, + methods: []string{http.MethodPost}, + }, { template: "/eth/v1/validator/sync_committee_contribution", name: namespace + ".ProduceSyncCommitteeContribution", diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index d77dc4257b59..463bf9126667 100644 --- a/beacon-chain/rpc/endpoints_test.go +++ b/beacon-chain/rpc/endpoints_test.go @@ -101,6 +101,7 @@ func Test_endpoints(t *testing.T) { "/eth/v1/validator/attestation_data": {http.MethodGet}, "/eth/v1/validator/aggregate_attestation": {http.MethodGet}, "/eth/v1/validator/aggregate_and_proofs": {http.MethodPost}, + "/eth/v2/validator/aggregate_and_proofs": {http.MethodPost}, "/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost}, "/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost}, "/eth/v1/validator/beacon_committee_selections": {http.MethodPost}, diff --git a/beacon-chain/rpc/eth/validator/BUILD.bazel b/beacon-chain/rpc/eth/validator/BUILD.bazel index dc941ae72578..ed434774d47d 100644 --- a/beacon-chain/rpc/eth/validator/BUILD.bazel +++ b/beacon-chain/rpc/eth/validator/BUILD.bazel @@ -85,6 +85,7 @@ go_test( "//encoding/bytesutil:go_default_library", "//network/httputil:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//runtime/version:go_default_library", "//testing/assert:go_default_library", "//testing/mock:go_default_library", "//testing/require:go_default_library", diff --git a/beacon-chain/rpc/eth/validator/handlers.go b/beacon-chain/rpc/eth/validator/handlers.go index 597af22476a6..5fc5c68e3e78 100644 --- a/beacon-chain/rpc/eth/validator/handlers.go +++ b/beacon-chain/rpc/eth/validator/handlers.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server/structs" "github.com/prysmaticlabs/prysm/v5/beacon-chain/builder" "github.com/prysmaticlabs/prysm/v5/beacon-chain/cache" @@ -31,6 +32,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" "github.com/prysmaticlabs/prysm/v5/network/httputil" ethpbalpha "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" @@ -118,30 +120,34 @@ func (s *Server) SubmitContributionAndProofs(w http.ResponseWriter, r *http.Requ ctx, span := trace.StartSpan(r.Context(), "validator.SubmitContributionAndProofs") defer span.End() - var req structs.SubmitContributionAndProofsRequest - err := json.NewDecoder(r.Body).Decode(&req.Data) - switch { - case errors.Is(err, io.EOF): - httputil.HandleError(w, "No data submitted", http.StatusBadRequest) - return - case err != nil: - httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + var reqData []json.RawMessage + if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { + if errors.Is(err, io.EOF) { + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + } else { + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + } return } - if len(req.Data) == 0 { + if len(reqData) == 0 { httputil.HandleError(w, "No data submitted", http.StatusBadRequest) return } - for _, item := range req.Data { - consensusItem, err := item.ToConsensus() + for _, item := range reqData { + var contribution structs.SignedContributionAndProof + if err := json.Unmarshal(item, &contribution); err != nil { + httputil.HandleError(w, "Could not decode item: "+err.Error(), http.StatusBadRequest) + return + } + consensusItem, err := contribution.ToConsensus() if err != nil { - httputil.HandleError(w, "Could not convert request contribution to consensus contribution: "+err.Error(), http.StatusBadRequest) + httputil.HandleError(w, "Could not convert contribution to consensus format: "+err.Error(), http.StatusBadRequest) return } - rpcError := s.CoreService.SubmitSignedContributionAndProof(ctx, consensusItem) - if rpcError != nil { + if rpcError := s.CoreService.SubmitSignedContributionAndProof(ctx, consensusItem); rpcError != nil { httputil.HandleError(w, rpcError.Err.Error(), core.ErrorReasonToHTTP(rpcError.Reason)) + return } } } @@ -168,7 +174,13 @@ func (s *Server) SubmitAggregateAndProofs(w http.ResponseWriter, r *http.Request broadcastFailed := false for _, item := range req.Data { - consensusItem, err := item.ToConsensus() + var signedAggregate structs.SignedAggregateAttestationAndProof + err := json.Unmarshal(item, &signedAggregate) + if err != nil { + httputil.HandleError(w, "Could not decode item: "+err.Error(), http.StatusBadRequest) + return + } + consensusItem, err := signedAggregate.ToConsensus() if err != nil { httputil.HandleError(w, "Could not convert request aggregate to consensus aggregate: "+err.Error(), http.StatusBadRequest) return @@ -191,6 +203,81 @@ func (s *Server) SubmitAggregateAndProofs(w http.ResponseWriter, r *http.Request } } +// SubmitAggregateAndProofsV2 verifies given aggregate and proofs and publishes them on appropriate gossipsub topic. +func (s *Server) SubmitAggregateAndProofsV2(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "validator.SubmitAggregateAndProofsV2") + defer span.End() + + var reqData []json.RawMessage + if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { + if errors.Is(err, io.EOF) { + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + } else { + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + } + return + } + if len(reqData) == 0 { + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + return + } + + versionHeader := r.Header.Get(api.VersionHeader) + if versionHeader == "" { + httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest) + } + v, err := version.FromString(versionHeader) + if err != nil { + httputil.HandleError(w, "Invalid version: "+err.Error(), http.StatusBadRequest) + return + } + + broadcastFailed := false + var rpcError *core.RpcError + for _, raw := range reqData { + if v >= version.Electra { + var signedAggregate structs.SignedAggregateAttestationAndProofElectra + err = json.Unmarshal(raw, &signedAggregate) + if err != nil { + httputil.HandleError(w, "Failed to parse aggregate attestation and proof: "+err.Error(), http.StatusBadRequest) + return + } + consensusItem, err := signedAggregate.ToConsensus() + if err != nil { + httputil.HandleError(w, "Could not convert request aggregate to consensus aggregate: "+err.Error(), http.StatusBadRequest) + return + } + rpcError = s.CoreService.SubmitSignedAggregateSelectionProof(ctx, consensusItem) + } else { + var signedAggregate structs.SignedAggregateAttestationAndProof + err = json.Unmarshal(raw, &signedAggregate) + if err != nil { + httputil.HandleError(w, "Failed to parse aggregate attestation and proof: "+err.Error(), http.StatusBadRequest) + return + } + consensusItem, err := signedAggregate.ToConsensus() + if err != nil { + httputil.HandleError(w, "Could not convert request aggregate to consensus aggregate: "+err.Error(), http.StatusBadRequest) + return + } + rpcError = s.CoreService.SubmitSignedAggregateSelectionProof(ctx, consensusItem) + } + + if rpcError != nil { + var aggregateBroadcastFailedError *core.AggregateBroadcastFailedError + if errors.As(rpcError.Err, &aggregateBroadcastFailedError) { + broadcastFailed = true + } else { + httputil.HandleError(w, rpcError.Err.Error(), core.ErrorReasonToHTTP(rpcError.Reason)) + return + } + } + } + if broadcastFailed { + httputil.HandleError(w, "Could not broadcast one or more signed aggregated attestations", http.StatusInternalServerError) + } +} + // SubmitSyncCommitteeSubscription subscribe to a number of sync committee subnets. // // Subscribing to sync committee subnets is an action performed by VC to enable diff --git a/beacon-chain/rpc/eth/validator/handlers_test.go b/beacon-chain/rpc/eth/validator/handlers_test.go index 0aa758fe48b8..5b5d2b101116 100644 --- a/beacon-chain/rpc/eth/validator/handlers_test.go +++ b/beacon-chain/rpc/eth/validator/handlers_test.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server/structs" mockChain "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing" builderTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/builder/testing" @@ -37,6 +38,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/network/httputil" ethpbalpha "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" @@ -427,85 +429,238 @@ func TestSubmitContributionAndProofs(t *testing.T) { } func TestSubmitAggregateAndProofs(t *testing.T) { - c := &core.Service{ - GenesisTimeFetcher: &mockChain.ChainService{}, - } - s := &Server{ - CoreService: c, + CoreService: &core.Service{GenesisTimeFetcher: &mockChain.ChainService{}}, } + t.Run("V1", func(t *testing.T) { + t.Run("single", func(t *testing.T) { + broadcaster := &p2pmock.MockBroadcaster{} + s.CoreService.Broadcaster = broadcaster - t.Run("single", func(t *testing.T) { - broadcaster := &p2pmock.MockBroadcaster{} - c.Broadcaster = broadcaster + var body bytes.Buffer + _, err := body.WriteString(singleAggregate) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - var body bytes.Buffer - _, err := body.WriteString(singleAggregate) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + s.SubmitAggregateAndProofs(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + assert.Equal(t, 1, len(broadcaster.BroadcastMessages)) + }) + t.Run("multiple", func(t *testing.T) { + broadcaster := &p2pmock.MockBroadcaster{} + s.CoreService.Broadcaster = broadcaster + s.CoreService.SyncCommitteePool = synccommittee.NewStore() - s.SubmitAggregateAndProofs(writer, request) - assert.Equal(t, http.StatusOK, writer.Code) - assert.Equal(t, 1, len(broadcaster.BroadcastMessages)) - }) - t.Run("multiple", func(t *testing.T) { - broadcaster := &p2pmock.MockBroadcaster{} - c.Broadcaster = broadcaster - c.SyncCommitteePool = synccommittee.NewStore() + var body bytes.Buffer + _, err := body.WriteString(multipleAggregates) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - var body bytes.Buffer - _, err := body.WriteString(multipleAggregates) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + s.SubmitAggregateAndProofs(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + assert.Equal(t, 2, len(broadcaster.BroadcastMessages)) + }) + t.Run("no body", func(t *testing.T) { + request := httptest.NewRequest(http.MethodPost, "http://example.com", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofs(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) + }) + t.Run("empty", func(t *testing.T) { + var body bytes.Buffer + _, err := body.WriteString("[]") + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofs(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) + }) + t.Run("invalid", func(t *testing.T) { + var body bytes.Buffer + _, err := body.WriteString(invalidAggregate) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.SubmitAggregateAndProofs(writer, request) - assert.Equal(t, http.StatusOK, writer.Code) - assert.Equal(t, 2, len(broadcaster.BroadcastMessages)) + s.SubmitAggregateAndProofs(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + }) }) - t.Run("no body", func(t *testing.T) { - request := httptest.NewRequest(http.MethodPost, "http://example.com", nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + t.Run("V2", func(t *testing.T) { + t.Run("single", func(t *testing.T) { + broadcaster := &p2pmock.MockBroadcaster{} + s.CoreService.Broadcaster = broadcaster - s.SubmitAggregateAndProofs(writer, request) - assert.Equal(t, http.StatusBadRequest, writer.Code) - e := &httputil.DefaultJsonError{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) - assert.Equal(t, http.StatusBadRequest, e.Code) - assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) - }) - t.Run("empty", func(t *testing.T) { - var body bytes.Buffer - _, err := body.WriteString("[]") - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + var body bytes.Buffer + _, err := body.WriteString(singleAggregateElectra) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.SubmitAggregateAndProofs(writer, request) - assert.Equal(t, http.StatusBadRequest, writer.Code) - e := &httputil.DefaultJsonError{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) - assert.Equal(t, http.StatusBadRequest, e.Code) - assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) - }) - t.Run("invalid", func(t *testing.T) { - var body bytes.Buffer - _, err := body.WriteString(invalidAggregate) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + assert.Equal(t, 1, len(broadcaster.BroadcastMessages)) + }) + t.Run("single-pre-electra", func(t *testing.T) { + broadcaster := &p2pmock.MockBroadcaster{} + s.CoreService.Broadcaster = broadcaster - s.SubmitAggregateAndProofs(writer, request) - assert.Equal(t, http.StatusBadRequest, writer.Code) - e := &httputil.DefaultJsonError{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) - assert.Equal(t, http.StatusBadRequest, e.Code) + var body bytes.Buffer + _, err := body.WriteString(singleAggregate) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Phase0)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + assert.Equal(t, 1, len(broadcaster.BroadcastMessages)) + }) + t.Run("multiple", func(t *testing.T) { + broadcaster := &p2pmock.MockBroadcaster{} + s.CoreService.Broadcaster = broadcaster + s.CoreService.SyncCommitteePool = synccommittee.NewStore() + + var body bytes.Buffer + _, err := body.WriteString(multipleAggregatesElectra) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + assert.Equal(t, 2, len(broadcaster.BroadcastMessages)) + }) + t.Run("multiple-pre-electra", func(t *testing.T) { + broadcaster := &p2pmock.MockBroadcaster{} + s.CoreService.Broadcaster = broadcaster + s.CoreService.SyncCommitteePool = synccommittee.NewStore() + + var body bytes.Buffer + _, err := body.WriteString(multipleAggregates) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Phase0)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + assert.Equal(t, 2, len(broadcaster.BroadcastMessages)) + }) + t.Run("no body", func(t *testing.T) { + request := httptest.NewRequest(http.MethodPost, "http://example.com", nil) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) + }) + t.Run("no body-pre-electra", func(t *testing.T) { + request := httptest.NewRequest(http.MethodPost, "http://example.com", nil) + request.Header.Set(api.VersionHeader, version.String(version.Bellatrix)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) + }) + t.Run("empty", func(t *testing.T) { + var body bytes.Buffer + _, err := body.WriteString("[]") + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) + }) + t.Run("empty-pre-electra", func(t *testing.T) { + var body bytes.Buffer + _, err := body.WriteString("[]") + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Altair)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.Equal(t, true, strings.Contains(e.Message, "No data submitted")) + }) + t.Run("invalid", func(t *testing.T) { + var body bytes.Buffer + _, err := body.WriteString(invalidAggregateElectra) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + }) + t.Run("invalid-pre-electra", func(t *testing.T) { + var body bytes.Buffer + _, err := body.WriteString(invalidAggregate) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com", &body) + request.Header.Set(api.VersionHeader, version.String(version.Deneb)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAggregateAndProofsV2(writer, request) + assert.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + }) }) } @@ -2766,6 +2921,115 @@ var ( "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" } ]` + + singleAggregateElectra = `[ + { + "message": { + "aggregator_index": "1", + "aggregate": { + "aggregation_bits": "0x01", + "committee_bits": "0x01", + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "data": { + "slot": "1", + "index": "1", + "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "source": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "target": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + } + } + }, + "selection_proof": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } +]` + multipleAggregatesElectra = `[ + { + "message": { + "aggregator_index": "1", + "aggregate": { + "aggregation_bits": "0x01", + "committee_bits": "0x01", + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "data": { + "slot": "1", + "index": "1", + "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "source": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "target": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + } + } + }, + "selection_proof": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, +{ + "message": { + "aggregator_index": "1", + "aggregate": { + "aggregation_bits": "0x01", + "committee_bits": "0x01", + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "data": { + "slot": "1", + "index": "1", + "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "source": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "target": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + } + } + }, + "selection_proof": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } +] +` + // aggregator_index is invalid + invalidAggregateElectra = `[ + { + "message": { + "aggregator_index": "foo", + "aggregate": { + "aggregation_bits": "0x01", + "committee_bits": "0x01", + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "data": { + "slot": "1", + "index": "1", + "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "source": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "target": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + } + } + }, + "selection_proof": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } +]` singleSyncCommitteeSubscription = `[ { "validator_index": "1", From 6ac809059983fd6b1cdf747184e9e1557f270250 Mon Sep 17 00:00:00 2001 From: Potuz Date: Fri, 18 Oct 2024 12:51:58 -0300 Subject: [PATCH 18/26] rollback on SaveState error (#14555) * rollback on SaveState error * add test --- CHANGELOG.md | 2 ++ beacon-chain/blockchain/process_block.go | 4 ++++ beacon-chain/blockchain/receive_block.go | 3 +++ beacon-chain/blockchain/receive_block_test.go | 2 ++ 4 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a01a870a659d..aa7c18cb6b35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Fix `engine_exchangeCapabilities` implementation. - Updated the default `scrape-interval` in `Client-stats` to 2 minutes to accommodate Beaconcha.in API rate limits. - Switch to compounding when consolidating with source==target. +- Revert block db save when saving state fails. +- Return false from HasBlock if the block is being synced. ### Deprecated diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 425da163ecd0..d7dd6e8057e5 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -404,6 +404,10 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b interface return errors.Wrapf(err, "could not save block from slot %d", b.Block().Slot()) } if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil { + log.Warnf("Rolling back insertion of block with root %#x", r) + if err := s.cfg.BeaconDB.DeleteBlock(ctx, r); err != nil { + log.WithError(err).Errorf("Could not delete block with block root %#x", r) + } return errors.Wrap(err, "could not save state") } return nil diff --git a/beacon-chain/blockchain/receive_block.go b/beacon-chain/blockchain/receive_block.go index 241c0ca9ced9..45543f9cee86 100644 --- a/beacon-chain/blockchain/receive_block.go +++ b/beacon-chain/blockchain/receive_block.go @@ -349,6 +349,9 @@ func (s *Service) ReceiveBlockBatch(ctx context.Context, blocks []blocks.ROBlock // HasBlock returns true if the block of the input root exists in initial sync blocks cache or DB. func (s *Service) HasBlock(ctx context.Context, root [32]byte) bool { + if s.BlockBeingSynced(root) { + return false + } return s.hasBlockInInitSyncOrDB(ctx, root) } diff --git a/beacon-chain/blockchain/receive_block_test.go b/beacon-chain/blockchain/receive_block_test.go index 1fdcb196e450..d8d36051a013 100644 --- a/beacon-chain/blockchain/receive_block_test.go +++ b/beacon-chain/blockchain/receive_block_test.go @@ -278,6 +278,8 @@ func TestService_HasBlock(t *testing.T) { r, err = b.Block.HashTreeRoot() require.NoError(t, err) require.Equal(t, true, s.HasBlock(context.Background(), r)) + s.blockBeingSynced.set(r) + require.Equal(t, false, s.HasBlock(context.Background(), r)) } func TestCheckSaveHotStateDB_Enabling(t *testing.T) { From 073cf19b691a4ea6770e94e634440b0e470d15f4 Mon Sep 17 00:00:00 2001 From: Potuz Date: Fri, 18 Oct 2024 13:49:55 -0300 Subject: [PATCH 19/26] Rollback on errors from forkchoice insertion (#14556) --- CHANGELOG.md | 1 + .../forkchoice/doubly-linked-tree/forkchoice.go | 9 ++++++++- .../doubly-linked-tree/forkchoice_test.go | 14 ++++++++++++++ .../forkchoice/doubly-linked-tree/store.go | 10 ++++++++-- .../forkchoice/doubly-linked-tree/store_test.go | 9 +++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa7c18cb6b35..22447bb5f331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Switch to compounding when consolidating with source==target. - Revert block db save when saving state fails. - Return false from HasBlock if the block is being synced. +- Cleanup forkchoice on failed insertions. ### Deprecated diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go index dea38cb49183..296dfbeeca3c 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go @@ -141,7 +141,14 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, ro } jc, fc = f.store.pullTips(state, node, jc, fc) - return f.updateCheckpoints(ctx, jc, fc) + if err := f.updateCheckpoints(ctx, jc, fc); err != nil { + _, remErr := f.store.removeNode(ctx, node) + if remErr != nil { + log.WithError(remErr).Error("could not remove node") + } + return errors.Wrap(err, "could not update checkpoints") + } + return nil } // updateCheckpoints update the checkpoints when inserting a new node. diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go index 54c6c53edad9..496a0dff1283 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go @@ -3,6 +3,7 @@ package doublylinkedtree import ( "context" "encoding/binary" + "errors" "testing" "time" @@ -887,3 +888,16 @@ func TestForkchoiceParentRoot(t *testing.T) { require.NoError(t, err) require.Equal(t, zeroHash, root) } + +func TestForkChoice_CleanupInserting(t *testing.T) { + f := setup(0, 0) + ctx := context.Background() + st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 2) + f.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { + return f.justifiedBalances, errors.New("mock err") + }) + + require.NoError(t, err) + require.NotNil(t, f.InsertNode(ctx, st, blkRoot)) + require.Equal(t, false, f.HasNode(blkRoot)) +} diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store.go b/beacon-chain/forkchoice/doubly-linked-tree/store.go index 77c896c46caf..931d7254dc8e 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store.go @@ -107,7 +107,9 @@ func (s *Store) insert(ctx context.Context, s.headNode = n s.highestReceivedNode = n } else { - return n, errInvalidParentRoot + delete(s.nodeByRoot, root) + delete(s.nodeByPayload, payloadHash) + return nil, errInvalidParentRoot } } else { parent.children = append(parent.children, n) @@ -128,7 +130,11 @@ func (s *Store) insert(ctx context.Context, jEpoch := s.justifiedCheckpoint.Epoch fEpoch := s.finalizedCheckpoint.Epoch if err := s.treeRootNode.updateBestDescendant(ctx, jEpoch, fEpoch, slots.ToEpoch(currentSlot)); err != nil { - return n, err + _, remErr := s.removeNode(ctx, n) + if remErr != nil { + log.WithError(remErr).Error("could not remove node") + } + return nil, errors.Wrap(err, "could not update best descendants") } } // Update metrics. diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store_test.go b/beacon-chain/forkchoice/doubly-linked-tree/store_test.go index ba621b56ca6d..29b60599f6c4 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store_test.go @@ -525,3 +525,12 @@ func TestStore_TargetRootForEpoch(t *testing.T) { require.NoError(t, err) require.Equal(t, root4, target) } + +func TestStore_CleanupInserting(t *testing.T) { + f := setup(0, 0) + ctx := context.Background() + st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, err) + require.NotNil(t, f.InsertNode(ctx, st, blkRoot)) + require.Equal(t, false, f.HasNode(blkRoot)) +} From 9ec8c6c4b529c166dce46a22f660c0c6cd0a64ae Mon Sep 17 00:00:00 2001 From: terence Date: Mon, 21 Oct 2024 13:49:18 -0700 Subject: [PATCH 20/26] Use read only validator for processing (#14558) --- CHANGELOG.md | 1 + beacon-chain/core/electra/consolidations.go | 6 +- beacon-chain/core/electra/deposits.go | 16 ++++-- beacon-chain/core/electra/validator.go | 7 ++- beacon-chain/core/electra/withdrawals.go | 13 +++-- beacon-chain/core/helpers/sync_committee.go | 15 +++-- beacon-chain/core/helpers/validators.go | 18 +++--- beacon-chain/core/helpers/validators_test.go | 31 +++------- beacon-chain/state/interfaces.go | 1 - beacon-chain/state/state-native/BUILD.bazel | 1 - .../state/state-native/getters_validator.go | 33 ++--------- .../state-native/getters_validator_test.go | 57 ------------------- .../state/state-native/getters_withdrawal.go | 10 ++-- beacon-chain/sync/validate_aggregate_proof.go | 10 ++-- beacon-chain/sync/validate_beacon_blocks.go | 2 +- 15 files changed, 70 insertions(+), 151 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22447bb5f331..c8e84e164b81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Revert block db save when saving state fails. - Return false from HasBlock if the block is being synced. - Cleanup forkchoice on failed insertions. +- Use read only validator for core processing to avoid unnecessary copying. ### Deprecated diff --git a/beacon-chain/core/electra/consolidations.go b/beacon-chain/core/electra/consolidations.go index 7b33f62fc2c0..a4e08c19f8c9 100644 --- a/beacon-chain/core/electra/consolidations.go +++ b/beacon-chain/core/electra/consolidations.go @@ -61,15 +61,15 @@ func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState) err } var nextPendingConsolidation uint64 for _, pc := range pendingConsolidations { - sourceValidator, err := st.ValidatorAtIndex(pc.SourceIndex) + sourceValidator, err := st.ValidatorAtIndexReadOnly(pc.SourceIndex) if err != nil { return err } - if sourceValidator.Slashed { + if sourceValidator.Slashed() { nextPendingConsolidation++ continue } - if sourceValidator.WithdrawableEpoch > nextEpoch { + if sourceValidator.WithdrawableEpoch() > nextEpoch { break } diff --git a/beacon-chain/core/electra/deposits.go b/beacon-chain/core/electra/deposits.go index 431ab79fe60b..31fec1191af7 100644 --- a/beacon-chain/core/electra/deposits.go +++ b/beacon-chain/core/electra/deposits.go @@ -8,6 +8,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/contracts/deposit" @@ -474,7 +475,10 @@ func ApplyPendingDeposit(ctx context.Context, st state.BeaconState, deposit *eth // set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000)) // set_or_append_list(state.inactivity_scores, index, uint64(0)) func AddValidatorToRegistry(beaconState state.BeaconState, pubKey []byte, withdrawalCredentials []byte, amount uint64) error { - val := GetValidatorFromDeposit(pubKey, withdrawalCredentials, amount) + val, err := GetValidatorFromDeposit(pubKey, withdrawalCredentials, amount) + if err != nil { + return errors.Wrap(err, "could not get validator from deposit") + } if err := beaconState.AppendValidator(val); err != nil { return err } @@ -516,7 +520,7 @@ func AddValidatorToRegistry(beaconState state.BeaconState, pubKey []byte, withdr // validator.effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance) // // return validator -func GetValidatorFromDeposit(pubKey []byte, withdrawalCredentials []byte, amount uint64) *ethpb.Validator { +func GetValidatorFromDeposit(pubKey []byte, withdrawalCredentials []byte, amount uint64) (*ethpb.Validator, error) { validator := ðpb.Validator{ PublicKey: pubKey, WithdrawalCredentials: withdrawalCredentials, @@ -526,9 +530,13 @@ func GetValidatorFromDeposit(pubKey []byte, withdrawalCredentials []byte, amount WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: 0, } - maxEffectiveBalance := helpers.ValidatorMaxEffectiveBalance(validator) + v, err := state_native.NewValidator(validator) + if err != nil { + return nil, err + } + maxEffectiveBalance := helpers.ValidatorMaxEffectiveBalance(v) validator.EffectiveBalance = min(amount-(amount%params.BeaconConfig().EffectiveBalanceIncrement), maxEffectiveBalance) - return validator + return validator, nil } // ProcessDepositRequests is a function as part of electra to process execution layer deposits diff --git a/beacon-chain/core/electra/validator.go b/beacon-chain/core/electra/validator.go index 31959bb2a26f..501e75e7d738 100644 --- a/beacon-chain/core/electra/validator.go +++ b/beacon-chain/core/electra/validator.go @@ -64,13 +64,14 @@ func QueueExcessActiveBalance(s state.BeaconState, idx primitives.ValidatorIndex return err } excessBalance := bal - params.BeaconConfig().MinActivationBalance - val, err := s.ValidatorAtIndex(idx) + val, err := s.ValidatorAtIndexReadOnly(idx) if err != nil { return err } + pk := val.PublicKey() return s.AppendPendingDeposit(ðpb.PendingDeposit{ - PublicKey: val.PublicKey, - WithdrawalCredentials: val.WithdrawalCredentials, + PublicKey: pk[:], + WithdrawalCredentials: val.GetWithdrawalCredentials(), Amount: excessBalance, Signature: common.InfiniteSignature[:], Slot: params.BeaconConfig().GenesisSlot, diff --git a/beacon-chain/core/electra/withdrawals.go b/beacon-chain/core/electra/withdrawals.go index e2b2cf88782c..9e54b66fe04d 100644 --- a/beacon-chain/core/electra/withdrawals.go +++ b/beacon-chain/core/electra/withdrawals.go @@ -111,30 +111,31 @@ func ProcessWithdrawalRequests(ctx context.Context, st state.BeaconState, wrs [] log.Debugf("Skipping execution layer withdrawal request, validator index for %s not found\n", hexutil.Encode(wr.ValidatorPubkey)) continue } - validator, err := st.ValidatorAtIndex(vIdx) + validator, err := st.ValidatorAtIndexReadOnly(vIdx) if err != nil { return nil, err } // Verify withdrawal credentials hasCorrectCredential := helpers.HasExecutionWithdrawalCredentials(validator) - isCorrectSourceAddress := bytes.Equal(validator.WithdrawalCredentials[12:], wr.SourceAddress) + wc := validator.GetWithdrawalCredentials() + isCorrectSourceAddress := bytes.Equal(wc[12:], wr.SourceAddress) if !hasCorrectCredential || !isCorrectSourceAddress { log.Debugln("Skipping execution layer withdrawal request, wrong withdrawal credentials") continue } // Verify the validator is active. - if !helpers.IsActiveValidator(validator, currentEpoch) { + if !helpers.IsActiveValidatorUsingTrie(validator, currentEpoch) { log.Debugln("Skipping execution layer withdrawal request, validator not active") continue } // Verify the validator has not yet submitted an exit. - if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch { + if validator.ExitEpoch() != params.BeaconConfig().FarFutureEpoch { log.Debugln("Skipping execution layer withdrawal request, validator has submitted an exit already") continue } // Verify the validator has been active long enough. - if currentEpoch < validator.ActivationEpoch.AddEpoch(params.BeaconConfig().ShardCommitteePeriod) { + if currentEpoch < validator.ActivationEpoch().AddEpoch(params.BeaconConfig().ShardCommitteePeriod) { log.Debugln("Skipping execution layer withdrawal request, validator has not been active long enough") continue } @@ -156,7 +157,7 @@ func ProcessWithdrawalRequests(ctx context.Context, st state.BeaconState, wrs [] continue } - hasSufficientEffectiveBalance := validator.EffectiveBalance >= params.BeaconConfig().MinActivationBalance + hasSufficientEffectiveBalance := validator.EffectiveBalance() >= params.BeaconConfig().MinActivationBalance vBal, err := st.BalanceAtIndex(vIdx) if err != nil { return nil, err diff --git a/beacon-chain/core/helpers/sync_committee.go b/beacon-chain/core/helpers/sync_committee.go index c28926f062b3..d012af07aed3 100644 --- a/beacon-chain/core/helpers/sync_committee.go +++ b/beacon-chain/core/helpers/sync_committee.go @@ -69,15 +69,16 @@ func IsNextPeriodSyncCommittee( } indices, err := syncCommitteeCache.NextPeriodIndexPosition(root, valIdx) if errors.Is(err, cache.ErrNonExistingSyncCommitteeKey) { - val, err := st.ValidatorAtIndex(valIdx) + val, err := st.ValidatorAtIndexReadOnly(valIdx) if err != nil { return false, err } + pk := val.PublicKey() committee, err := st.NextSyncCommittee() if err != nil { return false, err } - return len(findSubCommitteeIndices(val.PublicKey, committee.Pubkeys)) > 0, nil + return len(findSubCommitteeIndices(pk[:], committee.Pubkeys)) > 0, nil } if err != nil { return false, err @@ -96,10 +97,11 @@ func CurrentPeriodSyncSubcommitteeIndices( } indices, err := syncCommitteeCache.CurrentPeriodIndexPosition(root, valIdx) if errors.Is(err, cache.ErrNonExistingSyncCommitteeKey) { - val, err := st.ValidatorAtIndex(valIdx) + val, err := st.ValidatorAtIndexReadOnly(valIdx) if err != nil { return nil, err } + pk := val.PublicKey() committee, err := st.CurrentSyncCommittee() if err != nil { return nil, err @@ -112,7 +114,7 @@ func CurrentPeriodSyncSubcommitteeIndices( } }() - return findSubCommitteeIndices(val.PublicKey, committee.Pubkeys), nil + return findSubCommitteeIndices(pk[:], committee.Pubkeys), nil } if err != nil { return nil, err @@ -130,15 +132,16 @@ func NextPeriodSyncSubcommitteeIndices( } indices, err := syncCommitteeCache.NextPeriodIndexPosition(root, valIdx) if errors.Is(err, cache.ErrNonExistingSyncCommitteeKey) { - val, err := st.ValidatorAtIndex(valIdx) + val, err := st.ValidatorAtIndexReadOnly(valIdx) if err != nil { return nil, err } + pk := val.PublicKey() committee, err := st.NextSyncCommittee() if err != nil { return nil, err } - return findSubCommitteeIndices(val.PublicKey, committee.Pubkeys), nil + return findSubCommitteeIndices(pk[:], committee.Pubkeys), nil } if err != nil { return nil, err diff --git a/beacon-chain/core/helpers/validators.go b/beacon-chain/core/helpers/validators.go index 6c1f64a39480..d86180b4e5e0 100644 --- a/beacon-chain/core/helpers/validators.go +++ b/beacon-chain/core/helpers/validators.go @@ -584,23 +584,23 @@ func IsSameWithdrawalCredentials(a, b *ethpb.Validator) bool { // and validator.withdrawable_epoch <= epoch // and balance > 0 // ) -func IsFullyWithdrawableValidator(val *ethpb.Validator, balance uint64, epoch primitives.Epoch, fork int) bool { +func IsFullyWithdrawableValidator(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch, fork int) bool { if val == nil || balance <= 0 { return false } // Electra / EIP-7251 logic if fork >= version.Electra { - return HasExecutionWithdrawalCredentials(val) && val.WithdrawableEpoch <= epoch + return HasExecutionWithdrawalCredentials(val) && val.WithdrawableEpoch() <= epoch } - return HasETH1WithdrawalCredential(val) && val.WithdrawableEpoch <= epoch + return HasETH1WithdrawalCredential(val) && val.WithdrawableEpoch() <= epoch } // IsPartiallyWithdrawableValidator returns whether the validator is able to perform a // partial withdrawal. This function assumes that the caller has a lock on the state. // This method conditionally calls the fork appropriate implementation based on the epoch argument. -func IsPartiallyWithdrawableValidator(val *ethpb.Validator, balance uint64, epoch primitives.Epoch, fork int) bool { +func IsPartiallyWithdrawableValidator(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch, fork int) bool { if val == nil { return false } @@ -630,9 +630,9 @@ func IsPartiallyWithdrawableValidator(val *ethpb.Validator, balance uint64, epoc // and has_max_effective_balance // and has_excess_balance // ) -func isPartiallyWithdrawableValidatorElectra(val *ethpb.Validator, balance uint64, epoch primitives.Epoch) bool { +func isPartiallyWithdrawableValidatorElectra(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch) bool { maxEB := ValidatorMaxEffectiveBalance(val) - hasMaxBalance := val.EffectiveBalance == maxEB + hasMaxBalance := val.EffectiveBalance() == maxEB hasExcessBalance := balance > maxEB return HasExecutionWithdrawalCredentials(val) && @@ -652,8 +652,8 @@ func isPartiallyWithdrawableValidatorElectra(val *ethpb.Validator, balance uint6 // has_max_effective_balance = validator.effective_balance == MAX_EFFECTIVE_BALANCE // has_excess_balance = balance > MAX_EFFECTIVE_BALANCE // return has_eth1_withdrawal_credential(validator) and has_max_effective_balance and has_excess_balance -func isPartiallyWithdrawableValidatorCapella(val *ethpb.Validator, balance uint64, epoch primitives.Epoch) bool { - hasMaxBalance := val.EffectiveBalance == params.BeaconConfig().MaxEffectiveBalance +func isPartiallyWithdrawableValidatorCapella(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch) bool { + hasMaxBalance := val.EffectiveBalance() == params.BeaconConfig().MaxEffectiveBalance hasExcessBalance := balance > params.BeaconConfig().MaxEffectiveBalance return HasETH1WithdrawalCredential(val) && hasExcessBalance && hasMaxBalance } @@ -670,7 +670,7 @@ func isPartiallyWithdrawableValidatorCapella(val *ethpb.Validator, balance uint6 // return MAX_EFFECTIVE_BALANCE_ELECTRA // else: // return MIN_ACTIVATION_BALANCE -func ValidatorMaxEffectiveBalance(val *ethpb.Validator) uint64 { +func ValidatorMaxEffectiveBalance(val state.ReadOnlyValidator) uint64 { if HasCompoundingWithdrawalCredential(val) { return params.BeaconConfig().MaxEffectiveBalanceElectra } diff --git a/beacon-chain/core/helpers/validators_test.go b/beacon-chain/core/helpers/validators_test.go index 3d10c711db43..0cae1cb09aae 100644 --- a/beacon-chain/core/helpers/validators_test.go +++ b/beacon-chain/core/helpers/validators_test.go @@ -974,13 +974,6 @@ func TestIsFullyWithdrawableValidator(t *testing.T) { fork int want bool }{ - { - name: "Handles nil case", - validator: nil, - balance: 0, - epoch: 0, - want: false, - }, { name: "No ETH1 prefix", validator: ðpb.Validator{ @@ -1036,7 +1029,9 @@ func TestIsFullyWithdrawableValidator(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, helpers.IsFullyWithdrawableValidator(tt.validator, tt.balance, tt.epoch, tt.fork)) + v, err := state_native.NewValidator(tt.validator) + require.NoError(t, err) + assert.Equal(t, tt.want, helpers.IsFullyWithdrawableValidator(v, tt.balance, tt.epoch, tt.fork)) }) } } @@ -1050,13 +1045,6 @@ func TestIsPartiallyWithdrawableValidator(t *testing.T) { fork int want bool }{ - { - name: "Handles nil case", - validator: nil, - balance: 0, - epoch: 0, - want: false, - }, { name: "No ETH1 prefix", validator: ðpb.Validator{ @@ -1113,7 +1101,9 @@ func TestIsPartiallyWithdrawableValidator(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, helpers.IsPartiallyWithdrawableValidator(tt.validator, tt.balance, tt.epoch, tt.fork)) + v, err := state_native.NewValidator(tt.validator) + require.NoError(t, err) + assert.Equal(t, tt.want, helpers.IsPartiallyWithdrawableValidator(v, tt.balance, tt.epoch, tt.fork)) }) } } @@ -1167,15 +1157,12 @@ func TestValidatorMaxEffectiveBalance(t *testing.T) { validator: ðpb.Validator{WithdrawalCredentials: []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}}, want: params.BeaconConfig().MinActivationBalance, }, - { - "Handles nil case", - nil, - params.BeaconConfig().MinActivationBalance, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, helpers.ValidatorMaxEffectiveBalance(tt.validator)) + v, err := state_native.NewValidator(tt.validator) + require.NoError(t, err) + assert.Equal(t, tt.want, helpers.ValidatorMaxEffectiveBalance(v)) }) } // Sanity check that MinActivationBalance equals (pre-electra) MaxEffectiveBalance diff --git a/beacon-chain/state/interfaces.go b/beacon-chain/state/interfaces.go index 8724e5a3cf0a..7422c8d6de25 100644 --- a/beacon-chain/state/interfaces.go +++ b/beacon-chain/state/interfaces.go @@ -140,7 +140,6 @@ type ReadOnlyBalances interface { Balances() []uint64 BalanceAtIndex(idx primitives.ValidatorIndex) (uint64, error) BalancesLength() int - ActiveBalanceAtIndex(idx primitives.ValidatorIndex) (uint64, error) } // ReadOnlyCheckpoint defines a struct which only has read access to checkpoint methods. diff --git a/beacon-chain/state/state-native/BUILD.bazel b/beacon-chain/state/state-native/BUILD.bazel index 9a662fc22404..6d14d26723d6 100644 --- a/beacon-chain/state/state-native/BUILD.bazel +++ b/beacon-chain/state/state-native/BUILD.bazel @@ -135,7 +135,6 @@ go_test( "//config/features:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", - "//consensus-types:go_default_library", "//consensus-types/blocks:go_default_library", "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", diff --git a/beacon-chain/state/state-native/getters_validator.go b/beacon-chain/state/state-native/getters_validator.go index bc0ec49dfdd4..dc874bf16ccd 100644 --- a/beacon-chain/state/state-native/getters_validator.go +++ b/beacon-chain/state/state-native/getters_validator.go @@ -2,7 +2,6 @@ package state_native import ( "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/features" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" @@ -151,6 +150,10 @@ func (b *BeaconState) ValidatorAtIndexReadOnly(idx primitives.ValidatorIndex) (s b.lock.RLock() defer b.lock.RUnlock() + return b.validatorAtIndexReadOnly(idx) +} + +func (b *BeaconState) validatorAtIndexReadOnly(idx primitives.ValidatorIndex) (state.ReadOnlyValidator, error) { if features.Get().EnableExperimentalState { if b.validatorsMultiValue == nil { return nil, state.ErrNilValidatorsInState @@ -442,34 +445,6 @@ func (b *BeaconState) inactivityScoresVal() []uint64 { return res } -// ActiveBalanceAtIndex returns the active balance for the given validator. -// -// Spec definition: -// -// def get_active_balance(state: BeaconState, validator_index: ValidatorIndex) -> Gwei: -// max_effective_balance = get_validator_max_effective_balance(state.validators[validator_index]) -// return min(state.balances[validator_index], max_effective_balance) -func (b *BeaconState) ActiveBalanceAtIndex(i primitives.ValidatorIndex) (uint64, error) { - if b.version < version.Electra { - return 0, errNotSupported("ActiveBalanceAtIndex", b.version) - } - - b.lock.RLock() - defer b.lock.RUnlock() - - v, err := b.validatorAtIndex(i) - if err != nil { - return 0, err - } - - bal, err := b.balanceAtIndex(i) - if err != nil { - return 0, err - } - - return min(bal, helpers.ValidatorMaxEffectiveBalance(v)), nil -} - // PendingBalanceToWithdraw returns the sum of all pending withdrawals for the given validator. // // Spec definition: diff --git a/beacon-chain/state/state-native/getters_validator_test.go b/beacon-chain/state/state-native/getters_validator_test.go index fed0359806e3..0a9cce57202e 100644 --- a/beacon-chain/state/state-native/getters_validator_test.go +++ b/beacon-chain/state/state-native/getters_validator_test.go @@ -1,15 +1,12 @@ package state_native_test import ( - "math" "testing" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" statenative "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native" testtmpl "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/testing" - "github.com/prysmaticlabs/prysm/v5/config/params" - consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types" "github.com/prysmaticlabs/prysm/v5/crypto/bls" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/assert" @@ -72,60 +69,6 @@ func TestValidatorIndexes(t *testing.T) { }) } -func TestActiveBalanceAtIndex(t *testing.T) { - // Test setup with a state with 4 validators. - // Validators 0 & 1 have compounding withdrawal credentials while validators 2 & 3 have BLS withdrawal credentials. - pb := ðpb.BeaconStateElectra{ - Validators: []*ethpb.Validator{ - { - WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte}, - }, - { - WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte}, - }, - { - WithdrawalCredentials: []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}, - }, - { - WithdrawalCredentials: []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}, - }, - }, - Balances: []uint64{ - 55, - math.MaxUint64, - 55, - math.MaxUint64, - }, - } - state, err := statenative.InitializeFromProtoUnsafeElectra(pb) - require.NoError(t, err) - - ab, err := state.ActiveBalanceAtIndex(0) - require.NoError(t, err) - require.Equal(t, uint64(55), ab) - - ab, err = state.ActiveBalanceAtIndex(1) - require.NoError(t, err) - require.Equal(t, params.BeaconConfig().MaxEffectiveBalanceElectra, ab) - - ab, err = state.ActiveBalanceAtIndex(2) - require.NoError(t, err) - require.Equal(t, uint64(55), ab) - - ab, err = state.ActiveBalanceAtIndex(3) - require.NoError(t, err) - require.Equal(t, params.BeaconConfig().MinActivationBalance, ab) - - // Accessing a validator index out of bounds should error. - _, err = state.ActiveBalanceAtIndex(4) - require.ErrorIs(t, err, consensus_types.ErrOutOfBounds) - - // Accessing a validator wwhere balance slice is out of bounds for some reason. - require.NoError(t, state.SetBalances([]uint64{})) - _, err = state.ActiveBalanceAtIndex(0) - require.ErrorIs(t, err, consensus_types.ErrOutOfBounds) -} - func TestPendingBalanceToWithdraw(t *testing.T) { pb := ðpb.BeaconStateElectra{ PendingPartialWithdrawals: []*ethpb.PendingPartialWithdrawal{ diff --git a/beacon-chain/state/state-native/getters_withdrawal.go b/beacon-chain/state/state-native/getters_withdrawal.go index d1b9e4057dcb..ed78964044e8 100644 --- a/beacon-chain/state/state-native/getters_withdrawal.go +++ b/beacon-chain/state/state-native/getters_withdrawal.go @@ -120,7 +120,7 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, err break } - v, err := b.validatorAtIndex(w.Index) + v, err := b.validatorAtIndexReadOnly(w.Index) if err != nil { return nil, 0, fmt.Errorf("failed to determine withdrawals at index %d: %w", w.Index, err) } @@ -128,14 +128,14 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, err if err != nil { return nil, 0, fmt.Errorf("could not retrieve balance at index %d: %w", w.Index, err) } - hasSufficientEffectiveBalance := v.EffectiveBalance >= params.BeaconConfig().MinActivationBalance + hasSufficientEffectiveBalance := v.EffectiveBalance() >= params.BeaconConfig().MinActivationBalance hasExcessBalance := vBal > params.BeaconConfig().MinActivationBalance - if v.ExitEpoch == params.BeaconConfig().FarFutureEpoch && hasSufficientEffectiveBalance && hasExcessBalance { + if v.ExitEpoch() == params.BeaconConfig().FarFutureEpoch && hasSufficientEffectiveBalance && hasExcessBalance { amount := min(vBal-params.BeaconConfig().MinActivationBalance, w.Amount) withdrawals = append(withdrawals, &enginev1.Withdrawal{ Index: withdrawalIndex, ValidatorIndex: w.Index, - Address: v.WithdrawalCredentials[12:], + Address: v.GetWithdrawalCredentials()[12:], Amount: amount, }) withdrawalIndex++ @@ -147,7 +147,7 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, err validatorsLen := b.validatorsLen() bound := mathutil.Min(uint64(validatorsLen), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep) for i := uint64(0); i < bound; i++ { - val, err := b.validatorAtIndex(validatorIndex) + val, err := b.validatorAtIndexReadOnly(validatorIndex) if err != nil { return nil, 0, errors.Wrapf(err, "could not retrieve validator at index %d", validatorIndex) } diff --git a/beacon-chain/sync/validate_aggregate_proof.go b/beacon-chain/sync/validate_aggregate_proof.go index ba32cbe11bc8..7bb8cb592c61 100644 --- a/beacon-chain/sync/validate_aggregate_proof.go +++ b/beacon-chain/sync/validate_aggregate_proof.go @@ -305,11 +305,12 @@ func validateSelectionIndex( domain := params.BeaconConfig().DomainSelectionProof epoch := slots.ToEpoch(slot) - v, err := bs.ValidatorAtIndex(validatorIndex) + v, err := bs.ValidatorAtIndexReadOnly(validatorIndex) if err != nil { return nil, err } - publicKey, err := bls.PublicKeyFromBytes(v.PublicKey) + pk := v.PublicKey() + publicKey, err := bls.PublicKeyFromBytes(pk[:]) if err != nil { return nil, err } @@ -335,11 +336,12 @@ func validateSelectionIndex( func aggSigSet(s state.ReadOnlyBeaconState, a ethpb.SignedAggregateAttAndProof) (*bls.SignatureBatch, error) { aggregateAndProof := a.AggregateAttestationAndProof() - v, err := s.ValidatorAtIndex(aggregateAndProof.GetAggregatorIndex()) + v, err := s.ValidatorAtIndexReadOnly(aggregateAndProof.GetAggregatorIndex()) if err != nil { return nil, err } - publicKey, err := bls.PublicKeyFromBytes(v.PublicKey) + pk := v.PublicKey() + publicKey, err := bls.PublicKeyFromBytes(pk[:]) if err != nil { return nil, err } diff --git a/beacon-chain/sync/validate_beacon_blocks.go b/beacon-chain/sync/validate_beacon_blocks.go index 06c6e3a7fe09..42bd6e0aaa20 100644 --- a/beacon-chain/sync/validate_beacon_blocks.go +++ b/beacon-chain/sync/validate_beacon_blocks.go @@ -369,7 +369,7 @@ func (s *Service) verifyPendingBlockSignature(ctx context.Context, blk interface return pubsub.ValidationIgnore, err } // Ignore block in the event of non-existent proposer. - _, err = roState.ValidatorAtIndex(blk.Block().ProposerIndex()) + _, err = roState.ValidatorAtIndexReadOnly(blk.Block().ProposerIndex()) if err != nil { return pubsub.ValidationIgnore, err } From 361712e886848876b9d1523da51c9514c9296d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Tue, 22 Oct 2024 10:09:18 +0700 Subject: [PATCH 21/26] Update the monitor package to Electra (#14562) * Update the monitor package to Electra * changelog <3 --- CHANGELOG.md | 1 + beacon-chain/monitor/process_attestation.go | 28 ++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e84e164b81..fe65c1b4a1da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Light client support: Consensus types for Electra - Added SubmitPoolAttesterSlashingV2 endpoint. - Added SubmitAggregateAndProofsRequestV2 endpoint. +- Updated the `beacon-chain/monitor` package to Electra. [PR](https://github.com/prysmaticlabs/prysm/pull/14562) ### Changed diff --git a/beacon-chain/monitor/process_attestation.go b/beacon-chain/monitor/process_attestation.go index 7191aebdd19d..caaa66b1bcd6 100644 --- a/beacon-chain/monitor/process_attestation.go +++ b/beacon-chain/monitor/process_attestation.go @@ -185,42 +185,42 @@ func (s *Service) processUnaggregatedAttestation(ctx context.Context, att ethpb. } // processUnaggregatedAttestation logs when the beacon node observes an aggregated attestation from tracked validator. -func (s *Service) processAggregatedAttestation(ctx context.Context, att *ethpb.AggregateAttestationAndProof) { +func (s *Service) processAggregatedAttestation(ctx context.Context, att ethpb.AggregateAttAndProof) { s.Lock() defer s.Unlock() - if s.trackedIndex(att.AggregatorIndex) { + if s.trackedIndex(att.GetAggregatorIndex()) { log.WithFields(logrus.Fields{ - "aggregatorIndex": att.AggregatorIndex, - "slot": att.Aggregate.Data.Slot, + "aggregatorIndex": att.GetAggregatorIndex(), + "slot": att.AggregateVal().GetData().Slot, "beaconBlockRoot": fmt.Sprintf("%#x", bytesutil.Trunc( - att.Aggregate.Data.BeaconBlockRoot)), + att.AggregateVal().GetData().BeaconBlockRoot)), "sourceRoot": fmt.Sprintf("%#x", bytesutil.Trunc( - att.Aggregate.Data.Source.Root)), + att.AggregateVal().GetData().Source.Root)), "targetRoot": fmt.Sprintf("%#x", bytesutil.Trunc( - att.Aggregate.Data.Target.Root)), + att.AggregateVal().GetData().Target.Root)), }).Info("Processed attestation aggregation") - aggregatedPerf := s.aggregatedPerformance[att.AggregatorIndex] + aggregatedPerf := s.aggregatedPerformance[att.GetAggregatorIndex()] aggregatedPerf.totalAggregations++ - s.aggregatedPerformance[att.AggregatorIndex] = aggregatedPerf - aggregationCounter.WithLabelValues(fmt.Sprintf("%d", att.AggregatorIndex)).Inc() + s.aggregatedPerformance[att.GetAggregatorIndex()] = aggregatedPerf + aggregationCounter.WithLabelValues(fmt.Sprintf("%d", att.GetAggregatorIndex())).Inc() } var root [32]byte - copy(root[:], att.Aggregate.Data.BeaconBlockRoot) + copy(root[:], att.AggregateVal().GetData().BeaconBlockRoot) st := s.config.StateGen.StateByRootIfCachedNoCopy(root) if st == nil { log.WithField("beaconBlockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debug( "Skipping aggregated attestation due to state not found in cache") return } - attestingIndices, err := attestingIndices(ctx, st, att.Aggregate) + attestingIndices, err := attestingIndices(ctx, st, att.AggregateVal()) if err != nil { log.WithError(err).Error("Could not get attesting indices") return } for _, idx := range attestingIndices { - if s.canUpdateAttestedValidator(primitives.ValidatorIndex(idx), att.Aggregate.Data.Slot) { - logFields := logMessageTimelyFlagsForIndex(primitives.ValidatorIndex(idx), att.Aggregate.Data) + if s.canUpdateAttestedValidator(primitives.ValidatorIndex(idx), att.AggregateVal().GetData().Slot) { + logFields := logMessageTimelyFlagsForIndex(primitives.ValidatorIndex(idx), att.AggregateVal().GetData()) log.WithFields(logFields).Info("Processed aggregated attestation") } } From 616cdc1e8b94559008cc9d119a2b5d2e39383f6c Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Tue, 22 Oct 2024 10:28:38 -0500 Subject: [PATCH 22/26] Update CHANGELOG.md to reflect v5.1.2 hotfix release (#14547) --- CHANGELOG.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe65c1b4a1da..bfc83dd228b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. -## [Unreleased](https://github.com/prysmaticlabs/prysm/compare/v5.1.1...HEAD) +## [Unreleased](https://github.com/prysmaticlabs/prysm/compare/v5.1.2...HEAD) ### Added @@ -41,10 +41,26 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Fixed mesh size by appending `gParams.Dhi = gossipSubDhi` - Fix skipping partial withdrawals count. -- recover from panics when writing the event stream [pr](https://github.com/prysmaticlabs/prysm/pull/14545) ### Security +## [v5.1.2](https://github.com/prysmaticlabs/prysm/compare/v5.1.1...v5.1.2) - 2024-10-16 + +This is a hotfix release with one change. + +Prysm v5.1.1 contains an updated implementation of the beacon api streaming events endpoint. This +new implementation contains a bug that can cause a panic in certain conditions. The issue is +difficult to reproduce reliably and we are still trying to determine the root cause, but in the +meantime we are issuing a patch that recovers from the panic to prevent the node from crashing. + +This only impacts the v5.1.1 release beacon api event stream endpoints. This endpoint is used by the +prysm REST mode validator (a feature which requires the validator to be configured to use the beacon +api intead of prysm's stock grpc endpoints) or accessory software that connects to the events api, +like https://github.com/ethpandaops/ethereum-metrics-exporter + +### Fixed + +- Recover from panics when writing the event stream [#14545](https://github.com/prysmaticlabs/prysm/pull/14545) ## [v5.1.1](https://github.com/prysmaticlabs/prysm/compare/v5.1.0...v5.1.1) - 2024-10-15 From 83ed320826b95017a37f87d6318d72ed0918f1fe Mon Sep 17 00:00:00 2001 From: Potuz Date: Thu, 24 Oct 2024 10:27:15 -0300 Subject: [PATCH 23/26] Use ROBlock in block processing pipeline (#14571) * Use ROBlock in block processing pipeline * Pass blockchain tests * passing forkchoice tests * pass rpc test * change changelog --- CHANGELOG.md | 1 + beacon-chain/blockchain/chain_info.go | 3 +- .../blockchain/chain_info_forkchoice.go | 5 +- beacon-chain/blockchain/chain_info_test.go | 68 ++-- .../blockchain/execution_engine_test.go | 9 +- .../blockchain/process_attestation_test.go | 22 +- beacon-chain/blockchain/process_block.go | 27 +- .../blockchain/process_block_helpers.go | 45 ++- beacon-chain/blockchain/process_block_test.go | 147 +++++--- .../blockchain/receive_attestation_test.go | 17 +- beacon-chain/blockchain/receive_block.go | 8 +- beacon-chain/blockchain/service.go | 17 +- beacon-chain/blockchain/service_test.go | 12 +- beacon-chain/blockchain/testing/mock.go | 27 +- beacon-chain/forkchoice/BUILD.bazel | 1 + .../forkchoice/doubly-linked-tree/BUILD.bazel | 2 +- .../forkchoice/doubly-linked-tree/errors.go | 1 - .../doubly-linked-tree/forkchoice.go | 32 +- .../doubly-linked-tree/forkchoice_test.go | 317 ++++++++++-------- .../doubly-linked-tree/node_test.go | 114 +++---- .../doubly-linked-tree/proposer_boost_test.go | 12 +- .../reorg_late_blocks_test.go | 24 +- .../forkchoice/doubly-linked-tree/store.go | 18 +- .../doubly-linked-tree/store_test.go | 130 ++++--- beacon-chain/forkchoice/interfaces.go | 3 +- beacon-chain/forkchoice/types/BUILD.bazel | 2 +- beacon-chain/forkchoice/types/types.go | 4 +- beacon-chain/rpc/eth/beacon/handlers_test.go | 20 +- beacon-chain/rpc/eth/helpers/sync_test.go | 23 +- .../validator/proposer_builder_test.go | 25 +- 30 files changed, 683 insertions(+), 453 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc83dd228b3..48e15ab28b0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Return false from HasBlock if the block is being synced. - Cleanup forkchoice on failed insertions. - Use read only validator for core processing to avoid unnecessary copying. +- Use ROBlock across block processing pipeline ### Deprecated diff --git a/beacon-chain/blockchain/chain_info.go b/beacon-chain/blockchain/chain_info.go index 5cc2927206d2..47ba3ff86529 100644 --- a/beacon-chain/blockchain/chain_info.go +++ b/beacon-chain/blockchain/chain_info.go @@ -12,6 +12,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/forkchoice" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" @@ -44,7 +45,7 @@ type ForkchoiceFetcher interface { UpdateHead(context.Context, primitives.Slot) HighestReceivedBlockSlot() primitives.Slot ReceivedBlocksLastEpoch() (uint64, error) - InsertNode(context.Context, state.BeaconState, [32]byte) error + InsertNode(context.Context, state.BeaconState, consensus_blocks.ROBlock) error ForkChoiceDump(context.Context) (*forkchoice.Dump, error) NewSlot(context.Context, primitives.Slot) error ProposerBoost() [32]byte diff --git a/beacon-chain/blockchain/chain_info_forkchoice.go b/beacon-chain/blockchain/chain_info_forkchoice.go index cf735806fc58..e61444d1c024 100644 --- a/beacon-chain/blockchain/chain_info_forkchoice.go +++ b/beacon-chain/blockchain/chain_info_forkchoice.go @@ -4,6 +4,7 @@ import ( "context" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/forkchoice" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ) @@ -44,10 +45,10 @@ func (s *Service) ReceivedBlocksLastEpoch() (uint64, error) { } // InsertNode is a wrapper for node insertion which is self locked -func (s *Service) InsertNode(ctx context.Context, st state.BeaconState, root [32]byte) error { +func (s *Service) InsertNode(ctx context.Context, st state.BeaconState, block consensus_blocks.ROBlock) error { s.cfg.ForkChoiceStore.Lock() defer s.cfg.ForkChoiceStore.Unlock() - return s.cfg.ForkChoiceStore.InsertNode(ctx, st, root) + return s.cfg.ForkChoiceStore.InsertNode(ctx, st, block) } // ForkChoiceDump returns the corresponding value from forkchoice diff --git a/beacon-chain/blockchain/chain_info_test.go b/beacon-chain/blockchain/chain_info_test.go index a328e390dc36..b5f92c316e55 100644 --- a/beacon-chain/blockchain/chain_info_test.go +++ b/beacon-chain/blockchain/chain_info_test.go @@ -13,6 +13,7 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" @@ -38,7 +39,7 @@ func prepareForkchoiceState( payloadHash [32]byte, justified *ethpb.Checkpoint, finalized *ethpb.Checkpoint, -) (state.BeaconState, [32]byte, error) { +) (state.BeaconState, consensus_blocks.ROBlock, error) { blockHeader := ðpb.BeaconBlockHeader{ ParentRoot: parentRoot[:], } @@ -59,7 +60,26 @@ func prepareForkchoiceState( base.BlockRoots[0] = append(base.BlockRoots[0], blockRoot[:]...) st, err := state_native.InitializeFromProtoBellatrix(base) - return st, blockRoot, err + if err != nil { + return nil, consensus_blocks.ROBlock{}, err + } + blk := ðpb.SignedBeaconBlockBellatrix{ + Block: ðpb.BeaconBlockBellatrix{ + Slot: slot, + ParentRoot: parentRoot[:], + Body: ðpb.BeaconBlockBodyBellatrix{ + ExecutionPayload: &enginev1.ExecutionPayload{ + BlockHash: payloadHash[:], + }, + }, + }, + } + signed, err := blocks.NewSignedBeaconBlock(blk) + if err != nil { + return nil, consensus_blocks.ROBlock{}, err + } + roblock, err := consensus_blocks.NewROBlockWithRoot(signed, blockRoot) + return st, roblock, err } func TestHeadRoot_Nil(t *testing.T) { @@ -122,9 +142,9 @@ func TestUnrealizedJustifiedBlockHash(t *testing.T) { service := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}} ojc := ðpb.Checkpoint{Root: []byte{'j'}} ofc := ðpb.Checkpoint{Root: []byte{'f'}} - st, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) + st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) + require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) service.cfg.ForkChoiceStore.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return []uint64{}, nil }) require.NoError(t, service.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(ctx, &forkchoicetypes.Checkpoint{Epoch: 6, Root: [32]byte{'j'}})) @@ -316,24 +336,24 @@ func TestService_ChainHeads(t *testing.T) { c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}} ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} - st, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) + st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) roots, slots := c.ChainHeads() require.Equal(t, 3, len(roots)) @@ -413,12 +433,12 @@ func TestService_IsOptimistic(t *testing.T) { ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{root: [32]byte{'b'}}} - st, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) + st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) opt, err := c.IsOptimistic(ctx) require.NoError(t, err) @@ -449,12 +469,12 @@ func TestService_IsOptimisticForRoot(t *testing.T) { c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{root: [32]byte{'b'}}} ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} - st, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) + st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) + require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) opt, err := c.IsOptimisticForRoot(ctx, [32]byte{'a'}) require.NoError(t, err) diff --git a/beacon-chain/blockchain/execution_engine_test.go b/beacon-chain/blockchain/execution_engine_test.go index be5d700d52ea..ce8b132bc465 100644 --- a/beacon-chain/blockchain/execution_engine_test.go +++ b/beacon-chain/blockchain/execution_engine_test.go @@ -1135,9 +1135,14 @@ func TestComputePayloadAttribute(t *testing.T) { // Cache hit, advance state, no fee recipient slot := primitives.Slot(1) service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{}) + blk := util.NewBeaconBlockBellatrix() + signed, err := consensusblocks.NewSignedBeaconBlock(blk) + require.NoError(t, err) + roblock, err := consensusblocks.NewROBlockWithRoot(signed, [32]byte{'a'}) + require.NoError(t, err) cfg := &postBlockProcessConfig{ - ctx: ctx, - blockRoot: [32]byte{'a'}, + ctx: ctx, + roblock: roblock, } fcu := &fcuConfig{ headState: st, diff --git a/beacon-chain/blockchain/process_attestation_test.go b/beacon-chain/blockchain/process_attestation_test.go index f24db1f61a39..bb1ea712ccfd 100644 --- a/beacon-chain/blockchain/process_attestation_test.go +++ b/beacon-chain/blockchain/process_attestation_test.go @@ -32,18 +32,18 @@ func TestStore_OnAttestation_ErrorConditions(t *testing.T) { util.SaveBlock(t, ctx, beaconDB, blkWithoutState) cp := ðpb.Checkpoint{} - st, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, cp, cp) + st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, cp, cp) require.NoError(t, err) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) + require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) blkWithStateBadAtt := util.NewBeaconBlock() blkWithStateBadAtt.Block.Slot = 1 r, err := blkWithStateBadAtt.Block.HashTreeRoot() require.NoError(t, err) cp = ðpb.Checkpoint{Root: r[:]} - st, blkRoot, err = prepareForkchoiceState(ctx, blkWithStateBadAtt.Block.Slot, r, [32]byte{}, params.BeaconConfig().ZeroHash, cp, cp) + st, roblock, err = prepareForkchoiceState(ctx, blkWithStateBadAtt.Block.Slot, r, [32]byte{}, params.BeaconConfig().ZeroHash, cp, cp) require.NoError(t, err) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) + require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) util.SaveBlock(t, ctx, beaconDB, blkWithStateBadAtt) BlkWithStateBadAttRoot, err := blkWithStateBadAtt.Block.HashTreeRoot() require.NoError(t, err) @@ -139,9 +139,9 @@ func TestStore_OnAttestation_Ok_DoublyLinkedTree(t *testing.T) { require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot)) ojc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]} ofc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]} - state, blkRoot, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, ojc, ofc) + state, roblock, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, ojc, ofc) require.NoError(t, err) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) + require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, roblock)) require.NoError(t, service.OnAttestation(ctx, att[0], 0)) } @@ -318,10 +318,9 @@ func TestStore_UpdateCheckpointState(t *testing.T) { require.NoError(t, err) checkpoint := ðpb.Checkpoint{Epoch: epoch, Root: r1[:]} require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(checkpoint.Root))) - st, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r1, [32]byte{}, params.BeaconConfig().ZeroHash, checkpoint, checkpoint) + st, roblock, err := prepareForkchoiceState(ctx, blk.Block.Slot, r1, [32]byte{}, params.BeaconConfig().ZeroHash, checkpoint, checkpoint) require.NoError(t, err) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, r1)) + require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) returned, err := service.getAttPreState(ctx, checkpoint) require.NoError(t, err) assert.Equal(t, params.BeaconConfig().SlotsPerEpoch.Mul(uint64(checkpoint.Epoch)), returned.Slot(), "Incorrectly returned base state") @@ -337,10 +336,9 @@ func TestStore_UpdateCheckpointState(t *testing.T) { require.NoError(t, err) newCheckpoint := ðpb.Checkpoint{Epoch: epoch, Root: r2[:]} require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(newCheckpoint.Root))) - st, blkRoot, err = prepareForkchoiceState(ctx, blk.Block.Slot, r2, r1, params.BeaconConfig().ZeroHash, newCheckpoint, newCheckpoint) + st, roblock, err = prepareForkchoiceState(ctx, blk.Block.Slot, r2, r1, params.BeaconConfig().ZeroHash, newCheckpoint, newCheckpoint) require.NoError(t, err) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot)) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, r2)) + require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock)) returned, err = service.getAttPreState(ctx, newCheckpoint) require.NoError(t, err) s, err := slots.EpochStart(newCheckpoint.Epoch) diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index d7dd6e8057e5..6688ca8039e9 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -46,8 +46,7 @@ var initialSyncBlockCacheSize = uint64(2 * params.BeaconConfig().SlotsPerEpoch) // process the beacon block after validating the state transition function type postBlockProcessConfig struct { ctx context.Context - signed interfaces.ReadOnlySignedBeaconBlock - blockRoot [32]byte + roblock consensusblocks.ROBlock headRoot [32]byte postState state.BeaconState isValidPayload bool @@ -61,7 +60,7 @@ func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error { ctx, span := trace.StartSpan(cfg.ctx, "blockChain.onBlock") defer span.End() cfg.ctx = ctx - if err := consensusblocks.BeaconBlockIsNil(cfg.signed); err != nil { + if err := consensusblocks.BeaconBlockIsNil(cfg.roblock); err != nil { return invalidBlock{error: err} } startTime := time.Now() @@ -73,19 +72,19 @@ func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error { defer s.sendLightClientFeeds(cfg) defer s.sendStateFeedOnBlock(cfg) defer reportProcessingTime(startTime) - defer reportAttestationInclusion(cfg.signed.Block()) + defer reportAttestationInclusion(cfg.roblock.Block()) - err := s.cfg.ForkChoiceStore.InsertNode(ctx, cfg.postState, cfg.blockRoot) + err := s.cfg.ForkChoiceStore.InsertNode(ctx, cfg.postState, cfg.roblock) if err != nil { - return errors.Wrapf(err, "could not insert block %d to fork choice store", cfg.signed.Block().Slot()) + return errors.Wrapf(err, "could not insert block %d to fork choice store", cfg.roblock.Block().Slot()) } - if err := s.handleBlockAttestations(ctx, cfg.signed.Block(), cfg.postState); err != nil { + if err := s.handleBlockAttestations(ctx, cfg.roblock.Block(), cfg.postState); err != nil { return errors.Wrap(err, "could not handle block's attestations") } - s.InsertSlashingsToForkChoiceStore(ctx, cfg.signed.Block().Body().AttesterSlashings()) + s.InsertSlashingsToForkChoiceStore(ctx, cfg.roblock.Block().Body().AttesterSlashings()) if cfg.isValidPayload { - if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, cfg.blockRoot); err != nil { + if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, cfg.roblock.Root()); err != nil { return errors.Wrap(err, "could not set optimistic block to valid") } } @@ -95,8 +94,8 @@ func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error { log.WithError(err).Warn("Could not update head") } newBlockHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds())) - if cfg.headRoot != cfg.blockRoot { - s.logNonCanonicalBlockReceived(cfg.blockRoot, cfg.headRoot) + if cfg.headRoot != cfg.roblock.Root() { + s.logNonCanonicalBlockReceived(cfg.roblock.Root(), cfg.headRoot) return nil } if err := s.getFCUArgs(cfg, fcuArgs); err != nil { @@ -154,7 +153,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo } // Fill in missing blocks - if err := s.fillInForkChoiceMissingBlocks(ctx, blks[0].Block(), preState.CurrentJustifiedCheckpoint(), preState.FinalizedCheckpoint()); err != nil { + if err := s.fillInForkChoiceMissingBlocks(ctx, blks[0], preState.CurrentJustifiedCheckpoint(), preState.FinalizedCheckpoint()); err != nil { return errors.Wrap(err, "could not fill in missing blocks to forkchoice") } @@ -234,7 +233,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo if err := avs.IsDataAvailable(ctx, s.CurrentSlot(), b); err != nil { return errors.Wrapf(err, "could not validate blob data availability at slot %d", b.Block().Slot()) } - args := &forkchoicetypes.BlockAndCheckpoints{Block: b.Block(), + args := &forkchoicetypes.BlockAndCheckpoints{Block: b, JustifiedCheckpoint: jCheckpoints[i], FinalizedCheckpoint: fCheckpoints[i]} pendingNodes[len(blks)-i-1] = args @@ -279,7 +278,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo return errors.Wrap(err, "could not insert batch to forkchoice") } // Insert the last block to forkchoice - if err := s.cfg.ForkChoiceStore.InsertNode(ctx, preState, lastBR); err != nil { + if err := s.cfg.ForkChoiceStore.InsertNode(ctx, preState, lastB); err != nil { return errors.Wrap(err, "could not insert last block in batch to forkchoice") } // Set their optimistic status diff --git a/beacon-chain/blockchain/process_block_helpers.go b/beacon-chain/blockchain/process_block_helpers.go index 8f3da0a4236c..fe01f582bfa5 100644 --- a/beacon-chain/blockchain/process_block_helpers.go +++ b/beacon-chain/blockchain/process_block_helpers.go @@ -18,6 +18,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/config/features" field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" @@ -42,7 +43,7 @@ func (s *Service) getFCUArgs(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) er if !s.inRegularSync() { return nil } - slot := cfg.signed.Block().Slot() + slot := cfg.roblock.Block().Slot() if slots.WithinVotingWindow(uint64(s.genesisTime.Unix()), slot) { return nil } @@ -50,9 +51,9 @@ func (s *Service) getFCUArgs(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) er } func (s *Service) getFCUArgsEarlyBlock(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error { - if cfg.blockRoot == cfg.headRoot { + if cfg.roblock.Root() == cfg.headRoot { fcuArgs.headState = cfg.postState - fcuArgs.headBlock = cfg.signed + fcuArgs.headBlock = cfg.roblock fcuArgs.headRoot = cfg.headRoot fcuArgs.proposingSlot = s.CurrentSlot() + 1 return nil @@ -96,7 +97,7 @@ func (s *Service) fcuArgsNonCanonicalBlock(cfg *postBlockProcessConfig, fcuArgs // sendStateFeedOnBlock sends an event that a new block has been synced func (s *Service) sendStateFeedOnBlock(cfg *postBlockProcessConfig) { - optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(cfg.blockRoot) + optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(cfg.roblock.Root()) if err != nil { log.WithError(err).Debug("Could not check if block is optimistic") optimistic = true @@ -105,9 +106,9 @@ func (s *Service) sendStateFeedOnBlock(cfg *postBlockProcessConfig) { s.cfg.StateNotifier.StateFeed().Send(&feed.Event{ Type: statefeed.BlockProcessed, Data: &statefeed.BlockProcessedData{ - Slot: cfg.signed.Block().Slot(), - BlockRoot: cfg.blockRoot, - SignedBlock: cfg.signed, + Slot: cfg.roblock.Block().Slot(), + BlockRoot: cfg.roblock.Root(), + SignedBlock: cfg.roblock, Verified: true, Optimistic: optimistic, }, @@ -117,7 +118,7 @@ func (s *Service) sendStateFeedOnBlock(cfg *postBlockProcessConfig) { // sendLightClientFeeds sends the light client feeds when feature flag is enabled. func (s *Service) sendLightClientFeeds(cfg *postBlockProcessConfig) { if features.Get().EnableLightClient { - if _, err := s.sendLightClientOptimisticUpdate(cfg.ctx, cfg.signed, cfg.postState); err != nil { + if _, err := s.sendLightClientOptimisticUpdate(cfg.ctx, cfg.roblock, cfg.postState); err != nil { log.WithError(err).Error("Failed to send light client optimistic update") } @@ -125,7 +126,7 @@ func (s *Service) sendLightClientFeeds(cfg *postBlockProcessConfig) { finalized := s.ForkChoicer().FinalizedCheckpoint() // LightClientFinalityUpdate needs super majority - s.tryPublishLightClientFinalityUpdate(cfg.ctx, cfg.signed, finalized, cfg.postState) + s.tryPublishLightClientFinalityUpdate(cfg.ctx, cfg.roblock, finalized, cfg.postState) } } @@ -252,20 +253,21 @@ func (s *Service) sendLightClientOptimisticUpdate(ctx context.Context, signed in // before sending FCU to the engine. func (s *Service) updateCachesPostBlockProcessing(cfg *postBlockProcessConfig) error { slot := cfg.postState.Slot() - if err := transition.UpdateNextSlotCache(cfg.ctx, cfg.blockRoot[:], cfg.postState); err != nil { + root := cfg.roblock.Root() + if err := transition.UpdateNextSlotCache(cfg.ctx, root[:], cfg.postState); err != nil { return errors.Wrap(err, "could not update next slot state cache") } if !slots.IsEpochEnd(slot) { return nil } - return s.handleEpochBoundary(cfg.ctx, slot, cfg.postState, cfg.blockRoot[:]) + return s.handleEpochBoundary(cfg.ctx, slot, cfg.postState, root[:]) } // handleSecondFCUCall handles a second call to FCU when syncing a new block. // This is useful when proposing in the next block and we want to defer the // computation of the next slot shuffling. func (s *Service) handleSecondFCUCall(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) { - if (fcuArgs.attributes == nil || fcuArgs.attributes.IsEmpty()) && cfg.headRoot == cfg.blockRoot { + if (fcuArgs.attributes == nil || fcuArgs.attributes.IsEmpty()) && cfg.headRoot == cfg.roblock.Root() { go s.sendFCUWithAttributes(cfg, fcuArgs) } } @@ -281,7 +283,7 @@ func reportProcessingTime(startTime time.Time) { // called on blocks that arrive after the attestation voting window, or in a // background routine after syncing early blocks. func (s *Service) computePayloadAttributes(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error { - if cfg.blockRoot == cfg.headRoot { + if cfg.roblock.Root() == cfg.headRoot { if err := s.updateCachesPostBlockProcessing(cfg); err != nil { return err } @@ -438,7 +440,7 @@ func (s *Service) ancestorByDB(ctx context.Context, r [32]byte, slot primitives. // This retrieves missing blocks from DB (ie. the blocks that couldn't be received over sync) and inserts them to fork choice store. // This is useful for block tree visualizer and additional vote accounting. -func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, +func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, signed interfaces.ReadOnlySignedBeaconBlock, fCheckpoint, jCheckpoint *ethpb.Checkpoint) error { pendingNodes := make([]*forkchoicetypes.BlockAndCheckpoints, 0) @@ -448,10 +450,15 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk interfa if err != nil { return err } - pendingNodes = append(pendingNodes, &forkchoicetypes.BlockAndCheckpoints{Block: blk, + // The first block can have a bogus root since the block is not inserted in forkchoice + roblock, err := consensus_blocks.NewROBlockWithRoot(signed, [32]byte{}) + if err != nil { + return err + } + pendingNodes = append(pendingNodes, &forkchoicetypes.BlockAndCheckpoints{Block: roblock, JustifiedCheckpoint: jCheckpoint, FinalizedCheckpoint: fCheckpoint}) // As long as parent node is not in fork choice store, and parent node is in DB. - root := blk.ParentRoot() + root := roblock.Block().ParentRoot() for !s.cfg.ForkChoiceStore.HasNode(root) && s.cfg.BeaconDB.HasBlock(ctx, root) { b, err := s.getBlock(ctx, root) if err != nil { @@ -460,8 +467,12 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk interfa if b.Block().Slot() <= fSlot { break } + roblock, err := consensus_blocks.NewROBlockWithRoot(b, root) + if err != nil { + return err + } root = b.Block().ParentRoot() - args := &forkchoicetypes.BlockAndCheckpoints{Block: b.Block(), + args := &forkchoicetypes.BlockAndCheckpoints{Block: roblock, JustifiedCheckpoint: jCheckpoint, FinalizedCheckpoint: fCheckpoint} pendingNodes = append(pendingNodes, args) diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 886fb1b1fcf2..9f326dbf8a78 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -145,9 +145,8 @@ func TestFillForkChoiceMissingBlocks_CanSave(t *testing.T) { require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) fcp2 := &forkchoicetypes.Checkpoint{Epoch: 0, Root: r0} require.NoError(t, service.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(fcp2)) - err = service.fillInForkChoiceMissingBlocks( - context.Background(), wsb.Block(), beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) + context.Background(), wsb, beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) require.NoError(t, err) // 5 nodes from the block tree 1. B0 - B3 - B4 - B6 - B8 @@ -190,7 +189,7 @@ func TestFillForkChoiceMissingBlocks_RootsMatch(t *testing.T) { require.NoError(t, service.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(fcp2)) err = service.fillInForkChoiceMissingBlocks( - context.Background(), wsb.Block(), beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) + context.Background(), wsb, beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) require.NoError(t, err) // 5 nodes from the block tree 1. B0 - B3 - B4 - B6 - B8 @@ -246,7 +245,7 @@ func TestFillForkChoiceMissingBlocks_FilterFinalized(t *testing.T) { // Set finalized epoch to 2. require.NoError(t, service.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: 2, Root: r64})) err = service.fillInForkChoiceMissingBlocks( - context.Background(), wsb.Block(), beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) + context.Background(), wsb, beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) require.NoError(t, err) // There should be 1 node: block 65 @@ -279,7 +278,7 @@ func TestFillForkChoiceMissingBlocks_FinalizedSibling(t *testing.T) { require.NoError(t, err) err = service.fillInForkChoiceMissingBlocks( - context.Background(), wsb.Block(), beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) + context.Background(), wsb, beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint()) require.Equal(t, ErrNotDescendantOfFinalized.Error(), err.Error()) } @@ -566,7 +565,9 @@ func TestOnBlock_CanFinalize_WithOnTick(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, true})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, r) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true})) require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch)) _, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch) require.NoError(t, err) @@ -614,7 +615,9 @@ func TestOnBlock_CanFinalize(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, true})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, r) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true})) require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch)) _, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch) require.NoError(t, err) @@ -640,7 +643,9 @@ func TestOnBlock_CanFinalize(t *testing.T) { func TestOnBlock_NilBlock(t *testing.T) { service, tr := minimalTestService(t) - err := service.postBlockProcess(&postBlockProcessConfig{tr.ctx, nil, [32]byte{}, [32]byte{}, nil, true}) + signed := &consensusblocks.SignedBeaconBlock{} + roblock := consensusblocks.ROBlock{ReadOnlySignedBeaconBlock: signed} + err := service.postBlockProcess(&postBlockProcessConfig{tr.ctx, roblock, [32]byte{}, nil, true}) require.Equal(t, true, IsInvalidBlock(err)) } @@ -688,7 +693,9 @@ func TestOnBlock_CallNewPayloadAndForkchoiceUpdated(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, r) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) testState, err = service.cfg.StateGen.StateByRoot(ctx, r) require.NoError(t, err) } @@ -1114,7 +1121,9 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb1) require.NoError(t, err) lock.Lock() - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb1, r1, [32]byte{}, postState, true})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb1, r1) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true})) lock.Unlock() wg.Done() }() @@ -1124,7 +1133,9 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb2) require.NoError(t, err) lock.Lock() - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb2, r2, [32]byte{}, postState, true})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb2, r2) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true})) lock.Unlock() wg.Done() }() @@ -1134,7 +1145,9 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb3) require.NoError(t, err) lock.Lock() - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb3, r3, [32]byte{}, postState, true})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb3, r3) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true})) lock.Unlock() wg.Done() }() @@ -1144,7 +1157,9 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb4) require.NoError(t, err) lock.Lock() - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb4, r4, [32]byte{}, postState, true})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb4, r4) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true})) lock.Unlock() wg.Done() }() @@ -1219,7 +1234,9 @@ func TestStore_NoViableHead_FCU(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) } for i := 6; i < 12; i++ { @@ -1237,7 +1254,9 @@ func TestStore_NoViableHead_FCU(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) } @@ -1256,7 +1275,9 @@ func TestStore_NoViableHead_FCU(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) } // Check that we haven't justified the second epoch yet @@ -1278,7 +1299,9 @@ func TestStore_NoViableHead_FCU(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, firstInvalidRoot, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, firstInvalidRoot, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, firstInvalidRoot) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) jc = service.cfg.ForkChoiceStore.JustifiedCheckpoint() require.Equal(t, primitives.Epoch(2), jc.Epoch) @@ -1306,7 +1329,9 @@ func TestStore_NoViableHead_FCU(t *testing.T) { postState, err = service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}) + roblock, err = consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.ErrorContains(t, "received an INVALID payload from execution engine", err) // Check that forkchoice's head is the last invalid block imported. The // store's headroot is the previous head (since the invalid block did @@ -1335,7 +1360,9 @@ func TestStore_NoViableHead_FCU(t *testing.T) { postState, err = service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true}) + roblock, err = consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true}) require.NoError(t, err) // Check the newly imported block is head, it justified the right // checkpoint and the node is no longer optimistic @@ -1397,7 +1424,9 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) } for i := 6; i < 12; i++ { @@ -1415,7 +1444,9 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) } @@ -1435,7 +1466,9 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) } // Check that we haven't justified the second epoch yet @@ -1457,7 +1490,9 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, firstInvalidRoot, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, firstInvalidRoot, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, firstInvalidRoot) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) jc = service.cfg.ForkChoiceStore.JustifiedCheckpoint() require.Equal(t, primitives.Epoch(2), jc.Epoch) @@ -1513,7 +1548,9 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) { postState, err = service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true}) + roblock, err = consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true}) require.NoError(t, err) // Check the newly imported block is head, it justified the right // checkpoint and the node is no longer optimistic @@ -1578,7 +1615,9 @@ func TestStore_NoViableHead_Liveness(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) } for i := 6; i < 12; i++ { @@ -1597,7 +1636,9 @@ func TestStore_NoViableHead_Liveness(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) } @@ -1616,7 +1657,9 @@ func TestStore_NoViableHead_Liveness(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, lastValidRoot, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, lastValidRoot, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, lastValidRoot) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) // save the post state and the payload Hash of this block since it will // be the LVH @@ -1643,7 +1686,9 @@ func TestStore_NoViableHead_Liveness(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, invalidRoots[i-13], wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, invalidRoots[i-13], [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, invalidRoots[i-13]) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) } // Check that we have justified the second epoch @@ -1708,7 +1753,9 @@ func TestStore_NoViableHead_Liveness(t *testing.T) { postState, err = service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})) + roblock, err = consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true})) // Check that the head is still INVALID and the node is still optimistic require.Equal(t, invalidHeadRoot, service.cfg.ForkChoiceStore.CachedHeadRoot()) optimistic, err = service.IsOptimistic(ctx) @@ -1731,7 +1778,9 @@ func TestStore_NoViableHead_Liveness(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true}) require.NoError(t, err) st, err = service.cfg.StateGen.StateByRoot(ctx, root) require.NoError(t, err) @@ -1757,7 +1806,9 @@ func TestStore_NoViableHead_Liveness(t *testing.T) { postState, err = service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true}) + roblock, err = consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, true}) require.NoError(t, err) require.Equal(t, root, service.cfg.ForkChoiceStore.CachedHeadRoot()) sjc = service.CurrentJustifiedCheckpt() @@ -1813,7 +1864,9 @@ func TestNoViableHead_Reboot(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) } for i := 6; i < 12; i++ { @@ -1831,7 +1884,9 @@ func TestNoViableHead_Reboot(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) } @@ -1850,7 +1905,9 @@ func TestNoViableHead_Reboot(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, lastValidRoot, wsb, postState)) - err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, lastValidRoot, [32]byte{}, postState, false}) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, lastValidRoot) + require.NoError(t, err) + err = service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false}) require.NoError(t, err) // save the post state and the payload Hash of this block since it will // be the LVH @@ -1879,7 +1936,9 @@ func TestNoViableHead_Reboot(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch)) _, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch) require.NoError(t, err) @@ -1995,7 +2054,9 @@ func TestOnBlock_HandleBlockAttestations(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) st, err = service.HeadState(ctx) require.NoError(t, err) @@ -2059,7 +2120,9 @@ func TestOnBlock_HandleBlockAttestations(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) st, err = service.HeadState(ctx) require.NoError(t, err) @@ -2209,11 +2272,11 @@ func Test_getFCUArgs(t *testing.T) { require.NoError(t, err) wsb, err := consensusblocks.NewSignedBeaconBlock(b) require.NoError(t, err) - + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, [32]byte{'a'}) + require.NoError(t, err) cfg := &postBlockProcessConfig{ ctx: ctx, - signed: wsb, - blockRoot: [32]byte{'a'}, + roblock: roblock, postState: st, isValidPayload: true, } @@ -2223,11 +2286,11 @@ func Test_getFCUArgs(t *testing.T) { require.ErrorContains(t, "block does not exist", err) // canonical branch - cfg.headRoot = cfg.blockRoot + cfg.headRoot = cfg.roblock.Root() fcuArgs = &fcuConfig{} err = s.getFCUArgs(cfg, fcuArgs) require.NoError(t, err) - require.Equal(t, cfg.blockRoot, fcuArgs.headRoot) + require.Equal(t, cfg.roblock.Root(), fcuArgs.headRoot) } func fakeCommitments(n int) [][]byte { diff --git a/beacon-chain/blockchain/receive_attestation_test.go b/beacon-chain/blockchain/receive_attestation_test.go index d0df3e1b23a0..6a905d67c770 100644 --- a/beacon-chain/blockchain/receive_attestation_test.go +++ b/beacon-chain/blockchain/receive_attestation_test.go @@ -10,6 +10,7 @@ import ( forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" @@ -46,7 +47,7 @@ func TestVerifyLMDFFGConsistent(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, r32)) - state, r33, err := prepareForkchoiceState(ctx, 33, [32]byte{'b'}, r32, params.BeaconConfig().ZeroHash, fc, fc) + state, r33, err := prepareForkchoiceState(ctx, 33, [32]byte{'b'}, r32.Root(), params.BeaconConfig().ZeroHash, fc, fc) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, r33)) @@ -54,10 +55,12 @@ func TestVerifyLMDFFGConsistent(t *testing.T) { a := util.NewAttestation() a.Data.Target.Epoch = 1 a.Data.Target.Root = []byte{'c'} - a.Data.BeaconBlockRoot = r33[:] + r33Root := r33.Root() + a.Data.BeaconBlockRoot = r33Root[:] require.ErrorContains(t, wanted, service.VerifyLmdFfgConsistency(context.Background(), a)) - a.Data.Target.Root = r32[:] + r32Root := r32.Root() + a.Data.Target.Root = r32Root[:] err = service.VerifyLmdFfgConsistency(context.Background(), a) require.NoError(t, err, "Could not verify LMD and FFG votes to be consistent") } @@ -116,7 +119,9 @@ func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, tRoot, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, tRoot, [32]byte{}, postState, false})) + roblock, err := consensus_blocks.NewROBlockWithRoot(wsb, tRoot) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) copied, err = service.cfg.StateGen.StateByRoot(ctx, tRoot) require.NoError(t, err) require.Equal(t, 2, fcs.NodeCount()) @@ -176,7 +181,9 @@ func TestService_UpdateHead_NoAtts(t *testing.T) { postState, err := service.validateStateTransition(ctx, preState, wsb) require.NoError(t, err) require.NoError(t, service.savePostStateInfo(ctx, tRoot, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, tRoot, [32]byte{}, postState, false})) + roblock, err := consensus_blocks.NewROBlockWithRoot(wsb, tRoot) + require.NoError(t, err) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, roblock, [32]byte{}, postState, false})) require.Equal(t, 2, fcs.NodeCount()) require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb)) require.Equal(t, tRoot, service.head.root) diff --git a/beacon-chain/blockchain/receive_block.go b/beacon-chain/blockchain/receive_block.go index 45543f9cee86..e6afed076852 100644 --- a/beacon-chain/blockchain/receive_block.go +++ b/beacon-chain/blockchain/receive_block.go @@ -17,6 +17,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/features" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" @@ -100,10 +101,13 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig if err := s.savePostStateInfo(ctx, blockRoot, blockCopy, postState); err != nil { return errors.Wrap(err, "could not save post state info") } + roblock, err := consensus_blocks.NewROBlockWithRoot(blockCopy, blockRoot) + if err != nil { + return err + } args := &postBlockProcessConfig{ ctx: ctx, - signed: blockCopy, - blockRoot: blockRoot, + roblock: roblock, postState: postState, isValidPayload: isValidPayload, } diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index 925d22a6e796..c984a2f79750 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -36,6 +36,7 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" @@ -303,7 +304,15 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error { if err != nil { return errors.Wrap(err, "could not get finalized checkpoint state") } - if err := s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, fRoot); err != nil { + finalizedBlock, err := s.cfg.BeaconDB.Block(s.ctx, fRoot) + if err != nil { + return errors.Wrap(err, "could not get finalized checkpoint block") + } + roblock, err := consensus_blocks.NewROBlockWithRoot(finalizedBlock, fRoot) + if err != nil { + return err + } + if err := s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, roblock); err != nil { return errors.Wrap(err, "could not insert finalized block to forkchoice") } if !features.Get().EnableStartOptimistic { @@ -515,7 +524,11 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon s.cfg.ForkChoiceStore.Lock() defer s.cfg.ForkChoiceStore.Unlock() - if err := s.cfg.ForkChoiceStore.InsertNode(ctx, genesisState, genesisBlkRoot); err != nil { + gb, err := consensus_blocks.NewROBlockWithRoot(genesisBlk, genesisBlkRoot) + if err != nil { + return err + } + if err := s.cfg.ForkChoiceStore.InsertNode(ctx, genesisState, gb); err != nil { log.WithError(err).Fatal("Could not process genesis block for fork choice") } s.cfg.ForkChoiceStore.SetOriginRoot(genesisBlkRoot) diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index a2bf18ac1485..e511559ae5a4 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -376,11 +376,15 @@ func TestHasBlock_ForkChoiceAndDB_DoublyLinkedTree(t *testing.T) { cfg: &config{ForkChoiceStore: doublylinkedtree.New(), BeaconDB: beaconDB}, } b := util.NewBeaconBlock() + wsb, err := consensusblocks.NewSignedBeaconBlock(b) + require.NoError(t, err) r, err := b.Block.HashTreeRoot() require.NoError(t, err) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, r) + require.NoError(t, err) beaconState, err := util.NewBeaconState() require.NoError(t, err) - require.NoError(t, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, r)) + require.NoError(t, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, roblock)) assert.Equal(t, false, s.hasBlock(ctx, [32]byte{}), "Should not have block") assert.Equal(t, true, s.hasBlock(ctx, r), "Should have block") @@ -453,7 +457,11 @@ func BenchmarkHasBlockForkChoiceStore_DoublyLinkedTree(b *testing.B) { require.NoError(b, err) beaconState, err := util.NewBeaconState() require.NoError(b, err) - require.NoError(b, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, r)) + wsb, err := consensusblocks.NewSignedBeaconBlock(blk) + require.NoError(b, err) + roblock, err := consensusblocks.NewROBlockWithRoot(wsb, r) + require.NoError(b, err) + require.NoError(b, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, roblock)) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index 0ba1be076529..92edfa23a9a7 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -567,7 +567,7 @@ func prepareForkchoiceState( payloadHash [32]byte, justified *ethpb.Checkpoint, finalized *ethpb.Checkpoint, -) (state.BeaconState, [32]byte, error) { +) (state.BeaconState, blocks.ROBlock, error) { blockHeader := ðpb.BeaconBlockHeader{ ParentRoot: parentRoot[:], } @@ -588,7 +588,26 @@ func prepareForkchoiceState( base.BlockRoots[0] = append(base.BlockRoots[0], blockRoot[:]...) st, err := state_native.InitializeFromProtoBellatrix(base) - return st, blockRoot, err + if err != nil { + return nil, blocks.ROBlock{}, err + } + blk := ðpb.SignedBeaconBlockBellatrix{ + Block: ðpb.BeaconBlockBellatrix{ + Slot: slot, + ParentRoot: parentRoot[:], + Body: ðpb.BeaconBlockBodyBellatrix{ + ExecutionPayload: &enginev1.ExecutionPayload{ + BlockHash: payloadHash[:], + }, + }, + }, + } + signed, err := blocks.NewSignedBeaconBlock(blk) + if err != nil { + return nil, blocks.ROBlock{}, err + } + roblock, err := blocks.NewROBlockWithRoot(signed, blockRoot) + return st, roblock, err } // CachedHeadRoot mocks the same method in the chain service @@ -631,9 +650,9 @@ func (s *ChainService) HighestReceivedBlockSlot() primitives.Slot { } // InsertNode mocks the same method in the chain service -func (s *ChainService) InsertNode(ctx context.Context, st state.BeaconState, root [32]byte) error { +func (s *ChainService) InsertNode(ctx context.Context, st state.BeaconState, block blocks.ROBlock) error { if s.ForkChoiceStore != nil { - return s.ForkChoiceStore.InsertNode(ctx, st, root) + return s.ForkChoiceStore.InsertNode(ctx, st, block) } return nil } diff --git a/beacon-chain/forkchoice/BUILD.bazel b/beacon-chain/forkchoice/BUILD.bazel index 8dc67d81a000..7b922444c9f8 100644 --- a/beacon-chain/forkchoice/BUILD.bazel +++ b/beacon-chain/forkchoice/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//beacon-chain/forkchoice/types:go_default_library", "//beacon-chain/state:go_default_library", "//config/fieldparams:go_default_library", + "//consensus-types/blocks:go_default_library", "//consensus-types/forkchoice:go_default_library", "//consensus-types/primitives:go_default_library", "@com_github_pkg_errors//:go_default_library", diff --git a/beacon-chain/forkchoice/doubly-linked-tree/BUILD.bazel b/beacon-chain/forkchoice/doubly-linked-tree/BUILD.bazel index f7bdf4696334..1bf47ea02a47 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/BUILD.bazel +++ b/beacon-chain/forkchoice/doubly-linked-tree/BUILD.bazel @@ -23,13 +23,13 @@ go_library( "//testing/spectest:__subpackages__", ], deps = [ - "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/epoch/precompute:go_default_library", "//beacon-chain/forkchoice:go_default_library", "//beacon-chain/forkchoice/types:go_default_library", "//beacon-chain/state:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", + "//consensus-types/blocks:go_default_library", "//consensus-types/forkchoice:go_default_library", "//consensus-types/primitives:go_default_library", "//encoding/bytesutil:go_default_library", diff --git a/beacon-chain/forkchoice/doubly-linked-tree/errors.go b/beacon-chain/forkchoice/doubly-linked-tree/errors.go index 2a2795e6bc8e..4286765daa75 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/errors.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/errors.go @@ -11,4 +11,3 @@ var errInvalidOptimisticStatus = errors.New("invalid optimistic status") var errInvalidNilCheckpoint = errors.New("invalid nil checkpoint") var errInvalidUnrealizedJustifiedEpoch = errors.New("invalid unrealized justified epoch") var errInvalidUnrealizedFinalizedEpoch = errors.New("invalid unrealized finalized epoch") -var errNilBlockHeader = errors.New("invalid nil block header") diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go index 296dfbeeca3c..47ed987475fa 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go @@ -6,18 +6,17 @@ import ( "time" "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice" forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" forkchoice2 "github.com/prysmaticlabs/prysm/v5/consensus-types/forkchoice" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" - "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" "github.com/sirupsen/logrus" ) @@ -105,26 +104,10 @@ func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices [] } // InsertNode processes a new block by inserting it to the fork choice store. -func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, root [32]byte) error { +func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, roblock consensus_blocks.ROBlock) error { ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.InsertNode") defer span.End() - slot := state.Slot() - bh := state.LatestBlockHeader() - if bh == nil { - return errNilBlockHeader - } - parentRoot := bytesutil.ToBytes32(bh.ParentRoot) - var payloadHash [32]byte - if state.Version() >= version.Bellatrix { - ph, err := state.LatestExecutionPayloadHeader() - if err != nil { - return err - } - if ph != nil { - copy(payloadHash[:], ph.BlockHash()) - } - } jc := state.CurrentJustifiedCheckpoint() if jc == nil { return errInvalidNilCheckpoint @@ -135,7 +118,7 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, ro return errInvalidNilCheckpoint } finalizedEpoch := fc.Epoch - node, err := f.store.insert(ctx, slot, root, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch) + node, err := f.store.insert(ctx, roblock, justifiedEpoch, finalizedEpoch) if err != nil { return err } @@ -490,15 +473,8 @@ func (f *ForkChoice) InsertChain(ctx context.Context, chain []*forkchoicetypes.B return nil } for i := len(chain) - 1; i > 0; i-- { - b := chain[i].Block - r := chain[i-1].Block.ParentRoot() - parentRoot := b.ParentRoot() - payloadHash, err := blocks.GetBlockPayloadHash(b) - if err != nil { - return err - } if _, err := f.store.insert(ctx, - b.Slot(), r, parentRoot, payloadHash, + chain[i].Block, chain[i].JustifiedCheckpoint.Epoch, chain[i].FinalizedCheckpoint.Epoch); err != nil { return err } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go index 496a0dff1283..4a62115a66c9 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go @@ -32,7 +32,7 @@ func prepareForkchoiceState( payloadHash [32]byte, justifiedEpoch primitives.Epoch, finalizedEpoch primitives.Epoch, -) (state.BeaconState, [32]byte, error) { +) (state.BeaconState, blocks.ROBlock, error) { blockHeader := ðpb.BeaconBlockHeader{ ParentRoot: parentRoot[:], } @@ -59,21 +59,40 @@ func prepareForkchoiceState( } st, err := state_native.InitializeFromProtoBellatrix(base) - return st, blockRoot, err + if err != nil { + return nil, blocks.ROBlock{}, err + } + blk := ðpb.SignedBeaconBlockBellatrix{ + Block: ðpb.BeaconBlockBellatrix{ + Slot: slot, + ParentRoot: parentRoot[:], + Body: ðpb.BeaconBlockBodyBellatrix{ + ExecutionPayload: &enginev1.ExecutionPayload{ + BlockHash: payloadHash[:], + }, + }, + }, + } + signed, err := blocks.NewSignedBeaconBlock(blk) + if err != nil { + return nil, blocks.ROBlock{}, err + } + roblock, err := blocks.NewROBlockWithRoot(signed, blockRoot) + return st, roblock, err } func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) { f := setup(0, 0) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) + st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) f.votes = []Vote{ {indexToHash(1), indexToHash(1), 0}, @@ -94,15 +113,15 @@ func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) { func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) { f := setup(0, 0) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) + st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) s := f.store s.nodeByRoot[indexToHash(1)].balance = 100 s.nodeByRoot[indexToHash(2)].balance = 100 @@ -125,15 +144,15 @@ func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) { func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) { f := setup(0, 0) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) + st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) s := f.store s.nodeByRoot[indexToHash(1)].balance = 100 s.nodeByRoot[indexToHash(2)].balance = 100 @@ -156,24 +175,24 @@ func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) { func TestForkChoice_IsCanonical(t *testing.T) { f := setup(1, 1) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 6, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 6, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) require.Equal(t, true, f.IsCanonical(params.BeaconConfig().ZeroHash)) require.Equal(t, false, f.IsCanonical(indexToHash(1))) @@ -187,24 +206,24 @@ func TestForkChoice_IsCanonical(t *testing.T) { func TestForkChoice_IsCanonicalReorg(t *testing.T) { f := setup(1, 1) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) f.store.nodeByRoot[[32]byte{'3'}].balance = 10 require.NoError(t, f.store.treeRootNode.applyWeightChanges(ctx)) @@ -233,15 +252,15 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) { func TestForkChoice_AncestorRoot(t *testing.T) { f := setup(1, 1) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 5, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 5, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) f.store.treeRootNode = f.store.nodeByRoot[indexToHash(1)] f.store.treeRootNode.parent = nil @@ -265,12 +284,12 @@ func TestForkChoice_AncestorRoot(t *testing.T) { func TestForkChoice_AncestorEqualSlot(t *testing.T) { f := setup(1, 1) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 100) require.NoError(t, err) @@ -280,12 +299,12 @@ func TestForkChoice_AncestorEqualSlot(t *testing.T) { func TestForkChoice_AncestorLowerSlot(t *testing.T) { f := setup(1, 1) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 150) require.NoError(t, err) @@ -296,20 +315,20 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) { ctx := context.Background() f := setup(1, 1) // Insert a block it will be head - st, blkRoot, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) head, err := f.Head(ctx) require.NoError(t, err) require.Equal(t, [32]byte{'a'}, head) // Insert two extra blocks - st, blkRoot, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1) + st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) head, err = f.Head(ctx) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, head) @@ -378,36 +397,36 @@ func TestStore_CommonAncestor(t *testing.T) { // \-- c -- f // \-- g // \ -- h -- i -- j - st, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, [32]byte{'d'}, [32]byte{'b'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'d'}, [32]byte{'b'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 4, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 4, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 5, [32]byte{'f'}, [32]byte{'c'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 5, [32]byte{'f'}, [32]byte{'c'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 6, [32]byte{'g'}, [32]byte{'c'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 6, [32]byte{'g'}, [32]byte{'c'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 7, [32]byte{'h'}, [32]byte{'c'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 7, [32]byte{'h'}, [32]byte{'c'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 8, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 8, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 9, [32]byte{'j'}, [32]byte{'i'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 9, [32]byte{'j'}, [32]byte{'i'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) tests := []struct { name string @@ -498,18 +517,18 @@ func TestStore_CommonAncestor(t *testing.T) { // a -- b -- c -- d f = setup(0, 0) - st, blkRoot, err = prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) + st, roblock, err = prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 2, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 3, [32]byte{'d'}, [32]byte{'c'}, [32]byte{}, 1, 1) + require.NoError(t, f.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'d'}, [32]byte{'c'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) tests = []struct { name string r1 [32]byte @@ -590,7 +609,9 @@ func TestStore_InsertChain(t *testing.T) { require.NoError(t, err) wsb, err := blocks.NewSignedBeaconBlock(blk) require.NoError(t, err) - blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), + roblock, err := blocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: roblock, JustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, }) @@ -599,14 +620,16 @@ func TestStore_InsertChain(t *testing.T) { blk.Block.Slot = primitives.Slot(i) copiedRoot := root blk.Block.ParentRoot = copiedRoot[:] + root, err = blk.Block.HashTreeRoot() + require.NoError(t, err) wsb, err = blocks.NewSignedBeaconBlock(blk) require.NoError(t, err) - blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), + roblock, err := blocks.NewROBlockWithRoot(wsb, root) + require.NoError(t, err) + blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: roblock, JustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, }) - root, err = blk.Block.HashTreeRoot() - require.NoError(t, err) } args := make([]*forkchoicetypes.BlockAndCheckpoints, 10) for i := 0; i < len(blks); i++ { @@ -670,26 +693,26 @@ func TestForkChoice_UpdateCheckpoints(t *testing.T) { fcs.store.finalizedCheckpoint = tt.finalized fcs.store.genesisTime = uint64(time.Now().Unix()) - uint64(tt.currentSlot)*params.BeaconConfig().SecondsPerSlot - st, blkRoot, err := prepareForkchoiceState(ctx, 32, [32]byte{'f'}, + st, roblock, err := prepareForkchoiceState(ctx, 32, [32]byte{'f'}, [32]byte{}, [32]byte{}, tt.finalized.Epoch, tt.finalized.Epoch) require.NoError(t, err) - require.NoError(t, fcs.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 64, [32]byte{'j'}, + require.NoError(t, fcs.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 64, [32]byte{'j'}, [32]byte{'f'}, [32]byte{}, tt.justified.Epoch, tt.finalized.Epoch) require.NoError(t, err) - require.NoError(t, fcs.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 96, [32]byte{'b'}, + require.NoError(t, fcs.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 96, [32]byte{'b'}, [32]byte{'j'}, [32]byte{}, tt.newJustified.Epoch, tt.newFinalized.Epoch) require.NoError(t, err) - require.NoError(t, fcs.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 96, [32]byte{'c'}, + require.NoError(t, fcs.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 96, [32]byte{'c'}, [32]byte{'f'}, [32]byte{}, tt.newJustified.Epoch, tt.newFinalized.Epoch) require.NoError(t, err) - require.NoError(t, fcs.InsertNode(ctx, st, blkRoot)) - st, blkRoot, err = prepareForkchoiceState(ctx, 65, [32]byte{'h'}, + require.NoError(t, fcs.InsertNode(ctx, st, roblock)) + st, roblock, err = prepareForkchoiceState(ctx, 65, [32]byte{'h'}, [32]byte{'f'}, [32]byte{}, tt.newFinalized.Epoch, tt.newFinalized.Epoch) require.NoError(t, err) - require.NoError(t, fcs.InsertNode(ctx, st, blkRoot)) + require.NoError(t, fcs.InsertNode(ctx, st, roblock)) // restart justifications cause insertion messed it up fcs.store.justifiedCheckpoint = tt.justified fcs.store.finalizedCheckpoint = tt.finalized @@ -715,9 +738,9 @@ func TestWeight(t *testing.T) { f := setup(0, 0) root := [32]byte{'a'} - st, blkRoot, err := prepareForkchoiceState(ctx, 0, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 0, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) n, ok := f.store.nodeByRoot[root] require.Equal(t, true, ok) @@ -747,9 +770,9 @@ func TestForkChoice_UnrealizedJustifiedPayloadBlockHash(t *testing.T) { ctx := context.Background() f := setup(0, 0) - st, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) + st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, blkRoot)) + require.NoError(t, f.InsertNode(ctx, st, roblock)) f.store.unrealizedJustifiedCheckpoint.Root = [32]byte{'a'} got := f.UnrealizedJustifiedPayloadBlockHash() @@ -760,90 +783,90 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) { f := setup(0, 0) ctx := context.Background() - st, root, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) + st, blk, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) require.NoError(t, err) // No Node - viable, err := f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: root}) + viable, err := f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root()}) require.NoError(t, err) require.Equal(t, false, viable) // No Children - require.NoError(t, f.InsertNode(ctx, st, root)) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: root, Epoch: 0}) + require.NoError(t, f.InsertNode(ctx, st, blk)) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root(), Epoch: 0}) require.NoError(t, err) require.Equal(t, true, viable) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: root, Epoch: 1}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root(), Epoch: 1}) require.NoError(t, err) require.Equal(t, true, viable) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: root, Epoch: 2}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root(), Epoch: 2}) require.NoError(t, err) require.Equal(t, true, viable) - st, bRoot, err := prepareForkchoiceState(ctx, 1, [32]byte{'b'}, root, [32]byte{'B'}, 0, 0) + st, blk2, err := prepareForkchoiceState(ctx, 1, [32]byte{'b'}, blk.Root(), [32]byte{'B'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, bRoot)) + require.NoError(t, f.InsertNode(ctx, st, blk2)) // Epoch start - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: root}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root()}) require.NoError(t, err) require.Equal(t, true, viable) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: root, Epoch: 1}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root(), Epoch: 1}) require.NoError(t, err) require.Equal(t, false, viable) // No Children but impossible checkpoint - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: bRoot}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root()}) require.NoError(t, err) require.Equal(t, false, viable) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: bRoot, Epoch: 1}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root(), Epoch: 1}) require.NoError(t, err) require.Equal(t, true, viable) - st, cRoot, err := prepareForkchoiceState(ctx, 2, [32]byte{'c'}, bRoot, [32]byte{'C'}, 0, 0) + st, blk3, err := prepareForkchoiceState(ctx, 2, [32]byte{'c'}, blk2.Root(), [32]byte{'C'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, cRoot)) + require.NoError(t, f.InsertNode(ctx, st, blk3)) // Children in same epoch - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: bRoot}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root()}) require.NoError(t, err) require.Equal(t, false, viable) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: bRoot, Epoch: 1}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root(), Epoch: 1}) require.NoError(t, err) require.Equal(t, false, viable) - st, dRoot, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch, [32]byte{'d'}, bRoot, [32]byte{'D'}, 0, 0) + st, blk4, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch, [32]byte{'d'}, blk2.Root(), [32]byte{'D'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, dRoot)) + require.NoError(t, f.InsertNode(ctx, st, blk4)) // Children in next epoch but boundary - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: bRoot}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root()}) require.NoError(t, err) require.Equal(t, false, viable) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: bRoot, Epoch: 1}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root(), Epoch: 1}) require.NoError(t, err) require.Equal(t, false, viable) // Boundary block - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: dRoot, Epoch: 1}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk4.Root(), Epoch: 1}) require.NoError(t, err) require.Equal(t, true, viable) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: dRoot, Epoch: 0}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk4.Root(), Epoch: 0}) require.NoError(t, err) require.Equal(t, false, viable) // Children in next epoch - st, eRoot, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'e'}, bRoot, [32]byte{'E'}, 0, 0) + st, blk5, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'e'}, blk2.Root(), [32]byte{'E'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, eRoot)) + require.NoError(t, f.InsertNode(ctx, st, blk5)) - viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: bRoot, Epoch: 1}) + viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root(), Epoch: 1}) require.NoError(t, err) require.Equal(t, true, viable) } @@ -851,14 +874,14 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) { func TestForkChoiceSlot(t *testing.T) { f := setup(0, 0) ctx := context.Background() - st, root, err := prepareForkchoiceState(ctx, 3, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) + st, blk, err := prepareForkchoiceState(ctx, 3, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) require.NoError(t, err) // No Node - _, err = f.Slot(root) + _, err = f.Slot(blk.Root()) require.ErrorIs(t, ErrNilNode, err) - require.NoError(t, f.InsertNode(ctx, st, root)) - slot, err := f.Slot(root) + require.NoError(t, f.InsertNode(ctx, st, blk)) + slot, err := f.Slot(blk.Root()) require.NoError(t, err) require.Equal(t, primitives.Slot(3), slot) } @@ -867,16 +890,16 @@ func TestForkchoiceParentRoot(t *testing.T) { f := setup(0, 0) ctx := context.Background() root1 := [32]byte{'a'} - st, root, err := prepareForkchoiceState(ctx, 3, root1, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) + st, blk, err := prepareForkchoiceState(ctx, 3, root1, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) root2 := [32]byte{'b'} - st, root, err = prepareForkchoiceState(ctx, 3, root2, root1, [32]byte{'A'}, 0, 0) + st, blk, err = prepareForkchoiceState(ctx, 3, root2, root1, [32]byte{'A'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) - root, err = f.ParentRoot(root2) + root, err := f.ParentRoot(root2) require.NoError(t, err) require.Equal(t, root1, root) @@ -892,12 +915,12 @@ func TestForkchoiceParentRoot(t *testing.T) { func TestForkChoice_CleanupInserting(t *testing.T) { f := setup(0, 0) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 2) + st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 2) f.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return f.justifiedBalances, errors.New("mock err") }) require.NoError(t, err) - require.NotNil(t, f.InsertNode(ctx, st, blkRoot)) - require.Equal(t, false, f.HasNode(blkRoot)) + require.NotNil(t, f.InsertNode(ctx, st, roblock)) + require.Equal(t, false, f.HasNode(roblock.Root())) } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/node_test.go b/beacon-chain/forkchoice/doubly-linked-tree/node_test.go index 66bd0e6079e8..f6b40b1c98c9 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/node_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/node_test.go @@ -14,15 +14,15 @@ import ( func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) { f := setup(0, 0) ctx := context.Background() - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) // The updated balances of each node is 100 s := f.store @@ -41,15 +41,15 @@ func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) { func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) { f := setup(0, 0) ctx := context.Background() - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) // The updated balances of each node is 100 s := f.store @@ -72,9 +72,9 @@ func TestNode_UpdateBestDescendant_NonViableChild(t *testing.T) { f := setup(1, 1) ctx := context.Background() // Input child is not viable. - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 3) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 3) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) // Verify parent's best child and best descendant are `none`. s := f.store @@ -87,9 +87,9 @@ func TestNode_UpdateBestDescendant_ViableChild(t *testing.T) { f := setup(1, 1) ctx := context.Background() // Input child is the best descendant - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) s := f.store assert.Equal(t, 1, len(s.treeRootNode.children)) @@ -100,12 +100,12 @@ func TestNode_UpdateBestDescendant_HigherWeightChild(t *testing.T) { f := setup(1, 1) ctx := context.Background() // Input child is the best descendant - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) s := f.store s.nodeByRoot[indexToHash(1)].weight = 100 @@ -120,12 +120,12 @@ func TestNode_UpdateBestDescendant_LowerWeightChild(t *testing.T) { f := setup(1, 1) ctx := context.Background() // Input child is the best descendant - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) s := f.store s.nodeByRoot[indexToHash(1)].weight = 200 @@ -159,21 +159,21 @@ func TestNode_ViableForHead(t *testing.T) { func TestNode_LeadsToViableHead(t *testing.T) { f := setup(4, 3) ctx := context.Background() - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - state, blkRoot, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 4, 3) + require.NoError(t, f.InsertNode(ctx, state, blk)) + state, blk, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 4, 3) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 5)) require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 5)) @@ -192,28 +192,28 @@ func TestNode_SetFullyValidated(t *testing.T) { // \ // -- 5 (true) // - state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - storeNodes[1] = f.store.nodeByRoot[blkRoot] + require.NoError(t, f.InsertNode(ctx, state, blk)) + storeNodes[1] = f.store.nodeByRoot[blk.Root()] require.NoError(t, f.SetOptimisticToValid(ctx, params.BeaconConfig().ZeroHash)) - state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - storeNodes[2] = f.store.nodeByRoot[blkRoot] + require.NoError(t, f.InsertNode(ctx, state, blk)) + storeNodes[2] = f.store.nodeByRoot[blk.Root()] require.NoError(t, f.SetOptimisticToValid(ctx, indexToHash(1))) - state, blkRoot, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - storeNodes[3] = f.store.nodeByRoot[blkRoot] - state, blkRoot, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + storeNodes[3] = f.store.nodeByRoot[blk.Root()] + state, blk, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - storeNodes[4] = f.store.nodeByRoot[blkRoot] - state, blkRoot, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + storeNodes[4] = f.store.nodeByRoot[blk.Root()] + state, blk, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - storeNodes[5] = f.store.nodeByRoot[blkRoot] + require.NoError(t, f.InsertNode(ctx, state, blk)) + storeNodes[5] = f.store.nodeByRoot[blk.Root()] opt, err := f.IsOptimistic(indexToHash(5)) require.NoError(t, err) @@ -266,9 +266,9 @@ func TestNode_TimeStampsChecks(t *testing.T) { driftGenesisTime(f, 1, 1) root := [32]byte{'a'} f.justifiedBalances = []uint64{10} - state, blkRoot, err := prepareForkchoiceState(ctx, 1, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) + state, blk, err := prepareForkchoiceState(ctx, 1, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) headRoot, err := f.Head(ctx) require.NoError(t, err) require.Equal(t, root, headRoot) @@ -283,9 +283,9 @@ func TestNode_TimeStampsChecks(t *testing.T) { // late block driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1) root = [32]byte{'b'} - state, blkRoot, err = prepareForkchoiceState(ctx, 2, root, [32]byte{'a'}, [32]byte{'B'}, 0, 0) + state, blk, err = prepareForkchoiceState(ctx, 2, root, [32]byte{'a'}, [32]byte{'B'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) headRoot, err = f.Head(ctx) require.NoError(t, err) require.Equal(t, root, headRoot) @@ -299,9 +299,9 @@ func TestNode_TimeStampsChecks(t *testing.T) { // very late block driftGenesisTime(f, 3, ProcessAttestationsThreshold+1) root = [32]byte{'c'} - state, blkRoot, err = prepareForkchoiceState(ctx, 3, root, [32]byte{'b'}, [32]byte{'C'}, 0, 0) + state, blk, err = prepareForkchoiceState(ctx, 3, root, [32]byte{'b'}, [32]byte{'C'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) headRoot, err = f.Head(ctx) require.NoError(t, err) require.Equal(t, root, headRoot) @@ -314,9 +314,9 @@ func TestNode_TimeStampsChecks(t *testing.T) { // block from the future root = [32]byte{'d'} - state, blkRoot, err = prepareForkchoiceState(ctx, 5, root, [32]byte{'c'}, [32]byte{'D'}, 1, 1) + state, blk, err = prepareForkchoiceState(ctx, 5, root, [32]byte{'c'}, [32]byte{'D'}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) + require.NoError(t, f.InsertNode(ctx, state, blk)) headRoot, err = f.Head(ctx) require.NoError(t, err) require.Equal(t, root, headRoot) diff --git a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go index 28b1be753596..a4f79edbf3a1 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go @@ -484,23 +484,23 @@ func TestForkChoice_missingProposerBoostRoots(t *testing.T) { } f.justifiedBalances = balances driftGenesisTime(f, 1, 0) - st, root, err := prepareForkchoiceState(ctx, 1, [32]byte{'r'}, [32]byte{}, [32]byte{}, 1, 1) + st, blk, err := prepareForkchoiceState(ctx, 1, [32]byte{'r'}, [32]byte{}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) f.store.previousProposerBoostRoot = [32]byte{'p'} headRoot, err := f.Head(ctx) require.NoError(t, err) - require.Equal(t, root, headRoot) + require.Equal(t, blk.Root(), headRoot) require.Equal(t, [32]byte{'r'}, f.store.proposerBoostRoot) f.store.proposerBoostRoot = [32]byte{'p'} driftGenesisTime(f, 3, 0) - st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'a'}, [32]byte{'r'}, [32]byte{}, 1, 1) + st, blk, err = prepareForkchoiceState(ctx, 2, [32]byte{'a'}, [32]byte{'r'}, [32]byte{}, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) headRoot, err = f.Head(ctx) require.NoError(t, err) - require.Equal(t, root, headRoot) + require.Equal(t, blk.Root(), headRoot) require.Equal(t, [32]byte{'p'}, f.store.proposerBoostRoot) } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go b/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go index dd6694aed7cb..990a8105a445 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go @@ -19,23 +19,23 @@ func TestForkChoice_ShouldOverrideFCU(t *testing.T) { f.store.committeeWeight /= uint64(params.BeaconConfig().SlotsPerEpoch) ctx := context.Background() driftGenesisTime(f, 1, 0) - st, root, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, [32]byte{}, [32]byte{'A'}, 0, 0) + st, blk, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, [32]byte{}, [32]byte{'A'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) attesters := make([]uint64, f.numActiveValidators-64) for i := range attesters { attesters[i] = uint64(i + 64) } - f.ProcessAttestation(ctx, attesters, root, 0) + f.ProcessAttestation(ctx, attesters, blk.Root(), 0) orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1) - st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 0, 0) + st, blk, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) headRoot, err := f.Head(ctx) require.NoError(t, err) - require.Equal(t, root, headRoot) + require.Equal(t, blk.Root(), headRoot) t.Run("head is weak", func(t *testing.T) { require.Equal(t, true, f.ShouldOverrideFCU()) @@ -117,23 +117,23 @@ func TestForkChoice_GetProposerHead(t *testing.T) { ctx := context.Background() driftGenesisTime(f, 1, 0) parentRoot := [32]byte{'a'} - st, root, err := prepareForkchoiceState(ctx, 1, parentRoot, [32]byte{}, [32]byte{'A'}, 0, 0) + st, blk, err := prepareForkchoiceState(ctx, 1, parentRoot, [32]byte{}, [32]byte{'A'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) attesters := make([]uint64, f.numActiveValidators-64) for i := range attesters { attesters[i] = uint64(i + 64) } - f.ProcessAttestation(ctx, attesters, root, 0) + f.ProcessAttestation(ctx, attesters, blk.Root(), 0) driftGenesisTime(f, 3, 1) childRoot := [32]byte{'b'} - st, root, err = prepareForkchoiceState(ctx, 2, childRoot, [32]byte{'a'}, [32]byte{'B'}, 0, 0) + st, blk, err = prepareForkchoiceState(ctx, 2, childRoot, [32]byte{'a'}, [32]byte{'B'}, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, st, root)) + require.NoError(t, f.InsertNode(ctx, st, blk)) headRoot, err := f.Head(ctx) require.NoError(t, err) - require.Equal(t, root, headRoot) + require.Equal(t, blk.Root(), headRoot) orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot f.store.headNode.timestamp -= params.BeaconConfig().SecondsPerSlot - orphanLateBlockFirstThreshold t.Run("head is weak", func(t *testing.T) { diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store.go b/beacon-chain/forkchoice/doubly-linked-tree/store.go index 931d7254dc8e..68b99a1584dc 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store.go @@ -8,8 +8,10 @@ import ( "github.com/pkg/errors" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" ) @@ -63,12 +65,24 @@ func (s *Store) head(ctx context.Context) ([32]byte, error) { // insert registers a new block node to the fork choice store's node list. // It then updates the new node's parent with the best child and descendant node. func (s *Store) insert(ctx context.Context, - slot primitives.Slot, - root, parentRoot, payloadHash [fieldparams.RootLength]byte, + roblock consensus_blocks.ROBlock, justifiedEpoch, finalizedEpoch primitives.Epoch) (*Node, error) { ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.insert") defer span.End() + root := roblock.Root() + block := roblock.Block() + slot := block.Slot() + parentRoot := block.ParentRoot() + var payloadHash [32]byte + if block.Version() >= version.Bellatrix { + execution, err := block.Body().Execution() + if err != nil { + return nil, err + } + copy(payloadHash[:], execution.BlockHash()) + } + // Return if the block has been inserted into Store before. if n, ok := s.nodeByRoot[root]; ok { return n, nil diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store_test.go b/beacon-chain/forkchoice/doubly-linked-tree/store_test.go index 29b60599f6c4..383d377ad67e 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store_test.go @@ -26,9 +26,9 @@ func TestStore_FinalizedEpoch(t *testing.T) { func TestStore_NodeCount(t *testing.T) { f := setup(0, 0) - state, blkRoot, err := prepareForkchoiceState(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) + state, blk, err := prepareForkchoiceState(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NoError(t, f.InsertNode(context.Background(), state, blkRoot)) + require.NoError(t, f.InsertNode(context.Background(), state, blk)) require.Equal(t, 2, f.NodeCount()) } @@ -133,7 +133,10 @@ func TestStore_Insert(t *testing.T) { fc := &forkchoicetypes.Checkpoint{Epoch: 0} s := &Store{nodeByRoot: nodeByRoot, treeRootNode: treeRootNode, nodeByPayload: nodeByPayload, justifiedCheckpoint: jc, finalizedCheckpoint: fc, highestReceivedNode: &Node{}} payloadHash := [32]byte{'a'} - _, err := s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), payloadHash, 1, 1) + ctx := context.Background() + _, blk, err := prepareForkchoiceState(ctx, 100, indexToHash(100), indexToHash(0), payloadHash, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) assert.Equal(t, 2, len(s.nodeByRoot), "Did not insert block") assert.Equal(t, (*Node)(nil), treeRootNode.parent, "Incorrect parent") @@ -327,7 +330,10 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // Make sure it doesn't underflow s.genesisTime = uint64(time.Now().Add(time.Duration(-1*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) - _, err := s.insert(context.Background(), 1, [32]byte{'a'}, b, b, 1, 1) + ctx := context.Background() + _, blk, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) count, err := f.ReceivedBlocksLastEpoch() require.NoError(t, err) @@ -337,7 +343,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 64 // Received block last epoch is 1 - _, err = s.insert(context.Background(), 64, [32]byte{'A'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 64, [32]byte{'A'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration((-64*int64(params.BeaconConfig().SecondsPerSlot))-1) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -348,7 +356,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 64 65 // Received block last epoch is 2 - _, err = s.insert(context.Background(), 65, [32]byte{'B'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 65, [32]byte{'B'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration(-66*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -359,7 +369,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 64 65 66 // Received block last epoch is 3 - _, err = s.insert(context.Background(), 66, [32]byte{'C'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 66, [32]byte{'C'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration(-66*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -370,7 +382,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 64 65 66 // 98 // Received block last epoch is 1 - _, err = s.insert(context.Background(), 98, [32]byte{'D'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 98, [32]byte{'D'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration(-98*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -382,7 +396,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 98 // 132 // Received block last epoch is 1 - _, err = s.insert(context.Background(), 132, [32]byte{'E'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 132, [32]byte{'E'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration(-132*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -395,7 +411,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 132 // 99 // Received block last epoch is still 1. 99 is outside the window - _, err = s.insert(context.Background(), 99, [32]byte{'F'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 99, [32]byte{'F'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration(-132*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -408,7 +426,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 132 // 99 100 // Received block last epoch is still 1. 100 is at the same position as 132 - _, err = s.insert(context.Background(), 100, [32]byte{'G'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 100, [32]byte{'G'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration(-132*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -421,7 +441,9 @@ func TestForkChoice_ReceivedBlocksLastEpoch(t *testing.T) { // 132 // 99 100 101 // Received block last epoch is 2. 101 is within the window - _, err = s.insert(context.Background(), 101, [32]byte{'H'}, b, b, 1, 1) + _, blk, err = prepareForkchoiceState(ctx, 101, [32]byte{'H'}, b, b, 1, 1) + require.NoError(t, err) + _, err = s.insert(ctx, blk, 1, 1) require.NoError(t, err) s.genesisTime = uint64(time.Now().Add(time.Duration(-132*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second).Unix()) count, err = f.ReceivedBlocksLastEpoch() @@ -443,94 +465,94 @@ func TestStore_TargetRootForEpoch(t *testing.T) { ctx := context.Background() f := setup(1, 1) - state, blkRoot, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) + state, blk, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - target, err := f.TargetRootForEpoch(blkRoot, 1) + require.NoError(t, f.InsertNode(ctx, state, blk)) + target, err := f.TargetRootForEpoch(blk.Root(), 1) require.NoError(t, err) - require.Equal(t, target, blkRoot) + require.Equal(t, target, blk.Root()) - state, root1, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'b'}, blkRoot, params.BeaconConfig().ZeroHash, 1, 1) + state, blk1, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'b'}, blk.Root(), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, root1)) - target, err = f.TargetRootForEpoch(root1, 1) + require.NoError(t, f.InsertNode(ctx, state, blk1)) + target, err = f.TargetRootForEpoch(blk1.Root(), 1) require.NoError(t, err) - require.Equal(t, target, blkRoot) + require.Equal(t, target, blk.Root()) // Insert a block for the next epoch (missed slot 0) - state, root2, err := prepareForkchoiceState(ctx, 2*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'c'}, root1, params.BeaconConfig().ZeroHash, 1, 1) + state, blk2, err := prepareForkchoiceState(ctx, 2*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'c'}, blk1.Root(), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, root2)) - target, err = f.TargetRootForEpoch(root2, 2) + require.NoError(t, f.InsertNode(ctx, state, blk2)) + target, err = f.TargetRootForEpoch(blk2.Root(), 2) require.NoError(t, err) - require.Equal(t, target, root1) + require.Equal(t, target, blk1.Root()) - state, root3, err := prepareForkchoiceState(ctx, 2*params.BeaconConfig().SlotsPerEpoch+2, [32]byte{'d'}, root2, params.BeaconConfig().ZeroHash, 1, 1) + state, blk3, err := prepareForkchoiceState(ctx, 2*params.BeaconConfig().SlotsPerEpoch+2, [32]byte{'d'}, blk2.Root(), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, root3)) - target, err = f.TargetRootForEpoch(root2, 2) + require.NoError(t, f.InsertNode(ctx, state, blk3)) + target, err = f.TargetRootForEpoch(blk2.Root(), 2) require.NoError(t, err) - require.Equal(t, target, root1) + require.Equal(t, target, blk1.Root()) // Prune finalization s := f.store - s.finalizedCheckpoint.Root = root1 + s.finalizedCheckpoint.Root = blk1.Root() require.NoError(t, s.prune(ctx)) - target, err = f.TargetRootForEpoch(root1, 1) + target, err = f.TargetRootForEpoch(blk1.Root(), 1) require.NoError(t, err) require.Equal(t, [32]byte{}, target) // Insert a block for next epoch (slot 0 present) - state, root4, err := prepareForkchoiceState(ctx, 3*params.BeaconConfig().SlotsPerEpoch, [32]byte{'e'}, root1, params.BeaconConfig().ZeroHash, 1, 1) + state, blk4, err := prepareForkchoiceState(ctx, 3*params.BeaconConfig().SlotsPerEpoch, [32]byte{'e'}, blk1.Root(), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, root4)) - target, err = f.TargetRootForEpoch(root4, 3) + require.NoError(t, f.InsertNode(ctx, state, blk4)) + target, err = f.TargetRootForEpoch(blk4.Root(), 3) require.NoError(t, err) - require.Equal(t, target, root4) + require.Equal(t, target, blk4.Root()) - state, root5, err := prepareForkchoiceState(ctx, 3*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'f'}, root4, params.BeaconConfig().ZeroHash, 1, 1) + state, blk5, err := prepareForkchoiceState(ctx, 3*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'f'}, blk4.Root(), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, root5)) - target, err = f.TargetRootForEpoch(root5, 3) + require.NoError(t, f.InsertNode(ctx, state, blk5)) + target, err = f.TargetRootForEpoch(blk5.Root(), 3) require.NoError(t, err) - require.Equal(t, target, root4) + require.Equal(t, target, blk4.Root()) // Target root where the target epoch is same or ahead of the block slot - target, err = f.TargetRootForEpoch(root5, 4) + target, err = f.TargetRootForEpoch(blk5.Root(), 4) require.NoError(t, err) - require.Equal(t, target, root5) + require.Equal(t, target, blk5.Root()) // Target root where the target epoch is two epochs ago - target, err = f.TargetRootForEpoch(root5, 2) + target, err = f.TargetRootForEpoch(blk5.Root(), 2) require.NoError(t, err) - require.Equal(t, root1, target) // the parent of root4 in epoch 3 is root 1 in epoch 1 + require.Equal(t, blk1.Root(), target) // the parent of root4 in epoch 3 is root 1 in epoch 1 // Target root where the target is two epochs ago, slot 0 was missed - state, root6, err := prepareForkchoiceState(ctx, 4*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'g'}, root5, params.BeaconConfig().ZeroHash, 1, 1) + state, blk6, err := prepareForkchoiceState(ctx, 4*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'g'}, blk5.Root(), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) - require.NoError(t, f.InsertNode(ctx, state, root6)) - target, err = f.TargetRootForEpoch(root6, 4) + require.NoError(t, f.InsertNode(ctx, state, blk6)) + target, err = f.TargetRootForEpoch(blk6.Root(), 4) require.NoError(t, err) - require.Equal(t, target, root5) - target, err = f.TargetRootForEpoch(root6, 2) + require.Equal(t, target, blk5.Root()) + target, err = f.TargetRootForEpoch(blk6.Root(), 2) require.NoError(t, err) - require.Equal(t, target, root1) + require.Equal(t, target, blk1.Root()) // Prune finalization - s.finalizedCheckpoint.Root = root4 + s.finalizedCheckpoint.Root = blk4.Root() require.NoError(t, s.prune(ctx)) - target, err = f.TargetRootForEpoch(root4, 3) + target, err = f.TargetRootForEpoch(blk4.Root(), 3) require.NoError(t, err) - require.Equal(t, root4, target) + require.Equal(t, blk4.Root(), target) } func TestStore_CleanupInserting(t *testing.T) { f := setup(0, 0) ctx := context.Background() - st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) + st, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) - require.NotNil(t, f.InsertNode(ctx, st, blkRoot)) - require.Equal(t, false, f.HasNode(blkRoot)) + require.NotNil(t, f.InsertNode(ctx, st, blk)) + require.Equal(t, false, f.HasNode(blk.Root())) } diff --git a/beacon-chain/forkchoice/interfaces.go b/beacon-chain/forkchoice/interfaces.go index 9db82c6920a4..08a84d5ea425 100644 --- a/beacon-chain/forkchoice/interfaces.go +++ b/beacon-chain/forkchoice/interfaces.go @@ -6,6 +6,7 @@ import ( forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" forkchoice2 "github.com/prysmaticlabs/prysm/v5/consensus-types/forkchoice" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ) @@ -41,7 +42,7 @@ type HeadRetriever interface { // BlockProcessor processes the block that's used for accounting fork choice. type BlockProcessor interface { - InsertNode(context.Context, state.BeaconState, [32]byte) error + InsertNode(context.Context, state.BeaconState, consensus_blocks.ROBlock) error InsertChain(context.Context, []*forkchoicetypes.BlockAndCheckpoints) error } diff --git a/beacon-chain/forkchoice/types/BUILD.bazel b/beacon-chain/forkchoice/types/BUILD.bazel index b85cf2eed684..c6c258b94ed1 100644 --- a/beacon-chain/forkchoice/types/BUILD.bazel +++ b/beacon-chain/forkchoice/types/BUILD.bazel @@ -7,7 +7,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//config/fieldparams:go_default_library", - "//consensus-types/interfaces:go_default_library", + "//consensus-types/blocks:go_default_library", "//consensus-types/primitives:go_default_library", "//proto/prysm/v1alpha1:go_default_library", ], diff --git a/beacon-chain/forkchoice/types/types.go b/beacon-chain/forkchoice/types/types.go index 214b64b6959d..fb9bfbae3489 100644 --- a/beacon-chain/forkchoice/types/types.go +++ b/beacon-chain/forkchoice/types/types.go @@ -2,7 +2,7 @@ package types import ( fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" - "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" + consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) @@ -17,7 +17,7 @@ type Checkpoint struct { // BlockAndCheckpoints to call the InsertOptimisticChain function type BlockAndCheckpoints struct { - Block interfaces.ReadOnlyBeaconBlock + Block consensus_blocks.ROBlock JustifiedCheckpoint *ethpb.Checkpoint FinalizedCheckpoint *ethpb.Checkpoint } diff --git a/beacon-chain/rpc/eth/beacon/handlers_test.go b/beacon-chain/rpc/eth/beacon/handlers_test.go index b0d61ba3020c..76eeefe5a5ab 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_test.go @@ -2939,13 +2939,15 @@ func TestValidateEquivocation(t *testing.T) { st, err := util.NewBeaconState() require.NoError(t, err) require.NoError(t, st.SetSlot(10)) + blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock()) + require.NoError(t, err) + roblock, err := blocks.NewROBlockWithRoot(blk, bytesutil.ToBytes32([]byte("root"))) + require.NoError(t, err) fc := doublylinkedtree.New() - require.NoError(t, fc.InsertNode(context.Background(), st, bytesutil.ToBytes32([]byte("root")))) + require.NoError(t, fc.InsertNode(context.Background(), st, roblock)) server := &Server{ ForkchoiceFetcher: &chainMock.ChainService{ForkChoiceStore: fc}, } - blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock()) - require.NoError(t, err) blk.SetSlot(st.Slot() + 1) require.NoError(t, server.validateEquivocation(blk.Block())) @@ -2954,15 +2956,17 @@ func TestValidateEquivocation(t *testing.T) { st, err := util.NewBeaconState() require.NoError(t, err) require.NoError(t, st.SetSlot(10)) - fc := doublylinkedtree.New() - require.NoError(t, fc.InsertNode(context.Background(), st, bytesutil.ToBytes32([]byte("root")))) - server := &Server{ - ForkchoiceFetcher: &chainMock.ChainService{ForkChoiceStore: fc}, - } blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock()) require.NoError(t, err) blk.SetSlot(st.Slot()) + roblock, err := blocks.NewROBlockWithRoot(blk, bytesutil.ToBytes32([]byte("root"))) + require.NoError(t, err) + fc := doublylinkedtree.New() + require.NoError(t, fc.InsertNode(context.Background(), st, roblock)) + server := &Server{ + ForkchoiceFetcher: &chainMock.ChainService{ForkChoiceStore: fc}, + } err = server.validateEquivocation(blk.Block()) assert.ErrorContains(t, "already exists", err) require.ErrorIs(t, err, errEquivocatedBlock) diff --git a/beacon-chain/rpc/eth/helpers/sync_test.go b/beacon-chain/rpc/eth/helpers/sync_test.go index a760d1a0aaab..2823a80cbc91 100644 --- a/beacon-chain/rpc/eth/helpers/sync_test.go +++ b/beacon-chain/rpc/eth/helpers/sync_test.go @@ -329,7 +329,7 @@ func prepareForkchoiceState( payloadHash [32]byte, justified *eth.Checkpoint, finalized *eth.Checkpoint, -) (state.BeaconState, [32]byte, error) { +) (state.BeaconState, blocks.ROBlock, error) { blockHeader := ð.BeaconBlockHeader{ ParentRoot: parentRoot[:], } @@ -350,5 +350,24 @@ func prepareForkchoiceState( base.BlockRoots[0] = append(base.BlockRoots[0], blockRoot[:]...) st, err := state_native.InitializeFromProtoBellatrix(base) - return st, blockRoot, err + if err != nil { + return nil, blocks.ROBlock{}, err + } + blk := ð.SignedBeaconBlockBellatrix{ + Block: ð.BeaconBlockBellatrix{ + Slot: slot, + ParentRoot: parentRoot[:], + Body: ð.BeaconBlockBodyBellatrix{ + ExecutionPayload: &enginev1.ExecutionPayload{ + BlockHash: payloadHash[:], + }, + }, + }, + } + signed, err := blocks.NewSignedBeaconBlock(blk) + if err != nil { + return nil, blocks.ROBlock{}, err + } + roblock, err := blocks.NewROBlockWithRoot(signed, blockRoot) + return st, roblock, err } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_builder_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_builder_test.go index 70785d91c633..81dee365cda6 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_builder_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_builder_test.go @@ -14,8 +14,10 @@ import ( state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" + enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/require" @@ -149,7 +151,7 @@ func createState( payloadHash [32]byte, justified *ethpb.Checkpoint, finalized *ethpb.Checkpoint, -) (state.BeaconState, [32]byte, error) { +) (state.BeaconState, blocks.ROBlock, error) { base := ðpb.BeaconStateBellatrix{ Slot: slot, @@ -167,5 +169,24 @@ func createState( base.BlockRoots[0] = append(base.BlockRoots[0], blockRoot[:]...) st, err := state_native.InitializeFromProtoBellatrix(base) - return st, blockRoot, err + if err != nil { + return nil, blocks.ROBlock{}, err + } + blk := ðpb.SignedBeaconBlockBellatrix{ + Block: ðpb.BeaconBlockBellatrix{ + Slot: slot, + ParentRoot: parentRoot[:], + Body: ðpb.BeaconBlockBodyBellatrix{ + ExecutionPayload: &enginev1.ExecutionPayload{ + BlockHash: payloadHash[:], + }, + }, + }, + } + signed, err := blocks.NewSignedBeaconBlock(blk) + if err != nil { + return nil, blocks.ROBlock{}, err + } + roblock, err := blocks.NewROBlockWithRoot(signed, blockRoot) + return st, roblock, err } From 52cf3a155dcce1316505761cc876b76b7b38a830 Mon Sep 17 00:00:00 2001 From: kasey <489222+kasey@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:16:17 -0500 Subject: [PATCH 24/26] Safe StreamEvents write loop (#14557) * new type for tests where errors are only logged * StreamHandler waits for write loop exit * add test case for writer timeout * add changelog * add missing file * logging fix * fix logging test to allow info logs * naming/comments; make response controller private * simplify cancel defers * fix typo in test file name --------- Co-authored-by: Kasey Kirkham --- CHANGELOG.md | 1 + beacon-chain/rpc/eth/events/BUILD.bazel | 2 + beacon-chain/rpc/eth/events/events.go | 148 +++++++++++---- beacon-chain/rpc/eth/events/events_test.go | 202 +++++++++++++++------ beacon-chain/rpc/eth/events/http_test.go | 48 ++++- beacon-chain/rpc/eth/events/log.go | 6 + beacon-chain/rpc/eth/events/server.go | 1 + testing/util/BUILD.bazel | 5 + testing/util/logging.go | 90 +++++++++ testing/util/logging_test.go | 78 ++++++++ 10 files changed, 486 insertions(+), 95 deletions(-) create mode 100644 beacon-chain/rpc/eth/events/log.go create mode 100644 testing/util/logging.go create mode 100644 testing/util/logging_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 48e15ab28b0a..58f96e2ece12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Fixed mesh size by appending `gParams.Dhi = gossipSubDhi` - Fix skipping partial withdrawals count. +- wait for the async StreamEvent writer to exit before leaving the http handler, avoiding race condition panics [pr](https://github.com/prysmaticlabs/prysm/pull/14557) ### Security diff --git a/beacon-chain/rpc/eth/events/BUILD.bazel b/beacon-chain/rpc/eth/events/BUILD.bazel index caa311b6b515..ad6b09eb30bd 100644 --- a/beacon-chain/rpc/eth/events/BUILD.bazel +++ b/beacon-chain/rpc/eth/events/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "events.go", + "log.go", "server.go", ], importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/events", @@ -58,5 +59,6 @@ go_test( "//testing/util:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_r3labs_sse_v2//:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", ], ) diff --git a/beacon-chain/rpc/eth/events/events.go b/beacon-chain/rpc/eth/events/events.go index 7698d2fb9017..92071fda9d2a 100644 --- a/beacon-chain/rpc/eth/events/events.go +++ b/beacon-chain/rpc/eth/events/events.go @@ -28,7 +28,6 @@ import ( eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" - log "github.com/sirupsen/logrus" ) const DefaultEventFeedDepth = 1000 @@ -74,13 +73,6 @@ var ( errWriterUnusable = errors.New("http response writer is unusable") ) -// StreamingResponseWriter defines a type that can be used by the eventStreamer. -// This must be an http.ResponseWriter that supports flushing and hijacking. -type StreamingResponseWriter interface { - http.ResponseWriter - http.Flusher -} - // The eventStreamer uses lazyReaders to defer serialization until the moment the value is ready to be written to the client. type lazyReader func() io.Reader @@ -150,6 +142,7 @@ func newTopicRequest(topics []string) (*topicRequest, error) { // Servers may send SSE comments beginning with ':' for any purpose, // including to keep the event stream connection alive in the presence of proxy servers. func (s *Server) StreamEvents(w http.ResponseWriter, r *http.Request) { + log.Debug("Starting StreamEvents handler") ctx, span := trace.StartSpan(r.Context(), "events.StreamEvents") defer span.End() @@ -159,47 +152,51 @@ func (s *Server) StreamEvents(w http.ResponseWriter, r *http.Request) { return } - sw, ok := w.(StreamingResponseWriter) - if !ok { - msg := "beacon node misconfiguration: http stack may not support required response handling features, like flushing" - httputil.HandleError(w, msg, http.StatusInternalServerError) - return + timeout := s.EventWriteTimeout + if timeout == 0 { + timeout = time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second } - depth := s.EventFeedDepth - if depth == 0 { - depth = DefaultEventFeedDepth + ka := s.KeepAliveInterval + if ka == 0 { + ka = timeout } - es, err := newEventStreamer(depth, s.KeepAliveInterval) - if err != nil { - httputil.HandleError(w, err.Error(), http.StatusInternalServerError) - return + buffSize := s.EventFeedDepth + if buffSize == 0 { + buffSize = DefaultEventFeedDepth } + api.SetSSEHeaders(w) + sw := newStreamingResponseController(w, timeout) ctx, cancel := context.WithCancel(ctx) defer cancel() - api.SetSSEHeaders(sw) + es := newEventStreamer(buffSize, ka) + go es.outboxWriteLoop(ctx, cancel, sw) if err := es.recvEventLoop(ctx, cancel, topics, s); err != nil { log.WithError(err).Debug("Shutting down StreamEvents handler.") } + cleanupStart := time.Now() + es.waitForExit() + log.WithField("cleanup_wait", time.Since(cleanupStart)).Debug("streamEvents shutdown complete") } -func newEventStreamer(buffSize int, ka time.Duration) (*eventStreamer, error) { - if ka == 0 { - ka = time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second - } +func newEventStreamer(buffSize int, ka time.Duration) *eventStreamer { return &eventStreamer{ - outbox: make(chan lazyReader, buffSize), - keepAlive: ka, - }, nil + outbox: make(chan lazyReader, buffSize), + keepAlive: ka, + openUntilExit: make(chan struct{}), + } } type eventStreamer struct { - outbox chan lazyReader - keepAlive time.Duration + outbox chan lazyReader + keepAlive time.Duration + openUntilExit chan struct{} } func (es *eventStreamer) recvEventLoop(ctx context.Context, cancel context.CancelFunc, req *topicRequest, s *Server) error { + defer close(es.outbox) + defer cancel() eventsChan := make(chan *feed.Event, len(es.outbox)) if req.needOpsFeed { opsSub := s.OperationNotifier.OperationFeed().Subscribe(eventsChan) @@ -228,7 +225,6 @@ func (es *eventStreamer) recvEventLoop(ctx context.Context, cancel context.Cance // channel should stay relatively empty, which gives this loop time to unsubscribe // and cleanup before the event stream channel fills and disrupts other readers. if err := es.safeWrite(ctx, lr); err != nil { - cancel() // note: we could hijack the connection and close it here. Does that cause issues? What are the benefits? // A benefit of hijack and close is that it may force an error on the remote end, however just closing the context of the // http handler may be sufficient to cause the remote http response reader to close. @@ -265,12 +261,13 @@ func newlineReader() io.Reader { // outboxWriteLoop runs in a separate goroutine. Its job is to write the values in the outbox to // the client as fast as the client can read them. -func (es *eventStreamer) outboxWriteLoop(ctx context.Context, cancel context.CancelFunc, w StreamingResponseWriter) { +func (es *eventStreamer) outboxWriteLoop(ctx context.Context, cancel context.CancelFunc, w *streamingResponseWriterController) { var err error defer func() { if err != nil { log.WithError(err).Debug("Event streamer shutting down due to error.") } + es.exit() }() defer cancel() // Write a keepalive at the start to test the connection and simplify test setup. @@ -310,18 +307,43 @@ func (es *eventStreamer) outboxWriteLoop(ctx context.Context, cancel context.Can } } -func writeLazyReaderWithRecover(w StreamingResponseWriter, lr lazyReader) (err error) { +func (es *eventStreamer) exit() { + drained := 0 + for range es.outbox { + drained += 1 + } + log.WithField("undelivered_events", drained).Debug("Event stream outbox drained.") + close(es.openUntilExit) +} + +// waitForExit blocks until the outboxWriteLoop has exited. +// While this function blocks, it is not yet safe to exit the http handler, +// because the outboxWriteLoop may still be writing to the http ResponseWriter. +func (es *eventStreamer) waitForExit() { + <-es.openUntilExit +} + +func writeLazyReaderWithRecover(w *streamingResponseWriterController, lr lazyReader) (err error) { defer func() { if r := recover(); r != nil { log.WithField("panic", r).Error("Recovered from panic while writing event to client.") err = errWriterUnusable } }() - _, err = io.Copy(w, lr()) + r := lr() + out, err := io.ReadAll(r) + if err != nil { + return err + } + _, err = w.Write(out) return err } -func (es *eventStreamer) writeOutbox(ctx context.Context, w StreamingResponseWriter, first lazyReader) error { +func (es *eventStreamer) writeOutbox(ctx context.Context, w *streamingResponseWriterController, first lazyReader) error { + // The outboxWriteLoop is responsible for managing the keep-alive timer and toggling between reading from the outbox + // when it is ready, only allowing the keep-alive to fire when there hasn't been a write in the keep-alive interval. + // Since outboxWriteLoop will get either the first event or the keep-alive, we let it pass in the first event to write, + // either the event's lazyReader, or nil for a keep-alive. needKeepAlive := true if first != nil { if err := writeLazyReaderWithRecover(w, first); err != nil { @@ -337,6 +359,11 @@ func (es *eventStreamer) writeOutbox(ctx context.Context, w StreamingResponseWri case <-ctx.Done(): return ctx.Err() case rf := <-es.outbox: + // We don't want to call Flush until we've exhausted all the writes - it's always preferrable to + // just keep draining the outbox and rely on the underlying Write code to flush+block when it + // needs to based on buffering. Whenever we fill the buffer with a string of writes, the underlying + // code will flush on its own, so it's better to explicitly flush only once, after we've totally + // drained the outbox, to catch any dangling bytes stuck in a buffer. if err := writeLazyReaderWithRecover(w, rf); err != nil { return err } @@ -347,8 +374,7 @@ func (es *eventStreamer) writeOutbox(ctx context.Context, w StreamingResponseWri return err } } - w.Flush() - return nil + return w.Flush() } } } @@ -638,3 +664,51 @@ func (s *Server) currentPayloadAttributes(ctx context.Context) (lazyReader, erro }) }, nil } + +func newStreamingResponseController(rw http.ResponseWriter, timeout time.Duration) *streamingResponseWriterController { + rc := http.NewResponseController(rw) + return &streamingResponseWriterController{ + timeout: timeout, + rw: rw, + rc: rc, + } +} + +// streamingResponseWriterController provides an interface similar to an http.ResponseWriter, +// wrapping an http.ResponseWriter and an http.ResponseController, using the ResponseController +// to set and clear deadlines for Write and Flush methods, and delegating to the underlying +// types to Write and Flush. +type streamingResponseWriterController struct { + timeout time.Duration + rw http.ResponseWriter + rc *http.ResponseController +} + +func (c *streamingResponseWriterController) Write(b []byte) (int, error) { + if err := c.setDeadline(); err != nil { + return 0, err + } + out, err := c.rw.Write(b) + if err != nil { + return out, err + } + return out, c.clearDeadline() +} + +func (c *streamingResponseWriterController) setDeadline() error { + return c.rc.SetWriteDeadline(time.Now().Add(c.timeout)) +} + +func (c *streamingResponseWriterController) clearDeadline() error { + return c.rc.SetWriteDeadline(time.Time{}) +} + +func (c *streamingResponseWriterController) Flush() error { + if err := c.setDeadline(); err != nil { + return err + } + if err := c.rc.Flush(); err != nil { + return err + } + return c.clearDeadline() +} diff --git a/beacon-chain/rpc/eth/events/events_test.go b/beacon-chain/rpc/eth/events/events_test.go index 420f34fdf96d..32daf1c7218f 100644 --- a/beacon-chain/rpc/eth/events/events_test.go +++ b/beacon-chain/rpc/eth/events/events_test.go @@ -27,9 +27,12 @@ import ( "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" sse "github.com/r3labs/sse/v2" + "github.com/sirupsen/logrus" ) -func requireAllEventsReceived(t *testing.T, stn, opn *mockChain.EventFeedWrapper, events []*feed.Event, req *topicRequest, s *Server, w *StreamingResponseWriterRecorder) { +var testEventWriteTimeout = 100 * time.Millisecond + +func requireAllEventsReceived(t *testing.T, stn, opn *mockChain.EventFeedWrapper, events []*feed.Event, req *topicRequest, s *Server, w *StreamingResponseWriterRecorder, logs chan *logrus.Entry) { // maxBufferSize param copied from sse lib client code sseR := sse.NewEventStreamReader(w.Body(), 1<<24) ctx, cancel := context.WithTimeout(context.Background(), time.Second) @@ -77,21 +80,29 @@ func requireAllEventsReceived(t *testing.T, stn, opn *mockChain.EventFeedWrapper } } }() - select { - case <-done: - break - case <-ctx.Done(): - t.Fatalf("context canceled / timed out waiting for events, err=%v", ctx.Err()) + for { + select { + case entry := <-logs: + errAttr, ok := entry.Data[logrus.ErrorKey] + if ok { + t.Errorf("unexpected error in logs: %v", errAttr) + } + case <-done: + require.Equal(t, 0, len(expected), "expected events not seen") + return + case <-ctx.Done(): + t.Fatalf("context canceled / timed out waiting for events, err=%v", ctx.Err()) + } } - require.Equal(t, 0, len(expected), "expected events not seen") } -func (tr *topicRequest) testHttpRequest(_ *testing.T) *http.Request { +func (tr *topicRequest) testHttpRequest(ctx context.Context, _ *testing.T) *http.Request { tq := make([]string, 0, len(tr.topics)) for topic := range tr.topics { tq = append(tq, "topics="+topic) } - return httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com/eth/v1/events?%s", strings.Join(tq, "&")), nil) + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com/eth/v1/events?%s", strings.Join(tq, "&")), nil) + return req.WithContext(ctx) } func operationEventsFixtures(t *testing.T) (*topicRequest, []*feed.Event) { @@ -235,31 +246,77 @@ func operationEventsFixtures(t *testing.T) (*topicRequest, []*feed.Event) { } } +type streamTestSync struct { + done chan struct{} + cancel func() + undo func() + logs chan *logrus.Entry + ctx context.Context + t *testing.T +} + +func (s *streamTestSync) cleanup() { + s.cancel() + select { + case <-s.done: + case <-time.After(10 * time.Millisecond): + s.t.Fatal("timed out waiting for handler to finish") + } + s.undo() +} + +func (s *streamTestSync) markDone() { + close(s.done) +} + +func newStreamTestSync(t *testing.T) *streamTestSync { + logChan := make(chan *logrus.Entry, 100) + cew := util.NewChannelEntryWriter(logChan) + undo := util.RegisterHookWithUndo(logger, cew) + ctx, cancel := context.WithCancel(context.Background()) + return &streamTestSync{ + t: t, + ctx: ctx, + cancel: cancel, + logs: logChan, + undo: undo, + done: make(chan struct{}), + } +} + func TestStreamEvents_OperationsEvents(t *testing.T) { t.Run("operations", func(t *testing.T) { + testSync := newStreamTestSync(t) + defer testSync.cleanup() stn := mockChain.NewEventFeedWrapper() opn := mockChain.NewEventFeedWrapper() s := &Server{ StateNotifier: &mockChain.SimpleNotifier{Feed: stn}, OperationNotifier: &mockChain.SimpleNotifier{Feed: opn}, + EventWriteTimeout: testEventWriteTimeout, } topics, events := operationEventsFixtures(t) - request := topics.testHttpRequest(t) - w := NewStreamingResponseWriterRecorder() + request := topics.testHttpRequest(testSync.ctx, t) + w := NewStreamingResponseWriterRecorder(testSync.ctx) go func() { s.StreamEvents(w, request) + testSync.markDone() }() - requireAllEventsReceived(t, stn, opn, events, topics, s, w) + requireAllEventsReceived(t, stn, opn, events, topics, s, w, testSync.logs) }) t.Run("state", func(t *testing.T) { + testSync := newStreamTestSync(t) + defer testSync.cleanup() + stn := mockChain.NewEventFeedWrapper() opn := mockChain.NewEventFeedWrapper() s := &Server{ StateNotifier: &mockChain.SimpleNotifier{Feed: stn}, OperationNotifier: &mockChain.SimpleNotifier{Feed: opn}, + EventWriteTimeout: testEventWriteTimeout, } topics, err := newTopicRequest([]string{ @@ -269,8 +326,8 @@ func TestStreamEvents_OperationsEvents(t *testing.T) { BlockTopic, }) require.NoError(t, err) - request := topics.testHttpRequest(t) - w := NewStreamingResponseWriterRecorder() + request := topics.testHttpRequest(testSync.ctx, t) + w := NewStreamingResponseWriterRecorder(testSync.ctx) b, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlock(ð.SignedBeaconBlock{})) require.NoError(t, err) @@ -323,9 +380,10 @@ func TestStreamEvents_OperationsEvents(t *testing.T) { go func() { s.StreamEvents(w, request) + testSync.markDone() }() - requireAllEventsReceived(t, stn, opn, events, topics, s, w) + requireAllEventsReceived(t, stn, opn, events, topics, s, w, testSync.logs) }) t.Run("payload attributes", func(t *testing.T) { type testCase struct { @@ -396,59 +454,93 @@ func TestStreamEvents_OperationsEvents(t *testing.T) { }, } for _, tc := range testCases { - st := tc.getState() - v := ð.Validator{ExitEpoch: math.MaxUint64} - require.NoError(t, st.SetValidators([]*eth.Validator{v})) - currentSlot := primitives.Slot(0) - // to avoid slot processing - require.NoError(t, st.SetSlot(currentSlot+1)) - b := tc.getBlock() - mockChainService := &mockChain.ChainService{ - Root: make([]byte, 32), - State: st, - Block: b, - Slot: ¤tSlot, - } + t.Run(tc.name, func(t *testing.T) { + testSync := newStreamTestSync(t) + defer testSync.cleanup() - stn := mockChain.NewEventFeedWrapper() - opn := mockChain.NewEventFeedWrapper() - s := &Server{ - StateNotifier: &mockChain.SimpleNotifier{Feed: stn}, - OperationNotifier: &mockChain.SimpleNotifier{Feed: opn}, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainService, - TrackedValidatorsCache: cache.NewTrackedValidatorsCache(), - } - if tc.SetTrackedValidatorsCache != nil { - tc.SetTrackedValidatorsCache(s.TrackedValidatorsCache) - } - topics, err := newTopicRequest([]string{PayloadAttributesTopic}) - require.NoError(t, err) - request := topics.testHttpRequest(t) - w := NewStreamingResponseWriterRecorder() - events := []*feed.Event{&feed.Event{Type: statefeed.MissedSlot}} - - go func() { - s.StreamEvents(w, request) - }() - requireAllEventsReceived(t, stn, opn, events, topics, s, w) + st := tc.getState() + v := ð.Validator{ExitEpoch: math.MaxUint64} + require.NoError(t, st.SetValidators([]*eth.Validator{v})) + currentSlot := primitives.Slot(0) + // to avoid slot processing + require.NoError(t, st.SetSlot(currentSlot+1)) + b := tc.getBlock() + mockChainService := &mockChain.ChainService{ + Root: make([]byte, 32), + State: st, + Block: b, + Slot: ¤tSlot, + } + + stn := mockChain.NewEventFeedWrapper() + opn := mockChain.NewEventFeedWrapper() + s := &Server{ + StateNotifier: &mockChain.SimpleNotifier{Feed: stn}, + OperationNotifier: &mockChain.SimpleNotifier{Feed: opn}, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainService, + TrackedValidatorsCache: cache.NewTrackedValidatorsCache(), + EventWriteTimeout: testEventWriteTimeout, + } + if tc.SetTrackedValidatorsCache != nil { + tc.SetTrackedValidatorsCache(s.TrackedValidatorsCache) + } + topics, err := newTopicRequest([]string{PayloadAttributesTopic}) + require.NoError(t, err) + request := topics.testHttpRequest(testSync.ctx, t) + w := NewStreamingResponseWriterRecorder(testSync.ctx) + events := []*feed.Event{&feed.Event{Type: statefeed.MissedSlot}} + + go func() { + s.StreamEvents(w, request) + testSync.markDone() + }() + requireAllEventsReceived(t, stn, opn, events, topics, s, w, testSync.logs) + }) } }) } -func TestStuckReader(t *testing.T) { +func TestStuckReaderScenarios(t *testing.T) { + cases := []struct { + name string + queueDepth func([]*feed.Event) int + }{ + { + name: "slow reader - queue overflows", + queueDepth: func(events []*feed.Event) int { + return len(events) - 1 + }, + }, + { + name: "slow reader - all queued, but writer is stuck, write timeout", + queueDepth: func(events []*feed.Event) int { + return len(events) + 1 + }, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + wedgedWriterTestCase(t, c.queueDepth) + }) + } +} + +func wedgedWriterTestCase(t *testing.T, queueDepth func([]*feed.Event) int) { topics, events := operationEventsFixtures(t) require.Equal(t, 8, len(events)) + // set eventFeedDepth to a number lower than the events we intend to send to force the server to drop the reader. stn := mockChain.NewEventFeedWrapper() opn := mockChain.NewEventFeedWrapper() s := &Server{ + EventWriteTimeout: 10 * time.Millisecond, StateNotifier: &mockChain.SimpleNotifier{Feed: stn}, OperationNotifier: &mockChain.SimpleNotifier{Feed: opn}, - EventFeedDepth: len(events) - 1, + EventFeedDepth: queueDepth(events), } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() eventsWritten := make(chan struct{}) go func() { @@ -468,8 +560,8 @@ func TestStuckReader(t *testing.T) { close(eventsWritten) }() - request := topics.testHttpRequest(t) - w := NewStreamingResponseWriterRecorder() + request := topics.testHttpRequest(ctx, t) + w := NewStreamingResponseWriterRecorder(ctx) handlerFinished := make(chan struct{}) go func() { diff --git a/beacon-chain/rpc/eth/events/http_test.go b/beacon-chain/rpc/eth/events/http_test.go index 1bfaaa873da3..afff92518a32 100644 --- a/beacon-chain/rpc/eth/events/http_test.go +++ b/beacon-chain/rpc/eth/events/http_test.go @@ -1,10 +1,12 @@ package events import ( + "context" "io" "net/http" "net/http/httptest" "testing" + "time" "github.com/prysmaticlabs/prysm/v5/testing/require" ) @@ -17,32 +19,66 @@ type StreamingResponseWriterRecorder struct { status chan int bodyRecording []byte flushed bool + writeDeadline time.Time + ctx context.Context } func (w *StreamingResponseWriterRecorder) StatusChan() chan int { return w.status } -func NewStreamingResponseWriterRecorder() *StreamingResponseWriterRecorder { +func NewStreamingResponseWriterRecorder(ctx context.Context) *StreamingResponseWriterRecorder { r, w := io.Pipe() return &StreamingResponseWriterRecorder{ ResponseWriter: httptest.NewRecorder(), r: r, w: w, status: make(chan int, 1), + ctx: ctx, } } // Write implements http.ResponseWriter. func (w *StreamingResponseWriterRecorder) Write(data []byte) (int, error) { w.WriteHeader(http.StatusOK) - n, err := w.w.Write(data) + written, err := writeWithDeadline(w.ctx, w.w, data, w.writeDeadline) if err != nil { - return n, err + return written, err } + // The test response writer is non-blocking. return w.ResponseWriter.Write(data) } +var zeroTimeValue = time.Time{} + +func writeWithDeadline(ctx context.Context, w io.Writer, data []byte, deadline time.Time) (int, error) { + result := struct { + written int + err error + }{} + done := make(chan struct{}) + go func() { + defer close(done) + result.written, result.err = w.Write(data) + }() + if deadline == zeroTimeValue { + select { + case <-ctx.Done(): + return 0, ctx.Err() + case <-done: + return result.written, result.err + } + } + select { + case <-time.After(time.Until(deadline)): + return 0, http.ErrHandlerTimeout + case <-done: + return result.written, result.err + case <-ctx.Done(): + return 0, ctx.Err() + } +} + // WriteHeader implements http.ResponseWriter. func (w *StreamingResponseWriterRecorder) WriteHeader(statusCode int) { if w.statusWritten != nil { @@ -65,6 +101,7 @@ func (w *StreamingResponseWriterRecorder) RequireStatus(t *testing.T, status int } func (w *StreamingResponseWriterRecorder) Flush() { + w.WriteHeader(200) fw, ok := w.ResponseWriter.(http.Flusher) if ok { fw.Flush() @@ -72,4 +109,9 @@ func (w *StreamingResponseWriterRecorder) Flush() { w.flushed = true } +func (w *StreamingResponseWriterRecorder) SetWriteDeadline(d time.Time) error { + w.writeDeadline = d + return nil +} + var _ http.ResponseWriter = &StreamingResponseWriterRecorder{} diff --git a/beacon-chain/rpc/eth/events/log.go b/beacon-chain/rpc/eth/events/log.go new file mode 100644 index 000000000000..6d218a4f034a --- /dev/null +++ b/beacon-chain/rpc/eth/events/log.go @@ -0,0 +1,6 @@ +package events + +import "github.com/sirupsen/logrus" + +var logger = logrus.StandardLogger() +var log = logger.WithField("prefix", "events") diff --git a/beacon-chain/rpc/eth/events/server.go b/beacon-chain/rpc/eth/events/server.go index 26e83454e5c4..6b4e4b787f07 100644 --- a/beacon-chain/rpc/eth/events/server.go +++ b/beacon-chain/rpc/eth/events/server.go @@ -22,4 +22,5 @@ type Server struct { TrackedValidatorsCache *cache.TrackedValidatorsCache KeepAliveInterval time.Duration EventFeedDepth int + EventWriteTimeout time.Duration } diff --git a/testing/util/BUILD.bazel b/testing/util/BUILD.bazel index fb194bfc1093..16154398cf7e 100644 --- a/testing/util/BUILD.bazel +++ b/testing/util/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "electra_state.go", "helpers.go", "lightclient.go", + "logging.go", "merge.go", "state.go", "sync_aggregate.go", @@ -69,6 +70,7 @@ go_library( "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", + "@com_github_sirupsen_logrus//hooks/test:go_default_library", "@io_bazel_rules_go//go/tools/bazel:go_default_library", ], ) @@ -83,6 +85,7 @@ go_test( "deneb_test.go", "deposits_test.go", "helpers_test.go", + "logging_test.go", "state_test.go", ], embed = [":go_default_library"], @@ -106,6 +109,8 @@ go_test( "//testing/assert:go_default_library", "//testing/require:go_default_library", "//time/slots:go_default_library", + "@com_github_pkg_errors//:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", ], ) diff --git a/testing/util/logging.go b/testing/util/logging.go new file mode 100644 index 000000000000..a4da28453d0b --- /dev/null +++ b/testing/util/logging.go @@ -0,0 +1,90 @@ +package util + +import ( + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" +) + +// ComparableHook is an interface that allows hooks to be uniquely identified +// so that tests can safely unregister them as part of cleanup. +type ComparableHook interface { + logrus.Hook + Equal(other logrus.Hook) bool +} + +// UnregisterHook removes a hook that implements the HookIdentifier interface +// from all levels of the given logger. +func UnregisterHook(logger *logrus.Logger, unregister ComparableHook) { + found := false + replace := make(logrus.LevelHooks) + for lvl, hooks := range logger.Hooks { + for _, h := range hooks { + if unregister.Equal(h) { + found = true + continue + } + replace[lvl] = append(replace[lvl], h) + } + } + if !found { + return + } + logger.ReplaceHooks(replace) +} + +var highestLevel logrus.Level + +// RegisterHookWithUndo adds a hook to the logger and +// returns a function that can be called to remove it. This is intended to be used in tests +// to ensure that test hooks are removed after the test is complete. +func RegisterHookWithUndo(logger *logrus.Logger, hook ComparableHook) func() { + level := logger.Level + logger.AddHook(hook) + // set level to highest possible to ensure that hook is called for all log levels + logger.SetLevel(highestLevel) + return func() { + UnregisterHook(logger, hook) + logger.SetLevel(level) + } +} + +// NewChannelEntryWriter creates a new ChannelEntryWriter. +// The channel argument will be sent all log entries. +// Note that if this is an unbuffered channel, it is the responsibility +// of the code using it to make sure that it is drained appropriately, +// or calls to the logger can block. +func NewChannelEntryWriter(c chan *logrus.Entry) *ChannelEntryWriter { + return &ChannelEntryWriter{c: c} +} + +// ChannelEntryWriter embeds/wraps the test.Hook struct +// and adds a channel to receive log entries every time the +// Fire method of the Hook interface is called. +type ChannelEntryWriter struct { + test.Hook + c chan *logrus.Entry +} + +// Fire delegates to the embedded test.Hook Fire method after +// sending the log entry to the channel. +func (c *ChannelEntryWriter) Fire(e *logrus.Entry) error { + if c.c != nil { + c.c <- e + } + return c.Hook.Fire(e) +} + +func (c *ChannelEntryWriter) Equal(other logrus.Hook) bool { + return c == other +} + +var _ logrus.Hook = &ChannelEntryWriter{} +var _ ComparableHook = &ChannelEntryWriter{} + +func init() { + for _, level := range logrus.AllLevels { + if level > highestLevel { + highestLevel = level + } + } +} diff --git a/testing/util/logging_test.go b/testing/util/logging_test.go new file mode 100644 index 000000000000..d596abbfc466 --- /dev/null +++ b/testing/util/logging_test.go @@ -0,0 +1,78 @@ +package util + +import ( + "testing" + "time" + + "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v5/testing/require" + "github.com/sirupsen/logrus" +) + +func TestUnregister(t *testing.T) { + logger := logrus.New() + logger.SetLevel(logrus.PanicLevel) // set to lowest log level to test level override in + assertNoHooks(t, logger) + c := make(chan *logrus.Entry, 1) + tl := NewChannelEntryWriter(c) + undo := RegisterHookWithUndo(logger, tl) + assertRegistered(t, logger, tl) + logger.Trace("test") + select { + case <-c: + default: + t.Fatalf("Expected log entry, got none") + } + undo() + assertNoHooks(t, logger) + require.Equal(t, logrus.PanicLevel, logger.Level) +} + +var logTestErr = errors.New("test") + +func TestChannelEntryWriter(t *testing.T) { + logger := logrus.New() + c := make(chan *logrus.Entry) + tl := NewChannelEntryWriter(c) + logger.AddHook(tl) + msg := "test" + go func() { + logger.WithError(logTestErr).Info(msg) + }() + select { + case e := <-c: + gotErr := e.Data[logrus.ErrorKey] + if gotErr == nil { + t.Fatalf("Expected error in log entry, got nil") + } + ge, ok := gotErr.(error) + require.Equal(t, true, ok, "Expected error in log entry to be of type error, got %T", gotErr) + require.ErrorIs(t, ge, logTestErr) + require.Equal(t, msg, e.Message) + require.Equal(t, logrus.InfoLevel, e.Level) + case <-time.After(10 * time.Millisecond): + t.Fatalf("Timed out waiting for log entry") + } +} + +func assertNoHooks(t *testing.T, logger *logrus.Logger) { + for lvl, hooks := range logger.Hooks { + for _, hook := range hooks { + t.Fatalf("Expected no hooks, got %v at level %s", hook, lvl.String()) + } + } +} + +func assertRegistered(t *testing.T, logger *logrus.Logger, hook ComparableHook) { + for _, lvl := range hook.Levels() { + registered := logger.Hooks[lvl] + found := false + for _, h := range registered { + if hook.Equal(h) { + found = true + break + } + } + require.Equal(t, true, found, "Expected hook %v to be registered at level %s, but it was not", hook, lvl.String()) + } +} From 7ac522d8ff96740c33fe41abcb8301281df1ce2d Mon Sep 17 00:00:00 2001 From: terence Date: Thu, 24 Oct 2024 14:30:14 -0700 Subject: [PATCH 25/26] Use engine api `get-blobs` for block subscriber (#14513) * Use engine api get-blobs for block subscriber Debug changelog add proto marshal and unmarshal Kasey's feedback * Feedback * Preston's feedback * Exist argument should not be hardcoded with kzg count --- CHANGELOG.md | 1 + beacon-chain/execution/BUILD.bazel | 4 + beacon-chain/execution/engine_client.go | 128 +++++++++++++++++- beacon-chain/execution/engine_client_test.go | 113 +++++++++++++++- beacon-chain/execution/options.go | 9 ++ beacon-chain/execution/rpc_connection.go | 7 + beacon-chain/execution/service.go | 49 +++++++ beacon-chain/execution/service_test.go | 9 ++ .../execution/testing/mock_engine_client.go | 7 + beacon-chain/node/node.go | 101 +++++++------- beacon-chain/rpc/endpoints.go | 48 +++---- beacon-chain/rpc/eth/beacon/handlers.go | 2 +- beacon-chain/rpc/eth/beacon/server.go | 48 +++---- beacon-chain/rpc/service.go | 98 +++++++------- beacon-chain/sync/metrics.go | 14 ++ beacon-chain/sync/options.go | 4 +- .../sync/rpc_beacon_blocks_by_range.go | 2 +- .../sync/rpc_beacon_blocks_by_range_test.go | 10 +- .../sync/rpc_beacon_blocks_by_root.go | 2 +- .../sync/rpc_beacon_blocks_by_root_test.go | 10 +- beacon-chain/sync/service.go | 38 +++--- beacon-chain/sync/subscriber_beacon_blocks.go | 76 +++++++++++ .../sync/subscriber_beacon_blocks_test.go | 72 +++++++++- beacon-chain/sync/subscriber_blob_sidecar.go | 4 + beacon-chain/verification/batch_test.go | 16 +-- beacon-chain/verification/blob.go | 4 + beacon-chain/verification/mock.go | 4 +- proto/engine/v1/execution_engine.pb.go | 100 ++++++++++++-- proto/engine/v1/execution_engine.proto | 4 + proto/engine/v1/json_marshal_unmarshal.go | 23 ++++ proto/prysm/v1alpha1/light_client.pb.go | 5 +- 31 files changed, 799 insertions(+), 213 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58f96e2ece12..3cefcd7c4835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ Updating to this release is recommended at your convenience. - fastssz version bump (better error messages). - SSE implementation that sheds stuck clients. [pr](https://github.com/prysmaticlabs/prysm/pull/14413) - Added GetPoolAttesterSlashingsV2 endpoint. +- Use engine API get-blobs for block subscriber to reduce block import latency and potentially reduce bandwidth. ### Changed diff --git a/beacon-chain/execution/BUILD.bazel b/beacon-chain/execution/BUILD.bazel index 828ca2ea7fd5..b33fb4d0d8a8 100644 --- a/beacon-chain/execution/BUILD.bazel +++ b/beacon-chain/execution/BUILD.bazel @@ -37,6 +37,7 @@ go_library( "//beacon-chain/state:go_default_library", "//beacon-chain/state/state-native:go_default_library", "//beacon-chain/state/stategen:go_default_library", + "//beacon-chain/verification:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", @@ -105,8 +106,11 @@ go_test( "//beacon-chain/db/testing:go_default_library", "//beacon-chain/execution/testing:go_default_library", "//beacon-chain/execution/types:go_default_library", + "//beacon-chain/forkchoice:go_default_library", "//beacon-chain/forkchoice/doubly-linked-tree:go_default_library", + "//beacon-chain/startup:go_default_library", "//beacon-chain/state/stategen:go_default_library", + "//beacon-chain/verification:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", diff --git a/beacon-chain/execution/engine_client.go b/beacon-chain/execution/engine_client.go index 2dfc479f4214..2776b04eb767 100644 --- a/beacon-chain/execution/engine_client.go +++ b/beacon-chain/execution/engine_client.go @@ -14,6 +14,7 @@ import ( "github.com/holiman/uint256" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/types" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/verification" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" @@ -23,6 +24,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" pb "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" + ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" "github.com/sirupsen/logrus" @@ -79,6 +81,8 @@ const ( GetPayloadBodiesByRangeV1 = "engine_getPayloadBodiesByRangeV1" // ExchangeCapabilities request string for JSON-RPC. ExchangeCapabilities = "engine_exchangeCapabilities" + // GetBlobsV1 request string for JSON-RPC. + GetBlobsV1 = "engine_getBlobsV1" // Defines the seconds before timing out engine endpoints with non-block execution semantics. defaultEngineTimeout = time.Second ) @@ -93,16 +97,15 @@ type ForkchoiceUpdatedResponse struct { ValidationError string `json:"validationError"` } -// PayloadReconstructor defines a service that can reconstruct a full beacon -// block with an execution payload from a signed beacon block and a connection -// to an execution client's engine API. -type PayloadReconstructor interface { +// Reconstructor defines a service responsible for reconstructing full beacon chain objects by utilizing the execution API and making requests through the execution client. +type Reconstructor interface { ReconstructFullBlock( ctx context.Context, blindedBlock interfaces.ReadOnlySignedBeaconBlock, ) (interfaces.SignedBeaconBlock, error) ReconstructFullBellatrixBlockBatch( ctx context.Context, blindedBlocks []interfaces.ReadOnlySignedBeaconBlock, ) ([]interfaces.SignedBeaconBlock, error) + ReconstructBlobSidecars(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, indices []bool) ([]blocks.VerifiedROBlob, error) } // EngineCaller defines a client that can interact with an Ethereum @@ -494,6 +497,20 @@ func (s *Service) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H return hdr, err } +// GetBlobs returns the blob and proof from the execution engine for the given versioned hashes. +func (s *Service) GetBlobs(ctx context.Context, versionedHashes []common.Hash) ([]*pb.BlobAndProof, error) { + ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetBlobs") + defer span.End() + // If the execution engine does not support `GetBlobsV1`, return early to prevent encountering an error later. + if !s.capabilityCache.has(GetBlobsV1) { + return nil, nil + } + + result := make([]*pb.BlobAndProof, len(versionedHashes)) + err := s.rpcClient.CallContext(ctx, &result, GetBlobsV1, versionedHashes) + return result, handleRPCError(err) +} + // ReconstructFullBlock takes in a blinded beacon block and reconstructs // a beacon block with a full execution payload via the engine API. func (s *Service) ReconstructFullBlock( @@ -522,6 +539,109 @@ func (s *Service) ReconstructFullBellatrixBlockBatch( return unb, nil } +// ReconstructBlobSidecars reconstructs the verified blob sidecars for a given beacon block. +// It retrieves the KZG commitments from the block body, fetches the associated blobs and proofs, +// and constructs the corresponding verified read-only blob sidecars. +// +// The 'exists' argument is a boolean list (must be the same length as body.BlobKzgCommitments), where each element corresponds to whether a +// particular blob sidecar already exists. If exists[i] is true, the blob for the i-th KZG commitment +// has already been retrieved and does not need to be fetched again from the execution layer (EL). +// +// For example: +// - len(block.Body().BlobKzgCommitments()) == 6 +// - If exists = [true, false, true, false, true, false], the function will fetch the blobs +// associated with indices 1, 3, and 5 (since those are marked as non-existent). +// - If exists = [false ... x 6], the function will attempt to fetch all blobs. +// +// Only the blobs that do not already exist (where exists[i] is false) are fetched using the KZG commitments from block body. +func (s *Service) ReconstructBlobSidecars(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, exists []bool) ([]blocks.VerifiedROBlob, error) { + blockBody := block.Block().Body() + kzgCommitments, err := blockBody.BlobKzgCommitments() + if err != nil { + return nil, errors.Wrap(err, "could not get blob KZG commitments") + } + if len(kzgCommitments) != len(exists) { + return nil, fmt.Errorf("mismatched lengths: KZG commitments %d, exists %d", len(kzgCommitments), len(exists)) + } + + // Collect KZG hashes for non-existing blobs + var kzgHashes []common.Hash + for i, commitment := range kzgCommitments { + if !exists[i] { + kzgHashes = append(kzgHashes, primitives.ConvertKzgCommitmentToVersionedHash(commitment)) + } + } + if len(kzgHashes) == 0 { + return nil, nil + } + + // Fetch blobs from EL + blobs, err := s.GetBlobs(ctx, kzgHashes) + if err != nil { + return nil, errors.Wrap(err, "could not get blobs") + } + if len(blobs) == 0 { + return nil, nil + } + + header, err := block.Header() + if err != nil { + return nil, errors.Wrap(err, "could not get header") + } + + // Reconstruct verified blob sidecars + var verifiedBlobs []blocks.VerifiedROBlob + for i, blobIndex := 0, 0; i < len(kzgCommitments); i++ { + if exists[i] { + continue + } + + if blobIndex >= len(blobs) || blobs[blobIndex] == nil { + blobIndex++ + continue + } + blob := blobs[blobIndex] + blobIndex++ + + proof, err := blocks.MerkleProofKZGCommitment(blockBody, i) + if err != nil { + log.WithError(err).WithField("index", i).Error("failed to get Merkle proof for KZG commitment") + continue + } + sidecar := ðpb.BlobSidecar{ + Index: uint64(i), + Blob: blob.Blob, + KzgCommitment: kzgCommitments[i], + KzgProof: blob.KzgProof, + SignedBlockHeader: header, + CommitmentInclusionProof: proof, + } + + roBlob, err := blocks.NewROBlobWithRoot(sidecar, blockRoot) + if err != nil { + log.WithError(err).WithField("index", i).Error("failed to create RO blob with root") + continue + } + + // Verify the sidecar KZG proof + v := s.blobVerifier(roBlob, verification.ELMemPoolRequirements) + if err := v.SidecarKzgProofVerified(); err != nil { + log.WithError(err).WithField("index", i).Error("failed to verify KZG proof for sidecar") + continue + } + + verifiedBlob, err := v.VerifiedROBlob() + if err != nil { + log.WithError(err).WithField("index", i).Error("failed to verify RO blob") + continue + } + + verifiedBlobs = append(verifiedBlobs, verifiedBlob) + } + + return verifiedBlobs, nil +} + func fullPayloadFromPayloadBody( header interfaces.ExecutionData, body *pb.ExecutionPayloadBody, bVersion int, ) (interfaces.ExecutionData, error) { diff --git a/beacon-chain/execution/engine_client_test.go b/beacon-chain/execution/engine_client_test.go index 0e2ecb48bd2c..31db063ab59e 100644 --- a/beacon-chain/execution/engine_client_test.go +++ b/beacon-chain/execution/engine_client_test.go @@ -2,6 +2,7 @@ package execution import ( "context" + "crypto/rand" "encoding/json" "fmt" "io" @@ -20,6 +21,7 @@ import ( "github.com/holiman/uint256" "github.com/pkg/errors" mocks "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/verification" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" @@ -37,9 +39,9 @@ import ( ) var ( - _ = PayloadReconstructor(&Service{}) + _ = Reconstructor(&Service{}) _ = EngineCaller(&Service{}) - _ = PayloadReconstructor(&Service{}) + _ = Reconstructor(&Service{}) _ = EngineCaller(&mocks.EngineClient{}) ) @@ -2390,3 +2392,110 @@ func Test_ExchangeCapabilities(t *testing.T) { } }) } + +func TestReconstructBlobSidecars(t *testing.T) { + client := &Service{capabilityCache: &capabilityCache{}} + b := util.NewBeaconBlockDeneb() + kzgCommitments := createRandomKzgCommitments(t, 6) + + b.Block.Body.BlobKzgCommitments = kzgCommitments + r, err := b.Block.HashTreeRoot() + require.NoError(t, err) + sb, err := blocks.NewSignedBeaconBlock(b) + require.NoError(t, err) + + ctx := context.Background() + t.Run("all seen", func(t *testing.T) { + exists := []bool{true, true, true, true, true, true} + verifiedBlobs, err := client.ReconstructBlobSidecars(ctx, sb, r, exists) + require.NoError(t, err) + require.Equal(t, 0, len(verifiedBlobs)) + }) + + t.Run("get-blobs end point is not supported", func(t *testing.T) { + exists := []bool{true, true, true, true, true, false} + verifiedBlobs, err := client.ReconstructBlobSidecars(ctx, sb, r, exists) + require.NoError(t, err) + require.Equal(t, 0, len(verifiedBlobs)) + }) + + client.capabilityCache = &capabilityCache{capabilities: map[string]interface{}{GetBlobsV1: nil}} + + t.Run("recovered 6 missing blobs", func(t *testing.T) { + srv := createBlobServer(t, 6) + defer srv.Close() + + rpcClient, client := setupRpcClient(t, srv.URL, client) + defer rpcClient.Close() + + exists := [6]bool{} + verifiedBlobs, err := client.ReconstructBlobSidecars(ctx, sb, r, exists[:]) + require.NoError(t, err) + require.Equal(t, 6, len(verifiedBlobs)) + }) + + t.Run("recovered 3 missing blobs", func(t *testing.T) { + srv := createBlobServer(t, 3) + defer srv.Close() + + rpcClient, client := setupRpcClient(t, srv.URL, client) + defer rpcClient.Close() + + exists := []bool{true, false, true, false, true, false} + verifiedBlobs, err := client.ReconstructBlobSidecars(ctx, sb, r, exists) + require.NoError(t, err) + require.Equal(t, 3, len(verifiedBlobs)) + }) +} + +func createRandomKzgCommitments(t *testing.T, num int) [][]byte { + kzgCommitments := make([][]byte, num) + for i := range kzgCommitments { + kzgCommitments[i] = make([]byte, 48) + _, err := rand.Read(kzgCommitments[i]) + require.NoError(t, err) + } + return kzgCommitments +} + +func createBlobServer(t *testing.T, numBlobs int) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + defer func() { + require.NoError(t, r.Body.Close()) + }() + + blobs := make([]pb.BlobAndProofJson, numBlobs) + for i := range blobs { + blobs[i] = pb.BlobAndProofJson{Blob: []byte(fmt.Sprintf("blob%d", i+1)), KzgProof: []byte(fmt.Sprintf("proof%d", i+1))} + } + + respJSON := map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": blobs, + } + require.NoError(t, json.NewEncoder(w).Encode(respJSON)) + })) +} + +func setupRpcClient(t *testing.T, url string, client *Service) (*rpc.Client, *Service) { + rpcClient, err := rpc.DialHTTP(url) + require.NoError(t, err) + + client.rpcClient = rpcClient + client.capabilityCache = &capabilityCache{capabilities: map[string]interface{}{GetBlobsV1: nil}} + client.blobVerifier = testNewBlobVerifier() + + return rpcClient, client +} + +func testNewBlobVerifier() verification.NewBlobVerifier { + return func(b blocks.ROBlob, reqs []verification.Requirement) verification.BlobVerifier { + return &verification.MockBlobVerifier{ + CbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { + return blocks.VerifiedROBlob{}, nil + }, + } + } +} diff --git a/beacon-chain/execution/options.go b/beacon-chain/execution/options.go index edc616bcc533..028b7f0c1c38 100644 --- a/beacon-chain/execution/options.go +++ b/beacon-chain/execution/options.go @@ -7,6 +7,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/db" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/verification" "github.com/prysmaticlabs/prysm/v5/network" "github.com/prysmaticlabs/prysm/v5/network/authorization" ) @@ -115,3 +116,11 @@ func WithJwtId(jwtId string) Option { return nil } } + +// WithVerifierWaiter gives the sync package direct access to the verifier waiter. +func WithVerifierWaiter(v *verification.InitializerWaiter) Option { + return func(s *Service) error { + s.verifierWaiter = v + return nil + } +} diff --git a/beacon-chain/execution/rpc_connection.go b/beacon-chain/execution/rpc_connection.go index 04e63ab2a0ba..4730cf3d6a07 100644 --- a/beacon-chain/execution/rpc_connection.go +++ b/beacon-chain/execution/rpc_connection.go @@ -78,6 +78,13 @@ func (s *Service) pollConnectionStatus(ctx context.Context) { currClient.Close() } log.WithField("endpoint", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url)).Info("Connected to new endpoint") + + c, err := s.ExchangeCapabilities(ctx) + if err != nil { + errorLogger(err, "Could not exchange capabilities with execution client") + } + s.capabilityCache.save(c) + return case <-s.ctx.Done(): log.Debug("Received cancelled context,closing existing powchain service") diff --git a/beacon-chain/execution/service.go b/beacon-chain/execution/service.go index d71b0b949407..41dbfdb5dcd2 100644 --- a/beacon-chain/execution/service.go +++ b/beacon-chain/execution/service.go @@ -29,7 +29,9 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/verification" "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/container/trie" contracts "github.com/prysmaticlabs/prysm/v5/contracts/deposit" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" @@ -155,6 +157,9 @@ type Service struct { lastReceivedMerkleIndex int64 // Keeps track of the last received index to prevent log spam. runError error preGenesisState state.BeaconState + verifierWaiter *verification.InitializerWaiter + blobVerifier verification.NewBlobVerifier + capabilityCache *capabilityCache } // NewService sets up a new instance with an ethclient when given a web3 endpoint as a string in the config. @@ -192,6 +197,7 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) { lastReceivedMerkleIndex: -1, preGenesisState: genState, eth1HeadTicker: time.NewTicker(time.Duration(params.BeaconConfig().SecondsPerETH1Block) * time.Second), + capabilityCache: &capabilityCache{}, } for _, opt := range opts { @@ -229,6 +235,13 @@ func (s *Service) Start() { } } + v, err := s.verifierWaiter.WaitForInitializer(s.ctx) + if err != nil { + log.WithError(err).Error("Could not get verification initializer") + return + } + s.blobVerifier = newBlobVerifierFromInitializer(v) + s.isRunning = true // Poll the execution client connection and fallback if errors occur. @@ -886,3 +899,39 @@ func (s *Service) migrateOldDepositTree(eth1DataInDB *ethpb.ETH1ChainData) error func (s *Service) removeStartupState() { s.cfg.finalizedStateAtStartup = nil } + +func newBlobVerifierFromInitializer(ini *verification.Initializer) verification.NewBlobVerifier { + return func(b blocks.ROBlob, reqs []verification.Requirement) verification.BlobVerifier { + return ini.NewBlobVerifier(b, reqs) + } +} + +type capabilityCache struct { + capabilities map[string]interface{} + capabilitiesLock sync.RWMutex +} + +func (c *capabilityCache) save(cs []string) { + c.capabilitiesLock.Lock() + defer c.capabilitiesLock.Unlock() + + if c.capabilities == nil { + c.capabilities = make(map[string]interface{}) + } + + for _, capability := range cs { + c.capabilities[capability] = struct{}{} + } +} + +func (c *capabilityCache) has(capability string) bool { + c.capabilitiesLock.RLock() + defer c.capabilitiesLock.RUnlock() + + if c.capabilities == nil { + return false + } + + _, ok := c.capabilities[capability] + return ok +} diff --git a/beacon-chain/execution/service_test.go b/beacon-chain/execution/service_test.go index f32cdc54440c..e6b6e2ea075e 100644 --- a/beacon-chain/execution/service_test.go +++ b/beacon-chain/execution/service_test.go @@ -19,8 +19,11 @@ import ( dbutil "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/types" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice" doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/startup" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/verification" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/container/trie" contracts "github.com/prysmaticlabs/prysm/v5/contracts/deposit" @@ -92,10 +95,16 @@ func TestStart_OK(t *testing.T) { t.Cleanup(func() { server.Stop() }) + c := startup.NewClockSynchronizer() + require.NoError(t, c.SetClock(startup.NewClock(time.Unix(0, 0), [32]byte{}))) + waiter := verification.NewInitializerWaiter( + c, forkchoice.NewROForkChoice(nil), nil) + web3Service, err := NewService(context.Background(), WithHttpEndpoint(endpoint), WithDepositContractAddress(testAcc.ContractAddr), WithDatabase(beaconDB), + WithVerifierWaiter(waiter), ) require.NoError(t, err, "unable to setup execution service") web3Service = setDefaultMocks(web3Service) diff --git a/beacon-chain/execution/testing/mock_engine_client.go b/beacon-chain/execution/testing/mock_engine_client.go index 397384fb6d25..5bb5a7e6f785 100644 --- a/beacon-chain/execution/testing/mock_engine_client.go +++ b/beacon-chain/execution/testing/mock_engine_client.go @@ -36,6 +36,8 @@ type EngineClient struct { OverrideValidHash [32]byte GetPayloadResponse *blocks.GetPayloadResponse ErrGetPayload error + BlobSidecars []blocks.VerifiedROBlob + ErrorBlobSidecars error } // NewPayload -- @@ -106,6 +108,11 @@ func (e *EngineClient) ReconstructFullBellatrixBlockBatch( return fullBlocks, nil } +// ReconstructBlobSidecars is a mock implementation of the ReconstructBlobSidecars method. +func (e *EngineClient) ReconstructBlobSidecars(context.Context, interfaces.ReadOnlySignedBeaconBlock, [32]byte, []bool) ([]blocks.VerifiedROBlob, error) { + return e.BlobSidecars, e.ErrorBlobSidecars +} + // GetTerminalBlockHash -- func (e *EngineClient) GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error) { ttd := new(big.Int) diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index b5c735ba8957..fce4a4e56afd 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -796,6 +796,7 @@ func (b *BeaconNode) registerPOWChainService() error { execution.WithBeaconNodeStatsUpdater(bs), execution.WithFinalizedStateAtStartup(b.finalizedStateAtStartUp), execution.WithJwtId(b.cliCtx.String(flags.JwtId.Name)), + execution.WithVerifierWaiter(b.verifyInitWaiter), ) web3Service, err := execution.NewService(b.ctx, opts...) if err != nil { @@ -838,7 +839,7 @@ func (b *BeaconNode) registerSyncService(initialSyncComplete chan struct{}, bFil regularsync.WithStateGen(b.stateGen), regularsync.WithSlasherAttestationsFeed(b.slasherAttestationsFeed), regularsync.WithSlasherBlockHeadersFeed(b.slasherBlockHeadersFeed), - regularsync.WithPayloadReconstructor(web3Service), + regularsync.WithReconstructor(web3Service), regularsync.WithClockWaiter(b.clockWaiter), regularsync.WithInitialSyncComplete(initialSyncComplete), regularsync.WithStateNotifier(b), @@ -953,55 +954,55 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error { p2pService := b.fetchP2P() rpcService := rpc.NewService(b.ctx, &rpc.Config{ - ExecutionEngineCaller: web3Service, - ExecutionPayloadReconstructor: web3Service, - Host: host, - Port: port, - BeaconMonitoringHost: beaconMonitoringHost, - BeaconMonitoringPort: beaconMonitoringPort, - CertFlag: cert, - KeyFlag: key, - BeaconDB: b.db, - Broadcaster: p2pService, - PeersFetcher: p2pService, - PeerManager: p2pService, - MetadataProvider: p2pService, - ChainInfoFetcher: chainService, - HeadFetcher: chainService, - CanonicalFetcher: chainService, - ForkFetcher: chainService, - ForkchoiceFetcher: chainService, - FinalizationFetcher: chainService, - BlockReceiver: chainService, - BlobReceiver: chainService, - AttestationReceiver: chainService, - GenesisTimeFetcher: chainService, - GenesisFetcher: chainService, - OptimisticModeFetcher: chainService, - AttestationsPool: b.attestationPool, - ExitPool: b.exitPool, - SlashingsPool: b.slashingsPool, - BLSChangesPool: b.blsToExecPool, - SyncCommitteeObjectPool: b.syncCommitteePool, - ExecutionChainService: web3Service, - ExecutionChainInfoFetcher: web3Service, - ChainStartFetcher: chainStartFetcher, - MockEth1Votes: mockEth1DataVotes, - SyncService: syncService, - DepositFetcher: depositFetcher, - PendingDepositFetcher: b.depositCache, - BlockNotifier: b, - StateNotifier: b, - OperationNotifier: b, - StateGen: b.stateGen, - EnableDebugRPCEndpoints: enableDebugRPCEndpoints, - MaxMsgSize: maxMsgSize, - BlockBuilder: b.fetchBuilderService(), - Router: router, - ClockWaiter: b.clockWaiter, - BlobStorage: b.BlobStorage, - TrackedValidatorsCache: b.trackedValidatorsCache, - PayloadIDCache: b.payloadIDCache, + ExecutionEngineCaller: web3Service, + ExecutionReconstructor: web3Service, + Host: host, + Port: port, + BeaconMonitoringHost: beaconMonitoringHost, + BeaconMonitoringPort: beaconMonitoringPort, + CertFlag: cert, + KeyFlag: key, + BeaconDB: b.db, + Broadcaster: p2pService, + PeersFetcher: p2pService, + PeerManager: p2pService, + MetadataProvider: p2pService, + ChainInfoFetcher: chainService, + HeadFetcher: chainService, + CanonicalFetcher: chainService, + ForkFetcher: chainService, + ForkchoiceFetcher: chainService, + FinalizationFetcher: chainService, + BlockReceiver: chainService, + BlobReceiver: chainService, + AttestationReceiver: chainService, + GenesisTimeFetcher: chainService, + GenesisFetcher: chainService, + OptimisticModeFetcher: chainService, + AttestationsPool: b.attestationPool, + ExitPool: b.exitPool, + SlashingsPool: b.slashingsPool, + BLSChangesPool: b.blsToExecPool, + SyncCommitteeObjectPool: b.syncCommitteePool, + ExecutionChainService: web3Service, + ExecutionChainInfoFetcher: web3Service, + ChainStartFetcher: chainStartFetcher, + MockEth1Votes: mockEth1DataVotes, + SyncService: syncService, + DepositFetcher: depositFetcher, + PendingDepositFetcher: b.depositCache, + BlockNotifier: b, + StateNotifier: b, + OperationNotifier: b, + StateGen: b.stateGen, + EnableDebugRPCEndpoints: enableDebugRPCEndpoints, + MaxMsgSize: maxMsgSize, + BlockBuilder: b.fetchBuilderService(), + Router: router, + ClockWaiter: b.clockWaiter, + BlobStorage: b.BlobStorage, + TrackedValidatorsCache: b.trackedValidatorsCache, + PayloadIDCache: b.payloadIDCache, }) return b.services.RegisterService(rpcService) diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 2d61aee6173c..ecdb141ebc8b 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -464,30 +464,30 @@ func (s *Service) beaconEndpoints( coreService *core.Service, ) []endpoint { server := &beacon.Server{ - CanonicalHistory: ch, - BeaconDB: s.cfg.BeaconDB, - AttestationsPool: s.cfg.AttestationsPool, - SlashingsPool: s.cfg.SlashingsPool, - ChainInfoFetcher: s.cfg.ChainInfoFetcher, - GenesisTimeFetcher: s.cfg.GenesisTimeFetcher, - BlockNotifier: s.cfg.BlockNotifier, - OperationNotifier: s.cfg.OperationNotifier, - Broadcaster: s.cfg.Broadcaster, - BlockReceiver: s.cfg.BlockReceiver, - StateGenService: s.cfg.StateGen, - Stater: stater, - Blocker: blocker, - OptimisticModeFetcher: s.cfg.OptimisticModeFetcher, - HeadFetcher: s.cfg.HeadFetcher, - TimeFetcher: s.cfg.GenesisTimeFetcher, - VoluntaryExitsPool: s.cfg.ExitPool, - V1Alpha1ValidatorServer: validatorServer, - SyncChecker: s.cfg.SyncService, - ExecutionPayloadReconstructor: s.cfg.ExecutionPayloadReconstructor, - BLSChangesPool: s.cfg.BLSChangesPool, - FinalizationFetcher: s.cfg.FinalizationFetcher, - ForkchoiceFetcher: s.cfg.ForkchoiceFetcher, - CoreService: coreService, + CanonicalHistory: ch, + BeaconDB: s.cfg.BeaconDB, + AttestationsPool: s.cfg.AttestationsPool, + SlashingsPool: s.cfg.SlashingsPool, + ChainInfoFetcher: s.cfg.ChainInfoFetcher, + GenesisTimeFetcher: s.cfg.GenesisTimeFetcher, + BlockNotifier: s.cfg.BlockNotifier, + OperationNotifier: s.cfg.OperationNotifier, + Broadcaster: s.cfg.Broadcaster, + BlockReceiver: s.cfg.BlockReceiver, + StateGenService: s.cfg.StateGen, + Stater: stater, + Blocker: blocker, + OptimisticModeFetcher: s.cfg.OptimisticModeFetcher, + HeadFetcher: s.cfg.HeadFetcher, + TimeFetcher: s.cfg.GenesisTimeFetcher, + VoluntaryExitsPool: s.cfg.ExitPool, + V1Alpha1ValidatorServer: validatorServer, + SyncChecker: s.cfg.SyncService, + ExecutionReconstructor: s.cfg.ExecutionReconstructor, + BLSChangesPool: s.cfg.BLSChangesPool, + FinalizationFetcher: s.cfg.FinalizationFetcher, + ForkchoiceFetcher: s.cfg.ForkchoiceFetcher, + CoreService: coreService, } const namespace = "beacon" diff --git a/beacon-chain/rpc/eth/beacon/handlers.go b/beacon-chain/rpc/eth/beacon/handlers.go index 33eeb9dbae24..ec7d713011ea 100644 --- a/beacon-chain/rpc/eth/beacon/handlers.go +++ b/beacon-chain/rpc/eth/beacon/handlers.go @@ -65,7 +65,7 @@ func (s *Server) GetBlockV2(w http.ResponseWriter, r *http.Request) { // Deal with block unblinding. if blk.Version() >= version.Bellatrix && blk.IsBlinded() { - blk, err = s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk) + blk, err = s.ExecutionReconstructor.ReconstructFullBlock(ctx, blk) if err != nil { httputil.HandleError(w, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block").Error(), http.StatusBadRequest) return diff --git a/beacon-chain/rpc/eth/beacon/server.go b/beacon-chain/rpc/eth/beacon/server.go index e7eef22493f5..878f533f908d 100644 --- a/beacon-chain/rpc/eth/beacon/server.go +++ b/beacon-chain/rpc/eth/beacon/server.go @@ -24,28 +24,28 @@ import ( // Server defines a server implementation of the gRPC Beacon Chain service, // providing RPC endpoints to access data relevant to the Ethereum Beacon Chain. type Server struct { - BeaconDB db.ReadOnlyDatabase - ChainInfoFetcher blockchain.ChainInfoFetcher - GenesisTimeFetcher blockchain.TimeFetcher - BlockReceiver blockchain.BlockReceiver - BlockNotifier blockfeed.Notifier - OperationNotifier operation.Notifier - Broadcaster p2p.Broadcaster - AttestationsPool attestations.Pool - SlashingsPool slashings.PoolManager - VoluntaryExitsPool voluntaryexits.PoolManager - StateGenService stategen.StateManager - Stater lookup.Stater - Blocker lookup.Blocker - HeadFetcher blockchain.HeadFetcher - TimeFetcher blockchain.TimeFetcher - OptimisticModeFetcher blockchain.OptimisticModeFetcher - V1Alpha1ValidatorServer eth.BeaconNodeValidatorServer - SyncChecker sync.Checker - CanonicalHistory *stategen.CanonicalHistory - ExecutionPayloadReconstructor execution.PayloadReconstructor - FinalizationFetcher blockchain.FinalizationFetcher - BLSChangesPool blstoexec.PoolManager - ForkchoiceFetcher blockchain.ForkchoiceFetcher - CoreService *core.Service + BeaconDB db.ReadOnlyDatabase + ChainInfoFetcher blockchain.ChainInfoFetcher + GenesisTimeFetcher blockchain.TimeFetcher + BlockReceiver blockchain.BlockReceiver + BlockNotifier blockfeed.Notifier + OperationNotifier operation.Notifier + Broadcaster p2p.Broadcaster + AttestationsPool attestations.Pool + SlashingsPool slashings.PoolManager + VoluntaryExitsPool voluntaryexits.PoolManager + StateGenService stategen.StateManager + Stater lookup.Stater + Blocker lookup.Blocker + HeadFetcher blockchain.HeadFetcher + TimeFetcher blockchain.TimeFetcher + OptimisticModeFetcher blockchain.OptimisticModeFetcher + V1Alpha1ValidatorServer eth.BeaconNodeValidatorServer + SyncChecker sync.Checker + CanonicalHistory *stategen.CanonicalHistory + ExecutionReconstructor execution.Reconstructor + FinalizationFetcher blockchain.FinalizationFetcher + BLSChangesPool blstoexec.PoolManager + ForkchoiceFetcher blockchain.ForkchoiceFetcher + CoreService *core.Service } diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index c7d29594600c..4a56c0d4162e 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -91,55 +91,55 @@ type Service struct { // Config options for the beacon node RPC server. type Config struct { - ExecutionPayloadReconstructor execution.PayloadReconstructor - Host string - Port string - CertFlag string - KeyFlag string - BeaconMonitoringHost string - BeaconMonitoringPort int - BeaconDB db.HeadAccessDatabase - ChainInfoFetcher blockchain.ChainInfoFetcher - HeadFetcher blockchain.HeadFetcher - CanonicalFetcher blockchain.CanonicalFetcher - ForkFetcher blockchain.ForkFetcher - ForkchoiceFetcher blockchain.ForkchoiceFetcher - FinalizationFetcher blockchain.FinalizationFetcher - AttestationReceiver blockchain.AttestationReceiver - BlockReceiver blockchain.BlockReceiver - BlobReceiver blockchain.BlobReceiver - ExecutionChainService execution.Chain - ChainStartFetcher execution.ChainStartFetcher - ExecutionChainInfoFetcher execution.ChainInfoFetcher - GenesisTimeFetcher blockchain.TimeFetcher - GenesisFetcher blockchain.GenesisFetcher - MockEth1Votes bool - EnableDebugRPCEndpoints bool - AttestationsPool attestations.Pool - ExitPool voluntaryexits.PoolManager - SlashingsPool slashings.PoolManager - SyncCommitteeObjectPool synccommittee.Pool - BLSChangesPool blstoexec.PoolManager - SyncService chainSync.Checker - Broadcaster p2p.Broadcaster - PeersFetcher p2p.PeersProvider - PeerManager p2p.PeerManager - MetadataProvider p2p.MetadataProvider - DepositFetcher cache.DepositFetcher - PendingDepositFetcher depositsnapshot.PendingDepositsFetcher - StateNotifier statefeed.Notifier - BlockNotifier blockfeed.Notifier - OperationNotifier opfeed.Notifier - StateGen *stategen.State - MaxMsgSize int - ExecutionEngineCaller execution.EngineCaller - OptimisticModeFetcher blockchain.OptimisticModeFetcher - BlockBuilder builder.BlockBuilder - Router *http.ServeMux - ClockWaiter startup.ClockWaiter - BlobStorage *filesystem.BlobStorage - TrackedValidatorsCache *cache.TrackedValidatorsCache - PayloadIDCache *cache.PayloadIDCache + ExecutionReconstructor execution.Reconstructor + Host string + Port string + CertFlag string + KeyFlag string + BeaconMonitoringHost string + BeaconMonitoringPort int + BeaconDB db.HeadAccessDatabase + ChainInfoFetcher blockchain.ChainInfoFetcher + HeadFetcher blockchain.HeadFetcher + CanonicalFetcher blockchain.CanonicalFetcher + ForkFetcher blockchain.ForkFetcher + ForkchoiceFetcher blockchain.ForkchoiceFetcher + FinalizationFetcher blockchain.FinalizationFetcher + AttestationReceiver blockchain.AttestationReceiver + BlockReceiver blockchain.BlockReceiver + BlobReceiver blockchain.BlobReceiver + ExecutionChainService execution.Chain + ChainStartFetcher execution.ChainStartFetcher + ExecutionChainInfoFetcher execution.ChainInfoFetcher + GenesisTimeFetcher blockchain.TimeFetcher + GenesisFetcher blockchain.GenesisFetcher + MockEth1Votes bool + EnableDebugRPCEndpoints bool + AttestationsPool attestations.Pool + ExitPool voluntaryexits.PoolManager + SlashingsPool slashings.PoolManager + SyncCommitteeObjectPool synccommittee.Pool + BLSChangesPool blstoexec.PoolManager + SyncService chainSync.Checker + Broadcaster p2p.Broadcaster + PeersFetcher p2p.PeersProvider + PeerManager p2p.PeerManager + MetadataProvider p2p.MetadataProvider + DepositFetcher cache.DepositFetcher + PendingDepositFetcher depositsnapshot.PendingDepositsFetcher + StateNotifier statefeed.Notifier + BlockNotifier blockfeed.Notifier + OperationNotifier opfeed.Notifier + StateGen *stategen.State + MaxMsgSize int + ExecutionEngineCaller execution.EngineCaller + OptimisticModeFetcher blockchain.OptimisticModeFetcher + BlockBuilder builder.BlockBuilder + Router *http.ServeMux + ClockWaiter startup.ClockWaiter + BlobStorage *filesystem.BlobStorage + TrackedValidatorsCache *cache.TrackedValidatorsCache + PayloadIDCache *cache.PayloadIDCache } // NewService instantiates a new RPC service instance that will diff --git a/beacon-chain/sync/metrics.go b/beacon-chain/sync/metrics.go index 07502117a458..af7a73d1a5c8 100644 --- a/beacon-chain/sync/metrics.go +++ b/beacon-chain/sync/metrics.go @@ -170,6 +170,20 @@ var ( Help: "The number of blob sidecars that were dropped due to missing parent block", }, ) + + blobRecoveredFromELTotal = promauto.NewCounter( + prometheus.CounterOpts{ + Name: "blob_recovered_from_el_total", + Help: "Count the number of times blobs have been recovered from the execution layer.", + }, + ) + + blobExistedInDBTotal = promauto.NewCounter( + prometheus.CounterOpts{ + Name: "blob_existed_in_db_total", + Help: "Count the number of times blobs have been found in the database.", + }, + ) ) func (s *Service) updateMetrics() { diff --git a/beacon-chain/sync/options.go b/beacon-chain/sync/options.go index 9b0281ea667f..ff20b8b81212 100644 --- a/beacon-chain/sync/options.go +++ b/beacon-chain/sync/options.go @@ -127,9 +127,9 @@ func WithSlasherBlockHeadersFeed(slasherBlockHeadersFeed *event.Feed) Option { } } -func WithPayloadReconstructor(r execution.PayloadReconstructor) Option { +func WithReconstructor(r execution.Reconstructor) Option { return func(s *Service) error { - s.cfg.executionPayloadReconstructor = r + s.cfg.executionReconstructor = r return nil } } diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_range.go b/beacon-chain/sync/rpc_beacon_blocks_by_range.go index 116187899fa2..865195f9806c 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_range.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_range.go @@ -160,7 +160,7 @@ func (s *Service) writeBlockBatchToStream(ctx context.Context, batch blockBatch, return nil } - reconstructed, err := s.cfg.executionPayloadReconstructor.ReconstructFullBellatrixBlockBatch(ctx, blinded) + reconstructed, err := s.cfg.executionReconstructor.ReconstructFullBellatrixBlockBatch(ctx, blinded) if err != nil { log.WithError(err).Error("Could not reconstruct full bellatrix block batch from blinded bodies") return err diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go b/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go index 832940cedaa5..0178425a2c28 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go @@ -239,11 +239,11 @@ func TestRPCBeaconBlocksByRange_ReconstructsPayloads(t *testing.T) { // Start service with 160 as allowed blocks capacity (and almost zero capacity recovery). r := &Service{ cfg: &config{ - p2p: p1, - beaconDB: d, - chain: &chainMock.ChainService{}, - clock: clock, - executionPayloadReconstructor: mockEngine, + p2p: p1, + beaconDB: d, + chain: &chainMock.ChainService{}, + clock: clock, + executionReconstructor: mockEngine, }, rateLimiter: newRateLimiter(p1), availableBlocker: mockBlocker{avail: true}, diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_root.go b/beacon-chain/sync/rpc_beacon_blocks_by_root.go index ad1ffe83d292..4379bbf60057 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_root.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_root.go @@ -112,7 +112,7 @@ func (s *Service) beaconBlocksRootRPCHandler(ctx context.Context, msg interface{ } if blk.Block().IsBlinded() { - blk, err = s.cfg.executionPayloadReconstructor.ReconstructFullBlock(ctx, blk) + blk, err = s.cfg.executionReconstructor.ReconstructFullBlock(ctx, blk) if err != nil { if errors.Is(err, execution.ErrEmptyBlockHash) { log.WithError(err).Warn("Could not reconstruct block from header with syncing execution client. Waiting to complete syncing") diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go b/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go index d95306297025..4d6be0c9691d 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go @@ -151,11 +151,11 @@ func TestRecentBeaconBlocksRPCHandler_ReturnsBlocks_ReconstructsPayload(t *testi }, } r := &Service{cfg: &config{ - p2p: p1, - beaconDB: d, - executionPayloadReconstructor: mockEngine, - chain: &mock.ChainService{ValidatorsRoot: [32]byte{}}, - clock: startup.NewClock(time.Unix(0, 0), [32]byte{}), + p2p: p1, + beaconDB: d, + executionReconstructor: mockEngine, + chain: &mock.ChainService{ValidatorsRoot: [32]byte{}}, + clock: startup.NewClock(time.Unix(0, 0), [32]byte{}), }, rateLimiter: newRateLimiter(p1)} pcl := protocol.ID(p2p.RPCBlocksByRootTopicV1) topic := string(pcl) diff --git a/beacon-chain/sync/service.go b/beacon-chain/sync/service.go index 15196bf6ca74..473d3d9709ff 100644 --- a/beacon-chain/sync/service.go +++ b/beacon-chain/sync/service.go @@ -77,25 +77,25 @@ type validationFn func(ctx context.Context) (pubsub.ValidationResult, error) // config to hold dependencies for the sync service. type config struct { - attestationNotifier operation.Notifier - p2p p2p.P2P - beaconDB db.NoHeadAccessDatabase - attPool attestations.Pool - exitPool voluntaryexits.PoolManager - slashingPool slashings.PoolManager - syncCommsPool synccommittee.Pool - blsToExecPool blstoexec.PoolManager - chain blockchainService - initialSync Checker - blockNotifier blockfeed.Notifier - operationNotifier operation.Notifier - executionPayloadReconstructor execution.PayloadReconstructor - stateGen *stategen.State - slasherAttestationsFeed *event.Feed - slasherBlockHeadersFeed *event.Feed - clock *startup.Clock - stateNotifier statefeed.Notifier - blobStorage *filesystem.BlobStorage + attestationNotifier operation.Notifier + p2p p2p.P2P + beaconDB db.NoHeadAccessDatabase + attPool attestations.Pool + exitPool voluntaryexits.PoolManager + slashingPool slashings.PoolManager + syncCommsPool synccommittee.Pool + blsToExecPool blstoexec.PoolManager + chain blockchainService + initialSync Checker + blockNotifier blockfeed.Notifier + operationNotifier operation.Notifier + executionReconstructor execution.Reconstructor + stateGen *stategen.State + slasherAttestationsFeed *event.Feed + slasherBlockHeadersFeed *event.Feed + clock *startup.Clock + stateNotifier statefeed.Notifier + blobStorage *filesystem.BlobStorage } // This defines the interface for interacting with block chain service diff --git a/beacon-chain/sync/subscriber_beacon_blocks.go b/beacon-chain/sync/subscriber_beacon_blocks.go index ff5f4c16457a..45e8608449cb 100644 --- a/beacon-chain/sync/subscriber_beacon_blocks.go +++ b/beacon-chain/sync/subscriber_beacon_blocks.go @@ -12,6 +12,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/io/file" + "github.com/prysmaticlabs/prysm/v5/time/slots" "google.golang.org/protobuf/proto" ) @@ -33,6 +34,8 @@ func (s *Service) beaconBlockSubscriber(ctx context.Context, msg proto.Message) return err } + go s.reconstructAndBroadcastBlobs(ctx, signed) + if err := s.cfg.chain.ReceiveBlock(ctx, signed, root, nil); err != nil { if blockchain.IsInvalidBlock(err) { r := blockchain.InvalidBlockRoot(err) @@ -55,6 +58,79 @@ func (s *Service) beaconBlockSubscriber(ctx context.Context, msg proto.Message) return err } +// reconstructAndBroadcastBlobs processes and broadcasts blob sidecars for a given beacon block. +// This function reconstructs the blob sidecars from the EL using the block's KZG commitments, +// broadcasts the reconstructed blobs over P2P, and saves them into the blob storage. +func (s *Service) reconstructAndBroadcastBlobs(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) { + startTime, err := slots.ToTime(uint64(s.cfg.chain.GenesisTime().Unix()), block.Block().Slot()) + if err != nil { + log.WithError(err).Error("Failed to convert slot to time") + } + + blockRoot, err := block.Block().HashTreeRoot() + if err != nil { + log.WithError(err).Error("Failed to calculate block root") + return + } + + if s.cfg.blobStorage == nil { + return + } + indices, err := s.cfg.blobStorage.Indices(blockRoot) + if err != nil { + log.WithError(err).Error("Failed to retrieve indices for block") + return + } + for _, index := range indices { + if index { + blobExistedInDBTotal.Inc() + } + } + + // Reconstruct blob sidecars from the EL + blobSidecars, err := s.cfg.executionReconstructor.ReconstructBlobSidecars(ctx, block, blockRoot, indices[:]) + if err != nil { + log.WithError(err).Error("Failed to reconstruct blob sidecars") + return + } + if len(blobSidecars) == 0 { + return + } + + // Refresh indices as new blobs may have been added to the db + indices, err = s.cfg.blobStorage.Indices(blockRoot) + if err != nil { + log.WithError(err).Error("Failed to retrieve indices for block") + return + } + + // Broadcast blob sidecars first than save them to the db + for _, sidecar := range blobSidecars { + if sidecar.Index >= uint64(len(indices)) || indices[sidecar.Index] { + continue + } + if err := s.cfg.p2p.BroadcastBlob(ctx, sidecar.Index, sidecar.BlobSidecar); err != nil { + log.WithFields(blobFields(sidecar.ROBlob)).WithError(err).Error("Failed to broadcast blob sidecar") + } + } + + for _, sidecar := range blobSidecars { + if sidecar.Index >= uint64(len(indices)) || indices[sidecar.Index] { + blobExistedInDBTotal.Inc() + continue + } + if err := s.subscribeBlob(ctx, sidecar); err != nil { + log.WithFields(blobFields(sidecar.ROBlob)).WithError(err).Error("Failed to receive blob") + continue + } + + blobRecoveredFromELTotal.Inc() + fields := blobFields(sidecar.ROBlob) + fields["sinceSlotStartTime"] = s.cfg.clock.Now().Sub(startTime) + log.WithFields(fields).Debug("Processed blob sidecar from EL") + } +} + // WriteInvalidBlockToDisk as a block ssz. Writes to temp directory. func saveInvalidBlockToTemp(block interfaces.ReadOnlySignedBeaconBlock) { if !features.Get().SaveInvalidBlock { diff --git a/beacon-chain/sync/subscriber_beacon_blocks_test.go b/beacon-chain/sync/subscriber_beacon_blocks_test.go index 626215144af6..26a2cec418cd 100644 --- a/beacon-chain/sync/subscriber_beacon_blocks_test.go +++ b/beacon-chain/sync/subscriber_beacon_blocks_test.go @@ -9,14 +9,20 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" chainMock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem" dbtest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution" + mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations" + mockp2p "github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p/testing" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/startup" lruwrpr "github.com/prysmaticlabs/prysm/v5/cache/lru" + "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" + "github.com/prysmaticlabs/prysm/v5/time" "google.golang.org/protobuf/proto" ) @@ -66,7 +72,9 @@ func TestService_beaconBlockSubscriber(t *testing.T) { DB: db, Root: make([]byte, 32), }, - attPool: attestations.NewPool(), + attPool: attestations.NewPool(), + blobStorage: filesystem.NewEphemeralBlobStorage(t), + executionReconstructor: &mockExecution.EngineClient{}, }, } s.initCaches() @@ -124,3 +132,65 @@ func TestService_BeaconBlockSubscribe_UndefinedEeError(t *testing.T) { require.Equal(t, 0, len(s.badBlockCache.Keys())) require.Equal(t, 1, len(s.seenBlockCache.Keys())) } + +func TestReconstructAndBroadcastBlobs(t *testing.T) { + rob, err := blocks.NewROBlob( + ðpb.BlobSidecar{ + SignedBlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + ParentRoot: make([]byte, 32), + BodyRoot: make([]byte, 32), + StateRoot: make([]byte, 32), + }, + Signature: []byte("signature"), + }, + }) + require.NoError(t, err) + + chainService := &chainMock.ChainService{ + Genesis: time.Now(), + } + + b := util.NewBeaconBlockDeneb() + sb, err := blocks.NewSignedBeaconBlock(b) + require.NoError(t, err) + + tests := []struct { + name string + blobSidecars []blocks.VerifiedROBlob + expectedBlobCount int + }{ + { + name: "Constructed 0 blobs", + blobSidecars: nil, + expectedBlobCount: 0, + }, + { + name: "Constructed 6 blobs", + blobSidecars: []blocks.VerifiedROBlob{ + {ROBlob: rob}, {ROBlob: rob}, {ROBlob: rob}, {ROBlob: rob}, {ROBlob: rob}, {ROBlob: rob}, + }, + expectedBlobCount: 6, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := Service{ + cfg: &config{ + p2p: mockp2p.NewTestP2P(t), + chain: chainService, + clock: startup.NewClock(time.Now(), [32]byte{}), + blobStorage: filesystem.NewEphemeralBlobStorage(t), + executionReconstructor: &mockExecution.EngineClient{ + BlobSidecars: tt.blobSidecars, + }, + operationNotifier: &chainMock.MockOperationNotifier{}, + }, + seenBlobCache: lruwrpr.New(1), + } + s.reconstructAndBroadcastBlobs(context.Background(), sb) + require.Equal(t, tt.expectedBlobCount, len(chainService.Blobs)) + }) + } +} diff --git a/beacon-chain/sync/subscriber_blob_sidecar.go b/beacon-chain/sync/subscriber_blob_sidecar.go index e93638bdad10..43b5d4f7f4d0 100644 --- a/beacon-chain/sync/subscriber_blob_sidecar.go +++ b/beacon-chain/sync/subscriber_blob_sidecar.go @@ -16,6 +16,10 @@ func (s *Service) blobSubscriber(ctx context.Context, msg proto.Message) error { return fmt.Errorf("message was not type blocks.ROBlob, type=%T", msg) } + return s.subscribeBlob(ctx, b) +} + +func (s *Service) subscribeBlob(ctx context.Context, b blocks.VerifiedROBlob) error { s.setSeenBlobIndex(b.Slot(), b.ProposerIndex(), b.Index) if err := s.cfg.chain.ReceiveBlob(ctx, b); err != nil { diff --git a/beacon-chain/verification/batch_test.go b/beacon-chain/verification/batch_test.go index f0e987d79739..6bc33bea3d40 100644 --- a/beacon-chain/verification/batch_test.go +++ b/beacon-chain/verification/batch_test.go @@ -41,7 +41,7 @@ func TestBatchVerifier(t *testing.T) { }, nv: func() NewBlobVerifier { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { - return &MockBlobVerifier{cbVerifiedROBlob: vbcb(bl, nil)} + return &MockBlobVerifier{CbVerifiedROBlob: vbcb(bl, nil)} } }, nblobs: 0, @@ -50,7 +50,7 @@ func TestBatchVerifier(t *testing.T) { name: "happy path", nv: func() NewBlobVerifier { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { - return &MockBlobVerifier{cbVerifiedROBlob: vbcb(bl, nil)} + return &MockBlobVerifier{CbVerifiedROBlob: vbcb(bl, nil)} } }, bandb: func(t *testing.T, nb int) (blocks.ROBlock, []blocks.ROBlob) { @@ -62,7 +62,7 @@ func TestBatchVerifier(t *testing.T) { name: "partial batch", nv: func() NewBlobVerifier { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { - return &MockBlobVerifier{cbVerifiedROBlob: vbcb(bl, nil)} + return &MockBlobVerifier{CbVerifiedROBlob: vbcb(bl, nil)} } }, bandb: func(t *testing.T, nb int) (blocks.ROBlock, []blocks.ROBlob) { @@ -76,7 +76,7 @@ func TestBatchVerifier(t *testing.T) { name: "invalid commitment", nv: func() NewBlobVerifier { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { - return &MockBlobVerifier{cbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { + return &MockBlobVerifier{CbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { t.Fatal("Batch verifier should stop before this point") return blocks.VerifiedROBlob{}, nil }} @@ -93,7 +93,7 @@ func TestBatchVerifier(t *testing.T) { name: "signature mismatch", nv: func() NewBlobVerifier { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { - return &MockBlobVerifier{cbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { + return &MockBlobVerifier{CbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { t.Fatal("Batch verifier should stop before this point") return blocks.VerifiedROBlob{}, nil }} @@ -111,7 +111,7 @@ func TestBatchVerifier(t *testing.T) { name: "root mismatch", nv: func() NewBlobVerifier { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { - return &MockBlobVerifier{cbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { + return &MockBlobVerifier{CbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { t.Fatal("Batch verifier should stop before this point") return blocks.VerifiedROBlob{}, nil }} @@ -133,7 +133,7 @@ func TestBatchVerifier(t *testing.T) { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { return &MockBlobVerifier{ ErrBlobIndexInBounds: ErrBlobIndexInvalid, - cbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { + CbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { t.Fatal("Batch verifier should stop before this point") return blocks.VerifiedROBlob{}, nil }} @@ -151,7 +151,7 @@ func TestBatchVerifier(t *testing.T) { return func(bl blocks.ROBlob, reqs []Requirement) BlobVerifier { return &MockBlobVerifier{ ErrSidecarInclusionProven: ErrSidecarInclusionProofInvalid, - cbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { + CbVerifiedROBlob: func() (blocks.VerifiedROBlob, error) { t.Fatal("Batch verifier should stop before this point") return blocks.VerifiedROBlob{}, nil }} diff --git a/beacon-chain/verification/blob.go b/beacon-chain/verification/blob.go index 916ddff3bc31..0bef736b04c1 100644 --- a/beacon-chain/verification/blob.go +++ b/beacon-chain/verification/blob.go @@ -67,6 +67,10 @@ var InitsyncSidecarRequirements = requirementList(GossipSidecarRequirements).exc RequireSidecarProposerExpected, ) +// ELMemPoolRequirements is a list of verification requirements to be used when importing blobs and proof from +// execution layer mempool. Only the KZG proof verification is required. +var ELMemPoolRequirements = []Requirement{RequireSidecarKzgProofVerified} + // BackfillSidecarRequirements is the same as InitsyncSidecarRequirements. var BackfillSidecarRequirements = requirementList(InitsyncSidecarRequirements).excluding() diff --git a/beacon-chain/verification/mock.go b/beacon-chain/verification/mock.go index 8f956911de67..66c6e49071ff 100644 --- a/beacon-chain/verification/mock.go +++ b/beacon-chain/verification/mock.go @@ -18,11 +18,11 @@ type MockBlobVerifier struct { ErrSidecarInclusionProven error ErrSidecarKzgProofVerified error ErrSidecarProposerExpected error - cbVerifiedROBlob func() (blocks.VerifiedROBlob, error) + CbVerifiedROBlob func() (blocks.VerifiedROBlob, error) } func (m *MockBlobVerifier) VerifiedROBlob() (blocks.VerifiedROBlob, error) { - return m.cbVerifiedROBlob() + return m.CbVerifiedROBlob() } func (m *MockBlobVerifier) BlobIndexInBounds() (err error) { diff --git a/proto/engine/v1/execution_engine.pb.go b/proto/engine/v1/execution_engine.pb.go index 141fc09baf2d..74c7d1c267e4 100755 --- a/proto/engine/v1/execution_engine.pb.go +++ b/proto/engine/v1/execution_engine.pb.go @@ -1697,6 +1697,61 @@ func (x *Blob) GetData() []byte { return nil } +type BlobAndProof struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Blob []byte `protobuf:"bytes,1,opt,name=blob,proto3" json:"blob,omitempty" ssz-size:"131072"` + KzgProof []byte `protobuf:"bytes,2,opt,name=kzg_proof,json=kzgProof,proto3" json:"kzg_proof,omitempty" ssz-size:"48"` +} + +func (x *BlobAndProof) Reset() { + *x = BlobAndProof{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlobAndProof) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlobAndProof) ProtoMessage() {} + +func (x *BlobAndProof) ProtoReflect() protoreflect.Message { + mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[16] + 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 BlobAndProof.ProtoReflect.Descriptor instead. +func (*BlobAndProof) Descriptor() ([]byte, []int) { + return file_proto_engine_v1_execution_engine_proto_rawDescGZIP(), []int{16} +} + +func (x *BlobAndProof) GetBlob() []byte { + if x != nil { + return x.Blob + } + return nil +} + +func (x *BlobAndProof) GetKzgProof() []byte { + if x != nil { + return x.KzgProof + } + return nil +} + var File_proto_engine_v1_execution_engine_proto protoreflect.FileDescriptor var file_proto_engine_v1_execution_engine_proto_rawDesc = []byte{ @@ -2072,17 +2127,23 @@ var file_proto_engine_v1_execution_engine_proto_rawDesc = []byte{ 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x26, 0x0a, 0x04, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a, 0x8a, 0xb5, 0x18, 0x06, 0x31, - 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x96, 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, 0x14, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 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, + 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x53, 0x0a, 0x0c, 0x42, + 0x6c, 0x6f, 0x62, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x1e, 0x0a, 0x04, 0x62, + 0x6c, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a, 0x8a, 0xb5, 0x18, 0x06, 0x31, + 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x12, 0x23, 0x0a, 0x09, 0x6b, + 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x08, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x42, 0x96, 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, 0x14, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 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, } var ( @@ -2098,7 +2159,7 @@ func file_proto_engine_v1_execution_engine_proto_rawDescGZIP() []byte { } var file_proto_engine_v1_execution_engine_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_engine_v1_execution_engine_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_proto_engine_v1_execution_engine_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_proto_engine_v1_execution_engine_proto_goTypes = []interface{}{ (PayloadStatus_Status)(0), // 0: ethereum.engine.v1.PayloadStatus.Status (*ExecutionPayload)(nil), // 1: ethereum.engine.v1.ExecutionPayload @@ -2117,6 +2178,7 @@ var file_proto_engine_v1_execution_engine_proto_goTypes = []interface{}{ (*Withdrawal)(nil), // 14: ethereum.engine.v1.Withdrawal (*BlobsBundle)(nil), // 15: ethereum.engine.v1.BlobsBundle (*Blob)(nil), // 16: ethereum.engine.v1.Blob + (*BlobAndProof)(nil), // 17: ethereum.engine.v1.BlobAndProof } var file_proto_engine_v1_execution_engine_proto_depIdxs = []int32{ 14, // 0: ethereum.engine.v1.ExecutionPayloadCapella.withdrawals:type_name -> ethereum.engine.v1.Withdrawal @@ -2332,6 +2394,18 @@ func file_proto_engine_v1_execution_engine_proto_init() { return nil } } + file_proto_engine_v1_execution_engine_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlobAndProof); 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{ @@ -2339,7 +2413,7 @@ func file_proto_engine_v1_execution_engine_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_engine_v1_execution_engine_proto_rawDesc, NumEnums: 1, - NumMessages: 16, + NumMessages: 17, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/engine/v1/execution_engine.proto b/proto/engine/v1/execution_engine.proto index 17ac3b258248..28cf20f3a51b 100644 --- a/proto/engine/v1/execution_engine.proto +++ b/proto/engine/v1/execution_engine.proto @@ -224,3 +224,7 @@ message Blob { bytes data = 1 [(ethereum.eth.ext.ssz_size) = "blob.size"]; } +message BlobAndProof { + bytes blob = 1 [(ethereum.eth.ext.ssz_size) = "blob.size"]; + bytes kzg_proof = 2 [(ethereum.eth.ext.ssz_size) = "48"]; +} diff --git a/proto/engine/v1/json_marshal_unmarshal.go b/proto/engine/v1/json_marshal_unmarshal.go index c06b937136d0..1d1dcd1c0270 100644 --- a/proto/engine/v1/json_marshal_unmarshal.go +++ b/proto/engine/v1/json_marshal_unmarshal.go @@ -838,6 +838,11 @@ func (b BlobBundleJSON) ToProto() *BlobsBundle { } } +type BlobAndProofJson struct { + Blob hexutil.Bytes `json:"blob"` + KzgProof hexutil.Bytes `json:"proof"` +} + // MarshalJSON -- func (e *ExecutionPayloadDeneb) MarshalJSON() ([]byte, error) { transactions := make([]hexutil.Bytes, len(e.Transactions)) @@ -1259,3 +1264,21 @@ func RecastHexutilByteSlice(h []hexutil.Bytes) [][]byte { } return r } + +// UnmarshalJSON implements the json unmarshaler interface for BlobAndProof. +func (b *BlobAndProof) UnmarshalJSON(enc []byte) error { + var dec *BlobAndProofJson + if err := json.Unmarshal(enc, &dec); err != nil { + return err + } + + blob := make([]byte, fieldparams.BlobLength) + copy(blob, dec.Blob) + b.Blob = blob + + proof := make([]byte, fieldparams.BLSPubkeyLength) + copy(proof, dec.KzgProof) + b.KzgProof = proof + + return nil +} diff --git a/proto/prysm/v1alpha1/light_client.pb.go b/proto/prysm/v1alpha1/light_client.pb.go index e0bf066767c8..34992141bc7c 100755 --- a/proto/prysm/v1alpha1/light_client.pb.go +++ b/proto/prysm/v1alpha1/light_client.pb.go @@ -7,13 +7,14 @@ package eth import ( + reflect "reflect" + sync "sync" + github_com_prysmaticlabs_prysm_v5_consensus_types_primitives "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" _ "github.com/prysmaticlabs/prysm/v5/proto/eth/ext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( From 4386c244e1680d90ed3cc8123f453badf956efdd Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Thu, 24 Oct 2024 21:30:21 -0500 Subject: [PATCH 26/26] Docker fix: Update bazel-lib to latest version (#14579) * Update bazel-lib * Update rules_oci * Compress pkg_tar * Add a note about 401 download for distroless/cc-debian11 * Update CHANGELOG.md * Re-upload distroless/cc-debian11 to prysmaticlabs/distroless/cc-debian11 such that the image is public. Ran the following command: gcrane cp 'gcr.io/distroless/cc-debian11@sha256:b82f113425c5b5c714151aaacd8039bc141821cdcd3c65202d42bdf9c43ae60b' gcr.io/prysmaticlabs/distroless/cc-debian11:latest * Back out "Update rules_oci" This backs out commit 64200fb25e515c78e1d6d68be01a640508373d9d. --- CHANGELOG.md | 2 ++ WORKSPACE | 8 ++++---- tools/prysm_image.bzl | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cefcd7c4835..9e324fe5f3f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Fixed mesh size by appending `gParams.Dhi = gossipSubDhi` - Fix skipping partial withdrawals count. - wait for the async StreamEvent writer to exit before leaving the http handler, avoiding race condition panics [pr](https://github.com/prysmaticlabs/prysm/pull/14557) +- Certain deb files were returning a 404 which made building new docker images without an existing + cache impossible. This has been fixed with updates to rules_oci and bazel-lib. ### Security diff --git a/WORKSPACE b/WORKSPACE index c9906164adc5..9f52bbccf040 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -101,9 +101,9 @@ http_archive( http_archive( name = "aspect_bazel_lib", - sha256 = "f5ea76682b209cc0bd90d0f5a3b26d2f7a6a2885f0c5f615e72913f4805dbb0d", - strip_prefix = "bazel-lib-2.5.0", - url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.5.0/bazel-lib-v2.5.0.tar.gz", + sha256 = "a272d79bb0ac6b6965aa199b1f84333413452e87f043b53eca7f347a23a478e8", + strip_prefix = "bazel-lib-2.9.3", + url = "https://github.com/bazel-contrib/bazel-lib/releases/download/v2.9.3/bazel-lib-v2.9.3.tar.gz", ) load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies", "aspect_bazel_lib_register_toolchains") @@ -165,7 +165,7 @@ load("@rules_oci//oci:pull.bzl", "oci_pull") oci_pull( name = "linux_debian11_multiarch_base", # Debian bullseye digest = "sha256:b82f113425c5b5c714151aaacd8039bc141821cdcd3c65202d42bdf9c43ae60b", # 2023-12-12 - image = "gcr.io/distroless/cc-debian11", + image = "gcr.io/prysmaticlabs/distroless/cc-debian11", platforms = [ "linux/amd64", "linux/arm64/v8", diff --git a/tools/prysm_image.bzl b/tools/prysm_image.bzl index fa98bb6b6d0a..44814784cec8 100644 --- a/tools/prysm_image.bzl +++ b/tools/prysm_image.bzl @@ -14,6 +14,7 @@ def prysm_image_upload( srcs = [binary], symlinks = symlinks, tags = tags, + extension = "tar.gz", ) oci_image(