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

Commit

Permalink
Use context with timeout for external RPC API calls (#27)
Browse files Browse the repository at this point in the history
* Reorder import statements

* Introduce HashLength const

* Use context with timeout in rpc functions

* Provide timeout from RPC configuration

* ConvertProof unit tests

* Reuse decode hex function from Geth

* Address comments
  • Loading branch information
Stefan-Ethernal authored Dec 15, 2023
1 parent dc84ba1 commit 498b0fb
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 19 deletions.
3 changes: 2 additions & 1 deletion client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
"fmt"
"time"

"github.com/0xPolygon/beethoven/tx"
"github.com/0xPolygon/cdk-validium-node/ethtxmanager"
"github.com/0xPolygon/cdk-validium-node/jsonrpc/client"
"github.com/0xPolygon/cdk-validium-node/jsonrpc/types"
"github.com/ethereum/go-ethereum/common"

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

// ClientFactoryInterface interface for the client factory
Expand Down
5 changes: 3 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,9 @@ func start(cliCtx *cli.Context) error {
&dummyinterfaces.DummyStorage{},
[]jsonrpc.Service{
{
Name: rpc.INTEROP,
Service: rpc.NewInteropEndpoints(addr, storage, &ethMan, c.FullNodeRPCs, etm),
Name: rpc.INTEROP,
Service: rpc.NewInteropEndpoints(addr, storage, &ethMan,
c.FullNodeRPCs, c.RPC.ReadTimeout.Duration, etm),
},
},
)
Expand Down
3 changes: 2 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"path/filepath"
"strings"

"github.com/0xPolygon/beethoven/rpc"
"github.com/0xPolygon/cdk-validium-node/config/types"
"github.com/0xPolygon/cdk-validium-node/db"
"github.com/0xPolygon/cdk-validium-node/ethtxmanager"
Expand All @@ -16,6 +15,8 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"github.com/urfave/cli/v2"

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

const (
Expand Down
14 changes: 10 additions & 4 deletions etherman/etherman.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package etherman
import (
"context"
"errors"
"github.com/jackc/pgx/v4"
"math/big"
"time"

"github.com/0xPolygon/beethoven/tx"
"github.com/0xPolygon/cdk-validium-node/etherman/smartcontracts/cdkvalidium"
"github.com/0xPolygon/cdk-validium-node/log"
"github.com/0xPolygon/cdk-validium-node/state"
Expand All @@ -16,6 +14,14 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/jackc/pgx/v4"

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

const (
HashLength = 32
ProofLength = 24
)

type Etherman struct {
Expand All @@ -40,9 +46,9 @@ func (e *Etherman) GetSequencerAddr(l1Contract common.Address) (common.Address,
}

func (e *Etherman) BuildTrustedVerifyBatchesTxData(lastVerifiedBatch, newVerifiedBatch uint64, proof tx.ZKP) (data []byte, err error) {
var newLocalExitRoot [32]byte
var newLocalExitRoot [HashLength]byte
copy(newLocalExitRoot[:], proof.NewLocalExitRoot.Bytes())
var newStateRoot [32]byte
var newStateRoot [HashLength]byte
copy(newStateRoot[:], proof.NewStateRoot.Bytes())
finalProof, err := ConvertProof(proof.Proof.Hex())
if err != nil {
Expand Down
37 changes: 37 additions & 0 deletions etherman/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package etherman

import (
"bytes"
"crypto/rand"
"encoding/hex"
"testing"

"github.com/stretchr/testify/require"
)

func generateProof(t *testing.T) string {
t.Helper()

var buf bytes.Buffer
buf.WriteString("0x")

for i := 0; i < 2*ProofLength; i++ {
hash := generateRandomHexString(t, HashLength)
_, err := buf.WriteString(hash)
require.NoError(t, err)
}

return buf.String()
}

func generateRandomHexString(t *testing.T, length int) string {
t.Helper()

numBytes := length / 2
randomBytes := make([]byte, numBytes)

_, err := rand.Read(randomBytes)
require.NoError(t, err)

return hex.EncodeToString(randomBytes)
}
40 changes: 35 additions & 5 deletions etherman/proof.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
package etherman

import (
"bytes"
"fmt"
"strings"

"github.com/0xPolygon/cdk-validium-node/encoding"
)

type Proof [24][32]byte
type Proof [ProofLength][HashLength]byte

func (p Proof) Equals(other Proof) bool {
for i := 0; i < len(p); i++ {
if !bytes.Equal(p[i][:], other[i][:]) {
return false
}
}
return true
}

func BytesToProof(data []byte) (Proof, error) {
// Check if the length of the input data is compatible with the Proof type
expectedLength := ProofLength * HashLength
if len(data) != expectedLength {
return Proof{}, fmt.Errorf("invalid byte slice length. Expected length: %d, Actual length: %d", expectedLength, len(data))
}

// Create a Proof type and copy the data into it
var proof Proof
for i := 0; i < ProofLength; i++ {
copy(proof[i][:], data[i*HashLength:(i+1)*HashLength])
}

return proof, nil
}

func ConvertProof(p string) (Proof, error) {
const expectedLength = 24*32*2 + 2
const (
expectedLength = ProofLength*HashLength*2 + 2
rawHashLength = 2 * HashLength // 1 byte gets substituted with 2 hex digits (1 hex digit = nibble = 4 bits)
)

if len(p) != expectedLength {
return Proof{}, fmt.Errorf("invalid proof length. Expected length: %d, Actual length %d", expectedLength, len(p))
}
p = strings.TrimPrefix(p, "0x")
proof := Proof{}
for i := 0; i < 24; i++ {
data := p[i*64 : (i+1)*64]
for i := 0; i < ProofLength; i++ {
data := p[i*rawHashLength : (i+1)*rawHashLength]
p, err := encoding.DecodeBytes(&data)
if err != nil {
return Proof{}, fmt.Errorf("failed to decode proof, err: %w", err)
}
var aux [32]byte
var aux [HashLength]byte
copy(aux[:], p)
proof[i] = aux
}
Expand Down
51 changes: 51 additions & 0 deletions etherman/proof_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package etherman

import (
"fmt"
"testing"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
)

func TestConvertProof(t *testing.T) {
validInputProof := generateProof(t)

validRawProof, err := hexutil.Decode(validInputProof)
require.NoError(t, err)

validProof, err := BytesToProof(validRawProof)
require.NoError(t, err)

tests := []struct {
name string
input string
expected Proof
expectedErr string
}{
{
name: "valid proof",
input: validInputProof,
expected: validProof,
expectedErr: "",
},
{
name: "invalid proof length",
input: "0x1234",
expectedErr: fmt.Sprintf("invalid proof length. Expected length: %d, Actual length %d", ProofLength*HashLength*2+2, 6),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := ConvertProof(tt.input)

if tt.expectedErr != "" {
require.ErrorContains(t, err, tt.expectedErr)
} else {
require.NoError(t, err)
require.True(t, result.Equals(tt.expected), "expected: %v, actual: %v", tt.expected, result)
}
})
}
}
16 changes: 12 additions & 4 deletions rpc/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
"context"
"fmt"
"math/big"
"time"

"github.com/0xPolygon/beethoven/tx"
"github.com/0xPolygon/cdk-validium-node/jsonrpc/client"
"github.com/0xPolygon/cdk-validium-node/jsonrpc/types"
"github.com/0xPolygon/cdk-validium-node/log"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"

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

// INTEROP is the namespace of the interop service
Expand All @@ -35,6 +37,7 @@ type InteropEndpoints struct {
etherman EthermanInterface
interopAdminAddr common.Address
fullNodeRPCs FullNodeRPCs
rpcTimeout time.Duration
ethTxManager EthTxManager
zkEVMClientCreator ZkEVMClientClientCreator
}
Expand All @@ -45,20 +48,23 @@ func NewInteropEndpoints(
db DBInterface,
etherman EthermanInterface,
fullNodeRPCs FullNodeRPCs,
rpcTimeout time.Duration,
ethTxManager EthTxManager,
) *InteropEndpoints {
return &InteropEndpoints{
db: db,
interopAdminAddr: interopAdminAddr,
etherman: etherman,
fullNodeRPCs: fullNodeRPCs,
rpcTimeout: rpcTimeout,
ethTxManager: ethTxManager,
zkEVMClientCreator: &zkEVMClientCreator{},
}
}

func (i *InteropEndpoints) SendTx(signedTx tx.SignedTx) (interface{}, types.Error) {
ctx := context.TODO()
ctx, cancel := context.WithTimeout(context.Background(), i.rpcTimeout)
defer cancel()

// 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 _, ok := i.fullNodeRPCs[signedTx.Tx.L1Contract]; !ok {
Expand Down Expand Up @@ -141,7 +147,9 @@ func (i *InteropEndpoints) SendTx(signedTx tx.SignedTx) (interface{}, types.Erro
}

func (i *InteropEndpoints) GetTxStatus(hash common.Hash) (result interface{}, err types.Error) {
ctx := context.TODO()
ctx, cancel := context.WithTimeout(context.Background(), i.rpcTimeout)
defer cancel()

dbTx, innerErr := i.db.BeginStateTransaction(ctx)
if innerErr != nil {
result = "0x0"
Expand All @@ -167,5 +175,5 @@ func (i *InteropEndpoints) GetTxStatus(hash common.Hash) (result interface{}, er

result = res.Status.String()

return result, err
return
}
11 changes: 9 additions & 2 deletions rpc/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"github.com/0xPolygon/beethoven/mocks"
"math/big"
"testing"
"time"

"github.com/0xPolygon/beethoven/tx"
"github.com/0xPolygon/cdk-validium-node/ethtxmanager"
validiumTypes "github.com/0xPolygon/cdk-validium-node/jsonrpc/types"
"github.com/ethereum/go-ethereum"
Expand All @@ -17,8 +17,12 @@ import (
"github.com/jackc/pgx/v4"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

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

const rpcRequestTimeout = 10 * time.Second

var _ EthermanInterface = (*ethermanMock)(nil)

type ethermanMock struct {
Expand Down Expand Up @@ -137,6 +141,7 @@ func TestInteropEndpointsGetTxStatus(t *testing.T) {
dbMock,
new(ethermanMock),
nil,
rpcRequestTimeout,
new(ethTxManagerMock),
)

Expand Down Expand Up @@ -168,6 +173,7 @@ func TestInteropEndpointsGetTxStatus(t *testing.T) {
dbMock,
new(ethermanMock),
nil,
rpcRequestTimeout,
txManagerMock,
)

Expand Down Expand Up @@ -211,6 +217,7 @@ func TestInteropEndpointsGetTxStatus(t *testing.T) {
dbMock,
new(ethermanMock),
nil,
rpcRequestTimeout,
txManagerMock,
)

Expand Down Expand Up @@ -266,7 +273,7 @@ func TestInteropEndpointsSendTx(t *testing.T) {
ethTxManagerMock := new(ethTxManagerMock)

executeTestFn := func() {
i := NewInteropEndpoints(common.HexToAddress("0xadmin"), dbMock, ethermanMock, fullNodeRPCs, ethTxManagerMock)
i := NewInteropEndpoints(common.HexToAddress("0xadmin"), dbMock, ethermanMock, fullNodeRPCs, rpcRequestTimeout, ethTxManagerMock)
i.zkEVMClientCreator = zkEVMClientCreatorMock

result, err := i.SendTx(*signedTx)
Expand Down

0 comments on commit 498b0fb

Please sign in to comment.