@@ -116,7 +116,6 @@ def decode(pr: str) -> Invoice:
116
116
return invoice
117
117
118
118
119
-
120
119
def encode (options ):
121
120
""" Convert options into LnAddr and pass it to the encoder
122
121
"""
@@ -131,80 +130,91 @@ def encode(options):
131
130
addr .paymenthash = unhexlify (options .paymenthash )
132
131
133
132
if options .description :
134
- addr .tags .append (('d' , options .description ))
133
+ addr .tags .append (("d" , options .description ))
135
134
if options .description_hashed :
136
- addr .tags .append (('h' , options .description_hashed ))
135
+ addr .tags .append (("h" , options .description_hashed ))
137
136
if options .expires :
138
- addr .tags .append (('x' , options .expires ))
137
+ addr .tags .append (("x" , options .expires ))
139
138
140
139
if options .fallback :
141
- addr .tags .append (('f' , options .fallback ))
140
+ addr .tags .append (("f" , options .fallback ))
142
141
143
142
for r in options .route :
144
- splits = r .split ('/' )
145
- route = []
143
+ splits = r .split ("/" )
144
+ route = []
146
145
while len (splits ) >= 5 :
147
- route .append ((unhexlify (splits [0 ]),
148
- unhexlify (splits [1 ]),
149
- int (splits [2 ]),
150
- int (splits [3 ]),
151
- int (splits [4 ])))
146
+ route .append (
147
+ (
148
+ unhexlify (splits [0 ]),
149
+ unhexlify (splits [1 ]),
150
+ int (splits [2 ]),
151
+ int (splits [3 ]),
152
+ int (splits [4 ]),
153
+ )
154
+ )
152
155
splits = splits [5 :]
153
- assert ( len (splits ) == 0 )
154
- addr .tags .append (('r' , route ))
156
+ assert len (splits ) == 0
157
+ addr .tags .append (("r" , route ))
155
158
return lnencode (addr , options .privkey )
156
159
157
160
158
161
def lnencode (addr , privkey ):
159
162
if addr .amount :
160
163
amount = Decimal (str (addr .amount ))
161
164
# We can only send down to millisatoshi.
162
- if amount * 10 ** 12 % 10 :
163
- raise ValueError ("Cannot encode {}: too many decimal places" .format (
164
- addr .amount ))
165
+ if amount * 10 ** 12 % 10 :
166
+ raise ValueError (
167
+ "Cannot encode {}: too many decimal places" .format (addr .amount )
168
+ )
165
169
166
170
amount = addr .currency + shorten_amount (amount )
167
171
else :
168
- amount = addr .currency if addr .currency else ''
172
+ amount = addr .currency if addr .currency else ""
169
173
170
- hrp = 'ln' + amount
174
+ hrp = "ln" + amount
171
175
172
176
# Start with the timestamp
173
- data = bitstring .pack (' uint:35' , addr .date )
177
+ data = bitstring .pack (" uint:35" , addr .date )
174
178
175
179
# Payment hash
176
- data += tagged_bytes ('p' , addr .paymenthash )
180
+ data += tagged_bytes ("p" , addr .paymenthash )
177
181
tags_set = set ()
178
182
179
183
for k , v in addr .tags :
180
184
181
185
# BOLT #11:
182
186
#
183
187
# A writer MUST NOT include more than one `d`, `h`, `n` or `x` fields,
184
- if k in ('d' , 'h' , 'n' , 'x' ):
188
+ if k in ("d" , "h" , "n" , "x" ):
185
189
if k in tags_set :
186
190
raise ValueError ("Duplicate '{}' tag" .format (k ))
187
191
188
- if k == 'r' :
192
+ if k == "r" :
189
193
route = bitstring .BitArray ()
190
194
for step in v :
191
195
pubkey , channel , feebase , feerate , cltv = step
192
- route .append (bitstring .BitArray (pubkey ) + bitstring .BitArray (channel ) + bitstring .pack ('intbe:32' , feebase ) + bitstring .pack ('intbe:32' , feerate ) + bitstring .pack ('intbe:16' , cltv ))
193
- data += tagged ('r' , route )
194
- elif k == 'f' :
196
+ route .append (
197
+ bitstring .BitArray (pubkey )
198
+ + bitstring .BitArray (channel )
199
+ + bitstring .pack ("intbe:32" , feebase )
200
+ + bitstring .pack ("intbe:32" , feerate )
201
+ + bitstring .pack ("intbe:16" , cltv )
202
+ )
203
+ data += tagged ("r" , route )
204
+ elif k == "f" :
195
205
data += encode_fallback (v , addr .currency )
196
- elif k == 'd' :
197
- data += tagged_bytes ('d' , v .encode ())
198
- elif k == 'x' :
206
+ elif k == "d" :
207
+ data += tagged_bytes ("d" , v .encode ())
208
+ elif k == "x" :
199
209
# Get minimal length by trimming leading 5 bits at a time.
200
- expirybits = bitstring .pack (' intbe:64' , v )[4 :64 ]
201
- while expirybits .startswith (' 0b00000' ):
210
+ expirybits = bitstring .pack (" intbe:64" , v )[4 :64 ]
211
+ while expirybits .startswith (" 0b00000" ):
202
212
expirybits = expirybits [5 :]
203
- data += tagged ('x' , expirybits )
204
- elif k == 'h' :
205
- data += tagged_bytes ('h' , hashlib .sha256 (v .encode (' utf-8' )).digest ())
206
- elif k == 'n' :
207
- data += tagged_bytes ('n' , v )
213
+ data += tagged ("x" , expirybits )
214
+ elif k == "h" :
215
+ data += tagged_bytes ("h" , hashlib .sha256 (v .encode (" utf-8" )).digest ())
216
+ elif k == "n" :
217
+ data += tagged_bytes ("n" , v )
208
218
else :
209
219
# FIXME: Support unknown tags?
210
220
raise ValueError ("Unknown tag {}" .format (k ))
@@ -215,40 +225,45 @@ def lnencode(addr, privkey):
215
225
#
216
226
# A writer MUST include either a `d` or `h` field, and MUST NOT include
217
227
# both.
218
- if 'd' in tags_set and 'h' in tags_set :
228
+ if "d" in tags_set and "h" in tags_set :
219
229
raise ValueError ("Cannot include both 'd' and 'h'" )
220
- if not 'd' in tags_set and not 'h' in tags_set :
230
+ if not "d" in tags_set and not "h" in tags_set :
221
231
raise ValueError ("Must include either 'd' or 'h'" )
222
-
232
+
223
233
# We actually sign the hrp, then data (padded to 8 bits with zeroes).
224
234
privkey = secp256k1 .PrivateKey (bytes (unhexlify (privkey )))
225
- sig = privkey .ecdsa_sign_recoverable (bytearray ([ord (c ) for c in hrp ]) + data .tobytes ())
235
+ sig = privkey .ecdsa_sign_recoverable (
236
+ bytearray ([ord (c ) for c in hrp ]) + data .tobytes ()
237
+ )
226
238
# This doesn't actually serialize, but returns a pair of values :(
227
239
sig , recid = privkey .ecdsa_recoverable_serialize (sig )
228
240
data += bytes (sig ) + bytes ([recid ])
229
241
230
242
return bech32_encode (hrp , bitarray_to_u5 (data ))
231
243
244
+
232
245
class LnAddr (object ):
233
- def __init__ (self , paymenthash = None , amount = None , currency = 'bc' , tags = None , date = None ):
246
+ def __init__ (
247
+ self , paymenthash = None , amount = None , currency = "bc" , tags = None , date = None
248
+ ):
234
249
self .date = int (time .time ()) if not date else int (date )
235
250
self .tags = [] if not tags else tags
236
251
self .unknown_tags = []
237
- self .paymenthash = paymenthash
252
+ self .paymenthash = paymenthash
238
253
self .signature = None
239
254
self .pubkey = None
240
255
self .currency = currency
241
256
self .amount = amount
242
257
243
258
def __str__ (self ):
244
259
return "LnAddr[{}, amount={}{} tags=[{}]]" .format (
245
- hexlify (self .pubkey .serialize ()).decode ('utf-8' ),
246
- self .amount , self .currency ,
247
- ", " .join ([k + '=' + str (v ) for k , v in self .tags ])
260
+ hexlify (self .pubkey .serialize ()).decode ("utf-8" ),
261
+ self .amount ,
262
+ self .currency ,
263
+ ", " .join ([k + "=" + str (v ) for k , v in self .tags ]),
248
264
)
249
265
250
266
251
-
252
267
def _unshorten_amount (amount : str ) -> int :
253
268
"""Given a shortened amount, return millisatoshis"""
254
269
# BOLT #11:
0 commit comments