diff --git a/simulators/ethereum/engine/client/engine.go b/simulators/ethereum/engine/client/engine.go index 502d53a79f..25b93b8704 100644 --- a/simulators/ethereum/engine/client/engine.go +++ b/simulators/ethereum/engine/client/engine.go @@ -21,6 +21,7 @@ type Eth interface { BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) SendTransaction(ctx context.Context, tx *types.Transaction) error + SendTransactions(ctx context.Context, txs []*types.Transaction) []error StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) @@ -55,6 +56,7 @@ type EngineClient interface { // Local Test Account Management GetNextAccountNonce(testCtx context.Context, account common.Address) (uint64, error) + UpdateNonce(testCtx context.Context, account common.Address, newNonce uint64) error // TTD Methods TerminalTotalDifficulty() *big.Int diff --git a/simulators/ethereum/engine/client/hive_rpc/hive_rpc.go b/simulators/ethereum/engine/client/hive_rpc/hive_rpc.go index a5d98504f0..b2d35a7a75 100644 --- a/simulators/ethereum/engine/client/hive_rpc/hive_rpc.go +++ b/simulators/ethereum/engine/client/hive_rpc/hive_rpc.go @@ -449,6 +449,49 @@ func (ec *HiveRPCEngineClient) GetNextAccountNonce(testCtx context.Context, acco return nonce, nil } +func (ec *HiveRPCEngineClient) UpdateNonce(testCtx context.Context, account common.Address, newNonce uint64) error { + // First get the current head of the client where we will send the tx + ctx, cancel := context.WithTimeout(testCtx, globals.RPCTimeout) + defer cancel() + head, err := ec.HeaderByNumber(ctx, nil) + if err != nil { + return err + } + ec.accTxInfoMap[account] = &AccountTransactionInfo{ + PreviousBlock: head.Hash(), + PreviousNonce: newNonce, + } + return nil +} + +func (ec *HiveRPCEngineClient) SendTransactions(ctx context.Context, txs []*types.Transaction) []error { + reqs := make([]rpc.BatchElem, len(txs)) + hashes := make([]common.Hash, len(txs)) + for i := range reqs { + data, err := txs[i].MarshalBinary() + if err != nil { + return []error{err} + } + reqs[i] = rpc.BatchElem{ + Method: "eth_sendRawTransaction", + Args: []interface{}{hexutil.Encode(data)}, + Result: &hashes[i], + } + } + if err := ec.PrepareDefaultAuthCallToken(); err != nil { + return []error{err} + } + if err := ec.c.BatchCallContext(ctx, reqs); err != nil { + return []error{err} + } + + errs := make([]error, len(txs)) + for i := range reqs { + errs[i] = reqs[i].Error + } + return nil +} + func (ec *HiveRPCEngineClient) PostRunVerifications() error { // There are no post run verifications for RPC clients yet return nil diff --git a/simulators/ethereum/engine/client/node/node.go b/simulators/ethereum/engine/client/node/node.go index 6f46e6fdeb..8b8105b973 100644 --- a/simulators/ethereum/engine/client/node/node.go +++ b/simulators/ethereum/engine/client/node/node.go @@ -803,6 +803,16 @@ func (n *GethNode) SendTransaction(ctx context.Context, tx *types.Transaction) e return n.eth.APIBackend.SendTx(ctx, tx) } +func (n *GethNode) SendTransactions(ctx context.Context, txs []*types.Transaction) []error { + for _, tx := range txs { + err := n.eth.APIBackend.SendTx(ctx, tx) + if err != nil { + return []error{err} + } + } + return nil +} + func (n *GethNode) getStateDB(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) { b, err := n.eth.APIBackend.BlockByNumber(ctx, parseBlockNumber(blockNumber)) if err != nil { @@ -899,6 +909,19 @@ func (n *GethNode) GetNextAccountNonce(testCtx context.Context, account common.A return nonce, nil } +func (n *GethNode) UpdateNonce(testCtx context.Context, account common.Address, newNonce uint64) error { + // First get the current head of the client where we will send the tx + head, err := n.eth.APIBackend.BlockByNumber(testCtx, LatestBlockNumber) + if err != nil { + return err + } + n.accTxInfoMap[account] = &AccountTransactionInfo{ + PreviousBlock: head.Hash(), + PreviousNonce: newNonce, + } + return nil +} + func (n *GethNode) PostRunVerifications() error { // Check that the node did not receive more gossiped blocks than expected if n.config.ExpectedGossipNewBlocksCount != nil { diff --git a/simulators/ethereum/engine/helper/helper.go b/simulators/ethereum/engine/helper/helper.go index a3e90fdfcc..7620751df7 100644 --- a/simulators/ethereum/engine/helper/helper.go +++ b/simulators/ethereum/engine/helper/helper.go @@ -476,7 +476,8 @@ func (tc *BigInitcodeTransactionCreator) MakeTransaction(nonce uint64) (*types.T // already knew the tx (might happen if we produced a re-org where the tx was // unwind back into the txpool) func SentTxAlreadyKnown(err error) bool { - return strings.Contains(err.Error(), "already known") + return strings.Contains(err.Error(), "already known") || strings.Contains(err.Error(), "already in the TxPool") || + strings.Contains(err.Error(), "AlreadyKnown") } func SendNextTransaction(testCtx context.Context, node client.EngineClient, txCreator TransactionCreator) (*types.Transaction, error) { @@ -504,3 +505,29 @@ func SendNextTransaction(testCtx context.Context, node client.EngineClient, txCr } } } + +func SendNextTransactions(testCtx context.Context, node client.EngineClient, txCreator TransactionCreator, txCount uint64) ([]*types.Transaction, error) { + var err error + nonce, err := node.GetNextAccountNonce(testCtx, globals.VaultAccountAddress) + if err != nil { + return nil, err + } + txs := make([]*types.Transaction, txCount) + for i := range txs { + txs[i], err = txCreator.MakeTransaction(nonce) + if err != nil { + return nil, err + } + nonce++ + } + ctx, cancel := context.WithTimeout(testCtx, globals.RPCTimeout) + defer cancel() + errs := node.SendTransactions(ctx, txs) + for _, err := range errs { + if err != nil && !SentTxAlreadyKnown(err) { + return txs, err + } + } + node.UpdateNonce(testCtx, globals.VaultAccountAddress, nonce+txCount) + return txs, nil +} diff --git a/simulators/ethereum/engine/suites/withdrawals/tests.go b/simulators/ethereum/engine/suites/withdrawals/tests.go index 3cb75e65ab..94999790ea 100644 --- a/simulators/ethereum/engine/suites/withdrawals/tests.go +++ b/simulators/ethereum/engine/suites/withdrawals/tests.go @@ -327,6 +327,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 1, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 16, @@ -345,6 +346,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 1, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 16, @@ -363,6 +365,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 1, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 16, @@ -382,6 +385,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 8, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 8, @@ -401,6 +405,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 8, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 8, @@ -420,6 +425,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 8, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 8, @@ -440,6 +446,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 8, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 8, @@ -460,6 +467,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 8, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 8, @@ -481,6 +489,7 @@ var Tests = []test.SpecInterface{ `, SlotsToSafe: big.NewInt(32), SlotsToFinalized: big.NewInt(64), + TimeoutSeconds: 300, }, WithdrawalsForkHeight: 8, // Genesis is Pre-Withdrawals WithdrawalsBlockCount: 8, @@ -1553,25 +1562,25 @@ func (ws *WithdrawalsReorgSpec) Execute(t *test.Env) { }, OnRequestNextPayload: func() { // Send transactions to be included in the payload - for i := uint64(0); i < ws.GetTransactionCountPerPayload(); i++ { - tx, err := helper.SendNextTransaction( - t.TestContext, - t.CLMock.NextBlockProducer, - &helper.BaseTransactionCreator{ - Recipient: &globals.PrevRandaoContractAddr, - Amount: common.Big1, - Payload: nil, - TxType: t.TestTransactionType, - GasLimit: 75000, - }, - ) - if err != nil { - t.Fatalf("FAIL (%s): Error trying to send transaction: %v", t.TestName, err) - } - // Error will be ignored here since the tx could have been already relayed - secondaryEngine.SendTransaction(t.TestContext, tx) + txs, err := helper.SendNextTransactions( + t.TestContext, + t.CLMock.NextBlockProducer, + &helper.BaseTransactionCreator{ + Recipient: &globals.PrevRandaoContractAddr, + Amount: common.Big1, + Payload: nil, + TxType: t.TestTransactionType, + GasLimit: 75000, + }, + ws.GetTransactionCountPerPayload(), + ) + if err != nil { + t.Fatalf("FAIL (%s): Error trying to send transactions: %v", t.TestName, err) } + // Error will be ignored here since the tx could have been already relayed + secondaryEngine.SendTransactions(t.TestContext, txs) + if t.CLMock.CurrentPayloadNumber >= ws.GetSidechainSplitHeight() { // Also request a payload from the sidechain fcU := beacon.ForkchoiceStateV1{