diff --git a/Makefile b/Makefile index 29668831f4..4ed3a6e7b2 100644 --- a/Makefile +++ b/Makefile @@ -216,6 +216,7 @@ stop-explorer-db: ## Stops the explorer database run: ## Runs all the services $(RUNDB) $(RUNL1NETWORK) + sleep 2 $(RUNZKPROVER) sleep 5 $(RUNSEQUENCER) diff --git a/aggregator/aggregator.go b/aggregator/aggregator.go index 507e7fb887..a01c78c58d 100644 --- a/aggregator/aggregator.go +++ b/aggregator/aggregator.go @@ -27,9 +27,6 @@ type Aggregator struct { Ethman etherman ProverClient proverClient ProfitabilityChecker aggregatorTxProfitabilityChecker - - lastVerifiedBatchNum uint64 - batchesSent map[uint64]bool } // NewAggregator creates a new aggregator @@ -56,8 +53,6 @@ func NewAggregator( Ethman: etherman, ProverClient: prover.NewClient(zkProverClient), ProfitabilityChecker: profitabilityChecker, - - batchesSent: make(map[uint64]bool), } return a, nil @@ -119,15 +114,9 @@ func (a *Aggregator) tryVerifyBatch(ctx context.Context, ticker *time.Ticker) { } a.compareInputHashes(inputProver, resGetProof) - log.Infof("sending verified proof to the ethereum smart contract, batchNumber", batchToVerify.BatchNumber) - err = a.EthTxManager.VerifyBatch(batchToVerify.BatchNumber, resGetProof) - if err != nil { - log.Warnf("failed to send request to consolidate batch to ethereum, batch number: %d, err: %v", - batchToVerify.BatchNumber, err) - return - } - a.batchesSent[batchToVerify.BatchNumber] = true - log.Infof("proof for the batch was send, batchNumber: %d", batchToVerify.BatchNumber) + log.Infof("sending verified proof to the ethereum smart contract, batchNumber %d", batchToVerify.BatchNumber) + a.EthTxManager.VerifyBatch(batchToVerify.BatchNumber, resGetProof) + log.Infof("proof for the batch was sent, batchNumber: %d", batchToVerify.BatchNumber) } func (a *Aggregator) isSynced(ctx context.Context) bool { @@ -136,26 +125,28 @@ func (a *Aggregator) isSynced(ctx context.Context) bool { log.Warnf("failed to get last consolidated batch, err: %v", err) return false } - if lastVerifiedBatch != nil { - a.lastVerifiedBatchNum = lastVerifiedBatch.BatchNumber + if lastVerifiedBatch == nil { + return false } lastVerifiedEthBatchNum, err := a.Ethman.GetLatestVerifiedBatchNum() if err != nil { log.Warnf("failed to get last eth batch, err: %v", err) return false } - if a.lastVerifiedBatchNum < lastVerifiedEthBatchNum { + if lastVerifiedBatch.BatchNumber < lastVerifiedEthBatchNum { log.Infof("waiting for the state to be synced, lastVerifiedBatchNum: %d, lastVerifiedEthBatchNum: %d", - a.lastVerifiedBatchNum, lastVerifiedEthBatchNum) + lastVerifiedBatch.BatchNumber, lastVerifiedEthBatchNum) return false } return true } func (a *Aggregator) getBatchToVerify(ctx context.Context) (*state.Batch, error) { - delete(a.batchesSent, a.lastVerifiedBatchNum) - - batchToVerify, err := a.State.GetVirtualBatchByNumber(ctx, a.lastVerifiedBatchNum+1, nil) + lastVerifiedBatch, err := a.State.GetLastVerifiedBatch(ctx, nil) + if err != nil { + return nil, err + } + batchToVerify, err := a.State.GetVirtualBatchByNumber(ctx, lastVerifiedBatch.BatchNumber+1, nil) if err != nil { if errors.Is(err, state.ErrNotFound) { @@ -165,13 +156,6 @@ func (a *Aggregator) getBatchToVerify(ctx context.Context) (*state.Batch, error) log.Warnf("failed to get batch to consolidate, err: %v", err) return nil, err } - - if a.batchesSent[batchToVerify.BatchNumber] { - log.Infof("batch with number %d was already sent, but not yet consolidated by synchronizer", - batchToVerify.BatchNumber) - return nil, nil - } - return batchToVerify, nil } diff --git a/aggregator/aggregator_internal_test.go b/aggregator/aggregator_internal_test.go index f2614e61e1..c2d713dea7 100644 --- a/aggregator/aggregator_internal_test.go +++ b/aggregator/aggregator_internal_test.go @@ -47,28 +47,17 @@ func TestIsSyncedNotSynced(t *testing.T) { func TestGetBatchToVerify(t *testing.T) { st := new(aggrMocks.StateMock) batchToVerify := &state.Batch{BatchNumber: 1} - a := Aggregator{State: st, batchesSent: make(map[uint64]bool)} - a.batchesSent[a.lastVerifiedBatchNum] = true + a := Aggregator{State: st} ctx := context.Background() - st.On("GetVirtualBatchByNumber", ctx, a.lastVerifiedBatchNum+1, nil).Return(batchToVerify, nil) - res, err := a.getBatchToVerify(ctx) - require.NoError(t, err) - require.Equal(t, batchToVerify, res) - require.False(t, a.batchesSent[a.lastVerifiedBatchNum]) -} -func TestGetBatchToVerifyBatchAlreadySent(t *testing.T) { - st := new(aggrMocks.StateMock) - batchToVerify := &state.Batch{BatchNumber: 2} - a := Aggregator{State: st, batchesSent: make(map[uint64]bool)} - a.lastVerifiedBatchNum = 1 - a.batchesSent[a.lastVerifiedBatchNum+1] = true - ctx := context.Background() - st.On("GetVirtualBatchByNumber", ctx, a.lastVerifiedBatchNum+1, nil).Return(batchToVerify, nil) + verifiedBatch := &state.VerifiedBatch{BatchNumber: 1} + + st.On("GetLastVerifiedBatch", ctx, nil).Return(verifiedBatch, nil) + st.On("GetVirtualBatchByNumber", ctx, verifiedBatch.BatchNumber+1, nil).Return(batchToVerify, nil) + res, err := a.getBatchToVerify(ctx) require.NoError(t, err) - require.Nil(t, res) - require.False(t, a.batchesSent[a.lastVerifiedBatchNum]) + require.Equal(t, batchToVerify, res) } func TestBuildInputProver(t *testing.T) { @@ -77,11 +66,10 @@ func TestBuildInputProver(t *testing.T) { etherman := new(aggrMocks.Etherman) proverClient := new(aggrMocks.ProverClientMock) a := Aggregator{ - State: st, - EthTxManager: ethTxManager, - Ethman: etherman, - ProverClient: proverClient, - lastVerifiedBatchNum: 1, + State: st, + EthTxManager: ethTxManager, + Ethman: etherman, + ProverClient: proverClient, } var ( oldStateRoot = common.HexToHash("0xbdde84a5932a2f0a1a4c6c51f3b64ea265d4f1461749298cfdd09b31122ce0d6") @@ -96,9 +84,19 @@ func TestBuildInputProver(t *testing.T) { LocalExitRoot: oldLocalExitRoot, } ) - st.On("GetBatchByNumber", mock.Anything, a.lastVerifiedBatchNum, nil).Return(previousBatch, nil) ctx := context.Background() + + verifiedBatch := &state.VerifiedBatch{BatchNumber: 1} + + st.On("GetLastVerifiedBatch", ctx, nil).Return(verifiedBatch, nil) + etherman.On("GetLatestVerifiedBatchNum").Return(verifiedBatch.BatchNumber, nil) + + lastVerifiedBatch, err := a.State.GetLastVerifiedBatch(ctx, nil) + require.NoError(t, err) + + st.On("GetBatchByNumber", mock.Anything, lastVerifiedBatch.BatchNumber, nil).Return(previousBatch, nil) + tx := *types.NewTransaction(1, common.HexToAddress("1"), big.NewInt(1), 0, big.NewInt(1), []byte("bbb")) batchToVerify := &state.Batch{ BatchNumber: 2, @@ -145,20 +143,28 @@ func TestBuildInputProverError(t *testing.T) { etherman := new(aggrMocks.Etherman) proverClient := new(aggrMocks.ProverClientMock) a := Aggregator{ - State: st, - EthTxManager: ethTxManager, - Ethman: etherman, - ProverClient: proverClient, - lastVerifiedBatchNum: 1, + State: st, + EthTxManager: ethTxManager, + Ethman: etherman, + ProverClient: proverClient, } var ( newLocalExitRoot = common.HexToHash("0xbdde84a5932a2f0a1a4c6c51f3b64ea265d4f1461749298cfdd09b31122ce0d6") seqAddress = common.HexToAddress("0x123") batchL2Data = []byte("data") ) - st.On("GetBatchByNumber", mock.Anything, a.lastVerifiedBatchNum, nil).Return(nil, errors.New("error")) - ctx := context.Background() + + verifiedBatch := &state.VerifiedBatch{BatchNumber: 1} + + st.On("GetLastVerifiedBatch", ctx, nil).Return(verifiedBatch, nil) + etherman.On("GetLatestVerifiedBatchNum").Return(verifiedBatch.BatchNumber, nil) + + lastVerifiedBatch, err := a.State.GetLastVerifiedBatch(ctx, nil) + require.NoError(t, err) + + st.On("GetBatchByNumber", mock.Anything, lastVerifiedBatch.BatchNumber, nil).Return(nil, errors.New("error")) + tx := *types.NewTransaction(1, common.HexToAddress("1"), big.NewInt(1), 0, big.NewInt(1), []byte("bbb")) batchToVerify := &state.Batch{ BatchNumber: 2, @@ -189,8 +195,6 @@ func TestAggregatorFlow(t *testing.T) { Ethman: etherman, ProverClient: proverClient, ProfitabilityChecker: NewTxProfitabilityCheckerAcceptAll(st, 1*time.Second), - lastVerifiedBatchNum: 1, - batchesSent: make(map[uint64]bool), } var ( oldStateRoot = common.HexToHash("0xbdde84a5932a2f0a1a4c6c51f3b64ea265d4f1461749298cfdd09b31122ce0d6") @@ -256,10 +260,12 @@ func TestAggregatorFlow(t *testing.T) { st.On("GetLastVerifiedBatch", mock.Anything, nil).Return(verifiedBatch, nil) etherman.On("GetLatestVerifiedBatchNum").Return(uint64(1), nil) + lastVerifiedBatch, err := a.State.GetLastVerifiedBatch(context.Background(), nil) + require.NoError(t, err) // get batch to verify - st.On("GetVirtualBatchByNumber", mock.Anything, a.lastVerifiedBatchNum+1, nil).Return(batchToVerify, nil) + st.On("GetVirtualBatchByNumber", mock.Anything, lastVerifiedBatch.BatchNumber+1, nil).Return(batchToVerify, nil) // build input prover - st.On("GetBatchByNumber", mock.Anything, a.lastVerifiedBatchNum, nil).Return(previousBatch, nil) + st.On("GetBatchByNumber", mock.Anything, lastVerifiedBatch.BatchNumber, nil).Return(previousBatch, nil) // gen proof id proverClient.On("GetGenProofID", mock.Anything, expectedInputProver).Return("1", nil) // get proof @@ -270,5 +276,4 @@ func TestAggregatorFlow(t *testing.T) { defer ticker.Stop() ctx := context.Background() a.tryVerifyBatch(ctx, ticker) - require.True(t, a.batchesSent[batchToVerify.BatchNumber]) } diff --git a/aggregator/interfaces.go b/aggregator/interfaces.go index 3f3edd8075..a78ff4ef72 100644 --- a/aggregator/interfaces.go +++ b/aggregator/interfaces.go @@ -14,7 +14,7 @@ import ( // ethTxManager contains the methods required to send txs to // ethereum. type ethTxManager interface { - VerifyBatch(batchNum uint64, proof *pb.GetProofResponse) error + VerifyBatch(batchNum uint64, proof *pb.GetProofResponse) } // etherman contains the methods required to interact with ethereum diff --git a/aggregator/mocks/mock_ethtxmanager.go b/aggregator/mocks/mock_ethtxmanager.go index 38fcb30e91..f9f7351cda 100644 --- a/aggregator/mocks/mock_ethtxmanager.go +++ b/aggregator/mocks/mock_ethtxmanager.go @@ -13,17 +13,8 @@ type EthTxManager struct { } // VerifyBatch provides a mock function with given fields: batchNum, proof -func (_m *EthTxManager) VerifyBatch(batchNum uint64, proof *pb.GetProofResponse) error { - ret := _m.Called(batchNum, proof) - - var r0 error - if rf, ok := ret.Get(0).(func(uint64, *pb.GetProofResponse) error); ok { - r0 = rf(batchNum, proof) - } else { - r0 = ret.Error(0) - } - - return r0 +func (_m *EthTxManager) VerifyBatch(batchNum uint64, proof *pb.GetProofResponse) { + _m.Called(batchNum, proof) } type mockConstructorTestingTNewEthTxManager interface { diff --git a/ethtxmanager/ethtxmanager.go b/ethtxmanager/ethtxmanager.go index e2fa91c71a..8d83b4c512 100644 --- a/ethtxmanager/ethtxmanager.go +++ b/ethtxmanager/ethtxmanager.go @@ -5,45 +5,25 @@ package ethtxmanager import ( - "context" - "fmt" "strings" "time" ethmanTypes "github.com/0xPolygonHermez/zkevm-node/etherman/types" "github.com/0xPolygonHermez/zkevm-node/log" "github.com/0xPolygonHermez/zkevm-node/proverclient/pb" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -const ( - gasLimitIncrease = 1.2 - sentEthTxsChanLen = 100 ) // Client for eth tx manager type Client struct { - cfg Config - - ethMan etherman - verifyBatchTxsChan chan verifyBatchTx -} - -type verifyBatchTx struct { - batchNumber uint64 - resGetProof *pb.GetProofResponse - hash common.Hash - gasLimit uint64 + cfg Config + ethMan etherman } // New creates new eth tx manager func New(cfg Config, ethMan etherman) *Client { - verifyBatchTxsChan := make(chan verifyBatchTx, sentEthTxsChanLen) return &Client{ - cfg: cfg, - verifyBatchTxsChan: verifyBatchTxsChan, - ethMan: ethMan, + cfg: cfg, + ethMan: ethMan, } } @@ -87,103 +67,40 @@ func (c *Client) SequenceBatches(sequences []ethmanTypes.Sequence) { } // VerifyBatch send VerifyBatch request to ethereum -func (c *Client) VerifyBatch(batchNum uint64, resGetProof *pb.GetProofResponse) error { - gas, err := c.ethMan.EstimateGasForVerifyBatch(batchNum, resGetProof) - 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.VerifyBatch(batchNum, resGetProof, gasLimit) - if err != nil { - return err - } - c.verifyBatchTxsChan <- verifyBatchTx{ - batchNumber: batchNum, - resGetProof: resGetProof, - hash: tx.Hash(), - gasLimit: gasLimit, - } - return nil -} - -// TrackEthSentTransactions tracks sent txs to the ethereum -func (c *Client) TrackEthSentTransactions(ctx context.Context) { - for { - select { - case tx := <-c.verifyBatchTxsChan: - c.resendVerifyBatchTxIfNeeded(ctx, tx) - case <-ctx.Done(): - return +func (c *Client) VerifyBatch(batchNum uint64, resGetProof *pb.GetProofResponse) { + var attempts uint32 + var gas uint64 + log.Infof("sending batch %d verification to L1", batchNum) + for attempts < c.cfg.MaxVerifyBatchTxRetries { + tx, err := c.ethMan.VerifyBatch(batchNum, resGetProof, gas) + for err != nil && attempts < c.cfg.MaxSendBatchTxRetries { + log.Errorf("failed to send batch verification, trying once again, retry #%d, gasLimit: %d, err: %v", + attempts, 0, err) + time.Sleep(c.cfg.FrequencyForResendingFailedSendBatches.Duration) + attempts++ + tx, err = c.ethMan.VerifyBatch(batchNum, resGetProof, gas) } - } -} - -func (c *Client) resendVerifyBatchTxIfNeeded(ctx context.Context, tx verifyBatchTx) { - var ( - gasLimit uint64 - counter uint32 - isTxSuccessful bool - err error - ) - hash := tx.hash - for !isTxSuccessful && counter <= c.cfg.MaxVerifyBatchTxRetries { - time.Sleep(c.cfg.FrequencyForResendingFailedVerifyBatch.Duration) - receipt := c.getTxReceipt(ctx, hash) - if receipt == nil { - continue + if err != nil { + log.Fatalf("failed to send batch verification, maximum attempts exceeded, gasLimit: %d, err: %v", + 0, err) } - // tx is failed, so batch should be sent again - if receipt.Status == 0 { - gasLimit, hash, err = c.resendVerifyBatch(gasLimit, tx, hash, counter) - if err != nil { - log.Errorf("failed to resend verify batch to the ethereum, err: %v", err) + // Wait for tx to be mined + log.Infof("waiting for tx to be mined. Tx hash: %s", tx.Hash()) + // TODO: timeout via config file + err = c.ethMan.WaitTxToBeMined(tx.Hash(), time.Minute*2) //nolint:gomnd + if err != nil { + attempts++ + if strings.Contains(err.Error(), "out of gas") { + // TODO: percentage gas inncrease via config file + gas = uint64(float64(tx.Gas()) * 1.1) //nolint:gomnd + log.Infof("out of gas with %d, retrying with %d", tx.Gas(), gas) + continue } - counter++ - continue + // TODO: handle timeout by increasing gas price + log.Fatalf("tx %s failed, err: %v", tx.Hash(), err) + } else { + log.Infof("batch verification sent to L1 successfully. Tx hash: %s", tx.Hash()) + return } - - log.Infof("verifyBatch transaction %s is successful", hash.Hex()) - isTxSuccessful = true - } - if counter == c.cfg.MaxSendBatchTxRetries { - log.Fatalf("failed to send verify batch several times,"+ - "batchNumber %d, gas limit %d is too high, first tx hash %s, last tx hash %s", - tx.batchNumber, gasLimit, tx.hash.Hex(), hash.Hex()) - } -} - -func (c *Client) resendVerifyBatch(gasLimit uint64, tx verifyBatchTx, 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.VerifyBatch(tx.batchNumber, tx.resGetProof, 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 { - log.Warnf("failed to get tx with hash %s, err %v", hash, err) - return nil - } - if isPending { - log.Debugf("sendBatch transaction %s is pending", hash) - return nil - } - - receipt, err := c.ethMan.GetTxReceipt(ctx, hash) - if err != nil { - log.Warnf("failed to get tx receipt with hash %v, err %v", hash.Hex(), err) - return nil } - return receipt } diff --git a/sequencer/txmanager-mock_test.go b/sequencer/txmanager-mock_test.go index 22ece3c8a5..52464b4be3 100644 --- a/sequencer/txmanager-mock_test.go +++ b/sequencer/txmanager-mock_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.13.1. DO NOT EDIT. package sequencer @@ -14,17 +14,8 @@ type txmanagerMock struct { } // SequenceBatches provides a mock function with given fields: sequences -func (_m *txmanagerMock) SequenceBatches(sequences []types.Sequence) error { - ret := _m.Called(sequences) - - var r0 error - if rf, ok := ret.Get(0).(func([]types.Sequence) error); ok { - r0 = rf(sequences) - } else { - r0 = ret.Error(0) - } - - return r0 +func (_m *txmanagerMock) SequenceBatches(sequences []types.Sequence) { + _m.Called(sequences) } type mockConstructorTestingTnewTxmanagerMock interface {