From c4cde11deee1f0dca4d392b19c831790c3d66a21 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 10 Jan 2024 09:48:10 +0700
Subject: [PATCH 01/15] add function to build rfb transaction

---
 btc/btc.go      | 25 ++++++++++++++++++
 btc/btc_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++
 go.mod          |  2 ++
 go.sum          |  2 ++
 4 files changed, 97 insertions(+)

diff --git a/btc/btc.go b/btc/btc.go
index acf283b..0c7a18d 100644
--- a/btc/btc.go
+++ b/btc/btc.go
@@ -4,11 +4,13 @@ import (
 	"crypto/sha512"
 	"fmt"
 
+	"github.com/btcsuite/btcd/blockchain"
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/btcutil/hdkeychain"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/btcsuite/btcd/mempool"
 	"github.com/btcsuite/btcd/txscript"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/btcsuite/btcwallet/wallet/txsizes"
@@ -115,8 +117,20 @@ func BuildTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, u
 	totalIn, totalOut := int64(0), int64(0)
 	base, segwit := inputs.BaseSize, inputs.SegwitSize
 
+	// Calculate the minimum utxo value we want to add to the tx.
+	// This is to prevent adding a dust utxo and cause the tx use more fees.
+	minUtxoValue := 0
+	if sizeUpdater != nil {
+		minBase, minSegwit := sizeUpdater()
+		minVS := minBase + (minSegwit+3)/blockchain.WitnessScaleFactor
+		minUtxoValue = minVS * feeRate
+	}
 	// Adding required inputs
 	for _, utxo := range inputs.VIN {
+		// Skip the utxo if the amount is not large enough.
+		if utxo.Amount <= int64(minUtxoValue) {
+			continue
+		}
 		hash, err := chainhash.NewHashFromStr(utxo.TxID)
 		if err != nil {
 			return nil, err
@@ -218,3 +232,14 @@ func BuildTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, u
 
 	return nil, fmt.Errorf("funds not enough")
 }
+
+func BuildRbfTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, utxos []UTXO, recipients []Recipient, sizeUpdater func() (int, int), changeAddr btcutil.Address) (*wire.MsgTx, error) {
+	tx, err := BuildTransaction(feeRate, network, inputs, utxos, recipients, sizeUpdater, changeAddr)
+	if err != nil {
+		return nil, err
+	}
+	for i := range tx.TxIn {
+		tx.TxIn[i].Sequence = mempool.MaxRBFSequence
+	}
+	return tx, nil
+}
diff --git a/btc/btc_test.go b/btc/btc_test.go
index cb1841f..e27f8ee 100644
--- a/btc/btc_test.go
+++ b/btc/btc_test.go
@@ -2,10 +2,18 @@ package btc_test
 
 import (
 	"bytes"
+	"context"
+	"fmt"
 	"testing/quick"
+	"time"
 
 	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/btcsuite/btcd/chaincfg"
+	"github.com/btcsuite/btcd/txscript"
 	"github.com/catalogfi/blockchain/btc"
+	"github.com/catalogfi/blockchain/btc/btctest"
+	"github.com/catalogfi/blockchain/testutil"
+	"github.com/fatih/color"
 	"github.com/tyler-smith/go-bip39"
 
 	. "github.com/onsi/ginkgo/v2"
@@ -35,4 +43,64 @@ var _ = Describe("Bitcoin", func() {
 			Expect(quick.Check(test, nil)).NotTo(HaveOccurred())
 		})
 	})
+
+	Context("RBF", func() {
+		It("should be able to build transaction which support rbf", func(ctx context.Context) {
+			By("Initialization (Update these fields if testing on testnet/mainnet)")
+			network := &chaincfg.RegressionNetParams
+			privKey1, p2pkhAddr1, err := btctest.NewBtcKey(network)
+			Expect(err).To(BeNil())
+			_, p2pkhAddr2, err := btctest.NewBtcKey(network)
+			Expect(err).To(BeNil())
+			indexer := btctest.RegtestIndexer()
+
+			By("Funding the addresses")
+			txhash1, err := testutil.NigiriFaucet(p2pkhAddr1.EncodeAddress())
+			Expect(err).To(BeNil())
+			By(fmt.Sprintf("Funding address1 %v , txid = %v", p2pkhAddr1.EncodeAddress(), txhash1))
+			time.Sleep(5 * time.Second)
+
+			By("Construct a RBF tx which sends money from key1 to key2")
+			utxos, err := indexer.GetUTXOs(ctx, p2pkhAddr1)
+			Expect(err).To(BeNil())
+			amount, feeRate := int64(1e7), 4
+			recipients := []btc.Recipient{
+				{
+					To:     p2pkhAddr2.EncodeAddress(),
+					Amount: amount,
+				},
+			}
+			transaction, err := btc.BuildRbfTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, p2pkhAddr1)
+			Expect(err).To(BeNil())
+
+			By("Sign and submit the fund tx")
+			for i := range transaction.TxIn {
+				pkScript, err := txscript.PayToAddrScript(p2pkhAddr1)
+				Expect(err).To(BeNil())
+
+				sigScript, err := txscript.SignatureScript(transaction, i, pkScript, txscript.SigHashAll, privKey1, true)
+				Expect(err).To(BeNil())
+				transaction.TxIn[i].SignatureScript = sigScript
+			}
+			Expect(indexer.SubmitTx(ctx, transaction)).Should(Succeed())
+			By(color.GreenString("RBF tx hash = %v", transaction.TxHash().String()))
+
+			By("Construct a tx with higher fees")
+			transaction1, err := btc.BuildTransaction(feeRate+5, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, p2pkhAddr1)
+			Expect(err).To(BeNil())
+			transaction1.TxOut[1].Value++
+
+			By("Sign and submit replacement tx")
+			for i := range transaction1.TxIn {
+				pkScript, err := txscript.PayToAddrScript(p2pkhAddr1)
+				Expect(err).To(BeNil())
+
+				sigScript, err := txscript.SignatureScript(transaction1, i, pkScript, txscript.SigHashAll, privKey1, true)
+				Expect(err).To(BeNil())
+				transaction1.TxIn[i].SignatureScript = sigScript
+			}
+			Expect(indexer.SubmitTx(ctx, transaction1)).Should(Succeed())
+			color.Green("Replacement tx = %v", transaction1.TxHash().String())
+		})
+	})
 })
diff --git a/go.mod b/go.mod
index a954afe..90b3189 100644
--- a/go.mod
+++ b/go.mod
@@ -17,6 +17,7 @@ require (
 )
 
 require (
+	github.com/aead/siphash v1.0.1 // indirect
 	github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
 	github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
 	github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
@@ -27,6 +28,7 @@ require (
 	github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
+	github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect
 	github.com/kr/pretty v0.3.1 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.17 // indirect
diff --git a/go.sum b/go.sum
index 578e29f..1bb4ce3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,4 @@
+github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
 github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
 github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
@@ -74,6 +75,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
 github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
+github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE=
 github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=

From aa811abc3f4de6931d5b6697b30572e028e8b19d Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 10 Jan 2024 20:40:11 +0700
Subject: [PATCH 02/15] adding new address types

---
 btc/address.go | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 btc/address.go

diff --git a/btc/address.go b/btc/address.go
new file mode 100644
index 0000000..32180b7
--- /dev/null
+++ b/btc/address.go
@@ -0,0 +1,36 @@
+package btc
+
+import (
+	"fmt"
+
+	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/btcsuite/btcd/btcutil"
+	"github.com/btcsuite/btcd/chaincfg"
+)
+
+type AddressType string
+
+var (
+	AddressP2PK    AddressType = "P2PK"
+	AddressP2PKH   AddressType = "P2PKH"
+	AddressP2SH    AddressType = "P2SH"
+	AddressP2WPKH  AddressType = "P2WPKH"
+	AddressSegwit  AddressType = "Bech32"
+	AddressTaproot AddressType = "P2TR"
+)
+
+func PublicKeyAddress(pub btcec.PublicKey, network *chaincfg.Params, addressType AddressType) (btcutil.Address, error) {
+	switch addressType {
+	case AddressP2PK:
+		return btcutil.NewAddressPubKey(pub.SerializeCompressed(), network)
+	case AddressP2PKH:
+		return btcutil.NewAddressPubKeyHash(btcutil.Hash160(pub.SerializeCompressed()), network)
+	case AddressP2WPKH:
+		return btcutil.NewAddressWitnessScriptHash(btcutil.Hash160(pub.SerializeCompressed()), network)
+	case AddressTaproot:
+		// todo: add taproot address
+		panic("todo")
+	default:
+		return nil, fmt.Errorf("unsupported address type")
+	}
+}

From 07722f6ea9fc605e5bd95b9ee9fb1d3ac7410a04 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 10 Jan 2024 20:43:38 +0700
Subject: [PATCH 03/15] change to pointer

---
 btc/address.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/btc/address.go b/btc/address.go
index 32180b7..ee54a07 100644
--- a/btc/address.go
+++ b/btc/address.go
@@ -19,7 +19,7 @@ var (
 	AddressTaproot AddressType = "P2TR"
 )
 
-func PublicKeyAddress(pub btcec.PublicKey, network *chaincfg.Params, addressType AddressType) (btcutil.Address, error) {
+func PublicKeyAddress(pub *btcec.PublicKey, network *chaincfg.Params, addressType AddressType) (btcutil.Address, error) {
 	switch addressType {
 	case AddressP2PK:
 		return btcutil.NewAddressPubKey(pub.SerializeCompressed(), network)

From 6bc53f0da9e7086ba5c1bbfe5a96b511600ffcf8 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 10 Jan 2024 22:27:52 +0700
Subject: [PATCH 04/15] fix address parsing for p2wpkh

---
 btc/address.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/btc/address.go b/btc/address.go
index ee54a07..381b437 100644
--- a/btc/address.go
+++ b/btc/address.go
@@ -26,7 +26,7 @@ func PublicKeyAddress(pub *btcec.PublicKey, network *chaincfg.Params, addressTyp
 	case AddressP2PKH:
 		return btcutil.NewAddressPubKeyHash(btcutil.Hash160(pub.SerializeCompressed()), network)
 	case AddressP2WPKH:
-		return btcutil.NewAddressWitnessScriptHash(btcutil.Hash160(pub.SerializeCompressed()), network)
+		return btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(pub.SerializeCompressed()), network)
 	case AddressTaproot:
 		// todo: add taproot address
 		panic("todo")

From 9eff0d23b676732c55cb0aba87bb1c1e231dadc4 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Tue, 16 Jan 2024 16:21:07 +1100
Subject: [PATCH 05/15] update the indexer client to include tx output

---
 btc/fee_test.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++++
 btc/indexer.go  |   7 ++--
 2 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/btc/fee_test.go b/btc/fee_test.go
index 52b2e1f..f6a40a1 100644
--- a/btc/fee_test.go
+++ b/btc/fee_test.go
@@ -1,9 +1,12 @@
 package btc_test
 
 import (
+	"bytes"
 	"context"
 	"crypto/sha256"
+	"encoding/hex"
 	"fmt"
+	"log"
 	"math/rand"
 	"time"
 
@@ -17,6 +20,7 @@ import (
 	"github.com/catalogfi/blockchain/btc"
 	"github.com/catalogfi/blockchain/btc/btctest"
 	"github.com/catalogfi/blockchain/testutil"
+	"github.com/fatih/color"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -165,6 +169,104 @@ var _ = Describe("bitcoin fees", func() {
 		})
 	})
 
+	Context("Testing too long chain", func() {
+		XIt("should return an error", func(ctx context.Context) {
+			By("Initialization (Update these fields if testing on testnet/mainnet)")
+			network := &chaincfg.RegressionNetParams
+			privKey1, p2pkhAddr1, err := btctest.NewBtcKey(network)
+			Expect(err).To(BeNil())
+			privKey2, p2pkhAddr2, err := btctest.NewBtcKey(network)
+			Expect(err).To(BeNil())
+			indexer := btctest.RegtestIndexer()
+
+			By("Funding the addresses")
+			txhash1, err := testutil.NigiriFaucet(p2pkhAddr1.EncodeAddress())
+			Expect(err).To(BeNil())
+			By(fmt.Sprintf("Funding address1 %v , txid = %v", p2pkhAddr1.EncodeAddress(), txhash1))
+			By(fmt.Sprintf("address2 %v , txid = %v", p2pkhAddr2.EncodeAddress(), txhash1))
+			time.Sleep(5 * time.Second)
+
+			utxos, err := indexer.GetUTXOs(context.Background(), p2pkhAddr1)
+			Expect(err).To(BeNil())
+
+			var inputTx string
+			for i := 0; i < 25; i++ {
+				feeRate := 10
+				rawInputs := btc.RawInputs{
+					VIN:        utxos,
+					BaseSize:   txsizes.RedeemP2PKHSigScriptSize * len(utxos),
+					SegwitSize: 0,
+				}
+				var recipients []btc.Recipient
+				if i == 10 {
+					recipients = append(recipients, btc.Recipient{
+						To:     p2pkhAddr2.EncodeAddress(),
+						Amount: 1e6,
+					})
+				}
+
+				transaction, err := btc.BuildTransaction(feeRate, network, rawInputs, utxos, recipients, btc.P2pkhUpdater, p2pkhAddr1)
+				Expect(err).To(BeNil())
+
+				for i := range transaction.TxIn {
+					pkScript, err := txscript.PayToAddrScript(p2pkhAddr1)
+					Expect(err).To(BeNil())
+
+					sigScript, err := txscript.SignatureScript(transaction, i, pkScript, txscript.SigHashAll, privKey1, true)
+					Expect(err).To(BeNil())
+					transaction.TxIn[i].SignatureScript = sigScript
+				}
+				Expect(indexer.SubmitTx(ctx, transaction)).Should(Succeed())
+				By(fmt.Sprintf("txhash %v = %v", i+1, color.YellowString(transaction.TxHash().String())))
+
+				vout := uint32(0)
+				if i == 10 {
+					vout = uint32(1)
+					inputTx = transaction.TxHash().String()
+				}
+				utxos = []btc.UTXO{
+					{
+						TxID:   transaction.TxHash().String(),
+						Vout:   vout,
+						Amount: transaction.TxOut[vout].Value,
+					},
+				}
+			}
+
+			// Try contruct a new transaction
+			rawInputs := btc.RawInputs{
+				VIN: []btc.UTXO{
+					{
+						TxID:   inputTx,
+						Vout:   0,
+						Amount: 1e6,
+					},
+				},
+				BaseSize:   txsizes.RedeemP2PKHSigScriptSize,
+				SegwitSize: 0,
+			}
+
+			transaction, err := btc.BuildTransaction(10, network, rawInputs, nil, nil, nil, p2pkhAddr2)
+			Expect(err).To(BeNil())
+			for i := range transaction.TxIn {
+				pkScript, err := txscript.PayToAddrScript(p2pkhAddr2)
+				Expect(err).To(BeNil())
+
+				sigScript, err := txscript.SignatureScript(transaction, i, pkScript, txscript.SigHashAll, privKey2, true)
+				Expect(err).To(BeNil())
+				transaction.TxIn[i].SignatureScript = sigScript
+			}
+			buffer := bytes.NewBuffer([]byte{})
+			if err := transaction.Serialize(buffer); err != nil {
+				panic(err)
+			}
+			log.Print(hex.EncodeToString(buffer.Bytes()))
+			Expect(indexer.SubmitTx(ctx, transaction)).Should(Succeed())
+			By(fmt.Sprintf("txhash = %v", color.YellowString(transaction.TxHash().String())))
+
+		})
+	})
+
 	Context("estimate transaction fees", func() {
 		It("should return a proper estimate of tx size for a simple transfer", func() {
 			By("Initialization keys ")
diff --git a/btc/indexer.go b/btc/indexer.go
index f7a8305..5c91542 100644
--- a/btc/indexer.go
+++ b/btc/indexer.go
@@ -27,9 +27,10 @@ const (
 )
 
 type Transaction struct {
-	TxID   string `json:"txid"`
-	VINs   []VIN  `json:"vin"`
-	Status Status `json:"status"`
+	TxID   string    `json:"txid"`
+	VINs   []VIN     `json:"vin"`
+	VOUTs  []Prevout `json:"vout"`
+	Status Status    `json:"status"`
 }
 
 type VIN struct {

From 23087a9127bde484c1caa89db75ca2adc43e8adb Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 17 Jan 2024 14:57:45 +1100
Subject: [PATCH 06/15] add new rpc to client

---
 btc/btctest/client.go |  8 ++++++++
 btc/client.go         | 29 +++++++++++++++++++++++++++++
 btc/client_test.go    |  5 +++++
 3 files changed, 42 insertions(+)

diff --git a/btc/btctest/client.go b/btc/btctest/client.go
index 0605052..ac4909b 100644
--- a/btc/btctest/client.go
+++ b/btc/btctest/client.go
@@ -23,6 +23,7 @@ type MockClient struct {
 	FuncGetBlockByHeight  func(context.Context, int64) (*btcjson.GetBlockVerboseResult, error)
 	FuncGetBlockByHash    func(context.Context, *chainhash.Hash) (*btcjson.GetBlockVerboseResult, error)
 	FuncGetTxOut          func(context.Context, *chainhash.Hash, uint32) (*btcjson.GetTxOutResult, error)
+	FuncGetNetworkInfo    func(ctx context.Context) (*btcjson.GetNetworkInfoResult, error)
 }
 
 // Make sure the MockClient implements the Client interface
@@ -87,6 +88,13 @@ func (m *MockClient) GetTxOut(ctx context.Context, hash *chainhash.Hash, vout ui
 	return m.Client.GetTxOut(ctx, hash, vout)
 }
 
+func (m *MockClient) GetNetworkInfo(ctx context.Context) (*btcjson.GetNetworkInfoResult, error) {
+	if m.FuncGetNetworkInfo != nil {
+		return m.FuncGetNetworkInfo(ctx)
+	}
+	return m.Client.GetNetworkInfo(ctx)
+}
+
 // MockIndexerClient for testing purpose
 type MockIndexerClient struct {
 	Indexer               btc.IndexerClient
diff --git a/btc/client.go b/btc/client.go
index 73631cb..2f968f5 100644
--- a/btc/client.go
+++ b/btc/client.go
@@ -47,6 +47,9 @@ type Client interface {
 	// GetTxOut returns details about an unspent transaction output. It will return nil result if the utxo has been
 	// spent.
 	GetTxOut(ctx context.Context, hash *chainhash.Hash, vout uint32) (*btcjson.GetTxOutResult, error)
+
+	// GetNetworkInfo returns the network configuration of the node we connect to.
+	GetNetworkInfo(ctx context.Context) (*btcjson.GetNetworkInfoResult, error)
 }
 
 type client struct {
@@ -281,3 +284,29 @@ func (client *client) GetTxOut(ctx context.Context, hash *chainhash.Hash, vout u
 		return result, nil
 	}
 }
+
+func (client *client) GetNetworkInfo(ctx context.Context) (*btcjson.GetNetworkInfoResult, error) {
+	future := client.rpcClient.GetNetworkInfoAsync()
+	results := make(chan *btcjson.GetNetworkInfoResult, 1)
+	errs := make(chan error, 1)
+	go func() {
+		defer close(results)
+		defer close(errs)
+
+		result, err := future.Receive()
+		if err != nil {
+			errs <- err
+			return
+		}
+		results <- result
+	}()
+
+	select {
+	case <-ctx.Done():
+		return nil, fmt.Errorf("GetNetworkInfo : %w", ctx.Err())
+	case err := <-errs:
+		return nil, err
+	case result := <-results:
+		return result, nil
+	}
+}
diff --git a/btc/client_test.go b/btc/client_test.go
index c7f7afa..ad9ea9a 100644
--- a/btc/client_test.go
+++ b/btc/client_test.go
@@ -97,6 +97,11 @@ var _ = Describe("bitcoin client", func() {
 				scriptPubkey, err := txscript.PayToAddrScript(addr)
 				Expect(err).To(BeNil())
 				Expect(txout.ScriptPubKey.Hex).Should(Equal(hex.EncodeToString(scriptPubkey)))
+
+				By("GetNetworkInfo()")
+				netInfo, err := client.GetNetworkInfo(ctx)
+				Expect(err).To(BeNil())
+				Expect(netInfo.RelayFee).Should(BeNumerically(">=", 0))
 			})
 		})
 	})

From e7a6e5b26df85a00f0023f12b5d0abfc34929346 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 17 Jan 2024 15:25:02 +1100
Subject: [PATCH 07/15] add function get the transaciton total fee

---
 btc/fee.go | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/btc/fee.go b/btc/fee.go
index 5b487ee..a4af6aa 100644
--- a/btc/fee.go
+++ b/btc/fee.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/btcsuite/btcd/blockchain"
 	"github.com/btcsuite/btcd/chaincfg"
+	"github.com/btcsuite/btcd/txscript"
 	"github.com/btcsuite/btcd/wire"
 )
 
@@ -48,6 +49,19 @@ func TxVirtualSize(tx *wire.MsgTx) int {
 	return baseSize + (swSize+3)/blockchain.WitnessScaleFactor
 }
 
+// TotalFee returns the total amount fees used by the given tx.
+func TotalFee(tx *wire.MsgTx, fetcher txscript.PrevOutputFetcher) int64 {
+	fees := int64(0)
+	for _, in := range tx.TxIn {
+		output := fetcher.FetchPrevOutput(in.PreviousOutPoint)
+		fees += output.Value
+	}
+	for _, out := range tx.TxOut {
+		fees -= out.Value
+	}
+	return fees
+}
+
 type FeeSuggestion struct {
 	Minimum int `json:"minimumFee"`
 	Economy int `json:"economyFee"`

From b561f323d37c8ac366684e6dee76452ed002a029 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 17 Jan 2024 16:12:26 +1100
Subject: [PATCH 08/15] change the type from int64 to int

---
 btc/fee.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/btc/fee.go b/btc/fee.go
index a4af6aa..f6a06cf 100644
--- a/btc/fee.go
+++ b/btc/fee.go
@@ -50,7 +50,7 @@ func TxVirtualSize(tx *wire.MsgTx) int {
 }
 
 // TotalFee returns the total amount fees used by the given tx.
-func TotalFee(tx *wire.MsgTx, fetcher txscript.PrevOutputFetcher) int64 {
+func TotalFee(tx *wire.MsgTx, fetcher txscript.PrevOutputFetcher) int {
 	fees := int64(0)
 	for _, in := range tx.TxIn {
 		output := fetcher.FetchPrevOutput(in.PreviousOutPoint)
@@ -59,7 +59,7 @@ func TotalFee(tx *wire.MsgTx, fetcher txscript.PrevOutputFetcher) int64 {
 	for _, out := range tx.TxOut {
 		fees -= out.Value
 	}
-	return fees
+	return int(fees)
 }
 
 type FeeSuggestion struct {

From bd49ac68e3e2be05f4a0cc63923ac375532f363d Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 17 Jan 2024 18:33:58 +1100
Subject: [PATCH 09/15] adding test for rbf error

---
 btc/btc_test.go    | 73 ++++++++++++++++++++++++++++++++++++++++++++++
 btc/client_test.go |  2 +-
 2 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/btc/btc_test.go b/btc/btc_test.go
index e27f8ee..0898427 100644
--- a/btc/btc_test.go
+++ b/btc/btc_test.go
@@ -3,6 +3,7 @@ package btc_test
 import (
 	"bytes"
 	"context"
+	"errors"
 	"fmt"
 	"testing/quick"
 	"time"
@@ -102,5 +103,77 @@ var _ = Describe("Bitcoin", func() {
 			Expect(indexer.SubmitTx(ctx, transaction1)).Should(Succeed())
 			color.Green("Replacement tx = %v", transaction1.TxHash().String())
 		})
+
+		It("should get an error when trying to replace a mined tx", func() {
+			ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+			defer cancel()
+
+			By("Initialise a local regnet client")
+			network := &chaincfg.RegressionNetParams
+			client, err := btctest.RegtestClient()
+			Expect(err).To(BeNil())
+			indexer := btctest.RegtestIndexer()
+
+			By("New address")
+			privKey, pkAddr, err := btctest.NewBtcKey(network)
+			Expect(err).To(BeNil())
+
+			_, toAddr, err := btctest.NewBtcKey(network)
+			Expect(err).To(BeNil())
+
+			By("funding the addresses")
+			txhash, err := testutil.NigiriFaucet(pkAddr.EncodeAddress())
+			Expect(err).To(BeNil())
+			By(fmt.Sprintf("Funding address1 %v , txid = %v", pkAddr.EncodeAddress(), txhash))
+			time.Sleep(5 * time.Second)
+
+			By("Construct a new tx")
+			utxos, err := indexer.GetUTXOs(ctx, pkAddr)
+			Expect(err).To(BeNil())
+			amount, feeRate := int64(1e5), 5
+			recipients := []btc.Recipient{
+				{
+					To:     toAddr.EncodeAddress(),
+					Amount: amount,
+				},
+			}
+			transaction, err := btc.BuildRbfTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr)
+			Expect(err).To(BeNil())
+
+			By("Sign the transaction inputs")
+			for i := range transaction.TxIn {
+				pkScript, err := txscript.PayToAddrScript(pkAddr)
+				Expect(err).To(BeNil())
+
+				sigScript, err := txscript.SignatureScript(transaction, i, pkScript, txscript.SigHashAll, privKey, true)
+				Expect(err).To(BeNil())
+				transaction.TxIn[i].SignatureScript = sigScript
+			}
+
+			By("Submit the transaction")
+			Expect(client.SubmitTx(ctx, transaction)).Should(Succeed())
+			By(fmt.Sprintf("Funding tx hash = %v", color.YellowString(transaction.TxHash().String())))
+			time.Sleep(time.Second)
+
+			By("Build a new tx with higher fee")
+			feeRate += 2
+			transaction, err = btc.BuildRbfTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr)
+			Expect(err).To(BeNil())
+
+			By("Sign the transaction inputs")
+			for i := range transaction.TxIn {
+				pkScript, err := txscript.PayToAddrScript(pkAddr)
+				Expect(err).To(BeNil())
+
+				sigScript, err := txscript.SignatureScript(transaction, i, pkScript, txscript.SigHashAll, privKey, true)
+				Expect(err).To(BeNil())
+				transaction.TxIn[i].SignatureScript = sigScript
+				Expect(testutil.NigiriNewBlock()).Should(Succeed())
+			}
+
+			By("Submit the transaction again and it should be rejected")
+			err = client.SubmitTx(ctx, transaction)
+			Expect(errors.Is(err, btc.ErrTxInputsMissingOrSpent)).Should(BeTrue())
+		})
 	})
 })
diff --git a/btc/client_test.go b/btc/client_test.go
index ad9ea9a..5606d89 100644
--- a/btc/client_test.go
+++ b/btc/client_test.go
@@ -191,7 +191,7 @@ var _ = Describe("bitcoin client", func() {
 				Expect(err).To(BeNil())
 				transaction1.TxIn[i].SignatureScript = sigScript
 			}
-			By("Expect a `ErrAlreadyInChain` error if the tx is already in a block")
+			By("Expect a `ErrTxInputsMissingOrSpent` error if the tx is already in a block")
 			err = client.SubmitTx(ctx, transaction1)
 			Expect(errors.Is(err, btc.ErrTxInputsMissingOrSpent)).Should(BeTrue())
 		})

From b5293412435c5b06f3487f2efc9caecd8a0fd5cb Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Tue, 23 Jan 2024 23:38:20 +1100
Subject: [PATCH 10/15] update the tests

---
 btc/address.go      |   2 +-
 btc/btc.go          | 114 +++++++++++++++++++++++---------------------
 btc/btc_test.go     |  39 ++-------------
 btc/client_test.go  |  23 +++------
 btc/fee.go          |   9 ++--
 btc/fee_test.go     |  55 +++++++++++----------
 btc/indexer_test.go |   6 +--
 btc/scripts_test.go |  32 +++----------
 8 files changed, 115 insertions(+), 165 deletions(-)

diff --git a/btc/address.go b/btc/address.go
index 381b437..41ec219 100644
--- a/btc/address.go
+++ b/btc/address.go
@@ -28,7 +28,7 @@ func PublicKeyAddress(pub *btcec.PublicKey, network *chaincfg.Params, addressTyp
 	case AddressP2WPKH:
 		return btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(pub.SerializeCompressed()), network)
 	case AddressTaproot:
-		// todo: add taproot address
+		// todo: add taproot support
 		panic("todo")
 	default:
 		return nil, fmt.Errorf("unsupported address type")
diff --git a/btc/btc.go b/btc/btc.go
index 0c7a18d..409411e 100644
--- a/btc/btc.go
+++ b/btc/btc.go
@@ -2,6 +2,7 @@ package btc
 
 import (
 	"crypto/sha512"
+	"errors"
 	"fmt"
 
 	"github.com/btcsuite/btcd/blockchain"
@@ -30,6 +31,10 @@ const (
 	SigHashSingleAnyoneCanPay = txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
 )
 
+// SizeUpdater returns the base and segwit size of signing a particular type of utxo. This is used by the tx building
+// function to estimate the fee.
+type SizeUpdater func() (int, int)
+
 var (
 	P2pkhUpdater = func() (int, int) {
 		return txsizes.RedeemP2PKHSigScriptSize, 0
@@ -81,7 +86,7 @@ func GenerateSystemPrivKey(mnemonic string, userPubkeyHex []byte) (*btcec.Privat
 		masterKey, err = hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
 		if err != nil {
 			// Very small chance to happen
-			if err == hdkeychain.ErrUnusableSeed {
+			if errors.Is(err, hdkeychain.ErrUnusableSeed) {
 				continue
 			}
 			return nil, err
@@ -110,33 +115,28 @@ func NewRawInputs() RawInputs {
 // fees. `inputs` will be a list of utxos that required to be included in the transaction, it comes with the base and
 // segwit size of the signature for fee-estimation purpose. `utxos` is a list of transaction will be picked
 // to cover the output amount and fees. We assume the utxos all comes from a single address. The `sizeUpdater` function
-// returns the base and segwit size of each utxo from the utxos. If there's any change, it will be sent back to the
+// returns the base and segwit size of each utxo from the `utxos`. If there's any change, it will be sent back to the
 // `changeAddr`.
-func BuildTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, utxos []UTXO, recipients []Recipient, sizeUpdater func() (int, int), changeAddr btcutil.Address) (*wire.MsgTx, error) {
+func BuildTransaction(network *chaincfg.Params, feeRate int, inputs RawInputs, utxos []UTXO, sizeUpdater SizeUpdater, recipients []Recipient, changeAddr btcutil.Address) (*wire.MsgTx, error) {
 	tx := wire.NewMsgTx(DefaultBTCVersion)
 	totalIn, totalOut := int64(0), int64(0)
 	base, segwit := inputs.BaseSize, inputs.SegwitSize
 
-	// Calculate the minimum utxo value we want to add to the tx.
-	// This is to prevent adding a dust utxo and cause the tx use more fees.
-	minUtxoValue := 0
-	if sizeUpdater != nil {
-		minBase, minSegwit := sizeUpdater()
-		minVS := minBase + (minSegwit+3)/blockchain.WitnessScaleFactor
-		minUtxoValue = minVS * feeRate
-	}
-	// Adding required inputs
+	// Adding required inputs and output
 	for _, utxo := range inputs.VIN {
-		// Skip the utxo if the amount is not large enough.
-		if utxo.Amount <= int64(minUtxoValue) {
-			continue
-		}
+		// // Skip the utxo if the amount is not large enough.
+		// if utxo.Amount <= int64(minUtxoValue) {
+		// 	continue
+		// }
 		hash, err := chainhash.NewHashFromStr(utxo.TxID)
 		if err != nil {
 			return nil, err
 		}
 		txIn := wire.NewTxIn(wire.NewOutPoint(hash, utxo.Vout), nil, nil)
 		tx.AddTxIn(txIn)
+		if utxo.Amount == 0 {
+			return nil, fmt.Errorf("utxo amount is not set")
+		}
 		totalIn += utxo.Amount
 	}
 	for _, recipient := range recipients {
@@ -154,46 +154,41 @@ func BuildTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, u
 
 	// Function to check if the input amount is greater than or equal to the output amount plus fees
 	valueCheck := func() (bool, error) {
-		if totalIn > totalOut {
-			vs := EstimateVirtualSize(tx, base, segwit)
-			fees := int64(vs * feeRate)
-
-			// If the amount is enough to cover the outputs and fees
-			if totalIn > totalOut+fees {
-				// Add a change utxo to the output if the change amount is greater than the dust
-				if totalIn-totalOut-fees > DustAmount {
-					if changeAddr != nil {
-						changeScript, err := txscript.PayToAddrScript(changeAddr)
-						if err != nil {
-							return false, err
-						}
-						tx.AddTxOut(wire.NewTxOut(0, changeScript)) // adjust the amount later
-
-						// Estimate the fees again as we add a new output
-						vs := EstimateVirtualSize(tx, base, segwit)
-						fees := int64(vs * feeRate)
-
-						// Adjust the change utxo amount if it's still enough, delete it otherwise
-						if totalIn-totalOut-fees > DustAmount {
-							tx.TxOut[len(tx.TxOut)-1].Value = totalIn - totalOut - fees
-						} else {
-							tx.TxOut = tx.TxOut[:len(tx.TxOut)-1]
-						}
+		if totalIn <= totalOut {
+			return false, nil
+		}
+
+		vs := EstimateVirtualSize(tx, base, segwit)
+		fees := int64(vs * feeRate)
+
+		// If the amount is enough to cover the outputs and fees
+		if totalIn > totalOut+fees {
+			// Add a change utxo to the output if the change amount is greater than the dust
+			if totalIn-totalOut-fees > DustAmount {
+				if changeAddr != nil {
+					changeScript, err := txscript.PayToAddrScript(changeAddr)
+					if err != nil {
+						return false, err
+					}
+					tx.AddTxOut(wire.NewTxOut(0, changeScript)) // adjust the amount later
+
+					// Estimate the fees again as we add a new output
+					vs := EstimateVirtualSize(tx, base, segwit)
+					fees := int64(vs * feeRate)
+
+					// Adjust the change utxo amount if it's still enough, delete it otherwise
+					if totalIn-totalOut-fees > DustAmount {
+						tx.TxOut[len(tx.TxOut)-1].Value = totalIn - totalOut - fees
 					} else {
-						// Or adjust one of the output amount which is pending
-						for i, out := range tx.TxOut {
-							if out.Value == 0 {
-								tx.TxOut[i].Value = totalIn - totalOut - fees
-								break
-							}
-						}
+						tx.TxOut = tx.TxOut[:len(tx.TxOut)-1]
 					}
 				}
-				return true, nil
 			}
+
+			return true, nil
 		}
-		return false, nil
 
+		return false, nil
 	}
 
 	// Check if the existing inputs are enough and we might not need to add extra utxos
@@ -205,14 +200,25 @@ func BuildTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, u
 		return tx, nil
 	}
 
+	// Calculate the minimum utxo value we want to add to the tx.
+	// This is to prevent adding a dust utxo and cause the tx use more fees.
+	minUtxoValue := 0
+	if sizeUpdater != nil {
+		minBase, minSegwit := sizeUpdater()
+		minVS := minBase + (minSegwit+3)/blockchain.WitnessScaleFactor
+		minUtxoValue = minVS * feeRate
+	}
+
 	// Keep adding utxos until we have enough funds to cover the output amount
 	for _, utxo := range utxos {
 		hash, err := chainhash.NewHashFromStr(utxo.TxID)
 		if err != nil {
 			return nil, err
 		}
-		txIn := wire.NewTxIn(wire.NewOutPoint(hash, utxo.Vout), nil, nil)
-		tx.AddTxIn(txIn)
+		tx.AddTxIn(wire.NewTxIn(wire.NewOutPoint(hash, utxo.Vout), nil, nil))
+		if utxo.Amount <= int64(minUtxoValue) {
+			return nil, fmt.Errorf("utxo amount is not set")
+		}
 		totalIn += utxo.Amount
 		if sizeUpdater != nil {
 			additionalBaseSize, additionalSegwitSize := sizeUpdater()
@@ -233,8 +239,8 @@ func BuildTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, u
 	return nil, fmt.Errorf("funds not enough")
 }
 
-func BuildRbfTransaction(feeRate int, network *chaincfg.Params, inputs RawInputs, utxos []UTXO, recipients []Recipient, sizeUpdater func() (int, int), changeAddr btcutil.Address) (*wire.MsgTx, error) {
-	tx, err := BuildTransaction(feeRate, network, inputs, utxos, recipients, sizeUpdater, changeAddr)
+func BuildRbfTransaction(network *chaincfg.Params, feeRate int, inputs RawInputs, utxos []UTXO, sizeUpdater SizeUpdater, recipients []Recipient, changeAddr btcutil.Address) (*wire.MsgTx, error) {
+	tx, err := BuildTransaction(network, feeRate, inputs, utxos, sizeUpdater, recipients, changeAddr)
 	if err != nil {
 		return nil, err
 	}
diff --git a/btc/btc_test.go b/btc/btc_test.go
index 0898427..0a0bf87 100644
--- a/btc/btc_test.go
+++ b/btc/btc_test.go
@@ -1,50 +1,23 @@
 package btc_test
 
 import (
-	"bytes"
 	"context"
 	"errors"
 	"fmt"
-	"testing/quick"
 	"time"
 
-	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/txscript"
 	"github.com/catalogfi/blockchain/btc"
 	"github.com/catalogfi/blockchain/btc/btctest"
 	"github.com/catalogfi/blockchain/testutil"
 	"github.com/fatih/color"
-	"github.com/tyler-smith/go-bip39"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 )
 
 var _ = Describe("Bitcoin", func() {
-	Context("keys", func() {
-		It("should generate deterministic keys from mnemonic and user public key", func() {
-			test := func() bool {
-				entropy, err := bip39.NewEntropy(256)
-				Expect(err).To(BeNil())
-				mnemonic, err := bip39.NewMnemonic(entropy)
-				Expect(err).To(BeNil())
-				key, err := btcec.NewPrivateKey()
-				Expect(err).To(BeNil())
-
-				extendedKey1, err := btc.GenerateSystemPrivKey(mnemonic, key.PubKey().SerializeCompressed())
-				Expect(err).To(BeNil())
-				extendedKey2, err := btc.GenerateSystemPrivKey(mnemonic, key.PubKey().SerializeCompressed())
-				Expect(err).To(BeNil())
-
-				Expect(bytes.Equal(extendedKey1.Serialize(), extendedKey2.Serialize())).Should(BeTrue())
-				return true
-			}
-
-			Expect(quick.Check(test, nil)).NotTo(HaveOccurred())
-		})
-	})
-
 	Context("RBF", func() {
 		It("should be able to build transaction which support rbf", func(ctx context.Context) {
 			By("Initialization (Update these fields if testing on testnet/mainnet)")
@@ -61,7 +34,7 @@ var _ = Describe("Bitcoin", func() {
 			By(fmt.Sprintf("Funding address1 %v , txid = %v", p2pkhAddr1.EncodeAddress(), txhash1))
 			time.Sleep(5 * time.Second)
 
-			By("Construct a RBF tx which sends money from key1 to key2")
+			By("Construct a RBF tx which sends money from p2pkhAddr1 to p2pkhAddr2")
 			utxos, err := indexer.GetUTXOs(ctx, p2pkhAddr1)
 			Expect(err).To(BeNil())
 			amount, feeRate := int64(1e7), 4
@@ -71,7 +44,7 @@ var _ = Describe("Bitcoin", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildRbfTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, p2pkhAddr1)
+			transaction, err := btc.BuildRbfTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, p2pkhAddr1)
 			Expect(err).To(BeNil())
 
 			By("Sign and submit the fund tx")
@@ -87,7 +60,7 @@ var _ = Describe("Bitcoin", func() {
 			By(color.GreenString("RBF tx hash = %v", transaction.TxHash().String()))
 
 			By("Construct a tx with higher fees")
-			transaction1, err := btc.BuildTransaction(feeRate+5, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, p2pkhAddr1)
+			transaction1, err := btc.BuildTransaction(network, feeRate+5, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, p2pkhAddr1)
 			Expect(err).To(BeNil())
 			transaction1.TxOut[1].Value++
 
@@ -113,11 +86,9 @@ var _ = Describe("Bitcoin", func() {
 			client, err := btctest.RegtestClient()
 			Expect(err).To(BeNil())
 			indexer := btctest.RegtestIndexer()
-
 			By("New address")
 			privKey, pkAddr, err := btctest.NewBtcKey(network)
 			Expect(err).To(BeNil())
-
 			_, toAddr, err := btctest.NewBtcKey(network)
 			Expect(err).To(BeNil())
 
@@ -137,7 +108,7 @@ var _ = Describe("Bitcoin", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildRbfTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr)
+			transaction, err := btc.BuildRbfTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, pkAddr)
 			Expect(err).To(BeNil())
 
 			By("Sign the transaction inputs")
@@ -157,7 +128,7 @@ var _ = Describe("Bitcoin", func() {
 
 			By("Build a new tx with higher fee")
 			feeRate += 2
-			transaction, err = btc.BuildRbfTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr)
+			transaction, err = btc.BuildRbfTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, pkAddr)
 			Expect(err).To(BeNil())
 
 			By("Sign the transaction inputs")
diff --git a/btc/client_test.go b/btc/client_test.go
index 5606d89..f8a9e4d 100644
--- a/btc/client_test.go
+++ b/btc/client_test.go
@@ -8,8 +8,6 @@ import (
 	"reflect"
 	"time"
 
-	"github.com/btcsuite/btcd/btcec/v2"
-	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/rpcclient"
@@ -118,10 +116,7 @@ var _ = Describe("bitcoin client", func() {
 			indexer := btctest.RegtestIndexer()
 
 			By("New address")
-			privKey, err := btcec.NewPrivateKey()
-			Expect(err).To(BeNil())
-			pubKey := privKey.PubKey()
-			pkAddr, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(pubKey.SerializeCompressed()), network)
+			privKey, pkAddr, err := btctest.NewBtcKey(network)
 			Expect(err).To(BeNil())
 
 			By("funding the addresses")
@@ -140,7 +135,7 @@ var _ = Describe("bitcoin client", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr)
+			transaction, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, pkAddr)
 			Expect(err).To(BeNil())
 
 			By("Sign the transaction inputs")
@@ -181,7 +176,7 @@ var _ = Describe("bitcoin client", func() {
 				},
 			}
 
-			transaction1, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients1, btc.P2pkhUpdater, pkAddr)
+			transaction1, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients1, pkAddr)
 			Expect(err).To(BeNil())
 			for i := range transaction1.TxIn {
 				pkScript, err := txscript.PayToAddrScript(pkAddr)
@@ -202,15 +197,9 @@ var _ = Describe("bitcoin client", func() {
 
 			By("Initialization keys ")
 			network := &chaincfg.RegressionNetParams
-			privKey1, err := btcec.NewPrivateKey()
-			Expect(err).To(BeNil())
-			pubKey1 := privKey1.PubKey()
-			pkAddr1, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(pubKey1.SerializeCompressed()), network)
-			Expect(err).To(BeNil())
-			privKey2, err := btcec.NewPrivateKey()
+			privKey1, pkAddr1, err := btctest.NewBtcKey(network)
 			Expect(err).To(BeNil())
-			pubKey2 := privKey2.PubKey()
-			pkAddr2, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(pubKey2.SerializeCompressed()), network)
+			_, pkAddr2, err := btctest.NewBtcKey(network)
 			Expect(err).To(BeNil())
 			client, err := btctest.RegtestClient()
 			Expect(err).To(BeNil())
@@ -232,7 +221,7 @@ var _ = Describe("bitcoin client", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr1)
+			transaction, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, pkAddr1)
 			Expect(err).To(BeNil())
 
 			By("Sign and submit the fund tx")
diff --git a/btc/fee.go b/btc/fee.go
index f6a06cf..a228531 100644
--- a/btc/fee.go
+++ b/btc/fee.go
@@ -20,14 +20,17 @@ const (
 )
 
 var (
+	// RedeemHtlcRefundSigScriptSize is an estimate of the sigScript size when refunding an htlc script
 	// stack number + stack size * 4 + signature + public key + script size
 	RedeemHtlcRefundSigScriptSize = 1 + 4 + 73 + 33 + 89
 
+	// RedeemHtlcRedeemSigScriptSize is an estimate of the sigScript size when redeeming an htlc script
 	// stack number + stack size * 5 + signature + public key + secret + script size
 	RedeemHtlcRedeemSigScriptSize = func(secretSize int) int {
 		return 1 + 5 + 73 + 33 + secretSize + +1 + 89
 	}
 
+	// RedeemMultisigSigScriptSize is an estimate of the sigScript size from an 2-of-2 multisig script
 	// stack number + stack size * 4 + signature * 2 + script size
 	RedeemMultisigSigScriptSize = 1 + 4 + 73*2 + 71
 )
@@ -41,7 +44,7 @@ func EstimateVirtualSize(tx *wire.MsgTx, extraBaseSize, extraSegwitSize int) int
 	return baseSize + (swSize+3)/blockchain.WitnessScaleFactor
 }
 
-// TxVirtualSize returns the virtual size of a transaction
+// TxVirtualSize returns the virtual size of a transaction.
 func TxVirtualSize(tx *wire.MsgTx) int {
 	size := tx.SerializeSize()
 	baseSize := tx.SerializeSizeStripped()
@@ -116,7 +119,7 @@ func (f *mempoolFeeEstimator) FeeSuggestion() (FeeSuggestion, error) {
 		return f.last, nil
 	} else {
 		return FeeSuggestion{
-			2, 2, 2, 2, 2,
+			1, 1, 1, 1, 1,
 		}, nil
 	}
 }
@@ -174,7 +177,7 @@ func (f *blockstreamFeeEstimator) FeeSuggestion() (FeeSuggestion, error) {
 		}
 		return f.last, nil
 	} else {
-		return FeeSuggestion{2, 2, 2, 2, 2}, nil
+		return FeeSuggestion{1, 1, 1, 1, 1}, nil
 	}
 }
 
diff --git a/btc/fee_test.go b/btc/fee_test.go
index f6a40a1..8a578d0 100644
--- a/btc/fee_test.go
+++ b/btc/fee_test.go
@@ -73,21 +73,21 @@ var _ = Describe("bitcoin fees", func() {
 				estimatorTestnet := btc.NewMempoolFeeEstimator(&chaincfg.TestNet3Params, "", 15*time.Second)
 				fees, err := estimatorTestnet.FeeSuggestion()
 				Expect(err).Should(BeNil())
-				Expect(fees.Minimum).Should(Equal(2))
-				Expect(fees.Economy).Should(Equal(2))
-				Expect(fees.Low).Should(Equal(2))
-				Expect(fees.Medium).Should(Equal(2))
-				Expect(fees.High).Should(Equal(2))
+				Expect(fees.Minimum).Should(Equal(1))
+				Expect(fees.Economy).Should(Equal(1))
+				Expect(fees.Low).Should(Equal(1))
+				Expect(fees.Medium).Should(Equal(1))
+				Expect(fees.High).Should(Equal(1))
 
 				By("Regnet")
 				estimatorRegnet := btc.NewMempoolFeeEstimator(&chaincfg.RegressionNetParams, "", 15*time.Second)
 				fees, err = estimatorRegnet.FeeSuggestion()
 				Expect(err).Should(BeNil())
-				Expect(fees.Minimum).Should(Equal(2))
-				Expect(fees.Economy).Should(Equal(2))
-				Expect(fees.Low).Should(Equal(2))
-				Expect(fees.Medium).Should(Equal(2))
-				Expect(fees.High).Should(Equal(2))
+				Expect(fees.Minimum).Should(Equal(1))
+				Expect(fees.Economy).Should(Equal(1))
+				Expect(fees.Low).Should(Equal(1))
+				Expect(fees.Medium).Should(Equal(1))
+				Expect(fees.High).Should(Equal(1))
 			})
 		})
 
@@ -136,21 +136,21 @@ var _ = Describe("bitcoin fees", func() {
 				estimatorTestnet := btc.NewBlockstreamFeeEstimator(&chaincfg.TestNet3Params, "", 15*time.Second)
 				fees, err := estimatorTestnet.FeeSuggestion()
 				Expect(err).Should(BeNil())
-				Expect(fees.Minimum).Should(Equal(2))
-				Expect(fees.Economy).Should(Equal(2))
-				Expect(fees.Low).Should(Equal(2))
-				Expect(fees.Medium).Should(Equal(2))
-				Expect(fees.High).Should(Equal(2))
+				Expect(fees.Minimum).Should(Equal(1))
+				Expect(fees.Economy).Should(Equal(1))
+				Expect(fees.Low).Should(Equal(1))
+				Expect(fees.Medium).Should(Equal(1))
+				Expect(fees.High).Should(Equal(1))
 
 				By("Regnet")
 				estimatorRegnet := btc.NewBlockstreamFeeEstimator(&chaincfg.RegressionNetParams, "", 15*time.Second)
 				fees, err = estimatorRegnet.FeeSuggestion()
 				Expect(err).Should(BeNil())
-				Expect(fees.Minimum).Should(Equal(2))
-				Expect(fees.Economy).Should(Equal(2))
-				Expect(fees.Low).Should(Equal(2))
-				Expect(fees.Medium).Should(Equal(2))
-				Expect(fees.High).Should(Equal(2))
+				Expect(fees.Minimum).Should(Equal(1))
+				Expect(fees.Economy).Should(Equal(1))
+				Expect(fees.Low).Should(Equal(1))
+				Expect(fees.Medium).Should(Equal(1))
+				Expect(fees.High).Should(Equal(1))
 			})
 		})
 
@@ -205,7 +205,7 @@ var _ = Describe("bitcoin fees", func() {
 					})
 				}
 
-				transaction, err := btc.BuildTransaction(feeRate, network, rawInputs, utxos, recipients, btc.P2pkhUpdater, p2pkhAddr1)
+				transaction, err := btc.BuildTransaction(network, feeRate, rawInputs, utxos, btc.P2pkhUpdater, recipients, p2pkhAddr1)
 				Expect(err).To(BeNil())
 
 				for i := range transaction.TxIn {
@@ -233,7 +233,7 @@ var _ = Describe("bitcoin fees", func() {
 				}
 			}
 
-			// Try contruct a new transaction
+			// Try construct a new transaction
 			rawInputs := btc.RawInputs{
 				VIN: []btc.UTXO{
 					{
@@ -246,7 +246,7 @@ var _ = Describe("bitcoin fees", func() {
 				SegwitSize: 0,
 			}
 
-			transaction, err := btc.BuildTransaction(10, network, rawInputs, nil, nil, nil, p2pkhAddr2)
+			transaction, err := btc.BuildTransaction(network, 10, rawInputs, nil, nil, nil, p2pkhAddr2)
 			Expect(err).To(BeNil())
 			for i := range transaction.TxIn {
 				pkScript, err := txscript.PayToAddrScript(p2pkhAddr2)
@@ -263,7 +263,6 @@ var _ = Describe("bitcoin fees", func() {
 			log.Print(hex.EncodeToString(buffer.Bytes()))
 			Expect(indexer.SubmitTx(ctx, transaction)).Should(Succeed())
 			By(fmt.Sprintf("txhash = %v", color.YellowString(transaction.TxHash().String())))
-
 		})
 	})
 
@@ -299,7 +298,7 @@ var _ = Describe("bitcoin fees", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr1)
+			transaction, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, pkAddr1)
 			Expect(err).To(BeNil())
 
 			By("Estimate the tx size before signing")
@@ -364,7 +363,7 @@ var _ = Describe("bitcoin fees", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.MultisigUpdater, pkAddr1)
+			transaction, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.MultisigUpdater, recipients, pkAddr1)
 			Expect(err).To(BeNil())
 
 			By("Estimate the tx size before signing")
@@ -437,7 +436,7 @@ var _ = Describe("bitcoin fees", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.HtlcUpdater(len(secret)), pkAddr1)
+			transaction, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.HtlcUpdater(len(secret)), recipients, pkAddr1)
 			Expect(err).To(BeNil())
 
 			By("Estimate the tx size before signing")
@@ -505,7 +504,7 @@ var _ = Describe("bitcoin fees", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.HtlcUpdater(0), pkAddr1)
+			transaction, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.HtlcUpdater(0), recipients, pkAddr1)
 			Expect(err).To(BeNil())
 
 			By("Estimate the tx size before signing")
diff --git a/btc/indexer_test.go b/btc/indexer_test.go
index e5ce046..5a8ee53 100644
--- a/btc/indexer_test.go
+++ b/btc/indexer_test.go
@@ -64,7 +64,7 @@ var _ = Describe("Indexer client", func() {
 					Amount: amount,
 				},
 			}
-			rawTx, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, addr)
+			rawTx, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, addr)
 			Expect(err).To(BeNil())
 			for i := range rawTx.TxIn {
 				pkScript, err := txscript.PayToAddrScript(addr)
@@ -122,7 +122,7 @@ var _ = Describe("Indexer client", func() {
 					Amount: amount,
 				},
 			}
-			transaction, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients, btc.P2pkhUpdater, pkAddr)
+			transaction, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, pkAddr)
 			Expect(err).To(BeNil())
 			for i := range transaction.TxIn {
 				pkScript, err := txscript.PayToAddrScript(pkAddr)
@@ -151,7 +151,7 @@ var _ = Describe("Indexer client", func() {
 					Amount: 2 * amount,
 				},
 			}
-			transaction1, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, recipients1, btc.P2pkhUpdater, pkAddr)
+			transaction1, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients1, pkAddr)
 			Expect(err).To(BeNil())
 			for i := range transaction1.TxIn {
 				pkScript, err := txscript.PayToAddrScript(pkAddr)
diff --git a/btc/scripts_test.go b/btc/scripts_test.go
index 7ab79a2..7676f55 100644
--- a/btc/scripts_test.go
+++ b/btc/scripts_test.go
@@ -62,7 +62,7 @@ var _ = Describe("Bitcoin scripts", func() {
 					Amount: amount,
 				},
 			}
-			fundingTx, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, fundingRecipients, btc.P2pkhUpdater, p2pkhAddr1)
+			fundingTx, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, fundingRecipients, p2pkhAddr1)
 			Expect(err).To(BeNil())
 
 			By("Sign and submit the funding tx")
@@ -87,18 +87,12 @@ var _ = Describe("Bitcoin scripts", func() {
 					Amount: amount,
 				},
 			}
-			redeemRecipients := []btc.Recipient{
-				{
-					To:     p2pkhAddr2.EncodeAddress(),
-					Amount: 0,
-				},
-			}
 			rawInputs := btc.RawInputs{
 				VIN:        redeemInput,
 				BaseSize:   0,
 				SegwitSize: btc.RedeemMultisigSigScriptSize,
 			}
-			redeemTx, err := btc.BuildTransaction(feeRate, network, rawInputs, nil, redeemRecipients, nil, nil)
+			redeemTx, err := btc.BuildTransaction(network, feeRate, rawInputs, nil, nil, nil, p2pkhAddr2)
 			Expect(err).To(BeNil())
 
 			By("Sign and submit the redeem tx")
@@ -162,7 +156,7 @@ var _ = Describe("Bitcoin scripts", func() {
 					Amount: amount,
 				},
 			}
-			fundingTx, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, fundingRecipients, btc.P2pkhUpdater, p2pkhAddr1)
+			fundingTx, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, fundingRecipients, p2pkhAddr1)
 			Expect(err).To(BeNil())
 
 			By("Sign and submit the funding tx")
@@ -237,18 +231,12 @@ var _ = Describe("Bitcoin scripts", func() {
 					Amount: amount,
 				},
 			}
-			htlcSpendRecipients := []btc.Recipient{
-				{
-					To:     p2pkhAddr2.EncodeAddress(),
-					Amount: 0,
-				},
-			}
 			rawInputs := btc.RawInputs{
 				VIN:        htlcSpendInputs,
 				BaseSize:   0,
 				SegwitSize: btc.RedeemHtlcRedeemSigScriptSize(len(secret)),
 			}
-			htlcSpendTx, err := btc.BuildTransaction(feeRate, network, rawInputs, nil, htlcSpendRecipients, nil, nil)
+			htlcSpendTx, err := btc.BuildTransaction(network, feeRate, rawInputs, nil, nil, nil, p2pkhAddr2)
 			Expect(err).To(BeNil())
 
 			By("Sign and submit the tx")
@@ -302,7 +290,7 @@ var _ = Describe("Bitcoin scripts", func() {
 					Amount: amount,
 				},
 			}
-			fundingTx, err := btc.BuildTransaction(feeRate, network, btc.NewRawInputs(), utxos, fundingRecipients, btc.P2pkhUpdater, p2pkhAddr1)
+			fundingTx, err := btc.BuildTransaction(network, feeRate, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, fundingRecipients, p2pkhAddr1)
 			Expect(err).To(BeNil())
 
 			By("Sign and submit the fund tx")
@@ -383,18 +371,12 @@ var _ = Describe("Bitcoin scripts", func() {
 					Amount: amount,
 				},
 			}
-			htlcSpendRecipients := []btc.Recipient{
-				{
-					To:     p2pkhAddr2.EncodeAddress(),
-					Amount: 0,
-				},
-			}
 			rawInputs := btc.RawInputs{
 				VIN:        htlcSpendInput,
 				BaseSize:   0,
 				SegwitSize: btc.RedeemHtlcRefundSigScriptSize,
 			}
-			htlcSpendTx, err := btc.BuildTransaction(feeRate, network, rawInputs, nil, htlcSpendRecipients, nil, nil)
+			htlcSpendTx, err := btc.BuildTransaction(network, feeRate, rawInputs, nil, nil, nil, p2pkhAddr2)
 			Expect(err).To(BeNil())
 
 			By("Sign the htlc spend tx")
@@ -419,8 +401,8 @@ var _ = Describe("Bitcoin scripts", func() {
 			err = client.SubmitTx(ctx, htlcSpendTx)
 			Expect(err).To(BeNil())
 			By(fmt.Sprintf("Htlc SpendTx tx hash = %v", color.YellowString(htlcSpendTx.TxHash().String())))
+			Expect(testutil.NigiriNewBlock()).Should(Succeed())
 		})
-
 	})
 
 	Context("IsHtlc function", func() {

From 5d42bff4f7b7de49e8fe5df5ea35e57a887b9779 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 24 Jan 2024 12:10:23 +1100
Subject: [PATCH 11/15] add values field for output

---
 btc/indexer.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/btc/indexer.go b/btc/indexer.go
index 5c91542..84d39f8 100644
--- a/btc/indexer.go
+++ b/btc/indexer.go
@@ -45,6 +45,7 @@ type Prevout struct {
 	ScriptPubKeyType    string `json:"scriptpubkey_type"`
 	ScriptPubKey        string `json:"scriptpubkey"`
 	ScriptPubKeyAddress string `json:"scriptpubkey_address"`
+	Value               int    `json:"value"`
 }
 
 type Status struct {

From 113daa33422cbeae8faa6032e2e627da0cf27168 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 24 Jan 2024 20:38:42 +1100
Subject: [PATCH 12/15] update the indexer client to support lastSeenTxid

---
 btc/indexer.go | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/btc/indexer.go b/btc/indexer.go
index 84d39f8..c0980d1 100644
--- a/btc/indexer.go
+++ b/btc/indexer.go
@@ -58,7 +58,7 @@ type Status struct {
 type IndexerClient interface {
 
 	// GetAddressTxs returns the tx history of the given address.
-	GetAddressTxs(ctx context.Context, address btcutil.Address) ([]Transaction, error)
+	GetAddressTxs(ctx context.Context, address btcutil.Address, lastSeenTxid string) ([]Transaction, error)
 
 	// GetUTXOs return all utxos of the given address.
 	GetUTXOs(ctx context.Context, address btcutil.Address) (UTXOs, error)
@@ -90,11 +90,17 @@ func NewElectrsIndexerClient(logger *zap.Logger, url string, retryInterval time.
 	}
 }
 
-func (client *electrsIndexerClient) GetAddressTxs(ctx context.Context, address btcutil.Address) ([]Transaction, error) {
+func (client *electrsIndexerClient) GetAddressTxs(ctx context.Context, address btcutil.Address, lastSeenTxid string) ([]Transaction, error) {
 	endpoint, err := url.JoinPath(client.url, "address", address.EncodeAddress(), "txs")
 	if err != nil {
 		return nil, err
 	}
+	if lastSeenTxid != "" {
+		endpoint, err = url.JoinPath(client.url, "address", address.EncodeAddress(), "txs", "chain", lastSeenTxid)
+		if err != nil {
+			return nil, err
+		}
+	}
 
 	// Send the request
 	txs := []Transaction{}

From 096bd5d761677e143a801cdce85423f704e4196a Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Wed, 24 Jan 2024 20:58:31 +1100
Subject: [PATCH 13/15] fix comiplation issue

---
 btc/btctest/client.go | 8 ++++----
 btc/indexer_test.go   | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/btc/btctest/client.go b/btc/btctest/client.go
index ac4909b..f708dbc 100644
--- a/btc/btctest/client.go
+++ b/btc/btctest/client.go
@@ -98,7 +98,7 @@ func (m *MockClient) GetNetworkInfo(ctx context.Context) (*btcjson.GetNetworkInf
 // MockIndexerClient for testing purpose
 type MockIndexerClient struct {
 	Indexer               btc.IndexerClient
-	FuncGetAddressTxs     func(context.Context, btcutil.Address) ([]btc.Transaction, error)
+	FuncGetAddressTxs     func(context.Context, btcutil.Address, string) ([]btc.Transaction, error)
 	FuncGetUTXOs          func(context.Context, btcutil.Address) (btc.UTXOs, error)
 	FuncGetTipBlockHeight func(ctx context.Context) (uint64, error)
 	FuncGetTx             func(context.Context, string) (btc.Transaction, error)
@@ -119,11 +119,11 @@ func NewMockIndexerClientWrapper(indexer btc.IndexerClient) *MockIndexerClient {
 	}
 }
 
-func (client MockIndexerClient) GetAddressTxs(ctx context.Context, address btcutil.Address) ([]btc.Transaction, error) {
+func (client MockIndexerClient) GetAddressTxs(ctx context.Context, address btcutil.Address, lastSeenTxid string) ([]btc.Transaction, error) {
 	if client.FuncGetAddressTxs != nil {
-		return client.FuncGetAddressTxs(ctx, address)
+		return client.FuncGetAddressTxs(ctx, address, lastSeenTxid)
 	}
-	return client.Indexer.GetAddressTxs(ctx, address)
+	return client.Indexer.GetAddressTxs(ctx, address, lastSeenTxid)
 }
 
 func (client MockIndexerClient) GetUTXOs(ctx context.Context, address btcutil.Address) (btc.UTXOs, error) {
diff --git a/btc/indexer_test.go b/btc/indexer_test.go
index 5a8ee53..ccd7318 100644
--- a/btc/indexer_test.go
+++ b/btc/indexer_test.go
@@ -81,7 +81,7 @@ var _ = Describe("Indexer client", func() {
 			Expect(err.Error()).Should(ContainSubstring("not enough data"))
 
 			By("GetAddressTxs()")
-			txs, err := client.GetAddressTxs(context.Background(), addr)
+			txs, err := client.GetAddressTxs(context.Background(), addr, "")
 			Expect(err).To(BeNil())
 			has := false
 			for _, tx := range txs {

From 29e67939e09301389c6d30af914b0e578297706e Mon Sep 17 00:00:00 2001
From: revantark <revantark@gmail.com>
Date: Mon, 29 Jan 2024 17:00:07 +0530
Subject: [PATCH 14/15] add block_hash to status of tx

---
 btc/indexer.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/btc/indexer.go b/btc/indexer.go
index c0980d1..e0c2c99 100644
--- a/btc/indexer.go
+++ b/btc/indexer.go
@@ -51,6 +51,7 @@ type Prevout struct {
 type Status struct {
 	Confirmed   bool   `json:"confirmed"`
 	BlockHeight uint64 `json:"block_height"`
+	BlockHash   string `json:"block_hash"`
 }
 
 // IndexerClient provides some rpc functions which usually cannot be achieved by the standard bitcoin json-rpc methods.

From d66f17b012216c11f1281196b96740609c469fc8 Mon Sep 17 00:00:00 2001
From: tok-kkk <yunshi@catalog.fi>
Date: Tue, 30 Jan 2024 10:07:18 +1100
Subject: [PATCH 15/15] add address_test file

---
 btc/address_test.go | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 btc/address_test.go

diff --git a/btc/address_test.go b/btc/address_test.go
new file mode 100644
index 0000000..37b5b35
--- /dev/null
+++ b/btc/address_test.go
@@ -0,0 +1 @@
+package btc_test