From ccbe00b2eefd8de4319b9d9891305c6840af301b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Tue, 23 Jan 2024 14:15:02 +0100 Subject: [PATCH] Cleanup executor --- cmd/main.go | 5 +- interop/executor.go | 119 +------- interop/executor_test.go | 128 --------- rpc/rpc.go | 18 +- rpc/rpc_test.go | 552 ++------------------------------------ silencer/silencer.go | 6 +- silencer/silencer_test.go | 114 -------- workflow/workflow.go | 36 +-- 8 files changed, 67 insertions(+), 911 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 1a01dce..39e95a7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -29,6 +29,7 @@ import ( "github.com/0xPolygon/beethoven/interop" "github.com/0xPolygon/beethoven/network" "github.com/0xPolygon/beethoven/rpc" + "github.com/0xPolygon/beethoven/workflow" ) const appName = "cdk-beethoven" @@ -135,13 +136,15 @@ func start(cliCtx *cli.Context) error { etm, ) + workflow := workflow.New(c, addr, ðMan) + // Register services server := jRPC.NewServer( c.RPC, []jRPC.Service{ { Name: rpc.INTEROP, - Service: rpc.NewInteropEndpoints(ctx, executor, storage), + Service: rpc.NewInteropEndpoints(ctx, executor, workflow, storage), }, }, ) diff --git a/interop/executor.go b/interop/executor.go index aa0444b..ab28a98 100644 --- a/interop/executor.go +++ b/interop/executor.go @@ -2,7 +2,6 @@ package interop import ( "context" - "errors" "fmt" "math/big" @@ -12,18 +11,16 @@ import ( jRPC "github.com/0xPolygon/cdk-data-availability/rpc" "github.com/0xPolygonHermez/zkevm-node/log" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/jackc/pgx/v4" ) type Executor struct { - logger *log.Logger - interopAdminAddr common.Address - config *config.Config - ethTxMan types.IEthTxManager - etherman types.IEtherman - ZkEVMClientCreator types.IZkEVMClientClientCreator + logger *log.Logger + interopAdminAddr common.Address + config *config.Config + ethTxMan types.IEthTxManager + etherman types.IEtherman } func New(logger *log.Logger, cfg *config.Config, @@ -32,112 +29,16 @@ func New(logger *log.Logger, cfg *config.Config, ethTxManager types.IEthTxManager, ) *Executor { return &Executor{ - logger: logger, - interopAdminAddr: interopAdminAddr, - config: cfg, - ethTxMan: ethTxManager, - etherman: etherman, - ZkEVMClientCreator: &types.ZkEVMClientCreator{}, + logger: logger, + interopAdminAddr: interopAdminAddr, + config: cfg, + ethTxMan: ethTxManager, + etherman: etherman, } } const ethTxManOwner = "interop" -// @Stefan-Ethernal: Moved to Silencer.validate -func (e *Executor) CheckTx(tx tx.SignedTx) error { - // Check if the RPC is actually registered, if not it won't be possible to assert soundness (in the future once we are stateless won't be needed) - // TODO: The JSON parsing of the contract is incorrect - if _, ok := e.config.FullNodeRPCs[tx.Data.RollupID]; !ok { - return fmt.Errorf("there is no RPC registered for %v", tx.Data.RollupID) - } - - return nil -} - -// @Stefan-Ethernal: Moved to Silencer.verify -func (e *Executor) Verify(ctx context.Context, tx tx.SignedTx) error { - err := e.verifyZKP(ctx, tx) - if err != nil { - return fmt.Errorf("failed to verify ZKP: %s", err) - } - - return e.verifySignature(tx) -} - -// @Stefan-Ethernal: Moved to Silencer.verifyZKProof -func (e *Executor) verifyZKP(ctx context.Context, stx tx.SignedTx) error { - // Verify ZKP using eth_call - l1TxData, err := e.etherman.BuildTrustedVerifyBatchesTxData( - uint64(stx.Data.LastVerifiedBatch), - uint64(stx.Data.NewVerifiedBatch), - stx.Data.ZKP, - stx.Data.RollupID, - ) - if err != nil { - return fmt.Errorf("failed to build verify ZKP tx: %s", err) - } - msg := ethereum.CallMsg{ - From: e.interopAdminAddr, - To: &e.config.L1.RollupManagerContract, - Data: l1TxData, - } - res, err := e.etherman.CallContract(ctx, msg, nil) - if err != nil { - return fmt.Errorf("failed to call verify ZKP response: %s, error: %w", res, err) - } - - return nil -} - -// @Stefan-Ethernal: Moved to Silencer.verifyZKProof -func (e *Executor) verifySignature(stx tx.SignedTx) error { - // Auth: check signature vs admin - signer, err := stx.Signer() - if err != nil { - return errors.New("failed to get signer") - } - - sequencer, err := e.etherman.GetSequencerAddr(stx.Data.RollupID) - if err != nil { - return errors.New("failed to get admin from L1") - } - if sequencer != signer { - return errors.New("unexpected signer") - } - - return nil -} - -// @Stefan-Ethernal: Remove, moved into Silencer.Silence -func (e *Executor) Execute(ctx context.Context, signedTx tx.SignedTx) error { - // Check expected root vs root from the managed full node - // TODO: go stateless, depends on https://github.com/0xPolygonHermez/zkevm-prover/issues/581 - // when this happens we should go async from here, since processing all the batches could take a lot of time - zkEVMClient := e.ZkEVMClientCreator.NewClient(e.config.FullNodeRPCs[signedTx.Data.RollupID]) - batch, err := zkEVMClient.BatchByNumber( - ctx, - new(big.Int).SetUint64(uint64(signedTx.Data.NewVerifiedBatch)), - ) - if err != nil { - return fmt.Errorf("failed to get batch from our node: %w", err) - } - - if batch.StateRoot != signedTx.Data.ZKP.NewStateRoot { - return fmt.Errorf("mismatch in state roots detected (expected: '%s', actual: '%s')", - signedTx.Data.ZKP.NewStateRoot.Hex(), - batch.StateRoot.Hex(), - ) - } - - if batch.LocalExitRoot != signedTx.Data.ZKP.NewLocalExitRoot { - return fmt.Errorf("mismatch in local exit roots detected (expected: '%s', actual: '%s')", - signedTx.Data.ZKP.NewLocalExitRoot.Hex(), - batch.StateRoot.Hex()) - } - - return nil -} - func (e *Executor) Settle(ctx context.Context, signedTx tx.SignedTx, dbTx pgx.Tx) (common.Hash, error) { // Send L1 tx l1TxData, err := e.etherman.BuildTrustedVerifyBatchesTxData( diff --git a/interop/executor_test.go b/interop/executor_test.go index 336f0ac..04306a1 100644 --- a/interop/executor_test.go +++ b/interop/executor_test.go @@ -9,9 +9,7 @@ import ( jRPC "github.com/0xPolygon/cdk-data-availability/rpc" "github.com/0xPolygonHermez/zkevm-node/ethtxmanager" rpctypes "github.com/0xPolygonHermez/zkevm-node/jsonrpc/types" - "github.com/0xPolygonHermez/zkevm-node/log" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -34,132 +32,6 @@ func TestNewExecutor(t *testing.T) { assert.Equal(t, cfg, executor.config) assert.Equal(t, ethTxManager, executor.ethTxMan) assert.Equal(t, etherman, executor.etherman) - assert.NotNil(t, executor.ZkEVMClientCreator) -} - -func TestExecutor_CheckTx(t *testing.T) { - cfg := &config.Config{ - FullNodeRPCs: map[uint32]string{ - 1: "http://localhost:8545", - }, - } - interopAdminAddr := common.HexToAddress("0x1234567890abcdef") - etherman := mocks.NewEthermanMock(t) - ethTxManager := mocks.NewEthTxManagerMock(t) - - executor := New(log.WithFields("test", "test"), cfg, interopAdminAddr, etherman, ethTxManager) - - // Create a sample signed transaction for testing - signedTx := tx.SignedTx{ - Data: tx.Tx{ - LastVerifiedBatch: 0, - NewVerifiedBatch: 1, - ZKP: tx.ZKP{ - Proof: []byte("sampleProof"), - }, - RollupID: 1, - }, - } - - err := executor.CheckTx(signedTx) - assert.NoError(t, err) - - signedTx = tx.SignedTx{ - Data: tx.Tx{ - LastVerifiedBatch: 0, - NewVerifiedBatch: 1, - ZKP: tx.ZKP{ - Proof: []byte("sampleProof"), - }, - RollupID: 0, - }, - } - - err = executor.CheckTx(signedTx) - assert.Error(t, err) -} - -func TestExecutor_VerifyZKP(t *testing.T) { - cfg := &config.Config{} - interopAdminAddr := common.HexToAddress("0x1234567890abcdef") - etherman := mocks.NewEthermanMock(t) - ethTxManager := mocks.NewEthTxManagerMock(t) - tnx := tx.Tx{ - LastVerifiedBatch: 0, - NewVerifiedBatch: 1, - ZKP: tx.ZKP{ - Proof: []byte("sampleProof"), - }, - RollupID: 1, - } - - etherman.On( - "BuildTrustedVerifyBatchesTxData", - uint64(tnx.LastVerifiedBatch), - uint64(tnx.NewVerifiedBatch), - mock.Anything, - uint32(1), - ).Return( - []byte{}, - nil, - ).Once() - - etherman.On( - "CallContract", - mock.Anything, - mock.Anything, - mock.Anything, - ).Return( - []byte{}, - nil, - ).Once() - - executor := New(nil, cfg, interopAdminAddr, etherman, ethTxManager) - - // Create a sample signed transaction for testing - signedTx := tx.SignedTx{ - Data: tnx, - } - - err := executor.verifyZKP(context.Background(), signedTx) - assert.NoError(t, err) - etherman.AssertExpectations(t) -} - -func TestExecutor_VerifySignature(t *testing.T) { - cfg := &config.Config{} - interopAdminAddr := common.HexToAddress("0x1234567890abcdef") - etherman := mocks.NewEthermanMock(t) - ethTxManager := mocks.NewEthTxManagerMock(t) - - executor := New(nil, cfg, interopAdminAddr, etherman, ethTxManager) - - txn := tx.Tx{ - LastVerifiedBatch: 0, - NewVerifiedBatch: 1, - ZKP: tx.ZKP{ - Proof: []byte("sampleProof"), - }, - RollupID: 1, - } - - pk, err := crypto.GenerateKey() - require.NoError(t, err) - - signedTx, err := txn.Sign(pk) - require.NoError(t, err) - - etherman.On( - "GetSequencerAddr", - uint32(1), - ).Return( - crypto.PubkeyToAddress(pk.PublicKey), - nil, - ).Once() - - err = executor.verifySignature(*signedTx) - require.NoError(t, err) - etherman.AssertExpectations(t) } func TestExecutor_Settle(t *testing.T) { diff --git a/rpc/rpc.go b/rpc/rpc.go index 4c3a1f5..d3dd9b2 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -11,6 +11,7 @@ import ( "github.com/0xPolygon/beethoven/interop" "github.com/0xPolygon/beethoven/tx" "github.com/0xPolygon/beethoven/types" + "github.com/0xPolygon/beethoven/workflow" ) // INTEROP is the namespace of the interop service @@ -23,6 +24,7 @@ const ( type InteropEndpoints struct { ctx context.Context executor *interop.Executor + workflow *workflow.Workflow db types.IDB } @@ -30,28 +32,20 @@ type InteropEndpoints struct { func NewInteropEndpoints( ctx context.Context, executor *interop.Executor, + workflow *workflow.Workflow, db types.IDB, ) *InteropEndpoints { return &InteropEndpoints{ ctx: ctx, executor: executor, + workflow: workflow, db: db, } } func (i *InteropEndpoints) SendTx(signedTx tx.SignedTx) (interface{}, jRPC.Error) { - // Check if the RPC is actually registered, if not it won't be possible to assert soundness (in the future once we are stateless won't be needed) - if err := i.executor.CheckTx(signedTx); err != nil { - return "0x0", jRPC.NewRPCError(jRPC.DefaultErrorCode, fmt.Sprintf("there is no RPC registered for %d", signedTx.Data.RollupID)) - } - - // Verify ZKP using eth_call - if err := i.executor.Verify(i.ctx, signedTx); err != nil { - return "0x0", jRPC.NewRPCError(jRPC.DefaultErrorCode, fmt.Sprintf("failed to verify tx: %s", err)) - } - - if err := i.executor.Execute(i.ctx, signedTx); err != nil { - return "0x0", jRPC.NewRPCError(jRPC.DefaultErrorCode, fmt.Sprintf("failed to execute tx: %s", err)) + if err := i.workflow.Execute(i.ctx, signedTx); err != nil { + return "0x0", jRPC.NewRPCError(jRPC.DefaultErrorCode, err.Error()) } // Send L1 tx diff --git a/rpc/rpc_test.go b/rpc/rpc_test.go index e6998b5..adde79f 100644 --- a/rpc/rpc_test.go +++ b/rpc/rpc_test.go @@ -9,18 +9,14 @@ import ( "github.com/0xPolygon/beethoven/config" "github.com/0xPolygon/beethoven/interop" "github.com/0xPolygon/beethoven/mocks" + "github.com/0xPolygon/beethoven/workflow" - beethovenTypes "github.com/0xPolygon/beethoven/rpc/types" "github.com/0xPolygonHermez/zkevm-node/ethtxmanager" - validiumTypes "github.com/0xPolygonHermez/zkevm-node/jsonrpc/types" "github.com/0xPolygonHermez/zkevm-node/log" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - - "github.com/0xPolygon/beethoven/tx" ) func TestInteropEndpointsGetTxStatus(t *testing.T) { @@ -32,14 +28,18 @@ func TestInteropEndpointsGetTxStatus(t *testing.T) { dbMock := mocks.NewDBMock(t) dbMock.On("BeginStateTransaction", mock.Anything).Return(nil, errors.New("error")).Once() + interopAdmin := common.HexToAddress("0xadmin") + etherman := mocks.NewEthermanMock(t) + e := interop.New( log.WithFields("module", "test"), &config.Config{}, - common.HexToAddress("0xadmin"), - mocks.NewEthermanMock(t), + interopAdmin, + etherman, mocks.NewEthTxManagerMock(t), ) - i := NewInteropEndpoints(context.Background(), e, dbMock) + w := workflow.New(&config.Config{}, interopAdmin, etherman) + i := NewInteropEndpoints(context.Background(), e, w, dbMock) result, err := i.GetTxStatus(common.HexToHash("0xsomeTxHash")) @@ -64,14 +64,18 @@ func TestInteropEndpointsGetTxStatus(t *testing.T) { txManagerMock.On("Result", mock.Anything, ethTxManOwner, txHash.Hex(), txMock). Return(ethtxmanager.MonitoredTxResult{}, errors.New("error")).Once() + interopAdmin := common.HexToAddress("0xadmin") + etherman := mocks.NewEthermanMock(t) + e := interop.New( log.WithFields("module", "test"), &config.Config{}, - common.HexToAddress("0xadmin"), - mocks.NewEthermanMock(t), + interopAdmin, + etherman, txManagerMock, ) - i := NewInteropEndpoints(context.Background(), e, dbMock) + w := workflow.New(&config.Config{}, interopAdmin, etherman) + i := NewInteropEndpoints(context.Background(), e, w, dbMock) result, err := i.GetTxStatus(txHash) @@ -108,14 +112,18 @@ func TestInteropEndpointsGetTxStatus(t *testing.T) { txManagerMock.On("Result", mock.Anything, ethTxManOwner, txHash.Hex(), txMock). Return(result, nil).Once() + interopAdmin := common.HexToAddress("0xadmin") + etherman := mocks.NewEthermanMock(t) + e := interop.New( log.WithFields("module", "test"), &config.Config{}, - common.HexToAddress("0xadmin"), - mocks.NewEthermanMock(t), + interopAdmin, + etherman, txManagerMock, ) - i := NewInteropEndpoints(context.Background(), e, dbMock) + w := workflow.New(&config.Config{}, interopAdmin, etherman) + i := NewInteropEndpoints(context.Background(), e, w, dbMock) status, err := i.GetTxStatus(txHash) @@ -127,519 +135,3 @@ func TestInteropEndpointsGetTxStatus(t *testing.T) { txManagerMock.AssertExpectations(t) }) } - -func TestInteropEndpointsSendTx(t *testing.T) { - t.Parallel() - - type testConfig struct { - isL1ContractInMap bool - canBuildZKProof bool - isZKProofValid bool - isTxSigned bool - isAdminRetrieved bool - isSignerValid bool - canGetBatch bool - isBatchValid bool - isDbTxOpen bool - isTxAddedToEthTxMan bool - isTxCommitted bool - - expectedError string - } - - testFn := func(cfg testConfig) { - fullNodeRPCs := config.FullNodeRPCs{ - 1: "someRPC", - } - tnx := tx.Tx{ - LastVerifiedBatch: beethovenTypes.ArgUint64(1), - NewVerifiedBatch: *beethovenTypes.ArgUint64Ptr(2), - ZKP: tx.ZKP{ - NewStateRoot: common.BigToHash(big.NewInt(11)), - NewLocalExitRoot: common.BigToHash(big.NewInt(11)), - }, - RollupID: 1, - } - signedTx := &tx.SignedTx{Data: tnx} - ethermanMock := mocks.NewEthermanMock(t) - zkEVMClientCreatorMock := mocks.NewZkEVMClientClientCreatorMock(t) - zkEVMClientMock := mocks.NewZkEVMClientMock(t) - dbMock := mocks.NewDBMock(t) - txMock := new(mocks.TxMock) - ethTxManagerMock := mocks.NewEthTxManagerMock(t) - - executeTestFn := func() { - e := interop.New( - log.WithFields("module", "test"), - &config.Config{ - FullNodeRPCs: fullNodeRPCs, - L1: config.L1Config{RollupManagerContract: common.HexToAddress("0xdeadbeef")}, - }, - common.HexToAddress("0xadmin"), - ethermanMock, - ethTxManagerMock, - ) - i := NewInteropEndpoints(context.Background(), e, dbMock) - i.executor.ZkEVMClientCreator = zkEVMClientCreatorMock - - result, err := i.SendTx(*signedTx) - - if cfg.expectedError != "" { - require.Equal(t, "0x0", result) - require.ErrorContains(t, err, cfg.expectedError) - } else { - require.NoError(t, err) - require.Equal(t, signedTx.Data.Hash(), result) - } - - ethermanMock.AssertExpectations(t) - zkEVMClientCreatorMock.AssertExpectations(t) - zkEVMClientMock.AssertExpectations(t) - dbMock.AssertExpectations(t) - txMock.AssertExpectations(t) - ethTxManagerMock.AssertExpectations(t) - } - - if !cfg.isL1ContractInMap { - fullNodeRPCs = config.FullNodeRPCs{} - executeTestFn() - - return - } - - if !cfg.canBuildZKProof { - ethermanMock.On( - "BuildTrustedVerifyBatchesTxData", - uint64(tnx.LastVerifiedBatch), - uint64(tnx.NewVerifiedBatch), - mock.Anything, - uint32(1), - ).Return( - []byte{}, - errors.New("error"), - ).Once() - - executeTestFn() - - return - } - - ethermanMock.On( - "BuildTrustedVerifyBatchesTxData", - uint64(tnx.LastVerifiedBatch), - uint64(tnx.NewVerifiedBatch), - mock.Anything, - uint32(1), - ).Return( - []byte{1, 2}, - nil, - ).Once() - - if !cfg.isZKProofValid { - ethermanMock.On( - "CallContract", - mock.Anything, - mock.Anything, - mock.Anything, - ).Return( - []byte{}, - errors.New("error"), - ).Once() - - executeTestFn() - - return - } - - ethermanMock.On( - "CallContract", - mock.Anything, - mock.Anything, - mock.Anything, - ).Return( - []byte{1, 2}, - nil, - ).Once() - - if !cfg.isTxSigned { - executeTestFn() - - return - } - - privateKey, err := crypto.GenerateKey() - require.NoError(t, err) - - stx, err := tnx.Sign(privateKey) - require.NoError(t, err) - - signedTx = stx - - if !cfg.isAdminRetrieved { - ethermanMock.On( - "GetSequencerAddr", - uint32(1), - ).Return( - common.Address{}, - errors.New("error"), - ).Once() - - executeTestFn() - - return - } - - if !cfg.isSignerValid { - ethermanMock.On( - "GetSequencerAddr", - uint32(1), - ).Return( - common.BytesToAddress([]byte{1, 2, 3, 4}), - nil, - ).Once() - - executeTestFn() - - return - } - - ethermanMock.On( - "GetSequencerAddr", - uint32(1), - ).Return( - crypto.PubkeyToAddress(privateKey.PublicKey), - nil, - ).Once() - - zkEVMClientCreatorMock.On( - "NewClient", - mock.Anything, - ).Return( - zkEVMClientMock, - ) - - if !cfg.canGetBatch { - zkEVMClientMock.On( - "BatchByNumber", - mock.Anything, - big.NewInt(int64(signedTx.Data.NewVerifiedBatch)), - ).Return( - nil, - errors.New("error"), - ).Once() - - executeTestFn() - - return - } - - if !cfg.isBatchValid { - zkEVMClientMock.On( - "BatchByNumber", - mock.Anything, - big.NewInt(int64(signedTx.Data.NewVerifiedBatch)), - ).Return( - &validiumTypes.Batch{ - StateRoot: common.BigToHash(big.NewInt(12)), - }, - nil, - ).Once() - - executeTestFn() - - return - } - - zkEVMClientMock.On( - "BatchByNumber", - mock.Anything, - big.NewInt(int64(signedTx.Data.NewVerifiedBatch)), - ).Return( - &validiumTypes.Batch{ - StateRoot: common.BigToHash(big.NewInt(11)), - LocalExitRoot: common.BigToHash(big.NewInt(11)), - }, - nil, - ).Once() - - if !cfg.isDbTxOpen { - dbMock.On( - "BeginStateTransaction", - mock.Anything, - ).Return( - nil, - errors.New("error"), - ).Once() - - executeTestFn() - - return - } - - dbMock.On( - "BeginStateTransaction", - mock.Anything, - ).Return( - txMock, - nil, - ).Once() - - if !cfg.isTxAddedToEthTxMan { - ethTxManagerMock.On( - "Add", - mock.Anything, - ethTxManOwner, - signedTx.Data.Hash().Hex(), - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - txMock, - ).Return( - errors.New("error"), - ).Once() - - txMock.On( - "Rollback", - mock.Anything, - ).Return( - nil, - ).Once() - - ethermanMock.On( - "BuildTrustedVerifyBatchesTxData", - uint64(tnx.LastVerifiedBatch), - uint64(tnx.NewVerifiedBatch), - mock.Anything, - uint32(1), - ).Return( - []byte{1, 2}, - nil, - ).Once() - - executeTestFn() - - return - } - - ethTxManagerMock.On( - "Add", - mock.Anything, - ethTxManOwner, - signedTx.Data.Hash().Hex(), - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - txMock, - ).Return( - nil, - ).Once() - - if !cfg.isTxCommitted { - txMock.On( - "Commit", - mock.Anything, - ).Return( - errors.New("error"), - ).Once() - - ethermanMock.On( - "BuildTrustedVerifyBatchesTxData", - uint64(tnx.LastVerifiedBatch), - uint64(tnx.NewVerifiedBatch), - mock.Anything, - uint32(1), - ).Return( - []byte{1, 2}, - nil, - ).Once() - - executeTestFn() - - return - } - - ethermanMock.On( - "BuildTrustedVerifyBatchesTxData", - uint64(tnx.LastVerifiedBatch), - uint64(tnx.NewVerifiedBatch), - mock.Anything, - uint32(1), - ).Return( - []byte{1, 2}, - nil, - ).Once() - - txMock.On( - "Commit", - mock.Anything, - ).Return( - nil, - ).Once() - - executeTestFn() - } - - t.Run("don't have given contract in map", func(t *testing.T) { - t.Parallel() - - testFn(testConfig{ - isL1ContractInMap: false, - expectedError: "there is no RPC registered", - }) - }) - - t.Run("could not build verified ZKP tx data", func(t *testing.T) { - t.Parallel() - - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: false, - expectedError: "failed to build verify ZKP tx", - }) - }) - - t.Run("could not verified ZKP", func(t *testing.T) { - t.Parallel() - - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: false, - expectedError: "failed to call verify ZKP response", - }) - }) - - t.Run("could not get signer", func(t *testing.T) { - t.Parallel() - - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: false, - expectedError: "failed to get signer", - }) - }) - - t.Run("failed to get admin from L1", func(t *testing.T) { - t.Parallel() - - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: false, - expectedError: "failed to get admin from L1", - }) - }) - - t.Run("unexpected signer", func(t *testing.T) { - t.Parallel() - - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: true, - isSignerValid: false, - expectedError: "unexpected signer", - }) - }) - - t.Run("error on batch retrieval", func(t *testing.T) { - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: true, - isSignerValid: true, - canGetBatch: false, - expectedError: "failed to get batch from our node", - }) - }) - - t.Run("unexpected batch", func(t *testing.T) { - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: true, - isSignerValid: true, - canGetBatch: true, - isBatchValid: false, - expectedError: "mismatch in state roots detected", - }) - }) - - t.Run("failed to begin dbTx", func(t *testing.T) { - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: true, - isSignerValid: true, - canGetBatch: true, - isBatchValid: true, - isDbTxOpen: false, - expectedError: "failed to begin dbTx", - }) - }) - - t.Run("failed to add tx to ethTxMan", func(t *testing.T) { - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: true, - isSignerValid: true, - canGetBatch: true, - isBatchValid: true, - isDbTxOpen: true, - isTxAddedToEthTxMan: false, - expectedError: "failed to add tx to ethTxMan", - }) - }) - - t.Run("failed to commit tx", func(t *testing.T) { - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: true, - isSignerValid: true, - canGetBatch: true, - isBatchValid: true, - isDbTxOpen: true, - isTxAddedToEthTxMan: true, - isTxCommitted: false, - expectedError: "failed to commit dbTx", - }) - }) - - t.Run("happy path", func(t *testing.T) { - testFn(testConfig{ - isL1ContractInMap: true, - canBuildZKProof: true, - isZKProofValid: true, - isTxSigned: true, - isAdminRetrieved: true, - isSignerValid: true, - canGetBatch: true, - isBatchValid: true, - isDbTxOpen: true, - isTxAddedToEthTxMan: true, - isTxCommitted: true, - }) - }) -} diff --git a/silencer/silencer.go b/silencer/silencer.go index a396287..dd1fe8a 100644 --- a/silencer/silencer.go +++ b/silencer/silencer.go @@ -108,7 +108,11 @@ func (s *Silencer) verifyZKProof(ctx context.Context, stx tx.SignedTx) error { } res, err := s.etherman.CallContract(ctx, msg, nil) if err != nil { - return fmt.Errorf("failed to call ZK proof verification (response: %s): %w", res, err) + if len(res) > 0 { + return fmt.Errorf("failed to call ZK proof verification (response: %s): %w", res, err) + } + + return fmt.Errorf("failed to call ZK proof verification: %w", err) } return nil diff --git a/silencer/silencer_test.go b/silencer/silencer_test.go index 392dd8b..d55f8a4 100644 --- a/silencer/silencer_test.go +++ b/silencer/silencer_test.go @@ -176,120 +176,6 @@ func TestSilencer_Silence(t *testing.T) { } } -func TestSilencer_SilenceOld(t *testing.T) { - const rollupID = uint32(10) - cfg := &config.Config{FullNodeRPCs: map[uint32]string{ - rollupID: "http://localhost:10000", - }} - interopAdminAddr := common.HexToAddress("0x1001") - - createMockEtherman := func(sequencerAddr common.Address) *mocks.EthermanMock { - etherman := mocks.NewEthermanMock(t) - etherman.On("BuildTrustedVerifyBatchesTxData", - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - ).Return([]byte{}, nil).Once() - - etherman.On("CallContract", - mock.Anything, - mock.Anything, - mock.Anything, - ).Return([]byte{}, nil).Once() - - etherman.On( - "GetSequencerAddr", - mock.Anything, - ).Return( - sequencerAddr, - nil, - ).Once() - - return etherman - } - - setupMockZkEVMClient := func(batchStateRoot, batchLocalExitRoot common.Hash, err error) (*mocks.ZkEVMClientClientCreatorMock, *mocks.ZkEVMClientMock) { - clientMock := mocks.NewZkEVMClientMock(t) - if err == nil { - batch := &rpctypes.Batch{ - StateRoot: batchStateRoot, - LocalExitRoot: batchLocalExitRoot, - } - clientMock.On("BatchByNumber", mock.Anything, mock.Anything). - Return(batch, nil).Once() - } else { - clientMock.On("BatchByNumber", mock.Anything, mock.Anything). - Return(nil, err).Once() - } - - clientCreatorMock := mocks.NewZkEVMClientClientCreatorMock(t) - clientCreatorMock.On("NewClient", mock.Anything). - Return(clientMock).Once() - - return clientCreatorMock, clientMock - } - - t.Run("happy path", func(t *testing.T) { - tx := tx.Tx{ - LastVerifiedBatch: 1, - NewVerifiedBatch: 2, - RollupID: rollupID, - ZKP: tx.ZKP{ - NewStateRoot: common.BytesToHash([]byte("sampleNewStateRoot")), - Proof: []byte("sampleProof"), - }, - } - - sequencerKey, err := crypto.GenerateKey() - require.NoError(t, err) - - signedTx, err := tx.Sign(sequencerKey) - require.NoError(t, err) - - etherman := createMockEtherman(crypto.PubkeyToAddress(sequencerKey.PublicKey)) - clientCreatorMock, clientMock := setupMockZkEVMClient(tx.ZKP.NewStateRoot, tx.ZKP.NewLocalExitRoot, nil) - - silencer := New(cfg, interopAdminAddr, etherman, clientCreatorMock) - - err = silencer.Silence(context.Background(), *signedTx) - require.NoError(t, err) - - clientCreatorMock.AssertExpectations(t) - clientMock.AssertExpectations(t) - }) - - t.Run("failed to retrieve batch", func(t *testing.T) { - expectedErr := errors.New("timeout") - tx := tx.Tx{ - LastVerifiedBatch: 1, - NewVerifiedBatch: 2, - RollupID: rollupID, - ZKP: tx.ZKP{ - NewStateRoot: common.BytesToHash([]byte("sampleNewStateRoot")), - Proof: []byte("sampleProof"), - }, - } - - sequencerKey, err := crypto.GenerateKey() - require.NoError(t, err) - - signedTx, err := tx.Sign(sequencerKey) - require.NoError(t, err) - - etherman := createMockEtherman(crypto.PubkeyToAddress(sequencerKey.PublicKey)) - clientCreatorMock, clientMock := setupMockZkEVMClient(common.Hash{}, common.Hash{}, expectedErr) - - silencer := New(cfg, interopAdminAddr, etherman, clientCreatorMock) - - err = silencer.Silence(context.Background(), *signedTx) - require.ErrorContains(t, err, fmt.Sprintf("failed to get batch from our node: %s", expectedErr.Error())) - - clientCreatorMock.AssertExpectations(t) - clientMock.AssertExpectations(t) - }) -} - func TestSilencer_verify(t *testing.T) { createSilencer := func(cfg *config.Config, etherman types.IEtherman) *Silencer { interopAdmin := common.HexToAddress("0x100") diff --git a/workflow/workflow.go b/workflow/workflow.go index 3acaff7..87db08a 100644 --- a/workflow/workflow.go +++ b/workflow/workflow.go @@ -3,13 +3,9 @@ package workflow import ( "context" - "github.com/0xPolygonHermez/zkevm-node/pool" - "github.com/0xPolygonHermez/zkevm-node/sequencer" - "github.com/0xPolygonHermez/zkevm-node/state" abciTypes "github.com/cometbft/cometbft/abci/types" "github.com/ethereum/go-ethereum/common" - "github.com/0xPolygon/beethoven/aggregator" "github.com/0xPolygon/beethoven/config" "github.com/0xPolygon/beethoven/silencer" "github.com/0xPolygon/beethoven/tx" @@ -18,23 +14,31 @@ import ( var _ abciTypes.Application = (*Workflow)(nil) +type workflowOption func(*Workflow) + type Workflow struct { - silencer *silencer.Silencer - sequencer *sequencer.Sequencer - aggregator *aggregator.Aggregator + silencer silencer.ISilencer + // sequencer *sequencer.Sequencer + // aggregator *aggregator.Aggregator } -func New(cfg *config.Config, interopAdmin common.Address, etherman types.IEtherman) (*Workflow, error) { - seq, err := sequencer.New(sequencer.Config{}, state.BatchConfig{}, pool.Config{}, nil, nil, nil, nil) - if err != nil { - return nil, err +func New(cfg *config.Config, interopAdmin common.Address, etherman types.IEtherman, opts ...workflowOption) *Workflow { + // TODO: instantiate sequencer and aggregator + w := &Workflow{ + silencer: silencer.New(cfg, interopAdmin, etherman, &types.ZkEVMClientCreator{}), + } + + for _, opt := range opts { + opt(w) } - return &Workflow{ - silencer: silencer.New(cfg, interopAdmin, etherman, &types.ZkEVMClientCreator{}), - aggregator: aggregator.New(), - sequencer: seq, - }, nil + return w +} + +func WithCustomSilencer(silencer silencer.ISilencer) workflowOption { + return func(w *Workflow) { + w.silencer = silencer + } } func (w *Workflow) Execute(ctx context.Context, stx tx.SignedTx) error {