tx_test.go raw

   1  package tx
   2  
   3  import (
   4  	"encoding/hex"
   5  	"errors"
   6  	"strings"
   7  	"testing"
   8  )
   9  
  10  // The vector is a transaction the rig node built with createrawtransaction: one input spending
  11  // output 3 of 11..11, one output paying 1000 sats to a P2WSH script, sequence fffffffd.
  12  var nodeHex = "02000000" + "01" + strings.Repeat("11", 32) + "03000000" + "00" +
  13  	"fdffffff" + "01" + "e803000000000000" + "22" + nodeScript + "00000000"
  14  
  15  const (
  16  	// nodeScript is the P2WSH scriptPubKey the node paid.
  17  	nodeScript = "0020640dce64eb09d134b9ea24c4665270ae70109ed5b4866f67ac7a80c8c91d6f96"
  18  )
  19  
  20  // nodeTx builds the vector.
  21  func nodeTx(t *testing.T) *Tx {
  22  	t.Helper()
  23  
  24  	script, err := hex.DecodeString(nodeScript)
  25  
  26  	if err != nil {
  27  		t.Fatal(err)
  28  	}
  29  
  30  	transaction := New()
  31  	transaction.Version = 2
  32  
  33  	var hash [32]byte
  34  
  35  	for index := range hash {
  36  		hash[index] = 0x11
  37  	}
  38  
  39  	transaction.AddInput(Outpoint{Hash: hash, Index: 3}, 0xfffffffd)
  40  	transaction.AddOutput(1000, script)
  41  
  42  	return transaction
  43  }
  44  
  45  // TestSerializeMatchesTheNode: the serialiser produces the bytes the node produced.
  46  func TestSerializeMatchesTheNode(t *testing.T) {
  47  	raw, err := nodeTx(t).Serialize()
  48  
  49  	if err != nil {
  50  		t.Fatal(err)
  51  	}
  52  
  53  	if got := hex.EncodeToString(raw); got != nodeHex {
  54  		t.Fatalf("the transaction is\n%s\nwant\n%s", got, nodeHex)
  55  	}
  56  }
  57  
  58  // TestHexIsTheSameBeforeSigning: with no witnesses, the wire form is the base form.
  59  func TestHexIsTheSameBeforeSigning(t *testing.T) {
  60  	hexText, err := nodeTx(t).Hex()
  61  
  62  	if err != nil {
  63  		t.Fatal(err)
  64  	}
  65  
  66  	if hexText != nodeHex {
  67  		t.Fatalf("the hex is\n%s\nwant\n%s", hexText, nodeHex)
  68  	}
  69  }
  70  
  71  // TestWitnessSerialisationAddsTheMarker: one witness changes the wire form and not the id.
  72  func TestWitnessSerialisationAddsTheMarker(t *testing.T) {
  73  	transaction := nodeTx(t)
  74  
  75  	before, err := transaction.TxIDHex()
  76  
  77  	if err != nil {
  78  		t.Fatal(err)
  79  	}
  80  
  81  	transaction.Inputs[0].Witness = [][]byte{{0x01}, {0x02, 0x03}}
  82  
  83  	after, err := transaction.TxIDHex()
  84  
  85  	if err != nil {
  86  		t.Fatal(err)
  87  	}
  88  
  89  	if before != after {
  90  		t.Fatalf("the id changed from %s to %s", before, after)
  91  	}
  92  
  93  	raw, err := transaction.SerializeWitness()
  94  
  95  	if err != nil {
  96  		t.Fatal(err)
  97  	}
  98  
  99  	text := hex.EncodeToString(raw)
 100  
 101  	if !strings.HasPrefix(text, "020000000001") {
 102  		t.Fatalf("the witness form starts %s", text[:12])
 103  	}
 104  
 105  	// count 2, item 0x01, item 0x0203, then the locktime.
 106  	if !strings.Contains(text, "020101020203"+"00000000") {
 107  		t.Fatalf("the witness section is not in %s", text)
 108  	}
 109  }
 110  
 111  // TestTxIDIsReversed: the id a person sees is the hash written backwards.
 112  func TestTxIDIsReversed(t *testing.T) {
 113  	transaction := nodeTx(t)
 114  
 115  	id, err := transaction.TxID()
 116  
 117  	if err != nil {
 118  		t.Fatal(err)
 119  	}
 120  
 121  	text, err := transaction.TxIDHex()
 122  
 123  	if err != nil {
 124  		t.Fatal(err)
 125  	}
 126  
 127  	if text[:2] != hex.EncodeToString(id[31:32]) {
 128  		t.Fatalf("the id %s does not start with the last byte of the hash", text)
 129  	}
 130  }
 131  
 132  // TestEmptyTransactionsAreRefused: nothing to spend or nothing to pay is not a transaction.
 133  func TestEmptyTransactionsAreRefused(t *testing.T) {
 134  	transaction := New()
 135  
 136  	if _, err := transaction.Serialize(); !errors.Is(err, ErrNoInputs) {
 137  		t.Fatalf("an input-less transaction serialised: %v", err)
 138  	}
 139  
 140  	transaction.AddInput(Outpoint{}, SequenceFinal)
 141  
 142  	if _, err := transaction.Serialize(); !errors.Is(err, ErrNoOutputs) {
 143  		t.Fatalf("an output-less transaction serialised: %v", err)
 144  	}
 145  }
 146