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

     1  package lnutil
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  
     8  	"github.com/mit-dci/lit/btcutil"
     9  	"github.com/mit-dci/lit/btcutil/blockchain"
    10  	"github.com/mit-dci/lit/btcutil/txscript"
    11  	"github.com/mit-dci/lit/crypto/fastsha256"
    12  	"github.com/mit-dci/lit/wire"
    13  )
    14  
    15  // TxAndHeight is just a tx, and the height at which it was confirmed.
    16  type TxAndHeight struct {
    17  	Tx     *wire.MsgTx
    18  	Height int32
    19  }
    20  
    21  // OutPointEvent is a message describing events concerning an outpoint.
    22  // There's 2 event types: confirmation and spend.  If the Tx pointer is nil,
    23  // then it's a confirm.  If the Tx has an actual MsgTx in there, it's a spend.
    24  // The Height refers to either the confirmation height
    25  // or the height at which it was spent. (0 means seen but unconfirmed)
    26  type OutPointEvent struct {
    27  	Op     wire.OutPoint // the outpoint being described
    28  	Height int32         // the height of the event
    29  	Tx     *wire.MsgTx   // the tx spending the outpoint
    30  }
    31  
    32  // HeightEvent is to inform the LN node of a new blockheight on a particular
    33  // coin type. Used to detect and enforce HTLC timeouts
    34  type HeightEvent struct {
    35  	Height   int32
    36  	CoinType uint32
    37  }
    38  
    39  // need this because before I was comparing pointers maybe?
    40  // so they were the same outpoint but stored in 2 places so false negative?
    41  func OutPointsEqual(a, b wire.OutPoint) bool {
    42  	if !a.Hash.IsEqual(&b.Hash) {
    43  		return false
    44  	}
    45  	return a.Index == b.Index
    46  }
    47  
    48  /*----- serialization for tx outputs ------- */
    49  
    50  // outPointToBytes turns an outpoint into 36 bytes.
    51  func OutPointToBytes(op wire.OutPoint) (b [36]byte) {
    52  	var buf bytes.Buffer
    53  	_, err := buf.Write(op.Hash.CloneBytes())
    54  	if err != nil {
    55  		return
    56  	}
    57  	// write 4 byte outpoint index within the tx to spend
    58  	err = binary.Write(&buf, binary.BigEndian, op.Index)
    59  	if err != nil {
    60  		return
    61  	}
    62  	copy(b[:], buf.Bytes())
    63  
    64  	return
    65  }
    66  
    67  // OutPointFromBytes gives you an outpoint from 36 bytes.
    68  // since 36 is enforced, it doesn't error
    69  func OutPointFromBytes(b [36]byte) *wire.OutPoint {
    70  	op := new(wire.OutPoint)
    71  	_ = op.Hash.SetBytes(b[:32])
    72  	op.Index = BtU32(b[32:])
    73  	return op
    74  }
    75  
    76  // P2WSHify takes a script and turns it into a 34 byte long P2WSH PkScript
    77  func P2WSHify(scriptBytes []byte) []byte {
    78  	bldr := txscript.NewScriptBuilder()
    79  	bldr.AddOp(txscript.OP_0)
    80  	wsh := fastsha256.Sum256(scriptBytes)
    81  	bldr.AddData(wsh[:])
    82  	b, _ := bldr.Script() // ignore script errors
    83  	return b
    84  }
    85  
    86  func DirectWPKHScript(pub [33]byte) []byte {
    87  	builder := txscript.NewScriptBuilder()
    88  	builder.AddOp(txscript.OP_0).AddData(btcutil.Hash160(pub[:]))
    89  	b, _ := builder.Script()
    90  	return b
    91  }
    92  
    93  func DirectWPKHScriptFromPKH(pkh [20]byte) []byte {
    94  	builder := txscript.NewScriptBuilder()
    95  	builder.AddOp(txscript.OP_0).AddData(pkh[:])
    96  	b, _ := builder.Script()
    97  	return b
    98  }
    99  
   100  // KeyHashFromPkScript extracts the 20 or 32 byte hash from a txout PkScript
   101  func KeyHashFromPkScript(pkscript []byte) []byte {
   102  	// match p2pkh
   103  	if len(pkscript) == 25 && pkscript[0] == 0x76 && pkscript[1] == 0xa9 &&
   104  		pkscript[2] == 0x14 && pkscript[23] == 0x88 && pkscript[24] == 0xac {
   105  		return pkscript[3:23]
   106  	}
   107  
   108  	// match p2wpkh
   109  	if len(pkscript) == 22 && pkscript[0] == 0x00 && pkscript[1] == 0x14 {
   110  		return pkscript[2:]
   111  	}
   112  
   113  	// match p2wsh
   114  	if len(pkscript) == 34 && pkscript[0] == 0x00 && pkscript[1] == 0x20 {
   115  		return pkscript[2:]
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  // Not exported in txscript... so we copy and paste here
   122  func PayToPubKeyHashScript(pubKeyHash []byte) ([]byte, error) {
   123  	return txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).
   124  		AddOp(txscript.OP_HASH160).AddData(pubKeyHash).
   125  		AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script()
   126  }
   127  
   128  // TxToString prints out some info about a transaction. for testing / debugging
   129  func TxToString(tx *wire.MsgTx) string {
   130  	utx := btcutil.NewTx(tx)
   131  	str := fmt.Sprintf("size %d vsize %d wsize %d locktime %d wit: %t txid %s\n",
   132  		tx.SerializeSizeStripped(), blockchain.GetTxVirtualSize(utx),
   133  		tx.SerializeSize(), tx.LockTime, tx.HasWitness(), tx.TxHash().String())
   134  	for i, in := range tx.TxIn {
   135  		str += fmt.Sprintf("Input %d spends %s seq %d\n",
   136  			i, in.PreviousOutPoint.String(), in.Sequence)
   137  		str += fmt.Sprintf("\tSigScript: %x\n", in.SignatureScript)
   138  		for j, wit := range in.Witness {
   139  			str += fmt.Sprintf("\twitness %d: %x\n", j, wit)
   140  		}
   141  	}
   142  	for i, out := range tx.TxOut {
   143  		if out != nil {
   144  			str += fmt.Sprintf("output %d script: %x amt: %d\n",
   145  				i, out.PkScript, out.Value)
   146  		} else {
   147  			str += fmt.Sprintf("output %d nil (WARNING)\n", i)
   148  		}
   149  	}
   150  	return str
   151  }