diff --git a/rpc/core/1 b/rpc/core/1
new file mode 100644
index 0000000000000000000000000000000000000000..58483e40f7c5d8f22bcf0544344cbb2663b2586e
--- /dev/null
+++ b/rpc/core/1
@@ -0,0 +1,55 @@
+package core
+
+import (
+	stypes "github.com/eris-ltd/eris-db/state/types"
+	"github.com/eris-ltd/eris-db/tmsp"
+	"github.com/eris-ltd/eris-db/txs"
+
+	bc "github.com/tendermint/tendermint/blockchain"
+	"github.com/tendermint/tendermint/consensus"
+	mempl "github.com/tendermint/tendermint/mempool"
+	tmtypes "github.com/tendermint/tendermint/types"
+
+	"github.com/tendermint/go-p2p"
+)
+
+var blockStore *bc.BlockStore
+var consensusState *consensus.ConsensusState
+var consensusReactor *consensus.ConsensusReactor
+var mempoolReactor *mempl.MempoolReactor
+var p2pSwitch *p2p.Switch
+var privValidator *types.PrivValidator
+var genDoc *stypes.GenesisDoc // cache the genesis structure
+var erisdbApp *tmsp.ErisDBApp
+
+func SetErisDBApp(edbApp *tmsp.ErisDBApp) {
+	erisdbApp = edbApp
+}
+
+func SetBlockStore(bs *bc.BlockStore) {
+	blockStore = bs
+}
+
+func SetConsensusState(cs *consensus.ConsensusState) {
+	consensusState = cs
+}
+
+func SetConsensusReactor(cr *consensus.ConsensusReactor) {
+	consensusReactor = cr
+}
+
+func SetMempoolReactor(mr *mempl.MempoolReactor) {
+	mempoolReactor = mr
+}
+
+func SetSwitch(sw *p2p.Switch) {
+	p2pSwitch = sw
+}
+
+func SetPrivValidator(pv *tmtypes.PrivValidator) {
+	privValidator = pv
+}
+
+func SetGenDoc(doc *stypes.GenesisDoc) {
+	genDoc = doc
+}
diff --git a/rpc/core/accounts.go b/rpc/core/accounts.go
new file mode 100644
index 0000000000000000000000000000000000000000..161a14af67a8e19f392ed1e191f4e4d113cdebe3
--- /dev/null
+++ b/rpc/core/accounts.go
@@ -0,0 +1,68 @@
+package core
+
+import (
+	"fmt"
+	acm "github.com/eris-ltd/eris-db/account"
+	ctypes "github.com/eris-ltd/eris-db/rpc/core/types"
+	. "github.com/tendermint/go-common"
+)
+
+func GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) {
+	return &ctypes.ResultGenPrivAccount{acm.GenPrivAccount()}, nil
+}
+
+// If the account is not known, returns nil, nil.
+func GetAccount(address []byte) (*ctypes.ResultGetAccount, error) {
+	cache := erisdbApp.GetCheckCache()
+	// cache := mempoolReactor.Mempool.GetCache()
+	account := cache.GetAccount(address)
+	if account == nil {
+		return nil, nil
+	}
+	return &ctypes.ResultGetAccount{account}, nil
+}
+
+func GetStorage(address, key []byte) (*ctypes.ResultGetStorage, error) {
+	state := erisdbApp.GetState()
+	// state := consensusState.GetState()
+	account := state.GetAccount(address)
+	if account == nil {
+		return nil, fmt.Errorf("UnknownAddress: %X", address)
+	}
+	storageRoot := account.StorageRoot
+	storageTree := state.LoadStorage(storageRoot)
+
+	_, value, exists := storageTree.Get(LeftPadWord256(key).Bytes())
+	if !exists { // value == nil {
+		return &ctypes.ResultGetStorage{key, nil}, nil
+	}
+	return &ctypes.ResultGetStorage{key, value}, nil
+}
+
+func ListAccounts() (*ctypes.ResultListAccounts, error) {
+	var blockHeight int
+	var accounts []*acm.Account
+	state := erisdbApp.GetState()
+	blockHeight = state.LastBlockHeight
+	state.GetAccounts().Iterate(func(key []byte, value []byte) bool {
+		accounts = append(accounts, acm.DecodeAccount(value))
+		return false
+	})
+	return &ctypes.ResultListAccounts{blockHeight, accounts}, nil
+}
+
+func DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) {
+	state := erisdbApp.GetState()
+	account := state.GetAccount(address)
+	if account == nil {
+		return nil, fmt.Errorf("UnknownAddress: %X", address)
+	}
+	storageRoot := account.StorageRoot
+	storageTree := state.LoadStorage(storageRoot)
+	storageItems := []ctypes.StorageItem{}
+	storageTree.Iterate(func(key []byte, value []byte) bool {
+		storageItems = append(storageItems, ctypes.StorageItem{key, value})
+		return false
+	})
+	return &ctypes.ResultDumpStorage{storageRoot, storageItems}, nil
+}
diff --git a/rpc/core/config.go b/rpc/core/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..7f51b66625ecc9483046f5edcaad851edf89f86b
--- /dev/null
+++ b/rpc/core/config.go
@@ -0,0 +1,13 @@
+package core
+
+import (
+	cfg "github.com/tendermint/go-config"
+)
+
+var config cfg.Config = nil
+
+func init() {
+	cfg.OnConfig(func(newConfig cfg.Config) {
+		config = newConfig
+	})
+}
diff --git a/rpc/core/log.go b/rpc/core/log.go
new file mode 100644
index 0000000000000000000000000000000000000000..d359bee26498b53017a9aec516a65f6e2472d726
--- /dev/null
+++ b/rpc/core/log.go
@@ -0,0 +1,7 @@
+package core
+
+import (
+	"github.com/tendermint/log15"
+)
+
+var log = log15.New("module", "rpc")
diff --git a/rpc/core/names.go b/rpc/core/names.go
new file mode 100644
index 0000000000000000000000000000000000000000..4dcd891e89d626d0407dfbc114a12ddbd463494e
--- /dev/null
+++ b/rpc/core/names.go
@@ -0,0 +1,30 @@
+package core
+
+import (
+	"fmt"
+
+	ctypes "github.com/eris-ltd/eris-db/rpc/core/types"
+	sm "github.com/eris-ltd/eris-db/state"
+	"github.com/eris-ltd/eris-db/txs"
+)
+
+func GetName(name string) (*ctypes.ResultGetName, error) {
+	st := erisdbApp.GetState()
+	entry := st.GetNameRegEntry(name)
+	if entry == nil {
+		return nil, fmt.Errorf("Name %s not found", name)
+	}
+	return &ctypes.ResultGetName{entry}, nil
+}
+
+func ListNames() (*ctypes.ResultListNames, error) {
+	var blockHeight int
+	var names []*types.NameRegEntry
+	state := erisdbApp.GetState()
+	blockHeight = state.LastBlockHeight
+	state.GetNames().Iterate(func(key []byte, value []byte) bool {
+		names = append(names, sm.DecodeNameRegEntry(value))
+		return false
+	})
+	return &ctypes.ResultListNames{blockHeight, names}, nil
+}
diff --git a/rpc/core/net.go b/rpc/core/net.go
new file mode 100644
index 0000000000000000000000000000000000000000..2198731c5c4b6d3d5d7cf5d61aa3cbf7f6229e8f
--- /dev/null
+++ b/rpc/core/net.go
@@ -0,0 +1,69 @@
+package core
+
+import (
+	ctypes "github.com/eris-ltd/eris-db/rpc/core/types"
+	sm "github.com/eris-ltd/eris-db/state"
+	"github.com/tendermint/tendermint/types"
+
+	dbm "github.com/tendermint/go-db"
+)
+
+//-----------------------------------------------------------------------------
+
+// cache the genesis state
+var genesisState *sm.State
+
+func Status() (*ctypes.ResultStatus, error) {
+	db := dbm.NewMemDB()
+	if genesisState == nil {
+		genesisState = sm.MakeGenesisState(db, genDoc)
+	}
+	genesisHash := genesisState.Hash()
+	latestHeight := blockStore.Height()
+	var (
+		latestBlockMeta *types.BlockMeta
+		latestBlockHash []byte
+		latestBlockTime int64
+	)
+	if latestHeight != 0 {
+		latestBlockMeta = blockStore.LoadBlockMeta(latestHeight)
+		latestBlockHash = latestBlockMeta.Hash
+		latestBlockTime = latestBlockMeta.Header.Time.UnixNano()
+	}
+
+	return &ctypes.ResultStatus{
+		NodeInfo:          p2pSwitch.NodeInfo(),
+		GenesisHash:       genesisHash,
+		PubKey:            privValidator.PubKey,
+		LatestBlockHash:   latestBlockHash,
+		LatestBlockHeight: latestHeight,
+		LatestBlockTime:   latestBlockTime}, nil
+}
+
+//-----------------------------------------------------------------------------
+
+func NetInfo() (*ctypes.ResultNetInfo, error) {
+	listening := p2pSwitch.IsListening()
+	listeners := []string{}
+	for _, listener := range p2pSwitch.Listeners() {
+		listeners = append(listeners, listener.String())
+	}
+	peers := []ctypes.Peer{}
+	for _, peer := range p2pSwitch.Peers().List() {
+		peers = append(peers, ctypes.Peer{
+			NodeInfo:   *peer.NodeInfo,
+			IsOutbound: peer.IsOutbound(),
+		})
+	}
+	return &ctypes.ResultNetInfo{
+		Listening: listening,
+		Listeners: listeners,
+		Peers:     peers,
+	}, nil
+}
+
+//-----------------------------------------------------------------------------
+
+func Genesis() (*ctypes.ResultGenesis, error) {
+	return &ctypes.ResultGenesis{genDoc}, nil
+}
diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go
new file mode 100644
index 0000000000000000000000000000000000000000..c7f3fa4abf6d590e6186b029a267b520f456a6e1
--- /dev/null
+++ b/rpc/core/pipe.go
@@ -0,0 +1,54 @@
+package core
+
+import (
+	stypes "github.com/eris-ltd/eris-db/state/types"
+	"github.com/eris-ltd/eris-db/tmsp"
+
+	bc "github.com/tendermint/tendermint/blockchain"
+	"github.com/tendermint/tendermint/consensus"
+	mempl "github.com/tendermint/tendermint/mempool"
+	tmtypes "github.com/tendermint/tendermint/types"
+
+	"github.com/tendermint/go-p2p"
+)
+
+var blockStore *bc.BlockStore
+var consensusState *consensus.ConsensusState
+var consensusReactor *consensus.ConsensusReactor
+var mempoolReactor *mempl.MempoolReactor
+var p2pSwitch *p2p.Switch
+var privValidator *tmtypes.PrivValidator
+var genDoc *stypes.GenesisDoc // cache the genesis structure
+var erisdbApp *tmsp.ErisDBApp
+
+func SetErisDBApp(edbApp *tmsp.ErisDBApp) {
+	erisdbApp = edbApp
+}
+
+func SetBlockStore(bs *bc.BlockStore) {
+	blockStore = bs
+}
+
+func SetConsensusState(cs *consensus.ConsensusState) {
+	consensusState = cs
+}
+
+func SetConsensusReactor(cr *consensus.ConsensusReactor) {
+	consensusReactor = cr
+}
+
+func SetMempoolReactor(mr *mempl.MempoolReactor) {
+	mempoolReactor = mr
+}
+
+func SetSwitch(sw *p2p.Switch) {
+	p2pSwitch = sw
+}
+
+func SetPrivValidator(pv *tmtypes.PrivValidator) {
+	privValidator = pv
+}
+
+func SetGenDoc(doc *stypes.GenesisDoc) {
+	genDoc = doc
+}
diff --git a/rpc/core/routes.go b/rpc/core/routes.go
new file mode 100644
index 0000000000000000000000000000000000000000..9d8bf8f556d78a7deb73b28c04812380b5454861
--- /dev/null
+++ b/rpc/core/routes.go
@@ -0,0 +1,29 @@
+package core
+
+import (
+	rpc "github.com/tendermint/go-rpc/server"
+)
+
+// TODO: eliminate redundancy between here and reading code from core/
+var Routes = map[string]*rpc.RPCFunc{
+	"status":   rpc.NewRPCFunc(Status, ""),
+	"net_info": rpc.NewRPCFunc(NetInfo, ""),
+	//	"blockchain":              rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"),
+	"genesis": rpc.NewRPCFunc(Genesis, ""),
+	//	"get_block":               rpc.NewRPCFunc(GetBlock, "height"),
+	"get_account": rpc.NewRPCFunc(GetAccount, "address"),
+	"get_storage": rpc.NewRPCFunc(GetStorage, "address,key"),
+	"call":        rpc.NewRPCFunc(Call, "fromAddress,toAddress,data"),
+	"call_code":   rpc.NewRPCFunc(CallCode, "fromAddress,code,data"),
+	//"list_validators":         rpc.NewRPCFunc(ListValidators, ""),
+	// "dump_consensus_state":    rpc.NewRPCFunc(DumpConsensusState, ""),
+	"dump_storage": rpc.NewRPCFunc(DumpStorage, "address"),
+	// "broadcast_tx":            rpc.NewRPCFunc(BroadcastTx, "tx"),
+	// "list_unconfirmed_txs":    rpc.NewRPCFunc(ListUnconfirmedTxs, ""),
+	"list_accounts":           rpc.NewRPCFunc(ListAccounts, ""),
+	"get_name":                rpc.NewRPCFunc(GetName, "name"),
+	"list_names":              rpc.NewRPCFunc(ListNames, ""),
+	"unsafe/gen_priv_account": rpc.NewRPCFunc(GenPrivAccount, ""),
+	"unsafe/sign_tx":          rpc.NewRPCFunc(SignTx, "tx,privAccounts"),
+	// subscribe/unsubscribe are reserved for websocket events.
+}
diff --git a/rpc/core/txs.go b/rpc/core/txs.go
new file mode 100644
index 0000000000000000000000000000000000000000..7a93dec44768ea93cb80f59ca3234831baa268eb
--- /dev/null
+++ b/rpc/core/txs.go
@@ -0,0 +1,118 @@
+package core
+
+import (
+	"fmt"
+	acm "github.com/eris-ltd/eris-db/account"
+	"github.com/eris-ltd/eris-db/evm"
+	ctypes "github.com/eris-ltd/eris-db/rpc/core/types"
+	"github.com/eris-ltd/eris-db/state"
+	"github.com/eris-ltd/eris-db/txs"
+
+	. "github.com/tendermint/go-common"
+	"github.com/tendermint/go-crypto"
+)
+
+func toVMAccount(acc *acm.Account) *vm.Account {
+	return &vm.Account{
+		Address: LeftPadWord256(acc.Address),
+		Balance: acc.Balance,
+		Code:    acc.Code, // This is crazy.
+		Nonce:   int64(acc.Sequence),
+		Other:   acc.PubKey,
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+// Run a contract's code on an isolated and unpersisted state
+// Cannot be used to create new contracts
+func Call(fromAddress, toAddress, data []byte) (*ctypes.ResultCall, error) {
+	st := erisdbApp.GetState()
+	cache := state.NewBlockCache(st)
+	outAcc := cache.GetAccount(toAddress)
+	if outAcc == nil {
+		return nil, fmt.Errorf("Account %x does not exist", toAddress)
+	}
+	callee := toVMAccount(outAcc)
+	caller := &vm.Account{Address: LeftPadWord256(fromAddress)}
+	txCache := state.NewTxCache(cache)
+	params := vm.Params{
+		BlockHeight: int64(st.LastBlockHeight),
+		BlockHash:   LeftPadWord256(st.LastBlockHash),
+		BlockTime:   st.LastBlockTime.Unix(),
+		GasLimit:    st.GetGasLimit(),
+	}
+
+	vmach := vm.NewVM(txCache, params, caller.Address, nil)
+	gas := st.GetGasLimit()
+	ret, err := vmach.Call(caller, callee, callee.Code, data, 0, &gas)
+	if err != nil {
+		return nil, err
+	}
+	return &ctypes.ResultCall{Return: ret}, nil
+}
+
+// Run the given code on an isolated and unpersisted state
+// Cannot be used to create new contracts
+func CallCode(fromAddress, code, data []byte) (*ctypes.ResultCall, error) {
+
+	st := erisdbApp.GetState()
+	cache := erisdbApp.GetCheckCache()
+	callee := &vm.Account{Address: LeftPadWord256(fromAddress)}
+	caller := &vm.Account{Address: LeftPadWord256(fromAddress)}
+	txCache := state.NewTxCache(cache)
+	params := vm.Params{
+		BlockHeight: int64(st.LastBlockHeight),
+		BlockHash:   LeftPadWord256(st.LastBlockHash),
+		BlockTime:   st.LastBlockTime.Unix(),
+		GasLimit:    st.GetGasLimit(),
+	}
+
+	vmach := vm.NewVM(txCache, params, caller.Address, nil)
+	gas := st.GetGasLimit()
+	ret, err := vmach.Call(caller, callee, code, data, 0, &gas)
+	if err != nil {
+		return nil, err
+	}
+	return &ctypes.ResultCall{Return: ret}, nil
+}
+
+//-----------------------------------------------------------------------------
+
+func SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) {
+	// more checks?
+
+	for i, privAccount := range privAccounts {
+		if privAccount == nil || privAccount.PrivKey == nil {
+			return nil, fmt.Errorf("Invalid (empty) privAccount @%v", i)
+		}
+	}
+	switch tx.(type) {
+	case *types.SendTx:
+		sendTx := tx.(*types.SendTx)
+		for i, input := range sendTx.Inputs {
+			input.PubKey = privAccounts[i].PubKey
+			input.Signature = privAccounts[i].Sign(config.GetString("erisdb.chain_id"), sendTx)
+		}
+	case *types.CallTx:
+		callTx := tx.(*types.CallTx)
+		callTx.Input.PubKey = privAccounts[0].PubKey
+		callTx.Input.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), callTx)
+	case *types.BondTx:
+		bondTx := tx.(*types.BondTx)
+		// the first privaccount corresponds to the BondTx pub key.
+		// the rest to the inputs
+		bondTx.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), bondTx).(crypto.SignatureEd25519)
+		for i, input := range bondTx.Inputs {
+			input.PubKey = privAccounts[i+1].PubKey
+			input.Signature = privAccounts[i+1].Sign(config.GetString("erisdb.chain_id"), bondTx)
+		}
+	case *types.UnbondTx:
+		unbondTx := tx.(*types.UnbondTx)
+		unbondTx.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), unbondTx).(crypto.SignatureEd25519)
+	case *types.RebondTx:
+		rebondTx := tx.(*types.RebondTx)
+		rebondTx.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), rebondTx).(crypto.SignatureEd25519)
+	}
+	return &ctypes.ResultSignTx{tx}, nil
+}
diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go
new file mode 100644
index 0000000000000000000000000000000000000000..c1718dbee8d0012463b7fa8b0637929dab816dfe
--- /dev/null
+++ b/rpc/core/types/responses.go
@@ -0,0 +1,182 @@
+package core_types
+
+import (
+	acm "github.com/eris-ltd/eris-db/account"
+	stypes "github.com/eris-ltd/eris-db/state/types"
+	txtypes "github.com/eris-ltd/eris-db/txs"
+	"github.com/tendermint/tendermint/types"
+
+	"github.com/tendermint/go-crypto"
+	"github.com/tendermint/go-p2p"
+	"github.com/tendermint/go-wire"
+)
+
+type ResultGetStorage struct {
+	Key   []byte `json:"key"`
+	Value []byte `json:"value"`
+}
+
+type ResultCall struct {
+	Return  []byte `json:"return"`
+	GasUsed int64  `json:"gas_used"`
+	// TODO ...
+}
+
+type ResultListAccounts struct {
+	BlockHeight int            `json:"block_height"`
+	Accounts    []*acm.Account `json:"accounts"`
+}
+
+type ResultDumpStorage struct {
+	StorageRoot  []byte        `json:"storage_root"`
+	StorageItems []StorageItem `json:"storage_items"`
+}
+
+type StorageItem struct {
+	Key   []byte `json:"key"`
+	Value []byte `json:"value"`
+}
+
+type ResultBlockchainInfo struct {
+	LastHeight int                `json:"last_height"`
+	BlockMetas []*types.BlockMeta `json:"block_metas"`
+}
+
+type ResultGetBlock struct {
+	BlockMeta *types.BlockMeta `json:"block_meta"`
+	Block     *types.Block     `json:"block"`
+}
+
+type ResultStatus struct {
+	NodeInfo          *p2p.NodeInfo `json:"node_info"`
+	GenesisHash       []byte        `json:"genesis_hash"`
+	PubKey            crypto.PubKey `json:"pub_key"`
+	LatestBlockHash   []byte        `json:"latest_block_hash"`
+	LatestBlockHeight int           `json:"latest_block_height"`
+	LatestBlockTime   int64         `json:"latest_block_time"` // nano
+}
+
+type ResultNetInfo struct {
+	Listening bool     `json:"listening"`
+	Listeners []string `json:"listeners"`
+	Peers     []Peer   `json:"peers"`
+}
+
+type Peer struct {
+	p2p.NodeInfo `json:"node_info"`
+	IsOutbound   bool `json:"is_outbound"`
+}
+
+type ResultListValidators struct {
+	BlockHeight         int                `json:"block_height"`
+	BondedValidators    []*types.Validator `json:"bonded_validators"`
+	UnbondingValidators []*types.Validator `json:"unbonding_validators"`
+}
+
+type ResultDumpConsensusState struct {
+	RoundState      string   `json:"round_state"`
+	PeerRoundStates []string `json:"peer_round_states"`
+}
+
+type ResultListNames struct {
+	BlockHeight int                     `json:"block_height"`
+	Names       []*txtypes.NameRegEntry `json:"names"`
+}
+
+type ResultGenPrivAccount struct {
+	PrivAccount *acm.PrivAccount `json:"priv_account"`
+}
+
+type ResultGetAccount struct {
+	Account *acm.Account `json:"account"`
+}
+
+type ResultBroadcastTx struct {
+	Receipt Receipt `json:"receipt"`
+}
+
+type Receipt struct {
+	TxHash          []byte `json:"tx_hash"`
+	CreatesContract uint8  `json:"creates_contract"`
+	ContractAddr    []byte `json:"contract_addr"`
+}
+
+type ResultListUnconfirmedTxs struct {
+	N   int          `json:"n_txs"`
+	Txs []txtypes.Tx `json:"txs"`
+}
+
+type ResultGetName struct {
+	Entry *txtypes.NameRegEntry `json:"entry"`
+}
+
+type ResultGenesis struct {
+	Genesis *stypes.GenesisDoc `json:"genesis"`
+}
+
+type ResultSignTx struct {
+	Tx txtypes.Tx `json:"tx"`
+}
+
+type ResultEvent struct {
+	Event string            `json:"event"`
+	Data  types.TMEventData `json:"data"`
+}
+
+//----------------------------------------
+// response & result types
+
+type Response struct {
+	JSONRPC string `json:"jsonrpc"`
+	ID      string `json:"id"`
+	Result  Result `json:"result"`
+	Error   string `json:"error"`
+}
+
+const (
+	ResultTypeGetStorage         = byte(0x01)
+	ResultTypeCall               = byte(0x02)
+	ResultTypeListAccounts       = byte(0x03)
+	ResultTypeDumpStorage        = byte(0x04)
+	ResultTypeBlockchainInfo     = byte(0x05)
+	ResultTypeGetBlock           = byte(0x06)
+	ResultTypeStatus             = byte(0x07)
+	ResultTypeNetInfo            = byte(0x08)
+	ResultTypeListValidators     = byte(0x09)
+	ResultTypeDumpConsensusState = byte(0x0A)
+	ResultTypeListNames          = byte(0x0B)
+	ResultTypeGenPrivAccount     = byte(0x0C)
+	ResultTypeGetAccount         = byte(0x0D)
+	ResultTypeBroadcastTx        = byte(0x0E)
+	ResultTypeListUnconfirmedTxs = byte(0x0F)
+	ResultTypeGetName            = byte(0x10)
+	ResultTypeGenesis            = byte(0x11)
+	ResultTypeSignTx             = byte(0x12)
+	ResultTypeEvent              = byte(0x13) // so websockets can respond to rpc functions
+)
+
+type Result interface{}
+
+// for wire.readReflect
+var _ = wire.RegisterInterface(
+	struct{ Result }{},
+	wire.ConcreteType{&ResultGetStorage{}, ResultTypeGetStorage},
+	wire.ConcreteType{&ResultCall{}, ResultTypeCall},
+	wire.ConcreteType{&ResultListAccounts{}, ResultTypeListAccounts},
+	wire.ConcreteType{&ResultDumpStorage{}, ResultTypeDumpStorage},
+	wire.ConcreteType{&ResultBlockchainInfo{}, ResultTypeBlockchainInfo},
+	wire.ConcreteType{&ResultGetBlock{}, ResultTypeGetBlock},
+	wire.ConcreteType{&ResultStatus{}, ResultTypeStatus},
+	wire.ConcreteType{&ResultNetInfo{}, ResultTypeNetInfo},
+	wire.ConcreteType{&ResultListValidators{}, ResultTypeListValidators},
+	wire.ConcreteType{&ResultDumpConsensusState{}, ResultTypeDumpConsensusState},
+	wire.ConcreteType{&ResultListNames{}, ResultTypeListNames},
+	wire.ConcreteType{&ResultGenPrivAccount{}, ResultTypeGenPrivAccount},
+	wire.ConcreteType{&ResultGetAccount{}, ResultTypeGetAccount},
+	wire.ConcreteType{&ResultBroadcastTx{}, ResultTypeBroadcastTx},
+	wire.ConcreteType{&ResultListUnconfirmedTxs{}, ResultTypeListUnconfirmedTxs},
+	wire.ConcreteType{&ResultGetName{}, ResultTypeGetName},
+	wire.ConcreteType{&ResultGenesis{}, ResultTypeGenesis},
+	wire.ConcreteType{&ResultSignTx{}, ResultTypeSignTx},
+	wire.ConcreteType{&ResultEvent{}, ResultTypeEvent},
+)
diff --git a/rpc/core_client/client.go b/rpc/core_client/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..f95eadfb6335d126ccdcbbec7dbc8f1c632d31a3
--- /dev/null
+++ b/rpc/core_client/client.go
@@ -0,0 +1,236 @@
+package core_client
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+
+	ctypes "github.com/eris-ltd/eris-db/rpc/core/types"
+	rpctypes "github.com/tendermint/go-rpc/types"
+	"github.com/tendermint/go-wire"
+	//"reflect"
+	// Uncomment to use go:generate
+	// _ "github.com/tendermint/go-rpc-gen"
+)
+
+// maps camel-case function names to lower case rpc version
+var reverseFuncMap = map[string]string{
+	"Status":             "status",
+	"NetInfo":            "net_info",
+	"BlockchainInfo":     "blockchain",
+	"Genesis":            "genesis",
+	"GetBlock":           "get_block",
+	"GetAccount":         "get_account",
+	"GetStorage":         "get_storage",
+	"Call":               "call",
+	"CallCode":           "call_code",
+	"ListValidators":     "list_validators",
+	"DumpConsensusState": "dump_consensus_state",
+	"DumpStorage":        "dump_storage",
+	"BroadcastTx":        "broadcast_tx",
+	"ListUnconfirmedTxs": "list_unconfirmed_txs",
+	"ListAccounts":       "list_accounts",
+	"GetName":            "get_name",
+	"ListNames":          "list_names",
+	"GenPrivAccount":     "unsafe/gen_priv_account",
+	"SignTx":             "unsafe/sign_tx",
+}
+
+/*
+// fill the map from camelcase to lowercase
+func fillReverseFuncMap() map[string]string {
+	fMap := make(map[string]string)
+	for name, f := range core.Routes {
+		camelName := runtime.FuncForPC(f.f.Pointer()).Name()
+		spl := strings.Split(camelName, ".")
+		if len(spl) > 1 {
+			camelName = spl[len(spl)-1]
+		}
+		fMap[camelName] = name
+	}
+	return fMap
+}
+*/
+
+type Response struct {
+	Status string
+	Data   interface{}
+	Error  string
+}
+
+//go:generate go-rpc-gen -interface Client -dir ../core -pkg core -type *ClientHTTP,*ClientJSON -exclude pipe.go -out-pkg core_client
+
+type ClientJSON struct {
+	addr string
+}
+
+type ClientHTTP struct {
+	addr string
+}
+
+func NewClient(addr, typ string) Client {
+	switch typ {
+	case "HTTP":
+		return &ClientHTTP{addr}
+	case "JSONRPC":
+		return &ClientJSON{addr}
+	default:
+		panic("Unknown client type " + typ + ". Select HTTP or JSONRPC")
+	}
+	return nil
+}
+
+func argsToJson(args ...interface{}) ([]string, error) {
+	l := len(args)
+	jsons := make([]string, l)
+	n, err := new(int), new(error)
+	for i, a := range args {
+		buf := new(bytes.Buffer)
+		wire.WriteJSON(a, buf, n, err)
+		if *err != nil {
+			return nil, *err
+		}
+		jsons[i] = string(buf.Bytes())
+	}
+	return jsons, nil
+}
+
+func (c *ClientJSON) RequestResponse(s rpctypes.RPCRequest) (b []byte, err error) {
+	b = wire.JSONBytes(s)
+	buf := bytes.NewBuffer(b)
+	resp, err := http.Post(c.addr, "text/json", buf)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	return ioutil.ReadAll(resp.Body)
+}
+
+/*
+	What follows is used by `rpc-gen` when `go generate` is called
+	to populate the rpc client methods
+*/
+
+// first we define the base interface, which rpc-gen will further populate with generated methods
+
+/*rpc-gen:define-interface Client
+type Client interface {
+	Address() string // returns the remote address
+}
+*/
+
+// encoding functions
+
+func binaryWriter(args ...interface{}) ([]interface{}, error) {
+	list := []interface{}{}
+	for _, a := range args {
+		buf, n, err := new(bytes.Buffer), new(int), new(error)
+		wire.WriteJSON(a, buf, n, err)
+		if *err != nil {
+			return nil, *err
+		}
+		list = append(list, buf.Bytes())
+
+	}
+	return list, nil
+}
+
+func argsToURLValues(argNames []string, args ...interface{}) (url.Values, error) {
+	values := make(url.Values)
+	if len(argNames) == 0 {
+		return values, nil
+	}
+	if len(argNames) != len(args) {
+		return nil, fmt.Errorf("argNames and args have different lengths: %d, %d", len(argNames), len(args))
+	}
+	slice, err := argsToJson(args...)
+	if err != nil {
+		return nil, err
+	}
+	for i, name := range argNames {
+		s := slice[i]
+		values.Set(name, s) // s[0]
+		/*for j := 1; j < len(s); j++ {
+			values.Add(name, s[j])
+		}*/
+	}
+	return values, nil
+}
+
+func unmarshalCheckResponse(body []byte) (response *ctypes.Response, err error) {
+	response = new(ctypes.Response)
+	wire.ReadJSON(response, body, &err)
+	if err != nil {
+		return nil, err
+	}
+	if response.Error != "" {
+		return nil, fmt.Errorf(response.Error)
+	}
+	return response, nil
+}
+
+// import statements we will need for the templates
+
+/*rpc-gen:imports:
+rpctypes github.com/tendermint/tendermint/rpc/types
+net/http
+io/ioutil
+fmt
+*/
+
+// Template functions to be filled in
+
+/*rpc-gen:template:*ClientJSON func (c *ClientJSON) {{name}}({{args.def}}) ({{response}}) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["{{name}}"],
+		Params:  []interface{}{ {{args.ident}} },
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil{
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil{
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.({{response.0}})
+	if !ok{
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}*/
+
+/*rpc-gen:template:*ClientHTTP func (c *ClientHTTP) {{name}}({{args.def}}) ({{response}}){
+	values, err := argsToURLValues({{args.name}}, {{args.ident}})
+	if err != nil{
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["{{name}}"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil{
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.({{response.0}})
+	if !ok{
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}*/
diff --git a/rpc/core_client/client_methods.go b/rpc/core_client/client_methods.go
new file mode 100644
index 0000000000000000000000000000000000000000..dd1848c4b7df25373f2f2818f32dc1170e128502
--- /dev/null
+++ b/rpc/core_client/client_methods.go
@@ -0,0 +1,1042 @@
+// File generated by github.com/ebuchman/rpc-gen
+
+package core_client
+
+import (
+	"fmt"
+	acm "github.com/eris-ltd/eris-db/account"
+	ctypes "github.com/eris-ltd/eris-db/rpc/core/types"
+	"github.com/eris-ltd/eris-db/txs"
+	rpctypes "github.com/tendermint/go-rpc/types"
+	"io/ioutil"
+	"net/http"
+)
+
+type Client interface {
+	BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error)
+	BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
+	Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error)
+	CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error)
+	DumpConsensusState() (*ctypes.ResultDumpConsensusState, error)
+	DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error)
+	GenPrivAccount() (*ctypes.ResultGenPrivAccount, error)
+	Genesis() (*ctypes.ResultGenesis, error)
+	GetAccount(address []byte) (*ctypes.ResultGetAccount, error)
+	GetBlock(height int) (*ctypes.ResultGetBlock, error)
+	GetName(name string) (*ctypes.ResultGetName, error)
+	GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error)
+	ListAccounts() (*ctypes.ResultListAccounts, error)
+	ListNames() (*ctypes.ResultListNames, error)
+	ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error)
+	ListValidators() (*ctypes.ResultListValidators, error)
+	NetInfo() (*ctypes.ResultNetInfo, error)
+	SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error)
+	Status() (*ctypes.ResultStatus, error)
+}
+
+func (c *ClientHTTP) BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) {
+	values, err := argsToURLValues([]string{"minHeight", "maxHeight"}, minHeight, maxHeight)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["BlockchainInfo"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultBlockchainInfo)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
+	values, err := argsToURLValues([]string{"tx"}, tx)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["BroadcastTx"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultBroadcastTx)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) {
+	values, err := argsToURLValues([]string{"fromAddress", "toAddress", "data"}, fromAddress, toAddress, data)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["Call"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultCall)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) {
+	values, err := argsToURLValues([]string{"fromAddress", "code", "data"}, fromAddress, code, data)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["CallCode"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultCall)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["DumpConsensusState"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultDumpConsensusState)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) {
+	values, err := argsToURLValues([]string{"address"}, address)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["DumpStorage"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultDumpStorage)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["GenPrivAccount"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGenPrivAccount)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) Genesis() (*ctypes.ResultGenesis, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["Genesis"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGenesis)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) GetAccount(address []byte) (*ctypes.ResultGetAccount, error) {
+	values, err := argsToURLValues([]string{"address"}, address)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["GetAccount"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetAccount)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) GetBlock(height int) (*ctypes.ResultGetBlock, error) {
+	values, err := argsToURLValues([]string{"height"}, height)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["GetBlock"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetBlock)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) GetName(name string) (*ctypes.ResultGetName, error) {
+	values, err := argsToURLValues([]string{"name"}, name)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["GetName"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetName)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) {
+	values, err := argsToURLValues([]string{"address", "key"}, address, key)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["GetStorage"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetStorage)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) ListAccounts() (*ctypes.ResultListAccounts, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["ListAccounts"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListAccounts)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) ListNames() (*ctypes.ResultListNames, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["ListNames"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListNames)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["ListUnconfirmedTxs"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListUnconfirmedTxs)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) ListValidators() (*ctypes.ResultListValidators, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["ListValidators"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListValidators)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) NetInfo() (*ctypes.ResultNetInfo, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["NetInfo"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultNetInfo)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) {
+	values, err := argsToURLValues([]string{"tx", "privAccounts"}, tx, privAccounts)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["SignTx"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultSignTx)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientHTTP) Status() (*ctypes.ResultStatus, error) {
+	values, err := argsToURLValues(nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := http.PostForm(c.addr+reverseFuncMap["Status"], values)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultStatus)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["BlockchainInfo"],
+		Params:  []interface{}{minHeight, maxHeight},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultBlockchainInfo)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["BroadcastTx"],
+		Params:  []interface{}{tx},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultBroadcastTx)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["Call"],
+		Params:  []interface{}{fromAddress, toAddress, data},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultCall)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["CallCode"],
+		Params:  []interface{}{fromAddress, code, data},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultCall)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["DumpConsensusState"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultDumpConsensusState)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["DumpStorage"],
+		Params:  []interface{}{address},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultDumpStorage)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["GenPrivAccount"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGenPrivAccount)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) Genesis() (*ctypes.ResultGenesis, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["Genesis"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGenesis)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) GetAccount(address []byte) (*ctypes.ResultGetAccount, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["GetAccount"],
+		Params:  []interface{}{address},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetAccount)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) GetBlock(height int) (*ctypes.ResultGetBlock, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["GetBlock"],
+		Params:  []interface{}{height},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetBlock)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) GetName(name string) (*ctypes.ResultGetName, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["GetName"],
+		Params:  []interface{}{name},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetName)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["GetStorage"],
+		Params:  []interface{}{address, key},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultGetStorage)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) ListAccounts() (*ctypes.ResultListAccounts, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["ListAccounts"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListAccounts)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) ListNames() (*ctypes.ResultListNames, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["ListNames"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListNames)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["ListUnconfirmedTxs"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListUnconfirmedTxs)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) ListValidators() (*ctypes.ResultListValidators, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["ListValidators"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultListValidators)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) NetInfo() (*ctypes.ResultNetInfo, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["NetInfo"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultNetInfo)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["SignTx"],
+		Params:  []interface{}{tx, privAccounts},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultSignTx)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
+
+func (c *ClientJSON) Status() (*ctypes.ResultStatus, error) {
+	request := rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		Method:  reverseFuncMap["Status"],
+		Params:  []interface{}{},
+		ID:      "",
+	}
+	body, err := c.RequestResponse(request)
+	if err != nil {
+		return nil, err
+	}
+	response, err := unmarshalCheckResponse(body)
+	if err != nil {
+		return nil, err
+	}
+	if response.Result == nil {
+		return nil, nil
+	}
+	result, ok := response.Result.(*ctypes.ResultStatus)
+	if !ok {
+		return nil, fmt.Errorf("response result was wrong type")
+	}
+	return result, nil
+}
diff --git a/rpc/core_client/log.go b/rpc/core_client/log.go
new file mode 100644
index 0000000000000000000000000000000000000000..54b638dc1eafc6095f18e05e47ee64dc6bbd9fd8
--- /dev/null
+++ b/rpc/core_client/log.go
@@ -0,0 +1,7 @@
+package core_client
+
+import (
+	"github.com/tendermint/log15"
+)
+
+var log = log15.New("module", "core_client")
diff --git a/rpc/core_client/ws_client.go b/rpc/core_client/ws_client.go
new file mode 100644
index 0000000000000000000000000000000000000000..fbd3619b402910d856a7b038f0610302b662b517
--- /dev/null
+++ b/rpc/core_client/ws_client.go
@@ -0,0 +1,119 @@
+package core_client
+
+import (
+	"net/http"
+	"strings"
+	"time"
+
+	ctypes "github.com/eris-ltd/eris-db/rpc/core/types"
+	"github.com/gorilla/websocket"
+	. "github.com/tendermint/go-common"
+	"github.com/tendermint/go-rpc/types"
+	"github.com/tendermint/go-wire"
+)
+
+const (
+	wsEventsChannelCapacity  = 10
+	wsResultsChannelCapacity = 10
+	wsWriteTimeoutSeconds    = 10
+)
+
+type WSClient struct {
+	QuitService
+	Address string
+	*websocket.Conn
+	EventsCh  chan ctypes.ResultEvent
+	ResultsCh chan ctypes.Result
+}
+
+// create a new connection
+func NewWSClient(addr string) *WSClient {
+	wsClient := &WSClient{
+		Address:   addr,
+		Conn:      nil,
+		EventsCh:  make(chan ctypes.ResultEvent, wsEventsChannelCapacity),
+		ResultsCh: make(chan ctypes.Result, wsResultsChannelCapacity),
+	}
+	wsClient.QuitService = *NewQuitService(log, "WSClient", wsClient)
+	return wsClient
+}
+
+func (wsc *WSClient) OnStart() error {
+	wsc.QuitService.OnStart()
+	err := wsc.dial()
+	if err != nil {
+		return err
+	}
+	go wsc.receiveEventsRoutine()
+	return nil
+}
+
+func (wsc *WSClient) dial() error {
+	// Dial
+	dialer := websocket.DefaultDialer
+	rHeader := http.Header{}
+	con, _, err := dialer.Dial(wsc.Address, rHeader)
+	if err != nil {
+		return err
+	}
+	// Set the ping/pong handlers
+	con.SetPingHandler(func(m string) error {
+		con.WriteControl(websocket.PongMessage, []byte(m), time.Now().Add(time.Second*wsWriteTimeoutSeconds))
+		return nil
+	})
+	con.SetPongHandler(func(m string) error {
+		return nil
+	})
+	wsc.Conn = con
+	return nil
+}
+
+func (wsc *WSClient) OnStop() {
+	wsc.QuitService.OnStop()
+}
+
+func (wsc *WSClient) receiveEventsRoutine() {
+	for {
+		_, data, err := wsc.ReadMessage()
+		if err != nil {
+			log.Info("WSClient failed to read message", "error", err, "data", string(data))
+			wsc.Stop()
+			break
+		} else {
+			var response ctypes.Response
+			wire.ReadJSON(&response, data, &err)
+			if err != nil {
+				log.Info("WSClient failed to parse message", "error", err)
+				wsc.Stop()
+				break
+			}
+			if strings.HasSuffix(response.ID, "#event") {
+				wsc.EventsCh <- *response.Result.(*ctypes.ResultEvent)
+			} else {
+				wsc.ResultsCh <- response.Result
+			}
+		}
+	}
+}
+
+// subscribe to an event
+func (wsc *WSClient) Subscribe(eventid string) error {
+	err := wsc.WriteJSON(rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		ID:      "",
+		Method:  "subscribe",
+		Params:  []interface{}{eventid},
+	})
+	return err
+}
+
+// unsubscribe from an event
+func (wsc *WSClient) Unsubscribe(eventid string) error {
+	err := wsc.WriteJSON(rpctypes.RPCRequest{
+		JSONRPC: "2.0",
+		ID:      "",
+		Method:  "unsubscribe",
+		Params:  []interface{}{eventid},
+	})
+	return err
+}