Skip to content

Updated specification for eth_call RPC method #596

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion rpc/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
}
*reply = v
case "eth_call":
args := new(NewTxArgs)
args := new(CallArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
Expand Down
87 changes: 87 additions & 0 deletions rpc/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,93 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
return nil
}

type CallArgs struct {
From string
To string
Value *big.Int
Gas *big.Int
GasPrice *big.Int
Data string

BlockNumber int64
}

func (args *CallArgs) UnmarshalJSON(b []byte) (err error) {
var obj []json.RawMessage
var ext struct {
From string
To string
Value interface{}
Gas interface{}
GasPrice interface{}
Data string
}

// Decode byte slice to array of RawMessages
if err := json.Unmarshal(b, &obj); err != nil {
return NewDecodeParamError(err.Error())
}

// Check for sufficient params
if len(obj) < 1 {
return NewInsufficientParamsError(len(obj), 1)
}

// Decode 0th RawMessage to temporary struct
if err := json.Unmarshal(obj[0], &ext); err != nil {
return NewDecodeParamError(err.Error())
}

if len(ext.From) == 0 {
return NewValidationError("from", "is required")
}
args.From = ext.From

if len(ext.To) == 0 {
return NewValidationError("to", "is required")
}
args.To = ext.To

var num int64
if ext.Value == nil {
num = int64(0)
} else {
if err := numString(ext.Value, &num); err != nil {
return err
}
}
args.Value = big.NewInt(num)

if ext.Gas == nil {
num = int64(0)
} else {
if err := numString(ext.Gas, &num); err != nil {
return err
}
}
args.Gas = big.NewInt(num)

if ext.GasPrice == nil {
num = int64(0)
} else {
if err := numString(ext.GasPrice, &num); err != nil {
return err
}
}
args.GasPrice = big.NewInt(num)

args.Data = ext.Data

// Check for optional BlockNumber param
if len(obj) > 1 {
if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
return err
}
}

return nil
}

type GetStorageArgs struct {
Address string
BlockNumber int64
Expand Down
277 changes: 269 additions & 8 deletions rpc/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,14 +531,275 @@ func TestNewTxArgsFromEmpty(t *testing.T) {
input := `[{"to": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]`

args := new(NewTxArgs)
err := json.Unmarshal([]byte(input), &args)
switch err.(type) {
case nil:
t.Error("Expected error but didn't get one")
case *ValidationError:
break
default:
t.Errorf("Expected *rpc.ValidationError, but got %T with message `%s`", err, err.Error())
str := ExpectValidationError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgs(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"},
"0x10"]`
expected := new(CallArgs)
expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155"
expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675"
expected.Gas = big.NewInt(30400)
expected.GasPrice = big.NewInt(10000000000000)
expected.Value = big.NewInt(10000000000000)
expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
expected.BlockNumber = big.NewInt(16).Int64()

args := new(CallArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}

if expected.From != args.From {
t.Errorf("From shoud be %#v but is %#v", expected.From, args.From)
}

if expected.To != args.To {
t.Errorf("To shoud be %#v but is %#v", expected.To, args.To)
}

if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 {
t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes())
}

if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 {
t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice)
}

if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 {
t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value)
}

if expected.Data != args.Data {
t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data)
}

if expected.BlockNumber != args.BlockNumber {
t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber)
}
}

func TestCallArgsInt(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": 100,
"gasPrice": 50,
"value": 8765456789,
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"},
5]`
expected := new(CallArgs)
expected.Gas = big.NewInt(100)
expected.GasPrice = big.NewInt(50)
expected.Value = big.NewInt(8765456789)
expected.BlockNumber = int64(5)

args := new(CallArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}

if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 {
t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas)
}

if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 {
t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice)
}

if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 {
t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value)
}

if expected.BlockNumber != args.BlockNumber {
t.Errorf("BlockNumber shoud be %v but is %v", expected.BlockNumber, args.BlockNumber)
}
}

func TestCallArgsBlockBool(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"},
false]`

args := new(CallArgs)
str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgsGasInvalid(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": false,
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]`

args := new(CallArgs)
str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgsGaspriceInvalid(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": "0x76c0",
"gasPrice": false,
"value": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]`

args := new(CallArgs)
str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgsValueInvalid(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": false,
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]`

args := new(CallArgs)
str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgsGasMissing(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]`

args := new(CallArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}

expected := new(CallArgs)
expected.Gas = big.NewInt(0)

if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 {
t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas)
}

}

func TestCallArgsBlockGaspriceMissing(t *testing.T) {
input := `[{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": "0x76c0",
"value": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]`

args := new(CallArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}

expected := new(CallArgs)
expected.GasPrice = big.NewInt(0)

if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 {
t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice)
}
}

func TestCallArgsValueMissing(t *testing.T) {
input := `[{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]`

args := new(CallArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}

expected := new(CallArgs)
expected.Value = big.NewInt(int64(0))

if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 {
t.Errorf("GasPrice shoud be %v but is %v", expected.Value, args.Value)
}
}

func TestCallArgsEmpty(t *testing.T) {
input := `[]`

args := new(CallArgs)
str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgsInvalid(t *testing.T) {
input := `{}`

args := new(CallArgs)
str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}
func TestCallArgsNotStrings(t *testing.T) {
input := `[{"from":6}]`

args := new(CallArgs)
str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgsFromEmpty(t *testing.T) {
input := `[{"to": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]`

args := new(CallArgs)
str := ExpectValidationError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

func TestCallArgsToEmpty(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]`

args := new(CallArgs)
str := ExpectValidationError(json.Unmarshal([]byte(input), &args))
if len(str) > 0 {
t.Error(str)
}
}

Expand Down