diff --git a/CHANGELOG.md b/CHANGELOG.md index eab3ce24a62..cb8b45b8e3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Updated light client consensus types. [PR](https://github.com/prysmaticlabs/prysm/pull/14652) - Update earliest exit epoch for upgrade to electra - Add missed exit checks to consolidation processing +- Fixed pending deposits processing on Electra. ### Deprecated diff --git a/beacon-chain/core/electra/deposits.go b/beacon-chain/core/electra/deposits.go index b20ba0a308f..c77adb6040e 100644 --- a/beacon-chain/core/electra/deposits.go +++ b/beacon-chain/core/electra/deposits.go @@ -386,8 +386,14 @@ func batchProcessNewPendingDeposits(ctx context.Context, state state.BeaconState return errors.Wrap(err, "batch signature verification failed") } + pubKeyMap := make(map[[48]byte]struct{}, len(pendingDeposits)) + // Process each deposit individually for _, pendingDeposit := range pendingDeposits { + _, found := pubKeyMap[bytesutil.ToBytes48(pendingDeposit.PublicKey)] + if !found { + pubKeyMap[bytesutil.ToBytes48(pendingDeposit.PublicKey)] = struct{}{} + } validSignature := allSignaturesVerified // If batch verification failed, check the individual deposit signature @@ -405,9 +411,16 @@ func batchProcessNewPendingDeposits(ctx context.Context, state state.BeaconState // 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") + if found { + index, _ := state.ValidatorIndexByPubkey(bytesutil.ToBytes48(pendingDeposit.PublicKey)) + if err := helpers.IncreaseBalance(state, index, pendingDeposit.Amount); err != nil { + return errors.Wrap(err, "could not increase balance") + } + } else { + err = AddValidatorToRegistry(state, pendingDeposit.PublicKey, pendingDeposit.WithdrawalCredentials, pendingDeposit.Amount) + if err != nil { + return errors.Wrap(err, "failed to add validator to registry") + } } } } diff --git a/beacon-chain/core/electra/deposits_test.go b/beacon-chain/core/electra/deposits_test.go index 0cef53f329e..34f13631088 100644 --- a/beacon-chain/core/electra/deposits_test.go +++ b/beacon-chain/core/electra/deposits_test.go @@ -22,6 +22,40 @@ import ( "github.com/prysmaticlabs/prysm/v5/testing/util" ) +func TestProcessPendingDepositsMultiplesSameDeposits(t *testing.T) { + st := stateWithActiveBalanceETH(t, 1000) + deps := make([]*eth.PendingDeposit, 2) // Make same deposit twice + validators := st.Validators() + sk, err := bls.RandKey() + require.NoError(t, err) + for i := 0; i < len(deps); i += 1 { + 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, 32, bytesutil.ToBytes32(wc), 0) + } + require.NoError(t, st.SetPendingDeposits(deps)) + + err = electra.ProcessPendingDeposits(context.TODO(), st, 10000) + require.NoError(t, err) + + val := st.Validators() + seenPubkeys := make(map[string]struct{}) + for i := 0; i < len(val); i += 1 { + if len(val[i].PublicKey) == 0 { + continue + } + _, ok := seenPubkeys[string(val[i].PublicKey)] + if ok { + t.Fatalf("duplicated pubkeys") + } else { + seenPubkeys[string(val[i].PublicKey)] = struct{}{} + } + } +} + func TestProcessPendingDeposits(t *testing.T) { tests := []struct { name string @@ -285,7 +319,7 @@ func TestBatchProcessNewPendingDeposits(t *testing.T) { wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte wc[31] = byte(0) validDep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0) - invalidDep := ð.PendingDeposit{} + invalidDep := ð.PendingDeposit{PublicKey: make([]byte, 48)} // have a combination of valid and invalid deposits deps := []*eth.PendingDeposit{validDep, invalidDep} require.NoError(t, electra.BatchProcessNewPendingDeposits(context.Background(), st, deps))