Skip to content

Commit

Permalink
Improve start/stop of the NIP46 Client/Sever
Browse files Browse the repository at this point in the history
  • Loading branch information
monty committed Jun 13, 2024
1 parent b941215 commit 55cbc42
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 48 deletions.
54 changes: 31 additions & 23 deletions examples/nip46_client_post.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,44 @@ async def do_post(url, text):
"""
Example showing how to post a text note (Kind 1) to relay
using nip46 signer - we don't have access locally to the keys
here we use a with block but for long running you can also do -
my_signer = NIP46Signer(connection=con_str, auto_start=True)
or manually -
my_signer = NIP46Signer(connection=con_str)
my_signer.run()
then call end() when done... probably you shouldn't call run on a previously ended signer...
"""

# bunker://... e.g. printed out by nip46_signer_service.py
con_str = input('connection string: ').strip()

# we'll make post and they'll be signed by the bunker at con_str
my_signer = NIP46Signer(connection=con_str)

# from here it's just a signer interface same as if we were using BasicKeySigner
async with Client(url) as c:
# plain text
n_msg = await my_signer.ready_post(Event(kind=Event.KIND_TEXT_NOTE,
content=text))
c.publish(n_msg)

# nip4 encrypted to our self
enc_event = Event(kind=Event.KIND_TEXT_NOTE,
content=text+' - encrypted nip4')
enc_event = await my_signer.nip4_encrypt_event(enc_event,
to_pub_k=await my_signer.get_public_key())
await my_signer.sign_event(enc_event)
c.publish(enc_event)

# nip44 encrypted to our self
enc_event = Event(kind=Event.KIND_TEXT_NOTE,
content=text + ' - encrypted nip44')
enc_event = await my_signer.nip44_encrypt_event(enc_event,
to_pub_k=await my_signer.get_public_key())
await my_signer.sign_event(enc_event)
c.publish(enc_event)
async with NIP46Signer(connection=con_str) as my_signer:
# we'll make post and they'll be signed by the bunker at con_str
# plain text
n_msg = await my_signer.ready_post(Event(kind=Event.KIND_TEXT_NOTE,
content=text))
c.publish(n_msg)

# nip4 encrypted to our self
enc_event = Event(kind=Event.KIND_TEXT_NOTE,
content=text+' - encrypted nip4')
enc_event = await my_signer.nip4_encrypt_event(enc_event,
to_pub_k=await my_signer.get_public_key())
await my_signer.sign_event(enc_event)
c.publish(enc_event)

# nip44 encrypted to our self
enc_event = Event(kind=Event.KIND_TEXT_NOTE,
content=text + ' - encrypted nip44')
enc_event = await my_signer.nip44_encrypt_event(enc_event,
to_pub_k=await my_signer.get_public_key())
await my_signer.sign_event(enc_event)
c.publish(enc_event)


if __name__ == "__main__":
Expand Down
3 changes: 1 addition & 2 deletions examples/nip46_signer_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ async def run_signer():
print(await my_signer.bunker_url)

# wait forever...
while True:
await asyncio.sleep(0.1)
await my_signer.run()

if __name__ == "__main__":
logging.getLogger().setLevel(logging.DEBUG)
Expand Down
82 changes: 59 additions & 23 deletions src/monstr/signing/nip46.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,33 +43,14 @@ def __init__(self,
if isinstance(relay, str):
self._relay = [relay]

# make client obj that will actually do the comm
async def aconnect(my_client: Client):
# sub any NIP46 events to our pub_k
my_client.subscribe(
handlers=[self],
filters={
'#p': [await self._comm_signer.get_public_key()],
'kinds': [Event.KIND_NIP46]
}
)

def on_connect(my_client: Client):
asyncio.create_task(aconnect(my_client))

self._run = True
self._client = ClientPool(self._relay,
on_connect=on_connect)
self._client = ClientPool(relay)
self._run = False

# events queued and dealt with serially as they come in
self._event_q: asyncio.Queue = asyncio.Queue()
# start a process to work on the queued events
self._event_process_task = asyncio.create_task(self._my_event_consumer())

# TODO: we should probably have start and end, etc as client
# not just start a task here...
asyncio.create_task(self._client.run())

# called when we see method events - most likely when we're acting as signer
self._on_command = on_command

Expand All @@ -88,9 +69,12 @@ async def bunker_url(self):
async def bunker_key(self):
return await self._comm_signer.get_public_key()

@property
def running(self) -> bool:
return self._run

async def _get_msg_event(self, content: str, to_k: str) -> Event:
# returns encrypted and signed method for content
print(content)
# encrypt the content
content = await self._comm_signer.nip4_encrypt(content, to_pub_k=to_k)

Expand Down Expand Up @@ -184,11 +168,36 @@ async def do_request(self, method: str, params: [str], to_k, id: str = None):

return id

def run(self):
self._run = True

# make client obj that will actually do the comm
async def aconnect(my_client: Client):
# sub any NIP46 events to our pub_k
my_client.subscribe(
handlers=[self],
filters={
'#p': [await self._comm_signer.get_public_key()],
'kinds': [Event.KIND_NIP46]
}
)

def on_connect(my_client: Client):
asyncio.create_task(aconnect(my_client))

self._client.set_on_connect(on_connect)
asyncio.create_task(self._client.run())

def end(self):
self._run = False
self._client.end()

async def __aenter__(self):
self.run()

async def __aexit__(self, exc_type, exc_val, exc_tb):
self.end()


class NIP46ServerConnection:
"""
Expand All @@ -215,6 +224,11 @@ def __init__(self,
# all keys that are connected
self._connections = set()

# call run to start, do it via asyncio.create_task if you want to
# do something else at same time, make sure to call end() when done
self._run = False


@property
async def bunker_url(self):
# this will be different every time we're run
Expand Down Expand Up @@ -366,7 +380,14 @@ async def _do_command(self, id: str, method: str, params: [str], evt: Event) ->

return ret

async def run(self):
self._run = True
self._comm.run()
while self._run is True:
await asyncio.sleep(0.1)

def end(self):
self._run = False
self._comm.end()


Expand Down Expand Up @@ -406,7 +427,8 @@ class NIP46Signer(SignerInterface):
for now expects connection str, todo add where we initialise the connection
"""
def __init__(self, connection: str):
def __init__(self, connection: str,
auto_start=False):
parsed = urlparse(connection)

if parsed.scheme != 'bunker':
Expand All @@ -431,6 +453,8 @@ def __init__(self, connection: str):
# does the comm between us and the NIP46 server, we use ephemeral keys for signing
self._comm = NIP46Comm(relay=self._relay,
on_response=self._do_response)
if auto_start:
self.run()

# repsonses we waiting for key'd on id
self._responses = {}
Expand Down Expand Up @@ -530,3 +554,15 @@ async def nip44_decrypt_event(self, evt: Event) -> Event:
self._enc = NIP44SignerEncrypter(self)
return await self._enc.adecrypt_event(evt)

def run(self):
self._comm.run()

def end(self):
self._comm.end()

async def __aenter__(self):
self._comm.run()
return self

async def __aexit__(self, exc_type, exc_val, exc_tb):
self._comm.end()

0 comments on commit 55cbc42

Please sign in to comment.