diff --git a/event/filters_test.go b/event/filters_test.go
index cf78c3c11489c5e4265517a2e4492bc53a13dafc..959f4c47f1df7627eb9dc0c4630d75d801ac9fc9 100644
--- a/event/filters_test.go
+++ b/event/filters_test.go
@@ -33,8 +33,8 @@ type FilterableObject struct {
 // Ops: All
 type IntegerFilter struct {
 	op    string
-	value int64
-	match func(int64, int64) bool
+	value uint64
+	match func(uint64, uint64) bool
 }
 
 func (this *IntegerFilter) Configure(fd *FilterData) error {
@@ -57,7 +57,7 @@ func (this *IntegerFilter) Match(v interface{}) bool {
 	if !ok {
 		return false
 	}
-	return this.match(int64(fo.Integer), this.value)
+	return this.match(uint64(fo.Integer), this.value)
 }
 
 // Filter for integer value.
diff --git a/rpc/config.go b/rpc/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..230c2987fe0262b9fb8d7c82b1d0e1414cf1c7ea
--- /dev/null
+++ b/rpc/config.go
@@ -0,0 +1,25 @@
+package rpc
+
+type RPCConfig struct {
+	V0 *TMConfig `json:",omitempty" toml:",omitempty"`
+	TM *TMConfig `json:",omitempty" toml:",omitempty"`
+}
+
+type TMConfig struct {
+	ListenAddress string
+}
+
+type V0Config struct {
+}
+
+func DefaultRPCConfig() *RPCConfig {
+	return &RPCConfig{
+		TM: DefaultTMConfig(),
+	}
+}
+
+func DefaultTMConfig() *TMConfig {
+	return &TMConfig{
+		ListenAddress: ":46657",
+	}
+}
diff --git a/rpc/result.go b/rpc/result.go
new file mode 100644
index 0000000000000000000000000000000000000000..cee5d57e1508584143062b3975f87cef6d3a5404
--- /dev/null
+++ b/rpc/result.go
@@ -0,0 +1,229 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rpc
+
+import (
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/event"
+	"github.com/hyperledger/burrow/execution"
+	"github.com/hyperledger/burrow/genesis"
+	"github.com/hyperledger/burrow/txs"
+	"github.com/tendermint/go-wire/data"
+	ctypes "github.com/tendermint/tendermint/consensus/types"
+	"github.com/tendermint/tendermint/p2p"
+	tm_types "github.com/tendermint/tendermint/types"
+)
+
+type Result struct {
+	ResultInner `json:"unwrap"`
+}
+
+type ResultInner interface {
+}
+
+func (res Result) Unwrap() ResultInner {
+	return res.ResultInner
+}
+
+func (br Result) MarshalJSON() ([]byte, error) {
+	return mapper.ToJSON(br.ResultInner)
+}
+
+func (br *Result) UnmarshalJSON(data []byte) (err error) {
+	parsed, err := mapper.FromJSON(data)
+	if err == nil && parsed != nil {
+		br.ResultInner = parsed.(ResultInner)
+	}
+	return err
+}
+
+var mapper = data.NewMapper(Result{}).
+	// Transact
+	RegisterImplementation(&ResultBroadcastTx{}, "result_broadcast_tx", biota()).
+	// Events
+	RegisterImplementation(&ResultSubscribe{}, "result_subscribe", biota()).
+	RegisterImplementation(&ResultUnsubscribe{}, "result_unsubscribe", biota()).
+	RegisterImplementation(&ResultEvent{}, "result_event", biota()).
+	// Status
+	RegisterImplementation(&ResultStatus{}, "result_status", biota()).
+	RegisterImplementation(&ResultNetInfo{}, "result_net_info", biota()).
+	// Accounts
+	RegisterImplementation(&ResultGetAccount{}, "result_get_account", biota()).
+	RegisterImplementation(&ResultListAccounts{}, "result_list_account", biota()).
+	RegisterImplementation(&ResultGetStorage{}, "result_get_storage", biota()).
+	RegisterImplementation(&ResultDumpStorage{}, "result_dump_storage", biota()).
+	// Simulated call
+	RegisterImplementation(&ResultCall{}, "result_call", biota()).
+	// Blockchain
+	RegisterImplementation(&ResultGenesis{}, "result_genesis", biota()).
+	RegisterImplementation(&ResultChainId{}, "result_chain_id", biota()).
+	RegisterImplementation(&ResultBlockchainInfo{}, "result_blockchain_info", biota()).
+	RegisterImplementation(&ResultGetBlock{}, "result_get_block", biota()).
+	// Consensus
+	RegisterImplementation(&ResultListUnconfirmedTxs{}, "result_list_unconfirmed_txs", biota()).
+	RegisterImplementation(&ResultListValidators{}, "result_list_validators", biota()).
+	RegisterImplementation(&ResultDumpConsensusState{}, "result_dump_consensus_state", biota()).
+	RegisterImplementation(&ResultPeers{}, "result_peers", biota()).
+	// Names
+	RegisterImplementation(&ResultGetName{}, "result_get_name", biota()).
+	RegisterImplementation(&ResultListNames{}, "result_list_names", biota()).
+	// Private keys and signing
+	RegisterImplementation(&ResultSignTx{}, "result_sign_tx", biota()).
+	RegisterImplementation(&ResultGeneratePrivateAccount{}, "result_generate_private_account", biota())
+
+type ResultGetStorage struct {
+	Key   []byte `json:"key"`
+	Value []byte `json:"value"`
+}
+
+func (br Result) ResultGetStorage() *ResultGetStorage {
+	if res, ok := br.ResultInner.(*ResultGetStorage); ok {
+		return res
+	}
+	return nil
+}
+
+type ResultCall struct {
+	*execution.Call `json:"unwrap"`
+}
+
+type ResultListAccounts struct {
+	BlockHeight uint64                 `json:"block_height"`
+	Accounts    []*acm.ConcreteAccount `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 uint64                `json:"last_height"`
+	BlockMetas []*tm_types.BlockMeta `json:"block_metas"`
+}
+
+type ResultGetBlock struct {
+	BlockMeta *tm_types.BlockMeta `json:"block_meta"`
+	Block     *tm_types.Block     `json:"block"`
+}
+
+type ResultStatus struct {
+	NodeInfo          *p2p.NodeInfo `json:"node_info"`
+	GenesisHash       []byte        `json:"genesis_hash"`
+	PubKey            acm.PublicKey `json:"pub_key"`
+	LatestBlockHash   []byte        `json:"latest_block_hash"`
+	LatestBlockHeight uint64        `json:"latest_block_height"`
+	LatestBlockTime   int64         `json:"latest_block_time"` // nano
+}
+
+type ResultChainId struct {
+	ChainName   string `json:"chain_name"`
+	ChainId     string `json:"chain_id"`
+	GenesisHash []byte `json:"genesis_hash"`
+}
+
+type ResultSubscribe struct {
+	Event          string `json:"event"`
+	SubscriptionId string `json:"subscription_id"`
+}
+
+type ResultUnsubscribe struct {
+	SubscriptionId string `json:"subscription_id"`
+}
+
+type Peer struct {
+	NodeInfo   *p2p.NodeInfo `json:"node_info"`
+	IsOutbound bool          `json:"is_outbound"`
+}
+
+type ResultNetInfo struct {
+	Listening bool     `json:"listening"`
+	Listeners []string `json:"listeners"`
+	Peers     []*Peer  `json:"peers"`
+}
+
+type ResultListValidators struct {
+	BlockHeight         uint64                   `json:"block_height"`
+	BondedValidators    []*acm.ConcreteValidator `json:"bonded_validators"`
+	UnbondingValidators []*acm.ConcreteValidator `json:"unbonding_validators"`
+}
+
+type ResultDumpConsensusState struct {
+	RoundState      *ctypes.RoundState       `json:"consensus_state"`
+	PeerRoundStates []*ctypes.PeerRoundState `json:"peer_round_states"`
+}
+
+type ResultPeers struct {
+	Peers []*Peer `json:"peers"`
+}
+
+type ResultListNames struct {
+	BlockHeight uint64                    `json:"block_height"`
+	Names       []*execution.NameRegEntry `json:"names"`
+}
+
+type ResultGeneratePrivateAccount struct {
+	PrivAccount *acm.ConcretePrivateAccount `json:"priv_account"`
+}
+
+type ResultGetAccount struct {
+	Account *acm.ConcreteAccount `json:"account"`
+}
+
+type ResultBroadcastTx struct {
+	*txs.Receipt `json:"unwrap"`
+}
+
+type ResultListUnconfirmedTxs struct {
+	N   int           `json:"n_txs"`
+	Txs []txs.Wrapper `json:"txs"`
+}
+
+type ResultGetName struct {
+	Entry *execution.NameRegEntry `json:"entry"`
+}
+
+type ResultGenesis struct {
+	Genesis genesis.GenesisDoc `json:"genesis"`
+}
+
+type ResultSignTx struct {
+	Tx txs.Wrapper `json:"tx"`
+}
+
+type ResultEvent struct {
+	Event              string `json:"event"`
+	event.AnyEventData `json:"data"`
+}
+
+func (re ResultEvent) Wrap() Result {
+	return Result{
+		ResultInner: &re,
+	}
+}
+
+// Type byte helper
+var nextByte byte = 1
+
+func biota() (b byte) {
+	b = nextByte
+	nextByte++
+	return
+}
diff --git a/rpc/result_test.go b/rpc/result_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..983f7cf7258167bb13672e910ea32d954b2ed4df
--- /dev/null
+++ b/rpc/result_test.go
@@ -0,0 +1,84 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rpc
+
+import (
+	"testing"
+
+	"fmt"
+
+	"encoding/json"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/txs"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/tendermint/go-wire"
+)
+
+func TestResultBroadcastTx(t *testing.T) {
+	// Make sure these are unpacked as expected
+	res := ResultBroadcastTx{
+		Receipt: &txs.Receipt{
+			ContractAddr:    acm.Address{0, 2, 3},
+			CreatesContract: true,
+			TxHash:          []byte("foo"),
+		},
+	}
+
+	assert.Equal(t, `{"tx_hash":"666F6F","creates_contract":true,"contract_addr":"0002030000000000000000000000000000000000"}`,
+		string(wire.JSONBytes(res)))
+
+	res2 := new(ResultBroadcastTx)
+	wire.ReadBinaryBytes(wire.BinaryBytes(res), res2)
+	assert.Equal(t, res, *res2)
+}
+
+func TestListUnconfirmedTxs(t *testing.T) {
+	res := &ResultListUnconfirmedTxs{
+		N: 3,
+		Txs: []txs.Tx{
+			&txs.CallTx{
+				Address: &acm.Address{1},
+			},
+		},
+	}
+	fmt.Println(string(wire.JSONBytes(res)))
+
+}
+
+func TestResultListAccounts(t *testing.T) {
+	concreteAcc := acm.AsConcreteAccount(acm.FromAddressable(
+		acm.GeneratePrivateAccountFromSecret("Super Semi Secret")))
+	acc := concreteAcc
+	res := ResultListAccounts{
+		Accounts:    []*acm.ConcreteAccount{acc},
+		BlockHeight: 2,
+	}
+	bs, err := json.Marshal(res)
+	require.NoError(t, err)
+	resOut := new(ResultListAccounts)
+	json.Unmarshal(bs, resOut)
+	bsOut, err := json.Marshal(resOut)
+	require.NoError(t, err)
+	assert.Equal(t, string(bs), string(bsOut))
+}
+
+func TestResultEvent(t *testing.T) {
+	res := ResultEvent{
+		Event: "a",
+	}
+	fmt.Println(res)
+}
diff --git a/rpc/rpc_test.go b/rpc/rpc_test.go
deleted file mode 100644
index fbdfca374c8cd4a764ea064d06e74ce5b5ca0e4b..0000000000000000000000000000000000000000
--- a/rpc/rpc_test.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package rpc
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-// ...
-func TestNewJsonRpcResponse(t *testing.T) {
-	id := "testId"
-	data := "a string"
-	resp := RPCResponse(&RPCResultResponse{
-		Result:  data,
-		Id:      id,
-		JSONRPC: "2.0",
-	})
-	respGen := NewRPCResponse(id, data)
-	assert.Equal(t, respGen, resp)
-}
-
-// ...
-func TestNewJsonRpcErrorResponse(t *testing.T) {
-	id := "testId"
-	code := 100
-	message := "the error"
-	resp := RPCResponse(&RPCErrorResponse{
-		Error:   &RPCError{code, message},
-		Id:      id,
-		JSONRPC: "2.0",
-	})
-	respGen := NewRPCErrorResponse(id, code, message)
-	assert.Equal(t, respGen, resp)
-}
diff --git a/rpc/service.go b/rpc/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2fc8454be2d56643475d6cb46803ed6d87e3a98
--- /dev/null
+++ b/rpc/service.go
@@ -0,0 +1,401 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rpc
+
+import (
+	"fmt"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/binary"
+	bcm "github.com/hyperledger/burrow/blockchain"
+	"github.com/hyperledger/burrow/consensus/tendermint/query"
+	"github.com/hyperledger/burrow/event"
+	"github.com/hyperledger/burrow/execution"
+	"github.com/hyperledger/burrow/logging"
+	"github.com/hyperledger/burrow/logging/structure"
+	logging_types "github.com/hyperledger/burrow/logging/types"
+	"github.com/hyperledger/burrow/txs"
+	tm_types "github.com/tendermint/tendermint/types"
+)
+
+// Magic! Should probably be configurable, but not shouldn't be so huge we
+// end up DoSing ourselves.
+const MaxBlockLookback = 100
+
+// Base service that provides implementation for all underlying RPC methods
+type Service interface {
+	// Transact
+	BroadcastTx(tx txs.Tx) (*ResultBroadcastTx, error)
+	// Events
+	Subscribe(eventId string, callback func(eventData event.AnyEventData)) (*ResultSubscribe, error)
+	Unsubscribe(subscriptionId string) (*ResultUnsubscribe, error)
+	// Status
+	Status() (*ResultStatus, error)
+	NetInfo() (*ResultNetInfo, error)
+	// Accounts
+	GetAccount(address acm.Address) (*ResultGetAccount, error)
+	ListAccounts() (*ResultListAccounts, error)
+	GetStorage(address acm.Address, key []byte) (*ResultGetStorage, error)
+	DumpStorage(address acm.Address) (*ResultDumpStorage, error)
+	// Simulated call
+	Call(fromAddress, toAddress acm.Address, data []byte) (*ResultCall, error)
+	CallCode(fromAddress acm.Address, code, data []byte) (*ResultCall, error)
+	// Blockchain
+	Genesis() (*ResultGenesis, error)
+	ChainId() (*ResultChainId, error)
+	BlockchainInfo(minHeight, maxHeight uint64) (*ResultBlockchainInfo, error)
+	GetBlock(height uint64) (*ResultGetBlock, error)
+	// Consensus
+	ListUnconfirmedTxs(maxTxs int) (*ResultListUnconfirmedTxs, error)
+	ListValidators() (*ResultListValidators, error)
+	DumpConsensusState() (*ResultDumpConsensusState, error)
+	Peers() (*ResultPeers, error)
+	// Names
+	GetName(name string) (*ResultGetName, error)
+	ListNames() (*ResultListNames, error)
+	// Private keys and signing
+	SignTx(tx txs.Tx, concretePrivateAccounts []*acm.ConcretePrivateAccount) (*ResultSignTx, error)
+	GeneratePrivateAccount() (*ResultGeneratePrivateAccount, error)
+}
+
+type service struct {
+	state        acm.StateIterable
+	eventEmitter event.Emitter
+	nameReg      execution.NameRegIterable
+	blockchain   bcm.Blockchain
+	transactor   execution.Transactor
+	nodeView     query.NodeView
+	logger       logging_types.InfoTraceLogger
+}
+
+var _ Service = &service{}
+
+func NewService(state acm.StateIterable, eventEmitter event.Emitter, nameReg execution.NameRegIterable,
+	blockchain bcm.Blockchain, transactor execution.Transactor, nodeView query.NodeView,
+	logger logging_types.InfoTraceLogger) *service {
+
+	return &service{
+		state:        state,
+		eventEmitter: eventEmitter,
+		nameReg:      nameReg,
+		blockchain:   blockchain,
+		transactor:   transactor,
+		nodeView:     nodeView,
+		logger:       logger.With(structure.ComponentKey, "Service"),
+	}
+}
+
+// All methods in this file return (Result*, error) which is the return
+// signature assumed by go-rpc
+
+func (s *service) Subscribe(eventId string, callback func(event.AnyEventData)) (*ResultSubscribe, error) {
+	subscriptionId, err := event.GenerateSubId()
+
+	logging.InfoMsg(s.logger, "Subscribing to event",
+		"eventId", eventId, "subscriptionId", subscriptionId)
+	if err != nil {
+		return nil, err
+	}
+	err = s.eventEmitter.Subscribe(subscriptionId, eventId, callback)
+	if err != nil {
+		return nil, err
+	}
+	return &ResultSubscribe{
+		SubscriptionId: subscriptionId,
+		Event:          eventId,
+	}, nil
+}
+
+func (s *service) Unsubscribe(subscriptionId string) (*ResultUnsubscribe, error) {
+	err := s.eventEmitter.Unsubscribe(subscriptionId)
+	if err != nil {
+		return nil, err
+	} else {
+		return &ResultUnsubscribe{SubscriptionId: subscriptionId}, nil
+	}
+}
+
+func (s *service) Status() (*ResultStatus, error) {
+	tip := s.blockchain.Tip()
+	latestHeight := tip.LastBlockHeight()
+	var (
+		latestBlockMeta *tm_types.BlockMeta
+		latestBlockHash []byte
+		latestBlockTime int64
+	)
+	if latestHeight != 0 {
+		latestBlockMeta = s.nodeView.BlockStore().LoadBlockMeta(int(latestHeight))
+		latestBlockHash = latestBlockMeta.Header.Hash()
+		latestBlockTime = latestBlockMeta.Header.Time.UnixNano()
+	}
+	return &ResultStatus{
+		NodeInfo:          s.nodeView.NodeInfo(),
+		GenesisHash:       s.blockchain.GenesisHash(),
+		PubKey:            s.nodeView.PrivValidatorPublicKey(),
+		LatestBlockHash:   latestBlockHash,
+		LatestBlockHeight: latestHeight,
+		LatestBlockTime:   latestBlockTime}, nil
+}
+
+func (s *service) ChainId() (*ResultChainId, error) {
+	return &ResultChainId{
+		ChainName:   s.blockchain.GenesisDoc().ChainName,
+		ChainId:     s.blockchain.ChainID(),
+		GenesisHash: s.blockchain.GenesisHash(),
+	}, nil
+}
+
+func (s *service) Peers() (*ResultPeers, error) {
+	peers := make([]*Peer, s.nodeView.Peers().Size())
+	for i, peer := range s.nodeView.Peers().List() {
+		peers[i] = &Peer{
+			NodeInfo:   peer.NodeInfo(),
+			IsOutbound: peer.IsOutbound(),
+		}
+	}
+	return &ResultPeers{
+		Peers: peers,
+	}, nil
+}
+
+func (s *service) NetInfo() (*ResultNetInfo, error) {
+	listening := s.nodeView.IsListening()
+	listeners := []string{}
+	for _, listener := range s.nodeView.Listeners() {
+		listeners = append(listeners, listener.String())
+	}
+	peers, err := s.Peers()
+	if err != nil {
+		return nil, err
+	}
+	return &ResultNetInfo{
+		Listening: listening,
+		Listeners: listeners,
+		Peers:     peers.Peers,
+	}, nil
+}
+
+func (s *service) Genesis() (*ResultGenesis, error) {
+	return &ResultGenesis{
+		Genesis: s.blockchain.GenesisDoc(),
+	}, nil
+}
+
+// Accounts
+func (s *service) GetAccount(address acm.Address) (*ResultGetAccount, error) {
+	acc, err := s.state.GetAccount(address)
+	if err != nil {
+		return nil, err
+	}
+	return &ResultGetAccount{Account: acm.AsConcreteAccount(acc)}, nil
+}
+
+func (s *service) ListAccounts() (*ResultListAccounts, error) {
+	accounts := make([]*acm.ConcreteAccount, 0)
+	s.state.IterateAccounts(func(account acm.Account) (stop bool) {
+		accounts = append(accounts, acm.AsConcreteAccount(account))
+		return
+	})
+
+	return &ResultListAccounts{
+		BlockHeight: s.blockchain.Tip().LastBlockHeight(),
+		Accounts:    accounts,
+	}, nil
+}
+
+func (s *service) GetStorage(address acm.Address, key []byte) (*ResultGetStorage, error) {
+	account, err := s.state.GetAccount(address)
+	if err != nil {
+		return nil, err
+	}
+	if account == nil {
+		return nil, fmt.Errorf("UnknownAddress: %s", address)
+	}
+
+	value, err := s.state.GetStorage(address, binary.LeftPadWord256(key))
+	if err != nil {
+		return nil, err
+	}
+	if value == binary.Zero256 {
+		return &ResultGetStorage{Key: key, Value: nil}, nil
+	}
+	return &ResultGetStorage{Key: key, Value: value.UnpadLeft()}, nil
+}
+
+func (s *service) DumpStorage(address acm.Address) (*ResultDumpStorage, error) {
+	account, err := s.state.GetAccount(address)
+	if err != nil {
+		return nil, err
+	}
+	if account == nil {
+		return nil, fmt.Errorf("UnknownAddress: %X", address)
+	}
+	storageItems := []StorageItem{}
+	s.state.IterateStorage(address, func(key, value binary.Word256) (stop bool) {
+		storageItems = append(storageItems, StorageItem{Key: key.UnpadLeft(), Value: value.UnpadLeft()})
+		return
+	})
+	return &ResultDumpStorage{
+		StorageRoot:  account.StorageRoot(),
+		StorageItems: storageItems,
+	}, nil
+}
+
+func (s *service) Call(fromAddress, toAddress acm.Address, data []byte) (*ResultCall, error) {
+	call, err := s.transactor.Call(fromAddress, toAddress, data)
+	if err != nil {
+		return nil, err
+	}
+	return &ResultCall{
+		Call: call,
+	}, nil
+}
+
+func (s *service) CallCode(fromAddress acm.Address, code, data []byte) (*ResultCall, error) {
+	call, err := s.transactor.CallCode(fromAddress, code, data)
+	if err != nil {
+		return nil, err
+	}
+	return &ResultCall{
+		Call: call,
+	}, nil
+}
+
+// Name registry
+func (s *service) GetName(name string) (*ResultGetName, error) {
+	entry := s.nameReg.GetNameRegEntry(name)
+	if entry == nil {
+		return nil, fmt.Errorf("name %s not found", name)
+	}
+	return &ResultGetName{Entry: entry}, nil
+}
+
+func (s *service) ListNames() (*ResultListNames, error) {
+	var names []*execution.NameRegEntry
+	s.nameReg.IterateNameRegEntries(func(entry *execution.NameRegEntry) (stop bool) {
+		names = append(names, entry)
+		return false
+	})
+	return &ResultListNames{
+		BlockHeight: s.blockchain.Tip().LastBlockHeight(),
+		Names:       names,
+	}, nil
+}
+
+func (s *service) BroadcastTx(tx txs.Tx) (*ResultBroadcastTx, error) {
+	receipt, err := s.transactor.BroadcastTx(tx)
+	if err != nil {
+		return nil, err
+	}
+	return &ResultBroadcastTx{
+		Receipt: receipt,
+	}, nil
+}
+
+func (s *service) ListUnconfirmedTxs(maxTxs int) (*ResultListUnconfirmedTxs, error) {
+	// Get all transactions for now
+	transactions, err := s.nodeView.MempoolTransactions(maxTxs)
+	if err != nil {
+		return nil, err
+	}
+	wrappedTxs := make([]txs.Wrapper, len(transactions))
+	for i, tx := range transactions {
+		wrappedTxs[i] = txs.Wrap(tx)
+	}
+	return &ResultListUnconfirmedTxs{
+		N:   len(transactions),
+		Txs: wrappedTxs,
+	}, nil
+}
+
+// Returns the current blockchain height and metadata for a range of blocks
+// between minHeight and maxHeight. Only returns maxBlockLookback block metadata
+// from the top of the range of blocks.
+// Passing 0 for maxHeight sets the upper height of the range to the current
+// blockchain height.
+func (s *service) BlockchainInfo(minHeight, maxHeight uint64) (*ResultBlockchainInfo, error) {
+	latestHeight := s.blockchain.Tip().LastBlockHeight()
+
+	if minHeight == 0 {
+		minHeight = 1
+	}
+	if maxHeight == 0 || latestHeight < maxHeight {
+		maxHeight = latestHeight
+	}
+	if maxHeight > minHeight && maxHeight-minHeight > MaxBlockLookback {
+		minHeight = maxHeight - MaxBlockLookback
+	}
+
+	blockMetas := []*tm_types.BlockMeta{}
+	for height := maxHeight; height >= minHeight; height-- {
+		blockMeta := s.nodeView.BlockStore().LoadBlockMeta(int(height))
+		blockMetas = append(blockMetas, blockMeta)
+	}
+
+	return &ResultBlockchainInfo{
+		LastHeight: latestHeight,
+		BlockMetas: blockMetas,
+	}, nil
+}
+
+func (s *service) GetBlock(height uint64) (*ResultGetBlock, error) {
+	return &ResultGetBlock{
+		Block:     s.nodeView.BlockStore().LoadBlock(int(height)),
+		BlockMeta: s.nodeView.BlockStore().LoadBlockMeta(int(height)),
+	}, nil
+}
+
+func (s *service) ListValidators() (*ResultListValidators, error) {
+	// TODO: when we reintroduce support for bonding and unbonding update this
+	// to reflect the mutable bonding state
+	validators := s.blockchain.Validators()
+	concreteValidators := make([]*acm.ConcreteValidator, len(validators))
+	for i, validator := range validators {
+		concreteValidators[i] = acm.AsConcreteValidator(validator)
+	}
+	return &ResultListValidators{
+		BlockHeight:         s.blockchain.Tip().LastBlockHeight(),
+		BondedValidators:    concreteValidators,
+		UnbondingValidators: nil,
+	}, nil
+}
+
+func (s *service) DumpConsensusState() (*ResultDumpConsensusState, error) {
+	peerRoundState, err := s.nodeView.PeerRoundStates()
+	if err != nil {
+		return nil, err
+	}
+	return &ResultDumpConsensusState{
+		RoundState:      s.nodeView.RoundState(),
+		PeerRoundStates: peerRoundState,
+	}, nil
+}
+
+// TODO: Either deprecate this or ensure it can only happen over secure transport
+func (s *service) SignTx(tx txs.Tx, concretePrivateAccounts []*acm.ConcretePrivateAccount) (*ResultSignTx, error) {
+	privateAccounts := make([]acm.PrivateAccount, len(concretePrivateAccounts))
+	for i, cpa := range concretePrivateAccounts {
+		privateAccounts[i] = cpa.PrivateAccount()
+	}
+
+	tx, err := s.transactor.SignTx(tx, privateAccounts)
+	return &ResultSignTx{Tx: txs.Wrap(tx)}, err
+}
+
+func (s *service) GeneratePrivateAccount() (*ResultGeneratePrivateAccount, error) {
+	return &ResultGeneratePrivateAccount{
+		PrivAccount: acm.GeneratePrivateAccount().ConcretePrivateAccount,
+	}, nil
+}
diff --git a/rpc/tendermint/client/client.go b/rpc/tendermint/client/client.go
deleted file mode 100644
index fe9f8ca55e774e61098074c89a94b9f2e2b957e5..0000000000000000000000000000000000000000
--- a/rpc/tendermint/client/client.go
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package client
-
-import (
-	"errors"
-	"fmt"
-
-	acm "github.com/hyperledger/burrow/account"
-	core_types "github.com/hyperledger/burrow/core/types"
-	rpc_types "github.com/hyperledger/burrow/rpc/tendermint/core/types"
-	"github.com/hyperledger/burrow/txs"
-	"github.com/tendermint/go-wire"
-)
-
-type RPCClient interface {
-	Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
-}
-
-func Status(client RPCClient) (*rpc_types.ResultStatus, error) {
-	res, err := call(client, "status")
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultStatus), nil
-}
-
-func ChainId(client RPCClient) (*rpc_types.ResultChainId, error) {
-	res, err := call(client, "chain_id")
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultChainId), nil
-}
-
-func GenPrivAccount(client RPCClient) (*acm.PrivAccount, error) {
-	res, err := call(client, "unsafe/gen_priv_account")
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultGenPrivAccount).PrivAccount, nil
-}
-
-func GetAccount(client RPCClient, address []byte) (*acm.Account, error) {
-	res, err := call(client, "get_account",
-		"address", address)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultGetAccount).Account, nil
-}
-
-func SignTx(client RPCClient, tx txs.Tx,
-	privAccounts []*acm.PrivAccount) (txs.Tx, error) {
-	res, err := call(client, "unsafe/sign_tx",
-		"tx", wrappedTx{tx},
-		"privAccounts", privAccounts)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultSignTx).Tx, nil
-}
-
-func BroadcastTx(client RPCClient,
-	tx txs.Tx) (txs.Receipt, error) {
-	res, err := call(client, "broadcast_tx",
-		"tx", wrappedTx{tx})
-	if err != nil {
-		return txs.Receipt{}, err
-	}
-	receiptBytes := res.(*rpc_types.ResultBroadcastTx).Data
-	receipt := txs.Receipt{}
-	err = wire.ReadBinaryBytes(receiptBytes, &receipt)
-	return receipt, err
-
-}
-
-func DumpStorage(client RPCClient,
-	address []byte) (*rpc_types.ResultDumpStorage, error) {
-	res, err := call(client, "dump_storage",
-		"address", address)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultDumpStorage), err
-}
-
-func GetStorage(client RPCClient, address, key []byte) ([]byte, error) {
-	res, err := call(client, "get_storage",
-		"address", address,
-		"key", key)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultGetStorage).Value, nil
-}
-
-func CallCode(client RPCClient, fromAddress, code,
-	data []byte) (*rpc_types.ResultCall, error) {
-	res, err := call(client, "call_code",
-		"fromAddress", fromAddress,
-		"code", code,
-		"data", data)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultCall), err
-}
-
-func Call(client RPCClient, fromAddress, toAddress,
-	data []byte) (*rpc_types.ResultCall, error) {
-	res, err := call(client, "call",
-		"fromAddress", fromAddress,
-		"toAddress", toAddress,
-		"data", data)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultCall), err
-}
-
-func GetName(client RPCClient, name string) (*core_types.NameRegEntry, error) {
-	res, err := call(client, "get_name",
-		"name", name)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultGetName).Entry, nil
-}
-
-func BlockchainInfo(client RPCClient, minHeight,
-	maxHeight int) (*rpc_types.ResultBlockchainInfo, error) {
-	res, err := call(client, "blockchain",
-		"minHeight", minHeight,
-		"maxHeight", maxHeight)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultBlockchainInfo), err
-}
-
-func GetBlock(client RPCClient, height int) (*rpc_types.ResultGetBlock, error) {
-	res, err := call(client, "get_block",
-		"height", height)
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultGetBlock), err
-}
-
-func ListUnconfirmedTxs(client RPCClient) (*rpc_types.ResultListUnconfirmedTxs, error) {
-	res, err := call(client, "list_unconfirmed_txs")
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultListUnconfirmedTxs), err
-}
-
-func ListValidators(client RPCClient) (*rpc_types.ResultListValidators, error) {
-	res, err := call(client, "list_validators")
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultListValidators), err
-}
-
-func DumpConsensusState(client RPCClient) (*rpc_types.ResultDumpConsensusState, error) {
-	res, err := call(client, "dump_consensus_state")
-	if err != nil {
-		return nil, err
-	}
-	return res.(*rpc_types.ResultDumpConsensusState), err
-}
-
-func call(client RPCClient, method string,
-	paramKeyVals ...interface{}) (res rpc_types.BurrowResult, err error) {
-	pMap, err := paramsMap(paramKeyVals...)
-	if err != nil {
-		return
-	}
-	_, err = client.Call(method, pMap, &res)
-	return
-}
-
-func paramsMap(orderedKeyVals ...interface{}) (map[string]interface{}, error) {
-	if len(orderedKeyVals)%2 != 0 {
-		return nil, fmt.Errorf("mapAndValues requires a even length list of"+
-			" keys and values but got: %v (length %v)",
-			orderedKeyVals, len(orderedKeyVals))
-	}
-	paramsMap := make(map[string]interface{})
-	for i := 0; i < len(orderedKeyVals); i += 2 {
-		key, ok := orderedKeyVals[i].(string)
-		if !ok {
-			return nil, errors.New("mapAndValues requires every even element" +
-				" of orderedKeyVals to be a string key")
-		}
-		val := orderedKeyVals[i+1]
-		paramsMap[key] = val
-	}
-	return paramsMap, nil
-}
-
-type wrappedTx struct {
-	txs.Tx `json:"unwrap"`
-}
diff --git a/rpc/tendermint/client/client_test.go b/rpc/tendermint/client/client_test.go
deleted file mode 100644
index 13a5c98e48e668d53accfb113e91bf06c1ef72d1..0000000000000000000000000000000000000000
--- a/rpc/tendermint/client/client_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package client
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestParamsMap(t *testing.T) {
-	type aStruct struct {
-		Baz int
-	}
-	dict, err := paramsMap("Foo", aStruct{5},
-		"Bar", "Nibbles")
-	assert.NoError(t, err, "Should not be a paramsMaperror")
-	assert.Equal(t, map[string]interface{}{
-		"Foo": aStruct{5},
-		"Bar": "Nibbles",
-	}, dict)
-
-	// Empty map
-	dict, err = paramsMap()
-	assert.Equal(t, map[string]interface{}{}, dict)
-	assert.NoError(t, err, "Empty mapsAndValues call should be fine")
-
-	// Invalid maps
-	assert.NoError(t, err, "Empty mapsAndValues call should be fine")
-	_, err = paramsMap("Foo", 4, "Bar")
-	assert.Error(t, err, "Should be an error to get an odd number of arguments")
-
-	_, err = paramsMap("Foo", 4, 4, "Bar")
-	assert.Error(t, err, "Should be an error to provide non-string keys")
-}
diff --git a/rpc/tendermint/client/websocket_client.go b/rpc/tendermint/client/websocket_client.go
deleted file mode 100644
index 4749517b72febf7b62929fe4fa7b18c986567bdb..0000000000000000000000000000000000000000
--- a/rpc/tendermint/client/websocket_client.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package client
-
-import "github.com/tendermint/go-rpc/types"
-
-type WebsocketClient interface {
-	WriteJSON(v interface{}) error
-}
-
-func Subscribe(websocketClient WebsocketClient, eventId string) error {
-	return websocketClient.WriteJSON(rpctypes.RPCRequest{
-		JSONRPC: "2.0",
-		ID:      "",
-		Method:  "subscribe",
-		Params:  map[string]interface{}{"eventId": eventId},
-	})
-}
-
-func Unsubscribe(websocketClient WebsocketClient, subscriptionId string) error {
-	return websocketClient.WriteJSON(rpctypes.RPCRequest{
-		JSONRPC: "2.0",
-		ID:      "",
-		Method:  "unsubscribe",
-		Params:  map[string]interface{}{"subscriptionId": subscriptionId},
-	})
-}
diff --git a/rpc/tendermint/core/routes.go b/rpc/tendermint/core/routes.go
deleted file mode 100644
index b295b3a6de5d12b5b71dccb8b274f35aad7143c3..0000000000000000000000000000000000000000
--- a/rpc/tendermint/core/routes.go
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package core
-
-import (
-	"fmt"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/definitions"
-	ctypes "github.com/hyperledger/burrow/rpc/tendermint/core/types"
-	"github.com/hyperledger/burrow/txs"
-	rpc "github.com/tendermint/go-rpc/server"
-	rpctypes "github.com/tendermint/go-rpc/types"
-)
-
-// TODO: [ben] encapsulate Routes into a struct for a given TendermintPipe
-
-// Magic! Should probably be configurable, but not shouldn't be so huge we
-// end up DoSing ourselves.
-const maxBlockLookback = 20
-
-// TODO: eliminate redundancy between here and reading code from core/
-type TendermintRoutes struct {
-	tendermintPipe definitions.TendermintPipe
-}
-
-func (tmRoutes *TendermintRoutes) GetRoutes() map[string]*rpc.RPCFunc {
-	var routes = map[string]*rpc.RPCFunc{
-		"subscribe":               rpc.NewWSRPCFunc(tmRoutes.Subscribe, "eventId"),
-		"unsubscribe":             rpc.NewWSRPCFunc(tmRoutes.Unsubscribe, "subscriptionId"),
-		"status":                  rpc.NewRPCFunc(tmRoutes.StatusResult, ""),
-		"net_info":                rpc.NewRPCFunc(tmRoutes.NetInfoResult, ""),
-		"genesis":                 rpc.NewRPCFunc(tmRoutes.GenesisResult, ""),
-		"chain_id":                rpc.NewRPCFunc(tmRoutes.ChainIdResult, ""),
-		"get_account":             rpc.NewRPCFunc(tmRoutes.GetAccountResult, "address"),
-		"get_storage":             rpc.NewRPCFunc(tmRoutes.GetStorageResult, "address,key"),
-		"call":                    rpc.NewRPCFunc(tmRoutes.CallResult, "fromAddress,toAddress,data"),
-		"call_code":               rpc.NewRPCFunc(tmRoutes.CallCodeResult, "fromAddress,code,data"),
-		"dump_storage":            rpc.NewRPCFunc(tmRoutes.DumpStorageResult, "address"),
-		"list_accounts":           rpc.NewRPCFunc(tmRoutes.ListAccountsResult, ""),
-		"get_name":                rpc.NewRPCFunc(tmRoutes.GetNameResult, "name"),
-		"list_names":              rpc.NewRPCFunc(tmRoutes.ListNamesResult, ""),
-		"broadcast_tx":            rpc.NewRPCFunc(tmRoutes.BroadcastTxResult, "tx"),
-		"blockchain":              rpc.NewRPCFunc(tmRoutes.BlockchainInfo, "minHeight,maxHeight"),
-		"get_block":               rpc.NewRPCFunc(tmRoutes.GetBlock, "height"),
-		"list_unconfirmed_txs":    rpc.NewRPCFunc(tmRoutes.ListUnconfirmedTxs, ""),
-		"list_validators":         rpc.NewRPCFunc(tmRoutes.ListValidators, ""),
-		"dump_consensus_state":    rpc.NewRPCFunc(tmRoutes.DumpConsensusState, ""),
-		"unsafe/gen_priv_account": rpc.NewRPCFunc(tmRoutes.GenPrivAccountResult, ""),
-		"unsafe/sign_tx":          rpc.NewRPCFunc(tmRoutes.SignTxResult, "tx,privAccounts"),
-		// TODO: [Silas] do we also carry forward "consensus_state" as in v0?
-	}
-	return routes
-}
-
-func (tmRoutes *TendermintRoutes) Subscribe(wsCtx rpctypes.WSRPCContext,
-	eventId string) (ctypes.BurrowResult, error) {
-	// NOTE: RPCResponses of subscribed events have id suffix "#event"
-	// TODO: we really ought to allow multiple subscriptions from the same client address
-	// to the same event. The code as it stands reflects the somewhat broken tendermint
-	// implementation. We can use GenerateSubId to randomize the subscriptions id
-	// and return it in the result. This would require clients to hang on to a
-	// subscription id if they wish to unsubscribe, but then again they can just
-	// drop their connection
-	result, err := tmRoutes.tendermintPipe.Subscribe(eventId,
-		func(result ctypes.BurrowResult) {
-			wsCtx.GetRemoteAddr()
-			// NOTE: EventSwitch callbacks must be nonblocking
-			wsCtx.TryWriteRPCResponse(
-				rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", &result, ""))
-		})
-	if err != nil {
-		return nil, err
-	} else {
-		return result, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) Unsubscribe(wsCtx rpctypes.WSRPCContext,
-	subscriptionId string) (ctypes.BurrowResult, error) {
-	result, err := tmRoutes.tendermintPipe.Unsubscribe(subscriptionId)
-	if err != nil {
-		return nil, err
-	} else {
-		return result, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) StatusResult() (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.Status(); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) NetInfoResult() (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.NetInfo(); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) GenesisResult() (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.Genesis(); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) ChainIdResult() (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.ChainId(); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) GetAccountResult(address []byte) (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.GetAccount(address); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) GetStorageResult(address, key []byte) (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.GetStorage(address, key); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) CallResult(fromAddress, toAddress,
-	data []byte) (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.Call(fromAddress, toAddress, data); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) CallCodeResult(fromAddress, code,
-	data []byte) (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.CallCode(fromAddress, code, data); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) DumpStorageResult(address []byte) (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.DumpStorage(address); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) ListAccountsResult() (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.ListAccounts(); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) GetNameResult(name string) (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.GetName(name); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) ListNamesResult() (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.ListNames(); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) GenPrivAccountResult() (ctypes.BurrowResult, error) {
-	//if r, err := tmRoutes.tendermintPipe.GenPrivAccount(); err != nil {
-	//	return nil, err
-	//} else {
-	//	return r, nil
-	//}
-	return nil, fmt.Errorf("Unimplemented as poor practice to generate private account over unencrypted RPC")
-}
-
-func (tmRoutes *TendermintRoutes) SignTxResult(tx txs.Tx,
-	privAccounts []*acm.PrivAccount) (ctypes.BurrowResult, error) {
-	// if r, err := tmRoutes.tendermintPipe.SignTx(tx, privAccounts); err != nil {
-	// 	return nil, err
-	// } else {
-	// 	return r, nil
-	// }
-	return nil, fmt.Errorf("Unimplemented as poor practice to pass private account over unencrypted RPC")
-}
-
-func (tmRoutes *TendermintRoutes) BroadcastTxResult(tx txs.Tx) (ctypes.BurrowResult, error) {
-	if r, err := tmRoutes.tendermintPipe.BroadcastTxSync(tx); err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) BlockchainInfo(minHeight,
-	maxHeight int) (ctypes.BurrowResult, error) {
-	r, err := tmRoutes.tendermintPipe.BlockchainInfo(minHeight, maxHeight,
-		maxBlockLookback)
-	if err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-
-func (tmRoutes *TendermintRoutes) ListUnconfirmedTxs() (ctypes.BurrowResult, error) {
-	// Get all Txs for now
-	r, err := tmRoutes.tendermintPipe.ListUnconfirmedTxs(-1)
-	if err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-func (tmRoutes *TendermintRoutes) GetBlock(height int) (ctypes.BurrowResult, error) {
-	r, err := tmRoutes.tendermintPipe.GetBlock(height)
-	if err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-func (tmRoutes *TendermintRoutes) ListValidators() (ctypes.BurrowResult, error) {
-	r, err := tmRoutes.tendermintPipe.ListValidators()
-	if err != nil {
-		return nil, err
-	} else {
-		return r, nil
-	}
-}
-func (tmRoutes *TendermintRoutes) DumpConsensusState() (ctypes.BurrowResult, error) {
-	return tmRoutes.tendermintPipe.DumpConsensusState()
-}
diff --git a/rpc/tendermint/core/types/responses.go b/rpc/tendermint/core/types/responses.go
deleted file mode 100644
index 33b46d4265e23f7f7831fc2ca74effe45b0a2c15..0000000000000000000000000000000000000000
--- a/rpc/tendermint/core/types/responses.go
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package types
-
-import (
-	acm "github.com/hyperledger/burrow/account"
-	core_types "github.com/hyperledger/burrow/core/types"
-	genesis "github.com/hyperledger/burrow/genesis"
-	"github.com/hyperledger/burrow/txs"
-	tendermint_types "github.com/tendermint/tendermint/types"
-
-	consensus_types "github.com/hyperledger/burrow/consensus/types"
-	abcitypes "github.com/tendermint/abci/types"
-	"github.com/tendermint/go-crypto"
-	"github.com/tendermint/go-p2p"
-	"github.com/tendermint/go-rpc/types"
-	"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 []*tendermint_types.BlockMeta `json:"block_metas"`
-}
-
-type ResultGetBlock struct {
-	BlockMeta *tendermint_types.BlockMeta `json:"block_meta"`
-	Block     *tendermint_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 ResultChainId struct {
-	ChainName   string `json:"chain_name"`
-	ChainId     string `json:"chain_id"`
-	GenesisHash []byte `json:"genesis_hash"`
-}
-
-type ResultSubscribe struct {
-	Event          string `json:"event"`
-	SubscriptionId string `json:"subscription_id"`
-}
-
-type ResultUnsubscribe struct {
-	SubscriptionId string `json:"subscription_id"`
-}
-
-type ResultNetInfo struct {
-	Listening bool                    `json:"listening"`
-	Listeners []string                `json:"listeners"`
-	Peers     []*consensus_types.Peer `json:"peers"`
-}
-
-type ResultListValidators struct {
-	BlockHeight         int                         `json:"block_height"`
-	BondedValidators    []consensus_types.Validator `json:"bonded_validators"`
-	UnbondingValidators []consensus_types.Validator `json:"unbonding_validators"`
-}
-
-type ResultDumpConsensusState struct {
-	ConsensusState      *consensus_types.ConsensusState `json:"consensus_state"`
-	PeerConsensusStates []*ResultPeerConsensusState     `json:"peer_consensus_states"`
-}
-
-type ResultPeerConsensusState struct {
-	PeerKey            string `json:"peer_key"`
-	PeerConsensusState string `json:"peer_consensus_state"`
-}
-
-type ResultListNames struct {
-	BlockHeight int                        `json:"block_height"`
-	Names       []*core_types.NameRegEntry `json:"names"`
-}
-
-type ResultGenPrivAccount struct {
-	PrivAccount *acm.PrivAccount `json:"priv_account"`
-}
-
-type ResultGetAccount struct {
-	Account *acm.Account `json:"account"`
-}
-
-type ResultBroadcastTx struct {
-	Code abcitypes.CodeType `json:"code"`
-	Data []byte             `json:"data"`
-	Log  string             `json:"log"`
-}
-
-type ResultListUnconfirmedTxs struct {
-	N   int      `json:"n_txs"`
-	Txs []txs.Tx `json:"txs"`
-}
-
-type ResultGetName struct {
-	Entry *core_types.NameRegEntry `json:"entry"`
-}
-
-type ResultGenesis struct {
-	Genesis *genesis.GenesisDoc `json:"genesis"`
-}
-
-type ResultSignTx struct {
-	Tx txs.Tx `json:"tx"`
-}
-
-type ResultEvent struct {
-	Event string        `json:"event"`
-	Data  txs.EventData `json:"data"`
-}
-
-//----------------------------------------
-// result types
-
-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
-	ResultTypeSubscribe          = byte(0x14)
-	ResultTypeUnsubscribe        = byte(0x15)
-	ResultTypePeerConsensusState = byte(0x16)
-	ResultTypeChainId            = byte(0x17)
-)
-
-type BurrowResult interface {
-	rpctypes.Result
-}
-
-func ConcreteTypes() []wire.ConcreteType {
-	return []wire.ConcreteType{
-		{&ResultGetStorage{}, ResultTypeGetStorage},
-		{&ResultCall{}, ResultTypeCall},
-		{&ResultListAccounts{}, ResultTypeListAccounts},
-		{&ResultDumpStorage{}, ResultTypeDumpStorage},
-		{&ResultBlockchainInfo{}, ResultTypeBlockchainInfo},
-		{&ResultGetBlock{}, ResultTypeGetBlock},
-		{&ResultStatus{}, ResultTypeStatus},
-		{&ResultNetInfo{}, ResultTypeNetInfo},
-		{&ResultListValidators{}, ResultTypeListValidators},
-		{&ResultDumpConsensusState{}, ResultTypeDumpConsensusState},
-		{&ResultDumpConsensusState{}, ResultTypePeerConsensusState},
-		{&ResultListNames{}, ResultTypeListNames},
-		{&ResultGenPrivAccount{}, ResultTypeGenPrivAccount},
-		{&ResultGetAccount{}, ResultTypeGetAccount},
-		{&ResultBroadcastTx{}, ResultTypeBroadcastTx},
-		{&ResultListUnconfirmedTxs{}, ResultTypeListUnconfirmedTxs},
-		{&ResultGetName{}, ResultTypeGetName},
-		{&ResultGenesis{}, ResultTypeGenesis},
-		{&ResultSignTx{}, ResultTypeSignTx},
-		{&ResultEvent{}, ResultTypeEvent},
-		{&ResultSubscribe{}, ResultTypeSubscribe},
-		{&ResultUnsubscribe{}, ResultTypeUnsubscribe},
-		{&ResultChainId{}, ResultTypeChainId},
-	}
-}
-
-var _ = wire.RegisterInterface(struct{ BurrowResult }{}, ConcreteTypes()...)
diff --git a/rpc/tendermint/core/types/responses_test.go b/rpc/tendermint/core/types/responses_test.go
deleted file mode 100644
index c745017c8d74a8bc9d695667c5dec01a1abbb983..0000000000000000000000000000000000000000
--- a/rpc/tendermint/core/types/responses_test.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package types
-
-import (
-	"testing"
-
-	"time"
-
-	consensus_types "github.com/hyperledger/burrow/consensus/types"
-	"github.com/tendermint/go-wire"
-	tendermint_types "github.com/tendermint/tendermint/types"
-)
-
-func TestResultDumpConsensusState(t *testing.T) {
-	result := ResultDumpConsensusState{
-		ConsensusState: &consensus_types.ConsensusState{
-			Height:     3,
-			Round:      1,
-			Step:       uint8(1),
-			StartTime:  time.Now().Add(-time.Second * 100),
-			CommitTime: time.Now().Add(-time.Second * 10),
-			Validators: []consensus_types.Validator{
-				&consensus_types.TendermintValidator{},
-			},
-			Proposal: &tendermint_types.Proposal{},
-		},
-		PeerConsensusStates: []*ResultPeerConsensusState{
-			{
-				PeerKey:            "Foo",
-				PeerConsensusState: "Bar",
-			},
-		},
-	}
-	wire.JSONBytes(result)
-}
diff --git a/rpc/tendermint/core/types/responses_util.go b/rpc/tendermint/core/types/responses_util.go
deleted file mode 100644
index cd3e15e8c6675dd92aaf19610bc6f52608c87e8c..0000000000000000000000000000000000000000
--- a/rpc/tendermint/core/types/responses_util.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package types
-
-import (
-	"github.com/hyperledger/burrow/core/types"
-)
-
-// UnwrapResultDumpStorage does a deep copy to remove /rpc/tendermint/core/types
-// and expose /core/types instead.  This is largely an artefact to be removed once
-// go-wire and go-rpc are deprecated.
-// This is not an efficient code, especially given Storage can be big.
-func UnwrapResultDumpStorage(result *ResultDumpStorage) *types.Storage {
-	storageRoot := make([]byte, len(result.StorageRoot))
-	copy(storageRoot, result.StorageRoot)
-	storageItems := make([]types.StorageItem, len(result.StorageItems))
-	for i, item := range result.StorageItems {
-		key := make([]byte, len(item.Key))
-		value := make([]byte, len(item.Value))
-		copy(key, item.Key)
-		copy(value, item.Value)
-		storageItems[i] = types.StorageItem{
-			Key:   key,
-			Value: value,
-		}
-	}
-	return &types.Storage{
-		StorageRoot:  storageRoot,
-		StorageItems: storageItems,
-	}
-}
diff --git a/rpc/tendermint/core/websocket.go b/rpc/tendermint/core/websocket.go
deleted file mode 100644
index 2a4feffe1ff25787d298a4e51c2cd6232782e7ad..0000000000000000000000000000000000000000
--- a/rpc/tendermint/core/websocket.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package core
-
-import (
-	"fmt"
-	"net"
-	"net/http"
-	"strings"
-
-	events "github.com/tendermint/go-events"
-	rpcserver "github.com/tendermint/go-rpc/server"
-
-	definitions "github.com/hyperledger/burrow/definitions"
-	server "github.com/hyperledger/burrow/server"
-)
-
-type TendermintWebsocketServer struct {
-	routes    TendermintRoutes
-	listeners []net.Listener
-}
-
-func NewTendermintWebsocketServer(config *server.ServerConfig,
-	tendermintPipe definitions.TendermintPipe, evsw events.EventSwitch) (
-	*TendermintWebsocketServer, error) {
-
-	if tendermintPipe == nil {
-		return nil, fmt.Errorf("No Tendermint pipe provided.")
-	}
-	tendermintRoutes := TendermintRoutes{
-		tendermintPipe: tendermintPipe,
-	}
-	routes := tendermintRoutes.GetRoutes()
-	listenerAddresses := strings.Split(config.Tendermint.RpcLocalAddress, ",")
-	if len(listenerAddresses) == 0 {
-		return nil, fmt.Errorf("No RPC listening addresses provided in [servers.tendermint.rpc_local_address] in configuration file: %s",
-			listenerAddresses)
-	}
-	listeners := make([]net.Listener, len(listenerAddresses))
-	for i, listenerAddress := range listenerAddresses {
-		mux := http.NewServeMux()
-		wm := rpcserver.NewWebsocketManager(routes, evsw)
-		mux.HandleFunc(config.Tendermint.Endpoint, wm.WebsocketHandler)
-		rpcserver.RegisterRPCFuncs(mux, routes)
-		listener, err := rpcserver.StartHTTPServer(listenerAddress, mux)
-		if err != nil {
-			return nil, err
-		}
-		listeners[i] = listener
-	}
-	return &TendermintWebsocketServer{
-		routes:    tendermintRoutes,
-		listeners: listeners,
-	}, nil
-}
-
-func (tmServer *TendermintWebsocketServer) Shutdown() {
-	for _, listener := range tmServer.listeners {
-		listener.Close()
-	}
-}
diff --git a/rpc/tendermint/test/runner/main.go b/rpc/tendermint/test/runner/main.go
deleted file mode 100644
index e17ee5b8184791e65df67ec8a0ccceac216a0a29..0000000000000000000000000000000000000000
--- a/rpc/tendermint/test/runner/main.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// +build integration
-
-// Space above here matters
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"fmt"
-
-	rpctest "github.com/hyperledger/burrow/rpc/tendermint/test"
-	"github.com/hyperledger/burrow/util"
-)
-
-func main() {
-	//fmt.Printf("%s", util.IsAddress("hello"))
-	fmt.Printf("%s", util.IsAddress("hello"), rpctest.Successor(2))
-	//defer os.Exit(0)
-}
diff --git a/rpc/tendermint/test/shared.go b/rpc/tendermint/test/shared.go
deleted file mode 100644
index 4ca904ddea2817bcffe61c0e16f5e11966c85692..0000000000000000000000000000000000000000
--- a/rpc/tendermint/test/shared.go
+++ /dev/null
@@ -1,432 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
-	"bytes"
-	"errors"
-	"fmt"
-	"hash/fnv"
-	"path"
-	"strconv"
-	"testing"
-
-	"time"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/config"
-	"github.com/hyperledger/burrow/core"
-	core_types "github.com/hyperledger/burrow/core/types"
-	genesis "github.com/hyperledger/burrow/genesis"
-	"github.com/hyperledger/burrow/logging/lifecycle"
-	"github.com/hyperledger/burrow/manager/burrow-mint/evm"
-	ptypes "github.com/hyperledger/burrow/permission/types"
-	"github.com/hyperledger/burrow/rpc/tendermint/client"
-	edbcli "github.com/hyperledger/burrow/rpc/tendermint/client"
-	rpc_types "github.com/hyperledger/burrow/rpc/tendermint/core/types"
-	"github.com/hyperledger/burrow/server"
-	"github.com/hyperledger/burrow/test/fixtures"
-	"github.com/hyperledger/burrow/txs"
-	"github.com/hyperledger/burrow/word256"
-	"github.com/spf13/viper"
-	"github.com/tendermint/go-crypto"
-	rpcclient "github.com/tendermint/go-rpc/client"
-	"github.com/tendermint/tendermint/types"
-)
-
-const chainID = "RPC_Test_Chain"
-
-// global variables for use across all tests
-var (
-	serverConfig      *server.ServerConfig
-	rootWorkDir       string
-	mempoolCount      = 0
-	websocketAddr     string
-	genesisDoc        *genesis.GenesisDoc
-	websocketEndpoint string
-	users             = makeUsers(5) // make keys
-	jsonRpcClient     client.RPCClient
-	httpClient        client.RPCClient
-	clients           map[string]client.RPCClient
-	testCore          *core.Core
-)
-
-// We use this to wrap tests
-func TestWrapper(runner func() int) int {
-	fmt.Println("Running with integration TestWrapper (rpc/tendermint/test/shared_test.go)...")
-	ffs := fixtures.NewFileFixtures("burrow")
-
-	defer func() {
-		// Tendermint likes to try and save to priv_validator.json after its been
-		// asked to shutdown so we pause to try and avoid collision
-		time.Sleep(time.Second)
-		ffs.RemoveAll()
-	}()
-
-	vm.SetDebug(true)
-	err := initGlobalVariables(ffs)
-
-	if err != nil {
-		panic(err)
-	}
-
-	tmServer, err := testCore.NewGatewayTendermint(serverConfig)
-	defer func() {
-		// Shutdown -- make sure we don't hit a race on ffs.RemoveAll
-		tmServer.Shutdown()
-		testCore.Stop()
-	}()
-
-	if err != nil {
-		panic(err)
-	}
-
-	return runner()
-}
-
-// initialize config and create new node
-func initGlobalVariables(ffs *fixtures.FileFixtures) error {
-	configBytes, err := config.GetConfigurationFileBytes(chainID,
-		"test_single_node",
-		"",
-		"burrow",
-		true,
-		"46657",
-		"burrow serve")
-	if err != nil {
-		return err
-	}
-
-	genesisBytes, err := genesisFileBytesFromUsers(chainID, users)
-	if err != nil {
-		return err
-	}
-
-	testConfigFile := ffs.AddFile("config.toml", string(configBytes))
-	rootWorkDir = ffs.AddDir("rootWorkDir")
-	rootDataDir := ffs.AddDir("rootDataDir")
-	genesisFile := ffs.AddFile("rootWorkDir/genesis.json", string(genesisBytes))
-	genesisDoc = genesis.GenesisDocFromJSON(genesisBytes)
-
-	if ffs.Error != nil {
-		return ffs.Error
-	}
-
-	testConfig := viper.New()
-	testConfig.SetConfigFile(testConfigFile)
-	err = testConfig.ReadInConfig()
-
-	if err != nil {
-		return err
-	}
-
-	sconf, err := core.LoadServerConfig(chainID, testConfig)
-	if err != nil {
-		return err
-	}
-	serverConfig = sconf
-
-	rpcAddr := serverConfig.Tendermint.RpcLocalAddress
-	websocketAddr = rpcAddr
-	websocketEndpoint = "/websocket"
-
-	consensusConfig, err := core.LoadModuleConfig(testConfig, rootWorkDir,
-		rootDataDir, genesisFile, chainID, "consensus")
-	if err != nil {
-		return err
-	}
-
-	managerConfig, err := core.LoadModuleConfig(testConfig, rootWorkDir,
-		rootDataDir, genesisFile, chainID, "manager")
-	if err != nil {
-		return err
-	}
-
-	// Set up priv_validator.json before we start tendermint (otherwise it will
-	// create its own one.
-	saveNewPriv()
-	logger, _ := lifecycle.NewStdErrLogger()
-	// To spill tendermint logs on the floor:
-	// lifecycle.CaptureTendermintLog15Output(loggers.NewNoopInfoTraceLogger())
-	lifecycle.CaptureTendermintLog15Output(logger)
-	lifecycle.CaptureStdlibLogOutput(logger)
-
-	testCore, err = core.NewCore("testCore", consensusConfig, managerConfig,
-		logger)
-	if err != nil {
-		return err
-	}
-
-	jsonRpcClient = rpcclient.NewJSONRPCClient(rpcAddr)
-	httpClient = rpcclient.NewURIClient(rpcAddr)
-
-	clients = map[string]client.RPCClient{
-		"JSONRPC": jsonRpcClient,
-		"HTTP":    httpClient,
-	}
-	return nil
-}
-
-// Deterministic account generation helper. Pass number of accounts to make
-func makeUsers(n int) []*acm.PrivAccount {
-	accounts := []*acm.PrivAccount{}
-	for i := 0; i < n; i++ {
-		secret := "mysecret" + strconv.Itoa(i)
-		user := acm.GenPrivAccountFromSecret(secret)
-		accounts = append(accounts, user)
-	}
-	return accounts
-}
-
-func genesisFileBytesFromUsers(chainName string, accounts []*acm.PrivAccount) ([]byte, error) {
-	if len(accounts) < 1 {
-		return nil, errors.New("Please pass in at least 1 account to be the validator")
-	}
-	genesisValidators := make([]*genesis.GenesisValidator, 1)
-	genesisAccounts := make([]*genesis.GenesisAccount, len(accounts))
-	genesisValidators[0] = genesisValidatorFromPrivAccount(accounts[0])
-
-	for i, acc := range accounts {
-		genesisAccounts[i] = genesisAccountFromPrivAccount(acc)
-	}
-
-	return genesis.GenerateGenesisFileBytes(chainName, genesisAccounts, genesisValidators)
-}
-
-func genesisValidatorFromPrivAccount(account *acm.PrivAccount) *genesis.GenesisValidator {
-	return &genesis.GenesisValidator{
-		Amount: 1000000,
-		Name:   fmt.Sprintf("full-account_%X", account.Address),
-		PubKey: account.PubKey,
-		UnbondTo: []genesis.BasicAccount{
-			{
-				Address: account.Address,
-				Amount:  100,
-			},
-		},
-	}
-}
-
-func genesisAccountFromPrivAccount(account *acm.PrivAccount) *genesis.GenesisAccount {
-	return genesis.NewGenesisAccount(account.Address, 100000,
-		fmt.Sprintf("account_%X", account.Address), &ptypes.DefaultAccountPermissions)
-}
-
-func saveNewPriv() {
-	// Save new priv_validator file.
-	priv := &types.PrivValidator{
-		Address: users[0].Address,
-		PubKey:  crypto.PubKeyEd25519(users[0].PubKey.(crypto.PubKeyEd25519)),
-		PrivKey: crypto.PrivKeyEd25519(users[0].PrivKey.(crypto.PrivKeyEd25519)),
-	}
-	priv.SetFile(path.Join(rootWorkDir, "priv_validator.json"))
-	priv.Save()
-}
-
-//-------------------------------------------------------------------------------
-// some default transaction functions
-
-func makeDefaultSendTx(t *testing.T, client client.RPCClient, addr []byte,
-	amt int64) *txs.SendTx {
-	nonce := getNonce(t, client, users[0].Address)
-	tx := txs.NewSendTx()
-	tx.AddInputWithNonce(users[0].PubKey, amt, nonce+1)
-	tx.AddOutput(addr, amt)
-	return tx
-}
-
-func makeDefaultSendTxSigned(t *testing.T, client client.RPCClient, addr []byte,
-	amt int64) *txs.SendTx {
-	tx := makeDefaultSendTx(t, client, addr, amt)
-	tx.SignInput(chainID, 0, users[0])
-	return tx
-}
-
-func makeDefaultCallTx(t *testing.T, client client.RPCClient, addr, code []byte, amt, gasLim,
-	fee int64) *txs.CallTx {
-	nonce := getNonce(t, client, users[0].Address)
-	tx := txs.NewCallTxWithNonce(users[0].PubKey, addr, code, amt, gasLim, fee,
-		nonce+1)
-	tx.Sign(chainID, users[0])
-	return tx
-}
-
-func makeDefaultNameTx(t *testing.T, client client.RPCClient, name, value string, amt,
-	fee int64) *txs.NameTx {
-	nonce := getNonce(t, client, users[0].Address)
-	tx := txs.NewNameTxWithNonce(users[0].PubKey, name, value, amt, fee, nonce+1)
-	tx.Sign(chainID, users[0])
-	return tx
-}
-
-//-------------------------------------------------------------------------------
-// rpc call wrappers (fail on err)
-
-// get an account's nonce
-func getNonce(t *testing.T, client client.RPCClient, addr []byte) int {
-	ac, err := edbcli.GetAccount(client, addr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if ac == nil {
-		return 0
-	}
-	return ac.Sequence
-}
-
-// get the account
-func getAccount(t *testing.T, client client.RPCClient, addr []byte) *acm.Account {
-	ac, err := edbcli.GetAccount(client, addr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	return ac
-}
-
-// sign transaction
-func signTx(t *testing.T, client client.RPCClient, tx txs.Tx,
-	privAcc *acm.PrivAccount) txs.Tx {
-	signedTx, err := edbcli.SignTx(client, tx, []*acm.PrivAccount{privAcc})
-	if err != nil {
-		t.Fatal(err)
-	}
-	return signedTx
-}
-
-// broadcast transaction
-func broadcastTx(t *testing.T, client client.RPCClient, tx txs.Tx) txs.Receipt {
-	rec, err := edbcli.BroadcastTx(client, tx)
-	if err != nil {
-		t.Fatal(err)
-	}
-	mempoolCount += 1
-	return rec
-}
-
-// dump all storage for an account. currently unused
-func dumpStorage(t *testing.T, addr []byte) *rpc_types.ResultDumpStorage {
-	client := clients["HTTP"]
-	resp, err := edbcli.DumpStorage(client, addr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	return resp
-}
-
-func getStorage(t *testing.T, client client.RPCClient, addr, key []byte) []byte {
-	resp, err := edbcli.GetStorage(client, addr, key)
-	if err != nil {
-		t.Fatal(err)
-	}
-	return resp
-}
-
-func callCode(t *testing.T, client client.RPCClient, fromAddress, code, data,
-	expected []byte) {
-	resp, err := edbcli.CallCode(client, fromAddress, code, data)
-	if err != nil {
-		t.Fatal(err)
-	}
-	ret := resp.Return
-	// NOTE: we don't flip memory when it comes out of RETURN (?!)
-	if bytes.Compare(ret, word256.LeftPadWord256(expected).Bytes()) != 0 {
-		t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected)
-	}
-}
-
-func callContract(t *testing.T, client client.RPCClient, fromAddress, toAddress,
-	data, expected []byte) {
-	resp, err := edbcli.Call(client, fromAddress, toAddress, data)
-	if err != nil {
-		t.Fatal(err)
-	}
-	ret := resp.Return
-	// NOTE: we don't flip memory when it comes out of RETURN (?!)
-	if bytes.Compare(ret, word256.LeftPadWord256(expected).Bytes()) != 0 {
-		t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected)
-	}
-}
-
-// get the namereg entry
-func getNameRegEntry(t *testing.T, client client.RPCClient, name string) *core_types.NameRegEntry {
-	entry, err := edbcli.GetName(client, name)
-	if err != nil {
-		t.Fatal(err)
-	}
-	return entry
-}
-
-// Returns a positive int64 hash of text (consumers want int64 instead of uint64)
-func hashString(text string) int64 {
-	hasher := fnv.New64()
-	hasher.Write([]byte(text))
-	value := int64(hasher.Sum64())
-	// Flip the sign if we wrapped
-	if value < 0 {
-		return -value
-	}
-	return value
-}
-
-//--------------------------------------------------------------------------------
-// utility verification function
-
-// simple contract returns 5 + 6 = 0xb
-func simpleContract() ([]byte, []byte, []byte) {
-	// this is the code we want to run when the contract is called
-	contractCode := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20,
-		0x60, 0x0, 0xf3}
-	// the is the code we need to return the contractCode when the contract is initialized
-	lenCode := len(contractCode)
-	// push code to the stack
-	//code := append([]byte{byte(0x60 + lenCode - 1)}, RightPadWord256(contractCode).Bytes()...)
-	code := append([]byte{0x7f},
-		word256.RightPadWord256(contractCode).Bytes()...)
-	// store it in memory
-	code = append(code, []byte{0x60, 0x0, 0x52}...)
-	// return whats in memory
-	//code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...)
-	code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...)
-	// return init code, contract code, expected return
-	return code, contractCode, word256.LeftPadBytes([]byte{0xb}, 32)
-}
-
-// simple call contract calls another contract
-func simpleCallContract(addr []byte) ([]byte, []byte, []byte) {
-	gas1, gas2 := byte(0x1), byte(0x1)
-	value := byte(0x1)
-	inOff, inSize := byte(0x0), byte(0x0) // no call data
-	retOff, retSize := byte(0x0), byte(0x20)
-	// this is the code we want to run (call a contract and return)
-	contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff,
-		0x60, value, 0x73}
-	contractCode = append(contractCode, addr...)
-	contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20,
-		0x60, 0x0, 0xf3}...)
-
-	// the is the code we need to return; the contractCode when the contract is initialized
-	// it should copy the code from the input into memory
-	lenCode := len(contractCode)
-	memOff := byte(0x0)
-	inOff = byte(0xc) // length of code before codeContract
-	length := byte(lenCode)
-
-	code := []byte{0x60, length, 0x60, inOff, 0x60, memOff, 0x37}
-	// return whats in memory
-	code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...)
-	code = append(code, contractCode...)
-	// return init code, contract code, expected return
-	return code, contractCode, word256.LeftPadBytes([]byte{0xb}, 32)
-}
diff --git a/rpc/tm/client/client.go b/rpc/tm/client/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..f1654899e0baf27e0189fb644d0e33991370d4bb
--- /dev/null
+++ b/rpc/tm/client/client.go
@@ -0,0 +1,207 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package client
+
+import (
+	"errors"
+	"fmt"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/execution"
+	"github.com/hyperledger/burrow/rpc"
+	"github.com/hyperledger/burrow/rpc/tm/method"
+	"github.com/hyperledger/burrow/txs"
+)
+
+type RPCClient interface {
+	Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
+}
+
+func Status(client RPCClient) (*rpc.ResultStatus, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.Status, pmap(), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultStatus), nil
+}
+
+func ChainId(client RPCClient) (*rpc.ResultChainId, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.ChainID, pmap(), &res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultChainId), nil
+}
+
+func GenPrivAccount(client RPCClient) (*rpc.ResultGeneratePrivateAccount, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.GeneratePrivateAccount, pmap(), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultGeneratePrivateAccount), nil
+}
+
+func GetAccount(client RPCClient, address acm.Address) (acm.Account, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.GetAccount, pmap("address", address), res)
+	if err != nil {
+		return nil, err
+	}
+	concreteAccount := res.Unwrap().(*rpc.ResultGetAccount).Account
+	if concreteAccount == nil {
+		return nil, nil
+	}
+	return concreteAccount.Account(), nil
+}
+
+func SignTx(client RPCClient, tx txs.Tx, privAccounts []*acm.ConcretePrivateAccount) (txs.Tx, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.SignTx, pmap("tx", tx, "privAccounts", privAccounts), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultSignTx).Tx, nil
+}
+
+func BroadcastTx(client RPCClient, tx txs.Tx) (*txs.Receipt, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.BroadcastTx, pmap("tx", txs.Wrap(tx)), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultBroadcastTx).Receipt, nil
+}
+
+func DumpStorage(client RPCClient, address acm.Address) (*rpc.ResultDumpStorage, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.DumpStorage, pmap("address", address), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultDumpStorage), nil
+}
+
+func GetStorage(client RPCClient, address acm.Address, key []byte) ([]byte, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.GetStorage, pmap("address", address, "key", key), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultGetStorage).Value, nil
+}
+
+func CallCode(client RPCClient, fromAddress acm.Address, code, data []byte) (*rpc.ResultCall, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.CallCode, pmap("fromAddress", fromAddress, "code", code, "data", data), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultCall), nil
+}
+
+func Call(client RPCClient, fromAddress, toAddress acm.Address, data []byte) (*rpc.ResultCall, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.Call, pmap("fromAddress", fromAddress, "toAddress", toAddress,
+		"data", data), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultCall), nil
+}
+
+func GetName(client RPCClient, name string) (*execution.NameRegEntry, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.GetName, pmap("name", name), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultGetName).Entry, nil
+}
+
+func BlockchainInfo(client RPCClient, minHeight, maxHeight int) (*rpc.ResultBlockchainInfo, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.Blockchain, pmap("minHeight", minHeight, "maxHeight", maxHeight), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultBlockchainInfo), nil
+}
+
+func GetBlock(client RPCClient, height int) (*rpc.ResultGetBlock, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.GetBlock, pmap("height", height), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultGetBlock), nil
+}
+
+func ListUnconfirmedTxs(client RPCClient, maxTxs int) (*rpc.ResultListUnconfirmedTxs, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.ListUnconfirmedTxs, pmap("maxTxs", maxTxs), res)
+	if err != nil {
+		return nil, err
+	}
+	resCon := res.Unwrap().(*rpc.ResultListUnconfirmedTxs)
+	return resCon, nil
+}
+
+func ListValidators(client RPCClient) (*rpc.ResultListValidators, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.ListValidators, pmap(), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultListValidators), nil
+}
+
+func DumpConsensusState(client RPCClient) (*rpc.ResultDumpConsensusState, error) {
+	res := new(rpc.Result)
+	_, err := client.Call(method.DumpConsensusState, pmap(), res)
+	if err != nil {
+		return nil, err
+	}
+	return res.Unwrap().(*rpc.ResultDumpConsensusState), nil
+}
+
+func pmap(keyvals ...interface{}) map[string]interface{} {
+	pm, err := paramsMap(keyvals...)
+	if err != nil {
+		panic(err)
+	}
+	return pm
+}
+
+func paramsMap(orderedKeyVals ...interface{}) (map[string]interface{}, error) {
+	if len(orderedKeyVals)%2 != 0 {
+		return nil, fmt.Errorf("mapAndValues requires a even length list of"+
+			" keys and values but got: %v (length %v)",
+			orderedKeyVals, len(orderedKeyVals))
+	}
+	paramsMap := make(map[string]interface{})
+	for i := 0; i < len(orderedKeyVals); i += 2 {
+		key, ok := orderedKeyVals[i].(string)
+		if !ok {
+			return nil, errors.New("mapAndValues requires every even element" +
+				" of orderedKeyVals to be a string key")
+		}
+		val := orderedKeyVals[i+1]
+		paramsMap[key] = val
+	}
+	return paramsMap, nil
+}
diff --git a/rpc/tendermint/test/rpc_client_test.go b/rpc/tm/client/client_test.go
similarity index 66%
rename from rpc/tendermint/test/rpc_client_test.go
rename to rpc/tm/client/client_test.go
index 3f7da59afb790e358e2d0dda423ae7e2291067cc..4140a21531bce1e39af38662784a4e5c44150e8a 100644
--- a/rpc/tendermint/test/rpc_client_test.go
+++ b/rpc/tm/client/client_test.go
@@ -1,6 +1,3 @@
-// +build integration
-
-// Space above here matters
 // Copyright 2017 Monax Industries Limited
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,23 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package test
+package client
 
 import (
 	"bytes"
-	"fmt"
 	"testing"
 	"time"
 
-	"golang.org/x/crypto/ripemd160"
-
-	consensus_types "github.com/hyperledger/burrow/consensus/types"
-	burrow_client "github.com/hyperledger/burrow/rpc/tendermint/client"
+	"github.com/hyperledger/burrow/binary"
+	"github.com/hyperledger/burrow/event"
+	exe_events "github.com/hyperledger/burrow/execution/events"
 	"github.com/hyperledger/burrow/txs"
-	"github.com/hyperledger/burrow/word256"
-
 	"github.com/stretchr/testify/assert"
-	_ "github.com/tendermint/tendermint/config/tendermint_test"
+	"github.com/stretchr/testify/require"
+	"github.com/tendermint/tendermint/consensus/types"
+	tm_types "github.com/tendermint/tendermint/types"
+	"golang.org/x/crypto/ripemd160"
 )
 
 // When run with `-test.short` we only run:
@@ -40,8 +36,7 @@ import (
 // Note: the reason that we have tests implemented in tests.go is I believe
 // due to weirdness with go-wire's interface registration, and those global
 // registrations not being available within a *_test.go runtime context.
-func testWithAllClients(t *testing.T,
-	testFunction func(*testing.T, string, burrow_client.RPCClient)) {
+func testWithAllClients(t *testing.T, testFunction func(*testing.T, string, RPCClient)) {
 	for clientName, client := range clients {
 		testFunction(t, clientName, client)
 	}
@@ -49,28 +44,26 @@ func testWithAllClients(t *testing.T,
 
 //--------------------------------------------------------------------------------
 func TestStatus(t *testing.T) {
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
-		resp, err := burrow_client.Status(client)
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
+		resp, err := Status(client)
 		assert.NoError(t, err)
-		fmt.Println(resp)
-		if resp.NodeInfo.Network != chainID {
-			t.Fatal(fmt.Errorf("ChainID mismatch: got %s expected %s",
-				resp.NodeInfo.Network, chainID))
-		}
+		assert.Equal(t, genesisDoc.ChainID(), resp.NodeInfo.Network, "ChainID should match NodeInfo.Network")
 	})
 }
 
 func TestBroadcastTx(t *testing.T) {
 	wsc := newWSClient()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
 		// Avoid duplicate Tx in mempool
 		amt := hashString(clientName) % 1000
-		toAddr := users[1].Address
+		toAddr := privateAccounts[1].Address()
 		tx := makeDefaultSendTxSigned(t, client, toAddr, amt)
 		receipt, err := broadcastTxAndWaitForBlock(t, client, wsc, tx)
-		assert.NoError(t, err)
-		if receipt.CreatesContract > 0 {
-			t.Fatal("This tx does not create a contract")
+		if err != nil {
+			t.Fatal(err)
+		}
+		if receipt.CreatesContract {
+			t.Fatal("This tx should not create a contract")
 		}
 		if len(receipt.TxHash) == 0 {
 			t.Fatal("Failed to compute tx hash")
@@ -78,7 +71,7 @@ func TestBroadcastTx(t *testing.T) {
 		n, errp := new(int), new(error)
 		buf := new(bytes.Buffer)
 		hasher := ripemd160.New()
-		tx.WriteSignBytes(chainID, buf, n, errp)
+		tx.WriteSignBytes(genesisDoc.ChainID(), buf, n, errp)
 		assert.NoError(t, *errp)
 		txSignBytes := buf.Bytes()
 		hasher.Write(txSignBytes)
@@ -95,14 +88,14 @@ func TestGetAccount(t *testing.T) {
 	if testing.Short() {
 		t.Skip("skipping test in short mode.")
 	}
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
-		acc := getAccount(t, client, users[0].Address)
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
+		acc := getAccount(t, client, privateAccounts[0].Address())
 		if acc == nil {
 			t.Fatal("Account was nil")
 		}
-		if bytes.Compare(acc.Address, users[0].Address) != 0 {
-			t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address,
-				users[0].Address)
+		if acc.Address() != privateAccounts[0].Address() {
+			t.Fatalf("Failed to get correct account. Got %s, expected %s", acc.Address(),
+				privateAccounts[0].Address())
 		}
 	})
 }
@@ -112,23 +105,21 @@ func TestGetStorage(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	defer func() {
-		wsc.Stop()
-	}()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
-		eid := txs.EventStringNewBlock()
+	defer stopWSClient(wsc)
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
+		eid := tm_types.EventStringNewBlock()
 		subscribe(t, wsc, eid)
 		defer func() {
 			unsubscribe(t, wsc, eid)
 		}()
 
-		amt, gasLim, fee := int64(1100), int64(1000), int64(1000)
+		amt, gasLim, fee := uint64(1100), uint64(1000), uint64(1000)
 		code := []byte{0x60, 0x5, 0x60, 0x1, 0x55}
 		// Call with nil address will create a contract
 		tx := makeDefaultCallTx(t, client, nil, code, amt, gasLim, fee)
 		receipt, err := broadcastTxAndWaitForBlock(t, client, wsc, tx)
 		assert.NoError(t, err)
-		assert.Equal(t, uint8(1), receipt.CreatesContract, "This transaction should"+
+		assert.Equal(t, true, receipt.CreatesContract, "This transaction should"+
 			" create a contract")
 		assert.NotEqual(t, 0, len(receipt.TxHash), "Receipt should contain a"+
 			" transaction hash")
@@ -137,8 +128,8 @@ func TestGetStorage(t *testing.T) {
 			" created a contract but the contract address is empty")
 
 		v := getStorage(t, client, contractAddr, []byte{0x1})
-		got := word256.LeftPadWord256(v)
-		expected := word256.LeftPadWord256([]byte{0x5})
+		got := binary.LeftPadWord256(v)
+		expected := binary.LeftPadWord256([]byte{0x5})
 		if got.Compare(expected) != 0 {
 			t.Fatalf("Wrong storage value. Got %x, expected %x", got.Bytes(),
 				expected.Bytes())
@@ -151,21 +142,21 @@ func TestCallCode(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
 		// add two integers and return the result
 		code := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60,
 			0x0, 0xf3}
 		data := []byte{}
 		expected := []byte{0xb}
-		callCode(t, client, users[0].PubKey.Address(), code, data, expected)
+		callCode(t, client, privateAccounts[0].Address(), code, data, expected)
 
 		// pass two ints as calldata, add, and return the result
 		code = []byte{0x60, 0x0, 0x35, 0x60, 0x20, 0x35, 0x1, 0x60, 0x0, 0x52, 0x60,
 			0x20, 0x60, 0x0, 0xf3}
-		data = append(word256.LeftPadWord256([]byte{0x5}).Bytes(),
-			word256.LeftPadWord256([]byte{0x6}).Bytes()...)
+		data = append(binary.LeftPadWord256([]byte{0x5}).Bytes(),
+			binary.LeftPadWord256([]byte{0x6}).Bytes()...)
 		expected = []byte{0xb}
-		callCode(t, client, users[0].PubKey.Address(), code, data, expected)
+		callCode(t, client, privateAccounts[0].Address(), code, data, expected)
 	})
 }
 
@@ -174,18 +165,16 @@ func TestCallContract(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	defer func() {
-		wsc.Stop()
-	}()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
-		eid := txs.EventStringNewBlock()
+	defer stopWSClient(wsc)
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
+		eid := tm_types.EventStringNewBlock()
 		subscribe(t, wsc, eid)
 		defer func() {
 			unsubscribe(t, wsc, eid)
 		}()
 
 		// create the contract
-		amt, gasLim, fee := int64(6969), int64(1000), int64(1000)
+		amt, gasLim, fee := uint64(6969), uint64(1000), uint64(1000)
 		code, _, _ := simpleContract()
 		tx := makeDefaultCallTx(t, client, nil, code, amt, gasLim, fee)
 		receipt, err := broadcastTxAndWaitForBlock(t, client, wsc, tx)
@@ -193,7 +182,7 @@ func TestCallContract(t *testing.T) {
 		if err != nil {
 			t.Fatalf("Problem broadcasting transaction: %v", err)
 		}
-		assert.Equal(t, uint8(1), receipt.CreatesContract, "This transaction should"+
+		assert.Equal(t, true, receipt.CreatesContract, "This transaction should"+
 			" create a contract")
 		assert.NotEqual(t, 0, len(receipt.TxHash), "Receipt should contain a"+
 			" transaction hash")
@@ -204,7 +193,7 @@ func TestCallContract(t *testing.T) {
 		// run a call through the contract
 		data := []byte{}
 		expected := []byte{0xb}
-		callContract(t, client, users[0].PubKey.Address(), contractAddr, data, expected)
+		callContract(t, client, privateAccounts[0].Address(), contractAddr, data, expected)
 	})
 }
 
@@ -213,26 +202,27 @@ func TestNameReg(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
-
+	defer stopWSClient(wsc)
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
 		txs.MinNameRegistrationPeriod = 1
 
 		// register a new name, check if its there
 		// since entries ought to be unique and these run against different clients, we append the client
 		name := "ye_old_domain_name_" + clientName
 		const data = "if not now, when"
-		fee := int64(1000)
-		numDesiredBlocks := int64(2)
+		fee := uint64(1000)
+		numDesiredBlocks := uint64(2)
 		amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data)
 
 		tx := makeDefaultNameTx(t, client, name, data, amt, fee)
 		// verify the name by both using the event and by checking get_name
-		subscribeAndWaitForNext(t, wsc, txs.EventStringNameReg(name),
+		subscribeAndWaitForNext(t, wsc, exe_events.EventStringNameReg(name),
 			func() {
 				broadcastTxAndWaitForBlock(t, client, wsc, tx)
 			},
-			func(eid string, eventData txs.EventData) (bool, error) {
-				eventDataTx := asEventDataTx(t, eventData)
+			func(eid string, eventData event.AnyEventData) (bool, error) {
+				eventDataTx := eventData.EventDataTx()
+				assert.NotNil(t, eventDataTx, "could not convert %s to EventDataTx", eventData)
 				tx, ok := eventDataTx.Tx.(*txs.NameTx)
 				if !ok {
 					t.Fatalf("Could not convert %v to *NameTx", eventDataTx)
@@ -241,14 +231,13 @@ func TestNameReg(t *testing.T) {
 				assert.Equal(t, data, tx.Data)
 				return true, nil
 			})
-		mempoolCount = 0
 
 		entry := getNameRegEntry(t, client, name)
 		assert.Equal(t, data, entry.Data)
-		assert.Equal(t, users[0].Address, entry.Owner)
+		assert.Equal(t, privateAccounts[0].Address(), entry.Owner)
 
 		// update the data as the owner, make sure still there
-		numDesiredBlocks = int64(5)
+		numDesiredBlocks = uint64(5)
 		const updatedData = "these are amongst the things I wish to bestow upon " +
 			"the youth of generations come: a safe supply of honey, and a better " +
 			"money. For what else shall they need"
@@ -256,15 +245,14 @@ func TestNameReg(t *testing.T) {
 			txs.NameBlockCostMultiplier*txs.NameBaseCost(name, updatedData)
 		tx = makeDefaultNameTx(t, client, name, updatedData, amt, fee)
 		broadcastTxAndWaitForBlock(t, client, wsc, tx)
-		mempoolCount = 0
 		entry = getNameRegEntry(t, client, name)
 
 		assert.Equal(t, updatedData, entry.Data)
 
 		// try to update as non owner, should fail
-		tx = txs.NewNameTxWithNonce(users[1].PubKey, name, "never mind", amt, fee,
-			getNonce(t, client, users[1].Address)+1)
-		tx.Sign(chainID, users[1])
+		tx = txs.NewNameTxWithNonce(privateAccounts[1].PublicKey(), name, "never mind", amt, fee,
+			getNonce(t, client, privateAccounts[1].Address())+1)
+		tx.Sign(genesisDoc.ChainID(), privateAccounts[1])
 
 		_, err := broadcastTxAndWaitForBlock(t, client, wsc, tx)
 		assert.Error(t, err, "Expected error when updating someone else's unexpired"+
@@ -279,37 +267,44 @@ func TestNameReg(t *testing.T) {
 
 		//now the entry should be expired, so we can update as non owner
 		const data2 = "this is not my beautiful house"
-		tx = txs.NewNameTxWithNonce(users[1].PubKey, name, data2, amt, fee,
-			getNonce(t, client, users[1].Address)+1)
-		tx.Sign(chainID, users[1])
+		tx = txs.NewNameTxWithNonce(privateAccounts[1].PublicKey(), name, data2, amt, fee,
+			getNonce(t, client, privateAccounts[1].Address())+1)
+		tx.Sign(genesisDoc.ChainID(), privateAccounts[1])
 		_, err = broadcastTxAndWaitForBlock(t, client, wsc, tx)
 		assert.NoError(t, err, "Should be able to update a previously expired name"+
 			" registry entry as a different address")
-		mempoolCount = 0
 		entry = getNameRegEntry(t, client, name)
 		assert.Equal(t, data2, entry.Data)
-		assert.Equal(t, users[1].Address, entry.Owner)
+		assert.Equal(t, privateAccounts[1].Address(), entry.Owner)
 	})
 }
 
+func TestWaitBlocks(t *testing.T) {
+	wsc := newWSClient()
+	defer stopWSClient(wsc)
+	waitNBlocks(t, wsc, 5)
+}
+
 func TestBlockchainInfo(t *testing.T) {
 	wsc := newWSClient()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
+	defer stopWSClient(wsc)
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
 		// wait a mimimal number of blocks to ensure that the later query for block
 		// headers has a non-trivial length
 		nBlocks := 4
 		waitNBlocks(t, wsc, nBlocks)
 
-		resp, err := burrow_client.BlockchainInfo(client, 0, 0)
+		resp, err := BlockchainInfo(client, 0, 0)
 		if err != nil {
 			t.Fatalf("Failed to get blockchain info: %v", err)
 		}
 		lastBlockHeight := resp.LastHeight
 		nMetaBlocks := len(resp.BlockMetas)
-		assert.True(t, nMetaBlocks <= lastBlockHeight,
+		assert.True(t, uint64(nMetaBlocks) <= lastBlockHeight,
 			"Logically number of block metas should be equal or less than block height.")
 		assert.True(t, nBlocks <= len(resp.BlockMetas),
-			"Should see at least 4 BlockMetas after waiting for 4 blocks")
+			"Should see at least %v BlockMetas after waiting for %v blocks but saw %v",
+			nBlocks, nBlocks, len(resp.BlockMetas))
 		// For the maximum number (default to 20) of retrieved block headers,
 		// check that they correctly chain to each other.
 		lastBlockHash := resp.BlockMetas[nMetaBlocks-1].Header.Hash()
@@ -323,7 +318,7 @@ func TestBlockchainInfo(t *testing.T) {
 
 		// Now retrieve only two blockheaders (h=1, and h=2) and check that we got
 		// two results.
-		resp, err = burrow_client.BlockchainInfo(client, 1, 2)
+		resp, err = BlockchainInfo(client, 1, 2)
 		assert.NoError(t, err)
 		assert.Equal(t, 2, len(resp.BlockMetas),
 			"Should see 2 BlockMetas after extracting 2 blocks")
@@ -335,12 +330,13 @@ func TestListUnconfirmedTxs(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
-		amt, gasLim, fee := int64(1100), int64(1000), int64(1000)
+	defer stopWSClient(wsc)
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
+		amt, gasLim, fee := uint64(1100), uint64(1000), uint64(1000)
 		code := []byte{0x60, 0x5, 0x60, 0x1, 0x55}
 		// Call with nil address will create a contract
-		tx := makeDefaultCallTx(t, client, []byte{}, code, amt, gasLim, fee)
-		txChan := make(chan []txs.Tx)
+		tx := txs.Wrap(makeDefaultCallTx(t, client, nil, code, amt, gasLim, fee))
+		txChan := make(chan []txs.Wrapper)
 
 		// We want to catch the Tx in mempool before it gets reaped by tendermint
 		// consensus. We should be able to do this almost always if we broadcast our
@@ -351,8 +347,11 @@ func TestListUnconfirmedTxs(t *testing.T) {
 
 		go func() {
 			for {
-				resp, err := burrow_client.ListUnconfirmedTxs(client)
-				assert.NoError(t, err)
+				resp, err := ListUnconfirmedTxs(client, -1)
+				if resp != nil {
+
+				}
+				require.NoError(t, err)
 				if resp.N > 0 {
 					txChan <- resp.Txs
 				}
@@ -362,14 +361,12 @@ func TestListUnconfirmedTxs(t *testing.T) {
 		runThenWaitForBlock(t, wsc, nextBlockPredicateFn(), func() {
 			broadcastTx(t, client, tx)
 			select {
-			case <-time.After(time.Second * timeoutSeconds):
+			case <-time.After(time.Second * timeoutSeconds * 10):
 				t.Fatal("Timeout out waiting for unconfirmed transactions to appear")
 			case transactions := <-txChan:
-				assert.Len(t, transactions, 1,
-					"There should only be a single transaction in the mempool during "+
-						"this test (previous txs should have made it into a block)")
-				assert.Contains(t, transactions, tx,
-					"Transaction should be returned by ListUnconfirmedTxs")
+				assert.Len(t, transactions, 1, "There should only be a single transaction in the "+
+					"mempool during this test (previous txs should have made it into a block)")
+				assert.Contains(t, transactions, tx, "Transaction should be returned by ListUnconfirmedTxs")
 			}
 		})
 	})
@@ -377,9 +374,9 @@ func TestListUnconfirmedTxs(t *testing.T) {
 
 func TestGetBlock(t *testing.T) {
 	wsc := newWSClient()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
 		waitNBlocks(t, wsc, 3)
-		resp, err := burrow_client.GetBlock(client, 2)
+		resp, err := GetBlock(client, 2)
 		assert.NoError(t, err)
 		assert.Equal(t, 2, resp.Block.Height)
 		assert.Equal(t, 2, resp.BlockMeta.Header.Height)
@@ -388,38 +385,54 @@ func TestGetBlock(t *testing.T) {
 
 func TestListValidators(t *testing.T) {
 	wsc := newWSClient()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
 		waitNBlocks(t, wsc, 3)
-		resp, err := burrow_client.ListValidators(client)
+		resp, err := ListValidators(client)
 		assert.NoError(t, err)
 		assert.Len(t, resp.BondedValidators, 1)
-		validator := resp.BondedValidators[0].(*consensus_types.TendermintValidator)
-		assert.Equal(t, genesisDoc.Validators[0].PubKey, validator.PubKey)
+		validator := resp.BondedValidators[0]
+		assert.Equal(t, genesisDoc.Validators[0].PublicKey, validator.PublicKey)
 	})
 }
 
 func TestDumpConsensusState(t *testing.T) {
 	wsc := newWSClient()
-	testWithAllClients(t, func(t *testing.T, clientName string, client burrow_client.RPCClient) {
+	startTime := time.Now()
+	testWithAllClients(t, func(t *testing.T, clientName string, client RPCClient) {
 		waitNBlocks(t, wsc, 3)
-		resp, err := burrow_client.DumpConsensusState(client)
+		resp, err := DumpConsensusState(client)
 		assert.NoError(t, err)
-		startTime := resp.ConsensusState.StartTime
-		// TODO: uncomment lines involving commitTime when
-		// https://github.com/tendermint/tendermint/issues/277 is fixed in Tendermint
-		//commitTime := resp.ConsensusState.CommitTime
+		commitTime := resp.RoundState.CommitTime
 		assert.NotZero(t, startTime)
-		//assert.NotZero(t, commitTime)
-		//assert.True(t, commitTime.Unix() > startTime.Unix(),
-		//	"Commit time %v should be later than start time %v", commitTime, startTime)
-		assert.Equal(t, uint8(1), resp.ConsensusState.Step)
+		assert.NotZero(t, commitTime)
+		assert.True(t, commitTime.Unix() > startTime.Unix(),
+			"Commit time %v should be later than start time %v", commitTime, startTime)
+		assert.Equal(t, types.RoundStepNewHeight, resp.RoundState.Step)
 	})
 }
 
-func asEventDataTx(t *testing.T, eventData txs.EventData) txs.EventDataTx {
-	eventDataTx, ok := eventData.(txs.EventDataTx)
-	if !ok {
-		t.Fatalf("Expected eventData to be EventDataTx was %v", eventData)
+func TestParamsMap(t *testing.T) {
+	type aStruct struct {
+		Baz int
 	}
-	return eventDataTx
+	dict, err := paramsMap("Foo", aStruct{5},
+		"Bar", "Nibbles")
+	assert.NoError(t, err, "Should not be a paramsMaperror")
+	assert.Equal(t, map[string]interface{}{
+		"Foo": aStruct{5},
+		"Bar": "Nibbles",
+	}, dict)
+
+	// Empty map
+	dict, err = paramsMap()
+	assert.Equal(t, map[string]interface{}{}, dict)
+	assert.NoError(t, err, "Empty mapsAndValues call should be fine")
+
+	// Invalid maps
+	assert.NoError(t, err, "Empty mapsAndValues call should be fine")
+	_, err = paramsMap("Foo", 4, "Bar")
+	assert.Error(t, err, "Should be an error to get an odd number of arguments")
+
+	_, err = paramsMap("Foo", 4, 4, "Bar")
+	assert.Error(t, err, "Should be an error to provide non-string keys")
 }
diff --git a/rpc/tm/client/shared.go b/rpc/tm/client/shared.go
new file mode 100644
index 0000000000000000000000000000000000000000..0aaf157c5433901056eccaa4aadf933e3fa342bf
--- /dev/null
+++ b/rpc/tm/client/shared.go
@@ -0,0 +1,316 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package client
+
+import (
+	"bytes"
+	"fmt"
+	"hash/fnv"
+	"strconv"
+	"testing"
+
+	"os"
+
+	"time"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/binary"
+	"github.com/hyperledger/burrow/consensus/tendermint/validator"
+	"github.com/hyperledger/burrow/core"
+	"github.com/hyperledger/burrow/execution"
+	"github.com/hyperledger/burrow/genesis"
+	"github.com/hyperledger/burrow/logging/loggers"
+	"github.com/hyperledger/burrow/permission"
+	"github.com/hyperledger/burrow/rpc"
+	"github.com/hyperledger/burrow/txs"
+	"github.com/stretchr/testify/require"
+	tm_config "github.com/tendermint/tendermint/config"
+	"github.com/tendermint/tendermint/rpc/lib/client"
+)
+
+const (
+	chainName         = "RPC_Test_Chain"
+	rpcAddr           = "0.0.0.0:46657"
+	websocketAddr     = rpcAddr
+	websocketEndpoint = "/websocket"
+	testDir           = "./test_scratch/tm_test"
+)
+
+// global variables for use across all tests
+var (
+	privateAccounts = makePrivateAccounts(5) // make keys
+	jsonRpcClient   = rpcclient.NewJSONRPCClient(rpcAddr)
+	httpClient      = rpcclient.NewURIClient(rpcAddr)
+	clients         = map[string]RPCClient{
+		"JSONRPC": jsonRpcClient,
+		"HTTP":    httpClient,
+	}
+	// Initialised in initGlobalVariables
+	genesisDoc = new(genesis.GenesisDoc)
+	kernel     = new(core.Kernel)
+)
+
+// We use this to wrap tests
+func TestWrapper(runner func() int) int {
+	fmt.Println("Running with integration TestWrapper (rpc/tendermint/test/shared_test.go)...")
+
+	err := initGlobalVariables()
+	if err != nil {
+		panic(err)
+	}
+
+	err = kernel.Boot()
+	if err != nil {
+		panic(err)
+	}
+
+	defer kernel.Shutdown()
+
+	return runner()
+}
+
+func initGlobalVariables() error {
+	var err error
+	os.RemoveAll(testDir)
+	os.MkdirAll(testDir, 0777)
+	os.Chdir(testDir)
+	tmConf := tm_config.DefaultConfig()
+	//logger, _ := lifecycle.NewStdErrLogger()
+	logger := loggers.NewNoopInfoTraceLogger()
+	privValidator := validator.NewPrivValidatorMemory(privateAccounts[0], privateAccounts[0])
+	genesisDoc = testGenesisDoc()
+	kernel, err = core.NewKernel(privValidator, genesisDoc, tmConf, rpc.DefaultRPCConfig(), logger)
+	return err
+}
+
+func testGenesisDoc() *genesis.GenesisDoc {
+	accounts := make(map[string]acm.Account, len(privateAccounts))
+	for i, pa := range privateAccounts {
+		account := acm.FromAddressable(pa)
+		account.AddToBalance(1 << 32)
+		account.SetPermissions(permission.AllAccountPermissions.Clone())
+		accounts[fmt.Sprintf("user_%v", i)] = account
+	}
+	genesisTime, err := time.Parse("02-01-2006", "27-10-2017")
+	if err != nil {
+		panic("could not parse test genesis time")
+	}
+	return genesis.MakeGenesisDocFromAccounts(chainName, nil, genesisTime, accounts,
+		map[string]acm.Validator{
+			"genesis_validator": acm.AsValidator(accounts["user_0"]),
+		})
+}
+
+// Deterministic account generation helper. Pass number of accounts to make
+func makePrivateAccounts(n int) []acm.PrivateAccount {
+	accounts := make([]acm.PrivateAccount, n)
+	for i := 0; i < n; i++ {
+		accounts[i] = acm.GeneratePrivateAccountFromSecret("mysecret" + strconv.Itoa(i))
+	}
+	return accounts
+}
+
+//-------------------------------------------------------------------------------
+// some default transaction functions
+
+func makeDefaultSendTx(t *testing.T, client RPCClient, addr acm.Address, amt uint64) *txs.SendTx {
+	nonce := getNonce(t, client, privateAccounts[0].Address())
+	tx := txs.NewSendTx()
+	tx.AddInputWithNonce(privateAccounts[0].PublicKey(), amt, nonce+1)
+	tx.AddOutput(addr, amt)
+	return tx
+}
+
+func makeDefaultSendTxSigned(t *testing.T, client RPCClient, addr acm.Address, amt uint64) *txs.SendTx {
+	tx := makeDefaultSendTx(t, client, addr, amt)
+	tx.SignInput(genesisDoc.ChainID(), 0, privateAccounts[0])
+	return tx
+}
+
+func makeDefaultCallTx(t *testing.T, client RPCClient, addr *acm.Address, code []byte, amt, gasLim,
+	fee uint64) *txs.CallTx {
+	nonce := getNonce(t, client, privateAccounts[0].Address())
+	tx := txs.NewCallTxWithNonce(privateAccounts[0].PublicKey(), addr, code, amt, gasLim, fee,
+		nonce+1)
+	tx.Sign(genesisDoc.ChainID(), privateAccounts[0])
+	return tx
+}
+
+func makeDefaultCallTxWithNonce(t *testing.T, addr *acm.Address, sequence uint64, code []byte,
+	amt, gasLim, fee uint64) *txs.CallTx {
+
+	tx := txs.NewCallTxWithNonce(privateAccounts[0].PublicKey(), addr, code, amt, gasLim, fee, sequence)
+	tx.Sign(genesisDoc.ChainID(), privateAccounts[0])
+	return tx
+}
+
+func makeDefaultNameTx(t *testing.T, client RPCClient, name, value string, amt, fee uint64) *txs.NameTx {
+	nonce := getNonce(t, client, privateAccounts[0].Address())
+	tx := txs.NewNameTxWithNonce(privateAccounts[0].PublicKey(), name, value, amt, fee, nonce+1)
+	tx.Sign(genesisDoc.ChainID(), privateAccounts[0])
+	return tx
+}
+
+//-------------------------------------------------------------------------------
+// rpc call wrappers (fail on err)
+
+// get an account's nonce
+func getNonce(t *testing.T, client RPCClient, addr acm.Address) uint64 {
+	acc, err := GetAccount(client, addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if acc == nil {
+		return 0
+	}
+	return acc.Sequence()
+}
+
+// get the account
+func getAccount(t *testing.T, client RPCClient, addr acm.Address) acm.Account {
+	ac, err := GetAccount(client, addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return ac
+}
+
+// sign transaction
+func signTx(t *testing.T, client RPCClient, tx txs.Tx,
+	privAcc *acm.ConcretePrivateAccount) txs.Tx {
+	signedTx, err := SignTx(client, tx, []*acm.ConcretePrivateAccount{privAcc})
+	if err != nil {
+		t.Fatal(err)
+	}
+	return signedTx
+}
+
+// broadcast transaction
+func broadcastTx(t *testing.T, client RPCClient, tx txs.Tx) *txs.Receipt {
+	rec, err := BroadcastTx(client, tx)
+	require.NoError(t, err)
+	return rec
+}
+
+// dump all storage for an account. currently unused
+func dumpStorage(t *testing.T, addr acm.Address) *rpc.ResultDumpStorage {
+	client := clients["HTTP"]
+	resp, err := DumpStorage(client, addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return resp
+}
+
+func getStorage(t *testing.T, client RPCClient, addr acm.Address, key []byte) []byte {
+	resp, err := GetStorage(client, addr, key)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return resp
+}
+
+func callCode(t *testing.T, client RPCClient, fromAddress acm.Address, code, data,
+	expected []byte) {
+	resp, err := CallCode(client, fromAddress, code, data)
+	if err != nil {
+		t.Fatal(err)
+	}
+	ret := resp.Return
+	// NOTE: we don't flip memory when it comes out of RETURN (?!)
+	if bytes.Compare(ret, binary.LeftPadWord256(expected).Bytes()) != 0 {
+		t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected)
+	}
+}
+
+func callContract(t *testing.T, client RPCClient, fromAddress, toAddress acm.Address,
+	data, expected []byte) {
+	resp, err := Call(client, fromAddress, toAddress, data)
+	if err != nil {
+		t.Fatal(err)
+	}
+	ret := resp.Return
+	// NOTE: we don't flip memory when it comes out of RETURN (?!)
+	if bytes.Compare(ret, binary.LeftPadWord256(expected).Bytes()) != 0 {
+		t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected)
+	}
+}
+
+// get the namereg entry
+func getNameRegEntry(t *testing.T, client RPCClient, name string) *execution.NameRegEntry {
+	entry, err := GetName(client, name)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return entry
+}
+
+// Returns a positive int64 hash of text (consumers want int64 instead of uint64)
+func hashString(text string) uint64 {
+	hasher := fnv.New64()
+	hasher.Write([]byte(text))
+	return uint64(hasher.Sum64())
+}
+
+//--------------------------------------------------------------------------------
+// utility verification function
+
+// simple contract returns 5 + 6 = 0xb
+func simpleContract() ([]byte, []byte, []byte) {
+	// this is the code we want to run when the contract is called
+	contractCode := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20,
+		0x60, 0x0, 0xf3}
+	// the is the code we need to return the contractCode when the contract is initialized
+	lenCode := len(contractCode)
+	// push code to the stack
+	//code := append([]byte{byte(0x60 + lenCode - 1)}, RightPadWord256(contractCode).Bytes()...)
+	code := append([]byte{0x7f},
+		binary.RightPadWord256(contractCode).Bytes()...)
+	// store it in memory
+	code = append(code, []byte{0x60, 0x0, 0x52}...)
+	// return whats in memory
+	//code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...)
+	code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...)
+	// return init code, contract code, expected return
+	return code, contractCode, binary.LeftPadBytes([]byte{0xb}, 32)
+}
+
+// simple call contract calls another contract
+func simpleCallContract(addr acm.Address) ([]byte, []byte, []byte) {
+	gas1, gas2 := byte(0x1), byte(0x1)
+	value := byte(0x1)
+	inOff, inSize := byte(0x0), byte(0x0) // no call data
+	retOff, retSize := byte(0x0), byte(0x20)
+	// this is the code we want to run (call a contract and return)
+	contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff,
+		0x60, value, 0x73}
+	contractCode = append(contractCode, addr.Bytes()...)
+	contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20,
+		0x60, 0x0, 0xf3}...)
+
+	// the is the code we need to return; the contractCode when the contract is initialized
+	// it should copy the code from the input into memory
+	lenCode := len(contractCode)
+	memOff := byte(0x0)
+	inOff = byte(0xc) // length of code before codeContract
+	length := byte(lenCode)
+
+	code := []byte{0x60, length, 0x60, inOff, 0x60, memOff, 0x37}
+	// return whats in memory
+	code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...)
+	code = append(code, contractCode...)
+	// return init code, contract code, expected return
+	return code, contractCode, binary.LeftPadBytes([]byte{0xb}, 32)
+}
diff --git a/rpc/tendermint/test/shared_test.go b/rpc/tm/client/shared_test.go
similarity index 92%
rename from rpc/tendermint/test/shared_test.go
rename to rpc/tm/client/shared_test.go
index 5b2a1455c161aa75320800e5c89180554b91e1f5..46c939cf89dd282fa278cbec3ed83512ee3a3a39 100644
--- a/rpc/tendermint/test/shared_test.go
+++ b/rpc/tm/client/shared_test.go
@@ -1,6 +1,3 @@
-// +build integration
-
-// Space above here matters
 // Copyright 2017 Monax Industries Limited
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package test
+package client
 
 import (
 	"os"
diff --git a/rpc/tm/client/websocket_client.go b/rpc/tm/client/websocket_client.go
new file mode 100644
index 0000000000000000000000000000000000000000..d20eeb737395b377b40eb63adb391e9872c71fe2
--- /dev/null
+++ b/rpc/tm/client/websocket_client.go
@@ -0,0 +1,47 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package client
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/tendermint/tendermint/rpc/lib/types"
+)
+
+type WebsocketClient interface {
+	Send(ctx context.Context, request rpctypes.RPCRequest) error
+}
+
+func Subscribe(wsc WebsocketClient, eventId string) error {
+	req, err := rpctypes.MapToRequest(fmt.Sprintf("wsclient_subscribe?eventId=%s", eventId),
+		"subscribe", map[string]interface{}{"eventId": eventId})
+	if err != nil {
+		return err
+	}
+	return wsc.Send(context.Background(), req)
+
+	//return wsc.Call(context.Background(), "subscribe",
+	//	map[string]interface{}{"eventId": eventId})
+}
+
+func Unsubscribe(websocketClient WebsocketClient, subscriptionId string) error {
+	req, err := rpctypes.MapToRequest(fmt.Sprintf("wsclient_unsubscribe?subId=%s", subscriptionId),
+		"unsubscribe", map[string]interface{}{"subscriptionId": subscriptionId})
+	if err != nil {
+		return err
+	}
+	return websocketClient.Send(context.Background(), req)
+}
diff --git a/rpc/tendermint/test/websocket_client_test.go b/rpc/tm/client/websocket_client_test.go
similarity index 66%
rename from rpc/tendermint/test/websocket_client_test.go
rename to rpc/tm/client/websocket_client_test.go
index 4bb2609269bfdbb85978cc15f23fbea6d2292f3b..d5ce845a72e60a17b1987b2755137e9aaea9a5dc 100644
--- a/rpc/tendermint/test/websocket_client_test.go
+++ b/rpc/tm/client/websocket_client_test.go
@@ -1,6 +1,3 @@
-// +build integration
-
-// Space above here matters
 // Copyright 2017 Monax Industries Limited
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,18 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package test
+package client
 
 import (
 	"fmt"
 	"testing"
-
 	"time"
 
-	core_types "github.com/hyperledger/burrow/rpc/tendermint/core/types"
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/event"
+	exe_events "github.com/hyperledger/burrow/execution/events"
+	evm_events "github.com/hyperledger/burrow/execution/evm/events"
+	"github.com/hyperledger/burrow/rpc"
 	"github.com/hyperledger/burrow/txs"
 	"github.com/stretchr/testify/assert"
-	_ "github.com/tendermint/tendermint/config/tendermint_test"
+	"github.com/stretchr/testify/require"
+	tm_types "github.com/tendermint/tendermint/types"
 )
 
 //--------------------------------------------------------------------------------
@@ -35,21 +36,21 @@ import (
 // make a simple connection to the server
 func TestWSConnect(t *testing.T) {
 	wsc := newWSClient()
-	wsc.Stop()
+	stopWSClient(wsc)
 }
 
 // receive a new block message
 func TestWSNewBlock(t *testing.T) {
 	wsc := newWSClient()
-	eid := txs.EventStringNewBlock()
+	eid := tm_types.EventStringNewBlock()
 	subId := subscribeAndGetSubscriptionId(t, wsc, eid)
 	defer func() {
 		unsubscribe(t, wsc, subId)
-		wsc.Stop()
+		stopWSClient(wsc)
 	}()
 	waitForEvent(t, wsc, eid, func() {},
-		func(eid string, eventData txs.EventData) (bool, error) {
-			fmt.Println("Check: ", eventData.(txs.EventDataNewBlock).Block)
+		func(eid string, eventData event.AnyEventData) (bool, error) {
+			fmt.Println("Check: ", eventData.EventDataNewBlock().Block)
 			return true, nil
 		})
 }
@@ -60,19 +61,19 @@ func TestWSBlockchainGrowth(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	eid := txs.EventStringNewBlock()
+	eid := tm_types.EventStringNewBlock()
 	subId := subscribeAndGetSubscriptionId(t, wsc, eid)
 	defer func() {
 		unsubscribe(t, wsc, subId)
-		wsc.Stop()
+		stopWSClient(wsc)
 	}()
 	// listen for NewBlock, ensure height increases by 1
 	var initBlockN int
 	for i := 0; i < 2; i++ {
 		waitForEvent(t, wsc, eid, func() {},
-			func(eid string, eventData txs.EventData) (bool, error) {
-				eventDataNewBlock, ok := eventData.(txs.EventDataNewBlock)
-				if !ok {
+			func(eid string, eventData event.AnyEventData) (bool, error) {
+				eventDataNewBlock := eventData.EventDataNewBlock()
+				if eventDataNewBlock == nil {
 					t.Fatalf("Was expecting EventDataNewBlock but got %v", eventData)
 				}
 				block := eventDataNewBlock.Block
@@ -93,16 +94,16 @@ func TestWSBlockchainGrowth(t *testing.T) {
 // send a transaction and validate the events from listening for both sender and receiver
 func TestWSSend(t *testing.T) {
 	wsc := newWSClient()
-	toAddr := users[1].Address
-	amt := int64(100)
-	eidInput := txs.EventStringAccInput(users[0].Address)
-	eidOutput := txs.EventStringAccOutput(toAddr)
+	toAddr := privateAccounts[1].Address()
+	amt := uint64(100)
+	eidInput := exe_events.EventStringAccInput(privateAccounts[0].Address())
+	eidOutput := exe_events.EventStringAccOutput(toAddr)
 	subIdInput := subscribeAndGetSubscriptionId(t, wsc, eidInput)
 	subIdOutput := subscribeAndGetSubscriptionId(t, wsc, eidOutput)
 	defer func() {
 		unsubscribe(t, wsc, subIdInput)
 		unsubscribe(t, wsc, subIdOutput)
-		wsc.Stop()
+		stopWSClient(wsc)
 	}()
 	waitForEvent(t, wsc, eidInput, func() {
 		tx := makeDefaultSendTxSigned(t, jsonRpcClient, toAddr, amt)
@@ -119,25 +120,25 @@ func TestWSDoubleFire(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	eid := txs.EventStringAccInput(users[0].Address)
+	eid := exe_events.EventStringAccInput(privateAccounts[0].Address())
 	subId := subscribeAndGetSubscriptionId(t, wsc, eid)
 	defer func() {
 		unsubscribe(t, wsc, subId)
-		wsc.Stop()
+		stopWSClient(wsc)
 	}()
-	amt := int64(100)
-	toAddr := users[1].Address
+	amt := uint64(100)
+	toAddr := privateAccounts[1].Address()
 	// broadcast the transaction, wait to hear about it
 	waitForEvent(t, wsc, eid, func() {
 		tx := makeDefaultSendTxSigned(t, jsonRpcClient, toAddr, amt)
 		broadcastTx(t, jsonRpcClient, tx)
-	}, func(eid string, b txs.EventData) (bool, error) {
+	}, func(eid string, b event.AnyEventData) (bool, error) {
 		return true, nil
 	})
 	// but make sure we don't hear about it twice
 	err := waitForEvent(t, wsc, eid,
 		func() {},
-		func(eid string, b txs.EventData) (bool, error) {
+		func(eid string, b event.AnyEventData) (bool, error) {
 			return false, nil
 		})
 	assert.True(t, err.Timeout(), "We should have timed out waiting for second"+
@@ -150,15 +151,15 @@ func TestWSCallWait(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	eid1 := txs.EventStringAccInput(users[0].Address)
+	eid1 := exe_events.EventStringAccInput(privateAccounts[0].Address())
 	subId1 := subscribeAndGetSubscriptionId(t, wsc, eid1)
 	defer func() {
 		unsubscribe(t, wsc, subId1)
-		wsc.Stop()
+		stopWSClient(wsc)
 	}()
-	amt, gasLim, fee := int64(10000), int64(1000), int64(1000)
+	amt, gasLim, fee := uint64(10000), uint64(1000), uint64(1000)
 	code, returnCode, returnVal := simpleContract()
-	var contractAddr []byte
+	var contractAddr acm.Address
 	// wait for the contract to be created
 	waitForEvent(t, wsc, eid1, func() {
 		tx := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
@@ -167,8 +168,8 @@ func TestWSCallWait(t *testing.T) {
 	}, unmarshalValidateTx(amt, returnCode))
 
 	// susbscribe to the new contract
-	amt = int64(10001)
-	eid2 := txs.EventStringAccOutput(contractAddr)
+	amt = uint64(10001)
+	eid2 := exe_events.EventStringAccOutput(contractAddr)
 	subId2 := subscribeAndGetSubscriptionId(t, wsc, eid2)
 	defer func() {
 		unsubscribe(t, wsc, subId2)
@@ -176,7 +177,7 @@ func TestWSCallWait(t *testing.T) {
 	// get the return value from a call
 	data := []byte{0x1}
 	waitForEvent(t, wsc, eid2, func() {
-		tx := makeDefaultCallTx(t, jsonRpcClient, contractAddr, data, amt, gasLim, fee)
+		tx := makeDefaultCallTx(t, jsonRpcClient, &contractAddr, data, amt, gasLim, fee)
 		receipt := broadcastTx(t, jsonRpcClient, tx)
 		contractAddr = receipt.ContractAddr
 	}, unmarshalValidateTx(amt, returnVal))
@@ -189,25 +190,24 @@ func TestWSCallNoWait(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	amt, gasLim, fee := int64(10000), int64(1000), int64(1000)
+	defer stopWSClient(wsc)
+	amt, gasLim, fee := uint64(10000), uint64(1000), uint64(1000)
 	code, _, returnVal := simpleContract()
 
 	tx := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
-	receipt := broadcastTx(t, jsonRpcClient, tx)
+	receipt, err := broadcastTxAndWaitForBlock(t, jsonRpcClient, wsc, tx)
+	require.NoError(t, err)
 	contractAddr := receipt.ContractAddr
 
 	// susbscribe to the new contract
-	amt = int64(10001)
-	eid := txs.EventStringAccOutput(contractAddr)
+	amt = uint64(10001)
+	eid := exe_events.EventStringAccOutput(contractAddr)
 	subId := subscribeAndGetSubscriptionId(t, wsc, eid)
-	defer func() {
-		unsubscribe(t, wsc, subId)
-		wsc.Stop()
-	}()
+	defer unsubscribe(t, wsc, subId)
 	// get the return value from a call
 	data := []byte{0x1}
 	waitForEvent(t, wsc, eid, func() {
-		tx := makeDefaultCallTx(t, jsonRpcClient, contractAddr, data, amt, gasLim, fee)
+		tx := makeDefaultCallTx(t, jsonRpcClient, &contractAddr, data, amt, gasLim, fee)
 		broadcastTx(t, jsonRpcClient, tx)
 	}, unmarshalValidateTx(amt, returnVal))
 }
@@ -218,47 +218,52 @@ func TestWSCallCall(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	wsc := newWSClient()
-	amt, gasLim, fee := int64(10000), int64(1000), int64(1000)
+	defer stopWSClient(wsc)
+	amt, gasLim, fee := uint64(10000), uint64(1000), uint64(1000)
 	code, _, returnVal := simpleContract()
 	txid := new([]byte)
 
 	// deploy the two contracts
 	tx := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
-	receipt := broadcastTx(t, jsonRpcClient, tx)
+	receipt, err := broadcastTxAndWaitForBlock(t, jsonRpcClient, wsc, tx)
+	require.NoError(t, err)
 	contractAddr1 := receipt.ContractAddr
 
+	// subscribe to the new contracts
+	eid := evm_events.EventStringAccCall(contractAddr1)
+	subId := subscribeAndGetSubscriptionId(t, wsc, eid)
+	defer unsubscribe(t, wsc, subId)
+	// call contract2, which should call contract1, and wait for ev1
 	code, _, _ = simpleCallContract(contractAddr1)
 	tx = makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
 	receipt = broadcastTx(t, jsonRpcClient, tx)
 	contractAddr2 := receipt.ContractAddr
 
-	// subscribe to the new contracts
-	amt = int64(10001)
-	eid := txs.EventStringAccCall(contractAddr1)
-	subId := subscribeAndGetSubscriptionId(t, wsc, eid)
-	defer func() {
-		unsubscribe(t, wsc, subId)
-		wsc.Stop()
-	}()
-	// call contract2, which should call contract1, and wait for ev1
-
 	// let the contract get created first
-	waitForEvent(t, wsc, eid, func() {
-	}, func(eid string, b txs.EventData) (bool, error) {
-		return true, nil
-	})
+	waitForEvent(t, wsc, eid,
+		// Runner
+		func() {
+		},
+		// Event Checker
+		func(eid string, b event.AnyEventData) (bool, error) {
+			return true, nil
+		})
 	// call it
-	waitForEvent(t, wsc, eid, func() {
-		tx := makeDefaultCallTx(t, jsonRpcClient, contractAddr2, nil, amt, gasLim, fee)
-		broadcastTx(t, jsonRpcClient, tx)
-		*txid = txs.TxHash(chainID, tx)
-	}, unmarshalValidateCall(users[0].Address, returnVal, txid))
+	waitForEvent(t, wsc, eid,
+		// Runner
+		func() {
+			tx := makeDefaultCallTx(t, jsonRpcClient, &contractAddr2, nil, amt, gasLim, fee)
+			broadcastTx(t, jsonRpcClient, tx)
+			*txid = txs.TxHash(genesisDoc.ChainID(), tx)
+		},
+		// Event checker
+		unmarshalValidateCall(privateAccounts[0].Address(), returnVal, txid))
 }
 
 func TestSubscribe(t *testing.T) {
 	wsc := newWSClient()
 	var subId string
-	subscribe(t, wsc, txs.EventStringNewBlock())
+	subscribe(t, wsc, tm_types.EventStringNewBlock())
 
 	// timeout to check subscription process is live
 	timeout := time.After(timeoutSeconds * time.Second)
@@ -268,13 +273,12 @@ Subscribe:
 		case <-timeout:
 			t.Fatal("Timed out waiting for subscription result")
 
-		case bs := <-wsc.ResultsCh:
-			resultSubscribe, ok := readResult(t, bs).(*core_types.ResultSubscribe)
-			if ok {
-				assert.Equal(t, txs.EventStringNewBlock(), resultSubscribe.Event)
-				subId = resultSubscribe.SubscriptionId
-				break Subscribe
-			}
+		case response := <-wsc.ResponsesCh:
+			require.Nil(t, response.Error)
+			res := readResult(t, *response.Result).(*rpc.ResultSubscribe)
+			assert.Equal(t, tm_types.EventStringNewBlock(), res.Event)
+			subId = res.SubscriptionId
+			break Subscribe
 		}
 	}
 
@@ -289,11 +293,12 @@ Subscribe:
 			}
 			return
 
-		case bs := <-wsc.ResultsCh:
-			resultEvent, ok := readResult(t, bs).(*core_types.ResultEvent)
-			if ok {
-				_, ok := resultEvent.Data.(txs.EventDataNewBlock)
-				if ok {
+		case response := <-wsc.ResponsesCh:
+			require.Nil(t, response.Error)
+			if res, ok := readResult(t, *response.Result).(*rpc.ResultEvent); ok {
+				enb := res.EventDataNewBlock()
+				if enb != nil {
+					assert.Equal(t, genesisDoc.ChainID(), enb.Block.ChainID)
 					if blocksSeen > 1 {
 						t.Fatal("Continued to see NewBlock event after unsubscribing")
 					} else {
diff --git a/rpc/tendermint/test/websocket_helpers.go b/rpc/tm/client/websocket_helpers.go
similarity index 56%
rename from rpc/tendermint/test/websocket_helpers.go
rename to rpc/tm/client/websocket_helpers.go
index 1e013b5203dade9e0b76eac80e5f1361b88bac5b..05c0a8831d9a2dbcbdd7eaca9879808f439174a0 100644
--- a/rpc/tendermint/test/websocket_helpers.go
+++ b/rpc/tm/client/websocket_helpers.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package test
+package client
 
 import (
 	"bytes"
@@ -20,18 +20,20 @@ import (
 	"testing"
 	"time"
 
-	burrow_client "github.com/hyperledger/burrow/rpc/tendermint/client"
-	ctypes "github.com/hyperledger/burrow/rpc/tendermint/core/types"
-	"github.com/hyperledger/burrow/txs"
+	"encoding/json"
 
-	rpcclient "github.com/tendermint/go-rpc/client"
-	"github.com/tendermint/go-wire"
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/event"
+	"github.com/hyperledger/burrow/rpc"
+	"github.com/hyperledger/burrow/txs"
+	"github.com/stretchr/testify/require"
+	"github.com/tendermint/tendermint/rpc/lib/client"
 	tm_types "github.com/tendermint/tendermint/types"
 )
 
 const (
 	timeoutSeconds       = 2
-	expectBlockInSeconds = timeoutSeconds * 2
+	expectBlockInSeconds = 2
 )
 
 //--------------------------------------------------------------------------------
@@ -47,16 +49,20 @@ func newWSClient() *rpcclient.WSClient {
 	return wsc
 }
 
+func stopWSClient(wsc *rpcclient.WSClient) {
+	wsc.Stop()
+}
+
 // subscribe to an event
 func subscribe(t *testing.T, wsc *rpcclient.WSClient, eventId string) {
-	if err := burrow_client.Subscribe(wsc, eventId); err != nil {
+	if err := Subscribe(wsc, eventId); err != nil {
 		t.Fatal(err)
 	}
 }
 
 func subscribeAndGetSubscriptionId(t *testing.T, wsc *rpcclient.WSClient,
 	eventId string) string {
-	if err := burrow_client.Subscribe(wsc, eventId); err != nil {
+	if err := Subscribe(wsc, eventId); err != nil {
 		t.Fatal(err)
 	}
 
@@ -65,10 +71,11 @@ func subscribeAndGetSubscriptionId(t *testing.T, wsc *rpcclient.WSClient,
 		select {
 		case <-timeout.C:
 			t.Fatal("Timeout waiting for subscription result")
-		case bs := <-wsc.ResultsCh:
-			resultSubscribe, ok := readResult(t, bs).(*ctypes.ResultSubscribe)
+		case response := <-wsc.ResponsesCh:
+			require.Nil(t, response.Error, "got error response from websocket channel")
+			res, ok := readResult(t, *response.Result).(*rpc.ResultSubscribe)
 			if ok {
-				return resultSubscribe.SubscriptionId
+				return res.SubscriptionId
 			}
 		}
 	}
@@ -76,20 +83,20 @@ func subscribeAndGetSubscriptionId(t *testing.T, wsc *rpcclient.WSClient,
 
 // unsubscribe from an event
 func unsubscribe(t *testing.T, wsc *rpcclient.WSClient, subscriptionId string) {
-	if err := burrow_client.Unsubscribe(wsc, subscriptionId); err != nil {
+	if err := Unsubscribe(wsc, subscriptionId); err != nil {
 		t.Fatal(err)
 	}
 }
 
 // broadcast transaction and wait for new block
-func broadcastTxAndWaitForBlock(t *testing.T, client burrow_client.RPCClient,
-	wsc *rpcclient.WSClient, tx txs.Tx) (txs.Receipt, error) {
-	var rec txs.Receipt
+func broadcastTxAndWaitForBlock(t *testing.T, client RPCClient, wsc *rpcclient.WSClient,
+	tx txs.Tx) (*txs.Receipt, error) {
+
+	var rec *txs.Receipt
 	var err error
 	runThenWaitForBlock(t, wsc, nextBlockPredicateFn(),
 		func() {
-			rec, err = burrow_client.BroadcastTx(client, tx)
-			mempoolCount += 1
+			rec, err = BroadcastTx(client, tx)
 		})
 	return rec, err
 }
@@ -121,40 +128,37 @@ func waitNBlocks(t *testing.T, wsc *rpcclient.WSClient, n int) {
 		func() {})
 }
 
-func runThenWaitForBlock(t *testing.T, wsc *rpcclient.WSClient,
-	predicate blockPredicate, runner func()) {
-	subscribeAndWaitForNext(t, wsc, txs.EventStringNewBlock(),
-		runner,
-		func(event string, eventData txs.EventData) (bool, error) {
-			return predicate(eventData.(txs.EventDataNewBlock).Block), nil
-		})
+func runThenWaitForBlock(t *testing.T, wsc *rpcclient.WSClient, predicate blockPredicate, runner func()) {
+	eventDataChecker := func(event string, eventData event.AnyEventData) (bool, error) {
+		eventDataNewBlock := eventData.EventDataNewBlock()
+		if eventDataNewBlock == nil {
+			return false, fmt.Errorf("could not convert %#v to EventDataNewBlock", eventData)
+		}
+		return predicate(eventDataNewBlock.Block), nil
+	}
+	subscribeAndWaitForNext(t, wsc, tm_types.EventStringNewBlock(), runner, eventDataChecker)
 }
 
-func subscribeAndWaitForNext(t *testing.T, wsc *rpcclient.WSClient, event string,
-	runner func(),
-	eventDataChecker func(string, txs.EventData) (bool, error)) {
+func subscribeAndWaitForNext(t *testing.T, wsc *rpcclient.WSClient, event string, runner func(),
+	eventDataChecker func(string, event.AnyEventData) (bool, error)) {
+
 	subId := subscribeAndGetSubscriptionId(t, wsc, event)
 	defer unsubscribe(t, wsc, subId)
-	waitForEvent(t,
-		wsc,
-		event,
-		runner,
-		eventDataChecker)
+	waitForEvent(t, wsc, event, runner, eventDataChecker)
 }
 
 // waitForEvent executes runner that is expected to trigger events. It then
 // waits for any events on the supplies WSClient and checks the eventData with
 // the eventDataChecker which is a function that is passed the event name
-// and the EventData and returns the pair of stopWaiting, err. Where if
+// and the Data and returns the pair of stopWaiting, err. Where if
 // stopWaiting is true waitForEvent will return or if stopWaiting is false
 // waitForEvent will keep listening for new events. If an error is returned
 // waitForEvent will fail the test.
-func waitForEvent(t *testing.T, wsc *rpcclient.WSClient, eventid string,
-	runner func(),
-	eventDataChecker func(string, txs.EventData) (bool, error)) waitForEventResult {
+func waitForEvent(t *testing.T, wsc *rpcclient.WSClient, eventID string, runner func(),
+	eventDataChecker func(string, event.AnyEventData) (bool, error)) waitForEventResult {
 
 	// go routine to wait for websocket msg
-	eventsCh := make(chan txs.EventData)
+	eventsCh := make(chan event.AnyEventData)
 	shutdownEventsCh := make(chan bool, 1)
 	errCh := make(chan error)
 
@@ -163,27 +167,21 @@ func waitForEvent(t *testing.T, wsc *rpcclient.WSClient, eventid string,
 
 	// Read message
 	go func() {
-		var err error
 	LOOP:
 		for {
 			select {
 			case <-shutdownEventsCh:
 				break LOOP
-			case r := <-wsc.ResultsCh:
-				result := new(ctypes.BurrowResult)
-				wire.ReadJSONPtr(result, r, &err)
-				if err != nil {
-					errCh <- err
+			case r := <-wsc.ResponsesCh:
+				if r.Error != nil {
+					errCh <- r.Error
 					break LOOP
 				}
-				event, ok := (*result).(*ctypes.ResultEvent)
-				if ok && event.Event == eventid {
+				resultEvent, _ := readResult(t, *r.Result).(*rpc.ResultEvent)
+				if resultEvent != nil && resultEvent.Event == eventID {
 					// Keep feeding events
-					eventsCh <- event.Data
+					eventsCh <- resultEvent.AnyEventData
 				}
-			case err := <-wsc.ErrorsCh:
-				errCh <- err
-				break LOOP
 			case <-wsc.Quit:
 				break LOOP
 			}
@@ -200,10 +198,8 @@ func waitForEvent(t *testing.T, wsc *rpcclient.WSClient, eventid string,
 			return waitForEventResult{timeout: true}
 		case eventData := <-eventsCh:
 			// run the check
-			stopWaiting, err := eventDataChecker(eventid, eventData)
-			if err != nil {
-				t.Fatal(err) // Show the stack trace.
-			}
+			stopWaiting, err := eventDataChecker(eventID, eventData)
+			require.NoError(t, err)
 			if stopWaiting {
 				return waitForEventResult{}
 			}
@@ -224,38 +220,44 @@ func (err waitForEventResult) Timeout() bool {
 
 //--------------------------------------------------------------------------------
 
-func unmarshalValidateSend(amt int64,
-	toAddr []byte) func(string, txs.EventData) (bool, error) {
-	return func(eid string, eventData txs.EventData) (bool, error) {
-		var data = eventData.(txs.EventDataTx)
+func unmarshalValidateSend(amt uint64, toAddr acm.Address) func(string, event.AnyEventData) (bool, error) {
+	return func(eid string, eventData event.AnyEventData) (bool, error) {
+		data := eventData.EventDataTx()
+		if data == nil {
+			return true, fmt.Errorf("event data %s is not EventDataTx", eventData)
+		}
 		if data.Exception != "" {
 			return true, fmt.Errorf(data.Exception)
 		}
 		tx := data.Tx.(*txs.SendTx)
-		if !bytes.Equal(tx.Inputs[0].Address, users[0].Address) {
-			return true, fmt.Errorf("Senders do not match up! Got %x, expected %x", tx.Inputs[0].Address, users[0].Address)
+		if tx.Inputs[0].Address != privateAccounts[0].Address() {
+			return true, fmt.Errorf("senders do not match up! Got %s, expected %s", tx.Inputs[0].Address,
+				privateAccounts[0].Address)
 		}
 		if tx.Inputs[0].Amount != amt {
-			return true, fmt.Errorf("Amt does not match up! Got %d, expected %d", tx.Inputs[0].Amount, amt)
+			return true, fmt.Errorf("amt does not match up! Got %d, expected %d", tx.Inputs[0].Amount, amt)
 		}
-		if !bytes.Equal(tx.Outputs[0].Address, toAddr) {
-			return true, fmt.Errorf("Receivers do not match up! Got %x, expected %x", tx.Outputs[0].Address, users[0].Address)
+		if tx.Outputs[0].Address != toAddr {
+			return true, fmt.Errorf("receivers do not match up! Got %s, expected %s", tx.Outputs[0].Address,
+				privateAccounts[0].Address)
 		}
 		return true, nil
 	}
 }
 
-func unmarshalValidateTx(amt int64,
-	returnCode []byte) func(string, txs.EventData) (bool, error) {
-	return func(eid string, eventData txs.EventData) (bool, error) {
-		var data = eventData.(txs.EventDataTx)
+func unmarshalValidateTx(amt uint64, returnCode []byte) func(string, event.AnyEventData) (bool, error) {
+	return func(eid string, eventData event.AnyEventData) (bool, error) {
+		data := eventData.EventDataTx()
+		if data == nil {
+			return true, fmt.Errorf("event data %s is not EventDataTx", eventData)
+		}
 		if data.Exception != "" {
 			return true, fmt.Errorf(data.Exception)
 		}
 		tx := data.Tx.(*txs.CallTx)
-		if !bytes.Equal(tx.Input.Address, users[0].Address) {
+		if tx.Input.Address != privateAccounts[0].Address() {
 			return true, fmt.Errorf("Senders do not match up! Got %x, expected %x",
-				tx.Input.Address, users[0].Address)
+				tx.Input.Address, privateAccounts[0].Address)
 		}
 		if tx.Input.Amount != amt {
 			return true, fmt.Errorf("Amt does not match up! Got %d, expected %d",
@@ -269,20 +271,21 @@ func unmarshalValidateTx(amt int64,
 	}
 }
 
-func unmarshalValidateCall(origin,
-	returnCode []byte, txid *[]byte) func(string, txs.EventData) (bool, error) {
-	return func(eid string, eventData txs.EventData) (bool, error) {
-		var data = eventData.(txs.EventDataCall)
+func unmarshalValidateCall(origin acm.Address, returnCode []byte, txid *[]byte) func(string, event.AnyEventData) (bool, error) {
+	return func(eid string, eventData event.AnyEventData) (bool, error) {
+		data := eventData.EventDataCall()
+		if data == nil {
+			return true, fmt.Errorf("event data %s is not EventDataTx", eventData)
+		}
 		if data.Exception != "" {
 			return true, fmt.Errorf(data.Exception)
 		}
-		if !bytes.Equal(data.Origin, origin) {
-			return true, fmt.Errorf("Origin does not match up! Got %x, expected %x",
-				data.Origin, origin)
+		if data.Origin != origin {
+			return true, fmt.Errorf("origin does not match up! Got %s, expected %s", data.Origin, origin)
 		}
 		ret := data.Return
 		if !bytes.Equal(ret, returnCode) {
-			return true, fmt.Errorf("Call did not return correctly. Got %x, expected %x", ret, returnCode)
+			return true, fmt.Errorf("call did not return correctly. Got %x, expected %x", ret, returnCode)
 		}
 		if !bytes.Equal(data.TxID, *txid) {
 			return true, fmt.Errorf("TxIDs do not match up! Got %x, expected %x",
@@ -292,12 +295,11 @@ func unmarshalValidateCall(origin,
 	}
 }
 
-func readResult(t *testing.T, bs []byte) ctypes.BurrowResult {
-	var err error
-	result := new(ctypes.BurrowResult)
-	wire.ReadJSONPtr(result, bs, &err)
+func readResult(t *testing.T, bs []byte) rpc.ResultInner {
+	result := new(rpc.Result)
+	err := json.Unmarshal(bs, result)
 	if err != nil {
-		t.Fatal(err)
+		require.NoError(t, err)
 	}
-	return *result
+	return result.ResultInner
 }
diff --git a/rpc/tm/method/method.go b/rpc/tm/method/method.go
new file mode 100644
index 0000000000000000000000000000000000000000..927c2b4fd3529df075aa449a97b18892c2f4b8e1
--- /dev/null
+++ b/rpc/tm/method/method.go
@@ -0,0 +1,40 @@
+package method
+
+const (
+	Subscribe   = "subscribe"
+	Unsubscribe = "unsubscribe"
+
+	// Status
+	Status  = "status"
+	NetInfo = "net_info"
+
+	// Accounts
+	ListAccounts = "list_accounts"
+	GetAccount   = "get_account"
+	GetStorage   = "get_storage"
+	DumpStorage  = "dump_storage"
+
+	// Simulated call
+	Call     = "call"
+	CallCode = "call_code"
+
+	// Names
+	GetName     = "get_name"
+	ListNames   = "list_names"
+	BroadcastTx = "broadcast_tx"
+
+	// Blockchain
+	Genesis    = "genesis"
+	ChainID    = "chain_id"
+	Blockchain = "blockchain"
+	GetBlock   = "get_block"
+
+	// Consensus
+	ListUnconfirmedTxs = "list_unconfirmed_txs"
+	ListValidators     = "list_validators"
+	DumpConsensusState = "dump_consensus_state"
+
+	// Private keys and signing
+	GeneratePrivateAccount = "unsafe/gen_priv_account"
+	SignTx                 = "unsafe/sign_tx"
+)
diff --git a/rpc/tm/routes.go b/rpc/tm/routes.go
new file mode 100644
index 0000000000000000000000000000000000000000..8223795f7b6efb2c4da03b4c55a82b1ae9439209
--- /dev/null
+++ b/rpc/tm/routes.go
@@ -0,0 +1,141 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tm
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/hyperledger/burrow/event"
+	"github.com/hyperledger/burrow/rpc"
+	"github.com/hyperledger/burrow/rpc/tm/method"
+	"github.com/hyperledger/burrow/txs"
+	gorpc "github.com/tendermint/tendermint/rpc/lib/server"
+	"github.com/tendermint/tendermint/rpc/lib/types"
+)
+
+func GetRoutes(service rpc.Service) map[string]*gorpc.RPCFunc {
+	return map[string]*gorpc.RPCFunc{
+		// Transact
+		method.BroadcastTx: gorpc.NewRPCFunc(func(tx txs.Wrapper) (rpc.Result, error) {
+			return wrapReturnBurrowResult(service.BroadcastTx(tx.Unwrap()))
+		}, "tx"),
+		// Events
+		method.Subscribe: gorpc.NewWSRPCFunc(func(wsCtx rpctypes.WSRPCContext,
+			eventId string) (rpc.Result, error) {
+			return wrapReturnBurrowResult(service.Subscribe(eventId,
+				func(eventData event.AnyEventData) {
+					// NOTE: EventSwitch callbacks must be nonblocking
+					wsCtx.TryWriteRPCResponse(rpctypes.NewRPCSuccessResponse(wsCtx.Request.ID+"#event",
+						rpc.ResultEvent{Event: eventId, AnyEventData: eventData}.Wrap()))
+				}))
+		}, "eventId"),
+		method.Unsubscribe: gorpc.NewWSRPCFunc(func(wsCtx rpctypes.WSRPCContext,
+			subscriptionId string) (rpc.Result, error) {
+			return wrapReturnBurrowResult(service.Unsubscribe(subscriptionId))
+		}, "subscriptionId"),
+		// Status
+		method.Status:  gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.Status), ""),
+		method.NetInfo: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.NetInfo), ""),
+		// Accounts
+		method.ListAccounts: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.ListAccounts), ""),
+		method.GetAccount:   gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.GetAccount), "address"),
+		method.GetStorage:   gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.GetStorage), "address,key"),
+		method.DumpStorage:  gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.DumpStorage), "address"),
+		// Simulated call
+		method.Call:     gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.Call), "fromAddress,toAddress,data"),
+		method.CallCode: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.CallCode), "fromAddress,code,data"),
+		// Blockchain
+		method.Genesis:    gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.Genesis), ""),
+		method.ChainID:    gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.ChainId), ""),
+		method.Blockchain: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.BlockchainInfo), "minHeight,maxHeight"),
+		method.GetBlock:   gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.GetBlock), "height"),
+		// Consensus
+		method.ListUnconfirmedTxs: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.ListUnconfirmedTxs), "maxTxs"),
+		method.ListValidators:     gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.ListValidators), ""),
+		method.DumpConsensusState: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.DumpConsensusState), ""),
+		// Names
+		method.GetName:   gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.GetName), "name"),
+		method.ListNames: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.ListNames), ""),
+		// Private keys and signing
+		method.GeneratePrivateAccount: gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.GeneratePrivateAccount), ""),
+		method.SignTx:                 gorpc.NewRPCFunc(mustWrapFuncBurrowResult(service.SignTx), "tx,privAccounts"),
+	}
+}
+
+func mustWrapFuncBurrowResult(f interface{}) interface{} {
+	wrapped, err := wrapFuncBurrowResult(f)
+	if err != nil {
+		panic(fmt.Errorf("must be able to wrap RPC function: %v", err))
+	}
+	return wrapped
+}
+
+// Takes a function with a covariant return type in func(args...) (ResultInner, error)
+// and returns it as func(args...) (Result, error) so it can be serialised with the mapper
+func wrapFuncBurrowResult(f interface{}) (interface{}, error) {
+	rv := reflect.ValueOf(f)
+	rt := rv.Type()
+	if rt.Kind() != reflect.Func {
+		return nil, fmt.Errorf("must be passed a func f, but got: %#v", f)
+	}
+
+	in := make([]reflect.Type, rt.NumIn())
+	for i := 0; i < rt.NumIn(); i++ {
+		in[i] = rt.In(i)
+	}
+
+	if rt.NumOut() != 2 {
+		return nil, fmt.Errorf("expects f to return the pair of ResultInner, error but got %v return types",
+			rt.NumOut())
+	}
+
+	out := make([]reflect.Type, 2)
+	err := checkTypeImplements(rt.Out(0), (*rpc.ResultInner)(nil))
+	if err != nil {
+		return nil, fmt.Errorf("wrong first return type: %v", err)
+	}
+	err = checkTypeImplements(rt.Out(1), (*error)(nil))
+	if err != nil {
+		return nil, fmt.Errorf("wrong second return type: %v", err)
+	}
+
+	out[0] = reflect.TypeOf(rpc.Result{})
+	out[1] = rt.Out(1)
+
+	return reflect.MakeFunc(reflect.FuncOf(in, out, false),
+		func(args []reflect.Value) []reflect.Value {
+			ret := rv.Call(args)
+			burrowResult := reflect.New(out[0])
+			burrowResult.Elem().Field(0).Set(ret[0])
+			ret[0] = burrowResult.Elem()
+			return ret
+		}).Interface(), nil
+}
+
+func wrapReturnBurrowResult(result rpc.ResultInner, err error) (rpc.Result, error) {
+	return rpc.Result{ResultInner: result}, err
+}
+
+// Passed a type and a pointer to an interface value checks that typ implements that interface
+// returning a human-readable error if it does not
+// (e.g. ifacePtr := (*MyInterface)(nil) can be a reasonably convenient stand-in for an actual type literal),
+func checkTypeImplements(typ reflect.Type, ifacePtr interface{}) error {
+	ifaceType := reflect.TypeOf(ifacePtr).Elem()
+	if !typ.Implements(ifaceType) {
+		return fmt.Errorf("%s does not implement interface %s", typ, ifaceType)
+	}
+	return nil
+}
diff --git a/rpc/tm/routes_test.go b/rpc/tm/routes_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2136c389cdac0d1217971f8b38c308ba8a39873f
--- /dev/null
+++ b/rpc/tm/routes_test.go
@@ -0,0 +1,29 @@
+package tm
+
+import (
+	"testing"
+
+	"github.com/hyperledger/burrow/rpc"
+	"github.com/stretchr/testify/require"
+)
+
+func testChainId(chainName string) (*rpc.ResultChainId, error) {
+	return &rpc.ResultChainId{
+		ChainName:   chainName,
+		ChainId:     "Foos",
+		GenesisHash: []byte{},
+	}, nil
+}
+
+func TestWrapFuncBurrowResult(t *testing.T) {
+	f, err := wrapFuncBurrowResult(testChainId)
+	require.NoError(t, err)
+	fOut, ok := f.(func(string) (rpc.Result, error))
+	require.True(t, ok, "must be able to cast to function type")
+	br, err := fOut("Blum")
+	require.NoError(t, err)
+	bs, err := br.MarshalJSON()
+	require.NoError(t, err)
+	require.Equal(t, `{"type":"result_chain_id","data":{"chain_name":"Blum","chain_id":"Foos","genesis_hash":""}}`,
+		string(bs))
+}
diff --git a/rpc/tm/server.go b/rpc/tm/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..13726d58c0493bfc676f1815deb68c794acf64e8
--- /dev/null
+++ b/rpc/tm/server.go
@@ -0,0 +1,44 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tm
+
+import (
+	"net"
+	"net/http"
+
+	"github.com/hyperledger/burrow/consensus/tendermint"
+	"github.com/hyperledger/burrow/logging/structure"
+	logging_types "github.com/hyperledger/burrow/logging/types"
+	"github.com/hyperledger/burrow/rpc"
+	"github.com/tendermint/tendermint/rpc/lib/server"
+	"github.com/tendermint/tmlibs/events"
+)
+
+func StartServer(service rpc.Service, pattern, listenAddress string, evsw events.EventSwitch,
+	logger logging_types.InfoTraceLogger) (net.Listener, error) {
+
+	logger = logger.With(structure.ComponentKey, "RPC_TM")
+	routes := GetRoutes(service)
+	mux := http.NewServeMux()
+	wm := rpcserver.NewWebsocketManager(routes, evsw)
+	mux.HandleFunc(pattern, wm.WebsocketHandler)
+	tmLogger := tendermint.NewLogger(logger)
+	rpcserver.RegisterRPCFuncs(mux, routes, tmLogger)
+	listener, err := rpcserver.StartHTTPServer(listenAddress, mux, tmLogger)
+	if err != nil {
+		return nil, err
+	}
+	return listener, nil
+}