github.com/decred/dcrlnd@v0.7.6/lnwallet/chanfunding/psbt_assembler_test.go (about) 1 package chanfunding 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "fmt" 7 "reflect" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/davecgh/go-spew/spew" 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrd/chaincfg/v3" 15 "github.com/decred/dcrd/dcrec/secp256k1/v4" 16 "github.com/decred/dcrd/dcrutil/v4" 17 "github.com/decred/dcrd/txscript/v4/stdaddr" 18 "github.com/decred/dcrd/wire" 19 "github.com/decred/dcrlnd/input" 20 "github.com/decred/dcrlnd/internal/psbt" 21 "github.com/decred/dcrlnd/keychain" 22 "github.com/stretchr/testify/require" 23 ) 24 25 var ( 26 localPrivkey = []byte{1, 2, 3, 4, 5, 6} //nolint:unused 27 remotePrivkey = []byte{6, 5, 4, 3, 2, 1} //nolint:unused 28 chanCapacity dcrutil.Amount = 644000 29 params = *chaincfg.SimNetParams() //nolint:unused 30 defaultTimeout = 50 * time.Millisecond 31 ) 32 33 func privKeyFromBytes(b []byte) (*secp256k1.PrivateKey, *secp256k1.PublicKey) { //nolint:unused 34 key := secp256k1.PrivKeyFromBytes(b) 35 return key, key.PubKey() 36 } 37 38 // TestPsbtIntent tests the basic happy path of the PSBT assembler and intent. 39 func TestPsbtIntent(t *testing.T) { 40 t.Skipf("psbt not implemented") 41 42 t.Parallel() 43 44 // Create a simple assembler and ask it to provision a channel to get 45 // the funding intent. 46 a := NewPsbtAssembler(chanCapacity, nil, ¶ms, true) 47 intent, err := a.ProvisionChannel(&Request{LocalAmt: chanCapacity}) 48 if err != nil { 49 t.Fatalf("error provisioning channel: %v", err) 50 } 51 psbtIntent, ok := intent.(*PsbtIntent) 52 if !ok { 53 t.Fatalf("intent was not a PsbtIntent") 54 } 55 if psbtIntent.State != PsbtShimRegistered { 56 t.Fatalf("unexpected state. got %d wanted %d", psbtIntent.State, 57 PsbtShimRegistered) 58 } 59 60 // The first step with the intent is that the funding manager starts 61 // negotiating with the remote peer and they accept. By accepting, they 62 // send over their multisig key that's going to be used for the funding 63 // output. With that known, we can start crafting a PSBT. 64 _, localPubkey := privKeyFromBytes(localPrivkey) 65 _, remotePubkey := privKeyFromBytes(remotePrivkey) 66 psbtIntent.BindKeys( 67 &keychain.KeyDescriptor{PubKey: localPubkey}, remotePubkey, 68 ) 69 if psbtIntent.State != PsbtOutputKnown { 70 t.Fatalf("unexpected state. got %d wanted %d", psbtIntent.State, 71 PsbtOutputKnown) 72 } 73 74 // Make sure the output script address is correct. 75 script, _, err := input.GenFundingPkScript( 76 localPubkey.SerializeCompressed(), 77 remotePubkey.SerializeCompressed(), int64(chanCapacity), 78 ) 79 if err != nil { 80 t.Fatalf("error calculating script: %v", err) 81 } 82 witnessScriptHash := sha256.Sum256(script) 83 addr, err := stdaddr.NewAddressScriptHashV0( 84 witnessScriptHash[:], ¶ms, 85 ) 86 if err != nil { 87 t.Fatalf("unable to encode address: %v", err) 88 } 89 fundingAddr, amt, pendingPsbt, err := psbtIntent.FundingParams() 90 if err != nil { 91 t.Fatalf("unable to get funding params: %v", err) 92 } 93 if addr.String() != fundingAddr.String() { 94 t.Fatalf("unexpected address. got %s wanted %s", fundingAddr, 95 addr) 96 } 97 if amt != int64(chanCapacity) { 98 t.Fatalf("unexpected amount. got %d wanted %d", amt, 99 chanCapacity) 100 } 101 102 // Parse and check the returned PSBT packet. 103 if pendingPsbt == nil { 104 t.Fatalf("expected pending PSBT to be returned") 105 } 106 if len(pendingPsbt.UnsignedTx.TxOut) != 1 { 107 t.Fatalf("unexpected number of outputs. got %d wanted %d", 108 len(pendingPsbt.UnsignedTx.TxOut), 1) 109 } 110 txOut := pendingPsbt.UnsignedTx.TxOut[0] 111 if !bytes.Equal(txOut.PkScript[2:], witnessScriptHash[:]) { 112 t.Fatalf("unexpected PK script in output. got %x wanted %x", 113 txOut.PkScript[2:], witnessScriptHash) 114 } 115 if txOut.Value != int64(chanCapacity) { 116 t.Fatalf("unexpected value in output. got %d wanted %d", 117 txOut.Value, chanCapacity) 118 } 119 120 // Add an input to the pending TX to simulate it being funded. 121 pendingPsbt.UnsignedTx.TxIn = []*wire.TxIn{ 122 {PreviousOutPoint: wire.OutPoint{Index: 0}}, 123 } 124 pendingPsbt.Inputs = []psbt.PInput{ 125 {WitnessUtxo: &wire.TxOut{Value: int64(chanCapacity + 1)}}, 126 } 127 128 // Verify the dummy PSBT with the intent. 129 err = psbtIntent.Verify(pendingPsbt, false) 130 if err != nil { 131 t.Fatalf("error verifying pending PSBT: %v", err) 132 } 133 if psbtIntent.State != PsbtVerified { 134 t.Fatalf("unexpected state. got %d wanted %d", psbtIntent.State, 135 PsbtVerified) 136 } 137 138 // Add some fake witness data to the transaction so it thinks it's 139 // signed. 140 pendingPsbt.Inputs[0].WitnessUtxo = &wire.TxOut{ 141 Value: int64(chanCapacity) * 2, 142 PkScript: []byte{99, 99, 99}, 143 } 144 pendingPsbt.Inputs[0].FinalScriptSig = []byte{88, 88, 88} 145 pendingPsbt.Inputs[0].FinalScriptWitness = []byte{2, 0, 0} 146 147 // If we call Finalize, the intent will signal to the funding manager 148 // that it can continue with the funding flow. We want to make sure 149 // the signal arrives. 150 var wg sync.WaitGroup 151 errChan := make(chan error, 1) 152 wg.Add(1) 153 go func() { 154 defer wg.Done() 155 select { 156 case err := <-psbtIntent.PsbtReady: 157 errChan <- err 158 159 case <-time.After(defaultTimeout): 160 errChan <- fmt.Errorf("timed out") 161 } 162 }() 163 err = psbtIntent.Finalize(pendingPsbt) 164 if err != nil { 165 t.Fatalf("error finalizing pending PSBT: %v", err) 166 } 167 wg.Wait() 168 169 // We should have a nil error in our channel now. 170 err = <-errChan 171 if err != nil { 172 t.Fatalf("unexpected error after finalize: %v", err) 173 } 174 if psbtIntent.State != PsbtFinalized { 175 t.Fatalf("unexpected state. got %d wanted %d", psbtIntent.State, 176 PsbtFinalized) 177 } 178 179 // Make sure the funding transaction can be compiled. 180 _, err = psbtIntent.CompileFundingTx() 181 if err != nil { 182 t.Fatalf("error compiling funding TX from PSBT: %v", err) 183 } 184 if psbtIntent.State != PsbtFundingTxCompiled { 185 t.Fatalf("unexpected state. got %d wanted %d", psbtIntent.State, 186 PsbtFundingTxCompiled) 187 } 188 } 189 190 // TestPsbtIntentBasePsbt tests that a channel funding output can be appended to 191 // a given base PSBT in the funding flow. 192 func TestPsbtIntentBasePsbt(t *testing.T) { 193 t.Skipf("psbt not implemented") 194 t.Parallel() 195 196 // First create a dummy PSBT with a single output. 197 pendingPsbt, err := psbt.New( 198 []*wire.OutPoint{{}}, []*wire.TxOut{ 199 {Value: 999, PkScript: []byte{99, 88, 77}}, 200 }, 2, 0, []uint32{0}, 201 ) 202 if err != nil { 203 t.Fatalf("unable to create dummy PSBT") 204 } 205 206 // Generate the funding multisig keys and the address so we can compare 207 // it to the output of the intent. 208 _, localPubkey := privKeyFromBytes(localPrivkey) 209 _, remotePubkey := privKeyFromBytes(remotePrivkey) 210 // Make sure the output script address is correct. 211 script, _, err := input.GenFundingPkScript( 212 localPubkey.SerializeCompressed(), 213 remotePubkey.SerializeCompressed(), int64(chanCapacity), 214 ) 215 if err != nil { 216 t.Fatalf("error calculating script: %v", err) 217 } 218 witnessScriptHash := sha256.Sum256(script) 219 addr, err := stdaddr.NewAddressScriptHashV0( 220 witnessScriptHash[:], ¶ms, 221 ) 222 if err != nil { 223 t.Fatalf("unable to encode address: %v", err) 224 } 225 226 // Now as the next step, create a new assembler/intent pair with a base 227 // PSBT to see that we can add an additional output to it. 228 a := NewPsbtAssembler(chanCapacity, pendingPsbt, ¶ms, true) 229 intent, err := a.ProvisionChannel(&Request{LocalAmt: chanCapacity}) 230 if err != nil { 231 t.Fatalf("error provisioning channel: %v", err) 232 } 233 psbtIntent, ok := intent.(*PsbtIntent) 234 if !ok { 235 t.Fatalf("intent was not a PsbtIntent") 236 } 237 psbtIntent.BindKeys( 238 &keychain.KeyDescriptor{PubKey: localPubkey}, remotePubkey, 239 ) 240 newAddr, amt, twoOutPsbt, err := psbtIntent.FundingParams() 241 if err != nil { 242 t.Fatalf("unable to get funding params: %v", err) 243 } 244 if addr.String() != newAddr.String() { 245 t.Fatalf("unexpected address. got %s wanted %s", newAddr, 246 addr) 247 } 248 if amt != int64(chanCapacity) { 249 t.Fatalf("unexpected amount. got %d wanted %d", amt, 250 chanCapacity) 251 } 252 if len(twoOutPsbt.UnsignedTx.TxOut) != 2 { 253 t.Fatalf("unexpected number of outputs. got %d wanted %d", 254 len(twoOutPsbt.UnsignedTx.TxOut), 2) 255 } 256 if len(twoOutPsbt.UnsignedTx.TxIn) != 1 { 257 t.Fatalf("unexpected number of inputs. got %d wanted %d", 258 len(twoOutPsbt.UnsignedTx.TxIn), 1) 259 } 260 txOld := pendingPsbt.UnsignedTx 261 txNew := twoOutPsbt.UnsignedTx 262 prevoutEqual := reflect.DeepEqual( 263 txOld.TxIn[0].PreviousOutPoint, txNew.TxIn[0].PreviousOutPoint, 264 ) 265 if !prevoutEqual { 266 t.Fatalf("inputs changed. got %s wanted %s", 267 spew.Sdump(txOld.TxIn[0].PreviousOutPoint), 268 spew.Sdump(txNew.TxIn[0].PreviousOutPoint)) 269 } 270 if !reflect.DeepEqual(txOld.TxOut[0], txNew.TxOut[0]) { 271 t.Fatalf("existing output changed. got %v wanted %v", 272 txOld.TxOut[0], txNew.TxOut[0]) 273 } 274 } 275 276 // TestPsbtVerify tests the PSBT verification process more deeply than just 277 // the happy path. 278 func TestPsbtVerify(t *testing.T) { 279 t.Skipf("psbt not implemented") 280 t.Parallel() 281 282 testCases := []struct { 283 name string 284 expectedErr string 285 shouldPublish bool 286 doVerify func(int64, *psbt.Packet, *PsbtIntent) error 287 }{ 288 { 289 name: "nil packet", 290 expectedErr: "PSBT is nil", 291 shouldPublish: true, 292 doVerify: func(amt int64, p *psbt.Packet, 293 i *PsbtIntent) error { 294 295 return i.Verify(nil, false) 296 }, 297 }, 298 { 299 name: "wrong state", 300 shouldPublish: true, 301 expectedErr: "invalid state. got user_canceled " + 302 "expected output_known", 303 doVerify: func(amt int64, p *psbt.Packet, 304 i *PsbtIntent) error { 305 306 i.State = PsbtInitiatorCanceled 307 return i.Verify(p, false) 308 }, 309 }, 310 { 311 name: "output not found, value wrong", 312 shouldPublish: true, 313 expectedErr: "funding output not found in PSBT", 314 doVerify: func(amt int64, p *psbt.Packet, 315 i *PsbtIntent) error { 316 317 p.UnsignedTx.TxOut[0].Value = 123 318 return i.Verify(p, false) 319 }, 320 }, 321 { 322 name: "output not found, pk script wrong", 323 shouldPublish: true, 324 expectedErr: "funding output not found in PSBT", 325 doVerify: func(amt int64, p *psbt.Packet, 326 i *PsbtIntent) error { 327 328 p.UnsignedTx.TxOut[0].PkScript = []byte{1, 2, 3} 329 return i.Verify(p, false) 330 }, 331 }, 332 { 333 name: "no inputs", 334 shouldPublish: true, 335 expectedErr: "PSBT has no inputs", 336 doVerify: func(amt int64, p *psbt.Packet, 337 i *PsbtIntent) error { 338 339 return i.Verify(p, false) 340 }, 341 }, 342 { 343 name: "input(s) too small", 344 shouldPublish: true, 345 expectedErr: "input amount sum must be larger than " + 346 "output amount sum", 347 doVerify: func(amt int64, p *psbt.Packet, 348 i *PsbtIntent) error { 349 350 p.UnsignedTx.TxIn = []*wire.TxIn{{}} 351 p.Inputs = []psbt.PInput{{ 352 WitnessUtxo: &wire.TxOut{ 353 Value: int64(chanCapacity), 354 }, 355 }} 356 return i.Verify(p, false) 357 }, 358 }, 359 { 360 name: "missing witness-utxo field", 361 shouldPublish: true, 362 expectedErr: "cannot use TX for channel funding, not " + 363 "all inputs are SegWit spends, risk of " + 364 "malleability: input 1 is non-SegWit spend " + 365 "or missing redeem script", 366 doVerify: func(amt int64, p *psbt.Packet, 367 i *PsbtIntent) error { 368 369 txOut := &wire.TxOut{ 370 Value: int64(chanCapacity/2) + 1, 371 } 372 p.UnsignedTx.TxIn = []*wire.TxIn{ 373 {}, 374 { 375 PreviousOutPoint: wire.OutPoint{ 376 Index: 0, 377 }, 378 }, 379 } 380 p.Inputs = []psbt.PInput{ 381 { 382 WitnessUtxo: txOut, 383 }, 384 { 385 NonWitnessUtxo: &wire.MsgTx{ 386 TxOut: []*wire.TxOut{ 387 txOut, 388 }, 389 }, 390 }, 391 } 392 return i.Verify(p, false) 393 }, 394 }, 395 { 396 name: "skip verify", 397 shouldPublish: false, 398 expectedErr: "", 399 doVerify: func(amt int64, p *psbt.Packet, 400 i *PsbtIntent) error { 401 402 txOut := &wire.TxOut{ 403 Value: int64(chanCapacity/2) + 1, 404 } 405 p.UnsignedTx.TxIn = []*wire.TxIn{ 406 {}, 407 { 408 PreviousOutPoint: wire.OutPoint{ 409 Index: 0, 410 }, 411 }, 412 } 413 p.Inputs = []psbt.PInput{ 414 { 415 WitnessUtxo: txOut, 416 }, 417 { 418 WitnessUtxo: txOut, 419 }, 420 } 421 422 if err := i.Verify(p, true); err != nil { 423 return err 424 } 425 426 if i.FinalTX != p.UnsignedTx { 427 return fmt.Errorf("expected final TX " + 428 "to be set") 429 } 430 if i.State != PsbtFinalized { 431 return fmt.Errorf("expected state to " + 432 "be finalized") 433 } 434 435 select { 436 case <-i.PsbtReady: 437 438 case <-time.After(50 * time.Millisecond): 439 return fmt.Errorf("expected PSBT " + 440 "ready to be signaled") 441 } 442 443 return nil 444 }, 445 }, 446 { 447 name: "input correct", 448 shouldPublish: true, 449 expectedErr: "", 450 doVerify: func(amt int64, p *psbt.Packet, 451 i *PsbtIntent) error { 452 453 txOut := &wire.TxOut{ 454 Value: int64(chanCapacity/2) + 1, 455 } 456 p.UnsignedTx.TxIn = []*wire.TxIn{ 457 {}, 458 { 459 PreviousOutPoint: wire.OutPoint{ 460 Index: 0, 461 }, 462 }, 463 } 464 p.Inputs = []psbt.PInput{ 465 { 466 WitnessUtxo: txOut, 467 }, 468 { 469 WitnessUtxo: txOut, 470 }} 471 return i.Verify(p, false) 472 }, 473 }, 474 } 475 476 // Create a simple assembler and ask it to provision a channel to get 477 // the funding intent. 478 a := NewPsbtAssembler(chanCapacity, nil, ¶ms, true) 479 intent, err := a.ProvisionChannel(&Request{LocalAmt: chanCapacity}) 480 if err != nil { 481 t.Fatalf("error provisioning channel: %v", err) 482 } 483 psbtIntent := intent.(*PsbtIntent) 484 485 // Bind our test keys to get the funding parameters. 486 _, localPubkey := privKeyFromBytes(localPrivkey) 487 _, remotePubkey := privKeyFromBytes(remotePrivkey) 488 psbtIntent.BindKeys( 489 &keychain.KeyDescriptor{PubKey: localPubkey}, remotePubkey, 490 ) 491 492 // Loop through all our test cases. 493 for _, tc := range testCases { 494 tc := tc 495 t.Run(tc.name, func(t *testing.T) { 496 // Reset the state from a previous test and create a new 497 // pending PSBT that we can manipulate. 498 psbtIntent.shouldPublish = tc.shouldPublish 499 psbtIntent.State = PsbtOutputKnown 500 _, amt, pendingPsbt, err := psbtIntent.FundingParams() 501 if err != nil { 502 t.Fatalf("unable to get funding params: %v", err) 503 } 504 505 err = tc.doVerify(amt, pendingPsbt, psbtIntent) 506 if err != nil && tc.expectedErr == "" { 507 t.Fatalf("unexpected error, got '%v' wanted "+ 508 "'%v'", err, tc.expectedErr) 509 } 510 if err != nil && err.Error() != tc.expectedErr { 511 t.Fatalf("unexpected error, got '%v' wanted "+ 512 "'%v'", err, tc.expectedErr) 513 } 514 }) 515 } 516 } 517 518 // TestPsbtFinalize tests the PSBT finalization process more deeply than just 519 // the happy path. 520 func TestPsbtFinalize(t *testing.T) { 521 t.Skipf("psbt not implemented") 522 t.Parallel() 523 524 testCases := []struct { 525 name string 526 expectedErr string 527 doFinalize func(int64, *psbt.Packet, *PsbtIntent) error 528 }{ 529 { 530 name: "nil packet", 531 expectedErr: "PSBT is nil", 532 doFinalize: func(amt int64, p *psbt.Packet, 533 i *PsbtIntent) error { 534 535 return i.Finalize(nil) 536 }, 537 }, 538 { 539 name: "wrong state", 540 expectedErr: "invalid state. got user_canceled " + 541 "expected verified", 542 doFinalize: func(amt int64, p *psbt.Packet, 543 i *PsbtIntent) error { 544 545 i.State = PsbtInitiatorCanceled 546 return i.Finalize(p) 547 }, 548 }, 549 { 550 name: "not verified first", 551 expectedErr: "PSBT was not verified first", 552 doFinalize: func(amt int64, p *psbt.Packet, 553 i *PsbtIntent) error { 554 555 i.State = PsbtVerified 556 i.PendingPsbt = nil 557 return i.Finalize(p) 558 }, 559 }, 560 { 561 name: "output value changed", 562 expectedErr: "outputs differ from verified PSBT: " + 563 "output 0 is different", 564 doFinalize: func(amt int64, p *psbt.Packet, 565 i *PsbtIntent) error { 566 567 p.UnsignedTx.TxOut[0].Value = 123 568 return i.Finalize(p) 569 }, 570 }, 571 { 572 name: "output pk script changed", 573 expectedErr: "outputs differ from verified PSBT: " + 574 "output 0 is different", 575 doFinalize: func(amt int64, p *psbt.Packet, 576 i *PsbtIntent) error { 577 578 p.UnsignedTx.TxOut[0].PkScript = []byte{3, 2, 1} 579 return i.Finalize(p) 580 }, 581 }, 582 { 583 name: "input previous outpoint index changed", 584 expectedErr: "inputs differ from verified PSBT: " + 585 "previous outpoint of input 0 is different", 586 doFinalize: func(amt int64, p *psbt.Packet, 587 i *PsbtIntent) error { 588 589 p.UnsignedTx.TxIn[0].PreviousOutPoint.Index = 0 590 return i.Finalize(p) 591 }, 592 }, 593 { 594 name: "input previous outpoint hash changed", 595 expectedErr: "inputs differ from verified PSBT: " + 596 "previous outpoint of input 0 is different", 597 doFinalize: func(amt int64, p *psbt.Packet, 598 i *PsbtIntent) error { 599 600 prevout := &p.UnsignedTx.TxIn[0].PreviousOutPoint 601 prevout.Hash = chainhash.Hash{77, 88, 99, 11} 602 return i.Finalize(p) 603 }, 604 }, 605 { 606 name: "raw tx - nil transaction", 607 expectedErr: "raw transaction is nil", 608 doFinalize: func(amt int64, p *psbt.Packet, 609 i *PsbtIntent) error { 610 611 return i.FinalizeRawTX(nil) 612 }, 613 }, 614 { 615 name: "raw tx - no witness data in raw tx", 616 expectedErr: "inputs not signed: input 0 has no " + 617 "signature data attached", 618 doFinalize: func(amt int64, p *psbt.Packet, 619 i *PsbtIntent) error { 620 621 rawTx, err := psbt.Extract(p) 622 require.NoError(t, err) 623 rawTx.TxIn[0].SignatureScript = nil 624 625 return i.FinalizeRawTX(rawTx) 626 }, 627 }, 628 { 629 name: "happy path", 630 expectedErr: "", 631 doFinalize: func(amt int64, p *psbt.Packet, 632 i *PsbtIntent) error { 633 634 err := i.Finalize(p) 635 require.NoError(t, err) 636 637 require.Equal(t, PsbtFinalized, i.State) 638 require.NotNil(t, i.FinalTX) 639 640 return nil 641 }, 642 }, 643 } 644 645 // Create a simple assembler and ask it to provision a channel to get 646 // the funding intent. 647 a := NewPsbtAssembler(chanCapacity, nil, ¶ms, true) 648 intent, err := a.ProvisionChannel(&Request{LocalAmt: chanCapacity}) 649 if err != nil { 650 t.Fatalf("error provisioning channel: %v", err) 651 } 652 psbtIntent := intent.(*PsbtIntent) 653 654 // Bind our test keys to get the funding parameters. 655 _, localPubkey := privKeyFromBytes(localPrivkey) 656 _, remotePubkey := privKeyFromBytes(remotePrivkey) 657 psbtIntent.BindKeys( 658 &keychain.KeyDescriptor{PubKey: localPubkey}, remotePubkey, 659 ) 660 661 // Loop through all our test cases. 662 for _, tc := range testCases { 663 tc := tc 664 t.Run(tc.name, func(t *testing.T) { 665 // Reset the state from a previous test and create a new 666 // pending PSBT that we can manipulate. 667 psbtIntent.State = PsbtOutputKnown 668 _, amt, pendingPsbt, err := psbtIntent.FundingParams() 669 if err != nil { 670 t.Fatalf("unable to get funding params: %v", err) 671 } 672 673 // We need to have a simulated transaction here that is 674 // fully funded and signed. 675 pendingPsbt.UnsignedTx.TxIn = []*wire.TxIn{{ 676 PreviousOutPoint: wire.OutPoint{ 677 Index: 1, 678 Hash: chainhash.Hash{1, 2, 3}, 679 }, 680 }} 681 pendingPsbt.Inputs = []psbt.PInput{{ 682 WitnessUtxo: &wire.TxOut{ 683 Value: int64(chanCapacity) + 1, 684 PkScript: []byte{1, 2, 3}, 685 }, 686 FinalScriptWitness: []byte{0x01, 0x00}, 687 }} 688 err = psbtIntent.Verify(pendingPsbt, false) 689 if err != nil { 690 t.Fatalf("error verifying PSBT: %v", err) 691 } 692 693 // Deep clone the PSBT so we don't modify the pending 694 // one that was registered during Verify. 695 pendingPsbt = clonePsbt(t, pendingPsbt) 696 697 err = tc.doFinalize(amt, pendingPsbt, psbtIntent) 698 if (err == nil && tc.expectedErr != "") || 699 (err != nil && err.Error() != tc.expectedErr) { 700 701 t.Fatalf("unexpected error, got '%v' wanted "+ 702 "'%v'", err, tc.expectedErr) 703 } 704 }) 705 } 706 } 707 708 // clonePsbt creates a clone of a PSBT packet by serializing then de-serializing 709 // it. 710 func clonePsbt(t *testing.T, p *psbt.Packet) *psbt.Packet { 711 var buf bytes.Buffer 712 err := p.Serialize(&buf) 713 if err != nil { 714 t.Fatalf("error serializing PSBT: %v", err) 715 } 716 newPacket, err := psbt.NewFromRawBytes(&buf, false) 717 if err != nil { 718 t.Fatalf("error unserializing PSBT: %v", err) 719 } 720 return newPacket 721 }