decred.org/dcrdex@v1.0.5/server/asset/btc/btc_test.go (about) 1 //go:build !btclive && !btcfees && !feefetcher 2 3 // These tests will not be run if the btclive build tag is set. In that case, 4 // the live_test.go tests will run. 5 6 package btc 7 8 import ( 9 "bytes" 10 "context" 11 "crypto/sha256" 12 "encoding/hex" 13 "encoding/json" 14 "errors" 15 "fmt" 16 "math/rand" 17 "net" 18 "os" 19 "path/filepath" 20 "strconv" 21 "sync" 22 "testing" 23 "time" 24 25 "decred.org/dcrdex/dex" 26 "decred.org/dcrdex/dex/config" 27 dexbtc "decred.org/dcrdex/dex/networks/btc" 28 "github.com/btcsuite/btcd/btcec/v2" 29 "github.com/btcsuite/btcd/btcec/v2/ecdsa" 30 "github.com/btcsuite/btcd/btcjson" 31 "github.com/btcsuite/btcd/btcutil" 32 "github.com/btcsuite/btcd/chaincfg" 33 "github.com/btcsuite/btcd/chaincfg/chainhash" 34 "github.com/btcsuite/btcd/txscript" 35 "github.com/btcsuite/btcd/wire" 36 "gopkg.in/ini.v1" 37 ) 38 39 var ( 40 testParams = &chaincfg.MainNetParams 41 mainnetPort = 8332 42 blockPollDelay time.Duration 43 defaultHost = "127.0.0.1" 44 ) 45 46 func TestMain(m *testing.M) { 47 // Set any created Backends to poll for blocks every 50 ms to 48 // accommodate reorg testing. 49 blockPollInterval = time.Millisecond * 50 50 // blockPollDelay must be long enough to allow for block polling go-routine 51 // to run at least a single tick (must be at least as long as blockPollInterval 52 // and ideally significantly longer). 53 blockPollDelay = blockPollInterval + time.Millisecond*50 54 os.Exit(m.Run()) 55 } 56 57 // TestConfig tests the LoadConfig function. 58 func TestConfig(t *testing.T) { 59 cfg := &dexbtc.RPCConfig{} 60 parsedCfg := &dexbtc.RPCConfig{} 61 62 tempDir := t.TempDir() 63 filePath := filepath.Join(tempDir, "test.conf") 64 65 runCfg := func(inCfg *dexbtc.RPCConfig) error { 66 *cfg = *inCfg 67 cfgFile := ini.Empty() 68 err := cfgFile.ReflectFrom(cfg) 69 if err != nil { 70 return err 71 } 72 err = cfgFile.SaveTo(filePath) 73 if err != nil { 74 return err 75 } 76 parsedCfg = new(dexbtc.RPCConfig) 77 err = config.ParseInto(filePath, parsedCfg) 78 if err != nil { 79 return err 80 } 81 return dexbtc.CheckRPCConfig(parsedCfg, assetName, dex.Mainnet, dexbtc.RPCPorts) 82 } 83 84 // Check that there is an error from an unpopulated configuration. 85 err := runCfg(cfg) 86 if err == nil { 87 t.Fatalf("no error for empty config") 88 } 89 90 // Try with just the name. Error expected. 91 err = runCfg(&dexbtc.RPCConfig{ 92 RPCUser: "somename", 93 }) 94 if err == nil { 95 t.Fatalf("no error when just name provided") 96 } 97 98 // Try with just the password. Error expected. 99 err = runCfg(&dexbtc.RPCConfig{ 100 RPCPass: "somepass", 101 }) 102 if err == nil { 103 t.Fatalf("no error when just password provided") 104 } 105 106 // Give both name and password. This should not be an error. 107 err = runCfg(&dexbtc.RPCConfig{ 108 RPCUser: "somename", 109 RPCPass: "somepass", 110 }) 111 if err != nil { 112 t.Fatalf("unexpected error when both name and password provided: %v", err) 113 } 114 h, p, err := net.SplitHostPort(parsedCfg.RPCBind) 115 if err != nil { 116 t.Fatalf("error splitting host and port from %q: %v", parsedCfg.RPCBind, err) 117 } 118 if h != defaultHost { 119 t.Fatalf("unexpected default host. wanted %s, got %s", defaultHost, h) 120 } 121 if p != strconv.Itoa(mainnetPort) { 122 t.Fatalf("unexpected default port. wanted %d, got %s", mainnetPort, p) 123 } 124 // sanity check for name and password match 125 if parsedCfg.RPCUser != cfg.RPCUser { 126 t.Fatalf("name mismatch") 127 } 128 if parsedCfg.RPCPass != cfg.RPCPass { 129 t.Fatalf("password mismatch") 130 } 131 if parsedCfg.IsPublicProvider { 132 t.Fatalf("IsPublicProvider was unexpectedly set true") 133 } 134 135 // Check with a designated port, but no host specified. 136 err = runCfg(&dexbtc.RPCConfig{ 137 RPCUser: "somename", 138 RPCPass: "somepass", 139 RPCPort: 1234, 140 }) 141 if err != nil { 142 t.Fatalf("unexpected error when setting port: %v", err) 143 } 144 // See that RPCBind returns localhost. 145 h, p, err = net.SplitHostPort(parsedCfg.RPCBind) 146 if err != nil { 147 t.Fatalf("error splitting host and port when setting port: %v", err) 148 } 149 if h != defaultHost { 150 t.Fatalf("unexpected host when setting port. wanted %s, got %s", defaultHost, h) 151 } 152 if p != "1234" { 153 t.Fatalf("unexpected custom port. wanted 1234, got %s", p) 154 } 155 156 // Check with rpcbind set (without designated port) and custom rpcport. 157 err = runCfg(&dexbtc.RPCConfig{ 158 RPCUser: "somename", 159 RPCPass: "somepass", 160 RPCBind: "127.0.0.2", 161 RPCPort: 1234, 162 }) 163 if err != nil { 164 t.Fatalf("unexpected error when setting portless rpcbind: %v", err) 165 } 166 h, p, err = net.SplitHostPort(parsedCfg.RPCBind) 167 if err != nil { 168 t.Fatalf("error splitting host and port when setting portless rpcbind: %v", err) 169 } 170 if h != "127.0.0.2" { 171 t.Fatalf("unexpected host when setting portless rpcbind. wanted 127.0.0.2, got %s", h) 172 } 173 if p != "1234" { 174 t.Fatalf("unexpected custom port when setting portless rpcbind. wanted 1234, got %s", p) 175 } 176 177 // Check with a port set with both rpcbind and rpcport. The rpcbind port 178 // should take precedence. 179 err = runCfg(&dexbtc.RPCConfig{ 180 RPCUser: "somename", 181 RPCPass: "somepass", 182 RPCBind: "127.0.0.2:1234", 183 RPCPort: 1235, 184 }) 185 if err != nil { 186 t.Fatalf("unexpected error when setting port twice: %v", err) 187 } 188 h, p, err = net.SplitHostPort(parsedCfg.RPCBind) 189 if err != nil { 190 t.Fatalf("error splitting host and port when setting port twice: %v", err) 191 } 192 if h != "127.0.0.2" { 193 t.Fatalf("unexpected host when setting port twice. wanted 127.0.0.2, got %s", h) 194 } 195 if p != "1234" { 196 t.Fatalf("unexpected custom port when setting port twice. wanted 1234, got %s", p) 197 } 198 199 // Check with just a port for rpcbind and make sure it gets parsed. 200 err = runCfg(&dexbtc.RPCConfig{ 201 RPCUser: "somename", 202 RPCPass: "somepass", 203 RPCBind: ":1234", 204 }) 205 if err != nil { 206 t.Fatalf("unexpected error when setting port-only rpcbind: %v", err) 207 } 208 h, p, err = net.SplitHostPort(parsedCfg.RPCBind) 209 if err != nil { 210 t.Fatalf("error splitting host and port when setting port-only rpcbind: %v", err) 211 } 212 if h != defaultHost { 213 t.Fatalf("unexpected host when setting port-only rpcbind. wanted %s, got %s", defaultHost, h) 214 } 215 if p != "1234" { 216 t.Fatalf("unexpected custom port when setting port-only rpcbind. wanted 1234, got %s", p) 217 } 218 219 // IPv6 220 err = runCfg(&dexbtc.RPCConfig{ 221 RPCUser: "somename", 222 RPCPass: "somepass", 223 RPCBind: "[24c2:2865:4c7e:fd9b:76ea:4aa0:263d:6377]:1234", 224 }) 225 if err != nil { 226 t.Fatalf("unexpected error when trying IPv6: %v", err) 227 } 228 h, p, err = net.SplitHostPort(parsedCfg.RPCBind) 229 if err != nil { 230 t.Fatalf("error splitting host and port when trying IPv6: %v", err) 231 } 232 if h != "24c2:2865:4c7e:fd9b:76ea:4aa0:263d:6377" { 233 t.Fatalf("unexpected host when trying IPv6. wanted %s, got %s", "24c2:2865:4c7e:fd9b:76ea:4aa0:263d:6377", h) 234 } 235 if p != "1234" { 236 t.Fatalf("unexpected custom port when trying IPv6. wanted 1234, got %s", p) 237 } 238 239 // Check with an https URL, signifying a public RPC provider. 240 err = runCfg(&dexbtc.RPCConfig{ 241 RPCBind: "https://provider.com/my-api-key/btc", 242 }) 243 if err != nil { 244 t.Fatalf("unexpected error when trying public provider: %v", err) 245 } 246 if parsedCfg.RPCBind != "provider.com/my-api-key/btc" { 247 t.Fatalf("wrong host parsed. wanted %s, go %s", "provider.com/my-api-key/btc", parsedCfg.RPCBind) 248 } 249 if !parsedCfg.IsPublicProvider { 250 t.Fatalf("UseTLS not set") 251 } 252 } 253 254 // The remaining tests use the testBlockchain which feeds a testNode stub for 255 // rpcclient.Client. UTXOs, transactions and blocks are added to the blockchain 256 // as jsonrpc types to be requested by the Backend. 257 // 258 // General formula for testing 259 // 1. Create a Backend with the node field set to a testNode 260 // 2. Create a fake UTXO and all of the associated jsonrpc-type blocks and 261 // transactions and add the to the test blockchain. 262 // 3. Verify the Backend and UTXO methods are returning whatever is expected. 263 // 4. Optionally add more blocks and/or transactions to the blockchain and check 264 // return values again, as things near the top of the chain can change. 265 266 func randomBytes(len int) []byte { 267 bytes := make([]byte, len) 268 rand.Read(bytes) 269 return bytes 270 } 271 272 func randomHash() *chainhash.Hash { 273 hash := new(chainhash.Hash) 274 err := hash.SetBytes(randomBytes(32)) 275 if err != nil { 276 fmt.Printf("chainhash.Hash.SetBytes error: %v\n", err) 277 } 278 return hash 279 } 280 281 // A fake "blockchain" to be used for RPC calls by the btcNode. 282 type testBlockChain struct { 283 txOuts map[string]*btcjson.GetTxOutResult 284 txRaws map[chainhash.Hash]*btcjson.TxRawResult 285 blocks map[chainhash.Hash]*btcjson.GetBlockVerboseResult 286 hashes map[int64]*chainhash.Hash 287 } 288 289 // The testChain is a "blockchain" to store RPC responses for the Backend 290 // node stub to request. 291 var testChainMtx sync.RWMutex 292 var testChain testBlockChain 293 var testBestBlock testBlock 294 295 type testBlock struct { 296 hash chainhash.Hash 297 height uint32 298 } 299 300 // This must be called before using the testNode. 301 func cleanTestChain() { 302 testChainMtx.Lock() 303 defer testChainMtx.Unlock() 304 testBestBlock = testBlock{ 305 hash: zeroHash, 306 height: 0, 307 } 308 testChain = testBlockChain{ 309 txOuts: make(map[string]*btcjson.GetTxOutResult), 310 txRaws: make(map[chainhash.Hash]*btcjson.TxRawResult), 311 blocks: make(map[chainhash.Hash]*btcjson.GetBlockVerboseResult), 312 hashes: make(map[int64]*chainhash.Hash), 313 } 314 } 315 316 // A stub to replace rpcclient.Client for offline testing. 317 type testNode struct { 318 rawResult []byte 319 rawErr error 320 } 321 322 // Encode utxo info as a concatenated string hash:vout. 323 func txOutID(txHash *chainhash.Hash, index uint32) string { 324 return txHash.String() + ":" + strconv.Itoa(int(index)) 325 } 326 327 func mustUnmarshal(data []byte, thing any) { 328 if err := json.Unmarshal(data, thing); err != nil { 329 panic(err.Error()) 330 } 331 } 332 333 func (t *testNode) Shutdown() {} 334 335 func (t *testNode) WaitForShutdown() {} 336 337 func (t *testNode) RawRequest(_ context.Context, method string, params []json.RawMessage) (json.RawMessage, error) { 338 switch method { 339 case methodGetBestBlockHash: 340 testChainMtx.RLock() 341 defer testChainMtx.RUnlock() 342 bbHash := testBestBlock.hash 343 return json.Marshal(bbHash.String()) 344 case methodGetBlock: 345 testChainMtx.RLock() 346 defer testChainMtx.RUnlock() 347 var blkHash string 348 mustUnmarshal(params[0], &blkHash) 349 blockHash, err := chainhash.NewHashFromStr(blkHash) 350 if err != nil { 351 return nil, fmt.Errorf("rawrequest: %v method param unmarshal error: %v", 352 method, err) 353 } 354 block, found := testChain.blocks[*blockHash] 355 if !found { 356 return nil, fmt.Errorf("test block not found") 357 } 358 return json.Marshal(block) 359 case methodGetRawTransaction: 360 testChainMtx.RLock() 361 defer testChainMtx.RUnlock() 362 var hash string 363 mustUnmarshal(params[0], &hash) 364 txHash, err := chainhash.NewHashFromStr(hash) 365 if err != nil { 366 return nil, fmt.Errorf("rawrequest: %v method param unmarshal error: %v", 367 method, err) 368 } 369 tx, found := testChain.txRaws[*txHash] 370 if !found { 371 return nil, fmt.Errorf("test transaction not found") 372 } 373 return json.Marshal(tx) 374 case methodGetTxOut: 375 testChainMtx.RLock() 376 defer testChainMtx.RUnlock() 377 var hash string 378 mustUnmarshal(params[0], &hash) 379 txHash, err := chainhash.NewHashFromStr(hash) 380 if err != nil { 381 return nil, fmt.Errorf("rawrequest: %v method param unmarshal error: %v", 382 method, err) 383 } 384 var index uint32 385 mustUnmarshal(params[1], &index) 386 outID := txOutID(txHash, index) 387 out := testChain.txOuts[outID] 388 // Unfound is not an error for GetTxOut. 389 return json.Marshal(out) 390 case methodEstimateSmartFee: 391 const optimalFeeRate uint64 = 24 392 optimalRate := float64(optimalFeeRate) * 1e-5 393 return json.Marshal(&btcjson.EstimateSmartFeeResult{ 394 Blocks: 2, 395 FeeRate: &optimalRate, 396 }) 397 case methodGetBlockchainInfo: 398 if t.rawErr != nil { 399 return nil, t.rawErr 400 } 401 return t.rawResult, nil 402 } 403 return nil, nil 404 } 405 406 // Create a btcjson.GetTxOutResult such as is returned from GetTxOut. 407 func testGetTxOut(confirmations, value int64, pkScript []byte) *btcjson.GetTxOutResult { 408 return &btcjson.GetTxOutResult{ 409 Value: float64(value) / 1e8, 410 Confirmations: confirmations, 411 ScriptPubKey: btcjson.ScriptPubKeyResult{ 412 Hex: hex.EncodeToString(pkScript), 413 }, 414 } 415 } 416 417 // Create a *btcjson.TxRawResult such as is returned by 418 // GetRawTransactionVerbose. 419 func testRawTransactionVerbose(msgTx *wire.MsgTx, txid, blockHash *chainhash.Hash, 420 confirmations int64) *btcjson.TxRawResult { 421 422 var hash string 423 if blockHash != nil { 424 hash = blockHash.String() 425 } 426 w := bytes.NewBuffer(make([]byte, 0)) 427 err := msgTx.Serialize(w) 428 if err != nil { 429 fmt.Printf("error encoding MsgTx\n") 430 } 431 hexTx := w.Bytes() 432 return &btcjson.TxRawResult{ 433 Hex: hex.EncodeToString(hexTx), 434 Txid: txid.String(), 435 BlockHash: hash, 436 Confirmations: uint64(confirmations), 437 } 438 } 439 440 // Add a transaction output and it's getrawtransaction data. 441 func testAddTxOut(msgTx *wire.MsgTx, vout uint32, txHash, blockHash *chainhash.Hash, confirmations int64) *btcjson.GetTxOutResult { 442 testChainMtx.Lock() 443 defer testChainMtx.Unlock() 444 txOut := testGetTxOut(confirmations, msgTx.TxOut[vout].Value, msgTx.TxOut[vout].PkScript) 445 testChain.txOuts[txOutID(txHash, vout)] = txOut 446 testAddTxVerbose(msgTx, txHash, blockHash, confirmations) 447 return txOut 448 } 449 450 // Add a btcjson.TxRawResult to the blockchain. 451 func testAddTxVerbose(msgTx *wire.MsgTx, txHash, blockHash *chainhash.Hash, confirmations int64) *btcjson.TxRawResult { 452 tx := testRawTransactionVerbose(msgTx, txHash, blockHash, confirmations) 453 testChain.txRaws[*txHash] = tx 454 return tx 455 } 456 457 func testDeleteTxOut(txHash *chainhash.Hash, vout uint32) { 458 testChainMtx.Lock() 459 defer testChainMtx.Unlock() 460 txOutID(txHash, vout) 461 delete(testChain.txOuts, txOutID(txHash, vout)) 462 } 463 464 // Create a *btcjson.GetBlockVerboseResult such as is returned by 465 // GetBlockVerbose. 466 func testBlockVerbose(blockHash, prevHash *chainhash.Hash, confirmations, height int64) *btcjson.GetBlockVerboseResult { 467 return &btcjson.GetBlockVerboseResult{ 468 Hash: blockHash.String(), 469 Confirmations: confirmations, 470 Height: height, 471 PreviousHash: prevHash.String(), 472 } 473 } 474 475 // Add a GetBlockVerboseResult to the blockchain. 476 func testAddBlockVerbose(blockHash, prevHash *chainhash.Hash, confirmations int64, height uint32) *chainhash.Hash { 477 testChainMtx.Lock() 478 defer testChainMtx.Unlock() 479 if blockHash == nil { 480 blockHash = randomHash() 481 } 482 if height >= testBestBlock.height { 483 testBestBlock = testBlock{ 484 hash: *blockHash, 485 height: height, 486 } 487 } 488 if prevHash == nil { 489 if height == testBestBlock.height+1 { 490 prevHash = &testBestBlock.hash 491 } else { 492 prevHash = &zeroHash 493 } 494 } 495 testChain.blocks[*blockHash] = testBlockVerbose(blockHash, prevHash, confirmations, int64(height)) 496 return blockHash 497 } 498 499 func testClearBestBlock() { 500 testChainMtx.Lock() 501 defer testChainMtx.Unlock() 502 testBestBlock = testBlock{} 503 } 504 505 // An element of the TxRawResult vout array. 506 func testVout(value float64, pkScript []byte) btcjson.Vout { 507 return btcjson.Vout{ 508 Value: value, 509 ScriptPubKey: btcjson.ScriptPubKeyResult{ 510 Hex: hex.EncodeToString(pkScript), 511 }, 512 } 513 } 514 515 // An element of the TxRawResult vin array. 516 func testVin(txHash *chainhash.Hash, vout uint32) btcjson.Vin { 517 return btcjson.Vin{ 518 Txid: txHash.String(), 519 Vout: vout, 520 } 521 } 522 523 type testAuth struct { 524 pubkey []byte 525 pkHash []byte 526 msg []byte 527 sig []byte 528 } 529 530 type testMsgTx struct { 531 tx *wire.MsgTx 532 auth *testAuth 533 vout uint32 534 } 535 536 func s256Auth(msg []byte) *testAuth { 537 priv, err := btcec.NewPrivateKey() 538 if err != nil { 539 fmt.Printf("s256Auth error: %v\n", err) 540 } 541 pubkey := priv.PubKey().SerializeCompressed() 542 if msg == nil { 543 msg = randomBytes(32) 544 } 545 hash := sha256.Sum256(msg) 546 sig := ecdsa.Sign(priv, hash[:]) 547 return &testAuth{ 548 pubkey: pubkey, 549 pkHash: btcutil.Hash160(pubkey), 550 msg: msg, 551 sig: sig.Serialize(), 552 } 553 } 554 555 // Generate a public key on the secp256k1 curve. 556 func genPubkey() ([]byte, []byte) { 557 priv, err := btcec.NewPrivateKey() 558 if err != nil { 559 panic(err.Error()) 560 } 561 pub := priv.PubKey() 562 pubkey := pub.SerializeCompressed() 563 pkHash := btcutil.Hash160(pubkey) 564 return pubkey, pkHash 565 } 566 567 // A pay-to-script-hash pubkey script. 568 func newP2PKHScript(segwit bool) ([]byte, *testAuth) { 569 auth := s256Auth(nil) 570 var pkScript []byte 571 var err error 572 if segwit { 573 pkScript, err = txscript.NewScriptBuilder(). 574 AddOp(txscript.OP_0). 575 AddData(auth.pkHash). 576 Script() 577 if err != nil { 578 fmt.Printf("newP2PKHScript error: %v\n", err) 579 } 580 } else { 581 pkScript, err = txscript.NewScriptBuilder(). 582 AddOps([]byte{ 583 txscript.OP_DUP, 584 txscript.OP_HASH160, 585 }). 586 AddData(auth.pkHash). 587 AddOps([]byte{ 588 txscript.OP_EQUALVERIFY, 589 txscript.OP_CHECKSIG, 590 }).Script() 591 if err != nil { 592 fmt.Printf("newP2PKHScript error: %v\n", err) 593 } 594 } 595 596 return pkScript, auth 597 } 598 599 // A MsgTx for a regular transaction with a single output. No inputs, so it's 600 // not really a valid transaction, but that's okay on testBlockchain. 601 func testMakeMsgTx(segwit bool) *testMsgTx { 602 pkScript, auth := newP2PKHScript(segwit) 603 msgTx := wire.NewMsgTx(wire.TxVersion) 604 msgTx.AddTxOut(wire.NewTxOut(1, pkScript)) 605 return &testMsgTx{ 606 tx: msgTx, 607 auth: auth, 608 } 609 } 610 611 type testMsgTxSwap struct { 612 tx *wire.MsgTx 613 contract []byte 614 recipient btcutil.Address 615 refund btcutil.Address 616 } 617 618 // Create a swap (initialization) contract with random pubkeys and return the 619 // pubkey script and addresses. 620 func testSwapContract(segwit bool) ([]byte, btcutil.Address, btcutil.Address) { 621 lockTime := time.Now().Add(time.Hour * 8).Unix() 622 secretHash := randomBytes(32) 623 _, receiverPKH := genPubkey() 624 _, senderPKH := genPubkey() 625 contract, err := txscript.NewScriptBuilder(). 626 AddOps([]byte{ 627 txscript.OP_IF, 628 txscript.OP_SIZE, 629 }).AddInt64(32). 630 AddOps([]byte{ 631 txscript.OP_EQUALVERIFY, 632 txscript.OP_SHA256, 633 }).AddData(secretHash). 634 AddOps([]byte{ 635 txscript.OP_EQUALVERIFY, 636 txscript.OP_DUP, 637 txscript.OP_HASH160, 638 }).AddData(receiverPKH). 639 AddOp(txscript.OP_ELSE). 640 AddInt64(lockTime).AddOps([]byte{ 641 txscript.OP_CHECKLOCKTIMEVERIFY, 642 txscript.OP_DROP, 643 txscript.OP_DUP, 644 txscript.OP_HASH160, 645 }).AddData(senderPKH). 646 AddOps([]byte{ 647 txscript.OP_ENDIF, 648 txscript.OP_EQUALVERIFY, 649 txscript.OP_CHECKSIG, 650 }).Script() 651 if err != nil { 652 fmt.Printf("testSwapContract error: %v\n", err) 653 } 654 var receiverAddr, refundAddr btcutil.Address 655 if segwit { 656 receiverAddr, _ = btcutil.NewAddressWitnessPubKeyHash(receiverPKH, testParams) 657 refundAddr, _ = btcutil.NewAddressWitnessPubKeyHash(senderPKH, testParams) 658 } else { 659 receiverAddr, _ = btcutil.NewAddressPubKeyHash(receiverPKH, testParams) 660 refundAddr, _ = btcutil.NewAddressPubKeyHash(senderPKH, testParams) 661 } 662 663 return contract, receiverAddr, refundAddr 664 } 665 666 func testMsgTxSwapInit(val int64, segwit bool) *testMsgTxSwap { 667 msgTx := wire.NewMsgTx(wire.TxVersion) 668 contract, recipient, refund := testSwapContract(segwit) 669 scriptHash := btcutil.Hash160(contract) 670 pkScript, err := txscript.NewScriptBuilder(). 671 AddOp(txscript.OP_HASH160). 672 AddData(scriptHash). 673 AddOp(txscript.OP_EQUAL). 674 Script() 675 if err != nil { 676 fmt.Printf("script building error in testMsgTxSwapInit: %v", err) 677 } 678 msgTx.AddTxOut(wire.NewTxOut(val, pkScript)) 679 return &testMsgTxSwap{ 680 tx: msgTx, 681 contract: contract, 682 recipient: recipient, 683 refund: refund, 684 } 685 } 686 687 type testMultiSigAuth struct { 688 pubkeys [][]byte 689 pkHashes [][]byte 690 msg []byte 691 sigs [][]byte 692 } 693 694 // Information about a transaction with a P2SH output. 695 type testMsgTxP2SH struct { 696 tx *wire.MsgTx 697 auth *testMultiSigAuth 698 vout uint32 699 script []byte 700 n int 701 m int 702 } 703 704 // An M-of-N multi-sig script. 705 func testMultiSigScriptMofN(m, n int) ([]byte, *testMultiSigAuth) { 706 // serialized compressed pubkey used for multisig 707 addrs := make([]*btcutil.AddressPubKey, 0, n) 708 auth := &testMultiSigAuth{ 709 msg: randomBytes(32), 710 } 711 712 for i := 0; i < n; i++ { 713 a := s256Auth(auth.msg) 714 if i < m { 715 auth.pubkeys = append(auth.pubkeys, a.pubkey) 716 auth.pkHashes = append(auth.pkHashes, a.pkHash) 717 auth.sigs = append(auth.sigs, a.sig) 718 } 719 addr, err := btcutil.NewAddressPubKey(a.pubkey, testParams) 720 if err != nil { 721 fmt.Printf("error creating AddressSecpPubKey: %v\n", err) 722 return nil, nil 723 } 724 addrs = append(addrs, addr) 725 } 726 script, err := txscript.MultiSigScript(addrs, m) 727 if err != nil { 728 fmt.Printf("error creating MultiSigScript: %v\n", err) 729 return nil, nil 730 } 731 return script, auth 732 } 733 734 // A pay-to-script-hash M-of-N multi-sig output and vout 0 of a MsgTx. 735 func testMsgTxP2SHMofN(m, n int, segwit bool) *testMsgTxP2SH { 736 script, auth := testMultiSigScriptMofN(m, n) 737 var pkScript []byte 738 var err error 739 if segwit { 740 scriptHash := sha256.Sum256(script) 741 pkScript, err = txscript.NewScriptBuilder(). 742 AddOp(txscript.OP_0). 743 AddData(scriptHash[:]). 744 Script() 745 if err != nil { 746 fmt.Printf("error building script in testMsgTxP2SHMofN: %v", err) 747 } 748 } else { 749 scriptHash := btcutil.Hash160(script) 750 pkScript, err = txscript.NewScriptBuilder(). 751 AddOp(txscript.OP_HASH160). 752 AddData(scriptHash). 753 AddOp(txscript.OP_EQUAL). 754 Script() 755 if err != nil { 756 fmt.Printf("error building script in testMsgTxP2SHMofN: %v", err) 757 } 758 } 759 msgTx := wire.NewMsgTx(wire.TxVersion) 760 msgTx.AddTxOut(wire.NewTxOut(1, pkScript)) 761 return &testMsgTxP2SH{ 762 tx: msgTx, 763 auth: auth, 764 script: script, 765 vout: 0, 766 n: n, 767 m: m, 768 } 769 } 770 771 // Make a backend that logs to stdout. 772 func testBackend(segwit bool) (*Backend, func()) { 773 logger := dex.StdOutLogger("TEST", dex.LevelTrace) 774 // skip both loading config file and rpcclient.New in Connect. Manually 775 // create the Backend and set the node to our node stub. 776 btc := newBTC(&BackendCloneConfig{ 777 Name: "btc", 778 Segwit: segwit, 779 AddressDecoder: btcutil.DecodeAddress, 780 Logger: logger, 781 ChainParams: testParams, 782 }, nil) 783 btc.node = &RPCClient{ 784 requester: &testNode{}, 785 } 786 787 ctx, cancel := context.WithCancel(context.Background()) 788 var wg sync.WaitGroup 789 wg.Add(1) 790 go func() { 791 btc.run(ctx) 792 wg.Done() 793 }() 794 shutdown := func() { 795 cancel() 796 wg.Wait() 797 } 798 return btc, shutdown 799 } 800 801 // TestUTXOs tests UTXO-related paths. 802 func TestUTXOs(t *testing.T) { 803 // The various UTXO types to check: 804 // 1. A valid UTXO in a mempool transaction 805 // 2. A valid UTXO in a mined 806 // 3. A UTXO that is invalid because it is non-existent 807 // 4. A UTXO that is invalid because it has the wrong script type 808 // 5. A UTXO that becomes invalid in a reorg 809 // 6. A UTXO with a pay-to-script-hash for a 1-of-2 multisig redeem script 810 // 7. A UTXO with a pay-to-script-hash for a 2-of-2 multisig redeem script 811 // 8. A UTXO spending a pay-to-witness-pubkey-hash (P2WPKH) script. 812 // 9. A UTXO spending a pay-to-witness-script-hash (P2WSH) 2-of-2 multisig 813 // redeem script 814 // 10. A UTXO from a coinbase transaction, before and after maturing. 815 816 // Create a Backend with the test node. 817 btc, shutdown := testBackend(false) 818 defer shutdown() 819 820 // The vout will be randomized during reset. 821 const txHeight = uint32(50) 822 823 // A general reset function that clears the testBlockchain and the blockCache. 824 reset := func() { 825 btc.blockCache.mtx.Lock() 826 defer btc.blockCache.mtx.Unlock() 827 cleanTestChain() 828 newBC := newBlockCache() 829 btc.blockCache.blocks = newBC.blocks 830 btc.blockCache.mainchain = newBC.mainchain 831 btc.blockCache.best = newBC.best 832 } 833 834 // CASE 1: A valid UTXO in a mempool transaction 835 reset() 836 txHash := randomHash() 837 blockHash := randomHash() 838 msg := testMakeMsgTx(false) 839 // For a regular test tx, the output is at output index 0. Pass nil for the 840 // block hash and 0 for the block height and confirmations for a mempool tx. 841 txout := testAddTxOut(msg.tx, msg.vout, txHash, nil, 0) 842 // Set the value for this one. 843 txout.Value = 5.0 844 // There is no block info to add, since this is a mempool transaction 845 utxo, err := btc.utxo(txHash, msg.vout, nil) 846 if err != nil { 847 t.Fatalf("case 1 - unexpected error: %v", err) 848 } 849 // While we're here, check the spend script size and value are correct. 850 scriptSize := utxo.SpendSize() 851 wantScriptSize := uint32(dexbtc.TxInOverhead + 1 + dexbtc.RedeemP2PKHSigScriptSize) 852 if scriptSize != wantScriptSize { 853 t.Fatalf("case 1 - unexpected spend script size reported. expected %d, got %d", wantScriptSize, scriptSize) 854 } 855 if utxo.Value() != 500_000_000 { 856 t.Fatalf("case 1 - unexpected output value. expected 500,000,000, got %d", utxo.Value()) 857 } 858 // Now "mine" the transaction. 859 testAddBlockVerbose(blockHash, nil, 1, txHeight) 860 // Overwrite the test blockchain transaction details. 861 testAddTxOut(msg.tx, 0, txHash, blockHash, 1) 862 // "mining" the block should cause a reorg. 863 confs, err := utxo.Confirmations(context.Background()) 864 if err != nil { 865 t.Fatalf("case 1 - error retrieving confirmations after transaction \"mined\": %v", err) 866 } 867 if confs != 1 { 868 // The confirmation count is not taken from the wire.TxOut.Confirmations, 869 // so check that it is correctly calculated based on height. 870 t.Fatalf("case 1 - expected 1 confirmation after mining transaction, found %d", confs) 871 } 872 // Make sure the pubkey spends the output. 873 err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg) 874 if err != nil { 875 t.Fatalf("case 1 - Auth error: %v", err) 876 } 877 878 // CASE 2: A valid UTXO in a mined block. This UTXO will have non-zero 879 // confirmations, a valid pkScipt 880 reset() 881 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 882 txHash = randomHash() 883 msg = testMakeMsgTx(false) 884 testAddTxOut(msg.tx, msg.vout, txHash, blockHash, 1) 885 utxo, err = btc.utxo(txHash, msg.vout, nil) 886 if err != nil { 887 t.Fatalf("case 2 - unexpected error: %v", err) 888 } 889 err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg) 890 if err != nil { 891 t.Fatalf("case 2 - Auth error: %v", err) 892 } 893 894 // CASE 3: A UTXO that is invalid because it is non-existent 895 reset() 896 _, err = btc.utxo(randomHash(), 0, nil) 897 if err == nil { 898 t.Fatalf("case 3 - received no error for a non-existent UTXO") 899 } 900 901 // CASE 4: A UTXO that is invalid because it has the wrong script type. 902 reset() 903 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 904 txHash = randomHash() 905 msg = testMakeMsgTx(false) 906 // make the script nonsense. 907 msg.tx.TxOut[0].PkScript = []byte{0x00, 0x01, 0x02, 0x03} 908 testAddTxOut(msg.tx, msg.vout, txHash, blockHash, 1) 909 _, err = btc.utxo(txHash, msg.vout, nil) 910 if err == nil { 911 t.Fatalf("case 4 - received no error for a UTXO with wrong script type") 912 } 913 914 // CASE 5: A UTXO that becomes invalid in a reorg 915 reset() 916 txHash = randomHash() 917 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 918 msg = testMakeMsgTx(false) 919 testAddTxOut(msg.tx, msg.vout, txHash, blockHash, 1) 920 utxo, err = btc.utxo(txHash, msg.vout, nil) 921 if err != nil { 922 t.Fatalf("case 5 - received error for utxo") 923 } 924 _, err = utxo.Confirmations(context.Background()) 925 if err != nil { 926 t.Fatalf("case 5 - received error before reorg") 927 } 928 testAddBlockVerbose(nil, nil, 1, txHeight) 929 // Remove the txout from the blockchain, since bitcoind would no longer 930 // return it. 931 testDeleteTxOut(txHash, msg.vout) 932 time.Sleep(blockPollDelay) 933 _, err = utxo.Confirmations(context.Background()) 934 if err == nil { 935 t.Fatalf("case 5 - received no error for orphaned transaction") 936 } 937 // Now put it back in mempool and check again. 938 testAddTxOut(msg.tx, msg.vout, txHash, nil, 0) 939 confs, err = utxo.Confirmations(context.Background()) 940 if err != nil { 941 t.Fatalf("case 5 - error checking confirmations on orphaned transaction back in mempool: %v", err) 942 } 943 if confs != 0 { 944 t.Fatalf("case 5 - expected 0 confirmations, got %d", confs) 945 } 946 947 // CASE 6: A UTXO with a pay-to-script-hash for a 1-of-2 multisig redeem 948 // script 949 reset() 950 txHash = randomHash() 951 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 952 msgMultiSig := testMsgTxP2SHMofN(1, 2, false) 953 testAddTxOut(msgMultiSig.tx, msgMultiSig.vout, txHash, blockHash, 1) 954 // First try to get the UTXO without providing the raw script. 955 _, err = btc.utxo(txHash, msgMultiSig.vout, nil) 956 if err == nil { 957 t.Fatalf("no error thrown for p2sh utxo when no script was provided") 958 } 959 // Now provide the script. 960 utxo, err = btc.utxo(txHash, msgMultiSig.vout, msgMultiSig.script) 961 if err != nil { 962 t.Fatalf("case 6 - received error for utxo: %v", err) 963 } 964 confs, err = utxo.Confirmations(context.Background()) 965 if err != nil { 966 t.Fatalf("case 6 - error getting confirmations: %v", err) 967 } 968 if confs != 1 { 969 t.Fatalf("case 6 - expected 1 confirmation, got %d", confs) 970 } 971 err = utxo.Auth(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg) 972 if err != nil { 973 t.Fatalf("case 6 - Auth error: %v", err) 974 } 975 976 // CASE 7: A UTXO with a pay-to-script-hash for a 2-of-2 multisig redeem 977 // script 978 reset() 979 txHash = randomHash() 980 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 981 msgMultiSig = testMsgTxP2SHMofN(2, 2, false) 982 testAddTxOut(msgMultiSig.tx, msgMultiSig.vout, txHash, blockHash, 1) 983 utxo, err = btc.utxo(txHash, msgMultiSig.vout, msgMultiSig.script) 984 if err != nil { 985 t.Fatalf("case 7 - received error for utxo: %v", err) 986 } 987 // Try to get by with just one of the pubkeys. 988 err = utxo.Auth(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg) 989 if err == nil { 990 t.Fatalf("case 7 - no error when only provided one of two required pubkeys") 991 } 992 // Now do both. 993 err = utxo.Auth(msgMultiSig.auth.pubkeys, msgMultiSig.auth.sigs, msgMultiSig.auth.msg) 994 if err != nil { 995 t.Fatalf("case 7 - Auth error: %v", err) 996 } 997 998 // CASE 8: A UTXO spending a pay-to-witness-pubkey-hash (P2WPKH) script. 999 reset() 1000 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 1001 txHash = randomHash() 1002 msg = testMakeMsgTx(true) // true - P2WPKH at vout 0 1003 testAddTxOut(msg.tx, msg.vout, txHash, blockHash, 1) 1004 utxo, err = btc.utxo(txHash, msg.vout, nil) 1005 if err != nil { 1006 t.Fatalf("case 8 - unexpected error: %v", err) 1007 } 1008 // Check that the segwit flag is set. 1009 if !utxo.scriptType.IsSegwit() { 1010 t.Fatalf("case 8 - script type parsed as non-segwit") 1011 } 1012 err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg) 1013 if err != nil { 1014 t.Fatalf("case 8 - Auth error: %v", err) 1015 } 1016 1017 // CASE 9: A UTXO spending a pay-to-witness-script-hash (P2WSH) 2-of-2 1018 // multisig redeem script 1019 reset() 1020 txHash = randomHash() 1021 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 1022 msgMultiSig = testMsgTxP2SHMofN(2, 2, true) 1023 testAddTxOut(msgMultiSig.tx, msgMultiSig.vout, txHash, blockHash, 1) 1024 utxo, err = btc.utxo(txHash, msgMultiSig.vout, msgMultiSig.script) 1025 if err != nil { 1026 t.Fatalf("case 9 - received error for utxo: %v", err) 1027 } 1028 // Check that the script is flagged segwit 1029 if !utxo.scriptType.IsSegwit() { 1030 t.Fatalf("case 9 - script type parsed as non-segwit") 1031 } 1032 // Try to get by with just one of the pubkeys. 1033 err = utxo.Auth(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg) 1034 if err == nil { 1035 t.Fatalf("case 9 - no error when only provided one of two required pubkeys") 1036 } 1037 // Now do both. 1038 err = utxo.Auth(msgMultiSig.auth.pubkeys, msgMultiSig.auth.sigs, msgMultiSig.auth.msg) 1039 if err != nil { 1040 t.Fatalf("case 9 - Auth error: %v", err) 1041 } 1042 1043 // CASE 10: A UTXO from a coinbase transaction, before and after maturing. 1044 reset() 1045 blockHash = testAddBlockVerbose(nil, nil, 1, txHeight) 1046 txHash = randomHash() 1047 msg = testMakeMsgTx(false) 1048 txOut := testAddTxOut(msg.tx, msg.vout, txHash, blockHash, 1) 1049 txOut.Coinbase = true 1050 _, err = btc.utxo(txHash, msg.vout, nil) 1051 if err == nil { 1052 t.Fatalf("case 10 - no error for immature transaction") 1053 } 1054 if !errors.Is(err, immatureTransactionError) { 1055 t.Fatalf("case 10 - expected immatureTransactionError, got: %v", err) 1056 } 1057 // Mature the transaction 1058 maturity := uint32(testParams.CoinbaseMaturity) 1059 testAddBlockVerbose(nil, nil, 1, txHeight+maturity-1) 1060 txOut.Confirmations = int64(maturity) 1061 time.Sleep(blockPollDelay) 1062 _, err = btc.utxo(txHash, msg.vout, nil) 1063 if err != nil { 1064 t.Fatalf("case 10 - unexpected error after maturing: %v", err) 1065 } 1066 1067 // CASE 11: A swap contract 1068 val := int64(5) 1069 cleanTestChain() 1070 txHash = randomHash() 1071 blockHash = randomHash() 1072 swap := testMsgTxSwapInit(val, btc.segwit) 1073 testAddBlockVerbose(blockHash, nil, 1, txHeight) 1074 btcVal := btcutil.Amount(val).ToBTC() 1075 testAddTxOut(swap.tx, 0, txHash, blockHash, 1).Value = btcVal 1076 verboseTx := testChain.txRaws[*txHash] 1077 1078 spentTxHash := randomHash() 1079 verboseTx.Vin = append(verboseTx.Vin, testVin(spentTxHash, 0)) 1080 // We need to add that transaction to the blockchain too, because it will 1081 // be requested for the previous outpoint value. 1082 spentMsg := testMakeMsgTx(false) 1083 spentTx := testAddTxVerbose(spentMsg.tx, spentTxHash, blockHash, 2) 1084 spentTx.Vout = []btcjson.Vout{testVout(1, nil)} 1085 swapOut := swap.tx.TxOut[0] 1086 btcVal = btcutil.Amount(swapOut.Value).ToBTC() 1087 verboseTx.Vout = append(verboseTx.Vout, testVout(btcVal, swapOut.PkScript)) 1088 utxo, err = btc.utxo(txHash, 0, swap.contract) 1089 if err != nil { 1090 t.Fatalf("case 11 - received error for utxo: %v", err) 1091 } 1092 1093 // Now try again with the correct vout. 1094 contract, err := btc.auditContract(utxo.Output) // sets refund and swap addresses 1095 if err != nil { 1096 t.Fatalf("case 11 - unexpected error auditing contract: %v", err) 1097 } 1098 if contract.SwapAddress != swap.recipient.String() { 1099 t.Fatalf("case 11 - wrong recipient. wanted '%s' got '%s'", contract.SwapAddress, swap.recipient.String()) 1100 } 1101 if contract.Value() != 5 { 1102 t.Fatalf("case 11 - unexpected output value. wanted 5, got %d", contract.Value()) 1103 } 1104 } 1105 1106 func TestRedemption(t *testing.T) { 1107 t.Run("segwit", func(t *testing.T) { 1108 testRedemption(t, true) 1109 }) 1110 t.Run("non-segwit", func(t *testing.T) { 1111 testRedemption(t, false) 1112 }) 1113 } 1114 1115 func testRedemption(t *testing.T, segwit bool) { 1116 btc, shutdown := testBackend(false) 1117 defer shutdown() 1118 1119 // The vout will be randomized during reset. 1120 txHeight := uint32(32) 1121 cleanTestChain() 1122 txHash := randomHash() 1123 redemptionID := toCoinID(txHash, 0) 1124 // blockHash := randomHash() 1125 spentHash := randomHash() 1126 spentVout := uint32(0) 1127 spentID := toCoinID(spentHash, spentVout) 1128 verboseTx := testAddTxVerbose(testMakeMsgTx(segwit).tx, spentHash, nil, 0) 1129 verboseTx.Vout = append(verboseTx.Vout, btcjson.Vout{ 1130 Value: 5, 1131 }) 1132 msg := testMakeMsgTx(segwit) 1133 vin := btcjson.Vin{ 1134 Txid: spentHash.String(), 1135 Vout: spentVout, 1136 } 1137 1138 // A valid mempool redemption. 1139 verboseTx = testAddTxVerbose(msg.tx, txHash, nil, 0) 1140 verboseTx.Vin = append(verboseTx.Vin, vin) 1141 redemption, err := btc.Redemption(redemptionID, spentID, nil) 1142 if err != nil { 1143 t.Fatalf("Redemption error: %v", err) 1144 } 1145 confs, err := redemption.Confirmations(context.Background()) 1146 if err != nil { 1147 t.Fatalf("redemption Confirmations error: %v", err) 1148 } 1149 if confs != 0 { 1150 t.Fatalf("expected 0 confirmations, got %d", confs) 1151 } 1152 1153 // Missing transaction 1154 testChainMtx.Lock() 1155 delete(testChain.txRaws, *txHash) 1156 testChainMtx.Unlock() 1157 _, err = btc.Redemption(redemptionID, spentID, nil) 1158 if err == nil { 1159 t.Fatalf("No error for missing transaction") 1160 } 1161 1162 // Doesn't spend transaction. 1163 verboseTx = testAddTxVerbose(msg.tx, txHash, nil, 0) 1164 verboseTx.Vin = append(verboseTx.Vin, btcjson.Vin{ 1165 Txid: randomHash().String(), 1166 }) 1167 _, err = btc.Redemption(redemptionID, spentID, nil) 1168 if err == nil { 1169 t.Fatalf("No error for wrong previous outpoint") 1170 } 1171 1172 // Mined transaction. 1173 blockHash := randomHash() 1174 blockHeight := txHeight - 1 1175 verboseTx = testAddTxVerbose(msg.tx, txHash, blockHash, int64(blockHeight)) 1176 verboseTx.Vin = append(verboseTx.Vin, vin) 1177 testAddBlockVerbose(blockHash, nil, 1, blockHeight) 1178 redemption, err = btc.Redemption(redemptionID, spentID, nil) 1179 if err != nil { 1180 t.Fatalf("Redemption with confs error: %v", err) 1181 } 1182 confs, err = redemption.Confirmations(context.Background()) 1183 if err != nil { 1184 t.Fatalf("redemption with confs Confirmations error: %v", err) 1185 } 1186 if confs != 1 { 1187 t.Fatalf("expected 1 confirmation, got %d", confs) 1188 } 1189 } 1190 1191 // TestReorg tests various reorg paths. Because bitcoind doesn't support 1192 // websocket notifications, and ZeroMQ is not desirable, Backend polls for 1193 // new block data every 5 seconds or so. The poll interval means it's possible 1194 // for various flavors of reorg to happen before a new block is detected. These 1195 // tests check that reorgs of various depths are correctly handled by the 1196 // block monitor loop and the block cache. 1197 func TestReorg(t *testing.T) { 1198 // Create a Backend with the test node. 1199 btc, shutdown := testBackend(false) 1200 defer shutdown() 1201 1202 // Clear the blockchain and set the provided chain to build on the ancestor 1203 // block. 1204 reset := func() { 1205 btc.blockCache.mtx.Lock() 1206 defer btc.blockCache.mtx.Unlock() 1207 cleanTestChain() 1208 newBC := newBlockCache() 1209 btc.blockCache.blocks = newBC.blocks 1210 btc.blockCache.mainchain = newBC.mainchain 1211 btc.blockCache.best = newBC.best 1212 } 1213 reset() 1214 1215 ancestorHeight := uint32(50) 1216 ancestorHash := testAddBlockVerbose(nil, nil, 0, ancestorHeight) 1217 1218 makeChain := func() []*chainhash.Hash { 1219 chain := make([]*chainhash.Hash, 0, 3) 1220 for i := 0; i < 3; i++ { 1221 h := testAddBlockVerbose(nil, nil, 0, ancestorHeight+1+uint32(i)) 1222 chain = append(chain, h) 1223 } 1224 return chain 1225 } 1226 chainA := makeChain() 1227 chainB := makeChain() 1228 1229 setChain := func(hashes []*chainhash.Hash) { 1230 testClearBestBlock() 1231 rootConfs := int64(len(hashes)) + 1 1232 // Add the ancestor block 1233 testAddBlockVerbose(ancestorHash, nil, rootConfs, ancestorHeight) 1234 prevHash := ancestorHash 1235 for i, hash := range hashes { 1236 prevHash = testAddBlockVerbose(hash, prevHash, rootConfs-int64(i), ancestorHeight+uint32(i)) 1237 1238 } 1239 time.Sleep(blockPollDelay) 1240 } 1241 1242 setSidechainConfs := func(hashes []*chainhash.Hash) { 1243 btc.blockCache.mtx.Lock() // read from (*blockCache).add in cache.go 1244 testChainMtx.Lock() // field of testChain 1245 defer testChainMtx.Unlock() 1246 defer btc.blockCache.mtx.Unlock() 1247 for _, hash := range hashes { 1248 block, found := testChain.blocks[*hash] 1249 if !found { 1250 t.Fatalf("test block not found") 1251 } 1252 // Set Confirmations 1253 block.Confirmations = -1 1254 } 1255 } 1256 1257 checkOrphanState := func(hashes []*chainhash.Hash, orphanState bool) bool { 1258 for _, hash := range hashes { 1259 blk, err := btc.getBtcBlock(hash) 1260 if err != nil { 1261 t.Fatalf("error retrieving block after reorg: %v\n", err) 1262 } 1263 if blk.orphaned != orphanState { 1264 return false 1265 } 1266 } 1267 return true 1268 } 1269 1270 // The test will start with chain A at length lenA above the ancestor. A reorg 1271 // of length lenB will fully replace chain A. Chain A should be fully 1272 // orphaned and chain B should be fully mainchain afterwards. 1273 test := func(lenA, lenB int) { 1274 reset() 1275 setChain(chainA[:lenA]) 1276 // Check that chain A is all mainchain. 1277 if !checkOrphanState(chainA[:lenA], false) { 1278 t.Fatalf("chain A block not mainchain for test %d:%d before reorg", lenA, lenB) 1279 } 1280 // Reorg the chain. 1281 setSidechainConfs(chainA[:lenA]) 1282 setChain(chainB[:lenB]) 1283 // Chain A should all be orphaned. 1284 if !checkOrphanState(chainA[:lenA], true) { 1285 t.Fatalf("chain A block still mainchain for test %d:%d after reorg", lenA, lenB) 1286 } 1287 // Chain B should all be mainchain. 1288 if !checkOrphanState(chainB[:lenB], false) { 1289 t.Fatalf("chain B block not mainchain for test %d:%d after reorg", lenA, lenB) 1290 } 1291 } 1292 1293 // Now run 9 tests. 1294 for a := 1; a <= 3; a++ { 1295 for b := 1; b <= 3; b++ { 1296 test(a, b) 1297 } 1298 } 1299 1300 // Create a transaction at the tip, then orphan the block and move the 1301 // transaction to mempool. 1302 reset() 1303 setChain(chainA) 1304 tipHeight := btc.blockCache.tipHeight() 1305 txHash := randomHash() 1306 tip, found := btc.blockCache.atHeight(tipHeight) 1307 if !found { 1308 t.Fatalf("did not find newly connected block at height %d, likely cache sync issue", tipHeight) 1309 } 1310 msg := testMakeMsgTx(false) 1311 testAddBlockVerbose(&tip.hash, nil, 1, tipHeight) 1312 testAddTxOut(msg.tx, 0, txHash, &tip.hash, 1) 1313 utxo, err := btc.utxo(txHash, msg.vout, nil) 1314 if err != nil { 1315 t.Fatalf("utxo error 1: %v", err) 1316 } 1317 confs, err := utxo.Confirmations(context.Background()) 1318 if err != nil { 1319 t.Fatalf("Confirmations error: %v", err) 1320 } 1321 if confs != 1 { 1322 t.Fatalf("wrong number of confirmations. expected 1, got %d", confs) 1323 } 1324 1325 // Orphan the block and move the transaction to mempool. 1326 btc.blockCache.reorg(int64(ancestorHeight)) 1327 testAddTxOut(msg.tx, 0, txHash, nil, 0) 1328 confs, err = utxo.Confirmations(context.Background()) 1329 if err != nil { 1330 t.Fatalf("Confirmations error after reorg: %v", err) 1331 } 1332 if confs != 0 { 1333 t.Fatalf("Expected zero confirmations after reorg, found %d", confs) 1334 } 1335 1336 // Start over, but put it in a lower block instead. 1337 reset() 1338 setChain(chainA) 1339 tip, found = btc.blockCache.atHeight(tipHeight) 1340 if !found { 1341 t.Fatalf("did not find newly connected block at height %d, likely cache sync issue", tipHeight) 1342 } 1343 testAddBlockVerbose(&tip.hash, nil, 1, tipHeight) 1344 testAddTxOut(msg.tx, 0, txHash, &tip.hash, 1) 1345 utxo, err = btc.utxo(txHash, msg.vout, nil) 1346 if err != nil { 1347 t.Fatalf("utxo error 2: %v", err) 1348 } 1349 1350 // Reorg and add a single block with the transaction. 1351 btc.blockCache.reorg(int64(ancestorHeight)) 1352 newBlockHash := randomHash() 1353 testAddTxOut(msg.tx, 0, txHash, newBlockHash, 1) 1354 testAddBlockVerbose(newBlockHash, ancestorHash, 1, ancestorHeight+1) 1355 time.Sleep(blockPollDelay) 1356 confs, err = utxo.Confirmations(context.Background()) 1357 if err != nil { 1358 t.Fatalf("Confirmations error after reorg to lower block: %v", err) 1359 } 1360 if confs != 1 { 1361 t.Fatalf("Expected zero confirmations after reorg to lower block, found %d", confs) 1362 } 1363 } 1364 1365 // TestAuxiliary checks the UTXO convenience functions like TxHash, Vout, and 1366 // TxID. 1367 func TestAuxiliary(t *testing.T) { 1368 // Create a Backend with the test node. 1369 btc, shutdown := testBackend(false) 1370 defer shutdown() 1371 1372 // Add a transaction and retrieve it. 1373 cleanTestChain() 1374 maturity := int64(testParams.CoinbaseMaturity) 1375 msg := testMakeMsgTx(false) 1376 valueSats := int64(29e6) 1377 msg.tx.TxOut[0].Value = valueSats 1378 txid := hex.EncodeToString(randomBytes(32)) 1379 txHash, _ := chainhash.NewHashFromStr(txid) 1380 txHeight := rand.Uint32() 1381 blockHash := testAddBlockVerbose(nil, nil, 1, txHeight) 1382 testAddTxOut(msg.tx, msg.vout, txHash, blockHash, maturity) 1383 coinID := toCoinID(txHash, msg.vout) 1384 1385 verboseTx := testChain.txRaws[*txHash] 1386 vout := btcjson.Vout{ 1387 Value: 0.29, 1388 N: 0, 1389 //ScriptPubKey 1390 } 1391 verboseTx.Vout = append(verboseTx.Vout, vout) 1392 1393 utxo, err := btc.FundingCoin(context.Background(), coinID, nil) 1394 if err != nil { 1395 t.Fatalf("unexpected error: %v", err) 1396 } 1397 if utxo.Coin().TxID() != txid { 1398 t.Fatalf("utxo txid doesn't match") 1399 } 1400 1401 if utxo.(*UTXO).value != uint64(msg.tx.TxOut[0].Value) { 1402 t.Errorf("incorrect value. got %d, wanted %d", utxo.(*UTXO).value, msg.tx.TxOut[0].Value) 1403 } 1404 1405 voutVal, err := btc.prevOutputValue(txid, 0) 1406 if err != nil { 1407 t.Fatalf("prevOutputValue: %v", err) 1408 } 1409 if voutVal != uint64(msg.tx.TxOut[0].Value) { 1410 t.Errorf("incorrect value. got %d, wanted %d", utxo.(*UTXO).value, msg.tx.TxOut[0].Value) 1411 } 1412 } 1413 1414 // TestCheckSwapAddress checks that addresses are parsing or not parsing as 1415 // expected. 1416 func TestCheckSwapAddress(t *testing.T) { 1417 btc, shutdown := testBackend(false) // non-segwit 1418 defer shutdown() 1419 1420 type test struct { 1421 addr string 1422 wantErr bool 1423 } 1424 tests := []test{ 1425 {"", true}, // "unknown format" 1426 {"18Zpft83eov56iESWuPpV8XFLJ1b8gMZy7", false}, // p2pkh (ok) 1427 {"3GD2fSQxhkXDAW66i6JBwCqhLFSvhMNrtJ", true}, // p2sh 1428 {"03aab68960ac1cc2a4151e40c530fcf32284afaed0cebbaec98500c8f3c491d50b", true}, // pubkey (not really address) 1429 {"bc1qq3wc0u7x0nezw3hfjkh45ffk09gm4ghl0k7dwe", true}, // p2wpkh (segwit) 1430 {"bc1qdn28r3yr790mjzadkd79sgdkm92jdfq6j5zxsz6w0j9hvwsmr4ys7yn244", true}, // p2wsh 1431 {"28Zpft83eov56iESWuPpV8XFLJ1b8gMZy7", true}, // wrong network (checksum mismatch) 1432 {"3GD2fSQxhkXDAW66i6JBwCqhLFSvhMNrtO", true}, // capital letter O not base 58 (unknown format) 1433 {"3GD2fSQx", true}, // checksum mismatch 1434 } 1435 for _, test := range tests { 1436 if btc.CheckSwapAddress(test.addr) != !test.wantErr { 1437 t.Fatalf("wantErr = %t, address = %s", test.wantErr, test.addr) 1438 } 1439 } 1440 1441 btcSegwit, shutdown := testBackend(true) // segwit 1442 defer shutdown() 1443 1444 tests = []test{ 1445 {"18Zpft83eov56iESWuPpV8XFLJ1b8gMZy7", true}, // p2pkh (non-segwit) 1446 {"3GD2fSQxhkXDAW66i6JBwCqhLFSvhMNrtJ", true}, // p2sh 1447 {"03aab68960ac1cc2a4151e40c530fcf32284afaed0cebbaec98500c8f3c491d50b", true}, // pubkey (not really address) 1448 {"bc1qq3wc0u7x0nezw3hfjkh45ffk09gm4ghl0k7dwe", false}, // p2wpkh (ok) 1449 {"bc1qdn28r3yr790mjzadkd79sgdkm92jdfq6j5zxsz6w0j9hvwsmr4ys7yn244", true}, // p2wsh 1450 {"28Zpft83eov56iESWuPpV8XFLJ1b8gMZy7", true}, // wrong network (checksum mismatch) 1451 {"3GD2fSQxhkXDAW66i6JBwCqhLFSvhMNrtO", true}, // capital letter O not base 58 (unknown format) 1452 {"3GD2fSQx", true}, // checksum mismatch 1453 } 1454 for _, test := range tests { 1455 if btcSegwit.CheckSwapAddress(test.addr) != !test.wantErr { 1456 t.Fatalf("wantErr = %t, address = %s", test.wantErr, test.addr) 1457 } 1458 } 1459 } 1460 1461 func TestDriver_DecodeCoinID(t *testing.T) { 1462 tests := []struct { 1463 name string 1464 coinID []byte 1465 want string 1466 wantErr bool 1467 }{ 1468 { 1469 "ok", 1470 []byte{ 1471 0x16, 0x8f, 0x34, 0x3a, 0xdf, 0x17, 0xe0, 0xc3, 1472 0xa2, 0xe8, 0x88, 0x79, 0x8, 0x87, 0x17, 0xb8, 1473 0xac, 0x93, 0x47, 0xb9, 0x66, 0xd, 0xa7, 0x4b, 1474 0xde, 0x3e, 0x1d, 0x1f, 0x47, 0x94, 0x9f, 0xdf, // 32 byte hash 1475 0x0, 0x0, 0x0, 0x1, // 4 byte vout 1476 }, 1477 "df9f94471f1d3ede4ba70d66b94793acb81787087988e8a2c3e017df3a348f16:1", 1478 false, 1479 }, 1480 { 1481 "bad", 1482 []byte{ 1483 0x16, 0x8f, 0x34, 0x3a, 0xdf, 0x17, 0xe0, 0xc3, 1484 0xa2, 0xe8, 0x88, 0x79, 0x8, 0x87, 0x17, 0xb8, 1485 0xac, 0x93, 0x47, 0xb9, 0x66, 0xd, 0xa7, 0x4b, 1486 0xde, 0x3e, 0x1d, 0x1f, 0x47, 0x94, 0x9f, // 31 bytes 1487 0x0, 0x0, 0x0, 0x1, 1488 }, 1489 "", 1490 true, 1491 }, 1492 } 1493 for _, tt := range tests { 1494 t.Run(tt.name, func(t *testing.T) { 1495 d := &Driver{} 1496 got, err := d.DecodeCoinID(tt.coinID) 1497 if (err != nil) != tt.wantErr { 1498 t.Errorf("Driver.DecodeCoinID() error = %v, wantErr %v", err, tt.wantErr) 1499 return 1500 } 1501 if got != tt.want { 1502 t.Errorf("Driver.DecodeCoinID() = %v, want %v", got, tt.want) 1503 } 1504 }) 1505 } 1506 } 1507 1508 func TestSynced(t *testing.T) { 1509 btc, shutdown := testBackend(true) 1510 defer shutdown() 1511 tNode := btc.node.requester.(*testNode) 1512 tNode.rawResult, _ = json.Marshal(&btcjson.GetBlockChainInfoResult{ 1513 Headers: 100, 1514 Blocks: 99, 1515 }) 1516 synced, err := btc.Synced() 1517 if err != nil { 1518 t.Fatalf("Synced error: %v", err) 1519 } 1520 if !synced { 1521 t.Fatalf("not synced when should be synced") 1522 } 1523 1524 tNode.rawResult, _ = json.Marshal(&btcjson.GetBlockChainInfoResult{ 1525 Headers: 100, 1526 Blocks: 50, 1527 }) 1528 synced, err = btc.Synced() 1529 if err != nil { 1530 t.Fatalf("Synced error: %v", err) 1531 } 1532 if synced { 1533 t.Fatalf("synced when shouldn't be synced") 1534 } 1535 1536 tNode.rawErr = fmt.Errorf("test error") 1537 _, err = btc.Synced() 1538 if err == nil { 1539 t.Fatalf("getblockchaininfo error not propagated") 1540 } 1541 tNode.rawErr = nil 1542 }