mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-07 22:54:17 -05:00
Trigger Execution Requests In E2E (#14971)
* Trigger Consolidation * Finally have it working * Fix Build * Revert Change * Fix Context * Finally have consolidations working * Get Evaluator Working * Get Withdrawals Working * Changelog * Finally Passes * New line * Try again * fmt * fmt * Fix Test
This commit is contained in:
3
changelog/nisdas_e2e_execution_requests.md
Normal file
3
changelog/nisdas_e2e_execution_requests.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Added the ability for execution requests to be tested in e2e with electra.
|
||||
@@ -24,6 +24,7 @@ func E2ETestConfig() *BeaconChainConfig {
|
||||
e2eConfig.GenesisDelay = 10 // 10 seconds so E2E has enough time to process deposits and get started.
|
||||
e2eConfig.ChurnLimitQuotient = 65536
|
||||
e2eConfig.MaxValidatorsPerWithdrawalsSweep = 128
|
||||
e2eConfig.MinPerEpochChurnLimitElectra = 256000000000
|
||||
|
||||
// Time parameters.
|
||||
e2eConfig.SecondsPerSlot = 10
|
||||
@@ -73,6 +74,7 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
|
||||
e2eConfig.MinGenesisActiveValidatorCount = 256
|
||||
e2eConfig.GenesisDelay = 25 // 25 seconds so E2E has enough time to process deposits and get started.
|
||||
e2eConfig.ChurnLimitQuotient = 65536
|
||||
e2eConfig.MinPerEpochChurnLimitElectra = 256000000000
|
||||
|
||||
// Time parameters.
|
||||
e2eConfig.SecondsPerSlot = 6
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/async"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
||||
@@ -171,7 +172,7 @@ func createDepositData(privKey bls.SecretKey, pubKey bls.PublicKey, withExecCred
|
||||
if withExecCreds {
|
||||
newCredentials := make([]byte, 12)
|
||||
newCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
||||
execAddr := bytesutil.ToBytes20(pubKey.Marshal())
|
||||
execAddr := bytesutil.ToBytes20(hexutil.MustDecode(executionAddress))
|
||||
depositMessage.WithdrawalCredentials = append(newCredentials, execAddr[:]...)
|
||||
}
|
||||
sr, err := depositMessage.HashTreeRoot()
|
||||
|
||||
@@ -184,8 +184,10 @@ func GethTestnetGenesis(genesisTime uint64, cfg *clparams.BeaconChainConfig) *co
|
||||
Mixhash: common.HexToHash(defaultMixhash),
|
||||
Coinbase: common.HexToAddress(defaultCoinbase),
|
||||
Alloc: types.GenesisAlloc{
|
||||
da.Address: da.Account,
|
||||
ma.Address: ma.Account,
|
||||
da.Address: da.Account,
|
||||
ma.Address: ma.Account,
|
||||
params.WithdrawalQueueAddress: {Nonce: 1, Code: params.WithdrawalQueueCode, Balance: common.Big0},
|
||||
params.ConsolidationQueueAddress: {Nonce: 1, Code: params.ConsolidationQueueCode, Balance: common.Big0},
|
||||
},
|
||||
ParentHash: common.HexToHash(defaultParenthash),
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
|
||||
var errUnsupportedVersion = errors.New("schema version not supported by PremineGenesisConfig")
|
||||
|
||||
const executionAddress = "0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766"
|
||||
|
||||
type PremineGenesisConfig struct {
|
||||
GenesisTime uint64
|
||||
NVals uint64
|
||||
|
||||
@@ -28,7 +28,7 @@ type componentHandler struct {
|
||||
web3Signer e2etypes.ComponentRunner
|
||||
bootnode e2etypes.ComponentRunner
|
||||
eth1Miner e2etypes.ComponentRunner
|
||||
txGen e2etypes.ComponentRunner
|
||||
txGen *eth1.TransactionGenerator
|
||||
builders e2etypes.MultipleComponentRunners
|
||||
eth1Proxy e2etypes.MultipleComponentRunners
|
||||
eth1Nodes e2etypes.MultipleComponentRunners
|
||||
|
||||
@@ -33,9 +33,11 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//accounts/keystore:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//crypto:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//crypto/kzg4844:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient/gethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//params:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_mariusvanderwijden_fuzzyvm//filler:go_default_library",
|
||||
|
||||
@@ -16,15 +16,19 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
gethCrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/ethclient/gethclient"
|
||||
gethparams "github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/rand"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/interop"
|
||||
e2e "github.com/prysmaticlabs/prysm/v5/testing/endtoend/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -32,14 +36,23 @@ import (
|
||||
|
||||
const txCount = 20
|
||||
|
||||
type txType int
|
||||
|
||||
const (
|
||||
RandomTx txType = iota
|
||||
ConsolidationTx
|
||||
WithdrawalTx
|
||||
)
|
||||
|
||||
var fundedAccount *keystore.Key
|
||||
|
||||
type TransactionGenerator struct {
|
||||
keystore string
|
||||
seed int64
|
||||
started chan struct{}
|
||||
cancel context.CancelFunc
|
||||
paused bool
|
||||
keystore string
|
||||
seed int64
|
||||
started chan struct{}
|
||||
cancel context.CancelFunc
|
||||
paused bool
|
||||
txGenType txType
|
||||
}
|
||||
|
||||
func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
|
||||
@@ -49,7 +62,7 @@ func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
|
||||
}
|
||||
|
||||
func NewTransactionGenerator(keystore string, seed int64) *TransactionGenerator {
|
||||
return &TransactionGenerator{keystore: keystore, seed: seed}
|
||||
return &TransactionGenerator{keystore: keystore, seed: seed, txGenType: RandomTx}
|
||||
}
|
||||
|
||||
func (t *TransactionGenerator) Start(ctx context.Context) error {
|
||||
@@ -105,10 +118,26 @@ func (t *TransactionGenerator) Start(ctx context.Context) error {
|
||||
continue
|
||||
}
|
||||
backend := ethclient.NewClient(client)
|
||||
err = SendTransaction(client, mineKey.PrivateKey, f, gasPrice, mineKey.Address.String(), txCount, backend, false)
|
||||
if err != nil {
|
||||
return err
|
||||
switch t.txGenType {
|
||||
case ConsolidationTx:
|
||||
err = SendConsolidationTransaction(mineKey.PrivateKey, gasPrice, backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case WithdrawalTx:
|
||||
err = SendWithdrawalTransaction(mineKey.PrivateKey, newKey.PrivateKey, gasPrice, backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case RandomTx:
|
||||
err = SendTransaction(client, mineKey.PrivateKey, f, gasPrice, mineKey.Address.String(), txCount, backend, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
logrus.Warnf("Unknown transaction type: %v", t.txGenType)
|
||||
}
|
||||
|
||||
backend.Close()
|
||||
}
|
||||
}
|
||||
@@ -219,6 +248,195 @@ func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, f *filler.Filler
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendConsolidationTransaction(key *ecdsa.PrivateKey, gasPrice *big.Int, backend *ethclient.Client) error {
|
||||
totalCreds := e2e.TestParams.NumberOfExecutionCreds
|
||||
_, pKeys, err := interop.DeterministicallyGenerateKeys(0, totalCreds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
compoundedKey := pKeys[len(pKeys)-1].Marshal()
|
||||
|
||||
// Create compounding credentials
|
||||
if err := createAndSendConsolidation(compoundedKey, compoundedKey, key, gasPrice, backend); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, k := range pKeys {
|
||||
if err := createAndSendConsolidation(k.Marshal(), compoundedKey, key, gasPrice, backend); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Junk Requests
|
||||
for i := 0; i < 2; i++ {
|
||||
sourcePubkey := [48]byte{byte(i), 0xFF, 0x34, 0xEE}
|
||||
targetPubkey := compoundedKey
|
||||
if err := createAndSendConsolidation(sourcePubkey[:], targetPubkey, key, gasPrice, backend); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createAndSendConsolidation(sourceKey, targetKey []byte, key *ecdsa.PrivateKey, gasPrice *big.Int, backend *ethclient.Client) error {
|
||||
publicKey := key.Public().(*ecdsa.PublicKey)
|
||||
fromAddress := gethCrypto.PubkeyToAddress(*publicKey)
|
||||
// Get nonce
|
||||
nonce, err := backend.PendingNonceAt(context.Background(), fromAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chainid, err := backend.ChainID(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gasLimit := uint64(200000)
|
||||
|
||||
sourcePubkey := sourceKey
|
||||
targetPubkey := targetKey
|
||||
|
||||
consolidationData := []byte{}
|
||||
consolidationData = append(consolidationData, sourcePubkey...)
|
||||
consolidationData = append(consolidationData, targetPubkey...)
|
||||
|
||||
ret, err := backend.CallContract(context.Background(), ethereum.CallMsg{To: &gethparams.ConsolidationQueueAddress}, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "%s", string(ret))
|
||||
}
|
||||
fee := new(big.Int).SetBytes(ret)
|
||||
fee = fee.Mul(fee, big.NewInt(2))
|
||||
|
||||
// Create transaction
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: nonce,
|
||||
To: &gethparams.ConsolidationQueueAddress,
|
||||
Value: fee,
|
||||
Gas: gasLimit,
|
||||
GasPrice: gasPrice,
|
||||
Data: consolidationData,
|
||||
})
|
||||
|
||||
// Sign transaction
|
||||
signedTx, err := types.SignTx(tx, types.NewCancunSigner(chainid), key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return backend.SendTransaction(context.Background(), signedTx)
|
||||
}
|
||||
|
||||
func SendWithdrawalTransaction(key, newKey *ecdsa.PrivateKey, gasPrice *big.Int, backend *ethclient.Client) error {
|
||||
totalCreds := e2e.TestParams.NumberOfExecutionCreds
|
||||
_, pKeys, err := interop.DeterministicallyGenerateKeys(0, totalCreds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
compoundedKey := pKeys[len(pKeys)-1].Marshal()
|
||||
|
||||
_, invalidWithdrawalKeys, err := interop.DeterministicallyGenerateKeys(totalCreds, totalCreds+4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
publicKey := key.Public().(*ecdsa.PublicKey)
|
||||
fromAddress := gethCrypto.PubkeyToAddress(*publicKey)
|
||||
nonce, err := backend.PendingNonceAt(context.Background(), fromAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var withdrawalTxs []*types.Transaction
|
||||
// Create Withdrawal for compounded key.
|
||||
tx, err := createWithdrawal(compoundedKey, 0, nonce, key, gasPrice, backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
withdrawalTxs = append(withdrawalTxs, tx)
|
||||
nonce++
|
||||
|
||||
rGen := rand.NewDeterministicGenerator()
|
||||
for _, k := range pKeys {
|
||||
tx, err := createWithdrawal(k.Marshal(), uint64(rGen.Int63n(32000000000)), nonce, key, gasPrice, backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
withdrawalTxs = append(withdrawalTxs, tx)
|
||||
nonce++
|
||||
}
|
||||
|
||||
// Junk Requests
|
||||
for _, k := range invalidWithdrawalKeys {
|
||||
tx, err := createWithdrawal(k.Marshal(), uint64(rGen.Int63n(32000000000)), nonce, newKey, gasPrice, backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
withdrawalTxs = append(withdrawalTxs, tx)
|
||||
nonce++
|
||||
}
|
||||
currExecHead := uint64(0)
|
||||
// Batch And Send Withdrawals
|
||||
for len(withdrawalTxs) > 0 {
|
||||
currBlock, err := backend.BlockNumber(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currBlock > currExecHead {
|
||||
currExecHead = currBlock
|
||||
maxWithdrawalPerPayload := params.BeaconConfig().MaxWithdrawalRequestsPerPayload
|
||||
if maxWithdrawalPerPayload > uint64(len(withdrawalTxs)) {
|
||||
maxWithdrawalPerPayload = uint64(len(withdrawalTxs))
|
||||
}
|
||||
for _, tx := range withdrawalTxs[:maxWithdrawalPerPayload] {
|
||||
if err := backend.SendTransaction(context.Background(), tx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Shift slice to only have unsent transactions
|
||||
withdrawalTxs = withdrawalTxs[maxWithdrawalPerPayload:]
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createWithdrawal(sourceKey []byte, amount, nonce uint64, key *ecdsa.PrivateKey, gasPrice *big.Int, backend *ethclient.Client) (*types.Transaction, error) {
|
||||
chainid, err := backend.ChainID(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gasLimit := uint64(200000)
|
||||
|
||||
withdrawalData := []byte{}
|
||||
withdrawalData = append(withdrawalData, sourceKey...)
|
||||
withdrawalData = append(withdrawalData, bytesutil.Uint64ToBytesBigEndian(amount)...)
|
||||
|
||||
ret, err := backend.CallContract(context.Background(), ethereum.CallMsg{To: &gethparams.WithdrawalQueueAddress}, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "%s", string(ret))
|
||||
}
|
||||
fee := new(big.Int).SetBytes(ret)
|
||||
fee = fee.Mul(fee, big.NewInt(2))
|
||||
|
||||
// Create transaction
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: nonce,
|
||||
To: &gethparams.WithdrawalQueueAddress,
|
||||
Value: fee,
|
||||
Gas: gasLimit,
|
||||
GasPrice: gasPrice,
|
||||
Data: withdrawalData,
|
||||
})
|
||||
|
||||
// Sign transaction
|
||||
signedTx, err := types.SignTx(tx, types.NewCancunSigner(chainid), key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signedTx, nil
|
||||
}
|
||||
|
||||
func (t *TransactionGenerator) SetTxType(typ txType) {
|
||||
t.txGenType = typ
|
||||
}
|
||||
|
||||
// Pause pauses the component and its underlying process.
|
||||
func (t *TransactionGenerator) Pause() error {
|
||||
t.paused = true
|
||||
|
||||
@@ -73,17 +73,18 @@ func e2eMinimal(t *testing.T, cfg *params.BeaconChainConfig, cfgo ...types.E2ECo
|
||||
"--enable-tracing",
|
||||
"--trace-sample-fraction=1.0",
|
||||
},
|
||||
ValidatorFlags: []string{},
|
||||
EpochsToRun: uint64(epochsToRun),
|
||||
TestSync: true,
|
||||
TestFeature: true,
|
||||
TestDeposits: true,
|
||||
UsePrysmShValidator: false,
|
||||
UsePprof: true,
|
||||
TracingSinkEndpoint: tracingEndpoint,
|
||||
Evaluators: evals,
|
||||
EvalInterceptor: defaultInterceptor,
|
||||
Seed: int64(seed),
|
||||
ValidatorFlags: []string{},
|
||||
EpochsToRun: uint64(epochsToRun),
|
||||
TestSync: true,
|
||||
TestFeature: true,
|
||||
TestDeposits: true,
|
||||
UsePrysmShValidator: false,
|
||||
UsePprof: true,
|
||||
TestExecutionRequests: true,
|
||||
TracingSinkEndpoint: tracingEndpoint,
|
||||
Evaluators: evals,
|
||||
EvalInterceptor: defaultInterceptor,
|
||||
Seed: int64(seed),
|
||||
}
|
||||
for _, o := range cfgo {
|
||||
o(testConfig)
|
||||
@@ -149,18 +150,19 @@ func e2eMainnet(t *testing.T, usePrysmSh, useMultiClient bool, cfg *params.Beaco
|
||||
"--enable-tracing",
|
||||
"--trace-sample-fraction=1.0",
|
||||
},
|
||||
ValidatorFlags: []string{},
|
||||
EpochsToRun: uint64(epochsToRun),
|
||||
TestSync: true,
|
||||
TestFeature: true,
|
||||
TestDeposits: true,
|
||||
UseFixedPeerIDs: true,
|
||||
UsePrysmShValidator: usePrysmSh,
|
||||
UsePprof: true,
|
||||
TracingSinkEndpoint: tracingEndpoint,
|
||||
Evaluators: evals,
|
||||
EvalInterceptor: defaultInterceptor,
|
||||
Seed: int64(seed),
|
||||
ValidatorFlags: []string{},
|
||||
EpochsToRun: uint64(epochsToRun),
|
||||
TestSync: true,
|
||||
TestFeature: true,
|
||||
TestDeposits: true,
|
||||
UseFixedPeerIDs: true,
|
||||
UsePrysmShValidator: usePrysmSh,
|
||||
TestExecutionRequests: true,
|
||||
UsePprof: true,
|
||||
TracingSinkEndpoint: tracingEndpoint,
|
||||
Evaluators: evals,
|
||||
EvalInterceptor: defaultInterceptor,
|
||||
Seed: int64(seed),
|
||||
}
|
||||
for _, o := range cfgo {
|
||||
o(testConfig)
|
||||
|
||||
@@ -497,13 +497,15 @@ func (r *testRunner) defaultEndToEndRun() error {
|
||||
require.NoError(t, err)
|
||||
tickingStartTime := helpers.EpochTickerStartTime(genesis)
|
||||
|
||||
ec := e2etypes.NewEvaluationContext(r.depositor.History())
|
||||
ec, err := e2etypes.NewEvaluationContext(r.depositor.History(), e2e.TestParams.NumberOfExecutionCreds)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Run assigned evaluators.
|
||||
if err := r.runEvaluators(ec, conns, tickingStartTime); err != nil {
|
||||
return errors.Wrap(err, "one or more evaluators failed")
|
||||
}
|
||||
// Test execution request processing in electra.
|
||||
if r.config.TestDeposits && params.ElectraEnabled() {
|
||||
if r.config.TestDeposits && r.config.TestExecutionRequests && params.ElectraEnabled() {
|
||||
if err := r.comHandler.txGen.Pause(); err != nil {
|
||||
r.t.Error(err)
|
||||
}
|
||||
@@ -516,6 +518,33 @@ func (r *testRunner) defaultEndToEndRun() error {
|
||||
}
|
||||
}
|
||||
|
||||
if r.config.TestExecutionRequests && params.ElectraEnabled() {
|
||||
// Test Consolidation Transactions
|
||||
r.comHandler.txGen.SetTxType(eth1.ConsolidationTx)
|
||||
// Wait For an epoch before running evaluator
|
||||
secondsPerEpoch := time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
|
||||
waitForSync := secondsPerEpoch * time.Second
|
||||
time.Sleep(waitForSync)
|
||||
|
||||
for _, evaluator := range []e2etypes.Evaluator{ev.ValidatorsHaveConsolidated} {
|
||||
t.Run(evaluator.Name, func(t *testing.T) {
|
||||
assert.NoError(t, evaluator.Evaluation(ec, conns...), "Evaluation failed for sync node")
|
||||
})
|
||||
}
|
||||
|
||||
// Test Withdrawal Transactions
|
||||
r.comHandler.txGen.SetTxType(eth1.WithdrawalTx)
|
||||
// Wait For an epoch before running evaluator
|
||||
time.Sleep(waitForSync)
|
||||
|
||||
for _, evaluator := range []e2etypes.Evaluator{ev.ValidatorsHaveWithdrawnViaExecution} {
|
||||
t.Run(evaluator.Name, func(t *testing.T) {
|
||||
assert.NoError(t, evaluator.Evaluation(ec, conns...), "Evaluation failed for sync node")
|
||||
})
|
||||
}
|
||||
r.comHandler.txGen.SetTxType(eth1.RandomTx)
|
||||
}
|
||||
|
||||
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
|
||||
if config.TestSync {
|
||||
if err := r.testBeaconChainSync(ctx, g, conns, tickingStartTime, bootNode.ENR(), eth1Miner.ENR()); err != nil {
|
||||
@@ -596,7 +625,8 @@ func (r *testRunner) scenarioRun() error {
|
||||
require.NoError(t, err)
|
||||
tickingStartTime := helpers.EpochTickerStartTime(genesis)
|
||||
|
||||
ec := e2etypes.NewEvaluationContext(r.depositor.History())
|
||||
ec, err := e2etypes.NewEvaluationContext(r.depositor.History(), e2e.TestParams.NumberOfExecutionCreds)
|
||||
require.NoError(t, err)
|
||||
// Run assigned evaluators.
|
||||
return r.runEvaluators(ec, conns, tickingStartTime)
|
||||
}
|
||||
|
||||
@@ -110,6 +110,26 @@ var ValidatorsHaveWithdrawn = e2etypes.Evaluator{
|
||||
Evaluation: validatorsAreWithdrawn,
|
||||
}
|
||||
|
||||
// ValidatorsHaveConsolidated checks the beacon state for the consolidated validator and ensures it is exited.
|
||||
var ValidatorsHaveConsolidated = e2etypes.Evaluator{
|
||||
Name: "validators_have_consolidated_%d",
|
||||
Policy: func(currentEpoch primitives.Epoch) bool {
|
||||
fEpoch := params.BeaconConfig().ElectraForkEpoch
|
||||
return policies.OnwardsNthEpoch(fEpoch)(currentEpoch)
|
||||
},
|
||||
Evaluation: validatorsHaveBeenConsolidated,
|
||||
}
|
||||
|
||||
// ValidatorsHaveWithdrawnViaExecution checks the beacon state for the compounded validator and makes sure it is withdrawn.
|
||||
var ValidatorsHaveWithdrawnViaExecution = e2etypes.Evaluator{
|
||||
Name: "validators_have_withdrawn_with_execution_%d",
|
||||
Policy: func(currentEpoch primitives.Epoch) bool {
|
||||
fEpoch := params.BeaconConfig().ElectraForkEpoch
|
||||
return policies.OnwardsNthEpoch(fEpoch)(currentEpoch)
|
||||
},
|
||||
Evaluation: validatorsHaveBeenWithdrawnWithExecution,
|
||||
}
|
||||
|
||||
// ValidatorsVoteWithTheMajority verifies whether validator vote for eth1data using the majority algorithm.
|
||||
var ValidatorsVoteWithTheMajority = e2etypes.Evaluator{
|
||||
Name: "validators_vote_with_the_majority_%d",
|
||||
@@ -438,7 +458,7 @@ func proposeVoluntaryExit(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientC
|
||||
// Send an exit for a non-exited validator.
|
||||
for i := 0; i < numOfExits; {
|
||||
randIndex := primitives.ValidatorIndex(rand.Uint64() % params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
if ec.ExitedVals[bytesutil.ToBytes48(privKeys[randIndex].PublicKey().Marshal())] {
|
||||
if ec.ExitedVals[bytesutil.ToBytes48(privKeys[randIndex].PublicKey().Marshal())] || ec.ValidExecutionCredentials[[48]byte(privKeys[randIndex].PublicKey().Marshal())] {
|
||||
continue
|
||||
}
|
||||
if err := sendExit(randIndex); err != nil {
|
||||
@@ -697,3 +717,98 @@ func validatorsAreWithdrawn(ec *e2etypes.EvaluationContext, conns ...*grpc.Clien
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatorsHaveBeenConsolidated(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
|
||||
conn := conns[0]
|
||||
beaconClient := ethpb.NewBeaconChainClient(conn)
|
||||
debugClient := ethpb.NewDebugClient(conn)
|
||||
|
||||
ctx := context.Background()
|
||||
chainHead, err := beaconClient.GetChainHead(ctx, &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get chain head")
|
||||
}
|
||||
stObj, err := debugClient.GetBeaconState(ctx, ðpb.BeaconStateRequest{QueryFilter: ðpb.BeaconStateRequest_Slot{Slot: chainHead.HeadSlot}})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get state object")
|
||||
}
|
||||
versionedMarshaler, err := detect.FromState(stObj.Encoded)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get state marshaler")
|
||||
}
|
||||
st, err := versionedMarshaler.UnmarshalBeaconState(stObj.Encoded)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get state")
|
||||
}
|
||||
|
||||
var compoundingVal int
|
||||
|
||||
for pubkey := range ec.ValidExecutionCredentials {
|
||||
if ec.ExitedVals[pubkey] {
|
||||
continue
|
||||
}
|
||||
valIdx, ok := st.ValidatorIndexByPubkey(pubkey)
|
||||
if !ok {
|
||||
return errors.Errorf("pubkey %#x does not exist in our state", pubkey)
|
||||
}
|
||||
val, err := st.ValidatorAtIndexReadOnly(valIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val.HasCompoundingWithdrawalCredentials() {
|
||||
compoundingVal++
|
||||
continue
|
||||
}
|
||||
if val.ExitEpoch() == params.BeaconConfig().FarFutureEpoch {
|
||||
return errors.Errorf("validator was not exited after consolidation, its exit epoch is %d", val.ExitEpoch())
|
||||
}
|
||||
}
|
||||
if compoundingVal != 1 {
|
||||
return errors.Errorf("no compounding validators observed, wanted 1 but got %d", compoundingVal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatorsHaveBeenWithdrawnWithExecution(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
|
||||
conn := conns[0]
|
||||
beaconClient := ethpb.NewBeaconChainClient(conn)
|
||||
debugClient := ethpb.NewDebugClient(conn)
|
||||
|
||||
ctx := context.Background()
|
||||
chainHead, err := beaconClient.GetChainHead(ctx, &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get chain head")
|
||||
}
|
||||
stObj, err := debugClient.GetBeaconState(ctx, ðpb.BeaconStateRequest{QueryFilter: ðpb.BeaconStateRequest_Slot{Slot: chainHead.HeadSlot}})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get state object")
|
||||
}
|
||||
versionedMarshaler, err := detect.FromState(stObj.Encoded)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get state marshaler")
|
||||
}
|
||||
st, err := versionedMarshaler.UnmarshalBeaconState(stObj.Encoded)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get state")
|
||||
}
|
||||
|
||||
for pubkey := range ec.ValidExecutionCredentials {
|
||||
if ec.ExitedVals[pubkey] {
|
||||
continue
|
||||
}
|
||||
valIdx, ok := st.ValidatorIndexByPubkey(pubkey)
|
||||
if !ok {
|
||||
return errors.Errorf("pubkey %#x does not exist in our state", pubkey)
|
||||
}
|
||||
val, err := st.ValidatorAtIndexReadOnly(valIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val.HasCompoundingWithdrawalCredentials() {
|
||||
if val.ExitEpoch() == params.BeaconConfig().FarFutureEpoch {
|
||||
return errors.Errorf("validator was not exited after withdrawal, its exit epoch is %d", val.ExitEpoch())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ go_library(
|
||||
deps = [
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//runtime/interop:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
@@ -71,6 +72,7 @@ type E2EConfig struct {
|
||||
UseValidatorCrossClient bool
|
||||
UseBeaconRestApi bool
|
||||
UseBuilder bool
|
||||
TestExecutionRequests bool
|
||||
EpochsToRun uint64
|
||||
Seed int64
|
||||
TracingSinkEndpoint string
|
||||
@@ -129,18 +131,28 @@ type DepositBalancer interface {
|
||||
// EvaluationContext allows for additional data to be provided to evaluators that need extra state.
|
||||
type EvaluationContext struct {
|
||||
DepositBalancer
|
||||
ExitedVals map[[48]byte]bool
|
||||
SeenVotes map[primitives.Slot][]byte
|
||||
ExpectedEth1DataVote []byte
|
||||
ExitedVals map[[48]byte]bool
|
||||
SeenVotes map[primitives.Slot][]byte
|
||||
ExpectedEth1DataVote []byte
|
||||
ValidExecutionCredentials map[[48]byte]bool
|
||||
}
|
||||
|
||||
// NewEvaluationContext handles initializing internal datastructures (like maps) provided by the EvaluationContext.
|
||||
func NewEvaluationContext(d DepositBalancer) *EvaluationContext {
|
||||
return &EvaluationContext{
|
||||
DepositBalancer: d,
|
||||
ExitedVals: make(map[[48]byte]bool),
|
||||
SeenVotes: make(map[primitives.Slot][]byte),
|
||||
func NewEvaluationContext(d DepositBalancer, numExecCreds uint64) (*EvaluationContext, error) {
|
||||
_, pkeys, err := interop.DeterministicallyGenerateKeys(0, numExecCreds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
execMap := make(map[[48]byte]bool)
|
||||
for _, k := range pkeys {
|
||||
execMap[[48]byte(k.Marshal())] = true
|
||||
}
|
||||
return &EvaluationContext{
|
||||
DepositBalancer: d,
|
||||
ExitedVals: make(map[[48]byte]bool),
|
||||
SeenVotes: make(map[primitives.Slot][]byte),
|
||||
ValidExecutionCredentials: execMap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ComponentRunner defines an interface via which E2E component's configuration, execution and termination is managed.
|
||||
|
||||
Reference in New Issue
Block a user