@@ -2,12 +2,16 @@ package hop
22
33import (
44 "bytes"
5+ "encoding/binary"
6+ "errors"
57 "fmt"
68 "io"
9+ "time"
710
811 "github.com/btcsuite/btcd/btcec/v2"
912 sphinx "github.com/lightningnetwork/lightning-onion"
1013 "github.com/lightningnetwork/lnd/lnwire"
14+ "github.com/lightningnetwork/lnd/tlv"
1115)
1216
1317// EncrypterType establishes an enum used in serialization to indicate how to
@@ -37,6 +41,25 @@ const (
3741 // the same functionality as a EncrypterTypeSphinx, but is used to mark
3842 // our special-case error handling.
3943 EncrypterTypeRelaying = 4
44+
45+ // A set of tlv type definitions used to serialize the encrypter to the
46+ // database.
47+ //
48+ // NOTE: A migration should be added whenever this list changes. This
49+ // prevents against the database being rolled back to an older
50+ // format where the surrounding logic might assume a different set of
51+ // fields are known.
52+ creationTimeType tlv.Type = 0
53+ )
54+
55+ // AttrErrorStruct defines the message structure for an attributable error. Use
56+ // a maximum route length of 20, a fixed payload length of 4 bytes to
57+ // accommodate the a 32-bit hold time in milliseconds and use 4 byte hmacs.
58+ // Total size including a 256 byte message from the error source works out to
59+ // 1200 bytes.
60+ var (
61+ AttrErrorStruct = sphinx .NewAttrErrorStructure (20 , 4 , 4 )
62+ byteOrder = binary .BigEndian
4063)
4164
4265// IsBlinded returns a boolean indicating whether the error encrypter belongs
@@ -58,8 +81,8 @@ type ErrorEncrypter interface {
5881 // encrypted opaque failure reason. This method will be used at the
5982 // source that the error occurs. It differs from IntermediateEncrypt
6083 // slightly, in that it computes a proper MAC over the error.
61- EncryptFirstHop (lnwire.FailureMessage ) (lnwire.OpaqueReason , [] byte ,
62- error )
84+ EncryptFirstHop (lnwire.FailureMessage ) (lnwire.OpaqueReason ,
85+ [] byte , error )
6386
6487 // EncryptMalformedError is similar to EncryptFirstHop (it adds the
6588 // MAC), but it accepts an opaque failure reason rather than a failure
@@ -104,6 +127,7 @@ type SphinxErrorEncrypter struct {
104127 * sphinx.OnionErrorEncrypter
105128
106129 EphemeralKey * btcec.PublicKey
130+ CreatedAt time.Time
107131}
108132
109133// NewSphinxErrorEncrypterUninitialized initializes a blank sphinx error
@@ -115,8 +139,7 @@ type SphinxErrorEncrypter struct {
115139// OnionProcessor.
116140func NewSphinxErrorEncrypterUninitialized () * SphinxErrorEncrypter {
117141 return & SphinxErrorEncrypter {
118- OnionErrorEncrypter : nil ,
119- EphemeralKey : & btcec.PublicKey {},
142+ EphemeralKey : & btcec.PublicKey {},
120143 }
121144}
122145
@@ -131,15 +154,35 @@ func NewSphinxErrorEncrypter(ephemeralKey *btcec.PublicKey,
131154 EphemeralKey : ephemeralKey ,
132155 }
133156
157+ // Set creation time rounded to nanosecond to avoid differences after
158+ // serialization.
159+ encrypter .CreatedAt = time .Now ().Truncate (time .Nanosecond )
160+
134161 encrypter .initialize (sharedSecret )
135162
136163 return encrypter
137164}
138165
166+ // getHoldTimeMs returns the hold time in milliseconds since the first
167+ // instantiation of this sphinx error encrypter.
168+ func (s * SphinxErrorEncrypter ) getHoldTimeMs () uint32 {
169+ return uint32 (time .Since (s .CreatedAt ).Milliseconds ())
170+ }
171+
172+ // encrypt is a thin wrapper around the main encryption method, mainly used to
173+ // automatically derive the hold time to encode in the attribution structure.
174+ func (s * SphinxErrorEncrypter ) encrypt (initial bool ,
175+ data , attrData []byte ) (lnwire.OpaqueReason , []byte , error ) {
176+
177+ holdTimeMs := s .getHoldTimeMs ()
178+
179+ return s .EncryptError (initial , data , attrData , holdTimeMs )
180+ }
181+
139182// initialize creates the underlying instance of the sphinx error encrypter.
140183func (s * SphinxErrorEncrypter ) initialize (sharedSecret sphinx.Hash256 ) {
141184 s .OnionErrorEncrypter = sphinx .NewOnionErrorEncrypter (
142- sharedSecret , nil ,
185+ sharedSecret , AttrErrorStruct ,
143186 )
144187}
145188
@@ -157,9 +200,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
157200 return nil , nil , err
158201 }
159202
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 )
203+ return s .encrypt (true , b .Bytes (), nil )
163204}
164205
165206// EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but
@@ -172,7 +213,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
172213func (s * SphinxErrorEncrypter ) EncryptMalformedError (
173214 reason lnwire.OpaqueReason ) (lnwire.OpaqueReason , []byte , error ) {
174215
175- return s .EncryptError (true , reason , nil , 0 )
216+ return s .encrypt (true , reason , nil )
176217}
177218
178219// IntermediateEncrypt wraps an already encrypted opaque reason error in an
@@ -183,10 +224,25 @@ func (s *SphinxErrorEncrypter) EncryptMalformedError(
183224//
184225// NOTE: Part of the ErrorEncrypter interface.
185226func (s * SphinxErrorEncrypter ) IntermediateEncrypt (
186- reason lnwire.OpaqueReason , _ []byte ) (lnwire.OpaqueReason , []byte ,
187- error ) {
227+ reason lnwire.OpaqueReason , attrData []byte ) (lnwire.OpaqueReason ,
228+ []byte , error ) {
229+
230+ encrypted , attrData , err := s .encrypt (false , reason , attrData )
231+
232+ switch {
233+ // If the structure of the error received from downstream is invalid,
234+ // then generate a new attribution structure so that the sender is able
235+ // to penalize the offending node.
236+ case errors .Is (err , sphinx .ErrInvalidAttrStructure ):
237+ // Preserve the error message and initialize fresh attribution
238+ // data.
239+ return s .encrypt (true , reason , nil )
240+
241+ case err != nil :
242+ return lnwire.OpaqueReason {}, nil , err
243+ }
188244
189- return s . EncryptError ( false , reason , nil , 0 )
245+ return encrypted , attrData , nil
190246}
191247
192248// Type returns the identifier for a sphinx error encrypter.
@@ -199,7 +255,20 @@ func (s *SphinxErrorEncrypter) Type() EncrypterType {
199255func (s * SphinxErrorEncrypter ) Encode (w io.Writer ) error {
200256 ephemeral := s .EphemeralKey .SerializeCompressed ()
201257 _ , err := w .Write (ephemeral )
202- return err
258+ if err != nil {
259+ return err
260+ }
261+
262+ var creationTime = uint64 (s .CreatedAt .UnixNano ())
263+
264+ tlvStream , err := tlv .NewStream (
265+ tlv .MakePrimitiveRecord (creationTimeType , & creationTime ),
266+ )
267+ if err != nil {
268+ return err
269+ }
270+
271+ return tlvStream .Encode (w )
203272}
204273
205274// Decode reconstructs the error encrypter's ephemeral public key from the
@@ -216,6 +285,29 @@ func (s *SphinxErrorEncrypter) Decode(r io.Reader) error {
216285 return err
217286 }
218287
288+ // Try decode attributable error structure.
289+ var creationTime uint64
290+
291+ tlvStream , err := tlv .NewStream (
292+ tlv .MakePrimitiveRecord (creationTimeType , & creationTime ),
293+ )
294+ if err != nil {
295+ return err
296+ }
297+
298+ typeMap , err := tlvStream .DecodeWithParsedTypes (r )
299+ if err != nil {
300+ return err
301+ }
302+
303+ // Return early if this encrypter is not for attributable errors.
304+ if len (typeMap ) == 0 {
305+ return nil
306+ }
307+
308+ // Set attributable error creation time.
309+ s .CreatedAt = time .Unix (0 , int64 (creationTime ))
310+
219311 return nil
220312}
221313
0 commit comments