diff --git a/aggoracle/oracle.go b/aggoracle/oracle.go index 885e4feee..db6a0c1fe 100644 --- a/aggoracle/oracle.go +++ b/aggoracle/oracle.go @@ -8,7 +8,7 @@ import ( "github.com/agglayer/aggkit/aggoracle/metrics" "github.com/agglayer/aggkit/l1infotreesync" "github.com/agglayer/aggkit/log" - "github.com/ethereum/go-ethereum" + "github.com/agglayer/aggkit/types" "github.com/ethereum/go-ethereum/common" ) @@ -29,7 +29,7 @@ type ChainSender interface { type AggOracle struct { logger *log.Logger waitPeriodNextGER time.Duration - l1Client ethereum.ChainReader + l1Client types.EthChainReader l1Info L1InfoTreeSyncer chainSender ChainSender } @@ -38,7 +38,7 @@ type AggOracle struct { func New( logger *log.Logger, chainSender ChainSender, - l1Client ethereum.ChainReader, + l1Client types.EthChainReader, l1InfoTreeSyncer L1InfoTreeSyncer, waitPeriodNextGER time.Duration, ) (*AggOracle, error) { diff --git a/aggsender/query/l1info_tree_data_query.go b/aggsender/query/l1info_tree_data_query.go index 47601efa1..1e1860055 100644 --- a/aggsender/query/l1info_tree_data_query.go +++ b/aggsender/query/l1info_tree_data_query.go @@ -3,7 +3,6 @@ package query import ( "context" "fmt" - "math/big" "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayerger" "github.com/agglayer/aggkit/aggsender/types" @@ -15,8 +14,6 @@ import ( "github.com/ethereum/go-ethereum/common" ) -var finalizedBlockBigInt = big.NewInt(int64(aggkittypes.Finalized)) - var _ types.L1InfoTreeDataQuerier = (*L1InfoTreeDataQuerier)(nil) // L1InfoTreeDataQuerier is a struct that holds the logic to query the L1 Info tree data @@ -143,13 +140,13 @@ func (l *L1InfoTreeDataQuerier) GetProofForGER( // getLatestProcessedFinalizedBlock returns the latest processed finalized block from the l1infotreesyncer func (l *L1InfoTreeDataQuerier) getLatestProcessedFinalizedBlock(ctx context.Context) (uint64, error) { - lastFinalizedL1Block, err := l.l1Client.HeaderByNumber(ctx, finalizedBlockBigInt) + lastFinalizedL1Block, err := l.l1Client.CustomHeaderByNumber(ctx, &aggkittypes.FinalizedBlock) if err != nil { return 0, fmt.Errorf("error getting latest finalized L1 block: %w", err) } lastProcessedBlockNum, lastProcessedBlockHash, err := l.l1InfoTreeSyncer.GetProcessedBlockUntil(ctx, - lastFinalizedL1Block.Number.Uint64()) + lastFinalizedL1Block.Number) if err != nil { return 0, fmt.Errorf("error getting latest processed block from l1infotreesyncer: %w", err) } @@ -158,25 +155,25 @@ func (l *L1InfoTreeDataQuerier) getLatestProcessedFinalizedBlock(ctx context.Con return 0, fmt.Errorf("l1infotreesyncer did not process any block yet") } - if lastFinalizedL1Block.Number.Uint64() > lastProcessedBlockNum { + if lastFinalizedL1Block.Number > lastProcessedBlockNum { // syncer has a lower block than the finalized block, so we need to get that block from the l1 node - lastFinalizedL1Block, err = l.l1Client.HeaderByNumber(ctx, new(big.Int).SetUint64(lastProcessedBlockNum)) + lastFinalizedL1Block, err = l.l1Client.CustomHeaderByNumber(ctx, aggkittypes.NewBlockNumber(lastProcessedBlockNum)) if err != nil { return 0, fmt.Errorf("error getting latest processed finalized block: %d: %w", lastProcessedBlockNum, err) } } - if (lastProcessedBlockHash == common.Hash{}) || (lastProcessedBlockHash == lastFinalizedL1Block.Hash()) { + if (lastProcessedBlockHash == common.Hash{}) || (lastProcessedBlockHash == lastFinalizedL1Block.Hash) { // if the hash is empty it means that this is an old block that was processed before this // feature was added, so we will consider it finalized - return lastFinalizedL1Block.Number.Uint64(), nil + return lastFinalizedL1Block.Number, nil } return 0, fmt.Errorf("l1infotreesyncer returned a different hash for "+ "the latest finalized block: %d. Might be that syncer did not process a reorg yet. "+ "Expected hash: %s, got: %s", lastProcessedBlockNum, - lastFinalizedL1Block.Hash().String(), lastProcessedBlockHash.String()) + lastFinalizedL1Block.Hash.String(), lastProcessedBlockHash.String()) } // GetInfoByIndex returns the L1 Info tree leaf for the given index diff --git a/aggsender/query/l1info_tree_data_query_test.go b/aggsender/query/l1info_tree_data_query_test.go index 3bad0ab39..468ab482f 100644 --- a/aggsender/query/l1info_tree_data_query_test.go +++ b/aggsender/query/l1info_tree_data_query_test.go @@ -3,15 +3,14 @@ package query import ( "context" "errors" - "math/big" "testing" "github.com/agglayer/aggkit/aggsender/mocks" "github.com/agglayer/aggkit/l1infotreesync" treetypes "github.com/agglayer/aggkit/tree/types" + aggkittypes "github.com/agglayer/aggkit/types" aggkittypesmocks "github.com/agglayer/aggkit/types/mocks" "github.com/ethereum/go-ethereum/common" - gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" ) @@ -103,6 +102,8 @@ func Test_GetFinalizedL1InfoTreeData(t *testing.T) { } } +var finalizedBlockBigInt = &aggkittypes.FinalizedBlock + func Test_AggchainProverFlow_GetLatestProcessedFinalizedBlock(t *testing.T) { t.Parallel() @@ -117,45 +118,45 @@ func Test_AggchainProverFlow_GetLatestProcessedFinalizedBlock(t *testing.T) { { name: "error getting latest finalized L1 block", mockFn: func(mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncer, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { - mockL1Client.On("HeaderByNumber", ctx, finalizedBlockBigInt).Return(nil, errors.New("some error")) + mockL1Client.On("CustomHeaderByNumber", ctx, finalizedBlockBigInt).Return(nil, errors.New("some error")) }, expectedError: "error getting latest finalized L1 block: some error", }, { name: "error getting latest processed block from l1infotreesyncer", mockFn: func(mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncer, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { - l1Header := &gethtypes.Header{Number: big.NewInt(10)} - mockL1Client.On("HeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) - mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number.Uint64()).Return(uint64(0), common.Hash{}, errors.New("some error")) + l1Header := &aggkittypes.BlockHeader{Number: 10} + mockL1Client.On("CustomHeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) + mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number).Return(uint64(0), common.Hash{}, errors.New("some error")) }, expectedError: "error getting latest processed block from l1infotreesyncer: some error", }, { name: "l1infotreesyncer did not process any block yet", mockFn: func(mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncer, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { - l1Header := &gethtypes.Header{Number: big.NewInt(10)} - mockL1Client.On("HeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) - mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number.Uint64()).Return(uint64(0), common.Hash{}, nil) + l1Header := &aggkittypes.BlockHeader{Number: 10} + mockL1Client.On("CustomHeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) + mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number).Return(uint64(0), common.Hash{}, nil) }, expectedError: "l1infotreesyncer did not process any block yet", }, { name: "error getting latest processed finalized block", mockFn: func(mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncer, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { - l1Header := &gethtypes.Header{Number: big.NewInt(10)} - mockL1Client.On("HeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) - mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number.Uint64()).Return(uint64(9), common.Hash{}, nil) - mockL1Client.On("HeaderByNumber", ctx, big.NewInt(9)).Return(nil, errors.New("some error")) + l1Header := &aggkittypes.BlockHeader{Number: 10} + mockL1Client.On("CustomHeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) + mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number).Return(uint64(9), common.Hash{}, nil) + mockL1Client.On("CustomHeaderByNumber", ctx, aggkittypes.NewBlockNumber(9)).Return(nil, errors.New("some error")) }, expectedError: "error getting latest processed finalized block: 9: some error", }, { name: "l1infotreesyncer returned a different hash for the latest finalized block", mockFn: func(mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncer, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { - l1Header := &gethtypes.Header{Number: big.NewInt(10)} - mockL1Client.On("HeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) - mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number.Uint64()).Return( - l1Header.Number.Uint64(), common.HexToHash("0x2"), nil) + l1Header := &aggkittypes.BlockHeader{Number: 10, Hash: common.HexToHash("0xabc")} + mockL1Client.On("CustomHeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) + mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number).Return( + l1Header.Number, common.HexToHash("0x2"), nil) }, expectedError: "l1infotreesyncer returned a different hash for the latest finalized block: 10. " + "Might be that syncer did not process a reorg yet.", @@ -163,10 +164,10 @@ func Test_AggchainProverFlow_GetLatestProcessedFinalizedBlock(t *testing.T) { { name: "success", mockFn: func(mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncer, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { - l1Header := &gethtypes.Header{Number: big.NewInt(10)} - mockL1Client.On("HeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) - mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number.Uint64()).Return( - l1Header.Number.Uint64(), l1Header.Hash(), nil) + l1Header := &aggkittypes.BlockHeader{Number: 10} + mockL1Client.On("CustomHeaderByNumber", ctx, finalizedBlockBigInt).Return(l1Header, nil) + mockL1InfoTreeSyncer.On("GetProcessedBlockUntil", ctx, l1Header.Number).Return( + l1Header.Number, l1Header.Hash, nil) }, expectedBlock: 10, }, diff --git a/bridgesync/agglayer_bridge_l2_reader_test.go b/bridgesync/agglayer_bridge_l2_reader_test.go index 8ed09da71..c4a1a9827 100644 --- a/bridgesync/agglayer_bridge_l2_reader_test.go +++ b/bridgesync/agglayer_bridge_l2_reader_test.go @@ -7,6 +7,7 @@ import ( "github.com/agglayer/aggkit/bridgesync/types" aggkitcommon "github.com/agglayer/aggkit/common" + "github.com/agglayer/aggkit/etherman" aggkittypes "github.com/agglayer/aggkit/types" mocksethclient "github.com/agglayer/aggkit/types/mocks" "github.com/ethereum/go-ethereum/common" @@ -228,7 +229,7 @@ func TestAgglayerBridgeL2Reader_GetUnsetClaimsForBlockRange_SimulatedBackend(t * // Use the client from the simulated backend client := simulatedBackend.Client() - reader, err := NewAgglayerBridgeL2Reader(bridgeAddr, client) + reader, err := NewAgglayerBridgeL2Reader(bridgeAddr, etherman.NewDefaultEthClient(client, nil, nil)) require.NoError(t, err) // Test with the simulated backend - need to mine some blocks first @@ -253,7 +254,7 @@ func TestAgglayerBridgeL2Reader_GetUnsetClaimsForBlockRange_WithRealEvents(t *te // Use the client from the simulated backend client := simulatedBackend.Client() - reader, err := NewAgglayerBridgeL2Reader(bridgeAddr, client) + reader, err := NewAgglayerBridgeL2Reader(bridgeAddr, etherman.NewDefaultEthClient(client, nil, nil)) require.NoError(t, err) // Mine some blocks to create a valid range @@ -281,7 +282,7 @@ func TestAgglayerBridgeL2Reader_GetUnsetClaimsForBlockRange_IteratorBehavior(t * // Use the client from the simulated backend client := simulatedBackend.Client() - reader, err := NewAgglayerBridgeL2Reader(bridgeAddr, client) + reader, err := NewAgglayerBridgeL2Reader(bridgeAddr, etherman.NewDefaultEthClient(client, nil, nil)) require.NoError(t, err) // Mine some blocks to create a valid range diff --git a/bridgesync/backfill_tx_sender_test.go b/bridgesync/backfill_tx_sender_test.go index 685229a4b..9a3c714f1 100644 --- a/bridgesync/backfill_tx_sender_test.go +++ b/bridgesync/backfill_tx_sender_test.go @@ -13,8 +13,9 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayerbridge" "github.com/agglayer/aggkit/bridgesync/migrations" "github.com/agglayer/aggkit/db" + "github.com/agglayer/aggkit/etherman" + ethermanconfig "github.com/agglayer/aggkit/etherman/config" "github.com/agglayer/aggkit/log" - aggkittypes "github.com/agglayer/aggkit/types" "github.com/agglayer/aggkit/types/mocks" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/mock" @@ -1635,13 +1636,15 @@ func TestBackfillTxnSenderIntegration(t *testing.T) { err = tx.Commit() require.NoError(t, err) - + logger := log.WithFields("module", "test") // Create real client - client, err := aggkittypes.DialWithRetry(t.Context(), rpcURL, nil) + client, err := etherman.DialWithRetry(t.Context(), logger, ðermanconfig.RPCClientConfig{ + URL: rpcURL, + }) require.NoError(t, err) // Create backfill instance - logger := log.WithFields("module", "test") + backfiller, err := NewBackfillTxnSender(dbPath, client, common.HexToAddress("0x1234"), logger) require.NoError(t, err) defer backfiller.Close() diff --git a/bridgesync/bridgesync.go b/bridgesync/bridgesync.go index a9af9268e..722543c21 100644 --- a/bridgesync/bridgesync.go +++ b/bridgesync/bridgesync.go @@ -153,14 +153,14 @@ func newBridgeSync( } if lastProcessedBlock < cfg.InitialBlockNum { - header, err := ethClient.HeaderByNumber(ctx, new(big.Int).SetUint64(cfg.InitialBlockNum)) + header, err := ethClient.CustomHeaderByNumber(ctx, aggkittypes.NewBlockNumber(cfg.InitialBlockNum)) if err != nil { return nil, fmt.Errorf("failed to get initial block %d: %w", cfg.InitialBlockNum, err) } err = processor.ProcessBlock(ctx, sync.Block{ Num: cfg.InitialBlockNum, - Hash: header.Hash(), + Hash: header.Hash, }) if err != nil { return nil, err diff --git a/bridgesync/downloader_test.go b/bridgesync/downloader_test.go index d2d2ac5c2..cf0154d16 100644 --- a/bridgesync/downloader_test.go +++ b/bridgesync/downloader_test.go @@ -11,10 +11,10 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayerbridgel2" "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/polygonzkevmbridge" bridgetypes "github.com/agglayer/aggkit/bridgesync/types" + "github.com/agglayer/aggkit/etherman" logger "github.com/agglayer/aggkit/log" "github.com/agglayer/aggkit/sync" treetypes "github.com/agglayer/aggkit/tree/types" - aggkittypes "github.com/agglayer/aggkit/types" "github.com/agglayer/aggkit/types/mocks" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -34,7 +34,7 @@ func TestExtractTxnAddressesExploratory(t *testing.T) { l1url := os.Getenv("L1URL") ethRawClient, err := ethclient.Dial(l1url) require.NoError(t, err) - ethClient := aggkittypes.NewDefaultEthClient(ethRawClient, ethRawClient.Client()) + ethClient := etherman.NewDefaultEthClient(ethRawClient, ethRawClient.Client(), nil) bridgeAddr := common.HexToAddress("0x2a3dd3eb832af982ec71669e178424b10dca2ede") agglayerBridge, err := agglayerbridge.NewAgglayerbridge(bridgeAddr, ethRawClient) require.NoError(t, err) @@ -114,7 +114,7 @@ func TestExtractCallDataCaseNotMatchingExploratory(t *testing.T) { l1url := os.Getenv("L1URL") ethRawClient, err := ethclient.Dial(l1url) require.NoError(t, err) - ethClient := aggkittypes.NewDefaultEthClient(ethRawClient, ethRawClient.Client()) + ethClient := etherman.NewDefaultEthClient(ethRawClient, ethRawClient.Client(), nil) foundCalls, rootCall, err := extractCallData(ethClient, common.HexToAddress("0x2a3dd3eb832af982ec71669e178424b10dca2ede"), common.HexToHash("0x280334ea89e49380d29e3c3931b9217bf699eaa7fa23e126c74a05eea1258503"), logger.WithFields("module", "test"), nil) @@ -139,7 +139,7 @@ func TestExtractCallDataCaseMessageExploratory(t *testing.T) { l1url := os.Getenv("L1URL") ethRawClient, err := ethclient.Dial(l1url) require.NoError(t, err) - ethClient := aggkittypes.NewDefaultEthClient(ethRawClient, ethRawClient.Client()) + ethClient := etherman.NewDefaultEthClient(ethRawClient, ethRawClient.Client(), nil) foundCalls, rootCall, err := extractCallData(ethClient, common.HexToAddress("0x2a3dd3eb832af982ec71669e178424b10dca2ede"), common.HexToHash("0x84a7e20778bd35231bfaefdcbb4ada9169b08658db49d69d38e3f467a799db38"), logger.WithFields("module", "test"), nil) diff --git a/bridgesync/e2e_test.go b/bridgesync/e2e_test.go index 7746d90dd..7796afd3f 100644 --- a/bridgesync/e2e_test.go +++ b/bridgesync/e2e_test.go @@ -10,6 +10,8 @@ import ( "github.com/agglayer/aggkit/bridgesync" cfgtypes "github.com/agglayer/aggkit/config/types" + "github.com/agglayer/aggkit/etherman" + ethermanconfig "github.com/agglayer/aggkit/etherman/config" "github.com/agglayer/aggkit/log" "github.com/agglayer/aggkit/reorgdetector" "github.com/agglayer/aggkit/test/helpers" @@ -23,6 +25,12 @@ import ( "github.com/stretchr/testify/require" ) +var ( + ethClientConfig = ðermanconfig.RPCClientConfig{ + HashFromJSON: false, + } +) + func TestBridgeEventE2E(t *testing.T) { const ( blockTime = time.Millisecond * 10 @@ -102,7 +110,7 @@ func TestBridgeEventE2E(t *testing.T) { time.Sleep(time.Second * 2) // sleeping since the processor could be up to date, but have pending reorgs lb := getFinalizedBlockNumber(t, ctx, l1Setup.SimBackend.Client()) - helpers.RequireProcessorUpdated(t, l1Setup.BridgeSync, lb, l1Setup.SimBackend.Client()) + helpers.RequireProcessorUpdated(t, l1Setup.BridgeSync, lb, etherman.NewDefaultEthClient(l1Setup.SimBackend.Client(), nil, nil)) // Get bridges lastBlock, err := l1Setup.SimBackend.Client().BlockNumber(ctx) @@ -145,7 +153,7 @@ func TestBridgeL1SyncerWithReorgDetector(t *testing.T) { //nolint:dogsled client, auth, _, _, bridgeAddr, bridgeContract, _ := helpers.NewSimulatedL1(t) - rd, err := reorgdetector.New(client.Client(), reorgdetector.Config{ + rd, err := reorgdetector.New(etherman.NewDefaultEthClient(client.Client(), nil, nil), reorgdetector.Config{ DBPath: dbPathReorg, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 10), FinalizedBlock: aggkittypes.FinalizedBlock, @@ -174,7 +182,7 @@ func TestBridgeL1SyncerWithReorgDetector(t *testing.T) { require.True(t, ok) arg.Input = bridgesync.BridgeAssetMethodID }).Return(nil) - ethClient := aggkittypes.NewDefaultEthClient(client.Client(), rpcClient) + ethClient := etherman.NewDefaultEthClient(client.Client(), rpcClient, ethClientConfig) // Create the bridge syncer with reorg detector syncer, err := bridgesync.NewL1(ctx, bridgeSyncCfg, rd, ethClient, originNetwork) @@ -324,7 +332,7 @@ func TestReorgWithSameHashEdgeCase(t *testing.T) { //nolint:dogsled client, auth, _, _, bridgeAddr, bridgeContract, _ := helpers.NewSimulatedL1(t) - rd, err := reorgdetector.New(client.Client(), reorgdetector.Config{ + rd, err := reorgdetector.New(etherman.NewDefaultEthClient(client.Client(), nil, nil), reorgdetector.Config{ DBPath: dbPathReorg, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 10), FinalizedBlock: aggkittypes.FinalizedBlock, @@ -353,7 +361,7 @@ func TestReorgWithSameHashEdgeCase(t *testing.T) { require.True(t, ok) arg.Input = bridgesync.BridgeAssetMethodID }).Return(nil) - ethClient := aggkittypes.NewDefaultEthClient(client.Client(), rpcClient) + ethClient := etherman.NewDefaultEthClient(client.Client(), rpcClient, ethClientConfig) syncer, err := bridgesync.NewL1(ctx, bridgeSyncCfg, rd, ethClient, originNetwork) require.NoError(t, err) require.NotNil(t, syncer) @@ -435,7 +443,7 @@ func TestBridgeL1SyncerWithMultipleReorgs(t *testing.T) { //nolint:dogsled client, auth, _, _, bridgeAddr, bridgeContract, _ := helpers.NewSimulatedL1(t) - rd, err := reorgdetector.New(client.Client(), reorgdetector.Config{ + rd, err := reorgdetector.New(etherman.NewDefaultEthClient(client.Client(), nil, nil), reorgdetector.Config{ DBPath: dbPathReorg, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 10), FinalizedBlock: aggkittypes.FinalizedBlock, @@ -464,8 +472,7 @@ func TestBridgeL1SyncerWithMultipleReorgs(t *testing.T) { require.True(t, ok) arg.Input = bridgesync.BridgeAssetMethodID }).Return(nil) - ethClient := aggkittypes.NewDefaultEthClient(client.Client(), rpcClient) - + ethClient := etherman.NewDefaultEthClient(client.Client(), rpcClient, ethClientConfig) // Create the bridge syncer with reorg detector syncer, err := bridgesync.NewL1(ctx, bridgeSyncCfg, rd, ethClient, originNetwork) require.NoError(t, err) diff --git a/bridgesync/helpers_test.go b/bridgesync/helpers_test.go index d90023620..f38322cdd 100644 --- a/bridgesync/helpers_test.go +++ b/bridgesync/helpers_test.go @@ -10,6 +10,8 @@ import ( aggkitcommon "github.com/agglayer/aggkit/common" cfgtypes "github.com/agglayer/aggkit/config/types" + "github.com/agglayer/aggkit/etherman" + ethermanconfig "github.com/agglayer/aggkit/etherman/config" "github.com/agglayer/aggkit/log" aggkittypes "github.com/agglayer/aggkit/types" ethereum "github.com/ethereum/go-ethereum" @@ -37,19 +39,17 @@ func startGeth(t *testing.T, ctx context.Context, cancelFn context.CancelFunc) ( }) log.Debug("Geth docker container is started") - cfg, err := aggkitcommon.NewRetryBackoffConfig( - &aggkitcommon.RetryPolicyGenericConfig{ + cfgClient := ðermanconfig.RPCClientConfig{ + URL: "http://localhost:8545", + RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ MaxRetries: 10, InitialBackoff: cfgtypes.NewDuration(time.Second), MaxBackoff: cfgtypes.NewDuration(2 * time.Second), BackoffMultiplier: 2.0, - }) - require.NoError(t, err, "failed to create retry backoff config") - - retryHandler, err := cfg.NewRetryHandler() - require.NoError(t, err, "failed to create retry handler") + }, + } - client, err := aggkittypes.DialWithRetry(t.Context(), "http://127.0.0.1:8545", retryHandler) + client, err := etherman.DialWithRetry(t.Context(), nil, cfgClient) require.NoError(t, err) auth := createAuth(t, ctx, "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", client) diff --git a/cmd/run.go b/cmd/run.go index 16854222b..dffa2d6fe 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -83,7 +83,7 @@ func start(cliCtx *cli.Context) error { if cfg.Prometheus.Enabled { prometheus.Init() } - l1Client := runL1ClientIfNeeded(cliCtx.Context, components, cfg.L1NetworkConfig.RPC) + l1Client := runL1ClientIfNeeded(cliCtx.Context, cfg.L1NetworkConfig.RPC) l2Client := runL2ClientIfNeeded(cliCtx.Context, components, cfg.Common.L2RPC) reorgDetectorL1, errChanL1 := runReorgDetectorL1IfNeeded(cliCtx.Context, components, l1Client, &cfg.ReorgDetectorL1) go func() { @@ -107,7 +107,7 @@ func start(cliCtx *cli.Context) error { rpcServices = append(rpcServices, l1mdServices...) } - rollupDataQuerier, err := createRollupDataQuerier(cliCtx.Context, cfg.L1NetworkConfig) + rollupDataQuerier, err := createRollupDataQuerier(cliCtx.Context, cfg.L1NetworkConfig, l1Client) if err != nil { return fmt.Errorf("failed to create rollup data querier: %w", err) } @@ -540,27 +540,15 @@ func runL1InfoTreeSyncerIfNeeded( } func runL1ClientIfNeeded(ctx context.Context, - components []string, rpcClientCfg ethermanconfig.RPCClientConfig) aggkittypes.EthClienter { - if !isNeeded([]string{ - aggkitcommon.AGGORACLE, - aggkitcommon.AGGSENDER, - aggkitcommon.AGGSENDERVALIDATOR, - aggkitcommon.BRIDGE, - aggkitcommon.L1INFOTREESYNC, - aggkitcommon.L2GERSYNC, - aggkitcommon.AGGCHAINPROOFGEN, - aggkitcommon.L1BRIDGESYNC, - }, components) { - return nil - } + rpcClientCfg ethermanconfig.RPCClientConfig) aggkittypes.EthClienter { + // Always is required because is used to create a L1InfoTreeDataQuerier log.Debugf("dialing L1 client at: %s", rpcClientCfg.URL) - retryHandler, err := rpcClientCfg.NewRetryHandler() - if err != nil { - log.Fatalf("failed to create retry handler: %w", err) + if rpcClientCfg.Mode != ethermanconfig.RPCModeBasic { + log.Fatalf("only basic RPC mode is supported for L1 client, got: %s", rpcClientCfg.Mode) } - - ethClient, err := aggkittypes.DialWithRetry(ctx, rpcClientCfg.URL, retryHandler) + logger := log.WithFields("module", "l1client") + ethClient, err := etherman.NewRPCClient(ctx, logger, rpcClientCfg) if err != nil { log.Fatalf("failed to create client for L1 using URL: %s. Err:%v", rpcClientCfg.URL, err) } @@ -569,7 +557,7 @@ func runL1ClientIfNeeded(ctx context.Context, } func runL2ClientIfNeeded(ctx context.Context, - components []string, urlRPCL2 ethermanconfig.L2RPCClientConfig) aggkittypes.EthClienter { + components []string, urlRPCL2 ethermanconfig.RPCClientConfig) aggkittypes.EthClienter { if !isNeeded([]string{ aggkitcommon.AGGORACLE, aggkitcommon.BRIDGE, @@ -580,7 +568,8 @@ func runL2ClientIfNeeded(ctx context.Context, aggkitcommon.L2GERSYNC}, components) { return nil } - l2Client, err := etherman.NewRPCClient(ctx, urlRPCL2) + logger := log.WithFields("module", "l2client") + l2Client, err := etherman.NewRPCClient(ctx, logger, urlRPCL2) if err != nil { log.Fatalf("failed to create client for L2 using URL: %s. Err:%v", urlRPCL2, err) } @@ -887,17 +876,9 @@ func startPrometheusHTTPServer(c prometheus.Config) { // clients and rollup manager contracts. Returns (nil, nil) if none of the required components are needed. func createRollupDataQuerier(ctx context.Context, cfg ethermanconfig.L1NetworkConfig, + l1Client aggkittypes.BaseEthereumClienter, ) (*ethermanquierier.RollupDataQuerier, error) { - retryHandler, err := cfg.RPC.NewRetryHandler() - if err != nil { - log.Fatalf("failed to create retry handler: %w", err) - } - - ethClient, err := aggkittypes.DialWithRetry(ctx, cfg.RPC.URL, retryHandler) - if err != nil { - return nil, fmt.Errorf("failed to create Ethereum client for L1 using URL: %s. Err: %w", cfg.RPC.URL, err) - } - return ethermanquierier.NewRollupDataQuerier(ctx, cfg, ethClient, + return ethermanquierier.NewRollupDataQuerier(ctx, cfg, l1Client, func(rollupManagerAddr common.Address, client aggkittypes.BaseEthereumClienter) (ethermanquierier.RollupManagerContract, error) { return agglayermanager.NewAgglayermanager(rollupManagerAddr, client) diff --git a/config/config_test.go b/config/config_test.go index 205b21403..0eaf14bee 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -49,18 +49,17 @@ func TestLoadDefaultConfig(t *testing.T) { require.Equal(t, cfg.AggSender.RetriesToBuildAndSendCertificate.String(), "RetryPolicyConfig{Mode: delays, Config: RetryDelaysConfig{Delays: [1m0s 1m0s 2m0s 5m0s 5m0s 8m0s], MaxRetries: 6}}") require.Equal(t, cfg.L1InfoTreeSync.RequireStorageContentCompatibility, true) - require.Equal(t, ethermanconfig.L2RPCClientConfig{ - RPCClientConfig: ethermanconfig.RPCClientConfig{ - URL: "http://localhost:8123", - RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ - Mode: aggkitcommon.RetryConfigModeBackoff, - MaxRetries: 5, - InitialBackoff: types.NewDuration(2 * time.Second), - MaxBackoff: types.NewDuration(10 * time.Second), - BackoffMultiplier: 2.0, - }, + require.Equal(t, ethermanconfig.RPCClientConfig{ + URL: "http://localhost:8123", + RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ + Mode: aggkitcommon.RetryConfigModeBackoff, + MaxRetries: 5, + InitialBackoff: types.NewDuration(2 * time.Second), + MaxBackoff: types.NewDuration(10 * time.Second), + BackoffMultiplier: 2.0, }, - Mode: ethermanconfig.RPCModeBasic, + Mode: ethermanconfig.RPCModeBasic, + HashFromJSON: true, }, cfg.Common.L2RPC) require.Equal(t, cfg.Profiling.ProfilingEnabled, false) require.Equal(t, cfg.Profiling.ProfilingHost, "localhost") diff --git a/config/default.go b/config/default.go index e6a893287..b3881b125 100644 --- a/config/default.go +++ b/config/default.go @@ -51,6 +51,7 @@ defaultDBQueryTimeout = "60s" InitialBackoff = "2s" MaxBackoff = "10s" BackoffMultiplier = 2.0 + HashFromJSON = true ` // DefaultValues is the default configuration @@ -74,12 +75,14 @@ GlobalExitRootManagerAddr = "{{L1Config.polygonZkEVMGlobalExitRootAddress}}" RollupManagerCreationBlock = {{L1Config.RollupManagerCreationBlock}} BlocksChunkSize = {{L1Config.BlocksChunkSize}} [L1NetworkConfig.RPC] + Mode = "basic" URL = "{{L1Config.URL}}" RetryMode = "backoff" MaxRetries = 5 InitialBackoff = "2s" MaxBackoff = "10s" BackoffMultiplier = 2.0 + HashFromJSON = true [ReorgDetectorL1] DBPath = "{{PathRWData}}/reorgdetectorl1.sqlite" diff --git a/etherman/block_notifier/block_notifier_polling_test.go b/etherman/block_notifier/block_notifier_polling_test.go index 9a87f042d..343f82f7b 100644 --- a/etherman/block_notifier/block_notifier_polling_test.go +++ b/etherman/block_notifier/block_notifier_polling_test.go @@ -3,18 +3,19 @@ package blocknotifier import ( "context" "fmt" - "math/big" "os" "testing" "time" aggkitcommon "github.com/agglayer/aggkit/common" commonmocks "github.com/agglayer/aggkit/common/mocks" + cfgtypes "github.com/agglayer/aggkit/config/types" + "github.com/agglayer/aggkit/etherman" + ethermanconfig "github.com/agglayer/aggkit/etherman/config" ethmantypes "github.com/agglayer/aggkit/etherman/types" "github.com/agglayer/aggkit/log" aggkittypes "github.com/agglayer/aggkit/types" aggkittypesmocks "github.com/agglayer/aggkit/types/mocks" - "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -23,7 +24,16 @@ func TestExploratoryBlockNotifierPolling(t *testing.T) { t.Skip() urlRPCL1 := os.Getenv("L1URL") fmt.Println("URL=", urlRPCL1) - ethClient, err := aggkittypes.DialWithRetry(t.Context(), urlRPCL1, nil) + cfg := ðermanconfig.RPCClientConfig{ + URL: urlRPCL1, + RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ + MaxRetries: 5, + InitialBackoff: cfgtypes.NewDuration(time.Second * 1), + MaxBackoff: cfgtypes.NewDuration(time.Second * 5), + BackoffMultiplier: 2.0, + }, + } + ethClient, err := etherman.DialWithRetry(t.Context(), nil, cfg) require.NoError(t, err) sut, errSut := NewBlockNotifierPolling(ethClient, @@ -161,12 +171,12 @@ func TestBlockNotifierPollingStep(t *testing.T) { } if tt.headerByNumberError == false { - hdr1 := &types.Header{ - Number: big.NewInt(int64(tt.headerByNumberErrorNumber)), + hdr1 := &aggkittypes.BlockHeader{ + Number: tt.headerByNumberErrorNumber, } - testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil).Once() + testData.ethClientMock.EXPECT().CustomHeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil).Once() } else { - testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("error")).Once() + testData.ethClientMock.EXPECT().CustomHeaderByNumber(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("error")).Once() } if tt.mockLoggerFn != nil { @@ -217,14 +227,14 @@ func TestBlockNotifierPollingString(t *testing.T) { func TestBlockNotifierPollingStart(t *testing.T) { testData := newBlockNotifierPollingTestData(t, nil) ch := testData.sut.Subscribe("test") - hdr1 := &types.Header{ - Number: big.NewInt(100), + hdr1 := &aggkittypes.BlockHeader{ + Number: 100, } - testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil).Once() - hdr2 := &types.Header{ - Number: big.NewInt(101), + testData.ethClientMock.EXPECT().CustomHeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil).Once() + hdr2 := &aggkittypes.BlockHeader{ + Number: 101, } - testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr2, nil).Once() + testData.ethClientMock.EXPECT().CustomHeaderByNumber(mock.Anything, mock.Anything).Return(hdr2, nil).Once() ctx, cancel := context.WithCancel(context.Background()) defer cancel() go testData.sut.Start(ctx) @@ -237,14 +247,14 @@ func TestBlockGetCurrentBlockNumber(t *testing.T) { testData := newBlockNotifierPollingTestData(t, nil) bn := testData.sut.GetCurrentBlockNumber() require.Equal(t, uint64(0), bn, "no block means block 0") - hdr0 := &types.Header{ - Number: big.NewInt(int64(10)), + hdr0 := &aggkittypes.BlockHeader{ + Number: 10, } - hdr1 := &types.Header{ - Number: big.NewInt(int64(100)), + hdr1 := &aggkittypes.BlockHeader{ + Number: 100, } - testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr0, nil).Once() - testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil) + testData.ethClientMock.EXPECT().CustomHeaderByNumber(mock.Anything, mock.Anything).Return(hdr0, nil).Once() + testData.ethClientMock.EXPECT().CustomHeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil) ctx, cancel := context.WithCancel(context.Background()) defer cancel() go testData.sut.Start(ctx) diff --git a/etherman/config/network.go b/etherman/config/network.go index 81f28f570..cee679f14 100644 --- a/etherman/config/network.go +++ b/etherman/config/network.go @@ -3,8 +3,10 @@ package config import ( "errors" "fmt" + "time" "github.com/agglayer/aggkit/common" + "github.com/agglayer/aggkit/config/types" gethcommon "github.com/ethereum/go-ethereum/common" ) @@ -21,7 +23,7 @@ var ( // Config holds the common configuration for the Aggkit services type CommonConfig struct { // L2URL is the URL of the L2 node - L2RPC L2RPCClientConfig `mapstructure:"L2RPC"` + L2RPC RPCClientConfig `mapstructure:"L2RPC"` } // L1NetworkConfig represents the configuration of the network used in L1 @@ -70,53 +72,68 @@ func (c *L1NetworkConfig) Validate() error { return nil } -// RPCClientConfig represents the configuration of the RPC client -type RPCClientConfig struct { - common.RetryPolicyGenericConfig `mapstructure:",squash"` - // URL is the URL of the RPC client - URL string `mapstructure:"URL"` -} - -// Validate checks if the RPCClientConfig is valid -func (c *RPCClientConfig) Validate() error { - if c.URL == "" { - return ErrMissingRPCURL - } - - return c.RetryPolicyGenericConfig.Validate() -} - type RPCMode string var ( - RPCModeBasic RPCMode = "basic" - RPCModeOp RPCMode = "op" + RPCModeDefault RPCMode = "" + RPCModeBasic RPCMode = "basic" + RPCModeOp RPCMode = "op" ) -// L2RPCClientConfig represents the configuration of the L2 RPC client -type L2RPCClientConfig struct { - // RPCClientConfig contains the basic RPC client configuration - RPCClientConfig `mapstructure:",squash"` +// RPCClientConfig represents the configuration of the RPC client +type RPCClientConfig struct { + common.RetryPolicyGenericConfig `mapstructure:",squash"` + // URL is the URL of the RPC client + URL string `mapstructure:"URL"` // Mode defines the mode of the RPC client (basic or op) // In basic mode, the client connects to a standard RPC endpoint. // In op mode, the client connects to an Optimistic RPC endpoint. Mode RPCMode `jsonschema:"enum=basic, enum=op" mapstructure:"Mode"` + // + // Common params + // + // If true, the block Hash is getted from JSON RPC + // if false, the block Hash is getted from go-ethereum RLP hashing of header + HashFromJSON bool `mapstructure:"HashFromJSON"` + // + // Params specific per client // ExtraParams contains any additional parameters that may be needed for the RPC client ExtraParams map[string]any `jsonschema:"omitempty" mapstructure:",remain"` } +// NewDefaultRPCClientConfig returns a new RPCClientConfig with default values +func NewDefaultRPCClientConfig() *RPCClientConfig { + return &RPCClientConfig{ + Mode: RPCModeDefault, + HashFromJSON: false, + ExtraParams: make(map[string]any), + RetryPolicyGenericConfig: common.RetryPolicyGenericConfig{ + Mode: common.RetryConfigModeBackoff, + MaxRetries: 5, //nolint: mnd + InitialBackoff: types.Duration{Duration: 5 * time.Second}, //nolint: mnd + MaxBackoff: types.Duration{Duration: 60 * time.Second}, //nolint: mnd + BackoffMultiplier: 2.0, //nolint: mnd + }, + } +} + // Validate checks if the L2RPCClientConfig is valid -func (c *L2RPCClientConfig) Validate() error { - if err := c.RPCClientConfig.Validate(); err != nil { +func (c *RPCClientConfig) Validate() error { + if c.URL == "" { + return ErrMissingRPCURL + } + + if err := c.RetryPolicyGenericConfig.Validate(); err != nil { return fmt.Errorf("invalid RPC configuration: %w", err) } - if c.Mode != RPCModeBasic && c.Mode != RPCModeOp { + + if c.Mode != RPCModeDefault && c.Mode != RPCModeBasic && c.Mode != RPCModeOp { return fmt.Errorf("invalid RPC mode: %s", c.Mode) } return nil } -func (c L2RPCClientConfig) GetString(key string) (string, error) { +func (c RPCClientConfig) GetString(key string) (string, error) { valueAny, ok := c.ExtraParams[key] if !ok { return "", fmt.Errorf("field %s not found in extra params of rpcclient config", key) diff --git a/etherman/config/network_test.go b/etherman/config/network_test.go index c94b534c0..320bb63d2 100644 --- a/etherman/config/network_test.go +++ b/etherman/config/network_test.go @@ -3,23 +3,44 @@ package config import ( "errors" "fmt" + "math/big" + "os" "testing" "time" aggkitcommon "github.com/agglayer/aggkit/common" "github.com/agglayer/aggkit/config/types" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" "github.com/stretchr/testify/require" ) +// Test for issue: 1389 +func TestEthClientExploratory(t *testing.T) { + t.Skip("exploratory test") + l2url := os.Getenv("L2URL") + ethRawClient, err := ethclient.Dial(l2url) + require.NoError(t, err) + defer ethRawClient.Close() + ctx := t.Context() + number := big.NewInt(34797856) + header, err := ethRawClient.HeaderByNumber(ctx, number) + require.NoError(t, err) + fmt.Printf("block number: %d\n", header.Number.Uint64()) + hash := header.Hash() + fmt.Printf("block hash: %s\n", hash.Hex()) + + err = ethRawClient.Client().BatchCall(nil) + require.NoError(t, err) +} + func TestGetString(t *testing.T) { - cfg := L2RPCClientConfig{ - RPCClientConfig: RPCClientConfig{ - URL: "http://localhost:8123", - RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ - MaxRetries: 3, - InitialBackoff: types.Duration{Duration: 1000}, - }, + cfg := RPCClientConfig{ + + URL: "http://localhost:8123", + RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ + MaxRetries: 3, + InitialBackoff: types.Duration{Duration: 1000}, }, ExtraParams: map[string]any{ "key": "value", @@ -146,47 +167,45 @@ func TestL1NetworkConfig_Validate(t *testing.T) { func TestL2RPCClientConfig_Validate(t *testing.T) { tests := []struct { name string - cfg L2RPCClientConfig + cfg RPCClientConfig wantErr error }{ { name: "missing RPC config", - cfg: L2RPCClientConfig{}, - wantErr: fmt.Errorf("invalid RPC configuration: %w", ErrMissingRPCURL), + cfg: RPCClientConfig{}, + wantErr: ErrMissingRPCURL, }, { name: "missing RPC URL", - cfg: L2RPCClientConfig{ - RPCClientConfig: RPCClientConfig{ - RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ - // empty URL - MaxRetries: 1, - }, + cfg: RPCClientConfig{ + RetryPolicyGenericConfig: aggkitcommon.RetryPolicyGenericConfig{ + // empty URL + MaxRetries: 1, }, }, - wantErr: fmt.Errorf("invalid RPC configuration: %w", ErrMissingRPCURL), + wantErr: ErrMissingRPCURL, }, { name: "invalid RPC mode", - cfg: L2RPCClientConfig{ - RPCClientConfig: RPCClientConfig{URL: "http://localhost:8545"}, - Mode: "invalid_mode", + cfg: RPCClientConfig{ + URL: "http://localhost:8545", + Mode: "invalid_mode", }, - wantErr: fmt.Errorf("invalid RPC mode: %s", "invalid_mode"), + wantErr: fmt.Errorf("invalid RPC mode: invalid_mode"), }, { name: "valid config with basic mode", - cfg: L2RPCClientConfig{ - RPCClientConfig: RPCClientConfig{URL: "http://localhost:8545"}, - Mode: RPCModeBasic, + cfg: RPCClientConfig{ + URL: "http://localhost:8545", + Mode: RPCModeBasic, }, wantErr: nil, }, { name: "valid config with OP mode", - cfg: L2RPCClientConfig{ - RPCClientConfig: RPCClientConfig{URL: "http://localhost:8545"}, - Mode: RPCModeOp, + cfg: RPCClientConfig{ + URL: "http://localhost:8545", + Mode: RPCModeOp, }, wantErr: nil, }, @@ -195,7 +214,11 @@ func TestL2RPCClientConfig_Validate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.cfg.Validate() - require.Equal(t, tt.wantErr, err) + if tt.wantErr != nil { + require.ErrorContains(t, err, tt.wantErr.Error()) + } else { + require.NoError(t, err) + } }) } } @@ -306,3 +329,13 @@ func TestRPCClientConfig_Validate(t *testing.T) { }) } } + +func TestNewDefaultRPCClientConfig(t *testing.T) { + cfg := NewDefaultRPCClientConfig() + require.Equal(t, "", cfg.URL) + require.Equal(t, RPCModeDefault, cfg.Mode) + require.Equal(t, 5, cfg.MaxRetries) + require.Equal(t, time.Second*5, cfg.InitialBackoff.Duration) + require.Equal(t, time.Second*60, cfg.MaxBackoff.Duration) + require.Equal(t, 2.0, cfg.BackoffMultiplier) +} diff --git a/etherman/default_eth_client.go b/etherman/default_eth_client.go new file mode 100644 index 000000000..78820f313 --- /dev/null +++ b/etherman/default_eth_client.go @@ -0,0 +1,172 @@ +package etherman + +import ( + "context" + "errors" + "fmt" + "math/big" + + aggkitcommon "github.com/agglayer/aggkit/common" + ethermanconfig "github.com/agglayer/aggkit/etherman/config" + "github.com/agglayer/aggkit/log" + aggkittypes "github.com/agglayer/aggkit/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" +) + +var ( + _ aggkittypes.EthClienter = (*DefaultEthClient)(nil) + ErrNotImplemented = errors.New("not implemented") +) + +type DefaultEthClient struct { + aggkittypes.EthereumClienter + aggkittypes.RPCClienter + + // If true, the block Hash is getted from JSON RPC + // if false, the block Hash is getted from go-ethereum RLP hashing of header + HashFromJSON bool + logger aggkitcommon.Logger +} + +// DialWithRetry attempts to connect to an Ethereum client with retries and exponential backoff. +// It returns an EthClienter on success or an error if all attempts fail. +func DialWithRetry(ctx context.Context, + logger aggkitcommon.Logger, + cfg *ethermanconfig.RPCClientConfig) (aggkittypes.EthClienter, error) { + retryHandler, err := cfg.NewRetryHandler() + if err != nil { + return nil, fmt.Errorf("failed to create retry handler: %w", err) + } + if logger == nil { + logger = log.NewLoggerNil() + } + return aggkitcommon.Execute(retryHandler, ctx, log.Infof, fmt.Sprintf("dial %s rpc", cfg.URL), + func() (aggkittypes.EthClienter, error) { + client, err := ethclient.Dial(cfg.URL) + if err != nil { + return nil, err + } + return NewDefaultEthClientWithLogger(logger, client, client.Client(), cfg), nil + }) +} + +// This function is for legacy code that doesn't use logger +func NewDefaultEthClient(client aggkittypes.EthereumClienter, + rpcClient aggkittypes.RPCClienter, + cfg *ethermanconfig.RPCClientConfig, +) *DefaultEthClient { + return NewDefaultEthClientWithLogger(log.NewLoggerNil(), client, rpcClient, cfg) +} + +func NewDefaultEthClientWithLogger( + logger aggkitcommon.Logger, + client aggkittypes.EthereumClienter, + rpcClient aggkittypes.RPCClienter, + cfg *ethermanconfig.RPCClientConfig, +) *DefaultEthClient { + if cfg == nil { + cfg = ethermanconfig.NewDefaultRPCClientConfig() + } + hashFromJSON := cfg.HashFromJSON + // HashFromJSON requires rpcClient + if rpcClient == nil && cfg.HashFromJSON { + logger.Warnf("rpcClient is nil, cannot use HashFromJSON=true, setting to false") + hashFromJSON = false + } + + return &DefaultEthClient{ + EthereumClienter: client, + RPCClienter: rpcClient, + HashFromJSON: hashFromJSON, + logger: logger, + } +} + +func (c *DefaultEthClient) CustomBlockNumber(ctx context.Context, number aggkittypes.BlockName) (uint64, error) { + ethHeader, err := c.HeaderByNumber(ctx, number.ToBigInt()) + if err != nil { + return 0, err + } + return ethHeader.Number.Uint64(), nil +} + +func (c *DefaultEthClient) CustomHeaderByNumber(ctx context.Context, + number *aggkittypes.BlockNumberFinality) (*aggkittypes.BlockHeader, error) { + if number == nil { + number = &aggkittypes.LatestBlock + } + // The number can have an offset, so maybe we need to resolve the blockName, apply offset to require the header + numberBigInt, err := c.resolveBlockNumber(ctx, number) + if err != nil { + return nil, err + } + var result *aggkittypes.BlockHeader + if c.HashFromJSON { + result, err = c.rpcGetBlockByNumber(ctx, numberBigInt) + if err != nil { + return nil, err + } + } else { + ethHeader, err := c.HeaderByNumber(ctx, numberBigInt) + if err != nil { + return nil, err + } + result = aggkittypes.NewBlockHeaderFromEthHeader(ethHeader) + } + + result.RequestedBlock = number + return result, nil +} + +func (c *DefaultEthClient) resolveBlockNumber(ctx context.Context, + number *aggkittypes.BlockNumberFinality) (*big.Int, error) { + // If is a number or don't have offset with 1 query it's enough + if number.IsConstant() || !number.HasOffset() { + return number.ToBigInt(), nil + } + // Resolve the base block number + hdr, err := c.rpcGetBlockByNumber(ctx, number.ToBigInt()) + if err != nil { + return nil, err + } + num := number.CalculateBlockNumber(hdr.Number) + return big.NewInt(0).SetUint64(num), nil +} + +func (c *DefaultEthClient) rpcGetBlockByNumber(ctx context.Context, number *big.Int) (*aggkittypes.BlockHeader, error) { + var blockArg string + if number == nil { + blockArg = rpc.LatestBlockNumber.String() + } else { + blockArg = rpc.BlockNumber(number.Int64()).String() + } + var rawEthHeader *blockRawEth + err := c.CallContext(ctx, &rawEthHeader, "eth_getBlockByNumber", blockArg, false) + if err != nil { + return nil, fmt.Errorf("rpcGetBlockByNumber: %w", err) + } + return rawEthHeader.ToBlockHeader() +} + +func (c *DefaultEthClient) Call(result any, method string, args ...any) error { + if c.RPCClienter == nil { + return ErrNotImplemented + } + return c.RPCClienter.Call(result, method, args...) +} + +func (c *DefaultEthClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { + if c.RPCClienter == nil { + return ErrNotImplemented + } + return c.RPCClienter.BatchCallContext(ctx, b) +} + +func (c *DefaultEthClient) CallContext(ctx context.Context, + result interface{}, method string, args ...interface{}) error { + if c.RPCClienter == nil { + return ErrNotImplemented + } + return c.RPCClienter.CallContext(ctx, result, method, args...) +} diff --git a/etherman/default_eth_client_test.go b/etherman/default_eth_client_test.go new file mode 100644 index 000000000..04b991b4b --- /dev/null +++ b/etherman/default_eth_client_test.go @@ -0,0 +1,136 @@ +package etherman + +import ( + "context" + "fmt" + "math/big" + "os" + "testing" + + ethermanconfig "github.com/agglayer/aggkit/etherman/config" + aggkittypes "github.com/agglayer/aggkit/types" + "github.com/agglayer/aggkit/types/mocks" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestDefaultEthClientExploratory(t *testing.T) { + t.Skip("Exploratory test, enable manually") + l2url := os.Getenv("L2URL") + ctx := t.Context() + cfg := ethermanconfig.RPCClientConfig{ + URL: l2url, + Mode: ethermanconfig.RPCModeBasic, + } + + client, err := NewRPCClient(ctx, nil, cfg) + require.NoError(t, err) + clientEth, ok := client.(*DefaultEthClient) + require.True(t, ok) + clientEth.HashFromJSON = true + + bn, err := aggkittypes.NewBlockNumberFinality("FinalizedBlock/10") + require.NoError(t, err) + header, err := clientEth.CustomHeaderByNumber(ctx, bn) + require.NoError(t, err) + fmt.Printf("header: %+v\n", header) +} + +func TestDefaultEthClient_CustomHeaderByNumber(t *testing.T) { + mockEthClient := mocks.NewEthereumClienter(t) + mockRPCClient := mocks.NewRPCClienter(t) + + client := NewDefaultEthClient(mockEthClient, mockRPCClient, nil) + bnFinalized5, err := aggkittypes.NewBlockNumberFinality("FinalizedBlock/5") + require.NoError(t, err) + ctx := t.Context() + blockRaw95 := &blockRawEth{ + Number: "0x5f", // 95 in hex + Hash: "0xabc123", + Timestamp: "1234", + } + + blockRaw100 := &blockRawEth{ + Number: "0x64", // 100 in hex + Hash: "0xabc123", + Timestamp: "1234", + } + + t.Run("FinalizedBlock with offset", func(t *testing.T) { + client.HashFromJSON = true + // Setup mock for rpcGetBlockByNumber + // Call to resolve finalized block + mockRPCClient. + EXPECT(). + CallContext( + ctx, + mock.Anything, + "eth_getBlockByNumber", + "finalized", + false, + ). + Return(nil). + Run(func(ctx context.Context, result interface{}, method string, args ...interface{}) { + rawEth, ok := result.(**blockRawEth) + require.True(t, ok) + *rawEth = blockRaw95 + }).Once() + + mockRPCClient. + EXPECT(). + CallContext(ctx, mock.Anything, "eth_getBlockByNumber", "0x64", false). + Return(nil). + Run(func(ctx context.Context, result interface{}, method string, args ...interface{}) { + rawEth, ok := result.(**blockRawEth) + require.True(t, ok) + *rawEth = blockRaw100 + }).Once() + // Call CustomHeaderByNumber + header, err := client.CustomHeaderByNumber(ctx, bnFinalized5) + require.NoError(t, err) + require.NotNil(t, header) + require.Equal(t, uint64(100), header.Number) + require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000abc123", header.Hash.Hex()) + require.Equal(t, bnFinalized5, header.RequestedBlock) + }) + + t.Run("Latest block", func(t *testing.T) { + client.HashFromJSON = true + ctx := t.Context() + + mockRPCClient. + EXPECT(). + CallContext(ctx, mock.Anything, "eth_getBlockByNumber", "latest", false). + Return(nil). + Run(func(ctx context.Context, result interface{}, method string, args ...interface{}) { + rawEth, ok := result.(**blockRawEth) + require.True(t, ok) + *rawEth = blockRaw95 + }).Once() + header, err := client.CustomHeaderByNumber(ctx, nil) + require.NoError(t, err) + require.NotNil(t, header) + require.Equal(t, uint64(95), header.Number) + }) + + t.Run("failed to find blockNumber for tag block", func(t *testing.T) { + mockRPCClient. + EXPECT().CallContext(ctx, mock.Anything, "eth_getBlockByNumber", "finalized", false). + Return(fmt.Errorf("rpc error")) + _, err := client.CustomHeaderByNumber(ctx, bnFinalized5) + require.Error(t, err) + }) + + t.Run("use HashFromJSON=false (geth call)", func(t *testing.T) { + client.HashFromJSON = false + mockEthClient.EXPECT(). + HeaderByNumber(ctx, (*big.Int)(nil)). + Return(&types.Header{ + Number: big.NewInt(100), + }, nil).Once() + header, err := client.CustomHeaderByNumber(ctx, nil) + require.NoError(t, err) + require.NotNil(t, header) + }) +} diff --git a/etherman/querier/rollup_data_querier_test.go b/etherman/querier/rollup_data_querier_test.go index 02f7d7098..8972f10f9 100644 --- a/etherman/querier/rollup_data_querier_test.go +++ b/etherman/querier/rollup_data_querier_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayermanager" + "github.com/agglayer/aggkit/etherman" ethermanconfig "github.com/agglayer/aggkit/etherman/config" "github.com/agglayer/aggkit/etherman/mocks" "github.com/agglayer/aggkit/test/helpers" @@ -245,7 +246,7 @@ func TestRollupDataQuerier_GetUpgradeBlock(t *testing.T) { l1Setup, _ := helpers.NewSimulatedEVMEnvironment(t, helpers.DefaultEnvironmentConfig(helpers.LegacyL2GERContract)) upgradedMap, err := populateAgglayerManagerInitializedMap(t.Context(), - l1Setup.AgglayerManagerContract, l1Setup.SimBackend.Client(), startBlock, blocksChunkSize) + l1Setup.AgglayerManagerContract, etherman.NewDefaultEthClient(l1Setup.SimBackend.Client(), nil, nil), startBlock, blocksChunkSize) require.NoError(t, err) require.Len(t, upgradedMap, 1) require.Contains(t, upgradedMap, latestAgglayerManagerVersion) diff --git a/etherman/rpcfactory.go b/etherman/rpcfactory.go index e144fe8e2..907226dad 100644 --- a/etherman/rpcfactory.go +++ b/etherman/rpcfactory.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + aggkitcommon "github.com/agglayer/aggkit/common" ethermanconfig "github.com/agglayer/aggkit/etherman/config" "github.com/agglayer/aggkit/log" aggkittypes "github.com/agglayer/aggkit/types" @@ -13,21 +14,23 @@ import ( // It supports both basic RPC mode and OPNode mode. // In basic mode, it simply creates a client with the given URL. // In OPNode mode, it creates a client that uses the OPNode client to get the finalized block. -func NewRPCClient(ctx context.Context, cfg ethermanconfig.L2RPCClientConfig) (aggkittypes.EthClienter, error) { - switch cfg.Mode { +func NewRPCClient(ctx context.Context, + logger aggkitcommon.Logger, + cfg ethermanconfig.RPCClientConfig) (aggkittypes.EthClienter, error) { + mode := cfg.Mode + if mode == ethermanconfig.RPCModeDefault { + mode = ethermanconfig.RPCModeBasic + } + switch mode { case ethermanconfig.RPCModeBasic: log.Debugf("Creating basic RPC client with URL %s", cfg.URL) - retryHandler, err := cfg.NewRetryHandler() - if err != nil { - return nil, fmt.Errorf("failed to create retry handler: %w", err) - } - ethClient, err := aggkittypes.DialWithRetry(ctx, cfg.URL, retryHandler) + ethClient, err := DialWithRetry(ctx, logger, &cfg) if err != nil { return nil, fmt.Errorf("fails to create basic RPC client. Err: %w", err) } return ethClient, nil case ethermanconfig.RPCModeOp: - return NewRPCClientModeOp(ctx, cfg) + return NewRPCClientModeOp(ctx, logger, cfg) } return nil, fmt.Errorf("invalid RPC mode %s", cfg.Mode) } diff --git a/etherman/rpcfactory_test.go b/etherman/rpcfactory_test.go index dffa4ffbf..6682ee73d 100644 --- a/etherman/rpcfactory_test.go +++ b/etherman/rpcfactory_test.go @@ -4,34 +4,44 @@ import ( "testing" ethermanconfig "github.com/agglayer/aggkit/etherman/config" + "github.com/agglayer/aggkit/log" "github.com/stretchr/testify/require" ) func TestNewRPCClient(t *testing.T) { - cfg := ethermanconfig.L2RPCClientConfig{ - RPCClientConfig: ethermanconfig.RPCClientConfig{ - URL: "http://localhost:1234", - }, + cfg := ethermanconfig.RPCClientConfig{ + + URL: "http://localhost:1234", Mode: ethermanconfig.RPCModeBasic, ExtraParams: map[string]any{ ExtraParamFieldName: "http://anotherURL:1234", }, } + logger := log.WithFields("module", "test") ctx := t.Context() - eth, err := NewRPCClient(ctx, cfg) + eth, err := NewRPCClient(ctx, logger, cfg) require.NoError(t, err) require.NotNil(t, eth) cfg.Mode = ethermanconfig.RPCModeOp - eth, err = NewRPCClient(ctx, cfg) + eth, err = NewRPCClient(ctx, logger, cfg) require.NoError(t, err) require.NotNil(t, eth) cfg.URL = "noproto://localhost" - _, err = NewRPCClient(ctx, cfg) + _, err = NewRPCClient(ctx, logger, cfg) require.ErrorContains(t, err, "no known transport for URL scheme \"noproto\"") - cfg = ethermanconfig.L2RPCClientConfig{} - _, err = NewRPCClient(ctx, cfg) + cfg = ethermanconfig.RPCClientConfig{ + Mode: "invalid_mode", + } + _, err = NewRPCClient(ctx, logger, cfg) require.ErrorContains(t, err, "invalid RPC mode") + + cfg = ethermanconfig.RPCClientConfig{ + Mode: "", + } + // This is the default mode + _, err = NewRPCClient(ctx, logger, cfg) + require.ErrorContains(t, err, "dial unix: missing address") } diff --git a/etherman/rpcopnode.go b/etherman/rpcopnode.go index 003dc313f..cca668089 100644 --- a/etherman/rpcopnode.go +++ b/etherman/rpcopnode.go @@ -6,6 +6,7 @@ import ( "math/big" "strings" + aggkitcommon "github.com/agglayer/aggkit/common" ethermanconfig "github.com/agglayer/aggkit/etherman/config" "github.com/agglayer/aggkit/log" "github.com/agglayer/aggkit/opnode" @@ -31,7 +32,9 @@ type RPCOpNodeDecorator struct { } // NewRPCClientModeOp creates a new RPC client that uses the OPNode client to get the finalized block -func NewRPCClientModeOp(ctx context.Context, cfg ethermanconfig.L2RPCClientConfig) (aggkittypes.EthClienter, error) { +func NewRPCClientModeOp(ctx context.Context, + logger aggkitcommon.Logger, + cfg ethermanconfig.RPCClientConfig) (aggkittypes.EthClienter, error) { opNodeURL, err := cfg.GetString(ExtraParamFieldName) if err != nil { opNodeURL, err = cfg.GetString(strings.ToLower(ExtraParamFieldName)) @@ -41,11 +44,7 @@ func NewRPCClientModeOp(ctx context.Context, cfg ethermanconfig.L2RPCClientConfi } log.Debugf("Creating OPNode RPC client with URL %s %s:%s", cfg.URL, ExtraParamFieldName, opNodeURL) - retryHandler, err := cfg.NewRetryHandler() - if err != nil { - return nil, fmt.Errorf("failed to create retry handler: %w", err) - } - ethClient, err := aggkittypes.DialWithRetry(ctx, cfg.URL, retryHandler) + ethClient, err := DialWithRetry(ctx, logger, &cfg) if err != nil { return nil, fmt.Errorf("fails to create RPC client. Err: %w", err) } diff --git a/etherman/rpcopnode_test.go b/etherman/rpcopnode_test.go deleted file mode 100644 index eb917c629..000000000 --- a/etherman/rpcopnode_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package etherman - -import ( - "context" - "fmt" - "math/big" - "testing" - - ethermanconfig "github.com/agglayer/aggkit/etherman/config" - "github.com/agglayer/aggkit/etherman/mocks" - "github.com/agglayer/aggkit/opnode" - aggkittypesmocks "github.com/agglayer/aggkit/types/mocks" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rpc" - "github.com/stretchr/testify/require" -) - -func TestNewRPCClientModeOp(t *testing.T) { - cfg := ethermanconfig.L2RPCClientConfig{ - RPCClientConfig: ethermanconfig.RPCClientConfig{ - URL: "http://localhost:1234", - }, - Mode: ethermanconfig.RPCModeBasic, - ExtraParams: map[string]any{ - ExtraParamFieldName: "http://anotherURL:1234", - }, - } - ctx := t.Context() - eth, err := NewRPCClientModeOp(ctx, cfg) - require.NoError(t, err) - require.NotNil(t, eth) - - cfg.URL = "noproto://localhost" - _, err = NewRPCClientModeOp(ctx, cfg) - require.Error(t, err) -} - -func TestHeaderByNumber(t *testing.T) { - mockEth := aggkittypesmocks.NewEthClienter(t) - mockOpNode := mocks.NewOpNodeClienter(t) - sut := NewRPCOpNodeDecorator(mockEth, mockOpNode) - ctx := context.TODO() - finalizedBlockNumber := big.NewInt(int64(rpc.FinalizedBlockNumber)) - opBlock := &opnode.BlockInfo{ - Number: 1234, - } - rpcHeader := &types.Header{ - Number: big.NewInt(1234), - } - customError := fmt.Errorf("custom error") - t.Run("happy path", func(t *testing.T) { - mockOpNode.EXPECT().FinalizedL2Block().Return(opBlock, nil).Once() - - mockEth.EXPECT().HeaderByNumber(ctx, big.NewInt(1234)).Return(rpcHeader, nil).Once() - hdr, err := sut.HeaderByNumber(ctx, finalizedBlockNumber) - require.NoError(t, err) - require.NotNil(t, hdr) - require.Equal(t, rpcHeader, hdr) - }) - - t.Run("require no finalized block", func(t *testing.T) { - mockEth.EXPECT().HeaderByNumber(ctx, big.NewInt(1234)).Return(rpcHeader, nil).Once() - hdr, err := sut.HeaderByNumber(ctx, big.NewInt(1234)) - require.NoError(t, err) - require.NotNil(t, hdr) - require.Equal(t, rpcHeader, hdr) - }) - - t.Run("fails opNode call", func(t *testing.T) { - mockOpNode.EXPECT().FinalizedL2Block().Return(nil, customError).Once() - hdr, err := sut.HeaderByNumber(ctx, finalizedBlockNumber) - require.Error(t, err) - require.Nil(t, hdr) - }) -} diff --git a/l1infotreesync/e2e_test.go b/l1infotreesync/e2e_test.go index 7e2994236..d9621346e 100644 --- a/l1infotreesync/e2e_test.go +++ b/l1infotreesync/e2e_test.go @@ -11,6 +11,7 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayerger" cfgtypes "github.com/agglayer/aggkit/config/types" + "github.com/agglayer/aggkit/etherman" "github.com/agglayer/aggkit/l1infotreesync" "github.com/agglayer/aggkit/l1infotreesync/mocks" "github.com/agglayer/aggkit/log" @@ -88,14 +89,14 @@ func TestE2E(t *testing.T) { log.WithFields("module", "multidownloader"), cfgMD, "testMD", - client.Client(), + etherman.NewDefaultEthClient(client.Client(), nil, nil), nil, // rpcClient nil, nil, ) require.NoError(t, err) } else { - multidownloaderClient = sync.NewAdapterEthClientToMultidownloader(client.Client()) + multidownloaderClient = sync.NewAdapterEthClientToMultidownloader(etherman.NewDefaultEthClient(client.Client(), nil, nil)) } cfg := l1infotreesync.Config{ @@ -161,7 +162,7 @@ func TestWithReorgs(t *testing.T) { CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100), FinalizedBlock: aggkittypes.FinalizedBlock, } - rd, err := reorgdetector.New(client.Client(), rdConfig, reorgdetector.L1) + rd, err := reorgdetector.New(etherman.NewDefaultEthClient(client.Client(), nil, nil), rdConfig, reorgdetector.L1) require.NoError(t, err) require.NoError(t, rd.Start(ctx)) var multidownloaderClient aggkittypes.MultiDownloader @@ -172,14 +173,14 @@ func TestWithReorgs(t *testing.T) { log.WithFields("module", "multidownloader"), cfgMD, "testMD", - client.Client(), + etherman.NewDefaultEthClient(client.Client(), nil, nil), nil, // rpcClient nil, nil, ) require.NoError(t, err) } else { - multidownloaderClient = sync.NewAdapterEthClientToMultidownloader(client.Client()) + multidownloaderClient = sync.NewAdapterEthClientToMultidownloader(etherman.NewDefaultEthClient(client.Client(), nil, nil)) } cfg := l1infotreesync.Config{ @@ -313,7 +314,7 @@ func TestStressAndReorgs(t *testing.T) { DBPath: dbPathReorg, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100), FinalizedBlock: aggkittypes.FinalizedBlock} - rd, err := reorgdetector.New(client.Client(), reorgDetectorCfg, reorgdetector.L1) + rd, err := reorgdetector.New(etherman.NewDefaultEthClient(client.Client(), nil, nil), reorgDetectorCfg, reorgdetector.L1) require.NoError(t, err) require.NoError(t, rd.Start(ctx)) @@ -325,14 +326,14 @@ func TestStressAndReorgs(t *testing.T) { log.WithFields("module", "multidownloader"), cfgMD, "testMD", - client.Client(), + etherman.NewDefaultEthClient(client.Client(), nil, nil), nil, // rpcClient nil, nil, ) require.NoError(t, err) } else { - multidownloaderClient = sync.NewAdapterEthClientToMultidownloader(client.Client()) + multidownloaderClient = sync.NewAdapterEthClientToMultidownloader(etherman.NewDefaultEthClient(client.Client(), nil, nil)) } cfg := l1infotreesync.Config{ diff --git a/l1infotreesync/l1infotreesync.go b/l1infotreesync/l1infotreesync.go index 32ae61f1b..f9f713e9d 100644 --- a/l1infotreesync/l1infotreesync.go +++ b/l1infotreesync/l1infotreesync.go @@ -74,7 +74,7 @@ func New( parentBlockNumber := cfg.InitialBlock - 1 if cfg.InitialBlock > 0 && lastProcessedBlock < parentBlockNumber { - block, err := l1Client.HeaderByNumber(ctx, new(big.Int).SetUint64(parentBlockNumber)) + block, err := l1Client.HeaderByNumber(ctx, aggkittypes.NewBlockNumber(parentBlockNumber)) if err != nil { return nil, fmt.Errorf("failed to get initial block %d: %w", parentBlockNumber, err) } diff --git a/l2gersync/e2e_test.go b/l2gersync/e2e_test.go index 970e7aaf8..301f0dccc 100644 --- a/l2gersync/e2e_test.go +++ b/l2gersync/e2e_test.go @@ -9,6 +9,7 @@ import ( "time" cfgtypes "github.com/agglayer/aggkit/config/types" + "github.com/agglayer/aggkit/etherman" "github.com/agglayer/aggkit/l2gersync" "github.com/agglayer/aggkit/test/helpers" aggkittypes "github.com/agglayer/aggkit/types" @@ -50,7 +51,8 @@ func TestL2GERSyncE2E(t *testing.T) { RequireStorageContentCompatibility: true, } syncer, err := l2gersync.New(ctx, l2SyncerCfg, l2Setup.ReorgDetector, - l2Setup.SimBackend.Client(), l1Setup.InfoTreeSync, l1Setup.SimBackend.Client()) + etherman.NewDefaultEthClient(l2Setup.SimBackend.Client(), nil, nil), + l1Setup.InfoTreeSync, etherman.NewDefaultEthClient(l1Setup.SimBackend.Client(), nil, nil)) require.NoError(t, err) go syncer.Start(ctx) @@ -84,7 +86,8 @@ func TestL2GERSync_GERRemoval(t *testing.T) { RequireStorageContentCompatibility: true, } syncer, err := l2gersync.New(ctx, l2SyncerCfg, l2Setup.ReorgDetector, - l2Setup.SimBackend.Client(), l1Setup.InfoTreeSync, l1Setup.SimBackend.Client()) + etherman.NewDefaultEthClient(l2Setup.SimBackend.Client(), nil, nil), l1Setup.InfoTreeSync, + etherman.NewDefaultEthClient(l1Setup.SimBackend.Client(), nil, nil)) require.NoError(t, err) go syncer.Start(ctx) @@ -111,7 +114,7 @@ func TestL2GERSync_GERRemoval(t *testing.T) { // wait for the GER removal events to be processed lb, err := l2Setup.SimBackend.Client().BlockNumber(ctx) require.NoError(t, err) - helpers.RequireProcessorUpdated(t, syncer, lb, l2Setup.SimBackend.Client()) + helpers.RequireProcessorUpdated(t, syncer, lb, etherman.NewDefaultEthClient(l2Setup.SimBackend.Client(), nil, nil)) for _, removedGER := range gersToRemove { isInjected, err := l2Setup.AggoracleSender.IsGERInjected(removedGER) @@ -149,9 +152,9 @@ func TestL2GERSync_IndexLegacyGERManagerSC(t *testing.T) { ctx, l2SyncerCfg, l2Setup.ReorgDetector, - l2Setup.SimBackend.Client(), + etherman.NewDefaultEthClient(l2Setup.SimBackend.Client(), nil, nil), l1Setup.InfoTreeSync, - l1Setup.SimBackend.Client(), + etherman.NewDefaultEthClient(l1Setup.SimBackend.Client(), nil, nil), ) require.NoError(t, err) @@ -230,7 +233,7 @@ func testGERSyncer(t *testing.T, ctx context.Context, lb, err := l2Setup.SimBackend.Client().BlockNumber(ctx) require.NoError(t, err) - helpers.RequireProcessorUpdated(t, syncer, lb, l2Setup.SimBackend.Client()) + helpers.RequireProcessorUpdated(t, syncer, lb, etherman.NewDefaultEthClient(l2Setup.SimBackend.Client(), nil, nil)) e, err := syncer.GetFirstGERAfterL1InfoTreeIndex(ctx, uint32(i)) require.NoError(t, err, fmt.Sprintf("iteration: %d", i)) diff --git a/l2gersync/evm_downloader_sovereign_test.go b/l2gersync/evm_downloader_sovereign_test.go index fceb3036e..5e72a8398 100644 --- a/l2gersync/evm_downloader_sovereign_test.go +++ b/l2gersync/evm_downloader_sovereign_test.go @@ -37,17 +37,14 @@ func TestDownloaderSovereign_Download(t *testing.T) { testGER := common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") testHashChainValue := common.HexToHash("0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890") testL1InfoTreeIndex := uint32(42) - testBlockHeader := ðtypes.Header{ - Number: big.NewInt(int64(fromBlock)), - ParentHash: common.HexToHash("0xabc123"), - Root: common.HexToHash("0xdef456"), - TxHash: common.HexToHash("0x789abc"), - ReceiptHash: common.HexToHash("0x101112"), - Time: uint64(time.Now().Unix()), - GasLimit: 8000000, - GasUsed: 21000, + parentHash := common.HexToHash("0xabc123") + testBlockHeader := &aggkittypes.BlockHeader{ + Number: fromBlock, + ParentHash: &parentHash, + Hash: common.HexToHash("0xdef456"), + Time: uint64(time.Now().Unix()), } - testBlockHash := testBlockHeader.Hash() + testBlockHash := testBlockHeader.Hash testLogs := []ethtypes.Log{ { Address: l2GERAddr, @@ -63,18 +60,17 @@ func TestDownloaderSovereign_Download(t *testing.T) { mockL2Client.EXPECT().ChainID(mock.Anything).Return(big.NewInt(1), nil).Maybe() // First call to get latest block header (with nil) - mockL2Client.EXPECT().HeaderByNumber(mock.Anything, (*big.Int)(nil)).Return(ðtypes.Header{ - Number: big.NewInt(int64(latestBlock)), + mockL2Client.EXPECT().CustomHeaderByNumber(mock.Anything, (*aggkittypes.BlockNumberFinality)(nil)).Return(&aggkittypes.BlockHeader{ + Number: latestBlock, + }, nil).Maybe() + mockL2Client.EXPECT().CustomHeaderByNumber(mock.Anything, &aggkittypes.LatestBlock).Return(&aggkittypes.BlockHeader{ + Number: latestBlock, }, nil).Maybe() // Second call to get the offset block header (with latestBlock since offset is 0) - mockL2Client.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(int64(latestBlock))).Return(ðtypes.Header{ - Number: big.NewInt(int64(latestBlock)), + mockL2Client.EXPECT().CustomHeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(latestBlock)).Return(&aggkittypes.BlockHeader{ + Number: latestBlock, }, nil).Maybe() - mockL2Client.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(int64(fromBlock))).Return(testBlockHeader, nil).Maybe() - mockL1Client.EXPECT().BlockByNumber(mock.Anything, mock.Anything).Return(ethtypes.NewBlock( - ðtypes.Header{Number: big.NewInt(int64(latestBlock))}, - nil, nil, nil, - ), nil).Maybe() + mockL2Client.EXPECT().CustomHeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(fromBlock)).Return(testBlockHeader, nil).Maybe() mockL1InfoTreeSync.EXPECT().GetInfoByGlobalExitRoot(testGER).Return(&l1infotreesync.L1InfoTreeLeaf{ L1InfoTreeIndex: testL1InfoTreeIndex, @@ -330,10 +326,6 @@ func TestDownloaderSovereign_GetInfoByGlobalExitRootErrorHandlingInAppender(t *t Number: big.NewInt(int64(latestBlock)), }, nil).Maybe() mockL2Client.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(int64(fromBlock))).Return(testBlockHeader, nil).Maybe() - mockL1Client.EXPECT().BlockByNumber(mock.Anything, mock.Anything).Return(ethtypes.NewBlock( - ðtypes.Header{Number: big.NewInt(int64(latestBlock))}, - nil, nil, nil, - ), nil).Maybe() mockL1InfoTreeSync.EXPECT().GetInfoByGlobalExitRoot(testGER).Return(nil, tt.getInfoByGERError).Maybe() mockL1InfoTreeSync.EXPECT().IsUpToDate(mock.Anything, mock.Anything).Return(tt.isUpToDateResult, tt.isUpToDateError).Maybe() diff --git a/l2gersync/l2_evm_ger_reader_test.go b/l2gersync/l2_evm_ger_reader_test.go index 9aeb92948..45764085d 100644 --- a/l2gersync/l2_evm_ger_reader_test.go +++ b/l2gersync/l2_evm_ger_reader_test.go @@ -7,6 +7,7 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayergerl2" aggoraclemocks "github.com/agglayer/aggkit/aggoracle/mocks" + "github.com/agglayer/aggkit/etherman" "github.com/agglayer/aggkit/l1infotreesync" "github.com/agglayer/aggkit/l2gersync/mocks" "github.com/agglayer/aggkit/test/helpers" @@ -85,7 +86,7 @@ func TestL2EVMGERReader_GetInjectedGERsForRange(t *testing.T) { l1InfoTreeSync := mocks.NewL1InfoTreeQuerier(t) l1InfoTreeSync.EXPECT().GetInfoByGlobalExitRoot(mock.Anything).Return(&l1infotreesync.L1InfoTreeLeaf{L1InfoTreeIndex: 1}, nil).Maybe() - gerReader, err := NewL2EVMGERReader(l2.GERAddr, l2.SimBackend.Client(), l1InfoTreeSync) + gerReader, err := NewL2EVMGERReader(l2.GERAddr, etherman.NewDefaultEthClient(l2.SimBackend.Client(), nil, nil), l1InfoTreeSync) require.NoError(t, err) // Ensure we have enough blocks by committing several times @@ -233,7 +234,7 @@ func TestL2EVMGERReader_GetRemovedGERsForRange(t *testing.T) { l1InfoTreeSync := mocks.NewL1InfoTreeQuerier(t) - gerReader, err := NewL2EVMGERReader(l2.GERAddr, l2.SimBackend.Client(), l1InfoTreeSync) + gerReader, err := NewL2EVMGERReader(l2.GERAddr, etherman.NewDefaultEthClient(l2.SimBackend.Client(), nil, nil), l1InfoTreeSync) require.NoError(t, err) // commit one block so the current block is block 6 @@ -256,7 +257,7 @@ func TestL2EVMGERReader_GetRemovedGERsForRange(t *testing.T) { l1InfoTreeSync := mocks.NewL1InfoTreeQuerier(t) - gerReader, err := NewL2EVMGERReader(l2.GERAddr, l2.SimBackend.Client(), l1InfoTreeSync) + gerReader, err := NewL2EVMGERReader(l2.GERAddr, etherman.NewDefaultEthClient(l2.SimBackend.Client(), nil, nil), l1InfoTreeSync) require.NoError(t, err) // First insert a GER @@ -300,7 +301,7 @@ func TestL2EVMGERReader_GetRemovedGERsForRange(t *testing.T) { l1InfoTreeSync := mocks.NewL1InfoTreeQuerier(t) - gerReader, err := NewL2EVMGERReader(l2.GERAddr, l2.SimBackend.Client(), l1InfoTreeSync) + gerReader, err := NewL2EVMGERReader(l2.GERAddr, etherman.NewDefaultEthClient(l2.SimBackend.Client(), nil, nil), l1InfoTreeSync) require.NoError(t, err) // Insert and remove multiple GERs @@ -372,7 +373,7 @@ func TestL2EVMGERReader_GetRemovedGERsForRange(t *testing.T) { l1InfoTreeSync := mocks.NewL1InfoTreeQuerier(t) - gerReader, err := NewL2EVMGERReader(l2.GERAddr, l2.SimBackend.Client(), l1InfoTreeSync) + gerReader, err := NewL2EVMGERReader(l2.GERAddr, etherman.NewDefaultEthClient(l2.SimBackend.Client(), nil, nil), l1InfoTreeSync) require.NoError(t, err) // Test with a valid range that should not cause iterator errors diff --git a/log/log_nil.go b/log/log_nil.go new file mode 100644 index 000000000..57123abed --- /dev/null +++ b/log/log_nil.go @@ -0,0 +1,50 @@ +package log + +import ( + golog "log" +) + +type LoggerNil struct{} + +func NewLoggerNil() *LoggerNil { + return &LoggerNil{} +} + +// Debug calls log.Debug +func (l *LoggerNil) Debug(args ...interface{}) {} + +// Info calls log.Info +func (l *LoggerNil) Info(args ...interface{}) {} + +// Warn calls log.Warn +func (l *LoggerNil) Warn(args ...interface{}) {} + +// Error calls log.Error +func (l *LoggerNil) Error(args ...interface{}) {} + +// Fatal calls log.Fatal +func (l *LoggerNil) Fatal(args ...interface{}) { + golog.Fatal(args...) +} + +// Debugf calls log.Debugf +func (l *LoggerNil) Debugf(template string, args ...interface{}) {} + +// Infof calls log.Infof +func (l *LoggerNil) Infof(template string, args ...interface{}) {} + +// Warnf calls log.Warnf +func (l *LoggerNil) Warnf(template string, args ...interface{}) {} + +// Fatalf calls log.Fatalf +func (l *LoggerNil) Fatalf(template string, args ...interface{}) { + golog.Fatalf(template, args...) +} + +// Panicf calls log.Panicf +func (l *LoggerNil) Panicf(template string, args ...interface{}) { + golog.Panicf(template, args...) +} + +// Errorf calls log.Errorf +func (l *LoggerNil) Errorf(template string, args ...interface{}) {} diff --git a/multidownloader/evm_multidownloader_syncers.go b/multidownloader/evm_multidownloader_syncers.go index 615b30c79..76349cf49 100644 --- a/multidownloader/evm_multidownloader_syncers.go +++ b/multidownloader/evm_multidownloader_syncers.go @@ -4,7 +4,6 @@ package multidownloader import ( "context" "fmt" - "math/big" "time" mdrtypes "github.com/agglayer/aggkit/multidownloader/types" @@ -36,12 +35,12 @@ func (dh *EVMMultidownloader) BlockHeader(ctx context.Context, return nil, fmt.Errorf("EVMMultidownloader.BlockHeader: cannot get block number for finality=%s: %w", finality.String(), err) } - header, err := dh.ethClient.HeaderByNumber(ctx, big.NewInt(int64(number))) + header, err := dh.ethClient.CustomHeaderByNumber(ctx, aggkittypes.NewBlockNumber(number)) if err != nil { return nil, fmt.Errorf("EVMMultidownloader.BlockHeader: cannot get header for block number=%d: %w", number, err) } - return aggkittypes.NewBlockHeaderFromEthHeader(header), nil + return header, nil } // FilterLogs filters the logs. It gets them from storage or waits until they are available @@ -70,31 +69,31 @@ func (dh *EVMMultidownloader) FilterLogs(ctx context.Context, query ethereum.Fil } // HeaderByNumber gets the block header for the given block number from storage or ethClient -func (dh *EVMMultidownloader) HeaderByNumber(ctx context.Context, number *big.Int) (*aggkittypes.BlockHeader, error) { +func (dh *EVMMultidownloader) HeaderByNumber(ctx context.Context, + number *aggkittypes.BlockNumberFinality) (*aggkittypes.BlockHeader, error) { dh.log.Debugf("EVMMultidownloader.HeaderByNumber: received number: %s", number.String()) defer dh.log.Debugf("EVMMultidownloader.HeaderByNumber: finished number: %s", number.String()) - if number.Cmp(big.NewInt(0)) < 0 { - return nil, fmt.Errorf("EVMMultidownloader.HeaderByNumber: negative block numbers are not supported=%s", + if !number.IsConstant() { + return nil, fmt.Errorf("EVMMultidownloader.HeaderByNumber: only numeric blockNumbers are supported (got=%s)", number.String()) } - - block, _, err := dh.storage.GetBlockHeaderByNumber(nil, number.Uint64()) + blockNumber := number.Specific + block, _, err := dh.storage.GetBlockHeaderByNumber(nil, blockNumber) if err != nil { - return nil, fmt.Errorf("EVMMultidownloader.HeaderByNumber: cannot get BlockHeader number=%d: %w", - number.Uint64(), err) + return nil, fmt.Errorf("EVMMultidownloader.HeaderByNumber: cannot get BlockHeader number=%s: %w", + number.String(), err) } if block != nil { return block, nil } // This is a fallback mechanism in case the block is not found in storage (it must be in storage!) - dh.log.Debugf("EVMMultidownloader.HeaderByNumber: block number=%d not found in storage, fetching from ethClient", - number.Uint64()) - ethBlock, err := dh.ethClient.HeaderByNumber(ctx, number) + dh.log.Debugf("EVMMultidownloader.HeaderByNumber: block number=%s not found in storage, fetching from ethClient", + number.String()) + blockHeader, err := dh.ethClient.CustomHeaderByNumber(ctx, number) if err != nil { - return nil, fmt.Errorf("EVMMultidownloader.HeaderByNumber: ethClient.HeaderByNumber(%d) failed. Err: %w", - number.Uint64(), err) + return nil, fmt.Errorf("EVMMultidownloader.HeaderByNumber: ethClient.HeaderByNumber(%s) failed. Err: %w", + number.String(), err) } - blockHeader := aggkittypes.NewBlockHeaderFromEthHeader(ethBlock) return blockHeader, nil } diff --git a/multidownloader/evm_multidownloader_syncers_test.go b/multidownloader/evm_multidownloader_syncers_test.go index e12512d6a..e8b5a453b 100644 --- a/multidownloader/evm_multidownloader_syncers_test.go +++ b/multidownloader/evm_multidownloader_syncers_test.go @@ -11,7 +11,6 @@ import ( aggkittypes "github.com/agglayer/aggkit/types" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -63,9 +62,9 @@ func TestEVMMultidownloader_BlockHeader(t *testing.T) { testData := newEVMMultidownloaderTestData(t, true) testData.mockBlockNotifierManager.EXPECT().GetCurrentBlockNumber(mock.Anything, aggkittypes.LatestBlock). Return(uint64(123456), nil) - testData.mockEthClient.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(123456)). - Return(&types.Header{ - Number: big.NewInt(123456), + testData.mockEthClient.EXPECT().CustomHeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(123456)). + Return(&aggkittypes.BlockHeader{ + Number: 123456, }, nil) header, err := testData.mdr.BlockHeader(t.Context(), aggkittypes.LatestBlock) require.NoError(t, err) @@ -78,12 +77,12 @@ func TestEVMMultidownloader_HeaderByNumber(t *testing.T) { testData := newEVMMultidownloaderTestData(t, true) // Test - result, err := testData.mdr.HeaderByNumber(context.Background(), big.NewInt(-1)) + result, err := testData.mdr.HeaderByNumber(context.Background(), &aggkittypes.FinalizedBlock) // Assertions require.Nil(t, result) require.Error(t, err) - require.Contains(t, err.Error(), "negative block numbers are not supported") + require.Contains(t, err.Error(), "only numeric") }) t.Run("storage error returns error", func(t *testing.T) { @@ -93,7 +92,7 @@ func TestEVMMultidownloader_HeaderByNumber(t *testing.T) { Return(nil, false, errStorageExample) // Test - result, err := testData.mdr.HeaderByNumber(context.Background(), big.NewInt(123)) + result, err := testData.mdr.HeaderByNumber(context.Background(), aggkittypes.NewBlockNumber(123)) // Assertions require.Nil(t, result) @@ -112,8 +111,8 @@ func TestEVMMultidownloader_HeaderByNumber(t *testing.T) { testData.mockStorage.EXPECT().GetBlockHeaderByNumber(mock.Anything, expectedBlock.Number). Return(expectedBlock, false, nil) - // Test - result, err := testData.mdr.HeaderByNumber(context.Background(), big.NewInt(123)) + // Test + result, err := testData.mdr.HeaderByNumber(context.Background(), aggkittypes.NewBlockNumber(123)) // Assertions require.NoError(t, err) @@ -128,11 +127,11 @@ func TestEVMMultidownloader_HeaderByNumber(t *testing.T) { Return(nil, false, nil) // Block not found in storage ethClientErr := errors.New("eth client error") - testData.mockEthClient.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(123)). + testData.mockEthClient.EXPECT().CustomHeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(123)). Return(nil, ethClientErr) // Test - result, err := testData.mdr.HeaderByNumber(context.Background(), big.NewInt(123)) + result, err := testData.mdr.HeaderByNumber(context.Background(), aggkittypes.NewBlockNumber(123)) // Assertions require.Nil(t, result) @@ -148,14 +147,14 @@ func TestEVMMultidownloader_HeaderByNumber(t *testing.T) { testData.mockStorage.EXPECT().GetBlockHeaderByNumber(mock.Anything, uint64(123)). Return(nil, false, nil) // Block not found in storage - ethHeader := &types.Header{ - Number: big.NewInt(123), + ethHeader := &aggkittypes.BlockHeader{ + Number: 123, } - testData.mockEthClient.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(123)). + testData.mockEthClient.EXPECT().CustomHeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(123)). Return(ethHeader, nil) // Test - result, err := testData.mdr.HeaderByNumber(context.Background(), big.NewInt(123)) + result, err := testData.mdr.HeaderByNumber(context.Background(), aggkittypes.NewBlockNumber(123)) // Assertions require.NoError(t, err) diff --git a/multidownloader/evm_multidownloader_test.go b/multidownloader/evm_multidownloader_test.go index dbe3613ea..e3849a163 100644 --- a/multidownloader/evm_multidownloader_test.go +++ b/multidownloader/evm_multidownloader_test.go @@ -43,13 +43,13 @@ func TestEVMMultidownloader(t *testing.T) { l1url := os.Getenv("L1URL") ethRawClient, err := ethclient.Dial(l1url) require.NoError(t, err) - ethClient := aggkittypes.NewDefaultEthClient(ethRawClient, ethRawClient.Client()) + ethClient := etherman.NewDefaultEthClient(ethRawClient, ethRawClient.Client(), nil) ethRPCClient, err := rpc.DialContext(t.Context(), l1url) require.NoError(t, err) - block, err := ethClient.BlockByNumber(t.Context(), nil) // Test connection + block, err := ethClient.CustomHeaderByNumber(t.Context(), nil) // Test connection require.NoError(t, err) - log.Infof("Connected to Ethereum. Current block: %d", block.Number().Uint64()) + log.Infof("Connected to Ethereum. Current block: %d", block.Number) logger := log.WithFields("test", "test") @@ -187,6 +187,7 @@ func TestDownloaderParellelvsBatch(t *testing.T) { require.NoError(t, err) ethRPCClient, err := rpc.DialContext(t.Context(), l1url) require.NoError(t, err) + ethClientWrapped := etherman.NewDefaultEthClient(ethClient, ethRPCClient, nil) blockNumbersMap := make([]uint64, 0) var blockNumbersSlice []uint64 @@ -204,7 +205,7 @@ func TestDownloaderParellelvsBatch(t *testing.T) { log.Infof("BatchMode took %s", durationBatch.String()) start = time.Now() - headersParallel, err := etherman.RetrieveBlockHeaders(t.Context(), logger, ethClient, nil, blockNumbersMap, 20) + headersParallel, err := etherman.RetrieveBlockHeaders(t.Context(), logger, ethClientWrapped, nil, blockNumbersMap, 20) require.NoError(t, err) durationParallel := time.Since(start) log.Infof("Parallel RPC took %s", durationParallel.String()) diff --git a/multidownloader/storage/storage.go b/multidownloader/storage/storage.go index 2a8afc293..b38fc78e8 100644 --- a/multidownloader/storage/storage.go +++ b/multidownloader/storage/storage.go @@ -302,7 +302,7 @@ func (r *syncStatusRow) ToSyncSegment() (mdrtypes.SyncSegment, error) { } return mdrtypes.SyncSegment{ ContractAddr: r.Address, - TargetToBlock: targetToBlock, + TargetToBlock: *targetToBlock, BlockRange: aggkitcommon.NewBlockRange(r.SyncedFromBlock, r.SyncedToBlock), }, nil } diff --git a/multidownloader/types/syncer_config.go b/multidownloader/types/syncer_config.go index 25303ddf4..b394b4c39 100644 --- a/multidownloader/types/syncer_config.go +++ b/multidownloader/types/syncer_config.go @@ -4,6 +4,7 @@ import ( "sort" aggkitcommon "github.com/agglayer/aggkit/common" + "github.com/agglayer/aggkit/log" aggkittypes "github.com/agglayer/aggkit/types" "github.com/ethereum/go-ethereum/common" ) @@ -34,7 +35,13 @@ func (c *ContractConfig) Update(syncerConfig aggkittypes.SyncerConfig) { if syncerConfig.FromBlock < c.FromBlock { c.FromBlock = syncerConfig.FromBlock } - if syncerConfig.ToBlock.LessFinalThan(c.ToBlock) { + lessFinal, err := syncerConfig.ToBlock.LessFinalThan(c.ToBlock) + if err != nil { + // In case of error, we do not update ToBlock + log.Warnf("ContractConfig.Update: cannot compare ToBlock finality: %v", err) + return + } + if lessFinal { c.ToBlock = syncerConfig.ToBlock } if !elementMatch(c.Syncers, syncerConfig.SyncerID) { diff --git a/multidownloader/types/syncer_config_test.go b/multidownloader/types/syncer_config_test.go index 6f6a4a6e9..0207d16c9 100644 --- a/multidownloader/types/syncer_config_test.go +++ b/multidownloader/types/syncer_config_test.go @@ -101,3 +101,114 @@ func TestContractConfigs_MultipleSyncersMultipleContracts(t *testing.T) { require.True(t, found1) require.True(t, found2) } +func TestContractConfig_Update_FromBlock(t *testing.T) { + cc := &ContractConfig{ + Address: common.HexToAddress("0x1"), + FromBlock: 10, + ToBlock: aggkittypes.FinalizedBlock, + Syncers: []SyncerID{"syncer1"}, + } + + // Update with lower FromBlock + cc.Update(aggkittypes.SyncerConfig{ + SyncerID: "syncer2", + ContractsAddr: []common.Address{common.HexToAddress("0x1")}, + FromBlock: 5, + ToBlock: aggkittypes.FinalizedBlock, + }) + + require.Equal(t, uint64(5), cc.FromBlock) + require.Equal(t, []SyncerID{"syncer1", "syncer2"}, cc.Syncers) + + // Update with higher FromBlock (should not change) + cc.Update(aggkittypes.SyncerConfig{ + SyncerID: "syncer3", + ContractsAddr: []common.Address{common.HexToAddress("0x1")}, + FromBlock: 15, + ToBlock: aggkittypes.FinalizedBlock, + }) + + require.Equal(t, uint64(5), cc.FromBlock) + require.Equal(t, []SyncerID{"syncer1", "syncer2", "syncer3"}, cc.Syncers) +} + +func TestContractConfig_Update_ToBlock(t *testing.T) { + cc := &ContractConfig{ + Address: common.HexToAddress("0x1"), + FromBlock: 10, + ToBlock: aggkittypes.FinalizedBlock, + Syncers: []SyncerID{"syncer1"}, + } + + // Update with less final ToBlock (LatestBlock < FinalizedBlock) + cc.Update(aggkittypes.SyncerConfig{ + SyncerID: "syncer2", + ContractsAddr: []common.Address{common.HexToAddress("0x1")}, + FromBlock: 15, + ToBlock: aggkittypes.LatestBlock, + }) + + require.Equal(t, aggkittypes.LatestBlock, cc.ToBlock) + require.Equal(t, []SyncerID{"syncer1", "syncer2"}, cc.Syncers) + + // Update with more final ToBlock (should not change) + cc.Update(aggkittypes.SyncerConfig{ + SyncerID: "syncer3", + ContractsAddr: []common.Address{common.HexToAddress("0x1")}, + FromBlock: 20, + ToBlock: aggkittypes.SafeBlock, + }) + + require.Equal(t, aggkittypes.LatestBlock, cc.ToBlock) + require.Equal(t, []SyncerID{"syncer1", "syncer2", "syncer3"}, cc.Syncers) +} + +func TestContractConfig_Update_Syncers(t *testing.T) { + cc := &ContractConfig{ + Address: common.HexToAddress("0x1"), + FromBlock: 10, + ToBlock: aggkittypes.FinalizedBlock, + Syncers: []SyncerID{"syncer1", "syncer3"}, + } + + // Add new syncer + cc.Update(aggkittypes.SyncerConfig{ + SyncerID: "syncer2", + ContractsAddr: []common.Address{common.HexToAddress("0x1")}, + FromBlock: 15, + ToBlock: aggkittypes.FinalizedBlock, + }) + + require.Equal(t, []SyncerID{"syncer1", "syncer2", "syncer3"}, cc.Syncers) + + // Add existing syncer (should not duplicate) + cc.Update(aggkittypes.SyncerConfig{ + SyncerID: "syncer2", + ContractsAddr: []common.Address{common.HexToAddress("0x1")}, + FromBlock: 20, + ToBlock: aggkittypes.FinalizedBlock, + }) + + require.Equal(t, []SyncerID{"syncer1", "syncer2", "syncer3"}, cc.Syncers) +} + +func TestContractConfig_Update_Combined(t *testing.T) { + cc := &ContractConfig{ + Address: common.HexToAddress("0x1"), + FromBlock: 10, + ToBlock: aggkittypes.FinalizedBlock, + Syncers: []SyncerID{"syncer1"}, + } + + // Update all fields at once + cc.Update(aggkittypes.SyncerConfig{ + SyncerID: "syncer2", + ContractsAddr: []common.Address{common.HexToAddress("0x1")}, + FromBlock: 5, + ToBlock: aggkittypes.LatestBlock, + }) + + require.Equal(t, uint64(5), cc.FromBlock) + require.Equal(t, aggkittypes.LatestBlock, cc.ToBlock) + require.Equal(t, []SyncerID{"syncer1", "syncer2"}, cc.Syncers) +} diff --git a/reorgdetector/reorgdetector.go b/reorgdetector/reorgdetector.go index 998be0a71..ad003e718 100644 --- a/reorgdetector/reorgdetector.go +++ b/reorgdetector/reorgdetector.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" "fmt" - "math/big" "sync" "time" @@ -13,7 +12,6 @@ import ( "github.com/agglayer/aggkit/reorgdetector/migrations" aggkittypes "github.com/agglayer/aggkit/types" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "golang.org/x/sync/errgroup" ) @@ -40,7 +38,7 @@ type ReorgDetector struct { subscriptionsLock sync.RWMutex subscriptions map[string]*Subscription - headersCache map[uint64]*types.Header + headersCache map[uint64]*aggkittypes.BlockHeader headersCacheLock sync.Mutex log *log.Logger @@ -70,7 +68,7 @@ func New(client aggkittypes.BaseEthereumClienter, cfg Config, network Network) ( trackedBlocks: make(map[string]*headersList), subscriptions: make(map[string]*Subscription), log: log, - headersCache: make(map[uint64]*types.Header), + headersCache: make(map[uint64]*aggkittypes.BlockHeader), }, nil } @@ -156,7 +154,7 @@ func (rd *ReorgDetector) GetTrackedBlockByBlockNumber(id string, blockNumber uin // Notifies subscribers if reorg has happened func (rd *ReorgDetector) detectReorgInTrackedList(ctx context.Context) error { // Get the latest finalized block - lastFinalizedBlock, err := rd.finalizedBlockType.BlockHeader(ctx, rd.client) + lastFinalizedBlock, err := rd.client.CustomHeaderByNumber(ctx, &rd.finalizedBlockType) if err != nil { return fmt.Errorf("failed to get the latest finalized block: %w", err) } @@ -177,13 +175,14 @@ func (rd *ReorgDetector) detectReorgInTrackedList(ctx context.Context) error { continue } - rd.log.Debugf("Checking reorgs in all tracked blocks (finalized up to block %d)", lastFinalizedBlock.Number.Uint64()) + rd.log.Debugf("Checking reorgs in all tracked blocks (finalized up to block %d)", + lastFinalizedBlock.Number) errGroup.Go(func() error { headers := hdrs.getSorted() for _, hdr := range headers { // Get the actual header from the network or from the cache - currentHeader, err := rd.client.HeaderByNumber(ctx, new(big.Int).SetUint64(hdr.Num)) + currentHeader, err := rd.client.CustomHeaderByNumber(ctx, aggkittypes.NewBlockNumber(hdr.Num)) if err != nil { return fmt.Errorf("failed to get the header %d: %w", hdr.Num, err) } @@ -197,10 +196,10 @@ func (rd *ReorgDetector) detectReorgInTrackedList(ctx context.Context) error { rd.headersCacheLock.Unlock() // Check if the block hash matches with the actual block hash - if hdr.Hash == oldHeader.Hash() && currentHeader.Hash() == hdr.Hash { + if hdr.Hash == oldHeader.Hash && currentHeader.Hash == hdr.Hash { // Delete block from the tracked blocks list if it is less than or equal to the last finalized block // and hashes matches. If higher than finalized block, we assume a reorg still might happen. - if hdr.Num <= lastFinalizedBlock.Number.Uint64() { + if hdr.Num <= lastFinalizedBlock.Number { hdrs.removeRange(hdr.Num, hdr.Num) if err := rd.removeTrackedBlockRange(id, hdr.Num, hdr.Num); err != nil { @@ -216,7 +215,7 @@ func (rd *ReorgDetector) detectReorgInTrackedList(ctx context.Context) error { FromBlock: hdr.Num, ToBlock: headers[len(headers)-1].Num, SubscriberID: id, - CurrentHash: currentHeader.Hash(), + CurrentHash: currentHeader.Hash, TrackedHash: hdr.Hash, } if err := rd.insertReorgEvent(event); err != nil { diff --git a/reorgdetector/reorgdetector_test.go b/reorgdetector/reorgdetector_test.go index 4fdd1d8f3..2808a3cce 100644 --- a/reorgdetector/reorgdetector_test.go +++ b/reorgdetector/reorgdetector_test.go @@ -11,12 +11,11 @@ import ( cfgtypes "github.com/agglayer/aggkit/config/types" "github.com/agglayer/aggkit/db" + "github.com/agglayer/aggkit/etherman" aggkittypes "github.com/agglayer/aggkit/types" aggkittypesmocks "github.com/agglayer/aggkit/types/mocks" common "github.com/ethereum/go-ethereum/common" - types "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient/simulated" - "github.com/ethereum/go-ethereum/rpc" "github.com/stretchr/testify/require" ) @@ -30,7 +29,7 @@ func Test_ReorgDetector(t *testing.T) { // Create test DB dir testDir := path.Join(t.TempDir(), "reorgdetectorTest_ReorgDetector.sqlite") - reorgDetector, err := New(clientL1.Client(), + reorgDetector, err := New(etherman.NewDefaultEthClient(clientL1.Client(), nil, nil), Config{ DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100), @@ -110,7 +109,7 @@ func Test_ReorgDetector(t *testing.T) { func TestGetTrackedBlocks(t *testing.T) { clientL1 := simulated.NewBackend(nil, simulated.WithBlockGasLimit(10000000)) testDir := path.Join(t.TempDir(), "reorgdetector_TestGetTrackedBlocks.sqlite") - reorgDetector, err := New(clientL1.Client(), Config{ + reorgDetector, err := New(etherman.NewDefaultEthClient(clientL1.Client(), nil, nil), Config{ DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100), }, L1) @@ -211,7 +210,8 @@ func TestGetTrackedBlocks(t *testing.T) { func TestNotSubscribed(t *testing.T) { clientL1 := simulated.NewBackend(nil, simulated.WithBlockGasLimit(10000000)) testDir := path.Join(t.TempDir(), "reorgdetectorTestNotSubscribed.sqlite") - reorgDetector, err := New(clientL1.Client(), Config{DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100)}, L1) + reorgDetector, err := New(etherman.NewDefaultEthClient(clientL1.Client(), nil, nil), + Config{DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100)}, L1) require.NoError(t, err) err = reorgDetector.AddBlockToTrack(context.Background(), "foo", 1, common.Hash{}) require.True(t, strings.Contains(err.Error(), "is not subscribed")) @@ -222,15 +222,15 @@ func TestDetectReorgs(t *testing.T) { ctx := context.Background() syncerID := "test-syncer" - trackedBlock := &types.Header{Number: big.NewInt(9)} + trackedBlock := &aggkittypes.BlockHeader{Number: 9} t.Run("Block not finalized", func(t *testing.T) { t.Parallel() - lastFinalizedBlock := &types.Header{Number: big.NewInt(8)} + lastFinalizedBlock := &aggkittypes.BlockHeader{Number: 8} client := aggkittypesmocks.NewBaseEthereumClienter(t) - client.On("HeaderByNumber", ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))).Return(lastFinalizedBlock, nil) - client.On("HeaderByNumber", ctx, trackedBlock.Number).Return(trackedBlock, nil) + client.EXPECT().CustomHeaderByNumber(ctx, &aggkittypes.FinalizedBlock).Return(lastFinalizedBlock, nil).Maybe() + client.EXPECT().CustomHeaderByNumber(ctx, aggkittypes.NewBlockNumber(trackedBlock.Number)).Return(trackedBlock, nil).Maybe() testDir := path.Join(t.TempDir(), "reorgdetectorTestDetectReorgs.sqlite") reorgDetector, err := New(client, Config{DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100)}, L1) @@ -238,7 +238,7 @@ func TestDetectReorgs(t *testing.T) { _, err = reorgDetector.Subscribe(syncerID) require.NoError(t, err) - require.NoError(t, reorgDetector.AddBlockToTrack(ctx, syncerID, trackedBlock.Number.Uint64(), trackedBlock.Hash())) + require.NoError(t, reorgDetector.AddBlockToTrack(ctx, syncerID, trackedBlock.Number, trackedBlock.Hash)) require.NoError(t, reorgDetector.detectReorgInTrackedList(ctx)) @@ -256,8 +256,8 @@ func TestDetectReorgs(t *testing.T) { lastFinalizedBlock := trackedBlock client := aggkittypesmocks.NewBaseEthereumClienter(t) - client.On("HeaderByNumber", ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))).Return(lastFinalizedBlock, nil) - client.On("HeaderByNumber", ctx, trackedBlock.Number).Return(trackedBlock, nil) + client.EXPECT().CustomHeaderByNumber(ctx, &aggkittypes.FinalizedBlock).Return(lastFinalizedBlock, nil).Maybe() + client.EXPECT().CustomHeaderByNumber(ctx, aggkittypes.NewBlockNumber(trackedBlock.Number)).Return(trackedBlock, nil).Maybe() testDir := path.Join(t.TempDir(), "reorgdetectorTestDetectReorgs.sqlite") reorgDetector, err := New(client, Config{DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100)}, L1) @@ -265,7 +265,7 @@ func TestDetectReorgs(t *testing.T) { _, err = reorgDetector.Subscribe(syncerID) require.NoError(t, err) - require.NoError(t, reorgDetector.AddBlockToTrack(ctx, syncerID, trackedBlock.Number.Uint64(), trackedBlock.Hash())) + require.NoError(t, reorgDetector.AddBlockToTrack(ctx, syncerID, trackedBlock.Number, trackedBlock.Hash)) require.NoError(t, reorgDetector.detectReorgInTrackedList(ctx)) @@ -277,12 +277,12 @@ func TestDetectReorgs(t *testing.T) { t.Run("Reorg happened", func(t *testing.T) { t.Parallel() - lastFinalizedBlock := &types.Header{Number: big.NewInt(5)} - reorgedTrackedBlock := &types.Header{Number: trackedBlock.Number, Extra: []byte("reorged")} // Different hash + lastFinalizedBlock := &aggkittypes.BlockHeader{Number: 5, Hash: common.HexToHash("0x1")} + reorgedTrackedBlock := &aggkittypes.BlockHeader{Number: trackedBlock.Number, Hash: common.HexToHash("0x2")} // Different hash client := aggkittypesmocks.NewBaseEthereumClienter(t) - client.On("HeaderByNumber", ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))).Return(lastFinalizedBlock, nil) - client.On("HeaderByNumber", ctx, trackedBlock.Number).Return(reorgedTrackedBlock, nil) + client.EXPECT().CustomHeaderByNumber(ctx, &aggkittypes.FinalizedBlock).Return(lastFinalizedBlock, nil).Maybe() + client.EXPECT().CustomHeaderByNumber(ctx, aggkittypes.NewBlockNumber(trackedBlock.Number)).Return(reorgedTrackedBlock, nil).Maybe() testDir := path.Join(t.TempDir(), "reorgdetectorTestDetectReorgs.sqlite") reorgDetector, err := New(client, Config{DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100)}, L1) @@ -301,7 +301,7 @@ func TestDetectReorgs(t *testing.T) { wg.Done() }() - require.NoError(t, reorgDetector.AddBlockToTrack(ctx, syncerID, trackedBlock.Number.Uint64(), trackedBlock.Hash())) + require.NoError(t, reorgDetector.AddBlockToTrack(ctx, syncerID, trackedBlock.Number, trackedBlock.Hash)) require.NoError(t, reorgDetector.detectReorgInTrackedList(ctx)) @@ -316,7 +316,7 @@ func TestDetectReorgs(t *testing.T) { func TestLoadTrackedHeaders_ConcurrentWithSaveTrackedBlock(t *testing.T) { clientL1 := simulated.NewBackend(nil, simulated.WithBlockGasLimit(10000000)) testDir := path.Join(t.TempDir(), "reorgdetectorTestConcurrentSave.sqlite") - reorgDetector, err := New(clientL1.Client(), Config{ + reorgDetector, err := New(etherman.NewDefaultEthClient(clientL1.Client(), nil, nil), Config{ DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(100 * time.Millisecond), }, L1) @@ -367,7 +367,7 @@ func TestGetTrackedBlockByBlockNumber(t *testing.T) { clientL1 := simulated.NewBackend(nil, simulated.WithBlockGasLimit(10000000)) testDir := path.Join(t.TempDir(), "reorgdetectorTestGetTrackedBlockByBlockNumber.sqlite") - reorgDetector, err := New(clientL1.Client(), Config{ + reorgDetector, err := New(etherman.NewDefaultEthClient(clientL1.Client(), nil, nil), Config{ DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100), }, L1) @@ -405,7 +405,7 @@ func TestGetDB(t *testing.T) { clientL1 := simulated.NewBackend(nil, simulated.WithBlockGasLimit(10000000)) testDir := path.Join(t.TempDir(), "reorgdetectorTestGetDB.sqlite") - reorgDetector, err := New(clientL1.Client(), Config{ + reorgDetector, err := New(etherman.NewDefaultEthClient(clientL1.Client(), nil, nil), Config{ DBPath: testDir, CheckReorgsInterval: cfgtypes.NewDuration(time.Millisecond * 100), }, L1) diff --git a/sync/adapter_eth_to_multidownloader.go b/sync/adapter_eth_to_multidownloader.go index a59c71b5d..036e80013 100644 --- a/sync/adapter_eth_to_multidownloader.go +++ b/sync/adapter_eth_to_multidownloader.go @@ -5,7 +5,6 @@ package sync import ( "context" "fmt" - "math/big" aggkittypes "github.com/agglayer/aggkit/types" "github.com/ethereum/go-ethereum" @@ -46,21 +45,21 @@ func (a *AdaptEthClientToMultidownloader) BlockNumber(ctx context.Context, func (a *AdaptEthClientToMultidownloader) BlockHeader(ctx context.Context, finality aggkittypes.BlockNumberFinality) (*aggkittypes.BlockHeader, error) { - header, err := finality.BlockHeader(ctx, a.ethClient) + header, err := a.ethClient.CustomHeaderByNumber(ctx, &finality) if err != nil { return nil, fmt.Errorf("AdaptEthClient.BlockHeader: cannot get BlockHeader for finality=%s: %w", finality.String(), err) } - return aggkittypes.NewBlockHeaderFromEthHeader(header), nil + return header, nil } func (a *AdaptEthClientToMultidownloader) HeaderByNumber(ctx context.Context, - number *big.Int) (*aggkittypes.BlockHeader, error) { - header, err := a.ethClient.HeaderByNumber(ctx, number) + number *aggkittypes.BlockNumberFinality) (*aggkittypes.BlockHeader, error) { + header, err := a.ethClient.CustomHeaderByNumber(ctx, number) if err != nil { return nil, fmt.Errorf("AdaptEthClient.HeaderByNumber: cannot get BlockHeader number=%s: %w", number.String(), err) } - return aggkittypes.NewBlockHeaderFromEthHeader(header), nil + return header, nil } func (a *AdaptEthClientToMultidownloader) FilterLogs(ctx context.Context, diff --git a/sync/adapter_eth_to_multidownloader_test.go b/sync/adapter_eth_to_multidownloader_test.go index 54ab6a71a..9cf217555 100644 --- a/sync/adapter_eth_to_multidownloader_test.go +++ b/sync/adapter_eth_to_multidownloader_test.go @@ -41,10 +41,10 @@ func TestAdapterEthClientToMultidownloader_BlockNumber(t *testing.T) { mockEthClient := mocks.NewBaseEthereumClienter(t) sut := NewAdapterEthClientToMultidownloader(mockEthClient) - header := &types.Header{ - Number: big.NewInt(12345), + header := &aggkittypes.BlockHeader{ + Number: 12345, } - mockEthClient.EXPECT().HeaderByNumber(t.Context(), mock.Anything).Return(header, nil) + mockEthClient.EXPECT().CustomHeaderByNumber(t.Context(), mock.Anything).Return(header, nil) blockNumber, err := sut.BlockNumber(t.Context(), aggkittypes.FinalizedBlock) require.NoError(t, err) require.Equal(t, uint64(12345), blockNumber) @@ -54,10 +54,10 @@ func TestAdapterEthClientToMultidownloader_BlockHeader(t *testing.T) { mockEthClient := mocks.NewBaseEthereumClienter(t) sut := NewAdapterEthClientToMultidownloader(mockEthClient) - header := &types.Header{ - Number: big.NewInt(12345), + header := &aggkittypes.BlockHeader{ + Number: 12345, } - mockEthClient.EXPECT().HeaderByNumber(t.Context(), mock.Anything).Return(header, nil) + mockEthClient.EXPECT().CustomHeaderByNumber(t.Context(), mock.Anything).Return(header, nil) blockHeader, err := sut.BlockHeader(t.Context(), aggkittypes.FinalizedBlock) require.NoError(t, err) require.Equal(t, uint64(12345), blockHeader.Number) @@ -66,11 +66,11 @@ func TestAdapterEthClientToMultidownloader_HeaderByNumber(t *testing.T) { mockEthClient := mocks.NewBaseEthereumClienter(t) sut := NewAdapterEthClientToMultidownloader(mockEthClient) - header := &types.Header{ - Number: big.NewInt(12345), + header := &aggkittypes.BlockHeader{ + Number: 12345, } - mockEthClient.EXPECT().HeaderByNumber(t.Context(), big.NewInt(12345)).Return(header, nil) - blockHeader, err := sut.HeaderByNumber(t.Context(), big.NewInt(12345)) + mockEthClient.EXPECT().CustomHeaderByNumber(t.Context(), aggkittypes.NewBlockNumber(12345)).Return(header, nil) + blockHeader, err := sut.HeaderByNumber(t.Context(), aggkittypes.NewBlockNumber(12345)) require.NoError(t, err) require.Equal(t, uint64(12345), blockHeader.Number) } diff --git a/sync/evmdownloader.go b/sync/evmdownloader.go index 404d925cc..f06caecb2 100644 --- a/sync/evmdownloader.go +++ b/sync/evmdownloader.go @@ -75,7 +75,11 @@ func NewEVMDownloader( } logger := log.WithFields("syncer", syncerID) - if finalizedBlockType.LessFinalThan(finality) { + lessFinal, err := finalizedBlockType.LessFinalThan(finality) + if err != nil { + return nil, fmt.Errorf("error comparing finalized block type and block finality: %w", err) + } + if lessFinal { logger.Warnf("finalized block type: %s is less final than block finality: %s, overriding finalized block type to %s", finalizedBlockType.String(), finality.String(), finality.String()) finalizedBlockType = finality @@ -519,7 +523,7 @@ func (d *EVMDownloaderImplementation) filterLogs(unfilteredLogs []types.Log) []t func (d *EVMDownloaderImplementation) GetBlockHeader(ctx context.Context, blockNum uint64) (EVMBlockHeader, bool) { attempts := 0 for { - header, err := d.ethClient.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNum)) + header, err := d.ethClient.HeaderByNumber(ctx, aggkittypes.NewBlockNumber(blockNum)) if err != nil { if errors.Is(err, context.Canceled) { // context is canceled, we don't want to fatal on max attempts in this case diff --git a/sync/evmdownloader_test.go b/sync/evmdownloader_test.go index 10eb0831b..962b6fc9b 100644 --- a/sync/evmdownloader_test.go +++ b/sync/evmdownloader_test.go @@ -197,14 +197,14 @@ func TestGetEventsByBlockRange(t *testing.T) { ParentHash: common.HexToHash("foo"), } blockHash := header.Hash() - clientMock.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(10)). + clientMock.EXPECT().HeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(10)). Return(&aggkittypes.BlockHeader{ Number: 10, Hash: blockHash, ParentHash: &parentHash, }, nil).Once() // Second call returns correct hash - clientMock.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(10)). + clientMock.EXPECT().HeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(10)). Return(&aggkittypes.BlockHeader{ Number: 10, Hash: blockHash, @@ -228,7 +228,7 @@ func TestGetEventsByBlockRange(t *testing.T) { // This will trigger the retry logic and eventually exceed max retries for i := 0; i < MaxRetryCountBlockHashMismatch+1; i++ { parentHash := common.HexToHash("bar") // Different parent hash to create different block hash - clientMock.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(15)). + clientMock.EXPECT().HeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(15)). Return(&aggkittypes.BlockHeader{ Number: 15, ParentHash: &parentHash, // Different parent hash to create different block hash @@ -327,7 +327,7 @@ func TestGetEventsByBlockRange(t *testing.T) { ParentHash: common.HexToHash("foo"), } blockHash := header.Hash() - clientMock.EXPECT().HeaderByNumber(mock.Anything, big.NewInt(int64(b.Num))). + clientMock.EXPECT().HeaderByNumber(mock.Anything, aggkittypes.NewBlockNumber(b.Num)). Return(&aggkittypes.BlockHeader{ Number: b.Num, Hash: blockHash, @@ -477,18 +477,22 @@ func TestGetBlockHeader(t *testing.T) { d, clientMock := NewTestDownloader(t, time.Millisecond) blockNum := uint64(5) - blockNumBig := big.NewInt(5) - returnedBlockEth := &types.Header{ - Number: blockNumBig, + blockNumBig := aggkittypes.NewBlockNumber(blockNum) + parentHash := common.HexToHash("0x4343") + returnedBlockEth := &aggkittypes.BlockHeader{ + Number: blockNum, + Hash: common.HexToHash("0xabc"), + ParentHash: &parentHash, } returnedBlock := &aggkittypes.BlockHeader{ Number: blockNum, - Hash: returnedBlockEth.Hash(), - ParentHash: &returnedBlockEth.ParentHash, + Hash: returnedBlockEth.Hash, + ParentHash: returnedBlockEth.ParentHash, } expectedBlock := EVMBlockHeader{ - Num: 5, - Hash: returnedBlockEth.Hash(), + Num: 5, + Hash: returnedBlockEth.Hash, + ParentHash: *returnedBlockEth.ParentHash, } // at first attempt diff --git a/test/helpers/e2e.go b/test/helpers/e2e.go index 56f4f6a3d..3fa6c9c03 100644 --- a/test/helpers/e2e.go +++ b/test/helpers/e2e.go @@ -17,6 +17,7 @@ import ( "github.com/agglayer/aggkit/aggoracle/chaingersender" "github.com/agglayer/aggkit/bridgesync" cfgtypes "github.com/agglayer/aggkit/config/types" + "github.com/agglayer/aggkit/etherman" "github.com/agglayer/aggkit/l1infotreesync" "github.com/agglayer/aggkit/log" "github.com/agglayer/aggkit/multidownloader" @@ -91,8 +92,8 @@ type EnvironmentConfig struct { func DefaultEnvironmentConfig(l2GERManagerType L2GERManagerContractType) *EnvironmentConfig { return &EnvironmentConfig{ - L1RPCClient: &aggkittypes.NoopRPCClient{}, - L2RPCClient: &aggkittypes.NoopRPCClient{}, + L1RPCClient: nil, // &aggkittypes.NoopRPCClient{}, + L2RPCClient: nil, // &aggkittypes.NoopRPCClient{}, L2GERManagerType: l2GERManagerType, AggOracleCommitteeCfg: AggoraclecommitteeConfig{ EnableAggOracleCommittee: false, @@ -128,7 +129,8 @@ func L1Setup(t *testing.T, cfg *EnvironmentConfig) *L1Environment { // Reorg detector dbPathReorgDetectorL1 := path.Join(t.TempDir(), "ReorgDetectorL1.sqlite") - rdL1, err := reorgdetector.New(l1Client.Client(), reorgdetector.Config{ + l1EthClient := etherman.NewDefaultEthClient(l1Client.Client(), nil, nil) + rdL1, err := reorgdetector.New(l1EthClient, reorgdetector.Config{ DBPath: dbPathReorgDetectorL1, CheckReorgsInterval: cfgtypes.Duration{Duration: time.Millisecond * 100}, //nolint:mnd FinalizedBlock: aggkittypes.FinalizedBlock, @@ -156,20 +158,21 @@ func L1Setup(t *testing.T, cfg *EnvironmentConfig) *L1Environment { RequireStorageContentCompatibility: true, WaitForNewBlocksPeriod: cfgtypes.NewDuration(time.Millisecond), } + var multidownloaderClient aggkittypes.MultiDownloader if useMultidownloaderForTest { multidownloaderClient, err = multidownloader.NewEVMMultidownloader( log.WithFields("module", "multidownloader"), multidownloader.NewConfigDefault("l1", t.TempDir()), "testMD", - l1Client.Client(), + l1EthClient, nil, // RPC client is not simulated nil, nil, ) require.NoError(t, err) } else { - multidownloaderClient = aggkitsync.NewAdapterEthClientToMultidownloader(l1Client.Client()) + multidownloaderClient = aggkitsync.NewAdapterEthClientToMultidownloader(l1EthClient) } l1InfoTreeSync, err := l1infotreesync.New( ctx, @@ -287,7 +290,7 @@ func L2Setup(t *testing.T, cfg *EnvironmentConfig, l1Setup *L1Environment) *L2En log.Fatalf("failed to create binding for GER L2 manager (SC address: %s): %w", gerL2Addr, err) } sender, err = chaingersender.NewEVMChainGERSender( - log.GetDefaultLogger(), evmSenderCfg, l2Client.Client(), l2GERManager, + log.GetDefaultLogger(), evmSenderCfg, etherman.NewDefaultEthClient(l2Client.Client(), nil, nil), l2GERManager, ethTxManagerMock, cfg.AggOracleCommitteeCfg.EnableAggOracleCommittee, ) require.NoError(t, err) @@ -303,7 +306,7 @@ func L2Setup(t *testing.T, cfg *EnvironmentConfig, l1Setup *L1Environment) *L2En // Reorg detector dbPathReorgL2 := path.Join(t.TempDir(), "ReorgDetectorL2.sqlite") - rdL2, err := reorgdetector.New(l2Client.Client(), reorgdetector.Config{ + rdL2, err := reorgdetector.New(etherman.NewDefaultEthClient(l2Client.Client(), nil, nil), reorgdetector.Config{ DBPath: dbPathReorgL2, CheckReorgsInterval: cfgtypes.Duration{Duration: time.Millisecond * 100}, //nolint:mnd FinalizedBlock: aggkittypes.FinalizedBlock, diff --git a/test/helpers/simulated.go b/test/helpers/simulated.go index 84b3f002d..704c0199d 100644 --- a/test/helpers/simulated.go +++ b/test/helpers/simulated.go @@ -9,6 +9,7 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayerbridge" "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayermanager" + "github.com/agglayer/aggkit/etherman" "github.com/agglayer/aggkit/log" "github.com/agglayer/aggkit/test/contracts/proxy" aggkittypes "github.com/agglayer/aggkit/types" @@ -37,6 +38,8 @@ var _ aggkittypes.EthClienter = (*TestClient)(nil) type TestClient struct { simulated.Client aggkittypes.RPCClienter + aggkittypes.CustomEthereumClienter + defaultEthClient aggkittypes.EthClienter } // TestClientOption defines a function signature for optional parameters. @@ -45,7 +48,8 @@ type TestClientOption func(*TestClient) // NewTestClient creates a new TestClient with optional configurations. func NewTestClient(ethClient simulated.Client, opts ...TestClientOption) *TestClient { tc := &TestClient{ - Client: ethClient, + Client: ethClient, + defaultEthClient: etherman.NewDefaultEthClient(ethClient, nil, nil), } // Apply options @@ -67,6 +71,18 @@ func (t *TestClient) Call(result any, method string, args ...any) error { return t.RPCClienter.Call(result, method, args) } +func (t *TestClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + return t.RPCClienter.CallContext(ctx, result, method, args) +} + +func (t *TestClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { + return t.RPCClienter.BatchCallContext(ctx, b) +} + +func (t *TestClient) CustomHeaderByNumber(ctx context.Context, number *aggkittypes.BlockNumberFinality) (*aggkittypes.BlockHeader, error) { + return t.defaultEthClient.CustomHeaderByNumber(ctx, number) +} + // SimulatedBackendSetup defines the setup for a simulated backend. type SimulatedBackendSetup struct { UserAuth *bind.TransactOpts diff --git a/types/block_finality.go b/types/block_finality.go index a21f6e3be..a35fbd52a 100644 --- a/types/block_finality.go +++ b/types/block_finality.go @@ -7,14 +7,12 @@ import ( "strconv" "strings" - "github.com/agglayer/aggkit/log" - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" "github.com/invopop/jsonschema" ) const ( + ConstantBlockName = "ConstantBlock" SafeBlockName = "SafeBlock" FinalizedBlockName = "FinalizedBlock" LatestBlockName = "LatestBlock" @@ -39,36 +37,83 @@ var ( // BlockNumberFinality represents a block finality with an optional offset type BlockNumberFinality struct { - Block BlockNumber - Offset int64 + Block BlockName + Offset int64 + Specific uint64 // Specific block number, Block must be Constant +} + +const baseAutoDetect = 0 + +func ConvertStringToNumber[T any](s string) (T, error) { + zeroValue := *new(T) + + var result interface{} + var err error + + // Check if T is uint64 or int64 and call appropriate parser + switch any(zeroValue).(type) { + case uint64: + result, err = strconv.ParseUint(s, baseAutoDetect, 64) + case int64: + result, err = strconv.ParseInt(s, baseAutoDetect, 64) + default: + return zeroValue, fmt.Errorf("unsupported type for number conversion") + } + + if err == nil { + res, ok := result.(T) + if !ok { + return zeroValue, fmt.Errorf("type assertion failed during number conversion") + } + return res, nil + } + + return zeroValue, fmt.Errorf("invalid block number format: %s", s) +} + +func NewBlockNumber(number uint64) *BlockNumberFinality { + return &BlockNumberFinality{ + Block: Constant, + Specific: number, + } } // NewBlockNumberFinality creates a new BlockNumberFinality from a string // format: [/] e.g: "SafeBlock", "FinalizedBlock/-5", "LatestBlock/+10" -func NewBlockNumberFinality(s string) (BlockNumberFinality, error) { +// can be directly a number +func NewBlockNumberFinality(s string) (*BlockNumberFinality, error) { result := BlockNumberFinality{} splitted := strings.Split(s, blockNameAndOffsetSeparator) if len(splitted) == 0 || len(splitted) > 2 { - return result, fmt.Errorf("invalid block finality format: %s", s) + return nil, fmt.Errorf("invalid block finality format: %s", s) } - block, err := NewBlockNumber(splitted[0]) + block, err := NewBlockName(splitted[0]) if err != nil { - return result, err + // It's a constant block? + n, errParse := ConvertStringToNumber[uint64](splitted[0]) + if errParse == nil { + return NewBlockNumber(n), nil + } + return nil, err } + result.Block = block if len(splitted) == 2 { //nolint:mnd - offset, err := strconv.ParseInt(splitted[1], 10, 64) + offset, err := ConvertStringToNumber[int64](splitted[1]) if err != nil { - return result, fmt.Errorf("invalid block offset format: %s", splitted[1]) + return nil, fmt.Errorf("invalid block offset format: %s", splitted[1]) } result.Offset = offset } - return result, nil + return &result, nil } func (b *BlockNumberFinality) Equal(other BlockNumberFinality) bool { if b == nil { return false } + if b.Block.IsConstant() { + return b.Block == other.Block && b.Specific == other.Specific + } return b.Block == other.Block && b.Offset == other.Offset } @@ -77,6 +122,9 @@ func (b *BlockNumberFinality) String() string { if b == nil { return "nil" } + if b.Block.IsConstant() { + return fmt.Sprintf("%d", b.Specific) + } if b.Offset == 0 { return b.Block.String() } @@ -89,8 +137,7 @@ func (b *BlockNumberFinality) UnmarshalText(data []byte) error { if err != nil { return fmt.Errorf("failed to parse BlockNumberFinality %s: %w", string(data), err) } - b.Block = res.Block - b.Offset = res.Offset + *b = *res return nil } @@ -133,6 +180,20 @@ func (b *BlockNumberFinality) IsSafe() bool { return b.Block == Safe } +func (b *BlockNumberFinality) IsConstant() bool { + if b == nil { + return false + } + return b.Block.IsConstant() +} + +func (b *BlockNumberFinality) HasOffset() bool { + if b == nil { + return false + } + return !b.Block.IsConstant() && b.Offset != 0 +} + // Validate validates the BlockNumberFinality configuration, ensuring that: // - The block name is valid (one of LatestBlock, SafeBlock, FinalizedBlock, or PendingBlock) // - The positive offset does not exceed the maximum allowed for the specific block finality type @@ -170,75 +231,90 @@ func (b BlockNumberFinality) Validate() error { } return nil } +func (b *BlockNumberFinality) ToBigInt() *big.Int { + if b == nil { + return nil + } + if b.Block.IsConstant() { + return big.NewInt(0).SetUint64(b.Specific) + } + return b.Block.ToBigInt() +} + +func (b *BlockNumberFinality) BlockName() BlockName { + if b == nil { + return Empty + } + return b.Block +} + +func (c *BlockNumberFinality) CalculateBlockNumber(baseBlockNumber uint64) uint64 { + if c == nil { + return 0 + } + return c.Block.ApplyOffset(baseBlockNumber, c.Offset) +} // BlockNumber gets the block number from RPC with offset taken into account func (b *BlockNumberFinality) BlockNumber( ctx context.Context, - requester ethereum.ChainReader, + requester CustomEthereumClienter, ) (uint64, error) { if b.IsEmpty() { return 0, fmt.Errorf("BlockNumberFinality.BlockNumber: cannot get block number for empty finality") } - blockHeader, err := requester.HeaderByNumber(ctx, b.Block.toBigInt()) - if err != nil { - return 0, fmt.Errorf("BlockNumberFinality.BlockNumber: Error getting block %s. Err: %w", b.String(), err) + if b.IsConstant() { + return b.Specific, nil } - return b.Block.ApplyOffset(blockHeader.Number.Uint64(), b.Offset), nil -} -// BlockHeader gets the block header from RPC with offset taken into account -func (b *BlockNumberFinality) BlockHeader( - ctx context.Context, - requester ethereum.ChainReader, -) (*types.Header, error) { - blockHeader, err := requester.HeaderByNumber(ctx, b.Block.toBigInt()) + blockHeader, err := requester.CustomHeaderByNumber(ctx, b) if err != nil { - log.Errorf( - "BlockNumberFinality.BlockHeader: Error getting base header (block=%s, offset=%d). Err: %s", - b.String(), b.Offset, err.Error(), - ) - return nil, err - } - - blockNum := b.Block.ApplyOffset(blockHeader.Number.Uint64(), b.Offset) - if blockNum == blockHeader.Number.Uint64() { - return blockHeader, nil + return 0, fmt.Errorf("BlockNumberFinality.BlockNumber: Error getting block %s. Err: %w", b.String(), err) } - return requester.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNum)) + return blockHeader.Number, nil } // LessFinalThan returns true if b is less strict commitment level than other. // In case commitment level keywords are the same, it compares the offsets. // finalized ≤ safe ≤ latest ≤ pending -func (b *BlockNumberFinality) LessFinalThan(other BlockNumberFinality) bool { +func (b *BlockNumberFinality) LessFinalThan(other BlockNumberFinality) (bool, error) { + if b.Block.IsConstant() && other.Block.IsConstant() { + return b.Specific < other.Specific, nil + } + if b.Block.IsConstant() || other.Block.IsConstant() { + return false, fmt.Errorf("cannot compare constant block with non-constant block") + } if b == nil { - return true + return true, nil } if blockOrder[b.Block] > blockOrder[other.Block] { - return true + return true, nil } if b.Block == other.Block { - return b.Offset > other.Offset + return b.Offset > other.Offset, nil } - return false + return false, nil } -type BlockNumber int64 +type BlockName int64 var ( - blockOrder = map[BlockNumber]int{Finalized: 1, Safe: 2, Latest: 3, Pending: 4, Empty: 5} //nolint:mnd + blockOrder = map[BlockName]int{Finalized: 1, Safe: 2, Latest: 3, Pending: 4, Empty: 5} //nolint:mnd ) const ( - Safe = BlockNumber(rpc.SafeBlockNumber) - Finalized = BlockNumber(rpc.FinalizedBlockNumber) - Latest = BlockNumber(rpc.LatestBlockNumber) - Pending = BlockNumber(rpc.PendingBlockNumber) - Empty = BlockNumber(0) + Constant = BlockName(rpc.EarliestBlockNumber - 1) + Safe = BlockName(rpc.SafeBlockNumber) + Finalized = BlockName(rpc.FinalizedBlockNumber) + Latest = BlockName(rpc.LatestBlockNumber) + Pending = BlockName(rpc.PendingBlockNumber) + Empty = BlockName(0) ) -func NewBlockNumber(s string) (BlockNumber, error) { +func NewBlockName(s string) (BlockName, error) { switch strings.ToUpper(s) { + case strings.ToUpper(ConstantBlockName): + return Constant, nil case strings.ToUpper(FinalizedBlockName): return Finalized, nil case strings.ToUpper(SafeBlockName): @@ -252,7 +328,10 @@ func NewBlockNumber(s string) (BlockNumber, error) { } } -func (b BlockNumber) ApplyOffset(blockNumber uint64, offset int64) uint64 { +func (b BlockName) ApplyOffset(blockNumber uint64, offset int64) uint64 { + if b.IsConstant() { + return blockNumber + } originalBlockNumber := blockNumber if offset < 0 { if blockNumber < uint64(-offset) { @@ -270,8 +349,10 @@ func (b BlockNumber) ApplyOffset(blockNumber uint64, offset int64) uint64 { return blockNumber } -func (b BlockNumber) String() string { +func (b BlockName) String() string { switch b { + case Constant: + return ConstantBlockName case Finalized: return FinalizedBlockName case Safe: @@ -287,9 +368,17 @@ func (b BlockNumber) String() string { } } -func (b BlockNumber) toBigInt() *big.Int { - if b == Latest || b == Empty { +func (b BlockName) IsConstant() bool { + return b == Constant +} + +func (b BlockName) ToBigInt() *big.Int { + switch b { + case Latest, Empty: return nil + case Constant: + return big.NewInt(0) + default: + return big.NewInt(int64(b)) } - return big.NewInt(int64(b)) } diff --git a/types/block_finality_test.go b/types/block_finality_test.go index 623706623..c3298144d 100644 --- a/types/block_finality_test.go +++ b/types/block_finality_test.go @@ -3,13 +3,10 @@ package types_test import ( "bytes" "fmt" - "math/big" "testing" aggkittypes "github.com/agglayer/aggkit/types" "github.com/agglayer/aggkit/types/mocks" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rpc" "github.com/mitchellh/mapstructure" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -74,6 +71,9 @@ func TestBlockNumberFinalityReadFromConfigFile(t *testing.T) { require.Error(t, err) _, err = readConfigFile[configTest](t, "BlockFinality = \"\"") require.Error(t, err) + cfg, err = readConfigFile[configTest](t, "BlockFinality = \"123\"") + require.NoError(t, err) + require.Equal(t, "123", cfg.BlockFinality.String()) } func TestBlockNumberFinalityWithOffset(t *testing.T) { @@ -121,6 +121,7 @@ func TestBlockNumberFinality_LessFinalThan(t *testing.T) { firstFinality aggkittypes.BlockNumberFinality secondFinality aggkittypes.BlockNumberFinality isLessFinal bool + expectedError string }{ { name: "empty finality less final than pending block type", @@ -206,12 +207,40 @@ func TestBlockNumberFinality_LessFinalThan(t *testing.T) { }, isLessFinal: false, }, + { + name: "compare 2 cte", + firstFinality: *aggkittypes.NewBlockNumber(234), + secondFinality: *aggkittypes.NewBlockNumber(345), + isLessFinal: true, + }, + { + name: "compare 2 cte", + firstFinality: *aggkittypes.NewBlockNumber(345), + secondFinality: *aggkittypes.NewBlockNumber(234), + isLessFinal: false, + }, + { + name: "compare cte vs non-cte", + firstFinality: *aggkittypes.NewBlockNumber(234), + secondFinality: aggkittypes.LatestBlock, + expectedError: "cannot compare constant block with non-constant block", + }, + { + name: "compare cte vs non-cte", + firstFinality: aggkittypes.LatestBlock, + secondFinality: *aggkittypes.NewBlockNumber(234), + expectedError: "cannot compare constant block with non-constant block", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := tt.firstFinality.LessFinalThan(tt.secondFinality) - require.Equal(t, tt.isLessFinal, result) + result, err := tt.firstFinality.LessFinalThan(tt.secondFinality) + if tt.expectedError != "" { + require.ErrorContains(t, err, tt.expectedError) + } else { + require.Equal(t, tt.isLessFinal, result) + } }) } } @@ -219,7 +248,7 @@ func TestBlockNumberFinality_LessFinalThan(t *testing.T) { func TestBlockNumber_ApplyOffset(t *testing.T) { tests := []struct { name string - blockType aggkittypes.BlockNumber + blockType aggkittypes.BlockName blockNumber uint64 offset int64 expectedResult uint64 @@ -266,6 +295,13 @@ func TestBlockNumber_ApplyOffset(t *testing.T) { offset: 10, expectedResult: 500, }, + { + name: "const block ignore offset", + blockType: aggkittypes.Constant, + blockNumber: 500, + offset: 10, + expectedResult: 500, + }, } for _, tt := range tests { @@ -325,6 +361,11 @@ func TestBlockNumberFinality(t *testing.T) { input: "InvalidBlock", expectedErr: fmt.Errorf("invalid finality keyword: InvalidBlock"), }, + { + name: "cte block", + input: "1234", + expectedResult: *aggkittypes.NewBlockNumber(1234), + }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { @@ -349,66 +390,15 @@ func TestBlockNumberFinalityJSONSchema(t *testing.T) { func TestBlockNumberFinality_BlockNumber(t *testing.T) { ctx := t.Context() mockClient := mocks.NewBaseEthereumClienter(t) - finalizedHeader := &types.Header{Number: big.NewInt(100)} - mockClient.EXPECT().HeaderByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))).Return(finalizedHeader, nil).Maybe() + finalizedHeader := &aggkittypes.BlockHeader{Number: 100} + mockClient.EXPECT().CustomHeaderByNumber(ctx, &aggkittypes.FinalizedBlock).Return(finalizedHeader, nil).Maybe() _, err := blockFinalityEmpty.BlockNumber(ctx, mockClient) require.Error(t, err) _, err = blockFinalityCreated.BlockNumber(ctx, mockClient) require.Error(t, err) number, err := aggkittypes.FinalizedBlock.BlockNumber(ctx, mockClient) require.NoError(t, err) - require.Equal(t, finalizedHeader.Number.Uint64(), number) -} - -func TestBlockNumberFinality_BlockHeader(t *testing.T) { - ctx := t.Context() - - t.Run("Success with offset", func(t *testing.T) { - mockClient := mocks.NewBaseEthereumClienter(t) - blockFinality := aggkittypes.BlockNumberFinality{Block: aggkittypes.Finalized, Offset: -5} - - finalizedHeader := &types.Header{Number: big.NewInt(100)} - offsetHeader := &types.Header{Number: big.NewInt(95)} - - mockClient.EXPECT().HeaderByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))).Return(finalizedHeader, nil).Once() - mockClient.EXPECT().HeaderByNumber(ctx, big.NewInt(95)).Return(offsetHeader, nil).Once() - - result, err := blockFinality.BlockHeader(ctx, mockClient) - require.NoError(t, err) - require.Equal(t, offsetHeader, result) - }) - - t.Run("Error on first call", func(t *testing.T) { - mockClient := mocks.NewBaseEthereumClienter(t) - blockFinality := aggkittypes.BlockNumberFinality{Block: aggkittypes.Latest, Offset: 0} - - testErr := fmt.Errorf("first call error") - mockClient.EXPECT().HeaderByNumber(ctx, (*big.Int)(nil)).Return(nil, testErr).Once() - - result, err := blockFinality.BlockHeader(ctx, mockClient) - require.Error(t, err) - require.Nil(t, result) - require.Contains(t, err.Error(), testErr.Error()) - }) - - t.Run("Error on second call", func(t *testing.T) { - mockClient := mocks.NewBaseEthereumClienter(t) - // Safe with positive offset so the resolved block differs from the base and triggers a second fetch - blockFinality := aggkittypes.BlockNumberFinality{Block: aggkittypes.Safe, Offset: 10} - - safeHeader := &types.Header{Number: big.NewInt(100)} - testErr := fmt.Errorf("second call error") - - // First call resolves base Safe header (100) - mockClient.EXPECT().HeaderByNumber(ctx, big.NewInt(int64(rpc.SafeBlockNumber))).Return(safeHeader, nil).Once() - // Second call attempts to fetch 110 and fails - mockClient.EXPECT().HeaderByNumber(ctx, big.NewInt(110)).Return(nil, testErr).Once() - - result, err := blockFinality.BlockHeader(ctx, mockClient) - require.Error(t, err) - require.Nil(t, result) - require.Contains(t, err.Error(), testErr.Error()) - }) + require.Equal(t, finalizedHeader.Number, number) } func TestBlockNumberFinality_Validate(t *testing.T) { @@ -495,7 +485,7 @@ func TestBlockNumberFinality_Validate(t *testing.T) { }, { name: "Unknown block type should fail validation", - finality: aggkittypes.BlockNumberFinality{Block: aggkittypes.BlockNumber(999), Offset: 0}, + finality: aggkittypes.BlockNumberFinality{Block: aggkittypes.BlockName(999), Offset: 0}, expectedError: "block type must be one of LatestBlock, SafeBlock, FinalizedBlock, or PendingBlock", }, { @@ -528,9 +518,84 @@ func TestBlockNumberFinalityEqual(t *testing.T) { bn2 := aggkittypes.BlockNumberFinality{Block: aggkittypes.Safe, Offset: -5} bn3 := aggkittypes.BlockNumberFinality{Block: aggkittypes.Safe, Offset: 0} bn4 := aggkittypes.BlockNumberFinality{Block: aggkittypes.Finalized, Offset: -5} - + bn5 := aggkittypes.NewBlockNumber(1234) + bn6 := aggkittypes.NewBlockNumber(12345) require.False(t, blockFinalityEmpty.Equal(bn1), "bn1 should not be equal to empty finality") require.True(t, bn1.Equal(bn2), "bn1 should be equal to bn2") require.False(t, bn1.Equal(bn3), "bn1 should not be equal to bn3") require.False(t, bn1.Equal(bn4), "bn1 should not be equal to bn4") + require.False(t, bn5.Equal(bn3)) + require.False(t, bn3.Equal(*bn5)) + require.True(t, bn5.Equal(*bn5)) + require.False(t, bn5.Equal(*bn6)) +} +func TestNewBlockNumberFinalityCte(t *testing.T) { + sut, err := aggkittypes.NewBlockNumberFinality("1234") + require.NoError(t, err) + require.Equal(t, "1234", sut.String()) + sut, err = aggkittypes.NewBlockNumberFinality("0x1234") + require.NoError(t, err) + require.Equal(t, "4660", sut.String()) + sut, err = aggkittypes.NewBlockNumberFinality("0xBEEF") + require.NoError(t, err) + require.Equal(t, "48879", sut.String()) +} + +func TestConvertStringToNumber(t *testing.T) { + num, err := aggkittypes.ConvertStringToNumber[uint64]("1234") + require.NoError(t, err) + require.Equal(t, uint64(1234), num) + num, err = aggkittypes.ConvertStringToNumber[uint64]("0x1234") + require.NoError(t, err) + require.Equal(t, uint64(4660), num) + _, err = aggkittypes.ConvertStringToNumber[uint64]("123A") + require.Error(t, err) + + _, err = aggkittypes.ConvertStringToNumber[uint8]("123A") + require.ErrorContains(t, err, "unsupported type ") +} + +func TestBlockNumberFinality_HasOffset(t *testing.T) { + bn := aggkittypes.BlockNumberFinality{Block: aggkittypes.Safe, Offset: -5} + require.True(t, bn.HasOffset()) + bn = aggkittypes.BlockNumberFinality{Block: aggkittypes.Latest, Offset: 10} + require.True(t, bn.HasOffset()) + bn = aggkittypes.BlockNumberFinality{Block: aggkittypes.Pending, Offset: 0} + require.False(t, bn.HasOffset()) +} + +func TestBlockNumberFinality_ToBigInt(t *testing.T) { + bn := aggkittypes.BlockNumberFinality{Block: aggkittypes.Constant, Specific: 0} + num := bn.ToBigInt() + require.Equal(t, "0", num.String()) + bn = aggkittypes.BlockNumberFinality{Block: aggkittypes.Constant, Specific: 12345} + num = bn.ToBigInt() + require.Equal(t, "12345", num.String()) + var bnNil *aggkittypes.BlockNumberFinality + num = bnNil.ToBigInt() + require.Nil(t, num) +} + +func TestBlockNumberFinality_CalculateBlockNumber(t *testing.T) { + bn := aggkittypes.BlockNumberFinality{Block: aggkittypes.Safe, Offset: -5} + result := bn.CalculateBlockNumber(100) + require.Equal(t, uint64(95), result) + bn = aggkittypes.BlockNumberFinality{Block: aggkittypes.Safe, Offset: 10} + result = bn.CalculateBlockNumber(100) + require.Equal(t, uint64(110), result) + bn = aggkittypes.BlockNumberFinality{Block: aggkittypes.Latest, Offset: 10} + result = bn.CalculateBlockNumber(100) + require.Equal(t, uint64(100), result) + var bnNil *aggkittypes.BlockNumberFinality + result = bnNil.CalculateBlockNumber(100) + require.Equal(t, uint64(0), result) +} + +func TestBlockNumberFinality_BlockName(t *testing.T) { + bn := aggkittypes.BlockNumberFinality{Block: aggkittypes.Safe, Offset: -5} + require.Equal(t, aggkittypes.Safe, bn.BlockName()) + bn = aggkittypes.BlockNumberFinality{Block: aggkittypes.Latest, Offset: 10} + require.Equal(t, aggkittypes.Latest, bn.BlockName()) + var bnNil *aggkittypes.BlockNumberFinality + require.Equal(t, aggkittypes.Empty, bnNil.BlockName()) } diff --git a/types/block_header.go b/types/block_header.go index c07dc761f..184107dcc 100644 --- a/types/block_header.go +++ b/types/block_header.go @@ -12,6 +12,8 @@ type BlockHeader struct { Hash common.Hash `json:"hash"` Time uint64 `json:"timestamp"` ParentHash *common.Hash `json:"parentHash"` + // the RequestedBlock is the original Block requested + RequestedBlock *BlockNumberFinality } func NewBlockHeader(number uint64, hash common.Hash, time uint64, parentHash *common.Hash) *BlockHeader { diff --git a/types/eth_client.go b/types/eth_client.go index 2e7770f79..f3f888736 100644 --- a/types/eth_client.go +++ b/types/eth_client.go @@ -2,78 +2,58 @@ package types import ( "context" - "fmt" - aggkitcommon "github.com/agglayer/aggkit/common" - commontypes "github.com/agglayer/aggkit/common/types" - "github.com/agglayer/aggkit/log" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" ) -var _ EthClienter = (*DefaultEthClient)(nil) - -// DefaultEthClient is the default implementation of EthClienter. -type DefaultEthClient struct { +// EthClienter defines the methods for an Ethereum RPC client. +type EthClienter interface { BaseEthereumClienter RPCClienter + CustomEthereumClienter } -// NewDefaultEthClient creates a new DefaultEthClient. -func NewDefaultEthClient(baseClient BaseEthereumClienter, rpcClient RPCClienter) *DefaultEthClient { - return &DefaultEthClient{ - BaseEthereumClienter: baseClient, - RPCClienter: rpcClient, - } +// EthChainReader defines methods to read blocks and headers from the Ethereum chain. +// it's based on ethereum.ChainReader +// It have been removed HeaderByNumber in favour of CustomHeaderByNumber +type EthChainReader interface { + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) } -// EthClienter defines the methods for an Ethereum RPC client. -type EthClienter interface { - BaseEthereumClienter - RPCClienter +type EthereumClienter interface { + ethereum.BlockNumberReader + ethereum.ChainIDReader + EthChainReader + ethereum.ChainStateReader + ethereum.LogFilterer + ethereum.TransactionReader + bind.ContractBackend } // BaseEthereumClienter defines the methods required to interact with an Ethereum client. type BaseEthereumClienter interface { ethereum.BlockNumberReader ethereum.ChainIDReader - ethereum.ChainReader + EthChainReader ethereum.ChainStateReader ethereum.LogFilterer ethereum.TransactionReader bind.ContractBackend + CustomEthereumClienter +} + +type CustomEthereumClienter interface { + // Like HeaderByNumber but returns a custom BlockHeader type. + CustomHeaderByNumber(ctx context.Context, number *BlockNumberFinality) (*BlockHeader, error) } // RPCClienter defines an interface for making generic RPC calls. type RPCClienter interface { Call(result any, method string, args ...any) error + CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error BatchCallContext(ctx context.Context, b []rpc.BatchElem) error } - -var _ RPCClienter = (*NoopRPCClient)(nil) - -// NoopRPCClient is no operation implementation for the RPCClienter interface -type NoopRPCClient struct{} - -func (c *NoopRPCClient) Call(result any, method string, args ...any) error { - return nil -} - -func (c *NoopRPCClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - return nil -} - -// DialWithRetry attempts to connect to an Ethereum client with retries and exponential backoff. -// It returns an EthClienter on success or an error if all attempts fail. -func DialWithRetry(ctx context.Context, url string, retryHandler commontypes.RetryHandler) (EthClienter, error) { - return aggkitcommon.Execute(retryHandler, ctx, log.Infof, fmt.Sprintf("dial %s rpc", url), - func() (EthClienter, error) { - client, err := ethclient.Dial(url) - if err != nil { - return nil, err - } - return NewDefaultEthClient(client, client.Client()), nil - }) -} diff --git a/types/mocks/mock_base_ethereum_clienter.go b/types/mocks/mock_base_ethereum_clienter.go index 42024819d..8b5fad3e7 100644 --- a/types/mocks/mock_base_ethereum_clienter.go +++ b/types/mocks/mock_base_ethereum_clienter.go @@ -13,6 +13,8 @@ import ( ethereum "github.com/ethereum/go-ethereum" mock "github.com/stretchr/testify/mock" + + types "github.com/agglayer/aggkit/types" ) // BaseEthereumClienter is an autogenerated mock type for the BaseEthereumClienter type @@ -88,124 +90,6 @@ func (_c *BaseEthereumClienter_BalanceAt_Call) RunAndReturn(run func(context.Con return _c } -// BlockByHash provides a mock function with given fields: ctx, hash -func (_m *BaseEthereumClienter) BlockByHash(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHash") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Block, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Block); ok { - r0 = rf(ctx, hash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// BaseEthereumClienter_BlockByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHash' -type BaseEthereumClienter_BlockByHash_Call struct { - *mock.Call -} - -// BlockByHash is a helper method to define mock.On call -// - ctx context.Context -// - hash common.Hash -func (_e *BaseEthereumClienter_Expecter) BlockByHash(ctx interface{}, hash interface{}) *BaseEthereumClienter_BlockByHash_Call { - return &BaseEthereumClienter_BlockByHash_Call{Call: _e.mock.On("BlockByHash", ctx, hash)} -} - -func (_c *BaseEthereumClienter_BlockByHash_Call) Run(run func(ctx context.Context, hash common.Hash)) *BaseEthereumClienter_BlockByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *BaseEthereumClienter_BlockByHash_Call) Return(_a0 *coretypes.Block, _a1 error) *BaseEthereumClienter_BlockByHash_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *BaseEthereumClienter_BlockByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Block, error)) *BaseEthereumClienter_BlockByHash_Call { - _c.Call.Return(run) - return _c -} - -// BlockByNumber provides a mock function with given fields: ctx, number -func (_m *BaseEthereumClienter) BlockByNumber(ctx context.Context, number *big.Int) (*coretypes.Block, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumber") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Block, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Block); ok { - r0 = rf(ctx, number) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - 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 -} - -// BaseEthereumClienter_BlockByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumber' -type BaseEthereumClienter_BlockByNumber_Call struct { - *mock.Call -} - -// BlockByNumber is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *BaseEthereumClienter_Expecter) BlockByNumber(ctx interface{}, number interface{}) *BaseEthereumClienter_BlockByNumber_Call { - return &BaseEthereumClienter_BlockByNumber_Call{Call: _e.mock.On("BlockByNumber", ctx, number)} -} - -func (_c *BaseEthereumClienter_BlockByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *BaseEthereumClienter_BlockByNumber_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *BaseEthereumClienter_BlockByNumber_Call) Return(_a0 *coretypes.Block, _a1 error) *BaseEthereumClienter_BlockByNumber_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *BaseEthereumClienter_BlockByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Block, error)) *BaseEthereumClienter_BlockByNumber_Call { - _c.Call.Return(run) - return _c -} - // BlockNumber provides a mock function with given fields: ctx func (_m *BaseEthereumClienter) BlockNumber(ctx context.Context) (uint64, error) { ret := _m.Called(ctx) @@ -440,6 +324,65 @@ func (_c *BaseEthereumClienter_CodeAt_Call) RunAndReturn(run func(context.Contex return _c } +// CustomHeaderByNumber provides a mock function with given fields: ctx, number +func (_m *BaseEthereumClienter) CustomHeaderByNumber(ctx context.Context, number *types.BlockNumberFinality) (*types.BlockHeader, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for CustomHeaderByNumber") + } + + var r0 *types.BlockHeader + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.BlockNumberFinality) (*types.BlockHeader, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.BlockNumberFinality) *types.BlockHeader); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.BlockHeader) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.BlockNumberFinality) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BaseEthereumClienter_CustomHeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CustomHeaderByNumber' +type BaseEthereumClienter_CustomHeaderByNumber_Call struct { + *mock.Call +} + +// CustomHeaderByNumber is a helper method to define mock.On call +// - ctx context.Context +// - number *types.BlockNumberFinality +func (_e *BaseEthereumClienter_Expecter) CustomHeaderByNumber(ctx interface{}, number interface{}) *BaseEthereumClienter_CustomHeaderByNumber_Call { + return &BaseEthereumClienter_CustomHeaderByNumber_Call{Call: _e.mock.On("CustomHeaderByNumber", ctx, number)} +} + +func (_c *BaseEthereumClienter_CustomHeaderByNumber_Call) Run(run func(ctx context.Context, number *types.BlockNumberFinality)) *BaseEthereumClienter_CustomHeaderByNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.BlockNumberFinality)) + }) + return _c +} + +func (_c *BaseEthereumClienter_CustomHeaderByNumber_Call) Return(_a0 *types.BlockHeader, _a1 error) *BaseEthereumClienter_CustomHeaderByNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *BaseEthereumClienter_CustomHeaderByNumber_Call) RunAndReturn(run func(context.Context, *types.BlockNumberFinality) (*types.BlockHeader, error)) *BaseEthereumClienter_CustomHeaderByNumber_Call { + _c.Call.Return(run) + return _c +} + // EstimateGas provides a mock function with given fields: ctx, call func (_m *BaseEthereumClienter) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { ret := _m.Called(ctx, call) @@ -1016,65 +959,6 @@ func (_c *BaseEthereumClienter_SubscribeFilterLogs_Call) RunAndReturn(run func(c return _c } -// SubscribeNewHead provides a mock function with given fields: ctx, ch -func (_m *BaseEthereumClienter) SubscribeNewHead(ctx context.Context, ch chan<- *coretypes.Header) (ethereum.Subscription, error) { - ret := _m.Called(ctx, ch) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 ethereum.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *coretypes.Header) (ethereum.Subscription, error)); ok { - return rf(ctx, ch) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- *coretypes.Header) ethereum.Subscription); ok { - r0 = rf(ctx, ch) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(ethereum.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- *coretypes.Header) error); ok { - r1 = rf(ctx, ch) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// BaseEthereumClienter_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type BaseEthereumClienter_SubscribeNewHead_Call struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - ch chan<- *coretypes.Header -func (_e *BaseEthereumClienter_Expecter) SubscribeNewHead(ctx interface{}, ch interface{}) *BaseEthereumClienter_SubscribeNewHead_Call { - return &BaseEthereumClienter_SubscribeNewHead_Call{Call: _e.mock.On("SubscribeNewHead", ctx, ch)} -} - -func (_c *BaseEthereumClienter_SubscribeNewHead_Call) Run(run func(ctx context.Context, ch chan<- *coretypes.Header)) *BaseEthereumClienter_SubscribeNewHead_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- *coretypes.Header)) - }) - return _c -} - -func (_c *BaseEthereumClienter_SubscribeNewHead_Call) Return(_a0 ethereum.Subscription, _a1 error) *BaseEthereumClienter_SubscribeNewHead_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *BaseEthereumClienter_SubscribeNewHead_Call) RunAndReturn(run func(context.Context, chan<- *coretypes.Header) (ethereum.Subscription, error)) *BaseEthereumClienter_SubscribeNewHead_Call { - _c.Call.Return(run) - return _c -} - // SubscribeTransactionReceipts provides a mock function with given fields: ctx, q, ch func (_m *BaseEthereumClienter) SubscribeTransactionReceipts(ctx context.Context, q *ethereum.TransactionReceiptsQuery, ch chan<- []*coretypes.Receipt) (ethereum.Subscription, error) { ret := _m.Called(ctx, q, ch) @@ -1317,123 +1201,6 @@ func (_c *BaseEthereumClienter_TransactionByHash_Call) RunAndReturn(run func(con return _c } -// TransactionCount provides a mock function with given fields: ctx, blockHash -func (_m *BaseEthereumClienter) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { - ret := _m.Called(ctx, blockHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionCount") - } - - var r0 uint - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (uint, error)); ok { - return rf(ctx, blockHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) uint); ok { - r0 = rf(ctx, blockHash) - } else { - r0 = ret.Get(0).(uint) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, blockHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// BaseEthereumClienter_TransactionCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionCount' -type BaseEthereumClienter_TransactionCount_Call struct { - *mock.Call -} - -// TransactionCount is a helper method to define mock.On call -// - ctx context.Context -// - blockHash common.Hash -func (_e *BaseEthereumClienter_Expecter) TransactionCount(ctx interface{}, blockHash interface{}) *BaseEthereumClienter_TransactionCount_Call { - return &BaseEthereumClienter_TransactionCount_Call{Call: _e.mock.On("TransactionCount", ctx, blockHash)} -} - -func (_c *BaseEthereumClienter_TransactionCount_Call) Run(run func(ctx context.Context, blockHash common.Hash)) *BaseEthereumClienter_TransactionCount_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *BaseEthereumClienter_TransactionCount_Call) Return(_a0 uint, _a1 error) *BaseEthereumClienter_TransactionCount_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *BaseEthereumClienter_TransactionCount_Call) RunAndReturn(run func(context.Context, common.Hash) (uint, error)) *BaseEthereumClienter_TransactionCount_Call { - _c.Call.Return(run) - return _c -} - -// TransactionInBlock provides a mock function with given fields: ctx, blockHash, index -func (_m *BaseEthereumClienter) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*coretypes.Transaction, error) { - ret := _m.Called(ctx, blockHash, index) - - if len(ret) == 0 { - panic("no return value specified for TransactionInBlock") - } - - var r0 *coretypes.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) (*coretypes.Transaction, error)); ok { - return rf(ctx, blockHash, index) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) *coretypes.Transaction); ok { - r0 = rf(ctx, blockHash, index) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash, uint) error); ok { - r1 = rf(ctx, blockHash, index) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// BaseEthereumClienter_TransactionInBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionInBlock' -type BaseEthereumClienter_TransactionInBlock_Call struct { - *mock.Call -} - -// TransactionInBlock is a helper method to define mock.On call -// - ctx context.Context -// - blockHash common.Hash -// - index uint -func (_e *BaseEthereumClienter_Expecter) TransactionInBlock(ctx interface{}, blockHash interface{}, index interface{}) *BaseEthereumClienter_TransactionInBlock_Call { - return &BaseEthereumClienter_TransactionInBlock_Call{Call: _e.mock.On("TransactionInBlock", ctx, blockHash, index)} -} - -func (_c *BaseEthereumClienter_TransactionInBlock_Call) Run(run func(ctx context.Context, blockHash common.Hash, index uint)) *BaseEthereumClienter_TransactionInBlock_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash), args[2].(uint)) - }) - return _c -} - -func (_c *BaseEthereumClienter_TransactionInBlock_Call) Return(_a0 *coretypes.Transaction, _a1 error) *BaseEthereumClienter_TransactionInBlock_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *BaseEthereumClienter_TransactionInBlock_Call) RunAndReturn(run func(context.Context, common.Hash, uint) (*coretypes.Transaction, error)) *BaseEthereumClienter_TransactionInBlock_Call { - _c.Call.Return(run) - return _c -} - // TransactionReceipt provides a mock function with given fields: ctx, txHash func (_m *BaseEthereumClienter) TransactionReceipt(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { ret := _m.Called(ctx, txHash) diff --git a/types/mocks/mock_eth_clienter.go b/types/mocks/mock_eth_clienter.go index 55bc5e2d0..e2b42fef2 100644 --- a/types/mocks/mock_eth_clienter.go +++ b/types/mocks/mock_eth_clienter.go @@ -15,6 +15,8 @@ import ( mock "github.com/stretchr/testify/mock" rpc "github.com/ethereum/go-ethereum/rpc" + + types "github.com/agglayer/aggkit/types" ) // EthClienter is an autogenerated mock type for the EthClienter type @@ -137,124 +139,6 @@ func (_c *EthClienter_BatchCallContext_Call) RunAndReturn(run func(context.Conte return _c } -// BlockByHash provides a mock function with given fields: ctx, hash -func (_m *EthClienter) BlockByHash(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHash") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Block, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Block); ok { - r0 = rf(ctx, hash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EthClienter_BlockByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHash' -type EthClienter_BlockByHash_Call struct { - *mock.Call -} - -// BlockByHash is a helper method to define mock.On call -// - ctx context.Context -// - hash common.Hash -func (_e *EthClienter_Expecter) BlockByHash(ctx interface{}, hash interface{}) *EthClienter_BlockByHash_Call { - return &EthClienter_BlockByHash_Call{Call: _e.mock.On("BlockByHash", ctx, hash)} -} - -func (_c *EthClienter_BlockByHash_Call) Run(run func(ctx context.Context, hash common.Hash)) *EthClienter_BlockByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *EthClienter_BlockByHash_Call) Return(_a0 *coretypes.Block, _a1 error) *EthClienter_BlockByHash_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *EthClienter_BlockByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Block, error)) *EthClienter_BlockByHash_Call { - _c.Call.Return(run) - return _c -} - -// BlockByNumber provides a mock function with given fields: ctx, number -func (_m *EthClienter) BlockByNumber(ctx context.Context, number *big.Int) (*coretypes.Block, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumber") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Block, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Block); ok { - r0 = rf(ctx, number) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - 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 -} - -// EthClienter_BlockByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumber' -type EthClienter_BlockByNumber_Call struct { - *mock.Call -} - -// BlockByNumber is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *EthClienter_Expecter) BlockByNumber(ctx interface{}, number interface{}) *EthClienter_BlockByNumber_Call { - return &EthClienter_BlockByNumber_Call{Call: _e.mock.On("BlockByNumber", ctx, number)} -} - -func (_c *EthClienter_BlockByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *EthClienter_BlockByNumber_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *EthClienter_BlockByNumber_Call) Return(_a0 *coretypes.Block, _a1 error) *EthClienter_BlockByNumber_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *EthClienter_BlockByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Block, error)) *EthClienter_BlockByNumber_Call { - _c.Call.Return(run) - return _c -} - // BlockNumber provides a mock function with given fields: ctx func (_m *EthClienter) BlockNumber(ctx context.Context) (uint64, error) { ret := _m.Called(ctx) @@ -369,6 +253,65 @@ func (_c *EthClienter_Call_Call) RunAndReturn(run func(any, string, ...any) erro return _c } +// CallContext provides a mock function with given fields: ctx, result, method, args +func (_m *EthClienter) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + var _ca []interface{} + _ca = append(_ca, ctx, result, method) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CallContext") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { + r0 = rf(ctx, result, method, args...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EthClienter_CallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContext' +type EthClienter_CallContext_Call struct { + *mock.Call +} + +// CallContext is a helper method to define mock.On call +// - ctx context.Context +// - result interface{} +// - method string +// - args ...interface{} +func (_e *EthClienter_Expecter) CallContext(ctx interface{}, result interface{}, method interface{}, args ...interface{}) *EthClienter_CallContext_Call { + return &EthClienter_CallContext_Call{Call: _e.mock.On("CallContext", + append([]interface{}{ctx, result, method}, args...)...)} +} + +func (_c *EthClienter_CallContext_Call) Run(run func(ctx context.Context, result interface{}, method string, args ...interface{})) *EthClienter_CallContext_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-3) + for i, a := range args[3:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(context.Context), args[1].(interface{}), args[2].(string), variadicArgs...) + }) + return _c +} + +func (_c *EthClienter_CallContext_Call) Return(_a0 error) *EthClienter_CallContext_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EthClienter_CallContext_Call) RunAndReturn(run func(context.Context, interface{}, string, ...interface{}) error) *EthClienter_CallContext_Call { + _c.Call.Return(run) + return _c +} + // CallContract provides a mock function with given fields: ctx, call, blockNumber func (_m *EthClienter) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, call, blockNumber) @@ -547,6 +490,65 @@ func (_c *EthClienter_CodeAt_Call) RunAndReturn(run func(context.Context, common return _c } +// CustomHeaderByNumber provides a mock function with given fields: ctx, number +func (_m *EthClienter) CustomHeaderByNumber(ctx context.Context, number *types.BlockNumberFinality) (*types.BlockHeader, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for CustomHeaderByNumber") + } + + var r0 *types.BlockHeader + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.BlockNumberFinality) (*types.BlockHeader, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.BlockNumberFinality) *types.BlockHeader); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.BlockHeader) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.BlockNumberFinality) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthClienter_CustomHeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CustomHeaderByNumber' +type EthClienter_CustomHeaderByNumber_Call struct { + *mock.Call +} + +// CustomHeaderByNumber is a helper method to define mock.On call +// - ctx context.Context +// - number *types.BlockNumberFinality +func (_e *EthClienter_Expecter) CustomHeaderByNumber(ctx interface{}, number interface{}) *EthClienter_CustomHeaderByNumber_Call { + return &EthClienter_CustomHeaderByNumber_Call{Call: _e.mock.On("CustomHeaderByNumber", ctx, number)} +} + +func (_c *EthClienter_CustomHeaderByNumber_Call) Run(run func(ctx context.Context, number *types.BlockNumberFinality)) *EthClienter_CustomHeaderByNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.BlockNumberFinality)) + }) + return _c +} + +func (_c *EthClienter_CustomHeaderByNumber_Call) Return(_a0 *types.BlockHeader, _a1 error) *EthClienter_CustomHeaderByNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthClienter_CustomHeaderByNumber_Call) RunAndReturn(run func(context.Context, *types.BlockNumberFinality) (*types.BlockHeader, error)) *EthClienter_CustomHeaderByNumber_Call { + _c.Call.Return(run) + return _c +} + // EstimateGas provides a mock function with given fields: ctx, call func (_m *EthClienter) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { ret := _m.Called(ctx, call) @@ -1123,65 +1125,6 @@ func (_c *EthClienter_SubscribeFilterLogs_Call) RunAndReturn(run func(context.Co return _c } -// SubscribeNewHead provides a mock function with given fields: ctx, ch -func (_m *EthClienter) SubscribeNewHead(ctx context.Context, ch chan<- *coretypes.Header) (ethereum.Subscription, error) { - ret := _m.Called(ctx, ch) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 ethereum.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *coretypes.Header) (ethereum.Subscription, error)); ok { - return rf(ctx, ch) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- *coretypes.Header) ethereum.Subscription); ok { - r0 = rf(ctx, ch) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(ethereum.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- *coretypes.Header) error); ok { - r1 = rf(ctx, ch) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EthClienter_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type EthClienter_SubscribeNewHead_Call struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - ch chan<- *coretypes.Header -func (_e *EthClienter_Expecter) SubscribeNewHead(ctx interface{}, ch interface{}) *EthClienter_SubscribeNewHead_Call { - return &EthClienter_SubscribeNewHead_Call{Call: _e.mock.On("SubscribeNewHead", ctx, ch)} -} - -func (_c *EthClienter_SubscribeNewHead_Call) Run(run func(ctx context.Context, ch chan<- *coretypes.Header)) *EthClienter_SubscribeNewHead_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- *coretypes.Header)) - }) - return _c -} - -func (_c *EthClienter_SubscribeNewHead_Call) Return(_a0 ethereum.Subscription, _a1 error) *EthClienter_SubscribeNewHead_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *EthClienter_SubscribeNewHead_Call) RunAndReturn(run func(context.Context, chan<- *coretypes.Header) (ethereum.Subscription, error)) *EthClienter_SubscribeNewHead_Call { - _c.Call.Return(run) - return _c -} - // SubscribeTransactionReceipts provides a mock function with given fields: ctx, q, ch func (_m *EthClienter) SubscribeTransactionReceipts(ctx context.Context, q *ethereum.TransactionReceiptsQuery, ch chan<- []*coretypes.Receipt) (ethereum.Subscription, error) { ret := _m.Called(ctx, q, ch) @@ -1424,123 +1367,6 @@ func (_c *EthClienter_TransactionByHash_Call) RunAndReturn(run func(context.Cont return _c } -// TransactionCount provides a mock function with given fields: ctx, blockHash -func (_m *EthClienter) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { - ret := _m.Called(ctx, blockHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionCount") - } - - var r0 uint - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (uint, error)); ok { - return rf(ctx, blockHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) uint); ok { - r0 = rf(ctx, blockHash) - } else { - r0 = ret.Get(0).(uint) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, blockHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EthClienter_TransactionCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionCount' -type EthClienter_TransactionCount_Call struct { - *mock.Call -} - -// TransactionCount is a helper method to define mock.On call -// - ctx context.Context -// - blockHash common.Hash -func (_e *EthClienter_Expecter) TransactionCount(ctx interface{}, blockHash interface{}) *EthClienter_TransactionCount_Call { - return &EthClienter_TransactionCount_Call{Call: _e.mock.On("TransactionCount", ctx, blockHash)} -} - -func (_c *EthClienter_TransactionCount_Call) Run(run func(ctx context.Context, blockHash common.Hash)) *EthClienter_TransactionCount_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *EthClienter_TransactionCount_Call) Return(_a0 uint, _a1 error) *EthClienter_TransactionCount_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *EthClienter_TransactionCount_Call) RunAndReturn(run func(context.Context, common.Hash) (uint, error)) *EthClienter_TransactionCount_Call { - _c.Call.Return(run) - return _c -} - -// TransactionInBlock provides a mock function with given fields: ctx, blockHash, index -func (_m *EthClienter) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*coretypes.Transaction, error) { - ret := _m.Called(ctx, blockHash, index) - - if len(ret) == 0 { - panic("no return value specified for TransactionInBlock") - } - - var r0 *coretypes.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) (*coretypes.Transaction, error)); ok { - return rf(ctx, blockHash, index) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) *coretypes.Transaction); ok { - r0 = rf(ctx, blockHash, index) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash, uint) error); ok { - r1 = rf(ctx, blockHash, index) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EthClienter_TransactionInBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionInBlock' -type EthClienter_TransactionInBlock_Call struct { - *mock.Call -} - -// TransactionInBlock is a helper method to define mock.On call -// - ctx context.Context -// - blockHash common.Hash -// - index uint -func (_e *EthClienter_Expecter) TransactionInBlock(ctx interface{}, blockHash interface{}, index interface{}) *EthClienter_TransactionInBlock_Call { - return &EthClienter_TransactionInBlock_Call{Call: _e.mock.On("TransactionInBlock", ctx, blockHash, index)} -} - -func (_c *EthClienter_TransactionInBlock_Call) Run(run func(ctx context.Context, blockHash common.Hash, index uint)) *EthClienter_TransactionInBlock_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash), args[2].(uint)) - }) - return _c -} - -func (_c *EthClienter_TransactionInBlock_Call) Return(_a0 *coretypes.Transaction, _a1 error) *EthClienter_TransactionInBlock_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *EthClienter_TransactionInBlock_Call) RunAndReturn(run func(context.Context, common.Hash, uint) (*coretypes.Transaction, error)) *EthClienter_TransactionInBlock_Call { - _c.Call.Return(run) - return _c -} - // TransactionReceipt provides a mock function with given fields: ctx, txHash func (_m *EthClienter) TransactionReceipt(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { ret := _m.Called(ctx, txHash) diff --git a/types/mocks/mock_ethereum_clienter.go b/types/mocks/mock_ethereum_clienter.go new file mode 100644 index 000000000..5491bc945 --- /dev/null +++ b/types/mocks/mock_ethereum_clienter.go @@ -0,0 +1,1214 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + context "context" + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + coretypes "github.com/ethereum/go-ethereum/core/types" + + ethereum "github.com/ethereum/go-ethereum" + + mock "github.com/stretchr/testify/mock" +) + +// EthereumClienter is an autogenerated mock type for the EthereumClienter type +type EthereumClienter struct { + mock.Mock +} + +type EthereumClienter_Expecter struct { + mock *mock.Mock +} + +func (_m *EthereumClienter) EXPECT() *EthereumClienter_Expecter { + return &EthereumClienter_Expecter{mock: &_m.Mock} +} + +// BalanceAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *EthereumClienter) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + ret := _m.Called(ctx, account, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for BalanceAt") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (*big.Int, error)); ok { + return rf(ctx, account, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) *big.Int); ok { + r0 = rf(ctx, account, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, account, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_BalanceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BalanceAt' +type EthereumClienter_BalanceAt_Call struct { + *mock.Call +} + +// BalanceAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +// - blockNumber *big.Int +func (_e *EthereumClienter_Expecter) BalanceAt(ctx interface{}, account interface{}, blockNumber interface{}) *EthereumClienter_BalanceAt_Call { + return &EthereumClienter_BalanceAt_Call{Call: _e.mock.On("BalanceAt", ctx, account, blockNumber)} +} + +func (_c *EthereumClienter_BalanceAt_Call) Run(run func(ctx context.Context, account common.Address, blockNumber *big.Int)) *EthereumClienter_BalanceAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) + }) + return _c +} + +func (_c *EthereumClienter_BalanceAt_Call) Return(_a0 *big.Int, _a1 error) *EthereumClienter_BalanceAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_BalanceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (*big.Int, error)) *EthereumClienter_BalanceAt_Call { + _c.Call.Return(run) + return _c +} + +// BlockNumber provides a mock function with given fields: ctx +func (_m *EthereumClienter) BlockNumber(ctx context.Context) (uint64, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for BlockNumber") + } + + 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 +} + +// EthereumClienter_BlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockNumber' +type EthereumClienter_BlockNumber_Call struct { + *mock.Call +} + +// BlockNumber is a helper method to define mock.On call +// - ctx context.Context +func (_e *EthereumClienter_Expecter) BlockNumber(ctx interface{}) *EthereumClienter_BlockNumber_Call { + return &EthereumClienter_BlockNumber_Call{Call: _e.mock.On("BlockNumber", ctx)} +} + +func (_c *EthereumClienter_BlockNumber_Call) Run(run func(ctx context.Context)) *EthereumClienter_BlockNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EthereumClienter_BlockNumber_Call) Return(_a0 uint64, _a1 error) *EthereumClienter_BlockNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_BlockNumber_Call) RunAndReturn(run func(context.Context) (uint64, error)) *EthereumClienter_BlockNumber_Call { + _c.Call.Return(run) + return _c +} + +// CallContract provides a mock function with given fields: ctx, call, blockNumber +func (_m *EthereumClienter) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, call, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for CallContract") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error)); ok { + return rf(ctx, call, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) []byte); ok { + r0 = rf(ctx, call, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg, *big.Int) error); ok { + r1 = rf(ctx, call, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_CallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContract' +type EthereumClienter_CallContract_Call struct { + *mock.Call +} + +// CallContract is a helper method to define mock.On call +// - ctx context.Context +// - call ethereum.CallMsg +// - blockNumber *big.Int +func (_e *EthereumClienter_Expecter) CallContract(ctx interface{}, call interface{}, blockNumber interface{}) *EthereumClienter_CallContract_Call { + return &EthereumClienter_CallContract_Call{Call: _e.mock.On("CallContract", ctx, call, blockNumber)} +} + +func (_c *EthereumClienter_CallContract_Call) Run(run func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int)) *EthereumClienter_CallContract_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.CallMsg), args[2].(*big.Int)) + }) + return _c +} + +func (_c *EthereumClienter_CallContract_Call) Return(_a0 []byte, _a1 error) *EthereumClienter_CallContract_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_CallContract_Call) RunAndReturn(run func(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error)) *EthereumClienter_CallContract_Call { + _c.Call.Return(run) + return _c +} + +// ChainID provides a mock function with given fields: ctx +func (_m *EthereumClienter) ChainID(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + + 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 +} + +// EthereumClienter_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' +type EthereumClienter_ChainID_Call struct { + *mock.Call +} + +// ChainID is a helper method to define mock.On call +// - ctx context.Context +func (_e *EthereumClienter_Expecter) ChainID(ctx interface{}) *EthereumClienter_ChainID_Call { + return &EthereumClienter_ChainID_Call{Call: _e.mock.On("ChainID", ctx)} +} + +func (_c *EthereumClienter_ChainID_Call) Run(run func(ctx context.Context)) *EthereumClienter_ChainID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EthereumClienter_ChainID_Call) Return(_a0 *big.Int, _a1 error) *EthereumClienter_ChainID_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_ChainID_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *EthereumClienter_ChainID_Call { + _c.Call.Return(run) + return _c +} + +// CodeAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *EthereumClienter) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, account, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for CodeAt") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]byte, error)); ok { + return rf(ctx, account, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { + r0 = rf(ctx, account, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, account, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_CodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CodeAt' +type EthereumClienter_CodeAt_Call struct { + *mock.Call +} + +// CodeAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +// - blockNumber *big.Int +func (_e *EthereumClienter_Expecter) CodeAt(ctx interface{}, account interface{}, blockNumber interface{}) *EthereumClienter_CodeAt_Call { + return &EthereumClienter_CodeAt_Call{Call: _e.mock.On("CodeAt", ctx, account, blockNumber)} +} + +func (_c *EthereumClienter_CodeAt_Call) Run(run func(ctx context.Context, account common.Address, blockNumber *big.Int)) *EthereumClienter_CodeAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) + }) + return _c +} + +func (_c *EthereumClienter_CodeAt_Call) Return(_a0 []byte, _a1 error) *EthereumClienter_CodeAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_CodeAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) ([]byte, error)) *EthereumClienter_CodeAt_Call { + _c.Call.Return(run) + return _c +} + +// EstimateGas provides a mock function with given fields: ctx, call +func (_m *EthereumClienter) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { + ret := _m.Called(ctx, call) + + 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, ethereum.CallMsg) (uint64, error)); ok { + return rf(ctx, call) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg) uint64); ok { + r0 = rf(ctx, call) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg) error); ok { + r1 = rf(ctx, call) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_EstimateGas_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGas' +type EthereumClienter_EstimateGas_Call struct { + *mock.Call +} + +// EstimateGas is a helper method to define mock.On call +// - ctx context.Context +// - call ethereum.CallMsg +func (_e *EthereumClienter_Expecter) EstimateGas(ctx interface{}, call interface{}) *EthereumClienter_EstimateGas_Call { + return &EthereumClienter_EstimateGas_Call{Call: _e.mock.On("EstimateGas", ctx, call)} +} + +func (_c *EthereumClienter_EstimateGas_Call) Run(run func(ctx context.Context, call ethereum.CallMsg)) *EthereumClienter_EstimateGas_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.CallMsg)) + }) + return _c +} + +func (_c *EthereumClienter_EstimateGas_Call) Return(_a0 uint64, _a1 error) *EthereumClienter_EstimateGas_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_EstimateGas_Call) RunAndReturn(run func(context.Context, ethereum.CallMsg) (uint64, error)) *EthereumClienter_EstimateGas_Call { + _c.Call.Return(run) + return _c +} + +// FilterLogs provides a mock function with given fields: ctx, q +func (_m *EthereumClienter) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]coretypes.Log, error) { + ret := _m.Called(ctx, q) + + if len(ret) == 0 { + panic("no return value specified for FilterLogs") + } + + var r0 []coretypes.Log + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)); ok { + return rf(ctx, q) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []coretypes.Log); ok { + r0 = rf(ctx, q) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]coretypes.Log) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery) error); ok { + r1 = rf(ctx, q) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_FilterLogs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterLogs' +type EthereumClienter_FilterLogs_Call struct { + *mock.Call +} + +// FilterLogs is a helper method to define mock.On call +// - ctx context.Context +// - q ethereum.FilterQuery +func (_e *EthereumClienter_Expecter) FilterLogs(ctx interface{}, q interface{}) *EthereumClienter_FilterLogs_Call { + return &EthereumClienter_FilterLogs_Call{Call: _e.mock.On("FilterLogs", ctx, q)} +} + +func (_c *EthereumClienter_FilterLogs_Call) Run(run func(ctx context.Context, q ethereum.FilterQuery)) *EthereumClienter_FilterLogs_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.FilterQuery)) + }) + return _c +} + +func (_c *EthereumClienter_FilterLogs_Call) Return(_a0 []coretypes.Log, _a1 error) *EthereumClienter_FilterLogs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_FilterLogs_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)) *EthereumClienter_FilterLogs_Call { + _c.Call.Return(run) + return _c +} + +// HeaderByHash provides a mock function with given fields: ctx, hash +func (_m *EthereumClienter) HeaderByHash(ctx context.Context, hash common.Hash) (*coretypes.Header, error) { + ret := _m.Called(ctx, hash) + + if len(ret) == 0 { + panic("no return value specified for HeaderByHash") + } + + var r0 *coretypes.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Header, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Header); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_HeaderByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByHash' +type EthereumClienter_HeaderByHash_Call struct { + *mock.Call +} + +// HeaderByHash is a helper method to define mock.On call +// - ctx context.Context +// - hash common.Hash +func (_e *EthereumClienter_Expecter) HeaderByHash(ctx interface{}, hash interface{}) *EthereumClienter_HeaderByHash_Call { + return &EthereumClienter_HeaderByHash_Call{Call: _e.mock.On("HeaderByHash", ctx, hash)} +} + +func (_c *EthereumClienter_HeaderByHash_Call) Run(run func(ctx context.Context, hash common.Hash)) *EthereumClienter_HeaderByHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *EthereumClienter_HeaderByHash_Call) Return(_a0 *coretypes.Header, _a1 error) *EthereumClienter_HeaderByHash_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_HeaderByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Header, error)) *EthereumClienter_HeaderByHash_Call { + _c.Call.Return(run) + return _c +} + +// HeaderByNumber provides a mock function with given fields: ctx, number +func (_m *EthereumClienter) HeaderByNumber(ctx context.Context, number *big.Int) (*coretypes.Header, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + + var r0 *coretypes.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Header, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Header); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.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 +} + +// EthereumClienter_HeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByNumber' +type EthereumClienter_HeaderByNumber_Call struct { + *mock.Call +} + +// HeaderByNumber is a helper method to define mock.On call +// - ctx context.Context +// - number *big.Int +func (_e *EthereumClienter_Expecter) HeaderByNumber(ctx interface{}, number interface{}) *EthereumClienter_HeaderByNumber_Call { + return &EthereumClienter_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, number)} +} + +func (_c *EthereumClienter_HeaderByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *EthereumClienter_HeaderByNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*big.Int)) + }) + return _c +} + +func (_c *EthereumClienter_HeaderByNumber_Call) Return(_a0 *coretypes.Header, _a1 error) *EthereumClienter_HeaderByNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Header, error)) *EthereumClienter_HeaderByNumber_Call { + _c.Call.Return(run) + return _c +} + +// NonceAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *EthereumClienter) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + ret := _m.Called(ctx, account, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for NonceAt") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (uint64, error)); ok { + return rf(ctx, account, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) uint64); ok { + r0 = rf(ctx, account, blockNumber) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, account, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_NonceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NonceAt' +type EthereumClienter_NonceAt_Call struct { + *mock.Call +} + +// NonceAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +// - blockNumber *big.Int +func (_e *EthereumClienter_Expecter) NonceAt(ctx interface{}, account interface{}, blockNumber interface{}) *EthereumClienter_NonceAt_Call { + return &EthereumClienter_NonceAt_Call{Call: _e.mock.On("NonceAt", ctx, account, blockNumber)} +} + +func (_c *EthereumClienter_NonceAt_Call) Run(run func(ctx context.Context, account common.Address, blockNumber *big.Int)) *EthereumClienter_NonceAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) + }) + return _c +} + +func (_c *EthereumClienter_NonceAt_Call) Return(_a0 uint64, _a1 error) *EthereumClienter_NonceAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_NonceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (uint64, error)) *EthereumClienter_NonceAt_Call { + _c.Call.Return(run) + return _c +} + +// PendingCodeAt provides a mock function with given fields: ctx, account +func (_m *EthereumClienter) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + ret := _m.Called(ctx, account) + + if len(ret) == 0 { + panic("no return value specified for PendingCodeAt") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) ([]byte, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) []byte); ok { + r0 = rf(ctx, account) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + 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 +} + +// EthereumClienter_PendingCodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCodeAt' +type EthereumClienter_PendingCodeAt_Call struct { + *mock.Call +} + +// PendingCodeAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +func (_e *EthereumClienter_Expecter) PendingCodeAt(ctx interface{}, account interface{}) *EthereumClienter_PendingCodeAt_Call { + return &EthereumClienter_PendingCodeAt_Call{Call: _e.mock.On("PendingCodeAt", ctx, account)} +} + +func (_c *EthereumClienter_PendingCodeAt_Call) Run(run func(ctx context.Context, account common.Address)) *EthereumClienter_PendingCodeAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address)) + }) + return _c +} + +func (_c *EthereumClienter_PendingCodeAt_Call) Return(_a0 []byte, _a1 error) *EthereumClienter_PendingCodeAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_PendingCodeAt_Call) RunAndReturn(run func(context.Context, common.Address) ([]byte, error)) *EthereumClienter_PendingCodeAt_Call { + _c.Call.Return(run) + return _c +} + +// PendingNonceAt provides a mock function with given fields: ctx, account +func (_m *EthereumClienter) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + ret := _m.Called(ctx, account) + + if len(ret) == 0 { + panic("no return value specified for PendingNonceAt") + } + + 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 +} + +// EthereumClienter_PendingNonceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingNonceAt' +type EthereumClienter_PendingNonceAt_Call struct { + *mock.Call +} + +// PendingNonceAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +func (_e *EthereumClienter_Expecter) PendingNonceAt(ctx interface{}, account interface{}) *EthereumClienter_PendingNonceAt_Call { + return &EthereumClienter_PendingNonceAt_Call{Call: _e.mock.On("PendingNonceAt", ctx, account)} +} + +func (_c *EthereumClienter_PendingNonceAt_Call) Run(run func(ctx context.Context, account common.Address)) *EthereumClienter_PendingNonceAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address)) + }) + return _c +} + +func (_c *EthereumClienter_PendingNonceAt_Call) Return(_a0 uint64, _a1 error) *EthereumClienter_PendingNonceAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_PendingNonceAt_Call) RunAndReturn(run func(context.Context, common.Address) (uint64, error)) *EthereumClienter_PendingNonceAt_Call { + _c.Call.Return(run) + return _c +} + +// SendTransaction provides a mock function with given fields: ctx, tx +func (_m *EthereumClienter) SendTransaction(ctx context.Context, tx *coretypes.Transaction) error { + ret := _m.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for SendTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EthereumClienter_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' +type EthereumClienter_SendTransaction_Call struct { + *mock.Call +} + +// SendTransaction is a helper method to define mock.On call +// - ctx context.Context +// - tx *coretypes.Transaction +func (_e *EthereumClienter_Expecter) SendTransaction(ctx interface{}, tx interface{}) *EthereumClienter_SendTransaction_Call { + return &EthereumClienter_SendTransaction_Call{Call: _e.mock.On("SendTransaction", ctx, tx)} +} + +func (_c *EthereumClienter_SendTransaction_Call) Run(run func(ctx context.Context, tx *coretypes.Transaction)) *EthereumClienter_SendTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*coretypes.Transaction)) + }) + return _c +} + +func (_c *EthereumClienter_SendTransaction_Call) Return(_a0 error) *EthereumClienter_SendTransaction_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EthereumClienter_SendTransaction_Call) RunAndReturn(run func(context.Context, *coretypes.Transaction) error) *EthereumClienter_SendTransaction_Call { + _c.Call.Return(run) + return _c +} + +// StorageAt provides a mock function with given fields: ctx, account, key, blockNumber +func (_m *EthereumClienter) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, account, key, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for StorageAt") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Hash, *big.Int) ([]byte, error)); ok { + return rf(ctx, account, key, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Hash, *big.Int) []byte); ok { + r0 = rf(ctx, account, key, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Hash, *big.Int) error); ok { + r1 = rf(ctx, account, key, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_StorageAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StorageAt' +type EthereumClienter_StorageAt_Call struct { + *mock.Call +} + +// StorageAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +// - key common.Hash +// - blockNumber *big.Int +func (_e *EthereumClienter_Expecter) StorageAt(ctx interface{}, account interface{}, key interface{}, blockNumber interface{}) *EthereumClienter_StorageAt_Call { + return &EthereumClienter_StorageAt_Call{Call: _e.mock.On("StorageAt", ctx, account, key, blockNumber)} +} + +func (_c *EthereumClienter_StorageAt_Call) Run(run func(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int)) *EthereumClienter_StorageAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(common.Hash), args[3].(*big.Int)) + }) + return _c +} + +func (_c *EthereumClienter_StorageAt_Call) Return(_a0 []byte, _a1 error) *EthereumClienter_StorageAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_StorageAt_Call) RunAndReturn(run func(context.Context, common.Address, common.Hash, *big.Int) ([]byte, error)) *EthereumClienter_StorageAt_Call { + _c.Call.Return(run) + return _c +} + +// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch +func (_m *EthereumClienter) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { + ret := _m.Called(ctx, q, ch) + + if len(ret) == 0 { + panic("no return value specified for SubscribeFilterLogs") + } + + var r0 ethereum.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { + r0 = rf(ctx, q, ch) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ethereum.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { + r1 = rf(ctx, q, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_SubscribeFilterLogs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeFilterLogs' +type EthereumClienter_SubscribeFilterLogs_Call struct { + *mock.Call +} + +// SubscribeFilterLogs is a helper method to define mock.On call +// - ctx context.Context +// - q ethereum.FilterQuery +// - ch chan<- coretypes.Log +func (_e *EthereumClienter_Expecter) SubscribeFilterLogs(ctx interface{}, q interface{}, ch interface{}) *EthereumClienter_SubscribeFilterLogs_Call { + return &EthereumClienter_SubscribeFilterLogs_Call{Call: _e.mock.On("SubscribeFilterLogs", ctx, q, ch)} +} + +func (_c *EthereumClienter_SubscribeFilterLogs_Call) Run(run func(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log)) *EthereumClienter_SubscribeFilterLogs_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.FilterQuery), args[2].(chan<- coretypes.Log)) + }) + return _c +} + +func (_c *EthereumClienter_SubscribeFilterLogs_Call) Return(_a0 ethereum.Subscription, _a1 error) *EthereumClienter_SubscribeFilterLogs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_SubscribeFilterLogs_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)) *EthereumClienter_SubscribeFilterLogs_Call { + _c.Call.Return(run) + return _c +} + +// SubscribeTransactionReceipts provides a mock function with given fields: ctx, q, ch +func (_m *EthereumClienter) SubscribeTransactionReceipts(ctx context.Context, q *ethereum.TransactionReceiptsQuery, ch chan<- []*coretypes.Receipt) (ethereum.Subscription, error) { + ret := _m.Called(ctx, q, ch) + + if len(ret) == 0 { + panic("no return value specified for SubscribeTransactionReceipts") + } + + var r0 ethereum.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *ethereum.TransactionReceiptsQuery, chan<- []*coretypes.Receipt) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, *ethereum.TransactionReceiptsQuery, chan<- []*coretypes.Receipt) ethereum.Subscription); ok { + r0 = rf(ctx, q, ch) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ethereum.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *ethereum.TransactionReceiptsQuery, chan<- []*coretypes.Receipt) error); ok { + r1 = rf(ctx, q, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EthereumClienter_SubscribeTransactionReceipts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeTransactionReceipts' +type EthereumClienter_SubscribeTransactionReceipts_Call struct { + *mock.Call +} + +// SubscribeTransactionReceipts is a helper method to define mock.On call +// - ctx context.Context +// - q *ethereum.TransactionReceiptsQuery +// - ch chan<- []*coretypes.Receipt +func (_e *EthereumClienter_Expecter) SubscribeTransactionReceipts(ctx interface{}, q interface{}, ch interface{}) *EthereumClienter_SubscribeTransactionReceipts_Call { + return &EthereumClienter_SubscribeTransactionReceipts_Call{Call: _e.mock.On("SubscribeTransactionReceipts", ctx, q, ch)} +} + +func (_c *EthereumClienter_SubscribeTransactionReceipts_Call) Run(run func(ctx context.Context, q *ethereum.TransactionReceiptsQuery, ch chan<- []*coretypes.Receipt)) *EthereumClienter_SubscribeTransactionReceipts_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*ethereum.TransactionReceiptsQuery), args[2].(chan<- []*coretypes.Receipt)) + }) + return _c +} + +func (_c *EthereumClienter_SubscribeTransactionReceipts_Call) Return(_a0 ethereum.Subscription, _a1 error) *EthereumClienter_SubscribeTransactionReceipts_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_SubscribeTransactionReceipts_Call) RunAndReturn(run func(context.Context, *ethereum.TransactionReceiptsQuery, chan<- []*coretypes.Receipt) (ethereum.Subscription, error)) *EthereumClienter_SubscribeTransactionReceipts_Call { + _c.Call.Return(run) + return _c +} + +// SuggestGasPrice provides a mock function with given fields: ctx +func (_m *EthereumClienter) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SuggestGasPrice") + } + + 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 +} + +// EthereumClienter_SuggestGasPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasPrice' +type EthereumClienter_SuggestGasPrice_Call struct { + *mock.Call +} + +// SuggestGasPrice is a helper method to define mock.On call +// - ctx context.Context +func (_e *EthereumClienter_Expecter) SuggestGasPrice(ctx interface{}) *EthereumClienter_SuggestGasPrice_Call { + return &EthereumClienter_SuggestGasPrice_Call{Call: _e.mock.On("SuggestGasPrice", ctx)} +} + +func (_c *EthereumClienter_SuggestGasPrice_Call) Run(run func(ctx context.Context)) *EthereumClienter_SuggestGasPrice_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EthereumClienter_SuggestGasPrice_Call) Return(_a0 *big.Int, _a1 error) *EthereumClienter_SuggestGasPrice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_SuggestGasPrice_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *EthereumClienter_SuggestGasPrice_Call { + _c.Call.Return(run) + return _c +} + +// SuggestGasTipCap provides a mock function with given fields: ctx +func (_m *EthereumClienter) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SuggestGasTipCap") + } + + 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 +} + +// EthereumClienter_SuggestGasTipCap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasTipCap' +type EthereumClienter_SuggestGasTipCap_Call struct { + *mock.Call +} + +// SuggestGasTipCap is a helper method to define mock.On call +// - ctx context.Context +func (_e *EthereumClienter_Expecter) SuggestGasTipCap(ctx interface{}) *EthereumClienter_SuggestGasTipCap_Call { + return &EthereumClienter_SuggestGasTipCap_Call{Call: _e.mock.On("SuggestGasTipCap", ctx)} +} + +func (_c *EthereumClienter_SuggestGasTipCap_Call) Run(run func(ctx context.Context)) *EthereumClienter_SuggestGasTipCap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EthereumClienter_SuggestGasTipCap_Call) Return(_a0 *big.Int, _a1 error) *EthereumClienter_SuggestGasTipCap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_SuggestGasTipCap_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *EthereumClienter_SuggestGasTipCap_Call { + _c.Call.Return(run) + return _c +} + +// TransactionByHash provides a mock function with given fields: ctx, txHash +func (_m *EthereumClienter) TransactionByHash(ctx context.Context, txHash common.Hash) (*coretypes.Transaction, bool, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionByHash") + } + + var r0 *coretypes.Transaction + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Transaction, bool, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Transaction); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.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 +} + +// EthereumClienter_TransactionByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionByHash' +type EthereumClienter_TransactionByHash_Call struct { + *mock.Call +} + +// TransactionByHash is a helper method to define mock.On call +// - ctx context.Context +// - txHash common.Hash +func (_e *EthereumClienter_Expecter) TransactionByHash(ctx interface{}, txHash interface{}) *EthereumClienter_TransactionByHash_Call { + return &EthereumClienter_TransactionByHash_Call{Call: _e.mock.On("TransactionByHash", ctx, txHash)} +} + +func (_c *EthereumClienter_TransactionByHash_Call) Run(run func(ctx context.Context, txHash common.Hash)) *EthereumClienter_TransactionByHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *EthereumClienter_TransactionByHash_Call) Return(tx *coretypes.Transaction, isPending bool, err error) *EthereumClienter_TransactionByHash_Call { + _c.Call.Return(tx, isPending, err) + return _c +} + +func (_c *EthereumClienter_TransactionByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Transaction, bool, error)) *EthereumClienter_TransactionByHash_Call { + _c.Call.Return(run) + return _c +} + +// TransactionReceipt provides a mock function with given fields: ctx, txHash +func (_m *EthereumClienter) TransactionReceipt(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionReceipt") + } + + var r0 *coretypes.Receipt + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Receipt, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Receipt); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.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 +} + +// EthereumClienter_TransactionReceipt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceipt' +type EthereumClienter_TransactionReceipt_Call struct { + *mock.Call +} + +// TransactionReceipt is a helper method to define mock.On call +// - ctx context.Context +// - txHash common.Hash +func (_e *EthereumClienter_Expecter) TransactionReceipt(ctx interface{}, txHash interface{}) *EthereumClienter_TransactionReceipt_Call { + return &EthereumClienter_TransactionReceipt_Call{Call: _e.mock.On("TransactionReceipt", ctx, txHash)} +} + +func (_c *EthereumClienter_TransactionReceipt_Call) Run(run func(ctx context.Context, txHash common.Hash)) *EthereumClienter_TransactionReceipt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *EthereumClienter_TransactionReceipt_Call) Return(_a0 *coretypes.Receipt, _a1 error) *EthereumClienter_TransactionReceipt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *EthereumClienter_TransactionReceipt_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Receipt, error)) *EthereumClienter_TransactionReceipt_Call { + _c.Call.Return(run) + return _c +} + +// NewEthereumClienter creates a new instance of EthereumClienter. 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 NewEthereumClienter(t interface { + mock.TestingT + Cleanup(func()) +}) *EthereumClienter { + mock := &EthereumClienter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/types/mocks/mock_multi_downloader.go b/types/mocks/mock_multi_downloader.go index 2cfc6e6c9..91dbca44e 100644 --- a/types/mocks/mock_multi_downloader.go +++ b/types/mocks/mock_multi_downloader.go @@ -4,11 +4,9 @@ package mocks import ( context "context" - big "math/big" - - coretypes "github.com/ethereum/go-ethereum/core/types" ethereum "github.com/ethereum/go-ethereum" + coretypes "github.com/ethereum/go-ethereum/core/types" mock "github.com/stretchr/testify/mock" @@ -307,7 +305,7 @@ func (_c *MultiDownloader_FilterLogs_Call) RunAndReturn(run func(context.Context } // HeaderByNumber provides a mock function with given fields: ctx, number -func (_m *MultiDownloader) HeaderByNumber(ctx context.Context, number *big.Int) (*types.BlockHeader, error) { +func (_m *MultiDownloader) HeaderByNumber(ctx context.Context, number *types.BlockNumberFinality) (*types.BlockHeader, error) { ret := _m.Called(ctx, number) if len(ret) == 0 { @@ -316,10 +314,10 @@ func (_m *MultiDownloader) HeaderByNumber(ctx context.Context, number *big.Int) var r0 *types.BlockHeader var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.BlockHeader, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.BlockNumberFinality) (*types.BlockHeader, error)); ok { return rf(ctx, number) } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.BlockHeader); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.BlockNumberFinality) *types.BlockHeader); ok { r0 = rf(ctx, number) } else { if ret.Get(0) != nil { @@ -327,7 +325,7 @@ func (_m *MultiDownloader) HeaderByNumber(ctx context.Context, number *big.Int) } } - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.BlockNumberFinality) error); ok { r1 = rf(ctx, number) } else { r1 = ret.Error(1) @@ -343,14 +341,14 @@ type MultiDownloader_HeaderByNumber_Call struct { // HeaderByNumber is a helper method to define mock.On call // - ctx context.Context -// - number *big.Int +// - number *types.BlockNumberFinality func (_e *MultiDownloader_Expecter) HeaderByNumber(ctx interface{}, number interface{}) *MultiDownloader_HeaderByNumber_Call { return &MultiDownloader_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, number)} } -func (_c *MultiDownloader_HeaderByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *MultiDownloader_HeaderByNumber_Call { +func (_c *MultiDownloader_HeaderByNumber_Call) Run(run func(ctx context.Context, number *types.BlockNumberFinality)) *MultiDownloader_HeaderByNumber_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) + run(args[0].(context.Context), args[1].(*types.BlockNumberFinality)) }) return _c } @@ -360,7 +358,7 @@ func (_c *MultiDownloader_HeaderByNumber_Call) Return(_a0 *types.BlockHeader, _a return _c } -func (_c *MultiDownloader_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*types.BlockHeader, error)) *MultiDownloader_HeaderByNumber_Call { +func (_c *MultiDownloader_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *types.BlockNumberFinality) (*types.BlockHeader, error)) *MultiDownloader_HeaderByNumber_Call { _c.Call.Return(run) return _c } diff --git a/types/mocks/mock_rpc_clienter.go b/types/mocks/mock_rpc_clienter.go index 44f836377..2d2c63566 100644 --- a/types/mocks/mock_rpc_clienter.go +++ b/types/mocks/mock_rpc_clienter.go @@ -127,6 +127,65 @@ func (_c *RPCClienter_Call_Call) RunAndReturn(run func(any, string, ...any) erro return _c } +// CallContext provides a mock function with given fields: ctx, result, method, args +func (_m *RPCClienter) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + var _ca []interface{} + _ca = append(_ca, ctx, result, method) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CallContext") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { + r0 = rf(ctx, result, method, args...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RPCClienter_CallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContext' +type RPCClienter_CallContext_Call struct { + *mock.Call +} + +// CallContext is a helper method to define mock.On call +// - ctx context.Context +// - result interface{} +// - method string +// - args ...interface{} +func (_e *RPCClienter_Expecter) CallContext(ctx interface{}, result interface{}, method interface{}, args ...interface{}) *RPCClienter_CallContext_Call { + return &RPCClienter_CallContext_Call{Call: _e.mock.On("CallContext", + append([]interface{}{ctx, result, method}, args...)...)} +} + +func (_c *RPCClienter_CallContext_Call) Run(run func(ctx context.Context, result interface{}, method string, args ...interface{})) *RPCClienter_CallContext_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-3) + for i, a := range args[3:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(context.Context), args[1].(interface{}), args[2].(string), variadicArgs...) + }) + return _c +} + +func (_c *RPCClienter_CallContext_Call) Return(_a0 error) *RPCClienter_CallContext_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *RPCClienter_CallContext_Call) RunAndReturn(run func(context.Context, interface{}, string, ...interface{}) error) *RPCClienter_CallContext_Call { + _c.Call.Return(run) + return _c +} + // NewRPCClienter creates a new instance of RPCClienter. 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 NewRPCClienter(t interface { diff --git a/types/multidownloader.go b/types/multidownloader.go index 7ef7faec3..4c4dc279c 100644 --- a/types/multidownloader.go +++ b/types/multidownloader.go @@ -2,7 +2,6 @@ package types import ( "context" - "math/big" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -26,7 +25,7 @@ type MultiDownloader interface { // TODO: delete this method because it's only required for a intermediate fix of old RerogDetector BlockHeader(ctx context.Context, finality BlockNumberFinality) (*BlockHeader, error) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]ethtypes.Log, error) - HeaderByNumber(ctx context.Context, number *big.Int) (*BlockHeader, error) + HeaderByNumber(ctx context.Context, number *BlockNumberFinality) (*BlockHeader, error) EthClient() BaseEthereumClienter RegisterSyncer(data SyncerConfig) error Start(ctx context.Context) error