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

Modularize Beethoven logic #29

Merged
merged 31 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/0xPolygon/beethoven/config"
"github.com/0xPolygon/beethoven/db"
"github.com/0xPolygon/beethoven/etherman"
"github.com/0xPolygon/beethoven/interop"
"github.com/0xPolygon/beethoven/network"
"github.com/0xPolygon/beethoven/rpc"
)
Expand Down Expand Up @@ -123,14 +124,24 @@ func start(cliCtx *cli.Context) error {
log.Fatal(err)
}

ctx, cancel := context.WithTimeout(context.Background(), c.RPC.ReadTimeout.Duration)
defer cancel()

executor := interop.New(
log.WithFields("module", "executor"),
c,
addr,
&ethMan,
etm,
)

// Register services
server := jRPC.NewServer(
c.RPC,
[]jRPC.Service{
{
Name: rpc.INTEROP,
Service: rpc.NewInteropEndpoints(addr, storage, &ethMan,
c.FullNodeRPCs, c.RPC.ReadTimeout.Duration, etm),
Name: rpc.INTEROP,
Service: rpc.NewInteropEndpoints(ctx, executor, storage),
},
},
)
Expand Down
7 changes: 4 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,22 @@ import (
"github.com/0xPolygonHermez/zkevm-node/ethtxmanager"
"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"github.com/urfave/cli/v2"

"github.com/0xPolygon/beethoven/rpc"
)

const (
// FlagCfg flag used for config aka cfg
FlagCfg = "cfg"
)

type FullNodeRPCs map[common.Address]string

// Config represents the full configuration of the data node
type Config struct {
FullNodeRPCs rpc.FullNodeRPCs `mapstructure:"FullNodeRPCs"`
FullNodeRPCs FullNodeRPCs `mapstructure:"FullNodeRPCs"`
RPC jRPC.Config `mapstructure:"RPC"`
Log log.Config `mapstructure:"Log"`
DB db.Config `mapstructure:"DB"`
Expand Down
183 changes: 183 additions & 0 deletions interop/executor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package interop

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

"github.com/0xPolygon/beethoven/config"
"github.com/0xPolygon/beethoven/tx"
"github.com/0xPolygon/beethoven/types"

"github.com/0xPolygonHermez/zkevm-node/jsonrpc/client"
rpctypes "github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/jackc/pgx/v4"
)

var _ types.ZkEVMClientClientCreator = (*zkEVMClientCreator)(nil)

type zkEVMClientCreator struct{}

func (zc *zkEVMClientCreator) NewClient(rpc string) types.ZkEVMClientInterface {
return client.NewClient(rpc)
}

type Executor struct {
logger *log.Logger
interopAdminAddr common.Address
config *config.Config
ethTxMan types.EthTxManager
etherman types.EthermanInterface
ZkEVMClientCreator types.ZkEVMClientClientCreator
}

func New(logger *log.Logger, cfg *config.Config,
interopAdminAddr common.Address,
etherman types.EthermanInterface,
ethTxManager types.EthTxManager,
) *Executor {
return &Executor{
logger: logger,
interopAdminAddr: interopAdminAddr,
config: cfg,
ethTxMan: ethTxManager,
etherman: etherman,
ZkEVMClientCreator: &zkEVMClientCreator{},
}
}

const ethTxManOwner = "interop"

func (e *Executor) CheckTx(ctx context.Context, tx tx.SignedTx) error {
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
e.logger.Debug("check tx")
vcastellm marked this conversation as resolved.
Show resolved Hide resolved

// 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
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
if _, ok := e.config.FullNodeRPCs[tx.Tx.L1Contract]; !ok {
return fmt.Errorf("there is no RPC registered for %s", tx.Tx.L1Contract)
}

return nil
}

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)
}

func (e *Executor) VerifyZKP(ctx context.Context, stx tx.SignedTx) error {
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
// Verify ZKP using eth_call
l1TxData, err := e.etherman.BuildTrustedVerifyBatchesTxData(
uint64(stx.Tx.LastVerifiedBatch),
uint64(stx.Tx.NewVerifiedBatch),
stx.Tx.ZKP,
)
if err != nil {
return fmt.Errorf("failed to build verify ZKP tx: %s", err)
}
msg := ethereum.CallMsg{
From: e.interopAdminAddr,
To: &stx.Tx.L1Contract,
Data: l1TxData,
}
res, err := e.etherman.CallContract(ctx, msg, nil)
if err != nil {
return fmt.Errorf("failed to call verify ZKP response: %s, error: %s", res, err)
}

return nil
}

func (e *Executor) VerifySignature(stx tx.SignedTx) error {
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
// 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.Tx.L1Contract)
if err != nil {
return errors.New("failed to get admin from L1")
}
if sequencer != signer {
return errors.New("unexpected signer")
}

return nil
}

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.Tx.L1Contract])
batch, err := zkEVMClient.BatchByNumber(
ctx,
big.NewInt(int64(signedTx.Tx.NewVerifiedBatch)),
)
if err != nil {
return fmt.Errorf("failed to get batch from our node, error: %s", err)
}
if batch.StateRoot != signedTx.Tx.ZKP.NewStateRoot || batch.LocalExitRoot != signedTx.Tx.ZKP.NewLocalExitRoot {
return fmt.Errorf(
"Mismatch detected, expected local exit root: %s actual: %s. expected state root: %s actual: %s",
signedTx.Tx.ZKP.NewLocalExitRoot.Hex(),
batch.LocalExitRoot.Hex(),
signedTx.Tx.ZKP.NewStateRoot.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
// Verify ZKP using eth_call
l1TxData, err := e.etherman.BuildTrustedVerifyBatchesTxData(
uint64(signedTx.Tx.LastVerifiedBatch),
uint64(signedTx.Tx.NewVerifiedBatch),
signedTx.Tx.ZKP,
)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to build verify ZKP tx: %s", err)
}

if err := e.ethTxMan.Add(
ctx,
ethTxManOwner,
signedTx.Tx.Hash().Hex(),
e.interopAdminAddr,
&signedTx.Tx.L1Contract,
big.NewInt(0),
l1TxData,
0,
dbTx,
); err != nil {
return common.Hash{}, fmt.Errorf("failed to add tx to ethTxMan, error: %s", err)
}
log.Debugf("successfuly added tx %s to ethTxMan", signedTx.Tx.Hash().Hex())
return signedTx.Tx.Hash(), nil
}

func (e *Executor) GetTxStatus(ctx context.Context, hash common.Hash, dbTx pgx.Tx) (result string, err rpctypes.Error) {
res, innerErr := e.ethTxMan.Result(ctx, ethTxManOwner, hash.Hex(), dbTx)
if innerErr != nil {
result = "0x0"
err = rpctypes.NewRPCError(rpctypes.DefaultErrorCode, fmt.Sprintf("failed to get tx, error: %s", innerErr))

return
}

result = res.Status.String()

return
}
Loading
Loading