github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/portxo/portxo.go (about)

     1  package portxo
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  
     8  	"github.com/mit-dci/lit/wire"
     9  )
    10  
    11  type TxoMode uint8
    12  
    13  /* PorTxo specify a utxo, and all the information needed to spend it.
    14  The first 3 fields (Op, Amt, Mode) are required in all cases.
    15  If KeyGen.Depth != 0, that means no key path is supplied, and PrivKey
    16  is probably empty / ignored.  Having both a KeyGen and a PrivKey is redunant.
    17  Having neither KeyGen nor PrivKey means there's no private key, and no
    18  indication of how to get it; in that case get the private key from somewhere else.
    19  
    20  If BOTH KeyGen AND PrivKey are filled in, add em up!  Add the two private keys,
    21  modulo the curve order.
    22  
    23  PkScript can also be left empty depending on the mode.  Basically only script-hash
    24  modes need it, as the previous pkscript can be generated
    25  
    26  PreSigStack are data pushes that happen before the signature is pushed.
    27  
    28  I was thinking of putting PostSigStack as well but I think it makes more sense to
    29  always have the data before the sig, since there's pretty much only CHECKSIGVERIFY
    30  now and you might as well put the sig check last.
    31  
    32  Also makes sense that with a MAST or pay-to-script-merkle-root type of structure,
    33  you'd want a bunch stuff before the sig.
    34  
    35  */
    36  type PorTxo struct {
    37  	// got rid of NetID.  If you want to specify different networks / coins,
    38  	// use KeyGen.Step[1], that's what it's for.
    39  	// Heck, set KeyGen.Depth to 0 and still use Step[1] as the network / coin...
    40  	//	NetID  byte          // indicates what network / coin utxo is in
    41  	Op     wire.OutPoint `json:"outpoint"` // unique outpoint
    42  	Value  int64         `json:"val"`      // higher is better
    43  	Height int32         `json:"height"`   // block height of utxo (not needed? nice to know?)
    44  	Seq    uint32        `json:"seq"`      // used for relative timelock
    45  	Mode   TxoMode       `json:"mode"`     // what kind of output
    46  
    47  	KeyGen `json:"keygen"`
    48  
    49  	PkScript []byte `json:"privkeyscript"` // if empty, try to generate based on mode and priv key
    50  
    51  	PreSigStack [][]byte `json:"stack"` // items to push before the sig
    52  }
    53  
    54  // Constants defining txo modes
    55  const (
    56  	// Flags which combined can turn into full utxo modes
    57  	FlagTxoPubKeyHash   TxoMode = 0x01
    58  	FlagTxoScript       TxoMode = 0x02
    59  	FlagTxoWitness      TxoMode = 0x04
    60  	FlagTxoCompressed   TxoMode = 0x08
    61  	FlagTxoUncompressed TxoMode = 0x10
    62  
    63  	// fully specified tx output modes
    64  	// raw pubkey outputs (old school)
    65  	TxoP2PKUncomp = FlagTxoUncompressed
    66  	TxoP2PKComp   = FlagTxoCompressed
    67  
    68  	// pub key hash outputs, standard p2pkh (common)
    69  	TxoP2PKHUncomp = FlagTxoPubKeyHash | FlagTxoUncompressed
    70  	TxoP2PKHComp   = FlagTxoCompressed | FlagTxoPubKeyHash
    71  
    72  	// script hash
    73  	TxoP2SHUncomp = FlagTxoScript | FlagTxoUncompressed
    74  	TxoP2SHComp   = FlagTxoScript | FlagTxoCompressed
    75  
    76  	// witness p2wpkh modes
    77  	TxoP2WPKHUncomp = FlagTxoWitness | FlagTxoPubKeyHash | FlagTxoUncompressed
    78  	TxoP2WPKHComp   = FlagTxoWitness | FlagTxoPubKeyHash | FlagTxoCompressed
    79  
    80  	// witness script hash
    81  	TxoP2WSHUncomp = FlagTxoWitness | FlagTxoScript | FlagTxoUncompressed
    82  	TxoP2WSHComp   = FlagTxoWitness | FlagTxoScript | FlagTxoCompressed
    83  
    84  	// unknown
    85  	TxoUnknownMode = 0x80
    86  )
    87  
    88  var modeStrings = map[TxoMode]string{
    89  	TxoP2PKUncomp: "raw pubkey uncompressed",
    90  	TxoP2PKComp:   "raw pubkey compressed",
    91  
    92  	TxoP2PKHUncomp: "pubkey hash uncompressed",
    93  	TxoP2PKHComp:   "pubkey hash compressed",
    94  
    95  	TxoP2SHUncomp: "script hash uncompressed",
    96  	TxoP2SHComp:   "script hash compressed",
    97  
    98  	TxoP2WPKHUncomp: "witness pubkey hash uncompressed",
    99  	TxoP2WPKHComp:   "witness pubkey hash compressed",
   100  
   101  	TxoP2WSHUncomp: "witness script hash uncompressed",
   102  	TxoP2WSHComp:   "witness script hash compressed",
   103  }
   104  
   105  // String returns the InvType in human-readable form.
   106  func (m TxoMode) String() string {
   107  	s, ok := modeStrings[m]
   108  	if ok {
   109  		return s
   110  	}
   111  	return fmt.Sprintf("unknown TxoMode %x", uint8(m))
   112  }
   113  
   114  // Compare deep-compares two portable utxos, returning true if they're the same
   115  func (u *PorTxo) Equal(z *PorTxo) bool {
   116  	if u == nil || z == nil {
   117  		return false
   118  	}
   119  
   120  	if !u.Op.Hash.IsEqual(&z.Op.Hash) {
   121  		return false
   122  	}
   123  	if u.Op.Index != z.Op.Index {
   124  		return false
   125  	}
   126  	if u.Value != z.Value || u.Seq != z.Seq || u.Mode != z.Mode || u.Height != z.Height {
   127  		return false
   128  	}
   129  	if u.KeyGen.PrivKey != z.KeyGen.PrivKey {
   130  		return false
   131  	}
   132  	if !bytes.Equal(u.KeyGen.Bytes(), z.KeyGen.Bytes()) {
   133  		return false
   134  	}
   135  	if !bytes.Equal(u.PkScript, z.PkScript) {
   136  		return false
   137  	}
   138  
   139  	// compare pre sig stack lengths
   140  	if len(u.PreSigStack) != len(z.PreSigStack) {
   141  		return false
   142  	}
   143  
   144  	// if we're here, lengths for both are the same.  Iterate and compare stacks
   145  	for i, _ := range u.PreSigStack {
   146  		if !bytes.Equal(u.PreSigStack[i], z.PreSigStack[i]) {
   147  			return false
   148  		}
   149  	}
   150  
   151  	return true
   152  }
   153  
   154  func (u *PorTxo) String() string {
   155  	var s string
   156  	var empty [32]byte
   157  	if u == nil {
   158  		return "nil utxo"
   159  	}
   160  	s = u.Op.String()
   161  	s += fmt.Sprintf("\n\ta:%d h:%d seq:%d %s\n",
   162  		u.Value, u.Height, u.Seq, u.Mode.String())
   163  
   164  	if u.KeyGen.PrivKey == empty {
   165  		s += fmt.Sprintf("\tprivate key not available (zero)\n")
   166  	} else {
   167  		s += fmt.Sprintf("\tprivate key available (non-zero)\n")
   168  	}
   169  	if u.KeyGen.Depth == 0 || u.KeyGen.Depth > 5 {
   170  		s += fmt.Sprintf("\tno key derivation path\n")
   171  	} else {
   172  		s += fmt.Sprintf("%s\n", u.KeyGen.String())
   173  	}
   174  	s += fmt.Sprintf("\tPkScript (len %d): %x\n", len(u.PkScript), u.PkScript)
   175  
   176  	// list pre-sig elements
   177  	s += fmt.Sprintf("\t%d pre-sig elements:", len(u.PreSigStack))
   178  	for _, e := range u.PreSigStack {
   179  		s += fmt.Sprintf(" [%x]", e)
   180  	}
   181  	s += fmt.Sprintf("\n")
   182  
   183  	return s
   184  }
   185  
   186  /* serialized (im/ex)Portable Utxos are 106 up to 357 bytes.
   187  Op 36
   188  Amt 8
   189  Height 4
   190  Seq 4
   191  Mode 1
   192  
   193  Keygen 53
   194  
   195  PreStackNum (1 byte)
   196  	PreStackItemLen (1 byte)
   197  	PreStackItem (max 255 bytes each)
   198  PostStackNum (1 byte)
   199  	PostStackItemLen (1 byte)
   200  	PostStackItem (max 255 bytes each)
   201  PkScriptLen (1 byte)
   202  	PkScript (max 255 bytes)
   203  
   204  
   205  */
   206  
   207  func PorTxoFromBytes(b []byte) (*PorTxo, error) {
   208  	// should be max 1KiB for now
   209  	if len(b) < 106 || len(b) > 1024 {
   210  		return nil, fmt.Errorf("%d bytes, need 106-1024", len(b))
   211  	}
   212  
   213  	buf := bytes.NewBuffer(b)
   214  
   215  	var u PorTxo
   216  	var err error
   217  
   218  	err = u.Op.Hash.SetBytes(buf.Next(32))
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	err = binary.Read(buf, binary.BigEndian, &u.Op.Index)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  	err = binary.Read(buf, binary.BigEndian, &u.Value)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	err = binary.Read(buf, binary.BigEndian, &u.Height)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	err = binary.Read(buf, binary.BigEndian, &u.Seq)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	err = binary.Read(buf, binary.BigEndian, &u.Mode)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	var kgenarr [53]byte
   244  	copy(kgenarr[:], buf.Next(53))
   245  	u.KeyGen = KeyGenFromBytes(kgenarr)
   246  
   247  	// get PkScript length byte
   248  	PkScriptLen, err := buf.ReadByte()
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  	// make PkScript the right size to write into
   253  	u.PkScript = make([]byte, PkScriptLen)
   254  
   255  	// write from buffer into PkScript
   256  	_, err = buf.Read(u.PkScript)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	// get number of pre-sig stack items
   262  	PreSigStackNumItems, err := buf.ReadByte()
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  
   267  	u.PreSigStack = make([][]byte, PreSigStackNumItems)
   268  	// iterate through reading each presigStack item
   269  	for i, _ := range u.PreSigStack {
   270  		// read length of this element
   271  		elementLength, err := buf.ReadByte()
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  		// size element slice
   276  		u.PreSigStack[i] = make([]byte, elementLength)
   277  		// copy element data
   278  		_, err = buf.Read(u.PreSigStack[i])
   279  		if err != nil {
   280  			return nil, err
   281  		}
   282  	}
   283  
   284  	return &u, nil
   285  }
   286  
   287  // Spendable takes in the current block height, and returns whether
   288  // this utxo is spendable now or not.  It checks height (unconfirmed
   289  func (u *PorTxo) Mature(curHeight int32) bool {
   290  	// Nil portxo should be an error but just return false
   291  	if u == nil {
   292  		return false
   293  	}
   294  	//if sequence is nonzero, it's CSV locked, so check if it's either
   295  	// unconfirmed or confirmed at a height that's too shallow
   296  	if u.Seq > 1 &&
   297  		(u.Height < 100 || u.Height+int32(u.Seq) > curHeight) {
   298  		return false
   299  	}
   300  	return true
   301  }
   302  
   303  // EstSize returns an estimated vsize in bytes for using this portxo as
   304  // an input.  Might not be perfectly accurate.  Also maybe should switch
   305  // from vsize to weight..?  everything is vsize right now though.
   306  func (u *PorTxo) EstSize() int64 {
   307  	// Txins by mode:
   308  	// P2 PKH is op,seq (40) + pub(33) + sig(71) = 144
   309  	// P2 WPKH is op,seq(40) + [(33+71 / 4) = 26] = 66
   310  	// P2 WSH is op,seq(40) + [75(script) + 71]/4 (36) = 76
   311  	switch u.Mode {
   312  	case TxoP2PKHComp: // non witness is about 150 bytes
   313  		return 144
   314  	case TxoP2WPKHComp: // witness mode is around 66 vsize
   315  		return 66
   316  	case TxoP2WSHComp:
   317  		return 76
   318  	}
   319  	return 150 // guess that unknown is 150 bytes
   320  }
   321  
   322  func (u *PorTxo) Bytes() ([]byte, error) {
   323  	if u == nil {
   324  		return nil, fmt.Errorf("Can't serialize nil Utxo")
   325  	}
   326  
   327  	var buf bytes.Buffer
   328  
   329  	//	_, err := buf.Write(u.Op.Hash.CloneBytes())
   330  	_, err := buf.Write(u.Op.Hash.CloneBytes())
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	err = binary.Write(&buf, binary.BigEndian, u.Op.Index)
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	err = binary.Write(&buf, binary.BigEndian, u.Value)
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  	err = binary.Write(&buf, binary.BigEndian, u.Height)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  	err = binary.Write(&buf, binary.BigEndian, u.Seq)
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  	err = binary.Write(&buf, binary.BigEndian, u.Mode) // mode
   353  	if err != nil {
   354  		return nil, err
   355  	}
   356  	_, err = buf.Write(u.KeyGen.Bytes()) // keypath
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  
   361  	// check pkScript length
   362  	if len(u.PkScript) > 255 {
   363  		return nil, fmt.Errorf("PkScript too long (255 byte max)")
   364  	}
   365  	// write length of pkScript
   366  	err = buf.WriteByte(uint8(len(u.PkScript)))
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	// write PkScript
   371  	_, err = buf.Write(u.PkScript)
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  
   376  	// check Pre-sig stack number of elements
   377  	if len(u.PreSigStack) > 255 {
   378  		return nil, fmt.Errorf("Too many PreSigStack items (255 items max)")
   379  	}
   380  	// write number of PreSigStack items
   381  	err = buf.WriteByte(uint8(len(u.PreSigStack)))
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  	// iterate through PreSigStack items and write each
   386  	for i, element := range u.PreSigStack {
   387  		// check element length
   388  		if len(element) > 255 {
   389  			return nil, fmt.Errorf("PreSigStack item %d %d bytes (255 max)",
   390  				i, len(element))
   391  		}
   392  		// write length of element
   393  		err = buf.WriteByte(uint8(len(element)))
   394  		if err != nil {
   395  			return nil, err
   396  		}
   397  		// write element itself
   398  		_, err = buf.Write(element)
   399  		if err != nil {
   400  			return nil, err
   401  		}
   402  	}
   403  
   404  	return buf.Bytes(), nil
   405  }