From 1595b6ec1700e4447389e1ed7832f79c549438da Mon Sep 17 00:00:00 2001 From: Mikhail Wall Date: Tue, 2 Aug 2022 15:15:07 +0400 Subject: [PATCH] fixing close empty batch and failure to send tx to the ethereum (#971) * fixing close empty batch and failure to send tx to the ethereum * fixed config_test.go * WIP * Sequencer stability update * Imporve revert reason * Single MaxCumulativeGas config * Fix test int64 => uint64 Co-authored-by: Arnau Bennassar --- Makefile | 5 +- cmd/run.go | 2 +- config/config.debug.toml | 10 ++- config/config.local.toml | 10 ++- config/config_test.go | 20 +++--- config/default.go | 18 +++--- config/network.go | 7 --- config/network_test.go | 20 +++--- etherman/etherman.go | 20 +++--- ethtxmanager/ethtxmanager.go | 115 ++++++++--------------------------- ethtxmanager/interfaces.go | 2 + sequencer/config.go | 4 +- sequencer/sequencer.go | 68 ++++++++++++++------- state/state.go | 1 + test/e2e/uniswap_test.go | 2 +- test/operations/wait.go | 64 ++++++++++++++----- 16 files changed, 183 insertions(+), 185 deletions(-) diff --git a/Makefile b/Makefile index e5933c2cd3..8c25c6a1d5 100644 --- a/Makefile +++ b/Makefile @@ -195,12 +195,11 @@ stop-explorer-db: ## Stops the explorer database $(STOPEXPLORERDB) .PHONY: run -run: compile-scs ## Runs all the services +run: ## Runs all the services $(RUNDB) $(RUNL1NETWORK) - sleep 5 $(RUNZKPROVER) - sleep 2 + sleep 5 $(RUNSEQUENCER) $(RUNAGGREGATOR) $(RUNJSONRPC) diff --git a/cmd/run.go b/cmd/run.go index f662ec0061..0afa614b71 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -291,7 +291,7 @@ func newState(ctx context.Context, c *config.Config, sqlDB *pgxpool.Pool) *state stateTree := merkletree.NewStateTree(stateDBClient) stateCfg := state.Config{ - MaxCumulativeGasUsed: c.NetworkConfig.MaxCumulativeGasUsed, + MaxCumulativeGasUsed: c.Sequencer.MaxCumulativeGasUsed, } st := state.NewState(stateCfg, stateDb, executorClient, stateTree) diff --git a/config/config.debug.toml b/config/config.debug.toml index e8a7f55fb1..92231bb22e 100644 --- a/config/config.debug.toml +++ b/config/config.debug.toml @@ -32,11 +32,19 @@ TrustedSequencerURI = "" [Sequencer] WaitPeriodPoolIsEmpty = "15s" -LastBatchVirtualizationTimeMaxWaitPeriod = "15s" +LastBatchVirtualizationTimeMaxWaitPeriod = "300s" WaitBlocksToUpdateGER = 10 LastTimeBatchMaxWaitPeriod = "15s" BlocksAmountForTxsToBeDeleted = 100 FrequencyToCheckTxsForDelete = "12h" +MaxCumulativeGasUsed = 30000000 +MaxKeccakHashes = 468 +MaxPoseidonHashes = 279620 +MaxPoseidonPaddings = 149796 +MaxMemAligns = 262144 +MaxArithmetics = 262144 +MaxBinaries = 262144 +MaxSteps = 8388608 [Sequencer.ProfitabilityChecker] SendBatchesEvenWhenNotProfitable = "true" diff --git a/config/config.local.toml b/config/config.local.toml index 35be95f12c..d1e7aaf22b 100644 --- a/config/config.local.toml +++ b/config/config.local.toml @@ -32,11 +32,19 @@ TrustedSequencerURI = "" [Sequencer] WaitPeriodPoolIsEmpty = "15s" -LastBatchVirtualizationTimeMaxWaitPeriod = "15s" +LastBatchVirtualizationTimeMaxWaitPeriod = "300s" WaitBlocksToUpdateGER = 10 LastTimeBatchMaxWaitPeriod = "15s" BlocksAmountForTxsToBeDeleted = 100 FrequencyToCheckTxsForDelete = "12h" +MaxCumulativeGasUsed = 30000000 +MaxKeccakHashes = 468 +MaxPoseidonHashes = 279620 +MaxPoseidonPaddings = 149796 +MaxMemAligns = 262144 +MaxArithmetics = 262144 +MaxBinaries = 262144 +MaxSteps = 8388608 [Sequencer.ProfitabilityChecker] SendBatchesEvenWhenNotProfitable = "true" diff --git a/config/config_test.go b/config/config_test.go index a666db33eb..8277b07633 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -38,7 +38,7 @@ func Test_Defaults(t *testing.T) { }, { path: "Sequencer.LastBatchVirtualizationTimeMaxWaitPeriod", - expectedValue: types.NewDuration(15 * time.Second), + expectedValue: types.NewDuration(300 * time.Second), }, { path: "Sequencer.WaitBlocksToUpdateGER", @@ -61,36 +61,36 @@ func Test_Defaults(t *testing.T) { expectedValue: true, }, { - path: "Sequencer.MaxGasUsed", - expectedValue: int64(100000), + path: "Sequencer.MaxCumulativeGasUsed", + expectedValue: uint64(30000000), }, { path: "Sequencer.MaxKeccakHashes", - expectedValue: int32(100), + expectedValue: int32(468), }, { path: "Sequencer.MaxPoseidonHashes", - expectedValue: int32(100), + expectedValue: int32(279620), }, { path: "Sequencer.MaxPoseidonPaddings", - expectedValue: int32(100), + expectedValue: int32(149796), }, { path: "Sequencer.MaxMemAligns", - expectedValue: int32(100), + expectedValue: int32(262144), }, { path: "Sequencer.MaxArithmetics", - expectedValue: int32(100), + expectedValue: int32(262144), }, { path: "Sequencer.MaxBinaries", - expectedValue: int32(100), + expectedValue: int32(262144), }, { path: "Sequencer.MaxSteps", - expectedValue: int32(100), + expectedValue: int32(8388608), }, { path: "EthTxManager.MaxSendBatchTxRetries", diff --git a/config/default.go b/config/default.go index 837dd0eb6c..6389064d0e 100644 --- a/config/default.go +++ b/config/default.go @@ -42,19 +42,19 @@ TrustedSequencerURI = "" [Sequencer] WaitPeriodPoolIsEmpty = "15s" -LastBatchVirtualizationTimeMaxWaitPeriod = "15s" +LastBatchVirtualizationTimeMaxWaitPeriod = "300s" WaitBlocksToUpdateGER = 10 LastTimeBatchMaxWaitPeriod = "15s" BlocksAmountForTxsToBeDeleted = 100 FrequencyToCheckTxsForDelete = "12h" -MaxGasUsed = 100000 -MaxKeccakHashes = 100 -MaxPoseidonHashes = 100 -MaxPoseidonPaddings = 100 -MaxMemAligns = 100 -MaxArithmetics = 100 -MaxBinaries = 100 -MaxSteps = 100 +MaxCumulativeGasUsed = 30000000 +MaxKeccakHashes = 468 +MaxPoseidonHashes = 279620 +MaxPoseidonPaddings = 149796 +MaxMemAligns = 262144 +MaxArithmetics = 262144 +MaxBinaries = 262144 +MaxSteps = 8388608 [Sequencer.ProfitabilityChecker] SendBatchesEvenWhenNotProfitable = "true" diff --git a/config/network.go b/config/network.go index 524a5cd0e5..575c0fbd2b 100644 --- a/config/network.go +++ b/config/network.go @@ -29,7 +29,6 @@ type NetworkConfig struct { OldStateRootPosition uint64 ChainID uint64 Genesis Genesis - MaxCumulativeGasUsed uint64 } // Genesis is part of NetworkConfig @@ -52,7 +51,6 @@ type networkConfigFromJSON struct { OldStateRootPosition uint64 `json:"oldStateRootPosition"` ChainID uint64 `json:"chainID"` Genesis []genesisAccountFromJSON `json:"genesis"` - MaxCumulativeGasUsed uint64 `json:"maxCumulativeGasUsed"` } type genesisAccountFromJSON struct { @@ -92,7 +90,6 @@ var ( common.HexToAddress("0xb1D0Dc8E2Ce3a93EB2b32f4C7c3fD9dDAf1211FB"): big.NewInt(2000), }, }, - MaxCumulativeGasUsed: 30000000, } testnetConfig = NetworkConfig{ Arity: 4, @@ -112,7 +109,6 @@ var ( common.HexToAddress("0xb1D0Dc8E2Ce3a93EB2b32f4C7c3fD9dDAf1211FB"): big.NewInt(2000), }, }, - MaxCumulativeGasUsed: 30000000, } internalTestnetConfig = NetworkConfig{ Arity: 4, @@ -181,7 +177,6 @@ var ( common.HexToAddress("0x61ba0248b0986c2480181c6e76b6adeeaa962483"): bigIntFromBase10String("1"), }, }, - MaxCumulativeGasUsed: 30000000, } localConfig = NetworkConfig{ Arity: 4, @@ -251,7 +246,6 @@ var ( common.HexToAddress("0x61ba0248b0986c2480181c6e76b6adeeaa962483"): bigIntFromBase10String("1"), }, }, - MaxCumulativeGasUsed: 30000000, } networkConfigByName = map[string]NetworkConfig{ @@ -343,7 +337,6 @@ func loadCustomNetworkConfig(ctx *cli.Context) (NetworkConfig, error) { cfg.LocalExitRootStoragePosition = cfgJSON.LocalExitRootStoragePosition cfg.OldStateRootPosition = cfgJSON.OldStateRootPosition cfg.ChainID = cfgJSON.ChainID - cfg.MaxCumulativeGasUsed = cfgJSON.MaxCumulativeGasUsed if len(cfgJSON.Genesis) == 0 { return cfg, nil diff --git a/config/network_test.go b/config/network_test.go index f1a8a58416..b2aaff8fba 100644 --- a/config/network_test.go +++ b/config/network_test.go @@ -111,7 +111,6 @@ func TestLoadCustomNetworkConfig(t *testing.T) { common.HexToAddress("0x61ba0248b0986c2480181c6e76b6adeeaa962483"): bigIntFromBase10String("1"), }, }, - MaxCumulativeGasUsed: 300000, }, }, { @@ -163,7 +162,6 @@ func TestLoadCustomNetworkConfig(t *testing.T) { Storage: map[common.Address]map[*big.Int]*big.Int{}, Nonces: map[common.Address]*big.Int{}, }, - MaxCumulativeGasUsed: 123456, }, }, { @@ -272,18 +270,16 @@ func TestMergeNetworkConfig(t *testing.T) { MaticAddr: common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"), }, inputBaseConfig: NetworkConfig{ - PoEAddr: common.HexToAddress("0xb1Fe4a65D3392df68F96daC8eB4df56B2411afBf"), - Arity: 4, - ChainID: 5, - MaxCumulativeGasUsed: 300, + PoEAddr: common.HexToAddress("0xb1Fe4a65D3392df68F96daC8eB4df56B2411afBf"), + Arity: 4, + ChainID: 5, }, expectedOutputConfig: NetworkConfig{ - Arity: 4, - ChainID: 5, - MaxCumulativeGasUsed: 300, - GenBlockNumber: 300, - PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), - MaticAddr: common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"), + Arity: 4, + ChainID: 5, + GenBlockNumber: 300, + PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), + MaticAddr: common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"), }, }, { diff --git a/etherman/etherman.go b/etherman/etherman.go index b8c1eb56c6..5e5ce56178 100644 --- a/etherman/etherman.go +++ b/etherman/etherman.go @@ -16,6 +16,7 @@ import ( "github.com/0xPolygonHermez/zkevm-node/log" "github.com/0xPolygonHermez/zkevm-node/proverclient/pb" "github.com/0xPolygonHermez/zkevm-node/state" + "github.com/0xPolygonHermez/zkevm-node/test/operations" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -57,6 +58,7 @@ type ethClienter interface { ethereum.ChainReader ethereum.LogFilterer ethereum.TransactionReader + ethereum.ContractCaller } // Client is a simple implementation of EtherMan. @@ -190,6 +192,11 @@ func (etherMan *Client) updateGlobalExitRootEvent(ctx context.Context, vLog type return nil } +// WaitTxToBeMined waits for an L1 tx to be mined. It will return error if the tx is reverted or timeout is exceeded +func (etherMan *Client) WaitTxToBeMined(hash common.Hash, timeout time.Duration) error { + return operations.WaitTxToBeMined(etherMan.EtherClient, hash, timeout) +} + // EstimateGasSequenceBatches estimates gas for sending batches func (etherMan *Client) EstimateGasSequenceBatches(sequences []ethmanTypes.Sequence) (uint64, error) { noSendOpts := *etherMan.auth @@ -198,7 +205,7 @@ func (etherMan *Client) EstimateGasSequenceBatches(sequences []ethmanTypes.Seque if err != nil { return 0, err } - return tx.Cost().Uint64(), nil + return tx.Gas(), nil } // SequenceBatches send sequences of batches to the ethereum @@ -224,14 +231,7 @@ func (etherMan *Client) sequenceBatches(opts *bind.TransactOpts, sequences []eth batches = append(batches, batch) } - - tx, err := etherMan.PoE.SequenceBatches(opts, batches) - - if err != nil { - return nil, err - } - - return tx, nil + return etherMan.PoE.SequenceBatches(opts, batches) } // EstimateGasForVerifyBatch estimates gas for verify batch smart contract call @@ -242,7 +242,7 @@ func (etherMan *Client) EstimateGasForVerifyBatch(batchNumber uint64, resGetProo if err != nil { return 0, err } - return tx.Cost().Uint64(), nil + return tx.Gas(), nil } // VerifyBatch send verifyBatch request to the ethereum diff --git a/ethtxmanager/ethtxmanager.go b/ethtxmanager/ethtxmanager.go index 12e1044791..8d880d20d3 100644 --- a/ethtxmanager/ethtxmanager.go +++ b/ethtxmanager/ethtxmanager.go @@ -25,16 +25,9 @@ const ( type Client struct { cfg Config - ethMan etherman - sequenceBatchesTxsChan chan sequenceBatchesTx - sequencesToSendChan chan []ethmanTypes.Sequence - verifyBatchTxsChan chan verifyBatchTx -} - -type sequenceBatchesTx struct { - sequences []ethmanTypes.Sequence - hash common.Hash - gasLimit uint64 + ethMan etherman + sequencesToSendChan chan []ethmanTypes.Sequence + verifyBatchTxsChan chan verifyBatchTx } type verifyBatchTx struct { @@ -46,15 +39,13 @@ type verifyBatchTx struct { // New creates new eth tx manager func New(cfg Config, ethMan etherman) *Client { - sequenceBatchesTxsChan := make(chan sequenceBatchesTx, sentEthTxsChanLen) verifyBatchTxsChan := make(chan verifyBatchTx, sentEthTxsChanLen) sequencesToSendChan := make(chan []ethmanTypes.Sequence, sentEthTxsChanLen) return &Client{ - cfg: cfg, - sequenceBatchesTxsChan: sequenceBatchesTxsChan, - sequencesToSendChan: sequencesToSendChan, - verifyBatchTxsChan: verifyBatchTxsChan, - ethMan: ethMan, + cfg: cfg, + sequencesToSendChan: sequencesToSendChan, + verifyBatchTxsChan: verifyBatchTxsChan, + ethMan: ethMan, } } @@ -63,14 +54,28 @@ func (c *Client) TrackSequenceBatchesSending(ctx context.Context) { for { select { case sequences := <-c.sequencesToSendChan: - err := c.sequenceBatches(sequences) var attempts uint32 + log.Info("sending sequence to L1") + tx, err := c.ethMan.SequenceBatches(sequences, 0) for err != nil && attempts < c.cfg.MaxSendBatchTxRetries { - log.Errorf("failed to sequence batches, trying once again, retry #%d, err: %v", attempts, err) + log.Errorf("failed to sequence batches, trying once again, retry #%d, gasLimit: %d, err: %v", + attempts, 0, err) time.Sleep(c.cfg.FrequencyForResendingFailedSendBatches.Duration) attempts++ - err = c.sequenceBatches(sequences) + tx, err = c.ethMan.SequenceBatches(sequences, 0) + } + if err != nil { + log.Fatalf("failed to sequence batches, maximum attempts exceeded, gasLimit: %d, err: %v", + 0, err) } + // Wait for tx to be mined + log.Infof("waiting for sequence to be mined. Tx hash: %s", tx.Hash()) + err = c.ethMan.WaitTxToBeMined(tx.Hash(), time.Minute*2) //nolint:gomnd + if err != nil { + log.Fatalf("tx %s failed, err: %v", tx.Hash(), err) + } + log.Infof("sequence sent to L1 successfully. Tx hash: %s", tx.Hash()) + // Check if success case <-ctx.Done(): return } @@ -82,26 +87,6 @@ func (c *Client) SequenceBatches(sequences []ethmanTypes.Sequence) { c.sequencesToSendChan <- sequences } -// SequenceBatches send SequenceBatches request to ethereum -func (c *Client) sequenceBatches(sequences []ethmanTypes.Sequence) error { - gas, err := c.ethMan.EstimateGasSequenceBatches(sequences) - if err != nil { - return fmt.Errorf("failed to estimate gas for sending sequences batches, err: %v", err) - } - - gasLimit := uint64(float64(gas) * gasLimitIncrease) - tx, err := c.ethMan.SequenceBatches(sequences, gasLimit) - if err != nil { - return err - } - c.sequenceBatchesTxsChan <- sequenceBatchesTx{ - sequences: sequences, - hash: tx.Hash(), - gasLimit: gasLimit, - } - return nil -} - // VerifyBatch send VerifyBatch request to ethereum func (c *Client) VerifyBatch(batchNum uint64, resGetProof *pb.GetProofResponse) error { gas, err := c.ethMan.EstimateGasForVerifyBatch(batchNum, resGetProof) @@ -127,8 +112,6 @@ func (c *Client) VerifyBatch(batchNum uint64, resGetProof *pb.GetProofResponse) func (c *Client) TrackEthSentTransactions(ctx context.Context) { for { select { - case tx := <-c.sequenceBatchesTxsChan: - c.resendSendBatchesTxIfNeeded(ctx, tx) case tx := <-c.verifyBatchTxsChan: c.resendVerifyBatchTxIfNeeded(ctx, tx) case <-ctx.Done(): @@ -137,40 +120,6 @@ func (c *Client) TrackEthSentTransactions(ctx context.Context) { } } -func (c *Client) resendSendBatchesTxIfNeeded(ctx context.Context, tx sequenceBatchesTx) { - var ( - gasLimit uint64 - counter uint32 - isTxSuccessful bool - err error - ) - hash := tx.hash - for !isTxSuccessful && counter <= c.cfg.MaxSendBatchTxRetries { - time.Sleep(c.cfg.FrequencyForResendingFailedSendBatches.Duration) - receipt := c.getTxReceipt(ctx, hash) - if receipt == nil { - continue - } - // tx is failed, so batch should be sent again - if receipt.Status == 0 { - gasLimit, hash, err = c.resendSequenceBatches(gasLimit, tx, hash, counter) - if err != nil { - log.Errorf("failed to resend sequence batches to the ethereum, err: %v", err) - } - counter++ - continue - } - - log.Infof("sendBatch transaction %s is successful", hash.Hex()) - isTxSuccessful = true - } - if counter == c.cfg.MaxSendBatchTxRetries { - log.Fatalf("failed to send txs %v several times,"+ - " gas limit %d is too high, first tx hash %s, last tx hash %s", - tx.sequences, gasLimit, tx.hash.Hex(), hash.Hex()) - } -} - func (c *Client) resendVerifyBatchTxIfNeeded(ctx context.Context, tx verifyBatchTx) { var ( gasLimit uint64 @@ -221,22 +170,6 @@ func (c *Client) resendVerifyBatch(gasLimit uint64, tx verifyBatchTx, hash commo return gasLimit, hash, nil } -func (c *Client) resendSequenceBatches(gasLimit uint64, tx sequenceBatchesTx, hash common.Hash, counter uint32) (uint64, common.Hash, error) { - log.Warnf("increasing gas limit for the transaction sending, previous failed tx hash %v", hash) - - gasLimit = uint64(float64(gasLimit) * gasLimitIncrease) - sentTx, err := c.ethMan.SequenceBatches(tx.sequences, gasLimit) - if err != nil { - log.Warnf("failed to send batch once again, err: %v", err) - return gasLimit, hash, err - } - hash = sentTx.Hash() - log.Infof("sent sendBatch transaction with hash %s and gas limit %d with try number %d", - hash, gasLimit, counter) - - return gasLimit, hash, nil -} - func (c *Client) getTxReceipt(ctx context.Context, hash common.Hash) *types.Receipt { _, isPending, err := c.ethMan.GetTx(ctx, hash) if err != nil { diff --git a/ethtxmanager/interfaces.go b/ethtxmanager/interfaces.go index 1a10694969..1872f24ba8 100644 --- a/ethtxmanager/interfaces.go +++ b/ethtxmanager/interfaces.go @@ -2,6 +2,7 @@ package ethtxmanager import ( "context" + "time" ethmanTypes "github.com/0xPolygonHermez/zkevm-node/etherman/types" "github.com/0xPolygonHermez/zkevm-node/proverclient/pb" @@ -16,4 +17,5 @@ type etherman interface { EstimateGasSequenceBatches(sequences []ethmanTypes.Sequence) (uint64, error) GetTx(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) GetTxReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + WaitTxToBeMined(hash common.Hash, timeout time.Duration) error } diff --git a/sequencer/config.go b/sequencer/config.go index c3cd5ad053..dbadec2378 100644 --- a/sequencer/config.go +++ b/sequencer/config.go @@ -26,8 +26,8 @@ type Config struct { // FrequencyToCheckTxsForDelete is frequency with which txs will be checked for deleting FrequencyToCheckTxsForDelete types.Duration `mapstructure:"FrequencyToCheckTxsForDelete"` - // MaxGasUsed is max gas amount used by batch - MaxGasUsed int64 `mapstructure:"MaxGasUsed"` + // MaxCumulativeGasUsed is max gas amount used by batch + MaxCumulativeGasUsed uint64 `mapstructure:"MaxCumulativeGasUsed"` // MaxKeccakHashes is max keccak hashes used by batch MaxKeccakHashes int32 `mapstructure:"MaxKeccakHashes"` diff --git a/sequencer/sequencer.go b/sequencer/sequencer.go index 9707f13bdb..71a58c3e69 100644 --- a/sequencer/sequencer.go +++ b/sequencer/sequencer.go @@ -174,17 +174,26 @@ func (s *Sequencer) trackOldTxs(ctx context.Context) { } func (s *Sequencer) tryToProcessTx(ctx context.Context, ticker *time.Ticker) { + // Check if synchronizer is up to date if !s.isSynced(ctx) { log.Info("wait for synchronizer to sync last batch") waitTick(ctx, ticker) return } - log.Info("synchronizer has synced last batch, checking if current sequence should be closed") - if s.shouldCloseSequenceInProgress(ctx) && !s.closeSequence(ctx) { - return + + // Check if should close sequence + log.Infof("checking if current sequence should be closed") + if s.shouldCloseSequenceInProgress(ctx) { + log.Infof("current sequence should be closed") + err := s.closeSequence(ctx) + if err != nil { + log.Errorf("error closing sequence: %v", err) + return + } } + // Check if should send sequence log.Infof("checking if current sequence should be sent") shouldSent, shouldCut := s.shouldSendSequences(ctx) if shouldSent { @@ -192,6 +201,7 @@ func (s *Sequencer) tryToProcessTx(ctx context.Context, ticker *time.Ticker) { if shouldCut { log.Infof("current sequence should be cut") cutSequence := s.closedSequences[len(s.closedSequences)-1] + s.closedSequences = s.closedSequences[:len(s.closedSequences)-1] s.txManager.SequenceBatches(s.closedSequences) s.closedSequences = []types.Sequence{cutSequence} } else { @@ -200,13 +210,9 @@ func (s *Sequencer) tryToProcessTx(ctx context.Context, ticker *time.Ticker) { } } + // Get next tx from the pool log.Info("getting pending tx from the pool") - zkCounters := s.calculateZkCounters() - if zkCounters.IsZkCountersBelowZero() { - s.closeSequence(ctx) - return - } - tx, err := s.pool.GetTopPendingTxByProfitabilityAndZkCounters(ctx, zkCounters) + tx, err := s.pool.GetTopPendingTxByProfitabilityAndZkCounters(ctx, s.calculateZkCounters()) if err == pgpoolstorage.ErrNotFound { log.Infof("there is no suitable pending tx in the pool, waiting...") waitTick(ctx, ticker) @@ -256,14 +262,13 @@ func (s *Sequencer) tryToProcessTx(ctx context.Context, ticker *time.Ticker) { s.lastStateRoot = processBatchResp.NewStateRoot s.lastLocalExitRoot = processBatchResp.NewLocalExitRoot + processedTxs, unprocessedTxs := state.DetermineProcessedTransactions(processBatchResp.Responses) + // only save in DB processed transactions. dbTx, err = s.state.BeginStateTransaction(ctx) if err != nil { log.Errorf("failed to begin state transaction for StoreTransactions, err: %v", err) return } - - processedTxs, unprocessedTxs := state.DetermineProcessedTransactions(processBatchResp.Responses) - // only save in DB processed transactions. err = s.state.StoreTransactions(ctx, s.lastBatchNum, processedTxs, dbTx) if err != nil { s.sequenceInProgress.Txs = s.sequenceInProgress.Txs[:len(s.sequenceInProgress.Txs)-1] @@ -300,17 +305,15 @@ func (s *Sequencer) tryToProcessTx(ctx context.Context, ticker *time.Ticker) { } } -func (s *Sequencer) closeSequence(ctx context.Context) bool { - log.Infof("current sequence should be closed") +func (s *Sequencer) closeSequence(ctx context.Context) error { s.closedSequences = append(s.closedSequences, s.sequenceInProgress) newSequence, err := s.newSequence(ctx) if err != nil { - log.Errorf("failed to create new sequence, err: %v", err) s.closedSequences = s.closedSequences[:len(s.closedSequences)-1] - return false + return fmt.Errorf("failed to create new sequence, err: %v", err) } s.sequenceInProgress = newSequence - return true + return nil } func waitTick(ctx context.Context, ticker *time.Ticker) { @@ -347,6 +350,7 @@ func (s *Sequencer) shouldSendSequences(ctx context.Context) (bool, bool) { estimatedGas, err := s.etherman.EstimateGasSequenceBatches(s.closedSequences) if err != nil && isDataForEthTxTooBig(err) { log.Warnf("closedSequences eth data is too big, err: %v", err) + log.Info("sequence should be sent to L1, because it's already too big for a single L1 tx") return true, true } @@ -387,6 +391,7 @@ func (s *Sequencer) shouldSendSequences(ctx context.Context) (bool, bool) { if lastBatchVirtualizationTime.Before(time.Now().Add(-s.cfg.LastBatchVirtualizationTimeMaxWaitPeriod.Duration)) { // check profitability if s.checker.IsSendSequencesProfitable(new(big.Int).SetUint64(estimatedGas), s.closedSequences) { + log.Info("sequence should be sent to L1, because too long since didn't send anything to L1") return true, false } } @@ -397,22 +402,43 @@ func (s *Sequencer) shouldSendSequences(ctx context.Context) (bool, bool) { // shouldCloseSequenceInProgress checks if sequence should be closed or not // in case it's enough blocks since last GER update, long time since last batch and sequence is profitable func (s *Sequencer) shouldCloseSequenceInProgress(ctx context.Context) bool { + // Check if GER needs to be updated numberOfBlocks, err := s.state.GetNumberOfBlocksSinceLastGERUpdate(ctx, nil) if err != nil && err != state.ErrNotFound { log.Errorf("failed to get last time GER updated, err: %v", err) return false } if numberOfBlocks >= s.cfg.WaitBlocksToUpdateGER { - return s.isSequenceProfitable(ctx) + if len(s.sequenceInProgress.Txs) == 0 { + log.Warn("TODO: update GER without closing batch as no txs have been added yet") + return false + } + isProfitable := s.isSequenceProfitable(ctx) + if isProfitable { + log.Infof("current sequence should be closed because %d blocks have been mined since last GER and tx is profitable", numberOfBlocks) + return true + } } - + // Check if it has been to long since a batch is virtualized lastBatchTime, err := s.state.GetLastBatchTime(ctx, nil) if err != nil && !errors.Is(err, state.ErrNotFound) { log.Errorf("failed to get last batch time, err: %v", err) return false } if lastBatchTime.Before(time.Now().Add(-s.cfg.LastTimeBatchMaxWaitPeriod.Duration)) && len(s.sequenceInProgress.Txs) > 0 { - return s.isSequenceProfitable(ctx) + isProfitable := s.isSequenceProfitable(ctx) + if isProfitable { + log.Info( + "current sequence should be closed because LastTimeBatchMaxWaitPeriod has been exceeded, " + + "there are pending sequences to be sent and they are profitable") + return true + } + } + // Check ZK counters + zkCounters := s.calculateZkCounters() + if zkCounters.IsZkCountersBelowZero() && len(s.sequenceInProgress.Txs) != 0 { + log.Info("closing sequence because at least some ZK counter is bellow 0") + return true } return false @@ -506,7 +532,7 @@ func (s *Sequencer) newSequence(ctx context.Context) (types.Sequence, error) { func (s *Sequencer) calculateZkCounters() pool.ZkCounters { return pool.ZkCounters{ - CumulativeGasUsed: s.cfg.MaxGasUsed - s.sequenceInProgress.CumulativeGasUsed, + CumulativeGasUsed: int64(s.cfg.MaxCumulativeGasUsed) - s.sequenceInProgress.CumulativeGasUsed, UsedKeccakHashes: s.cfg.MaxKeccakHashes - s.sequenceInProgress.UsedKeccakHashes, UsedPoseidonHashes: s.cfg.MaxPoseidonHashes - s.sequenceInProgress.UsedKeccakHashes, UsedPoseidonPaddings: s.cfg.MaxPoseidonPaddings - s.sequenceInProgress.UsedPoseidonPaddings, diff --git a/state/state.go b/state/state.go index 8b0711b6be..0d8da2d333 100644 --- a/state/state.go +++ b/state/state.go @@ -928,6 +928,7 @@ func DetermineProcessedTransactions(responses []*ProcessTransactionResponse) ([] if isTransactionProcessed(response.UnprocessedTransaction) { processedTxResponses = append(processedTxResponses, response) } else { + log.Infof("Tx %s has not been processed", response.TxHash) unprocessedTxResponses[response.TxHash.String()] = response } } diff --git a/test/e2e/uniswap_test.go b/test/e2e/uniswap_test.go index 8b873ca0d9..d75a395976 100644 --- a/test/e2e/uniswap_test.go +++ b/test/e2e/uniswap_test.go @@ -37,7 +37,7 @@ func TestUniswap(t *testing.T) { opsCfg := &operations.Config{ Arity: cfg.NetworkConfig.Arity, State: &state.Config{ - MaxCumulativeGasUsed: cfg.NetworkConfig.MaxCumulativeGasUsed, + MaxCumulativeGasUsed: cfg.Sequencer.MaxCumulativeGasUsed, }, Sequencer: &operations.SequencerConfig{ Address: "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D", diff --git a/test/operations/wait.go b/test/operations/wait.go index 7bdd6b838a..0856c9e2fc 100644 --- a/test/operations/wait.go +++ b/test/operations/wait.go @@ -3,10 +3,10 @@ package operations import ( "bytes" "context" - "encoding/hex" "encoding/json" "fmt" "io/ioutil" + "math/big" "net/http" "os" "os/signal" @@ -17,7 +17,6 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/health/grpc_health_v1" @@ -62,8 +61,13 @@ func Poll(interval, deadline time.Duration, condition ConditionFunc) error { } } +type ethClienter interface { + ethereum.TransactionReader + ethereum.ContractCaller +} + // WaitTxToBeMined waits until a tx has been mined or the given timeout expires. -func WaitTxToBeMined(client *ethclient.Client, hash common.Hash, timeout time.Duration) error { +func WaitTxToBeMined(client ethClienter, hash common.Hash, timeout time.Duration) error { ctx := context.Background() return Poll(DefaultInterval, timeout, func() (bool, error) { return txMinedCondition(ctx, client, hash) @@ -189,28 +193,56 @@ func grpcHealthyCondition(address string) (bool, error) { } // txMinedCondition -func txMinedCondition(ctx context.Context, client *ethclient.Client, hash common.Hash) (bool, error) { - _, isPending, err := client.TransactionByHash(ctx, hash) - if err == ethereum.NotFound { +func txMinedCondition(ctx context.Context, client ethClienter, hash common.Hash) (bool, error) { + // Get tx status + tx, isPending, err := client.TransactionByHash(ctx, hash) + if err == ethereum.NotFound || isPending { return false, nil } - if err != nil { return false, err } + // Check if tx has failed + receipt, err := client.TransactionReceipt(ctx, hash) + if err != nil { + return false, err + } + if receipt.Status == types.ReceiptStatusFailed { + // Get revert reason + reason, reasonErr := revertReason(ctx, client, tx, receipt.BlockNumber) + if reasonErr != nil { + reason = reasonErr.Error() + } + return false, fmt.Errorf("transaction has failed, reason: %s, receipt: %+v. tx: %+v", reason, receipt, tx) + } + return true, nil +} - var done bool - if !isPending { - r, err := client.TransactionReceipt(ctx, hash) +func revertReason(ctx context.Context, c ethClienter, tx *types.Transaction, blockNumber *big.Int) (string, error) { + from, err := types.Sender(types.NewEIP155Signer(tx.ChainId()), tx) + if err != nil { + signer := types.LatestSignerForChainID(tx.ChainId()) + from, err = types.Sender(signer, tx) if err != nil { - return false, err - } - if r.Status == types.ReceiptStatusFailed { - return false, fmt.Errorf("transaction has failed: %s", hex.EncodeToString(r.PostState)) + return "", err } - done = true } - return done, nil + msg := ethereum.CallMsg{ + From: from, + To: tx.To(), + Gas: tx.Gas(), + + Value: tx.Value(), + Data: tx.Data(), + } + hex, err := c.CallContract(ctx, msg, blockNumber) + if err != nil { + return "", err + } + + reasonOffset := new(big.Int).SetBytes(hex[4 : 4+32]) + reason := string(hex[4+32+int(reasonOffset.Uint64()):]) + return reason, nil } // WaitSignal blocks until an Interrupt or Kill signal is received, then it