From b82b6f0346a5c0990260d932cec0b17968cf1793 Mon Sep 17 00:00:00 2001
From: Silas Davis <silas@monax.io>
Date: Tue, 3 Apr 2018 22:47:05 +0100
Subject: [PATCH] Remove panic from ChainSign and return error. Unify
 transactioning signing under a single interface method.

Signed-off-by: Silas Davis <silas@monax.io>
---
 account/private_account.go        |  6 +--
 blockchain/blockchain.go          | 19 +++-----
 execution/execution_test.go       | 77 +++++++++++++++----------------
 execution/transactor.go           | 38 ++-------------
 rpc/tm/integration/client_test.go |  5 +-
 rpc/tm/integration/shared.go      |  8 ++--
 txs/bond_tx.go                    | 26 +++++++----
 txs/call_tx.go                    | 15 ++++--
 txs/name_tx.go                    | 15 ++++--
 txs/permission_tx.go              | 16 +++++--
 txs/rebond_tx.go                  | 13 +++++-
 txs/send_tx.go                    | 17 +++++--
 txs/tx.go                         |  1 +
 txs/unbond_tx.go                  | 13 +++++-
 14 files changed, 144 insertions(+), 125 deletions(-)

diff --git a/account/private_account.go b/account/private_account.go
index 120adf54..0ef0bed6 100644
--- a/account/private_account.go
+++ b/account/private_account.go
@@ -86,12 +86,12 @@ func (pa ConcretePrivateAccount) Sign(msg []byte) (Signature, error) {
 	return pa.PrivateKey.Sign(msg)
 }
 
-func ChainSign(signer Signer, chainID string, o Signable) Signature {
+func ChainSign(signer Signer, chainID string, o Signable) (Signature, error) {
 	sig, err := signer.Sign(SignBytes(chainID, o))
 	if err != nil {
-		panic(err)
+		return Signature{}, err
 	}
-	return sig
+	return sig, nil
 }
 
 func (pa *ConcretePrivateAccount) String() string {
diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go
index 26e3eb5b..c81f4524 100644
--- a/blockchain/blockchain.go
+++ b/blockchain/blockchain.go
@@ -132,12 +132,9 @@ func NewBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc) *blockchain {
 	}
 	root := NewRoot(genesisDoc)
 	return &blockchain{
-		db:   db,
-		root: root,
-		tip: &tip{
-			lastBlockTime:         root.genesisDoc.GenesisTime,
-			appHashAfterLastBlock: root.genesisHash,
-		},
+		db:         db,
+		root:       root,
+		tip:        NewTip(genesisDoc.ChainID(), root.genesisDoc.GenesisTime, root.genesisHash),
 		validators: validators,
 	}
 }
@@ -164,14 +161,12 @@ func NewRoot(genesisDoc *genesis.GenesisDoc) *root {
 	}
 }
 
-// Create
-func NewTip(chainID string, lastBlockHeight uint64, lastBlockTime time.Time, lastBlockHash []byte, appHashAfterLastBlock []byte) *tip {
+// Create genesis Tip
+func NewTip(chainID string, genesisTime time.Time, genesisHash []byte) *tip {
 	return &tip{
 		chainID:               chainID,
-		lastBlockHeight:       lastBlockHeight,
-		lastBlockTime:         lastBlockTime,
-		lastBlockHash:         lastBlockHash,
-		appHashAfterLastBlock: appHashAfterLastBlock,
+		lastBlockTime:         genesisTime,
+		appHashAfterLastBlock: genesisHash,
 	}
 }
 
diff --git a/execution/execution_test.go b/execution/execution_test.go
index 9e20db6b..6401c8fc 100644
--- a/execution/execution_test.go
+++ b/execution/execution_test.go
@@ -192,7 +192,7 @@ func TestSendFails(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[1].Address(), 5)
-	tx.SignInput(testChainID, 0, users[0])
+	require.NoError(t, tx.Sign(testChainID, users[0]))
 	if err := batchCommitter.Execute(tx); err == nil {
 		t.Fatal("Expected error")
 	} else {
@@ -205,7 +205,7 @@ func TestSendFails(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[4].Address(), 5)
-	tx.SignInput(testChainID, 0, users[2])
+	require.NoError(t, tx.Sign(testChainID, users[2]))
 	if err := batchCommitter.Execute(tx); err == nil {
 		t.Fatal("Expected error")
 	} else {
@@ -218,7 +218,7 @@ func TestSendFails(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[4].Address(), 5)
-	tx.SignInput(testChainID, 0, users[3])
+	require.NoError(t, tx.Sign(testChainID, users[3]))
 	if err := batchCommitter.Execute(tx); err == nil {
 		t.Fatal("Expected error")
 	} else {
@@ -234,7 +234,7 @@ func TestSendFails(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[6].Address(), 5)
-	tx.SignInput(testChainID, 0, users[3])
+	require.NoError(t, tx.Sign(testChainID, users[3]))
 	if err := batchCommitter.Execute(tx); err == nil {
 		t.Fatal("Expected error")
 	} else {
@@ -366,7 +366,7 @@ func TestSendPermission(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[1].Address(), 5)
-	tx.SignInput(testChainID, 0, users[0])
+	require.NoError(t, tx.Sign(testChainID, users[0]))
 	if err := batchCommitter.Execute(tx); err != nil {
 		t.Fatal("Transaction failed", err)
 	}
@@ -380,8 +380,7 @@ func TestSendPermission(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[2].Address(), 10)
-	tx.SignInput(testChainID, 0, users[0])
-	tx.SignInput(testChainID, 1, users[1])
+	require.NoError(t, tx.Sign(testChainID, users[:2]...))
 	if err := batchCommitter.Execute(tx); err == nil {
 		t.Fatal("Expected error")
 	} else {
@@ -651,7 +650,7 @@ func TestCreateAccountPermission(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[6].Address(), 5)
-	tx.SignInput(testChainID, 0, users[0])
+	require.NoError(t, tx.Sign(testChainID, users[0]))
 	if err := batchCommitter.Execute(tx); err != nil {
 		t.Fatal("Transaction failed", err)
 	}
@@ -665,8 +664,7 @@ func TestCreateAccountPermission(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[7].Address(), 10)
-	tx.SignInput(testChainID, 0, users[0])
-	tx.SignInput(testChainID, 1, users[1])
+	require.NoError(t, tx.Sign(testChainID, users[:2]...))
 	if err := batchCommitter.Execute(tx); err == nil {
 		t.Fatal("Expected error")
 	} else {
@@ -683,8 +681,7 @@ func TestCreateAccountPermission(t *testing.T) {
 	}
 	tx.AddOutput(users[7].Address(), 4)
 	tx.AddOutput(users[4].Address(), 6)
-	tx.SignInput(testChainID, 0, users[0])
-	tx.SignInput(testChainID, 1, users[1])
+	require.NoError(t, tx.Sign(testChainID, users[:2]...))
 	if err := batchCommitter.Execute(tx); err == nil {
 		t.Fatal("Expected error")
 	} else {
@@ -703,8 +700,7 @@ func TestCreateAccountPermission(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[7].Address(), 10)
-	tx.SignInput(testChainID, 0, users[0])
-	tx.SignInput(testChainID, 1, users[1])
+	require.NoError(t, tx.Sign(testChainID, users[:2]...))
 	if err := batchCommitter.Execute(tx); err != nil {
 		t.Fatal("Unexpected error", err)
 	}
@@ -719,8 +715,7 @@ func TestCreateAccountPermission(t *testing.T) {
 	}
 	tx.AddOutput(users[7].Address(), 7)
 	tx.AddOutput(users[4].Address(), 3)
-	tx.SignInput(testChainID, 0, users[0])
-	tx.SignInput(testChainID, 1, users[1])
+	require.NoError(t, tx.Sign(testChainID, users[:2]...))
 	if err := batchCommitter.Execute(tx); err != nil {
 		t.Fatal("Unexpected error", err)
 	}
@@ -1002,7 +997,7 @@ func TestTxSequence(t *testing.T) {
 		tx := txs.NewSendTx()
 		tx.AddInputWithSequence(acc0PubKey, 1, sequence)
 		tx.AddOutput(acc1.Address(), 1)
-		tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 		stateCopy := state.Copy(dbm.NewMemDB())
 		err := execTxWithState(stateCopy, tx)
 		if i == 1 {
@@ -1216,19 +1211,19 @@ func TestNameTxs(t *testing.T) {
 // Test creating a contract from futher down the call stack
 /*
 contract Factory {
-    address a;
-    function create() returns (address){
-        a = new PreFactory();
-        return a;
-    }
+   address a;
+   function create() returns (address){
+       a = new PreFactory();
+       return a;
+   }
 }
 
 contract PreFactory{
-    address a;
-    function create(Factory c) returns (address) {
-    	a = c.create();
-    	return a;
-    }
+   address a;
+   function create(Factory c) returns (address) {
+   	a = c.create();
+   	return a;
+   }
 }
 */
 
@@ -1270,7 +1265,7 @@ func TestCreates(t *testing.T) {
 		Data:     createData,
 	}
 
-	tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 	err := execTxWithState(state, tx)
 	if err != nil {
 		t.Errorf("Got error in executing call transaction, %v", err)
@@ -1294,7 +1289,7 @@ func TestCreates(t *testing.T) {
 		Data:     createData,
 	}
 
-	tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 	err = execTxWithState(state, tx)
 	if err != nil {
 		t.Errorf("Got error in executing call transaction, %v", err)
@@ -1311,9 +1306,9 @@ func TestCreates(t *testing.T) {
 
 /*
 contract Caller {
-    function send(address x){
-        x.send(msg.value);
-    }
+   function send(address x){
+       x.send(msg.value);
+   }
 }
 */
 var callerCode, _ = hex.DecodeString("60606040526000357c0100000000000000000000000000000000000000000000000000000000900480633e58c58c146037576035565b005b604b6004808035906020019091905050604d565b005b8073ffffffffffffffffffffffffffffffffffffffff16600034604051809050600060405180830381858888f19350505050505b5056")
@@ -1349,7 +1344,7 @@ func TestContractSend(t *testing.T) {
 		Data:     sendData,
 	}
 
-	tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 	err := execTxWithState(state, tx)
 	if err != nil {
 		t.Errorf("Got error in executing call transaction, %v", err)
@@ -1391,7 +1386,7 @@ func TestMerklePanic(t *testing.T) {
 			},
 		}
 
-		tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 		err := execTxWithState(stateSendTx, tx)
 		if err != nil {
 			t.Errorf("Got error in executing send transaction, %v", err)
@@ -1417,7 +1412,7 @@ func TestMerklePanic(t *testing.T) {
 			GasLimit: 10,
 		}
 
-		tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 		err := execTxWithState(stateCallTx, tx)
 		if err != nil {
 			t.Errorf("Got error in executing call transaction, %v", err)
@@ -1458,7 +1453,7 @@ func TestTxs(t *testing.T) {
 			},
 		}
 
-		tx.Inputs[0].Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 		err := execTxWithState(stateSendTx, tx)
 		if err != nil {
 			t.Errorf("Got error in executing send transaction, %v", err)
@@ -1492,7 +1487,7 @@ func TestTxs(t *testing.T) {
 			GasLimit: 10,
 		}
 
-		tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 		err := execTxWithState(stateCallTx, tx)
 		if err != nil {
 			t.Errorf("Got error in executing call transaction, %v", err)
@@ -1543,7 +1538,7 @@ proof-of-work chain as proof of what happened while they were gone `
 			Data: entryData,
 		}
 
-		tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 
 		err := execTxWithState(stateNameTx, tx)
 		if err != nil {
@@ -1566,7 +1561,7 @@ proof-of-work chain as proof of what happened while they were gone `
 		// test a bad string
 		tx.Data = string([]byte{0, 1, 2, 3, 127, 128, 129, 200, 251})
 		tx.Input.Sequence += 1
-		tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 		err = execTxWithState(stateNameTx, tx)
 		if _, ok := err.(txs.ErrTxInvalidString); !ok {
 			t.Errorf("Expected invalid string error. Got: %s", err.Error())
@@ -1648,7 +1643,7 @@ func TestSelfDestruct(t *testing.T) {
 
 	// send call tx with no data, cause self-destruct
 	tx := txs.NewCallTxWithSequence(acc0PubKey, addressPtr(acc1), nil, sendingAmount, 1000, 0, acc0.Sequence()+1)
-	tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 
 	// we use cache instead of execTxWithState so we can run the tx twice
 	exe := NewBatchCommitter(state, testChainID, bcm.NewBlockchain(nil, testGenesisDoc), event.NewNoOpPublisher(), logger)
@@ -1659,7 +1654,7 @@ func TestSelfDestruct(t *testing.T) {
 	// if we do it again, we won't get an error, but the self-destruct
 	// shouldn't happen twice and the caller should lose fee
 	tx.Input.Sequence += 1
-	tx.Input.Signature = acm.ChainSign(privAccounts[0], testChainID, tx)
+	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
 	if err := exe.Execute(tx); err != nil {
 		t.Errorf("Got error in executing call transaction, %v", err)
 	}
diff --git a/execution/transactor.go b/execution/transactor.go
index 952aaab0..a875fe6b 100644
--- a/execution/transactor.go
+++ b/execution/transactor.go
@@ -346,7 +346,6 @@ func (trans *Transactor) SendAndHold(privKey []byte, toAddress acm.Address, amou
 }
 
 func (trans *Transactor) TransactNameReg(privKey []byte, name, data string, amount, fee uint64) (*txs.Receipt, error) {
-
 	if len(privKey) != 64 {
 		return nil, fmt.Errorf("Private key is not of the right length: %d\n", len(privKey))
 	}
@@ -377,40 +376,9 @@ func (trans *Transactor) TransactNameReg(privKey []byte, name, data string, amou
 // Sign a transaction
 func (trans *Transactor) SignTx(tx txs.Tx, signingAccounts []acm.SigningAccount) (txs.Tx, error) {
 	// more checks?
-
-	chainID := trans.tip.ChainID()
-	switch tx.(type) {
-	case *txs.NameTx:
-		nameTx := tx.(*txs.NameTx)
-		nameTx.Input.PublicKey = signingAccounts[0].PublicKey()
-		nameTx.Input.Signature = acm.ChainSign(signingAccounts[0], chainID, nameTx)
-	case *txs.SendTx:
-		sendTx := tx.(*txs.SendTx)
-		for i, input := range sendTx.Inputs {
-			input.PublicKey = signingAccounts[i].PublicKey()
-			input.Signature = acm.ChainSign(signingAccounts[i], chainID, sendTx)
-		}
-	case *txs.CallTx:
-		callTx := tx.(*txs.CallTx)
-		callTx.Input.PublicKey = signingAccounts[0].PublicKey()
-		callTx.Input.Signature = acm.ChainSign(signingAccounts[0], chainID, callTx)
-	case *txs.BondTx:
-		bondTx := tx.(*txs.BondTx)
-		// the first privaccount corresponds to the BondTx pub key.
-		// the rest to the inputs
-		bondTx.Signature = acm.ChainSign(signingAccounts[0], chainID, bondTx)
-		for i, input := range bondTx.Inputs {
-			input.PublicKey = signingAccounts[i+1].PublicKey()
-			input.Signature = acm.ChainSign(signingAccounts[i+1], chainID, bondTx)
-		}
-	case *txs.UnbondTx:
-		unbondTx := tx.(*txs.UnbondTx)
-		unbondTx.Signature = acm.ChainSign(signingAccounts[0], chainID, unbondTx)
-	case *txs.RebondTx:
-		rebondTx := tx.(*txs.RebondTx)
-		rebondTx.Signature = acm.ChainSign(signingAccounts[0], chainID, rebondTx)
-	default:
-		return nil, fmt.Errorf("Object is not a proper transaction: %v\n", tx)
+	err := tx.Sign(trans.tip.ChainID(), signingAccounts...)
+	if err != nil {
+		return nil, err
 	}
 	return tx, nil
 }
diff --git a/rpc/tm/integration/client_test.go b/rpc/tm/integration/client_test.go
index a827524f..268953bc 100644
--- a/rpc/tm/integration/client_test.go
+++ b/rpc/tm/integration/client_test.go
@@ -59,14 +59,15 @@ func TestBroadcastTx(t *testing.T) {
 		require.NoError(t, err)
 		assert.False(t, receipt.CreatesContract, "This tx should not create a contract")
 		assert.NotEmpty(t, receipt.TxHash, "Failed to compute tx hash")
-		n, errp := new(int), new(error)
-		buf := new(bytes.Buffer)
+
+		buf, n, errp := new(bytes.Buffer), new(int), new(error)
 		hasher := ripemd160.New()
 		tx.WriteSignBytes(genesisDoc.ChainID(), buf, n, errp)
 		assert.NoError(t, *errp)
 		txSignBytes := buf.Bytes()
 		hasher.Write(txSignBytes)
 		txHashExpected := hasher.Sum(nil)
+
 		if bytes.Compare(receipt.TxHash, txHashExpected) != 0 {
 			t.Fatalf("The receipt hash '%x' does not equal the ripemd160 hash of the "+
 				"transaction signed bytes calculated in the test: '%x'",
diff --git a/rpc/tm/integration/shared.go b/rpc/tm/integration/shared.go
index 0844aa2b..bd7fb72e 100644
--- a/rpc/tm/integration/shared.go
+++ b/rpc/tm/integration/shared.go
@@ -56,7 +56,7 @@ const (
 )
 
 // Enable logger output during tests
-var debugLogging = true
+var debugLogging = false
 
 // global variables for use across all tests
 var (
@@ -165,7 +165,7 @@ func makeDefaultSendTx(t *testing.T, client tm_client.RPCClient, addr acm.Addres
 
 func makeDefaultSendTxSigned(t *testing.T, client tm_client.RPCClient, addr acm.Address, amt uint64) *txs.SendTx {
 	tx := makeDefaultSendTx(t, client, addr, amt)
-	tx.SignInput(genesisDoc.ChainID(), 0, privateAccounts[0])
+	require.NoError(t, tx.Sign(genesisDoc.ChainID(), privateAccounts[0]))
 	return tx
 }
 
@@ -174,14 +174,14 @@ func makeDefaultCallTx(t *testing.T, client tm_client.RPCClient, addr *acm.Addre
 	sequence := getSequence(t, client, privateAccounts[0].Address())
 	tx := txs.NewCallTxWithSequence(privateAccounts[0].PublicKey(), addr, code, amt, gasLim, fee,
 		sequence+1)
-	tx.Sign(genesisDoc.ChainID(), privateAccounts[0])
+	require.NoError(t, tx.Sign(genesisDoc.ChainID(), privateAccounts[0]))
 	return tx
 }
 
 func makeDefaultNameTx(t *testing.T, client tm_client.RPCClient, name, value string, amt, fee uint64) *txs.NameTx {
 	sequence := getSequence(t, client, privateAccounts[0].Address())
 	tx := txs.NewNameTxWithSequence(privateAccounts[0].PublicKey(), name, value, amt, fee, sequence+1)
-	tx.Sign(genesisDoc.ChainID(), privateAccounts[0])
+	require.NoError(t, tx.Sign(genesisDoc.ChainID(), privateAccounts[0]))
 	return tx
 }
 
diff --git a/txs/bond_tx.go b/txs/bond_tx.go
index cbb4bf8a..b6537092 100644
--- a/txs/bond_tx.go
+++ b/txs/bond_tx.go
@@ -90,16 +90,22 @@ func (tx *BondTx) AddOutput(addr acm.Address, amt uint64) error {
 	return nil
 }
 
-func (tx *BondTx) SignBond(chainID string, privAccount acm.SigningAccount) error {
-	tx.Signature = acm.ChainSign(privAccount, chainID, tx)
-	return nil
-}
-
-func (tx *BondTx) SignInput(chainID string, i int, privAccount acm.SigningAccount) error {
-	if i >= len(tx.Inputs) {
-		return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs))
+func (tx *BondTx) Sign(chainID string, signingAccounts ...acm.SigningAccount) error {
+	if len(signingAccounts) != len(tx.Inputs)+1 {
+		return fmt.Errorf("BondTx expects %v SigningAccounts but got %v", len(tx.Inputs)+1,
+			len(signingAccounts))
+	}
+	var err error
+	tx.Signature, err = acm.ChainSign(signingAccounts[0], chainID, tx)
+	if err != nil {
+		return fmt.Errorf("could not sign %v: %v", tx, err)
+	}
+	for i := 1; i <= len(signingAccounts); i++ {
+		tx.Inputs[i].PublicKey = signingAccounts[i].PublicKey()
+		tx.Inputs[i].Signature, err = acm.ChainSign(signingAccounts[i], chainID, tx)
+		if err != nil {
+			return fmt.Errorf("could not sign tx %v input %v: %v", tx, tx.Inputs[i], err)
+		}
 	}
-	tx.Inputs[i].PublicKey = privAccount.PublicKey()
-	tx.Inputs[i].Signature = acm.ChainSign(privAccount, chainID, tx)
 	return nil
 }
diff --git a/txs/call_tx.go b/txs/call_tx.go
index ff568aef..234972cd 100644
--- a/txs/call_tx.go
+++ b/txs/call_tx.go
@@ -55,9 +55,18 @@ func NewCallTxWithSequence(from acm.PublicKey, to *acm.Address, data []byte,
 	}
 }
 
-func (tx *CallTx) Sign(chainID string, privAccount acm.SigningAccount) {
-	tx.Input.PublicKey = privAccount.PublicKey()
-	tx.Input.Signature = acm.ChainSign(privAccount, chainID, tx)
+func (tx *CallTx) Sign(chainID string, signingAccounts ...acm.SigningAccount) error {
+	if len(signingAccounts) != 1 {
+		return fmt.Errorf("CallTx expects a single SigningAccount for its single Input but %v were provieded",
+			len(signingAccounts))
+	}
+	var err error
+	tx.Input.PublicKey = signingAccounts[0].PublicKey()
+	tx.Input.Signature, err = acm.ChainSign(signingAccounts[0], chainID, tx)
+	if err != nil {
+		return fmt.Errorf("could not sign %v: %v", tx, err)
+	}
+	return nil
 }
 
 func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
diff --git a/txs/name_tx.go b/txs/name_tx.go
index c00b6839..a9143ab3 100644
--- a/txs/name_tx.go
+++ b/txs/name_tx.go
@@ -56,9 +56,18 @@ func NewNameTxWithSequence(from acm.PublicKey, name, data string, amt, fee, sequ
 	}
 }
 
-func (tx *NameTx) Sign(chainID string, signingAccount acm.SigningAccount) {
-	tx.Input.PublicKey = signingAccount.PublicKey()
-	tx.Input.Signature = acm.ChainSign(signingAccount, chainID, tx)
+func (tx *NameTx) Sign(chainID string, signingAccounts ...acm.SigningAccount) error {
+	if len(signingAccounts) != 1 {
+		return fmt.Errorf("NameTx expects a single SigningAccount for its single Input but %v were provieded",
+			len(signingAccounts))
+	}
+	var err error
+	tx.Input.PublicKey = signingAccounts[0].PublicKey()
+	tx.Input.Signature, err = acm.ChainSign(signingAccounts[0], chainID, tx)
+	if err != nil {
+		return fmt.Errorf("could not sign %v: %v", tx, err)
+	}
+	return nil
 }
 
 func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
diff --git a/txs/permission_tx.go b/txs/permission_tx.go
index 755dbfe3..a1ed4969 100644
--- a/txs/permission_tx.go
+++ b/txs/permission_tx.go
@@ -46,10 +46,20 @@ func NewPermissionsTxWithSequence(from acm.PublicKey, args snatives.PermArgs, se
 	}
 }
 
-func (tx *PermissionsTx) Sign(chainID string, privAccount acm.SigningAccount) {
-	tx.Input.PublicKey = privAccount.PublicKey()
-	tx.Input.Signature = acm.ChainSign(privAccount, chainID, tx)
+func (tx *PermissionsTx) Sign(chainID string, signingAccounts ...acm.SigningAccount) error {
+	if len(signingAccounts) != 1 {
+		return fmt.Errorf("PermissionsTx expects a single SigningAccount for its single Input but %v were provieded",
+			len(signingAccounts))
+	}
+	var err error
+	tx.Input.PublicKey = signingAccounts[0].PublicKey()
+	tx.Input.Signature, err = acm.ChainSign(signingAccounts[0], chainID, tx)
+	if err != nil {
+		return fmt.Errorf("could not sign %v: %v", tx, err)
+	}
+	return nil
 }
+
 func (tx *PermissionsTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
 	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
 	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"args":"`, TxTypePermissions)), w, n, err)
diff --git a/txs/rebond_tx.go b/txs/rebond_tx.go
index ec13498b..93b95898 100644
--- a/txs/rebond_tx.go
+++ b/txs/rebond_tx.go
@@ -24,8 +24,17 @@ func NewRebondTx(addr acm.Address, height int) *RebondTx {
 	}
 }
 
-func (tx *RebondTx) Sign(chainID string, privAccount acm.SigningAccount) {
-	tx.Signature = acm.ChainSign(privAccount, chainID, tx)
+func (tx *RebondTx) Sign(chainID string, signingAccounts ...acm.SigningAccount) error {
+	if len(signingAccounts) != 1 {
+		return fmt.Errorf("RebondTx expects a single SigningAccount for its signature but %v were provieded",
+			len(signingAccounts))
+	}
+	var err error
+	tx.Signature, err = acm.ChainSign(signingAccounts[0], chainID, tx)
+	if err != nil {
+		return fmt.Errorf("could not sign %v: %v", tx, err)
+	}
+	return nil
 }
 
 func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
diff --git a/txs/send_tx.go b/txs/send_tx.go
index 06c651a3..02c7039c 100644
--- a/txs/send_tx.go
+++ b/txs/send_tx.go
@@ -86,11 +86,18 @@ func (tx *SendTx) AddOutput(addr acm.Address, amt uint64) error {
 	return nil
 }
 
-func (tx *SendTx) SignInput(chainID string, i int, privAccount acm.SigningAccount) error {
-	if i >= len(tx.Inputs) {
-		return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs))
+func (tx *SendTx) Sign(chainID string, signingAccounts ...acm.SigningAccount) error {
+	if len(signingAccounts) != len(tx.Inputs) {
+		return fmt.Errorf("SendTx has %v Inputs but was provided with %v SigningAccounts", len(tx.Inputs),
+			len(signingAccounts))
+	}
+	var err error
+	for i, signingAccount := range signingAccounts {
+		tx.Inputs[i].PublicKey = signingAccount.PublicKey()
+		tx.Inputs[i].Signature, err = acm.ChainSign(signingAccount, chainID, tx)
+		if err != nil {
+			return fmt.Errorf("could not sign tx %v input %v: %v", tx, tx.Inputs[i], err)
+		}
 	}
-	tx.Inputs[i].PublicKey = privAccount.PublicKey()
-	tx.Inputs[i].Signature = acm.ChainSign(privAccount, chainID, tx)
 	return nil
 }
diff --git a/txs/tx.go b/txs/tx.go
index bfcf3ac1..3e7f0419 100644
--- a/txs/tx.go
+++ b/txs/tx.go
@@ -84,6 +84,7 @@ type Tx interface {
 	String() string
 	GetInputs() []TxInput
 	Hash(chainID string) []byte
+	Sign(chainID string, signingAccounts ...acm.SigningAccount) error
 }
 
 type Encoder interface {
diff --git a/txs/unbond_tx.go b/txs/unbond_tx.go
index 2411eaf9..ab016a0d 100644
--- a/txs/unbond_tx.go
+++ b/txs/unbond_tx.go
@@ -24,8 +24,17 @@ func NewUnbondTx(addr acm.Address, height int) *UnbondTx {
 	}
 }
 
-func (tx *UnbondTx) Sign(chainID string, privAccount acm.SigningAccount) {
-	tx.Signature = acm.ChainSign(privAccount, chainID, tx)
+func (tx *UnbondTx) Sign(chainID string, signingAccounts ...acm.SigningAccount) error {
+	if len(signingAccounts) != 1 {
+		return fmt.Errorf("UnbondTx expects a single SigningAccount for its signature but %v were provieded",
+			len(signingAccounts))
+	}
+	var err error
+	tx.Signature, err = acm.ChainSign(signingAccounts[0], chainID, tx)
+	if err != nil {
+		return fmt.Errorf("could not sign %v: %v", tx, err)
+	}
+	return nil
 }
 
 func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-- 
GitLab