Skip to content

Commit

Permalink
Add optional parameter tolerance for unsigned transactions (0xPolygon…
Browse files Browse the repository at this point in the history
  • Loading branch information
tclemos authored Aug 17, 2022
1 parent 49f9920 commit cfc2959
Show file tree
Hide file tree
Showing 14 changed files with 961 additions and 109 deletions.
2 changes: 2 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ func runJSONRPCServer(c config.Config, pool *pool.Pool, st *state.State, gpe gas
log.Fatal(err)
}

c.RPC.MaxCumulativeGasUsed = c.Sequencer.MaxCumulativeGasUsed

if err := jsonrpc.NewServer(c.RPC, pool, st, gpe, storage, apis).Start(); err != nil {
log.Fatal(err)
}
Expand Down
1 change: 1 addition & 0 deletions config/config.debug.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Port = 8123
MaxRequestsPerIPAndSecond = 100
SequencerNodeURI = ""
BroadcastURI = "127.0.0.1:61090"
DefaultSenderAddress = "0x1111111111111111111111111111111111111111"

[Synchronizer]
SyncInterval = "5s"
Expand Down
1 change: 1 addition & 0 deletions config/config.local.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Port = 8123
MaxRequestsPerIPAndSecond = 5000
SequencerNodeURI = ""
BroadcastURI = "127.0.0.1:61090"
DefaultSenderAddress = "0x1111111111111111111111111111111111111111"

[Synchronizer]
SyncInterval = "1s"
Expand Down
4 changes: 4 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ func Test_Defaults(t *testing.T) {
path: "RPC.BroadcastURI",
expectedValue: "127.0.0.1:61090",
},
{
path: "RPC.DefaultSenderAddress",
expectedValue: "0x1111111111111111111111111111111111111111",
},
{
path: "Executor.URI",
expectedValue: "127.0.0.1:50071",
Expand Down
1 change: 1 addition & 0 deletions config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Port = 8123
MaxRequestsPerIPAndSecond = 50
SequencerNodeURI = ""
BroadcastURI = "127.0.0.1:61090"
DefaultSenderAddress = "0x1111111111111111111111111111111111111111"
[Synchronizer]
SyncInterval = "0s"
Expand Down
9 changes: 9 additions & 0 deletions jsonrpc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,14 @@ type Config struct {
// to relay transactions to the Sequencer node
SequencerNodeURI string `mapstructure:"SequencerNodeURI"`

// BroadcastURI is the URL of the Trusted State broadcast service
BroadcastURI string `mapstructure:"BroadcastURI"`

// DefaultSenderAddress is the address that jRPC will use
// to communicate with the state for eth_EstimateGas and eth_Call when
// the From field is not specified because it is optional
DefaultSenderAddress string `mapstructure:"DefaultSenderAddress"`

// MaxCumulativeGasUsed is the max gas allowed per batch
MaxCumulativeGasUsed uint64
}
36 changes: 9 additions & 27 deletions jsonrpc/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,14 @@ func (e *Eth) Call(arg *txnArgs, number *BlockNumber) (interface{}, rpcError) {
return nil, rpcErr
}

if arg.From == state.ZeroAddress && arg.Nonce == nil {
nonce := argUint64(0)
arg.Nonce = &nonce
} else if arg.From != state.ZeroAddress && arg.Nonce == nil {
n, err := e.state.GetNonce(ctx, arg.From, blockNumber, dbTx)
if err != nil {
return rpcErrorResponse(defaultErrorCode, "failed to get nonce", err)
}
nonce := argUint64(n)
arg.Nonce = &nonce
sender, tx, err := arg.ToUnsignedTransaction(ctx, e.state, blockNumber, e.cfg, dbTx)
if err != nil {
return rpcErrorResponse(defaultErrorCode, "failed to convert arguments into an unsigned transaction", err)
}

tx := arg.ToTransaction()

result := e.state.ProcessUnsignedTransaction(ctx, tx, arg.From, blockNumber, dbTx)
result := e.state.ProcessUnsignedTransaction(ctx, tx, sender, blockNumber, dbTx)
if result.Failed() {
return rpcErrorResponse(defaultErrorCode, "failed to execute call", result.Err)
return rpcErrorResponse(defaultErrorCode, result.Err.Error(), nil)
}

return argBytesPtr(result.ReturnValue), nil
Expand Down Expand Up @@ -105,21 +96,12 @@ func (e *Eth) EstimateGas(arg *txnArgs, number *BlockNumber) (interface{}, rpcEr
return nil, rpcErr
}

if arg.From == state.ZeroAddress && arg.Nonce == nil {
nonce := argUint64(0)
arg.Nonce = &nonce
} else if arg.From != state.ZeroAddress && arg.Nonce == nil {
n, err := e.state.GetNonce(ctx, arg.From, blockNumber, dbTx)
if err != nil {
return rpcErrorResponse(defaultErrorCode, "failed to get nonce", err)
}
nonce := argUint64(n)
arg.Nonce = &nonce
sender, tx, err := arg.ToUnsignedTransaction(ctx, e.state, blockNumber, e.cfg, dbTx)
if err != nil {
return rpcErrorResponse(defaultErrorCode, "failed to convert arguments into an unsigned transaction", err)
}

tx := arg.ToTransaction()

gasEstimation, err := e.state.EstimateGas(tx, arg.From, blockNumber, dbTx)
gasEstimation, err := e.state.EstimateGas(tx, sender, blockNumber, dbTx)
if err != nil {
return rpcErrorResponse(defaultErrorCode, err.Error(), nil)
}
Expand Down
72 changes: 37 additions & 35 deletions jsonrpc/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func TestCall(t *testing.T) {
blockNumber *big.Int
expectedResult []byte
expectedError interface{}
setupMocks func(*mocks, *testCase)
setupMocks func(Config, *mocks, *testCase)
}

testCases := []*testCase{
Expand All @@ -130,7 +130,7 @@ func TestCall(t *testing.T) {
data: []byte("data"),
expectedResult: []byte("hello world"),
expectedError: nil,
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
blockNumber := uint64(1)
nonce := uint64(7)
m.DbTx.On("Commit", context.Background()).Return(nil).Once()
Expand All @@ -152,41 +152,42 @@ func TestCall(t *testing.T) {
{
name: "Transaction without from and gas from latest block",
to: addressPtr(common.HexToAddress("0x2")),
gasPrice: big.NewInt(1),
gasPrice: big.NewInt(0),
value: big.NewInt(2),
data: []byte("data"),
expectedResult: []byte("hello world"),
expectedError: nil,
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
blockNumber := uint64(1)
block := types.NewBlockWithHeader(&types.Header{Root: common.Hash{}, GasLimit: 123456})
block := types.NewBlockWithHeader(&types.Header{Root: common.Hash{}, GasLimit: s.Config.MaxCumulativeGasUsed})
m.DbTx.On("Commit", context.Background()).Return(nil).Once()
m.State.On("BeginStateTransaction", context.Background()).Return(m.DbTx, nil).Once()
m.State.On("GetLastL2BlockNumber", context.Background(), m.DbTx).Return(blockNumber, nil).Once()
m.State.On("GetLastL2Block", context.Background(), m.DbTx).Return(block, nil).Once()
txMatchBy := mock.MatchedBy(func(tx *types.Transaction) bool {
return tx != nil &&
tx.Gas() == block.Header().GasLimit &&
tx.To().Hex() == testCase.to.Hex() &&
tx.GasPrice().Uint64() == testCase.gasPrice.Uint64() &&
tx.Value().Uint64() == testCase.value.Uint64() &&
hex.EncodeToHex(tx.Data()) == hex.EncodeToHex(testCase.data)
hasTx := tx != nil
gasMatch := tx.Gas() == block.Header().GasLimit
toMatch := tx.To().Hex() == testCase.to.Hex()
gasPriceMatch := tx.GasPrice().Uint64() == testCase.gasPrice.Uint64()
valueMatch := tx.Value().Uint64() == testCase.value.Uint64()
dataMatch := hex.EncodeToHex(tx.Data()) == hex.EncodeToHex(testCase.data)
return hasTx && gasMatch && toMatch && gasPriceMatch && valueMatch && dataMatch
})
m.State.On("ProcessUnsignedTransaction", context.Background(), txMatchBy, testCase.from, blockNumber, m.DbTx).Return(&runtime.ExecutionResult{ReturnValue: testCase.expectedResult}).Once()
m.State.On("ProcessUnsignedTransaction", context.Background(), txMatchBy, common.HexToAddress(c.DefaultSenderAddress), blockNumber, m.DbTx).Return(&runtime.ExecutionResult{ReturnValue: testCase.expectedResult}).Once()
},
},
{
name: "Transaction without from and gas from pending block",
to: addressPtr(common.HexToAddress("0x2")),
gasPrice: big.NewInt(1),
gasPrice: big.NewInt(0),
value: big.NewInt(2),
data: []byte("data"),
blockNumber: big.NewInt(-1),
expectedResult: []byte("hello world"),
expectedError: nil,
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
blockNumber := uint64(1)
block := types.NewBlockWithHeader(&types.Header{Number: big.NewInt(1), Root: common.Hash{}, GasLimit: 123456})
block := types.NewBlockWithHeader(&types.Header{Number: big.NewInt(1), Root: common.Hash{}, GasLimit: s.Config.MaxCumulativeGasUsed})
m.DbTx.On("Commit", context.Background()).Return(nil).Once()
m.State.On("BeginStateTransaction", context.Background()).Return(m.DbTx, nil).Once()
m.State.On("GetLastL2BlockNumber", context.Background(), m.DbTx).Return(blockNumber, nil).Once()
Expand All @@ -200,7 +201,7 @@ func TestCall(t *testing.T) {
dataMatch := hex.EncodeToHex(tx.Data()) == hex.EncodeToHex(testCase.data)
return hasTx && gasMatch && toMatch && gasPriceMatch && valueMatch && dataMatch
})
m.State.On("ProcessUnsignedTransaction", context.Background(), txMatchBy, testCase.from, blockNumber, m.DbTx).Return(&runtime.ExecutionResult{ReturnValue: testCase.expectedResult}).Once()
m.State.On("ProcessUnsignedTransaction", context.Background(), txMatchBy, common.HexToAddress(c.DefaultSenderAddress), blockNumber, m.DbTx).Return(&runtime.ExecutionResult{ReturnValue: testCase.expectedResult}).Once()
},
},
{
Expand All @@ -211,7 +212,7 @@ func TestCall(t *testing.T) {
data: []byte("data"),
expectedResult: nil,
expectedError: newRPCError(defaultErrorCode, "failed to get block header"),
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
m.DbTx.On("Rollback", context.Background()).Return(nil).Once()
m.State.On("BeginStateTransaction", context.Background()).Return(m.DbTx, nil).Once()
m.State.On("GetLastL2Block", context.Background(), m.DbTx).Return(nil, errors.New("failed to get last block")).Once()
Expand All @@ -226,7 +227,7 @@ func TestCall(t *testing.T) {
blockNumber: big.NewInt(-1),
expectedResult: nil,
expectedError: newRPCError(defaultErrorCode, "failed to get block header"),
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
m.DbTx.On("Rollback", context.Background()).Return(nil).Once()
m.State.On("BeginStateTransaction", context.Background()).Return(m.DbTx, nil).Once()
m.State.On("GetLastL2Block", context.Background(), m.DbTx).Return(nil, errors.New("failed to get last block")).Once()
Expand All @@ -242,7 +243,7 @@ func TestCall(t *testing.T) {
data: []byte("data"),
expectedResult: nil,
expectedError: newRPCError(defaultErrorCode, "failed to get the last block number from state"),
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
m.DbTx.On("Rollback", context.Background()).Return(nil).Once()
m.State.On("BeginStateTransaction", context.Background()).Return(m.DbTx, nil).Once()
m.State.On("GetLastL2BlockNumber", context.Background(), m.DbTx).Return(uint64(0), errors.New("failed to get last block number")).Once()
Expand All @@ -257,21 +258,22 @@ func TestCall(t *testing.T) {
value: big.NewInt(2),
data: []byte("data"),
expectedResult: nil,
expectedError: newRPCError(defaultErrorCode, "failed to execute call"),
setupMocks: func(m *mocks, testCase *testCase) {
expectedError: newRPCError(defaultErrorCode, "failed to process unsigned transaction"),
setupMocks: func(c Config, m *mocks, testCase *testCase) {
blockNumber := uint64(1)
nonce := uint64(7)
m.DbTx.On("Rollback", context.Background()).Return(nil).Once()
m.State.On("BeginStateTransaction", context.Background()).Return(m.DbTx, nil).Once()
m.State.On("GetLastL2BlockNumber", context.Background(), m.DbTx).Return(blockNumber, nil).Once()
txMatchBy := mock.MatchedBy(func(tx *types.Transaction) bool {
return tx != nil &&
tx.Gas() == testCase.gas &&
tx.To().Hex() == testCase.to.Hex() &&
tx.GasPrice().Uint64() == testCase.gasPrice.Uint64() &&
tx.Value().Uint64() == testCase.value.Uint64() &&
hex.EncodeToHex(tx.Data()) == hex.EncodeToHex(testCase.data) &&
tx.Nonce() == nonce
hasTx := tx != nil
gasMatch := tx.Gas() == testCase.gas
toMatch := tx.To().Hex() == testCase.to.Hex()
gasPriceMatch := tx.GasPrice().Uint64() == testCase.gasPrice.Uint64()
valueMatch := tx.Value().Uint64() == testCase.value.Uint64()
dataMatch := hex.EncodeToHex(tx.Data()) == hex.EncodeToHex(testCase.data)
nonceMatch := tx.Nonce() == nonce
return hasTx && gasMatch && toMatch && gasPriceMatch && valueMatch && dataMatch && nonceMatch
})
m.State.On("GetNonce", context.Background(), testCase.from, blockNumber, m.DbTx).Return(nonce, nil).Once()
m.State.On("ProcessUnsignedTransaction", context.Background(), txMatchBy, testCase.from, blockNumber, m.DbTx).Return(&runtime.ExecutionResult{Err: errors.New("failed to process unsigned transaction")}).Once()
Expand All @@ -283,7 +285,7 @@ func TestCall(t *testing.T) {
t.Run(testCase.name, func(t *testing.T) {
msg := ethereum.CallMsg{From: testCase.from, To: testCase.to, Gas: testCase.gas, GasPrice: testCase.gasPrice, Value: testCase.value, Data: testCase.data}

testCase.setupMocks(m, testCase)
testCase.setupMocks(s.Config, m, testCase)

result, err := c.CallContract(context.Background(), msg, testCase.blockNumber)
assert.Equal(t, testCase.expectedResult, result)
Expand Down Expand Up @@ -322,7 +324,7 @@ func TestEstimateGas(t *testing.T) {
gasPrice *big.Int
value *big.Int
data []byte
setupMocks func(*mocks, *testCase)
setupMocks func(Config, *mocks, *testCase)

expectedResult uint64
}
Expand All @@ -337,7 +339,7 @@ func TestEstimateGas(t *testing.T) {
value: big.NewInt(2),
data: []byte("data"),
expectedResult: 100,
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
blockNumber := uint64(10)
nonce := uint64(7)
txMatchBy := mock.MatchedBy(func(tx *types.Transaction) bool {
Expand Down Expand Up @@ -375,11 +377,11 @@ func TestEstimateGas(t *testing.T) {
{
name: "Transaction without from and gas",
to: addressPtr(common.HexToAddress("0x2")),
gasPrice: big.NewInt(1),
gasPrice: big.NewInt(0),
value: big.NewInt(2),
data: []byte("data"),
expectedResult: 100,
setupMocks: func(m *mocks, testCase *testCase) {
setupMocks: func(c Config, m *mocks, testCase *testCase) {
blockNumber := uint64(9)
nonce := uint64(0)
txMatchBy := mock.MatchedBy(func(tx *types.Transaction) bool {
Expand All @@ -404,7 +406,7 @@ func TestEstimateGas(t *testing.T) {
Once()

m.State.
On("EstimateGas", txMatchBy, testCase.from, blockNumber, m.DbTx).
On("EstimateGas", txMatchBy, common.HexToAddress(c.DefaultSenderAddress), blockNumber, m.DbTx).
Return(testCase.expectedResult, nil).
Once()
},
Expand All @@ -414,7 +416,7 @@ func TestEstimateGas(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
tc := testCase
tc.setupMocks(m, &tc)
tc.setupMocks(s.Config, m, &tc)

msg := ethereum.CallMsg{From: testCase.from, To: testCase.to, Gas: testCase.gas, GasPrice: testCase.gasPrice, Value: testCase.value, Data: testCase.data}
result, err := c.EstimateGas(context.Background(), msg)
Expand Down
20 changes: 12 additions & 8 deletions jsonrpc/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
)

type mockedServer struct {
Config Config
Server *Server
ServerURL string
}
Expand Down Expand Up @@ -67,6 +68,7 @@ func newMockedServer(t *testing.T, cfg Config) (*mockedServer, *mocks, *ethclien
require.NoError(t, err)

msv := &mockedServer{
Config: cfg,
Server: server,
ServerURL: serverURL,
}
Expand All @@ -82,24 +84,26 @@ func newMockedServer(t *testing.T, cfg Config) (*mockedServer, *mocks, *ethclien
return msv, mks, ethClient
}

func newSequencerMockedServer(t *testing.T) (*mockedServer, *mocks, *ethclient.Client) {
func getDefaultConfig() Config {
cfg := Config{
Host: host,
Port: 8123,
MaxRequestsPerIPAndSecond: maxRequestsPerIPAndSecond,
DefaultSenderAddress: "0x1111111111111111111111111111111111111111",
MaxCumulativeGasUsed: 300000,
}
return cfg
}

func newSequencerMockedServer(t *testing.T) (*mockedServer, *mocks, *ethclient.Client) {
cfg := getDefaultConfig()
return newMockedServer(t, cfg)
}

func newNonSequencerMockedServer(t *testing.T, sequencerNodeURI string) (*mockedServer, *mocks, *ethclient.Client) {
cfg := Config{
Host: host,
Port: 8124,
MaxRequestsPerIPAndSecond: maxRequestsPerIPAndSecond,
SequencerNodeURI: sequencerNodeURI,
}

cfg := getDefaultConfig()
cfg.Port = 8124
cfg.SequencerNodeURI = sequencerNodeURI
return newMockedServer(t, cfg)
}

Expand Down
Loading

0 comments on commit cfc2959

Please sign in to comment.