From ab786c0748593bc395ad0cc224a60a8573fcad8c Mon Sep 17 00:00:00 2001
From: Siggi <github@icellan.net>
Date: Mon, 11 Apr 2022 18:43:34 +0200
Subject: [PATCH] Fixed transaction signing

---
 model_draft_transactions.go      |   4 +
 model_draft_transactions_test.go | 121 +++++++++++++++++++++++++++++++
 utils/scripts.go                 |  17 +++--
 3 files changed, 134 insertions(+), 8 deletions(-)

diff --git a/model_draft_transactions.go b/model_draft_transactions.go
index abe588ba..14370135 100644
--- a/model_draft_transactions.go
+++ b/model_draft_transactions.go
@@ -303,6 +303,9 @@ func (m *DraftTransaction) processUtxos(ctx context.Context, utxos []*Utxo) erro
 		if err != nil {
 			return err
 		}
+		if destination == nil {
+			return ErrMissingDestination
+		}
 		m.Configuration.Inputs = append(
 			m.Configuration.Inputs, &TransactionInput{
 				Utxo:        *utxo,
@@ -673,6 +676,7 @@ func (m *DraftTransaction) SignInputs(xPriv *bip32.ExtendedKey) (signedHex strin
 			return
 		}
 		txDraft.Inputs[index].PreviousTxScript = ls
+		txDraft.Inputs[index].PreviousTxSatoshis = input.Satoshis
 
 		// Derive the child key (chain)
 		var chainKey *bip32.ExtendedKey
diff --git a/model_draft_transactions_test.go b/model_draft_transactions_test.go
index 8313f9d5..291ff027 100644
--- a/model_draft_transactions_test.go
+++ b/model_draft_transactions_test.go
@@ -3,13 +3,20 @@ package bux
 import (
 	"encoding/hex"
 	"encoding/json"
+	"fmt"
 	"io/ioutil"
 	"os"
+	"strings"
 	"testing"
 	"time"
 
 	"github.com/BuxOrg/bux/utils"
+	"github.com/bitcoinschema/go-bitcoin/v2"
+	"github.com/libsv/go-bk/bec"
+	"github.com/libsv/go-bk/bip32"
 	"github.com/libsv/go-bt/v2"
+	"github.com/libsv/go-bt/v2/bscript"
+	"github.com/libsv/go-bt/v2/sighash"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -830,3 +837,117 @@ func TestDraftTransaction_RegisterTasks(t *testing.T) {
 		assert.Equal(t, 10*time.Minute, client.GetTaskPeriod(draftCleanupTask))
 	})
 }
+
+func TestDraftTransaction_SignInputs(t *testing.T) {
+	ctx, client, deferMe := CreateTestSQLiteClient(t, true, false)
+	defer deferMe()
+
+	xPrivString := "xprv9s21ZrQH143K31pvNoYNcRZjtdJXnNVEc5NmBbgJmEg27YWbZVL7jTLQhPELqAR7tcJTnF9AJLwVN5w3ABZvrfeDLm4vnBDw76bkx8a2NxK"
+	xPrivHD, err := bitcoin.GenerateHDKeyFromString(xPrivString)
+	require.NoError(t, err)
+	xPubHD, _ := xPrivHD.Neuter()
+	xPubID := utils.Hash(xPubHD.String())
+
+	xPub := newXpub(xPubHD.String(), client.DefaultModelOptions(New())...)
+	err = xPub.Save(ctx)
+	require.NoError(t, err)
+
+	// Derive the child key (chain)
+	var chainKey *bip32.ExtendedKey
+	if chainKey, err = xPrivHD.Child(
+		0,
+	); err != nil {
+		return
+	}
+
+	// Derive the child key (num)
+	var numKey *bip32.ExtendedKey
+	if numKey, err = chainKey.Child(
+		0,
+	); err != nil {
+		return
+	}
+
+	// Get the private key
+	var privateKey *bec.PrivateKey
+	if privateKey, err = bitcoin.GetPrivateKeyFromHDKey(
+		numKey,
+	); err != nil {
+		return
+	}
+
+	// create a destination for the utxo
+	lockingScript := "76a91447868e6b13de36e2739d8f2a9e0e0a323ad9b8ff88ac"
+	destination := newDestination(xPubID, lockingScript, append(client.DefaultModelOptions(), New())...)
+	err = destination.Save(ctx)
+	require.NoError(t, err)
+
+	// create a utxo with enough output for all our tests
+	txID := "aa9cd99d6c1c179de6ab9785f6d27d6589a72c1523f1fc37de041a66dbbdbdb6"
+	utxo := newUtxo(xPubID, txID, lockingScript, 0, 12229, client.DefaultModelOptions(New())...)
+	err = utxo.Save(ctx)
+	require.NoError(t, err)
+
+	tests := []struct {
+		name    string
+		config  *TransactionConfig
+		xPriv   *bip32.ExtendedKey
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "sign 1",
+			config: &TransactionConfig{
+				SendAllTo: "1AqYEDUf16CHaD2guBLHHhosfV2AyYJLz",
+			},
+			xPriv:   xPrivHD,
+			wantErr: assert.NoError,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			m := newDraftTransaction(xPub.rawXpubKey, tt.config, client.DefaultModelOptions(New())...)
+			err = m.createTransactionHex(ctx)
+			require.NoError(t, err)
+
+			var gotSignedHex string
+			gotSignedHex, err = m.SignInputs(tt.xPriv)
+			if !tt.wantErr(t, err, fmt.Sprintf("SignInputs(%v)", tt.xPriv)) {
+				return
+			}
+
+			var tx *bt.Tx
+			tx, err = bt.NewTxFromString(gotSignedHex)
+
+			var ls *bscript.Script
+			if ls, err = bscript.NewFromHexString(
+				lockingScript,
+			); err != nil {
+				return
+			}
+			tx.Inputs[0].PreviousTxScript = ls
+			tx.Inputs[0].PreviousTxSatoshis = 12229
+
+			require.NoError(t, err)
+			assert.True(t, tx.Version > 0)
+			for _, input := range tx.Inputs {
+				var unlocker string
+				unlocker, err = input.UnlockingScript.ToASM()
+				require.NoError(t, err)
+				scriptParts := strings.Split(unlocker, " ")
+				pubKey := hex.EncodeToString(privateKey.PubKey().SerialiseCompressed())
+
+				var hash []byte
+				hash, err = tx.CalcInputSignatureHash(0, sighash.AllForkID)
+				require.NoError(t, err)
+
+				var hash32 [32]byte
+				copy(hash32[:], hash)
+				var verified bool
+				verified, err = bitcoin.VerifyMessageDER(hash32, pubKey, scriptParts[0])
+				require.NoError(t, err)
+				assert.True(t, verified)
+			}
+		})
+	}
+}
diff --git a/utils/scripts.go b/utils/scripts.go
index 7068fb47..d324cc4e 100644
--- a/utils/scripts.go
+++ b/utils/scripts.go
@@ -9,24 +9,25 @@ import (
 
 // GetUnlockingScript will generate an unlocking script
 func GetUnlockingScript(tx *bt.Tx, inputIndex uint32, privateKey *bec.PrivateKey) (*bscript.Script, error) {
-	shf := sighash.AllForkID
+	sigHashFlags := sighash.AllForkID
 
-	sh, err := tx.CalcInputSignatureHash(inputIndex, shf)
+	sigHash, err := tx.CalcInputSignatureHash(inputIndex, sigHashFlags)
 	if err != nil {
 		return nil, err
 	}
 
 	var sig *bec.Signature
-	if sig, err = privateKey.Sign(bt.ReverseBytes(sh)); err != nil {
+	if sig, err = privateKey.Sign(sigHash); err != nil {
 		return nil, err
 	}
 
-	var s *bscript.Script
-	if s, err = bscript.NewP2PKHUnlockingScript(
-		privateKey.PubKey().SerialiseCompressed(), sig.Serialise(), shf,
-	); err != nil {
+	pubKey := privateKey.PubKey().SerialiseCompressed()
+	signature := sig.Serialise()
+
+	var script *bscript.Script
+	if script, err = bscript.NewP2PKHUnlockingScript(pubKey, signature, sigHashFlags); err != nil {
 		return nil, err
 	}
 
-	return s, nil
+	return script, nil
 }