2015-12-23 09:08:34 +01:00
|
|
|
package lnwire
|
|
|
|
|
|
|
|
import (
|
2017-09-19 02:27:37 +02:00
|
|
|
"bytes"
|
2015-12-23 09:08:34 +01:00
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
2017-12-03 03:23:05 +01:00
|
|
|
"image/color"
|
2016-05-23 22:50:36 +02:00
|
|
|
"io"
|
2017-07-26 05:39:59 +02:00
|
|
|
"math"
|
2016-12-24 23:55:31 +01:00
|
|
|
"net"
|
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2018-06-05 03:34:16 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
2016-12-07 16:46:22 +01:00
|
|
|
"github.com/go-errors/errors"
|
2018-04-25 19:22:47 +02:00
|
|
|
"github.com/lightningnetwork/lnd/tor"
|
2015-12-23 09:08:34 +01:00
|
|
|
)
|
|
|
|
|
2020-01-28 02:25:01 +01:00
|
|
|
const (
|
|
|
|
// MaxSliceLength is the maximum allowed length for any opaque byte
|
|
|
|
// slices in the wire protocol.
|
|
|
|
MaxSliceLength = 65535
|
|
|
|
|
|
|
|
// MaxMsgBody is the largest payload any message is allowed to provide.
|
|
|
|
// This is two less than the MaxSliceLength as each message has a 2
|
|
|
|
// byte type that precedes the message body.
|
|
|
|
MaxMsgBody = 65533
|
|
|
|
)
|
2016-01-05 17:19:22 +01:00
|
|
|
|
2016-05-23 22:50:36 +02:00
|
|
|
// PkScript is simple type definition which represents a raw serialized public
|
|
|
|
// key script.
|
2015-12-23 09:08:34 +01:00
|
|
|
type PkScript []byte
|
|
|
|
|
2017-02-17 10:29:23 +01:00
|
|
|
// addressType specifies the network protocol and version that should be used
|
|
|
|
// when connecting to a node at a particular address.
|
|
|
|
type addressType uint8
|
|
|
|
|
|
|
|
const (
|
2017-09-19 02:27:37 +02:00
|
|
|
// noAddr denotes a blank address. An address of this type indicates
|
2018-01-30 01:07:26 +01:00
|
|
|
// that a node doesn't have any advertised addresses.
|
2017-09-19 02:27:37 +02:00
|
|
|
noAddr addressType = 0
|
|
|
|
|
|
|
|
// tcp4Addr denotes an IPv4 TCP address.
|
|
|
|
tcp4Addr addressType = 1
|
|
|
|
|
2018-01-30 02:31:48 +01:00
|
|
|
// tcp6Addr denotes an IPv6 TCP address.
|
2017-09-19 02:27:37 +02:00
|
|
|
tcp6Addr addressType = 2
|
|
|
|
|
|
|
|
// v2OnionAddr denotes a version 2 Tor onion service address.
|
|
|
|
v2OnionAddr addressType = 3
|
|
|
|
|
2018-04-25 19:22:47 +02:00
|
|
|
// v3OnionAddr denotes a version 3 Tor (prop224) onion service address.
|
2017-09-19 02:27:37 +02:00
|
|
|
v3OnionAddr addressType = 4
|
2017-02-17 10:29:23 +01:00
|
|
|
)
|
|
|
|
|
2017-09-19 02:27:37 +02:00
|
|
|
// AddrLen returns the number of bytes that it takes to encode the target
|
|
|
|
// address.
|
|
|
|
func (a addressType) AddrLen() uint16 {
|
|
|
|
switch a {
|
|
|
|
case noAddr:
|
|
|
|
return 0
|
|
|
|
case tcp4Addr:
|
|
|
|
return 6
|
|
|
|
case tcp6Addr:
|
|
|
|
return 18
|
|
|
|
case v2OnionAddr:
|
|
|
|
return 12
|
|
|
|
case v3OnionAddr:
|
|
|
|
return 37
|
|
|
|
default:
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-10 03:27:41 +01:00
|
|
|
// WriteElement is a one-stop shop to write the big endian representation of
|
2021-06-17 08:17:30 +02:00
|
|
|
// any element which is to be serialized for the wire protocol.
|
2021-06-18 08:40:59 +02:00
|
|
|
//
|
|
|
|
// TODO(yy): rm this method once we finish dereferencing it from other
|
|
|
|
// packages.
|
2021-06-17 08:17:30 +02:00
|
|
|
func WriteElement(w *bytes.Buffer, element interface{}) error {
|
2015-12-23 09:08:34 +01:00
|
|
|
switch e := element.(type) {
|
2019-01-04 05:52:03 +01:00
|
|
|
case NodeAlias:
|
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-17 03:47:53 +02:00
|
|
|
case ShortChanIDEncoding:
|
|
|
|
var b [1]byte
|
|
|
|
b[0] = uint8(e)
|
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-23 09:08:34 +01:00
|
|
|
case uint8:
|
|
|
|
var b [1]byte
|
2017-02-23 20:07:01 +01:00
|
|
|
b[0] = e
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
2015-12-23 09:08:34 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-11-09 20:59:30 +01:00
|
|
|
case FundingFlag:
|
|
|
|
var b [1]byte
|
|
|
|
b[0] = uint8(e)
|
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-01-05 17:19:22 +01:00
|
|
|
case uint16:
|
|
|
|
var b [2]byte
|
2017-02-23 20:07:01 +01:00
|
|
|
binary.BigEndian.PutUint16(b[:], e)
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
2016-01-05 17:19:22 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-01-12 18:59:43 +01:00
|
|
|
case ChanUpdateMsgFlags:
|
|
|
|
var b [1]byte
|
|
|
|
b[0] = uint8(e)
|
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case ChanUpdateChanFlags:
|
|
|
|
var b [1]byte
|
|
|
|
b[0] = uint8(e)
|
2017-12-01 07:21:27 +01:00
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-22 07:28:24 +02:00
|
|
|
case MilliSatoshi:
|
|
|
|
var b [8]byte
|
|
|
|
binary.BigEndian.PutUint64(b[:], uint64(e))
|
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-02-16 13:25:36 +01:00
|
|
|
case btcutil.Amount:
|
|
|
|
var b [8]byte
|
|
|
|
binary.BigEndian.PutUint64(b[:], uint64(e))
|
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
2016-01-05 17:19:22 +01:00
|
|
|
return err
|
|
|
|
}
|
2015-12-23 09:08:34 +01:00
|
|
|
case uint32:
|
|
|
|
var b [4]byte
|
2017-02-23 20:07:01 +01:00
|
|
|
binary.BigEndian.PutUint32(b[:], e)
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
2015-12-23 09:08:34 +01:00
|
|
|
return err
|
|
|
|
}
|
2015-12-30 14:38:57 +01:00
|
|
|
case uint64:
|
|
|
|
var b [8]byte
|
2017-02-23 20:07:01 +01:00
|
|
|
binary.BigEndian.PutUint64(b[:], e)
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
2015-12-30 14:38:57 +01:00
|
|
|
return err
|
|
|
|
}
|
2015-12-23 09:08:34 +01:00
|
|
|
case *btcec.PublicKey:
|
2017-08-22 07:28:24 +02:00
|
|
|
if e == nil {
|
2017-08-22 10:00:07 +02:00
|
|
|
return fmt.Errorf("cannot write nil pubkey")
|
2017-08-22 07:28:24 +02:00
|
|
|
}
|
|
|
|
|
2015-12-23 09:08:34 +01:00
|
|
|
var b [33]byte
|
|
|
|
serializedPubkey := e.SerializeCompressed()
|
|
|
|
copy(b[:], serializedPubkey)
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
2015-12-23 09:08:34 +01:00
|
|
|
return err
|
|
|
|
}
|
2018-01-31 04:41:52 +01:00
|
|
|
case []Sig:
|
2017-07-29 01:26:06 +02:00
|
|
|
var b [2]byte
|
|
|
|
numSigs := uint16(len(e))
|
|
|
|
binary.BigEndian.PutUint16(b[:], numSigs)
|
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, sig := range e {
|
2018-12-10 03:27:41 +01:00
|
|
|
if err := WriteElement(w, sig); err != nil {
|
2017-07-29 01:26:06 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-01-31 04:41:52 +01:00
|
|
|
case Sig:
|
2016-12-08 21:56:37 +01:00
|
|
|
// Write buffer
|
2018-01-31 04:41:52 +01:00
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
2015-12-30 14:38:57 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-04-17 03:11:39 +02:00
|
|
|
case PingPayload:
|
|
|
|
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 PongPayload:
|
|
|
|
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
|
|
|
|
}
|
2017-04-17 00:23:40 +02:00
|
|
|
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
|
|
|
|
}
|
2017-02-21 07:02:42 +01:00
|
|
|
case OpaqueReason:
|
|
|
|
var l [2]byte
|
|
|
|
binary.BigEndian.PutUint16(l[:], uint16(len(e)))
|
|
|
|
if _, err := w.Write(l[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-31 04:53:49 +01:00
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case [33]byte:
|
2017-02-21 07:02:42 +01:00
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-01-05 17:19:22 +01:00
|
|
|
case []byte:
|
2017-02-16 13:25:36 +01:00
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
2016-05-31 00:42:53 +02:00
|
|
|
return err
|
|
|
|
}
|
2015-12-23 09:08:34 +01:00
|
|
|
case PkScript:
|
2017-04-20 01:04:38 +02:00
|
|
|
// The largest script we'll accept is a p2wsh which is exactly
|
|
|
|
// 34 bytes long.
|
2015-12-23 09:08:34 +01:00
|
|
|
scriptLength := len(e)
|
2017-04-20 01:04:38 +02:00
|
|
|
if scriptLength > 34 {
|
2017-02-23 20:56:47 +01:00
|
|
|
return fmt.Errorf("'PkScript' too long")
|
2015-12-23 09:08:34 +01:00
|
|
|
}
|
2016-05-23 22:51:21 +02:00
|
|
|
|
|
|
|
if err := wire.WriteVarBytes(w, 0, e); err != nil {
|
2015-12-23 09:08:34 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-10-11 20:37:54 +02:00
|
|
|
case *RawFeatureVector:
|
2017-08-22 07:28:24 +02:00
|
|
|
if e == nil {
|
|
|
|
return fmt.Errorf("cannot write nil feature vector")
|
|
|
|
}
|
|
|
|
|
2017-02-16 13:31:19 +01:00
|
|
|
if err := e.Encode(w); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-02-16 13:34:44 +01:00
|
|
|
|
|
|
|
case wire.OutPoint:
|
2017-07-26 05:39:59 +02:00
|
|
|
var h [32]byte
|
|
|
|
copy(h[:], e.Hash[:])
|
|
|
|
if _, err := w.Write(h[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Index > math.MaxUint16 {
|
|
|
|
return fmt.Errorf("index for outpoint (%v) is "+
|
|
|
|
"greater than max index of %v", e.Index,
|
|
|
|
math.MaxUint16)
|
|
|
|
}
|
|
|
|
|
|
|
|
var idx [2]byte
|
|
|
|
binary.BigEndian.PutUint16(idx[:], uint16(e.Index))
|
|
|
|
if _, err := w.Write(idx[:]); err != nil {
|
2016-05-31 00:42:53 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-04-17 00:22:20 +02:00
|
|
|
|
|
|
|
case ChannelID:
|
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-06-28 17:22:23 +02:00
|
|
|
case FailCode:
|
2018-12-10 03:27:41 +01:00
|
|
|
if err := WriteElement(w, uint16(e)); err != nil {
|
2017-06-28 17:22:23 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-03-27 17:22:37 +02:00
|
|
|
case ShortChannelID:
|
2016-12-07 16:46:22 +01:00
|
|
|
// 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:
|
2017-08-22 07:28:24 +02:00
|
|
|
if e == nil {
|
|
|
|
return fmt.Errorf("cannot write nil TCPAddr")
|
|
|
|
}
|
|
|
|
|
2017-02-17 10:29:23 +01:00
|
|
|
if e.IP.To4() != nil {
|
|
|
|
var descriptor [1]byte
|
|
|
|
descriptor[0] = uint8(tcp4Addr)
|
|
|
|
if _, err := w.Write(descriptor[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-12-07 16:46:22 +01:00
|
|
|
|
2017-02-17 10:29:23 +01:00
|
|
|
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))
|
2016-12-07 16:46:22 +01:00
|
|
|
if _, err := w.Write(port[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
|
2018-04-25 19:22:47 +02:00
|
|
|
case *tor.OnionAddr:
|
|
|
|
if e == nil {
|
|
|
|
return errors.New("cannot write nil onion address")
|
|
|
|
}
|
|
|
|
|
|
|
|
var suffixIndex int
|
|
|
|
switch len(e.OnionService) {
|
|
|
|
case tor.V2Len:
|
|
|
|
descriptor := []byte{byte(v2OnionAddr)}
|
|
|
|
if _, err := w.Write(descriptor); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
suffixIndex = tor.V2Len - tor.OnionSuffixLen
|
|
|
|
case tor.V3Len:
|
|
|
|
descriptor := []byte{byte(v3OnionAddr)}
|
|
|
|
if _, err := w.Write(descriptor); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
suffixIndex = tor.V3Len - tor.OnionSuffixLen
|
|
|
|
default:
|
|
|
|
return errors.New("unknown onion service length")
|
|
|
|
}
|
|
|
|
|
|
|
|
host, err := tor.Base32Encoding.DecodeString(
|
|
|
|
e.OnionService[:suffixIndex],
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := w.Write(host); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var port [2]byte
|
|
|
|
binary.BigEndian.PutUint16(port[:], uint16(e.Port))
|
|
|
|
if _, err := w.Write(port[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-17 10:29:23 +01:00
|
|
|
case []net.Addr:
|
2017-09-19 02:27:37 +02:00
|
|
|
// First, we'll encode all the addresses into an intermediate
|
|
|
|
// buffer. We need to do this in order to compute the total
|
|
|
|
// length of the addresses.
|
|
|
|
var addrBuf bytes.Buffer
|
|
|
|
for _, address := range e {
|
2018-12-10 03:27:41 +01:00
|
|
|
if err := WriteElement(&addrBuf, address); err != nil {
|
2017-09-19 02:27:37 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the addresses fully encoded, we can now write out the
|
|
|
|
// number of bytes needed to encode them.
|
|
|
|
addrLen := addrBuf.Len()
|
2018-12-10 03:27:41 +01:00
|
|
|
if err := WriteElement(w, uint16(addrLen)); err != nil {
|
2017-02-17 10:29:23 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-19 02:27:37 +02:00
|
|
|
// Finally, we'll write out the raw addresses themselves, but
|
|
|
|
// only if we have any bytes to write.
|
|
|
|
if addrLen > 0 {
|
|
|
|
if _, err := w.Write(addrBuf.Bytes()); err != nil {
|
2017-02-17 10:29:23 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-12-03 03:23:05 +01:00
|
|
|
case color.RGBA:
|
2018-12-10 03:27:41 +01:00
|
|
|
if err := WriteElements(w, e.R, e.G, e.B); err != nil {
|
2016-12-07 16:46:22 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
|
2017-03-09 00:32:11 +01:00
|
|
|
case DeliveryAddress:
|
|
|
|
var length [2]byte
|
|
|
|
binary.BigEndian.PutUint16(length[:], uint16(len(e)))
|
|
|
|
if _, err := w.Write(length[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := w.Write(e[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-12-07 16:46:22 +01:00
|
|
|
|
2019-03-11 00:28:18 +01:00
|
|
|
case bool:
|
|
|
|
var b [1]byte
|
|
|
|
if e {
|
|
|
|
b[0] = 1
|
|
|
|
}
|
|
|
|
if _, err := w.Write(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-28 02:25:01 +01:00
|
|
|
|
|
|
|
case ExtraOpaqueData:
|
|
|
|
return e.Encode(w)
|
|
|
|
|
2015-12-23 09:08:34 +01:00
|
|
|
default:
|
2020-04-14 19:56:05 +02:00
|
|
|
return fmt.Errorf("unknown type in WriteElement: %T", e)
|
2015-12-23 09:08:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-10 03:27:41 +01:00
|
|
|
// WriteElements is writes each element in the elements slice to the passed
|
2021-06-17 08:17:30 +02:00
|
|
|
// buffer using WriteElement.
|
2021-06-18 08:40:59 +02:00
|
|
|
//
|
|
|
|
// TODO(yy): rm this method once we finish dereferencing it from other
|
|
|
|
// packages.
|
2021-06-17 08:17:30 +02:00
|
|
|
func WriteElements(buf *bytes.Buffer, elements ...interface{}) error {
|
2015-12-28 12:24:16 +01:00
|
|
|
for _, element := range elements {
|
2021-06-17 08:17:30 +02:00
|
|
|
err := WriteElement(buf, element)
|
2015-12-28 12:24:16 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-10 03:27:41 +01:00
|
|
|
// ReadElement is a one-stop utility function to deserialize any datastructure
|
2016-05-23 22:51:21 +02:00
|
|
|
// encoded using the serialization format of lnwire.
|
2018-12-10 03:27:41 +01:00
|
|
|
func ReadElement(r io.Reader, element interface{}) error {
|
2015-12-23 09:08:34 +01:00
|
|
|
var err error
|
|
|
|
switch e := element.(type) {
|
2019-03-11 00:28:18 +01:00
|
|
|
case *bool:
|
|
|
|
var b [1]byte
|
|
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if b[0] == 1 {
|
|
|
|
*e = true
|
|
|
|
}
|
|
|
|
|
2019-01-04 05:52:03 +01:00
|
|
|
case *NodeAlias:
|
|
|
|
var a [32]byte
|
|
|
|
if _, err := io.ReadFull(r, a[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
alias, err := NewNodeAlias(string(a[:]))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*e = alias
|
2018-04-17 03:47:53 +02:00
|
|
|
case *ShortChanIDEncoding:
|
|
|
|
var b [1]uint8
|
|
|
|
if _, err := r.Read(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = ShortChanIDEncoding(b[0])
|
2015-12-27 03:20:25 +01:00
|
|
|
case *uint8:
|
2015-12-27 08:52:20 +01:00
|
|
|
var b [1]uint8
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := r.Read(b[:]); err != nil {
|
2015-12-27 03:20:25 +01:00
|
|
|
return err
|
|
|
|
}
|
2015-12-27 08:52:20 +01:00
|
|
|
*e = b[0]
|
2017-11-09 20:59:30 +01:00
|
|
|
case *FundingFlag:
|
|
|
|
var b [1]uint8
|
|
|
|
if _, err := r.Read(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = FundingFlag(b[0])
|
2016-01-05 17:19:22 +01:00
|
|
|
case *uint16:
|
|
|
|
var b [2]byte
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
2016-01-05 17:19:22 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = binary.BigEndian.Uint16(b[:])
|
2019-01-12 18:59:43 +01:00
|
|
|
case *ChanUpdateMsgFlags:
|
|
|
|
var b [1]uint8
|
|
|
|
if _, err := r.Read(b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = ChanUpdateMsgFlags(b[0])
|
|
|
|
case *ChanUpdateChanFlags:
|
|
|
|
var b [1]uint8
|
|
|
|
if _, err := r.Read(b[:]); err != nil {
|
2017-12-01 07:21:27 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-01-12 18:59:43 +01:00
|
|
|
*e = ChanUpdateChanFlags(b[0])
|
2015-12-23 09:08:34 +01:00
|
|
|
case *uint32:
|
|
|
|
var b [4]byte
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
2015-12-23 09:08:34 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = binary.BigEndian.Uint32(b[:])
|
2015-12-30 14:38:57 +01:00
|
|
|
case *uint64:
|
|
|
|
var b [8]byte
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
2015-12-30 14:38:57 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = binary.BigEndian.Uint64(b[:])
|
2017-08-22 07:28:24 +02:00
|
|
|
case *MilliSatoshi:
|
|
|
|
var b [8]byte
|
|
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = MilliSatoshi(int64(binary.BigEndian.Uint64(b[:])))
|
2015-12-27 03:20:25 +01:00
|
|
|
case *btcutil.Amount:
|
|
|
|
var b [8]byte
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err := io.ReadFull(r, b[:]); err != nil {
|
2015-12-27 03:20:25 +01:00
|
|
|
return err
|
|
|
|
}
|
2015-12-27 08:52:20 +01:00
|
|
|
*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
|
|
|
|
case **btcec.PublicKey:
|
2016-12-07 16:46:22 +01:00
|
|
|
var b [btcec.PubKeyBytesLenCompressed]byte
|
2016-05-23 22:51:21 +02:00
|
|
|
if _, err = io.ReadFull(r, b[:]); err != nil {
|
2015-12-27 03:20:25 +01:00
|
|
|
return err
|
|
|
|
}
|
2016-05-23 22:51:21 +02:00
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
pubKey, err := btcec.ParsePubKey(b[:])
|
2015-12-30 14:38:57 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-05-23 22:51:21 +02:00
|
|
|
*e = pubKey
|
2017-10-11 20:37:54 +02:00
|
|
|
case **RawFeatureVector:
|
|
|
|
f := NewRawFeatureVector()
|
|
|
|
err = f.Decode(r)
|
2017-02-16 13:31:19 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*e = f
|
|
|
|
|
2018-01-31 04:41:52 +01:00
|
|
|
case *[]Sig:
|
2017-07-29 01:26:06 +02:00
|
|
|
var l [2]byte
|
|
|
|
if _, err := io.ReadFull(r, l[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
numSigs := binary.BigEndian.Uint16(l[:])
|
|
|
|
|
2018-01-31 04:41:52 +01:00
|
|
|
var sigs []Sig
|
2017-07-29 01:26:06 +02:00
|
|
|
if numSigs > 0 {
|
2018-01-31 04:41:52 +01:00
|
|
|
sigs = make([]Sig, numSigs)
|
2017-07-29 01:26:06 +02:00
|
|
|
for i := 0; i < int(numSigs); i++ {
|
2018-12-10 03:27:41 +01:00
|
|
|
if err := ReadElement(r, &sigs[i]); err != nil {
|
2017-07-29 01:26:06 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*e = sigs
|
|
|
|
|
2018-01-31 04:41:52 +01:00
|
|
|
case *Sig:
|
|
|
|
if _, err := io.ReadFull(r, e[:]); err != nil {
|
2015-12-30 14:38:57 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-02-21 07:02:42 +01:00
|
|
|
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
|
|
|
|
}
|
2017-04-17 00:23:40 +02:00
|
|
|
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
|
|
|
|
}
|
2017-04-17 03:11:39 +02:00
|
|
|
case *PingPayload:
|
|
|
|
var l [2]byte
|
|
|
|
if _, err := io.ReadFull(r, l[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pingLen := binary.BigEndian.Uint16(l[:])
|
|
|
|
|
|
|
|
*e = PingPayload(make([]byte, pingLen))
|
|
|
|
if _, err := io.ReadFull(r, *e); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case *PongPayload:
|
|
|
|
var l [2]byte
|
|
|
|
if _, err := io.ReadFull(r, l[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pongLen := binary.BigEndian.Uint16(l[:])
|
|
|
|
|
|
|
|
*e = PongPayload(make([]byte, pongLen))
|
|
|
|
if _, err := io.ReadFull(r, *e); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-31 04:53:49 +01:00
|
|
|
case *[33]byte:
|
|
|
|
if _, err := io.ReadFull(r, e[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-02-16 13:25:36 +01:00
|
|
|
case []byte:
|
|
|
|
if _, err := io.ReadFull(r, e); err != nil {
|
2016-01-05 17:19:22 +01:00
|
|
|
return err
|
|
|
|
}
|
2015-12-27 03:20:25 +01:00
|
|
|
case *PkScript:
|
2017-04-20 01:04:38 +02:00
|
|
|
pkScript, err := wire.ReadVarBytes(r, 0, 34, "pkscript")
|
2015-12-27 03:20:25 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-05-23 22:51:21 +02:00
|
|
|
*e = pkScript
|
2017-02-16 13:34:44 +01:00
|
|
|
case *wire.OutPoint:
|
2017-07-26 05:39:59 +02:00
|
|
|
var h [32]byte
|
|
|
|
if _, err = io.ReadFull(r, h[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
hash, err := chainhash.NewHash(h[:])
|
|
|
|
if err != nil {
|
2017-06-28 17:22:23 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-04-17 00:22:20 +02:00
|
|
|
|
2017-07-26 05:39:59 +02:00
|
|
|
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 *FailCode:
|
2018-12-10 03:27:41 +01:00
|
|
|
if err := ReadElement(r, (*uint16)(e)); err != nil {
|
2017-07-26 05:39:59 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-04-17 00:22:20 +02:00
|
|
|
case *ChannelID:
|
|
|
|
if _, err := io.ReadFull(r, e[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-03-27 17:22:37 +02:00
|
|
|
case *ShortChannelID:
|
2016-12-07 16:46:22 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-03-27 17:22:37 +02:00
|
|
|
*e = ShortChannelID{
|
2016-12-07 16:46:22 +01:00
|
|
|
BlockHeight: binary.BigEndian.Uint32(blockHeight[:]),
|
|
|
|
TxIndex: binary.BigEndian.Uint32(txIndex[:]),
|
|
|
|
TxPosition: binary.BigEndian.Uint16(txPosition[:]),
|
|
|
|
}
|
|
|
|
|
2017-02-17 10:29:23 +01:00
|
|
|
case *[]net.Addr:
|
2017-09-19 02:27:37 +02:00
|
|
|
// First, we'll read the number of total bytes that have been
|
|
|
|
// used to encode the set of addresses.
|
2017-03-30 01:55:28 +02:00
|
|
|
var numAddrsBytes [2]byte
|
|
|
|
if _, err = io.ReadFull(r, numAddrsBytes[:]); err != nil {
|
2016-12-07 16:46:22 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
addrsLen := binary.BigEndian.Uint16(numAddrsBytes[:])
|
2016-12-07 16:46:22 +01:00
|
|
|
|
2017-09-19 02:27:37 +02:00
|
|
|
// With the number of addresses, read, we'll now pull in the
|
|
|
|
// buffer of the encoded addresses into memory.
|
|
|
|
addrs := make([]byte, addrsLen)
|
|
|
|
if _, err := io.ReadFull(r, addrs[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
addrBuf := bytes.NewReader(addrs)
|
2017-03-30 01:55:28 +02:00
|
|
|
|
2017-09-19 02:27:37 +02:00
|
|
|
// Finally, we'll parse the remaining address payload in
|
|
|
|
// series, using the first byte to denote how to decode the
|
|
|
|
// address itself.
|
|
|
|
var (
|
|
|
|
addresses []net.Addr
|
|
|
|
addrBytesRead uint16
|
|
|
|
)
|
2018-04-25 19:22:47 +02:00
|
|
|
|
2017-09-19 02:27:37 +02:00
|
|
|
for addrBytesRead < addrsLen {
|
2017-02-17 10:29:23 +01:00
|
|
|
var descriptor [1]byte
|
2017-09-19 02:27:37 +02:00
|
|
|
if _, err = io.ReadFull(addrBuf, descriptor[:]); err != nil {
|
2017-02-17 10:29:23 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-19 02:40:24 +02:00
|
|
|
addrBytesRead++
|
2017-09-19 02:27:37 +02:00
|
|
|
|
2018-04-25 19:22:47 +02:00
|
|
|
var address net.Addr
|
|
|
|
switch aType := addressType(descriptor[0]); aType {
|
2017-09-19 02:27:37 +02:00
|
|
|
case noAddr:
|
|
|
|
addrBytesRead += aType.AddrLen()
|
|
|
|
continue
|
|
|
|
|
|
|
|
case tcp4Addr:
|
2017-02-17 10:29:23 +01:00
|
|
|
var ip [4]byte
|
2018-04-25 19:22:47 +02:00
|
|
|
if _, err := io.ReadFull(addrBuf, ip[:]); err != nil {
|
2017-02-17 10:29:23 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
|
|
|
|
var port [2]byte
|
2018-04-25 19:22:47 +02:00
|
|
|
if _, err := io.ReadFull(addrBuf, port[:]); err != nil {
|
2017-09-19 02:27:37 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-25 19:22:47 +02:00
|
|
|
address = &net.TCPAddr{
|
|
|
|
IP: net.IP(ip[:]),
|
|
|
|
Port: int(binary.BigEndian.Uint16(port[:])),
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
addrBytesRead += aType.AddrLen()
|
|
|
|
|
|
|
|
case tcp6Addr:
|
2017-02-17 10:29:23 +01:00
|
|
|
var ip [16]byte
|
2018-04-25 19:22:47 +02:00
|
|
|
if _, err := io.ReadFull(addrBuf, ip[:]); err != nil {
|
2017-02-17 10:29:23 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-19 02:27:37 +02:00
|
|
|
var port [2]byte
|
2018-04-25 19:22:47 +02:00
|
|
|
if _, err := io.ReadFull(addrBuf, port[:]); err != nil {
|
2017-09-19 02:27:37 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-25 19:22:47 +02:00
|
|
|
address = &net.TCPAddr{
|
|
|
|
IP: net.IP(ip[:]),
|
|
|
|
Port: int(binary.BigEndian.Uint16(port[:])),
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
addrBytesRead += aType.AddrLen()
|
|
|
|
|
|
|
|
case v2OnionAddr:
|
2018-04-25 19:22:47 +02:00
|
|
|
var h [tor.V2DecodedLen]byte
|
|
|
|
if _, err := io.ReadFull(addrBuf, h[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var p [2]byte
|
|
|
|
if _, err := io.ReadFull(addrBuf, p[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
onionService := tor.Base32Encoding.EncodeToString(h[:])
|
|
|
|
onionService += tor.OnionSuffix
|
|
|
|
port := int(binary.BigEndian.Uint16(p[:]))
|
|
|
|
|
|
|
|
address = &tor.OnionAddr{
|
|
|
|
OnionService: onionService,
|
|
|
|
Port: port,
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
addrBytesRead += aType.AddrLen()
|
|
|
|
|
|
|
|
case v3OnionAddr:
|
2018-04-25 19:22:47 +02:00
|
|
|
var h [tor.V3DecodedLen]byte
|
|
|
|
if _, err := io.ReadFull(addrBuf, h[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var p [2]byte
|
|
|
|
if _, err := io.ReadFull(addrBuf, p[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
onionService := tor.Base32Encoding.EncodeToString(h[:])
|
|
|
|
onionService += tor.OnionSuffix
|
|
|
|
port := int(binary.BigEndian.Uint16(p[:]))
|
|
|
|
|
|
|
|
address = &tor.OnionAddr{
|
|
|
|
OnionService: onionService,
|
|
|
|
Port: port,
|
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
addrBytesRead += aType.AddrLen()
|
|
|
|
|
|
|
|
default:
|
2022-04-21 10:53:06 +02:00
|
|
|
// If we don't understand this address type,
|
|
|
|
// we just store it along with the remaining
|
|
|
|
// address bytes as type OpaqueAddrs. We need
|
|
|
|
// to hold onto the bytes so that we can still
|
|
|
|
// write them back to the wire when we
|
|
|
|
// propagate this message.
|
|
|
|
payloadLen := 1 + addrsLen - addrBytesRead
|
|
|
|
payload := make([]byte, payloadLen)
|
|
|
|
|
|
|
|
// First write a byte for the address type that
|
|
|
|
// we already read.
|
|
|
|
payload[0] = byte(aType)
|
|
|
|
|
|
|
|
// Now append the rest of the address bytes.
|
|
|
|
_, err := io.ReadFull(addrBuf, payload[1:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
address = &OpaqueAddrs{
|
|
|
|
Payload: payload,
|
|
|
|
}
|
|
|
|
addrBytesRead = addrsLen
|
2017-02-17 10:29:23 +01:00
|
|
|
}
|
2016-12-07 16:46:22 +01:00
|
|
|
|
2017-02-17 10:29:23 +01:00
|
|
|
addresses = append(addresses, address)
|
2016-12-07 16:46:22 +01:00
|
|
|
}
|
2017-09-19 02:27:37 +02:00
|
|
|
|
2017-02-17 10:29:23 +01:00
|
|
|
*e = addresses
|
2017-12-03 03:23:05 +01:00
|
|
|
case *color.RGBA:
|
2018-12-10 03:27:41 +01:00
|
|
|
err := ReadElements(r,
|
2017-12-03 03:23:05 +01:00
|
|
|
&e.R,
|
|
|
|
&e.G,
|
|
|
|
&e.B,
|
2016-12-07 16:46:22 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-03-09 00:32:11 +01:00
|
|
|
case *DeliveryAddress:
|
|
|
|
var addrLen [2]byte
|
|
|
|
if _, err = io.ReadFull(r, addrLen[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
length := binary.BigEndian.Uint16(addrLen[:])
|
|
|
|
|
2019-12-03 10:38:21 +01:00
|
|
|
var addrBytes [deliveryAddressMaxSize]byte
|
2021-06-18 08:40:59 +02:00
|
|
|
|
2019-12-03 10:38:21 +01:00
|
|
|
if length > deliveryAddressMaxSize {
|
2021-06-18 08:40:59 +02:00
|
|
|
return fmt.Errorf(
|
|
|
|
"cannot read %d bytes into addrBytes", length,
|
|
|
|
)
|
2017-09-01 14:26:57 +02:00
|
|
|
}
|
2017-03-09 00:32:11 +01:00
|
|
|
if _, err = io.ReadFull(r, addrBytes[:length]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*e = addrBytes[:length]
|
2020-01-28 02:25:01 +01:00
|
|
|
|
|
|
|
case *ExtraOpaqueData:
|
|
|
|
return e.Decode(r)
|
|
|
|
|
2015-12-27 03:20:25 +01:00
|
|
|
default:
|
2020-04-14 19:56:05 +02:00
|
|
|
return fmt.Errorf("unknown type in ReadElement: %T", e)
|
2015-12-27 03:20:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-10 03:27:41 +01:00
|
|
|
// ReadElements deserializes a variable number of elements into the passed
|
|
|
|
// io.Reader, with each element being deserialized according to the ReadElement
|
2016-05-23 22:51:21 +02:00
|
|
|
// function.
|
2018-12-10 03:27:41 +01:00
|
|
|
func ReadElements(r io.Reader, elements ...interface{}) error {
|
2015-12-28 12:24:16 +01:00
|
|
|
for _, element := range elements {
|
2018-12-10 03:27:41 +01:00
|
|
|
err := ReadElement(r, element)
|
2015-12-28 12:24:16 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-23 09:08:34 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|