From e2a168ea9a6295d60d5af4cfe0c711fde974afc3 Mon Sep 17 00:00:00 2001
From: Silas Davis <silas@monax.io>
Date: Tue, 13 Feb 2018 22:44:38 +0000
Subject: [PATCH] Make keys_client_test a monax-keys binary-based integration
 test

Signed-off-by: Silas Davis <silas@monax.io>
---
 .circleci/config.yml                    |   3 +-
 Makefile                                |   5 -
 account/account.go                      |  17 ++-
 account/account_test.go                 |  14 ++-
 account/crypto.go                       |  46 +++++---
 account/private_account.go              |   3 +
 client/mock/client_mock.go              |   7 +-
 consensus/tendermint/query/node_view.go |   4 +-
 core/kernel.go                          |   5 +-
 execution/execution_test.go             |  30 ++++--
 execution/state.go                      |  25 +++--
 execution/state_test.go                 |   9 +-
 genesis/deterministic_genesis.go        |   9 +-
 keys/integration/key_client_test.go     | 134 ++++++++++++++++++++++++
 keys/key_client_test.go                 |  87 ---------------
 keys/mock/key_client_mock.go            |   2 +-
 rpc/service.go                          |   6 +-
 rpc/v0/codec_test.go                    |  14 ++-
 18 files changed, 261 insertions(+), 159 deletions(-)
 create mode 100644 keys/integration/key_client_test.go
 delete mode 100644 keys/key_client_test.go

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 48e2a273..24d6db7a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -38,10 +38,9 @@ jobs:
     steps:
       - attach_workspace:
           at: .
-	  - run: go get -u github.com/monax/keys/cmd/monax-keys
       - run: sudo apt-get install libgmp3-dev
       - run: make test_integration
-      
+
   ensure_vendor:
     <<: *defaults
     steps:
diff --git a/Makefile b/Makefile
index d796092b..00f9c54a 100644
--- a/Makefile
+++ b/Makefile
@@ -133,13 +133,8 @@ test: check
 
 .PHONY: test_integration
 test_integration:
-<<<<<<< Updated upstream
-||||||| merged common ancestors
 	@go get -u github.com/monax/keys/cmd/monax-keys
 	@go test ./keys/integration -tags integration
-=======
-	@go test ./keys/integration -tags integration
->>>>>>> Stashed changes
 	@go test ./rpc/tm/integration -tags integration
 
 # test burrow with checks for race conditions
diff --git a/account/account.go b/account/account.go
index 532bc9e3..f17ceeae 100644
--- a/account/account.go
+++ b/account/account.go
@@ -65,7 +65,7 @@ type Account interface {
 	Permissions() ptypes.AccountPermissions
 	// Obtain a deterministic serialisation of this account
 	// (i.e. update order and Go runtime independent)
-	Encode() []byte
+	Encode() ([]byte, error)
 }
 
 type MutableAccount interface {
@@ -118,7 +118,7 @@ func NewConcreteAccount(pubKey PublicKey) ConcreteAccount {
 }
 
 func NewConcreteAccountFromSecret(secret string) ConcreteAccount {
-	return NewConcreteAccount(PublicKeyFromGoCryptoPubKey(PrivateKeyFromSecret(secret).PubKey()))
+	return NewConcreteAccount(PrivateKeyFromSecret(secret).PublicKey())
 }
 
 // Return as immutable Account
@@ -131,8 +131,15 @@ func (acc ConcreteAccount) MutableAccount() MutableAccount {
 	return concreteAccountWrapper{&acc}
 }
 
-func (acc *ConcreteAccount) Encode() []byte {
-	return wire.BinaryBytes(acc)
+func (acc *ConcreteAccount) Encode() ([]byte, error) {
+	buf := new(bytes.Buffer)
+	var n int
+	var err error
+	wire.WriteBinary(acc, buf, &n, &err)
+	if err != nil {
+		return nil, err
+	}
+	return buf.Bytes(), nil
 }
 
 func (acc *ConcreteAccount) Copy() *ConcreteAccount {
@@ -255,7 +262,7 @@ func (caw concreteAccountWrapper) Permissions() ptypes.AccountPermissions {
 	return caw.ConcreteAccount.Permissions
 }
 
-func (caw concreteAccountWrapper) Encode() []byte {
+func (caw concreteAccountWrapper) Encode() ([]byte, error) {
 	return caw.ConcreteAccount.Encode()
 }
 
diff --git a/account/account_test.go b/account/account_test.go
index f8c775cf..800ea4ff 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -69,7 +69,8 @@ func TestAccountSerialise(t *testing.T) {
 	accStructOut := AccountContainingStruct{}
 
 	// We must pass in a value type to read from (accStruct), but provide a pointer type to write into (accStructout
-	wire.ReadBinaryBytes(wire.BinaryBytes(accStruct), &accStructOut)
+	err := wire.ReadBinaryBytes(wire.BinaryBytes(accStruct), &accStructOut)
+	require.NoError(t, err)
 
 	assert.Equal(t, accStruct, accStructOut)
 }
@@ -77,7 +78,8 @@ func TestAccountSerialise(t *testing.T) {
 func TestDecodeConcrete(t *testing.T) {
 	concreteAcc := NewConcreteAccountFromSecret("Super Semi Secret")
 	acc := concreteAcc.Account()
-	encodedAcc := acc.Encode()
+	encodedAcc, err := acc.Encode()
+	require.NoError(t, err)
 	concreteAccOut, err := DecodeConcrete(encodedAcc)
 	require.NoError(t, err)
 	assert.Equal(t, concreteAcc, *concreteAccOut)
@@ -88,12 +90,14 @@ func TestDecodeConcrete(t *testing.T) {
 func TestDecode(t *testing.T) {
 	concreteAcc := NewConcreteAccountFromSecret("Super Semi Secret")
 	acc := concreteAcc.Account()
-	accOut, err := Decode(acc.Encode())
-	assert.NoError(t, err)
+	encodedAcc, err := acc.Encode()
+	require.NoError(t, err)
+	accOut, err := Decode(encodedAcc)
+	require.NoError(t, err)
 	assert.Equal(t, concreteAcc, *AsConcreteAccount(accOut))
 
 	accOut, err = Decode([]byte("flungepliffery munknut tolopops"))
-	assert.Error(t, err)
+	require.Error(t, err)
 	assert.Nil(t, accOut)
 }
 
diff --git a/account/crypto.go b/account/crypto.go
index e04f257f..942901a3 100644
--- a/account/crypto.go
+++ b/account/crypto.go
@@ -19,13 +19,18 @@ type Signer interface {
 }
 
 // PublicKey
-
 type PublicKey struct {
 	crypto.PubKey `json:"unwrap"`
 }
 
-func PublicKeyFromGoCryptoPubKey(pubKey crypto.PubKey) PublicKey {
-	return PublicKey{PubKey: pubKey}
+func PublicKeyFromGoCryptoPubKey(pubKey crypto.PubKey) (PublicKey, error) {
+	_, err := AddressFromBytes(pubKey.Address())
+	if err != nil {
+		return PublicKey{}, fmt.Errorf("could not make valid address from public key %v: %v", pubKey, err)
+	}
+	return PublicKey{
+		PubKey: pubKey,
+	}, nil
 }
 
 // Currently this is a stub that reads the raw bytes returned by key_client and returns
@@ -39,9 +44,7 @@ func PublicKeyFromBytes(bs []byte) (PublicKey, error) {
 			len(bs), len(pubKeyEd25519))
 	}
 	copy(pubKeyEd25519[:], bs)
-	return PublicKey{
-		PubKey: pubKeyEd25519.Wrap(),
-	}, nil
+	return PublicKeyFromGoCryptoPubKey(pubKeyEd25519.Wrap())
 }
 
 // Returns a copy of the raw untyped public key bytes
@@ -65,6 +68,9 @@ func (pk PublicKey) VerifyBytes(msg []byte, signature Signature) bool {
 }
 
 func (pk PublicKey) Address() Address {
+	// We check this on initialisation to avoid this panic, but returning an error here is ugly and caching
+	// the address on PublicKey initialisation breaks go-wire serialisation since with unwrap we can only have one field.
+	// We can do something better with better serialisation
 	return MustAddressFromBytes(pk.PubKey.Address())
 }
 
@@ -90,8 +96,14 @@ type PrivateKey struct {
 	crypto.PrivKey `json:"unwrap"`
 }
 
-func PrivateKeyFromGoCryptoPrivKey(privKey crypto.PrivKey) PrivateKey {
-	return PrivateKey{PrivKey: privKey}
+func PrivateKeyFromGoCryptoPrivKey(privKey crypto.PrivKey) (PrivateKey, error) {
+	_, err := PublicKeyFromGoCryptoPubKey(privKey.PubKey())
+	if err != nil {
+		return PrivateKey{}, fmt.Errorf("could not create public key from private key: %v", err)
+	}
+	return PrivateKey{
+		PrivKey: privKey,
+	}, nil
 }
 
 func PrivateKeyFromSecret(secret string) PrivateKey {
@@ -126,9 +138,7 @@ func Ed25519PrivateKeyFromRawBytes(privKeyBytes []byte) (PrivateKey, error) {
 		return PrivateKey{}, err
 	}
 	copy(privKeyEd25519[:], privKeyBytes)
-	return PrivateKey{
-		PrivKey: privKeyEd25519.Wrap(),
-	}, nil
+	return PrivateKeyFromGoCryptoPrivKey(privKeyEd25519.Wrap())
 }
 
 // Ensures the last 32 bytes of the ed25519 private key is the public key derived from the first 32 private bytes
@@ -148,6 +158,16 @@ func EnsureEd25519PrivateKeyCorrect(candidatePrivateKey ed25519.PrivateKey) erro
 	return nil
 }
 
+func (pk PrivateKey) PublicKey() PublicKey {
+	publicKey, err := PublicKeyFromGoCryptoPubKey(pk.PrivKey.PubKey())
+	if err != nil {
+		// We check this on initialisation to avoid this panic, but returning an error here is ugly and  caching
+		// the public key on PrivateKey on initialisation breaks go-wire. We can do something better with better serialisation
+		panic(fmt.Errorf("error making public key from private key: %v", publicKey))
+	}
+	return publicKey
+}
+
 // Returns a copy of the raw untyped private key bytes
 func (pk PrivateKey) RawBytes() []byte {
 	switch privKey := pk.PrivKey.Unwrap().(type) {
@@ -164,10 +184,6 @@ func (pk PrivateKey) RawBytes() []byte {
 	}
 }
 
-func (pk PrivateKey) PublicKey() PublicKey {
-	return PublicKeyFromGoCryptoPubKey(pk.PubKey())
-}
-
 func (pk PrivateKey) Sign(msg []byte) (Signature, error) {
 	return Signature{Signature: pk.PrivKey.Sign(msg)}, nil
 }
diff --git a/account/private_account.go b/account/private_account.go
index d1c46644..59f8245f 100644
--- a/account/private_account.go
+++ b/account/private_account.go
@@ -55,6 +55,7 @@ func AsConcretePrivateAccount(privateAccount PrivateAccount) *ConcretePrivateAcc
 		PrivateKey: privateAccount.PrivateKey(),
 	}
 }
+
 func (cpaw concretePrivateAccountWrapper) Address() Address {
 	return cpaw.ConcretePrivateAccount.Address
 }
@@ -67,6 +68,8 @@ func (cpaw concretePrivateAccountWrapper) PrivateKey() PrivateKey {
 	return cpaw.ConcretePrivateAccount.PrivateKey
 }
 
+// ConcretePrivateAccount
+
 func (pa ConcretePrivateAccount) PrivateAccount() PrivateAccount {
 	return concretePrivateAccountWrapper{ConcretePrivateAccount: &pa}
 }
diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go
index 9ae5f192..5e195aef 100644
--- a/client/mock/client_mock.go
+++ b/client/mock/client_mock.go
@@ -51,12 +51,7 @@ func (mock *MockNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketC
 
 func (mock *MockNodeClient) GetAccount(address acm.Address) (acm.Account, error) {
 	// make zero account
-	return acm.ConcreteAccount{
-		Address:     address,
-		PublicKey:   acm.PublicKeyFromGoCryptoPubKey(crypto.PubKeyEd25519{}.Wrap()),
-		Code:        make([]byte, 0),
-		StorageRoot: make([]byte, 0),
-	}.Account(), nil
+	return acm.FromAddressable(acm.GeneratePrivateAccountFromSecret("mock-node-client-account")), nil
 }
 
 func (mock *MockNodeClient) MockAddAccount(account *acm.ConcreteAccount) {
diff --git a/consensus/tendermint/query/node_view.go b/consensus/tendermint/query/node_view.go
index 7aa45176..b0ecc2bd 100644
--- a/consensus/tendermint/query/node_view.go
+++ b/consensus/tendermint/query/node_view.go
@@ -15,7 +15,7 @@ import (
 // You're like the interface I never had
 type NodeView interface {
 	// PrivValidator public key
-	PrivValidatorPublicKey() acm.PublicKey
+	PrivValidatorPublicKey() (acm.PublicKey, error)
 	// NodeInfo for this node broadcast to other nodes (including ephemeral STS ED25519 public key)
 	NodeInfo() *p2p.NodeInfo
 	// Whether the Tendermint node is listening
@@ -46,7 +46,7 @@ func NewNodeView(tmNode *node.Node, txDecoder txs.Decoder) NodeView {
 	}
 }
 
-func (nv *nodeView) PrivValidatorPublicKey() acm.PublicKey {
+func (nv *nodeView) PrivValidatorPublicKey() (acm.PublicKey, error) {
 	return acm.PublicKeyFromGoCryptoPubKey(nv.tmNode.PrivValidator().GetPubKey())
 }
 
diff --git a/core/kernel.go b/core/kernel.go
index 8f7b04f3..89123de5 100644
--- a/core/kernel.go
+++ b/core/kernel.go
@@ -64,7 +64,10 @@ func NewKernel(privValidator tm_types.PrivValidator, genesisDoc *genesis.Genesis
 	logger = logging.WithScope(logger, "NewKernel")
 
 	stateDB := dbm.NewDB("burrow_state", dbm.GoLevelDBBackendStr, tmConf.DBDir())
-	state := execution.MakeGenesisState(stateDB, genesisDoc)
+	state, err := execution.MakeGenesisState(stateDB, genesisDoc)
+	if err != nil {
+		return nil, fmt.Errorf("could not make genesis state: %v", err)
+	}
 	state.Save()
 
 	blockchain := bcm.NewBlockchain(genesisDoc)
diff --git a/execution/execution_test.go b/execution/execution_test.go
index 7d1ac046..a6774d05 100644
--- a/execution/execution_test.go
+++ b/execution/execution_test.go
@@ -35,6 +35,7 @@ import (
 	"github.com/hyperledger/burrow/permission"
 	ptypes "github.com/hyperledger/burrow/permission/types"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/stretchr/testify/require"
 	dbm "github.com/tendermint/tmlibs/db"
 )
 
@@ -168,7 +169,8 @@ func TestSendFails(t *testing.T) {
 	genDoc.Accounts[1].Permissions.Base.Set(permission.Send, true)
 	genDoc.Accounts[2].Permissions.Base.Set(permission.Call, true)
 	genDoc.Accounts[3].Permissions.Base.Set(permission.CreateContract, true)
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//-------------------
@@ -235,7 +237,8 @@ func TestName(t *testing.T) {
 	genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions)
 	genDoc.Accounts[0].Permissions.Base.Set(permission.Send, true)
 	genDoc.Accounts[1].Permissions.Base.Set(permission.Name, true)
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//-------------------
@@ -270,7 +273,8 @@ func TestCallFails(t *testing.T) {
 	genDoc.Accounts[1].Permissions.Base.Set(permission.Send, true)
 	genDoc.Accounts[2].Permissions.Base.Set(permission.Call, true)
 	genDoc.Accounts[3].Permissions.Base.Set(permission.CreateContract, true)
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//-------------------
@@ -339,7 +343,8 @@ func TestSendPermission(t *testing.T) {
 	stateDB := dbm.NewDB("state", dbBackend, dbDir)
 	genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions)
 	genDoc.Accounts[0].Permissions.Base.Set(permission.Send, true) // give the 0 account permission
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	// A single input, having the permission, should succeed
@@ -375,7 +380,8 @@ func TestCallPermission(t *testing.T) {
 	stateDB := dbm.NewDB("state", dbBackend, dbDir)
 	genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions)
 	genDoc.Accounts[0].Permissions.Base.Set(permission.Call, true) // give the 0 account permission
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//------------------------------
@@ -498,7 +504,8 @@ func TestCreatePermission(t *testing.T) {
 	genDoc := newBaseGenDoc(permission.ZeroAccountPermissions, permission.ZeroAccountPermissions)
 	genDoc.Accounts[0].Permissions.Base.Set(permission.CreateContract, true) // give the 0 account permission
 	genDoc.Accounts[0].Permissions.Base.Set(permission.Call, true)           // give the 0 account permission
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//------------------------------
@@ -613,7 +620,7 @@ func TestCreatePermission(t *testing.T) {
 func TestBondPermission(t *testing.T) {
 	stateDB := dbm.NewDB("state",dbBackend,dbDir)
 	genDoc := newBaseGenDoc(PermsAllFalse, PermsAllFalse)
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
 	batchCommitter := makeExecutor(st)
 	var bondAcc *acm.Account
 
@@ -740,7 +747,8 @@ func TestCreateAccountPermission(t *testing.T) {
 	genDoc.Accounts[0].Permissions.Base.Set(permission.Send, true)          // give the 0 account permission
 	genDoc.Accounts[1].Permissions.Base.Set(permission.Send, true)          // give the 0 account permission
 	genDoc.Accounts[0].Permissions.Base.Set(permission.CreateAccount, true) // give the 0 account permission
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//----------------------------------------------------------
@@ -888,7 +896,8 @@ func TestSNativeCALL(t *testing.T) {
 	genDoc.Accounts[3].Permissions.Base.Set(permission.Bond, true) // some arbitrary permission to play with
 	genDoc.Accounts[3].Permissions.AddRole("bumble")
 	genDoc.Accounts[3].Permissions.AddRole("bee")
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//----------------------------------------------------------
@@ -1023,7 +1032,8 @@ func TestSNativeTx(t *testing.T) {
 	genDoc.Accounts[3].Permissions.Base.Set(permission.Bond, true) // some arbitrary permission to play with
 	genDoc.Accounts[3].Permissions.AddRole("bumble")
 	genDoc.Accounts[3].Permissions.AddRole("bee")
-	st := MakeGenesisState(stateDB, &genDoc)
+	st, err := MakeGenesisState(stateDB, &genDoc)
+	require.NoError(t, err)
 	batchCommitter := makeExecutor(st)
 
 	//----------------------------------------------------------
diff --git a/execution/state.go b/execution/state.go
index eb71cf6b..6928480b 100644
--- a/execution/state.go
+++ b/execution/state.go
@@ -27,7 +27,6 @@ import (
 	"github.com/hyperledger/burrow/permission"
 	ptypes "github.com/hyperledger/burrow/permission"
 	"github.com/hyperledger/burrow/txs"
-	"github.com/hyperledger/burrow/util"
 	"github.com/tendermint/go-wire"
 	"github.com/tendermint/merkleeyes/iavl"
 	dbm "github.com/tendermint/tmlibs/db"
@@ -67,9 +66,9 @@ var _ acm.StateIterable = &State{}
 
 var _ acm.StateWriter = &State{}
 
-func MakeGenesisState(db dbm.DB, genDoc *genesis.GenesisDoc) *State {
+func MakeGenesisState(db dbm.DB, genDoc *genesis.GenesisDoc) (*State, error) {
 	if len(genDoc.Validators) == 0 {
-		util.Fatalf("The genesis file has no validators")
+		return nil, fmt.Errorf("the genesis file has no validators")
 	}
 
 	if genDoc.GenesisTime.IsZero() {
@@ -90,7 +89,11 @@ func MakeGenesisState(db dbm.DB, genDoc *genesis.GenesisDoc) *State {
 			Balance:     genAcc.Amount,
 			Permissions: perm,
 		}
-		accounts.Set(acc.Address.Bytes(), acc.Encode())
+		encodedAcc, err := acc.Encode()
+		if err != nil {
+			return nil, err
+		}
+		accounts.Set(acc.Address.Bytes(), encodedAcc)
 	}
 
 	// global permissions are saved as the 0 address
@@ -106,7 +109,11 @@ func MakeGenesisState(db dbm.DB, genDoc *genesis.GenesisDoc) *State {
 		Balance:     1337,
 		Permissions: globalPerms,
 	}
-	accounts.Set(permsAcc.Address.Bytes(), permsAcc.Encode())
+	encodedPermsAcc, err := permsAcc.Encode()
+	if err != nil {
+		return nil, err
+	}
+	accounts.Set(permsAcc.Address.Bytes(), encodedPermsAcc)
 
 	// Make validatorInfos state tree && validators slice
 	/*
@@ -158,7 +165,7 @@ func MakeGenesisState(db dbm.DB, genDoc *genesis.GenesisDoc) *State {
 		accounts: accounts,
 		//validatorInfos:       validatorInfos,
 		nameReg: nameReg,
-	}
+	}, nil
 }
 
 func LoadState(db dbm.DB) (*State, error) {
@@ -248,7 +255,11 @@ func (s *State) GetAccount(address acm.Address) (acm.Account, error) {
 func (s *State) UpdateAccount(account acm.Account) error {
 	s.Lock()
 	defer s.Unlock()
-	s.accounts.Set(account.Address().Bytes(), account.Encode())
+	encodedAccount, err := account.Encode()
+	if err != nil {
+		return err
+	}
+	s.accounts.Set(account.Address().Bytes(), encodedAccount)
 	return nil
 }
 
diff --git a/execution/state_test.go b/execution/state_test.go
index 8ffb5ff2..3297a0d9 100644
--- a/execution/state_test.go
+++ b/execution/state_test.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 
 	"github.com/hyperledger/burrow/execution/evm/sha3"
+	"github.com/stretchr/testify/require"
 
 	"time"
 
@@ -71,7 +72,10 @@ func makeGenesisState(numAccounts int, randBalance bool, minBalance uint64, numV
 	minBonded int64) (*State, []acm.PrivateAccount) {
 	testGenesisDoc, privAccounts := deterministicGenesis.GenesisDoc(numAccounts, randBalance, minBalance,
 		numValidators, randBonded, minBonded)
-	s0 := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc)
+	s0, err := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc)
+	if err != nil {
+		panic(fmt.Errorf("could not make genesis state: %v", err))
+	}
 	s0.Save()
 	return s0, privAccounts
 }
@@ -275,7 +279,8 @@ func TestTxSequence(t *testing.T) {
 }
 
 func TestNameTxs(t *testing.T) {
-	state := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc)
+	state, err := MakeGenesisState(dbm.NewMemDB(), testGenesisDoc)
+	require.NoError(t, err)
 	state.Save()
 
 	txs.MinNameRegistrationPeriod = 5
diff --git a/genesis/deterministic_genesis.go b/genesis/deterministic_genesis.go
index 19d94cbf..d511cc20 100644
--- a/genesis/deterministic_genesis.go
+++ b/genesis/deterministic_genesis.go
@@ -64,15 +64,14 @@ func (dg *deterministicGenesis) GenesisDoc(numAccounts int, randBalance bool, mi
 }
 
 func (dg *deterministicGenesis) Account(randBalance bool, minBalance uint64) (acm.Account, acm.PrivateAccount) {
-	privKey, err := acm.GeneratePrivateKey(dg.random)
+	privateKey, err := acm.GeneratePrivateKey(dg.random)
 	if err != nil {
 		panic(fmt.Errorf("could not generate private key deterministically"))
 	}
-	pubKey := acm.PublicKeyFromGoCryptoPubKey(privKey.PubKey())
 	privAccount := &acm.ConcretePrivateAccount{
-		PublicKey:  pubKey,
-		PrivateKey: privKey,
-		Address:    pubKey.Address(),
+		PublicKey:  privateKey.PublicKey(),
+		PrivateKey: privateKey,
+		Address:    privateKey.PublicKey().Address(),
 	}
 	perms := permission.DefaultAccountPermissions
 	acc := &acm.ConcreteAccount{
diff --git a/keys/integration/key_client_test.go b/keys/integration/key_client_test.go
new file mode 100644
index 00000000..f165074b
--- /dev/null
+++ b/keys/integration/key_client_test.go
@@ -0,0 +1,134 @@
+// +build integration
+
+// Space above here matters
+package integration
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"testing"
+	"time"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/keys"
+	"github.com/hyperledger/burrow/logging/loggers"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+//var logger, _ = lifecycle.NewStdErrLogger()
+var logger = loggers.NewNoopInfoTraceLogger()
+
+const monaxKeysBin = "monax-keys"
+const keysHost = "localhost"
+const keysPort = "56667"
+const keysTimeoutSeconds = 3
+
+var rpcString = fmt.Sprintf("http://%s:%s", keysHost, keysPort)
+
+func TestMain(m *testing.M) {
+	fmt.Fprint(os.Stderr, "Running monax-keys using test main\n")
+	_, err := exec.LookPath(monaxKeysBin)
+	if err != nil {
+		fatalf("could not run keys integration tests because could not find keys binary: %v", err)
+	}
+
+	keysDir, err := ioutil.TempDir("", "key_client_test")
+	if err != nil {
+		fatalf("could not create temp dir: %v", err)
+	}
+	cmd := exec.Command(monaxKeysBin, "server", "--dir", keysDir, "--port", keysPort)
+	err = cmd.Start()
+	if err != nil {
+		fatalf("could not start command: %v", err)
+	}
+
+	select {
+	case <-waitKeysRunning():
+		// A plain call to os.Exit will terminate before deferred calls run, so defer that too.
+		defer os.Exit(m.Run())
+	case <-time.After(keysTimeoutSeconds * time.Second):
+		defer fatalf("timed out waiting for monax-keys to become live")
+	}
+
+	defer func() {
+		err := cmd.Process.Kill()
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error killing monax-keys from test main:%v\n", err)
+		}
+		fmt.Fprint(os.Stderr, "Killed monax-keys from test main\n")
+	}()
+}
+
+func TestMonaxKeyClient_Generate(t *testing.T) {
+	keyClient := keys.NewKeyClient(rpcString, logger)
+	addr, err := keyClient.Generate("I'm a lovely hat", keys.KeyTypeEd25519Ripemd160)
+	assert.NoError(t, err)
+	assert.NotEqual(t, acm.ZeroAddress, addr)
+}
+
+func TestMonaxKeyClient_PublicKey(t *testing.T) {
+	keyClient := keys.NewKeyClient(rpcString, logger)
+	addr, err := keyClient.Generate("I'm a lovely hat", keys.KeyTypeEd25519Ripemd160)
+	assert.NoError(t, err)
+	pubKey, err := keyClient.PublicKey(addr)
+	assert.Equal(t, addr, pubKey.Address())
+}
+
+func TestMonaxKeyClient_PublicKey_NonExistent(t *testing.T) {
+	keyClient := keys.NewKeyClient(rpcString, logger)
+	_, err := keyClient.PublicKey(acm.Address{8, 7, 6, 222})
+	assert.Error(t, err)
+}
+
+func TestMonaxKeyClient_Sign(t *testing.T) {
+	keyClient := keys.NewKeyClient(rpcString, logger)
+	addr, err := keyClient.Generate("I'm a lovely hat", keys.KeyTypeEd25519Ripemd160)
+	require.NoError(t, err)
+	pubKey, err := keyClient.PublicKey(addr)
+	assert.NoError(t, err)
+	message := []byte("I'm a hat, a hat, a hat")
+	signature, err := keyClient.Sign(addr, message)
+	assert.NoError(t, err)
+	assert.True(t, pubKey.VerifyBytes(message, signature), "signature should verify message")
+}
+
+func TestMonaxKeyClient_HealthCheck(t *testing.T) {
+	deadKeyClient := keys.NewKeyClient("http://localhost:99999", logger)
+	assert.NotNil(t, deadKeyClient.HealthCheck())
+	keyClient := keys.NewKeyClient(rpcString, logger)
+	assert.Nil(t, keyClient.HealthCheck())
+}
+
+func TestPublicKeyAddressAgreement(t *testing.T) {
+	keyClient := keys.NewKeyClient(rpcString, logger)
+	addr, err := keyClient.Generate("I'm a lovely hat", keys.KeyTypeEd25519Ripemd160)
+	require.NoError(t, err)
+	pubKey, err := keyClient.PublicKey(addr)
+	addrOut := pubKey.Address()
+	require.NoError(t, err)
+	assert.Equal(t, addr, addrOut)
+}
+
+func fatalf(format string, a ...interface{}) {
+	fmt.Fprintf(os.Stderr, format, a...)
+	os.Exit(1)
+}
+
+func waitKeysRunning() chan bool {
+	ch := make(chan bool)
+	keyClient := keys.NewKeyClient(rpcString, logger)
+	go func() {
+		for {
+			err := keyClient.HealthCheck()
+			if err == nil {
+				ch <- true
+				return
+			}
+		}
+
+	}()
+	return ch
+}
diff --git a/keys/key_client_test.go b/keys/key_client_test.go
deleted file mode 100644
index b1c68678..00000000
--- a/keys/key_client_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package keys
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"testing"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/logging/loggers"
-	"github.com/monax/keys/monax-keys"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-//var logger, _ = lifecycle.NewStdErrLogger()
-var logger = loggers.NewNoopInfoTraceLogger()
-
-const keysHost = "localhost"
-const keysPort = "56757"
-
-var rpcString = fmt.Sprintf("http://%s:%s", keysHost, keysPort)
-
-func TestMain(m *testing.M) {
-	var err error
-	keys.KeysDir, err = ioutil.TempDir("", "key_client_test")
-	if err != nil {
-		fatalf("couldn't create temp dir: %v", err)
-	}
-	go keys.StartServer(keysHost, keysPort)
-	os.Exit(m.Run())
-}
-
-func TestMonaxKeyClient_Generate(t *testing.T) {
-	keyClient := NewKeyClient(rpcString, logger)
-	addr, err := keyClient.Generate("I'm a lovely hat", KeyTypeEd25519Ripemd160)
-	assert.NoError(t, err)
-	assert.NotEqual(t, acm.ZeroAddress, addr)
-}
-
-func TestMonaxKeyClient_PublicKey(t *testing.T) {
-	keyClient := NewKeyClient(rpcString, logger)
-	addr, err := keyClient.Generate("I'm a lovely hat", KeyTypeEd25519Ripemd160)
-	assert.NoError(t, err)
-	pubKey, err := keyClient.PublicKey(addr)
-	assert.Equal(t, addr, pubKey.Address())
-}
-
-func TestMonaxKeyClient_PublicKey_NonExistent(t *testing.T) {
-	keyClient := NewKeyClient(rpcString, logger)
-	_, err := keyClient.PublicKey(acm.Address{8, 7, 6, 222})
-	assert.Error(t, err)
-}
-
-func TestMonaxKeyClient_Sign(t *testing.T) {
-	keyClient := NewKeyClient(rpcString, logger)
-	addr, err := keyClient.Generate("I'm a lovely hat", KeyTypeEd25519Ripemd160)
-	require.NoError(t, err)
-	pubKey, err := keyClient.PublicKey(addr)
-	assert.NoError(t, err)
-	message := []byte("I'm a hat, a hat, a hat")
-	signature, err := keyClient.Sign(addr, message)
-	assert.NoError(t, err)
-	assert.True(t, pubKey.VerifyBytes(message, signature), "signature should verify message")
-}
-
-func TestMonaxKeyClient_HealthCheck(t *testing.T) {
-	deadKeyClient := NewKeyClient("http://localhost:99999", logger)
-	assert.NotNil(t, deadKeyClient.HealthCheck())
-	keyClient := NewKeyClient(rpcString, logger)
-	assert.Nil(t, keyClient.HealthCheck())
-}
-
-func TestPublicKeyAddressAgreement(t *testing.T) {
-	keyClient := NewKeyClient(rpcString, logger)
-	addr, err := keyClient.Generate("I'm a lovely hat", KeyTypeEd25519Ripemd160)
-	require.NoError(t, err)
-	pubKey, err := keyClient.PublicKey(addr)
-	addrOut := pubKey.Address()
-	require.NoError(t, err)
-	assert.Equal(t, addr, addrOut)
-}
-
-func fatalf(format string, a ...interface{}) {
-	fmt.Fprintf(os.Stderr, format, a...)
-	os.Exit(1)
-}
diff --git a/keys/mock/key_client_mock.go b/keys/mock/key_client_mock.go
index 53b311ae..2254c110 100644
--- a/keys/mock/key_client_mock.go
+++ b/keys/mock/key_client_mock.go
@@ -104,7 +104,7 @@ func (mock *MockKeyClient) PublicKey(address acm.Address) (acm.PublicKey, error)
 	}
 	pubKeyEd25519 := crypto.PubKeyEd25519{}
 	copy(pubKeyEd25519[:], key.PublicKey)
-	return acm.PublicKeyFromGoCryptoPubKey(pubKeyEd25519.Wrap()), nil
+	return acm.PublicKeyFromGoCryptoPubKey(pubKeyEd25519.Wrap())
 }
 
 func (mock *MockKeyClient) Generate(keyName string, keyType KeyType) (acm.Address, error) {
diff --git a/rpc/service.go b/rpc/service.go
index 8ccc0725..bab21407 100644
--- a/rpc/service.go
+++ b/rpc/service.go
@@ -143,10 +143,14 @@ func (s *service) Status() (*ResultStatus, error) {
 		latestBlockHash = latestBlockMeta.Header.Hash()
 		latestBlockTime = latestBlockMeta.Header.Time.UnixNano()
 	}
+	publicKey, err := s.nodeView.PrivValidatorPublicKey()
+	if err != nil {
+		return nil, err
+	}
 	return &ResultStatus{
 		NodeInfo:          s.nodeView.NodeInfo(),
 		GenesisHash:       s.blockchain.GenesisHash(),
-		PubKey:            s.nodeView.PrivValidatorPublicKey(),
+		PubKey:            publicKey,
 		LatestBlockHash:   latestBlockHash,
 		LatestBlockHeight: latestHeight,
 		LatestBlockTime:   latestBlockTime,
diff --git a/rpc/v0/codec_test.go b/rpc/v0/codec_test.go
index e8ebaa7e..5bd6d231 100644
--- a/rpc/v0/codec_test.go
+++ b/rpc/v0/codec_test.go
@@ -25,17 +25,21 @@ import (
 func TestKeysEncoding(t *testing.T) {
 	codec := NewTCodec()
 	privateKey := acm.PrivateKeyFromSecret("foo")
-	keyPair := struct {
+	type keyPair struct {
 		PrivateKey acm.PrivateKey
 		PublicKey  acm.PublicKey
-	}{
+	}
+
+	kp := keyPair{
 		PrivateKey: privateKey,
 		PublicKey:  privateKey.PublicKey(),
 	}
 
-	bs, err := codec.EncodeBytes(keyPair)
+	bs, err := codec.EncodeBytes(kp)
 	require.NoError(t, err)
-	assert.Equal(t, `{"PrivateKey":[1,"2C26B46B68FFC68FF99B453C1D30413413422D706483BFA0F98A5E886266E7AE34D26579DBB456693E540672CF922F52DDE0D6532E35BF06BE013A7C532F20E0"],"PublicKey":[1,"34D26579DBB456693E540672CF922F52DDE0D6532E35BF06BE013A7C532F20E0"]}`,
-		string(bs))
 
+	kpOut := keyPair{}
+	codec.DecodeBytes(&kpOut, bs)
+
+	assert.Equal(t, kp, kpOut)
 }
-- 
GitLab