github.com/decred/dcrlnd@v0.7.6/htlcswitch/circuit.go (about) 1 package htlcswitch 2 3 import ( 4 "encoding/binary" 5 "io" 6 7 "github.com/decred/dcrlnd/channeldb" 8 "github.com/decred/dcrlnd/htlcswitch/hop" 9 "github.com/decred/dcrlnd/lnwire" 10 ) 11 12 // EmptyCircuitKey is a default value for an outgoing circuit key returned when 13 // a circuit's keystone has not been set. Note that this value is invalid for 14 // use as a keystone, since the outgoing channel id can never be equal to 15 // sourceHop. 16 var EmptyCircuitKey CircuitKey 17 18 // CircuitKey is a tuple of channel ID and HTLC ID, used to uniquely identify 19 // HTLCs in a circuit. Circuits are identified primarily by the circuit key of 20 // the incoming HTLC. However, a circuit may also be referenced by its outgoing 21 // circuit key after the HTLC has been forwarded via the outgoing link. 22 type CircuitKey = channeldb.CircuitKey 23 24 // PaymentCircuit is used by the switch as placeholder between when the 25 // switch makes a forwarding decision and the outgoing link determines the 26 // proper HTLC ID for the local log. After the outgoing HTLC ID has been 27 // determined, the half circuit will be converted into a full PaymentCircuit. 28 type PaymentCircuit struct { 29 // AddRef is the forward reference of the Add update in the incoming 30 // link's forwarding package. This value is set on the htlcPacket of the 31 // returned settle/fail so that it can be removed from disk. 32 AddRef channeldb.AddRef 33 34 // Incoming is the circuit key identifying the incoming channel and htlc 35 // index from which this ADD originates. 36 Incoming CircuitKey 37 38 // Outgoing is the circuit key identifying the outgoing channel, and the 39 // HTLC index that was used to forward the ADD. It will be nil if this 40 // circuit's keystone has not been set. 41 Outgoing *CircuitKey 42 43 // PaymentHash used as unique identifier of payment. 44 PaymentHash [32]byte 45 46 // IncomingAmount is the value of the HTLC from the incoming link. 47 IncomingAmount lnwire.MilliAtom 48 49 // OutgoingAmount specifies the value of the HTLC leaving the switch, 50 // either as a payment or forwarded amount. 51 OutgoingAmount lnwire.MilliAtom 52 53 // ErrorEncrypter is used to re-encrypt the onion failure before 54 // sending it back to the originator of the payment. 55 ErrorEncrypter hop.ErrorEncrypter 56 57 // LoadedFromDisk is set true for any circuits loaded after the circuit 58 // map is reloaded from disk. 59 // 60 // NOTE: This value is determined implicitly during a restart. It is not 61 // persisted, and should never be set outside the circuit map. 62 LoadedFromDisk bool 63 } 64 65 // HasKeystone returns true if an outgoing link has assigned this circuit's 66 // outgoing circuit key. 67 func (c *PaymentCircuit) HasKeystone() bool { 68 return c.Outgoing != nil 69 } 70 71 // newPaymentCircuit initializes a payment circuit on the heap using the payment 72 // hash and an in-memory htlc packet. 73 func newPaymentCircuit(hash *[32]byte, pkt *htlcPacket) *PaymentCircuit { 74 var addRef channeldb.AddRef 75 if pkt.sourceRef != nil { 76 addRef = *pkt.sourceRef 77 } 78 79 return &PaymentCircuit{ 80 AddRef: addRef, 81 Incoming: CircuitKey{ 82 ChanID: pkt.incomingChanID, 83 HtlcID: pkt.incomingHTLCID, 84 }, 85 PaymentHash: *hash, 86 IncomingAmount: pkt.incomingAmount, 87 OutgoingAmount: pkt.amount, 88 ErrorEncrypter: pkt.obfuscator, 89 } 90 } 91 92 // makePaymentCircuit initializes a payment circuit on the stack using the 93 // payment hash and an in-memory htlc packet. 94 func makePaymentCircuit(hash *[32]byte, pkt *htlcPacket) PaymentCircuit { 95 var addRef channeldb.AddRef 96 if pkt.sourceRef != nil { 97 addRef = *pkt.sourceRef 98 } 99 100 return PaymentCircuit{ 101 AddRef: addRef, 102 Incoming: CircuitKey{ 103 ChanID: pkt.incomingChanID, 104 HtlcID: pkt.incomingHTLCID, 105 }, 106 PaymentHash: *hash, 107 IncomingAmount: pkt.incomingAmount, 108 OutgoingAmount: pkt.amount, 109 ErrorEncrypter: pkt.obfuscator, 110 } 111 } 112 113 // Encode writes a PaymentCircuit to the provided io.Writer. 114 func (c *PaymentCircuit) Encode(w io.Writer) error { 115 if err := c.AddRef.Encode(w); err != nil { 116 return err 117 } 118 119 if err := c.Incoming.Encode(w); err != nil { 120 return err 121 } 122 123 if _, err := w.Write(c.PaymentHash[:]); err != nil { 124 return err 125 } 126 127 var scratch [8]byte 128 129 binary.BigEndian.PutUint64(scratch[:], uint64(c.IncomingAmount)) 130 if _, err := w.Write(scratch[:]); err != nil { 131 return err 132 } 133 134 binary.BigEndian.PutUint64(scratch[:], uint64(c.OutgoingAmount)) 135 if _, err := w.Write(scratch[:]); err != nil { 136 return err 137 } 138 139 // Defaults to EncrypterTypeNone. 140 var encrypterType hop.EncrypterType 141 if c.ErrorEncrypter != nil { 142 encrypterType = c.ErrorEncrypter.Type() 143 } 144 145 err := binary.Write(w, binary.BigEndian, encrypterType) 146 if err != nil { 147 return err 148 } 149 150 // Skip encoding of error encrypter if this half add does not have one. 151 if encrypterType == hop.EncrypterTypeNone { 152 return nil 153 } 154 155 return c.ErrorEncrypter.Encode(w) 156 } 157 158 // Decode reads a PaymentCircuit from the provided io.Reader. 159 func (c *PaymentCircuit) Decode(r io.Reader) error { 160 if err := c.AddRef.Decode(r); err != nil { 161 return err 162 } 163 164 if err := c.Incoming.Decode(r); err != nil { 165 return err 166 } 167 168 if _, err := io.ReadFull(r, c.PaymentHash[:]); err != nil { 169 return err 170 } 171 172 var scratch [8]byte 173 174 if _, err := io.ReadFull(r, scratch[:]); err != nil { 175 return err 176 } 177 c.IncomingAmount = lnwire.MilliAtom( 178 binary.BigEndian.Uint64(scratch[:])) 179 180 if _, err := io.ReadFull(r, scratch[:]); err != nil { 181 return err 182 } 183 c.OutgoingAmount = lnwire.MilliAtom( 184 binary.BigEndian.Uint64(scratch[:])) 185 186 // Read the encrypter type used for this circuit. 187 var encrypterType hop.EncrypterType 188 err := binary.Read(r, binary.BigEndian, &encrypterType) 189 if err != nil { 190 return err 191 } 192 193 switch encrypterType { 194 case hop.EncrypterTypeNone: 195 // No encrypter was provided, such as when the payment is 196 // locally initiated. 197 return nil 198 199 case hop.EncrypterTypeSphinx: 200 // Sphinx encrypter was used as this is a forwarded HTLC. 201 c.ErrorEncrypter = hop.NewSphinxErrorEncrypter() 202 203 case hop.EncrypterTypeMock: 204 // Test encrypter. 205 c.ErrorEncrypter = NewMockObfuscator() 206 207 default: 208 return UnknownEncrypterType(encrypterType) 209 } 210 211 return c.ErrorEncrypter.Decode(r) 212 } 213 214 // InKey returns the primary identifier for the circuit corresponding to the 215 // incoming HTLC. 216 func (c *PaymentCircuit) InKey() CircuitKey { 217 return c.Incoming 218 } 219 220 // OutKey returns the keystone identifying the outgoing link and HTLC ID. If the 221 // circuit hasn't been completed, this method returns an EmptyKeystone, which is 222 // an invalid outgoing circuit key. Only call this method if HasKeystone returns 223 // true. 224 func (c *PaymentCircuit) OutKey() CircuitKey { 225 if c.Outgoing != nil { 226 return *c.Outgoing 227 } 228 229 return EmptyCircuitKey 230 }