d17b11862b
With this change we move one step closer to matching the wire protocol currently defined within the spec.
784 lines
18 KiB
Go
784 lines
18 KiB
Go
package lnwire
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"github.com/go-errors/errors"
|
|
"github.com/roasbeef/btcd/btcec"
|
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
|
"github.com/roasbeef/btcd/txscript"
|
|
"github.com/roasbeef/btcd/wire"
|
|
"github.com/roasbeef/btcutil"
|
|
)
|
|
|
|
// MaxSliceLength is the maximum allowed lenth for any opaque byte slices in
|
|
// the wire protocol.
|
|
const MaxSliceLength = 65535
|
|
|
|
// PkScript is simple type definition which represents a raw serialized public
|
|
// key script.
|
|
type PkScript []byte
|
|
|
|
// CreditsAmount are the native currency unit used within the Lightning Network.
|
|
// Credits are denominated in sub-satoshi amounts, so micro-satoshis (1/1000).
|
|
// This value is purposefully signed in order to allow the expression of negative
|
|
// fees.
|
|
//
|
|
// "In any science-fiction movie, anywhere in the galaxy, currency is referred
|
|
// to as 'credits.'"
|
|
// --Sam Humphries. Ebert, Roger (1999). Ebert's bigger little movie
|
|
// glossary. Andrews McMeel. p. 172.
|
|
//
|
|
// https://en.wikipedia.org/wiki/List_of_fictional_currencies
|
|
// https://en.wikipedia.org/wiki/Fictional_currency#Trends_in_the_use_of_fictional_currencies
|
|
// http://tvtropes.org/pmwiki/pmwiki.php/Main/WeWillSpendCreditsInTheFuture
|
|
// US Display format: 1 BTC = 100,000,000'000 XCB
|
|
// Or in BTC = 1.00000000'000
|
|
// Credits (XCB, accountants should use XCB :^)
|
|
type CreditsAmount int64
|
|
|
|
// ToSatoshi converts an amount in Credits to the coresponding amount
|
|
// expressed in Satoshis.
|
|
//
|
|
// NOTE: This function rounds down by default (floor).
|
|
func (c CreditsAmount) ToSatoshi() int64 {
|
|
return int64(c / 1000)
|
|
}
|
|
|
|
// addressType specifies the network protocol and version that should be used
|
|
// when connecting to a node at a particular address.
|
|
type addressType uint8
|
|
|
|
const (
|
|
tcp4Addr addressType = 1
|
|
tcp6Addr addressType = 2
|
|
onionAddr addressType = 3
|
|
)
|
|
|
|
// writeElement is a one-stop shop to write the big endian representation of
|
|
// any element which is to be serialized for the wire protocol. The passed
|
|
// io.Writer should be backed by an appropriatly sized byte slice, or be able
|
|
// to dynamically expand to accomdate additional data.
|
|
//
|
|
// TODO(roasbeef): this should eventually draw from a buffer pool for
|
|
// serialization.
|
|
// TODO(roasbeef): switch to var-ints for all?
|
|
func writeElement(w io.Writer, element interface{}) error {
|
|
switch e := element.(type) {
|
|
case uint8:
|
|
var b [1]byte
|
|
b[0] = e
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case uint16:
|
|
var b [2]byte
|
|
binary.BigEndian.PutUint16(b[:], e)
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case ErrorCode:
|
|
var b [2]byte
|
|
binary.BigEndian.PutUint16(b[:], uint16(e))
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case btcutil.Amount:
|
|
var b [8]byte
|
|
binary.BigEndian.PutUint64(b[:], uint64(e))
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case uint32:
|
|
var b [4]byte
|
|
binary.BigEndian.PutUint32(b[:], e)
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case uint64:
|
|
var b [8]byte
|
|
binary.BigEndian.PutUint64(b[:], e)
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case *btcec.PublicKey:
|
|
var b [33]byte
|
|
serializedPubkey := e.SerializeCompressed()
|
|
copy(b[:], serializedPubkey)
|
|
// TODO(roasbeef): use WriteVarBytes here?
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case []uint64:
|
|
// Enforce a max number of elements in a uint64 slice.
|
|
numItems := len(e)
|
|
if numItems > 65535 {
|
|
return fmt.Errorf("Too many []uint64s")
|
|
}
|
|
|
|
// First write out the the number of elements in the slice as a
|
|
// length prefix.
|
|
if err := writeElement(w, uint16(numItems)); err != nil {
|
|
return err
|
|
}
|
|
|
|
// After the prefix detailing the number of elements, write out
|
|
// each uint64 in series.
|
|
for i := 0; i < numItems; i++ {
|
|
if err := writeElement(w, e[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case []*btcec.Signature:
|
|
// Enforce a sane number for the maximum number of signatures.
|
|
numSigs := len(e)
|
|
if numSigs > 127 {
|
|
return fmt.Errorf("too many signatures")
|
|
}
|
|
|
|
// First write out the the number of elements in the slice as a
|
|
// length prefix.
|
|
if err := writeElement(w, uint8(numSigs)); err != nil {
|
|
return err
|
|
}
|
|
|
|
// After the prefix detailing the number of elements, write out
|
|
// each signature in series.
|
|
for i := 0; i < numSigs; i++ {
|
|
if err := writeElement(w, e[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case *btcec.Signature:
|
|
var b [64]byte
|
|
err := serializeSigToWire(&b, e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Write buffer
|
|
if _, err = w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case *chainhash.Hash:
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
return err
|
|
}
|
|
case wire.BitcoinNet:
|
|
var b [4]byte
|
|
binary.BigEndian.PutUint32(b[:], uint32(e))
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
return err
|
|
}
|
|
case ErrorData:
|
|
var l [2]byte
|
|
binary.BigEndian.PutUint16(l[:], uint16(len(e)))
|
|
if _, err := w.Write(l[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
return err
|
|
}
|
|
case OpaqueReason:
|
|
var l [2]byte
|
|
binary.BigEndian.PutUint16(l[:], uint16(len(e)))
|
|
if _, err := w.Write(l[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
return err
|
|
}
|
|
case [6]byte:
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
return err
|
|
}
|
|
case []byte:
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
return err
|
|
}
|
|
case PkScript:
|
|
// Make sure it's P2PKH or P2SH size or less.
|
|
scriptLength := len(e)
|
|
if scriptLength > 25 {
|
|
return fmt.Errorf("'PkScript' too long")
|
|
}
|
|
|
|
if err := wire.WriteVarBytes(w, 0, e); err != nil {
|
|
return err
|
|
}
|
|
case string:
|
|
strlen := len(e)
|
|
if strlen > MaxSliceLength {
|
|
return fmt.Errorf("string too long")
|
|
}
|
|
|
|
if err := wire.WriteVarString(w, 0, e); err != nil {
|
|
return err
|
|
}
|
|
case []*wire.TxIn:
|
|
// Write the size (1-byte)
|
|
if len(e) > 127 {
|
|
return fmt.Errorf("Too many txins")
|
|
}
|
|
|
|
// Write out the number of txins.
|
|
if err := writeElement(w, uint8(len(e))); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Append the actual TxIns (Size: NumOfTxins * 36)
|
|
// During serialization we leave out the sequence number to
|
|
// eliminate any funny business.
|
|
for _, in := range e {
|
|
if err := writeElement(w, in); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case *wire.TxIn:
|
|
// First write out the previous txid.
|
|
var h [32]byte
|
|
copy(h[:], e.PreviousOutPoint.Hash[:])
|
|
if _, err := w.Write(h[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Then the exact index of the previous out point.
|
|
var idx [4]byte
|
|
binary.BigEndian.PutUint32(idx[:], e.PreviousOutPoint.Index)
|
|
if _, err := w.Write(idx[:]); err != nil {
|
|
return err
|
|
}
|
|
case *FeatureVector:
|
|
if err := e.Encode(w); err != nil {
|
|
return err
|
|
}
|
|
|
|
case wire.OutPoint:
|
|
var h [32]byte
|
|
copy(h[:], e.Hash[:])
|
|
if _, err := w.Write(h[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
var idx [2]byte
|
|
binary.BigEndian.PutUint16(idx[:], uint16(e.Index))
|
|
if _, err := w.Write(idx[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
case int64, float64:
|
|
err := binary.Write(w, binary.BigEndian, e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
case ChannelID:
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
case ShortChannelID:
|
|
// Check that field fit in 3 bytes and write the blockHeight
|
|
if e.BlockHeight > ((1 << 24) - 1) {
|
|
return errors.New("block height should fit in 3 bytes")
|
|
}
|
|
|
|
var blockHeight [4]byte
|
|
binary.BigEndian.PutUint32(blockHeight[:], e.BlockHeight)
|
|
|
|
if _, err := w.Write(blockHeight[1:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check that field fit in 3 bytes and write the txIndex
|
|
if e.TxIndex > ((1 << 24) - 1) {
|
|
return errors.New("tx index should fit in 3 bytes")
|
|
}
|
|
|
|
var txIndex [4]byte
|
|
binary.BigEndian.PutUint32(txIndex[:], e.TxIndex)
|
|
if _, err := w.Write(txIndex[1:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write the txPosition
|
|
var txPosition [2]byte
|
|
binary.BigEndian.PutUint16(txPosition[:], e.TxPosition)
|
|
if _, err := w.Write(txPosition[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
case *net.TCPAddr:
|
|
if e.IP.To4() != nil {
|
|
var descriptor [1]byte
|
|
descriptor[0] = uint8(tcp4Addr)
|
|
if _, err := w.Write(descriptor[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
var ip [4]byte
|
|
copy(ip[:], e.IP.To4())
|
|
if _, err := w.Write(ip[:]); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
var descriptor [1]byte
|
|
descriptor[0] = uint8(tcp6Addr)
|
|
if _, err := w.Write(descriptor[:]); err != nil {
|
|
return err
|
|
}
|
|
var ip [16]byte
|
|
copy(ip[:], e.IP.To16())
|
|
if _, err := w.Write(ip[:]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
var port [2]byte
|
|
binary.BigEndian.PutUint16(port[:], uint16(e.Port))
|
|
if _, err := w.Write(port[:]); err != nil {
|
|
return err
|
|
}
|
|
case []net.Addr:
|
|
// Write out the number of addresses.
|
|
if err := writeElement(w, uint16(len(e))); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Append the actual addresses.
|
|
for _, address := range e {
|
|
if err := writeElement(w, address); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case RGB:
|
|
err := writeElements(w,
|
|
e.red,
|
|
e.green,
|
|
e.blue,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case Alias:
|
|
if err := writeElements(w, e.data[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
default:
|
|
return fmt.Errorf("Unknown type in writeElement: %T", e)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// writeElements is writes each element in the elements slice to the passed
|
|
// io.Writer using writeElement.
|
|
func writeElements(w io.Writer, elements ...interface{}) error {
|
|
for _, element := range elements {
|
|
err := writeElement(w, element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// readElement is a one-stop utility function to deserialize any datastructure
|
|
// encoded using the serialization format of lnwire.
|
|
func readElement(r io.Reader, element interface{}) error {
|
|
var err error
|
|
switch e := element.(type) {
|
|
case *uint8:
|
|
var b [1]uint8
|
|
if _, err := r.Read(b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = b[0]
|
|
case *uint16:
|
|
var b [2]byte
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = binary.BigEndian.Uint16(b[:])
|
|
case *ErrorCode:
|
|
var b [2]byte
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = ErrorCode(binary.BigEndian.Uint16(b[:]))
|
|
case *uint32:
|
|
var b [4]byte
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = binary.BigEndian.Uint32(b[:])
|
|
case *uint64:
|
|
var b [8]byte
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = binary.BigEndian.Uint64(b[:])
|
|
case *btcutil.Amount:
|
|
var b [8]byte
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
|
|
case **chainhash.Hash:
|
|
var b chainhash.Hash
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = &b
|
|
case **btcec.PublicKey:
|
|
var b [btcec.PubKeyBytesLenCompressed]byte
|
|
if _, err = io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
pubKey, err := btcec.ParsePubKey(b[:], btcec.S256())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = pubKey
|
|
case **FeatureVector:
|
|
f, err := NewFeatureVectorFromReader(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*e = f
|
|
|
|
case *[]uint64:
|
|
var numItems uint16
|
|
if err := readElement(r, &numItems); err != nil {
|
|
return err
|
|
}
|
|
// if numItems > 65535 {
|
|
// return fmt.Errorf("Too many items in []uint64")
|
|
// }
|
|
|
|
// Read the number of items
|
|
var items []uint64
|
|
for i := uint16(0); i < numItems; i++ {
|
|
var item uint64
|
|
err = readElement(r, &item)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
*e = items
|
|
case *[]*btcec.Signature:
|
|
var numSigs uint8
|
|
err = readElement(r, &numSigs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if numSigs > 127 {
|
|
return fmt.Errorf("too many signatures")
|
|
}
|
|
|
|
// Read that number of signatures
|
|
var sigs []*btcec.Signature
|
|
for i := uint8(0); i < numSigs; i++ {
|
|
sig := new(btcec.Signature)
|
|
err = readElement(r, &sig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sigs = append(sigs, sig)
|
|
}
|
|
*e = sigs
|
|
return nil
|
|
case **btcec.Signature:
|
|
var b [64]byte
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
err = deserializeSigFromWire(e, b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case *wire.BitcoinNet:
|
|
var b [4]byte
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
return err
|
|
}
|
|
*e = wire.BitcoinNet(binary.BigEndian.Uint32(b[:]))
|
|
return nil
|
|
case *OpaqueReason:
|
|
var l [2]byte
|
|
if _, err := io.ReadFull(r, l[:]); err != nil {
|
|
return err
|
|
}
|
|
reasonLen := binary.BigEndian.Uint16(l[:])
|
|
|
|
*e = OpaqueReason(make([]byte, reasonLen))
|
|
if _, err := io.ReadFull(r, *e); err != nil {
|
|
return err
|
|
}
|
|
case *ErrorData:
|
|
var l [2]byte
|
|
if _, err := io.ReadFull(r, l[:]); err != nil {
|
|
return err
|
|
}
|
|
errorLen := binary.BigEndian.Uint16(l[:])
|
|
|
|
*e = ErrorData(make([]byte, errorLen))
|
|
if _, err := io.ReadFull(r, *e); err != nil {
|
|
return err
|
|
}
|
|
case *[6]byte:
|
|
if _, err := io.ReadFull(r, e[:]); err != nil {
|
|
return err
|
|
}
|
|
case []byte:
|
|
if _, err := io.ReadFull(r, e); err != nil {
|
|
return err
|
|
}
|
|
case *PkScript:
|
|
pkScript, err := wire.ReadVarBytes(r, 0, 25, "pkscript")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = pkScript
|
|
case *string:
|
|
str, err := wire.ReadVarString(r, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = str
|
|
case *[]*wire.TxIn:
|
|
// Read the size (1-byte number of txins)
|
|
var numScripts uint8
|
|
if err := readElement(r, &numScripts); err != nil {
|
|
return err
|
|
}
|
|
if numScripts > 127 {
|
|
return fmt.Errorf("Too many txins")
|
|
}
|
|
|
|
// Append the actual TxIns
|
|
txins := make([]*wire.TxIn, 0, numScripts)
|
|
for i := uint8(0); i < numScripts; i++ {
|
|
outpoint := new(wire.OutPoint)
|
|
txin := wire.NewTxIn(outpoint, nil, nil)
|
|
if err := readElement(r, &txin); err != nil {
|
|
return err
|
|
}
|
|
txins = append(txins, txin)
|
|
}
|
|
*e = txins
|
|
case **wire.TxIn:
|
|
// Hash
|
|
var h [32]byte
|
|
if _, err = io.ReadFull(r, h[:]); err != nil {
|
|
return err
|
|
}
|
|
hash, err := chainhash.NewHash(h[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
(*e).PreviousOutPoint.Hash = *hash
|
|
|
|
// Index
|
|
var idxBytes [4]byte
|
|
_, err = io.ReadFull(r, idxBytes[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
(*e).PreviousOutPoint.Index = binary.BigEndian.Uint32(idxBytes[:])
|
|
return nil
|
|
case *wire.OutPoint:
|
|
var h [32]byte
|
|
if _, err = io.ReadFull(r, h[:]); err != nil {
|
|
return err
|
|
}
|
|
hash, err := chainhash.NewHash(h[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var idxBytes [2]byte
|
|
_, err = io.ReadFull(r, idxBytes[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
index := binary.BigEndian.Uint16(idxBytes[:])
|
|
|
|
*e = wire.OutPoint{
|
|
Hash: *hash,
|
|
Index: uint32(index),
|
|
}
|
|
case *int64, *float64:
|
|
err := binary.Read(r, binary.BigEndian, e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
case *ChannelID:
|
|
if _, err := io.ReadFull(r, e[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
case *ShortChannelID:
|
|
var blockHeight [4]byte
|
|
if _, err = io.ReadFull(r, blockHeight[1:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
var txIndex [4]byte
|
|
if _, err = io.ReadFull(r, txIndex[1:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
var txPosition [2]byte
|
|
if _, err = io.ReadFull(r, txPosition[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
*e = ShortChannelID{
|
|
BlockHeight: binary.BigEndian.Uint32(blockHeight[:]),
|
|
TxIndex: binary.BigEndian.Uint32(txIndex[:]),
|
|
TxPosition: binary.BigEndian.Uint16(txPosition[:]),
|
|
}
|
|
|
|
case *[]net.Addr:
|
|
var numAddrsBytes [2]byte
|
|
if _, err = io.ReadFull(r, numAddrsBytes[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
numAddrs := binary.BigEndian.Uint16(numAddrsBytes[:])
|
|
addresses := make([]net.Addr, 0, numAddrs)
|
|
|
|
for i := 0; i < int(numAddrs); i++ {
|
|
var descriptor [1]byte
|
|
if _, err = io.ReadFull(r, descriptor[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
address := &net.TCPAddr{}
|
|
switch descriptor[0] {
|
|
case 1:
|
|
var ip [4]byte
|
|
if _, err = io.ReadFull(r, ip[:]); err != nil {
|
|
return err
|
|
}
|
|
address.IP = (net.IP)(ip[:])
|
|
case 2:
|
|
var ip [16]byte
|
|
if _, err = io.ReadFull(r, ip[:]); err != nil {
|
|
return err
|
|
}
|
|
address.IP = (net.IP)(ip[:])
|
|
}
|
|
|
|
var port [2]byte
|
|
if _, err = io.ReadFull(r, port[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
address.Port = int(binary.BigEndian.Uint16(port[:]))
|
|
addresses = append(addresses, address)
|
|
}
|
|
*e = addresses
|
|
case *RGB:
|
|
err := readElements(r,
|
|
&e.red,
|
|
&e.green,
|
|
&e.blue,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case *Alias:
|
|
var a [32]byte
|
|
if err := readElements(r, a[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
*e, err = newAlias(a[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("Unknown type in readElement: %T", e)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// readElements deserializes a variable number of elements into the passed
|
|
// io.Reader, with each element being deserialized according to the readElement
|
|
// function.
|
|
func readElements(r io.Reader, elements ...interface{}) error {
|
|
for _, element := range elements {
|
|
err := readElement(r, element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// validatePkScript determines if the passed pkScript is a valid pkScript within
|
|
// lnwire. The only pkScript templates that lnwire currently allows are:
|
|
// P2SH, P2WSH, P2PKH, and P2WKH.
|
|
func isValidPkScript(pkScript PkScript) bool {
|
|
// A nil pkScript is obviously invalid.
|
|
if pkScript == nil {
|
|
return false
|
|
}
|
|
|
|
switch len(pkScript) {
|
|
case 25:
|
|
// A valid p2pkh script must be exactly 25 bytes. It must begin
|
|
// with the define prefix, and end with the define suffix.
|
|
p2pkhPrefix := []byte{txscript.OP_DUP, txscript.OP_HASH160}
|
|
p2pkhSuffix := []byte{txscript.OP_EQUALVERIFY, txscript.OP_CHECKSIG,
|
|
txscript.OP_DATA_20}
|
|
if !bytes.Equal(pkScript[0:3], p2pkhPrefix) ||
|
|
!bytes.Equal(pkScript[23:25], p2pkhSuffix) {
|
|
return false
|
|
}
|
|
case 22:
|
|
// P2WKH
|
|
// A valid P2WKH script must be exactly 22 bytes, with the first
|
|
// two op codes being an OP_0 marking a version zero witness
|
|
// program, and the second byte being a 20 byte push data.
|
|
if pkScript[0] != txscript.OP_0 ||
|
|
pkScript[1] != txscript.OP_DATA_20 {
|
|
return false
|
|
}
|
|
case 23:
|
|
// A valid P2SH script must begin with OP_HASH160 PUSHDATA(20),
|
|
// contain 20 bytes, then end with an OP_EQUAL.
|
|
p2shPrefix := []byte{txscript.OP_HASH160, txscript.OP_DATA_20}
|
|
p2shSuffix := []byte{txscript.OP_EQUAL}
|
|
if !bytes.Equal(pkScript[0:2], p2shPrefix) ||
|
|
!bytes.Equal(pkScript[22:23], p2shSuffix) {
|
|
return false
|
|
}
|
|
case 34:
|
|
// A P2WSH script must be exactly 34 bytes, with the first two
|
|
// op codes being an OP_0 marking a version zero witness program,
|
|
// and the second byte being a 32 byte push data.
|
|
if pkScript[0] != txscript.OP_0 ||
|
|
pkScript[1] != txscript.OP_DATA_32 {
|
|
return false
|
|
}
|
|
default:
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|