@@ -2,12 +2,15 @@ package hop
22
33import (
44 "bytes"
5+ "errors"
56 "fmt"
67 "io"
8+ "time"
79
810 "github.com/btcsuite/btcd/btcec/v2"
911 sphinx "github.com/lightningnetwork/lightning-onion"
1012 "github.com/lightningnetwork/lnd/lnwire"
13+ "github.com/lightningnetwork/lnd/tlv"
1114)
1215
1316// EncrypterType establishes an enum used in serialization to indicate how to
@@ -37,6 +40,24 @@ const (
3740 // the same functionality as a EncrypterTypeSphinx, but is used to mark
3841 // our special-case error handling.
3942 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 )
4061)
4162
4263// IsBlinded returns a boolean indicating whether the error encrypter belongs
@@ -58,8 +79,8 @@ type ErrorEncrypter interface {
5879 // encrypted opaque failure reason. This method will be used at the
5980 // source that the error occurs. It differs from IntermediateEncrypt
6081 // 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 )
6384
6485 // EncryptMalformedError is similar to EncryptFirstHop (it adds the
6586 // MAC), but it accepts an opaque failure reason rather than a failure
@@ -104,6 +125,7 @@ type SphinxErrorEncrypter struct {
104125 * sphinx.OnionErrorEncrypter
105126
106127 EphemeralKey * btcec.PublicKey
128+ CreatedAt time.Time
107129}
108130
109131// NewSphinxErrorEncrypterUninitialized initializes a blank sphinx error
@@ -115,8 +137,7 @@ type SphinxErrorEncrypter struct {
115137// OnionProcessor.
116138func NewSphinxErrorEncrypterUninitialized () * SphinxErrorEncrypter {
117139 return & SphinxErrorEncrypter {
118- OnionErrorEncrypter : nil ,
119- EphemeralKey : & btcec.PublicKey {},
140+ EphemeralKey : & btcec.PublicKey {},
120141 }
121142}
122143
@@ -131,15 +152,35 @@ func NewSphinxErrorEncrypter(ephemeralKey *btcec.PublicKey,
131152 EphemeralKey : ephemeralKey ,
132153 }
133154
155+ // Set creation time rounded to nanosecond to avoid differences after
156+ // serialization.
157+ encrypter .CreatedAt = time .Now ().Truncate (time .Nanosecond )
158+
134159 encrypter .initialize (sharedSecret )
135160
136161 return encrypter
137162}
138163
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+
139180// initialize creates the underlying instance of the sphinx error encrypter.
140181func (s * SphinxErrorEncrypter ) initialize (sharedSecret sphinx.Hash256 ) {
141182 s .OnionErrorEncrypter = sphinx .NewOnionErrorEncrypter (
142- sharedSecret , nil ,
183+ sharedSecret , AttrErrorStruct ,
143184 )
144185}
145186
@@ -157,9 +198,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
157198 return nil , nil , err
158199 }
159200
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 )
163202}
164203
165204// EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but
@@ -172,7 +211,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
172211func (s * SphinxErrorEncrypter ) EncryptMalformedError (
173212 reason lnwire.OpaqueReason ) (lnwire.OpaqueReason , []byte , error ) {
174213
175- return s .EncryptError (true , reason , nil , 0 )
214+ return s .encrypt (true , reason , nil )
176215}
177216
178217// IntermediateEncrypt wraps an already encrypted opaque reason error in an
@@ -183,10 +222,25 @@ func (s *SphinxErrorEncrypter) EncryptMalformedError(
183222//
184223// NOTE: Part of the ErrorEncrypter interface.
185224func (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+ }
188242
189- return s . EncryptError ( false , reason , nil , 0 )
243+ return encrypted , attrData , nil
190244}
191245
192246// Type returns the identifier for a sphinx error encrypter.
@@ -199,7 +253,20 @@ func (s *SphinxErrorEncrypter) Type() EncrypterType {
199253func (s * SphinxErrorEncrypter ) Encode (w io.Writer ) error {
200254 ephemeral := s .EphemeralKey .SerializeCompressed ()
201255 _ , 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 )
203270}
204271
205272// Decode reconstructs the error encrypter's ephemeral public key from the
@@ -216,6 +283,29 @@ func (s *SphinxErrorEncrypter) Decode(r io.Reader) error {
216283 return err
217284 }
218285
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+
219309 return nil
220310}
221311
0 commit comments