@@ -2,12 +2,15 @@ package hop
2
2
3
3
import (
4
4
"bytes"
5
+ "errors"
5
6
"fmt"
6
7
"io"
8
+ "time"
7
9
8
10
"github.com/btcsuite/btcd/btcec/v2"
9
11
sphinx "github.com/lightningnetwork/lightning-onion"
10
12
"github.com/lightningnetwork/lnd/lnwire"
13
+ "github.com/lightningnetwork/lnd/tlv"
11
14
)
12
15
13
16
// EncrypterType establishes an enum used in serialization to indicate how to
@@ -37,6 +40,24 @@ const (
37
40
// the same functionality as a EncrypterTypeSphinx, but is used to mark
38
41
// our special-case error handling.
39
42
EncrypterTypeRelaying = 4
43
+
44
+ // A set of tlv type definitions used to serialize the encrypter to the
45
+ // database.
46
+ //
47
+ // NOTE: A migration should be added whenever this list changes. This
48
+ // prevents against the database being rolled back to an older
49
+ // format where the surrounding logic might assume a different set of
50
+ // fields are known.
51
+ creationTimeType tlv.Type = 0
52
+ )
53
+
54
+ // AttrErrorStruct defines the message structure for an attributable error. Use
55
+ // a maximum route length of 20, a fixed payload length of 4 bytes to
56
+ // accommodate the a 32-bit hold time in milliseconds and use 4 byte hmacs.
57
+ // Total size including a 256 byte message from the error source works out to
58
+ // 1200 bytes.
59
+ var (
60
+ AttrErrorStruct = sphinx .NewAttrErrorStructure (20 , 4 , 4 )
40
61
)
41
62
42
63
// IsBlinded returns a boolean indicating whether the error encrypter belongs
@@ -58,8 +79,8 @@ type ErrorEncrypter interface {
58
79
// encrypted opaque failure reason. This method will be used at the
59
80
// source that the error occurs. It differs from IntermediateEncrypt
60
81
// slightly, in that it computes a proper MAC over the error.
61
- EncryptFirstHop (lnwire.FailureMessage ) (lnwire.OpaqueReason , [] byte ,
62
- error )
82
+ EncryptFirstHop (lnwire.FailureMessage ) (lnwire.OpaqueReason ,
83
+ [] byte , error )
63
84
64
85
// EncryptMalformedError is similar to EncryptFirstHop (it adds the
65
86
// MAC), but it accepts an opaque failure reason rather than a failure
@@ -104,6 +125,7 @@ type SphinxErrorEncrypter struct {
104
125
* sphinx.OnionErrorEncrypter
105
126
106
127
EphemeralKey * btcec.PublicKey
128
+ CreatedAt time.Time
107
129
}
108
130
109
131
// NewSphinxErrorEncrypterUninitialized initializes a blank sphinx error
@@ -115,8 +137,7 @@ type SphinxErrorEncrypter struct {
115
137
// OnionProcessor.
116
138
func NewSphinxErrorEncrypterUninitialized () * SphinxErrorEncrypter {
117
139
return & SphinxErrorEncrypter {
118
- OnionErrorEncrypter : nil ,
119
- EphemeralKey : & btcec.PublicKey {},
140
+ EphemeralKey : & btcec.PublicKey {},
120
141
}
121
142
}
122
143
@@ -131,15 +152,35 @@ func NewSphinxErrorEncrypter(ephemeralKey *btcec.PublicKey,
131
152
EphemeralKey : ephemeralKey ,
132
153
}
133
154
155
+ // Set creation time rounded to nanosecond to avoid differences after
156
+ // serialization.
157
+ encrypter .CreatedAt = time .Now ().Truncate (time .Nanosecond )
158
+
134
159
encrypter .initialize (sharedSecret )
135
160
136
161
return encrypter
137
162
}
138
163
164
+ // getHoldTime returns the hold time in decaseconds since the first
165
+ // instantiation of this sphinx error encrypter.
166
+ func (s * SphinxErrorEncrypter ) getHoldTime () uint32 {
167
+ return uint32 (time .Since (s .CreatedAt ).Milliseconds () / 100 )
168
+ }
169
+
170
+ // encrypt is a thin wrapper around the main encryption method, mainly used to
171
+ // automatically derive the hold time to encode in the attribution structure.
172
+ func (s * SphinxErrorEncrypter ) encrypt (initial bool ,
173
+ data , attrData []byte ) (lnwire.OpaqueReason , []byte , error ) {
174
+
175
+ holdTime := s .getHoldTime ()
176
+
177
+ return s .EncryptError (initial , data , attrData , holdTime )
178
+ }
179
+
139
180
// initialize creates the underlying instance of the sphinx error encrypter.
140
181
func (s * SphinxErrorEncrypter ) initialize (sharedSecret sphinx.Hash256 ) {
141
182
s .OnionErrorEncrypter = sphinx .NewOnionErrorEncrypter (
142
- sharedSecret , nil ,
183
+ sharedSecret , AttrErrorStruct ,
143
184
)
144
185
}
145
186
@@ -157,9 +198,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
157
198
return nil , nil , err
158
199
}
159
200
160
- // We pass a true as the first parameter to indicate that a MAC should
161
- // be added.
162
- return s .EncryptError (true , b .Bytes (), nil , 0 )
201
+ return s .encrypt (true , b .Bytes (), nil )
163
202
}
164
203
165
204
// EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but
@@ -172,7 +211,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
172
211
func (s * SphinxErrorEncrypter ) EncryptMalformedError (
173
212
reason lnwire.OpaqueReason ) (lnwire.OpaqueReason , []byte , error ) {
174
213
175
- return s .EncryptError (true , reason , nil , 0 )
214
+ return s .encrypt (true , reason , nil )
176
215
}
177
216
178
217
// IntermediateEncrypt wraps an already encrypted opaque reason error in an
@@ -183,10 +222,25 @@ func (s *SphinxErrorEncrypter) EncryptMalformedError(
183
222
//
184
223
// NOTE: Part of the ErrorEncrypter interface.
185
224
func (s * SphinxErrorEncrypter ) IntermediateEncrypt (
186
- reason lnwire.OpaqueReason , _ []byte ) (lnwire.OpaqueReason , []byte ,
187
- error ) {
225
+ reason lnwire.OpaqueReason , attrData []byte ) (lnwire.OpaqueReason ,
226
+ []byte , error ) {
227
+
228
+ encrypted , attrData , err := s .encrypt (false , reason , attrData )
229
+
230
+ switch {
231
+ // If the structure of the error received from downstream is invalid,
232
+ // then generate a new attribution structure so that the sender is able
233
+ // to penalize the offending node.
234
+ case errors .Is (err , sphinx .ErrInvalidAttrStructure ):
235
+ // Preserve the error message and initialize fresh attribution
236
+ // data.
237
+ return s .encrypt (true , reason , nil )
238
+
239
+ case err != nil :
240
+ return lnwire.OpaqueReason {}, nil , err
241
+ }
188
242
189
- return s . EncryptError ( false , reason , nil , 0 )
243
+ return encrypted , attrData , nil
190
244
}
191
245
192
246
// Type returns the identifier for a sphinx error encrypter.
@@ -199,7 +253,20 @@ func (s *SphinxErrorEncrypter) Type() EncrypterType {
199
253
func (s * SphinxErrorEncrypter ) Encode (w io.Writer ) error {
200
254
ephemeral := s .EphemeralKey .SerializeCompressed ()
201
255
_ , err := w .Write (ephemeral )
202
- return err
256
+ if err != nil {
257
+ return err
258
+ }
259
+
260
+ var creationTime = uint64 (s .CreatedAt .UnixNano ())
261
+
262
+ tlvStream , err := tlv .NewStream (
263
+ tlv .MakePrimitiveRecord (creationTimeType , & creationTime ),
264
+ )
265
+ if err != nil {
266
+ return err
267
+ }
268
+
269
+ return tlvStream .Encode (w )
203
270
}
204
271
205
272
// Decode reconstructs the error encrypter's ephemeral public key from the
@@ -216,6 +283,29 @@ func (s *SphinxErrorEncrypter) Decode(r io.Reader) error {
216
283
return err
217
284
}
218
285
286
+ // Try decode attributable error structure.
287
+ var creationTime uint64
288
+
289
+ tlvStream , err := tlv .NewStream (
290
+ tlv .MakePrimitiveRecord (creationTimeType , & creationTime ),
291
+ )
292
+ if err != nil {
293
+ return err
294
+ }
295
+
296
+ typeMap , err := tlvStream .DecodeWithParsedTypes (r )
297
+ if err != nil {
298
+ return err
299
+ }
300
+
301
+ // Return early if this encrypter is not for attributable errors.
302
+ if len (typeMap ) == 0 {
303
+ return nil
304
+ }
305
+
306
+ // Set attributable error creation time.
307
+ s .CreatedAt = time .Unix (0 , int64 (creationTime ))
308
+
219
309
return nil
220
310
}
221
311
0 commit comments