Skip to content

Commit 4addfd1

Browse files
committed
lnwire: introduce PureTLVMessage
PureTLVMessage describes an LN message that is a pure TLV stream. If the message includes a signature, it will sign all the TLV records in the inclusive ranges: 0 to 159 and 1000000000 to 2999999999. A comprehensive test is added that shows how two versions of the same message remain forward compatible.
1 parent cd6971e commit 4addfd1

File tree

2 files changed

+494
-0
lines changed

2 files changed

+494
-0
lines changed

lnwire/pure_tlv.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package lnwire
2+
3+
import (
4+
"bytes"
5+
6+
"github.com/lightningnetwork/lnd/tlv"
7+
)
8+
9+
const (
10+
// pureTLVUnsignedRangeOneStart defines the start of the first unsigned
11+
// TLV range used for pure TLV messages. The range is inclusive of this
12+
// number.
13+
pureTLVUnsignedRangeOneStart = 160
14+
15+
// pureTLVSignedSecondRangeStart defines the start of the second signed
16+
// TLV range used for pure TLV messages. The range is inclusive of this
17+
// number. Note that the first range is the inclusive range of 0-159.
18+
pureTLVSignedSecondRangeStart = 1000000000
19+
20+
// pureTLVUnsignedRangeTwoStart defines the start of the second unsigned
21+
// TLV range used for pure TLV message.
22+
pureTLVUnsignedRangeTwoStart = 3000000000
23+
)
24+
25+
// PureTLVMessage describes an LN message that is a pure TLV stream. If the
26+
// message includes a signature, it will sign all the TLV records in the
27+
// inclusive ranges: 0 to 159 and 1000000000 to 2999999999.
28+
type PureTLVMessage interface {
29+
// AllRecords returns all the TLV records for the message. This will
30+
// include all the records we know about along with any that we don't
31+
// know about but that fall in the signed TLV range.
32+
AllRecords() []tlv.Record
33+
}
34+
35+
// EncodePureTLVMessage encodes the given PureTLVMessage to the given buffer.
36+
func EncodePureTLVMessage(msg PureTLVMessage, buf *bytes.Buffer) error {
37+
return EncodeRecordsTo(buf, msg.AllRecords())
38+
}
39+
40+
// SerialiseFieldsToSign serialises all the records from the given
41+
// PureTLVMessage that fall within the signed TLV range.
42+
func SerialiseFieldsToSign(msg PureTLVMessage) ([]byte, error) {
43+
// Filter out all the fields not in the signed ranges.
44+
var signedRecords []tlv.Record
45+
for _, record := range msg.AllRecords() {
46+
if InUnsignedRange(record.Type()) {
47+
continue
48+
}
49+
50+
signedRecords = append(signedRecords, record)
51+
}
52+
53+
var buf bytes.Buffer
54+
if err := EncodeRecordsTo(&buf, signedRecords); err != nil {
55+
return nil, err
56+
}
57+
58+
return buf.Bytes(), nil
59+
}
60+
61+
// InUnsignedRange returns true if the given TLV type falls outside the TLV
62+
// ranges that the signature of a pure TLV message will cover.
63+
func InUnsignedRange(t tlv.Type) bool {
64+
return (t >= pureTLVUnsignedRangeOneStart &&
65+
t < pureTLVSignedSecondRangeStart) ||
66+
t >= pureTLVUnsignedRangeTwoStart
67+
}
68+
69+
// ExtraSignedFields is a type that stores a map from TLV types in the signed
70+
// range (for PureMessages) to their corresponding serialised values. This type
71+
// can be used to keep around data that we don't yet understand but that we need
72+
// for re-composing the wire message since the signature covers these fields.
73+
type ExtraSignedFields map[uint64][]byte
74+
75+
// ExtraSignedFieldsFromTypeMap is a helper that can be used alongside calls to
76+
// the tlv.Stream DecodeWithParsedTypesP2P or DecodeWithParsedTypes methods to
77+
// extract the tlv type and value pairs in the defined PureTLVMessage signed
78+
// range which we have not handled with any of our defined Records. These
79+
// methods will return a tlv.TypeMap containing the records that were extracted
80+
// from an io.Reader. If the record was know and handled by a defined record,
81+
// then the value accompanying the record's type in the map will be nil.
82+
// Otherwise, if the record was unhandled, it will be non-nil.
83+
func ExtraSignedFieldsFromTypeMap(m tlv.TypeMap) ExtraSignedFields {
84+
extraFields := make(ExtraSignedFields)
85+
for t, v := range m {
86+
// If the value in the type map is nil, then it indicates that
87+
// we know this type, and it was handled by one of the records
88+
// we passed to the decode function vai the TLV stream.
89+
if v == nil {
90+
continue
91+
}
92+
93+
// No need to keep this field if it is unknown to us and is not
94+
// in the sign range.
95+
if InUnsignedRange(t) {
96+
continue
97+
}
98+
99+
// Otherwise, this is an un-handled type, so we keep track of
100+
// it for signature validation and re-encoding later on.
101+
extraFields[uint64(t)] = v
102+
}
103+
104+
return extraFields
105+
}

0 commit comments

Comments
 (0)