Skip to content

Commit

Permalink
add function to build rfb transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
tok-kkk committed Jan 10, 2024
1 parent 36f3ec0 commit c4cde11
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 0 deletions.
25 changes: 25 additions & 0 deletions btc/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
68 changes: 68 additions & 0 deletions btc/btc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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())
})
})
})
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand Down Expand Up @@ -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=
Expand Down

0 comments on commit c4cde11

Please sign in to comment.