From df99ab4968e4cd5da1766b2d8fc48426e9b29dbf Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Mon, 30 Sep 2024 11:20:48 +0200 Subject: [PATCH 1/4] feat: prepare txns before routines --- ethtxmanager/ethtxmanager.go | 272 ++++++++++------------------------- ethtxmanager/monitoredtx.go | 68 +++++++++ 2 files changed, 141 insertions(+), 199 deletions(-) diff --git a/ethtxmanager/ethtxmanager.go b/ethtxmanager/ethtxmanager.go index 0124944..9f74275 100644 --- a/ethtxmanager/ethtxmanager.go +++ b/ethtxmanager/ethtxmanager.go @@ -44,12 +44,10 @@ type Client struct { ctx context.Context cancel context.CancelFunc - cfg Config - etherman ethermanInterface - storage storageInterface - from common.Address - nonceMutex sync.Mutex - addTxMutex sync.Mutex + cfg Config + etherman ethermanInterface + storage storageInterface + from common.Address } type pending struct { @@ -154,47 +152,6 @@ func pendingL1Txs(URL string, from common.Address, httpHeaders map[string]string return mTxs, nil } -// getTxNonce get the nonce for the given account -func (c *Client) getTxNonce(ctx context.Context, from common.Address, checkCreatedTxs bool) (uint64, error) { - c.nonceMutex.Lock() - defer c.nonceMutex.Unlock() - - var ( - localNonce, pendingNonce uint64 - err error - ) - - if checkCreatedTxs { - // Get created transactions from the database for the given account - createdTxs, err := c.storage.GetByStatus(ctx, []MonitoredTxStatus{MonitoredTxStatusCreated}) - if err != nil { - return 0, fmt.Errorf("failed to get monitored txs in getTxNonce: %w", err) - } - - if len(createdTxs) > 0 { - // if there are pending txs, we adjust the nonce accordingly - for _, createdTx := range createdTxs { - if createdTx.Nonce > localNonce { - localNonce = createdTx.Nonce - } - } - - localNonce++ - } - } - - // if there are no pending txs, we get the pending nonce from the etherman - if pendingNonce, err = c.etherman.PendingNonce(ctx, from); err != nil { - return 0, fmt.Errorf("failed to get pending nonce: %w", err) - } - - if localNonce > pendingNonce { - return localNonce, nil - } - - return pendingNonce, nil -} - // Add a transaction to be sent and monitored func (c *Client) Add(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar) (common.Hash, error) { return c.add(ctx, to, forcedNonce, value, data, gasOffset, sidecar, 0) @@ -209,25 +166,12 @@ func (c *Client) add(ctx context.Context, to *common.Address, forcedNonce *uint6 var nonce uint64 var err error - c.addTxMutex.Lock() - defer c.addTxMutex.Unlock() - - if forcedNonce == nil { - // get next nonce - nonce, err = c.getTxNonce(ctx, c.from, true) - if err != nil { - err := fmt.Errorf("failed to get current nonce: %w", err) - log.Errorf(err.Error()) - return common.Hash{}, err - } - } else { + if forcedNonce != nil { + // we should review this forced nonce feature, because now it doesn't make sense to have it + // since we are always updating the nonce before sending the tx nonce = *forcedNonce } - // Avoid nonces to be assigned while a new tx is being created - c.nonceMutex.Lock() - defer c.nonceMutex.Unlock() - // get gas price gasPrice, err := c.suggestedGasPrice(ctx) if err != nil { @@ -495,26 +439,25 @@ func (c *Client) Stop() { // monitorTxs processes all pending monitored txs func (c *Client) monitorTxs(ctx context.Context) error { - statusesFilter := []MonitoredTxStatus{MonitoredTxStatusCreated, MonitoredTxStatusSent} - mTxs, err := c.storage.GetByStatus(ctx, statusesFilter) + iterations, err := c.getMonitoredTxnIteration(ctx) if err != nil { - return fmt.Errorf("failed to get created monitored txs: %v", err) + return fmt.Errorf("failed to get monitored txs: %v", err) } - log.Debugf("found %v monitored tx to process", len(mTxs)) + log.Debugf("found %v monitored tx to process", len(iterations)) wg := sync.WaitGroup{} - wg.Add(len(mTxs)) - for _, mTx := range mTxs { + wg.Add(len(iterations)) + for _, mTx := range iterations { mTx := mTx // force variable shadowing to avoid pointer conflicts - go func(c *Client, mTx monitoredTx) { - mTxLogger := createMonitoredTxLogger(mTx) - defer func(mTx monitoredTx, mTxLogger *log.Logger) { + go func(c *Client, mTx *monitoredTxnIteration) { + mTxLogger := createMonitoredTxLogger(*mTx.monitoredTx) + defer func(mTxLogger *log.Logger) { if err := recover(); err != nil { mTxLogger.Errorf("monitoring recovered from this err: %v", err) } wg.Done() - }(mTx, mTxLogger) + }(mTxLogger) c.monitorTx(ctx, mTx, mTxLogger) }(c, mTx) } @@ -609,82 +552,19 @@ func (c *Client) waitSafeTxToBeFinalized(ctx context.Context) error { } // monitorTx does all the monitoring steps to the monitored tx -func (c *Client) monitorTx(ctx context.Context, mTx monitoredTx, logger *log.Logger) { +func (c *Client) monitorTx(ctx context.Context, mTx *monitoredTxnIteration, logger *log.Logger) { var err error logger.Info("processing") - // check if any of the txs in the history was confirmed - var lastReceiptChecked types.Receipt - // monitored tx is confirmed until we find a successful receipt - confirmed := false - // monitored tx doesn't have a failed receipt until we find a failed receipt for any - // tx in the monitored tx history - hasFailedReceipts := false - // all history txs are considered mined until we can't find a receipt for any - // tx in the monitored tx history - allHistoryTxsWereMined := true - for txHash := range mTx.History { - mined, receipt, err := c.etherman.CheckTxWasMined(ctx, txHash) - if err != nil { - logger.Errorf("failed to check if tx %v was mined: %v", txHash.String(), err) - continue - } - - // if the tx is not mined yet, check that not all the tx were mined and go to the next - if !mined { - allHistoryTxsWereMined = false - continue - } - - lastReceiptChecked = *receipt - - // if the tx was mined successfully we can set it as confirmed and break the loop - if lastReceiptChecked.Status == types.ReceiptStatusSuccessful { - confirmed = true - break - } - - // if the tx was mined but failed, we continue to consider it was not confirmed - // and set that we have found a failed receipt. This info will be used later - // to check if nonce needs to be reviewed - confirmed = false - hasFailedReceipts = true - } - - // we need to check if we need to review the nonce carefully, to avoid sending - // duplicated data to the roll-up and causing an unnecessary trusted state reorg. - // - // if we have failed receipts, this means at least one of the generated txs was mined, - // in this case maybe the current nonce was already consumed(if this is the first iteration - // of this cycle, next iteration might have the nonce already updated by the preivous one), - // then we need to check if there are tx that were not mined yet, if so, we just need to wait - // because maybe one of them will get mined successfully - // - // in case of the monitored tx is not confirmed yet, all tx were mined and none of them were - // mined successfully, we need to review the nonce - if !confirmed && hasFailedReceipts && allHistoryTxsWereMined { - logger.Infof("nonce needs to be updated") - - err := c.reviewMonitoredTxNonce(ctx, mTx, logger) - if err != nil { - logger.Errorf("failed to review monitored tx nonce: %v", err) - return - } - } var signedTx *types.Transaction - if !confirmed { + if !mTx.confirmed { // review tx and increase gas and gas price if needed if mTx.Status == MonitoredTxStatusSent { - err := c.reviewMonitoredTx(ctx, &mTx, logger) + err := c.reviewMonitoredTxGas(ctx, mTx, logger) if err != nil { logger.Errorf("failed to review monitored tx: %v", err) return } - err = c.storage.Update(ctx, mTx) - if err != nil { - logger.Errorf("failed to update monitored tx review change: %v", err) - return - } } // rebuild transaction @@ -708,7 +588,7 @@ func (c *Client) monitorTx(ctx context.Context, mTx monitoredTx, logger *log.Log return } else { // update monitored tx changes into storage - err = c.storage.Update(ctx, mTx) + err = c.storage.Update(ctx, *mTx.monitoredTx) if err != nil { logger.Errorf("failed to update monitored tx: %v", err) return @@ -732,7 +612,7 @@ func (c *Client) monitorTx(ctx context.Context, mTx monitoredTx, logger *log.Log mTx.Status = MonitoredTxStatusSent logger.Debugf("status changed to %v", string(mTx.Status)) // update monitored tx changes into storage - err = c.storage.Update(ctx, mTx) + err = c.storage.Update(ctx, *mTx.monitoredTx) if err != nil { logger.Errorf("failed to update monitored tx changes: %v", err) return @@ -745,7 +625,7 @@ func (c *Client) monitorTx(ctx context.Context, mTx monitoredTx, logger *log.Log log.Infof("waiting signedTx to be mined...") // wait tx to get mined - confirmed, err = c.etherman.WaitTxToBeMined(ctx, signedTx, c.cfg.WaitTxToBeMined.Duration) + confirmed, err := c.etherman.WaitTxToBeMined(ctx, signedTx, c.cfg.WaitTxToBeMined.Duration) if err != nil { logger.Warnf("failed to wait tx to be mined: %v", err) return @@ -772,28 +652,29 @@ func (c *Client) monitorTx(ctx context.Context, mTx monitoredTx, logger *log.Log } } - lastReceiptChecked = *txReceipt + mTx.lastReceipt = txReceipt + mTx.confirmed = confirmed } // if mined, check receipt and mark as Failed or Confirmed - if lastReceiptChecked.Status == types.ReceiptStatusSuccessful { + if mTx.lastReceipt.Status == types.ReceiptStatusSuccessful { mTx.Status = MonitoredTxStatusMined - mTx.BlockNumber = lastReceiptChecked.BlockNumber + mTx.BlockNumber = mTx.lastReceipt.BlockNumber logger.Info("mined") } else { // if we should continue to monitor, we move to the next one and this will // be reviewed in the next monitoring cycle - if c.shouldContinueToMonitorThisTx(ctx, lastReceiptChecked) { + if c.shouldContinueToMonitorThisTx(ctx, mTx.lastReceipt) { return } // otherwise we understand this monitored tx has failed mTx.Status = MonitoredTxStatusFailed - mTx.BlockNumber = lastReceiptChecked.BlockNumber + mTx.BlockNumber = mTx.lastReceipt.BlockNumber logger.Info("failed") } // update monitored tx changes into storage - err = c.storage.Update(ctx, mTx) + err = c.storage.Update(ctx, *mTx.monitoredTx) if err != nil { logger.Errorf("failed to update monitored tx: %v", err) return @@ -802,7 +683,7 @@ func (c *Client) monitorTx(ctx context.Context, mTx monitoredTx, logger *log.Log // shouldContinueToMonitorThisTx checks the the tx receipt and decides if it should // continue or not to monitor the monitored tx related to the tx from this receipt -func (c *Client) shouldContinueToMonitorThisTx(ctx context.Context, receipt types.Receipt) bool { +func (c *Client) shouldContinueToMonitorThisTx(ctx context.Context, receipt *types.Receipt) bool { // if the receipt has a is successful result, stop monitoring if receipt.Status == types.ReceiptStatusSuccessful { return false @@ -826,14 +707,16 @@ func (c *Client) shouldContinueToMonitorThisTx(ctx context.Context, receipt type return false } -// reviewMonitoredTx checks if some field needs to be updated +// reviewMonitoredTxGas checks if gas fields needs to be updated // accordingly to the current information stored and the current // state of the blockchain -func (c *Client) reviewMonitoredTx(ctx context.Context, mTx *monitoredTx, mTxLogger *log.Logger) error { +func (c *Client) reviewMonitoredTxGas(ctx context.Context, mTx *monitoredTxnIteration, mTxLogger *log.Logger) error { mTxLogger.Debug("reviewing") - isBlobTx := mTx.BlobSidecar != nil - var err error - var gas uint64 + var ( + isBlobTx = mTx.BlobSidecar != nil + err error + gas uint64 + ) // get gas price gasPrice, err := c.suggestedGasPrice(ctx) @@ -912,66 +795,57 @@ func (c *Client) reviewMonitoredTx(ctx context.Context, mTx *monitoredTx, mTxLog mTxLogger.Infof("monitored tx (blob? %t) Gas updated from %v to %v", isBlobTx, mTx.Gas, gas) mTx.Gas = gas } + + err = c.storage.Update(ctx, *mTx.monitoredTx) + if err != nil { + return fmt.Errorf("failed to update monitored tx changes: %w", err) + } + return nil } -// reviewMonitoredTxNonce checks if the nonce needs to be updated accordingly to -// the current nonce of the sender account. -// -// IMPORTANT: Nonce is reviewed apart from the other fields because it is a very -// sensible information and can make duplicated data to be sent to the blockchain, -// causing possible side effects and wasting resources. -func (c *Client) reviewMonitoredTxNonce(ctx context.Context, mTx monitoredTx, mTxLogger *log.Logger) error { - // Avoid txs being added while we are reviewing the nonce - c.addTxMutex.Lock() - defer c.addTxMutex.Unlock() - - prevNonce := mTx.Nonce - - mTxLogger.Debug("reviewing nonce") - currentNonce, err := c.getTxNonce(ctx, mTx.From, false) +// getMonitoredTxnIteration gets all monitored txs that need to be sent or resent in current monitor iteration +func (c *Client) getMonitoredTxnIteration(ctx context.Context) ([]*monitoredTxnIteration, error) { + txsToUpdate, err := c.storage.GetByStatus(ctx, []MonitoredTxStatus{MonitoredTxStatusCreated, MonitoredTxStatusSent}) if err != nil { - err := fmt.Errorf("failed to load current nonce for acc %v: %w", mTx.From.String(), err) - mTxLogger.Errorf(err.Error()) - return err + return nil, fmt.Errorf("failed to get txs to update nonces: %w", err) } - if currentNonce > mTx.Nonce { - mTxLogger.Infof("monitored tx nonce updated from %v to %v", mTx.Nonce, currentNonce) - mTx.Nonce = currentNonce + iterations := make([]*monitoredTxnIteration, 0, len(txsToUpdate)) + senderNonces := make(map[common.Address]uint64) - err = c.storage.Update(ctx, mTx) - if err != nil { - mTxLogger.Errorf("failed to update monitored tx nonce change: %v", err) - return err - } - } + for _, tx := range txsToUpdate { + tx := tx - // we need to update the rest of pending txs nonces - // to avoid nonce conflicts - createdTxs, err := c.storage.GetByStatus(ctx, []MonitoredTxStatus{MonitoredTxStatusCreated}) - if err != nil { - mTxLogger.Errorf("failed to get created monitored txs: %v", err) - return err - } + iteration := &monitoredTxnIteration{monitoredTx: &tx} + iterations = append(iterations, iteration) - for _, cTx := range createdTxs { - // Avoid memory aliasing - createdTx := cTx - if createdTx.Nonce > prevNonce && createdTx.Nonce < currentNonce { - currentNonce++ - mTxLogger.Infof("monitored tx nonce updated from %v to %v", createdTx.Nonce, currentNonce) - createdTx.Nonce = currentNonce + updateNonce := iteration.shouldUpdateNonce(ctx, c.etherman) + if !updateNonce { + continue + } - err = c.storage.Update(ctx, createdTx) + nonce, ok := senderNonces[tx.From] + if !ok { + // if there are no pending txs, we get the pending nonce from the etherman + nonce, err = c.etherman.PendingNonce(ctx, tx.From) if err != nil { - mTxLogger.Errorf("failed to update monitored tx nonce change: %v", err) - return err + return nil, fmt.Errorf("failed to get pending nonce for sender: %s. Error: %w", tx.From, err) } + + senderNonces[tx.From] = nonce } + + iteration.Nonce = nonce + err = c.storage.Update(ctx, tx) + if err != nil { + return nil, fmt.Errorf("failed to update nonce for tx %v: %w", tx.ID.String(), err) + } + + senderNonces[tx.From]++ } - return nil + return iterations, nil } func (c *Client) suggestedGasPrice(ctx context.Context) (*big.Int, error) { diff --git a/ethtxmanager/monitoredtx.go b/ethtxmanager/monitoredtx.go index 17e3c00..585e9bd 100644 --- a/ethtxmanager/monitoredtx.go +++ b/ethtxmanager/monitoredtx.go @@ -1,6 +1,7 @@ package ethtxmanager import ( + "context" "math/big" "time" @@ -171,3 +172,70 @@ type TxResult struct { Receipt *types.Receipt RevertMessage string } + +type monitoredTxnIteration struct { + *monitoredTx + confirmed bool + lastReceipt *types.Receipt +} + +func (m *monitoredTxnIteration) shouldUpdateNonce(ctx context.Context, etherman ethermanInterface) bool { + if m.Status == MonitoredTxStatusCreated { + // transaction was not sent, so no need to check if it was mined + // we need to update the nonce in this case + return true + } + + // check if any of the txs in the history was confirmed + var lastReceiptChecked *types.Receipt + // monitored tx is confirmed until we find a successful receipt + confirmed := false + // monitored tx doesn't have a failed receipt until we find a failed receipt for any + // tx in the monitored tx history + hasFailedReceipts := false + // all history txs are considered mined until we can't find a receipt for any + // tx in the monitored tx history + allHistoryTxsWereMined := true + for txHash := range m.History { + mined, receipt, err := etherman.CheckTxWasMined(ctx, txHash) + if err != nil { + continue + } + + // if the tx is not mined yet, check that not all the tx were mined and go to the next + if !mined { + allHistoryTxsWereMined = false + continue + } + + lastReceiptChecked = receipt + + // if the tx was mined successfully we can set it as confirmed and break the loop + if lastReceiptChecked.Status == types.ReceiptStatusSuccessful { + confirmed = true + break + } + + // if the tx was mined but failed, we continue to consider it was not confirmed + // and set that we have found a failed receipt. This info will be used later + // to check if nonce needs to be reviewed + confirmed = false + hasFailedReceipts = true + } + + m.confirmed = confirmed + m.lastReceipt = lastReceiptChecked + + // we need to check if we need to review the nonce carefully, to avoid sending + // duplicated data to the roll-up and causing an unnecessary trusted state reorg. + // + // if we have failed receipts, this means at least one of the generated txs was mined, + // in this case maybe the current nonce was already consumed(if this is the first iteration + // of this cycle, next iteration might have the nonce already updated by the preivous one), + // then we need to check if there are tx that were not mined yet, if so, we just need to wait + // because maybe one of them will get mined successfully + // + // in case of the monitored tx is not confirmed yet, all tx were mined and none of them were + // mined successfully, we need to review the nonce + return !confirmed && hasFailedReceipts && allHistoryTxsWereMined +} From f83c5022e6cd4fe4610040a912e1fe00763b3bb4 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Mon, 30 Sep 2024 12:08:40 +0200 Subject: [PATCH 2/4] fix: remove forcedNonce feature --- ethtxmanager/ethtxmanager.go | 21 ++++++--------------- test/main.go | 25 ++++++------------------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/ethtxmanager/ethtxmanager.go b/ethtxmanager/ethtxmanager.go index 9f74275..16eff05 100644 --- a/ethtxmanager/ethtxmanager.go +++ b/ethtxmanager/ethtxmanager.go @@ -153,25 +153,18 @@ func pendingL1Txs(URL string, from common.Address, httpHeaders map[string]string } // Add a transaction to be sent and monitored -func (c *Client) Add(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar) (common.Hash, error) { - return c.add(ctx, to, forcedNonce, value, data, gasOffset, sidecar, 0) +func (c *Client) Add(ctx context.Context, to *common.Address, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar) (common.Hash, error) { + return c.add(ctx, to, value, data, gasOffset, sidecar, 0) } // AddWithGas adds a transaction to be sent and monitored with a defined gas to be used so it's not estimated -func (c *Client) AddWithGas(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar, gas uint64) (common.Hash, error) { - return c.add(ctx, to, forcedNonce, value, data, gasOffset, sidecar, gas) +func (c *Client) AddWithGas(ctx context.Context, to *common.Address, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar, gas uint64) (common.Hash, error) { + return c.add(ctx, to, value, data, gasOffset, sidecar, gas) } -func (c *Client) add(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar, gas uint64) (common.Hash, error) { - var nonce uint64 +func (c *Client) add(ctx context.Context, to *common.Address, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar, gas uint64) (common.Hash, error) { var err error - if forcedNonce != nil { - // we should review this forced nonce feature, because now it doesn't make sense to have it - // since we are always updating the nonce before sending the tx - nonce = *forcedNonce - } - // get gas price gasPrice, err := c.suggestedGasPrice(ctx) if err != nil { @@ -252,14 +245,12 @@ func (c *Client) add(ctx context.Context, to *common.Address, forcedNonce *uint6 if sidecar == nil { tx = types.NewTx(&types.LegacyTx{ To: to, - Nonce: nonce, Value: value, Data: data, }) } else { tx = types.NewTx(&types.BlobTx{ To: *to, - Nonce: nonce, Value: uint256.MustFromBig(value), Data: data, BlobHashes: sidecar.BlobHashes(), @@ -272,7 +263,7 @@ func (c *Client) add(ctx context.Context, to *common.Address, forcedNonce *uint6 // create monitored tx mTx := monitoredTx{ ID: id, From: c.from, To: to, - Nonce: nonce, Value: value, Data: data, + Value: value, Data: data, Gas: gas, GasPrice: gasPrice, GasOffset: gasOffset, BlobSidecar: sidecar, BlobGas: tx.BlobGas(), diff --git a/test/main.go b/test/main.go index 506bdd1..88f9d62 100644 --- a/test/main.go +++ b/test/main.go @@ -15,8 +15,7 @@ import ( ) var ( - to0 = common.HexToAddress("0x0000000000000000000000000000000000000000") - from = common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") + to0 = common.HexToAddress("0x0000000000000000000000000000000000000000") ) func main() { @@ -51,17 +50,6 @@ func main() { ctx := context.Background() - // Get starting nonce - testEtherman, err := etherman.NewClient(config.Etherman) - if err != nil { - log.Fatalf("Error creating etherman client: %s", err) - } - - nonce, err := testEtherman.CurrentNonce(ctx, from) - if err != nil { - log.Fatalf("Error getting nonce: %s", err) - } - go client.Start() log.Debug("ethtxmanager started") // sendBlobTransaction(ctx, client, nonce) @@ -69,8 +57,7 @@ func main() { for i := 0; i < 1; i++ { time.Sleep(100 * time.Millisecond) - sendTransaction(ctx, client, nonce) - nonce++ + sendTransaction(ctx, client) } for { @@ -110,8 +97,8 @@ func main() { } } -func sendTransaction(ctx context.Context, ethtxmanager *ethtxmanager.Client, nonce uint64) common.Hash { - id, err := ethtxmanager.Add(ctx, &to0, &nonce, big.NewInt(1), []byte{byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256))}, 0, nil) +func sendTransaction(ctx context.Context, ethtxmanager *ethtxmanager.Client) common.Hash { + id, err := ethtxmanager.Add(ctx, &to0, big.NewInt(1), []byte{byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256))}, 0, nil) if err != nil { log.Errorf("Error sending transaction: %s", err) } else { @@ -120,7 +107,7 @@ func sendTransaction(ctx context.Context, ethtxmanager *ethtxmanager.Client, non return id } -func sendBlobTransaction(ctx context.Context, ethtxmanager *ethtxmanager.Client, nonce uint64) common.Hash { +func sendBlobTransaction(ctx context.Context, ethtxmanager *ethtxmanager.Client) common.Hash { blobBytes := []byte{255, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256))} blob, err := ethtxmanager.EncodeBlobData(blobBytes) if err != nil { @@ -131,7 +118,7 @@ func sendBlobTransaction(ctx context.Context, ethtxmanager *ethtxmanager.Client, // data := []byte{228, 103, 97, 196} // pol method data := []byte{} - id, err := ethtxmanager.Add(ctx, &to0, &nonce, big.NewInt(0), data, 0, blobSidecar) + id, err := ethtxmanager.Add(ctx, &to0, big.NewInt(0), data, 0, blobSidecar) if err != nil { log.Errorf("Error sending Blob transaction: %s", err) } else { From 3ce78737dea1d68a82b866ee4fd0b8c6b2ff4db5 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Mon, 30 Sep 2024 13:30:02 +0200 Subject: [PATCH 3/4] feat: uts --- .mockery.yml | 17 + Makefile | 8 + ethtxmanager/ethtxmanager.go | 4 +- ethtxmanager/ethtxmanager_test.go | 133 +++ ethtxmanager/interfaces.go | 4 +- ethtxmanager/monitoredtx.go | 2 +- ethtxmanager/monitoredtx_test.go | 94 ++ ethtxmanager/storageinterface.generated.go | 401 +++++++++ go.mod | 1 + mocks/ethermaninterface.generated.go | 982 +++++++++++++++++++++ 10 files changed, 1641 insertions(+), 5 deletions(-) create mode 100644 .mockery.yml create mode 100644 ethtxmanager/ethtxmanager_test.go create mode 100644 ethtxmanager/storageinterface.generated.go create mode 100644 mocks/ethermaninterface.generated.go diff --git a/.mockery.yml b/.mockery.yml new file mode 100644 index 0000000..1db307f --- /dev/null +++ b/.mockery.yml @@ -0,0 +1,17 @@ +with-expecter: true +dir: "mocks" +filename: "{{.InterfaceName | lower }}.generated.go" +mockname: "{{.InterfaceName}}" +outpkg: "mocks" +packages: + github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager: + interfaces: + EthermanInterface: + config: + StorageInterface: + config: + dir: "{{.InterfaceDir}}" + filename: "{{.InterfaceName | lower }}.generated.go" + mockname: "{{.InterfaceName}}Mock" + outpkg: "{{.PackageName}}" + inpackage: True diff --git a/Makefile b/Makefile index 4e85688..b85a54f 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,10 @@ check-go: check-curl: @which curl > /dev/null || (echo "Error: curl is not installed" && exit 1) +.PHONY: check-mockery +check-mockery: + @which mockery > /dev/null || (echo "Error: mockery is not installed" && exit 1) + # Targets that require the checks build: check-go lint: check-go @@ -37,6 +41,10 @@ install-linter: ## Installs the linter lint: ## Runs the linter export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/golangci-lint run +.PHONY: generate +generate: ## Generates mocks and other autogenerated types + mockery + ## Help display. ## Pulls comments from beside commands and prints a nicely formatted ## display with the commands and their usage information. diff --git a/ethtxmanager/ethtxmanager.go b/ethtxmanager/ethtxmanager.go index 16eff05..9109494 100644 --- a/ethtxmanager/ethtxmanager.go +++ b/ethtxmanager/ethtxmanager.go @@ -45,8 +45,8 @@ type Client struct { cancel context.CancelFunc cfg Config - etherman ethermanInterface - storage storageInterface + etherman EthermanInterface + storage StorageInterface from common.Address } diff --git a/ethtxmanager/ethtxmanager_test.go b/ethtxmanager/ethtxmanager_test.go new file mode 100644 index 0000000..9542bdd --- /dev/null +++ b/ethtxmanager/ethtxmanager_test.go @@ -0,0 +1,133 @@ +package ethtxmanager + +import ( + context "context" + "errors" + "testing" + + "github.com/0xPolygonHermez/zkevm-ethtx-manager/mocks" + common "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestGetMonitoredTxnIteration(t *testing.T) { + ctx := context.Background() + etherman := mocks.NewEthermanInterface(t) + storage := NewMemStorage("") + + client := &Client{ + etherman: etherman, + storage: storage, + } + + tests := []struct { + name string + storageTxn *monitoredTx + ethermanNonce uint64 + shouldUpdate bool + expectedResult []*monitoredTxnIteration + expectedError error + }{ + { + name: "No transactions to update", + expectedError: nil, + expectedResult: []*monitoredTxnIteration{}, + }, + { + name: "Transaction should not update nonce", + storageTxn: &monitoredTx{ + ID: common.HexToHash("0x1"), + From: common.HexToAddress("0x1"), + Status: MonitoredTxStatusSent, + History: map[common.Hash]bool{ + common.HexToHash("0x1"): true, + }, + }, + shouldUpdate: false, + expectedResult: []*monitoredTxnIteration{ + { + monitoredTx: &monitoredTx{ + ID: common.HexToHash("0x1"), + From: common.HexToAddress("0x1"), + Status: MonitoredTxStatusSent, + History: map[common.Hash]bool{ + common.HexToHash("0x1"): true, + }, + }, + confirmed: true, + lastReceipt: &types.Receipt{Status: types.ReceiptStatusSuccessful}, + }, + }, + expectedError: nil, + }, + { + name: "Transaction should update nonce", + storageTxn: &monitoredTx{ + ID: common.HexToHash("0x1"), + From: common.HexToAddress("0x1"), + Status: MonitoredTxStatusCreated, + }, + shouldUpdate: true, + ethermanNonce: 1, + expectedResult: []*monitoredTxnIteration{ + { + monitoredTx: &monitoredTx{ + ID: common.HexToHash("0x1"), + From: common.HexToAddress("0x1"), + Status: MonitoredTxStatusCreated, + Nonce: 1, + }, + }, + }, + expectedError: nil, + }, + { + name: "Error getting pending nonce", + storageTxn: &monitoredTx{ + ID: common.HexToHash("0x1"), + From: common.HexToAddress("0x1"), + Status: MonitoredTxStatusCreated, + }, + shouldUpdate: true, + expectedError: errors.New("failed to get pending nonce for sender: 0x1. Error: some error"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + storage.Transactions = make(map[common.Hash]monitoredTx, 1) + if tt.storageTxn != nil { + storage.Transactions[tt.storageTxn.ID] = *tt.storageTxn + } + + etherman.ExpectedCalls = nil + + if tt.shouldUpdate { + etherman.On("PendingNonce", ctx, common.HexToAddress("0x1")).Return(tt.ethermanNonce, tt.expectedError).Once() + } else if len(tt.expectedResult) > 0 { + etherman.On("CheckTxWasMined", ctx, mock.Anything).Return(tt.expectedResult[0].confirmed, tt.expectedResult[0].lastReceipt, nil) + } + + result, err := client.getMonitoredTxnIteration(ctx) + if tt.expectedError != nil { + require.Error(t, err) + require.ErrorContains(t, err, tt.expectedError.Error()) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedResult, result) + + // now check from storage + if len(tt.expectedResult) > 0 { + dbTxns, err := storage.GetByStatus(ctx, []MonitoredTxStatus{tt.storageTxn.Status}) + require.NoError(t, err) + require.Len(t, dbTxns, 1) + require.Equal(t, tt.expectedResult[0].monitoredTx.Nonce, dbTxns[0].Nonce) + } + } + + etherman.AssertExpectations(t) + }) + } +} diff --git a/ethtxmanager/interfaces.go b/ethtxmanager/interfaces.go index 70da0d9..be45af9 100644 --- a/ethtxmanager/interfaces.go +++ b/ethtxmanager/interfaces.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -type ethermanInterface interface { +type EthermanInterface interface { GetTx(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) GetTxReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) WaitTxToBeMined(ctx context.Context, tx *types.Transaction, timeout time.Duration) (bool, error) @@ -28,7 +28,7 @@ type ethermanInterface interface { HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) } -type storageInterface interface { +type StorageInterface interface { Add(ctx context.Context, mTx monitoredTx) error Remove(ctx context.Context, id common.Hash) error Get(ctx context.Context, id common.Hash) (monitoredTx, error) diff --git a/ethtxmanager/monitoredtx.go b/ethtxmanager/monitoredtx.go index 585e9bd..4708d7c 100644 --- a/ethtxmanager/monitoredtx.go +++ b/ethtxmanager/monitoredtx.go @@ -179,7 +179,7 @@ type monitoredTxnIteration struct { lastReceipt *types.Receipt } -func (m *monitoredTxnIteration) shouldUpdateNonce(ctx context.Context, etherman ethermanInterface) bool { +func (m *monitoredTxnIteration) shouldUpdateNonce(ctx context.Context, etherman EthermanInterface) bool { if m.Status == MonitoredTxStatusCreated { // transaction was not sent, so no need to check if it was mined // we need to update the nonce in this case diff --git a/ethtxmanager/monitoredtx_test.go b/ethtxmanager/monitoredtx_test.go index b0cd8ee..37d62b3 100644 --- a/ethtxmanager/monitoredtx_test.go +++ b/ethtxmanager/monitoredtx_test.go @@ -1,12 +1,16 @@ package ethtxmanager import ( + "context" "math/big" "testing" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/mocks" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestTx(t *testing.T) { @@ -76,3 +80,93 @@ func TestBlobTx(t *testing.T) { assert.Equal(t, blobGas, tx.BlobGas()) assert.Equal(t, blobGasPrice, tx.BlobGasFeeCap()) } + +func TestShouldUpdateNonce(t *testing.T) { + ctx := context.Background() + etherman := mocks.NewEthermanInterface(t) + + tests := []struct { + name string + status MonitoredTxStatus + history map[common.Hash]bool + mockResponses []*mock.Call + expectedResult bool + }{ + { + name: "StatusCreated", + status: MonitoredTxStatusCreated, + history: map[common.Hash]bool{ + common.HexToHash("0x1"): true, + }, + expectedResult: true, + }, + { + name: "ConfirmedTx", + status: MonitoredTxStatusSent, + history: map[common.Hash]bool{ + common.HexToHash("0x1"): true, + }, + mockResponses: []*mock.Call{ + etherman.On("CheckTxWasMined", ctx, common.HexToHash("0x1")).Return(true, &types.Receipt{Status: types.ReceiptStatusSuccessful}, nil), + }, + expectedResult: false, + }, + { + name: "FailedTx", + status: MonitoredTxStatusSent, + history: map[common.Hash]bool{ + common.HexToHash("0x1"): true, + }, + mockResponses: []*mock.Call{ + etherman.On("CheckTxWasMined", ctx, common.HexToHash("0x1")).Return(true, &types.Receipt{Status: types.ReceiptStatusFailed}, nil), + }, + expectedResult: true, + }, + { + name: "PendingTx", + status: MonitoredTxStatusSent, + history: map[common.Hash]bool{ + common.HexToHash("0x1"): true, + }, + mockResponses: []*mock.Call{ + etherman.On("CheckTxWasMined", ctx, common.HexToHash("0x1")).Return(false, nil, nil), + }, + expectedResult: false, + }, + { + name: "MixedTx", + status: MonitoredTxStatusSent, + history: map[common.Hash]bool{ + common.HexToHash("0x1"): true, + common.HexToHash("0x2"): true, + }, + mockResponses: []*mock.Call{ + etherman.On("CheckTxWasMined", ctx, common.HexToHash("0x2")).Return(false, nil, nil), + etherman.On("CheckTxWasMined", ctx, common.HexToHash("0x1")).Return(true, &types.Receipt{Status: types.ReceiptStatusFailed}, nil), + }, + expectedResult: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + etherman.ExpectedCalls = tt.mockResponses + + for _, mockResponse := range tt.mockResponses { + mockResponse.Once() + } + + m := &monitoredTxnIteration{ + monitoredTx: &monitoredTx{ + Status: tt.status, + History: tt.history, + }, + } + + result := m.shouldUpdateNonce(ctx, etherman) + assert.Equal(t, tt.expectedResult, result) + + etherman.AssertExpectations(t) + }) + } +} diff --git a/ethtxmanager/storageinterface.generated.go b/ethtxmanager/storageinterface.generated.go new file mode 100644 index 0000000..82813a8 --- /dev/null +++ b/ethtxmanager/storageinterface.generated.go @@ -0,0 +1,401 @@ +// Code generated by mockery v2.45.0. DO NOT EDIT. + +package ethtxmanager + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" +) + +// StorageInterfaceMock is an autogenerated mock type for the StorageInterface type +type StorageInterfaceMock struct { + mock.Mock +} + +type StorageInterfaceMock_Expecter struct { + mock *mock.Mock +} + +func (_m *StorageInterfaceMock) EXPECT() *StorageInterfaceMock_Expecter { + return &StorageInterfaceMock_Expecter{mock: &_m.Mock} +} + +// Add provides a mock function with given fields: ctx, mTx +func (_m *StorageInterfaceMock) Add(ctx context.Context, mTx monitoredTx) error { + ret := _m.Called(ctx, mTx) + + if len(ret) == 0 { + panic("no return value specified for Add") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, monitoredTx) error); ok { + r0 = rf(ctx, mTx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StorageInterfaceMock_Add_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Add' +type StorageInterfaceMock_Add_Call struct { + *mock.Call +} + +// Add is a helper method to define mock.On call +// - ctx context.Context +// - mTx monitoredTx +func (_e *StorageInterfaceMock_Expecter) Add(ctx interface{}, mTx interface{}) *StorageInterfaceMock_Add_Call { + return &StorageInterfaceMock_Add_Call{Call: _e.mock.On("Add", ctx, mTx)} +} + +func (_c *StorageInterfaceMock_Add_Call) Run(run func(ctx context.Context, mTx monitoredTx)) *StorageInterfaceMock_Add_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(monitoredTx)) + }) + return _c +} + +func (_c *StorageInterfaceMock_Add_Call) Return(_a0 error) *StorageInterfaceMock_Add_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StorageInterfaceMock_Add_Call) RunAndReturn(run func(context.Context, monitoredTx) error) *StorageInterfaceMock_Add_Call { + _c.Call.Return(run) + return _c +} + +// Empty provides a mock function with given fields: ctx +func (_m *StorageInterfaceMock) Empty(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Empty") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StorageInterfaceMock_Empty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Empty' +type StorageInterfaceMock_Empty_Call struct { + *mock.Call +} + +// Empty is a helper method to define mock.On call +// - ctx context.Context +func (_e *StorageInterfaceMock_Expecter) Empty(ctx interface{}) *StorageInterfaceMock_Empty_Call { + return &StorageInterfaceMock_Empty_Call{Call: _e.mock.On("Empty", ctx)} +} + +func (_c *StorageInterfaceMock_Empty_Call) Run(run func(ctx context.Context)) *StorageInterfaceMock_Empty_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *StorageInterfaceMock_Empty_Call) Return(_a0 error) *StorageInterfaceMock_Empty_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StorageInterfaceMock_Empty_Call) RunAndReturn(run func(context.Context) error) *StorageInterfaceMock_Empty_Call { + _c.Call.Return(run) + return _c +} + +// Get provides a mock function with given fields: ctx, id +func (_m *StorageInterfaceMock) Get(ctx context.Context, id common.Hash) (monitoredTx, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 monitoredTx + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (monitoredTx, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) monitoredTx); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(monitoredTx) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StorageInterfaceMock_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type StorageInterfaceMock_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - id common.Hash +func (_e *StorageInterfaceMock_Expecter) Get(ctx interface{}, id interface{}) *StorageInterfaceMock_Get_Call { + return &StorageInterfaceMock_Get_Call{Call: _e.mock.On("Get", ctx, id)} +} + +func (_c *StorageInterfaceMock_Get_Call) Run(run func(ctx context.Context, id common.Hash)) *StorageInterfaceMock_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *StorageInterfaceMock_Get_Call) Return(_a0 monitoredTx, _a1 error) *StorageInterfaceMock_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *StorageInterfaceMock_Get_Call) RunAndReturn(run func(context.Context, common.Hash) (monitoredTx, error)) *StorageInterfaceMock_Get_Call { + _c.Call.Return(run) + return _c +} + +// GetByBlock provides a mock function with given fields: ctx, fromBlock, toBlock +func (_m *StorageInterfaceMock) GetByBlock(ctx context.Context, fromBlock *uint64, toBlock *uint64) ([]monitoredTx, error) { + ret := _m.Called(ctx, fromBlock, toBlock) + + if len(ret) == 0 { + panic("no return value specified for GetByBlock") + } + + var r0 []monitoredTx + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *uint64, *uint64) ([]monitoredTx, error)); ok { + return rf(ctx, fromBlock, toBlock) + } + if rf, ok := ret.Get(0).(func(context.Context, *uint64, *uint64) []monitoredTx); ok { + r0 = rf(ctx, fromBlock, toBlock) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]monitoredTx) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *uint64, *uint64) error); ok { + r1 = rf(ctx, fromBlock, toBlock) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StorageInterfaceMock_GetByBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByBlock' +type StorageInterfaceMock_GetByBlock_Call struct { + *mock.Call +} + +// GetByBlock is a helper method to define mock.On call +// - ctx context.Context +// - fromBlock *uint64 +// - toBlock *uint64 +func (_e *StorageInterfaceMock_Expecter) GetByBlock(ctx interface{}, fromBlock interface{}, toBlock interface{}) *StorageInterfaceMock_GetByBlock_Call { + return &StorageInterfaceMock_GetByBlock_Call{Call: _e.mock.On("GetByBlock", ctx, fromBlock, toBlock)} +} + +func (_c *StorageInterfaceMock_GetByBlock_Call) Run(run func(ctx context.Context, fromBlock *uint64, toBlock *uint64)) *StorageInterfaceMock_GetByBlock_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*uint64), args[2].(*uint64)) + }) + return _c +} + +func (_c *StorageInterfaceMock_GetByBlock_Call) Return(_a0 []monitoredTx, _a1 error) *StorageInterfaceMock_GetByBlock_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *StorageInterfaceMock_GetByBlock_Call) RunAndReturn(run func(context.Context, *uint64, *uint64) ([]monitoredTx, error)) *StorageInterfaceMock_GetByBlock_Call { + _c.Call.Return(run) + return _c +} + +// GetByStatus provides a mock function with given fields: ctx, statuses +func (_m *StorageInterfaceMock) GetByStatus(ctx context.Context, statuses []MonitoredTxStatus) ([]monitoredTx, error) { + ret := _m.Called(ctx, statuses) + + if len(ret) == 0 { + panic("no return value specified for GetByStatus") + } + + var r0 []monitoredTx + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []MonitoredTxStatus) ([]monitoredTx, error)); ok { + return rf(ctx, statuses) + } + if rf, ok := ret.Get(0).(func(context.Context, []MonitoredTxStatus) []monitoredTx); ok { + r0 = rf(ctx, statuses) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]monitoredTx) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []MonitoredTxStatus) error); ok { + r1 = rf(ctx, statuses) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StorageInterfaceMock_GetByStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByStatus' +type StorageInterfaceMock_GetByStatus_Call struct { + *mock.Call +} + +// GetByStatus is a helper method to define mock.On call +// - ctx context.Context +// - statuses []MonitoredTxStatus +func (_e *StorageInterfaceMock_Expecter) GetByStatus(ctx interface{}, statuses interface{}) *StorageInterfaceMock_GetByStatus_Call { + return &StorageInterfaceMock_GetByStatus_Call{Call: _e.mock.On("GetByStatus", ctx, statuses)} +} + +func (_c *StorageInterfaceMock_GetByStatus_Call) Run(run func(ctx context.Context, statuses []MonitoredTxStatus)) *StorageInterfaceMock_GetByStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]MonitoredTxStatus)) + }) + return _c +} + +func (_c *StorageInterfaceMock_GetByStatus_Call) Return(_a0 []monitoredTx, _a1 error) *StorageInterfaceMock_GetByStatus_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *StorageInterfaceMock_GetByStatus_Call) RunAndReturn(run func(context.Context, []MonitoredTxStatus) ([]monitoredTx, error)) *StorageInterfaceMock_GetByStatus_Call { + _c.Call.Return(run) + return _c +} + +// Remove provides a mock function with given fields: ctx, id +func (_m *StorageInterfaceMock) Remove(ctx context.Context, id common.Hash) error { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for Remove") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StorageInterfaceMock_Remove_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Remove' +type StorageInterfaceMock_Remove_Call struct { + *mock.Call +} + +// Remove is a helper method to define mock.On call +// - ctx context.Context +// - id common.Hash +func (_e *StorageInterfaceMock_Expecter) Remove(ctx interface{}, id interface{}) *StorageInterfaceMock_Remove_Call { + return &StorageInterfaceMock_Remove_Call{Call: _e.mock.On("Remove", ctx, id)} +} + +func (_c *StorageInterfaceMock_Remove_Call) Run(run func(ctx context.Context, id common.Hash)) *StorageInterfaceMock_Remove_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *StorageInterfaceMock_Remove_Call) Return(_a0 error) *StorageInterfaceMock_Remove_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StorageInterfaceMock_Remove_Call) RunAndReturn(run func(context.Context, common.Hash) error) *StorageInterfaceMock_Remove_Call { + _c.Call.Return(run) + return _c +} + +// Update provides a mock function with given fields: ctx, mTx +func (_m *StorageInterfaceMock) Update(ctx context.Context, mTx monitoredTx) error { + ret := _m.Called(ctx, mTx) + + if len(ret) == 0 { + panic("no return value specified for Update") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, monitoredTx) error); ok { + r0 = rf(ctx, mTx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StorageInterfaceMock_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' +type StorageInterfaceMock_Update_Call struct { + *mock.Call +} + +// Update is a helper method to define mock.On call +// - ctx context.Context +// - mTx monitoredTx +func (_e *StorageInterfaceMock_Expecter) Update(ctx interface{}, mTx interface{}) *StorageInterfaceMock_Update_Call { + return &StorageInterfaceMock_Update_Call{Call: _e.mock.On("Update", ctx, mTx)} +} + +func (_c *StorageInterfaceMock_Update_Call) Run(run func(ctx context.Context, mTx monitoredTx)) *StorageInterfaceMock_Update_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(monitoredTx)) + }) + return _c +} + +func (_c *StorageInterfaceMock_Update_Call) Return(_a0 error) *StorageInterfaceMock_Update_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StorageInterfaceMock_Update_Call) RunAndReturn(run func(context.Context, monitoredTx) error) *StorageInterfaceMock_Update_Call { + _c.Call.Return(run) + return _c +} + +// NewStorageInterfaceMock creates a new instance of StorageInterfaceMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewStorageInterfaceMock(t interface { + mock.TestingT + Cleanup(func()) +}) *StorageInterfaceMock { + mock := &StorageInterfaceMock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/go.mod b/go.mod index 427d2bb..f0abf87 100644 --- a/go.mod +++ b/go.mod @@ -63,6 +63,7 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect diff --git a/mocks/ethermaninterface.generated.go b/mocks/ethermaninterface.generated.go new file mode 100644 index 0000000..67380dc --- /dev/null +++ b/mocks/ethermaninterface.generated.go @@ -0,0 +1,982 @@ +// Code generated by mockery v2.45.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + time "time" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// EthermanInterface is an autogenerated mock type for the EthermanInterface type +type EthermanInterface struct { + mock.Mock +} + +type EthermanInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *EthermanInterface) EXPECT() *EthermanInterface_Expecter { + return &EthermanInterface_Expecter{mock: &_m.Mock} +} + +// CheckTxWasMined provides a mock function with given fields: ctx, txHash +func (_m *EthermanInterface) CheckTxWasMined(ctx context.Context, txHash common.Hash) (bool, *types.Receipt, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for CheckTxWasMined") + } + + var r0 bool + var r1 *types.Receipt + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (bool, *types.Receipt, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) bool); ok { + r0 = rf(ctx, txHash) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) *types.Receipt); ok { + r1 = rf(ctx, txHash) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*types.Receipt) + } + } + + if rf, ok := ret.Get(2).(func(context.Context, common.Hash) error); ok { + r2 = rf(ctx, txHash) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// EthermanInterface_CheckTxWasMined_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckTxWasMined' +type EthermanInterface_CheckTxWasMined_Call struct { + *mock.Call +} + +// CheckTxWasMined is a helper method to define mock.On call +// - ctx context.Context +// - txHash common.Hash +func (_e *EthermanInterface_Expecter) CheckTxWasMined(ctx interface{}, txHash interface{}) *EthermanInterface_CheckTxWasMined_Call { + return &EthermanInterface_CheckTxWasMined_Call{Call: _e.mock.On("CheckTxWasMined", ctx, txHash)} +} + +func (_c *EthermanInterface_CheckTxWasMined_Call) Run(run func(ctx context.Context, txHash common.Hash)) *EthermanInterface_CheckTxWasMined_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *EthermanInterface_CheckTxWasMined_Call) Return(_a0 bool, _a1 *types.Receipt, _a2 error) *EthermanInterface_CheckTxWasMined_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *EthermanInterface_CheckTxWasMined_Call) RunAndReturn(run func(context.Context, common.Hash) (bool, *types.Receipt, error)) *EthermanInterface_CheckTxWasMined_Call { + _c.Call.Return(run) + return _c +} + +// CurrentNonce provides a mock function with given fields: ctx, account +func (_m *EthermanInterface) CurrentNonce(ctx context.Context, account common.Address) (uint64, error) { + ret := _m.Called(ctx, account) + + if len(ret) == 0 { + panic("no return value specified for CurrentNonce") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) (uint64, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) uint64); ok { + r0 = rf(ctx, account) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, account) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_CurrentNonce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentNonce' +type EthermanInterface_CurrentNonce_Call struct { + *mock.Call +} + +// CurrentNonce is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +func (_e *EthermanInterface_Expecter) CurrentNonce(ctx interface{}, account interface{}) *EthermanInterface_CurrentNonce_Call { + return &EthermanInterface_CurrentNonce_Call{Call: _e.mock.On("CurrentNonce", ctx, account)} +} + +func (_c *EthermanInterface_CurrentNonce_Call) Run(run func(ctx context.Context, account common.Address)) *EthermanInterface_CurrentNonce_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address)) + }) + return _c +} + +func (_c *EthermanInterface_CurrentNonce_Call) Return(_a0 uint64, _a1 error) *EthermanInterface_CurrentNonce_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_CurrentNonce_Call) RunAndReturn(run func(context.Context, common.Address) (uint64, error)) *EthermanInterface_CurrentNonce_Call { + _c.Call.Return(run) + return _c +} + +// EstimateGas provides a mock function with given fields: ctx, from, to, value, data +func (_m *EthermanInterface) EstimateGas(ctx context.Context, from common.Address, to *common.Address, value *big.Int, data []byte) (uint64, error) { + ret := _m.Called(ctx, from, to, value, data) + + if len(ret) == 0 { + panic("no return value specified for EstimateGas") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *common.Address, *big.Int, []byte) (uint64, error)); ok { + return rf(ctx, from, to, value, data) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *common.Address, *big.Int, []byte) uint64); ok { + r0 = rf(ctx, from, to, value, data) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *common.Address, *big.Int, []byte) error); ok { + r1 = rf(ctx, from, to, value, data) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_EstimateGas_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGas' +type EthermanInterface_EstimateGas_Call struct { + *mock.Call +} + +// EstimateGas is a helper method to define mock.On call +// - ctx context.Context +// - from common.Address +// - to *common.Address +// - value *big.Int +// - data []byte +func (_e *EthermanInterface_Expecter) EstimateGas(ctx interface{}, from interface{}, to interface{}, value interface{}, data interface{}) *EthermanInterface_EstimateGas_Call { + return &EthermanInterface_EstimateGas_Call{Call: _e.mock.On("EstimateGas", ctx, from, to, value, data)} +} + +func (_c *EthermanInterface_EstimateGas_Call) Run(run func(ctx context.Context, from common.Address, to *common.Address, value *big.Int, data []byte)) *EthermanInterface_EstimateGas_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(*common.Address), args[3].(*big.Int), args[4].([]byte)) + }) + return _c +} + +func (_c *EthermanInterface_EstimateGas_Call) Return(_a0 uint64, _a1 error) *EthermanInterface_EstimateGas_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_EstimateGas_Call) RunAndReturn(run func(context.Context, common.Address, *common.Address, *big.Int, []byte) (uint64, error)) *EthermanInterface_EstimateGas_Call { + _c.Call.Return(run) + return _c +} + +// EstimateGasBlobTx provides a mock function with given fields: ctx, from, to, gasFeeCap, gasTipCap, value, data +func (_m *EthermanInterface) EstimateGasBlobTx(ctx context.Context, from common.Address, to *common.Address, gasFeeCap *big.Int, gasTipCap *big.Int, value *big.Int, data []byte) (uint64, error) { + ret := _m.Called(ctx, from, to, gasFeeCap, gasTipCap, value, data) + + if len(ret) == 0 { + panic("no return value specified for EstimateGasBlobTx") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *common.Address, *big.Int, *big.Int, *big.Int, []byte) (uint64, error)); ok { + return rf(ctx, from, to, gasFeeCap, gasTipCap, value, data) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *common.Address, *big.Int, *big.Int, *big.Int, []byte) uint64); ok { + r0 = rf(ctx, from, to, gasFeeCap, gasTipCap, value, data) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *common.Address, *big.Int, *big.Int, *big.Int, []byte) error); ok { + r1 = rf(ctx, from, to, gasFeeCap, gasTipCap, value, data) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_EstimateGasBlobTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGasBlobTx' +type EthermanInterface_EstimateGasBlobTx_Call struct { + *mock.Call +} + +// EstimateGasBlobTx is a helper method to define mock.On call +// - ctx context.Context +// - from common.Address +// - to *common.Address +// - gasFeeCap *big.Int +// - gasTipCap *big.Int +// - value *big.Int +// - data []byte +func (_e *EthermanInterface_Expecter) EstimateGasBlobTx(ctx interface{}, from interface{}, to interface{}, gasFeeCap interface{}, gasTipCap interface{}, value interface{}, data interface{}) *EthermanInterface_EstimateGasBlobTx_Call { + return &EthermanInterface_EstimateGasBlobTx_Call{Call: _e.mock.On("EstimateGasBlobTx", ctx, from, to, gasFeeCap, gasTipCap, value, data)} +} + +func (_c *EthermanInterface_EstimateGasBlobTx_Call) Run(run func(ctx context.Context, from common.Address, to *common.Address, gasFeeCap *big.Int, gasTipCap *big.Int, value *big.Int, data []byte)) *EthermanInterface_EstimateGasBlobTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(*common.Address), args[3].(*big.Int), args[4].(*big.Int), args[5].(*big.Int), args[6].([]byte)) + }) + return _c +} + +func (_c *EthermanInterface_EstimateGasBlobTx_Call) Return(_a0 uint64, _a1 error) *EthermanInterface_EstimateGasBlobTx_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_EstimateGasBlobTx_Call) RunAndReturn(run func(context.Context, common.Address, *common.Address, *big.Int, *big.Int, *big.Int, []byte) (uint64, error)) *EthermanInterface_EstimateGasBlobTx_Call { + _c.Call.Return(run) + return _c +} + +// GetHeaderByNumber provides a mock function with given fields: ctx, number +func (_m *EthermanInterface) GetHeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for GetHeaderByNumber") + } + + var r0 *types.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Header); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_GetHeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetHeaderByNumber' +type EthermanInterface_GetHeaderByNumber_Call struct { + *mock.Call +} + +// GetHeaderByNumber is a helper method to define mock.On call +// - ctx context.Context +// - number *big.Int +func (_e *EthermanInterface_Expecter) GetHeaderByNumber(ctx interface{}, number interface{}) *EthermanInterface_GetHeaderByNumber_Call { + return &EthermanInterface_GetHeaderByNumber_Call{Call: _e.mock.On("GetHeaderByNumber", ctx, number)} +} + +func (_c *EthermanInterface_GetHeaderByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *EthermanInterface_GetHeaderByNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*big.Int)) + }) + return _c +} + +func (_c *EthermanInterface_GetHeaderByNumber_Call) Return(_a0 *types.Header, _a1 error) *EthermanInterface_GetHeaderByNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_GetHeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*types.Header, error)) *EthermanInterface_GetHeaderByNumber_Call { + _c.Call.Return(run) + return _c +} + +// GetLatestBlockNumber provides a mock function with given fields: ctx +func (_m *EthermanInterface) GetLatestBlockNumber(ctx context.Context) (uint64, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetLatestBlockNumber") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) uint64); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_GetLatestBlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLatestBlockNumber' +type EthermanInterface_GetLatestBlockNumber_Call struct { + *mock.Call +} + +// GetLatestBlockNumber is a helper method to define mock.On call +// - ctx context.Context +func (_e *EthermanInterface_Expecter) GetLatestBlockNumber(ctx interface{}) *EthermanInterface_GetLatestBlockNumber_Call { + return &EthermanInterface_GetLatestBlockNumber_Call{Call: _e.mock.On("GetLatestBlockNumber", ctx)} +} + +func (_c *EthermanInterface_GetLatestBlockNumber_Call) Run(run func(ctx context.Context)) *EthermanInterface_GetLatestBlockNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EthermanInterface_GetLatestBlockNumber_Call) Return(_a0 uint64, _a1 error) *EthermanInterface_GetLatestBlockNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_GetLatestBlockNumber_Call) RunAndReturn(run func(context.Context) (uint64, error)) *EthermanInterface_GetLatestBlockNumber_Call { + _c.Call.Return(run) + return _c +} + +// GetRevertMessage provides a mock function with given fields: ctx, tx +func (_m *EthermanInterface) GetRevertMessage(ctx context.Context, tx *types.Transaction) (string, error) { + ret := _m.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for GetRevertMessage") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction) (string, error)); ok { + return rf(ctx, tx) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction) string); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.Transaction) error); ok { + r1 = rf(ctx, tx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_GetRevertMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRevertMessage' +type EthermanInterface_GetRevertMessage_Call struct { + *mock.Call +} + +// GetRevertMessage is a helper method to define mock.On call +// - ctx context.Context +// - tx *types.Transaction +func (_e *EthermanInterface_Expecter) GetRevertMessage(ctx interface{}, tx interface{}) *EthermanInterface_GetRevertMessage_Call { + return &EthermanInterface_GetRevertMessage_Call{Call: _e.mock.On("GetRevertMessage", ctx, tx)} +} + +func (_c *EthermanInterface_GetRevertMessage_Call) Run(run func(ctx context.Context, tx *types.Transaction)) *EthermanInterface_GetRevertMessage_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.Transaction)) + }) + return _c +} + +func (_c *EthermanInterface_GetRevertMessage_Call) Return(_a0 string, _a1 error) *EthermanInterface_GetRevertMessage_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_GetRevertMessage_Call) RunAndReturn(run func(context.Context, *types.Transaction) (string, error)) *EthermanInterface_GetRevertMessage_Call { + _c.Call.Return(run) + return _c +} + +// GetSuggestGasTipCap provides a mock function with given fields: ctx +func (_m *EthermanInterface) GetSuggestGasTipCap(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetSuggestGasTipCap") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_GetSuggestGasTipCap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSuggestGasTipCap' +type EthermanInterface_GetSuggestGasTipCap_Call struct { + *mock.Call +} + +// GetSuggestGasTipCap is a helper method to define mock.On call +// - ctx context.Context +func (_e *EthermanInterface_Expecter) GetSuggestGasTipCap(ctx interface{}) *EthermanInterface_GetSuggestGasTipCap_Call { + return &EthermanInterface_GetSuggestGasTipCap_Call{Call: _e.mock.On("GetSuggestGasTipCap", ctx)} +} + +func (_c *EthermanInterface_GetSuggestGasTipCap_Call) Run(run func(ctx context.Context)) *EthermanInterface_GetSuggestGasTipCap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EthermanInterface_GetSuggestGasTipCap_Call) Return(_a0 *big.Int, _a1 error) *EthermanInterface_GetSuggestGasTipCap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_GetSuggestGasTipCap_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *EthermanInterface_GetSuggestGasTipCap_Call { + _c.Call.Return(run) + return _c +} + +// GetTx provides a mock function with given fields: ctx, txHash +func (_m *EthermanInterface) GetTx(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for GetTx") + } + + var r0 *types.Transaction + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Transaction, bool, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Transaction); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) bool); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func(context.Context, common.Hash) error); ok { + r2 = rf(ctx, txHash) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// EthermanInterface_GetTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTx' +type EthermanInterface_GetTx_Call struct { + *mock.Call +} + +// GetTx is a helper method to define mock.On call +// - ctx context.Context +// - txHash common.Hash +func (_e *EthermanInterface_Expecter) GetTx(ctx interface{}, txHash interface{}) *EthermanInterface_GetTx_Call { + return &EthermanInterface_GetTx_Call{Call: _e.mock.On("GetTx", ctx, txHash)} +} + +func (_c *EthermanInterface_GetTx_Call) Run(run func(ctx context.Context, txHash common.Hash)) *EthermanInterface_GetTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *EthermanInterface_GetTx_Call) Return(_a0 *types.Transaction, _a1 bool, _a2 error) *EthermanInterface_GetTx_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *EthermanInterface_GetTx_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Transaction, bool, error)) *EthermanInterface_GetTx_Call { + _c.Call.Return(run) + return _c +} + +// GetTxReceipt provides a mock function with given fields: ctx, txHash +func (_m *EthermanInterface) GetTxReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for GetTxReceipt") + } + + var r0 *types.Receipt + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Receipt, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Receipt); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Receipt) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_GetTxReceipt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTxReceipt' +type EthermanInterface_GetTxReceipt_Call struct { + *mock.Call +} + +// GetTxReceipt is a helper method to define mock.On call +// - ctx context.Context +// - txHash common.Hash +func (_e *EthermanInterface_Expecter) GetTxReceipt(ctx interface{}, txHash interface{}) *EthermanInterface_GetTxReceipt_Call { + return &EthermanInterface_GetTxReceipt_Call{Call: _e.mock.On("GetTxReceipt", ctx, txHash)} +} + +func (_c *EthermanInterface_GetTxReceipt_Call) Run(run func(ctx context.Context, txHash common.Hash)) *EthermanInterface_GetTxReceipt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *EthermanInterface_GetTxReceipt_Call) Return(_a0 *types.Receipt, _a1 error) *EthermanInterface_GetTxReceipt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_GetTxReceipt_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Receipt, error)) *EthermanInterface_GetTxReceipt_Call { + _c.Call.Return(run) + return _c +} + +// HeaderByNumber provides a mock function with given fields: ctx, number +func (_m *EthermanInterface) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + + var r0 *types.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Header); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_HeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByNumber' +type EthermanInterface_HeaderByNumber_Call struct { + *mock.Call +} + +// HeaderByNumber is a helper method to define mock.On call +// - ctx context.Context +// - number *big.Int +func (_e *EthermanInterface_Expecter) HeaderByNumber(ctx interface{}, number interface{}) *EthermanInterface_HeaderByNumber_Call { + return &EthermanInterface_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, number)} +} + +func (_c *EthermanInterface_HeaderByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *EthermanInterface_HeaderByNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*big.Int)) + }) + return _c +} + +func (_c *EthermanInterface_HeaderByNumber_Call) Return(_a0 *types.Header, _a1 error) *EthermanInterface_HeaderByNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*types.Header, error)) *EthermanInterface_HeaderByNumber_Call { + _c.Call.Return(run) + return _c +} + +// PendingNonce provides a mock function with given fields: ctx, account +func (_m *EthermanInterface) PendingNonce(ctx context.Context, account common.Address) (uint64, error) { + ret := _m.Called(ctx, account) + + if len(ret) == 0 { + panic("no return value specified for PendingNonce") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) (uint64, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) uint64); ok { + r0 = rf(ctx, account) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, account) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_PendingNonce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingNonce' +type EthermanInterface_PendingNonce_Call struct { + *mock.Call +} + +// PendingNonce is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +func (_e *EthermanInterface_Expecter) PendingNonce(ctx interface{}, account interface{}) *EthermanInterface_PendingNonce_Call { + return &EthermanInterface_PendingNonce_Call{Call: _e.mock.On("PendingNonce", ctx, account)} +} + +func (_c *EthermanInterface_PendingNonce_Call) Run(run func(ctx context.Context, account common.Address)) *EthermanInterface_PendingNonce_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address)) + }) + return _c +} + +func (_c *EthermanInterface_PendingNonce_Call) Return(_a0 uint64, _a1 error) *EthermanInterface_PendingNonce_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_PendingNonce_Call) RunAndReturn(run func(context.Context, common.Address) (uint64, error)) *EthermanInterface_PendingNonce_Call { + _c.Call.Return(run) + return _c +} + +// SendTx provides a mock function with given fields: ctx, tx +func (_m *EthermanInterface) SendTx(ctx context.Context, tx *types.Transaction) error { + ret := _m.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for SendTx") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EthermanInterface_SendTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTx' +type EthermanInterface_SendTx_Call struct { + *mock.Call +} + +// SendTx is a helper method to define mock.On call +// - ctx context.Context +// - tx *types.Transaction +func (_e *EthermanInterface_Expecter) SendTx(ctx interface{}, tx interface{}) *EthermanInterface_SendTx_Call { + return &EthermanInterface_SendTx_Call{Call: _e.mock.On("SendTx", ctx, tx)} +} + +func (_c *EthermanInterface_SendTx_Call) Run(run func(ctx context.Context, tx *types.Transaction)) *EthermanInterface_SendTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.Transaction)) + }) + return _c +} + +func (_c *EthermanInterface_SendTx_Call) Return(_a0 error) *EthermanInterface_SendTx_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EthermanInterface_SendTx_Call) RunAndReturn(run func(context.Context, *types.Transaction) error) *EthermanInterface_SendTx_Call { + _c.Call.Return(run) + return _c +} + +// SignTx provides a mock function with given fields: ctx, sender, tx +func (_m *EthermanInterface) SignTx(ctx context.Context, sender common.Address, tx *types.Transaction) (*types.Transaction, error) { + ret := _m.Called(ctx, sender, tx) + + if len(ret) == 0 { + panic("no return value specified for SignTx") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *types.Transaction) (*types.Transaction, error)); ok { + return rf(ctx, sender, tx) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *types.Transaction) *types.Transaction); ok { + r0 = rf(ctx, sender, tx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *types.Transaction) error); ok { + r1 = rf(ctx, sender, tx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_SignTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SignTx' +type EthermanInterface_SignTx_Call struct { + *mock.Call +} + +// SignTx is a helper method to define mock.On call +// - ctx context.Context +// - sender common.Address +// - tx *types.Transaction +func (_e *EthermanInterface_Expecter) SignTx(ctx interface{}, sender interface{}, tx interface{}) *EthermanInterface_SignTx_Call { + return &EthermanInterface_SignTx_Call{Call: _e.mock.On("SignTx", ctx, sender, tx)} +} + +func (_c *EthermanInterface_SignTx_Call) Run(run func(ctx context.Context, sender common.Address, tx *types.Transaction)) *EthermanInterface_SignTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(*types.Transaction)) + }) + return _c +} + +func (_c *EthermanInterface_SignTx_Call) Return(_a0 *types.Transaction, _a1 error) *EthermanInterface_SignTx_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_SignTx_Call) RunAndReturn(run func(context.Context, common.Address, *types.Transaction) (*types.Transaction, error)) *EthermanInterface_SignTx_Call { + _c.Call.Return(run) + return _c +} + +// SuggestedGasPrice provides a mock function with given fields: ctx +func (_m *EthermanInterface) SuggestedGasPrice(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SuggestedGasPrice") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_SuggestedGasPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestedGasPrice' +type EthermanInterface_SuggestedGasPrice_Call struct { + *mock.Call +} + +// SuggestedGasPrice is a helper method to define mock.On call +// - ctx context.Context +func (_e *EthermanInterface_Expecter) SuggestedGasPrice(ctx interface{}) *EthermanInterface_SuggestedGasPrice_Call { + return &EthermanInterface_SuggestedGasPrice_Call{Call: _e.mock.On("SuggestedGasPrice", ctx)} +} + +func (_c *EthermanInterface_SuggestedGasPrice_Call) Run(run func(ctx context.Context)) *EthermanInterface_SuggestedGasPrice_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EthermanInterface_SuggestedGasPrice_Call) Return(_a0 *big.Int, _a1 error) *EthermanInterface_SuggestedGasPrice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_SuggestedGasPrice_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *EthermanInterface_SuggestedGasPrice_Call { + _c.Call.Return(run) + return _c +} + +// WaitTxToBeMined provides a mock function with given fields: ctx, tx, timeout +func (_m *EthermanInterface) WaitTxToBeMined(ctx context.Context, tx *types.Transaction, timeout time.Duration) (bool, error) { + ret := _m.Called(ctx, tx, timeout) + + if len(ret) == 0 { + panic("no return value specified for WaitTxToBeMined") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, time.Duration) (bool, error)); ok { + return rf(ctx, tx, timeout) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, time.Duration) bool); ok { + r0 = rf(ctx, tx, timeout) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.Transaction, time.Duration) error); ok { + r1 = rf(ctx, tx, timeout) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthermanInterface_WaitTxToBeMined_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WaitTxToBeMined' +type EthermanInterface_WaitTxToBeMined_Call struct { + *mock.Call +} + +// WaitTxToBeMined is a helper method to define mock.On call +// - ctx context.Context +// - tx *types.Transaction +// - timeout time.Duration +func (_e *EthermanInterface_Expecter) WaitTxToBeMined(ctx interface{}, tx interface{}, timeout interface{}) *EthermanInterface_WaitTxToBeMined_Call { + return &EthermanInterface_WaitTxToBeMined_Call{Call: _e.mock.On("WaitTxToBeMined", ctx, tx, timeout)} +} + +func (_c *EthermanInterface_WaitTxToBeMined_Call) Run(run func(ctx context.Context, tx *types.Transaction, timeout time.Duration)) *EthermanInterface_WaitTxToBeMined_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.Transaction), args[2].(time.Duration)) + }) + return _c +} + +func (_c *EthermanInterface_WaitTxToBeMined_Call) Return(_a0 bool, _a1 error) *EthermanInterface_WaitTxToBeMined_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthermanInterface_WaitTxToBeMined_Call) RunAndReturn(run func(context.Context, *types.Transaction, time.Duration) (bool, error)) *EthermanInterface_WaitTxToBeMined_Call { + _c.Call.Return(run) + return _c +} + +// NewEthermanInterface creates a new instance of EthermanInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEthermanInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *EthermanInterface { + mock := &EthermanInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 8b4bd5e8b0bed428c3078167221d65aa708b643f Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Mon, 30 Sep 2024 13:39:43 +0200 Subject: [PATCH 4/4] fix: lint --- .golangci.yml | 1 + ethtxmanager/interfaces.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index df418a6..31fa013 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,6 +3,7 @@ run: timeout: 5m skip-dirs: - test + - mocks linters: diff --git a/ethtxmanager/interfaces.go b/ethtxmanager/interfaces.go index be45af9..4ffeb7c 100644 --- a/ethtxmanager/interfaces.go +++ b/ethtxmanager/interfaces.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) +// EthermanInterface is the interface that wraps the basic Ethereum transaction operations. type EthermanInterface interface { GetTx(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) GetTxReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) @@ -28,6 +29,7 @@ type EthermanInterface interface { HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) } +// StorageInterface is the interface that wraps the basic storage operations for monitored transactions. type StorageInterface interface { Add(ctx context.Context, mTx monitoredTx) error Remove(ctx context.Context, id common.Hash) error