From 0b7b478a5c7a52b8c1e5d38549c5a83c56223e49 Mon Sep 17 00:00:00 2001 From: Joan Esteban <129153821+joanestebanr@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:03:56 +0200 Subject: [PATCH 1/3] feat: add documentation for local debug (#26) * add documentation for local debug * Update test/scripts/config_kurtosis_for_local_run.sh to be more robust and check tools and state --- README.md | 2 ++ docs/local_debug.md | 31 ++++++++++++++++ test/scripts/config_kurtosis_for_local_run.sh | 35 +++++++++++++++---- 3 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 docs/local_debug.md diff --git a/README.md b/README.md index 0dac8a73..9d0427d5 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ The following actions are required once you create a new repository from this re ### Local Development +- You can run locally against kurtosis-cdk environment using: [docs/local_debug.md](docs/local_debug.md) + ### Database ### Installation diff --git a/docs/local_debug.md b/docs/local_debug.md new file mode 100644 index 00000000..1bcd3b7a --- /dev/null +++ b/docs/local_debug.md @@ -0,0 +1,31 @@ +# Setup environment to local debug on VSCode + +## Clone kurtosis + +Set KURTOSIS_FOLDER to the folder where do you want to clone `kurtosis_cdk` + +```bash +KURTOSIS_FOLDER="/tmp/kurtosis/" ./test/scripts/clone_kurtosis.sh develop $KURTOSIS_FOLDER +``` + +## Run kurtosis + +Set KURTOSIS_FOLDER to the folder where do you clone `kurtosis_cdk` + +```bash +KURTOSIS_FOLDER="/tmp/kurtosis/" kurtosis run --enclave cdk-v1 --args-file $KURTOSIS_FOLDER/cdk-erigon-sequencer-params.yml --image-download always $KURTOSIS_FOLDER +``` + +## Create configuration for this kurtosis environment + +`./test/scripts/config_kurtosis_for_local_run.sh` + +## Stop sequence-sender started by Kurtosis + +```bash +kurtosis service stop cdk-v1 zkevm-node-sequence-sender-001 +``` + +## Add to vscode launch.json + +After execution `config_kurtosis_for_local_run.sh` it suggest a entry for `launch.json` configurations diff --git a/test/scripts/config_kurtosis_for_local_run.sh b/test/scripts/config_kurtosis_for_local_run.sh index a96146f3..e8c6b737 100755 --- a/test/scripts/config_kurtosis_for_local_run.sh +++ b/test/scripts/config_kurtosis_for_local_run.sh @@ -10,18 +10,40 @@ source $(dirname $0)/env.sh ############################################################################### set -o pipefail # enable strict command pipe error detection +which kurtosis > /dev/null +if [ $? -ne 0 ]; then + echo "kurtosis is not installed. Please install it:" + cat << EOF + echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list + echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list + sudo apt install kurtosis-cli=0.90.1 + kurtosis version +EOF + exit 1 + +fi + if [ -z $TMP_CDK_FOLDER -o -z $ENCLAVE ]; then echo "TMP_CDK_FOLDER or ENCLAVE is not set. Must be set on file env.sh" exit 1 fi +kurtosis enclave inspect $ENCLAVE > /dev/null +if [ $? -ne 0 ]; then + echo "Error inspecting enclave $ENCLAVE" + echo "You must start kurtosis environment before running this script" + echo "- start kurtosis:" + echo " kurtosis clean --all; kurtosis run --enclave $ENCLAVE --args-file cdk-erigon-sequencer-params.yml --image-download always ." + + exit 1 +fi DEST=${TMP_CDK_FOLDER}/local_config [ ! -d ${DEST} ] && mkdir -p ${DEST} rm $DEST/* -kurtosis files download cdk-v1 genesis $DEST +kurtosis files download $ENCLAVE genesis $DEST [ $? -ne 0 ] && echo "Error downloading genesis" && exit 1 export genesis_file=$DEST/genesis.json -kurtosis files download cdk-v1 sequencer-keystore $DEST +kurtosis files download $ENCLAVE sequencer-keystore $DEST [ $? -ne 0 ] && echo "Error downloading sequencer-keystore" && exit 1 export sequencer_keystore_file=$DEST/sequencer.keystore @@ -38,16 +60,17 @@ export l1_chain_id=$(cat $DEST/config.toml |grep L1ChainID | cut -f 2 -d '=' export zkevm_is_validium=$(cat $DEST/config.toml |grep IsValidiumMode | cut -f 2 -d '=') if [ "$zkevm_is_validium" == "true" ]; then - dac_port=$(kurtosis port print $ENCLAVE zkevm-dac http-rpc | cut -f 3 -d ":") + echo "Validium mode detected... Retrieving the dac_port" + dac_port=$(kurtosis port print $ENCLAVE zkevm-dac-001 dac | cut -f 3 -d ":") [ $? -ne 0 ] && echo "Error getting dac_port" && exit 1 || export dac_port && echo "dac_port=$dac_port" fi envsubst < test/config/test.kurtosis_template.toml > $DEST/test.kurtosis.toml -echo "- start kurtosis" +echo "- to restart kurtosis:" echo " kurtosis clean --all; kurtosis run --enclave cdk-v1 --args-file cdk-erigon-sequencer-params.yml --image-download always ." echo " " -echo "- Stop sequence-sender" +echo "- Stop sequence-sender:" echo " kurtosis service stop cdk-v1 zkevm-node-sequence-sender-001" echo " " echo "- Add next configuration to vscode launch.json" @@ -63,4 +86,4 @@ cat << EOF "--custom-network-file", "$DEST/local_config/genesis.json" ] }, - +EOF From 880bccbe1db1c16d88c52326bcc0e5bf75b4dc79 Mon Sep 17 00:00:00 2001 From: Arnau Bennassar Date: Thu, 1 Aug 2024 11:37:30 +0200 Subject: [PATCH 2/3] Feature/agg oracle (#23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * implementation completed, missing tests * WIP * WIP * WIP * sync refactor * decouple sync processors from EVM * Add CLI for aggOracle * WIP * wip * WIP * start reorg detector * fix docker * pass test with docker * add TODO * go mod tidy * Add PR review suggestions from Stefan-Ethernal * fix UTs * Add PR review suggestions from joanestebanr --------- Co-authored-by: Stefan Negovanović --- Dockerfile | 7 +- Makefile | 2 +- aggoracle/chaingersender/evm.go | 117 ++++ aggoracle/config.go | 25 + aggoracle/e2e_test.go | 214 +++++++ aggoracle/mock_ethtxmanager_test.go | 127 +++++ aggoracle/oracle.go | 110 ++++ cmd/main.go | 4 +- cmd/run.go | 104 ++++ common/common.go | 26 +- config/config.go | 9 + config/default.go | 45 ++ go.mod | 2 +- go.sum | 4 +- l1infotreesync/downloader.go | 49 ++ l1infotreesync/e2e_test.go | 82 +++ l1infotreesync/l1infotreesync.go | 124 +++++ l1infotreesync/mock_reorgdetector_test.go | 73 +++ l1infotreesync/processor.go | 523 ++++++++++++++++++ l1infotreesync/processor_test.go | 3 + localbridgesync/downloader.go | 201 +------ localbridgesync/driver.go | 140 ----- localbridgesync/e2e_test.go | 3 + localbridgesync/localbridgesync.go | 67 ++- localbridgesync/processor.go | 59 +- localbridgesync/processor_test.go | 169 +++--- localbridgesync/types.go | 42 -- reorgdetector/mock_eth_client.go | 19 +- reorgdetector/reorgdetector.go | 4 + sync/common.go | 21 + sync/driver.go | 14 + sync/evmdownloader.go | 208 +++++++ .../evmdownloader_test.go | 311 ++++------- sync/evmdriver.go | 151 +++++ .../driver_test.go => sync/evmdriver_test.go | 101 ++-- sync/evmtypes.go | 15 + .../mock_downloader_test.go | 41 +- {localbridgesync => sync}/mock_l2_test.go | 2 +- .../mock_processor_test.go | 26 +- .../mock_reorgdetector_test.go | 16 +- test/Makefile | 25 +- 41 files changed, 2503 insertions(+), 782 deletions(-) create mode 100644 aggoracle/chaingersender/evm.go create mode 100644 aggoracle/config.go create mode 100644 aggoracle/e2e_test.go create mode 100644 aggoracle/mock_ethtxmanager_test.go create mode 100644 aggoracle/oracle.go create mode 100644 l1infotreesync/downloader.go create mode 100644 l1infotreesync/e2e_test.go create mode 100644 l1infotreesync/l1infotreesync.go create mode 100644 l1infotreesync/mock_reorgdetector_test.go create mode 100644 l1infotreesync/processor.go create mode 100644 l1infotreesync/processor_test.go delete mode 100644 localbridgesync/driver.go create mode 100644 localbridgesync/e2e_test.go delete mode 100644 localbridgesync/types.go create mode 100644 sync/common.go create mode 100644 sync/driver.go create mode 100644 sync/evmdownloader.go rename localbridgesync/downloader_test.go => sync/evmdownloader_test.go (55%) create mode 100644 sync/evmdriver.go rename localbridgesync/driver_test.go => sync/evmdriver_test.go (67%) create mode 100644 sync/evmtypes.go rename {localbridgesync => sync}/mock_downloader_test.go (54%) rename {localbridgesync => sync}/mock_l2_test.go (99%) rename {localbridgesync => sync}/mock_processor_test.go (70%) rename {localbridgesync => sync}/mock_reorgdetector_test.go (82%) diff --git a/Dockerfile b/Dockerfile index 0b509075..719868f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ # CONTAINER FOR BUILDING BINARY -FROM golang:1.22.4 AS build +FROM golang:1.22.5-alpine3.20 AS build WORKDIR $GOPATH/src/github.com/0xPolygon/cdk +RUN apk update && apk add --no-cache make build-base git # INSTALL DEPENDENCIES COPY go.mod go.sum /src/ RUN cd /src && go mod download @@ -12,9 +13,9 @@ COPY . /src RUN cd /src && make build # CONTAINER FOR RUNNING BINARY -FROM alpine:3.18.4 +FROM alpine:3.20 COPY --from=build /src/dist/cdk /app/cdk RUN mkdir /app/data && apk update && apk add postgresql15-client EXPOSE 8123 -CMD ["/bin/sh", "-c", "/app/cdk run"] +CMD ["/bin/sh", "-c", "/app/cdk run"] \ No newline at end of file diff --git a/Makefile b/Makefile index d092a0f3..e081dcb7 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ else endif GOBASE := $(shell pwd) GOBIN := $(GOBASE)/dist -GOENVVARS := GOBIN=$(GOBIN) CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) +GOENVVARS := GOBIN=$(GOBIN) CGO_ENABLED=1 GOOS=linux GOARCH=$(ARCH) GOBINARY := cdk GOCMD := $(GOBASE)/cmd diff --git a/aggoracle/chaingersender/evm.go b/aggoracle/chaingersender/evm.go new file mode 100644 index 00000000..859f4b8b --- /dev/null +++ b/aggoracle/chaingersender/evm.go @@ -0,0 +1,117 @@ +package chaingersender + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/pessimisticglobalexitroot" + cfgTypes "github.com/0xPolygon/cdk/config/types" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type EthClienter interface { + ethereum.LogFilterer + ethereum.BlockNumberReader + ethereum.ChainReader + bind.ContractBackend +} + +type EthTxManager interface { + Remove(ctx context.Context, id common.Hash) error + ResultsByStatus(ctx context.Context, statuses []ethtxmanager.MonitoredTxStatus) ([]ethtxmanager.MonitoredTxResult, error) + Result(ctx context.Context, id common.Hash) (ethtxmanager.MonitoredTxResult, error) + Add(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar) (common.Hash, error) +} + +type EVMChainGERSender struct { + gerContract *pessimisticglobalexitroot.Pessimisticglobalexitroot + gerAddr common.Address + sender common.Address + client EthClienter + ethTxMan EthTxManager + gasOffset uint64 + waitPeriodMonitorTx time.Duration +} + +type EVMConfig struct { + GlobalExitRootL2Addr common.Address `mapstructure:"GlobalExitRootL2"` + URLRPCL2 string `mapstructure:"URLRPCL2"` + ChainIDL2 uint64 `mapstructure:"ChainIDL2"` + GasOffset uint64 `mapstructure:"GasOffset"` + WaitPeriodMonitorTx cfgTypes.Duration `mapstructure:"WaitPeriodMonitorTx"` + SenderAddr common.Address `mapstructure:"SenderAddr"` + EthTxManager ethtxmanager.Config `mapstructure:"EthTxManager"` +} + +func NewEVMChainGERSender( + l2GlobalExitRoot, sender common.Address, + l2Client EthClienter, + ethTxMan EthTxManager, + gasOffset uint64, + waitPeriodMonitorTx time.Duration, +) (*EVMChainGERSender, error) { + gerContract, err := pessimisticglobalexitroot.NewPessimisticglobalexitroot(l2GlobalExitRoot, l2Client) + if err != nil { + return nil, err + } + return &EVMChainGERSender{ + gerContract: gerContract, + gerAddr: l2GlobalExitRoot, + sender: sender, + client: l2Client, + ethTxMan: ethTxMan, + gasOffset: gasOffset, + waitPeriodMonitorTx: waitPeriodMonitorTx, + }, nil +} + +func (c *EVMChainGERSender) IsGERAlreadyInjected(ger common.Hash) (bool, error) { + timestamp, err := c.gerContract.GlobalExitRootMap(&bind.CallOpts{Pending: false}, ger) + if err != nil { + return false, fmt.Errorf("error calling gerContract.GlobalExitRootMap: %w", err) + } + return timestamp.Cmp(big.NewInt(0)) != 0, nil +} + +func (c *EVMChainGERSender) UpdateGERWaitUntilMined(ctx context.Context, ger common.Hash) error { + abi, err := pessimisticglobalexitroot.PessimisticglobalexitrootMetaData.GetAbi() + if err != nil { + return err + } + data, err := abi.Pack("updateGlobalExitRoot", ger) + if err != nil { + return err + } + id, err := c.ethTxMan.Add(ctx, &c.gerAddr, nil, big.NewInt(0), data, c.gasOffset, nil) + if err != nil { + return err + } + for { + time.Sleep(c.waitPeriodMonitorTx) + log.Debugf("waiting for tx %s to be mined", id.Hex()) + res, err := c.ethTxMan.Result(ctx, id) + if err != nil { + log.Error("error calling ethTxMan.Result: ", err) + } + switch res.Status { + case ethtxmanager.MonitoredTxStatusCreated, + ethtxmanager.MonitoredTxStatusSent: + continue + case ethtxmanager.MonitoredTxStatusFailed: + return fmt.Errorf("tx %s failed", res.ID) + case ethtxmanager.MonitoredTxStatusMined, + ethtxmanager.MonitoredTxStatusSafe, + ethtxmanager.MonitoredTxStatusFinalized: + return nil + default: + log.Error("unexpected tx status: ", res.Status) + } + } +} diff --git a/aggoracle/config.go b/aggoracle/config.go new file mode 100644 index 00000000..2dd39403 --- /dev/null +++ b/aggoracle/config.go @@ -0,0 +1,25 @@ +package aggoracle + +import ( + "github.com/0xPolygon/cdk/aggoracle/chaingersender" + "github.com/0xPolygon/cdk/config/types" +) + +type TargetChainType string + +const ( + EVMChain TargetChainType = "EVM" +) + +var ( + SupportedChainTypes = []TargetChainType{EVMChain} +) + +type Config struct { + TargetChainType TargetChainType `mapstructure:"TargetChainType"` + URLRPCL1 string `mapstructure:"URLRPCL1"` + // TODO: BlockFinality doesnt work as per the jsonschema + BlockFinality string `jsonschema:"enum=latest,enum=safe, enum=pending, enum=finalized" mapstructure:"BlockFinality"` + WaitPeriodNextGER types.Duration `mapstructure:"WaitPeriodNextGER"` + EVMSender chaingersender.EVMConfig `mapstructure:"EVMSender"` +} diff --git a/aggoracle/e2e_test.go b/aggoracle/e2e_test.go new file mode 100644 index 00000000..ce081c35 --- /dev/null +++ b/aggoracle/e2e_test.go @@ -0,0 +1,214 @@ +package aggoracle_test + +import ( + "context" + "errors" + "fmt" + "math/big" + "strconv" + "testing" + "time" + + gerContractL1 "github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/globalexitrootnopush0" + gerContractEVMChain "github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/pessimisticglobalexitrootnopush0" + "github.com/0xPolygon/cdk/aggoracle" + "github.com/0xPolygon/cdk/aggoracle/chaingersender" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/l1infotreesync" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/reorgdetector" + ethtxmanager "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient/simulated" + mock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestEVM(t *testing.T) { + ctx := context.Background() + l1Client, syncer, gerL1Contract, authL1 := commonSetup(t) + sender := evmSetup(t) + oracle, err := aggoracle.New(sender, l1Client.Client(), syncer, etherman.LatestBlock, time.Millisecond) + require.NoError(t, err) + go oracle.Start(ctx) + + runTest(t, gerL1Contract, sender, l1Client, authL1) +} + +func commonSetup(t *testing.T) ( + *simulated.Backend, + *l1infotreesync.L1InfoTreeSync, + *gerContractL1.Globalexitrootnopush0, + *bind.TransactOpts, +) { + // Config and spin up + ctx := context.Background() + // Simulated L1 + privateKeyL1, err := crypto.GenerateKey() + require.NoError(t, err) + authL1, err := bind.NewKeyedTransactorWithChainID(privateKeyL1, big.NewInt(1337)) + require.NoError(t, err) + l1Client, gerL1Addr, gerL1Contract, err := newSimulatedL1(authL1) + require.NoError(t, err) + // Reorg detector + dbPathReorgDetector := t.TempDir() + reorg, err := reorgdetector.New(ctx, l1Client.Client(), dbPathReorgDetector) + require.NoError(t, err) + // Syncer + dbPathSyncer := t.TempDir() + syncer, err := l1infotreesync.New(ctx, dbPathSyncer, gerL1Addr, 10, etherman.LatestBlock, reorg, l1Client.Client(), time.Millisecond, 0, 100*time.Millisecond, 3) + require.NoError(t, err) + go syncer.Start(ctx) + + return l1Client, syncer, gerL1Contract, authL1 +} + +func evmSetup(t *testing.T) aggoracle.ChainSender { + privateKeyL2, err := crypto.GenerateKey() + require.NoError(t, err) + authL2, err := bind.NewKeyedTransactorWithChainID(privateKeyL2, big.NewInt(1337)) + require.NoError(t, err) + l2Client, gerL2Addr, _, err := newSimulatedEVMAggSovereignChain(authL2) + require.NoError(t, err) + ethTxManMock := aggoracle.NewEthTxManagerMock(t) + // id, err := c.ethTxMan.Add(ctx, &c.gerAddr, nil, big.NewInt(0), tx.Data(), c.gasOffset, nil) + ethTxManMock.On("Add", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + ctx := context.Background() + nonce, err := l2Client.Client().PendingNonceAt(ctx, authL2.From) + if err != nil { + log.Error(err) + return + } + gas, err := l2Client.Client().EstimateGas(ctx, ethereum.CallMsg{ + From: authL2.From, + To: args.Get(1).(*common.Address), + Value: big.NewInt(0), + Data: args.Get(4).([]byte), + }) + if err != nil { + log.Error(err) + res, err := l2Client.Client().CallContract(ctx, ethereum.CallMsg{ + From: authL2.From, + To: args.Get(1).(*common.Address), + Value: big.NewInt(0), + Data: args.Get(4).([]byte), + }, nil) + log.Debugf("contract call: %s", res) + if err != nil { + log.Error(err) + } + return + } + price, err := l2Client.Client().SuggestGasPrice(ctx) + if err != nil { + log.Error(err) + } + tx := types.NewTx(&types.LegacyTx{ + To: args.Get(1).(*common.Address), + Nonce: nonce, + Value: big.NewInt(0), + Data: args.Get(4).([]byte), + Gas: gas, + GasPrice: price, + }) + tx.Gas() + signedTx, err := authL2.Signer(authL2.From, tx) + if err != nil { + log.Error(err) + return + } + err = l2Client.Client().SendTransaction(ctx, signedTx) + if err != nil { + log.Error(err) + return + } + l2Client.Commit() + }). + Return(common.Hash{}, nil) + // res, err := c.ethTxMan.Result(ctx, id) + ethTxManMock.On("Result", mock.Anything, mock.Anything). + Return(ethtxmanager.MonitoredTxResult{Status: ethtxmanager.MonitoredTxStatusMined}, nil) + sender, err := chaingersender.NewEVMChainGERSender(gerL2Addr, authL2.From, l2Client.Client(), ethTxManMock, 0, time.Millisecond*50) + require.NoError(t, err) + + return sender +} + +func newSimulatedL1(auth *bind.TransactOpts) ( + client *simulated.Backend, + gerAddr common.Address, + gerContract *gerContractL1.Globalexitrootnopush0, + err error, +) { + balance, _ := new(big.Int).SetString("10000000000000000000000000", 10) //nolint:gomnd + address := auth.From + genesisAlloc := map[common.Address]types.Account{ + address: { + Balance: balance, + }, + } + blockGasLimit := uint64(999999999999999999) //nolint:gomnd + client = simulated.NewBackend(genesisAlloc, simulated.WithBlockGasLimit(blockGasLimit)) + + gerAddr, _, gerContract, err = gerContractL1.DeployGlobalexitrootnopush0(auth, client.Client(), auth.From, auth.From) + + client.Commit() + return +} + +func newSimulatedEVMAggSovereignChain(auth *bind.TransactOpts) ( + client *simulated.Backend, + gerAddr common.Address, + gerContract *gerContractEVMChain.Pessimisticglobalexitrootnopush0, + err error, +) { + balance, _ := new(big.Int).SetString("10000000000000000000000000", 10) //nolint:gomnd + address := auth.From + genesisAlloc := map[common.Address]types.Account{ + address: { + Balance: balance, + }, + } + blockGasLimit := uint64(999999999999999999) //nolint:gomnd + client = simulated.NewBackend(genesisAlloc, simulated.WithBlockGasLimit(blockGasLimit)) + + gerAddr, _, gerContract, err = gerContractEVMChain.DeployPessimisticglobalexitrootnopush0(auth, client.Client(), auth.From) + if err != nil { + return + } + client.Commit() + + _GLOBAL_EXIT_ROOT_SETTER_ROLE := common.HexToHash("0x7b95520991dfda409891be0afa2635b63540f92ee996fda0bf695a166e5c5176") + _, err = gerContract.GrantRole(auth, _GLOBAL_EXIT_ROOT_SETTER_ROLE, auth.From) + client.Commit() + hasRole, _ := gerContract.HasRole(&bind.CallOpts{Pending: false}, _GLOBAL_EXIT_ROOT_SETTER_ROLE, auth.From) + if !hasRole { + err = errors.New("failed to set role") + } + return +} + +func runTest( + t *testing.T, + gerL1Contract *gerContractL1.Globalexitrootnopush0, + sender aggoracle.ChainSender, + l1Client *simulated.Backend, + authL1 *bind.TransactOpts, +) { + for i := 0; i < 10; i++ { + _, err := gerL1Contract.UpdateExitRoot(authL1, common.HexToHash(strconv.Itoa(i))) + require.NoError(t, err) + l1Client.Commit() + time.Sleep(time.Millisecond * 50) + expectedGER, err := gerL1Contract.GetLastGlobalExitRoot(&bind.CallOpts{Pending: false}) + require.NoError(t, err) + isInjected, err := sender.IsGERAlreadyInjected(expectedGER) + require.NoError(t, err) + require.True(t, isInjected, fmt.Sprintf("iteration %d, GER: %s", i, common.Bytes2Hex(expectedGER[:]))) + } +} diff --git a/aggoracle/mock_ethtxmanager_test.go b/aggoracle/mock_ethtxmanager_test.go new file mode 100644 index 00000000..37bcbeda --- /dev/null +++ b/aggoracle/mock_ethtxmanager_test.go @@ -0,0 +1,127 @@ +// Code generated by mockery v2.22.1. DO NOT EDIT. + +package aggoracle + +import ( + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + context "context" + + ethtxmanager "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + + mock "github.com/stretchr/testify/mock" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// EthTxManagerMock is an autogenerated mock type for the EthTxManager type +type EthTxManagerMock struct { + mock.Mock +} + +// Add provides a mock function with given fields: ctx, to, forcedNonce, value, data, gasOffset, sidecar +func (_m *EthTxManagerMock) Add(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar) (common.Hash, error) { + ret := _m.Called(ctx, to, forcedNonce, value, data, gasOffset, sidecar) + + var r0 common.Hash + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *common.Address, *uint64, *big.Int, []byte, uint64, *types.BlobTxSidecar) (common.Hash, error)); ok { + return rf(ctx, to, forcedNonce, value, data, gasOffset, sidecar) + } + if rf, ok := ret.Get(0).(func(context.Context, *common.Address, *uint64, *big.Int, []byte, uint64, *types.BlobTxSidecar) common.Hash); ok { + r0 = rf(ctx, to, forcedNonce, value, data, gasOffset, sidecar) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Hash) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *common.Address, *uint64, *big.Int, []byte, uint64, *types.BlobTxSidecar) error); ok { + r1 = rf(ctx, to, forcedNonce, value, data, gasOffset, sidecar) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Remove provides a mock function with given fields: ctx, id +func (_m *EthTxManagerMock) Remove(ctx context.Context, id common.Hash) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Result provides a mock function with given fields: ctx, id +func (_m *EthTxManagerMock) Result(ctx context.Context, id common.Hash) (ethtxmanager.MonitoredTxResult, error) { + ret := _m.Called(ctx, id) + + var r0 ethtxmanager.MonitoredTxResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (ethtxmanager.MonitoredTxResult, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) ethtxmanager.MonitoredTxResult); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(ethtxmanager.MonitoredTxResult) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResultsByStatus provides a mock function with given fields: ctx, statuses +func (_m *EthTxManagerMock) ResultsByStatus(ctx context.Context, statuses []ethtxmanager.MonitoredTxStatus) ([]ethtxmanager.MonitoredTxResult, error) { + ret := _m.Called(ctx, statuses) + + var r0 []ethtxmanager.MonitoredTxResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []ethtxmanager.MonitoredTxStatus) ([]ethtxmanager.MonitoredTxResult, error)); ok { + return rf(ctx, statuses) + } + if rf, ok := ret.Get(0).(func(context.Context, []ethtxmanager.MonitoredTxStatus) []ethtxmanager.MonitoredTxResult); ok { + r0 = rf(ctx, statuses) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]ethtxmanager.MonitoredTxResult) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []ethtxmanager.MonitoredTxStatus) error); ok { + r1 = rf(ctx, statuses) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewEthTxManagerMock interface { + mock.TestingT + Cleanup(func()) +} + +// NewEthTxManagerMock creates a new instance of EthTxManagerMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEthTxManagerMock(t mockConstructorTestingTNewEthTxManagerMock) *EthTxManagerMock { + mock := &EthTxManagerMock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggoracle/oracle.go b/aggoracle/oracle.go new file mode 100644 index 00000000..f22ee1f0 --- /dev/null +++ b/aggoracle/oracle.go @@ -0,0 +1,110 @@ +package aggoracle + +import ( + "context" + "math/big" + "time" + + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/l1infotreesync" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" +) + +type L1InfoTreer interface { + GetLatestInfoUntilBlock(ctx context.Context, blockNum uint64) (*l1infotreesync.L1InfoTreeLeaf, error) +} + +type ChainSender interface { + IsGERAlreadyInjected(ger common.Hash) (bool, error) + UpdateGERWaitUntilMined(ctx context.Context, ger common.Hash) error +} + +type AggOracle struct { + ticker *time.Ticker + l1Client ethereum.ChainReader + l1Info L1InfoTreer + chainSender ChainSender + blockFinality *big.Int +} + +func New( + chainSender ChainSender, + l1Client ethereum.ChainReader, + l1InfoTreeSyncer L1InfoTreer, + blockFinalityType etherman.BlockNumberFinality, + waitPeriodNextGER time.Duration, +) (*AggOracle, error) { + ticker := time.NewTicker(waitPeriodNextGER) + finality, err := blockFinalityType.ToBlockNum() + if err != nil { + return nil, err + } + return &AggOracle{ + ticker: ticker, + l1Client: l1Client, + l1Info: l1InfoTreeSyncer, + chainSender: chainSender, + blockFinality: finality, + }, nil +} + +func (a *AggOracle) Start(ctx context.Context) { + var ( + blockNumToFetch uint64 + gerToInject common.Hash + err error + ) + for { + select { + case <-a.ticker.C: + blockNumToFetch, gerToInject, err = a.getLastFinalisedGER(ctx, blockNumToFetch) + if err != nil { + if err == l1infotreesync.ErrBlockNotProcessed { + log.Debugf("syncer is not ready for the block %d", blockNumToFetch) + } else if err == l1infotreesync.ErrNotFound { + blockNumToFetch = 0 + log.Debugf("syncer has not found any GER until block %d", blockNumToFetch) + } else { + log.Error("error calling getLastFinalisedGER: ", err) + } + continue + } + if alreadyInjected, err := a.chainSender.IsGERAlreadyInjected(gerToInject); err != nil { + log.Error("error calling isGERAlreadyInjected: ", err) + continue + } else if alreadyInjected { + log.Debugf("GER %s already injected", gerToInject.Hex()) + continue + } + log.Infof("injecting new GER: %s", gerToInject.Hex()) + if err := a.chainSender.UpdateGERWaitUntilMined(ctx, gerToInject); err != nil { + log.Errorf("error calling updateGERWaitUntilMined, when trying to inject GER %s: %v", gerToInject.Hex(), err) + continue + } + log.Infof("GER %s injected", gerToInject.Hex()) + case <-ctx.Done(): + return + } + } +} + +// getLastFinalisedGER tries to return a finalised GER: +// If blockNumToFetch != 0: it will try to fetch it until the given block +// Else it will ask the L1 client for the latest finalised block and use that +// If it fails to get the GER from the syncer, it will retunr the block number that used to query +func (a *AggOracle) getLastFinalisedGER(ctx context.Context, blockNumToFetch uint64) (uint64, common.Hash, error) { + if blockNumToFetch == 0 { + header, err := a.l1Client.HeaderByNumber(ctx, a.blockFinality) + if err != nil { + return 0, common.Hash{}, err + } + blockNumToFetch = header.Number.Uint64() + } + info, err := a.l1Info.GetLatestInfoUntilBlock(ctx, blockNumToFetch) + if err != nil { + return blockNumToFetch, common.Hash{}, err + } + return 0, info.GlobalExitRoot, nil +} diff --git a/cmd/main.go b/cmd/main.go index ce319729..a13f43e1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -16,6 +16,8 @@ const ( SEQUENCE_SENDER = "sequence-sender" // AGGREGATOR name to identify the aggregator component AGGREGATOR = "aggregator" + // AGGORACLE name to identify the aggoracle component + AGGORACLE = "aggoracle" ) const ( @@ -47,7 +49,7 @@ var ( Aliases: []string{"co"}, Usage: "List of components to run", Required: false, - Value: cli.NewStringSlice(SEQUENCE_SENDER, AGGREGATOR), + Value: cli.NewStringSlice(SEQUENCE_SENDER, AGGREGATOR, AGGORACLE), } ) diff --git a/cmd/run.go b/cmd/run.go index 15d24c29..f7d36b74 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -10,6 +10,8 @@ import ( zkevm "github.com/0xPolygon/cdk" dataCommitteeClient "github.com/0xPolygon/cdk-data-availability/client" + "github.com/0xPolygon/cdk/aggoracle" + "github.com/0xPolygon/cdk/aggoracle/chaingersender" "github.com/0xPolygon/cdk/aggregator" "github.com/0xPolygon/cdk/aggregator/db" "github.com/0xPolygon/cdk/config" @@ -18,7 +20,9 @@ import ( "github.com/0xPolygon/cdk/etherman" ethermanconfig "github.com/0xPolygon/cdk/etherman/config" "github.com/0xPolygon/cdk/etherman/contracts" + "github.com/0xPolygon/cdk/l1infotreesync" "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/reorgdetector" "github.com/0xPolygon/cdk/sequencesender" "github.com/0xPolygon/cdk/sequencesender/txbuilder" "github.com/0xPolygon/cdk/state" @@ -26,6 +30,9 @@ import ( "github.com/0xPolygon/cdk/translator" ethtxman "github.com/0xPolygonHermez/zkevm-ethtx-manager/etherman" "github.com/0xPolygonHermez/zkevm-ethtx-manager/etherman/etherscan" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + ethtxlog "github.com/0xPolygonHermez/zkevm-ethtx-manager/log" + "github.com/ethereum/go-ethereum/ethclient" "github.com/jackc/pgx/v4/pgxpool" "github.com/urfave/cli/v2" ) @@ -63,6 +70,17 @@ func start(cliCtx *cli.Context) error { log.Fatal(err) } }() + case AGGORACLE: + l1Client, err := ethclient.Dial(c.AggOracle.URLRPCL1) + if err != nil { + log.Fatal(err) + } + reorgDetector := newReorgDetectorL1(cliCtx.Context, *c, l1Client) + go reorgDetector.Start(cliCtx.Context) + syncer := newL1InfoTreeSyncer(cliCtx.Context, *c, l1Client, reorgDetector) + go syncer.Start(cliCtx.Context) + aggOracle := createAggoracle(*c, l1Client, syncer) + go aggOracle.Start(cliCtx.Context) } } @@ -180,6 +198,55 @@ func newTxBuilder(cfg config.Config, ethman *etherman.Client) (txbuilder.TxBuild return txBuilder, err } +func createAggoracle(cfg config.Config, l1Client *ethclient.Client, syncer *l1infotreesync.L1InfoTreeSync) *aggoracle.AggOracle { + var sender aggoracle.ChainSender + switch cfg.AggOracle.TargetChainType { + case aggoracle.EVMChain: + cfg.AggOracle.EVMSender.EthTxManager.Log = ethtxlog.Config{ + Environment: ethtxlog.LogEnvironment(cfg.Log.Environment), + Level: cfg.Log.Level, + Outputs: cfg.Log.Outputs, + } + ethTxManager, err := ethtxmanager.New(cfg.AggOracle.EVMSender.EthTxManager) + if err != nil { + log.Fatal(err) + } + go ethTxManager.Start() + l2CLient, err := ethclient.Dial(cfg.AggOracle.EVMSender.URLRPCL2) + if err != nil { + log.Fatal(err) + } + sender, err = chaingersender.NewEVMChainGERSender( + cfg.AggOracle.EVMSender.GlobalExitRootL2Addr, + cfg.AggOracle.EVMSender.SenderAddr, + l2CLient, + ethTxManager, + cfg.AggOracle.EVMSender.GasOffset, + cfg.AggOracle.EVMSender.WaitPeriodMonitorTx.Duration, + ) + if err != nil { + log.Fatal(err) + } + default: + log.Fatalf( + "Unsupported chaintype %s. Supported values: %v", + cfg.AggOracle.TargetChainType, aggoracle.SupportedChainTypes, + ) + } + aggOracle, err := aggoracle.New( + sender, + l1Client, + syncer, + etherman.BlockNumberFinality(cfg.AggOracle.BlockFinality), + cfg.AggOracle.WaitPeriodNextGER.Duration, + ) + if err != nil { + log.Fatal(err) + } + + return aggOracle +} + func newDataAvailability(c config.Config, etherman *etherman.Client) (*dataavailability.DataAvailability, error) { if !c.Common.IsValidiumMode { return nil, nil @@ -292,3 +359,40 @@ func newState(c *config.Config, l2ChainID uint64, sqlDB *pgxpool.Pool) *state.St st := state.NewState(stateCfg, stateDb) return st } + +func newReorgDetectorL1( + ctx context.Context, + cfg config.Config, + l1Client *ethclient.Client, +) *reorgdetector.ReorgDetector { + rd, err := reorgdetector.New(ctx, l1Client, cfg.ReorgDetectorL1.DBPath) + if err != nil { + log.Fatal(err) + } + return rd +} + +func newL1InfoTreeSyncer( + ctx context.Context, + cfg config.Config, + l1Client *ethclient.Client, + reorgDetector *reorgdetector.ReorgDetector, +) *l1infotreesync.L1InfoTreeSync { + syncer, err := l1infotreesync.New( + ctx, + cfg.L1InfoTreeSync.DBPath, + cfg.L1InfoTreeSync.GlobalExitRootAddr, + cfg.L1InfoTreeSync.SyncBlockChunkSize, + etherman.BlockNumberFinality(cfg.L1InfoTreeSync.BlockFinality), + reorgDetector, + l1Client, + cfg.L1InfoTreeSync.WaitForNewBlocksPeriod.Duration, + cfg.L1InfoTreeSync.InitialBlock, + cfg.L1InfoTreeSync.RetryAfterErrorPeriod.Duration, + cfg.L1InfoTreeSync.MaxRetryAttemptsAfterError, + ) + if err != nil { + log.Fatal(err) + } + return syncer +} diff --git a/common/common.go b/common/common.go index 4e93d5f8..ebbafd69 100644 --- a/common/common.go +++ b/common/common.go @@ -9,17 +9,29 @@ import ( "github.com/iden3/go-iden3-crypto/keccak256" ) -// BlockNum2Bytes converts a block number to a byte slice -func BlockNum2Bytes(blockNum uint64) []byte { - key := make([]byte, 8) - binary.LittleEndian.PutUint64(key, blockNum) +// Uint64ToBytes converts a uint64 to a byte slice +func Uint64ToBytes(num uint64) []byte { + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, num) + return bytes +} + +// BytesToUint64 converts a byte slice to a uint64 +func BytesToUint64(bytes []byte) uint64 { + return binary.LittleEndian.Uint64(bytes) +} + +// Uint32To2Bytes converts a uint32 to a byte slice +func Uint32ToBytes(num uint32) []byte { + key := make([]byte, 4) + binary.LittleEndian.PutUint32(key, num) return key } -// Bytes2BlockNum converts a byte slice to a block number -func Bytes2BlockNum(key []byte) uint64 { - return binary.LittleEndian.Uint64(key) +// BytesToUint32 converts a byte slice to a uint32 +func BytesToUint32(bytes []byte) uint32 { + return binary.LittleEndian.Uint32(bytes) } func CalculateAccInputHash( diff --git a/config/config.go b/config/config.go index 02ede0fb..ed452417 100644 --- a/config/config.go +++ b/config/config.go @@ -5,10 +5,13 @@ import ( "path/filepath" "strings" + "github.com/0xPolygon/cdk/aggoracle" "github.com/0xPolygon/cdk/aggregator" "github.com/0xPolygon/cdk/common" ethermanconfig "github.com/0xPolygon/cdk/etherman/config" + "github.com/0xPolygon/cdk/l1infotreesync" "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/reorgdetector" "github.com/0xPolygon/cdk/sequencesender" "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" "github.com/mitchellh/mapstructure" @@ -70,6 +73,12 @@ type Config struct { // Common Config that affects all the services Common common.Config + // Configuration of the reorg detector service to be used for the L1 + ReorgDetectorL1 reorgdetector.Config + // Configuration of the aggOracle service + AggOracle aggoracle.Config + // Configuration of the L1 Info Treee Sync service + L1InfoTreeSync l1infotreesync.Config } // Default parses the default configuration values. diff --git a/config/default.go b/config/default.go index 7b12e6f3..85dbd1cd 100644 --- a/config/default.go +++ b/config/default.go @@ -118,4 +118,49 @@ SequencerPrivateKey = {} [Aggregator.Synchronizer.Etherman] [Aggregator.Synchronizer.Etherman.Validium] Enabled = false + +[ReorgDetectorL1] +DBPath = "/tmp/reorgdetector" + +[L1InfoTreeSync] +DBPath = "/tmp/L1InfoTreeSync" +GlobalExitRootAddr="0x8464135c8F25Da09e49BC8782676a84730C318bC" +SyncBlockChunkSize=10 +BlockFinality="latest" +URLRPCL1="http://test-aggoracle-l1:8545" +WaitForNewBlocksPeriod="100ms" +InitialBlock=0 + +[AggOracle] +TargetChainType="EVM" +URLRPCL1="http://test-aggoracle-l1:8545" +BlockFinality="latest" +WaitPeriodNextGER="100ms" + [EVMSender] + GlobalExitRootL2="0x8464135c8F25Da09e49BC8782676a84730C318bC" + URLRPCL2="http://test-aggoracle-l2:8545" + ChainIDL2=1337 + GasOffset=0 + WaitPeriodMonitorTx="100ms" + SenderAddr="0x70997970c51812dc3a010c7d01b50e0d17dc79c8" + [SequenceSender.EthTxManager] + FrequencyToMonitorTxs = "1s" + WaitTxToBeMined = "2s" + GetReceiptMaxTime = "250ms" + GetReceiptWaitInterval = "1s" + PrivateKeys = [ + {Path = "/app/keystore/aggoracle.keystore", Password = "testonly"}, + ] + ForcedGas = 0 + GasPriceMarginFactor = 1 + MaxGasPriceLimit = 0 + PersistenceFilename = "/tmp/ethtxmanager.json" + ReadPendingL1Txs = false + SafeStatusL1NumberOfBlocks = 5 + FinalizedStatusL1NumberOfBlocks = 10 + [SequenceSender.EthTxManager.Etherman] + URL = "http://test-aggoracle-l2" + MultiGasProvider = false + L1ChainID = 1337 + HTTPHeaders = [] ` diff --git a/go.mod b/go.mod index e2f17883..5b2fe026 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240726125827-301fa4c59245 github.com/0xPolygon/cdk-data-availability v0.0.8 github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d - github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2 + github.com/0xPolygonHermez/zkevm-data-streamer v0.2.3-RC4 github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9 github.com/0xPolygonHermez/zkevm-synchronizer-l1 v0.6.3-0.20240712085301-0310358abb59 github.com/ethereum/go-ethereum v1.14.5 diff --git a/go.sum b/go.sum index 3a9df8da..a1c78e52 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/0xPolygon/cdk-data-availability v0.0.8 h1:bMmOYZ7Ei683y80ric3KzMPXtRG github.com/0xPolygon/cdk-data-availability v0.0.8/go.mod h1:3XkZ0zn0GsvAT01MPQMmukF534CVSFmtrcoK3F/BK6Q= github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d h1:sxh6hZ2jF/sxxj2jd5o1vuNNCZjYmn4aRG9SRlVaEFs= github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d/go.mod h1:2scWqMMufrQXu7TikDgQ3BsyaKoX8qP26D6E262vSOg= -github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2 h1:XRMTk+W6vtJVGVjuEznfWyNt7HkRkkuSmlN5Y6p60Sc= -github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2/go.mod h1:0QkAXcFa92mFJrCbN3UPUJGJYes851yEgYHLONnaosE= +github.com/0xPolygonHermez/zkevm-data-streamer v0.2.3-RC4 h1:+4K+xSzv0ImbK30B/T9FauNTrTFUmWcNKYhIgwsE4C4= +github.com/0xPolygonHermez/zkevm-data-streamer v0.2.3-RC4/go.mod h1:0QkAXcFa92mFJrCbN3UPUJGJYes851yEgYHLONnaosE= github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9 h1:vrAezzwTNke6NroDAltGh1k2AJ6ibmZPBsG0bCltbRc= github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9/go.mod h1:pRqfLQVM3nbzdhy3buqjAgcVyNDKAXOHqTSgkwiKpic= github.com/0xPolygonHermez/zkevm-synchronizer-l1 v0.6.3-0.20240712085301-0310358abb59 h1:Qwh92vFEXnpmDggQaZA3648viEQfLdMnAw/WFSY+2i8= diff --git a/l1infotreesync/downloader.go b/l1infotreesync/downloader.go new file mode 100644 index 00000000..255395dd --- /dev/null +++ b/l1infotreesync/downloader.go @@ -0,0 +1,49 @@ +package l1infotreesync + +import ( + "fmt" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonzkevmglobalexitrootv2" + "github.com/0xPolygon/cdk/sync" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + updateL1InfoTreeSignature = crypto.Keccak256Hash([]byte("UpdateL1InfoTree(bytes32,bytes32)")) +) + +type EthClienter interface { + ethereum.LogFilterer + ethereum.BlockNumberReader + ethereum.ChainReader + bind.ContractBackend +} + +func buildAppender(client EthClienter, globalExitRoot common.Address) (sync.LogAppenderMap, error) { + contract, err := polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2(globalExitRoot, client) + if err != nil { + return nil, err + } + appender := make(sync.LogAppenderMap) + appender[updateL1InfoTreeSignature] = func(b *sync.EVMBlock, l types.Log) error { + l1InfoTreeUpdate, err := contract.ParseUpdateL1InfoTree(l) + if err != nil { + return fmt.Errorf( + "error parsing log %+v using contract.ParseUpdateL1InfoTree: %v", + l, err, + ) + } + b.Events = append(b.Events, Event{ + MainnetExitRoot: l1InfoTreeUpdate.MainnetExitRoot, + RollupExitRoot: l1InfoTreeUpdate.RollupExitRoot, + ParentHash: b.ParentHash, + Timestamp: b.Timestamp, + }) + return nil + } + return appender, nil +} diff --git a/l1infotreesync/e2e_test.go b/l1infotreesync/e2e_test.go new file mode 100644 index 00000000..82bef733 --- /dev/null +++ b/l1infotreesync/e2e_test.go @@ -0,0 +1,82 @@ +package l1infotreesync + +import ( + "context" + "fmt" + "math/big" + "strconv" + "testing" + "time" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/globalexitrootnopush0" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/reorgdetector" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient/simulated" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func newSimulatedClient(auth *bind.TransactOpts) ( + client *simulated.Backend, + gerAddr common.Address, + gerContract *globalexitrootnopush0.Globalexitrootnopush0, + err error, +) { + balance, _ := new(big.Int).SetString("10000000000000000000000000", 10) //nolint:gomnd + address := auth.From + genesisAlloc := map[common.Address]types.Account{ + address: { + Balance: balance, + }, + } + blockGasLimit := uint64(999999999999999999) //nolint:gomnd + client = simulated.NewBackend(genesisAlloc, simulated.WithBlockGasLimit(blockGasLimit)) + + gerAddr, _, gerContract, err = globalexitrootnopush0.DeployGlobalexitrootnopush0(auth, client.Client(), auth.From, auth.From) + + client.Commit() + return +} + +func TestE2E(t *testing.T) { + ctx := context.Background() + dbPath := t.TempDir() + privateKey, err := crypto.GenerateKey() + require.NoError(t, err) + auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1337)) + require.NoError(t, err) + rdm := NewReorgDetectorMock(t) + rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{}, nil) + rdm.On("AddBlockToTrack", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + client, gerAddr, gerSc, err := newSimulatedClient(auth) + require.NoError(t, err) + syncer, err := New(ctx, dbPath, gerAddr, 10, etherman.LatestBlock, rdm, client.Client(), time.Millisecond, 0, 100*time.Millisecond, 3) + require.NoError(t, err) + go syncer.Start(ctx) + + // Update GER 10 times + // TODO: test syncer restart + for i := 0; i < 10; i++ { + _, err := gerSc.UpdateExitRoot(auth, common.HexToHash(strconv.Itoa(i))) + require.NoError(t, err) + client.Commit() + // Let the processor catch up + time.Sleep(time.Millisecond * 10) + + expectedRoot, err := gerSc.GetRoot(&bind.CallOpts{Pending: false}) + require.NoError(t, err) + expectedGER, err := gerSc.GetLastGlobalExitRoot(&bind.CallOpts{Pending: false}) + require.NoError(t, err) + info2, err := syncer.GetInfoByIndex(ctx, uint32(i)) + require.NoError(t, err, fmt.Sprintf("index: %d, root: %s", i, common.Bytes2Hex(expectedRoot[:]))) + require.Equal(t, common.Hash(expectedGER), info2.GlobalExitRoot, fmt.Sprintf("index: %d", i)) + info, err := syncer.GetInfoByRoot(ctx, expectedRoot) + require.NoError(t, err, fmt.Sprintf("index: %d, expected root: %s, actual root: %s", i, common.Bytes2Hex(expectedRoot[:]), info2.L1InfoTreeRoot)) + require.Equal(t, common.Hash(expectedRoot), info.L1InfoTreeRoot) + require.Equal(t, info, info2) + } +} diff --git a/l1infotreesync/l1infotreesync.go b/l1infotreesync/l1infotreesync.go new file mode 100644 index 00000000..7b3be753 --- /dev/null +++ b/l1infotreesync/l1infotreesync.go @@ -0,0 +1,124 @@ +package l1infotreesync + +import ( + "context" + "time" + + "github.com/0xPolygon/cdk/config/types" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/sync" + "github.com/ethereum/go-ethereum/common" +) + +const ( + reorgDetectorID = "l1infotreesync" + downloadBufferSize = 1000 +) + +type Config struct { + DBPath string `mapstructure:"DBPath"` + GlobalExitRootAddr common.Address `mapstructure:"GlobalExitRootAddr"` + SyncBlockChunkSize uint64 `mapstructure:"SyncBlockChunkSize"` + // TODO: BlockFinality doesnt work as per the jsonschema + BlockFinality string `jsonschema:"enum=latest,enum=safe, enum=pending, enum=finalized" mapstructure:"BlockFinality"` + URLRPCL1 string `mapstructure:"URLRPCL1"` + WaitForNewBlocksPeriod types.Duration `mapstructure:"WaitForNewBlocksPeriod"` + InitialBlock uint64 `mapstructure:"InitialBlock"` + RetryAfterErrorPeriod types.Duration `mapstructure:"RetryAfterErrorPeriod"` + MaxRetryAttemptsAfterError int `mapstructure:"MaxRetryAttemptsAfterError"` +} + +type L1InfoTreeSync struct { + processor *processor + driver *sync.EVMDriver +} + +func New( + ctx context.Context, + dbPath string, + globalExitRoot common.Address, + syncBlockChunkSize uint64, + blockFinalityType etherman.BlockNumberFinality, + rd sync.ReorgDetector, + l1Client EthClienter, + waitForNewBlocksPeriod time.Duration, + initialBlock uint64, + retryAfterErrorPeriod time.Duration, + maxRetryAttemptsAfterError int, +) (*L1InfoTreeSync, error) { + processor, err := newProcessor(ctx, dbPath) + if err != nil { + return nil, err + } + // TODO: get the initialBlock from L1 to simplify config + lastProcessedBlock, err := processor.GetLastProcessedBlock(ctx) + if err != nil { + return nil, err + } + if initialBlock > 0 && lastProcessedBlock < initialBlock-1 { + err = processor.ProcessBlock(sync.Block{ + Num: initialBlock - 1, + }) + if err != nil { + return nil, err + } + } + rh := &sync.RetryHandler{ + RetryAfterErrorPeriod: retryAfterErrorPeriod, + MaxRetryAttemptsAfterError: maxRetryAttemptsAfterError, + } + + appender, err := buildAppender(l1Client, globalExitRoot) + if err != nil { + return nil, err + } + downloader, err := sync.NewEVMDownloader( + l1Client, + syncBlockChunkSize, + blockFinalityType, + waitForNewBlocksPeriod, + appender, + []common.Address{globalExitRoot}, + rh, + ) + if err != nil { + return nil, err + } + + driver, err := sync.NewEVMDriver(rd, processor, downloader, reorgDetectorID, downloadBufferSize, rh) + if err != nil { + return nil, err + } + return &L1InfoTreeSync{ + processor: processor, + driver: driver, + }, nil +} + +func (s *L1InfoTreeSync) Start(ctx context.Context) { + s.driver.Sync(ctx) +} + +func (s *L1InfoTreeSync) ComputeMerkleProofByIndex(ctx context.Context, index uint32) ([][32]byte, common.Hash, error) { + return s.processor.ComputeMerkleProofByIndex(ctx, index) +} + +func (s *L1InfoTreeSync) ComputeMerkleProofByRoot(ctx context.Context, root common.Hash) ([][32]byte, common.Hash, error) { + return s.processor.ComputeMerkleProofByRoot(ctx, root) +} + +func (s *L1InfoTreeSync) GetInfoByRoot(ctx context.Context, root common.Hash) (*L1InfoTreeLeaf, error) { + return s.processor.GetInfoByRoot(ctx, root) +} + +func (s *L1InfoTreeSync) GetLatestInfoUntilBlock(ctx context.Context, blockNum uint64) (*L1InfoTreeLeaf, error) { + return s.processor.GetLatestInfoUntilBlock(ctx, blockNum) +} + +func (s *L1InfoTreeSync) GetInfoByIndex(ctx context.Context, index uint32) (*L1InfoTreeLeaf, error) { + return s.processor.GetInfoByIndex(ctx, index) +} + +func (s *L1InfoTreeSync) GetInfoByHash(ctx context.Context, hash []byte) (*L1InfoTreeLeaf, error) { + return s.processor.GetInfoByHash(ctx, hash) +} diff --git a/l1infotreesync/mock_reorgdetector_test.go b/l1infotreesync/mock_reorgdetector_test.go new file mode 100644 index 00000000..22d174d4 --- /dev/null +++ b/l1infotreesync/mock_reorgdetector_test.go @@ -0,0 +1,73 @@ +// Code generated by mockery v2.22.1. DO NOT EDIT. + +package l1infotreesync + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + reorgdetector "github.com/0xPolygon/cdk/reorgdetector" +) + +// ReorgDetectorMock is an autogenerated mock type for the ReorgDetector type +type ReorgDetectorMock struct { + mock.Mock +} + +// AddBlockToTrack provides a mock function with given fields: ctx, id, blockNum, blockHash +func (_m *ReorgDetectorMock) AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error { + ret := _m.Called(ctx, id, blockNum, blockHash) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, uint64, common.Hash) error); ok { + r0 = rf(ctx, id, blockNum, blockHash) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Subscribe provides a mock function with given fields: id +func (_m *ReorgDetectorMock) Subscribe(id string) (*reorgdetector.Subscription, error) { + ret := _m.Called(id) + + var r0 *reorgdetector.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(string) (*reorgdetector.Subscription, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) *reorgdetector.Subscription); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*reorgdetector.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewReorgDetectorMock interface { + mock.TestingT + Cleanup(func()) +} + +// NewReorgDetectorMock creates a new instance of ReorgDetectorMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewReorgDetectorMock(t mockConstructorTestingTNewReorgDetectorMock) *ReorgDetectorMock { + mock := &ReorgDetectorMock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/l1infotreesync/processor.go b/l1infotreesync/processor.go new file mode 100644 index 00000000..35191062 --- /dev/null +++ b/l1infotreesync/processor.go @@ -0,0 +1,523 @@ +package l1infotreesync + +import ( + "context" + "encoding/json" + "errors" + "fmt" + + "github.com/0xPolygon/cdk/common" + "github.com/0xPolygon/cdk/l1infotree" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/sync" + ethCommon "github.com/ethereum/go-ethereum/common" + "github.com/ledgerwatch/erigon-lib/kv" + "github.com/ledgerwatch/erigon-lib/kv/mdbx" + "golang.org/x/crypto/sha3" +) + +const ( + // rootTable stores the L1 info tree roots + // Key: root (common.Hash) + // Value: hash of the leaf that caused the update (common.Hash) + rootTable = "l1infotreesync-root" + // indexTable stores the L1 info tree indexes + // Key: index (uint32 converted to bytes) + // Value: hash of the leaf that caused the update (common.Hash) + indexTable = "l1infotreesync-index" + // infoTable stores the information of the tree (the leaves). Note that the value + // of rootTable and indexTable references the key of the infoTable + // Key: hash of the leaf that caused the update (common.Hash) + // Value: JSON of storeLeaf struct + infoTable = "l1infotreesync-info" + // blockTable stores the first and last index of L1 Info Tree that have been updated on + // a block. This is useful in case there are blocks with multiple updates and a reorg is needed. + // Or for when querying by block number + // Key: block number (uint64 converted to bytes) + // Value: JSON of blockWithLeafs + blockTable = "l1infotreesync-block" + // lastBlockTable used to store the last block processed. This is needed to know the last processed blcok + // when it doesn't have events that make other tables get populated + // Key: it's always lastBlockKey + // Value: block number (uint64 converted to bytes) + lastBlockTable = "l1infotreesync-lastBlock" + + treeHeight uint8 = 32 +) + +var ( + ErrBlockNotProcessed = errors.New("given block(s) have not been processed yet") + ErrNotFound = errors.New("not found") + ErrNoBlock0 = errors.New("blockNum must be greater than 0") + lastBlockKey = []byte("lb") +) + +type processor struct { + db kv.RwDB + tree *l1infotree.L1InfoTree +} + +type Event struct { + MainnetExitRoot ethCommon.Hash + RollupExitRoot ethCommon.Hash + ParentHash ethCommon.Hash + Timestamp uint64 +} + +type L1InfoTreeLeaf struct { + L1InfoTreeRoot ethCommon.Hash + L1InfoTreeIndex uint32 + PreviousBlockHash ethCommon.Hash + BlockNumber uint64 + Timestamp uint64 + MainnetExitRoot ethCommon.Hash + RollupExitRoot ethCommon.Hash + GlobalExitRoot ethCommon.Hash +} + +type storeLeaf struct { + MainnetExitRoot ethCommon.Hash + RollupExitRoot ethCommon.Hash + ParentHash ethCommon.Hash + InfoRoot ethCommon.Hash + Index uint32 + Timestamp uint64 + BlockNumber uint64 +} + +type blockWithLeafs struct { + // inclusive + FirstIndex uint32 + // not inclusive + LastIndex uint32 +} + +func (l *storeLeaf) GlobalExitRoot() ethCommon.Hash { + var gerBytes [32]byte + hasher := sha3.NewLegacyKeccak256() + hasher.Write(l.MainnetExitRoot[:]) + hasher.Write(l.RollupExitRoot[:]) + copy(gerBytes[:], hasher.Sum(nil)) + return gerBytes +} + +func tableCfgFunc(defaultBuckets kv.TableCfg) kv.TableCfg { + return kv.TableCfg{ + rootTable: {}, + indexTable: {}, + infoTable: {}, + blockTable: {}, + lastBlockTable: {}, + } +} + +func newProcessor(ctx context.Context, dbPath string) (*processor, error) { + db, err := mdbx.NewMDBX(nil). + Path(dbPath). + WithTableCfg(tableCfgFunc). + Open() + if err != nil { + return nil, err + } + p := &processor{ + db: db, + } + + tx, err := p.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + leaves, err := p.getAllLeavesHashed(tx) + if err != nil { + return nil, err + } + tree, err := l1infotree.NewL1InfoTree(treeHeight, leaves) + if err != nil { + return nil, err + } + p.tree = tree + return p, nil +} + +func (p *processor) getAllLeavesHashed(tx kv.Tx) ([][32]byte, error) { + // TODO: same coment about refactor that appears at ComputeMerkleProofByIndex + index, err := p.getLastIndex(tx) + if err == ErrNotFound || index == 0 { + return nil, nil + } + if err != nil { + return nil, err + } + + return p.getHashedLeaves(tx, index) +} + +func (p *processor) ComputeMerkleProofByIndex(ctx context.Context, index uint32) ([][32]byte, ethCommon.Hash, error) { + // TODO: refactor the tree to store the nodes so it's not neede to load all the leaves and compute the tree + // every time this function is called. Since it's not a sparse MT, an alternative could be to store the proofs + // as part of the info + tx, err := p.db.BeginRo(ctx) + if err != nil { + return nil, ethCommon.Hash{}, err + } + defer tx.Rollback() + + leaves, err := p.getHashedLeaves(tx, index) + if err != nil { + return nil, ethCommon.Hash{}, err + } + return p.tree.ComputeMerkleProof(index, leaves) +} + +func (p *processor) getHashedLeaves(tx kv.Tx, untilIndex uint32) ([][32]byte, error) { + leaves := [][32]byte{} + for i := uint32(0); i <= untilIndex; i++ { + info, err := p.getInfoByIndexWithTx(tx, i) + if err != nil { + return nil, err + } + h := l1infotree.HashLeafData(info.GlobalExitRoot, info.PreviousBlockHash, info.Timestamp) + leaves = append(leaves, h) + } + return leaves, nil +} + +func (p *processor) ComputeMerkleProofByRoot(ctx context.Context, root ethCommon.Hash) ([][32]byte, ethCommon.Hash, error) { + info, err := p.GetInfoByRoot(ctx, root) + if err != nil { + return nil, ethCommon.Hash{}, err + } + return p.ComputeMerkleProofByIndex(ctx, info.L1InfoTreeIndex) +} + +func (p *processor) GetInfoByRoot(ctx context.Context, root ethCommon.Hash) (*L1InfoTreeLeaf, error) { + tx, err := p.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + hash, err := tx.GetOne(rootTable, root[:]) + if err != nil { + return nil, err + } + if hash == nil { + return nil, ErrNotFound + } + return p.getInfoByHashWithTx(tx, hash) +} + +// GetLatestInfoUntilBlock returns the most recent L1InfoTreeLeaf that occurred before or at blockNum. +// If the blockNum has not been processed yet the error ErrBlockNotProcessed will be returned +func (p *processor) GetLatestInfoUntilBlock(ctx context.Context, blockNum uint64) (*L1InfoTreeLeaf, error) { + if blockNum == 0 { + return nil, ErrNoBlock0 + } + tx, err := p.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + lpb, err := p.getLastProcessedBlockWithTx(tx) + if err != nil { + return nil, err + } + if lpb < blockNum { + return nil, ErrBlockNotProcessed + } + iter, err := tx.RangeDescend(blockTable, common.Uint64ToBytes(blockNum), common.Uint64ToBytes(0), 1) + if err != nil { + return nil, fmt.Errorf( + "error calling RangeDescend(blockTable, %d, 0, 1): %w", blockNum, err, + ) + } + k, v, err := iter.Next() + if err != nil { + return nil, err + } + if k == nil { + return nil, ErrNotFound + } + blk := blockWithLeafs{} + if err := json.Unmarshal(v, &blk); err != nil { + return nil, err + } + hash, err := tx.GetOne(indexTable, common.Uint32ToBytes(blk.LastIndex-1)) + if err != nil { + return nil, err + } + return p.getInfoByHashWithTx(tx, hash) +} + +func (p *processor) GetInfoByIndex(ctx context.Context, index uint32) (*L1InfoTreeLeaf, error) { + tx, err := p.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + return p.getInfoByIndexWithTx(tx, index) +} + +func (p *processor) getInfoByIndexWithTx(tx kv.Tx, index uint32) (*L1InfoTreeLeaf, error) { + hash, err := tx.GetOne(indexTable, common.Uint32ToBytes(index)) + if err != nil { + return nil, err + } + if hash == nil { + return nil, ErrNotFound + } + return p.getInfoByHashWithTx(tx, hash) +} + +func (p *processor) GetInfoByHash(ctx context.Context, hash []byte) (*L1InfoTreeLeaf, error) { + tx, err := p.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + return p.getInfoByHashWithTx(tx, hash) +} + +func (p *processor) getInfoByHashWithTx(tx kv.Tx, hash []byte) (*L1InfoTreeLeaf, error) { + infoBytes, err := tx.GetOne(infoTable, hash) + if err != nil { + return nil, err + } + if infoBytes == nil { + return nil, ErrNotFound + } + var info storeLeaf + if err := json.Unmarshal(infoBytes, &info); err != nil { + return nil, err + } + + return &L1InfoTreeLeaf{ + L1InfoTreeRoot: info.InfoRoot, + L1InfoTreeIndex: info.Index, + PreviousBlockHash: info.ParentHash, + BlockNumber: info.BlockNumber, + Timestamp: info.Timestamp, + MainnetExitRoot: info.MainnetExitRoot, + RollupExitRoot: info.RollupExitRoot, + GlobalExitRoot: info.GlobalExitRoot(), + }, nil +} + +func (p *processor) GetLastProcessedBlock(ctx context.Context) (uint64, error) { + tx, err := p.db.BeginRo(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback() + return p.getLastProcessedBlockWithTx(tx) +} + +func (p *processor) getLastProcessedBlockWithTx(tx kv.Tx) (uint64, error) { + blockNumBytes, err := tx.GetOne(lastBlockTable, lastBlockKey) + if err != nil { + return 0, err + } else if blockNumBytes == nil { + return 0, nil + } + return common.BytesToUint64(blockNumBytes), nil +} + +func (p *processor) Reorg(firstReorgedBlock uint64) error { + tx, err := p.db.BeginRw(context.Background()) + if err != nil { + return err + } + c, err := tx.Cursor(blockTable) + if err != nil { + return err + } + defer c.Close() + firstKey := common.Uint64ToBytes(firstReorgedBlock) + for blkKey, blkValue, err := c.Seek(firstKey); blkKey != nil; blkKey, blkValue, err = c.Next() { + if err != nil { + tx.Rollback() + return err + } + var blk blockWithLeafs + if err := json.Unmarshal(blkValue, &blk); err != nil { + tx.Rollback() + return err + } + for i := blk.FirstIndex; i < blk.LastIndex; i++ { + if err := p.deleteLeaf(tx, i); err != nil { + tx.Rollback() + return err + } + } + if err := tx.Delete(blockTable, blkKey); err != nil { + tx.Rollback() + return err + } + } + if err := p.updateLastProcessedBlock(tx, firstReorgedBlock-1); err != nil { + tx.Rollback() + return err + } + leaves, err := p.getAllLeavesHashed(tx) + if err != nil { + tx.Rollback() + return err + } + tree, err := l1infotree.NewL1InfoTree(treeHeight, leaves) + if err != nil { + tx.Rollback() + return err + } + p.tree = tree + return tx.Commit() +} + +func (p *processor) deleteLeaf(tx kv.RwTx, index uint32) error { + // TODO: do we need to do something with p.tree here? + // Get leaf info to delete all relations + hash, err := tx.GetOne(indexTable, common.Uint32ToBytes(index)) + if err != nil { + return err + } + if hash == nil { + return ErrNotFound + } + infoBytes, err := tx.GetOne(infoTable, hash) + if err != nil { + return err + } + if infoBytes == nil { + return ErrNotFound + } + var info storeLeaf + if err := json.Unmarshal(infoBytes, &info); err != nil { + return err + } + + // Delete + if err := tx.Delete(rootTable, info.InfoRoot[:]); err != nil { + return err + } + if err := tx.Delete(indexTable, common.Uint32ToBytes(index)); err != nil { + return err + } + if err := tx.Delete(infoTable, hash); err != nil { + return err + } + return nil +} + +// ProcessBlock process the leafs of the L1 info tree found on a block +// this function can be called without leafs with the intention to track the last processed block +func (p *processor) ProcessBlock(b sync.Block) error { + tx, err := p.db.BeginRw(context.Background()) + if err != nil { + return err + } + events := make([]Event, len(b.Events)) + if len(b.Events) > 0 { + var initialIndex uint32 + lastIndex, err := p.getLastIndex(tx) + if err == ErrNotFound { + initialIndex = 0 + } else if err != nil { + tx.Rollback() + return err + } else { + initialIndex = lastIndex + 1 + } + for i, e := range b.Events { + event := e.(Event) + events = append(events, event) + leafToStore := storeLeaf{ + Index: initialIndex + uint32(i), + MainnetExitRoot: event.MainnetExitRoot, + RollupExitRoot: event.RollupExitRoot, + ParentHash: event.ParentHash, + Timestamp: event.Timestamp, + BlockNumber: b.Num, + } + if err := p.addLeaf(tx, leafToStore); err != nil { + tx.Rollback() + return err + } + } + bwl := blockWithLeafs{ + FirstIndex: initialIndex, + LastIndex: initialIndex + uint32(len(b.Events)), + } + blockValue, err := json.Marshal(bwl) + if err != nil { + tx.Rollback() + return err + } + if err := tx.Put(blockTable, common.Uint64ToBytes(b.Num), blockValue); err != nil { + tx.Rollback() + return err + } + } + if err := p.updateLastProcessedBlock(tx, b.Num); err != nil { + tx.Rollback() + return err + } + log.Debugf("block %d processed with events: %+v", b.Num, events) + return tx.Commit() +} + +func (p *processor) getLastIndex(tx kv.Tx) (uint32, error) { + bNum, err := p.getLastProcessedBlockWithTx(tx) + if err != nil { + return 0, err + } + if bNum == 0 { + return 0, nil + } + iter, err := tx.RangeDescend(blockTable, common.Uint64ToBytes(bNum), common.Uint64ToBytes(0), 1) + if err != nil { + return 0, err + } + _, blkBytes, err := iter.Next() + if err != nil { + return 0, err + } + if blkBytes == nil { + return 0, ErrNotFound + } + var blk blockWithLeafs + if err := json.Unmarshal(blkBytes, &blk); err != nil { + return 0, err + } + return blk.LastIndex - 1, nil +} + +func (p *processor) addLeaf(tx kv.RwTx, leaf storeLeaf) error { + // Update tree + hash := l1infotree.HashLeafData(leaf.GlobalExitRoot(), leaf.ParentHash, leaf.Timestamp) + root, err := p.tree.AddLeaf(leaf.Index, hash) + if err != nil { + return err + } + leaf.InfoRoot = root + // store info + leafValue, err := json.Marshal(leaf) + if err != nil { + return err + } + if err := tx.Put(infoTable, hash[:], leafValue); err != nil { + return err + } + // store index relation + if err := tx.Put(indexTable, common.Uint32ToBytes(leaf.Index), hash[:]); err != nil { + return err + } + // store root relation + if err := tx.Put(rootTable, root.Bytes(), hash[:]); err != nil { + return err + } + return nil +} + +func (p *processor) updateLastProcessedBlock(tx kv.RwTx, blockNum uint64) error { + blockNumBytes := common.Uint64ToBytes(blockNum) + return tx.Put(lastBlockTable, lastBlockKey, blockNumBytes) +} diff --git a/l1infotreesync/processor_test.go b/l1infotreesync/processor_test.go new file mode 100644 index 00000000..01550f31 --- /dev/null +++ b/l1infotreesync/processor_test.go @@ -0,0 +1,3 @@ +package l1infotreesync + +// TODO: add unit test diff --git a/localbridgesync/downloader.go b/localbridgesync/downloader.go index 9763f818..5b6ab8f6 100644 --- a/localbridgesync/downloader.go +++ b/localbridgesync/downloader.go @@ -1,14 +1,13 @@ package localbridgesync import ( - "context" + "fmt" "math/big" "time" "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridge" "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridgev2" - "github.com/0xPolygon/cdk/etherman" - "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/sync" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -33,164 +32,26 @@ type EthClienter interface { bind.ContractBackend } -type downloaderInterface interface { - waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) (newLastBlock uint64) - getEventsByBlockRange(ctx context.Context, fromBlock, toBlock uint64) []block - getLogs(ctx context.Context, fromBlock, toBlock uint64) []types.Log - appendLog(b *block, l types.Log) - getBlockHeader(ctx context.Context, blockNum uint64) blockHeader -} - -type downloader struct { - syncBlockChunkSize uint64 - downloaderInterface -} - -func newDownloader( - bridgeAddr common.Address, - ethClient EthClienter, - syncBlockChunkSize uint64, - blockFinalityType etherman.BlockNumberFinality, -) (*downloader, error) { - bridgeContractV1, err := polygonzkevmbridge.NewPolygonzkevmbridge(bridgeAddr, ethClient) +func buildAppender(client EthClienter, bridge common.Address) (sync.LogAppenderMap, error) { + bridgeContractV1, err := polygonzkevmbridge.NewPolygonzkevmbridge(bridge, client) if err != nil { return nil, err } - bridgeContractV2, err := polygonzkevmbridgev2.NewPolygonzkevmbridgev2(bridgeAddr, ethClient) + bridgeContractV2, err := polygonzkevmbridgev2.NewPolygonzkevmbridgev2(bridge, client) if err != nil { return nil, err } - finality, err := blockFinalityType.ToBlockNum() - if err != nil { - return nil, err - } - return &downloader{ - syncBlockChunkSize: syncBlockChunkSize, - downloaderInterface: &downloaderImplementation{ - bridgeAddr: bridgeAddr, - bridgeContractV1: bridgeContractV1, - bridgeContractV2: bridgeContractV2, - ethClient: ethClient, - blockFinality: finality, - }, - }, nil -} - -func (d *downloader) download(ctx context.Context, fromBlock uint64, downloadedCh chan block) { - lastBlock := d.waitForNewBlocks(ctx, 0) - for { - select { - case <-ctx.Done(): - log.Debug("closing channel") - close(downloadedCh) - return - default: - } - toBlock := fromBlock + d.syncBlockChunkSize - if toBlock > lastBlock { - toBlock = lastBlock - } - if fromBlock > toBlock { - log.Debug("waiting for new blocks, last block ", toBlock) - lastBlock = d.waitForNewBlocks(ctx, toBlock) - continue - } - log.Debugf("getting events from blocks %d to %d", fromBlock, toBlock) - blocks := d.getEventsByBlockRange(ctx, fromBlock, toBlock) - for _, b := range blocks { - log.Debugf("sending block %d to the driver (with events)", b.Num) - downloadedCh <- b - } - if len(blocks) == 0 || blocks[len(blocks)-1].Num < toBlock { - // Indicate the last downloaded block if there are not events on it - log.Debugf("sending block %d to the driver (without evvents)", toBlock) - downloadedCh <- block{ - blockHeader: d.getBlockHeader(ctx, toBlock), - } - } - fromBlock = toBlock + 1 - } -} - -type downloaderImplementation struct { - bridgeAddr common.Address - bridgeContractV1 *polygonzkevmbridge.Polygonzkevmbridge - bridgeContractV2 *polygonzkevmbridgev2.Polygonzkevmbridgev2 - ethClient EthClienter - blockFinality *big.Int -} - -func (d *downloaderImplementation) waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) (newLastBlock uint64) { - attempts := 0 - for { - header, err := d.ethClient.HeaderByNumber(ctx, d.blockFinality) - if err != nil { - attempts++ - log.Error("error geting last block num from eth client: ", err) - retryHandler("waitForNewBlocks", attempts) - continue - } - if header.Number.Uint64() > lastBlockSeen { - return header.Number.Uint64() - } - time.Sleep(waitForNewBlocksPeriod) - } -} - -func (d *downloaderImplementation) getEventsByBlockRange(ctx context.Context, fromBlock, toBlock uint64) []block { - blocks := []block{} - logs := d.getLogs(ctx, fromBlock, toBlock) - for _, l := range logs { - if len(blocks) == 0 || blocks[len(blocks)-1].Num < l.BlockNumber { - blocks = append(blocks, block{ - blockHeader: blockHeader{ - Num: l.BlockNumber, - Hash: l.BlockHash, - }, - Events: []BridgeEvent{}, - }) - } - d.appendLog(&blocks[len(blocks)-1], l) - } - - return blocks -} - -func (d *downloaderImplementation) getLogs(ctx context.Context, fromBlock, toBlock uint64) []types.Log { - query := ethereum.FilterQuery{ - FromBlock: new(big.Int).SetUint64(fromBlock), - Addresses: []common.Address{d.bridgeAddr}, - Topics: [][]common.Hash{ - {bridgeEventSignature}, - {claimEventSignature}, - {claimEventSignaturePreEtrog}, - }, - ToBlock: new(big.Int).SetUint64(toBlock), - } - attempts := 0 - for { - logs, err := d.ethClient.FilterLogs(ctx, query) - if err != nil { - attempts++ - log.Error("error calling FilterLogs to eth client: ", err) - retryHandler("getLogs", attempts) - continue - } - return logs - } -} + appender := make(sync.LogAppenderMap) -func (d *downloaderImplementation) appendLog(b *block, l types.Log) { - switch l.Topics[0] { - case bridgeEventSignature: - bridge, err := d.bridgeContractV2.ParseBridgeEvent(l) + appender[bridgeEventSignature] = func(b *sync.EVMBlock, l types.Log) error { + bridge, err := bridgeContractV2.ParseBridgeEvent(l) if err != nil { - log.Fatalf( + return fmt.Errorf( "error parsing log %+v using d.bridgeContractV2.ParseBridgeEvent: %v", l, err, ) } - b.Events = append(b.Events, BridgeEvent{Bridge: &Bridge{ + b.Events = append(b.Events, Event{Bridge: &Bridge{ LeafType: bridge.LeafType, OriginNetwork: bridge.OriginNetwork, OriginAddress: bridge.OriginAddress, @@ -200,54 +61,44 @@ func (d *downloaderImplementation) appendLog(b *block, l types.Log) { Metadata: bridge.Metadata, DepositCount: bridge.DepositCount, }}) - case claimEventSignature: - claim, err := d.bridgeContractV2.ParseClaimEvent(l) + return nil + } + + appender[claimEventSignature] = func(b *sync.EVMBlock, l types.Log) error { + claim, err := bridgeContractV2.ParseClaimEvent(l) if err != nil { - log.Fatalf( + return fmt.Errorf( "error parsing log %+v using d.bridgeContractV2.ParseClaimEvent: %v", l, err, ) } - b.Events = append(b.Events, BridgeEvent{Claim: &Claim{ + b.Events = append(b.Events, Event{Claim: &Claim{ GlobalIndex: claim.GlobalIndex, OriginNetwork: claim.OriginNetwork, OriginAddress: claim.OriginAddress, DestinationAddress: claim.DestinationAddress, Amount: claim.Amount, }}) - case claimEventSignaturePreEtrog: - claim, err := d.bridgeContractV1.ParseClaimEvent(l) + return nil + } + + appender[claimEventSignature] = func(b *sync.EVMBlock, l types.Log) error { + claim, err := bridgeContractV1.ParseClaimEvent(l) if err != nil { - log.Fatalf( + return fmt.Errorf( "error parsing log %+v using d.bridgeContractV1.ParseClaimEvent: %v", l, err, ) } - b.Events = append(b.Events, BridgeEvent{Claim: &Claim{ + b.Events = append(b.Events, Event{Claim: &Claim{ GlobalIndex: big.NewInt(int64(claim.Index)), OriginNetwork: claim.OriginNetwork, OriginAddress: claim.OriginAddress, DestinationAddress: claim.DestinationAddress, Amount: claim.Amount, }}) - default: - log.Fatalf("unexpected log %+v", l) + return nil } -} -func (d *downloaderImplementation) getBlockHeader(ctx context.Context, blockNum uint64) blockHeader { - attempts := 0 - for { - header, err := d.ethClient.HeaderByNumber(ctx, big.NewInt(int64(blockNum))) - if err != nil { - attempts++ - log.Errorf("error getting block header for block %d, err: %v", blockNum, err) - retryHandler("getBlockHeader", attempts) - continue - } - return blockHeader{ - Num: header.Number.Uint64(), - Hash: header.Hash(), - } - } + return appender, nil } diff --git a/localbridgesync/driver.go b/localbridgesync/driver.go deleted file mode 100644 index 997fd215..00000000 --- a/localbridgesync/driver.go +++ /dev/null @@ -1,140 +0,0 @@ -package localbridgesync - -import ( - "context" - - "github.com/0xPolygon/cdk/log" - "github.com/0xPolygon/cdk/reorgdetector" - "github.com/ethereum/go-ethereum/common" -) - -const ( - downloadBufferSize = 1000 - reorgDetectorID = "localbridgesync" -) - -type downloaderFull interface { - downloaderInterface - download(ctx context.Context, fromBlock uint64, downloadedCh chan block) -} - -type driver struct { - reorgDetector ReorgDetector - reorgSub *reorgdetector.Subscription - processor processorInterface - downloader downloaderFull -} - -type processorInterface interface { - getLastProcessedBlock(ctx context.Context) (uint64, error) - storeBridgeEvents(blockNum uint64, events []BridgeEvent) error - reorg(firstReorgedBlock uint64) error -} - -type ReorgDetector interface { - Subscribe(id string) *reorgdetector.Subscription - AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error -} - -func newDriver( - reorgDetector ReorgDetector, - processor processorInterface, - downloader downloaderFull, -) (*driver, error) { - reorgSub := reorgDetector.Subscribe(reorgDetectorID) - return &driver{ - reorgDetector: reorgDetector, - reorgSub: reorgSub, - processor: processor, - downloader: downloader, - }, nil -} - -func (d *driver) Sync(ctx context.Context) { -reset: - var ( - lastProcessedBlock uint64 - attempts int - err error - ) - for { - lastProcessedBlock, err = d.processor.getLastProcessedBlock(ctx) - if err != nil { - attempts++ - log.Error("error geting last processed block: ", err) - retryHandler("Sync", attempts) - continue - } - break - } - cancellableCtx, cancel := context.WithCancel(ctx) - defer cancel() - - // start downloading - downloadCh := make(chan block, downloadBufferSize) - go d.downloader.download(cancellableCtx, lastProcessedBlock, downloadCh) - - for { - select { - case b := <-downloadCh: - log.Debug("handleNewBlock") - d.handleNewBlock(ctx, b) - case firstReorgedBlock := <-d.reorgSub.FirstReorgedBlock: - log.Debug("handleReorg") - d.handleReorg(cancel, downloadCh, firstReorgedBlock) - goto reset - } - } -} - -func (d *driver) handleNewBlock(ctx context.Context, b block) { - attempts := 0 - for { - err := d.reorgDetector.AddBlockToTrack(ctx, reorgDetectorID, b.Num, b.Hash) - if err != nil { - attempts++ - log.Errorf("error adding block %d to tracker: %v", b.Num, err) - retryHandler("handleNewBlock", attempts) - continue - } - break - } - attempts = 0 - for { - err := d.processor.storeBridgeEvents(b.Num, b.Events) - if err != nil { - attempts++ - log.Errorf("error processing events for blcok %d, err: ", b.Num, err) - retryHandler("handleNewBlock", attempts) - continue - } - break - } -} - -func (d *driver) handleReorg( - cancel context.CancelFunc, downloadCh chan block, firstReorgedBlock uint64, -) { - // stop downloader - cancel() - _, ok := <-downloadCh - for ok { - _, ok = <-downloadCh - } - // handle reorg - attempts := 0 - for { - err := d.processor.reorg(firstReorgedBlock) - if err != nil { - attempts++ - log.Errorf( - "error processing reorg, last valid block %d, err: %v", - firstReorgedBlock, err, - ) - retryHandler("handleReorg", attempts) - continue - } - break - } - d.reorgSub.ReorgProcessed <- true -} diff --git a/localbridgesync/e2e_test.go b/localbridgesync/e2e_test.go new file mode 100644 index 00000000..c84d8d33 --- /dev/null +++ b/localbridgesync/e2e_test.go @@ -0,0 +1,3 @@ +package localbridgesync + +// TODO: add E2E test, prolly need a mock contract diff --git a/localbridgesync/localbridgesync.go b/localbridgesync/localbridgesync.go index 42ab67d1..94e60b59 100644 --- a/localbridgesync/localbridgesync.go +++ b/localbridgesync/localbridgesync.go @@ -1,52 +1,87 @@ package localbridgesync import ( + "context" "time" "github.com/0xPolygon/cdk/etherman" - "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/sync" "github.com/ethereum/go-ethereum/common" ) +const ( + reorgDetectorID = "localbridgesync" + downloadBufferSize = 1000 +) + var ( retryAfterErrorPeriod = time.Second * 10 maxRetryAttemptsAfterError = 5 ) type LocalBridgeSync struct { - *processor - *driver + processor *processor + driver *sync.EVMDriver } func New( + ctx context.Context, dbPath string, bridge common.Address, syncBlockChunkSize uint64, blockFinalityType etherman.BlockNumberFinality, - rd ReorgDetector, + rd sync.ReorgDetector, l2Client EthClienter, + initialBlock uint64, ) (*LocalBridgeSync, error) { - p, err := newProcessor(dbPath) + processor, err := newProcessor(dbPath) if err != nil { return nil, err } - dwn, err := newDownloader(bridge, l2Client, syncBlockChunkSize, blockFinalityType) + lastProcessedBlock, err := processor.GetLastProcessedBlock(ctx) if err != nil { return nil, err } - dri, err := newDriver(rd, p, dwn) + if lastProcessedBlock < initialBlock { + err = processor.ProcessBlock(sync.Block{ + Num: initialBlock, + }) + if err != nil { + return nil, err + } + } + rh := &sync.RetryHandler{ + MaxRetryAttemptsAfterError: maxRetryAttemptsAfterError, + RetryAfterErrorPeriod: retryAfterErrorPeriod, + } + + appender, err := buildAppender(l2Client, bridge) + if err != nil { + return nil, err + } + downloader, err := sync.NewEVMDownloader( + l2Client, + syncBlockChunkSize, + blockFinalityType, + waitForNewBlocksPeriod, + appender, + []common.Address{bridge}, + rh, + ) if err != nil { return nil, err } - return &LocalBridgeSync{p, dri}, nil -} -func retryHandler(funcName string, attempts int) { - if attempts >= maxRetryAttemptsAfterError { - log.Fatalf( - "%s failed too many times (%d)", - funcName, maxRetryAttemptsAfterError, - ) + driver, err := sync.NewEVMDriver(rd, processor, downloader, reorgDetectorID, downloadBufferSize, rh) + if err != nil { + return nil, err } - time.Sleep(retryAfterErrorPeriod) + return &LocalBridgeSync{ + processor: processor, + driver: driver, + }, nil +} + +func (s *LocalBridgeSync) Start(ctx context.Context) { + s.driver.Sync(ctx) } diff --git a/localbridgesync/processor.go b/localbridgesync/processor.go index 69c885b8..5d644a9a 100644 --- a/localbridgesync/processor.go +++ b/localbridgesync/processor.go @@ -4,8 +4,11 @@ import ( "context" "encoding/json" "errors" + "math/big" "github.com/0xPolygon/cdk/common" + "github.com/0xPolygon/cdk/sync" + ethCommon "github.com/ethereum/go-ethereum/common" "github.com/ledgerwatch/erigon-lib/kv" "github.com/ledgerwatch/erigon-lib/kv/mdbx" ) @@ -20,6 +23,30 @@ var ( lastBlokcKey = []byte("lb") ) +type Bridge struct { + LeafType uint8 + OriginNetwork uint32 + OriginAddress ethCommon.Address + DestinationNetwork uint32 + DestinationAddress ethCommon.Address + Amount *big.Int + Metadata []byte + DepositCount uint32 +} + +type Claim struct { + GlobalIndex *big.Int + OriginNetwork uint32 + OriginAddress ethCommon.Address + DestinationAddress ethCommon.Address + Amount *big.Int +} + +type Event struct { + Bridge *Bridge + Claim *Claim +} + type processor struct { db kv.RwDB } @@ -48,8 +75,8 @@ func newProcessor(dbPath string) (*processor, error) { // If toBlock has not been porcessed yet, ErrBlockNotProcessed will be returned func (p *processor) GetClaimsAndBridges( ctx context.Context, fromBlock, toBlock uint64, -) ([]BridgeEvent, error) { - events := []BridgeEvent{} +) ([]Event, error) { + events := []Event{} tx, err := p.db.BeginRo(ctx) if err != nil { @@ -69,14 +96,14 @@ func (p *processor) GetClaimsAndBridges( } defer c.Close() - for k, v, err := c.Seek(common.BlockNum2Bytes(fromBlock)); k != nil; k, v, err = c.Next() { + for k, v, err := c.Seek(common.Uint64ToBytes(fromBlock)); k != nil; k, v, err = c.Next() { if err != nil { return nil, err } - if common.Bytes2BlockNum(k) > toBlock { + if common.BytesToUint64(k) > toBlock { break } - blockEvents := []BridgeEvent{} + blockEvents := []Event{} err := json.Unmarshal(v, &blockEvents) if err != nil { return nil, err @@ -87,7 +114,7 @@ func (p *processor) GetClaimsAndBridges( return events, nil } -func (p *processor) getLastProcessedBlock(ctx context.Context) (uint64, error) { +func (p *processor) GetLastProcessedBlock(ctx context.Context) (uint64, error) { tx, err := p.db.BeginRo(ctx) if err != nil { return 0, err @@ -102,11 +129,11 @@ func (p *processor) getLastProcessedBlockWithTx(tx kv.Tx) (uint64, error) { } else if blockNumBytes == nil { return 0, nil } else { - return common.Bytes2BlockNum(blockNumBytes), nil + return common.BytesToUint64(blockNumBytes), nil } } -func (p *processor) reorg(firstReorgedBlock uint64) error { +func (p *processor) Reorg(firstReorgedBlock uint64) error { tx, err := p.db.BeginRw(context.Background()) if err != nil { return err @@ -116,7 +143,7 @@ func (p *processor) reorg(firstReorgedBlock uint64) error { return err } defer c.Close() - firstKey := common.BlockNum2Bytes(firstReorgedBlock) + firstKey := common.Uint64ToBytes(firstReorgedBlock) for k, _, err := c.Seek(firstKey); k != nil; k, _, err = c.Next() { if err != nil { tx.Rollback() @@ -134,23 +161,27 @@ func (p *processor) reorg(firstReorgedBlock uint64) error { return tx.Commit() } -func (p *processor) storeBridgeEvents(blockNum uint64, events []BridgeEvent) error { +func (p *processor) ProcessBlock(block sync.Block) error { tx, err := p.db.BeginRw(context.Background()) if err != nil { return err } - if len(events) > 0 { + if len(block.Events) > 0 { + events := []Event{} + for _, e := range block.Events { + events = append(events, e.(Event)) + } value, err := json.Marshal(events) if err != nil { tx.Rollback() return err } - if err := tx.Put(eventsTable, common.BlockNum2Bytes(blockNum), value); err != nil { + if err := tx.Put(eventsTable, common.Uint64ToBytes(block.Num), value); err != nil { tx.Rollback() return err } } - if err := p.updateLastProcessedBlock(tx, blockNum); err != nil { + if err := p.updateLastProcessedBlock(tx, block.Num); err != nil { tx.Rollback() return err } @@ -158,6 +189,6 @@ func (p *processor) storeBridgeEvents(blockNum uint64, events []BridgeEvent) err } func (p *processor) updateLastProcessedBlock(tx kv.RwTx, blockNum uint64) error { - blockNumBytes := common.BlockNum2Bytes(blockNum) + blockNumBytes := common.Uint64ToBytes(blockNum) return tx.Put(lastBlockTable, lastBlokcKey, blockNumBytes) } diff --git a/localbridgesync/processor_test.go b/localbridgesync/processor_test.go index 8e6884c2..dbf0d74c 100644 --- a/localbridgesync/processor_test.go +++ b/localbridgesync/processor_test.go @@ -7,6 +7,7 @@ import ( "slices" "testing" + "github.com/0xPolygon/cdk/sync" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ) @@ -45,11 +46,10 @@ func TestProceessor(t *testing.T) { expectedEvents: nil, expectedErr: ErrBlockNotProcessed, }, - &storeBridgeEventsAction{ + &processBlockAction{ p: p, description: "block1", - blockNum: block1.Num, - events: block1.Events, + block: block1, expectedErr: nil, }, // processed: block1 @@ -75,7 +75,7 @@ func TestProceessor(t *testing.T) { ctx: context.Background(), fromBlock: 1, toBlock: 1, - expectedEvents: block1.Events, + expectedEvents: eventsToBridgeEvents(block1.Events), expectedErr: nil, }, &reorgAction{ @@ -94,19 +94,17 @@ func TestProceessor(t *testing.T) { expectedEvents: nil, expectedErr: ErrBlockNotProcessed, }, - &storeBridgeEventsAction{ + &processBlockAction{ p: p, description: "block1 (after it's reorged)", - blockNum: block1.Num, - events: block1.Events, + block: block1, expectedErr: nil, }, // processed: block3 - &storeBridgeEventsAction{ + &processBlockAction{ p: p, description: "block3", - blockNum: block3.Num, - events: block3.Events, + block: block3, expectedErr: nil, }, // processed: block1, block3 @@ -123,17 +121,20 @@ func TestProceessor(t *testing.T) { ctx: context.Background(), fromBlock: 2, toBlock: 2, - expectedEvents: []BridgeEvent{}, + expectedEvents: []Event{}, expectedErr: nil, }, &getClaimsAndBridgesAction{ - p: p, - description: "after block3: range 1, 3", - ctx: context.Background(), - fromBlock: 1, - toBlock: 3, - expectedEvents: append(block1.Events, block3.Events...), - expectedErr: nil, + p: p, + description: "after block3: range 1, 3", + ctx: context.Background(), + fromBlock: 1, + toBlock: 3, + expectedEvents: append( + eventsToBridgeEvents(block1.Events), + eventsToBridgeEvents(block3.Events)..., + ), + expectedErr: nil, }, &reorgAction{ p: p, @@ -162,27 +163,24 @@ func TestProceessor(t *testing.T) { expectedLastProcessedBlock: 1, expectedErr: nil, }, - &storeBridgeEventsAction{ + &processBlockAction{ p: p, description: "block3 after reorg", - blockNum: block3.Num, - events: block3.Events, + block: block3, expectedErr: nil, }, // processed: block1, block3 - &storeBridgeEventsAction{ + &processBlockAction{ p: p, description: "block4", - blockNum: block4.Num, - events: block4.Events, + block: block4, expectedErr: nil, }, // processed: block1, block3, block4 - &storeBridgeEventsAction{ + &processBlockAction{ p: p, description: "block5", - blockNum: block5.Num, - events: block5.Events, + block: block5, expectedErr: nil, }, // processed: block1, block3, block4, block5 @@ -194,22 +192,28 @@ func TestProceessor(t *testing.T) { expectedErr: nil, }, &getClaimsAndBridgesAction{ - p: p, - description: "after block5: range 1, 3", - ctx: context.Background(), - fromBlock: 1, - toBlock: 3, - expectedEvents: append(block1.Events, block3.Events...), - expectedErr: nil, + p: p, + description: "after block5: range 1, 3", + ctx: context.Background(), + fromBlock: 1, + toBlock: 3, + expectedEvents: append( + eventsToBridgeEvents(block1.Events), + eventsToBridgeEvents(block3.Events)..., + ), + expectedErr: nil, }, &getClaimsAndBridgesAction{ - p: p, - description: "after block5: range 4, 5", - ctx: context.Background(), - fromBlock: 4, - toBlock: 5, - expectedEvents: append(block4.Events, block5.Events...), - expectedErr: nil, + p: p, + description: "after block5: range 4, 5", + ctx: context.Background(), + fromBlock: 4, + toBlock: 5, + expectedEvents: append( + eventsToBridgeEvents(block4.Events), + eventsToBridgeEvents(block5.Events)..., + ), + expectedErr: nil, }, &getClaimsAndBridgesAction{ p: p, @@ -218,10 +222,10 @@ func TestProceessor(t *testing.T) { fromBlock: 0, toBlock: 5, expectedEvents: slices.Concat( - block1.Events, - block3.Events, - block4.Events, - block5.Events, + eventsToBridgeEvents(block1.Events), + eventsToBridgeEvents(block3.Events), + eventsToBridgeEvents(block4.Events), + eventsToBridgeEvents(block5.Events), ), expectedErr: nil, }, @@ -237,13 +241,10 @@ func TestProceessor(t *testing.T) { // blocks var ( - block1 = block{ - blockHeader: blockHeader{ - Num: 1, - Hash: common.HexToHash("01"), - }, - Events: []BridgeEvent{ - {Bridge: &Bridge{ + block1 = sync.Block{ + Num: 1, + Events: []interface{}{ + Event{Bridge: &Bridge{ LeafType: 1, OriginNetwork: 1, OriginAddress: common.HexToAddress("01"), @@ -253,7 +254,7 @@ var ( Metadata: common.Hex2Bytes("01"), DepositCount: 1, }}, - {Claim: &Claim{ + Event{Claim: &Claim{ GlobalIndex: big.NewInt(1), OriginNetwork: 1, OriginAddress: common.HexToAddress("01"), @@ -262,13 +263,10 @@ var ( }}, }, } - block3 = block{ - blockHeader: blockHeader{ - Num: 3, - Hash: common.HexToHash("02"), - }, - Events: []BridgeEvent{ - {Bridge: &Bridge{ + block3 = sync.Block{ + Num: 3, + Events: []interface{}{ + Event{Bridge: &Bridge{ LeafType: 2, OriginNetwork: 2, OriginAddress: common.HexToAddress("02"), @@ -278,7 +276,7 @@ var ( Metadata: common.Hex2Bytes("02"), DepositCount: 2, }}, - {Bridge: &Bridge{ + Event{Bridge: &Bridge{ LeafType: 3, OriginNetwork: 3, OriginAddress: common.HexToAddress("03"), @@ -290,27 +288,21 @@ var ( }}, }, } - block4 = block{ - blockHeader: blockHeader{ - Num: 4, - Hash: common.HexToHash("03"), - }, - Events: []BridgeEvent{}, + block4 = sync.Block{ + Num: 4, + Events: []interface{}{}, } - block5 = block{ - blockHeader: blockHeader{ - Num: 5, - Hash: common.HexToHash("04"), - }, - Events: []BridgeEvent{ - {Claim: &Claim{ + block5 = sync.Block{ + Num: 5, + Events: []interface{}{ + Event{Claim: &Claim{ GlobalIndex: big.NewInt(4), OriginNetwork: 4, OriginAddress: common.HexToAddress("04"), DestinationAddress: common.HexToAddress("04"), Amount: big.NewInt(4), }}, - {Claim: &Claim{ + Event{Claim: &Claim{ GlobalIndex: big.NewInt(5), OriginNetwork: 5, OriginAddress: common.HexToAddress("05"), @@ -337,7 +329,7 @@ type getClaimsAndBridgesAction struct { ctx context.Context fromBlock uint64 toBlock uint64 - expectedEvents []BridgeEvent + expectedEvents []Event expectedErr error } @@ -374,7 +366,7 @@ func (a *getLastProcessedBlockAction) desc() string { } func (a *getLastProcessedBlockAction) execute(t *testing.T) { - actualLastProcessedBlock, actualErr := a.p.getLastProcessedBlock(a.ctx) + actualLastProcessedBlock, actualErr := a.p.GetLastProcessedBlock(a.ctx) require.Equal(t, a.expectedLastProcessedBlock, actualLastProcessedBlock) require.Equal(t, a.expectedErr, actualErr) } @@ -397,29 +389,36 @@ func (a *reorgAction) desc() string { } func (a *reorgAction) execute(t *testing.T) { - actualErr := a.p.reorg(a.firstReorgedBlock) + actualErr := a.p.Reorg(a.firstReorgedBlock) require.Equal(t, a.expectedErr, actualErr) } // storeBridgeEvents -type storeBridgeEventsAction struct { +type processBlockAction struct { p *processor description string - blockNum uint64 - events []BridgeEvent + block sync.Block expectedErr error } -func (a *storeBridgeEventsAction) method() string { +func (a *processBlockAction) method() string { return "storeBridgeEvents" } -func (a *storeBridgeEventsAction) desc() string { +func (a *processBlockAction) desc() string { return a.description } -func (a *storeBridgeEventsAction) execute(t *testing.T) { - actualErr := a.p.storeBridgeEvents(a.blockNum, a.events) +func (a *processBlockAction) execute(t *testing.T) { + actualErr := a.p.ProcessBlock(a.block) require.Equal(t, a.expectedErr, actualErr) } + +func eventsToBridgeEvents(events []interface{}) []Event { + bridgeEvents := []Event{} + for _, event := range events { + bridgeEvents = append(bridgeEvents, event.(Event)) + } + return bridgeEvents +} diff --git a/localbridgesync/types.go b/localbridgesync/types.go deleted file mode 100644 index 3a6a508e..00000000 --- a/localbridgesync/types.go +++ /dev/null @@ -1,42 +0,0 @@ -package localbridgesync - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -type Bridge struct { - LeafType uint8 - OriginNetwork uint32 - OriginAddress common.Address - DestinationNetwork uint32 - DestinationAddress common.Address - Amount *big.Int - Metadata []byte - DepositCount uint32 -} - -type Claim struct { - // TODO: pre uLxLy there was Index instead of GlobalIndex, should we treat this differently? - GlobalIndex *big.Int - OriginNetwork uint32 - OriginAddress common.Address - DestinationAddress common.Address - Amount *big.Int -} - -type BridgeEvent struct { - Bridge *Bridge - Claim *Claim -} - -type block struct { - blockHeader - Events []BridgeEvent -} - -type blockHeader struct { - Num uint64 - Hash common.Hash -} diff --git a/reorgdetector/mock_eth_client.go b/reorgdetector/mock_eth_client.go index add883f6..85376cc4 100644 --- a/reorgdetector/mock_eth_client.go +++ b/reorgdetector/mock_eth_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package reorgdetector @@ -20,10 +20,6 @@ type EthClientMock struct { func (_m *EthClientMock) 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 { @@ -48,10 +44,6 @@ func (_m *EthClientMock) BlockNumber(ctx context.Context) (uint64, error) { func (_m *EthClientMock) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { ret := _m.Called(ctx, number) - if len(ret) == 0 { - panic("no return value specified for HeaderByNumber") - } - var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { @@ -74,12 +66,13 @@ func (_m *EthClientMock) HeaderByNumber(ctx context.Context, number *big.Int) (* return r0, r1 } -// NewEthClientMock creates a new instance of EthClientMock. 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 NewEthClientMock(t interface { +type mockConstructorTestingTNewEthClientMock interface { mock.TestingT Cleanup(func()) -}) *EthClientMock { +} + +// NewEthClientMock creates a new instance of EthClientMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEthClientMock(t mockConstructorTestingTNewEthClientMock) *EthClientMock { mock := &EthClientMock{} mock.Mock.Test(t) diff --git a/reorgdetector/reorgdetector.go b/reorgdetector/reorgdetector.go index 4b5a963b..c0b0caa2 100644 --- a/reorgdetector/reorgdetector.go +++ b/reorgdetector/reorgdetector.go @@ -140,6 +140,10 @@ type ReorgDetector struct { waitPeriodBlockAdder time.Duration } +type Config struct { + DBPath string `mapstructure:"DBPath"` +} + // New creates a new instance of ReorgDetector func New(ctx context.Context, client EthClient, dbPath string) (*ReorgDetector, error) { db, err := mdbx.NewMDBX(nil). diff --git a/sync/common.go b/sync/common.go new file mode 100644 index 00000000..4fe049d5 --- /dev/null +++ b/sync/common.go @@ -0,0 +1,21 @@ +package sync + +import ( + "log" + "time" +) + +type RetryHandler struct { + RetryAfterErrorPeriod time.Duration + MaxRetryAttemptsAfterError int +} + +func (h *RetryHandler) Handle(funcName string, attempts int) { + if h.MaxRetryAttemptsAfterError > -1 && attempts >= h.MaxRetryAttemptsAfterError { + log.Fatalf( + "%s failed too many times (%d)", + funcName, h.MaxRetryAttemptsAfterError, + ) + } + time.Sleep(h.RetryAfterErrorPeriod) +} diff --git a/sync/driver.go b/sync/driver.go new file mode 100644 index 00000000..bd066ba1 --- /dev/null +++ b/sync/driver.go @@ -0,0 +1,14 @@ +package sync + +import "context" + +type Block struct { + Num uint64 + Events []interface{} +} + +type ProcessorInterface interface { + GetLastProcessedBlock(ctx context.Context) (uint64, error) + ProcessBlock(block Block) error + Reorg(firstReorgedBlock uint64) error +} diff --git a/sync/evmdownloader.go b/sync/evmdownloader.go new file mode 100644 index 00000000..ad452856 --- /dev/null +++ b/sync/evmdownloader.go @@ -0,0 +1,208 @@ +package sync + +import ( + "context" + "math/big" + "time" + + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type EthClienter interface { + ethereum.LogFilterer + ethereum.BlockNumberReader + ethereum.ChainReader + bind.ContractBackend +} + +type evmDownloaderInterface interface { + waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) (newLastBlock uint64) + getEventsByBlockRange(ctx context.Context, fromBlock, toBlock uint64) []EVMBlock + getLogs(ctx context.Context, fromBlock, toBlock uint64) []types.Log + getBlockHeader(ctx context.Context, blockNum uint64) EVMBlockHeader +} + +type LogAppenderMap map[common.Hash]func(b *EVMBlock, l types.Log) error + +type EVMDownloader struct { + syncBlockChunkSize uint64 + evmDownloaderInterface +} + +func NewEVMDownloader( + ethClient EthClienter, + syncBlockChunkSize uint64, + blockFinalityType etherman.BlockNumberFinality, + waitForNewBlocksPeriod time.Duration, + appender LogAppenderMap, + adressessToQuery []common.Address, + rh *RetryHandler, +) (*EVMDownloader, error) { + finality, err := blockFinalityType.ToBlockNum() + if err != nil { + return nil, err + } + topicsToQuery := [][]common.Hash{} + for topic := range appender { + topicsToQuery = append(topicsToQuery, []common.Hash{topic}) + } + return &EVMDownloader{ + syncBlockChunkSize: syncBlockChunkSize, + evmDownloaderInterface: &downloaderImplementation{ + ethClient: ethClient, + blockFinality: finality, + waitForNewBlocksPeriod: waitForNewBlocksPeriod, + appender: appender, + topicsToQuery: topicsToQuery, + adressessToQuery: adressessToQuery, + rh: rh, + }, + }, nil +} + +func (d *EVMDownloader) download(ctx context.Context, fromBlock uint64, downloadedCh chan EVMBlock) { + lastBlock := d.waitForNewBlocks(ctx, 0) + for { + select { + case <-ctx.Done(): + log.Debug("closing channel") + close(downloadedCh) + return + default: + } + toBlock := fromBlock + d.syncBlockChunkSize + if toBlock > lastBlock { + toBlock = lastBlock + } + if fromBlock > toBlock { + log.Debug("waiting for new blocks, last block ", toBlock) + lastBlock = d.waitForNewBlocks(ctx, toBlock) + continue + } + log.Debugf("getting events from blocks %d to %d", fromBlock, toBlock) + blocks := d.getEventsByBlockRange(ctx, fromBlock, toBlock) + for _, b := range blocks { + log.Debugf("sending block %d to the driver (with events)", b.Num) + downloadedCh <- b + } + if len(blocks) == 0 || blocks[len(blocks)-1].Num < toBlock { + // Indicate the last downloaded block if there are not events on it + log.Debugf("sending block %d to the driver (without evvents)", toBlock) + downloadedCh <- EVMBlock{ + EVMBlockHeader: d.getBlockHeader(ctx, toBlock), + } + } + fromBlock = toBlock + 1 + } +} + +type downloaderImplementation struct { + ethClient EthClienter + blockFinality *big.Int + waitForNewBlocksPeriod time.Duration + appender LogAppenderMap + topicsToQuery [][]common.Hash + adressessToQuery []common.Address + rh *RetryHandler +} + +func (d *downloaderImplementation) waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) (newLastBlock uint64) { + attempts := 0 + ticker := time.NewTicker(d.waitForNewBlocksPeriod) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + log.Info("context cancelled") + return lastBlockSeen + case <-ticker.C: + header, err := d.ethClient.HeaderByNumber(ctx, d.blockFinality) + if err != nil { + attempts++ + log.Error("error getting last block num from eth client: ", err) + d.rh.Handle("waitForNewBlocks", attempts) + continue + } + if header.Number.Uint64() > lastBlockSeen { + return header.Number.Uint64() + } + } + } +} + +func (d *downloaderImplementation) getEventsByBlockRange(ctx context.Context, fromBlock, toBlock uint64) []EVMBlock { + blocks := []EVMBlock{} + logs := d.getLogs(ctx, fromBlock, toBlock) + for _, l := range logs { + if len(blocks) == 0 || blocks[len(blocks)-1].Num < l.BlockNumber { + b := d.getBlockHeader(ctx, l.BlockNumber) + blocks = append(blocks, EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ + Num: l.BlockNumber, + Hash: l.BlockHash, + Timestamp: b.Timestamp, + ParentHash: b.ParentHash, + }, + Events: []interface{}{}, + }) + } + + for { + attempts := 0 + err := d.appender[l.Topics[0]](&blocks[len(blocks)-1], l) + if err != nil { + attempts++ + log.Error("error trying to append log: ", err) + d.rh.Handle("getLogs", attempts) + continue + } + break + } + } + + return blocks +} + +func (d *downloaderImplementation) getLogs(ctx context.Context, fromBlock, toBlock uint64) []types.Log { + query := ethereum.FilterQuery{ + FromBlock: new(big.Int).SetUint64(fromBlock), + Addresses: d.adressessToQuery, + Topics: d.topicsToQuery, + ToBlock: new(big.Int).SetUint64(toBlock), + } + attempts := 0 + for { + logs, err := d.ethClient.FilterLogs(ctx, query) + if err != nil { + attempts++ + log.Error("error calling FilterLogs to eth client: ", err) + d.rh.Handle("getLogs", attempts) + continue + } + return logs + } +} + +func (d *downloaderImplementation) getBlockHeader(ctx context.Context, blockNum uint64) EVMBlockHeader { + attempts := 0 + for { + header, err := d.ethClient.HeaderByNumber(ctx, big.NewInt(int64(blockNum))) + if err != nil { + attempts++ + log.Errorf("error getting block header for block %d, err: %v", blockNum, err) + d.rh.Handle("getBlockHeader", attempts) + continue + } + return EVMBlockHeader{ + Num: header.Number.Uint64(), + Hash: header.Hash(), + ParentHash: header.ParentHash, + Timestamp: header.Time, + } + } +} diff --git a/localbridgesync/downloader_test.go b/sync/evmdownloader_test.go similarity index 55% rename from localbridgesync/downloader_test.go rename to sync/evmdownloader_test.go index 553efbec..2f5a7ee5 100644 --- a/localbridgesync/downloader_test.go +++ b/sync/evmdownloader_test.go @@ -1,46 +1,45 @@ -package localbridgesync +package sync import ( "context" "errors" "math/big" + "strconv" "testing" "time" - "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridge" - "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridgev2" - cdkcommon "github.com/0xPolygon/cdk/common" "github.com/0xPolygon/cdk/etherman" "github.com/0xPolygon/cdk/log" "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) var ( - contractAddr = common.HexToAddress("1234567890") + contractAddr = common.HexToAddress("f00") + eventSignature = crypto.Keccak256Hash([]byte("foo")) ) const ( syncBlockChunck = uint64(10) ) +type testEvent common.Hash + func TestGetEventsByBlockRange(t *testing.T) { type testCase struct { description string inputLogs []types.Log fromBlock, toBlock uint64 - expectedBlocks []block + expectedBlocks []EVMBlock } testCases := []testCase{} - clientMock := NewL2Mock(t) ctx := context.Background() - d, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) - require.NoError(t, err) + d, clientMock := NewTestDownloader(t) // case 0: single block, no events case0 := testCase{ @@ -48,26 +47,23 @@ func TestGetEventsByBlockRange(t *testing.T) { inputLogs: []types.Log{}, fromBlock: 1, toBlock: 3, - expectedBlocks: []block{}, + expectedBlocks: []EVMBlock{}, } testCases = append(testCases, case0) // case 1: single block, single event - logC1, bridgeC1 := generateBridge(t, 3) + logC1, updateC1 := generateEvent(3) logsC1 := []types.Log{ *logC1, } - blocksC1 := []block{ + blocksC1 := []EVMBlock{ { - blockHeader: blockHeader{ - Num: logC1.BlockNumber, - Hash: logC1.BlockHash, - }, - Events: []BridgeEvent{ - { - Bridge: &bridgeC1, - }, + EVMBlockHeader: EVMBlockHeader{ + Num: logC1.BlockNumber, + Hash: logC1.BlockHash, + ParentHash: common.HexToHash("foo"), }, + Events: []interface{}{updateC1}, }, } case1 := testCase{ @@ -80,27 +76,28 @@ func TestGetEventsByBlockRange(t *testing.T) { testCases = append(testCases, case1) // case 2: single block, multiple events - logC2_1, bridgeC2_1 := generateBridge(t, 5) - logC2_2, bridgeC2_2 := generateBridge(t, 5) - logC2_3, claimC2_1 := generateClaimV1(t, 5) - logC2_4, claimC2_2 := generateClaimV2(t, 5) + logC2_1, updateC2_1 := generateEvent(5) + logC2_2, updateC2_2 := generateEvent(5) + logC2_3, updateC2_3 := generateEvent(5) + logC2_4, updateC2_4 := generateEvent(5) logsC2 := []types.Log{ *logC2_1, *logC2_2, *logC2_3, *logC2_4, } - blocksC2 := []block{ + blocksC2 := []EVMBlock{ { - blockHeader: blockHeader{ - Num: logC2_1.BlockNumber, - Hash: logC2_1.BlockHash, + EVMBlockHeader: EVMBlockHeader{ + Num: logC2_1.BlockNumber, + Hash: logC2_1.BlockHash, + ParentHash: common.HexToHash("foo"), }, - Events: []BridgeEvent{ - {Bridge: &bridgeC2_1}, - {Bridge: &bridgeC2_2}, - {Claim: &claimC2_1}, - {Claim: &claimC2_2}, + Events: []interface{}{ + updateC2_1, + updateC2_2, + updateC2_3, + updateC2_4, }, }, } @@ -114,35 +111,37 @@ func TestGetEventsByBlockRange(t *testing.T) { testCases = append(testCases, case2) // case 3: multiple blocks, some events - logC3_1, bridgeC3_1 := generateBridge(t, 7) - logC3_2, bridgeC3_2 := generateBridge(t, 7) - logC3_3, claimC3_1 := generateClaimV1(t, 8) - logC3_4, claimC3_2 := generateClaimV2(t, 8) + logC3_1, updateC3_1 := generateEvent(7) + logC3_2, updateC3_2 := generateEvent(7) + logC3_3, updateC3_3 := generateEvent(8) + logC3_4, updateC3_4 := generateEvent(8) logsC3 := []types.Log{ *logC3_1, *logC3_2, *logC3_3, *logC3_4, } - blocksC3 := []block{ + blocksC3 := []EVMBlock{ { - blockHeader: blockHeader{ - Num: logC3_1.BlockNumber, - Hash: logC3_1.BlockHash, + EVMBlockHeader: EVMBlockHeader{ + Num: logC3_1.BlockNumber, + Hash: logC3_1.BlockHash, + ParentHash: common.HexToHash("foo"), }, - Events: []BridgeEvent{ - {Bridge: &bridgeC3_1}, - {Bridge: &bridgeC3_2}, + Events: []interface{}{ + updateC3_1, + updateC3_2, }, }, { - blockHeader: blockHeader{ - Num: logC3_3.BlockNumber, - Hash: logC3_3.BlockHash, + EVMBlockHeader: EVMBlockHeader{ + Num: logC3_3.BlockNumber, + Hash: logC3_3.BlockHash, + ParentHash: common.HexToHash("foo"), }, - Events: []BridgeEvent{ - {Claim: &claimC3_1}, - {Claim: &claimC3_2}, + Events: []interface{}{ + updateC3_3, + updateC3_4, }, }, } @@ -160,114 +159,40 @@ func TestGetEventsByBlockRange(t *testing.T) { FromBlock: new(big.Int).SetUint64(tc.fromBlock), Addresses: []common.Address{contractAddr}, Topics: [][]common.Hash{ - {bridgeEventSignature}, - {claimEventSignature}, - {claimEventSignaturePreEtrog}, + {eventSignature}, }, ToBlock: new(big.Int).SetUint64(tc.toBlock), } clientMock. On("FilterLogs", mock.Anything, query). Return(tc.inputLogs, nil) + for _, b := range tc.expectedBlocks { + clientMock. + On("HeaderByNumber", mock.Anything, big.NewInt(int64(b.Num))). + Return(&types.Header{ + Number: big.NewInt(int64(b.Num)), + ParentHash: common.HexToHash("foo"), + }, nil) + } actualBlocks := d.getEventsByBlockRange(ctx, tc.fromBlock, tc.toBlock) require.Equal(t, tc.expectedBlocks, actualBlocks, tc.description) } } -func generateBridge(t *testing.T, blockNum uint32) (*types.Log, Bridge) { - b := Bridge{ - LeafType: 1, - OriginNetwork: blockNum, - OriginAddress: contractAddr, - DestinationNetwork: blockNum, - DestinationAddress: contractAddr, - Amount: big.NewInt(int64(blockNum)), - Metadata: common.Hex2Bytes("01"), - DepositCount: blockNum, - } - abi, err := polygonzkevmbridgev2.Polygonzkevmbridgev2MetaData.GetAbi() - require.NoError(t, err) - event, err := abi.EventByID(bridgeEventSignature) - require.NoError(t, err) - data, err := event.Inputs.Pack( - b.LeafType, - b.OriginNetwork, - b.OriginAddress, - b.DestinationNetwork, - b.DestinationAddress, - b.Amount, - b.Metadata, - b.DepositCount, - ) - require.NoError(t, err) - log := &types.Log{ - Address: contractAddr, - BlockNumber: uint64(blockNum), - BlockHash: common.BytesToHash(cdkcommon.BlockNum2Bytes(uint64(blockNum))), - Topics: []common.Hash{bridgeEventSignature}, - Data: data, - } - return log, b -} - -func generateClaimV1(t *testing.T, blockNum uint32) (*types.Log, Claim) { - abi, err := polygonzkevmbridge.PolygonzkevmbridgeMetaData.GetAbi() - require.NoError(t, err) - event, err := abi.EventByID(claimEventSignaturePreEtrog) - require.NoError(t, err) - return generateClaim(t, blockNum, event, true) -} - -func generateClaimV2(t *testing.T, blockNum uint32) (*types.Log, Claim) { - abi, err := polygonzkevmbridgev2.Polygonzkevmbridgev2MetaData.GetAbi() - require.NoError(t, err) - event, err := abi.EventByID(claimEventSignature) - require.NoError(t, err) - return generateClaim(t, blockNum, event, false) -} - -func generateClaim(t *testing.T, blockNum uint32, event *abi.Event, isV1 bool) (*types.Log, Claim) { - c := Claim{ - GlobalIndex: big.NewInt(int64(blockNum)), - OriginNetwork: blockNum, - OriginAddress: contractAddr, - DestinationAddress: contractAddr, - Amount: big.NewInt(int64(blockNum)), - } - var ( - data []byte - err error - signature common.Hash - ) - if isV1 { - data, err = event.Inputs.Pack( - uint32(c.GlobalIndex.Uint64()), - c.OriginNetwork, - c.OriginAddress, - c.DestinationAddress, - c.Amount, - ) - signature = claimEventSignaturePreEtrog - } else { - data, err = event.Inputs.Pack( - c.GlobalIndex, - c.OriginNetwork, - c.OriginAddress, - c.DestinationAddress, - c.Amount, - ) - signature = claimEventSignature - } - require.NoError(t, err) +func generateEvent(blockNum uint32) (*types.Log, testEvent) { + h := common.HexToHash(strconv.Itoa(int(blockNum))) log := &types.Log{ Address: contractAddr, BlockNumber: uint64(blockNum), - BlockHash: common.BytesToHash(cdkcommon.BlockNum2Bytes(uint64(blockNum))), - Topics: []common.Hash{signature}, - Data: data, + BlockHash: h, + Topics: []common.Hash{ + eventSignature, + h, + }, + Data: nil, } - return log, c + return log, testEvent(h) } func TestDownload(t *testing.T) { @@ -275,31 +200,29 @@ func TestDownload(t *testing.T) { NOTE: due to the concurrent nature of this test (the function being tested runs through a goroutine) if the mock doesn't match, the goroutine will get stuck and the test will timeout */ - d := NewDownloaderMock(t) - downloadCh := make(chan block, 1) + d := NewEVMDownloaderMock(t) + downloadCh := make(chan EVMBlock, 1) ctx := context.Background() ctx1, cancel := context.WithCancel(ctx) - expectedBlocks := []block{} - clientMock := NewL2Mock(t) - dwnldr, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) - require.NoError(t, err) - dwnldr.downloaderInterface = d + expectedBlocks := []EVMBlock{} + dwnldr, _ := NewTestDownloader(t) + dwnldr.evmDownloaderInterface = d d.On("waitForNewBlocks", mock.Anything, uint64(0)). Return(uint64(1)) // iteratiion 0: // last block is 1, download that block (no events and wait) - b1 := block{ - blockHeader: blockHeader{ + b1 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 1, Hash: common.HexToHash("01"), }, } expectedBlocks = append(expectedBlocks, b1) d.On("getEventsByBlockRange", mock.Anything, uint64(0), uint64(1)). - Return([]block{}) + Return([]EVMBlock{}) d.On("getBlockHeader", mock.Anything, uint64(1)). - Return(b1.blockHeader) + Return(b1.EVMBlockHeader) // iteration 1: wait for next block to be created d.On("waitForNewBlocks", mock.Anything, uint64(1)). @@ -307,15 +230,15 @@ func TestDownload(t *testing.T) { Return(uint64(2)).Once() // iteration 2: block 2 has events - b2 := block{ - blockHeader: blockHeader{ + b2 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 2, Hash: common.HexToHash("02"), }, } expectedBlocks = append(expectedBlocks, b2) d.On("getEventsByBlockRange", mock.Anything, uint64(2), uint64(2)). - Return([]block{b2}) + Return([]EVMBlock{b2}) // iteration 3: wait for next block to be created (jump to block 8) d.On("waitForNewBlocks", mock.Anything, uint64(2)). @@ -323,35 +246,31 @@ func TestDownload(t *testing.T) { Return(uint64(8)).Once() // iteration 4: blocks 6 and 7 have events - b6 := block{ - blockHeader: blockHeader{ + b6 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 6, Hash: common.HexToHash("06"), }, - Events: []BridgeEvent{ - {Claim: &Claim{OriginNetwork: 6}}, - }, + Events: []interface{}{"06"}, } - b7 := block{ - blockHeader: blockHeader{ + b7 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 7, Hash: common.HexToHash("07"), }, - Events: []BridgeEvent{ - {Bridge: &Bridge{DestinationNetwork: 7}}, - }, + Events: []interface{}{"07"}, } - b8 := block{ - blockHeader: blockHeader{ + b8 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 8, Hash: common.HexToHash("08"), }, } expectedBlocks = append(expectedBlocks, b6, b7, b8) d.On("getEventsByBlockRange", mock.Anything, uint64(3), uint64(8)). - Return([]block{b6, b7}) + Return([]EVMBlock{b6, b7}) d.On("getBlockHeader", mock.Anything, uint64(8)). - Return(b8.blockHeader) + Return(b8.EVMBlockHeader) // iteration 5: wait for next block to be created (jump to block 30) d.On("waitForNewBlocks", mock.Anything, uint64(8)). @@ -359,31 +278,29 @@ func TestDownload(t *testing.T) { Return(uint64(30)).Once() // iteration 6: from block 9 to 19, no events - b19 := block{ - blockHeader: blockHeader{ + b19 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 19, Hash: common.HexToHash("19"), }, } expectedBlocks = append(expectedBlocks, b19) d.On("getEventsByBlockRange", mock.Anything, uint64(9), uint64(19)). - Return([]block{}) + Return([]EVMBlock{}) d.On("getBlockHeader", mock.Anything, uint64(19)). - Return(b19.blockHeader) + Return(b19.EVMBlockHeader) // iteration 7: from block 20 to 30, events on last block - b30 := block{ - blockHeader: blockHeader{ + b30 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 30, Hash: common.HexToHash("30"), }, - Events: []BridgeEvent{ - {Bridge: &Bridge{DestinationNetwork: 30}}, - }, + Events: []interface{}{testEvent(common.HexToHash("30"))}, } expectedBlocks = append(expectedBlocks, b30) d.On("getEventsByBlockRange", mock.Anything, uint64(20), uint64(30)). - Return([]block{b30}) + Return([]EVMBlock{b30}) // iteration 8: wait for next block to be created (jump to block 35) d.On("waitForNewBlocks", mock.Anything, uint64(30)). @@ -403,11 +320,8 @@ func TestDownload(t *testing.T) { } func TestWaitForNewBlocks(t *testing.T) { - retryAfterErrorPeriod = time.Millisecond * 100 - clientMock := NewL2Mock(t) ctx := context.Background() - d, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) - require.NoError(t, err) + d, clientMock := NewTestDownloader(t) // at first attempt currentBlock := uint64(5) @@ -438,18 +352,15 @@ func TestWaitForNewBlocks(t *testing.T) { } func TestGetBlockHeader(t *testing.T) { - retryAfterErrorPeriod = time.Millisecond * 100 - clientMock := NewL2Mock(t) ctx := context.Background() - d, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) - require.NoError(t, err) + d, clientMock := NewTestDownloader(t) blockNum := uint64(5) blockNumBig := big.NewInt(5) returnedBlock := &types.Header{ Number: blockNumBig, } - expectedBlock := blockHeader{ + expectedBlock := EVMBlockHeader{ Num: 5, Hash: returnedBlock.Hash(), } @@ -465,3 +376,23 @@ func TestGetBlockHeader(t *testing.T) { actualBlock = d.getBlockHeader(ctx, blockNum) assert.Equal(t, expectedBlock, actualBlock) } + +func buildAppender() LogAppenderMap { + appender := make(LogAppenderMap) + appender[eventSignature] = func(b *EVMBlock, l types.Log) error { + b.Events = append(b.Events, testEvent(l.Topics[1])) + return nil + } + return appender +} + +func NewTestDownloader(t *testing.T) (*EVMDownloader, *L2Mock) { + rh := &RetryHandler{ + MaxRetryAttemptsAfterError: 5, + RetryAfterErrorPeriod: time.Millisecond * 100, + } + clientMock := NewL2Mock(t) + d, err := NewEVMDownloader(clientMock, syncBlockChunck, etherman.LatestBlock, time.Millisecond, buildAppender(), []common.Address{contractAddr}, rh) + require.NoError(t, err) + return d, clientMock +} diff --git a/sync/evmdriver.go b/sync/evmdriver.go new file mode 100644 index 00000000..0e20731f --- /dev/null +++ b/sync/evmdriver.go @@ -0,0 +1,151 @@ +package sync + +import ( + "context" + + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/reorgdetector" + "github.com/ethereum/go-ethereum/common" +) + +type evmDownloaderFull interface { + evmDownloaderInterface + download(ctx context.Context, fromBlock uint64, downloadedCh chan EVMBlock) +} + +type EVMDriver struct { + reorgDetector ReorgDetector + reorgSub *reorgdetector.Subscription + processor processorInterface + downloader evmDownloaderFull + reorgDetectorID string + downloadBufferSize int + rh *RetryHandler +} + +type processorInterface interface { + GetLastProcessedBlock(ctx context.Context) (uint64, error) + ProcessBlock(block Block) error + Reorg(firstReorgedBlock uint64) error +} + +type ReorgDetector interface { + Subscribe(id string) (*reorgdetector.Subscription, error) + AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error +} + +func NewEVMDriver( + reorgDetector ReorgDetector, + processor processorInterface, + downloader evmDownloaderFull, + reorgDetectorID string, + downloadBufferSize int, + rh *RetryHandler, +) (*EVMDriver, error) { + reorgSub, err := reorgDetector.Subscribe(reorgDetectorID) + if err != nil { + return nil, err + } + return &EVMDriver{ + reorgDetector: reorgDetector, + reorgSub: reorgSub, + processor: processor, + downloader: downloader, + reorgDetectorID: reorgDetectorID, + downloadBufferSize: downloadBufferSize, + rh: rh, + }, nil +} + +func (d *EVMDriver) Sync(ctx context.Context) { +reset: + var ( + lastProcessedBlock uint64 + attempts int + err error + ) + for { + lastProcessedBlock, err = d.processor.GetLastProcessedBlock(ctx) + if err != nil { + attempts++ + log.Error("error geting last processed block: ", err) + d.rh.Handle("Sync", attempts) + continue + } + break + } + cancellableCtx, cancel := context.WithCancel(ctx) + defer cancel() + + // start downloading + downloadCh := make(chan EVMBlock, d.downloadBufferSize) + go d.downloader.download(cancellableCtx, lastProcessedBlock, downloadCh) + + for { + select { + case b := <-downloadCh: + log.Debug("handleNewBlock") + d.handleNewBlock(ctx, b) + case firstReorgedBlock := <-d.reorgSub.FirstReorgedBlock: + log.Debug("handleReorg") + d.handleReorg(cancel, downloadCh, firstReorgedBlock) + goto reset + } + } +} + +func (d *EVMDriver) handleNewBlock(ctx context.Context, b EVMBlock) { + attempts := 0 + for { + err := d.reorgDetector.AddBlockToTrack(ctx, d.reorgDetectorID, b.Num, b.Hash) + if err != nil { + attempts++ + log.Errorf("error adding block %d to tracker: %v", b.Num, err) + d.rh.Handle("handleNewBlock", attempts) + continue + } + break + } + attempts = 0 + for { + blockToProcess := Block{ + Num: b.Num, + Events: b.Events, + } + err := d.processor.ProcessBlock(blockToProcess) + if err != nil { + attempts++ + log.Errorf("error processing events for blcok %d, err: ", b.Num, err) + d.rh.Handle("handleNewBlock", attempts) + continue + } + break + } +} + +func (d *EVMDriver) handleReorg( + cancel context.CancelFunc, downloadCh chan EVMBlock, firstReorgedBlock uint64, +) { + // stop downloader + cancel() + _, ok := <-downloadCh + for ok { + _, ok = <-downloadCh + } + // handle reorg + attempts := 0 + for { + err := d.processor.Reorg(firstReorgedBlock) + if err != nil { + attempts++ + log.Errorf( + "error processing reorg, last valid Block %d, err: %v", + firstReorgedBlock, err, + ) + d.rh.Handle("handleReorg", attempts) + continue + } + break + } + d.reorgSub.ReorgProcessed <- true +} diff --git a/localbridgesync/driver_test.go b/sync/evmdriver_test.go similarity index 67% rename from localbridgesync/driver_test.go rename to sync/evmdriver_test.go index 543542f7..853dda81 100644 --- a/localbridgesync/driver_test.go +++ b/sync/evmdriver_test.go @@ -1,4 +1,4 @@ -package localbridgesync +package sync import ( "context" @@ -14,28 +14,35 @@ import ( "github.com/stretchr/testify/require" ) +var ( + reorgDetectorID = "foo" +) + func TestSync(t *testing.T) { - retryAfterErrorPeriod = time.Millisecond * 100 + rh := &RetryHandler{ + MaxRetryAttemptsAfterError: 5, + RetryAfterErrorPeriod: time.Millisecond * 100, + } rdm := NewReorgDetectorMock(t) pm := NewProcessorMock(t) - dm := NewDownloaderMock(t) + dm := NewEVMDownloaderMock(t) firstReorgedBlock := make(chan uint64) reorgProcessed := make(chan bool) rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{ FirstReorgedBlock: firstReorgedBlock, ReorgProcessed: reorgProcessed, - }) - driver, err := newDriver(rdm, pm, dm) + }, nil) + driver, err := NewEVMDriver(rdm, pm, dm, reorgDetectorID, 10, rh) require.NoError(t, err) ctx := context.Background() - expectedBlock1 := block{ - blockHeader: blockHeader{ + expectedBlock1 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 3, Hash: common.HexToHash("03"), }, } - expectedBlock2 := block{ - blockHeader: blockHeader{ + expectedBlock2 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 9, Hash: common.HexToHash("09"), }, @@ -47,7 +54,7 @@ func TestSync(t *testing.T) { reorg1Completed := reorgSemaphore{} dm.On("download", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ctx := args.Get(0).(context.Context) - downloadedCh := args.Get(2).(chan block) + downloadedCh := args.Get(2).(chan EVMBlock) log.Info("entering mock loop") for { select { @@ -70,22 +77,22 @@ func TestSync(t *testing.T) { }) // Mocking this actions, the driver should "store" all the blocks from the downloader - pm.On("getLastProcessedBlock", ctx). + pm.On("GetLastProcessedBlock", ctx). Return(uint64(3), nil) rdm.On("AddBlockToTrack", ctx, reorgDetectorID, expectedBlock1.Num, expectedBlock1.Hash). Return(nil) - pm.On("storeBridgeEvents", expectedBlock1.Num, expectedBlock1.Events). + pm.On("ProcessBlock", Block{Num: expectedBlock1.Num, Events: expectedBlock1.Events}). Return(nil) rdm.On("AddBlockToTrack", ctx, reorgDetectorID, expectedBlock2.Num, expectedBlock2.Hash). Return(nil) - pm.On("storeBridgeEvents", expectedBlock2.Num, expectedBlock2.Events). + pm.On("ProcessBlock", Block{Num: expectedBlock2.Num, Events: expectedBlock2.Events}). Return(nil) go driver.Sync(ctx) time.Sleep(time.Millisecond * 200) // time to download expectedBlock1 // Trigger reorg 1 reorgedBlock1 := uint64(5) - pm.On("reorg", reorgedBlock1).Return(nil) + pm.On("Reorg", reorgedBlock1).Return(nil) firstReorgedBlock <- reorgedBlock1 ok := <-reorgProcessed require.True(t, ok) @@ -96,25 +103,28 @@ func TestSync(t *testing.T) { // Trigger reorg 2: syncer restarts the porcess reorgedBlock2 := uint64(7) - pm.On("reorg", reorgedBlock2).Return(nil) + pm.On("Reorg", reorgedBlock2).Return(nil) firstReorgedBlock <- reorgedBlock2 ok = <-reorgProcessed require.True(t, ok) } func TestHandleNewBlock(t *testing.T) { - retryAfterErrorPeriod = time.Millisecond * 100 + rh := &RetryHandler{ + MaxRetryAttemptsAfterError: 5, + RetryAfterErrorPeriod: time.Millisecond * 100, + } rdm := NewReorgDetectorMock(t) pm := NewProcessorMock(t) - dm := NewDownloaderMock(t) - rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{}) - driver, err := newDriver(rdm, pm, dm) + dm := NewEVMDownloaderMock(t) + rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{}, nil) + driver, err := NewEVMDriver(rdm, pm, dm, reorgDetectorID, 10, rh) require.NoError(t, err) ctx := context.Background() // happy path - b1 := block{ - blockHeader: blockHeader{ + b1 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 1, Hash: common.HexToHash("f00"), }, @@ -122,13 +132,13 @@ func TestHandleNewBlock(t *testing.T) { rdm. On("AddBlockToTrack", ctx, reorgDetectorID, b1.Num, b1.Hash). Return(nil) - pm.On("storeBridgeEvents", b1.Num, b1.Events). + pm.On("ProcessBlock", Block{Num: b1.Num, Events: b1.Events}). Return(nil) driver.handleNewBlock(ctx, b1) // reorg deteector fails once - b2 := block{ - blockHeader: blockHeader{ + b2 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 2, Hash: common.HexToHash("f00"), }, @@ -139,13 +149,13 @@ func TestHandleNewBlock(t *testing.T) { rdm. On("AddBlockToTrack", ctx, reorgDetectorID, b2.Num, b2.Hash). Return(nil).Once() - pm.On("storeBridgeEvents", b2.Num, b2.Events). + pm.On("ProcessBlock", Block{Num: b2.Num, Events: b2.Events}). Return(nil) driver.handleNewBlock(ctx, b2) // processor fails once - b3 := block{ - blockHeader: blockHeader{ + b3 := EVMBlock{ + EVMBlockHeader: EVMBlockHeader{ Num: 3, Hash: common.HexToHash("f00"), }, @@ -153,32 +163,35 @@ func TestHandleNewBlock(t *testing.T) { rdm. On("AddBlockToTrack", ctx, reorgDetectorID, b3.Num, b3.Hash). Return(nil) - pm.On("storeBridgeEvents", b3.Num, b3.Events). + pm.On("ProcessBlock", Block{Num: b3.Num, Events: b3.Events}). Return(errors.New("foo")).Once() - pm.On("storeBridgeEvents", b3.Num, b3.Events). + pm.On("ProcessBlock", Block{Num: b3.Num, Events: b3.Events}). Return(nil).Once() driver.handleNewBlock(ctx, b3) } func TestHandleReorg(t *testing.T) { - retryAfterErrorPeriod = time.Millisecond * 100 + rh := &RetryHandler{ + MaxRetryAttemptsAfterError: 5, + RetryAfterErrorPeriod: time.Millisecond * 100, + } rdm := NewReorgDetectorMock(t) pm := NewProcessorMock(t) - dm := NewDownloaderMock(t) + dm := NewEVMDownloaderMock(t) reorgProcessed := make(chan bool) rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{ ReorgProcessed: reorgProcessed, - }) - driver, err := newDriver(rdm, pm, dm) + }, nil) + driver, err := NewEVMDriver(rdm, pm, dm, reorgDetectorID, 10, rh) require.NoError(t, err) ctx := context.Background() // happy path _, cancel := context.WithCancel(ctx) - downloadCh := make(chan block) + downloadCh := make(chan EVMBlock) firstReorgedBlock := uint64(5) - pm.On("reorg", firstReorgedBlock).Return(nil) + pm.On("Reorg", firstReorgedBlock).Return(nil) go driver.handleReorg(cancel, downloadCh, firstReorgedBlock) close(downloadCh) done := <-reorgProcessed @@ -186,24 +199,24 @@ func TestHandleReorg(t *testing.T) { // download ch sends some garbage _, cancel = context.WithCancel(ctx) - downloadCh = make(chan block) + downloadCh = make(chan EVMBlock) firstReorgedBlock = uint64(6) - pm.On("reorg", firstReorgedBlock).Return(nil) + pm.On("Reorg", firstReorgedBlock).Return(nil) go driver.handleReorg(cancel, downloadCh, firstReorgedBlock) - downloadCh <- block{} - downloadCh <- block{} - downloadCh <- block{} + downloadCh <- EVMBlock{} + downloadCh <- EVMBlock{} + downloadCh <- EVMBlock{} close(downloadCh) done = <-reorgProcessed require.True(t, done) // processor fails 2 times _, cancel = context.WithCancel(ctx) - downloadCh = make(chan block) + downloadCh = make(chan EVMBlock) firstReorgedBlock = uint64(7) - pm.On("reorg", firstReorgedBlock).Return(errors.New("foo")).Once() - pm.On("reorg", firstReorgedBlock).Return(errors.New("foo")).Once() - pm.On("reorg", firstReorgedBlock).Return(nil).Once() + pm.On("Reorg", firstReorgedBlock).Return(errors.New("foo")).Once() + pm.On("Reorg", firstReorgedBlock).Return(errors.New("foo")).Once() + pm.On("Reorg", firstReorgedBlock).Return(nil).Once() go driver.handleReorg(cancel, downloadCh, firstReorgedBlock) close(downloadCh) done = <-reorgProcessed diff --git a/sync/evmtypes.go b/sync/evmtypes.go new file mode 100644 index 00000000..d242dbc4 --- /dev/null +++ b/sync/evmtypes.go @@ -0,0 +1,15 @@ +package sync + +import "github.com/ethereum/go-ethereum/common" + +type EVMBlock struct { + EVMBlockHeader + Events []interface{} +} + +type EVMBlockHeader struct { + Num uint64 + Hash common.Hash + ParentHash common.Hash + Timestamp uint64 +} diff --git a/localbridgesync/mock_downloader_test.go b/sync/mock_downloader_test.go similarity index 54% rename from localbridgesync/mock_downloader_test.go rename to sync/mock_downloader_test.go index f2df97d0..738fc873 100644 --- a/localbridgesync/mock_downloader_test.go +++ b/sync/mock_downloader_test.go @@ -1,6 +1,6 @@ // Code generated by mockery v2.22.1. DO NOT EDIT. -package localbridgesync +package sync import ( context "context" @@ -9,45 +9,40 @@ import ( mock "github.com/stretchr/testify/mock" ) -// DownloaderMock is an autogenerated mock type for the downloaderFull type -type DownloaderMock struct { +// EVMDownloaderMock is an autogenerated mock type for the evmDownloaderFull type +type EVMDownloaderMock struct { mock.Mock } -// appendLog provides a mock function with given fields: b, l -func (_m *DownloaderMock) appendLog(b *block, l types.Log) { - _m.Called(b, l) -} - // download provides a mock function with given fields: ctx, fromBlock, downloadedCh -func (_m *DownloaderMock) download(ctx context.Context, fromBlock uint64, downloadedCh chan block) { +func (_m *EVMDownloaderMock) download(ctx context.Context, fromBlock uint64, downloadedCh chan EVMBlock) { _m.Called(ctx, fromBlock, downloadedCh) } // getBlockHeader provides a mock function with given fields: ctx, blockNum -func (_m *DownloaderMock) getBlockHeader(ctx context.Context, blockNum uint64) blockHeader { +func (_m *EVMDownloaderMock) getBlockHeader(ctx context.Context, blockNum uint64) EVMBlockHeader { ret := _m.Called(ctx, blockNum) - var r0 blockHeader - if rf, ok := ret.Get(0).(func(context.Context, uint64) blockHeader); ok { + var r0 EVMBlockHeader + if rf, ok := ret.Get(0).(func(context.Context, uint64) EVMBlockHeader); ok { r0 = rf(ctx, blockNum) } else { - r0 = ret.Get(0).(blockHeader) + r0 = ret.Get(0).(EVMBlockHeader) } return r0 } // getEventsByBlockRange provides a mock function with given fields: ctx, fromBlock, toBlock -func (_m *DownloaderMock) getEventsByBlockRange(ctx context.Context, fromBlock uint64, toBlock uint64) []block { +func (_m *EVMDownloaderMock) getEventsByBlockRange(ctx context.Context, fromBlock uint64, toBlock uint64) []EVMBlock { ret := _m.Called(ctx, fromBlock, toBlock) - var r0 []block - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []block); ok { + var r0 []EVMBlock + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []EVMBlock); ok { r0 = rf(ctx, fromBlock, toBlock) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]block) + r0 = ret.Get(0).([]EVMBlock) } } @@ -55,7 +50,7 @@ func (_m *DownloaderMock) getEventsByBlockRange(ctx context.Context, fromBlock u } // getLogs provides a mock function with given fields: ctx, fromBlock, toBlock -func (_m *DownloaderMock) getLogs(ctx context.Context, fromBlock uint64, toBlock uint64) []types.Log { +func (_m *EVMDownloaderMock) getLogs(ctx context.Context, fromBlock uint64, toBlock uint64) []types.Log { ret := _m.Called(ctx, fromBlock, toBlock) var r0 []types.Log @@ -71,7 +66,7 @@ func (_m *DownloaderMock) getLogs(ctx context.Context, fromBlock uint64, toBlock } // waitForNewBlocks provides a mock function with given fields: ctx, lastBlockSeen -func (_m *DownloaderMock) waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) uint64 { +func (_m *EVMDownloaderMock) waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) uint64 { ret := _m.Called(ctx, lastBlockSeen) var r0 uint64 @@ -84,14 +79,14 @@ func (_m *DownloaderMock) waitForNewBlocks(ctx context.Context, lastBlockSeen ui return r0 } -type mockConstructorTestingTNewDownloaderMock interface { +type mockConstructorTestingTNewEVMDownloaderMock interface { mock.TestingT Cleanup(func()) } -// NewDownloaderMock creates a new instance of DownloaderMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDownloaderMock(t mockConstructorTestingTNewDownloaderMock) *DownloaderMock { - mock := &DownloaderMock{} +// NewEVMDownloaderMock creates a new instance of EVMDownloaderMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEVMDownloaderMock(t mockConstructorTestingTNewEVMDownloaderMock) *EVMDownloaderMock { + mock := &EVMDownloaderMock{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/localbridgesync/mock_l2_test.go b/sync/mock_l2_test.go similarity index 99% rename from localbridgesync/mock_l2_test.go rename to sync/mock_l2_test.go index 78baa614..0d1e03da 100644 --- a/localbridgesync/mock_l2_test.go +++ b/sync/mock_l2_test.go @@ -1,6 +1,6 @@ // Code generated by mockery v2.22.1. DO NOT EDIT. -package localbridgesync +package sync import ( context "context" diff --git a/localbridgesync/mock_processor_test.go b/sync/mock_processor_test.go similarity index 70% rename from localbridgesync/mock_processor_test.go rename to sync/mock_processor_test.go index 4a629f5c..d2c3e299 100644 --- a/localbridgesync/mock_processor_test.go +++ b/sync/mock_processor_test.go @@ -1,6 +1,6 @@ // Code generated by mockery v2.22.1. DO NOT EDIT. -package localbridgesync +package sync import ( context "context" @@ -13,8 +13,8 @@ type ProcessorMock struct { mock.Mock } -// getLastProcessedBlock provides a mock function with given fields: ctx -func (_m *ProcessorMock) getLastProcessedBlock(ctx context.Context) (uint64, error) { +// GetLastProcessedBlock provides a mock function with given fields: ctx +func (_m *ProcessorMock) GetLastProcessedBlock(ctx context.Context) (uint64, error) { ret := _m.Called(ctx) var r0 uint64 @@ -37,13 +37,13 @@ func (_m *ProcessorMock) getLastProcessedBlock(ctx context.Context) (uint64, err return r0, r1 } -// reorg provides a mock function with given fields: firstReorgedBlock -func (_m *ProcessorMock) reorg(firstReorgedBlock uint64) error { - ret := _m.Called(firstReorgedBlock) +// ProcessBlock provides a mock function with given fields: block +func (_m *ProcessorMock) ProcessBlock(block Block) error { + ret := _m.Called(block) var r0 error - if rf, ok := ret.Get(0).(func(uint64) error); ok { - r0 = rf(firstReorgedBlock) + if rf, ok := ret.Get(0).(func(Block) error); ok { + r0 = rf(block) } else { r0 = ret.Error(0) } @@ -51,13 +51,13 @@ func (_m *ProcessorMock) reorg(firstReorgedBlock uint64) error { return r0 } -// storeBridgeEvents provides a mock function with given fields: blockNum, events -func (_m *ProcessorMock) storeBridgeEvents(blockNum uint64, events []BridgeEvent) error { - ret := _m.Called(blockNum, events) +// Reorg provides a mock function with given fields: firstReorgedBlock +func (_m *ProcessorMock) Reorg(firstReorgedBlock uint64) error { + ret := _m.Called(firstReorgedBlock) var r0 error - if rf, ok := ret.Get(0).(func(uint64, []BridgeEvent) error); ok { - r0 = rf(blockNum, events) + if rf, ok := ret.Get(0).(func(uint64) error); ok { + r0 = rf(firstReorgedBlock) } else { r0 = ret.Error(0) } diff --git a/localbridgesync/mock_reorgdetector_test.go b/sync/mock_reorgdetector_test.go similarity index 82% rename from localbridgesync/mock_reorgdetector_test.go rename to sync/mock_reorgdetector_test.go index d11434a1..056da2a1 100644 --- a/localbridgesync/mock_reorgdetector_test.go +++ b/sync/mock_reorgdetector_test.go @@ -1,6 +1,6 @@ // Code generated by mockery v2.22.1. DO NOT EDIT. -package localbridgesync +package sync import ( context "context" @@ -32,10 +32,14 @@ func (_m *ReorgDetectorMock) AddBlockToTrack(ctx context.Context, id string, blo } // Subscribe provides a mock function with given fields: id -func (_m *ReorgDetectorMock) Subscribe(id string) *reorgdetector.Subscription { +func (_m *ReorgDetectorMock) Subscribe(id string) (*reorgdetector.Subscription, error) { ret := _m.Called(id) var r0 *reorgdetector.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(string) (*reorgdetector.Subscription, error)); ok { + return rf(id) + } if rf, ok := ret.Get(0).(func(string) *reorgdetector.Subscription); ok { r0 = rf(id) } else { @@ -44,7 +48,13 @@ func (_m *ReorgDetectorMock) Subscribe(id string) *reorgdetector.Subscription { } } - return r0 + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } type mockConstructorTestingTNewReorgDetectorMock interface { diff --git a/test/Makefile b/test/Makefile index 3511b4f7..0e671023 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,14 +1,9 @@ .PHONY: generate-mocks generate-mocks: - $(MAKE) generate-mocks-localbridgesync $(MAKE) generate-mocks-reorgdetector - -.PHONY: generate-mocks-localbridgesync -generate-mocks-localbridgesync: ## Generates mocks for localbridgesync, using mockery tool - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=EthClienter --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=L2Mock --filename=mock_l2_test.go - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=downloaderFull --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=DownloaderMock --filename=mock_downloader_test.go - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=processorInterface --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=ProcessorMock --filename=mock_processor_test.go - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=ReorgDetector --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=ReorgDetectorMock --filename=mock_reorgdetector_test.go + $(MAKE) generate-mocks-l1infotreesync + $(MAKE) generate-mocks-aggoracle + $(MAKE) generate-mocks-sync .PHONY: generate-mocks-reorgdetector generate-mocks-reorgdetector: ## Generates mocks for reorgdetector, using mockery tool @@ -37,3 +32,17 @@ help: ## Prints this help @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ | sort \ | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +.PHONY: generate-mocks-l1infotreesync +generate-mocks-l1infotreesync: ## Generates mocks for l1infotreesync , using mockery tool + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=ReorgDetector --dir=../sync --output=../l1infotreesync --outpkg=l1infotreesync --structname=ReorgDetectorMock --filename=mock_reorgdetector_test.go + +.PHONY: generate-mocks-aggoracle +generate-mocks-aggoracle: ## Generates mocks for aggoracle , using mockery tool + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=EthTxManager --dir=../aggoracle/chaingersender --output=../aggoracle --outpkg=aggoracle --structname=EthTxManagerMock --filename=mock_ethtxmanager_test.go + +.PHONY: generate-mocks-sync +generate-mocks-sync: ## Generates mocks for sync, using mockery tool + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=EthClienter --dir=../sync --output=../sync --outpkg=sync --inpackage --structname=L2Mock --filename=mock_l2_test.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=evmDownloaderFull --dir=../sync --output=../sync --outpkg=sync --inpackage --structname=EVMDownloaderMock --filename=mock_downloader_test.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=processorInterface --dir=../sync --output=../sync --outpkg=sync --inpackage --structname=ProcessorMock --filename=mock_processor_test.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=ReorgDetector --dir=../sync --output=../sync --outpkg=sync --inpackage --structname=ReorgDetectorMock --filename=mock_reorgdetector_test.go From 12d1e3f644cfcd1f18abffc62eb01a567963dc32 Mon Sep 17 00:00:00 2001 From: Joan Esteban <129153821+joanestebanr@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:01:48 +0200 Subject: [PATCH 3/3] Add unittest for multi-fork support (#25) - Add unittest for new code under sequencesender/txbuilder (> 80%) - Improved contracts base clase and is no longer needed Contract() - Removed param sender for txBuilder.BuildSequenceBatchesTx because it use the From from bind.TransactOpts variable - Require just 1 verified batch to pass e2e --- Makefile | 5 + aggoracle/mock_ethtxmanager_test.go | 27 +- cmd/run.go | 8 +- dataavailability/dataavailability.go | 4 +- .../datacommittee/datacommittee.go | 2 +- dataavailability/interfaces.go | 12 +- .../mocks_da/batch_data_provider.go | 96 +++ dataavailability/mocks_da/da_backender.go | 263 +++++++++ dataavailability/mocks_da/data_manager.go | 218 +++++++ dataavailability/mocks_da/func_sign_type.go | 94 +++ .../mocks_da/sequence_retriever.go | 98 ++++ dataavailability/mocks_da/sequence_sender.go | 156 +++++ .../mocks_da/sequence_sender_banana.go | 97 +++ .../mocks_da/sequence_sender_elderberry.go | 95 +++ etherman/aggregator.go | 4 +- etherman/contracts/base.go | 47 +- etherman/contracts/base_test.go | 16 + etherman/contracts/contracts_banana.go | 24 +- etherman/contracts/contracts_elderberry.go | 23 +- etherman/etherman.go | 22 +- l1infotreesync/mock_reorgdetector_test.go | 19 +- localbridgesync/mock_l2_test.go | 555 ++++++++++++++++++ reorgdetector/mock_eth_client.go | 19 +- sequencesender/sequencesender.go | 2 +- sequencesender/txbuilder/banana_base.go | 122 ++-- sequencesender/txbuilder/banana_base_test.go | 86 +++ sequencesender/txbuilder/banana_validium.go | 50 +- .../txbuilder/banana_validium_test.go | 107 ++++ sequencesender/txbuilder/banana_zkevm.go | 36 +- sequencesender/txbuilder/banana_zkevm_test.go | 105 ++++ sequencesender/txbuilder/elderberry_base.go | 17 +- .../txbuilder/elderberry_base_test.go | 98 ++++ sequencesender/txbuilder/elderberry_types.go | 7 + .../txbuilder/elderberry_validium.go | 44 +- .../txbuilder/elderberry_validium_test.go | 116 ++++ sequencesender/txbuilder/elderberry_zkevm.go | 36 +- .../txbuilder/elderberry_zkevm_test.go | 110 ++++ sequencesender/txbuilder/interface.go | 11 +- sequencesender/txbuilder/interface_test.go | 44 ++ .../mocks_txbuilder/cond_new_sequence.go | 103 ++++ .../global_exit_root_banana_contractor.go | 139 +++++ ...lobal_exit_root_banana_zkevm_contractor.go | 139 +++++ .../rollup_banana_base_contractor.go | 93 +++ .../rollup_banana_validium_contractor.go | 163 +++++ .../rollup_banana_zkevm_contractor.go | 162 +++++ .../rollup_elderberry_validium_contractor.go | 104 ++++ .../rollup_elderberry_zkevm_contractor.go | 103 ++++ .../txbuilder/mocks_txbuilder/tx_builder.go | 366 ++++++++++++ .../txbuilder/validium_cond_num_batches.go | 14 +- .../validium_cond_num_batches_test.go | 45 ++ .../txbuilder/zkevm_cond_max_size.go | 64 +- .../txbuilder/zkevm_cond_max_size_test.go | 95 +++ sync/mock_downloader_test.go | 27 +- sync/mock_l2_test.go | 83 ++- sync/mock_processor_test.go | 23 +- sync/mock_reorgdetector_test.go | 19 +- test/Makefile | 23 +- test/run-e2e-seq_sender.sh | 2 +- 58 files changed, 4413 insertions(+), 249 deletions(-) create mode 100644 dataavailability/mocks_da/batch_data_provider.go create mode 100644 dataavailability/mocks_da/da_backender.go create mode 100644 dataavailability/mocks_da/data_manager.go create mode 100644 dataavailability/mocks_da/func_sign_type.go create mode 100644 dataavailability/mocks_da/sequence_retriever.go create mode 100644 dataavailability/mocks_da/sequence_sender.go create mode 100644 dataavailability/mocks_da/sequence_sender_banana.go create mode 100644 dataavailability/mocks_da/sequence_sender_elderberry.go create mode 100644 etherman/contracts/base_test.go create mode 100644 localbridgesync/mock_l2_test.go create mode 100644 sequencesender/txbuilder/banana_base_test.go create mode 100644 sequencesender/txbuilder/banana_validium_test.go create mode 100644 sequencesender/txbuilder/banana_zkevm_test.go create mode 100644 sequencesender/txbuilder/elderberry_base_test.go create mode 100644 sequencesender/txbuilder/elderberry_validium_test.go create mode 100644 sequencesender/txbuilder/elderberry_zkevm_test.go create mode 100644 sequencesender/txbuilder/interface_test.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/cond_new_sequence.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_contractor.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_zkevm_contractor.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/rollup_banana_base_contractor.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/rollup_banana_validium_contractor.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/rollup_banana_zkevm_contractor.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_validium_contractor.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_zkevm_contractor.go create mode 100644 sequencesender/txbuilder/mocks_txbuilder/tx_builder.go create mode 100644 sequencesender/txbuilder/validium_cond_num_batches_test.go create mode 100644 sequencesender/txbuilder/zkevm_cond_max_size_test.go diff --git a/Makefile b/Makefile index e081dcb7..9b0d5b74 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,12 @@ stop: ## Stops all services .PHONY: test test: trap '$(STOP)' EXIT; MallocNanoZone=0 go test -count=1 -short -race -p 1 -covermode=atomic -coverprofile=../coverage.out -coverpkg ./... -timeout 200s ./... + +.PHONY: test-seq_sender +test-seq_sender: + trap '$(STOP)' EXIT; MallocNanoZone=0 go test -count=1 -short -race -p 1 -covermode=atomic -coverprofile=../coverage.out -timeout 200s ./sequencesender/... + .PHONY: install-linter install-linter: ## Installs the linter curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$(go env GOPATH)/bin v1.54.2 diff --git a/aggoracle/mock_ethtxmanager_test.go b/aggoracle/mock_ethtxmanager_test.go index 37bcbeda..0deb775d 100644 --- a/aggoracle/mock_ethtxmanager_test.go +++ b/aggoracle/mock_ethtxmanager_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package aggoracle @@ -25,6 +25,10 @@ type EthTxManagerMock struct { func (_m *EthTxManagerMock) Add(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar) (common.Hash, error) { ret := _m.Called(ctx, to, forcedNonce, value, data, gasOffset, sidecar) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 common.Hash var r1 error if rf, ok := ret.Get(0).(func(context.Context, *common.Address, *uint64, *big.Int, []byte, uint64, *types.BlobTxSidecar) (common.Hash, error)); ok { @@ -51,6 +55,10 @@ func (_m *EthTxManagerMock) Add(ctx context.Context, to *common.Address, forcedN func (_m *EthTxManagerMock) Remove(ctx context.Context, id common.Hash) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Remove") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) error); ok { r0 = rf(ctx, id) @@ -65,6 +73,10 @@ func (_m *EthTxManagerMock) Remove(ctx context.Context, id common.Hash) error { func (_m *EthTxManagerMock) Result(ctx context.Context, id common.Hash) (ethtxmanager.MonitoredTxResult, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Result") + } + var r0 ethtxmanager.MonitoredTxResult var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (ethtxmanager.MonitoredTxResult, error)); ok { @@ -89,6 +101,10 @@ func (_m *EthTxManagerMock) Result(ctx context.Context, id common.Hash) (ethtxma func (_m *EthTxManagerMock) ResultsByStatus(ctx context.Context, statuses []ethtxmanager.MonitoredTxStatus) ([]ethtxmanager.MonitoredTxResult, error) { ret := _m.Called(ctx, statuses) + if len(ret) == 0 { + panic("no return value specified for ResultsByStatus") + } + var r0 []ethtxmanager.MonitoredTxResult var r1 error if rf, ok := ret.Get(0).(func(context.Context, []ethtxmanager.MonitoredTxStatus) ([]ethtxmanager.MonitoredTxResult, error)); ok { @@ -111,13 +127,12 @@ func (_m *EthTxManagerMock) ResultsByStatus(ctx context.Context, statuses []etht return r0, r1 } -type mockConstructorTestingTNewEthTxManagerMock interface { +// NewEthTxManagerMock creates a new instance of EthTxManagerMock. 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 NewEthTxManagerMock(t interface { mock.TestingT Cleanup(func()) -} - -// NewEthTxManagerMock creates a new instance of EthTxManagerMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewEthTxManagerMock(t mockConstructorTestingTNewEthTxManagerMock) *EthTxManagerMock { +}) *EthTxManagerMock { mock := &EthTxManagerMock{} mock.Mock.Test(t) diff --git a/cmd/run.go b/cmd/run.go index f7d36b74..7f9ed6ed 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -181,15 +181,15 @@ func newTxBuilder(cfg config.Config, ethman *etherman.Client) (txbuilder.TxBuild switch contracts.VersionType(cfg.Common.ContractVersions) { case contracts.VersionBanana: if cfg.Common.IsValidiumMode { - txBuilder = txbuilder.NewTxBuilderBananaValidium(ethman.Contracts.Banana.Rollup, ethman.Contracts.Banana.GlobalExitRoot, da, *auth, auth.From, cfg.SequenceSender.MaxBatchesForL1) + txBuilder = txbuilder.NewTxBuilderBananaValidium(ethman.Contracts.Banana.Rollup, ethman.Contracts.Banana.GlobalExitRoot, da, *auth, cfg.SequenceSender.MaxBatchesForL1) } else { - txBuilder = txbuilder.NewTxBuilderBananaZKEVM(ethman.Contracts.Banana.Rollup, ethman.Contracts.Banana.GlobalExitRoot, *auth, auth.From, cfg.SequenceSender.MaxTxSizeForL1) + txBuilder = txbuilder.NewTxBuilderBananaZKEVM(ethman.Contracts.Banana.Rollup, ethman.Contracts.Banana.GlobalExitRoot, *auth, cfg.SequenceSender.MaxTxSizeForL1) } case contracts.VersionElderberry: if cfg.Common.IsValidiumMode { - txBuilder = txbuilder.NewTxBuilderElderberryValidium(ethman.Contracts.Elderberry.Rollup, da, *auth, auth.From, cfg.SequenceSender.MaxBatchesForL1) + txBuilder = txbuilder.NewTxBuilderElderberryValidium(ethman.Contracts.Elderberry.Rollup, da, *auth, cfg.SequenceSender.MaxBatchesForL1) } else { - txBuilder = txbuilder.NewTxBuilderElderberryZKEVM(ethman.Contracts.Elderberry.Rollup, *auth, auth.From, cfg.SequenceSender.MaxTxSizeForL1) + txBuilder = txbuilder.NewTxBuilderElderberryZKEVM(ethman.Contracts.Elderberry.Rollup, *auth, cfg.SequenceSender.MaxTxSizeForL1) } default: err = fmt.Errorf("unknown contract version: %s", cfg.Common.ContractVersions) diff --git a/dataavailability/dataavailability.go b/dataavailability/dataavailability.go index 284f0b64..2e8ec124 100644 --- a/dataavailability/dataavailability.go +++ b/dataavailability/dataavailability.go @@ -20,8 +20,8 @@ func New(backend DABackender) (*DataAvailability, error) { return da, da.backend.Init() } -func (d *DataAvailability) PostSequence(ctx context.Context, sequenceBanana etherman.SequenceBanana) ([]byte, error) { - return d.backend.PostSequence(ctx, sequenceBanana) +func (d *DataAvailability) PostSequenceBanana(ctx context.Context, sequenceBanana etherman.SequenceBanana) ([]byte, error) { + return d.backend.PostSequenceBanana(ctx, sequenceBanana) } func (d *DataAvailability) PostSequenceElderberry(ctx context.Context, batchesData [][]byte) ([]byte, error) { diff --git a/dataavailability/datacommittee/datacommittee.go b/dataavailability/datacommittee/datacommittee.go index 54b3882f..67c7884a 100644 --- a/dataavailability/datacommittee/datacommittee.go +++ b/dataavailability/datacommittee/datacommittee.go @@ -191,7 +191,7 @@ func (d *Backend) PostSequenceElderberry(ctx context.Context, batchesData [][]by return collectSignatures(committee, ch, cancelSignatureCollection) } -func (d *Backend) PostSequence(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) { +func (d *Backend) PostSequenceBanana(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) { // Get current committee committee, err := d.getCurrentDataCommittee() if err != nil { diff --git a/dataavailability/interfaces.go b/dataavailability/interfaces.go index 8292fd71..b3630871 100644 --- a/dataavailability/interfaces.go +++ b/dataavailability/interfaces.go @@ -17,18 +17,20 @@ type DABackender interface { // SequenceSender is used to send provided sequence of batches type SequenceSender interface { + SequenceSenderElderberry + SequenceSenderBanana +} + +type SequenceSenderElderberry interface { // PostSequence sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage // as expected by the contract - PostSequence(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) - // PostSequenceElderberry sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage - // as expected by the contract PostSequenceElderberry(ctx context.Context, batchesData [][]byte) ([]byte, error) } -type SequenceSenderElderberry interface { +type SequenceSenderBanana interface { // PostSequence sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage // as expected by the contract - PostSequenceElderberry(ctx context.Context, batchesData [][]byte) ([]byte, error) + PostSequenceBanana(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) } // SequenceRetriever is used to retrieve batch data diff --git a/dataavailability/mocks_da/batch_data_provider.go b/dataavailability/mocks_da/batch_data_provider.go new file mode 100644 index 00000000..36e782ac --- /dev/null +++ b/dataavailability/mocks_da/batch_data_provider.go @@ -0,0 +1,96 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" +) + +// BatchDataProvider is an autogenerated mock type for the BatchDataProvider type +type BatchDataProvider struct { + mock.Mock +} + +type BatchDataProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *BatchDataProvider) EXPECT() *BatchDataProvider_Expecter { + return &BatchDataProvider_Expecter{mock: &_m.Mock} +} + +// GetBatchL2Data provides a mock function with given fields: batchNum, batchHashes, dataAvailabilityMessage +func (_m *BatchDataProvider) GetBatchL2Data(batchNum []uint64, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) { + ret := _m.Called(batchNum, batchHashes, dataAvailabilityMessage) + + if len(ret) == 0 { + panic("no return value specified for GetBatchL2Data") + } + + var r0 [][]byte + var r1 error + if rf, ok := ret.Get(0).(func([]uint64, []common.Hash, []byte) ([][]byte, error)); ok { + return rf(batchNum, batchHashes, dataAvailabilityMessage) + } + if rf, ok := ret.Get(0).(func([]uint64, []common.Hash, []byte) [][]byte); ok { + r0 = rf(batchNum, batchHashes, dataAvailabilityMessage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([][]byte) + } + } + + if rf, ok := ret.Get(1).(func([]uint64, []common.Hash, []byte) error); ok { + r1 = rf(batchNum, batchHashes, dataAvailabilityMessage) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BatchDataProvider_GetBatchL2Data_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBatchL2Data' +type BatchDataProvider_GetBatchL2Data_Call struct { + *mock.Call +} + +// GetBatchL2Data is a helper method to define mock.On call +// - batchNum []uint64 +// - batchHashes []common.Hash +// - dataAvailabilityMessage []byte +func (_e *BatchDataProvider_Expecter) GetBatchL2Data(batchNum interface{}, batchHashes interface{}, dataAvailabilityMessage interface{}) *BatchDataProvider_GetBatchL2Data_Call { + return &BatchDataProvider_GetBatchL2Data_Call{Call: _e.mock.On("GetBatchL2Data", batchNum, batchHashes, dataAvailabilityMessage)} +} + +func (_c *BatchDataProvider_GetBatchL2Data_Call) Run(run func(batchNum []uint64, batchHashes []common.Hash, dataAvailabilityMessage []byte)) *BatchDataProvider_GetBatchL2Data_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]uint64), args[1].([]common.Hash), args[2].([]byte)) + }) + return _c +} + +func (_c *BatchDataProvider_GetBatchL2Data_Call) Return(_a0 [][]byte, _a1 error) *BatchDataProvider_GetBatchL2Data_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *BatchDataProvider_GetBatchL2Data_Call) RunAndReturn(run func([]uint64, []common.Hash, []byte) ([][]byte, error)) *BatchDataProvider_GetBatchL2Data_Call { + _c.Call.Return(run) + return _c +} + +// NewBatchDataProvider creates a new instance of BatchDataProvider. 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 NewBatchDataProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *BatchDataProvider { + mock := &BatchDataProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/dataavailability/mocks_da/da_backender.go b/dataavailability/mocks_da/da_backender.go new file mode 100644 index 00000000..773e447c --- /dev/null +++ b/dataavailability/mocks_da/da_backender.go @@ -0,0 +1,263 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + etherman "github.com/0xPolygon/cdk/etherman" + + mock "github.com/stretchr/testify/mock" +) + +// DABackender is an autogenerated mock type for the DABackender type +type DABackender struct { + mock.Mock +} + +type DABackender_Expecter struct { + mock *mock.Mock +} + +func (_m *DABackender) EXPECT() *DABackender_Expecter { + return &DABackender_Expecter{mock: &_m.Mock} +} + +// GetSequence provides a mock function with given fields: ctx, batchHashes, dataAvailabilityMessage +func (_m *DABackender) GetSequence(ctx context.Context, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) { + ret := _m.Called(ctx, batchHashes, dataAvailabilityMessage) + + if len(ret) == 0 { + panic("no return value specified for GetSequence") + } + + var r0 [][]byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []common.Hash, []byte) ([][]byte, error)); ok { + return rf(ctx, batchHashes, dataAvailabilityMessage) + } + if rf, ok := ret.Get(0).(func(context.Context, []common.Hash, []byte) [][]byte); ok { + r0 = rf(ctx, batchHashes, dataAvailabilityMessage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([][]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []common.Hash, []byte) error); ok { + r1 = rf(ctx, batchHashes, dataAvailabilityMessage) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DABackender_GetSequence_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSequence' +type DABackender_GetSequence_Call struct { + *mock.Call +} + +// GetSequence is a helper method to define mock.On call +// - ctx context.Context +// - batchHashes []common.Hash +// - dataAvailabilityMessage []byte +func (_e *DABackender_Expecter) GetSequence(ctx interface{}, batchHashes interface{}, dataAvailabilityMessage interface{}) *DABackender_GetSequence_Call { + return &DABackender_GetSequence_Call{Call: _e.mock.On("GetSequence", ctx, batchHashes, dataAvailabilityMessage)} +} + +func (_c *DABackender_GetSequence_Call) Run(run func(ctx context.Context, batchHashes []common.Hash, dataAvailabilityMessage []byte)) *DABackender_GetSequence_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]common.Hash), args[2].([]byte)) + }) + return _c +} + +func (_c *DABackender_GetSequence_Call) Return(_a0 [][]byte, _a1 error) *DABackender_GetSequence_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DABackender_GetSequence_Call) RunAndReturn(run func(context.Context, []common.Hash, []byte) ([][]byte, error)) *DABackender_GetSequence_Call { + _c.Call.Return(run) + return _c +} + +// Init provides a mock function with given fields: +func (_m *DABackender) Init() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Init") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DABackender_Init_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Init' +type DABackender_Init_Call struct { + *mock.Call +} + +// Init is a helper method to define mock.On call +func (_e *DABackender_Expecter) Init() *DABackender_Init_Call { + return &DABackender_Init_Call{Call: _e.mock.On("Init")} +} + +func (_c *DABackender_Init_Call) Run(run func()) *DABackender_Init_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *DABackender_Init_Call) Return(_a0 error) *DABackender_Init_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *DABackender_Init_Call) RunAndReturn(run func() error) *DABackender_Init_Call { + _c.Call.Return(run) + return _c +} + +// PostSequenceBanana provides a mock function with given fields: ctx, sequence +func (_m *DABackender) PostSequenceBanana(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) { + ret := _m.Called(ctx, sequence) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceBanana") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) ([]byte, error)); ok { + return rf(ctx, sequence) + } + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) []byte); ok { + r0 = rf(ctx, sequence) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, etherman.SequenceBanana) error); ok { + r1 = rf(ctx, sequence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DABackender_PostSequenceBanana_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceBanana' +type DABackender_PostSequenceBanana_Call struct { + *mock.Call +} + +// PostSequenceBanana is a helper method to define mock.On call +// - ctx context.Context +// - sequence etherman.SequenceBanana +func (_e *DABackender_Expecter) PostSequenceBanana(ctx interface{}, sequence interface{}) *DABackender_PostSequenceBanana_Call { + return &DABackender_PostSequenceBanana_Call{Call: _e.mock.On("PostSequenceBanana", ctx, sequence)} +} + +func (_c *DABackender_PostSequenceBanana_Call) Run(run func(ctx context.Context, sequence etherman.SequenceBanana)) *DABackender_PostSequenceBanana_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(etherman.SequenceBanana)) + }) + return _c +} + +func (_c *DABackender_PostSequenceBanana_Call) Return(_a0 []byte, _a1 error) *DABackender_PostSequenceBanana_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DABackender_PostSequenceBanana_Call) RunAndReturn(run func(context.Context, etherman.SequenceBanana) ([]byte, error)) *DABackender_PostSequenceBanana_Call { + _c.Call.Return(run) + return _c +} + +// PostSequenceElderberry provides a mock function with given fields: ctx, batchesData +func (_m *DABackender) PostSequenceElderberry(ctx context.Context, batchesData [][]byte) ([]byte, error) { + ret := _m.Called(ctx, batchesData) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceElderberry") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) ([]byte, error)); ok { + return rf(ctx, batchesData) + } + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) []byte); ok { + r0 = rf(ctx, batchesData) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, [][]byte) error); ok { + r1 = rf(ctx, batchesData) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DABackender_PostSequenceElderberry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceElderberry' +type DABackender_PostSequenceElderberry_Call struct { + *mock.Call +} + +// PostSequenceElderberry is a helper method to define mock.On call +// - ctx context.Context +// - batchesData [][]byte +func (_e *DABackender_Expecter) PostSequenceElderberry(ctx interface{}, batchesData interface{}) *DABackender_PostSequenceElderberry_Call { + return &DABackender_PostSequenceElderberry_Call{Call: _e.mock.On("PostSequenceElderberry", ctx, batchesData)} +} + +func (_c *DABackender_PostSequenceElderberry_Call) Run(run func(ctx context.Context, batchesData [][]byte)) *DABackender_PostSequenceElderberry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([][]byte)) + }) + return _c +} + +func (_c *DABackender_PostSequenceElderberry_Call) Return(_a0 []byte, _a1 error) *DABackender_PostSequenceElderberry_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DABackender_PostSequenceElderberry_Call) RunAndReturn(run func(context.Context, [][]byte) ([]byte, error)) *DABackender_PostSequenceElderberry_Call { + _c.Call.Return(run) + return _c +} + +// NewDABackender creates a new instance of DABackender. 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 NewDABackender(t interface { + mock.TestingT + Cleanup(func()) +}) *DABackender { + mock := &DABackender{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/dataavailability/mocks_da/data_manager.go b/dataavailability/mocks_da/data_manager.go new file mode 100644 index 00000000..34345d71 --- /dev/null +++ b/dataavailability/mocks_da/data_manager.go @@ -0,0 +1,218 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + etherman "github.com/0xPolygon/cdk/etherman" + + mock "github.com/stretchr/testify/mock" +) + +// DataManager is an autogenerated mock type for the DataManager type +type DataManager struct { + mock.Mock +} + +type DataManager_Expecter struct { + mock *mock.Mock +} + +func (_m *DataManager) EXPECT() *DataManager_Expecter { + return &DataManager_Expecter{mock: &_m.Mock} +} + +// GetBatchL2Data provides a mock function with given fields: batchNum, batchHashes, dataAvailabilityMessage +func (_m *DataManager) GetBatchL2Data(batchNum []uint64, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) { + ret := _m.Called(batchNum, batchHashes, dataAvailabilityMessage) + + if len(ret) == 0 { + panic("no return value specified for GetBatchL2Data") + } + + var r0 [][]byte + var r1 error + if rf, ok := ret.Get(0).(func([]uint64, []common.Hash, []byte) ([][]byte, error)); ok { + return rf(batchNum, batchHashes, dataAvailabilityMessage) + } + if rf, ok := ret.Get(0).(func([]uint64, []common.Hash, []byte) [][]byte); ok { + r0 = rf(batchNum, batchHashes, dataAvailabilityMessage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([][]byte) + } + } + + if rf, ok := ret.Get(1).(func([]uint64, []common.Hash, []byte) error); ok { + r1 = rf(batchNum, batchHashes, dataAvailabilityMessage) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DataManager_GetBatchL2Data_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBatchL2Data' +type DataManager_GetBatchL2Data_Call struct { + *mock.Call +} + +// GetBatchL2Data is a helper method to define mock.On call +// - batchNum []uint64 +// - batchHashes []common.Hash +// - dataAvailabilityMessage []byte +func (_e *DataManager_Expecter) GetBatchL2Data(batchNum interface{}, batchHashes interface{}, dataAvailabilityMessage interface{}) *DataManager_GetBatchL2Data_Call { + return &DataManager_GetBatchL2Data_Call{Call: _e.mock.On("GetBatchL2Data", batchNum, batchHashes, dataAvailabilityMessage)} +} + +func (_c *DataManager_GetBatchL2Data_Call) Run(run func(batchNum []uint64, batchHashes []common.Hash, dataAvailabilityMessage []byte)) *DataManager_GetBatchL2Data_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]uint64), args[1].([]common.Hash), args[2].([]byte)) + }) + return _c +} + +func (_c *DataManager_GetBatchL2Data_Call) Return(_a0 [][]byte, _a1 error) *DataManager_GetBatchL2Data_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DataManager_GetBatchL2Data_Call) RunAndReturn(run func([]uint64, []common.Hash, []byte) ([][]byte, error)) *DataManager_GetBatchL2Data_Call { + _c.Call.Return(run) + return _c +} + +// PostSequenceBanana provides a mock function with given fields: ctx, sequence +func (_m *DataManager) PostSequenceBanana(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) { + ret := _m.Called(ctx, sequence) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceBanana") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) ([]byte, error)); ok { + return rf(ctx, sequence) + } + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) []byte); ok { + r0 = rf(ctx, sequence) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, etherman.SequenceBanana) error); ok { + r1 = rf(ctx, sequence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DataManager_PostSequenceBanana_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceBanana' +type DataManager_PostSequenceBanana_Call struct { + *mock.Call +} + +// PostSequenceBanana is a helper method to define mock.On call +// - ctx context.Context +// - sequence etherman.SequenceBanana +func (_e *DataManager_Expecter) PostSequenceBanana(ctx interface{}, sequence interface{}) *DataManager_PostSequenceBanana_Call { + return &DataManager_PostSequenceBanana_Call{Call: _e.mock.On("PostSequenceBanana", ctx, sequence)} +} + +func (_c *DataManager_PostSequenceBanana_Call) Run(run func(ctx context.Context, sequence etherman.SequenceBanana)) *DataManager_PostSequenceBanana_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(etherman.SequenceBanana)) + }) + return _c +} + +func (_c *DataManager_PostSequenceBanana_Call) Return(_a0 []byte, _a1 error) *DataManager_PostSequenceBanana_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DataManager_PostSequenceBanana_Call) RunAndReturn(run func(context.Context, etherman.SequenceBanana) ([]byte, error)) *DataManager_PostSequenceBanana_Call { + _c.Call.Return(run) + return _c +} + +// PostSequenceElderberry provides a mock function with given fields: ctx, batchesData +func (_m *DataManager) PostSequenceElderberry(ctx context.Context, batchesData [][]byte) ([]byte, error) { + ret := _m.Called(ctx, batchesData) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceElderberry") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) ([]byte, error)); ok { + return rf(ctx, batchesData) + } + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) []byte); ok { + r0 = rf(ctx, batchesData) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, [][]byte) error); ok { + r1 = rf(ctx, batchesData) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DataManager_PostSequenceElderberry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceElderberry' +type DataManager_PostSequenceElderberry_Call struct { + *mock.Call +} + +// PostSequenceElderberry is a helper method to define mock.On call +// - ctx context.Context +// - batchesData [][]byte +func (_e *DataManager_Expecter) PostSequenceElderberry(ctx interface{}, batchesData interface{}) *DataManager_PostSequenceElderberry_Call { + return &DataManager_PostSequenceElderberry_Call{Call: _e.mock.On("PostSequenceElderberry", ctx, batchesData)} +} + +func (_c *DataManager_PostSequenceElderberry_Call) Run(run func(ctx context.Context, batchesData [][]byte)) *DataManager_PostSequenceElderberry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([][]byte)) + }) + return _c +} + +func (_c *DataManager_PostSequenceElderberry_Call) Return(_a0 []byte, _a1 error) *DataManager_PostSequenceElderberry_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DataManager_PostSequenceElderberry_Call) RunAndReturn(run func(context.Context, [][]byte) ([]byte, error)) *DataManager_PostSequenceElderberry_Call { + _c.Call.Return(run) + return _c +} + +// NewDataManager creates a new instance of DataManager. 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 NewDataManager(t interface { + mock.TestingT + Cleanup(func()) +}) *DataManager { + mock := &DataManager{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/dataavailability/mocks_da/func_sign_type.go b/dataavailability/mocks_da/func_sign_type.go new file mode 100644 index 00000000..6a343269 --- /dev/null +++ b/dataavailability/mocks_da/func_sign_type.go @@ -0,0 +1,94 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + client "github.com/0xPolygon/cdk-data-availability/client" + + mock "github.com/stretchr/testify/mock" +) + +// FuncSignType is an autogenerated mock type for the funcSignType type +type FuncSignType struct { + mock.Mock +} + +type FuncSignType_Expecter struct { + mock *mock.Mock +} + +func (_m *FuncSignType) EXPECT() *FuncSignType_Expecter { + return &FuncSignType_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: c +func (_m *FuncSignType) Execute(c client.Client) ([]byte, error) { + ret := _m.Called(c) + + if len(ret) == 0 { + panic("no return value specified for Execute") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(client.Client) ([]byte, error)); ok { + return rf(c) + } + if rf, ok := ret.Get(0).(func(client.Client) []byte); ok { + r0 = rf(c) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(client.Client) error); ok { + r1 = rf(c) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FuncSignType_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type FuncSignType_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - c client.Client +func (_e *FuncSignType_Expecter) Execute(c interface{}) *FuncSignType_Execute_Call { + return &FuncSignType_Execute_Call{Call: _e.mock.On("Execute", c)} +} + +func (_c *FuncSignType_Execute_Call) Run(run func(c client.Client)) *FuncSignType_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(client.Client)) + }) + return _c +} + +func (_c *FuncSignType_Execute_Call) Return(_a0 []byte, _a1 error) *FuncSignType_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *FuncSignType_Execute_Call) RunAndReturn(run func(client.Client) ([]byte, error)) *FuncSignType_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewFuncSignType creates a new instance of FuncSignType. 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 NewFuncSignType(t interface { + mock.TestingT + Cleanup(func()) +}) *FuncSignType { + mock := &FuncSignType{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/dataavailability/mocks_da/sequence_retriever.go b/dataavailability/mocks_da/sequence_retriever.go new file mode 100644 index 00000000..f82d9a70 --- /dev/null +++ b/dataavailability/mocks_da/sequence_retriever.go @@ -0,0 +1,98 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" +) + +// SequenceRetriever is an autogenerated mock type for the SequenceRetriever type +type SequenceRetriever struct { + mock.Mock +} + +type SequenceRetriever_Expecter struct { + mock *mock.Mock +} + +func (_m *SequenceRetriever) EXPECT() *SequenceRetriever_Expecter { + return &SequenceRetriever_Expecter{mock: &_m.Mock} +} + +// GetSequence provides a mock function with given fields: ctx, batchHashes, dataAvailabilityMessage +func (_m *SequenceRetriever) GetSequence(ctx context.Context, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) { + ret := _m.Called(ctx, batchHashes, dataAvailabilityMessage) + + if len(ret) == 0 { + panic("no return value specified for GetSequence") + } + + var r0 [][]byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []common.Hash, []byte) ([][]byte, error)); ok { + return rf(ctx, batchHashes, dataAvailabilityMessage) + } + if rf, ok := ret.Get(0).(func(context.Context, []common.Hash, []byte) [][]byte); ok { + r0 = rf(ctx, batchHashes, dataAvailabilityMessage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([][]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []common.Hash, []byte) error); ok { + r1 = rf(ctx, batchHashes, dataAvailabilityMessage) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SequenceRetriever_GetSequence_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSequence' +type SequenceRetriever_GetSequence_Call struct { + *mock.Call +} + +// GetSequence is a helper method to define mock.On call +// - ctx context.Context +// - batchHashes []common.Hash +// - dataAvailabilityMessage []byte +func (_e *SequenceRetriever_Expecter) GetSequence(ctx interface{}, batchHashes interface{}, dataAvailabilityMessage interface{}) *SequenceRetriever_GetSequence_Call { + return &SequenceRetriever_GetSequence_Call{Call: _e.mock.On("GetSequence", ctx, batchHashes, dataAvailabilityMessage)} +} + +func (_c *SequenceRetriever_GetSequence_Call) Run(run func(ctx context.Context, batchHashes []common.Hash, dataAvailabilityMessage []byte)) *SequenceRetriever_GetSequence_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]common.Hash), args[2].([]byte)) + }) + return _c +} + +func (_c *SequenceRetriever_GetSequence_Call) Return(_a0 [][]byte, _a1 error) *SequenceRetriever_GetSequence_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SequenceRetriever_GetSequence_Call) RunAndReturn(run func(context.Context, []common.Hash, []byte) ([][]byte, error)) *SequenceRetriever_GetSequence_Call { + _c.Call.Return(run) + return _c +} + +// NewSequenceRetriever creates a new instance of SequenceRetriever. 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 NewSequenceRetriever(t interface { + mock.TestingT + Cleanup(func()) +}) *SequenceRetriever { + mock := &SequenceRetriever{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/dataavailability/mocks_da/sequence_sender.go b/dataavailability/mocks_da/sequence_sender.go new file mode 100644 index 00000000..f1e44741 --- /dev/null +++ b/dataavailability/mocks_da/sequence_sender.go @@ -0,0 +1,156 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + context "context" + + etherman "github.com/0xPolygon/cdk/etherman" + + mock "github.com/stretchr/testify/mock" +) + +// SequenceSender is an autogenerated mock type for the SequenceSender type +type SequenceSender struct { + mock.Mock +} + +type SequenceSender_Expecter struct { + mock *mock.Mock +} + +func (_m *SequenceSender) EXPECT() *SequenceSender_Expecter { + return &SequenceSender_Expecter{mock: &_m.Mock} +} + +// PostSequenceBanana provides a mock function with given fields: ctx, sequence +func (_m *SequenceSender) PostSequenceBanana(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) { + ret := _m.Called(ctx, sequence) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceBanana") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) ([]byte, error)); ok { + return rf(ctx, sequence) + } + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) []byte); ok { + r0 = rf(ctx, sequence) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, etherman.SequenceBanana) error); ok { + r1 = rf(ctx, sequence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SequenceSender_PostSequenceBanana_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceBanana' +type SequenceSender_PostSequenceBanana_Call struct { + *mock.Call +} + +// PostSequenceBanana is a helper method to define mock.On call +// - ctx context.Context +// - sequence etherman.SequenceBanana +func (_e *SequenceSender_Expecter) PostSequenceBanana(ctx interface{}, sequence interface{}) *SequenceSender_PostSequenceBanana_Call { + return &SequenceSender_PostSequenceBanana_Call{Call: _e.mock.On("PostSequenceBanana", ctx, sequence)} +} + +func (_c *SequenceSender_PostSequenceBanana_Call) Run(run func(ctx context.Context, sequence etherman.SequenceBanana)) *SequenceSender_PostSequenceBanana_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(etherman.SequenceBanana)) + }) + return _c +} + +func (_c *SequenceSender_PostSequenceBanana_Call) Return(_a0 []byte, _a1 error) *SequenceSender_PostSequenceBanana_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SequenceSender_PostSequenceBanana_Call) RunAndReturn(run func(context.Context, etherman.SequenceBanana) ([]byte, error)) *SequenceSender_PostSequenceBanana_Call { + _c.Call.Return(run) + return _c +} + +// PostSequenceElderberry provides a mock function with given fields: ctx, batchesData +func (_m *SequenceSender) PostSequenceElderberry(ctx context.Context, batchesData [][]byte) ([]byte, error) { + ret := _m.Called(ctx, batchesData) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceElderberry") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) ([]byte, error)); ok { + return rf(ctx, batchesData) + } + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) []byte); ok { + r0 = rf(ctx, batchesData) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, [][]byte) error); ok { + r1 = rf(ctx, batchesData) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SequenceSender_PostSequenceElderberry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceElderberry' +type SequenceSender_PostSequenceElderberry_Call struct { + *mock.Call +} + +// PostSequenceElderberry is a helper method to define mock.On call +// - ctx context.Context +// - batchesData [][]byte +func (_e *SequenceSender_Expecter) PostSequenceElderberry(ctx interface{}, batchesData interface{}) *SequenceSender_PostSequenceElderberry_Call { + return &SequenceSender_PostSequenceElderberry_Call{Call: _e.mock.On("PostSequenceElderberry", ctx, batchesData)} +} + +func (_c *SequenceSender_PostSequenceElderberry_Call) Run(run func(ctx context.Context, batchesData [][]byte)) *SequenceSender_PostSequenceElderberry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([][]byte)) + }) + return _c +} + +func (_c *SequenceSender_PostSequenceElderberry_Call) Return(_a0 []byte, _a1 error) *SequenceSender_PostSequenceElderberry_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SequenceSender_PostSequenceElderberry_Call) RunAndReturn(run func(context.Context, [][]byte) ([]byte, error)) *SequenceSender_PostSequenceElderberry_Call { + _c.Call.Return(run) + return _c +} + +// NewSequenceSender creates a new instance of SequenceSender. 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 NewSequenceSender(t interface { + mock.TestingT + Cleanup(func()) +}) *SequenceSender { + mock := &SequenceSender{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/dataavailability/mocks_da/sequence_sender_banana.go b/dataavailability/mocks_da/sequence_sender_banana.go new file mode 100644 index 00000000..aca7b1a3 --- /dev/null +++ b/dataavailability/mocks_da/sequence_sender_banana.go @@ -0,0 +1,97 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + context "context" + + etherman "github.com/0xPolygon/cdk/etherman" + + mock "github.com/stretchr/testify/mock" +) + +// SequenceSenderBanana is an autogenerated mock type for the SequenceSenderBanana type +type SequenceSenderBanana struct { + mock.Mock +} + +type SequenceSenderBanana_Expecter struct { + mock *mock.Mock +} + +func (_m *SequenceSenderBanana) EXPECT() *SequenceSenderBanana_Expecter { + return &SequenceSenderBanana_Expecter{mock: &_m.Mock} +} + +// PostSequenceBanana provides a mock function with given fields: ctx, sequence +func (_m *SequenceSenderBanana) PostSequenceBanana(ctx context.Context, sequence etherman.SequenceBanana) ([]byte, error) { + ret := _m.Called(ctx, sequence) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceBanana") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) ([]byte, error)); ok { + return rf(ctx, sequence) + } + if rf, ok := ret.Get(0).(func(context.Context, etherman.SequenceBanana) []byte); ok { + r0 = rf(ctx, sequence) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, etherman.SequenceBanana) error); ok { + r1 = rf(ctx, sequence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SequenceSenderBanana_PostSequenceBanana_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceBanana' +type SequenceSenderBanana_PostSequenceBanana_Call struct { + *mock.Call +} + +// PostSequenceBanana is a helper method to define mock.On call +// - ctx context.Context +// - sequence etherman.SequenceBanana +func (_e *SequenceSenderBanana_Expecter) PostSequenceBanana(ctx interface{}, sequence interface{}) *SequenceSenderBanana_PostSequenceBanana_Call { + return &SequenceSenderBanana_PostSequenceBanana_Call{Call: _e.mock.On("PostSequenceBanana", ctx, sequence)} +} + +func (_c *SequenceSenderBanana_PostSequenceBanana_Call) Run(run func(ctx context.Context, sequence etherman.SequenceBanana)) *SequenceSenderBanana_PostSequenceBanana_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(etherman.SequenceBanana)) + }) + return _c +} + +func (_c *SequenceSenderBanana_PostSequenceBanana_Call) Return(_a0 []byte, _a1 error) *SequenceSenderBanana_PostSequenceBanana_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SequenceSenderBanana_PostSequenceBanana_Call) RunAndReturn(run func(context.Context, etherman.SequenceBanana) ([]byte, error)) *SequenceSenderBanana_PostSequenceBanana_Call { + _c.Call.Return(run) + return _c +} + +// NewSequenceSenderBanana creates a new instance of SequenceSenderBanana. 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 NewSequenceSenderBanana(t interface { + mock.TestingT + Cleanup(func()) +}) *SequenceSenderBanana { + mock := &SequenceSenderBanana{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/dataavailability/mocks_da/sequence_sender_elderberry.go b/dataavailability/mocks_da/sequence_sender_elderberry.go new file mode 100644 index 00000000..3816fa1b --- /dev/null +++ b/dataavailability/mocks_da/sequence_sender_elderberry.go @@ -0,0 +1,95 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_da + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// SequenceSenderElderberry is an autogenerated mock type for the SequenceSenderElderberry type +type SequenceSenderElderberry struct { + mock.Mock +} + +type SequenceSenderElderberry_Expecter struct { + mock *mock.Mock +} + +func (_m *SequenceSenderElderberry) EXPECT() *SequenceSenderElderberry_Expecter { + return &SequenceSenderElderberry_Expecter{mock: &_m.Mock} +} + +// PostSequenceElderberry provides a mock function with given fields: ctx, batchesData +func (_m *SequenceSenderElderberry) PostSequenceElderberry(ctx context.Context, batchesData [][]byte) ([]byte, error) { + ret := _m.Called(ctx, batchesData) + + if len(ret) == 0 { + panic("no return value specified for PostSequenceElderberry") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) ([]byte, error)); ok { + return rf(ctx, batchesData) + } + if rf, ok := ret.Get(0).(func(context.Context, [][]byte) []byte); ok { + r0 = rf(ctx, batchesData) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, [][]byte) error); ok { + r1 = rf(ctx, batchesData) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SequenceSenderElderberry_PostSequenceElderberry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostSequenceElderberry' +type SequenceSenderElderberry_PostSequenceElderberry_Call struct { + *mock.Call +} + +// PostSequenceElderberry is a helper method to define mock.On call +// - ctx context.Context +// - batchesData [][]byte +func (_e *SequenceSenderElderberry_Expecter) PostSequenceElderberry(ctx interface{}, batchesData interface{}) *SequenceSenderElderberry_PostSequenceElderberry_Call { + return &SequenceSenderElderberry_PostSequenceElderberry_Call{Call: _e.mock.On("PostSequenceElderberry", ctx, batchesData)} +} + +func (_c *SequenceSenderElderberry_PostSequenceElderberry_Call) Run(run func(ctx context.Context, batchesData [][]byte)) *SequenceSenderElderberry_PostSequenceElderberry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([][]byte)) + }) + return _c +} + +func (_c *SequenceSenderElderberry_PostSequenceElderberry_Call) Return(_a0 []byte, _a1 error) *SequenceSenderElderberry_PostSequenceElderberry_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SequenceSenderElderberry_PostSequenceElderberry_Call) RunAndReturn(run func(context.Context, [][]byte) ([]byte, error)) *SequenceSenderElderberry_PostSequenceElderberry_Call { + _c.Call.Return(run) + return _c +} + +// NewSequenceSenderElderberry creates a new instance of SequenceSenderElderberry. 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 NewSequenceSenderElderberry(t interface { + mock.TestingT + Cleanup(func()) +}) *SequenceSenderElderberry { + mock := &SequenceSenderElderberry{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/etherman/aggregator.go b/etherman/aggregator.go index ed8d3575..87384a95 100644 --- a/etherman/aggregator.go +++ b/etherman/aggregator.go @@ -41,7 +41,7 @@ func (etherMan *Client) BuildTrustedVerifyBatchesTxData(lastVerifiedBatch, newVe const pendStateNum = 0 // TODO hardcoded for now until we implement the pending state feature - tx, err := etherMan.Contracts.Banana.RollupManager.Contract().VerifyBatchesTrustedAggregator( + tx, err := etherMan.Contracts.Banana.RollupManager.VerifyBatchesTrustedAggregator( &opts, etherMan.RollupID, pendStateNum, @@ -64,7 +64,7 @@ func (etherMan *Client) BuildTrustedVerifyBatchesTxData(lastVerifiedBatch, newVe // GetBatchAccInputHash gets the batch accumulated input hash from the ethereum func (etherman *Client) GetBatchAccInputHash(ctx context.Context, batchNumber uint64) (common.Hash, error) { - rollupData, err := etherman.Contracts.Banana.RollupManager.Contract().GetRollupSequencedBatches(&bind.CallOpts{Pending: false}, etherman.RollupID, batchNumber) + rollupData, err := etherman.Contracts.Banana.RollupManager.GetRollupSequencedBatches(&bind.CallOpts{Pending: false}, etherman.RollupID, batchNumber) if err != nil { return common.Hash{}, err } diff --git a/etherman/contracts/base.go b/etherman/contracts/base.go index ebe17711..c2aabd02 100644 --- a/etherman/contracts/base.go +++ b/etherman/contracts/base.go @@ -1,52 +1,55 @@ package contracts import ( + "reflect" + "github.com/0xPolygon/cdk/log" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ) -type ContractBase[T any] struct { - contract *T +type ContractBase struct { address common.Address contractName NameType version VersionType } -type contractConstructorFunc[T any] func(address common.Address, backend bind.ContractBackend) (*T, error) +type contractConstructorFunc[T any] func(address common.Address, backend bind.ContractBackend) (T, error) -func NewContractBase[T any](constructor contractConstructorFunc[T], address common.Address, backend bind.ContractBackend, - name NameType, version VersionType) (*ContractBase[T], error) { - contractBind, err := constructor(address, backend) - if err != nil { - log.Errorf("failed to bind contract %s at address %s. Err:%w", name, address.String(), err) - return nil, err - } - - return &ContractBase[T]{ - contract: contractBind, +func NewContractBase(address common.Address, backend bind.ContractBackend, + name NameType, version VersionType) *ContractBase { + return &ContractBase{ address: address, contractName: name, version: version, - }, nil -} - -func (e *ContractBase[T]) Contract() *T { - return e.contract + } } -func (e *ContractBase[T]) Address() common.Address { +func (e *ContractBase) Address() common.Address { return e.address } -func (e *ContractBase[T]) Name() string { +func (e *ContractBase) Name() string { return string(e.contractName) } -func (e *ContractBase[T]) Version() string { +func (e *ContractBase) Version() string { return string(e.version) } -func (e *ContractBase[T]) String() string { +func (e *ContractBase) String() string { return e.Version() + "/" + e.Name() + "@" + e.Address().String() } + +func NewContractMagic[C any, T any](constructor contractConstructorFunc[T], address common.Address, backend bind.ContractBackend, name NameType, version VersionType) (*C, error) { + contractBind, err := constructor(address, backend) + if err != nil { + log.Errorf("failed to bind contract %s at address %s. Err:%w", name, address.String(), err) + return nil, err + } + tmp := new(C) + values := reflect.ValueOf(tmp).Elem() + values.FieldByIndex([]int{0}).Set(reflect.ValueOf(contractBind)) + values.FieldByIndex([]int{1}).Set(reflect.ValueOf(NewContractBase(address, backend, name, version))) + return tmp, nil +} diff --git a/etherman/contracts/base_test.go b/etherman/contracts/base_test.go new file mode 100644 index 00000000..2261220a --- /dev/null +++ b/etherman/contracts/base_test.go @@ -0,0 +1,16 @@ +package contracts_test + +import ( + "testing" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/banana/polygonzkevmglobalexitrootv2" + "github.com/0xPolygon/cdk/etherman/contracts" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestNewContractMagic(t *testing.T) { + ger, err := contracts.NewContractMagic[contracts.GlobalExitRootBananaType](polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2, common.Address{}, nil, contracts.ContractNameGlobalExitRoot, contracts.VersionBanana) + require.NoError(t, err) + require.NotNil(t, ger) +} diff --git a/etherman/contracts/contracts_banana.go b/etherman/contracts/contracts_banana.go index d01e9782..39e3eb12 100644 --- a/etherman/contracts/contracts_banana.go +++ b/etherman/contracts/contracts_banana.go @@ -8,9 +8,20 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" ) -type GlobalExitRootBananaType = ContractBase[polygonzkevmglobalexitrootv2.Polygonzkevmglobalexitrootv2] -type RollupBananaType = ContractBase[polygonvalidiumetrog.Polygonvalidiumetrog] -type RollupManagerBananaType = ContractBase[polygonrollupmanager.Polygonrollupmanager] +type GlobalExitRootBananaType struct { + *polygonzkevmglobalexitrootv2.Polygonzkevmglobalexitrootv2 + *ContractBase +} + +type RollupManagerBananaType struct { + *polygonrollupmanager.Polygonrollupmanager + *ContractBase +} + +type RollupBananaType struct { + *polygonvalidiumetrog.Polygonvalidiumetrog + *ContractBase +} type ContractsBanana struct { GlobalExitRoot GlobalExitRootBananaType @@ -19,16 +30,17 @@ type ContractsBanana struct { } func NewContractsBanana(cfg config.L1Config, backend bind.ContractBackend) (*ContractsBanana, error) { - ger, err := NewContractBase(polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2, cfg.GlobalExitRootManagerAddr, backend, ContractNameGlobalExitRoot, VersionBanana) + + ger, err := NewContractMagic[GlobalExitRootBananaType](polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2, cfg.GlobalExitRootManagerAddr, backend, ContractNameGlobalExitRoot, VersionBanana) if err != nil { return nil, err } - rollup, err := NewContractBase(polygonvalidiumetrog.NewPolygonvalidiumetrog, cfg.ZkEVMAddr, backend, ContractNameRollup, VersionBanana) + rollup, err := NewContractMagic[RollupBananaType](polygonvalidiumetrog.NewPolygonvalidiumetrog, cfg.ZkEVMAddr, backend, ContractNameRollup, VersionBanana) if err != nil { return nil, err } - rollupManager, err := NewContractBase(polygonrollupmanager.NewPolygonrollupmanager, cfg.RollupManagerAddr, backend, ContractNameRollupManager, VersionBanana) + rollupManager, err := NewContractMagic[RollupManagerBananaType](polygonrollupmanager.NewPolygonrollupmanager, cfg.RollupManagerAddr, backend, ContractNameRollupManager, VersionBanana) if err != nil { return nil, err } diff --git a/etherman/contracts/contracts_elderberry.go b/etherman/contracts/contracts_elderberry.go index 4127ff11..45f53d14 100644 --- a/etherman/contracts/contracts_elderberry.go +++ b/etherman/contracts/contracts_elderberry.go @@ -8,9 +8,20 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" ) -type GlobalExitRootElderberryType = ContractBase[polygonzkevmglobalexitrootv2.Polygonzkevmglobalexitrootv2] -type RollupElderberryType = ContractBase[polygonvalidiumetrog.Polygonvalidiumetrog] -type RollupManagerElderberryType = ContractBase[polygonrollupmanager.Polygonrollupmanager] +type GlobalExitRootElderberryType struct { + *polygonzkevmglobalexitrootv2.Polygonzkevmglobalexitrootv2 + *ContractBase +} + +type RollupElderberryType struct { + *polygonvalidiumetrog.Polygonvalidiumetrog + *ContractBase +} + +type RollupManagerElderberryType struct { + *polygonrollupmanager.Polygonrollupmanager + *ContractBase +} type ContractsElderberry struct { GlobalExitRoot GlobalExitRootElderberryType @@ -19,16 +30,16 @@ type ContractsElderberry struct { } func NewContractsElderberry(cfg config.L1Config, backend bind.ContractBackend) (*ContractsElderberry, error) { - ger, err := NewContractBase(polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2, cfg.GlobalExitRootManagerAddr, backend, ContractNameGlobalExitRoot, VersionElderberry) + ger, err := NewContractMagic[GlobalExitRootElderberryType](polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2, cfg.GlobalExitRootManagerAddr, backend, ContractNameGlobalExitRoot, VersionElderberry) if err != nil { return nil, err } - rollup, err := NewContractBase(polygonvalidiumetrog.NewPolygonvalidiumetrog, cfg.ZkEVMAddr, backend, ContractNameRollup, VersionElderberry) + rollup, err := NewContractMagic[RollupElderberryType](polygonvalidiumetrog.NewPolygonvalidiumetrog, cfg.ZkEVMAddr, backend, ContractNameRollup, VersionElderberry) if err != nil { return nil, err } - rollupManager, err := NewContractBase(polygonrollupmanager.NewPolygonrollupmanager, cfg.RollupManagerAddr, backend, ContractNameRollupManager, VersionElderberry) + rollupManager, err := NewContractMagic[RollupManagerElderberryType](polygonrollupmanager.NewPolygonrollupmanager, cfg.RollupManagerAddr, backend, ContractNameRollupManager, VersionElderberry) if err != nil { return nil, err } diff --git a/etherman/etherman.go b/etherman/etherman.go index e47477c8..dc639ff4 100644 --- a/etherman/etherman.go +++ b/etherman/etherman.go @@ -93,7 +93,7 @@ func NewClient(cfg config.Config, l1Config config.L1Config, commonConfig cdkcomm } log.Info(contracts.String()) // Get RollupID - rollupID, err := contracts.Banana.RollupManager.Contract().RollupAddressToID(&bind.CallOpts{Pending: false}, l1Config.ZkEVMAddr) + rollupID, err := contracts.Banana.RollupManager.RollupAddressToID(&bind.CallOpts{Pending: false}, l1Config.ZkEVMAddr) if err != nil { log.Errorf("error getting rollupID from %s : %+v", contracts.Banana.RollupManager.String(), err) return nil, err @@ -114,7 +114,7 @@ func NewClient(cfg config.Config, l1Config config.L1Config, commonConfig cdkcomm } if commonConfig.IsValidiumMode { - dapAddr, err := contracts.Banana.Rollup.Contract().DataAvailabilityProtocol(&bind.CallOpts{Pending: false}) + dapAddr, err := contracts.Banana.Rollup.DataAvailabilityProtocol(&bind.CallOpts{Pending: false}) if err != nil { return nil, err } @@ -148,7 +148,7 @@ func (etherMan *Client) WaitTxToBeMined(ctx context.Context, tx *types.Transacti // GetSendSequenceFee get super/trusted sequencer fee func (etherMan *Client) GetSendSequenceFee(numBatches uint64) (*big.Int, error) { - f, err := etherMan.Contracts.Banana.RollupManager.Contract().GetBatchFee(&bind.CallOpts{Pending: false}) + f, err := etherMan.Contracts.Banana.RollupManager.GetBatchFee(&bind.CallOpts{Pending: false}) if err != nil { return nil, err } @@ -158,7 +158,7 @@ func (etherMan *Client) GetSendSequenceFee(numBatches uint64) (*big.Int, error) // TrustedSequencer gets trusted sequencer address func (etherMan *Client) TrustedSequencer() (common.Address, error) { - return etherMan.Contracts.Banana.Rollup.Contract().TrustedSequencer(&bind.CallOpts{Pending: false}) + return etherMan.Contracts.Banana.Rollup.TrustedSequencer(&bind.CallOpts{Pending: false}) } // HeaderByNumber returns a block header from the current canonical chain. If number is @@ -181,7 +181,7 @@ func (etherMan *Client) EthBlockByNumber(ctx context.Context, blockNumber uint64 // GetLatestBatchNumber function allows to retrieve the latest proposed batch in the smc func (etherMan *Client) GetLatestBatchNumber() (uint64, error) { - rollupData, err := etherMan.Contracts.Banana.RollupManager.Contract().RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) + rollupData, err := etherMan.Contracts.Banana.RollupManager.RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) if err != nil { return 0, err } @@ -223,7 +223,7 @@ func (etherMan *Client) GetLatestBlockTimestamp(ctx context.Context) (uint64, er // GetLatestVerifiedBatchNum gets latest verified batch from ethereum func (etherMan *Client) GetLatestVerifiedBatchNum() (uint64, error) { - rollupData, err := etherMan.Contracts.Banana.RollupManager.Contract().RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) + rollupData, err := etherMan.Contracts.Banana.RollupManager.RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) if err != nil { return 0, err } @@ -242,12 +242,12 @@ func (etherMan *Client) GetTxReceipt(ctx context.Context, txHash common.Hash) (* // GetTrustedSequencerURL Gets the trusted sequencer url from rollup smc func (etherMan *Client) GetTrustedSequencerURL() (string, error) { - return etherMan.Contracts.Banana.Rollup.Contract().TrustedSequencerURL(&bind.CallOpts{Pending: false}) + return etherMan.Contracts.Banana.Rollup.TrustedSequencerURL(&bind.CallOpts{Pending: false}) } // GetL2ChainID returns L2 Chain ID func (etherMan *Client) GetL2ChainID() (uint64, error) { - rollupData, err := etherMan.Contracts.Banana.RollupManager.Contract().RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) + rollupData, err := etherMan.Contracts.Banana.RollupManager.RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) log.Debug("chainID read from rollupManager: ", rollupData.ChainID) if err != nil { log.Debug("error from rollupManager: ", err) @@ -397,7 +397,7 @@ func (etherMan *Client) GetLatestBlockHeader(ctx context.Context) (*types.Header // GetDAProtocolAddr returns the address of the data availability protocol func (etherMan *Client) GetDAProtocolAddr() (common.Address, error) { - return etherMan.Contracts.Banana.Rollup.Contract().DataAvailabilityProtocol(&bind.CallOpts{Pending: false}) + return etherMan.Contracts.Banana.Rollup.DataAvailabilityProtocol(&bind.CallOpts{Pending: false}) } // GetDAProtocolName returns the name of the data availability protocol @@ -407,7 +407,7 @@ func (etherMan *Client) GetDAProtocolName() (string, error) { // LastAccInputHash gets the last acc input hash from the SC func (etherMan *Client) LastAccInputHash() (common.Hash, error) { - return etherMan.Contracts.Banana.Rollup.Contract().LastAccInputHash(&bind.CallOpts{Pending: false}) + return etherMan.Contracts.Banana.Rollup.LastAccInputHash(&bind.CallOpts{Pending: false}) } // GetL1InfoRoot gets the L1 info root from the SC @@ -419,7 +419,7 @@ func (etherMan *Client) GetL1InfoRoot(indexL1InfoRoot uint32) (common.Hash, erro ) if indexL1InfoRoot > 0 { - lastL1InfoTreeRoot, err = etherMan.Contracts.Banana.GlobalExitRoot.Contract().L1InfoRootMap(&bind.CallOpts{Pending: false}, indexL1InfoRoot) + lastL1InfoTreeRoot, err = etherMan.Contracts.Banana.GlobalExitRoot.L1InfoRootMap(&bind.CallOpts{Pending: false}, indexL1InfoRoot) if err != nil { log.Errorf("error calling SC globalexitroot L1InfoLeafMap: %v", err) } diff --git a/l1infotreesync/mock_reorgdetector_test.go b/l1infotreesync/mock_reorgdetector_test.go index 22d174d4..18ac7bc8 100644 --- a/l1infotreesync/mock_reorgdetector_test.go +++ b/l1infotreesync/mock_reorgdetector_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package l1infotreesync @@ -21,6 +21,10 @@ type ReorgDetectorMock struct { func (_m *ReorgDetectorMock) AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error { ret := _m.Called(ctx, id, blockNum, blockHash) + if len(ret) == 0 { + panic("no return value specified for AddBlockToTrack") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, uint64, common.Hash) error); ok { r0 = rf(ctx, id, blockNum, blockHash) @@ -35,6 +39,10 @@ func (_m *ReorgDetectorMock) AddBlockToTrack(ctx context.Context, id string, blo func (_m *ReorgDetectorMock) Subscribe(id string) (*reorgdetector.Subscription, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + var r0 *reorgdetector.Subscription var r1 error if rf, ok := ret.Get(0).(func(string) (*reorgdetector.Subscription, error)); ok { @@ -57,13 +65,12 @@ func (_m *ReorgDetectorMock) Subscribe(id string) (*reorgdetector.Subscription, return r0, r1 } -type mockConstructorTestingTNewReorgDetectorMock interface { +// NewReorgDetectorMock creates a new instance of ReorgDetectorMock. 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 NewReorgDetectorMock(t interface { mock.TestingT Cleanup(func()) -} - -// NewReorgDetectorMock creates a new instance of ReorgDetectorMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewReorgDetectorMock(t mockConstructorTestingTNewReorgDetectorMock) *ReorgDetectorMock { +}) *ReorgDetectorMock { mock := &ReorgDetectorMock{} mock.Mock.Test(t) diff --git a/localbridgesync/mock_l2_test.go b/localbridgesync/mock_l2_test.go new file mode 100644 index 00000000..22432fd1 --- /dev/null +++ b/localbridgesync/mock_l2_test.go @@ -0,0 +1,555 @@ +// Code generated by mockery v2.39.0. DO NOT EDIT. + +package localbridgesync + +import ( + context "context" + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + ethereum "github.com/ethereum/go-ethereum" + + mock "github.com/stretchr/testify/mock" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// L2Mock is an autogenerated mock type for the EthClienter type +type L2Mock struct { + mock.Mock +} + +// BlockByHash provides a mock function with given fields: ctx, hash +func (_m *L2Mock) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + ret := _m.Called(ctx, hash) + + if len(ret) == 0 { + panic("no return value specified for BlockByHash") + } + + var r0 *types.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Block, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Block); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.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 +} + +// BlockByNumber provides a mock function with given fields: ctx, number +func (_m *L2Mock) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for BlockByNumber") + } + + var r0 *types.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Block, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Block); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.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 +} + +// BlockNumber provides a mock function with given fields: ctx +func (_m *L2Mock) 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 +} + +// CallContract provides a mock function with given fields: ctx, call, blockNumber +func (_m *L2Mock) 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 +} + +// CodeAt provides a mock function with given fields: ctx, contract, blockNumber +func (_m *L2Mock) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, contract, 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, contract, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { + r0 = rf(ctx, contract, 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, contract, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EstimateGas provides a mock function with given fields: ctx, call +func (_m *L2Mock) 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 +} + +// FilterLogs provides a mock function with given fields: ctx, q +func (_m *L2Mock) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + ret := _m.Called(ctx, q) + + if len(ret) == 0 { + panic("no return value specified for FilterLogs") + } + + var r0 []types.Log + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]types.Log, error)); ok { + return rf(ctx, q) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []types.Log); ok { + r0 = rf(ctx, q) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.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 +} + +// HeaderByHash provides a mock function with given fields: ctx, hash +func (_m *L2Mock) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + ret := _m.Called(ctx, hash) + + if len(ret) == 0 { + panic("no return value specified for HeaderByHash") + } + + var r0 *types.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Header, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Header); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.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 +} + +// HeaderByNumber provides a mock function with given fields: ctx, number +func (_m *L2Mock) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + + var r0 *types.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Header); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingCodeAt provides a mock function with given fields: ctx, account +func (_m *L2Mock) 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 +} + +// PendingNonceAt provides a mock function with given fields: ctx, account +func (_m *L2Mock) 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 +} + +// SendTransaction provides a mock function with given fields: ctx, tx +func (_m *L2Mock) SendTransaction(ctx context.Context, tx *types.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, *types.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch +func (_m *L2Mock) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.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<- types.Log) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- types.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<- types.Log) error); ok { + r1 = rf(ctx, q, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribeNewHead provides a mock function with given fields: ctx, ch +func (_m *L2Mock) SubscribeNewHead(ctx context.Context, ch chan<- *types.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<- *types.Header) (ethereum.Subscription, error)); ok { + return rf(ctx, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.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<- *types.Header) error); ok { + r1 = rf(ctx, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuggestGasPrice provides a mock function with given fields: ctx +func (_m *L2Mock) 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 +} + +// SuggestGasTipCap provides a mock function with given fields: ctx +func (_m *L2Mock) 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 +} + +// TransactionCount provides a mock function with given fields: ctx, blockHash +func (_m *L2Mock) 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 +} + +// TransactionInBlock provides a mock function with given fields: ctx, blockHash, index +func (_m *L2Mock) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { + ret := _m.Called(ctx, blockHash, index) + + if len(ret) == 0 { + panic("no return value specified for TransactionInBlock") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) (*types.Transaction, error)); ok { + return rf(ctx, blockHash, index) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) *types.Transaction); ok { + r0 = rf(ctx, blockHash, index) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.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 +} + +// NewL2Mock creates a new instance of L2Mock. 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 NewL2Mock(t interface { + mock.TestingT + Cleanup(func()) +}) *L2Mock { + mock := &L2Mock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/reorgdetector/mock_eth_client.go b/reorgdetector/mock_eth_client.go index 85376cc4..e0eef607 100644 --- a/reorgdetector/mock_eth_client.go +++ b/reorgdetector/mock_eth_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package reorgdetector @@ -20,6 +20,10 @@ type EthClientMock struct { func (_m *EthClientMock) 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 { @@ -44,6 +48,10 @@ func (_m *EthClientMock) BlockNumber(ctx context.Context) (uint64, error) { func (_m *EthClientMock) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { ret := _m.Called(ctx, number) + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { @@ -66,13 +74,12 @@ func (_m *EthClientMock) HeaderByNumber(ctx context.Context, number *big.Int) (* return r0, r1 } -type mockConstructorTestingTNewEthClientMock interface { +// NewEthClientMock creates a new instance of EthClientMock. 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 NewEthClientMock(t interface { mock.TestingT Cleanup(func()) -} - -// NewEthClientMock creates a new instance of EthClientMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewEthClientMock(t mockConstructorTestingTNewEthClientMock) *EthClientMock { +}) *EthClientMock { mock := &EthClientMock{} mock.Mock.Test(t) diff --git a/sequencesender/sequencesender.go b/sequencesender/sequencesender.go index 961b6f75..941e6db6 100644 --- a/sequencesender/sequencesender.go +++ b/sequencesender/sequencesender.go @@ -520,7 +520,7 @@ func (s *SequenceSender) tryToSendSequence(ctx context.Context) { log.Infof("[SeqSender] sending sequences to L1. From batch %d to batch %d", firstSequence.BatchNumber(), lastSequence.BatchNumber()) log.Infof(sequence.String()) - tx, err := s.TxBuilder.BuildSequenceBatchesTx(ctx, s.cfg.SenderAddress, sequence) + tx, err := s.TxBuilder.BuildSequenceBatchesTx(ctx, sequence) if err != nil { log.Errorf("[SeqSender] error building sequenceBatches tx: %v", err) return diff --git a/sequencesender/txbuilder/banana_base.go b/sequencesender/txbuilder/banana_base.go index 60c6c4a7..93536c6a 100644 --- a/sequencesender/txbuilder/banana_base.go +++ b/sequencesender/txbuilder/banana_base.go @@ -5,7 +5,6 @@ import ( cdkcommon "github.com/0xPolygon/cdk/common" "github.com/0xPolygon/cdk/etherman" - "github.com/0xPolygon/cdk/etherman/contracts" "github.com/0xPolygon/cdk/log" "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" "github.com/0xPolygon/cdk/state/datastream" @@ -13,71 +12,32 @@ import ( "github.com/ethereum/go-ethereum/common" ) -type TxBuilderBananaBase struct { - rollupContract contracts.RollupBananaType - globalExitRootContract contracts.GlobalExitRootBananaType +type rollupBananaBaseContractor interface { + LastAccInputHash(opts *bind.CallOpts) ([32]byte, error) +} + +type globalExitRootBananaContractor interface { + L1InfoRootMap(opts *bind.CallOpts, index uint32) ([32]byte, error) + String() string +} - opts bind.TransactOpts - SenderAddress common.Address +type TxBuilderBananaBase struct { + rollupContract rollupBananaBaseContractor + globalExitRootContract globalExitRootBananaContractor + opts bind.TransactOpts } -func NewTxBuilderBananaBase(rollupContract contracts.RollupBananaType, - gerContract contracts.GlobalExitRootBananaType, - opts bind.TransactOpts, - sender common.Address) *TxBuilderBananaBase { +func NewTxBuilderBananaBase(rollupContract rollupBananaBaseContractor, + gerContract globalExitRootBananaContractor, + opts bind.TransactOpts) *TxBuilderBananaBase { return &TxBuilderBananaBase{ rollupContract: rollupContract, globalExitRootContract: gerContract, opts: opts, - SenderAddress: sender, } } -func convertToSequenceBanana(sequences seqsendertypes.Sequence) (etherman.SequenceBanana, error) { - seqEth, ok := sequences.(*BananaSequence) - if !ok { - log.Error("sequences is not a BananaSequence") - return etherman.SequenceBanana{}, fmt.Errorf("sequences is not a BananaSequence") - } - seqEth.SequenceBanana.Batches = make([]etherman.Batch, len(sequences.Batches())) - for _, batch := range sequences.Batches() { - ethBatch, err := convertToEthermanBatch(batch) - if err != nil { - return etherman.SequenceBanana{}, err - } - seqEth.SequenceBanana.Batches = append(seqEth.SequenceBanana.Batches, ethBatch) - } - return seqEth.SequenceBanana, nil -} - -func convertToEthermanBatch(batch seqsendertypes.Batch) (etherman.Batch, error) { - return etherman.Batch{ - L2Data: batch.L2Data(), - LastCoinbase: batch.LastCoinbase(), - ForcedGlobalExitRoot: batch.ForcedGlobalExitRoot(), - ForcedBlockHashL1: batch.ForcedBlockHashL1(), - ForcedBatchTimestamp: batch.ForcedBatchTimestamp(), - BatchNumber: batch.BatchNumber(), - L1InfoTreeIndex: batch.L1InfoTreeIndex(), - LastL2BLockTimestamp: batch.LastL2BLockTimestamp(), - GlobalExitRoot: batch.GlobalExitRoot(), - }, nil -} - -func convertToEthermanBatches(batch []seqsendertypes.Batch) ([]etherman.Batch, error) { - result := make([]etherman.Batch, len(batch)) - for i, b := range batch { - var err error - result[i], err = convertToEthermanBatch(b) - if err != nil { - return nil, err - } - } - - return result, nil -} - func (t *TxBuilderBananaBase) NewBatchFromL2Block(l2Block *datastream.L2Block) seqsendertypes.Batch { batch := ðerman.Batch{ LastL2BLockTimestamp: l2Block.Timestamp, @@ -90,7 +50,7 @@ func (t *TxBuilderBananaBase) NewBatchFromL2Block(l2Block *datastream.L2Block) s } func (t *TxBuilderBananaBase) NewSequence(batches []seqsendertypes.Batch, coinbase common.Address) (seqsendertypes.Sequence, error) { - ethBatches, err := convertToEthermanBatches(batches) + ethBatches, err := toEthermanBatches(batches) if err != nil { return nil, err } @@ -103,7 +63,7 @@ func (t *TxBuilderBananaBase) NewSequence(batches []seqsendertypes.Batch, coinba sequence.L1InfoRoot = l1InfoRoot - accInputHash, err := t.rollupContract.Contract().LastAccInputHash(&bind.CallOpts{Pending: false}) + accInputHash, err := t.rollupContract.LastAccInputHash(&bind.CallOpts{Pending: false}) if err != nil { return nil, err } @@ -141,11 +101,55 @@ func (t *TxBuilderBananaBase) getL1InfoRoot(indexL1InfoRoot uint32) (common.Hash ) if indexL1InfoRoot > 0 { - lastL1InfoTreeRoot, err = t.globalExitRootContract.Contract().L1InfoRootMap(&bind.CallOpts{Pending: false}, indexL1InfoRoot) + lastL1InfoTreeRoot, err = t.globalExitRootContract.L1InfoRootMap(&bind.CallOpts{Pending: false}, indexL1InfoRoot) if err != nil { - log.Errorf("error calling SC globalexitroot L1InfoLeafMap: %v", err) + log.Errorf("error calling SC globalexitroot L1InfoLeafMap (%s) Err: %w", t.globalExitRootContract.String(), err) } } return lastL1InfoTreeRoot, err } + +func convertToSequenceBanana(sequences seqsendertypes.Sequence) (etherman.SequenceBanana, error) { + seqEth, ok := sequences.(*BananaSequence) + if !ok { + log.Error("sequences is not a BananaSequence") + return etherman.SequenceBanana{}, fmt.Errorf("sequences is not a BananaSequence") + } + seqEth.SequenceBanana.Batches = make([]etherman.Batch, len(sequences.Batches())) + for _, batch := range sequences.Batches() { + ethBatch, err := toEthermanBatch(batch) + if err != nil { + return etherman.SequenceBanana{}, err + } + seqEth.SequenceBanana.Batches = append(seqEth.SequenceBanana.Batches, ethBatch) + } + return seqEth.SequenceBanana, nil +} + +func toEthermanBatch(batch seqsendertypes.Batch) (etherman.Batch, error) { + return etherman.Batch{ + L2Data: batch.L2Data(), + LastCoinbase: batch.LastCoinbase(), + ForcedGlobalExitRoot: batch.ForcedGlobalExitRoot(), + ForcedBlockHashL1: batch.ForcedBlockHashL1(), + ForcedBatchTimestamp: batch.ForcedBatchTimestamp(), + BatchNumber: batch.BatchNumber(), + L1InfoTreeIndex: batch.L1InfoTreeIndex(), + LastL2BLockTimestamp: batch.LastL2BLockTimestamp(), + GlobalExitRoot: batch.GlobalExitRoot(), + }, nil +} + +func toEthermanBatches(batch []seqsendertypes.Batch) ([]etherman.Batch, error) { + result := make([]etherman.Batch, len(batch)) + for i, b := range batch { + var err error + result[i], err = toEthermanBatch(b) + if err != nil { + return nil, err + } + } + + return result, nil +} diff --git a/sequencesender/txbuilder/banana_base_test.go b/sequencesender/txbuilder/banana_base_test.go new file mode 100644 index 00000000..a2f54566 --- /dev/null +++ b/sequencesender/txbuilder/banana_base_test.go @@ -0,0 +1,86 @@ +package txbuilder_test + +import ( + "testing" + + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/sequencesender/txbuilder/mocks_txbuilder" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestBananaBaseNewSequenceEmpty(t *testing.T) { + testData := newBananaBaseTestData(t) + lastAcc := common.HexToHash("0x8aca9664752dbae36135fd0956c956fc4a370feeac67485b49bcd4b99608ae41") + testData.rollupContract.EXPECT().LastAccInputHash(mock.Anything).Return(lastAcc, nil) + seq, err := testData.sut.NewSequence(nil, common.Address{}) + require.NotNil(t, seq) + require.NoError(t, err) + // TODO check values + //require.Equal(t, lastAcc, seq.LastAccInputHash()) +} + +func TestBananaBaseNewBatchFromL2Block(t *testing.T) { + testData := newBananaBaseTestData(t) + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 2, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batch := testData.sut.NewBatchFromL2Block(l2Block) + require.NotNil(t, batch) + require.Equal(t, l2Block.Timestamp, batch.LastL2BLockTimestamp()) + require.Equal(t, l2Block.BatchNumber, batch.BatchNumber()) + require.Equal(t, l2Block.L1InfotreeIndex, batch.L1InfoTreeIndex()) + require.Equal(t, common.BytesToAddress(l2Block.Coinbase), batch.LastCoinbase()) + require.Equal(t, common.BytesToHash(l2Block.GlobalExitRoot), batch.GlobalExitRoot()) +} + +func TestBananaBaseNewSequenceBatch(t *testing.T) { + testData := newBananaBaseTestData(t) + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 2, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batch := testData.sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{batch} + lastAcc := common.HexToHash("0x8aca9664752dbae36135fd0956c956fc4a370feeac67485b49bcd4b99608ae41") + testData.rollupContract.EXPECT().LastAccInputHash(mock.Anything).Return(lastAcc, nil) + l1infoRoot := common.HexToHash("0x66ca9664752dbae36135fd0956c956fc4a370feeac67485b49bcd4b99608ae41") + testData.getContract.EXPECT().L1InfoRootMap(mock.Anything, uint32(3)).Return(l1infoRoot, nil) + + seq, err := testData.sut.NewSequence(batches, common.Address{}) + require.NotNil(t, seq) + require.NoError(t, err) + // TODO: check that the seq have the right values +} + +type testDataBananaBase struct { + rollupContract *mocks_txbuilder.RollupBananaBaseContractor + getContract *mocks_txbuilder.GlobalExitRootBananaContractor + opts bind.TransactOpts + sut *txbuilder.TxBuilderBananaBase +} + +func newBananaBaseTestData(t *testing.T) *testDataBananaBase { + zkevmContractMock := mocks_txbuilder.NewRollupBananaBaseContractor(t) + gerContractMock := mocks_txbuilder.NewGlobalExitRootBananaContractor(t) + opts := bind.TransactOpts{} + sut := txbuilder.NewTxBuilderBananaBase(zkevmContractMock, gerContractMock, opts) + require.NotNil(t, sut) + return &testDataBananaBase{ + rollupContract: zkevmContractMock, + getContract: gerContractMock, + opts: opts, + sut: sut, + } +} diff --git a/sequencesender/txbuilder/banana_validium.go b/sequencesender/txbuilder/banana_validium.go index d926143a..4693c6cd 100644 --- a/sequencesender/txbuilder/banana_validium.go +++ b/sequencesender/txbuilder/banana_validium.go @@ -2,43 +2,56 @@ package txbuilder import ( "context" + "fmt" "math/big" "github.com/0xPolygon/cdk-contracts-tooling/contracts/banana/polygonvalidiumetrog" "github.com/0xPolygon/cdk/dataavailability" "github.com/0xPolygon/cdk/etherman" - "github.com/0xPolygon/cdk/etherman/contracts" "github.com/0xPolygon/cdk/log" "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" ) type TxBuilderBananaValidium struct { TxBuilderBananaBase - da dataavailability.SequenceSender - condNewSeq CondNewSequence + da dataavailability.SequenceSenderBanana + condNewSeq CondNewSequence + rollupContract rollupBananaValidiumContractor } -func NewTxBuilderBananaValidium(rollupContract contracts.RollupBananaType, - gerContract contracts.GlobalExitRootBananaType, - da dataavailability.SequenceSender, opts bind.TransactOpts, sender common.Address, maxBatchesForL1 uint64) *TxBuilderBananaValidium { +type rollupBananaValidiumContractor interface { + rollupBananaBaseContractor + SequenceBatchesValidium(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, indexL1InfoRoot uint32, maxSequenceTimestamp uint64, expectedFinalAccInputHash [32]byte, l2Coinbase common.Address, dataAvailabilityMessage []byte) (*types.Transaction, error) +} + +func NewTxBuilderBananaValidium(rollupContract rollupBananaValidiumContractor, + gerContract globalExitRootBananaContractor, + da dataavailability.SequenceSenderBanana, opts bind.TransactOpts, maxBatchesForL1 uint64) *TxBuilderBananaValidium { return &TxBuilderBananaValidium{ - TxBuilderBananaBase: *NewTxBuilderBananaBase(rollupContract, gerContract, opts, sender), + TxBuilderBananaBase: *NewTxBuilderBananaBase(rollupContract, gerContract, opts), da: da, - condNewSeq: &NewSequenceConditionalNumBatches{ - maxBatchesForL1: maxBatchesForL1, - }, + condNewSeq: NewConditionalNewSequenceNumBatches(maxBatchesForL1), + rollupContract: rollupContract, } } func (t *TxBuilderBananaValidium) NewSequenceIfWorthToSend(ctx context.Context, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) { - return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, t.SenderAddress, l2Coinbase, batchNumber) + return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, l2Coinbase) +} + +// SetCondNewSeq allow to override the default conditional for new sequence +func (t *TxBuilderBananaValidium) SetCondNewSeq(cond CondNewSequence) CondNewSequence { + previous := t.condNewSeq + t.condNewSeq = cond + return previous } -func (t *TxBuilderBananaValidium) BuildSequenceBatchesTx(ctx context.Context, sender common.Address, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { +func (t *TxBuilderBananaValidium) BuildSequenceBatchesTx(ctx context.Context, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { // TODO: param sender // Post sequences to DA backend var dataAvailabilityMessage []byte @@ -49,14 +62,19 @@ func (t *TxBuilderBananaValidium) BuildSequenceBatchesTx(ctx context.Context, se return nil, err } - dataAvailabilityMessage, err = t.da.PostSequence(ctx, ethseq) + dataAvailabilityMessage, err = t.da.PostSequenceBanana(ctx, ethseq) if err != nil { log.Error("error posting sequences to the data availability protocol: ", err) return nil, err } + if dataAvailabilityMessage == nil { + err := fmt.Errorf("data availability message is nil") + log.Error("error posting sequences to the data availability protocol: ", err.Error()) + return nil, err + } // Build sequence data - tx, err := t.internalBuildSequenceBatchesTx(t.SenderAddress, ethseq, dataAvailabilityMessage) + tx, err := t.internalBuildSequenceBatchesTx(ethseq, dataAvailabilityMessage) if err != nil { log.Errorf("[SeqSender] error estimating new sequenceBatches to add to ethtxmanager: ", err) return nil, err @@ -65,7 +83,7 @@ func (t *TxBuilderBananaValidium) BuildSequenceBatchesTx(ctx context.Context, se } // BuildSequenceBatchesTx builds a tx to be sent to the PoE SC method SequenceBatches. -func (t *TxBuilderBananaValidium) internalBuildSequenceBatchesTx(sender common.Address, sequence etherman.SequenceBanana, +func (t *TxBuilderBananaValidium) internalBuildSequenceBatchesTx(sequence etherman.SequenceBanana, dataAvailabilityMessage []byte) (*ethtypes.Transaction, error) { newopts := t.opts newopts.NoSend = true @@ -94,7 +112,7 @@ func (t *TxBuilderBananaValidium) sequenceBatchesValidium(opts bind.TransactOpts } } - tx, err := t.rollupContract.Contract().SequenceBatchesValidium(&opts, batches, sequence.IndexL1InfoRoot, sequence.MaxSequenceTimestamp, sequence.AccInputHash, sequence.L2Coinbase, dataAvailabilityMessage) + tx, err := t.rollupContract.SequenceBatchesValidium(&opts, batches, sequence.IndexL1InfoRoot, sequence.MaxSequenceTimestamp, sequence.AccInputHash, sequence.L2Coinbase, dataAvailabilityMessage) if err != nil { log.Debugf("Batches to send: %+v", batches) log.Debug("l2CoinBase: ", sequence.L2Coinbase) diff --git a/sequencesender/txbuilder/banana_validium_test.go b/sequencesender/txbuilder/banana_validium_test.go new file mode 100644 index 00000000..5c6f0d50 --- /dev/null +++ b/sequencesender/txbuilder/banana_validium_test.go @@ -0,0 +1,107 @@ +package txbuilder_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/0xPolygon/cdk/dataavailability/mocks_da" + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/sequencesender/txbuilder/mocks_txbuilder" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestBananaValidiumName(t *testing.T) { + testData := newBananaValidiumTestData(t, txbuilder.MaxBatchesForL1Disabled) + require.NotNil(t, testData.sut) + require.True(t, strings.Contains(testData.sut.String(), "Banana")) + require.True(t, strings.Contains(testData.sut.String(), "Validium")) +} + +func TestBananaValidiumBuildSequenceBatchesTxSequenceErrorsFromDA(t *testing.T) { + testData := newBananaValidiumTestData(t, txbuilder.MaxBatchesForL1Disabled) + seq, err := newSequenceBananaValidiumForTest(testData) + require.NoError(t, err) + ctx := context.TODO() + testData.da.EXPECT().PostSequenceBanana(ctx, mock.Anything).Return(nil, nil).Once() + + _, err = testData.sut.BuildSequenceBatchesTx(ctx, seq) + require.Error(t, err, "data availability message is nil") + + testData.da.EXPECT().PostSequenceBanana(ctx, mock.Anything).Return(nil, fmt.Errorf("test error")) + _, err = testData.sut.BuildSequenceBatchesTx(ctx, seq) + require.Error(t, err, "error posting sequences to the data availability protocol: test error") + +} + +func TestBananaValidiumBuildSequenceBatchesTxSequenceDAOk(t *testing.T) { + testData := newBananaValidiumTestData(t, txbuilder.MaxBatchesForL1Disabled) + seq, err := newSequenceBananaValidiumForTest(testData) + require.NoError(t, err) + ctx := context.TODO() + daMessage := []byte{1} + testData.da.EXPECT().PostSequenceBanana(ctx, mock.Anything).Return(daMessage, nil) + inner := ðtypes.LegacyTx{} + seqBatchesTx := ethtypes.NewTx(inner) + testData.rollupContract.EXPECT().SequenceBatchesValidium(mock.MatchedBy(func(opts *bind.TransactOpts) bool { + return opts.NoSend == true + }), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, daMessage).Return(seqBatchesTx, nil).Once() + tx, err := testData.sut.BuildSequenceBatchesTx(ctx, seq) + require.NoError(t, err) + require.Equal(t, seqBatchesTx, tx) +} + +type testDataBananaValidium struct { + rollupContract *mocks_txbuilder.RollupBananaValidiumContractor + getContract *mocks_txbuilder.GlobalExitRootBananaContractor + cond *mocks_txbuilder.CondNewSequence + da *mocks_da.SequenceSenderBanana + opts bind.TransactOpts + sut *txbuilder.TxBuilderBananaValidium +} + +func newBananaValidiumTestData(t *testing.T, maxBatchesForL1 uint64) *testDataBananaValidium { + zkevmContractMock := mocks_txbuilder.NewRollupBananaValidiumContractor(t) + gerContractMock := mocks_txbuilder.NewGlobalExitRootBananaContractor(t) + condMock := mocks_txbuilder.NewCondNewSequence(t) + daMock := mocks_da.NewSequenceSenderBanana(t) + + opts := bind.TransactOpts{} + sut := txbuilder.NewTxBuilderBananaValidium(zkevmContractMock, gerContractMock, daMock, opts, maxBatchesForL1) + require.NotNil(t, sut) + sut.SetCondNewSeq(condMock) + return &testDataBananaValidium{ + rollupContract: zkevmContractMock, + getContract: gerContractMock, + cond: condMock, + da: daMock, + opts: opts, + sut: sut, + } +} + +func newSequenceBananaValidiumForTest(testData *testDataBananaValidium) (seqsendertypes.Sequence, error) { + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 1, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batch := testData.sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batch, + } + lastAcc := common.HexToHash("0x8aca9664752dbae36135fd0956c956fc4a370feeac67485b49bcd4b99608ae41") + testData.rollupContract.EXPECT().LastAccInputHash(mock.Anything).Return(lastAcc, nil).Once() + l1infoRoot := common.HexToHash("0x66ca9664752dbae36135fd0956c956fc4a370feeac67485b49bcd4b99608ae41") + testData.getContract.EXPECT().L1InfoRootMap(mock.Anything, uint32(3)).Return(l1infoRoot, nil).Once() + return testData.sut.NewSequence(batches, common.Address{}) +} diff --git a/sequencesender/txbuilder/banana_zkevm.go b/sequencesender/txbuilder/banana_zkevm.go index dbb679fb..0fa9ddd9 100644 --- a/sequencesender/txbuilder/banana_zkevm.go +++ b/sequencesender/txbuilder/banana_zkevm.go @@ -6,33 +6,49 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/banana/polygonvalidiumetrog" "github.com/0xPolygon/cdk/etherman" - "github.com/0xPolygon/cdk/etherman/contracts" "github.com/0xPolygon/cdk/log" "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types" ) type TxBuilderBananaZKEVM struct { TxBuilderBananaBase - condNewSeq CondNewSequence + condNewSeq CondNewSequence + rollupContract rollupBananaZKEVMContractor } -func NewTxBuilderBananaZKEVM(rollupContract contracts.RollupBananaType, gerContract contracts.GlobalExitRootBananaType, opts bind.TransactOpts, sender common.Address, maxTxSizeForL1 uint64) *TxBuilderBananaZKEVM { +type rollupBananaZKEVMContractor interface { + rollupBananaBaseContractor + SequenceBatches(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, indexL1InfoRoot uint32, maxSequenceTimestamp uint64, expectedFinalAccInputHash [32]byte, l2Coinbase common.Address) (*types.Transaction, error) +} + +type globalExitRootBananaZKEVMContractor interface { + globalExitRootBananaContractor +} + +func NewTxBuilderBananaZKEVM(rollupContract rollupBananaZKEVMContractor, gerContract globalExitRootBananaZKEVMContractor, opts bind.TransactOpts, maxTxSizeForL1 uint64) *TxBuilderBananaZKEVM { return &TxBuilderBananaZKEVM{ - TxBuilderBananaBase: *NewTxBuilderBananaBase(rollupContract, gerContract, opts, sender), - condNewSeq: &NewSequenceConditionalMaxSize{ - maxTxSizeForL1: maxTxSizeForL1, - }, + TxBuilderBananaBase: *NewTxBuilderBananaBase(rollupContract, gerContract, opts), + condNewSeq: NewConditionalNewSequenceMaxSize(maxTxSizeForL1), + rollupContract: rollupContract, } } func (t *TxBuilderBananaZKEVM) NewSequenceIfWorthToSend(ctx context.Context, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) { - return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, t.SenderAddress, l2Coinbase, batchNumber) + return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, l2Coinbase) +} + +// SetCondNewSeq allow to override the default conditional for new sequence +func (t *TxBuilderBananaZKEVM) SetCondNewSeq(cond CondNewSequence) CondNewSequence { + previous := t.condNewSeq + t.condNewSeq = cond + return previous } -func (t *TxBuilderBananaZKEVM) BuildSequenceBatchesTx(ctx context.Context, sender common.Address, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { +func (t *TxBuilderBananaZKEVM) BuildSequenceBatchesTx(ctx context.Context, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { var err error ethseq, err := convertToSequenceBanana(sequences) if err != nil { @@ -71,7 +87,7 @@ func (t *TxBuilderBananaZKEVM) sequenceBatchesRollup(opts bind.TransactOpts, seq } } - tx, err := t.rollupContract.Contract().SequenceBatches(&opts, batches, sequence.IndexL1InfoRoot, sequence.MaxSequenceTimestamp, sequence.AccInputHash, sequence.L2Coinbase) + tx, err := t.rollupContract.SequenceBatches(&opts, batches, sequence.IndexL1InfoRoot, sequence.MaxSequenceTimestamp, sequence.AccInputHash, sequence.L2Coinbase) if err != nil { log.Debugf("Batches to send: %+v", batches) log.Debug("l2CoinBase: ", sequence.L2Coinbase) diff --git a/sequencesender/txbuilder/banana_zkevm_test.go b/sequencesender/txbuilder/banana_zkevm_test.go new file mode 100644 index 00000000..8653c504 --- /dev/null +++ b/sequencesender/txbuilder/banana_zkevm_test.go @@ -0,0 +1,105 @@ +package txbuilder_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/sequencesender/txbuilder/mocks_txbuilder" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestBananaZkevmName(t *testing.T) { + testData := newBananaZKEVMTestData(t, txbuilder.MaxTxSizeForL1Disabled) + require.True(t, strings.Contains(testData.sut.String(), "Banana")) + require.True(t, strings.Contains(testData.sut.String(), "ZKEVM")) +} + +func TestBananaZkevmNewSequenceIfWorthToSend(t *testing.T) { + testData := newBananaZKEVMTestData(t, txbuilder.MaxTxSizeForL1Disabled) + + testSequenceIfWorthToSendNoNewSeq(t, testData.sut) + testSequenceIfWorthToSendErr(t, testData.sut) + testSetCondNewSeq(t, testData.sut) +} + +func TestBananaZkevmBuildSequenceBatchesTxOk(t *testing.T) { + testData := newBananaZKEVMTestData(t, txbuilder.MaxTxSizeForL1Disabled) + seq, err := newSequenceBananaZKEVMForTest(testData) + require.NoError(t, err) + + inner := ðtypes.LegacyTx{} + tx := ethtypes.NewTx(inner) + + // It check that SequenceBatches is not going to be send + testData.rollupContract.EXPECT().SequenceBatches(mock.MatchedBy(func(opts *bind.TransactOpts) bool { + return opts.NoSend == true + }), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tx, nil).Once() + returnTx, err := testData.sut.BuildSequenceBatchesTx(context.TODO(), seq) + require.NoError(t, err) + require.Equal(t, tx, returnTx) +} + +func TestBananaZkevmBuildSequenceBatchesTxErr(t *testing.T) { + testData := newBananaZKEVMTestData(t, txbuilder.MaxTxSizeForL1Disabled) + seq, err := newSequenceBananaZKEVMForTest(testData) + require.NoError(t, err) + + err = fmt.Errorf("test-error") + testData.rollupContract.EXPECT().SequenceBatches(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, err).Once() + returnedTx, returnedErr := testData.sut.BuildSequenceBatchesTx(context.TODO(), seq) + require.ErrorContains(t, returnedErr, err.Error()) + require.Nil(t, returnedTx) +} + +type testDataBananaZKEVM struct { + rollupContract *mocks_txbuilder.RollupBananaZKEVMContractor + getContract *mocks_txbuilder.GlobalExitRootBananaContractor + cond *mocks_txbuilder.CondNewSequence + opts bind.TransactOpts + sut *txbuilder.TxBuilderBananaZKEVM +} + +func newBananaZKEVMTestData(t *testing.T, maxTxSizeForL1 uint64) *testDataBananaZKEVM { + zkevmContractMock := mocks_txbuilder.NewRollupBananaZKEVMContractor(t) + gerContractMock := mocks_txbuilder.NewGlobalExitRootBananaContractor(t) + condMock := mocks_txbuilder.NewCondNewSequence(t) + opts := bind.TransactOpts{} + sut := txbuilder.NewTxBuilderBananaZKEVM(zkevmContractMock, gerContractMock, opts, maxTxSizeForL1) + require.NotNil(t, sut) + sut.SetCondNewSeq(condMock) + return &testDataBananaZKEVM{ + rollupContract: zkevmContractMock, + getContract: gerContractMock, + cond: condMock, + opts: opts, + sut: sut, + } +} + +func newSequenceBananaZKEVMForTest(testData *testDataBananaZKEVM) (seqsendertypes.Sequence, error) { + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 1, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batch := testData.sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batch, + } + lastAcc := common.HexToHash("0x8aca9664752dbae36135fd0956c956fc4a370feeac67485b49bcd4b99608ae41") + testData.rollupContract.EXPECT().LastAccInputHash(mock.Anything).Return(lastAcc, nil).Once() + l1infoRoot := common.HexToHash("0x66ca9664752dbae36135fd0956c956fc4a370feeac67485b49bcd4b99608ae41") + testData.getContract.EXPECT().L1InfoRootMap(mock.Anything, uint32(3)).Return(l1infoRoot, nil).Once() + return testData.sut.NewSequence(batches, common.Address{}) +} diff --git a/sequencesender/txbuilder/elderberry_base.go b/sequencesender/txbuilder/elderberry_base.go index f643a96c..64971e9c 100644 --- a/sequencesender/txbuilder/elderberry_base.go +++ b/sequencesender/txbuilder/elderberry_base.go @@ -2,7 +2,6 @@ package txbuilder import ( "github.com/0xPolygon/cdk/etherman" - "github.com/0xPolygon/cdk/etherman/contracts" "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" "github.com/0xPolygon/cdk/state/datastream" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -10,17 +9,20 @@ import ( ) type TxBuilderElderberryBase struct { - rollupContract contracts.RollupElderberryType - opts bind.TransactOpts + opts bind.TransactOpts } -func NewTxBuilderElderberryBase(rollupContract contracts.RollupElderberryType, opts bind.TransactOpts) *TxBuilderElderberryBase { +func NewTxBuilderElderberryBase(opts bind.TransactOpts) *TxBuilderElderberryBase { return &TxBuilderElderberryBase{ - rollupContract: rollupContract, - opts: opts, + opts: opts, } } +// SetAuth sets the auth for the tx builder +func (t *TxBuilderElderberryBase) SetAuth(auth *bind.TransactOpts) { + t.opts = *auth +} + func (t *TxBuilderElderberryBase) NewSequence(batches []seqsendertypes.Batch, coinbase common.Address) (seqsendertypes.Sequence, error) { seq := ElderberrySequence{ l2Coinbase: coinbase, @@ -44,5 +46,8 @@ func getLastSequencedBatchNumber(sequences seqsendertypes.Sequence) uint64 { if sequences.Len() == 0 { return 0 } + if sequences.FirstBatch().BatchNumber() == 0 { + panic("First batch number is 0, that is not allowed!") + } return sequences.FirstBatch().BatchNumber() - 1 } diff --git a/sequencesender/txbuilder/elderberry_base_test.go b/sequencesender/txbuilder/elderberry_base_test.go new file mode 100644 index 00000000..43141f22 --- /dev/null +++ b/sequencesender/txbuilder/elderberry_base_test.go @@ -0,0 +1,98 @@ +package txbuilder + +import ( + "testing" + + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestElderberryBaseNewSequence(t *testing.T) { + opts := bind.TransactOpts{} + sut := NewTxBuilderElderberryBase(opts) + require.NotNil(t, sut) + seq, err := sut.NewSequence(nil, common.Address{}) + require.NotNil(t, seq) + require.NoError(t, err) +} + +func TestElderberryBaseNewBatchFromL2Block(t *testing.T) { + sut := newElderberryBaseSUT(t) + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 2, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batch := sut.NewBatchFromL2Block(l2Block) + require.NotNil(t, batch) + require.Equal(t, l2Block.Timestamp, batch.LastL2BLockTimestamp()) + require.Equal(t, l2Block.BatchNumber, batch.BatchNumber()) + require.Equal(t, l2Block.L1InfotreeIndex, batch.L1InfoTreeIndex()) + require.Equal(t, common.BytesToAddress(l2Block.Coinbase), batch.LastCoinbase()) + require.Equal(t, common.BytesToHash(l2Block.GlobalExitRoot), batch.GlobalExitRoot()) + +} + +func TestElderberryBasegetLastSequencedBatchNumberEmpty(t *testing.T) { + sut := newElderberryBaseSUT(t) + seq, err := sut.NewSequence(nil, common.Address{}) + require.NoError(t, err) + + require.Equal(t, uint64(0), getLastSequencedBatchNumber(seq)) +} + +func TestElderberryBasegetLastSequencedBatch1Batch(t *testing.T) { + sut := newElderberryBaseSUT(t) + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 2, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batchElder := sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batchElder, + } + + seq, err := sut.NewSequence(batches, common.Address{}) + require.NoError(t, err) + + require.Equal(t, l2Block.BatchNumber-1, getLastSequencedBatchNumber(seq)) +} + +func TestElderberryBaseGetLastSequencedBatchFirstBatchIsZeroThrowAPanic(t *testing.T) { + sut := newElderberryBaseSUT(t) + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 0, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batchElder := sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batchElder, + } + + seq, err := sut.NewSequence(batches, common.Address{}) + require.NoError(t, err) + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + getLastSequencedBatchNumber(seq) +} + +func newElderberryBaseSUT(t *testing.T) *TxBuilderElderberryBase { + opts := bind.TransactOpts{} + sut := NewTxBuilderElderberryBase(opts) + require.NotNil(t, sut) + return sut +} diff --git a/sequencesender/txbuilder/elderberry_types.go b/sequencesender/txbuilder/elderberry_types.go index fc57bc18..bbc7d56a 100644 --- a/sequencesender/txbuilder/elderberry_types.go +++ b/sequencesender/txbuilder/elderberry_types.go @@ -13,6 +13,13 @@ type ElderberrySequence struct { batches []seqsendertypes.Batch } +func NewElderberrySequence(batches []seqsendertypes.Batch, l2Coinbase common.Address) *ElderberrySequence { + return &ElderberrySequence{ + l2Coinbase: l2Coinbase, + batches: batches, + } +} + func (b *ElderberrySequence) IndexL1InfoRoot() uint32 { log.Fatal("Elderberry Sequence does not have IndexL1InfoRoot") return 0 diff --git a/sequencesender/txbuilder/elderberry_validium.go b/sequencesender/txbuilder/elderberry_validium.go index a0369191..638ba3b1 100644 --- a/sequencesender/txbuilder/elderberry_validium.go +++ b/sequencesender/txbuilder/elderberry_validium.go @@ -3,6 +3,7 @@ package txbuilder import ( "context" "encoding/hex" + "fmt" "math/big" "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" @@ -20,35 +21,51 @@ import ( type TxBuilderElderberryValidium struct { TxBuilderElderberryBase - da dataavailability.SequenceSenderElderberry - condNewSeq CondNewSequence + da dataavailability.SequenceSenderElderberry + condNewSeq CondNewSequence + rollupContract rollupElderberryValidiumContractor +} + +type rollupElderberryValidiumContractor interface { + SequenceBatchesValidium(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, maxSequenceTimestamp uint64, initSequencedBatch uint64, l2Coinbase common.Address, dataAvailabilityMessage []byte) (*types.Transaction, error) } func NewTxBuilderElderberryValidium(zkevm contracts.RollupElderberryType, da dataavailability.SequenceSenderElderberry, - opts bind.TransactOpts, sender common.Address, maxBatchesForL1 uint64) *TxBuilderElderberryValidium { + opts bind.TransactOpts, maxBatchesForL1 uint64) *TxBuilderElderberryValidium { return &TxBuilderElderberryValidium{ - da: da, - TxBuilderElderberryBase: *NewTxBuilderElderberryBase( - zkevm, opts, - ), - condNewSeq: &NewSequenceConditionalNumBatches{ - maxBatchesForL1: maxBatchesForL1, - }, + da: da, + TxBuilderElderberryBase: *NewTxBuilderElderberryBase(opts), + condNewSeq: NewConditionalNewSequenceNumBatches(maxBatchesForL1), + rollupContract: zkevm, } } func (t *TxBuilderElderberryValidium) NewSequenceIfWorthToSend(ctx context.Context, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) { - return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, t.opts.From, l2Coinbase, batchNumber) + return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, l2Coinbase) } -func (t *TxBuilderElderberryValidium) BuildSequenceBatchesTx(ctx context.Context, sender common.Address, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { +// SetCondNewSeq allow to override the default conditional for new sequence +func (t *TxBuilderElderberryValidium) SetCondNewSeq(cond CondNewSequence) CondNewSequence { + previous := t.condNewSeq + t.condNewSeq = cond + return previous +} +func (t *TxBuilderElderberryValidium) BuildSequenceBatchesTx(ctx context.Context, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { + if sequences == nil || sequences.Len() == 0 { + return nil, fmt.Errorf("can't sequence an empty sequence") + } batchesData := convertToBatchesData(sequences) dataAvailabilityMessage, err := t.da.PostSequenceElderberry(ctx, batchesData) if err != nil { log.Error("error posting sequences to the data availability protocol: ", err) return nil, err } + if dataAvailabilityMessage == nil { + err := fmt.Errorf("data availability message is nil") + log.Error("error posting sequences to the data availability protocol: ", err.Error()) + return nil, err + } newopts := t.opts newopts.NoSend = true @@ -76,10 +93,9 @@ func (t *TxBuilderElderberryValidium) buildSequenceBatchesTxValidium(opts *bind. } } lastSequencedBatchNumber := getLastSequencedBatchNumber(sequences) - ZkEVM := t.rollupContract.Contract() log.Infof("SequenceBatchesValidium(from=%s, len(batches)=%d, MaxSequenceTimestamp=%d, lastSequencedBatchNumber=%d, L2Coinbase=%s, dataAvailabilityMessage=%s)", t.opts.From.String(), len(batches), sequences.MaxSequenceTimestamp(), lastSequencedBatchNumber, sequences.L2Coinbase().String(), hex.EncodeToString(dataAvailabilityMessage)) - tx, err := ZkEVM.SequenceBatchesValidium(opts, batches, sequences.MaxSequenceTimestamp(), + tx, err := t.rollupContract.SequenceBatchesValidium(opts, batches, sequences.MaxSequenceTimestamp(), lastSequencedBatchNumber, sequences.L2Coinbase(), dataAvailabilityMessage) if err != nil { if parsedErr, ok := etherman.TryParseError(err); ok { diff --git a/sequencesender/txbuilder/elderberry_validium_test.go b/sequencesender/txbuilder/elderberry_validium_test.go new file mode 100644 index 00000000..1964867c --- /dev/null +++ b/sequencesender/txbuilder/elderberry_validium_test.go @@ -0,0 +1,116 @@ +package txbuilder_test + +import ( + "context" + "fmt" + "math/big" + "strings" + "testing" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" + "github.com/0xPolygon/cdk/dataavailability/mocks_da" + "github.com/0xPolygon/cdk/etherman/contracts" + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestElderberryValidiumName(t *testing.T) { + testData := newElderberryValidiumSUT(t) + require.NotNil(t, testData.sut) + require.True(t, strings.Contains(testData.sut.String(), "Elderberry")) + require.True(t, strings.Contains(testData.sut.String(), "Validium")) +} + +func TestElderberryValidiumBuildSequenceBatchesTxEmtpySequence(t *testing.T) { + testData := newElderberryValidiumSUT(t) + ctx := context.TODO() + _, err := testData.sut.BuildSequenceBatchesTx(ctx, nil) + require.Error(t, err) + + seq, err := testData.sut.NewSequence(nil, common.Address{}) + require.NoError(t, err) + _, err = testData.sut.BuildSequenceBatchesTx(ctx, seq) + require.Error(t, err) +} + +func TestElderberryValidiumBuildSequenceBatchesTxSequenceErrorsFromDA(t *testing.T) { + testData := newElderberryValidiumSUT(t) + ctx := context.TODO() + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 1, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batchElder := testData.sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batchElder, + } + seq, err := testData.sut.NewSequence(batches, common.Address{}) + require.NoError(t, err) + testData.mockDA.EXPECT().PostSequenceElderberry(ctx, mock.Anything).Return(nil, nil) + _, err = testData.sut.BuildSequenceBatchesTx(ctx, seq) + require.Error(t, err, "data availability message is nil") + testData.mockDA.EXPECT().PostSequenceElderberry(ctx, mock.Anything).Return(nil, fmt.Errorf("test error")) + _, err = testData.sut.BuildSequenceBatchesTx(ctx, seq) + require.Error(t, err, "error posting sequences to the data availability protocol: test error") +} + +func TestElderberryValidiumBuildSequenceBatchesTxSequenceDAOk(t *testing.T) { + testData := newElderberryValidiumSUT(t) + ctx := context.TODO() + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 1, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batchElder := testData.sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batchElder, + } + seq, err := testData.sut.NewSequence(batches, common.Address{}) + require.NoError(t, err) + testData.mockDA.EXPECT().PostSequenceElderberry(ctx, mock.Anything).Return([]byte{1}, nil) + tx, err := testData.sut.BuildSequenceBatchesTx(ctx, seq) + require.NoError(t, err) + require.NotNil(t, tx) +} + +func TestElderberryValidiumNewSequenceIfWorthToSend(t *testing.T) { + testData := newElderberryValidiumSUT(t) + testSequenceIfWorthToSendNoNewSeq(t, testData.sut) + testSequenceIfWorthToSendErr(t, testData.sut) + testSetCondNewSeq(t, testData.sut) +} + +type testDataElderberryValidium struct { + mockDA *mocks_da.SequenceSenderElderberry + sut *txbuilder.TxBuilderElderberryValidium +} + +func newElderberryValidiumSUT(t *testing.T) *testDataElderberryValidium { + zkevmContract, err := contracts.NewContractMagic[contracts.RollupElderberryType](polygonvalidiumetrog.NewPolygonvalidiumetrog, common.Address{}, nil, contracts.ContractNameRollup, contracts.VersionElderberry) + require.NoError(t, err) + privateKey, err := crypto.HexToECDSA("64e679029f5032046955d41713dcc4b565de77ab891748d31bcf38864b54c175") + require.NoError(t, err) + opts, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1)) + require.NoError(t, err) + + da := mocks_da.NewSequenceSenderElderberry(t) + + sut := txbuilder.NewTxBuilderElderberryValidium(*zkevmContract, da, *opts, uint64(100)) + require.NotNil(t, sut) + return &testDataElderberryValidium{ + mockDA: da, + sut: sut, + } +} diff --git a/sequencesender/txbuilder/elderberry_zkevm.go b/sequencesender/txbuilder/elderberry_zkevm.go index 39979375..83e70971 100644 --- a/sequencesender/txbuilder/elderberry_zkevm.go +++ b/sequencesender/txbuilder/elderberry_zkevm.go @@ -2,11 +2,11 @@ package txbuilder import ( "context" + "fmt" "math/big" "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" "github.com/0xPolygon/cdk/etherman" - "github.com/0xPolygon/cdk/etherman/contracts" "github.com/0xPolygon/cdk/log" "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -17,26 +17,34 @@ import ( type TxBuilderElderberryZKEVM struct { TxBuilderElderberryBase - condNewSeq CondNewSequence + condNewSeq CondNewSequence + rollupContract rollupElderberryZKEVMContractor } -func NewTxBuilderElderberryZKEVM(zkevm contracts.RollupElderberryType, opts bind.TransactOpts, sender common.Address, maxTxSizeForL1 uint64) *TxBuilderElderberryZKEVM { +type rollupElderberryZKEVMContractor interface { + SequenceBatches(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, maxSequenceTimestamp uint64, initSequencedBatch uint64, l2Coinbase common.Address) (*types.Transaction, error) +} + +func NewTxBuilderElderberryZKEVM(zkevm rollupElderberryZKEVMContractor, opts bind.TransactOpts, maxTxSizeForL1 uint64) *TxBuilderElderberryZKEVM { return &TxBuilderElderberryZKEVM{ - TxBuilderElderberryBase: *NewTxBuilderElderberryBase( - zkevm, opts, - ), - condNewSeq: &NewSequenceConditionalMaxSize{ - maxTxSizeForL1: maxTxSizeForL1, - }, + TxBuilderElderberryBase: *NewTxBuilderElderberryBase(opts), + condNewSeq: NewConditionalNewSequenceMaxSize(maxTxSizeForL1), + rollupContract: zkevm, } } func (t *TxBuilderElderberryZKEVM) NewSequenceIfWorthToSend(ctx context.Context, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) { - return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, t.opts.From, l2Coinbase, batchNumber) + return t.condNewSeq.NewSequenceIfWorthToSend(ctx, t, sequenceBatches, l2Coinbase) } -func (t *TxBuilderElderberryZKEVM) BuildSequenceBatchesTx(ctx context.Context, sender common.Address, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { +// SetCondNewSeq allow to override the default conditional for new sequence +func (t *TxBuilderElderberryZKEVM) SetCondNewSeq(cond CondNewSequence) CondNewSequence { + previous := t.condNewSeq + t.condNewSeq = cond + return previous +} +func (t *TxBuilderElderberryZKEVM) BuildSequenceBatchesTx(ctx context.Context, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) { newopts := t.opts newopts.NoSend = true @@ -49,6 +57,9 @@ func (t *TxBuilderElderberryZKEVM) BuildSequenceBatchesTx(ctx context.Context, s } func (t *TxBuilderElderberryZKEVM) sequenceBatchesRollup(opts bind.TransactOpts, sequences seqsendertypes.Sequence) (*types.Transaction, error) { + if sequences == nil || sequences.Len() == 0 { + return nil, fmt.Errorf("can't sequence an empty sequence") + } batches := make([]polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, sequences.Len()) for i, seq := range sequences.Batches() { var ger common.Hash @@ -65,8 +76,7 @@ func (t *TxBuilderElderberryZKEVM) sequenceBatchesRollup(opts bind.TransactOpts, } } lastSequencedBatchNumber := getLastSequencedBatchNumber(sequences) - ZkEVM := t.rollupContract.Contract() - tx, err := ZkEVM.SequenceBatches(&opts, batches, sequences.MaxSequenceTimestamp(), lastSequencedBatchNumber, sequences.L2Coinbase()) + tx, err := t.rollupContract.SequenceBatches(&opts, batches, sequences.MaxSequenceTimestamp(), lastSequencedBatchNumber, sequences.L2Coinbase()) if err != nil { t.warningMessage(batches, sequences.L2Coinbase(), &opts) if parsedErr, ok := etherman.TryParseError(err); ok { diff --git a/sequencesender/txbuilder/elderberry_zkevm_test.go b/sequencesender/txbuilder/elderberry_zkevm_test.go new file mode 100644 index 00000000..9ed29823 --- /dev/null +++ b/sequencesender/txbuilder/elderberry_zkevm_test.go @@ -0,0 +1,110 @@ +package txbuilder_test + +import ( + "context" + "math/big" + "strings" + "testing" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" + "github.com/0xPolygon/cdk/etherman/contracts" + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +func TestElderberryZkevmName(t *testing.T) { + zkevmContract := contracts.RollupElderberryType{} + opts := bind.TransactOpts{} + sut := txbuilder.NewTxBuilderElderberryZKEVM(zkevmContract, opts, 100) + require.NotNil(t, sut) + require.True(t, strings.Contains(sut.String(), "Elderberry")) + require.True(t, strings.Contains(sut.String(), "ZKEVM")) +} + +func TestElderberryZkevmNewSequence(t *testing.T) { + zkevmContract := contracts.RollupElderberryType{} + opts := bind.TransactOpts{} + sut := txbuilder.NewTxBuilderElderberryZKEVM(zkevmContract, opts, 100) + require.NotNil(t, sut) + seq, err := sut.NewSequence(nil, common.Address{}) + require.NoError(t, err) + require.NotNil(t, seq) +} + +func TestElderberryZkevmBuildSequenceBatchesTxEmtpySequence(t *testing.T) { + sut := newElderberryZkevmSUT(t) + ctx := context.TODO() + _, err := sut.BuildSequenceBatchesTx(ctx, nil) + require.Error(t, err) + + seq, err := sut.NewSequence(nil, common.Address{}) + require.NoError(t, err) + _, err = sut.BuildSequenceBatchesTx(ctx, seq) + require.Error(t, err) +} + +func TestElderberryZkevmBuildSequenceBatchesTxSequence1Batch(t *testing.T) { + sut := newElderberryZkevmSUT(t) + ctx := context.TODO() + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 1, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batchElder := sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batchElder, + } + seq, err := sut.NewSequence(batches, common.Address{}) + require.NoError(t, err) + _, err = sut.BuildSequenceBatchesTx(ctx, seq) + require.NoError(t, err) +} + +// This have to signer so produce an error +func TestElderberryZkevmBuildSequenceBatchesTxSequence1BatchError(t *testing.T) { + sut := newElderberryZkevmSUT(t) + sut.SetAuth(&bind.TransactOpts{}) + ctx := context.TODO() + l2Block := &datastream.L2Block{ + Timestamp: 1, + BatchNumber: 1, + L1InfotreeIndex: 3, + Coinbase: []byte{1, 2, 3}, + GlobalExitRoot: []byte{4, 5, 6}, + } + batchElder := sut.NewBatchFromL2Block(l2Block) + batches := []seqsendertypes.Batch{ + batchElder, + } + seq, err := sut.NewSequence(batches, common.Address{}) + require.NoError(t, err) + _, err = sut.BuildSequenceBatchesTx(ctx, seq) + require.Error(t, err) +} + +func TestElderberryZkevmNewSequenceIfWorthToSend(t *testing.T) { + sut := newElderberryZkevmSUT(t) + testSequenceIfWorthToSendNoNewSeq(t, sut) + testSequenceIfWorthToSendErr(t, sut) + testSetCondNewSeq(t, sut) +} + +func newElderberryZkevmSUT(t *testing.T) *txbuilder.TxBuilderElderberryZKEVM { + zkevmContract, err := contracts.NewContractMagic[contracts.RollupElderberryType](polygonvalidiumetrog.NewPolygonvalidiumetrog, common.Address{}, nil, contracts.ContractNameRollup, contracts.VersionElderberry) + require.NoError(t, err) + privateKey, err := crypto.HexToECDSA("64e679029f5032046955d41713dcc4b565de77ab891748d31bcf38864b54c175") + require.NoError(t, err) + opts, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1)) + require.NoError(t, err) + sut := txbuilder.NewTxBuilderElderberryZKEVM(*zkevmContract, *opts, 100) + require.NotNil(t, sut) + return sut +} diff --git a/sequencesender/txbuilder/interface.go b/sequencesender/txbuilder/interface.go index 8f3b4661..7bfa51be 100644 --- a/sequencesender/txbuilder/interface.go +++ b/sequencesender/txbuilder/interface.go @@ -10,13 +10,20 @@ import ( ) type TxBuilder interface { - BuildSequenceBatchesTx(ctx context.Context, sender common.Address, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) + // BuildSequenceBatchesTx Builds a sequence of batches transaction + BuildSequenceBatchesTx(ctx context.Context, sequences seqsendertypes.Sequence) (*ethtypes.Transaction, error) + // NewSequence Creates a new sequence NewSequence(batches []seqsendertypes.Batch, coinbase common.Address) (seqsendertypes.Sequence, error) + // NewSequenceIfWorthToSend Creates a new sequence if it is worth sending NewSequenceIfWorthToSend(ctx context.Context, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) + // NewBatchFromL2Block Creates a new batch from the L2 block from a datastream NewBatchFromL2Block(l2Block *datastream.L2Block) seqsendertypes.Batch + //SetCondNewSeq Allows to override the condition to send a new sequence, returns previous one + SetCondNewSeq(cond CondNewSequence) CondNewSequence String() string } type CondNewSequence interface { - NewSequenceIfWorthToSend(ctx context.Context, txBuilder TxBuilder, sequenceBatches []seqsendertypes.Batch, senderAddress, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) + //NewSequenceIfWorthToSend Return nil, nil if the sequence is not worth sending + NewSequenceIfWorthToSend(ctx context.Context, txBuilder TxBuilder, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address) (seqsendertypes.Sequence, error) } diff --git a/sequencesender/txbuilder/interface_test.go b/sequencesender/txbuilder/interface_test.go new file mode 100644 index 00000000..7a27e2f1 --- /dev/null +++ b/sequencesender/txbuilder/interface_test.go @@ -0,0 +1,44 @@ +package txbuilder_test + +import ( + "context" + "fmt" + "testing" + + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/sequencesender/txbuilder/mocks_txbuilder" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +/* +This test ara auxiliars function based on the common behaviour of the interfaces +*/ + +func testSequenceIfWorthToSendNoNewSeq(t *testing.T, sut txbuilder.TxBuilder) { + cond := mocks_txbuilder.NewCondNewSequence(t) + sut.SetCondNewSeq(cond) + cond.EXPECT().NewSequenceIfWorthToSend(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Once() + seq, err := sut.NewSequenceIfWorthToSend(context.TODO(), nil, common.Address{}, 0) + require.NoError(t, err) + require.Nil(t, seq) +} + +func testSequenceIfWorthToSendErr(t *testing.T, sut txbuilder.TxBuilder) { + cond := mocks_txbuilder.NewCondNewSequence(t) + sut.SetCondNewSeq(cond) + returnErr := fmt.Errorf("test-error") + cond.EXPECT().NewSequenceIfWorthToSend(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, returnErr).Once() + seq, err := sut.NewSequenceIfWorthToSend(context.TODO(), nil, common.Address{}, 0) + require.ErrorIs(t, returnErr, err) + require.Nil(t, seq) +} + +func testSetCondNewSeq(t *testing.T, sut txbuilder.TxBuilder) { + cond := mocks_txbuilder.NewCondNewSequence(t) + previous := sut.SetCondNewSeq(cond) + cond2 := mocks_txbuilder.NewCondNewSequence(t) + previous = sut.SetCondNewSeq(cond2) + require.Equal(t, cond, previous) +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/cond_new_sequence.go b/sequencesender/txbuilder/mocks_txbuilder/cond_new_sequence.go new file mode 100644 index 00000000..ae818ce9 --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/cond_new_sequence.go @@ -0,0 +1,103 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + seqsendertypes "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + + txbuilder "github.com/0xPolygon/cdk/sequencesender/txbuilder" +) + +// CondNewSequence is an autogenerated mock type for the CondNewSequence type +type CondNewSequence struct { + mock.Mock +} + +type CondNewSequence_Expecter struct { + mock *mock.Mock +} + +func (_m *CondNewSequence) EXPECT() *CondNewSequence_Expecter { + return &CondNewSequence_Expecter{mock: &_m.Mock} +} + +// NewSequenceIfWorthToSend provides a mock function with given fields: ctx, txBuilder, sequenceBatches, l2Coinbase +func (_m *CondNewSequence) NewSequenceIfWorthToSend(ctx context.Context, txBuilder txbuilder.TxBuilder, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address) (seqsendertypes.Sequence, error) { + ret := _m.Called(ctx, txBuilder, sequenceBatches, l2Coinbase) + + if len(ret) == 0 { + panic("no return value specified for NewSequenceIfWorthToSend") + } + + var r0 seqsendertypes.Sequence + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, txbuilder.TxBuilder, []seqsendertypes.Batch, common.Address) (seqsendertypes.Sequence, error)); ok { + return rf(ctx, txBuilder, sequenceBatches, l2Coinbase) + } + if rf, ok := ret.Get(0).(func(context.Context, txbuilder.TxBuilder, []seqsendertypes.Batch, common.Address) seqsendertypes.Sequence); ok { + r0 = rf(ctx, txBuilder, sequenceBatches, l2Coinbase) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(seqsendertypes.Sequence) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, txbuilder.TxBuilder, []seqsendertypes.Batch, common.Address) error); ok { + r1 = rf(ctx, txBuilder, sequenceBatches, l2Coinbase) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CondNewSequence_NewSequenceIfWorthToSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewSequenceIfWorthToSend' +type CondNewSequence_NewSequenceIfWorthToSend_Call struct { + *mock.Call +} + +// NewSequenceIfWorthToSend is a helper method to define mock.On call +// - ctx context.Context +// - txBuilder txbuilder.TxBuilder +// - sequenceBatches []seqsendertypes.Batch +// - l2Coinbase common.Address +func (_e *CondNewSequence_Expecter) NewSequenceIfWorthToSend(ctx interface{}, txBuilder interface{}, sequenceBatches interface{}, l2Coinbase interface{}) *CondNewSequence_NewSequenceIfWorthToSend_Call { + return &CondNewSequence_NewSequenceIfWorthToSend_Call{Call: _e.mock.On("NewSequenceIfWorthToSend", ctx, txBuilder, sequenceBatches, l2Coinbase)} +} + +func (_c *CondNewSequence_NewSequenceIfWorthToSend_Call) Run(run func(ctx context.Context, txBuilder txbuilder.TxBuilder, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address)) *CondNewSequence_NewSequenceIfWorthToSend_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(txbuilder.TxBuilder), args[2].([]seqsendertypes.Batch), args[3].(common.Address)) + }) + return _c +} + +func (_c *CondNewSequence_NewSequenceIfWorthToSend_Call) Return(_a0 seqsendertypes.Sequence, _a1 error) *CondNewSequence_NewSequenceIfWorthToSend_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CondNewSequence_NewSequenceIfWorthToSend_Call) RunAndReturn(run func(context.Context, txbuilder.TxBuilder, []seqsendertypes.Batch, common.Address) (seqsendertypes.Sequence, error)) *CondNewSequence_NewSequenceIfWorthToSend_Call { + _c.Call.Return(run) + return _c +} + +// NewCondNewSequence creates a new instance of CondNewSequence. 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 NewCondNewSequence(t interface { + mock.TestingT + Cleanup(func()) +}) *CondNewSequence { + mock := &CondNewSequence{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_contractor.go b/sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_contractor.go new file mode 100644 index 00000000..93dadeef --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_contractor.go @@ -0,0 +1,139 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + mock "github.com/stretchr/testify/mock" +) + +// GlobalExitRootBananaContractor is an autogenerated mock type for the globalExitRootBananaContractor type +type GlobalExitRootBananaContractor struct { + mock.Mock +} + +type GlobalExitRootBananaContractor_Expecter struct { + mock *mock.Mock +} + +func (_m *GlobalExitRootBananaContractor) EXPECT() *GlobalExitRootBananaContractor_Expecter { + return &GlobalExitRootBananaContractor_Expecter{mock: &_m.Mock} +} + +// L1InfoRootMap provides a mock function with given fields: opts, index +func (_m *GlobalExitRootBananaContractor) L1InfoRootMap(opts *bind.CallOpts, index uint32) ([32]byte, error) { + ret := _m.Called(opts, index) + + if len(ret) == 0 { + panic("no return value specified for L1InfoRootMap") + } + + var r0 [32]byte + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint32) ([32]byte, error)); ok { + return rf(opts, index) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint32) [32]byte); ok { + r0 = rf(opts, index) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([32]byte) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, uint32) error); ok { + r1 = rf(opts, index) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GlobalExitRootBananaContractor_L1InfoRootMap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'L1InfoRootMap' +type GlobalExitRootBananaContractor_L1InfoRootMap_Call struct { + *mock.Call +} + +// L1InfoRootMap is a helper method to define mock.On call +// - opts *bind.CallOpts +// - index uint32 +func (_e *GlobalExitRootBananaContractor_Expecter) L1InfoRootMap(opts interface{}, index interface{}) *GlobalExitRootBananaContractor_L1InfoRootMap_Call { + return &GlobalExitRootBananaContractor_L1InfoRootMap_Call{Call: _e.mock.On("L1InfoRootMap", opts, index)} +} + +func (_c *GlobalExitRootBananaContractor_L1InfoRootMap_Call) Run(run func(opts *bind.CallOpts, index uint32)) *GlobalExitRootBananaContractor_L1InfoRootMap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint32)) + }) + return _c +} + +func (_c *GlobalExitRootBananaContractor_L1InfoRootMap_Call) Return(_a0 [32]byte, _a1 error) *GlobalExitRootBananaContractor_L1InfoRootMap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *GlobalExitRootBananaContractor_L1InfoRootMap_Call) RunAndReturn(run func(*bind.CallOpts, uint32) ([32]byte, error)) *GlobalExitRootBananaContractor_L1InfoRootMap_Call { + _c.Call.Return(run) + return _c +} + +// String provides a mock function with given fields: +func (_m *GlobalExitRootBananaContractor) String() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for String") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GlobalExitRootBananaContractor_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +type GlobalExitRootBananaContractor_String_Call struct { + *mock.Call +} + +// String is a helper method to define mock.On call +func (_e *GlobalExitRootBananaContractor_Expecter) String() *GlobalExitRootBananaContractor_String_Call { + return &GlobalExitRootBananaContractor_String_Call{Call: _e.mock.On("String")} +} + +func (_c *GlobalExitRootBananaContractor_String_Call) Run(run func()) *GlobalExitRootBananaContractor_String_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *GlobalExitRootBananaContractor_String_Call) Return(_a0 string) *GlobalExitRootBananaContractor_String_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *GlobalExitRootBananaContractor_String_Call) RunAndReturn(run func() string) *GlobalExitRootBananaContractor_String_Call { + _c.Call.Return(run) + return _c +} + +// NewGlobalExitRootBananaContractor creates a new instance of GlobalExitRootBananaContractor. 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 NewGlobalExitRootBananaContractor(t interface { + mock.TestingT + Cleanup(func()) +}) *GlobalExitRootBananaContractor { + mock := &GlobalExitRootBananaContractor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_zkevm_contractor.go b/sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_zkevm_contractor.go new file mode 100644 index 00000000..f7aed125 --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/global_exit_root_banana_zkevm_contractor.go @@ -0,0 +1,139 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + mock "github.com/stretchr/testify/mock" +) + +// GlobalExitRootBananaZKEVMContractor is an autogenerated mock type for the globalExitRootBananaZKEVMContractor type +type GlobalExitRootBananaZKEVMContractor struct { + mock.Mock +} + +type GlobalExitRootBananaZKEVMContractor_Expecter struct { + mock *mock.Mock +} + +func (_m *GlobalExitRootBananaZKEVMContractor) EXPECT() *GlobalExitRootBananaZKEVMContractor_Expecter { + return &GlobalExitRootBananaZKEVMContractor_Expecter{mock: &_m.Mock} +} + +// L1InfoRootMap provides a mock function with given fields: opts, index +func (_m *GlobalExitRootBananaZKEVMContractor) L1InfoRootMap(opts *bind.CallOpts, index uint32) ([32]byte, error) { + ret := _m.Called(opts, index) + + if len(ret) == 0 { + panic("no return value specified for L1InfoRootMap") + } + + var r0 [32]byte + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint32) ([32]byte, error)); ok { + return rf(opts, index) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint32) [32]byte); ok { + r0 = rf(opts, index) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([32]byte) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, uint32) error); ok { + r1 = rf(opts, index) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'L1InfoRootMap' +type GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call struct { + *mock.Call +} + +// L1InfoRootMap is a helper method to define mock.On call +// - opts *bind.CallOpts +// - index uint32 +func (_e *GlobalExitRootBananaZKEVMContractor_Expecter) L1InfoRootMap(opts interface{}, index interface{}) *GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call { + return &GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call{Call: _e.mock.On("L1InfoRootMap", opts, index)} +} + +func (_c *GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call) Run(run func(opts *bind.CallOpts, index uint32)) *GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint32)) + }) + return _c +} + +func (_c *GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call) Return(_a0 [32]byte, _a1 error) *GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call) RunAndReturn(run func(*bind.CallOpts, uint32) ([32]byte, error)) *GlobalExitRootBananaZKEVMContractor_L1InfoRootMap_Call { + _c.Call.Return(run) + return _c +} + +// String provides a mock function with given fields: +func (_m *GlobalExitRootBananaZKEVMContractor) String() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for String") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GlobalExitRootBananaZKEVMContractor_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +type GlobalExitRootBananaZKEVMContractor_String_Call struct { + *mock.Call +} + +// String is a helper method to define mock.On call +func (_e *GlobalExitRootBananaZKEVMContractor_Expecter) String() *GlobalExitRootBananaZKEVMContractor_String_Call { + return &GlobalExitRootBananaZKEVMContractor_String_Call{Call: _e.mock.On("String")} +} + +func (_c *GlobalExitRootBananaZKEVMContractor_String_Call) Run(run func()) *GlobalExitRootBananaZKEVMContractor_String_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *GlobalExitRootBananaZKEVMContractor_String_Call) Return(_a0 string) *GlobalExitRootBananaZKEVMContractor_String_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *GlobalExitRootBananaZKEVMContractor_String_Call) RunAndReturn(run func() string) *GlobalExitRootBananaZKEVMContractor_String_Call { + _c.Call.Return(run) + return _c +} + +// NewGlobalExitRootBananaZKEVMContractor creates a new instance of GlobalExitRootBananaZKEVMContractor. 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 NewGlobalExitRootBananaZKEVMContractor(t interface { + mock.TestingT + Cleanup(func()) +}) *GlobalExitRootBananaZKEVMContractor { + mock := &GlobalExitRootBananaZKEVMContractor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_base_contractor.go b/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_base_contractor.go new file mode 100644 index 00000000..acd82a4e --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_base_contractor.go @@ -0,0 +1,93 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + mock "github.com/stretchr/testify/mock" +) + +// RollupBananaBaseContractor is an autogenerated mock type for the rollupBananaBaseContractor type +type RollupBananaBaseContractor struct { + mock.Mock +} + +type RollupBananaBaseContractor_Expecter struct { + mock *mock.Mock +} + +func (_m *RollupBananaBaseContractor) EXPECT() *RollupBananaBaseContractor_Expecter { + return &RollupBananaBaseContractor_Expecter{mock: &_m.Mock} +} + +// LastAccInputHash provides a mock function with given fields: opts +func (_m *RollupBananaBaseContractor) LastAccInputHash(opts *bind.CallOpts) ([32]byte, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for LastAccInputHash") + } + + var r0 [32]byte + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([32]byte, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) [32]byte); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([32]byte) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RollupBananaBaseContractor_LastAccInputHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LastAccInputHash' +type RollupBananaBaseContractor_LastAccInputHash_Call struct { + *mock.Call +} + +// LastAccInputHash is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *RollupBananaBaseContractor_Expecter) LastAccInputHash(opts interface{}) *RollupBananaBaseContractor_LastAccInputHash_Call { + return &RollupBananaBaseContractor_LastAccInputHash_Call{Call: _e.mock.On("LastAccInputHash", opts)} +} + +func (_c *RollupBananaBaseContractor_LastAccInputHash_Call) Run(run func(opts *bind.CallOpts)) *RollupBananaBaseContractor_LastAccInputHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *RollupBananaBaseContractor_LastAccInputHash_Call) Return(_a0 [32]byte, _a1 error) *RollupBananaBaseContractor_LastAccInputHash_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RollupBananaBaseContractor_LastAccInputHash_Call) RunAndReturn(run func(*bind.CallOpts) ([32]byte, error)) *RollupBananaBaseContractor_LastAccInputHash_Call { + _c.Call.Return(run) + return _c +} + +// NewRollupBananaBaseContractor creates a new instance of RollupBananaBaseContractor. 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 NewRollupBananaBaseContractor(t interface { + mock.TestingT + Cleanup(func()) +}) *RollupBananaBaseContractor { + mock := &RollupBananaBaseContractor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_validium_contractor.go b/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_validium_contractor.go new file mode 100644 index 00000000..a59b88dd --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_validium_contractor.go @@ -0,0 +1,163 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + polygonvalidiumetrog "github.com/0xPolygon/cdk-contracts-tooling/contracts/banana/polygonvalidiumetrog" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// RollupBananaValidiumContractor is an autogenerated mock type for the rollupBananaValidiumContractor type +type RollupBananaValidiumContractor struct { + mock.Mock +} + +type RollupBananaValidiumContractor_Expecter struct { + mock *mock.Mock +} + +func (_m *RollupBananaValidiumContractor) EXPECT() *RollupBananaValidiumContractor_Expecter { + return &RollupBananaValidiumContractor_Expecter{mock: &_m.Mock} +} + +// LastAccInputHash provides a mock function with given fields: opts +func (_m *RollupBananaValidiumContractor) LastAccInputHash(opts *bind.CallOpts) ([32]byte, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for LastAccInputHash") + } + + var r0 [32]byte + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([32]byte, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) [32]byte); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([32]byte) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RollupBananaValidiumContractor_LastAccInputHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LastAccInputHash' +type RollupBananaValidiumContractor_LastAccInputHash_Call struct { + *mock.Call +} + +// LastAccInputHash is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *RollupBananaValidiumContractor_Expecter) LastAccInputHash(opts interface{}) *RollupBananaValidiumContractor_LastAccInputHash_Call { + return &RollupBananaValidiumContractor_LastAccInputHash_Call{Call: _e.mock.On("LastAccInputHash", opts)} +} + +func (_c *RollupBananaValidiumContractor_LastAccInputHash_Call) Run(run func(opts *bind.CallOpts)) *RollupBananaValidiumContractor_LastAccInputHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *RollupBananaValidiumContractor_LastAccInputHash_Call) Return(_a0 [32]byte, _a1 error) *RollupBananaValidiumContractor_LastAccInputHash_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RollupBananaValidiumContractor_LastAccInputHash_Call) RunAndReturn(run func(*bind.CallOpts) ([32]byte, error)) *RollupBananaValidiumContractor_LastAccInputHash_Call { + _c.Call.Return(run) + return _c +} + +// SequenceBatchesValidium provides a mock function with given fields: opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase, dataAvailabilityMessage +func (_m *RollupBananaValidiumContractor) SequenceBatchesValidium(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, indexL1InfoRoot uint32, maxSequenceTimestamp uint64, expectedFinalAccInputHash [32]byte, l2Coinbase common.Address, dataAvailabilityMessage []byte) (*types.Transaction, error) { + ret := _m.Called(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase, dataAvailabilityMessage) + + if len(ret) == 0 { + panic("no return value specified for SequenceBatchesValidium") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint32, uint64, [32]byte, common.Address, []byte) (*types.Transaction, error)); ok { + return rf(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase, dataAvailabilityMessage) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint32, uint64, [32]byte, common.Address, []byte) *types.Transaction); ok { + r0 = rf(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase, dataAvailabilityMessage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint32, uint64, [32]byte, common.Address, []byte) error); ok { + r1 = rf(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase, dataAvailabilityMessage) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RollupBananaValidiumContractor_SequenceBatchesValidium_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceBatchesValidium' +type RollupBananaValidiumContractor_SequenceBatchesValidium_Call struct { + *mock.Call +} + +// SequenceBatchesValidium is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData +// - indexL1InfoRoot uint32 +// - maxSequenceTimestamp uint64 +// - expectedFinalAccInputHash [32]byte +// - l2Coinbase common.Address +// - dataAvailabilityMessage []byte +func (_e *RollupBananaValidiumContractor_Expecter) SequenceBatchesValidium(opts interface{}, batches interface{}, indexL1InfoRoot interface{}, maxSequenceTimestamp interface{}, expectedFinalAccInputHash interface{}, l2Coinbase interface{}, dataAvailabilityMessage interface{}) *RollupBananaValidiumContractor_SequenceBatchesValidium_Call { + return &RollupBananaValidiumContractor_SequenceBatchesValidium_Call{Call: _e.mock.On("SequenceBatchesValidium", opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase, dataAvailabilityMessage)} +} + +func (_c *RollupBananaValidiumContractor_SequenceBatchesValidium_Call) Run(run func(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, indexL1InfoRoot uint32, maxSequenceTimestamp uint64, expectedFinalAccInputHash [32]byte, l2Coinbase common.Address, dataAvailabilityMessage []byte)) *RollupBananaValidiumContractor_SequenceBatchesValidium_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData), args[2].(uint32), args[3].(uint64), args[4].([32]byte), args[5].(common.Address), args[6].([]byte)) + }) + return _c +} + +func (_c *RollupBananaValidiumContractor_SequenceBatchesValidium_Call) Return(_a0 *types.Transaction, _a1 error) *RollupBananaValidiumContractor_SequenceBatchesValidium_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RollupBananaValidiumContractor_SequenceBatchesValidium_Call) RunAndReturn(run func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint32, uint64, [32]byte, common.Address, []byte) (*types.Transaction, error)) *RollupBananaValidiumContractor_SequenceBatchesValidium_Call { + _c.Call.Return(run) + return _c +} + +// NewRollupBananaValidiumContractor creates a new instance of RollupBananaValidiumContractor. 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 NewRollupBananaValidiumContractor(t interface { + mock.TestingT + Cleanup(func()) +}) *RollupBananaValidiumContractor { + mock := &RollupBananaValidiumContractor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_zkevm_contractor.go b/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_zkevm_contractor.go new file mode 100644 index 00000000..e29e3252 --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/rollup_banana_zkevm_contractor.go @@ -0,0 +1,162 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + polygonvalidiumetrog "github.com/0xPolygon/cdk-contracts-tooling/contracts/banana/polygonvalidiumetrog" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// RollupBananaZKEVMContractor is an autogenerated mock type for the rollupBananaZKEVMContractor type +type RollupBananaZKEVMContractor struct { + mock.Mock +} + +type RollupBananaZKEVMContractor_Expecter struct { + mock *mock.Mock +} + +func (_m *RollupBananaZKEVMContractor) EXPECT() *RollupBananaZKEVMContractor_Expecter { + return &RollupBananaZKEVMContractor_Expecter{mock: &_m.Mock} +} + +// LastAccInputHash provides a mock function with given fields: opts +func (_m *RollupBananaZKEVMContractor) LastAccInputHash(opts *bind.CallOpts) ([32]byte, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for LastAccInputHash") + } + + var r0 [32]byte + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([32]byte, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) [32]byte); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([32]byte) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RollupBananaZKEVMContractor_LastAccInputHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LastAccInputHash' +type RollupBananaZKEVMContractor_LastAccInputHash_Call struct { + *mock.Call +} + +// LastAccInputHash is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *RollupBananaZKEVMContractor_Expecter) LastAccInputHash(opts interface{}) *RollupBananaZKEVMContractor_LastAccInputHash_Call { + return &RollupBananaZKEVMContractor_LastAccInputHash_Call{Call: _e.mock.On("LastAccInputHash", opts)} +} + +func (_c *RollupBananaZKEVMContractor_LastAccInputHash_Call) Run(run func(opts *bind.CallOpts)) *RollupBananaZKEVMContractor_LastAccInputHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *RollupBananaZKEVMContractor_LastAccInputHash_Call) Return(_a0 [32]byte, _a1 error) *RollupBananaZKEVMContractor_LastAccInputHash_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RollupBananaZKEVMContractor_LastAccInputHash_Call) RunAndReturn(run func(*bind.CallOpts) ([32]byte, error)) *RollupBananaZKEVMContractor_LastAccInputHash_Call { + _c.Call.Return(run) + return _c +} + +// SequenceBatches provides a mock function with given fields: opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase +func (_m *RollupBananaZKEVMContractor) SequenceBatches(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, indexL1InfoRoot uint32, maxSequenceTimestamp uint64, expectedFinalAccInputHash [32]byte, l2Coinbase common.Address) (*types.Transaction, error) { + ret := _m.Called(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase) + + if len(ret) == 0 { + panic("no return value specified for SequenceBatches") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint32, uint64, [32]byte, common.Address) (*types.Transaction, error)); ok { + return rf(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint32, uint64, [32]byte, common.Address) *types.Transaction); ok { + r0 = rf(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint32, uint64, [32]byte, common.Address) error); ok { + r1 = rf(opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RollupBananaZKEVMContractor_SequenceBatches_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceBatches' +type RollupBananaZKEVMContractor_SequenceBatches_Call struct { + *mock.Call +} + +// SequenceBatches is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData +// - indexL1InfoRoot uint32 +// - maxSequenceTimestamp uint64 +// - expectedFinalAccInputHash [32]byte +// - l2Coinbase common.Address +func (_e *RollupBananaZKEVMContractor_Expecter) SequenceBatches(opts interface{}, batches interface{}, indexL1InfoRoot interface{}, maxSequenceTimestamp interface{}, expectedFinalAccInputHash interface{}, l2Coinbase interface{}) *RollupBananaZKEVMContractor_SequenceBatches_Call { + return &RollupBananaZKEVMContractor_SequenceBatches_Call{Call: _e.mock.On("SequenceBatches", opts, batches, indexL1InfoRoot, maxSequenceTimestamp, expectedFinalAccInputHash, l2Coinbase)} +} + +func (_c *RollupBananaZKEVMContractor_SequenceBatches_Call) Run(run func(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, indexL1InfoRoot uint32, maxSequenceTimestamp uint64, expectedFinalAccInputHash [32]byte, l2Coinbase common.Address)) *RollupBananaZKEVMContractor_SequenceBatches_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData), args[2].(uint32), args[3].(uint64), args[4].([32]byte), args[5].(common.Address)) + }) + return _c +} + +func (_c *RollupBananaZKEVMContractor_SequenceBatches_Call) Return(_a0 *types.Transaction, _a1 error) *RollupBananaZKEVMContractor_SequenceBatches_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RollupBananaZKEVMContractor_SequenceBatches_Call) RunAndReturn(run func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint32, uint64, [32]byte, common.Address) (*types.Transaction, error)) *RollupBananaZKEVMContractor_SequenceBatches_Call { + _c.Call.Return(run) + return _c +} + +// NewRollupBananaZKEVMContractor creates a new instance of RollupBananaZKEVMContractor. 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 NewRollupBananaZKEVMContractor(t interface { + mock.TestingT + Cleanup(func()) +}) *RollupBananaZKEVMContractor { + mock := &RollupBananaZKEVMContractor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_validium_contractor.go b/sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_validium_contractor.go new file mode 100644 index 00000000..0d94c081 --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_validium_contractor.go @@ -0,0 +1,104 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + polygonvalidiumetrog "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// RollupElderberryValidiumContractor is an autogenerated mock type for the rollupElderberryValidiumContractor type +type RollupElderberryValidiumContractor struct { + mock.Mock +} + +type RollupElderberryValidiumContractor_Expecter struct { + mock *mock.Mock +} + +func (_m *RollupElderberryValidiumContractor) EXPECT() *RollupElderberryValidiumContractor_Expecter { + return &RollupElderberryValidiumContractor_Expecter{mock: &_m.Mock} +} + +// SequenceBatchesValidium provides a mock function with given fields: opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase, dataAvailabilityMessage +func (_m *RollupElderberryValidiumContractor) SequenceBatchesValidium(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, maxSequenceTimestamp uint64, initSequencedBatch uint64, l2Coinbase common.Address, dataAvailabilityMessage []byte) (*types.Transaction, error) { + ret := _m.Called(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase, dataAvailabilityMessage) + + if len(ret) == 0 { + panic("no return value specified for SequenceBatchesValidium") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint64, uint64, common.Address, []byte) (*types.Transaction, error)); ok { + return rf(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase, dataAvailabilityMessage) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint64, uint64, common.Address, []byte) *types.Transaction); ok { + r0 = rf(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase, dataAvailabilityMessage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint64, uint64, common.Address, []byte) error); ok { + r1 = rf(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase, dataAvailabilityMessage) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RollupElderberryValidiumContractor_SequenceBatchesValidium_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceBatchesValidium' +type RollupElderberryValidiumContractor_SequenceBatchesValidium_Call struct { + *mock.Call +} + +// SequenceBatchesValidium is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData +// - maxSequenceTimestamp uint64 +// - initSequencedBatch uint64 +// - l2Coinbase common.Address +// - dataAvailabilityMessage []byte +func (_e *RollupElderberryValidiumContractor_Expecter) SequenceBatchesValidium(opts interface{}, batches interface{}, maxSequenceTimestamp interface{}, initSequencedBatch interface{}, l2Coinbase interface{}, dataAvailabilityMessage interface{}) *RollupElderberryValidiumContractor_SequenceBatchesValidium_Call { + return &RollupElderberryValidiumContractor_SequenceBatchesValidium_Call{Call: _e.mock.On("SequenceBatchesValidium", opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase, dataAvailabilityMessage)} +} + +func (_c *RollupElderberryValidiumContractor_SequenceBatchesValidium_Call) Run(run func(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, maxSequenceTimestamp uint64, initSequencedBatch uint64, l2Coinbase common.Address, dataAvailabilityMessage []byte)) *RollupElderberryValidiumContractor_SequenceBatchesValidium_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData), args[2].(uint64), args[3].(uint64), args[4].(common.Address), args[5].([]byte)) + }) + return _c +} + +func (_c *RollupElderberryValidiumContractor_SequenceBatchesValidium_Call) Return(_a0 *types.Transaction, _a1 error) *RollupElderberryValidiumContractor_SequenceBatchesValidium_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RollupElderberryValidiumContractor_SequenceBatchesValidium_Call) RunAndReturn(run func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, uint64, uint64, common.Address, []byte) (*types.Transaction, error)) *RollupElderberryValidiumContractor_SequenceBatchesValidium_Call { + _c.Call.Return(run) + return _c +} + +// NewRollupElderberryValidiumContractor creates a new instance of RollupElderberryValidiumContractor. 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 NewRollupElderberryValidiumContractor(t interface { + mock.TestingT + Cleanup(func()) +}) *RollupElderberryValidiumContractor { + mock := &RollupElderberryValidiumContractor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_zkevm_contractor.go b/sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_zkevm_contractor.go new file mode 100644 index 00000000..1ed208ab --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/rollup_elderberry_zkevm_contractor.go @@ -0,0 +1,103 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + polygonvalidiumetrog "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// RollupElderberryZKEVMContractor is an autogenerated mock type for the rollupElderberryZKEVMContractor type +type RollupElderberryZKEVMContractor struct { + mock.Mock +} + +type RollupElderberryZKEVMContractor_Expecter struct { + mock *mock.Mock +} + +func (_m *RollupElderberryZKEVMContractor) EXPECT() *RollupElderberryZKEVMContractor_Expecter { + return &RollupElderberryZKEVMContractor_Expecter{mock: &_m.Mock} +} + +// SequenceBatches provides a mock function with given fields: opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase +func (_m *RollupElderberryZKEVMContractor) SequenceBatches(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, maxSequenceTimestamp uint64, initSequencedBatch uint64, l2Coinbase common.Address) (*types.Transaction, error) { + ret := _m.Called(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase) + + if len(ret) == 0 { + panic("no return value specified for SequenceBatches") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint64, uint64, common.Address) (*types.Transaction, error)); ok { + return rf(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint64, uint64, common.Address) *types.Transaction); ok { + r0 = rf(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint64, uint64, common.Address) error); ok { + r1 = rf(opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RollupElderberryZKEVMContractor_SequenceBatches_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceBatches' +type RollupElderberryZKEVMContractor_SequenceBatches_Call struct { + *mock.Call +} + +// SequenceBatches is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData +// - maxSequenceTimestamp uint64 +// - initSequencedBatch uint64 +// - l2Coinbase common.Address +func (_e *RollupElderberryZKEVMContractor_Expecter) SequenceBatches(opts interface{}, batches interface{}, maxSequenceTimestamp interface{}, initSequencedBatch interface{}, l2Coinbase interface{}) *RollupElderberryZKEVMContractor_SequenceBatches_Call { + return &RollupElderberryZKEVMContractor_SequenceBatches_Call{Call: _e.mock.On("SequenceBatches", opts, batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase)} +} + +func (_c *RollupElderberryZKEVMContractor_SequenceBatches_Call) Run(run func(opts *bind.TransactOpts, batches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, maxSequenceTimestamp uint64, initSequencedBatch uint64, l2Coinbase common.Address)) *RollupElderberryZKEVMContractor_SequenceBatches_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData), args[2].(uint64), args[3].(uint64), args[4].(common.Address)) + }) + return _c +} + +func (_c *RollupElderberryZKEVMContractor_SequenceBatches_Call) Return(_a0 *types.Transaction, _a1 error) *RollupElderberryZKEVMContractor_SequenceBatches_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RollupElderberryZKEVMContractor_SequenceBatches_Call) RunAndReturn(run func(*bind.TransactOpts, []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, uint64, uint64, common.Address) (*types.Transaction, error)) *RollupElderberryZKEVMContractor_SequenceBatches_Call { + _c.Call.Return(run) + return _c +} + +// NewRollupElderberryZKEVMContractor creates a new instance of RollupElderberryZKEVMContractor. 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 NewRollupElderberryZKEVMContractor(t interface { + mock.TestingT + Cleanup(func()) +}) *RollupElderberryZKEVMContractor { + mock := &RollupElderberryZKEVMContractor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/mocks_txbuilder/tx_builder.go b/sequencesender/txbuilder/mocks_txbuilder/tx_builder.go new file mode 100644 index 00000000..14f0539b --- /dev/null +++ b/sequencesender/txbuilder/mocks_txbuilder/tx_builder.go @@ -0,0 +1,366 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks_txbuilder + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + datastream "github.com/0xPolygon/cdk/state/datastream" + + mock "github.com/stretchr/testify/mock" + + seqsendertypes "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + + txbuilder "github.com/0xPolygon/cdk/sequencesender/txbuilder" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// TxBuilder is an autogenerated mock type for the TxBuilder type +type TxBuilder struct { + mock.Mock +} + +type TxBuilder_Expecter struct { + mock *mock.Mock +} + +func (_m *TxBuilder) EXPECT() *TxBuilder_Expecter { + return &TxBuilder_Expecter{mock: &_m.Mock} +} + +// BuildSequenceBatchesTx provides a mock function with given fields: ctx, sequences +func (_m *TxBuilder) BuildSequenceBatchesTx(ctx context.Context, sequences seqsendertypes.Sequence) (*types.Transaction, error) { + ret := _m.Called(ctx, sequences) + + if len(ret) == 0 { + panic("no return value specified for BuildSequenceBatchesTx") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, seqsendertypes.Sequence) (*types.Transaction, error)); ok { + return rf(ctx, sequences) + } + if rf, ok := ret.Get(0).(func(context.Context, seqsendertypes.Sequence) *types.Transaction); ok { + r0 = rf(ctx, sequences) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, seqsendertypes.Sequence) error); ok { + r1 = rf(ctx, sequences) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TxBuilder_BuildSequenceBatchesTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildSequenceBatchesTx' +type TxBuilder_BuildSequenceBatchesTx_Call struct { + *mock.Call +} + +// BuildSequenceBatchesTx is a helper method to define mock.On call +// - ctx context.Context +// - sequences seqsendertypes.Sequence +func (_e *TxBuilder_Expecter) BuildSequenceBatchesTx(ctx interface{}, sequences interface{}) *TxBuilder_BuildSequenceBatchesTx_Call { + return &TxBuilder_BuildSequenceBatchesTx_Call{Call: _e.mock.On("BuildSequenceBatchesTx", ctx, sequences)} +} + +func (_c *TxBuilder_BuildSequenceBatchesTx_Call) Run(run func(ctx context.Context, sequences seqsendertypes.Sequence)) *TxBuilder_BuildSequenceBatchesTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(seqsendertypes.Sequence)) + }) + return _c +} + +func (_c *TxBuilder_BuildSequenceBatchesTx_Call) Return(_a0 *types.Transaction, _a1 error) *TxBuilder_BuildSequenceBatchesTx_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *TxBuilder_BuildSequenceBatchesTx_Call) RunAndReturn(run func(context.Context, seqsendertypes.Sequence) (*types.Transaction, error)) *TxBuilder_BuildSequenceBatchesTx_Call { + _c.Call.Return(run) + return _c +} + +// NewBatchFromL2Block provides a mock function with given fields: l2Block +func (_m *TxBuilder) NewBatchFromL2Block(l2Block *datastream.L2Block) seqsendertypes.Batch { + ret := _m.Called(l2Block) + + if len(ret) == 0 { + panic("no return value specified for NewBatchFromL2Block") + } + + var r0 seqsendertypes.Batch + if rf, ok := ret.Get(0).(func(*datastream.L2Block) seqsendertypes.Batch); ok { + r0 = rf(l2Block) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(seqsendertypes.Batch) + } + } + + return r0 +} + +// TxBuilder_NewBatchFromL2Block_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewBatchFromL2Block' +type TxBuilder_NewBatchFromL2Block_Call struct { + *mock.Call +} + +// NewBatchFromL2Block is a helper method to define mock.On call +// - l2Block *datastream.L2Block +func (_e *TxBuilder_Expecter) NewBatchFromL2Block(l2Block interface{}) *TxBuilder_NewBatchFromL2Block_Call { + return &TxBuilder_NewBatchFromL2Block_Call{Call: _e.mock.On("NewBatchFromL2Block", l2Block)} +} + +func (_c *TxBuilder_NewBatchFromL2Block_Call) Run(run func(l2Block *datastream.L2Block)) *TxBuilder_NewBatchFromL2Block_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*datastream.L2Block)) + }) + return _c +} + +func (_c *TxBuilder_NewBatchFromL2Block_Call) Return(_a0 seqsendertypes.Batch) *TxBuilder_NewBatchFromL2Block_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *TxBuilder_NewBatchFromL2Block_Call) RunAndReturn(run func(*datastream.L2Block) seqsendertypes.Batch) *TxBuilder_NewBatchFromL2Block_Call { + _c.Call.Return(run) + return _c +} + +// NewSequence provides a mock function with given fields: batches, coinbase +func (_m *TxBuilder) NewSequence(batches []seqsendertypes.Batch, coinbase common.Address) (seqsendertypes.Sequence, error) { + ret := _m.Called(batches, coinbase) + + if len(ret) == 0 { + panic("no return value specified for NewSequence") + } + + var r0 seqsendertypes.Sequence + var r1 error + if rf, ok := ret.Get(0).(func([]seqsendertypes.Batch, common.Address) (seqsendertypes.Sequence, error)); ok { + return rf(batches, coinbase) + } + if rf, ok := ret.Get(0).(func([]seqsendertypes.Batch, common.Address) seqsendertypes.Sequence); ok { + r0 = rf(batches, coinbase) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(seqsendertypes.Sequence) + } + } + + if rf, ok := ret.Get(1).(func([]seqsendertypes.Batch, common.Address) error); ok { + r1 = rf(batches, coinbase) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TxBuilder_NewSequence_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewSequence' +type TxBuilder_NewSequence_Call struct { + *mock.Call +} + +// NewSequence is a helper method to define mock.On call +// - batches []seqsendertypes.Batch +// - coinbase common.Address +func (_e *TxBuilder_Expecter) NewSequence(batches interface{}, coinbase interface{}) *TxBuilder_NewSequence_Call { + return &TxBuilder_NewSequence_Call{Call: _e.mock.On("NewSequence", batches, coinbase)} +} + +func (_c *TxBuilder_NewSequence_Call) Run(run func(batches []seqsendertypes.Batch, coinbase common.Address)) *TxBuilder_NewSequence_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]seqsendertypes.Batch), args[1].(common.Address)) + }) + return _c +} + +func (_c *TxBuilder_NewSequence_Call) Return(_a0 seqsendertypes.Sequence, _a1 error) *TxBuilder_NewSequence_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *TxBuilder_NewSequence_Call) RunAndReturn(run func([]seqsendertypes.Batch, common.Address) (seqsendertypes.Sequence, error)) *TxBuilder_NewSequence_Call { + _c.Call.Return(run) + return _c +} + +// NewSequenceIfWorthToSend provides a mock function with given fields: ctx, sequenceBatches, l2Coinbase, batchNumber +func (_m *TxBuilder) NewSequenceIfWorthToSend(ctx context.Context, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) { + ret := _m.Called(ctx, sequenceBatches, l2Coinbase, batchNumber) + + if len(ret) == 0 { + panic("no return value specified for NewSequenceIfWorthToSend") + } + + var r0 seqsendertypes.Sequence + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []seqsendertypes.Batch, common.Address, uint64) (seqsendertypes.Sequence, error)); ok { + return rf(ctx, sequenceBatches, l2Coinbase, batchNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, []seqsendertypes.Batch, common.Address, uint64) seqsendertypes.Sequence); ok { + r0 = rf(ctx, sequenceBatches, l2Coinbase, batchNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(seqsendertypes.Sequence) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []seqsendertypes.Batch, common.Address, uint64) error); ok { + r1 = rf(ctx, sequenceBatches, l2Coinbase, batchNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TxBuilder_NewSequenceIfWorthToSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewSequenceIfWorthToSend' +type TxBuilder_NewSequenceIfWorthToSend_Call struct { + *mock.Call +} + +// NewSequenceIfWorthToSend is a helper method to define mock.On call +// - ctx context.Context +// - sequenceBatches []seqsendertypes.Batch +// - l2Coinbase common.Address +// - batchNumber uint64 +func (_e *TxBuilder_Expecter) NewSequenceIfWorthToSend(ctx interface{}, sequenceBatches interface{}, l2Coinbase interface{}, batchNumber interface{}) *TxBuilder_NewSequenceIfWorthToSend_Call { + return &TxBuilder_NewSequenceIfWorthToSend_Call{Call: _e.mock.On("NewSequenceIfWorthToSend", ctx, sequenceBatches, l2Coinbase, batchNumber)} +} + +func (_c *TxBuilder_NewSequenceIfWorthToSend_Call) Run(run func(ctx context.Context, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address, batchNumber uint64)) *TxBuilder_NewSequenceIfWorthToSend_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]seqsendertypes.Batch), args[2].(common.Address), args[3].(uint64)) + }) + return _c +} + +func (_c *TxBuilder_NewSequenceIfWorthToSend_Call) Return(_a0 seqsendertypes.Sequence, _a1 error) *TxBuilder_NewSequenceIfWorthToSend_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *TxBuilder_NewSequenceIfWorthToSend_Call) RunAndReturn(run func(context.Context, []seqsendertypes.Batch, common.Address, uint64) (seqsendertypes.Sequence, error)) *TxBuilder_NewSequenceIfWorthToSend_Call { + _c.Call.Return(run) + return _c +} + +// SetCondNewSeq provides a mock function with given fields: cond +func (_m *TxBuilder) SetCondNewSeq(cond txbuilder.CondNewSequence) txbuilder.CondNewSequence { + ret := _m.Called(cond) + + if len(ret) == 0 { + panic("no return value specified for SetCondNewSeq") + } + + var r0 txbuilder.CondNewSequence + if rf, ok := ret.Get(0).(func(txbuilder.CondNewSequence) txbuilder.CondNewSequence); ok { + r0 = rf(cond) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(txbuilder.CondNewSequence) + } + } + + return r0 +} + +// TxBuilder_SetCondNewSeq_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetCondNewSeq' +type TxBuilder_SetCondNewSeq_Call struct { + *mock.Call +} + +// SetCondNewSeq is a helper method to define mock.On call +// - cond txbuilder.CondNewSequence +func (_e *TxBuilder_Expecter) SetCondNewSeq(cond interface{}) *TxBuilder_SetCondNewSeq_Call { + return &TxBuilder_SetCondNewSeq_Call{Call: _e.mock.On("SetCondNewSeq", cond)} +} + +func (_c *TxBuilder_SetCondNewSeq_Call) Run(run func(cond txbuilder.CondNewSequence)) *TxBuilder_SetCondNewSeq_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(txbuilder.CondNewSequence)) + }) + return _c +} + +func (_c *TxBuilder_SetCondNewSeq_Call) Return(_a0 txbuilder.CondNewSequence) *TxBuilder_SetCondNewSeq_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *TxBuilder_SetCondNewSeq_Call) RunAndReturn(run func(txbuilder.CondNewSequence) txbuilder.CondNewSequence) *TxBuilder_SetCondNewSeq_Call { + _c.Call.Return(run) + return _c +} + +// String provides a mock function with given fields: +func (_m *TxBuilder) String() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for String") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// TxBuilder_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +type TxBuilder_String_Call struct { + *mock.Call +} + +// String is a helper method to define mock.On call +func (_e *TxBuilder_Expecter) String() *TxBuilder_String_Call { + return &TxBuilder_String_Call{Call: _e.mock.On("String")} +} + +func (_c *TxBuilder_String_Call) Run(run func()) *TxBuilder_String_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *TxBuilder_String_Call) Return(_a0 string) *TxBuilder_String_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *TxBuilder_String_Call) RunAndReturn(run func() string) *TxBuilder_String_Call { + _c.Call.Return(run) + return _c +} + +// NewTxBuilder creates a new instance of TxBuilder. 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 NewTxBuilder(t interface { + mock.TestingT + Cleanup(func()) +}) *TxBuilder { + mock := &TxBuilder{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sequencesender/txbuilder/validium_cond_num_batches.go b/sequencesender/txbuilder/validium_cond_num_batches.go index da663063..fd1ad22a 100644 --- a/sequencesender/txbuilder/validium_cond_num_batches.go +++ b/sequencesender/txbuilder/validium_cond_num_batches.go @@ -8,12 +8,20 @@ import ( "github.com/ethereum/go-ethereum/common" ) -type NewSequenceConditionalNumBatches struct { +var MaxBatchesForL1Disabled = uint64(0) + +type ConditionalNewSequenceNumBatches struct { maxBatchesForL1 uint64 // cfg.MaxBatchesForL1 } -func (c *NewSequenceConditionalNumBatches) NewSequenceIfWorthToSend(ctx context.Context, txBuilder TxBuilder, sequenceBatches []seqsendertypes.Batch, senderAddress, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) { - if c.maxBatchesForL1 > 0 && len(sequenceBatches) >= int(c.maxBatchesForL1) { +func NewConditionalNewSequenceNumBatches(maxBatchesForL1 uint64) *ConditionalNewSequenceNumBatches { + return &ConditionalNewSequenceNumBatches{ + maxBatchesForL1: maxBatchesForL1, + } +} + +func (c *ConditionalNewSequenceNumBatches) NewSequenceIfWorthToSend(ctx context.Context, txBuilder TxBuilder, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address) (seqsendertypes.Sequence, error) { + if c.maxBatchesForL1 != MaxBatchesForL1Disabled && len(sequenceBatches) >= int(c.maxBatchesForL1) { log.Infof( "[SeqSender] sequence should be sent to L1, because MaxBatchesForL1 (%d) has been reached", c.maxBatchesForL1, diff --git a/sequencesender/txbuilder/validium_cond_num_batches_test.go b/sequencesender/txbuilder/validium_cond_num_batches_test.go new file mode 100644 index 00000000..c449f161 --- /dev/null +++ b/sequencesender/txbuilder/validium_cond_num_batches_test.go @@ -0,0 +1,45 @@ +package txbuilder_test + +import ( + "testing" + + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/sequencesender/txbuilder/mocks_txbuilder" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestConditionalNumBatchesDisabled(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceNumBatches(0) + + tx, err := sut.NewSequenceIfWorthToSend(nil, mockTxBuilder, nil, common.Address{}) + require.NoError(t, err) + require.Nil(t, tx) +} + +// It have 1 batch and minium are 2, so no new sequence +func TestConditionalNumBatchesDontFulfillCondition(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceNumBatches(2) + var sequenceBatches []seqsendertypes.Batch + sequenceBatches = append(sequenceBatches, &txbuilder.BananaBatch{}) + tx, err := sut.NewSequenceIfWorthToSend(nil, mockTxBuilder, sequenceBatches, common.Address{}) + require.NoError(t, err) + require.Nil(t, tx) +} + +// It have 2 batch and minium are 2, so new sequence +func TestConditionalNumBatchesFulfillCondition(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceNumBatches(2) + var sequenceBatches []seqsendertypes.Batch + sequenceBatches = append(sequenceBatches, &txbuilder.BananaBatch{}) + sequenceBatches = append(sequenceBatches, &txbuilder.BananaBatch{}) + mockTxBuilder.EXPECT().NewSequence(mock.Anything, mock.Anything).Return(nil, nil) + tx, err := sut.NewSequenceIfWorthToSend(nil, mockTxBuilder, sequenceBatches, common.Address{}) + require.NoError(t, err) + require.Nil(t, tx) +} diff --git a/sequencesender/txbuilder/zkevm_cond_max_size.go b/sequencesender/txbuilder/zkevm_cond_max_size.go index 3218d015..98267e17 100644 --- a/sequencesender/txbuilder/zkevm_cond_max_size.go +++ b/sequencesender/txbuilder/zkevm_cond_max_size.go @@ -3,6 +3,7 @@ package txbuilder import ( "context" "errors" + "fmt" "github.com/0xPolygon/cdk/etherman" "github.com/0xPolygon/cdk/log" @@ -12,29 +13,50 @@ import ( var ( // ErrOversizedData when transaction input data is greater than a limit (DOS protection) - ErrOversizedData = errors.New("oversized data") + ErrOversizedData = errors.New("oversized data") + MaxTxSizeForL1Disabled = uint64(0) ) -type NewSequenceConditionalMaxSize struct { +type ConditionalNewSequenceMaxSize struct { maxTxSizeForL1 uint64 // cfg.MaxTxSizeForL1 } -func (c *NewSequenceConditionalMaxSize) NewSequenceIfWorthToSend(ctx context.Context, txBuilder TxBuilder, sequenceBatches []seqsendertypes.Batch, senderAddress, l2Coinbase common.Address, batchNumber uint64) (seqsendertypes.Sequence, error) { +func NewConditionalNewSequenceMaxSize(maxTxSizeForL1 uint64) *ConditionalNewSequenceMaxSize { + return &ConditionalNewSequenceMaxSize{ + maxTxSizeForL1: maxTxSizeForL1, + } +} + +func (c *ConditionalNewSequenceMaxSize) NewSequenceIfWorthToSend(ctx context.Context, txBuilder TxBuilder, sequenceBatches []seqsendertypes.Batch, l2Coinbase common.Address) (seqsendertypes.Sequence, error) { + if c.maxTxSizeForL1 == MaxTxSizeForL1Disabled { + log.Debugf("maxTxSizeForL1 is %d, so is disabled", MaxTxSizeForL1Disabled) + return nil, nil + } sequence, err := txBuilder.NewSequence(sequenceBatches, l2Coinbase) if err != nil { return nil, err } + if sequence == nil { + err = fmt.Errorf("error txBuilder.NewSequence, returns sequence=nil and err==nil, is not expected") + log.Errorf(err.Error()) + return nil, err + } // Check if can be sent - tx, err := txBuilder.BuildSequenceBatchesTx(ctx, senderAddress, sequence) - if err == nil && tx.Size() > c.maxTxSizeForL1 { - log.Infof("[SeqSender] oversized Data on TX oldHash %s (txSize %d > %d)", tx.Hash(), tx.Size(), c.maxTxSizeForL1) + tx, err := txBuilder.BuildSequenceBatchesTx(ctx, sequence) + if tx == nil && err == nil { + err = fmt.Errorf("error txBuilder.BuildSequenceBatchesTx, returns tx=nil and err==nil, is not expected") + log.Errorf(err.Error()) + return nil, err + } + if err == nil && tx != nil && tx.Size() > c.maxTxSizeForL1 { + log.Infof("Oversized Data on TX oldHash %s (txSize %d > %d)", tx.Hash(), tx.Size(), c.maxTxSizeForL1) err = ErrOversizedData } if err != nil { - log.Infof("[SeqSender] handling estimate gas send sequence error: %v", err) - sequenceBatches, err = handleEstimateGasSendSequenceErr(sequence.Batches(), batchNumber, err) + log.Debugf("Handling estimate gas send sequence error: %v", err) + sequenceBatches, err = handleEstimateGasSendSequenceErr(sequence.Batches(), err) if sequenceBatches != nil { // Handling the error gracefully, re-processing the sequence as a sanity check //sequence, err = s.newSequenceBanana(sequenceBatches, s.cfg.L2Coinbase) @@ -43,36 +65,42 @@ func (c *NewSequenceConditionalMaxSize) NewSequenceIfWorthToSend(ctx context.Con return nil, err } - _, err = txBuilder.BuildSequenceBatchesTx(ctx, senderAddress, sequence) + txReduced, err := txBuilder.BuildSequenceBatchesTx(ctx, sequence) + log.Debugf("After reducing batches: (txSize %d -> %d)", tx.Size(), txReduced.Size()) + if err == nil && txReduced != nil && txReduced.Size() > c.maxTxSizeForL1 { + log.Warnf("After reducing batches: (txSize %d -> %d) is still too big > %d", tx.Size(), txReduced.Size(), c.maxTxSizeForL1) + } return sequence, err } return sequence, err } - log.Infof("[SeqSender] [MaxSize] current size:%d max_size:%d num_batches: %d", tx.Size(), c.maxTxSizeForL1, sequence.Len()) + log.Debugf("Current size:%d < max_size:%d num_batches: %d, no sequence promoted yet", tx.Size(), c.maxTxSizeForL1, sequence.Len()) return nil, nil } // handleEstimateGasSendSequenceErr handles an error on the estimate gas. Results: (nil,nil)=requires waiting, (nil,error)=no handled gracefully, (seq,nil) handled gracefully -func handleEstimateGasSendSequenceErr(sequenceBatches []seqsendertypes.Batch, currentBatchNumToSequence uint64, err error) ([]seqsendertypes.Batch, error) { +func handleEstimateGasSendSequenceErr(sequenceBatches []seqsendertypes.Batch, err error) ([]seqsendertypes.Batch, error) { // Insufficient allowance if errors.Is(err, etherman.ErrInsufficientAllowance) { return nil, err } + errMsg := fmt.Sprintf("due to unknown error: %v", err) if isDataForEthTxTooBig(err) { - // Remove the latest item and send the sequences - log.Infof("Done building sequences, selected batches to %d. Batch %d caused the L1 tx to be too big: %v", currentBatchNumToSequence-1, currentBatchNumToSequence, err) - } else { - // Remove the latest item and send the sequences - log.Infof("Done building sequences, selected batches to %d. Batch %d excluded due to unknown error: %v", currentBatchNumToSequence, currentBatchNumToSequence+1, err) + errMsg = fmt.Sprintf("caused the L1 tx to be too big: %v", err) } - + adjustMsg := "" if len(sequenceBatches) > 1 { + lastPrevious := sequenceBatches[len(sequenceBatches)-1].BatchNumber() sequenceBatches = sequenceBatches[:len(sequenceBatches)-1] + lastCurrent := sequenceBatches[len(sequenceBatches)-1].BatchNumber() + adjustMsg = fmt.Sprintf("removing last batch: old BatchNumber:%d -> %d, new length: %d", lastPrevious, lastCurrent, len(sequenceBatches)) } else { sequenceBatches = nil + adjustMsg = "removing all batches" + log.Warnf("No more batches to remove, sequence is empty... it could be a deadlock situation") } - + log.Infof("Adjusted sequence, %s, because %s", adjustMsg, errMsg) return sequenceBatches, nil } diff --git a/sequencesender/txbuilder/zkevm_cond_max_size_test.go b/sequencesender/txbuilder/zkevm_cond_max_size_test.go new file mode 100644 index 00000000..32e49eed --- /dev/null +++ b/sequencesender/txbuilder/zkevm_cond_max_size_test.go @@ -0,0 +1,95 @@ +package txbuilder_test + +import ( + "context" + "testing" + + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/sequencesender/seqsendertypes" + "github.com/0xPolygon/cdk/sequencesender/txbuilder" + "github.com/0xPolygon/cdk/sequencesender/txbuilder/mocks_txbuilder" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestConditionalMaxSizeDisabled(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceMaxSize(txbuilder.MaxTxSizeForL1Disabled) + + tx, err := sut.NewSequenceIfWorthToSend(nil, mockTxBuilder, nil, common.Address{}) + require.NoError(t, err) + require.Nil(t, tx) +} + +func TestConditionalMaxSizeTxBuilderNewSequenceReturnsNil(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceMaxSize(1024) + var sequenceBatches []seqsendertypes.Batch + sequenceBatches = append(sequenceBatches, &txbuilder.BananaBatch{}) + mockTxBuilder.EXPECT().NewSequence(sequenceBatches, common.Address{}).Return(nil, nil) + _, err := sut.NewSequenceIfWorthToSend(nil, mockTxBuilder, sequenceBatches, common.Address{}) + require.Error(t, err) +} + +func TestConditionalMaxSizeTxBuilderBuildSequenceBatchesTxReturnsNil(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceMaxSize(1024) + var sequenceBatches []seqsendertypes.Batch + sequenceBatches = append(sequenceBatches, &txbuilder.BananaBatch{}) + seq := &txbuilder.ElderberrySequence{} + mockTxBuilder.EXPECT().NewSequence(sequenceBatches, common.Address{}).Return(seq, nil) + mockTxBuilder.EXPECT().BuildSequenceBatchesTx(mock.Anything, mock.Anything).Return(nil, nil) + _, err := sut.NewSequenceIfWorthToSend(nil, mockTxBuilder, sequenceBatches, common.Address{}) + require.Error(t, err) +} + +func TestConditionalMaxSizeTxBuilderDontFulFill(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceMaxSize(1024) + var sequenceBatches []seqsendertypes.Batch + sequenceBatches = append(sequenceBatches, &txbuilder.BananaBatch{}) + seq := &txbuilder.ElderberrySequence{} + mockTxBuilder.EXPECT().NewSequence(sequenceBatches, common.Address{}).Return(seq, nil) + inner := ðtypes.LegacyTx{} + tx := ethtypes.NewTx(inner) + mockTxBuilder.EXPECT().BuildSequenceBatchesTx(mock.Anything, mock.Anything).Return(tx, nil) + + res, err := sut.NewSequenceIfWorthToSend(nil, mockTxBuilder, sequenceBatches, common.Address{}) + + require.NoError(t, err) + require.Nil(t, res) +} + +func TestConditionalMaxSizeTxBuilderFulFill(t *testing.T) { + mockTxBuilder := mocks_txbuilder.NewTxBuilder(t) + sut := txbuilder.NewConditionalNewSequenceMaxSize(10) + l2coinbase := common.Address{} + ctx := context.TODO() + + newSeq := newTestSeq(3, 100, l2coinbase) + mockTxBuilder.EXPECT().NewSequence(newSeq.Batches(), l2coinbase).Return(newSeq, nil) + inner := ðtypes.LegacyTx{ + Data: []byte{0x01, 0x02, 0x03, 0x04}, + } + tx := ethtypes.NewTx(inner) + mockTxBuilder.EXPECT().BuildSequenceBatchesTx(ctx, newSeq).Return(tx, nil) + // The size of result Tx is 14 that is > 10, so it reduce 1 batch + newSeqReduced := newTestSeq(2, 100, l2coinbase) + mockTxBuilder.EXPECT().NewSequence(newSeqReduced.Batches(), l2coinbase).Return(newSeqReduced, nil) + mockTxBuilder.EXPECT().BuildSequenceBatchesTx(ctx, newSeqReduced).Return(tx, nil) + + res, err := sut.NewSequenceIfWorthToSend(ctx, mockTxBuilder, newSeq.Batches(), l2coinbase) + + require.NoError(t, err) + require.NotNil(t, res) +} + +func newTestSeq(numBatches int, firstBatch uint64, l2coinbase common.Address) *txbuilder.ElderberrySequence { + var sequenceBatches []seqsendertypes.Batch + for i := 0; i < numBatches; i++ { + sequenceBatches = append(sequenceBatches, txbuilder.NewBananaBatch(ðerman.Batch{BatchNumber: firstBatch + uint64(i)})) + } + return txbuilder.NewElderberrySequence(sequenceBatches, l2coinbase) +} diff --git a/sync/mock_downloader_test.go b/sync/mock_downloader_test.go index 738fc873..2e953672 100644 --- a/sync/mock_downloader_test.go +++ b/sync/mock_downloader_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package sync @@ -23,6 +23,10 @@ func (_m *EVMDownloaderMock) download(ctx context.Context, fromBlock uint64, dow func (_m *EVMDownloaderMock) getBlockHeader(ctx context.Context, blockNum uint64) EVMBlockHeader { ret := _m.Called(ctx, blockNum) + if len(ret) == 0 { + panic("no return value specified for getBlockHeader") + } + var r0 EVMBlockHeader if rf, ok := ret.Get(0).(func(context.Context, uint64) EVMBlockHeader); ok { r0 = rf(ctx, blockNum) @@ -37,6 +41,10 @@ func (_m *EVMDownloaderMock) getBlockHeader(ctx context.Context, blockNum uint64 func (_m *EVMDownloaderMock) getEventsByBlockRange(ctx context.Context, fromBlock uint64, toBlock uint64) []EVMBlock { ret := _m.Called(ctx, fromBlock, toBlock) + if len(ret) == 0 { + panic("no return value specified for getEventsByBlockRange") + } + var r0 []EVMBlock if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []EVMBlock); ok { r0 = rf(ctx, fromBlock, toBlock) @@ -53,6 +61,10 @@ func (_m *EVMDownloaderMock) getEventsByBlockRange(ctx context.Context, fromBloc func (_m *EVMDownloaderMock) getLogs(ctx context.Context, fromBlock uint64, toBlock uint64) []types.Log { ret := _m.Called(ctx, fromBlock, toBlock) + if len(ret) == 0 { + panic("no return value specified for getLogs") + } + var r0 []types.Log if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []types.Log); ok { r0 = rf(ctx, fromBlock, toBlock) @@ -69,6 +81,10 @@ func (_m *EVMDownloaderMock) getLogs(ctx context.Context, fromBlock uint64, toBl func (_m *EVMDownloaderMock) waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) uint64 { ret := _m.Called(ctx, lastBlockSeen) + if len(ret) == 0 { + panic("no return value specified for waitForNewBlocks") + } + var r0 uint64 if rf, ok := ret.Get(0).(func(context.Context, uint64) uint64); ok { r0 = rf(ctx, lastBlockSeen) @@ -79,13 +95,12 @@ func (_m *EVMDownloaderMock) waitForNewBlocks(ctx context.Context, lastBlockSeen return r0 } -type mockConstructorTestingTNewEVMDownloaderMock interface { +// NewEVMDownloaderMock creates a new instance of EVMDownloaderMock. 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 NewEVMDownloaderMock(t interface { mock.TestingT Cleanup(func()) -} - -// NewEVMDownloaderMock creates a new instance of EVMDownloaderMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewEVMDownloaderMock(t mockConstructorTestingTNewEVMDownloaderMock) *EVMDownloaderMock { +}) *EVMDownloaderMock { mock := &EVMDownloaderMock{} mock.Mock.Test(t) diff --git a/sync/mock_l2_test.go b/sync/mock_l2_test.go index 0d1e03da..7a4bae36 100644 --- a/sync/mock_l2_test.go +++ b/sync/mock_l2_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package sync @@ -24,6 +24,10 @@ type L2Mock struct { func (_m *L2Mock) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { ret := _m.Called(ctx, hash) + if len(ret) == 0 { + panic("no return value specified for BlockByHash") + } + var r0 *types.Block var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Block, error)); ok { @@ -50,6 +54,10 @@ func (_m *L2Mock) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo func (_m *L2Mock) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { ret := _m.Called(ctx, number) + if len(ret) == 0 { + panic("no return value specified for BlockByNumber") + } + var r0 *types.Block var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Block, error)); ok { @@ -76,6 +84,10 @@ func (_m *L2Mock) BlockByNumber(ctx context.Context, number *big.Int) (*types.Bl func (_m *L2Mock) 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 { @@ -100,6 +112,10 @@ func (_m *L2Mock) BlockNumber(ctx context.Context) (uint64, error) { func (_m *L2Mock) 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 { @@ -126,6 +142,10 @@ func (_m *L2Mock) CallContract(ctx context.Context, call ethereum.CallMsg, block func (_m *L2Mock) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, contract, 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 { @@ -152,6 +172,10 @@ func (_m *L2Mock) CodeAt(ctx context.Context, contract common.Address, blockNumb func (_m *L2Mock) 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 { @@ -176,6 +200,10 @@ func (_m *L2Mock) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint6 func (_m *L2Mock) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { ret := _m.Called(ctx, q) + if len(ret) == 0 { + panic("no return value specified for FilterLogs") + } + var r0 []types.Log var r1 error if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]types.Log, error)); ok { @@ -202,6 +230,10 @@ func (_m *L2Mock) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]typ func (_m *L2Mock) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { ret := _m.Called(ctx, hash) + if len(ret) == 0 { + panic("no return value specified for HeaderByHash") + } + var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Header, error)); ok { @@ -228,6 +260,10 @@ func (_m *L2Mock) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He func (_m *L2Mock) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { ret := _m.Called(ctx, number) + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { @@ -254,6 +290,10 @@ func (_m *L2Mock) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H func (_m *L2Mock) 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 { @@ -280,6 +320,10 @@ func (_m *L2Mock) PendingCodeAt(ctx context.Context, account common.Address) ([] func (_m *L2Mock) 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 { @@ -304,6 +348,10 @@ func (_m *L2Mock) PendingNonceAt(ctx context.Context, account common.Address) (u func (_m *L2Mock) SendTransaction(ctx context.Context, tx *types.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, *types.Transaction) error); ok { r0 = rf(ctx, tx) @@ -318,6 +366,10 @@ func (_m *L2Mock) SendTransaction(ctx context.Context, tx *types.Transaction) er func (_m *L2Mock) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.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<- types.Log) (ethereum.Subscription, error)); ok { @@ -344,6 +396,10 @@ func (_m *L2Mock) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuer func (_m *L2Mock) SubscribeNewHead(ctx context.Context, ch chan<- *types.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<- *types.Header) (ethereum.Subscription, error)); ok { @@ -370,6 +426,10 @@ func (_m *L2Mock) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) func (_m *L2Mock) 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 { @@ -396,6 +456,10 @@ func (_m *L2Mock) SuggestGasPrice(ctx context.Context) (*big.Int, error) { func (_m *L2Mock) 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 { @@ -422,6 +486,10 @@ func (_m *L2Mock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { func (_m *L2Mock) 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 { @@ -446,6 +514,10 @@ func (_m *L2Mock) TransactionCount(ctx context.Context, blockHash common.Hash) ( func (_m *L2Mock) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { ret := _m.Called(ctx, blockHash, index) + if len(ret) == 0 { + panic("no return value specified for TransactionInBlock") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) (*types.Transaction, error)); ok { @@ -468,13 +540,12 @@ func (_m *L2Mock) TransactionInBlock(ctx context.Context, blockHash common.Hash, return r0, r1 } -type mockConstructorTestingTNewL2Mock interface { +// NewL2Mock creates a new instance of L2Mock. 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 NewL2Mock(t interface { mock.TestingT Cleanup(func()) -} - -// NewL2Mock creates a new instance of L2Mock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewL2Mock(t mockConstructorTestingTNewL2Mock) *L2Mock { +}) *L2Mock { mock := &L2Mock{} mock.Mock.Test(t) diff --git a/sync/mock_processor_test.go b/sync/mock_processor_test.go index d2c3e299..acf57388 100644 --- a/sync/mock_processor_test.go +++ b/sync/mock_processor_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package sync @@ -17,6 +17,10 @@ type ProcessorMock struct { func (_m *ProcessorMock) GetLastProcessedBlock(ctx context.Context) (uint64, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetLastProcessedBlock") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { @@ -41,6 +45,10 @@ func (_m *ProcessorMock) GetLastProcessedBlock(ctx context.Context) (uint64, err func (_m *ProcessorMock) ProcessBlock(block Block) error { ret := _m.Called(block) + if len(ret) == 0 { + panic("no return value specified for ProcessBlock") + } + var r0 error if rf, ok := ret.Get(0).(func(Block) error); ok { r0 = rf(block) @@ -55,6 +63,10 @@ func (_m *ProcessorMock) ProcessBlock(block Block) error { func (_m *ProcessorMock) Reorg(firstReorgedBlock uint64) error { ret := _m.Called(firstReorgedBlock) + if len(ret) == 0 { + panic("no return value specified for Reorg") + } + var r0 error if rf, ok := ret.Get(0).(func(uint64) error); ok { r0 = rf(firstReorgedBlock) @@ -65,13 +77,12 @@ func (_m *ProcessorMock) Reorg(firstReorgedBlock uint64) error { return r0 } -type mockConstructorTestingTNewProcessorMock interface { +// NewProcessorMock creates a new instance of ProcessorMock. 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 NewProcessorMock(t interface { mock.TestingT Cleanup(func()) -} - -// NewProcessorMock creates a new instance of ProcessorMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewProcessorMock(t mockConstructorTestingTNewProcessorMock) *ProcessorMock { +}) *ProcessorMock { mock := &ProcessorMock{} mock.Mock.Test(t) diff --git a/sync/mock_reorgdetector_test.go b/sync/mock_reorgdetector_test.go index 056da2a1..9689f7e7 100644 --- a/sync/mock_reorgdetector_test.go +++ b/sync/mock_reorgdetector_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package sync @@ -21,6 +21,10 @@ type ReorgDetectorMock struct { func (_m *ReorgDetectorMock) AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error { ret := _m.Called(ctx, id, blockNum, blockHash) + if len(ret) == 0 { + panic("no return value specified for AddBlockToTrack") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, uint64, common.Hash) error); ok { r0 = rf(ctx, id, blockNum, blockHash) @@ -35,6 +39,10 @@ func (_m *ReorgDetectorMock) AddBlockToTrack(ctx context.Context, id string, blo func (_m *ReorgDetectorMock) Subscribe(id string) (*reorgdetector.Subscription, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + var r0 *reorgdetector.Subscription var r1 error if rf, ok := ret.Get(0).(func(string) (*reorgdetector.Subscription, error)); ok { @@ -57,13 +65,12 @@ func (_m *ReorgDetectorMock) Subscribe(id string) (*reorgdetector.Subscription, return r0, r1 } -type mockConstructorTestingTNewReorgDetectorMock interface { +// NewReorgDetectorMock creates a new instance of ReorgDetectorMock. 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 NewReorgDetectorMock(t interface { mock.TestingT Cleanup(func()) -} - -// NewReorgDetectorMock creates a new instance of ReorgDetectorMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewReorgDetectorMock(t mockConstructorTestingTNewReorgDetectorMock) *ReorgDetectorMock { +}) *ReorgDetectorMock { mock := &ReorgDetectorMock{} mock.Mock.Test(t) diff --git a/test/Makefile b/test/Makefile index 0e671023..07f7aaaa 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,14 +1,27 @@ .PHONY: generate-mocks -generate-mocks: - $(MAKE) generate-mocks-reorgdetector - $(MAKE) generate-mocks-l1infotreesync - $(MAKE) generate-mocks-aggoracle - $(MAKE) generate-mocks-sync +generate-mocks: generate-mocks-localbridgesync generate-mocks-reorgdetector generate-mocks-sequencesender generate-mocks-da generate-mocks-l1infotreesync generate-mocks-aggoracle generate-mocks-sync + +.PHONY: generate-mocks-localbridgesync +generate-mocks-localbridgesync: ## Generates mocks for localbridgesync, using mockery tool + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=EthClienter --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=L2Mock --filename=mock_l2_test.go + .PHONY: generate-mocks-reorgdetector generate-mocks-reorgdetector: ## Generates mocks for reorgdetector, using mockery tool export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=EthClient --dir=../reorgdetector --output=../reorgdetector --outpkg=reorgdetector --inpackage --structname=EthClientMock --filename=mock_eth_client.go +COMMON_MOCKERY_PARAMS=--disable-version-string --with-expecter --exported +.PHONY: generate-mocks-sequencesender +generate-mocks-sequencesender: ## Generates mocks for sequencesender, using mockery tool + rm -Rf ../sequencesender/txbuilder/mocks_txbuilder + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --all --case snake --dir ../sequencesender/txbuilder --output ../sequencesender/txbuilder/mocks_txbuilder --outpkg mocks_txbuilder ${COMMON_MOCKERY_PARAMS} + +.PHONY: generate-mocks-da +generate-mocks-da: ## Generates mocks for dataavailability, using mockery tool + rm -Rf ../dataavailability/mocks_da + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --all --case snake --dir ../dataavailability --output ../dataavailability/mocks_da --outpkg mocks_da ${COMMON_MOCKERY_PARAMS} + + .PHONY: test-e2e-elderberry-validium test-e2e-elderberry-validium: stop ## Runs e2e tests checking elderberry/validium ./run-e2e-seq_sender.sh cdk-validium diff --git a/test/run-e2e-seq_sender.sh b/test/run-e2e-seq_sender.sh index 8fe81590..95101ed3 100755 --- a/test/run-e2e-seq_sender.sh +++ b/test/run-e2e-seq_sender.sh @@ -31,4 +31,4 @@ kurtosis clean --all kurtosis run --enclave cdk-v1 --args-file $DEST_KURTOSIS_PARAMS_YML --image-download always $KURTOSIS_FOLDER #[ $? -ne 0 ] && echo "Error running kurtosis" && exit 1 echo "Waiting 10 minutes to get some verified batch...." -$KURTOSIS_FOLDER/.github/actions/monitor-cdk-verified-batches/batch_verification_monitor.sh 1 600 \ No newline at end of file +$KURTOSIS_FOLDER/.github/actions/monitor-cdk-verified-batches/batch_verification_monitor.sh 0 600 \ No newline at end of file