package tx import ( "encoding/hex" "errors" "strings" "testing" ) // The vector is a transaction the rig node built with createrawtransaction: one input spending // output 3 of 11..11, one output paying 1000 sats to a P2WSH script, sequence fffffffd. var nodeHex = "02000000" + "01" + strings.Repeat("11", 32) + "03000000" + "00" + "fdffffff" + "01" + "e803000000000000" + "22" + nodeScript + "00000000" const ( // nodeScript is the P2WSH scriptPubKey the node paid. nodeScript = "0020640dce64eb09d134b9ea24c4665270ae70109ed5b4866f67ac7a80c8c91d6f96" ) // nodeTx builds the vector. func nodeTx(t *testing.T) *Tx { t.Helper() script, err := hex.DecodeString(nodeScript) if err != nil { t.Fatal(err) } transaction := New() transaction.Version = 2 var hash [32]byte for index := range hash { hash[index] = 0x11 } transaction.AddInput(Outpoint{Hash: hash, Index: 3}, 0xfffffffd) transaction.AddOutput(1000, script) return transaction } // TestSerializeMatchesTheNode: the serialiser produces the bytes the node produced. func TestSerializeMatchesTheNode(t *testing.T) { raw, err := nodeTx(t).Serialize() if err != nil { t.Fatal(err) } if got := hex.EncodeToString(raw); got != nodeHex { t.Fatalf("the transaction is\n%s\nwant\n%s", got, nodeHex) } } // TestHexIsTheSameBeforeSigning: with no witnesses, the wire form is the base form. func TestHexIsTheSameBeforeSigning(t *testing.T) { hexText, err := nodeTx(t).Hex() if err != nil { t.Fatal(err) } if hexText != nodeHex { t.Fatalf("the hex is\n%s\nwant\n%s", hexText, nodeHex) } } // TestWitnessSerialisationAddsTheMarker: one witness changes the wire form and not the id. func TestWitnessSerialisationAddsTheMarker(t *testing.T) { transaction := nodeTx(t) before, err := transaction.TxIDHex() if err != nil { t.Fatal(err) } transaction.Inputs[0].Witness = [][]byte{{0x01}, {0x02, 0x03}} after, err := transaction.TxIDHex() if err != nil { t.Fatal(err) } if before != after { t.Fatalf("the id changed from %s to %s", before, after) } raw, err := transaction.SerializeWitness() if err != nil { t.Fatal(err) } text := hex.EncodeToString(raw) if !strings.HasPrefix(text, "020000000001") { t.Fatalf("the witness form starts %s", text[:12]) } // count 2, item 0x01, item 0x0203, then the locktime. if !strings.Contains(text, "020101020203"+"00000000") { t.Fatalf("the witness section is not in %s", text) } } // TestTxIDIsReversed: the id a person sees is the hash written backwards. func TestTxIDIsReversed(t *testing.T) { transaction := nodeTx(t) id, err := transaction.TxID() if err != nil { t.Fatal(err) } text, err := transaction.TxIDHex() if err != nil { t.Fatal(err) } if text[:2] != hex.EncodeToString(id[31:32]) { t.Fatalf("the id %s does not start with the last byte of the hash", text) } } // TestEmptyTransactionsAreRefused: nothing to spend or nothing to pay is not a transaction. func TestEmptyTransactionsAreRefused(t *testing.T) { transaction := New() if _, err := transaction.Serialize(); !errors.Is(err, ErrNoInputs) { t.Fatalf("an input-less transaction serialised: %v", err) } transaction.AddInput(Outpoint{}, SequenceFinal) if _, err := transaction.Serialize(); !errors.Is(err, ErrNoOutputs) { t.Fatalf("an output-less transaction serialised: %v", err) } }