Skip to content

Commit

Permalink
Sync Trusted State via jRPC instead of Broadcast (0xPolygonHermez#1840)
Browse files Browse the repository at this point in the history
* break down jrpc package to share objects
* sync trusted state via jRPC instead of broadcast

---------

Co-authored-by: ToniRamirezM <[email protected]>
  • Loading branch information
tclemos and ToniRamirezM authored Mar 17, 2023
1 parent 13dc968 commit 743b904
Show file tree
Hide file tree
Showing 44 changed files with 2,360 additions and 1,999 deletions.
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
Loading

0 comments on commit 743b904

Please sign in to comment.