11import base64
22import http .client
33import json
4+ import logging
45import os
56import ssl
67from abc import ABC , abstractmethod
@@ -112,12 +113,14 @@ def to_lnd_chanpolicy(self, capacity):
112113
113114class LNNode (ABC ):
114115 @abstractmethod
115- def __init__ (self , pod_name , logger = None ):
116- self .log = logger
116+ def __init__ (self , pod_name ):
117117 self .name = pod_name
118-
119- def setLogger (self , logger ):
120- self .log = logger
118+ self .log = logging .getLogger (pod_name )
119+ handler = logging .StreamHandler ()
120+ formatter = logging .Formatter (f'%(name)-8s - %(levelname)s: %(message)s' )
121+ handler .setFormatter (formatter )
122+ self .log .addHandler (handler )
123+ self .log .setLevel (logging .INFO )
121124
122125 @staticmethod
123126 def param_dict_to_list (params : dict ) -> list [str ]:
@@ -164,11 +167,78 @@ def update(self, txid_hex: str, policy: dict, capacity: int) -> dict:
164167
165168
166169class CLN (LNNode ):
167- def __init__ (self , pod_name , logger = None ):
168- super ().__init__ (pod_name , logger )
170+ def __init__ (self , pod_name ):
171+ super ().__init__ (pod_name )
172+ self .conn = http .client .HTTPSConnection (
173+ host = pod_name , port = 8080 , timeout = 5 , context = INSECURE_CONTEXT
174+ )
169175 self .headers = {}
170176 self .impl = "cln"
171177
178+ def reset_connection (self ):
179+ self .conn = http .client .HTTPSConnection (
180+ host = self .name , port = 3010 , timeout = 5 , context = INSECURE_CONTEXT
181+ )
182+
183+ def setRune (self , rune ):
184+ self .headers = {
185+ "Rune" : rune
186+ }
187+
188+ def get (self , uri ):
189+ attempt = 0
190+ while True :
191+ try :
192+ self .log .warning (f"headers: { self .headers } " )
193+ self .conn .request (
194+ method = "GET" ,
195+ url = uri ,
196+ headers = self .headers ,
197+ )
198+ return self .conn .getresponse ().read ().decode ("utf8" )
199+ except Exception as e :
200+ self .reset_connection ()
201+ attempt += 1
202+ if attempt > 5 :
203+ self .log .error (f"Error CLN GET, Abort: { e } " )
204+ return None
205+ sleep (1 )
206+
207+ def post (self , uri , data = {}):
208+ body = json .dumps (data )
209+ post_header = self .headers
210+ post_header ["Content-Length" ] = str (len (body ))
211+ post_header ["Content-Type" ] = "application/json"
212+ attempt = 0
213+ while True :
214+ try :
215+ self .conn .request (
216+ method = "POST" ,
217+ url = uri ,
218+ body = body ,
219+ headers = post_header ,
220+ )
221+ # Stream output, otherwise we get a timeout error
222+ res = self .conn .getresponse ()
223+ stream = ""
224+ while True :
225+ try :
226+ data = res .read (1 )
227+ if len (data ) == 0 :
228+ break
229+ else :
230+ stream += data .decode ("utf8" )
231+ except Exception :
232+ break
233+ return stream
234+ except Exception as e :
235+ self .reset_connection ()
236+ attempt += 1
237+ if attempt > 5 :
238+ self .log .error (f"Error CLN POST, Abort: { e } " )
239+ return None
240+ sleep (1 )
241+
172242 def rpc (
173243 self ,
174244 method : str ,
@@ -191,12 +261,26 @@ def rpc(
191261 self .log .error (f"CLN rpc error: { e } , wait and retry..." )
192262 sleep (2 )
193263 return None
264+
265+ def createrune (self , max_tries = 2 ):
266+ attempt = 0
267+ while attempt < max_tries :
268+ attempt += 1
269+ response = self .rpc ("createrune" )
270+ if not response :
271+ sleep (2 )
272+ continue
273+ res = json .loads (response )
274+ self .setRune (res ["rune" ])
275+ return
276+ raise Exception (f"Unable to fetch rune from { self .name } " )
194277
195278 def newaddress (self , max_tries = 2 ):
279+ self .createrune ()
196280 attempt = 0
197281 while attempt < max_tries :
198282 attempt += 1
199- response = self .rpc ( " newaddr" )
283+ response = self .post ( "/v1/ newaddr" )
200284 if not response :
201285 sleep (2 )
202286 continue
@@ -211,7 +295,7 @@ def newaddress(self, max_tries=2):
211295 return False , ""
212296
213297 def uri (self ):
214- res = json .loads (self .rpc ( " getinfo" ))
298+ res = json .loads (self .post ( "/v1/ getinfo" ))
215299 if len (res ["address" ]) < 1 :
216300 return None
217301 return f"{ res ['id' ]} @{ res ['address' ][0 ]['address' ]} :{ res ['address' ][0 ]['port' ]} "
@@ -220,7 +304,7 @@ def walletbalance(self, max_tries=2) -> int:
220304 attempt = 0
221305 while attempt < max_tries :
222306 attempt += 1
223- response = self .rpc ( " listfunds" )
307+ response = self .post ( "/v1/ listfunds" )
224308 if not response :
225309 sleep (2 )
226310 continue
@@ -232,7 +316,7 @@ def channelbalance(self, max_tries=2) -> int:
232316 attempt = 0
233317 while attempt < max_tries :
234318 attempt += 1
235- response = self .rpc ( " listfunds" )
319+ response = self .post ( "/v1/ listfunds" )
236320 if not response :
237321 sleep (2 )
238322 continue
@@ -244,7 +328,7 @@ def connect(self, target_uri, max_tries=5) -> dict:
244328 attempt = 0
245329 while attempt < max_tries :
246330 attempt += 1
247- response = self .rpc ( " connect" , [ target_uri ] )
331+ response = self .post ( "/v1/ connect" , { "id" : target_uri } )
248332 if response :
249333 res = json .loads (response )
250334 if "id" in res :
@@ -269,7 +353,7 @@ def channel(self, pk, capacity, push_amt, fee_rate, max_tries=5) -> dict:
269353 attempt = 0
270354 while attempt < max_tries :
271355 attempt += 1
272- response = self .rpc ( " fundchannel" , self . param_dict_to_list ( data ) )
356+ response = self .post ( "/v1/ fundchannel" , data )
273357 if response :
274358 res = json .loads (response )
275359 if "txid" in res :
@@ -283,14 +367,14 @@ def channel(self, pk, capacity, push_amt, fee_rate, max_tries=5) -> dict:
283367 return None
284368
285369 def createinvoice (self , sats , label , description = "new invoice" ) -> str :
286- response = self .rpc ("invoice" , [ sats * 1000 , label , description ] )
370+ response = self .post ("invoice" , { "amount_msat" : sats * 1000 , " label" : label , " description" : description } )
287371 if response :
288372 res = json .loads (response )
289373 return res ["bolt11" ]
290374 return None
291375
292376 def payinvoice (self , payment_request ) -> str :
293- response = self .rpc ("pay" , [ payment_request ] )
377+ response = self .rpc ("pay" , { "bolt11" : payment_request } )
294378 if response :
295379 res = json .loads (response )
296380 if "code" in res :
@@ -303,7 +387,7 @@ def graph(self, max_tries=2) -> dict:
303387 attempt = 0
304388 while attempt < max_tries :
305389 attempt += 1
306- response = self .rpc ( " listchannels" )
390+ response = self .post ( "/v1/ listchannels" )
307391 if response :
308392 res = json .loads (response )
309393 if "channels" in res :
@@ -329,8 +413,8 @@ def update(self, txid_hex: str, policy: dict, capacity: int, max_tries=2) -> dic
329413
330414
331415class LND (LNNode ):
332- def __init__ (self , pod_name , logger = None ):
333- super ().__init__ (pod_name , logger )
416+ def __init__ (self , pod_name ):
417+ super ().__init__ (pod_name )
334418 self .conn = http .client .HTTPSConnection (
335419 host = pod_name , port = 8080 , timeout = 5 , context = INSECURE_CONTEXT
336420 )
0 commit comments