66import re
77import requests
88import threading
9+ import asyncio
910
1011from abc import ABC , abstractmethod
1112from typing import List
1213
14+ import ldk_node
1315from ldk_node import *
1416
1517DEFAULT_ESPLORA_SERVER_URL = "http://127.0.0.1:3002"
1820
1921class AbstractKvStore (ABC ):
2022 @abstractmethod
21- def list (self , primary_namespace : str ,secondary_namespace : str ) :
23+ async def list_async (self , primary_namespace : " str" ,secondary_namespace : " str" ) -> "typing.List[str]" :
2224 pass
2325
2426 @abstractmethod
25- def read (self , primary_namespace : str ,secondary_namespace : str , key : str ) :
27+ def list_sync (self , primary_namespace : " str" ,secondary_namespace : " str" ) -> "typing.List[ str]" :
2628 pass
2729
2830 @abstractmethod
29- def remove (self , primary_namespace : str ,secondary_namespace : str ,key : str , lazy : bool ) :
31+ async def read_async (self , primary_namespace : " str" ,secondary_namespace : " str" ,key : " str" ) -> "typing.List[int]" :
3032 pass
3133
3234 @abstractmethod
33- def write (self , primary_namespace : str ,secondary_namespace : str ,key : str ,buf : List [int ]):
35+ def read_sync (self , primary_namespace : "str" ,secondary_namespace : "str" ,key : "str" ) -> "typing.List[int]" :
36+ pass
37+
38+ @abstractmethod
39+ async def remove_async (self , primary_namespace : "str" ,secondary_namespace : "str" ,key : "str" ,lazy : "bool" ) -> None :
40+ pass
41+
42+ @abstractmethod
43+ def remove_sync (self , primary_namespace : "str" ,secondary_namespace : "str" ,key : "str" ,lazy : "bool" ) -> None :
44+ pass
45+
46+ @abstractmethod
47+ async def write_async (self , primary_namespace : "str" ,secondary_namespace : "str" ,key : "str" ,buf : "typing.List[int]" ) -> None :
48+ pass
49+
50+ @abstractmethod
51+ def write_sync (self , primary_namespace : "str" ,secondary_namespace : "str" ,key : "str" ,buf : "typing.List[int]" ) -> None :
3452 pass
3553
3654class TestKvStore (AbstractKvStore ):
@@ -40,14 +58,15 @@ def __init__(self, name: str):
4058 self .storage = {}
4159 self ._lock = threading .Lock ()
4260
43- def list (self , primary_namespace : str ,secondary_namespace : str ) -> List [str ]:
61+ # Sync methods
62+ def list_sync (self , primary_namespace : str , secondary_namespace : str ) -> List [str ]:
4463 with self ._lock :
4564 namespace_key = (primary_namespace , secondary_namespace )
4665 if namespace_key in self .storage :
4766 return list (self .storage [namespace_key ].keys ())
4867 return []
4968
50- def read (self , primary_namespace : str ,secondary_namespace : str ,key : str ) -> List [int ]:
69+ def read_sync (self , primary_namespace : str , secondary_namespace : str , key : str ) -> List [int ]:
5170 with self ._lock :
5271 print (f"[{ self .name } ] READ: { primary_namespace } /{ secondary_namespace } /{ key } " )
5372 namespace_key = (primary_namespace , secondary_namespace )
@@ -64,7 +83,15 @@ def read(self, primary_namespace: str,secondary_namespace: str,key: str) -> List
6483 print (f" -> returning { len (data )} bytes" )
6584 return data
6685
67- def remove (self , primary_namespace : str ,secondary_namespace : str ,key : str ,lazy : bool ) -> None :
86+ def write_sync (self , primary_namespace : str , secondary_namespace : str , key : str , buf : List [int ]) -> None :
87+ with self ._lock :
88+ namespace_key = (primary_namespace , secondary_namespace )
89+ if namespace_key not in self .storage :
90+ self .storage [namespace_key ] = {}
91+
92+ self .storage [namespace_key ][key ] = buf .copy ()
93+
94+ def remove_sync (self , primary_namespace : str , secondary_namespace : str , key : str , lazy : bool ) -> None :
6895 with self ._lock :
6996 namespace_key = (primary_namespace , secondary_namespace )
7097 if namespace_key not in self .storage :
@@ -78,13 +105,18 @@ def remove(self, primary_namespace: str,secondary_namespace: str,key: str,lazy:
78105 if not self .storage [namespace_key ]:
79106 del self .storage [namespace_key ]
80107
81- def write (self , primary_namespace : str ,secondary_namespace : str ,key : str ,buf : List [int ]) -> None :
82- with self ._lock :
83- namespace_key = (primary_namespace , secondary_namespace )
84- if namespace_key not in self .storage :
85- self .storage [namespace_key ] = {}
108+ # Async methods
109+ async def list_async (self , primary_namespace : str , secondary_namespace : str ) -> List [str ]:
110+ return self .list_sync (primary_namespace , secondary_namespace )
86111
87- self .storage [namespace_key ][key ] = buf .copy ()
112+ async def read_async (self , primary_namespace : str , secondary_namespace : str , key : str ) -> List [int ]:
113+ return self .read_sync (primary_namespace , secondary_namespace , key )
114+
115+ async def write_async (self , primary_namespace : str , secondary_namespace : str , key : str , buf : List [int ]) -> None :
116+ self .write_sync (primary_namespace , secondary_namespace , key , buf )
117+
118+ async def remove_async (self , primary_namespace : str , secondary_namespace : str , key : str , lazy : bool ) -> None :
119+ self .remove_sync (primary_namespace , secondary_namespace , key , lazy )
88120
89121 def dump (self ):
90122 print (f"\n [{ self .name } ] Store contents:" )
@@ -250,6 +282,17 @@ def setup_node_with_tier_store(tmp_dir, esplora_endpoint, listening_addresses):
250282 backoff_multiplier = 2.0
251283 )
252284
285+ # Set event loop for async Python callbacks from Rust
286+ loop = asyncio .new_event_loop ()
287+
288+ def run_loop ():
289+ asyncio .set_event_loop (loop )
290+ loop .run_forever ()
291+
292+ loop_thread = threading .Thread (target = run_loop , daemon = True )
293+ loop_thread .start ()
294+ ldk_node .uniffi_set_event_loop (loop )
295+
253296 builder = Builder .from_config (config )
254297 builder .set_storage_dir_path (tmp_dir )
255298 builder .set_chain_source_esplora (esplora_endpoint , None )
0 commit comments