From 2f55a1d79853c1348fb1a4332fff98110167da80 Mon Sep 17 00:00:00 2001
From: Bas van Kervel <basvankervel@ziggo.nl>
Date: Mon, 8 Jun 2015 10:23:54 +0200
Subject: restructured eth rpc API

---
 rpc/api/api.go       |  12 +
 rpc/api/api_test.go  |  42 +++
 rpc/api/eth.go       | 523 ++++++++++++++++++++++++++++++++
 rpc/api/eth_args.go  | 835 +++++++++++++++++++++++++++++++++++++++++++++++++++
 rpc/api/eth_js.go    |   3 +
 rpc/api/parsing.go   | 460 ++++++++++++++++++++++++++++
 rpc/api/utils.go     |  36 +++
 rpc/codec/codec.go   |  47 +++
 rpc/codec/json.go    |  75 +++++
 rpc/shared/errors.go |  96 ++++++
 rpc/shared/types.go  |  38 +++
 11 files changed, 2167 insertions(+)
 create mode 100644 rpc/api/api.go
 create mode 100644 rpc/api/api_test.go
 create mode 100644 rpc/api/eth.go
 create mode 100644 rpc/api/eth_args.go
 create mode 100644 rpc/api/eth_js.go
 create mode 100644 rpc/api/parsing.go
 create mode 100644 rpc/api/utils.go
 create mode 100644 rpc/codec/codec.go
 create mode 100644 rpc/codec/json.go
 create mode 100644 rpc/shared/errors.go
 create mode 100644 rpc/shared/types.go

(limited to 'rpc')

diff --git a/rpc/api/api.go b/rpc/api/api.go
new file mode 100644
index 000000000..758e056ed
--- /dev/null
+++ b/rpc/api/api.go
@@ -0,0 +1,12 @@
+package api
+
+import "github.com/ethereum/go-ethereum/rpc/shared"
+
+// Ethereum RPC API interface
+type EthereumApi interface {
+	// Execute the given request and returns the response or an error
+	Execute(*shared.Request) (interface{}, error)
+
+	// List of supported RCP methods this API provides
+	Methods() []string
+}
diff --git a/rpc/api/api_test.go b/rpc/api/api_test.go
new file mode 100644
index 000000000..f1a47944d
--- /dev/null
+++ b/rpc/api/api_test.go
@@ -0,0 +1,42 @@
+package api
+
+import (
+	"testing"
+
+	"github.com/ethereum/go-ethereum/rpc/codec"
+)
+
+func TestParseApiString(t *testing.T) {
+	apis, err := ParseApiString("", codec.JSON, nil, nil)
+	if err == nil {
+		t.Errorf("Expected an err from parsing empty API string but got nil")
+	}
+
+	if len(apis) != 0 {
+		t.Errorf("Expected 0 apis from empty API string")
+	}
+
+	apis, err = ParseApiString("eth", codec.JSON, nil, nil)
+	if err != nil {
+		t.Errorf("Expected nil err from parsing empty API string but got %v", err)
+	}
+
+	if len(apis) != 1 {
+		t.Errorf("Expected 1 apis but got %d - %v", apis, apis)
+	}
+
+	apis, err = ParseApiString("eth,eth", codec.JSON, nil, nil)
+	if err != nil {
+		t.Errorf("Expected nil err from parsing empty API string but got \"%v\"", err)
+	}
+
+	if len(apis) != 2 {
+		t.Errorf("Expected 2 apis but got %d - %v", apis, apis)
+	}
+
+	apis, err = ParseApiString("eth,invalid", codec.JSON, nil, nil)
+	if err == nil {
+		t.Errorf("Expected an err but got no err")
+	}
+
+}
diff --git a/rpc/api/eth.go b/rpc/api/eth.go
new file mode 100644
index 000000000..fa14aa41e
--- /dev/null
+++ b/rpc/api/eth.go
@@ -0,0 +1,523 @@
+package api
+
+import (
+	"bytes"
+	"encoding/json"
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/rpc/codec"
+	"github.com/ethereum/go-ethereum/rpc/shared"
+	"github.com/ethereum/go-ethereum/xeth"
+)
+
+// eth api provider
+// See https://github.com/ethereum/wiki/wiki/JSON-RPC
+type EthApi struct {
+	xeth    *xeth.XEth
+	methods map[string]ethhandler
+	codec   codec.ApiCoder
+}
+
+// eth callback handler
+type ethhandler func(*EthApi, *shared.Request) (interface{}, error)
+
+var (
+	ethMapping = map[string]ethhandler{
+		"eth_accounts":                          (*EthApi).Accounts,
+		"eth_blockNumber":                       (*EthApi).BlockNumber,
+		"eth_getBalance":                        (*EthApi).GetBalance,
+		"eth_protocolVersion":                   (*EthApi).ProtocolVersion,
+		"eth_coinbase":                          (*EthApi).Coinbase,
+		"eth_mining":                            (*EthApi).IsMining,
+		"eth_gasPrice":                          (*EthApi).GasPrice,
+		"eth_getStorage":                        (*EthApi).GetStorage,
+		"eth_storageAt":                         (*EthApi).GetStorage,
+		"eth_getStorageAt":                      (*EthApi).GetStorageAt,
+		"eth_getTransactionCount":               (*EthApi).GetTransactionCount,
+		"eth_getBlockTransactionCountByHash":    (*EthApi).GetBlockTransactionCountByHash,
+		"eth_getBlockTransactionCountByNumber":  (*EthApi).GetBlockTransactionCountByNumber,
+		"eth_getUncleCountByBlockHash":          (*EthApi).GetUncleCountByBlockHash,
+		"eth_getUncleCountByBlockNumber":        (*EthApi).GetUncleCountByBlockNumber,
+		"eth_getData":                           (*EthApi).GetData,
+		"eth_getCode":                           (*EthApi).GetData,
+		"eth_sign":                              (*EthApi).Sign,
+		"eth_sendTransaction":                   (*EthApi).SendTransaction,
+		"eth_transact":                          (*EthApi).SendTransaction,
+		"eth_estimateGas":                       (*EthApi).EstimateGas,
+		"eth_call":                              (*EthApi).Call,
+		"eth_flush":                             (*EthApi).Flush,
+		"eth_getBlockByHash":                    (*EthApi).GetBlockByHash,
+		"eth_getBlockByNumber":                  (*EthApi).GetBlockByNumber,
+		"eth_getTransactionByHash":              (*EthApi).GetTransactionByHash,
+		"eth_getTransactionByBlockHashAndIndex": (*EthApi).GetTransactionByBlockHashAndIndex,
+		"eth_getUncleByBlockHashAndIndex":       (*EthApi).GetUncleByBlockHashAndIndex,
+		"eth_getUncleByBlockNumberAndIndex":     (*EthApi).GetUncleByBlockNumberAndIndex,
+		"eth_getCompilers":                      (*EthApi).GetCompilers,
+		"eth_compileSolidity":                   (*EthApi).CompileSolidity,
+		"eth_newFilter":                         (*EthApi).NewFilter,
+		"eth_newBlockFilter":                    (*EthApi).NewBlockFilter,
+		"eth_newPendingTransactionFilter":       (*EthApi).NewPendingTransactionFilter,
+		"eth_uninstallFilter":                   (*EthApi).UninstallFilter,
+		"eth_getFilterChanges":                  (*EthApi).GetFilterChanges,
+		"eth_getFilterLogs":                     (*EthApi).GetFilterLogs,
+		"eth_getLogs":                           (*EthApi).GetLogs,
+		"eth_hashrate":                          (*EthApi).Hashrate,
+		"eth_getWork":                           (*EthApi).GetWork,
+		"eth_submitWork":                        (*EthApi).SubmitWork,
+	}
+)
+
+// create new EthApi instance
+func NewEthApi(xeth *xeth.XEth, codec codec.Codec) *EthApi {
+	return &EthApi{xeth, ethMapping, codec.New(nil)}
+}
+
+// collection with supported methods
+func (self *EthApi) Methods() []string {
+	methods := make([]string, len(self.methods))
+	i := 0
+	for k := range self.methods {
+		methods[i] = k
+		i++
+	}
+	return methods
+}
+
+// Execute given request
+func (self *EthApi) Execute(req *shared.Request) (interface{}, error) {
+	if callback, ok := self.methods[req.Method]; ok {
+		return callback(self, req)
+	}
+
+	return nil, shared.NewNotImplementedError(req.Method)
+}
+
+func (self *EthApi) Accounts(req *shared.Request) (interface{}, error) {
+	return self.xeth.Accounts(), nil
+}
+
+func (self *EthApi) Hashrate(req *shared.Request) (interface{}, error) {
+	return newHexNum(self.xeth.HashRate()), nil
+}
+
+func (self *EthApi) BlockNumber(req *shared.Request) (interface{}, error) {
+	return self.xeth.CurrentBlock().Number(), nil
+}
+
+func (self *EthApi) GetBalance(req *shared.Request) (interface{}, error) {
+	args := new(GetBalanceArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil
+}
+
+func (self *EthApi) ProtocolVersion(req *shared.Request) (interface{}, error) {
+	return self.xeth.EthVersion(), nil
+}
+
+func (self *EthApi) Coinbase(req *shared.Request) (interface{}, error) {
+	return newHexData(self.xeth.Coinbase()), nil
+}
+
+func (self *EthApi) IsMining(req *shared.Request) (interface{}, error) {
+	return self.xeth.IsMining(), nil
+}
+
+func (self *EthApi) GasPrice(req *shared.Request) (interface{}, error) {
+	return newHexNum(xeth.DefaultGasPrice().Bytes()), nil
+}
+
+func (self *EthApi) GetStorage(req *shared.Request) (interface{}, error) {
+	args := new(GetStorageArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil
+}
+
+func (self *EthApi) GetStorageAt(req *shared.Request) (interface{}, error) {
+	args := new(GetStorageAtArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil
+}
+
+func (self *EthApi) GetTransactionCount(req *shared.Request) (interface{}, error) {
+	args := new(GetTxCountArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address)
+	return newHexNum(big.NewInt(int64(count)).Bytes()), nil
+}
+
+func (self *EthApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) {
+	args := new(HashArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := NewBlockRes(self.xeth.EthBlockByHash(args.Hash), false)
+	if block == nil {
+		return nil, nil
+	} else {
+		return newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes()), nil
+	}
+}
+
+func (self *EthApi) GetBlockTransactionCountByNumber(req *shared.Request) (interface{}, error) {
+	args := new(BlockNumArg)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := NewBlockRes(self.xeth.EthBlockByNumber(args.BlockNumber), false)
+	if block == nil {
+		return nil, nil
+	} else {
+		return newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes()), nil
+	}
+}
+
+func (self *EthApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) {
+	args := new(HashArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := self.xeth.EthBlockByHash(args.Hash)
+	br := NewBlockRes(block, false)
+	if br == nil {
+		return nil, nil
+	}
+	return newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()), nil
+}
+
+func (self *EthApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}, error) {
+	args := new(BlockNumArg)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := self.xeth.EthBlockByNumber(args.BlockNumber)
+	br := NewBlockRes(block, false)
+	if br == nil {
+		return nil, nil
+	}
+	return newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()), nil
+}
+
+func (self *EthApi) GetData(req *shared.Request) (interface{}, error) {
+	args := new(GetDataArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+	v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
+	return newHexData(v), nil
+}
+
+func (self *EthApi) Sign(req *shared.Request) (interface{}, error) {
+	args := new(NewSignArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+	v, err := self.xeth.Sign(args.From, args.Data, false)
+	if err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+func (self *EthApi) SendTransaction(req *shared.Request) (interface{}, error) {
+	args := new(NewTxArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	// nonce may be nil ("guess" mode)
+	var nonce string
+	if args.Nonce != nil {
+		nonce = args.Nonce.String()
+	}
+
+	v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+	if err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+func (self *EthApi) EstimateGas(req *shared.Request) (interface{}, error) {
+	_, gas, err := self.doCall(req.Params)
+	if err != nil {
+		return nil, err
+	}
+
+	// TODO unwrap the parent method's ToHex call
+	if len(gas) == 0 {
+		return newHexNum(0), nil
+	} else {
+		return newHexNum(gas), nil
+	}
+}
+
+func (self *EthApi) Call(req *shared.Request) (interface{}, error) {
+	v, _, err := self.doCall(req.Params)
+	if err != nil {
+		return nil, err
+	}
+
+	// TODO unwrap the parent method's ToHex call
+	if v == "0x0" {
+		return newHexData([]byte{}), nil
+	} else {
+		return newHexData(common.FromHex(v)), nil
+	}
+}
+
+func (self *EthApi) Flush(req *shared.Request) (interface{}, error) {
+	return nil, shared.NewNotImplementedError(req.Method)
+}
+
+func (self *EthApi) doCall(params json.RawMessage) (string, string, error) {
+	args := new(CallArgs)
+	if err := self.codec.Decode(params, &args); err != nil {
+		return "", "", err
+	}
+
+	return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+}
+
+func (self *EthApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
+	args := new(GetBlockByHashArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := self.xeth.EthBlockByHash(args.BlockHash)
+	return NewBlockRes(block, args.IncludeTxs), nil
+}
+
+func (self *EthApi) GetBlockByNumber(req *shared.Request) (interface{}, error) {
+	args := new(GetBlockByNumberArgs)
+	if err := json.Unmarshal(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := self.xeth.EthBlockByNumber(args.BlockNumber)
+	br := NewBlockRes(block, args.IncludeTxs)
+	// If request was for "pending", nil nonsensical fields
+	if args.BlockNumber == -2 {
+		br.BlockHash = nil
+		br.BlockNumber = nil
+		br.Miner = nil
+		br.Nonce = nil
+		br.LogsBloom = nil
+	}
+	return br, nil
+}
+
+func (self *EthApi) GetTransactionByHash(req *shared.Request) (interface{}, error) {
+	args := new(HashArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash)
+	if tx != nil {
+		v := NewTransactionRes(tx)
+		// if the blockhash is 0, assume this is a pending transaction
+		if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 {
+			v.BlockHash = newHexData(bhash)
+			v.BlockNumber = newHexNum(bnum)
+			v.TxIndex = newHexNum(txi)
+		}
+		return v, nil
+	}
+	return nil, nil
+}
+
+func (self *EthApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) {
+	args := new(HashIndexArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := self.xeth.EthBlockByHash(args.Hash)
+	br := NewBlockRes(block, true)
+	if br == nil {
+		return nil, nil
+	}
+
+	if args.Index >= int64(len(br.Transactions)) || args.Index < 0 {
+		return nil, nil
+	} else {
+		return br.Transactions[args.Index], nil
+	}
+}
+
+func (self *EthApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) {
+	args := new(BlockNumIndexArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := self.xeth.EthBlockByNumber(args.BlockNumber)
+	v := NewBlockRes(block, true)
+	if v == nil {
+		return nil, nil
+	}
+
+	if args.Index >= int64(len(v.Transactions)) || args.Index < 0 {
+		// return NewValidationError("Index", "does not exist")
+		return nil, nil
+	}
+	return v.Transactions[args.Index], nil
+}
+
+func (self *EthApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) {
+	args := new(HashIndexArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	br := NewBlockRes(self.xeth.EthBlockByHash(args.Hash), false)
+	if br == nil {
+		return nil, nil
+	}
+
+	if args.Index >= int64(len(br.Uncles)) || args.Index < 0 {
+		// return NewValidationError("Index", "does not exist")
+		return nil, nil
+	}
+
+	return br.Uncles[args.Index], nil
+}
+
+func (self *EthApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) {
+	args := new(BlockNumIndexArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	block := self.xeth.EthBlockByNumber(args.BlockNumber)
+	v := NewBlockRes(block, true)
+
+	if v == nil {
+		return nil, nil
+	}
+
+	if args.Index >= int64(len(v.Uncles)) || args.Index < 0 {
+		return nil, nil
+	} else {
+		return v.Uncles[args.Index], nil
+	}
+}
+
+func (self *EthApi) GetCompilers(req *shared.Request) (interface{}, error) {
+	var lang string
+	if solc, _ := self.xeth.Solc(); solc != nil {
+		lang = "Solidity"
+	}
+	c := []string{lang}
+	return c, nil
+}
+
+func (self *EthApi) CompileSolidity(req *shared.Request) (interface{}, error) {
+	solc, _ := self.xeth.Solc()
+	if solc == nil {
+		return nil, shared.NewNotAvailableError(req.Method, "solc (solidity compiler) not found")
+	}
+
+	args := new(SourceArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	contracts, err := solc.Compile(args.Source)
+	if err != nil {
+		return nil, err
+	}
+	return contracts, nil
+}
+
+func (self *EthApi) NewFilter(req *shared.Request) (interface{}, error) {
+	args := new(BlockFilterArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	id := self.xeth.NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)
+	return newHexNum(big.NewInt(int64(id)).Bytes()), nil
+}
+
+func (self *EthApi) NewBlockFilter(req *shared.Request) (interface{}, error) {
+	return newHexNum(self.xeth.NewBlockFilter()), nil
+}
+
+func (self *EthApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) {
+	return newHexNum(self.xeth.NewTransactionFilter()), nil
+}
+
+func (self *EthApi) UninstallFilter(req *shared.Request) (interface{}, error) {
+	args := new(FilterIdArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+	return self.xeth.UninstallFilter(args.Id), nil
+}
+
+func (self *EthApi) GetFilterChanges(req *shared.Request) (interface{}, error) {
+	args := new(FilterIdArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	switch self.xeth.GetFilterType(args.Id) {
+	case xeth.BlockFilterTy:
+		return NewHashesRes(self.xeth.BlockFilterChanged(args.Id)), nil
+	case xeth.TransactionFilterTy:
+		return NewHashesRes(self.xeth.TransactionFilterChanged(args.Id)), nil
+	case xeth.LogFilterTy:
+		return NewLogsRes(self.xeth.LogFilterChanged(args.Id)), nil
+	default:
+		return []string{}, nil // reply empty string slice
+	}
+}
+
+func (self *EthApi) GetFilterLogs(req *shared.Request) (interface{}, error) {
+	args := new(FilterIdArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	return NewLogsRes(self.xeth.Logs(args.Id)), nil
+}
+
+func (self *EthApi) GetLogs(req *shared.Request) (interface{}, error) {
+	args := new(BlockFilterArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+	return NewLogsRes(self.xeth.AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)), nil
+}
+
+func (self *EthApi) GetWork(req *shared.Request) (interface{}, error) {
+	self.xeth.SetMining(true, 0)
+	return self.xeth.RemoteMining().GetWork(), nil
+}
+
+func (self *EthApi) SubmitWork(req *shared.Request) (interface{}, error) {
+	args := new(SubmitWorkArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+	return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil
+}
diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go
new file mode 100644
index 000000000..1ef6f9efb
--- /dev/null
+++ b/rpc/api/eth_args.go
@@ -0,0 +1,835 @@
+package api
+
+import (
+	"encoding/json"
+	"fmt"
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+const (
+	defaultLogLimit  = 100
+	defaultLogOffset = 0
+)
+
+type GetBalanceArgs struct {
+	Address     string
+	BlockNumber int64
+}
+
+func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	addstr, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("address", "not a string")
+	}
+	args.Address = addstr
+
+	if len(obj) > 1 {
+		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
+			return err
+		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	return nil
+}
+
+type GetStorageArgs struct {
+	Address     string
+	BlockNumber int64
+}
+
+func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	addstr, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("address", "not a string")
+	}
+	args.Address = addstr
+
+	if len(obj) > 1 {
+		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
+			return err
+		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	return nil
+}
+
+type GetStorageAtArgs struct {
+	Address     string
+	BlockNumber int64
+	Key         string
+}
+
+func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 2 {
+		return shared.NewInsufficientParamsError(len(obj), 2)
+	}
+
+	addstr, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("address", "not a string")
+	}
+	args.Address = addstr
+
+	keystr, ok := obj[1].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("key", "not a string")
+	}
+	args.Key = keystr
+
+	if len(obj) > 2 {
+		if err := blockHeight(obj[2], &args.BlockNumber); err != nil {
+			return err
+		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	return nil
+}
+
+type GetTxCountArgs struct {
+	Address     string
+	BlockNumber int64
+}
+
+func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	addstr, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("address", "not a string")
+	}
+	args.Address = addstr
+
+	if len(obj) > 1 {
+		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
+			return err
+		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	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 shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	arg0, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("hash", "not a string")
+	}
+	args.Hash = arg0
+
+	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 shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+type GetDataArgs struct {
+	Address     string
+	BlockNumber int64
+}
+
+func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	addstr, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("address", "not a string")
+	}
+	args.Address = addstr
+
+	if len(obj) > 1 {
+		if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
+			return err
+		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	return nil
+}
+
+type NewSignArgs struct {
+	From string
+	Data string
+}
+
+func (args *NewSignArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []json.RawMessage
+	var ext struct {
+		From string
+		Data string
+	}
+
+	// Decode byte slice to array of RawMessages
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	// Check for sufficient params
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	// Decode 0th RawMessage to temporary struct
+	if err := json.Unmarshal(obj[0], &ext); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(ext.From) == 0 {
+		return shared.NewValidationError("from", "is required")
+	}
+
+	if len(ext.Data) == 0 {
+		return shared.NewValidationError("data", "is required")
+	}
+
+	args.From = ext.From
+	args.Data = ext.Data
+	return nil
+}
+
+type NewTxArgs struct {
+	From     string
+	To       string
+	Nonce    *big.Int
+	Value    *big.Int
+	Gas      *big.Int
+	GasPrice *big.Int
+	Data     string
+
+	BlockNumber int64
+}
+
+func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []json.RawMessage
+	var ext struct {
+		From     string
+		To       string
+		Nonce    interface{}
+		Value    interface{}
+		Gas      interface{}
+		GasPrice interface{}
+		Data     string
+	}
+
+	// Decode byte slice to array of RawMessages
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	// Check for sufficient params
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	// Decode 0th RawMessage to temporary struct
+	if err := json.Unmarshal(obj[0], &ext); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(ext.From) == 0 {
+		return shared.NewValidationError("from", "is required")
+	}
+
+	args.From = ext.From
+	args.To = ext.To
+	args.Data = ext.Data
+
+	var num *big.Int
+	if ext.Nonce != nil {
+		num, err = numString(ext.Nonce)
+		if err != nil {
+			return err
+		}
+	}
+	args.Nonce = num
+
+	if ext.Value == nil {
+		num = big.NewInt(0)
+	} else {
+		num, err = numString(ext.Value)
+		if err != nil {
+			return err
+		}
+	}
+	args.Value = num
+
+	num = nil
+	if ext.Gas == nil {
+		num = big.NewInt(0)
+	} else {
+		if num, err = numString(ext.Gas); err != nil {
+			return err
+		}
+	}
+	args.Gas = num
+
+	num = nil
+	if ext.GasPrice == nil {
+		num = big.NewInt(0)
+	} else {
+		if num, err = numString(ext.GasPrice); err != nil {
+			return err
+		}
+	}
+	args.GasPrice = num
+
+	// Check for optional BlockNumber param
+	if len(obj) > 1 {
+		if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
+			return err
+		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	return nil
+}
+
+type SourceArgs struct {
+	Source string
+}
+
+func (args *SourceArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	arg0, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("source code", "not a string")
+	}
+	args.Source = arg0
+
+	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 shared.NewDecodeParamError(err.Error())
+	}
+
+	// Check for sufficient params
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	// Decode 0th RawMessage to temporary struct
+	if err := json.Unmarshal(obj[0], &ext); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	args.From = ext.From
+
+	if len(ext.To) == 0 {
+		return shared.NewValidationError("to", "is required")
+	}
+	args.To = ext.To
+
+	var num *big.Int
+	if ext.Value == nil {
+		num = big.NewInt(0)
+	} else {
+		if num, err = numString(ext.Value); err != nil {
+			return err
+		}
+	}
+	args.Value = num
+
+	if ext.Gas == nil {
+		num = big.NewInt(0)
+	} else {
+		if num, err = numString(ext.Gas); err != nil {
+			return err
+		}
+	}
+	args.Gas = num
+
+	if ext.GasPrice == nil {
+		num = big.NewInt(0)
+	} else {
+		if num, err = numString(ext.GasPrice); err != nil {
+			return err
+		}
+	}
+	args.GasPrice = 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
+		}
+	} else {
+		args.BlockNumber = -1
+	}
+
+	return nil
+}
+
+type HashIndexArgs struct {
+	Hash  string
+	Index int64
+}
+
+func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 2 {
+		return shared.NewInsufficientParamsError(len(obj), 2)
+	}
+
+	arg0, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("hash", "not a string")
+	}
+	args.Hash = arg0
+
+	arg1, ok := obj[1].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("index", "not a string")
+	}
+	args.Index = common.Big(arg1).Int64()
+
+	return nil
+}
+
+type BlockNumIndexArgs struct {
+	BlockNumber int64
+	Index       int64
+}
+
+func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 2 {
+		return shared.NewInsufficientParamsError(len(obj), 2)
+	}
+
+	if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
+		return err
+	}
+
+	var arg1 *big.Int
+	if arg1, err = numString(obj[1]); err != nil {
+		return err
+	}
+	args.Index = arg1.Int64()
+
+	return nil
+}
+
+type GetBlockByHashArgs struct {
+	BlockHash  string
+	IncludeTxs bool
+}
+
+func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 2 {
+		return shared.NewInsufficientParamsError(len(obj), 2)
+	}
+
+	argstr, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("blockHash", "not a string")
+	}
+	args.BlockHash = argstr
+
+	args.IncludeTxs = obj[1].(bool)
+
+	return nil
+}
+
+type GetBlockByNumberArgs struct {
+	BlockNumber int64
+	IncludeTxs  bool
+}
+
+func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 2 {
+		return shared.NewInsufficientParamsError(len(obj), 2)
+	}
+
+	if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
+		return err
+	}
+
+	args.IncludeTxs = obj[1].(bool)
+
+	return nil
+}
+
+type BlockFilterArgs struct {
+	Earliest int64
+	Latest   int64
+	Address  []string
+	Topics   [][]string
+	Skip     int
+	Max      int
+}
+
+func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []struct {
+		FromBlock interface{} `json:"fromBlock"`
+		ToBlock   interface{} `json:"toBlock"`
+		Limit     interface{} `json:"limit"`
+		Offset    interface{} `json:"offset"`
+		Address   interface{} `json:"address"`
+		Topics    interface{} `json:"topics"`
+	}
+
+	if err = json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	// args.Earliest, err = toNumber(obj[0].ToBlock)
+	// if err != nil {
+	// 	return shared.NewDecodeParamError(fmt.Sprintf("FromBlock %v", err))
+	// }
+	// args.Latest, err = toNumber(obj[0].FromBlock)
+	// if err != nil {
+	// 	return shared.NewDecodeParamError(fmt.Sprintf("ToBlock %v", err))
+
+	var num int64
+	var numBig *big.Int
+
+	// if blank then latest
+	if obj[0].FromBlock == nil {
+		num = -1
+	} else {
+		if err := blockHeight(obj[0].FromBlock, &num); err != nil {
+			return err
+		}
+	}
+	// if -2 or other "silly" number, use latest
+	if num < 0 {
+		args.Earliest = -1 //latest block
+	} else {
+		args.Earliest = num
+	}
+
+	// if blank than latest
+	if obj[0].ToBlock == nil {
+		num = -1
+	} else {
+		if err := blockHeight(obj[0].ToBlock, &num); err != nil {
+			return err
+		}
+	}
+	args.Latest = num
+
+	if obj[0].Limit == nil {
+		numBig = big.NewInt(defaultLogLimit)
+	} else {
+		if numBig, err = numString(obj[0].Limit); err != nil {
+			return err
+		}
+	}
+	args.Max = int(numBig.Int64())
+
+	if obj[0].Offset == nil {
+		numBig = big.NewInt(defaultLogOffset)
+	} else {
+		if numBig, err = numString(obj[0].Offset); err != nil {
+			return err
+		}
+	}
+	args.Skip = int(numBig.Int64())
+
+	if obj[0].Address != nil {
+		marg, ok := obj[0].Address.([]interface{})
+		if ok {
+			v := make([]string, len(marg))
+			for i, arg := range marg {
+				argstr, ok := arg.(string)
+				if !ok {
+					return shared.NewInvalidTypeError(fmt.Sprintf("address[%d]", i), "is not a string")
+				}
+				v[i] = argstr
+			}
+			args.Address = v
+		} else {
+			argstr, ok := obj[0].Address.(string)
+			if ok {
+				v := make([]string, 1)
+				v[0] = argstr
+				args.Address = v
+			} else {
+				return shared.NewInvalidTypeError("address", "is not a string or array")
+			}
+		}
+	}
+
+	if obj[0].Topics != nil {
+		other, ok := obj[0].Topics.([]interface{})
+		if ok {
+			topicdbl := make([][]string, len(other))
+			for i, iv := range other {
+				if argstr, ok := iv.(string); ok {
+					// Found a string, push into first element of array
+					topicsgl := make([]string, 1)
+					topicsgl[0] = argstr
+					topicdbl[i] = topicsgl
+				} else if argarray, ok := iv.([]interface{}); ok {
+					// Found an array of other
+					topicdbl[i] = make([]string, len(argarray))
+					for j, jv := range argarray {
+						if v, ok := jv.(string); ok {
+							topicdbl[i][j] = v
+						} else if jv == nil {
+							topicdbl[i][j] = ""
+						} else {
+							return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string")
+						}
+					}
+				} else if iv == nil {
+					topicdbl[i] = []string{""}
+				} else {
+					return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array")
+				}
+			}
+			args.Topics = topicdbl
+			return nil
+		} else {
+			return shared.NewInvalidTypeError("topic", "is not a string or array")
+		}
+	}
+
+	return nil
+}
+
+type FilterIdArgs struct {
+	Id int
+}
+
+func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	var num *big.Int
+	if num, err = numString(obj[0]); err != nil {
+		return err
+	}
+	args.Id = int(num.Int64())
+
+	return nil
+}
+
+type LogRes struct {
+	Address          *hexdata   `json:"address"`
+	Topics           []*hexdata `json:"topics"`
+	Data             *hexdata   `json:"data"`
+	BlockNumber      *hexnum    `json:"blockNumber"`
+	LogIndex         *hexnum    `json:"logIndex"`
+	BlockHash        *hexdata   `json:"blockHash"`
+	TransactionHash  *hexdata   `json:"transactionHash"`
+	TransactionIndex *hexnum    `json:"transactionIndex"`
+}
+
+func NewLogRes(log *state.Log) LogRes {
+	var l LogRes
+	l.Topics = make([]*hexdata, len(log.Topics))
+	for j, topic := range log.Topics {
+		l.Topics[j] = newHexData(topic)
+	}
+	l.Address = newHexData(log.Address)
+	l.Data = newHexData(log.Data)
+	l.BlockNumber = newHexNum(log.Number)
+	l.LogIndex = newHexNum(log.Index)
+	l.TransactionHash = newHexData(log.TxHash)
+	l.TransactionIndex = newHexNum(log.TxIndex)
+	l.BlockHash = newHexData(log.BlockHash)
+
+	return l
+}
+
+func NewLogsRes(logs state.Logs) (ls []LogRes) {
+	ls = make([]LogRes, len(logs))
+
+	for i, log := range logs {
+		ls[i] = NewLogRes(log)
+	}
+
+	return
+}
+
+func NewHashesRes(hs []common.Hash) []string {
+	hashes := make([]string, len(hs))
+
+	for i, hash := range hs {
+		hashes[i] = hash.Hex()
+	}
+
+	return hashes
+}
+
+type SubmitWorkArgs struct {
+	Nonce  uint64
+	Header string
+	Digest string
+}
+
+func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err = json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 3 {
+		return shared.NewInsufficientParamsError(len(obj), 3)
+	}
+
+	var objstr string
+	var ok bool
+	if objstr, ok = obj[0].(string); !ok {
+		return shared.NewInvalidTypeError("nonce", "not a string")
+	}
+
+	args.Nonce = common.String2Big(objstr).Uint64()
+	if objstr, ok = obj[1].(string); !ok {
+		return shared.NewInvalidTypeError("header", "not a string")
+	}
+
+	args.Header = objstr
+
+	if objstr, ok = obj[2].(string); !ok {
+		return shared.NewInvalidTypeError("digest", "not a string")
+	}
+
+	args.Digest = objstr
+
+	return nil
+}
diff --git a/rpc/api/eth_js.go b/rpc/api/eth_js.go
new file mode 100644
index 000000000..f7630bdd5
--- /dev/null
+++ b/rpc/api/eth_js.go
@@ -0,0 +1,3 @@
+package api
+
+// JS api provided by web3.js
diff --git a/rpc/api/parsing.go b/rpc/api/parsing.go
new file mode 100644
index 000000000..85a9165e5
--- /dev/null
+++ b/rpc/api/parsing.go
@@ -0,0 +1,460 @@
+package api
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"encoding/json"
+	"math/big"
+	"strings"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+type hexdata struct {
+	data  []byte
+	isNil bool
+}
+
+func (d *hexdata) String() string {
+	return "0x" + common.Bytes2Hex(d.data)
+}
+
+func (d *hexdata) MarshalJSON() ([]byte, error) {
+	if d.isNil {
+		return json.Marshal(nil)
+	}
+	return json.Marshal(d.String())
+}
+
+func newHexData(input interface{}) *hexdata {
+	d := new(hexdata)
+
+	if input == nil {
+		d.isNil = true
+		return d
+	}
+	switch input := input.(type) {
+	case []byte:
+		d.data = input
+	case common.Hash:
+		d.data = input.Bytes()
+	case *common.Hash:
+		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:
+		if input == nil {
+			d.isNil = true
+		} else {
+			d.data = input.Bytes()
+		}
+	case int64:
+		d.data = big.NewInt(input).Bytes()
+	case uint64:
+		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, 2)
+		binary.BigEndian.PutUint16(buff, input)
+		d.data = buff
+	case int32:
+		d.data = big.NewInt(int64(input)).Bytes()
+	case uint32:
+		buff := make([]byte, 4)
+		binary.BigEndian.PutUint32(buff, input)
+		d.data = buff
+	case string: // hexstring
+		// aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded
+		bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
+		if err != nil {
+			d.isNil = true
+		} else {
+			d.data = bytes
+		}
+	default:
+		d.isNil = true
+	}
+
+	return d
+}
+
+type hexnum struct {
+	data  []byte
+	isNil bool
+}
+
+func (d *hexnum) String() string {
+	// Get hex string from bytes
+	out := common.Bytes2Hex(d.data)
+	// Trim leading 0s
+	out = strings.TrimLeft(out, "0")
+	// Output "0x0" when value is 0
+	if len(out) == 0 {
+		out = "0"
+	}
+	return "0x" + out
+}
+
+func (d *hexnum) MarshalJSON() ([]byte, error) {
+	if d.isNil {
+		return json.Marshal(nil)
+	}
+	return json.Marshal(d.String())
+}
+
+func newHexNum(input interface{}) *hexnum {
+	d := new(hexnum)
+
+	d.data = newHexData(input).data
+
+	return d
+}
+
+type BlockRes struct {
+	fullTx bool
+
+	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"`
+	GasUsed         *hexnum           `json:"gasUsed"`
+	UnixTimestamp   *hexnum           `json:"timestamp"`
+	Transactions    []*TransactionRes `json:"transactions"`
+	Uncles          []*UncleRes       `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"`
+			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.GasUsed = b.GasUsed
+		ext.UnixTimestamp = b.UnixTimestamp
+		ext.Transactions = b.Transactions
+		ext.Uncles = make([]*hexdata, len(b.Uncles))
+		for i, u := range b.Uncles {
+			ext.Uncles[i] = u.BlockHash
+		}
+		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"`
+			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.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 = make([]*hexdata, len(b.Uncles))
+		for i, u := range b.Uncles {
+			ext.Uncles[i] = u.BlockHash
+		}
+		return json.Marshal(ext)
+	}
+}
+
+func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
+	if block == nil {
+		return nil
+	}
+
+	res := new(BlockRes)
+	res.fullTx = fullTx
+	res.BlockNumber = newHexNum(block.Number())
+	res.BlockHash = newHexData(block.Hash())
+	res.ParentHash = newHexData(block.ParentHash())
+	res.Nonce = newHexData(block.Nonce())
+	res.Sha3Uncles = newHexData(block.Header().UncleHash)
+	res.LogsBloom = newHexData(block.Bloom())
+	res.TransactionRoot = newHexData(block.Header().TxHash)
+	res.StateRoot = newHexData(block.Root())
+	res.Miner = newHexData(block.Header().Coinbase)
+	res.Difficulty = newHexNum(block.Difficulty())
+	res.TotalDifficulty = newHexNum(block.Td)
+	res.Size = newHexNum(block.Size().Int64())
+	res.ExtraData = newHexData(block.Header().Extra)
+	res.GasLimit = newHexNum(block.GasLimit())
+	res.GasUsed = newHexNum(block.GasUsed())
+	res.UnixTimestamp = newHexNum(block.Time())
+
+	res.Transactions = make([]*TransactionRes, len(block.Transactions()))
+	for i, tx := range block.Transactions() {
+		res.Transactions[i] = NewTransactionRes(tx)
+		res.Transactions[i].BlockHash = res.BlockHash
+		res.Transactions[i].BlockNumber = res.BlockNumber
+		res.Transactions[i].TxIndex = newHexNum(i)
+	}
+
+	res.Uncles = make([]*UncleRes, len(block.Uncles()))
+	for i, uncle := range block.Uncles() {
+		res.Uncles[i] = NewUncleRes(uncle)
+	}
+
+	return res
+}
+
+type TransactionRes struct {
+	Hash        *hexdata `json:"hash"`
+	Nonce       *hexnum  `json:"nonce"`
+	BlockHash   *hexdata `json:"blockHash"`
+	BlockNumber *hexnum  `json:"blockNumber"`
+	TxIndex     *hexnum  `json:"transactionIndex"`
+	From        *hexdata `json:"from"`
+	To          *hexdata `json:"to"`
+	Value       *hexnum  `json:"value"`
+	Gas         *hexnum  `json:"gas"`
+	GasPrice    *hexnum  `json:"gasPrice"`
+	Input       *hexdata `json:"input"`
+}
+
+func NewTransactionRes(tx *types.Transaction) *TransactionRes {
+	if tx == nil {
+		return nil
+	}
+
+	var v = new(TransactionRes)
+	v.Hash = newHexData(tx.Hash())
+	v.Nonce = newHexNum(tx.Nonce())
+	// v.BlockHash =
+	// v.BlockNumber =
+	// v.TxIndex =
+	from, _ := tx.From()
+	v.From = newHexData(from)
+	v.To = newHexData(tx.To())
+	v.Value = newHexNum(tx.Value())
+	v.Gas = newHexNum(tx.Gas())
+	v.GasPrice = newHexNum(tx.GasPrice())
+	v.Input = newHexData(tx.Data())
+	return v
+}
+
+type UncleRes struct {
+	BlockNumber     *hexnum  `json:"number"`
+	BlockHash       *hexdata `json:"hash"`
+	ParentHash      *hexdata `json:"parentHash"`
+	Nonce           *hexdata `json:"nonce"`
+	Sha3Uncles      *hexdata `json:"sha3Uncles"`
+	ReceiptHash     *hexdata `json:"receiptHash"`
+	LogsBloom       *hexdata `json:"logsBloom"`
+	TransactionRoot *hexdata `json:"transactionsRoot"`
+	StateRoot       *hexdata `json:"stateRoot"`
+	Miner           *hexdata `json:"miner"`
+	Difficulty      *hexnum  `json:"difficulty"`
+	ExtraData       *hexdata `json:"extraData"`
+	GasLimit        *hexnum  `json:"gasLimit"`
+	GasUsed         *hexnum  `json:"gasUsed"`
+	UnixTimestamp   *hexnum  `json:"timestamp"`
+}
+
+func NewUncleRes(h *types.Header) *UncleRes {
+	if h == nil {
+		return nil
+	}
+
+	var v = new(UncleRes)
+	v.BlockNumber = newHexNum(h.Number)
+	v.BlockHash = newHexData(h.Hash())
+	v.ParentHash = newHexData(h.ParentHash)
+	v.Sha3Uncles = newHexData(h.UncleHash)
+	v.Nonce = newHexData(h.Nonce[:])
+	v.LogsBloom = newHexData(h.Bloom)
+	v.TransactionRoot = newHexData(h.TxHash)
+	v.StateRoot = newHexData(h.Root)
+	v.Miner = newHexData(h.Coinbase)
+	v.Difficulty = newHexNum(h.Difficulty)
+	v.ExtraData = newHexData(h.Extra)
+	v.GasLimit = newHexNum(h.GasLimit)
+	v.GasUsed = newHexNum(h.GasUsed)
+	v.UnixTimestamp = newHexNum(h.Time)
+	v.ReceiptHash = newHexData(h.ReceiptHash)
+
+	return v
+}
+
+// type FilterLogRes struct {
+// 	Hash             string `json:"hash"`
+// 	Address          string `json:"address"`
+// 	Data             string `json:"data"`
+// 	BlockNumber      string `json:"blockNumber"`
+// 	TransactionHash  string `json:"transactionHash"`
+// 	BlockHash        string `json:"blockHash"`
+// 	TransactionIndex string `json:"transactionIndex"`
+// 	LogIndex         string `json:"logIndex"`
+// }
+
+// type FilterWhisperRes struct {
+// 	Hash       string `json:"hash"`
+// 	From       string `json:"from"`
+// 	To         string `json:"to"`
+// 	Expiry     string `json:"expiry"`
+// 	Sent       string `json:"sent"`
+// 	Ttl        string `json:"ttl"`
+// 	Topics     string `json:"topics"`
+// 	Payload    string `json:"payload"`
+// 	WorkProved string `json:"workProved"`
+// }
+
+func numString(raw interface{}) (*big.Int, error) {
+	var number *big.Int
+	// Parse as integer
+	num, ok := raw.(float64)
+	if ok {
+		number = big.NewInt(int64(num))
+		return number, nil
+	}
+
+	// Parse as string/hexstring
+	str, ok := raw.(string)
+	if ok {
+		number = common.String2Big(str)
+		return number, nil
+	}
+
+	return nil, shared.NewInvalidTypeError("", "not a number or string")
+}
+
+func blockHeight(raw interface{}, number *int64) error {
+	// Parse as integer
+	num, ok := raw.(float64)
+	if ok {
+		*number = int64(num)
+		return nil
+	}
+
+	// Parse as string/hexstring
+	str, ok := raw.(string)
+	if !ok {
+		return shared.NewInvalidTypeError("", "not a number or string")
+	}
+
+	switch str {
+	case "earliest":
+		*number = 0
+	case "latest":
+		*number = -1
+	case "pending":
+		*number = -2
+	default:
+		if common.HasHexPrefix(str) {
+			*number = common.String2Big(str).Int64()
+		} else {
+			return shared.NewInvalidTypeError("blockNumber", "is not a valid string")
+		}
+	}
+
+	return nil
+}
+
+func blockHeightFromJson(msg json.RawMessage, number *int64) error {
+	var raw interface{}
+	if err := json.Unmarshal(msg, &raw); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+	return blockHeight(raw, number)
+}
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
new file mode 100644
index 000000000..76f00c251
--- /dev/null
+++ b/rpc/api/utils.go
@@ -0,0 +1,36 @@
+package api
+
+import (
+	"strings"
+
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/eth"
+	"github.com/ethereum/go-ethereum/rpc/codec"
+	"github.com/ethereum/go-ethereum/xeth"
+)
+
+const (
+	EthApiName = "eth"
+)
+
+// Parse a comma separated API string to individual api's
+func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.Ethereum) ([]EthereumApi, error) {
+	if len(strings.TrimSpace(apistr)) == 0 {
+		return nil, fmt.Errorf("Empty apistr provided")
+	}
+
+	names := strings.Split(apistr, ",")
+	apis := make([]EthereumApi, len(names))
+
+	for i, name := range names {
+		switch strings.ToLower(strings.TrimSpace(name)) {
+		case EthApiName:
+			apis[i] = NewEthApi(xeth, codec)
+		default:
+			return nil, fmt.Errorf("Unknown API '%s'", name)
+		}
+	}
+
+	return apis, nil
+}
diff --git a/rpc/codec/codec.go b/rpc/codec/codec.go
new file mode 100644
index 000000000..5e8f38438
--- /dev/null
+++ b/rpc/codec/codec.go
@@ -0,0 +1,47 @@
+package codec
+
+import (
+	"net"
+	"strconv"
+
+	"github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+type Codec int
+
+// (de)serialization support for rpc interface
+type ApiCoder interface {
+	// Parse message to request from underlying stream
+	ReadRequest() (*shared.Request, error)
+	// Parse response message from underlying stream
+	ReadResponse() (interface{}, error)
+	// Encode response to encoded form in underlying stream
+	WriteResponse(interface{}) error
+	// Decode single message from data
+	Decode([]byte, interface{}) error
+	// Encode msg to encoded form
+	Encode(msg interface{}) ([]byte, error)
+	// close the underlying stream
+	Close()
+}
+
+// supported codecs
+const (
+	JSON Codec = iota
+	nCodecs
+)
+
+var (
+	// collection with supported coders
+	coders = make([]func(net.Conn) ApiCoder, nCodecs)
+)
+
+// create a new coder instance
+func (c Codec) New(conn net.Conn) ApiCoder {
+	switch c {
+	case JSON:
+		return NewJsonCoder(conn)
+	}
+
+	panic("codec: request for codec #" + strconv.Itoa(int(c)) + " is unavailable")
+}
diff --git a/rpc/codec/json.go b/rpc/codec/json.go
new file mode 100644
index 000000000..31024ee74
--- /dev/null
+++ b/rpc/codec/json.go
@@ -0,0 +1,75 @@
+package codec
+
+import (
+	"encoding/json"
+	"net"
+
+	"github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+const (
+	MAX_RESPONSE_SIZE = 64 * 1024
+)
+
+// Json serialization support
+type JsonCodec struct {
+	c net.Conn
+	d *json.Decoder
+	e *json.Encoder
+}
+
+// Create new JSON coder instance
+func NewJsonCoder(conn net.Conn) ApiCoder {
+	return &JsonCodec{
+		c: conn,
+		d: json.NewDecoder(conn),
+		e: json.NewEncoder(conn),
+	}
+}
+
+// Serialize obj to JSON and write it to conn
+func (self *JsonCodec) ReadRequest() (*shared.Request, error) {
+	req := shared.Request{}
+	err := self.d.Decode(&req)
+	if err == nil {
+		return &req, nil
+	}
+	return nil, err
+}
+
+func (self *JsonCodec) ReadResponse() (interface{}, error) {
+	var err error
+	buf := make([]byte, MAX_RESPONSE_SIZE)
+	n, _ := self.c.Read(buf)
+
+	var failure shared.ErrorResponse
+	if err = json.Unmarshal(buf[:n], &failure); err == nil && failure.Error != nil {
+		return failure, nil
+	}
+
+	var success shared.SuccessResponse
+	if err = json.Unmarshal(buf[:n], &success); err == nil {
+		return success, nil
+	}
+
+	return nil, err
+}
+
+// Encode response to encoded form in underlying stream
+func (self *JsonCodec) Decode(data []byte, msg interface{}) error {
+	return json.Unmarshal(data, msg)
+}
+
+func (self *JsonCodec) Encode(msg interface{}) ([]byte, error) {
+	return json.Marshal(msg)
+}
+
+// Parse JSON data from conn to obj
+func (self *JsonCodec) WriteResponse(res interface{}) error {
+	return self.e.Encode(&res)
+}
+
+// Close decoder and encoder
+func (self *JsonCodec) Close() {
+	self.c.Close()
+}
diff --git a/rpc/shared/errors.go b/rpc/shared/errors.go
new file mode 100644
index 000000000..bd10b33a0
--- /dev/null
+++ b/rpc/shared/errors.go
@@ -0,0 +1,96 @@
+package shared
+
+import "fmt"
+
+type InvalidTypeError struct {
+	method string
+	msg    string
+}
+
+func (e *InvalidTypeError) Error() string {
+	return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg)
+}
+
+func NewInvalidTypeError(method, msg string) *InvalidTypeError {
+	return &InvalidTypeError{
+		method: method,
+		msg:    msg,
+	}
+}
+
+type InsufficientParamsError struct {
+	have int
+	want int
+}
+
+func (e *InsufficientParamsError) Error() string {
+	return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have)
+}
+
+func NewInsufficientParamsError(have int, want int) *InsufficientParamsError {
+	return &InsufficientParamsError{
+		have: have,
+		want: want,
+	}
+}
+
+type NotImplementedError struct {
+	Method string
+}
+
+func (e *NotImplementedError) Error() string {
+	return fmt.Sprintf("%s method not implemented", e.Method)
+}
+
+func NewNotImplementedError(method string) *NotImplementedError {
+	return &NotImplementedError{
+		Method: method,
+	}
+}
+
+type DecodeParamError struct {
+	err string
+}
+
+func (e *DecodeParamError) Error() string {
+	return fmt.Sprintf("could not decode, %s", e.err)
+
+}
+
+func NewDecodeParamError(errstr string) error {
+	return &DecodeParamError{
+		err: errstr,
+	}
+}
+
+type ValidationError struct {
+	ParamName string
+	msg       string
+}
+
+func (e *ValidationError) Error() string {
+	return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg)
+}
+
+func NewValidationError(param string, msg string) error {
+	return &ValidationError{
+		ParamName: param,
+		msg:       msg,
+	}
+}
+
+type NotAvailableError struct {
+	Method string
+	Reason string
+}
+
+func (e *NotAvailableError) Error() string {
+	return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason)
+}
+
+func NewNotAvailableError(method string, reason string) *NotAvailableError {
+	return &NotAvailableError{
+		Method: method,
+		Reason: reason,
+	}
+}
diff --git a/rpc/shared/types.go b/rpc/shared/types.go
new file mode 100644
index 000000000..46fd5552c
--- /dev/null
+++ b/rpc/shared/types.go
@@ -0,0 +1,38 @@
+package shared
+
+import "encoding/json"
+
+// RPC request
+type Request struct {
+	Id      interface{}     `json:"id"`
+	Jsonrpc string          `json:"jsonrpc"`
+	Method  string          `json:"method"`
+	Params  json.RawMessage `json:"params"`
+}
+
+// RPC response
+type Response struct {
+	Id      interface{} `json:"id"`
+	Jsonrpc string      `json:"jsonrpc"`
+}
+
+// RPC success response
+type SuccessResponse struct {
+	Id      interface{} `json:"id"`
+	Jsonrpc string      `json:"jsonrpc"`
+	Result  interface{} `json:"result"`
+}
+
+// RPC error response
+type ErrorResponse struct {
+	Id      interface{}  `json:"id"`
+	Jsonrpc string       `json:"jsonrpc"`
+	Error   *ErrorObject `json:"error"`
+}
+
+// RPC error response details
+type ErrorObject struct {
+	Code    int    `json:"code"`
+	Message string `json:"message"`
+	// Data    interface{} `json:"data"`
+}
-- 
cgit v1.2.3