mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -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.GenesisDelay = 10 // 10 seconds so E2E has enough time to process deposits and get started.
|
||||||
e2eConfig.ChurnLimitQuotient = 65536
|
e2eConfig.ChurnLimitQuotient = 65536
|
||||||
e2eConfig.MaxValidatorsPerWithdrawalsSweep = 128
|
e2eConfig.MaxValidatorsPerWithdrawalsSweep = 128
|
||||||
|
e2eConfig.MinPerEpochChurnLimitElectra = 256000000000
|
||||||
|
|
||||||
// Time parameters.
|
// Time parameters.
|
||||||
e2eConfig.SecondsPerSlot = 10
|
e2eConfig.SecondsPerSlot = 10
|
||||||
@@ -73,6 +74,7 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
|
|||||||
e2eConfig.MinGenesisActiveValidatorCount = 256
|
e2eConfig.MinGenesisActiveValidatorCount = 256
|
||||||
e2eConfig.GenesisDelay = 25 // 25 seconds so E2E has enough time to process deposits and get started.
|
e2eConfig.GenesisDelay = 25 // 25 seconds so E2E has enough time to process deposits and get started.
|
||||||
e2eConfig.ChurnLimitQuotient = 65536
|
e2eConfig.ChurnLimitQuotient = 65536
|
||||||
|
e2eConfig.MinPerEpochChurnLimitElectra = 256000000000
|
||||||
|
|
||||||
// Time parameters.
|
// Time parameters.
|
||||||
e2eConfig.SecondsPerSlot = 6
|
e2eConfig.SecondsPerSlot = 6
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/prysmaticlabs/prysm/v5/async"
|
"github.com/prysmaticlabs/prysm/v5/async"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
||||||
@@ -171,7 +172,7 @@ func createDepositData(privKey bls.SecretKey, pubKey bls.PublicKey, withExecCred
|
|||||||
if withExecCreds {
|
if withExecCreds {
|
||||||
newCredentials := make([]byte, 12)
|
newCredentials := make([]byte, 12)
|
||||||
newCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
newCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
||||||
execAddr := bytesutil.ToBytes20(pubKey.Marshal())
|
execAddr := bytesutil.ToBytes20(hexutil.MustDecode(executionAddress))
|
||||||
depositMessage.WithdrawalCredentials = append(newCredentials, execAddr[:]...)
|
depositMessage.WithdrawalCredentials = append(newCredentials, execAddr[:]...)
|
||||||
}
|
}
|
||||||
sr, err := depositMessage.HashTreeRoot()
|
sr, err := depositMessage.HashTreeRoot()
|
||||||
|
|||||||
@@ -184,8 +184,10 @@ func GethTestnetGenesis(genesisTime uint64, cfg *clparams.BeaconChainConfig) *co
|
|||||||
Mixhash: common.HexToHash(defaultMixhash),
|
Mixhash: common.HexToHash(defaultMixhash),
|
||||||
Coinbase: common.HexToAddress(defaultCoinbase),
|
Coinbase: common.HexToAddress(defaultCoinbase),
|
||||||
Alloc: types.GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
da.Address: da.Account,
|
da.Address: da.Account,
|
||||||
ma.Address: ma.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),
|
ParentHash: common.HexToHash(defaultParenthash),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import (
|
|||||||
|
|
||||||
var errUnsupportedVersion = errors.New("schema version not supported by PremineGenesisConfig")
|
var errUnsupportedVersion = errors.New("schema version not supported by PremineGenesisConfig")
|
||||||
|
|
||||||
|
const executionAddress = "0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766"
|
||||||
|
|
||||||
type PremineGenesisConfig struct {
|
type PremineGenesisConfig struct {
|
||||||
GenesisTime uint64
|
GenesisTime uint64
|
||||||
NVals uint64
|
NVals uint64
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ type componentHandler struct {
|
|||||||
web3Signer e2etypes.ComponentRunner
|
web3Signer e2etypes.ComponentRunner
|
||||||
bootnode e2etypes.ComponentRunner
|
bootnode e2etypes.ComponentRunner
|
||||||
eth1Miner e2etypes.ComponentRunner
|
eth1Miner e2etypes.ComponentRunner
|
||||||
txGen e2etypes.ComponentRunner
|
txGen *eth1.TransactionGenerator
|
||||||
builders e2etypes.MultipleComponentRunners
|
builders e2etypes.MultipleComponentRunners
|
||||||
eth1Proxy e2etypes.MultipleComponentRunners
|
eth1Proxy e2etypes.MultipleComponentRunners
|
||||||
eth1Nodes 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//accounts/keystore:go_default_library",
|
||||||
"@com_github_ethereum_go_ethereum//common: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//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//crypto/kzg4844:go_default_library",
|
||||||
"@com_github_ethereum_go_ethereum//ethclient: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//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_ethereum_go_ethereum//rpc:go_default_library",
|
||||||
"@com_github_holiman_uint256//:go_default_library",
|
"@com_github_holiman_uint256//:go_default_library",
|
||||||
"@com_github_mariusvanderwijden_fuzzyvm//filler: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/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/ethclient/gethclient"
|
"github.com/ethereum/go-ethereum/ethclient/gethclient"
|
||||||
|
gethparams "github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
"github.com/prysmaticlabs/prysm/v5/crypto/rand"
|
"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"
|
e2e "github.com/prysmaticlabs/prysm/v5/testing/endtoend/params"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -32,14 +36,23 @@ import (
|
|||||||
|
|
||||||
const txCount = 20
|
const txCount = 20
|
||||||
|
|
||||||
|
type txType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RandomTx txType = iota
|
||||||
|
ConsolidationTx
|
||||||
|
WithdrawalTx
|
||||||
|
)
|
||||||
|
|
||||||
var fundedAccount *keystore.Key
|
var fundedAccount *keystore.Key
|
||||||
|
|
||||||
type TransactionGenerator struct {
|
type TransactionGenerator struct {
|
||||||
keystore string
|
keystore string
|
||||||
seed int64
|
seed int64
|
||||||
started chan struct{}
|
started chan struct{}
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
paused bool
|
paused bool
|
||||||
|
txGenType txType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
|
func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
|
||||||
@@ -49,7 +62,7 @@ func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewTransactionGenerator(keystore string, seed int64) *TransactionGenerator {
|
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 {
|
func (t *TransactionGenerator) Start(ctx context.Context) error {
|
||||||
@@ -105,10 +118,26 @@ func (t *TransactionGenerator) Start(ctx context.Context) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
backend := ethclient.NewClient(client)
|
backend := ethclient.NewClient(client)
|
||||||
err = SendTransaction(client, mineKey.PrivateKey, f, gasPrice, mineKey.Address.String(), txCount, backend, false)
|
switch t.txGenType {
|
||||||
if err != nil {
|
case ConsolidationTx:
|
||||||
return err
|
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()
|
backend.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,6 +248,195 @@ func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, f *filler.Filler
|
|||||||
return nil
|
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.
|
// Pause pauses the component and its underlying process.
|
||||||
func (t *TransactionGenerator) Pause() error {
|
func (t *TransactionGenerator) Pause() error {
|
||||||
t.paused = true
|
t.paused = true
|
||||||
|
|||||||
@@ -73,17 +73,18 @@ func e2eMinimal(t *testing.T, cfg *params.BeaconChainConfig, cfgo ...types.E2ECo
|
|||||||
"--enable-tracing",
|
"--enable-tracing",
|
||||||
"--trace-sample-fraction=1.0",
|
"--trace-sample-fraction=1.0",
|
||||||
},
|
},
|
||||||
ValidatorFlags: []string{},
|
ValidatorFlags: []string{},
|
||||||
EpochsToRun: uint64(epochsToRun),
|
EpochsToRun: uint64(epochsToRun),
|
||||||
TestSync: true,
|
TestSync: true,
|
||||||
TestFeature: true,
|
TestFeature: true,
|
||||||
TestDeposits: true,
|
TestDeposits: true,
|
||||||
UsePrysmShValidator: false,
|
UsePrysmShValidator: false,
|
||||||
UsePprof: true,
|
UsePprof: true,
|
||||||
TracingSinkEndpoint: tracingEndpoint,
|
TestExecutionRequests: true,
|
||||||
Evaluators: evals,
|
TracingSinkEndpoint: tracingEndpoint,
|
||||||
EvalInterceptor: defaultInterceptor,
|
Evaluators: evals,
|
||||||
Seed: int64(seed),
|
EvalInterceptor: defaultInterceptor,
|
||||||
|
Seed: int64(seed),
|
||||||
}
|
}
|
||||||
for _, o := range cfgo {
|
for _, o := range cfgo {
|
||||||
o(testConfig)
|
o(testConfig)
|
||||||
@@ -149,18 +150,19 @@ func e2eMainnet(t *testing.T, usePrysmSh, useMultiClient bool, cfg *params.Beaco
|
|||||||
"--enable-tracing",
|
"--enable-tracing",
|
||||||
"--trace-sample-fraction=1.0",
|
"--trace-sample-fraction=1.0",
|
||||||
},
|
},
|
||||||
ValidatorFlags: []string{},
|
ValidatorFlags: []string{},
|
||||||
EpochsToRun: uint64(epochsToRun),
|
EpochsToRun: uint64(epochsToRun),
|
||||||
TestSync: true,
|
TestSync: true,
|
||||||
TestFeature: true,
|
TestFeature: true,
|
||||||
TestDeposits: true,
|
TestDeposits: true,
|
||||||
UseFixedPeerIDs: true,
|
UseFixedPeerIDs: true,
|
||||||
UsePrysmShValidator: usePrysmSh,
|
UsePrysmShValidator: usePrysmSh,
|
||||||
UsePprof: true,
|
TestExecutionRequests: true,
|
||||||
TracingSinkEndpoint: tracingEndpoint,
|
UsePprof: true,
|
||||||
Evaluators: evals,
|
TracingSinkEndpoint: tracingEndpoint,
|
||||||
EvalInterceptor: defaultInterceptor,
|
Evaluators: evals,
|
||||||
Seed: int64(seed),
|
EvalInterceptor: defaultInterceptor,
|
||||||
|
Seed: int64(seed),
|
||||||
}
|
}
|
||||||
for _, o := range cfgo {
|
for _, o := range cfgo {
|
||||||
o(testConfig)
|
o(testConfig)
|
||||||
|
|||||||
@@ -497,13 +497,15 @@ func (r *testRunner) defaultEndToEndRun() error {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tickingStartTime := helpers.EpochTickerStartTime(genesis)
|
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.
|
// Run assigned evaluators.
|
||||||
if err := r.runEvaluators(ec, conns, tickingStartTime); err != nil {
|
if err := r.runEvaluators(ec, conns, tickingStartTime); err != nil {
|
||||||
return errors.Wrap(err, "one or more evaluators failed")
|
return errors.Wrap(err, "one or more evaluators failed")
|
||||||
}
|
}
|
||||||
// Test execution request processing in electra.
|
// 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 {
|
if err := r.comHandler.txGen.Pause(); err != nil {
|
||||||
r.t.Error(err)
|
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
|
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
|
||||||
if config.TestSync {
|
if config.TestSync {
|
||||||
if err := r.testBeaconChainSync(ctx, g, conns, tickingStartTime, bootNode.ENR(), eth1Miner.ENR()); err != nil {
|
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)
|
require.NoError(t, err)
|
||||||
tickingStartTime := helpers.EpochTickerStartTime(genesis)
|
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.
|
// Run assigned evaluators.
|
||||||
return r.runEvaluators(ec, conns, tickingStartTime)
|
return r.runEvaluators(ec, conns, tickingStartTime)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,26 @@ var ValidatorsHaveWithdrawn = e2etypes.Evaluator{
|
|||||||
Evaluation: validatorsAreWithdrawn,
|
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.
|
// ValidatorsVoteWithTheMajority verifies whether validator vote for eth1data using the majority algorithm.
|
||||||
var ValidatorsVoteWithTheMajority = e2etypes.Evaluator{
|
var ValidatorsVoteWithTheMajority = e2etypes.Evaluator{
|
||||||
Name: "validators_vote_with_the_majority_%d",
|
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.
|
// Send an exit for a non-exited validator.
|
||||||
for i := 0; i < numOfExits; {
|
for i := 0; i < numOfExits; {
|
||||||
randIndex := primitives.ValidatorIndex(rand.Uint64() % params.BeaconConfig().MinGenesisActiveValidatorCount)
|
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
|
continue
|
||||||
}
|
}
|
||||||
if err := sendExit(randIndex); err != nil {
|
if err := sendExit(randIndex); err != nil {
|
||||||
@@ -697,3 +717,98 @@ func validatorsAreWithdrawn(ec *e2etypes.EvaluationContext, conns ...*grpc.Clien
|
|||||||
}
|
}
|
||||||
return nil
|
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 = [
|
deps = [
|
||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
|
"//runtime/interop:go_default_library",
|
||||||
"//runtime/version:go_default_library",
|
"//runtime/version:go_default_library",
|
||||||
"@org_golang_google_grpc//: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/config/params"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/runtime/interop"
|
||||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
@@ -71,6 +72,7 @@ type E2EConfig struct {
|
|||||||
UseValidatorCrossClient bool
|
UseValidatorCrossClient bool
|
||||||
UseBeaconRestApi bool
|
UseBeaconRestApi bool
|
||||||
UseBuilder bool
|
UseBuilder bool
|
||||||
|
TestExecutionRequests bool
|
||||||
EpochsToRun uint64
|
EpochsToRun uint64
|
||||||
Seed int64
|
Seed int64
|
||||||
TracingSinkEndpoint string
|
TracingSinkEndpoint string
|
||||||
@@ -129,18 +131,28 @@ type DepositBalancer interface {
|
|||||||
// EvaluationContext allows for additional data to be provided to evaluators that need extra state.
|
// EvaluationContext allows for additional data to be provided to evaluators that need extra state.
|
||||||
type EvaluationContext struct {
|
type EvaluationContext struct {
|
||||||
DepositBalancer
|
DepositBalancer
|
||||||
ExitedVals map[[48]byte]bool
|
ExitedVals map[[48]byte]bool
|
||||||
SeenVotes map[primitives.Slot][]byte
|
SeenVotes map[primitives.Slot][]byte
|
||||||
ExpectedEth1DataVote []byte
|
ExpectedEth1DataVote []byte
|
||||||
|
ValidExecutionCredentials map[[48]byte]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvaluationContext handles initializing internal datastructures (like maps) provided by the EvaluationContext.
|
// NewEvaluationContext handles initializing internal datastructures (like maps) provided by the EvaluationContext.
|
||||||
func NewEvaluationContext(d DepositBalancer) *EvaluationContext {
|
func NewEvaluationContext(d DepositBalancer, numExecCreds uint64) (*EvaluationContext, error) {
|
||||||
return &EvaluationContext{
|
_, pkeys, err := interop.DeterministicallyGenerateKeys(0, numExecCreds)
|
||||||
DepositBalancer: d,
|
if err != nil {
|
||||||
ExitedVals: make(map[[48]byte]bool),
|
return nil, err
|
||||||
SeenVotes: make(map[primitives.Slot][]byte),
|
|
||||||
}
|
}
|
||||||
|
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.
|
// ComponentRunner defines an interface via which E2E component's configuration, execution and termination is managed.
|
||||||
|
|||||||
Reference in New Issue
Block a user