Test Execution Deposit Requests in E2E (#14964)

* Test Deposit Requests

* Remove extra epochs

* Clean up Panic

* Fix Slashing Config

* Fix Slashing Test

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
This commit is contained in:
Nishant Das
2025-02-20 22:54:45 +08:00
committed by GitHub
parent e0e7354708
commit a77234e637
8 changed files with 93 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
### Added
- Added deposit request testing for electra.

View File

@@ -159,6 +159,7 @@ func GethTestnetGenesis(genesisTime uint64, cfg *clparams.BeaconChainConfig) *co
ShanghaiTime: shanghaiTime,
CancunTime: cancunTime,
PragueTime: pragueTime,
DepositContractAddress: common.HexToAddress(cfg.DepositContractAddress),
BlobScheduleConfig: &params.BlobScheduleConfig{
Cancun: &params.BlobConfig{
Target: 3,

View File

@@ -28,6 +28,7 @@ type componentHandler struct {
web3Signer e2etypes.ComponentRunner
bootnode e2etypes.ComponentRunner
eth1Miner e2etypes.ComponentRunner
txGen e2etypes.ComponentRunner
builders e2etypes.MultipleComponentRunners
eth1Proxy e2etypes.MultipleComponentRunners
eth1Nodes e2etypes.MultipleComponentRunners

View File

@@ -176,6 +176,62 @@ func (d *Depositor) SendAndMine(ctx context.Context, offset, nvals int, batch ty
return nil
}
// SendAndMineByBatch uses the deterministic validator generator to generate deposits for `nvals` (number of validators).
// To control which validators should receive deposits, so that we can generate deposits at different stages of e2e,
// the `offset` parameter skips the first N validators in the deterministic list.
// In order to test the requirement that our deposit follower is able to handle multiple partial deposits,
// the `partial` flag specifies that half of the deposits should be broken up into 2 transactions.
// Once the set of deposits has been generated, it submits a transaction for each deposit
// (using 2 transactions for partial deposits) and then uses WaitForBlocks to send these transactions by one batch per block.
// The batch size is determined by the provided batch size provided as an argument.
func (d *Depositor) SendAndMineByBatch(ctx context.Context, offset, nvals, batchSize int, batch types.DepositBatch, partial bool) error {
balance, err := d.Client.BalanceAt(ctx, d.Key.Address, nil)
if err != nil {
return err
}
// This is the "Send" part of the function. Compute deposits for `nvals` validators,
// with half of those deposits being split over 2 transactions if the `partial` flag is true,
// and throwing away any validators before `offset`.
deposits, err := computeDeposits(offset, nvals, partial)
if err != nil {
return err
}
numBatch := len(deposits) / batchSize
log.WithField("numDeposits", len(deposits)).WithField("batchSize", batchSize).WithField("numBatches", numBatch).WithField("balance", balance.String()).WithField("account", d.Key.Address.Hex()).Info("SendAndMineByBatch check")
for i := 0; i < numBatch; i++ {
txo, err := d.txops(ctx)
if err != nil {
return err
}
for _, dd := range deposits[i*batchSize : (i+1)*batchSize] {
if err := d.SendDeposit(dd, txo, batch); err != nil {
return err
}
}
// This is the "AndMine" part of the function. WaitForBlocks will spam transactions to/from the given key
// to advance the EL chain and until the chain has advanced the requested amount.
if err = WaitForBlocks(d.Client, d.Key, 1); err != nil {
return fmt.Errorf("failed to mine blocks %w", err)
}
}
txo, err := d.txops(ctx)
if err != nil {
return err
}
// Send out the last partial batch
for _, dd := range deposits[numBatch*batchSize:] {
if err := d.SendDeposit(dd, txo, batch); err != nil {
return err
}
}
// This is the "AndMine" part of the function. WaitForBlocks will spam transactions to/from the given key
// to advance the EL chain and until the chain has advanced the requested amount.
if err = WaitForBlocks(d.Client, d.Key, 1); err != nil {
return fmt.Errorf("failed to mine blocks %w", err)
}
return nil
}
// SendDeposit sends a single deposit. A record of this deposit will be tracked for the life of the Depositor,
// allowing evaluators to use the deposit history to make assertions about those deposits.
func (d *Depositor) SendDeposit(dep *eth.Deposit, txo *bind.TransactOpts, batch types.DepositBatch) error {

View File

@@ -39,6 +39,13 @@ type TransactionGenerator struct {
seed int64
started chan struct{}
cancel context.CancelFunc
paused bool
}
func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
// Transaction Generator runs under the same underlying process so
// we return an empty process object.
return &os.Process{}
}
func NewTransactionGenerator(keystore string, seed int64) *TransactionGenerator {
@@ -94,6 +101,9 @@ func (t *TransactionGenerator) Start(ctx context.Context) error {
case <-ctx.Done():
return nil
case <-ticker.C:
if t.paused {
continue
}
backend := ethclient.NewClient(client)
err = SendTransaction(client, mineKey.PrivateKey, f, gasPrice, mineKey.Address.String(), txCount, backend, false)
if err != nil {
@@ -211,11 +221,13 @@ func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, f *filler.Filler
// Pause pauses the component and its underlying process.
func (t *TransactionGenerator) Pause() error {
t.paused = true
return nil
}
// Resume resumes the component and its underlying process.
func (t *TransactionGenerator) Resume() error {
t.paused = false
return nil
}

View File

@@ -225,6 +225,7 @@ func (r *testRunner) testDepositsAndTx(ctx context.Context, g *errgroup.Group,
func (r *testRunner) testTxGeneration(ctx context.Context, g *errgroup.Group, keystorePath string, requiredNodes []e2etypes.ComponentRunner) {
txGenerator := eth1.NewTransactionGenerator(keystorePath, r.config.Seed)
r.comHandler.txGen = txGenerator
g.Go(func() error {
if err := helpers.ComponentsStarted(ctx, requiredNodes); err != nil {
return fmt.Errorf("transaction generator requires eth1 nodes to be run: %w", err)
@@ -501,6 +502,19 @@ func (r *testRunner) defaultEndToEndRun() error {
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 err := r.comHandler.txGen.Pause(); err != nil {
r.t.Error(err)
}
err = r.depositor.SendAndMineByBatch(ctx, int(params.BeaconConfig().MinGenesisActiveValidatorCount)+int(e2e.DepositCount), int(e2e.PostElectraDepositCount), int(params.BeaconConfig().MaxDepositRequestsPerPayload), e2etypes.PostElectraDepositBatch, false)
if err != nil {
r.t.Error(err)
}
if err := r.comHandler.txGen.Resume(); err != nil {
r.t.Error(err)
}
}
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
if config.TestSync {

View File

@@ -120,6 +120,9 @@ var StandardLighthouseNodeCount = 2
// DepositCount is the number of deposits the E2E runner should make to evaluate post-genesis deposit processing.
var DepositCount = uint64(64)
// PostElectraDepositCount is the number of deposits the E2E runner should make to evaluate post-electra deposit processing.
var PostElectraDepositCount = uint64(32)
// PregenesisExecCreds is the number of withdrawal credentials of genesis validators which use an execution address.
var PregenesisExecCreds = uint64(8)

View File

@@ -116,6 +116,9 @@ const (
// PostGenesisDepositBatch deposits are sent to test that deposits appear in blocks as expected
// and validators become active.
PostGenesisDepositBatch
// PostElectraDepositBatch deposits are sent to test that deposits sent after electra has been transitioned
// work as expected.
PostElectraDepositBatch
)
// DepositBalancer represents a type that can sum, by validator, all deposits made in E2E prior to the function call.