Skip to content
This repository has been archived by the owner on Sep 23, 2024. It is now read-only.

Commit

Permalink
Cleanup executor
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan-Ethernal committed Jan 23, 2024
1 parent 5b30a0f commit ccbe00b
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 911 deletions.
5 changes: 4 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -135,13 +136,15 @@ func start(cliCtx *cli.Context) error {
etm,
)

workflow := workflow.New(c, addr, &ethMan)

// 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),
},
},
)
Expand Down
119 changes: 10 additions & 109 deletions interop/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package interop

import (
"context"
"errors"
"fmt"
"math/big"

Expand All @@ -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,
Expand All @@ -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(
Expand Down
128 changes: 0 additions & 128 deletions interop/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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) {
Expand Down
18 changes: 6 additions & 12 deletions rpc/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,35 +24,28 @@ const (
type InteropEndpoints struct {
ctx context.Context
executor *interop.Executor
workflow *workflow.Workflow
db types.IDB
}

// NewInteropEndpoints returns InteropEndpoints
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
Expand Down
Loading

0 comments on commit ccbe00b

Please sign in to comment.