Explorar o código

Merge pull request #619 from tgerring/rpcfabian

RPC Fabian fixes
Jeffrey Wilcke %!s(int64=10) %!d(string=hai) anos
pai
achega
607fc788e3
Modificáronse 9 ficheiros con 389 adicións e 82 borrados
  1. 4 0
      common/size.go
  2. 19 9
      rpc/api.go
  3. 79 29
      rpc/args.go
  4. 37 10
      rpc/args_test.go
  5. 100 5
      rpc/responses.go
  6. 88 23
      rpc/responses_test.go
  7. 58 6
      rpc/types.go
  8. 0 0
      rpc/types_test.go
  9. 4 0
      xeth/xeth.go

+ 4 - 0
common/size.go

@@ -17,6 +17,10 @@ func (self StorageSize) String() string {
 	}
 }
 
+func (self StorageSize) Int64() int64 {
+	return int64(self)
+}
+
 // The different number of units
 var (
 	Douglas  = BigPow(10, 42)

+ 19 - 9
rpc/api.go

@@ -106,15 +106,15 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 		count := api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address)
 		*reply = common.ToHex(big.NewInt(int64(count)).Bytes())
 	case "eth_getBlockTransactionCountByHash":
-		args := new(GetBlockByHashArgs)
+		args := new(HashArgs)
 		if err := json.Unmarshal(req.Params, &args); err != nil {
 			return err
 		}
 
-		block := NewBlockRes(api.xeth().EthBlockByHash(args.BlockHash), false)
+		block := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false)
 		*reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes())
 	case "eth_getBlockTransactionCountByNumber":
-		args := new(GetBlockByNumberArgs)
+		args := new(BlockNumArg)
 		if err := json.Unmarshal(req.Params, &args); err != nil {
 			return err
 		}
@@ -122,16 +122,16 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 		block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber), false)
 		*reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes())
 	case "eth_getUncleCountByBlockHash":
-		args := new(GetBlockByHashArgs)
+		args := new(HashArgs)
 		if err := json.Unmarshal(req.Params, &args); err != nil {
 			return err
 		}
 
-		block := api.xeth().EthBlockByHash(args.BlockHash)
+		block := api.xeth().EthBlockByHash(args.Hash)
 		br := NewBlockRes(block, false)
 		*reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes())
 	case "eth_getUncleCountByBlockNumber":
-		args := new(GetBlockByNumberArgs)
+		args := new(BlockNumArg)
 		if err := json.Unmarshal(req.Params, &args); err != nil {
 			return err
 		}
@@ -144,7 +144,8 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 		if err := json.Unmarshal(req.Params, &args); err != nil {
 			return err
 		}
-		*reply = api.xethAtStateNum(args.BlockNumber).CodeAt(args.Address)
+		v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
+		*reply = newHexData(v)
 	case "eth_sendTransaction", "eth_transact":
 		args := new(NewTxArgs)
 		if err := json.Unmarshal(req.Params, &args); err != nil {
@@ -177,7 +178,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 		}
 
 		block := api.xeth().EthBlockByHash(args.BlockHash)
-		br := NewBlockRes(block, true)
+		br := NewBlockRes(block, args.IncludeTxs)
 
 		*reply = br
 	case "eth_getBlockByNumber":
@@ -187,7 +188,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 		}
 
 		block := api.xeth().EthBlockByNumber(args.BlockNumber)
-		br := NewBlockRes(block, true)
+		br := NewBlockRes(block, args.IncludeTxs)
 
 		*reply = br
 	case "eth_getTransactionByHash":
@@ -236,6 +237,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 		}
 
 		br := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false)
+		if br == nil {
+			*reply = nil
+			return nil
+		}
 
 		if args.Index >= int64(len(br.Uncles)) || args.Index < 0 {
 			return NewValidationError("Index", "does not exist")
@@ -254,6 +259,11 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
 		block := api.xeth().EthBlockByNumber(args.BlockNumber)
 		v := NewBlockRes(block, true)
 
+		if v == nil {
+			*reply = nil
+			return nil
+		}
+
 		if args.Index >= int64(len(v.Uncles)) || args.Index < 0 {
 			return NewValidationError("Index", "does not exist")
 		}

+ 79 - 29
rpc/args.go

@@ -108,8 +108,8 @@ func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
 		return NewDecodeParamError(err.Error())
 	}
 
-	if len(obj) < 1 {
-		return NewInsufficientParamsError(len(obj), 1)
+	if len(obj) < 2 {
+		return NewInsufficientParamsError(len(obj), 2)
 	}
 
 	argstr, ok := obj[0].(string)
@@ -118,9 +118,7 @@ func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
 	}
 	args.BlockHash = argstr
 
-	if len(obj) > 1 {
-		args.IncludeTxs = obj[1].(bool)
-	}
+	args.IncludeTxs = obj[1].(bool)
 
 	return nil
 }
@@ -136,8 +134,8 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
 		return NewDecodeParamError(err.Error())
 	}
 
-	if len(obj) < 1 {
-		return NewInsufficientParamsError(len(obj), 1)
+	if len(obj) < 2 {
+		return NewInsufficientParamsError(len(obj), 2)
 	}
 
 	if v, ok := obj[0].(float64); ok {
@@ -148,9 +146,7 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
 		return NewInvalidTypeError("blockNumber", "not a number or string")
 	}
 
-	if len(obj) > 1 {
-		args.IncludeTxs = obj[1].(bool)
-	}
+	args.IncludeTxs = obj[1].(bool)
 
 	return nil
 }
@@ -202,7 +198,7 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
 
 	var num int64
 	if ext.Value == nil {
-		return NewValidationError("value", "is required")
+		num = 0
 	} else {
 		if err := numString(ext.Value, &num); err != nil {
 			return err
@@ -211,7 +207,7 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
 	args.Value = big.NewInt(num)
 
 	if ext.Gas == nil {
-		return NewValidationError("gas", "is required")
+		num = 0
 	} else {
 		if err := numString(ext.Gas, &num); err != nil {
 			return err
@@ -220,7 +216,7 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
 	args.Gas = big.NewInt(num)
 
 	if ext.GasPrice == nil {
-		return NewValidationError("gasprice", "is required")
+		num = 0
 	} else {
 		if err := numString(ext.GasPrice, &num); err != nil {
 			return err
@@ -233,6 +229,8 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
 		if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
 			return err
 		}
+	} else {
+		args.BlockNumber = -1
 	}
 
 	return nil
@@ -320,6 +318,8 @@ func (args *CallArgs) UnmarshalJSON(b []byte) (err error) {
 		if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
 			return err
 		}
+	} else {
+		args.BlockNumber = -1
 	}
 
 	return nil
@@ -350,6 +350,8 @@ func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
 		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
 			return err
 		}
+	} else {
+		args.BlockNumber = -1
 	}
 
 	return nil
@@ -387,6 +389,8 @@ func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) {
 		if err := blockHeight(obj[2], &args.BlockNumber); err != nil {
 			return err
 		}
+	} else {
+		args.BlockNumber = -1
 	}
 
 	return nil
@@ -417,6 +421,8 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
 		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
 			return err
 		}
+	} else {
+		args.BlockNumber = -1
 	}
 
 	return nil
@@ -447,6 +453,8 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
 		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
 			return err
 		}
+	} else {
+		args.BlockNumber = -1
 	}
 
 	return nil
@@ -477,6 +485,29 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
 		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
 			return err
 		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	return nil
+}
+
+type BlockNumArg struct {
+	BlockNumber int64
+}
+
+func (args *BlockNumArg) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return NewInsufficientParamsError(len(obj), 1)
+	}
+
+	if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
+		return err
 	}
 
 	return nil
@@ -493,21 +524,42 @@ func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) {
 		return NewDecodeParamError(err.Error())
 	}
 
-	if len(obj) < 1 {
-		return NewInsufficientParamsError(len(obj), 1)
+	if len(obj) < 2 {
+		return NewInsufficientParamsError(len(obj), 2)
 	}
 
 	if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
 		return err
 	}
 
-	if len(obj) > 1 {
-		arg1, ok := obj[1].(string)
-		if !ok {
-			return NewInvalidTypeError("index", "not a string")
-		}
-		args.Index = common.Big(arg1).Int64()
+	arg1, ok := obj[1].(string)
+	if !ok {
+		return NewInvalidTypeError("index", "not a string")
 	}
+	args.Index = common.Big(arg1).Int64()
+
+	return nil
+}
+
+type HashArgs struct {
+	Hash string
+}
+
+func (args *HashArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return NewInsufficientParamsError(len(obj), 1)
+	}
+
+	arg0, ok := obj[0].(string)
+	if !ok {
+		return NewInvalidTypeError("hash", "not a string")
+	}
+	args.Hash = arg0
 
 	return nil
 }
@@ -523,8 +575,8 @@ func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) {
 		return NewDecodeParamError(err.Error())
 	}
 
-	if len(obj) < 1 {
-		return NewInsufficientParamsError(len(obj), 1)
+	if len(obj) < 2 {
+		return NewInsufficientParamsError(len(obj), 2)
 	}
 
 	arg0, ok := obj[0].(string)
@@ -533,13 +585,11 @@ func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) {
 	}
 	args.Hash = arg0
 
-	if len(obj) > 1 {
-		arg1, ok := obj[1].(string)
-		if !ok {
-			return NewInvalidTypeError("index", "not a string")
-		}
-		args.Index = common.Big(arg1).Int64()
+	arg1, ok := obj[1].(string)
+	if !ok {
+		return NewInvalidTypeError("index", "not a string")
 	}
+	args.Index = common.Big(arg1).Int64()
 
 	return nil
 }

+ 37 - 10
rpc/args_test.go

@@ -225,7 +225,7 @@ func TestGetBlockByHashArgsHashInt(t *testing.T) {
 	input := `[8]`
 
 	args := new(GetBlockByHashArgs)
-	str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args))
+	str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args))
 	if len(str) > 0 {
 		t.Error(str)
 	}
@@ -281,6 +281,16 @@ func TestGetBlockByNumberEmpty(t *testing.T) {
 	}
 }
 
+func TestGetBlockByNumberShort(t *testing.T) {
+	input := `["0xbbb"]`
+
+	args := new(GetBlockByNumberArgs)
+	str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args))
+	if len(str) > 0 {
+		t.Error(str)
+	}
+}
+
 func TestGetBlockByNumberBool(t *testing.T) {
 	input := `[true, true]`
 
@@ -458,11 +468,16 @@ func TestNewTxArgsGasMissing(t *testing.T) {
   "value": "0x9184e72a000",
   "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
   }]`
+	expected := new(NewTxArgs)
+	expected.Gas = big.NewInt(0)
 
 	args := new(NewTxArgs)
-	str := ExpectValidationError(json.Unmarshal([]byte(input), &args))
-	if len(str) > 0 {
-		t.Error(str)
+	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)
 	}
 }
 
@@ -474,12 +489,18 @@ func TestNewTxArgsBlockGaspriceMissing(t *testing.T) {
   "value": "0x9184e72a000",
   "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
   }]`
+	expected := new(NewTxArgs)
+	expected.GasPrice = big.NewInt(0)
 
 	args := new(NewTxArgs)
-	str := ExpectValidationError(json.Unmarshal([]byte(input), &args))
-	if len(str) > 0 {
-		t.Error(str)
+	if err := json.Unmarshal([]byte(input), &args); err != nil {
+		t.Error(err)
 	}
+
+	if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 {
+		t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice)
+	}
+
 }
 
 func TestNewTxArgsValueMissing(t *testing.T) {
@@ -490,12 +511,18 @@ func TestNewTxArgsValueMissing(t *testing.T) {
   "gasPrice": "0x9184e72a000",
   "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
 	}]`
+	expected := new(NewTxArgs)
+	expected.Value = big.NewInt(0)
 
 	args := new(NewTxArgs)
-	str := ExpectValidationError(json.Unmarshal([]byte(input), &args))
-	if len(str) > 0 {
-		t.Error(str)
+	if err := json.Unmarshal([]byte(input), &args); err != nil {
+		t.Error(err)
 	}
+
+	if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 {
+		t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value)
+	}
+
 }
 
 func TestNewTxArgsEmpty(t *testing.T) {

+ 100 - 5
rpc/responses.go

@@ -1,6 +1,8 @@
 package rpc
 
 import (
+	"encoding/json"
+
 	"github.com/ethereum/go-ethereum/core/state"
 	"github.com/ethereum/go-ethereum/core/types"
 )
@@ -11,10 +13,10 @@ type BlockRes struct {
 	BlockNumber     *hexnum           `json:"number"`
 	BlockHash       *hexdata          `json:"hash"`
 	ParentHash      *hexdata          `json:"parentHash"`
-	Nonce           *hexnum           `json:"nonce"`
+	Nonce           *hexdata          `json:"nonce"`
 	Sha3Uncles      *hexdata          `json:"sha3Uncles"`
 	LogsBloom       *hexdata          `json:"logsBloom"`
-	TransactionRoot *hexdata          `json:"transactionRoot"`
+	TransactionRoot *hexdata          `json:"transactionsRoot"`
 	StateRoot       *hexdata          `json:"stateRoot"`
 	Miner           *hexdata          `json:"miner"`
 	Difficulty      *hexnum           `json:"difficulty"`
@@ -29,11 +31,104 @@ type BlockRes struct {
 	Uncles          []*hexdata        `json:"uncles"`
 }
 
+func (b *BlockRes) MarshalJSON() ([]byte, error) {
+	if b.fullTx {
+		var ext struct {
+			BlockNumber     *hexnum           `json:"number"`
+			BlockHash       *hexdata          `json:"hash"`
+			ParentHash      *hexdata          `json:"parentHash"`
+			Nonce           *hexdata          `json:"nonce"`
+			Sha3Uncles      *hexdata          `json:"sha3Uncles"`
+			LogsBloom       *hexdata          `json:"logsBloom"`
+			TransactionRoot *hexdata          `json:"transactionsRoot"`
+			StateRoot       *hexdata          `json:"stateRoot"`
+			Miner           *hexdata          `json:"miner"`
+			Difficulty      *hexnum           `json:"difficulty"`
+			TotalDifficulty *hexnum           `json:"totalDifficulty"`
+			Size            *hexnum           `json:"size"`
+			ExtraData       *hexdata          `json:"extraData"`
+			GasLimit        *hexnum           `json:"gasLimit"`
+			MinGasPrice     *hexnum           `json:"minGasPrice"`
+			GasUsed         *hexnum           `json:"gasUsed"`
+			UnixTimestamp   *hexnum           `json:"timestamp"`
+			Transactions    []*TransactionRes `json:"transactions"`
+			Uncles          []*hexdata        `json:"uncles"`
+		}
+
+		ext.BlockNumber = b.BlockNumber
+		ext.BlockHash = b.BlockHash
+		ext.ParentHash = b.ParentHash
+		ext.Nonce = b.Nonce
+		ext.Sha3Uncles = b.Sha3Uncles
+		ext.LogsBloom = b.LogsBloom
+		ext.TransactionRoot = b.TransactionRoot
+		ext.StateRoot = b.StateRoot
+		ext.Miner = b.Miner
+		ext.Difficulty = b.Difficulty
+		ext.TotalDifficulty = b.TotalDifficulty
+		ext.Size = b.Size
+		ext.ExtraData = b.ExtraData
+		ext.GasLimit = b.GasLimit
+		ext.MinGasPrice = b.MinGasPrice
+		ext.GasUsed = b.GasUsed
+		ext.UnixTimestamp = b.UnixTimestamp
+		ext.Transactions = b.Transactions
+		ext.Uncles = b.Uncles
+		return json.Marshal(ext)
+	} else {
+		var ext struct {
+			BlockNumber     *hexnum    `json:"number"`
+			BlockHash       *hexdata   `json:"hash"`
+			ParentHash      *hexdata   `json:"parentHash"`
+			Nonce           *hexdata   `json:"nonce"`
+			Sha3Uncles      *hexdata   `json:"sha3Uncles"`
+			LogsBloom       *hexdata   `json:"logsBloom"`
+			TransactionRoot *hexdata   `json:"transactionsRoot"`
+			StateRoot       *hexdata   `json:"stateRoot"`
+			Miner           *hexdata   `json:"miner"`
+			Difficulty      *hexnum    `json:"difficulty"`
+			TotalDifficulty *hexnum    `json:"totalDifficulty"`
+			Size            *hexnum    `json:"size"`
+			ExtraData       *hexdata   `json:"extraData"`
+			GasLimit        *hexnum    `json:"gasLimit"`
+			MinGasPrice     *hexnum    `json:"minGasPrice"`
+			GasUsed         *hexnum    `json:"gasUsed"`
+			UnixTimestamp   *hexnum    `json:"timestamp"`
+			Transactions    []*hexdata `json:"transactions"`
+			Uncles          []*hexdata `json:"uncles"`
+		}
+
+		ext.BlockNumber = b.BlockNumber
+		ext.BlockHash = b.BlockHash
+		ext.ParentHash = b.ParentHash
+		ext.Nonce = b.Nonce
+		ext.Sha3Uncles = b.Sha3Uncles
+		ext.LogsBloom = b.LogsBloom
+		ext.TransactionRoot = b.TransactionRoot
+		ext.StateRoot = b.StateRoot
+		ext.Miner = b.Miner
+		ext.Difficulty = b.Difficulty
+		ext.TotalDifficulty = b.TotalDifficulty
+		ext.Size = b.Size
+		ext.ExtraData = b.ExtraData
+		ext.GasLimit = b.GasLimit
+		ext.MinGasPrice = b.MinGasPrice
+		ext.GasUsed = b.GasUsed
+		ext.UnixTimestamp = b.UnixTimestamp
+		ext.Transactions = make([]*hexdata, len(b.Transactions))
+		for i, tx := range b.Transactions {
+			ext.Transactions[i] = tx.Hash
+		}
+		ext.Uncles = b.Uncles
+		return json.Marshal(ext)
+	}
+}
+
 func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
 	// TODO respect fullTx flag
 
 	if block == nil {
-		return &BlockRes{}
+		return nil
 	}
 
 	res := new(BlockRes)
@@ -41,7 +136,7 @@ func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
 	res.BlockNumber = newHexNum(block.Number())
 	res.BlockHash = newHexData(block.Hash())
 	res.ParentHash = newHexData(block.ParentHash())
-	res.Nonce = newHexNum(block.Header().Nonce)
+	res.Nonce = newHexData(block.Nonce())
 	res.Sha3Uncles = newHexData(block.Header().UncleHash)
 	res.LogsBloom = newHexData(block.Bloom())
 	res.TransactionRoot = newHexData(block.Header().TxHash)
@@ -49,7 +144,7 @@ func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
 	res.Miner = newHexData(block.Header().Coinbase)
 	res.Difficulty = newHexNum(block.Difficulty())
 	res.TotalDifficulty = newHexNum(block.Td)
-	res.Size = newHexNum(block.Size())
+	res.Size = newHexNum(block.Size().Int64())
 	res.ExtraData = newHexData(block.Header().Extra)
 	res.GasLimit = newHexNum(block.GasLimit())
 	// res.MinGasPrice =

+ 88 - 23
rpc/responses_test.go

@@ -13,12 +13,16 @@ import (
 )
 
 const (
-	reHash       = `"0x[0-9a-f]{64}"`               // 32 bytes
-	reHashOpt    = `"(0x[0-9a-f]{64})"|null`        // 32 bytes or null
-	reAddress    = `"0x[0-9a-f]{40}"`               // 20 bytes
-	reAddressOpt = `"0x[0-9a-f]{40}"|null`          // 20 bytes or null
-	reNum        = `"0x([1-9a-f][0-9a-f]{1,15})|0"` // must not have left-padded zeros
-	reData       = `"0x[0-9a-f]*"`                  // can be "empty"
+	reHash       = `"0x[0-9a-f]{64}"`                    // 32 bytes
+	reHashOpt    = `"(0x[0-9a-f]{64})"|null`             // 32 bytes or null
+	reAddress    = `"0x[0-9a-f]{40}"`                    // 20 bytes
+	reAddressOpt = `"0x[0-9a-f]{40}"|null`               // 20 bytes or null
+	reNum        = `"0x([1-9a-f][0-9a-f]{0,15})|0"`      // must not have left-padded zeros
+	reNumNonZero = `"0x([1-9a-f][0-9a-f]{0,15})"`        // non-zero required must not have left-padded zeros
+	reNumOpt     = `"0x([1-9a-f][0-9a-f]{0,15})|0"|null` // must not have left-padded zeros or null
+	reData       = `"0x[0-9a-f]*"`                       // can be "empty"
+	// reListHash   = `[("\w":"0x[0-9a-f]{64}",?)*]`
+	// reListObj    = `[("\w":(".+"|null),?)*]`
 )
 
 func TestNewBlockRes(t *testing.T) {
@@ -30,26 +34,87 @@ func TestNewBlockRes(t *testing.T) {
 	extra := ""
 	block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, extra)
 	tests := map[string]string{
-		"number":          reNum,
-		"hash":            reHash,
-		"parentHash":      reHash,
-		"nonce":           reNum,
-		"sha3Uncles":      reHash,
-		"logsBloom":       reData,
-		"transactionRoot": reHash,
-		"stateRoot":       reHash,
-		"miner":           reAddress,
-		"difficulty":      `"0x1"`,
-		"totalDifficulty": reNum,
-		"size":            reNum,
-		"extraData":       reData,
-		"gasLimit":        reNum,
+		"number":           reNum,
+		"hash":             reHash,
+		"parentHash":       reHash,
+		"nonce":            reData,
+		"sha3Uncles":       reHash,
+		"logsBloom":        reData,
+		"transactionsRoot": reHash,
+		"stateRoot":        reHash,
+		"miner":            reAddress,
+		"difficulty":       `"0x1"`,
+		"totalDifficulty":  reNum,
+		"size":             reNumNonZero,
+		"extraData":        reData,
+		"gasLimit":         reNum,
 		// "minGasPrice":  "0x",
 		"gasUsed":   reNum,
 		"timestamp": reNum,
+		// "transactions": reListHash,
+		// "uncles":       reListHash,
 	}
 
+	to := common.HexToAddress("0x02")
+	amount := big.NewInt(1)
+	gasAmount := big.NewInt(1)
+	gasPrice := big.NewInt(1)
+	data := []byte{1, 2, 3}
+	tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data)
+
 	v := NewBlockRes(block, false)
+	v.Transactions = make([]*TransactionRes, 1)
+	v.Transactions[0] = NewTransactionRes(tx)
+	j, _ := json.Marshal(v)
+
+	for k, re := range tests {
+		match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
+		if !match {
+			t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
+		}
+	}
+}
+
+func TestNewBlockResWithTrans(t *testing.T) {
+	parentHash := common.HexToHash("0x01")
+	coinbase := common.HexToAddress("0x01")
+	root := common.HexToHash("0x01")
+	difficulty := common.Big1
+	nonce := uint64(1)
+	extra := ""
+	block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, extra)
+	tests := map[string]string{
+		"number":           reNum,
+		"hash":             reHash,
+		"parentHash":       reHash,
+		"nonce":            reData,
+		"sha3Uncles":       reHash,
+		"logsBloom":        reData,
+		"transactionsRoot": reHash,
+		"stateRoot":        reHash,
+		"miner":            reAddress,
+		"difficulty":       `"0x1"`,
+		"totalDifficulty":  reNum,
+		"size":             reNumNonZero,
+		"extraData":        reData,
+		"gasLimit":         reNum,
+		// "minGasPrice":  "0x",
+		"gasUsed":   reNum,
+		"timestamp": reNum,
+		// "transactions": `[{.*}]`,
+		// "uncles":       reListHash,
+	}
+
+	to := common.HexToAddress("0x02")
+	amount := big.NewInt(1)
+	gasAmount := big.NewInt(1)
+	gasPrice := big.NewInt(1)
+	data := []byte{1, 2, 3}
+	tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data)
+
+	v := NewBlockRes(block, true)
+	v.Transactions = make([]*TransactionRes, 1)
+	v.Transactions[0] = NewTransactionRes(tx)
 	j, _ := json.Marshal(v)
 
 	for k, re := range tests {
@@ -71,9 +136,9 @@ func TestNewTransactionRes(t *testing.T) {
 	tests := map[string]string{
 		"hash":             reHash,
 		"nonce":            reNum,
-		"blockHash":        reHash,
-		"blockNum":         reNum,
-		"transactionIndex": reNum,
+		"blockHash":        reHashOpt,
+		"blockNum":         reNumOpt,
+		"transactionIndex": reNumOpt,
 		"from":             reAddress,
 		"to":               reAddressOpt,
 		"value":            reNum,

+ 58 - 6
rpc/messages.go → rpc/types.go

@@ -17,16 +17,19 @@
 package rpc
 
 import (
+	"encoding/binary"
 	"encoding/json"
 	"fmt"
 	"math/big"
 	"strings"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/types"
 )
 
 type hexdata struct {
-	data []byte
+	data  []byte
+	isNil bool
 }
 
 func (d *hexdata) String() string {
@@ -34,6 +37,9 @@ func (d *hexdata) String() string {
 }
 
 func (d *hexdata) MarshalJSON() ([]byte, error) {
+	if d.isNil {
+		return json.Marshal(nil)
+	}
 	return json.Marshal(d.String())
 }
 
@@ -45,27 +51,69 @@ func (d *hexdata) UnmarshalJSON(b []byte) (err error) {
 func newHexData(input interface{}) *hexdata {
 	d := new(hexdata)
 
+	if input == nil {
+		d.data = nil
+		return d
+	}
 	switch input := input.(type) {
 	case []byte:
 		d.data = input
 	case common.Hash:
 		d.data = input.Bytes()
 	case *common.Hash:
-		d.data = input.Bytes()
+		if input == nil {
+			d.isNil = true
+		} else {
+			d.data = input.Bytes()
+		}
 	case common.Address:
 		d.data = input.Bytes()
 	case *common.Address:
+		if input == nil {
+			d.isNil = true
+		} else {
+			d.data = input.Bytes()
+		}
+	case types.Bloom:
 		d.data = input.Bytes()
+	case *types.Bloom:
+		if input == nil {
+			d.isNil = true
+		} else {
+			d.data = input.Bytes()
+		}
 	case *big.Int:
-		d.data = input.Bytes()
+		if input == nil {
+			d.isNil = true
+		} else {
+			d.data = input.Bytes()
+		}
 	case int64:
 		d.data = big.NewInt(input).Bytes()
 	case uint64:
-		d.data = big.NewInt(int64(input)).Bytes()
+		buff := make([]byte, 8)
+		binary.BigEndian.PutUint64(buff, input)
+		d.data = buff
 	case int:
 		d.data = big.NewInt(int64(input)).Bytes()
 	case uint:
 		d.data = big.NewInt(int64(input)).Bytes()
+	case int8:
+		d.data = big.NewInt(int64(input)).Bytes()
+	case uint8:
+		d.data = big.NewInt(int64(input)).Bytes()
+	case int16:
+		d.data = big.NewInt(int64(input)).Bytes()
+	case uint16:
+		buff := make([]byte, 8)
+		binary.BigEndian.PutUint16(buff, input)
+		d.data = buff
+	case int32:
+		d.data = big.NewInt(int64(input)).Bytes()
+	case uint32:
+		buff := make([]byte, 8)
+		binary.BigEndian.PutUint32(buff, input)
+		d.data = buff
 	case string: // hexstring
 		d.data = common.Big(input).Bytes()
 	default:
@@ -76,14 +124,15 @@ func newHexData(input interface{}) *hexdata {
 }
 
 type hexnum struct {
-	data []byte
+	data  []byte
+	isNil bool
 }
 
 func (d *hexnum) String() string {
 	// Get hex string from bytes
 	out := common.Bytes2Hex(d.data)
 	// Trim leading 0s
-	out = strings.Trim(out, "0")
+	out = strings.TrimLeft(out, "0")
 	// Output "0x0" when value is 0
 	if len(out) == 0 {
 		out = "0"
@@ -92,6 +141,9 @@ func (d *hexnum) String() string {
 }
 
 func (d *hexnum) MarshalJSON() ([]byte, error) {
+	if d.isNil {
+		return json.Marshal(nil)
+	}
 	return json.Marshal(d.String())
 }
 

+ 0 - 0
rpc/messages_test.go → rpc/types_test.go


+ 4 - 0
xeth/xeth.go

@@ -323,6 +323,10 @@ func (self *XEth) CodeAt(address string) string {
 	return common.ToHex(self.State().state.GetCode(common.HexToAddress(address)))
 }
 
+func (self *XEth) CodeAtBytes(address string) []byte {
+	return self.State().SafeGet(address).Code()
+}
+
 func (self *XEth) IsContract(address string) bool {
 	return len(self.State().SafeGet(address).Code()) > 0
 }