diff --git a/testutil/btcaccount/btcaccount_test.go b/testutil/btcaccount/btcaccount_test.go index db0eaa9..78e9c4e 100644 --- a/testutil/btcaccount/btcaccount_test.go +++ b/testutil/btcaccount/btcaccount_test.go @@ -81,6 +81,63 @@ var _ = Describe("btc account", func() { fmt.Println("txHash: ", txHash[:]) }) + It("should be able to transfer funds to itself (legacy address)", func() { + // Get the account with actual balance + client := btcclient.NewClient(logger, btctypes.BchLocalnet) + wallet, err := testutil.LoadHdWalletFromEnv("BCH_TEST_MNEMONIC", "BCH_TEST_PASSPHRASE", client.Network()) + Expect(err).NotTo(HaveOccurred()) + key, err := wallet.EcdsaKey(44, 1, 0, 0, 2) + Expect(err).NotTo(HaveOccurred()) + account, err := NewAccount(client, key) + Expect(err).NotTo(HaveOccurred()) + fmt.Println("from address: ", account.Address().EncodeAddress()) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + utxos, err := account.UTXOs(ctx) + Expect(err).NotTo(HaveOccurred()) + Expect(len(utxos)).Should(BeNumerically(">", 0)) + + // Build the transaction + toAddress, err := btctypes.AddressFromPubKey(key.PublicKey, btctypes.BtcLocalnet) + Expect(err).NotTo(HaveOccurred()) + amount := 50000 * btctypes.SAT + fmt.Println("to address: ", toAddress.EncodeAddress()) + + txHash, err := account.Transfer(ctx, toAddress, amount, types.Standard, true) + Expect(err).NotTo(HaveOccurred()) + fmt.Println("txHash: ", txHash[:]) + }) + + It("should be able to transfer funds to itself (cash address)", func() { + // Get the account with actual balance + client := btcclient.NewClient(logger, btctypes.BchLocalnet) + wallet, err := testutil.LoadHdWalletFromEnv("BCH_TEST_MNEMONIC", "BCH_TEST_PASSPHRASE", client.Network()) + Expect(err).NotTo(HaveOccurred()) + key, err := wallet.EcdsaKey(44, 1, 0, 0, 2) + Expect(err).NotTo(HaveOccurred()) + account, err := NewAccount(client, key) + Expect(err).NotTo(HaveOccurred()) + fmt.Println("from address: ", account.Address().EncodeAddress()) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + utxos, err := account.UTXOs(ctx) + Expect(err).NotTo(HaveOccurred()) + Expect(len(utxos)).Should(BeNumerically(">", 0)) + + // Build the transaction + toAddress := account.Address() + amount := 50000 * btctypes.SAT + fmt.Println("to address: ", toAddress.EncodeAddress()) + + txHash, err := account.Transfer(ctx, toAddress, amount, types.Standard, true) + Expect(err).NotTo(HaveOccurred()) + fmt.Println("txHash: ", txHash[:]) + }) + It("should be able to transfer funds to itself", func() { // Get the account with actual balance client := btcclient.NewClient(logger, btctypes.ZecLocalnet) diff --git a/types/btctypes/bch/bch.go b/types/btctypes/bch/bch.go index 608623d..5a02e3f 100644 --- a/types/btctypes/bch/bch.go +++ b/types/btctypes/bch/bch.go @@ -167,6 +167,18 @@ func AppendChecksum(prefix string, payload []byte) []byte { } func DecodeAddress(addr string, params *chaincfg.Params) (btcutil.Address, error) { + // Legacy address decoding + if address, err := btcutil.DecodeAddress(addr, params); err == nil { + switch address.(type) { + case *btcutil.AddressPubKeyHash, *btcutil.AddressScriptHash, *btcutil.AddressPubKey: + return address, nil + case *btcutil.AddressWitnessPubKeyHash, *btcutil.AddressWitnessScriptHash: + return nil, fmt.Errorf("bitcoin cash does not support SegWit addresses") + default: + return nil, fmt.Errorf("unsuported legacy bitcoin address type: %T", address) + } + } + if addrParts := strings.Split(addr, ":"); len(addrParts) != 1 { addr = addrParts[1] } @@ -210,6 +222,8 @@ func PayToAddrScript(addr btcutil.Address) ([]byte, error) { case *P2SHAddress: return txscript.NewScriptBuilder().AddOp(txscript.OP_HASH160).AddData(addr.scriptHash). AddOp(txscript.OP_EQUAL).Script() + case *btcutil.AddressPubKeyHash, *btcutil.AddressScriptHash, *btcutil.AddressPubKey: + return txscript.PayToAddrScript(addr) default: return nil, fmt.Errorf("unsupported address type %T", addr) } diff --git a/types/btctypes/btctypes_test.go b/types/btctypes/btctypes_test.go index 035878e..434fd5f 100644 --- a/types/btctypes/btctypes_test.go +++ b/types/btctypes/btctypes_test.go @@ -101,6 +101,28 @@ var _ = Describe("btc types", func() { }) }) + Context("bitcoin cash address", func() { + It("should decode the legacy addresses correctly", func() { + laddr, err := testutil.RandomAddress(BtcTestnet) + Expect(err).Should(BeNil()) + addr, err := AddressFromBase58(laddr.EncodeAddress(), BchTestnet) + Expect(err).Should(BeNil()) + Expect(addr.String()).Should(Equal(laddr.EncodeAddress())) + }) + + It("should decode the cash addresses (with prefix) correctly", func() { + addr, err := AddressFromBase58("bchtest:qrch9dvf9rc45p728n7d8p4r7n067jrdxgjyklgcg6", BchTestnet) + Expect(err).Should(BeNil()) + Expect(addr.String()).Should(Equal("qrch9dvf9rc45p728n7d8p4r7n067jrdxgjyklgcg6")) + }) + + It("should decode the cash addresses (without prefix) correctly", func() { + addr, err := AddressFromBase58("qrch9dvf9rc45p728n7d8p4r7n067jrdxgjyklgcg6", BchTestnet) + Expect(err).Should(BeNil()) + Expect(addr.String()).Should(Equal("qrch9dvf9rc45p728n7d8p4r7n067jrdxgjyklgcg6")) + }) + }) + Context("bitcoin networks", func() { It("should be able to parse network from a string", func() { testnet := "testnet"