github.com/decred/dcrlnd@v0.7.6/htlcswitch/hop/iterator.go (about) 1 package hop 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "sync" 8 9 "github.com/decred/dcrd/dcrec/secp256k1/v4" 10 "github.com/decred/dcrlnd/lnwire" 11 sphinx "github.com/decred/lightning-onion/v4" 12 ) 13 14 // Iterator is an interface that abstracts away the routing information 15 // included in HTLC's which includes the entirety of the payment path of an 16 // HTLC. This interface provides two basic method which carry out: how to 17 // interpret the forwarding information encoded within the HTLC packet, and hop 18 // to encode the forwarding information for the _next_ hop. 19 type Iterator interface { 20 // HopPayload returns the set of fields that detail exactly _how_ this 21 // hop should forward the HTLC to the next hop. Additionally, the 22 // information encoded within the returned ForwardingInfo is to be used 23 // by each hop to authenticate the information given to it by the prior 24 // hop. The payload will also contain any additional TLV fields provided 25 // by the sender. 26 HopPayload() (*Payload, error) 27 28 // EncodeNextHop encodes the onion packet destined for the next hop 29 // into the passed io.Writer. 30 EncodeNextHop(w io.Writer) error 31 32 // ExtractErrorEncrypter returns the ErrorEncrypter needed for this hop, 33 // along with a failure code to signal if the decoding was successful. 34 ExtractErrorEncrypter(ErrorEncrypterExtracter) (ErrorEncrypter, 35 lnwire.FailCode) 36 } 37 38 // sphinxHopIterator is the Sphinx implementation of hop iterator which uses 39 // onion routing to encode the payment route in such a way so that node might 40 // see only the next hop in the route.. 41 type sphinxHopIterator struct { 42 // ogPacket is the original packet from which the processed packet is 43 // derived. 44 ogPacket *sphinx.OnionPacket 45 46 // processedPacket is the outcome of processing an onion packet. It 47 // includes the information required to properly forward the packet to 48 // the next hop. 49 processedPacket *sphinx.ProcessedPacket 50 } 51 52 // makeSphinxHopIterator converts a processed packet returned from a sphinx 53 // router and converts it into an hop iterator for usage in the link. 54 func makeSphinxHopIterator(ogPacket *sphinx.OnionPacket, 55 packet *sphinx.ProcessedPacket) *sphinxHopIterator { 56 57 return &sphinxHopIterator{ 58 ogPacket: ogPacket, 59 processedPacket: packet, 60 } 61 } 62 63 // A compile time check to ensure sphinxHopIterator implements the HopIterator 64 // interface. 65 var _ Iterator = (*sphinxHopIterator)(nil) 66 67 // Encode encodes iterator and writes it to the writer. 68 // 69 // NOTE: Part of the HopIterator interface. 70 func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error { 71 return r.processedPacket.NextPacket.Encode(w) 72 } 73 74 // HopPayload returns the set of fields that detail exactly _how_ this hop 75 // should forward the HTLC to the next hop. Additionally, the information 76 // encoded within the returned ForwardingInfo is to be used by each hop to 77 // authenticate the information given to it by the prior hop. The payload will 78 // also contain any additional TLV fields provided by the sender. 79 // 80 // NOTE: Part of the HopIterator interface. 81 func (r *sphinxHopIterator) HopPayload() (*Payload, error) { 82 switch r.processedPacket.Payload.Type { 83 84 // If this is the legacy payload, then we'll extract the information 85 // directly from the pre-populated ForwardingInstructions field. 86 case sphinx.PayloadLegacy: 87 fwdInst := r.processedPacket.ForwardingInstructions 88 return NewLegacyPayload(fwdInst), nil 89 90 // Otherwise, if this is the TLV payload, then we'll make a new stream 91 // to decode only what we need to make routing decisions. 92 case sphinx.PayloadTLV: 93 return NewPayloadFromReader(bytes.NewReader( 94 r.processedPacket.Payload.Payload, 95 )) 96 97 default: 98 return nil, fmt.Errorf("unknown sphinx payload type: %v", 99 r.processedPacket.Payload.Type) 100 } 101 } 102 103 // ExtractErrorEncrypter decodes and returns the ErrorEncrypter for this hop, 104 // along with a failure code to signal if the decoding was successful. The 105 // ErrorEncrypter is used to encrypt errors back to the sender in the event that 106 // a payment fails. 107 // 108 // NOTE: Part of the HopIterator interface. 109 func (r *sphinxHopIterator) ExtractErrorEncrypter( 110 extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) { 111 112 return extracter(r.ogPacket.EphemeralKey) 113 } 114 115 // OnionProcessor is responsible for keeping all sphinx dependent parts inside 116 // and expose only decoding function. With such approach we give freedom for 117 // subsystems which wants to decode sphinx path to not be dependable from 118 // sphinx at all. 119 // 120 // NOTE: The reason for keeping decoder separated from hop iterator is too 121 // maintain the hop iterator abstraction. Without it the structures which using 122 // the hop iterator should contain sphinx router which makes their creations in 123 // tests dependent from the sphinx internal parts. 124 type OnionProcessor struct { 125 router *sphinx.Router 126 } 127 128 // NewOnionProcessor creates new instance of decoder. 129 func NewOnionProcessor(router *sphinx.Router) *OnionProcessor { 130 return &OnionProcessor{router} 131 } 132 133 // Start spins up the onion processor's sphinx router. 134 func (p *OnionProcessor) Start() error { 135 return p.router.Start() 136 } 137 138 // Stop shutsdown the onion processor's sphinx router. 139 func (p *OnionProcessor) Stop() error { 140 141 log.Info("Onion processor shutting down") 142 143 p.router.Stop() 144 return nil 145 } 146 147 // DecodeHopIterator attempts to decode a valid sphinx packet from the passed io.Reader 148 // instance using the rHash as the associated data when checking the relevant 149 // MACs during the decoding process. 150 func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte, 151 incomingCltv uint32) (Iterator, lnwire.FailCode) { 152 153 onionPkt := &sphinx.OnionPacket{} 154 if err := onionPkt.Decode(r); err != nil { 155 switch err { 156 case sphinx.ErrInvalidOnionVersion: 157 return nil, lnwire.CodeInvalidOnionVersion 158 case sphinx.ErrInvalidOnionKey: 159 return nil, lnwire.CodeInvalidOnionKey 160 default: 161 log.Errorf("unable to decode onion packet: %v", err) 162 return nil, lnwire.CodeInvalidOnionKey 163 } 164 } 165 166 // Attempt to process the Sphinx packet. We include the payment hash of 167 // the HTLC as it's authenticated within the Sphinx packet itself as 168 // associated data in order to thwart attempts a replay attacks. In the 169 // case of a replay, an attacker is *forced* to use the same payment 170 // hash twice, thereby losing their money entirely. 171 sphinxPacket, err := p.router.ProcessOnionPacket( 172 onionPkt, rHash, incomingCltv, 173 ) 174 if err != nil { 175 switch err { 176 case sphinx.ErrInvalidOnionVersion: 177 return nil, lnwire.CodeInvalidOnionVersion 178 case sphinx.ErrInvalidOnionHMAC: 179 return nil, lnwire.CodeInvalidOnionHmac 180 case sphinx.ErrInvalidOnionKey: 181 return nil, lnwire.CodeInvalidOnionKey 182 default: 183 log.Errorf("unable to process onion packet: %v", err) 184 return nil, lnwire.CodeInvalidOnionKey 185 } 186 } 187 188 return makeSphinxHopIterator(onionPkt, sphinxPacket), lnwire.CodeNone 189 } 190 191 // ReconstructHopIterator attempts to decode a valid sphinx packet from the passed io.Reader 192 // instance using the rHash as the associated data when checking the relevant 193 // MACs during the decoding process. 194 func (p *OnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte) ( 195 Iterator, error) { 196 197 onionPkt := &sphinx.OnionPacket{} 198 if err := onionPkt.Decode(r); err != nil { 199 return nil, err 200 } 201 202 // Attempt to process the Sphinx packet. We include the payment hash of 203 // the HTLC as it's authenticated within the Sphinx packet itself as 204 // associated data in order to thwart attempts a replay attacks. In the 205 // case of a replay, an attacker is *forced* to use the same payment 206 // hash twice, thereby losing their money entirely. 207 sphinxPacket, err := p.router.ReconstructOnionPacket(onionPkt, rHash) 208 if err != nil { 209 return nil, err 210 } 211 212 return makeSphinxHopIterator(onionPkt, sphinxPacket), nil 213 } 214 215 // DecodeHopIteratorRequest encapsulates all date necessary to process an onion 216 // packet, perform sphinx replay detection, and schedule the entry for garbage 217 // collection. 218 type DecodeHopIteratorRequest struct { 219 OnionReader io.Reader 220 RHash []byte 221 IncomingCltv uint32 222 } 223 224 // DecodeHopIteratorResponse encapsulates the outcome of a batched sphinx onion 225 // processing. 226 type DecodeHopIteratorResponse struct { 227 HopIterator Iterator 228 FailCode lnwire.FailCode 229 } 230 231 // Result returns the (HopIterator, lnwire.FailCode) tuple, which should 232 // correspond to the index of a particular DecodeHopIteratorRequest. 233 // 234 // NOTE: The HopIterator should be considered invalid if the fail code is 235 // anything but lnwire.CodeNone. 236 func (r *DecodeHopIteratorResponse) Result() (Iterator, lnwire.FailCode) { 237 return r.HopIterator, r.FailCode 238 } 239 240 // DecodeHopIterators performs batched decoding and validation of incoming 241 // sphinx packets. For the same `id`, this method will return the same iterators 242 // and failcodes upon subsequent invocations. 243 // 244 // NOTE: In order for the responses to be valid, the caller must guarantee that 245 // the presented readers and rhashes *NEVER* deviate across invocations for the 246 // same id. 247 func (p *OnionProcessor) DecodeHopIterators(id []byte, 248 reqs []DecodeHopIteratorRequest) ([]DecodeHopIteratorResponse, error) { 249 250 var ( 251 batchSize = len(reqs) 252 onionPkts = make([]sphinx.OnionPacket, batchSize) 253 resps = make([]DecodeHopIteratorResponse, batchSize) 254 ) 255 256 tx := p.router.BeginTxn(id, batchSize) 257 258 decode := func(seqNum uint16, onionPkt *sphinx.OnionPacket, 259 req DecodeHopIteratorRequest) lnwire.FailCode { 260 261 err := onionPkt.Decode(req.OnionReader) 262 switch err { 263 case nil: 264 // success 265 266 case sphinx.ErrInvalidOnionVersion: 267 return lnwire.CodeInvalidOnionVersion 268 269 case sphinx.ErrInvalidOnionKey: 270 return lnwire.CodeInvalidOnionKey 271 272 default: 273 log.Errorf("unable to decode onion packet: %v", err) 274 return lnwire.CodeInvalidOnionKey 275 } 276 277 err = tx.ProcessOnionPacket( 278 seqNum, onionPkt, req.RHash, req.IncomingCltv, 279 ) 280 switch err { 281 case nil: 282 // success 283 return lnwire.CodeNone 284 285 case sphinx.ErrInvalidOnionVersion: 286 return lnwire.CodeInvalidOnionVersion 287 288 case sphinx.ErrInvalidOnionHMAC: 289 return lnwire.CodeInvalidOnionHmac 290 291 case sphinx.ErrInvalidOnionKey: 292 return lnwire.CodeInvalidOnionKey 293 294 default: 295 log.Errorf("unable to process onion packet: %v", err) 296 return lnwire.CodeInvalidOnionKey 297 } 298 } 299 300 // Execute cpu-heavy onion decoding in parallel. 301 var wg sync.WaitGroup 302 for i := range reqs { 303 wg.Add(1) 304 go func(seqNum uint16) { 305 defer wg.Done() 306 307 onionPkt := &onionPkts[seqNum] 308 309 resps[seqNum].FailCode = decode( 310 seqNum, onionPkt, reqs[seqNum], 311 ) 312 }(uint16(i)) 313 } 314 wg.Wait() 315 316 // With that batch created, we will now attempt to write the shared 317 // secrets to disk. This operation will returns the set of indices that 318 // were detected as replays, and the computed sphinx packets for all 319 // indices that did not fail the above loop. Only indices that are not 320 // in the replay set should be considered valid, as they are 321 // opportunistically computed. 322 packets, replays, err := tx.Commit() 323 if err != nil { 324 log.Errorf("unable to process onion packet batch %x: %v", 325 id, err) 326 327 // If we failed to commit the batch to the secret share log, we 328 // will mark all not-yet-failed channels with a temporary 329 // channel failure and exit since we cannot proceed. 330 for i := range resps { 331 resp := &resps[i] 332 333 // Skip any indexes that already failed onion decoding. 334 if resp.FailCode != lnwire.CodeNone { 335 continue 336 } 337 338 log.Errorf("unable to process onion packet %x-%v", 339 id, i) 340 resp.FailCode = lnwire.CodeTemporaryChannelFailure 341 } 342 343 // TODO(conner): return real errors to caller so link can fail? 344 return resps, err 345 } 346 347 // Otherwise, the commit was successful. Now we will post process any 348 // remaining packets, additionally failing any that were included in the 349 // replay set. 350 for i := range resps { 351 resp := &resps[i] 352 353 // Skip any indexes that already failed onion decoding. 354 if resp.FailCode != lnwire.CodeNone { 355 continue 356 } 357 358 // If this index is contained in the replay set, mark it with a 359 // temporary channel failure error code. We infer that the 360 // offending error was due to a replayed packet because this 361 // index was found in the replay set. 362 if replays.Contains(uint16(i)) { 363 log.Errorf("unable to process onion packet: %v", 364 sphinx.ErrReplayedPacket) 365 resp.FailCode = lnwire.CodeTemporaryChannelFailure 366 continue 367 } 368 369 // Finally, construct a hop iterator from our processed sphinx 370 // packet, simultaneously caching the original onion packet. 371 resp.HopIterator = makeSphinxHopIterator(&onionPkts[i], &packets[i]) 372 } 373 374 return resps, nil 375 } 376 377 // ExtractErrorEncrypter takes an io.Reader which should contain the onion 378 // packet as original received by a forwarding node and creates an 379 // ErrorEncrypter instance using the derived shared secret. In the case that en 380 // error occurs, a lnwire failure code detailing the parsing failure will be 381 // returned. 382 func (p *OnionProcessor) ExtractErrorEncrypter(ephemeralKey *secp256k1.PublicKey) ( 383 ErrorEncrypter, lnwire.FailCode) { 384 385 onionObfuscator, err := sphinx.NewOnionErrorEncrypter( 386 p.router, ephemeralKey, 387 ) 388 if err != nil { 389 switch err { 390 case sphinx.ErrInvalidOnionVersion: 391 return nil, lnwire.CodeInvalidOnionVersion 392 case sphinx.ErrInvalidOnionHMAC: 393 return nil, lnwire.CodeInvalidOnionHmac 394 case sphinx.ErrInvalidOnionKey: 395 return nil, lnwire.CodeInvalidOnionKey 396 default: 397 log.Errorf("unable to process onion packet: %v", err) 398 return nil, lnwire.CodeInvalidOnionKey 399 } 400 } 401 402 return &SphinxErrorEncrypter{ 403 OnionErrorEncrypter: onionObfuscator, 404 EphemeralKey: ephemeralKey, 405 }, lnwire.CodeNone 406 }