decred.org/dcrdex@v1.0.5/client/asset/btc/electrum_client_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 "encoding/hex" 12 "sync" 13 "testing" 14 "time" 15 16 "decred.org/dcrdex/client/asset/btc/electrum" 17 "decred.org/dcrdex/dex" 18 19 "github.com/btcsuite/btcd/chaincfg" 20 "github.com/btcsuite/btcd/chaincfg/chainhash" 21 "github.com/btcsuite/btcd/txscript" 22 ) 23 24 func Test_electrumWallet(t *testing.T) { 25 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 26 defer cancel() 27 28 // This test uses a *testnet* BTC wallet listening on localhost port 6789. 29 // Testnet is used because it tests interaction with actual remote servers 30 // (and the bitcoin network) and there are historical transactions that can 31 // be inspected in public block explorers. 32 const walletPass = "walletpass" // set me 33 ewc := electrum.NewWalletClient("user", "pass", "http://127.0.0.1:5678", "~/.electrum/testnet/wallets/default_wallet") 34 ew := newElectrumWallet(ewc, &electrumWalletConfig{ 35 params: &chaincfg.TestNet3Params, 36 log: dex.StdOutLogger("ELECTRUM-TEST", dex.LevelTrace), 37 segwit: true, 38 }) 39 var wg sync.WaitGroup 40 err := ew.Connect(ctx, &wg) 41 if err != nil { 42 t.Fatal(err) 43 } 44 45 medianTime, err := ew.calcMedianTime(ctx, 2286089) // ltc testnet 46 if err != nil { 47 t.Fatal(err) 48 } 49 t.Log(medianTime.UTC()) // 2022-07-15 13:09:39 +0000 UTC 50 51 // Output: 52 // https://tbtc.bitaps.com/b9890847952be7ca8918368cbfa88fea0efb71ffe9549de0c4ae6e23c2b02e4c/output/0 53 // Spending input: 54 // https://tbtc.bitaps.com/9dbed377dbbee2ac98dcf5edfc504e04f16afa73b07afc81bc0711b7625f66d1/input/0 55 56 // findOutputSpender 57 fundHash, _ := chainhash.NewHashFromStr("b9890847952be7ca8918368cbfa88fea0efb71ffe9549de0c4ae6e23c2b02e4c") 58 fundVout := uint32(0) 59 fundVal := int64(1922946974) 60 fundPkScript, _ := hex.DecodeString("00145bb911c50e0f79e0212aae1e0503ee8192038b57") 61 fundAddr := "tb1qtwu3r3gwpau7qgf24c0q2qlwsxfq8z6hl6efm4" 62 spendMsgTx, spendVin, err := ew.findOutputSpender(ctx, fundHash, fundVout) 63 if err != nil { 64 t.Fatal(err) 65 } 66 spendHash := spendMsgTx.TxHash() 67 wantSpendTxID := "9dbed377dbbee2ac98dcf5edfc504e04f16afa73b07afc81bc0711b7625f66d1" 68 if spendHash.String() != wantSpendTxID { 69 t.Errorf("Incorrect spending tx hash %v, want %v", spendHash, wantSpendTxID) 70 } 71 wantSpendVin := uint32(0) 72 if spendVin != wantSpendVin { 73 t.Errorf("Incorrect spending tx input index %d, want %d", spendVin, wantSpendVin) 74 } 75 76 // gettxout - first the spent output 77 fundTxOut, fundConfs, err := ew.GetTxOut(fundHash, fundVout, nil, time.Time{}) 78 if err != nil { 79 t.Fatal(err) 80 } 81 if fundTxOut != nil { 82 t.Errorf("expected a nil TxOut for a spent output, but got one") 83 } 84 85 // gettxout - next an old unspent output that's likely to stay unspent 86 fundHash, _ = chainhash.NewHashFromStr("34fd12be115549ec482fdb20bc5196e0107cfcda32c252ffea9e8116ee6fcf8e") 87 fundVout = 0 88 fundVal = 20000 89 fundPkScript, _ = hex.DecodeString("76a914b3e0f80ce29ac48793de504ae9aa0b6579dda29a88ac") 90 fundAddr = "mwv4kWRXkc2w42823FU9SJ16cvZH8Aobke" 91 fundTxOut, fundConfs, err = ew.GetTxOut(fundHash, fundVout, nil, time.Time{}) 92 if err != nil { 93 t.Fatal(err) 94 } 95 t.Logf("%v confs = %d", fundHash, fundConfs) 96 if fundTxOut.Value != fundVal { 97 t.Errorf("wrong output value %d, wanted %d", fundTxOut.Value, fundVal) 98 } 99 if !bytes.Equal(fundTxOut.PkScript, fundPkScript) { 100 t.Errorf("wanted pkScript %x, got %x", fundPkScript, fundTxOut.PkScript) 101 } 102 scriptClass, addrs, reqSigs, err := txscript.ExtractPkScriptAddrs(fundTxOut.PkScript, ew.chainParams) 103 if err != nil { 104 t.Fatal(err) 105 } 106 if scriptClass != txscript.PubKeyHashTy { 107 t.Errorf("got script class %v, wanted %v", scriptClass, txscript.PubKeyHashTy) 108 } 109 if len(addrs) != 1 { 110 t.Fatalf("got %d addresses, wanted 1", len(addrs)) 111 } 112 if addrs[0].String() != fundAddr { 113 t.Errorf("got address %v, wanted %v", addrs[0], fundAddr) 114 } 115 if reqSigs != 1 { 116 t.Fatalf("requires %d sigs, expected 1", reqSigs) 117 } 118 119 addr, err := ew.wallet.GetUnusedAddress(ctx) // or ew.externalAddress(), but let's not blow up the gap limit testing 120 if err != nil { 121 t.Fatal(err) 122 } 123 t.Log(addr) 124 125 err = ew.WalletUnlock([]byte(walletPass)) 126 if err != nil { 127 t.Fatal(err) 128 } 129 130 _, err = ew.PrivKeyForAddress(addr) 131 if err != nil { 132 t.Fatal(err) 133 } 134 135 // Try the following with your own addresses! 136 // sent1, err := ew.sendToAddress("tltc1qn7dsn7glncdgf078pauerlqum6ma8peyvfa7gz", toSatoshi(0.2345), 4, false) 137 // if err != nil { 138 // t.Fatal(err) 139 // } 140 // t.Log(sent1) 141 // sent2, err := ew.sendToAddress("tltc1qpth226vjw2vp3mk8fvjfnrc4ygy8vtnx8p2q78", toSatoshi(0.2345), 5, true) 142 // if err != nil { 143 // t.Fatal(err) 144 // } 145 // t.Log(sent2) 146 147 // addr, err := ew.externalAddress() 148 // if err != nil { 149 // t.Fatal(err) 150 // } 151 // sweepTx, err := ew.sweep(addr.EncodeAddress(), 6) 152 // if err != nil { 153 // t.Fatal(err) 154 // } 155 // t.Log(sweepTx) 156 157 // Hang out until the context times out. Maybe see a new block. Think about 158 // other tests... 159 wg.Wait() 160 }