decred.org/dcrdex@v1.0.5/client/asset/btc/electrum_test.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 //go:build electrumlive 5 6 package btc 7 8 import ( 9 "bytes" 10 "context" 11 "crypto/sha256" 12 "encoding/hex" 13 "fmt" 14 "testing" 15 "time" 16 17 "decred.org/dcrdex/client/asset" 18 "github.com/btcsuite/btcd/btcutil/psbt" 19 "github.com/btcsuite/btcd/chaincfg" 20 "github.com/btcsuite/btcd/chaincfg/chainhash" 21 "github.com/btcsuite/btcd/wire" 22 ) 23 24 const walletPass = "walletpass" 25 26 func TestPSBT(t *testing.T) { 27 // an unsigned swap txn funded by a single p2wpkh 28 txRaw, _ := hex.DecodeString("010000000118f36e05994f8e69dace16f4a3d4c9ff8db6f58bf48d498b1c0d6daf63c6f8690100000000ffffffff02408e2c0000000000220020e0133024bb27f510f6959467df91ed5daa791f598ba1c09ef1f1ac4540bfdda16e7710000000000016001460a8dbedd538501e2ad9e9f5a4d8a11e35a6c4e700000000") 29 msgTx := wire.NewMsgTx(wire.TxVersion) 30 err := msgTx.Deserialize(bytes.NewReader(txRaw)) 31 if err != nil { 32 t.Fatal(err) 33 } 34 35 packet, err := psbt.NewFromUnsignedTx(msgTx) 36 if err != nil { 37 t.Fatal(err) 38 } 39 enc, err := packet.B64Encode() 40 if err != nil { 41 t.Fatal(err) 42 } 43 fmt.Println(enc) 44 } 45 46 // This test uses a testnet BTC Electrum wallet listening on localhost 6789. 47 48 func TestElectrumExchangeWallet(t *testing.T) { 49 ctx, cancel := context.WithCancel(context.Background()) 50 defer cancel() 51 52 notes := make(chan asset.WalletNotification) 53 walletCfg := &asset.WalletConfig{ 54 Type: walletTypeElectrum, 55 Settings: map[string]string{ 56 "rpcuser": "user", 57 "rpcpassword": "pass", 58 "rpcbind": "127.0.0.1:6789", 59 }, 60 Emit: asset.NewWalletEmitter(notes, BipID, tLogger), 61 PeersChange: func(num uint32, err error) { 62 t.Logf("peer count = %d, err = %v", num, err) 63 }, 64 } 65 cfg := &BTCCloneCFG{ 66 WalletCFG: walletCfg, 67 Symbol: "btc", 68 Logger: tLogger, 69 ChainParams: &chaincfg.TestNet3Params, 70 WalletInfo: WalletInfo, 71 DefaultFallbackFee: defaultFee, 72 DefaultFeeRateLimit: defaultFeeRateLimit, 73 Segwit: true, 74 // ElectrumWallet constructor overrides btc.localFeeRate = eew.walletFeeRate 75 } 76 eew, err := ElectrumWallet(cfg) 77 if err != nil { 78 t.Fatal(err) 79 } 80 81 wg, err := eew.Connect(ctx) 82 if err != nil { 83 t.Fatal(err) 84 } 85 86 addr, err := eew.DepositAddress() 87 if err != nil { 88 t.Fatal(err) 89 } 90 t.Log(addr) 91 92 feeRate := eew.FeeRate() 93 if feeRate == 0 { 94 t.Fatal("zero fee rate") 95 } 96 t.Log("feeRate:", feeRate) 97 98 // findRedemption 99 swapTxHash, _ := chainhash.NewHashFromStr("1e99930f76638e3eddd79de94bf0ff574c7a400d1e6986cd61b3b5fd8212b1a3") 100 swapVout := uint32(0) 101 redeemTxHash, _ := chainhash.NewHashFromStr("4283c1fefa4898eb1bd2041547cd6361f8167dec004890b58ba1817914dc8541") 102 redeemVin := uint32(0) 103 // P2WSH: tb1q9vqw464klshj80vklss0ms4h82082y8q86x8cen3934r7zrvt6vs4e3m8u / 00202b00eaeab6fc2f23bd96fc20fdc2b73a9e7510e03e8c7c66712c6a3f086c5e99 104 // contract: 6382012088a820135a45665765d68dc255ecd5f1870a1b29b8f1fd95c1e02ca5151e354d2a2cf68876a9144fdb2cad8e98983b25675d3ebe2133e650c56dbf6704ecb0d262b17576a9144fdb2cad8e98983b25675d3ebe2133e650c56dbf6888ac 105 contract, _ := hex.DecodeString("6382012088a820135a45665765d68dc255ecd5f1870a1b29b8f1fd95c1e02ca5151e354d2a2cf68876a9144fdb2cad8e98983b25675d3ebe2133e650c56dbf6704ecb0d262b17576a9144fdb2cad8e98983b25675d3ebe2133e650c56dbf6888ac") 106 // contractHash, _ := hex.DecodeString("2b00eaeab6fc2f23bd96fc20fdc2b73a9e7510e03e8c7c66712c6a3f086c5e99") 107 contractHash := sha256.Sum256(contract) 108 wantSecret, _ := hex.DecodeString("aa8e04bb335da65d362b89ec0630dc76fd02ffaca783ae58cb712a2820f504ce") 109 foundTxHash, foundVin, secret, err := eew.findRedemption(ctx, NewOutPoint(swapTxHash, swapVout), contractHash[:]) 110 if err != nil { 111 t.Fatal(err) 112 } 113 if !bytes.Equal(secret, wantSecret) { 114 t.Errorf("incorrect secret %x, wanted %x", secret, wantSecret) 115 } 116 if !foundTxHash.IsEqual(redeemTxHash) { 117 t.Errorf("incorrect redeem tx hash %v, wanted %v", foundTxHash, redeemTxHash) 118 } 119 if redeemVin != foundVin { 120 t.Errorf("incorrect redeem tx input %d, wanted %d", foundVin, redeemVin) 121 } 122 123 // FindRedemption 124 redeemCoin, secretBytes, err := eew.FindRedemption(ctx, ToCoinID(swapTxHash, swapVout), contract) 125 if err != nil { 126 t.Fatal(err) 127 } 128 foundTxHash, foundVin, err = decodeCoinID(redeemCoin) 129 if err != nil { 130 t.Fatal(err) 131 } 132 if !foundTxHash.IsEqual(redeemTxHash) { 133 t.Errorf("incorrect redeem tx hash %v, wanted %v", foundTxHash, redeemTxHash) 134 } 135 if redeemVin != foundVin { 136 t.Errorf("incorrect redeem tx input %d, wanted %d", foundVin, redeemVin) 137 } 138 if !secretBytes.Equal(wantSecret) { 139 t.Errorf("incorrect secret %v, wanted %x", secretBytes, wantSecret) 140 } 141 142 t.Logf("Found redemption of contract %v:%d at %v:%d!", swapTxHash, swapVout, foundTxHash, foundVin) 143 144 // err = eew.Unlock([]byte(walletPass)) 145 // if err != nil { 146 // t.Fatal(err) 147 // } 148 149 // wdCoin, err := eew.Withdraw(addr, toSatoshi(1.2435), feeRate) 150 // if err != nil { 151 // t.Fatal(err) 152 // } 153 // t.Log(wdCoin.String()) 154 155 select { 156 case <-time.After(10 * time.Second): // a bit of best block polling 157 cancel() 158 case ni := <-notes: 159 if _, is := ni.(*asset.TipChangeNote); is { 160 cancel() 161 } 162 163 case <-ctx.Done(): // or until TipChange cancels the context 164 } 165 166 wg.Wait() 167 }