fixture_test.go raw

   1  package tx
   2  
   3  import (
   4  	"encoding/hex"
   5  	"encoding/json"
   6  	"os"
   7  	"strings"
   8  	"testing"
   9  )
  10  
  11  // bip143Fixture is a spend the rig node signed: an unsigned transaction, the prevout it
  12  // spends, and the finished signed transaction. It is the end-to-end anchor for this package —
  13  // the serialiser has to rebuild the unsigned bytes exactly, and the signed form has to be the
  14  // one the node produced for those bytes.
  15  type bip143Fixture struct {
  16  	Secret       string `json:"secret"`
  17  	Pubkey       string `json:"pubkey"`
  18  	ScriptPubKey string `json:"scriptPubKey"`
  19  	Sequence     uint32 `json:"sequence"`
  20  	Prevout      struct {
  21  		TxID   string `json:"txid"`
  22  		Vout   uint32 `json:"vout"`
  23  		Amount uint64 `json:"amount"`
  24  	} `json:"prevout"`
  25  	Unsigned string `json:"unsigned"`
  26  	Signed   string `json:"signed"`
  27  	Complete bool   `json:"complete"`
  28  }
  29  
  30  // loadFixture reads it.
  31  func loadFixture(t *testing.T) bip143Fixture {
  32  	t.Helper()
  33  
  34  	raw, err := os.ReadFile("testdata/bip143.json")
  35  
  36  	if err != nil {
  37  		t.Fatal(err)
  38  	}
  39  
  40  	var fixture bip143Fixture
  41  
  42  	if err := json.Unmarshal(raw, &fixture); err != nil {
  43  		t.Fatal(err)
  44  	}
  45  
  46  	return fixture
  47  }
  48  
  49  // TestTheFixtureRebuilds: the package rebuilds the node's unsigned transaction byte for byte
  50  // from the fields a wallet holds, which is what makes the fixture usable for the sighash.
  51  func TestTheFixtureRebuilds(t *testing.T) {
  52  	fixture := loadFixture(t)
  53  
  54  	hash, err := hex.DecodeString(fixture.Prevout.TxID)
  55  
  56  	if err != nil {
  57  		t.Fatal(err)
  58  	}
  59  
  60  	var outpoint [32]byte
  61  	copy(outpoint[:], hash)
  62  
  63  	payScript, err := hex.DecodeString(fixture.PayScript(t))
  64  
  65  	if err != nil {
  66  		t.Fatal(err)
  67  	}
  68  
  69  	transaction := New()
  70  	transaction.Version = 2
  71  	transaction.AddInput(Outpoint{Hash: outpoint, Index: fixture.Prevout.Vout}, fixture.Sequence)
  72  	transaction.AddOutput(50000, payScript)
  73  
  74  	raw, err := transaction.Serialize()
  75  
  76  	if err != nil {
  77  		t.Fatal(err)
  78  	}
  79  
  80  	if got := hex.EncodeToString(raw); got != fixture.Unsigned {
  81  		t.Fatalf("rebuilt\n%s\nwant\n%s", got, fixture.Unsigned)
  82  	}
  83  }
  84  
  85  // PayScript reads the output script out of the fixture's own unsigned transaction, so the test
  86  // does not need a second copy of it.
  87  func (f bip143Fixture) PayScript(t *testing.T) string {
  88  	t.Helper()
  89  
  90  	raw, err := hex.DecodeString(f.Unsigned)
  91  
  92  	if err != nil {
  93  		t.Fatal(err)
  94  	}
  95  
  96  	// The output script is the last varint-prefixed field before the locktime; for this
  97  	// fixture it is a 22-byte P2WPKH script, so it is the 22 bytes that follow the 0x16 length
  98  	// in the output section.
  99  	index := strings.LastIndex(f.Unsigned, "160014")
 100  
 101  	if index < 0 {
 102  		t.Fatal("the fixture has no P2WPKH output")
 103  	}
 104  
 105  	start := index / 2
 106  
 107  	return hex.EncodeToString(raw[start+1 : start+23])
 108  }
 109  
 110  // TestTheSignedFormIsPinned: the node's signed transaction has the marker, the flag, and this
 111  // key in its witness.
 112  func TestTheSignedFormIsPinned(t *testing.T) {
 113  	fixture := loadFixture(t)
 114  
 115  	if !fixture.Complete {
 116  		t.Fatal("the fixture is not a complete signature")
 117  	}
 118  
 119  	if !strings.HasPrefix(fixture.Signed, "020000000001") {
 120  		t.Fatalf("the signed form starts %s", fixture.Signed[:12])
 121  	}
 122  
 123  	if !strings.Contains(fixture.Signed, fixture.Pubkey) {
 124  		t.Fatal("the witness does not carry the signing key")
 125  	}
 126  }
 127