|
| 1 | +//go:build rpctest |
| 2 | +// +build rpctest |
| 3 | + |
| 4 | +package integration |
| 5 | + |
| 6 | +import ( |
| 7 | + "testing" |
| 8 | + |
| 9 | + "github.com/btcsuite/btcd/btcutil" |
| 10 | + "github.com/btcsuite/btcd/chaincfg" |
| 11 | + "github.com/btcsuite/btcd/integration/rpctest" |
| 12 | + "github.com/btcsuite/btcd/txscript" |
| 13 | + "github.com/btcsuite/btcd/wire" |
| 14 | +) |
| 15 | + |
| 16 | +// TestPayToAnchorSimple tests creating and spending P2A outputs. |
| 17 | +func TestPayToAnchorSimple(t *testing.T) { |
| 18 | + t.Parallel() |
| 19 | + |
| 20 | + // Integration tests require a full harness setup which is |
| 21 | + // resource-intensive. |
| 22 | + if testing.Short() { |
| 23 | + t.Skip("Skipping P2A integration test in short mode") |
| 24 | + } |
| 25 | + |
| 26 | + // Create a btcd instance for testing P2A functionality in a controlled |
| 27 | + // environment. |
| 28 | + harness, err := rpctest.New( |
| 29 | + &chaincfg.SimNetParams, nil, nil, "", |
| 30 | + ) |
| 31 | + if err != nil { |
| 32 | + t.Fatalf("unable to create test harness: %v", err) |
| 33 | + } |
| 34 | + defer harness.TearDown() |
| 35 | + |
| 36 | + // Initialize the test harness with mining enabled to confirm |
| 37 | + // transactions. |
| 38 | + err = harness.SetUp(true, 25) |
| 39 | + if err != nil { |
| 40 | + t.Fatalf("unable to setup test harness: %v", err) |
| 41 | + } |
| 42 | + |
| 43 | + // Create a P2A output using the helper to get a P2A address. This |
| 44 | + // ensures we're using the same P2A script generation logic. |
| 45 | + p2aAddr, err := btcutil.NewAddressPayToAnchor(&chaincfg.SimNetParams) |
| 46 | + if err != nil { |
| 47 | + t.Fatalf("unable to create P2A address: %v", err) |
| 48 | + } |
| 49 | + |
| 50 | + // Use the harness to create a transaction that sends to the P2A |
| 51 | + // address. This handles all the UTXO selection and signing for us. |
| 52 | + amount := btcutil.Amount(10_000) |
| 53 | + createP2ATxHash, err := harness.SendOutputs([]*wire.TxOut{ |
| 54 | + wire.NewTxOut(int64(amount), p2aAddr.ScriptAddress()), |
| 55 | + }, 10) |
| 56 | + if err != nil { |
| 57 | + t.Fatalf("unable to send P2A creation transaction: %v", err) |
| 58 | + } |
| 59 | + |
| 60 | + // Mine a block to confirm the P2A creation. |
| 61 | + blockHashes, err := harness.Client.Generate(1) |
| 62 | + if err != nil { |
| 63 | + t.Fatalf("unable to generate block: %v", err) |
| 64 | + } |
| 65 | + if len(blockHashes) != 1 { |
| 66 | + t.Fatalf("expected 1 block hash, got %d", len(blockHashes)) |
| 67 | + } |
| 68 | + |
| 69 | + // Test spending the P2A output to verify it works as anyone-can-spend. |
| 70 | + spendP2ATx := wire.NewMsgTx(wire.TxVersion) |
| 71 | + |
| 72 | + // Reference the P2A output we just created. |
| 73 | + p2aOutpoint := wire.NewOutPoint(createP2ATxHash, 0) |
| 74 | + p2aInput := wire.NewTxIn(p2aOutpoint, nil, nil) |
| 75 | + |
| 76 | + // P2A outputs are designed to be spent without signatures for CPFP fee |
| 77 | + // bumping. The signature script is completely empty for P2A outputs. |
| 78 | + p2aInput.SignatureScript = []byte{} |
| 79 | + spendP2ATx.AddTxIn(p2aInput) |
| 80 | + |
| 81 | + // Send the P2A funds to a regular address. |
| 82 | + spendAddr, err := harness.NewAddress() |
| 83 | + if err != nil { |
| 84 | + t.Fatalf("unable to get spend address: %v", err) |
| 85 | + } |
| 86 | + spendScript, err := txscript.PayToAddrScript(spendAddr) |
| 87 | + if err != nil { |
| 88 | + t.Fatalf("unable to create spend script: %v", err) |
| 89 | + } |
| 90 | + |
| 91 | + // Deduct a small fee from the P2A output value. |
| 92 | + spendOut := wire.NewTxOut(int64(amount-100), spendScript) |
| 93 | + spendP2ATx.AddTxOut(spendOut) |
| 94 | + |
| 95 | + // Broadcast the spend transaction to verify network acceptance. P2A |
| 96 | + // outputs are witness programs and are validated through the normal |
| 97 | + // transaction validation path in the mempool and consensus. |
| 98 | + spendTxHash, err := harness.Client.SendRawTransaction(spendP2ATx, true) |
| 99 | + if err != nil { |
| 100 | + t.Fatalf("unable to send P2A spend transaction: %v", err) |
| 101 | + } |
| 102 | + |
| 103 | + // Mine a block to confirm the spend. |
| 104 | + blockHashes, err = harness.Client.Generate(1) |
| 105 | + if err != nil { |
| 106 | + t.Fatalf("unable to generate block after spend: %v", err) |
| 107 | + } |
| 108 | + |
| 109 | + // Ensure the spend transaction was actually mined to prove full P2A |
| 110 | + // support. |
| 111 | + // |
| 112 | + block, err := harness.Client.GetBlock(blockHashes[0]) |
| 113 | + if err != nil { |
| 114 | + t.Fatalf("unable to get block: %v", err) |
| 115 | + } |
| 116 | + |
| 117 | + // Confirm the spend transaction exists in the confirmed block. |
| 118 | + found := false |
| 119 | + for _, tx := range block.Transactions { |
| 120 | + txHash := tx.TxHash() |
| 121 | + if txHash.IsEqual(spendTxHash) { |
| 122 | + found = true |
| 123 | + break |
| 124 | + } |
| 125 | + } |
| 126 | + if !found { |
| 127 | + t.Errorf("P2A spend transaction not found in block") |
| 128 | + } |
| 129 | + |
| 130 | + t.Logf("Successfully created P2A output in tx %v and spent it in tx %v", |
| 131 | + createP2ATxHash, spendTxHash) |
| 132 | +} |
| 133 | + |
0 commit comments