From 2f3a9681360f5326137de3aeb0aa8e2130de562a Mon Sep 17 00:00:00 2001
From: Taylor Gerring <taylor.gerring@gmail.com>
Date: Mon, 30 Mar 2015 16:20:30 +0200
Subject: New CallArgs

Requirements for calls differ from transactions
---
 rpc/api.go       |   2 +-
 rpc/args.go      |  87 +++++++++++++++++
 rpc/args_test.go | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 357 insertions(+), 9 deletions(-)

diff --git a/rpc/api.go b/rpc/api.go
index 502079177..bcd073ed2 100644
--- a/rpc/api.go
+++ b/rpc/api.go
@@ -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
 		}
diff --git a/rpc/args.go b/rpc/args.go
index 25a6c7a4f..dd013147d 100644
--- a/rpc/args.go
+++ b/rpc/args.go
@@ -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
diff --git a/rpc/args_test.go b/rpc/args_test.go
index 602631b67..3635882c0 100644
--- a/rpc/args_test.go
+++ b/rpc/args_test.go
@@ -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)
 	}
 }
 
-- 
cgit v1.2.3