Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync Trusted State via jRPC instead of Broadcast #1840

Merged
merged 7 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/0xPolygonHermez/zkevm-node/ethtxmanager"
"github.com/0xPolygonHermez/zkevm-node/gasprice"
"github.com/0xPolygonHermez/zkevm-node/jsonrpc"
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/client"
"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/0xPolygonHermez/zkevm-node/merkletree"
"github.com/0xPolygonHermez/zkevm-node/metrics"
Expand Down Expand Up @@ -197,7 +198,19 @@ func newEtherman(c config.Config) (*etherman.Client, error) {
}

func runSynchronizer(cfg config.Config, etherman *etherman.Client, ethTxManager *ethtxmanager.Client, st *state.State, pool *pool.Pool) {
sy, err := synchronizer.NewSynchronizer(cfg.IsTrustedSequencer, etherman, st, pool, ethTxManager, cfg.NetworkConfig.Genesis, cfg.Synchronizer)
var trustedSequencerURL string
var err error
if !cfg.IsTrustedSequencer {
log.Debug("getting trusted sequencer URL from smc")
trustedSequencerURL, err = etherman.GetTrustedSequencerURL()
if err != nil {
log.Fatal("error getting trusted sequencer URI. Error: %v", err)
}
log.Debug("trustedSequencerURL ", trustedSequencerURL)
}
zkEVMClient := client.NewClient(trustedSequencerURL)

sy, err := synchronizer.NewSynchronizer(cfg.IsTrustedSequencer, etherman, st, pool, ethTxManager, zkEVMClient, cfg.NetworkConfig.Genesis, cfg.Synchronizer)
if err != nil {
log.Fatal(err)
}
Expand Down
6 changes: 6 additions & 0 deletions hex/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ func MustDecodeHex(str string) []byte {
return buf
}

// DecodeUint64 type-checks and converts a hex string to a uint64
func DecodeUint64(str string) uint64 {
i := DecodeBig(str)
return i.Uint64()
}

// EncodeUint64 encodes a number as a hex string with 0x prefix.
func EncodeUint64(i uint64) string {
enc := make([]byte, 2, 10) //nolint:gomnd
Expand Down
36 changes: 25 additions & 11 deletions jsonrpc/client.go → jsonrpc/client/client.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
package jsonrpc
package client

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
)

// Client defines typed wrappers for the zkEVM RPC API.
type Client struct {
url string
}

// NewClient creates an instance of client
func NewClient(url string) *Client {
return &Client{
url: url,
}
}

// JSONRPCCall executes a 2.0 JSON RPC HTTP Post Request to the provided URL with
// the provided method and parameters, which is compatible with the Ethereum
// JSON RPC Server.
func JSONRPCCall(url, method string, parameters ...interface{}) (Response, error) {
func JSONRPCCall(url, method string, parameters ...interface{}) (types.Response, error) {
const jsonRPCVersion = "2.0"

params, err := json.Marshal(parameters)
if err != nil {
return Response{}, err
return types.Response{}, err
}

req := Request{
req := types.Request{
JSONRPC: jsonRPCVersion,
ID: float64(1),
Method: method,
Expand All @@ -28,36 +42,36 @@ func JSONRPCCall(url, method string, parameters ...interface{}) (Response, error

reqBody, err := json.Marshal(req)
if err != nil {
return Response{}, err
return types.Response{}, err
}

reqBodyReader := bytes.NewReader(reqBody)
httpReq, err := http.NewRequest(http.MethodPost, url, reqBodyReader)
if err != nil {
return Response{}, err
return types.Response{}, err
}

httpReq.Header.Add("Content-type", "application/json")

httpRes, err := http.DefaultClient.Do(httpReq)
if err != nil {
return Response{}, err
return types.Response{}, err
}

if httpRes.StatusCode != http.StatusOK {
return Response{}, fmt.Errorf("Invalid status code, expected: %v, found: %v", http.StatusOK, httpRes.StatusCode)
return types.Response{}, fmt.Errorf("Invalid status code, expected: %v, found: %v", http.StatusOK, httpRes.StatusCode)
}

resBody, err := io.ReadAll(httpRes.Body)
if err != nil {
return Response{}, err
return types.Response{}, err
}
defer httpRes.Body.Close()

var res Response
var res types.Response
err = json.Unmarshal(resBody, &res)
if err != nil {
return Response{}, err
return types.Response{}, err
}

return res, nil
Expand Down
57 changes: 57 additions & 0 deletions jsonrpc/client/zkevm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package client

import (
"context"
"encoding/json"
"fmt"
"math/big"

"github.com/0xPolygonHermez/zkevm-node/hex"
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
"github.com/0xPolygonHermez/zkevm-node/log"
)

// BatchNumber returns the latest batch number
func (c *Client) BatchNumber(ctx context.Context) (uint64, error) {
response, err := JSONRPCCall(c.url, "zkevm_batchNumber")
if err != nil {
return 0, err
}

if response.Error != nil {
return 0, fmt.Errorf("%v %v", response.Error.Code, response.Error.Message)
}

var result string
err = json.Unmarshal(response.Result, &result)
if err != nil {
return 0, err
}

bigBatchNumber := hex.DecodeBig(result)
batchNumber := bigBatchNumber.Uint64()

return batchNumber, nil
}

// BatchByNumber returns a batch from the current canonical chain. If number is nil, the
// latest known batch is returned.
func (c *Client) BatchByNumber(ctx context.Context, number *big.Int) (*types.Batch, error) {
response, err := JSONRPCCall(c.url, "zkevm_getBatchByNumber", types.ToBatchNumArg(number), true)
if err != nil {
return nil, err
}

if response.Error != nil {
return nil, fmt.Errorf("%v %v", response.Error.Code, response.Error.Message)
}

var result *types.Batch
log.Debugf(string(response.Result))
err = json.Unmarshal(response.Result, &result)
if err != nil {
return nil, err
}

return result, nil
}
11 changes: 6 additions & 5 deletions jsonrpc/dbtxmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,31 @@ package jsonrpc
import (
"context"

"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
"github.com/jackc/pgx/v4"
)

type dbTxManager struct{}

type dbTxScopedFn func(ctx context.Context, dbTx pgx.Tx) (interface{}, rpcError)
type dbTxScopedFn func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error)

func (f *dbTxManager) NewDbTxScope(st stateInterface, scopedFn dbTxScopedFn) (interface{}, rpcError) {
func (f *dbTxManager) NewDbTxScope(st types.StateInterface, scopedFn dbTxScopedFn) (interface{}, types.Error) {
ctx := context.Background()
dbTx, err := st.BeginStateTransaction(ctx)
if err != nil {
return rpcErrorResponse(defaultErrorCode, "failed to connect to the state", err)
return rpcErrorResponse(types.DefaultErrorCode, "failed to connect to the state", err)
}

v, rpcErr := scopedFn(ctx, dbTx)
if rpcErr != nil {
if txErr := dbTx.Rollback(context.Background()); txErr != nil {
return rpcErrorResponse(defaultErrorCode, "failed to rollback db transaction", txErr)
return rpcErrorResponse(types.DefaultErrorCode, "failed to rollback db transaction", txErr)
}
return v, rpcErr
}

if txErr := dbTx.Commit(context.Background()); txErr != nil {
return rpcErrorResponse(defaultErrorCode, "failed to commit db transaction", txErr)
return rpcErrorResponse(types.DefaultErrorCode, "failed to commit db transaction", txErr)
}
return v, rpcErr
}
42 changes: 22 additions & 20 deletions jsonrpc/dbtxmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"errors"
"testing"

"github.com/0xPolygonHermez/zkevm-node/jsonrpc/mocks"
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
"github.com/jackc/pgx/v4"
"github.com/stretchr/testify/assert"
)
Expand All @@ -14,75 +16,75 @@ func TestNewDbTxScope(t *testing.T) {
Name string
Fn dbTxScopedFn
ExpectedResult interface{}
ExpectedError rpcError
SetupMocks func(s *stateMock, d *dbTxMock)
ExpectedError types.Error
SetupMocks func(s *mocks.StateMock, d *mocks.DBTxMock)
}

testCases := []testCase{
{
Name: "Run scoped func commits DB tx",
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, rpcError) {
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error) {
return 1, nil
},
ExpectedResult: 1,
ExpectedError: nil,
SetupMocks: func(s *stateMock, d *dbTxMock) {
SetupMocks: func(s *mocks.StateMock, d *mocks.DBTxMock) {
d.On("Commit", context.Background()).Return(nil).Once()
s.On("BeginStateTransaction", context.Background()).Return(d, nil).Once()
},
},
{
Name: "Run scoped func rollbacks DB tx",
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, rpcError) {
return nil, newRPCError(defaultErrorCode, "func returned an error")
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error) {
return nil, types.NewRPCError(types.DefaultErrorCode, "func returned an error")
},
ExpectedResult: nil,
ExpectedError: newRPCError(defaultErrorCode, "func returned an error"),
SetupMocks: func(s *stateMock, d *dbTxMock) {
ExpectedError: types.NewRPCError(types.DefaultErrorCode, "func returned an error"),
SetupMocks: func(s *mocks.StateMock, d *mocks.DBTxMock) {
d.On("Rollback", context.Background()).Return(nil).Once()
s.On("BeginStateTransaction", context.Background()).Return(d, nil).Once()
},
},
{
Name: "Run scoped func but fails create a db tx",
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, rpcError) {
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error) {
return nil, nil
},
ExpectedResult: nil,
ExpectedError: newRPCError(defaultErrorCode, "failed to connect to the state"),
SetupMocks: func(s *stateMock, d *dbTxMock) {
ExpectedError: types.NewRPCError(types.DefaultErrorCode, "failed to connect to the state"),
SetupMocks: func(s *mocks.StateMock, d *mocks.DBTxMock) {
s.On("BeginStateTransaction", context.Background()).Return(nil, errors.New("failed to create db tx")).Once()
},
},
{
Name: "Run scoped func but fails to commit DB tx",
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, rpcError) {
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error) {
return 1, nil
},
ExpectedResult: nil,
ExpectedError: newRPCError(defaultErrorCode, "failed to commit db transaction"),
SetupMocks: func(s *stateMock, d *dbTxMock) {
ExpectedError: types.NewRPCError(types.DefaultErrorCode, "failed to commit db transaction"),
SetupMocks: func(s *mocks.StateMock, d *mocks.DBTxMock) {
d.On("Commit", context.Background()).Return(errors.New("failed to commit db tx")).Once()
s.On("BeginStateTransaction", context.Background()).Return(d, nil).Once()
},
},
{
Name: "Run scoped func but fails to rollbacks DB tx",
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, rpcError) {
return nil, newRPCError(defaultErrorCode, "func returned an error")
Fn: func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error) {
return nil, types.NewRPCError(types.DefaultErrorCode, "func returned an error")
},
ExpectedResult: nil,
ExpectedError: newRPCError(defaultErrorCode, "failed to rollback db transaction"),
SetupMocks: func(s *stateMock, d *dbTxMock) {
ExpectedError: types.NewRPCError(types.DefaultErrorCode, "failed to rollback db transaction"),
SetupMocks: func(s *mocks.StateMock, d *mocks.DBTxMock) {
d.On("Rollback", context.Background()).Return(errors.New("failed to rollback db tx")).Once()
s.On("BeginStateTransaction", context.Background()).Return(d, nil).Once()
},
},
}

dbTxManager := dbTxManager{}
s := newStateMock(t)
d := newDbTxMock(t)
s := mocks.NewStateMock(t)
d := mocks.NewDBTxMock(t)

for _, testCase := range testCases {
t.Run(testCase.Name, func(t *testing.T) {
Expand Down