Reticulum/tests/link.py
2024-10-07 10:52:43 +02:00

799 lines
28 KiB
Python

import unittest
import subprocess
import shlex
import threading
import time
import random
from unittest import skipIf
import RNS
import os
from tests.channel import MessageTest
from RNS.Channel import MessageBase
from RNS.Buffer import StreamDataMessage
from RNS.Interfaces.LocalInterface import LocalClientInterface
from math import ceil
APP_NAME = "rns_unit_tests"
fixed_keys = [
("f8953ffaf607627e615603ff1530c82c434cf87c07179dd7689ea776f30b964cfb7ba6164af00c5111a45e69e57d885e1285f8dbfe3a21e95ae17cf676b0f8b7", "650b5d76b6bec0390d1f8cfca5bd33f9"),
("d85d036245436a3c33d3228affae06721f8203bc364ee0ee7556368ac62add650ebf8f926abf628da9d92baaa12db89bd6516ee92ec29765f3afafcb8622d697", "1469e89450c361b253aefb0c606b6111"),
("8893e2bfd30fc08455997caf7abb7a6341716768dbbf9a91cc1455bd7eeaf74cdc10ec72a4d4179696040bac620ee97ebc861e2443e5270537ae766d91b58181", "e5fe93ee4acba095b3b9b6541515ed3e"),
("b82c7a4f047561d974de7e38538281d7f005d3663615f30d9663bad35a716063c931672cd452175d55bcdd70bb7aa35a9706872a97963dc52029938ea7341b39", "1333b911fa8ebb16726996adbe3c6262"),
("08bb35f92b06a0832991165a0d9b4fd91af7b7765ce4572aa6222070b11b767092b61b0fd18b3a59cae6deb9db6d4bfb1c7fcfe076cfd66eea7ddd5f877543b9", "d13712efc45ef87674fb5ac26c37c912"),
]
BUFFER_TEST_TARGET = 32000
def targets_job(caller):
cmd = "python -c \"from tests.link import targets; targets()\""
print(f"Opening subprocess for {cmd}...", RNS.LOG_VERBOSE)
ppath = os.getcwd()
try:
caller.process = subprocess.Popen(shlex.split(cmd), cwd=ppath, stdout=subprocess.PIPE)
except Exception as e:
raise e
caller.pipe_is_open = False
c_rns = None
def init_rns(caller=None):
global c_rns
if c_rns == None:
if caller != None:
targets_job(caller)
time.sleep(2)
print("Starting local RNS instance...")
c_rns = RNS.Reticulum("./tests/rnsconfig")
if caller != None:
c_rns.m_proc = caller.process
print("Done starting local RNS instance...")
def close_rns():
global c_rns
if c_rns != None:
c_rns.m_proc.kill()
class TestLink(unittest.TestCase):
def setUp(self):
pass
@classmethod
def tearDownClass(cls):
close_rns()
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_0_valid_announce(self):
init_rns(self)
print("")
fid = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[3][0]))
dst = RNS.Destination(fid, RNS.Destination.IN, RNS.Destination.SINGLE, "test", "announce")
ap = dst.announce(send=False)
ap.pack()
self.assertEqual(RNS.Identity.validate_announce(ap), True)
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_1_invalid_announce(self):
init_rns(self)
print("")
fid = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[4][0]))
dst = RNS.Destination(fid, RNS.Destination.IN, RNS.Destination.SINGLE, "test", "announce")
ap = dst.announce(send=False)
fake_dst = bytes.fromhex("1333b911fa8ebb16726996adbe3c6262")
pre_len = len(ap.data)
ap.data = fake_dst+ap.data[16:]
self.assertEqual(pre_len, len(ap.data))
ap.pack()
ap.send()
self.assertEqual(RNS.Identity.validate_announce(ap), False)
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_2_establish(self):
init_rns(self)
print("")
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_3_packets(self):
init_rns(self)
print("")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
b = 0
pr_t = 0
receipts = []
if RNS.Cryptography.backend() == "internal" or RNS.Reticulum.MTU > 500:
num_packets = 50
else:
num_packets = 500
packet_size = RNS.Link.MDU
pstart = time.time()
print(f"Sending {num_packets} link packets of {packet_size} bytes...")
for i in range(0, num_packets):
time.sleep(0.003)
b += packet_size
data = os.urandom(packet_size)
start = time.time()
p = RNS.Packet(l1, data)
receipts.append(p.send())
pr_t += time.time() - start
print(f"Sent {self.size_str(b)}, {self.size_str(b / pr_t, 'b')}ps")
print("Checking receipts...", end=" ")
all_ok = False
receipt_timeout = time.time() + 35
while not all_ok and time.time() < receipt_timeout:
for r in receipts:
all_ok = True
if not r.status == RNS.PacketReceipt.DELIVERED:
all_ok = False
break
pduration = time.time()-pstart
n_failed = 0
for r in receipts:
if not r.status == RNS.PacketReceipt.DELIVERED:
n_failed += 1
if n_failed > 0:
ns = "s" if n_failed != 1 else ""
print(f"Failed to receive proof for {n_failed} packet{ns}")
self.assertEqual(all_ok, True)
print("OK!")
print(f"Single packet and proof round-trip throughput is {self.size_str(b / pduration, 'b')}ps")
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_4_micro_resource(self):
init_rns(self)
print("")
print("Micro resource test")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
resource_timeout = 120
resource_size = 128
data = os.urandom(resource_size)
print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_5_mini_resource(self):
init_rns(self)
print("")
print("Mini resource test")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
resource_timeout = 120
resource_size = 256*1000
data = os.urandom(resource_size)
print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_6_small_resource(self):
init_rns(self)
print("")
print("Small resource test")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
resource_timeout = 120
resource_size = 1000*1000
data = os.urandom(resource_size)
print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_7_medium_resource(self):
if RNS.Cryptography.backend() == "internal":
print("Skipping medium resource test...")
return
init_rns(self)
print("")
print("Medium resource test")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
resource_timeout = 120
resource_size = 5*1000*1000
data = os.urandom(resource_size)
print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
large_resource_status = None
def lr_callback(self, resource):
TestLink.large_resource_status = resource.status
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_9_large_resource(self):
if RNS.Cryptography.backend() == "internal":
print("Skipping large resource test...")
return
init_rns(self)
print("")
print("Large resource test")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
resource_timeout = 120
resource_size = 50*1000*1000
data = os.urandom(resource_size)
print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback)
start = time.time()
TestLink.large_resource_status = resource.status
while TestLink.large_resource_status < RNS.Resource.COMPLETE:
time.sleep(0.01)
t = time.time() - start
self.assertEqual(TestLink.large_resource_status, RNS.Resource.COMPLETE)
print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
#@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_10_channel_round_trip(self):
global c_rns
init_rns(self)
print("")
print("Channel round trip test")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(1)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
received = []
def handle_message(message: MessageBase):
received.append(message)
test_message = MessageTest()
test_message.data = "Hello"
channel = l1.get_channel()
channel.register_message_type(MessageTest)
channel.add_message_handler(handle_message)
channel.send(test_message)
time.sleep(0.5)
self.assertEqual(1, len(received))
rx_message = received[0]
self.assertIsInstance(rx_message, MessageTest)
self.assertEqual("Hello back", rx_message.data)
self.assertEqual(test_message.id, rx_message.id)
self.assertNotEqual(test_message.not_serialized, rx_message.not_serialized)
self.assertEqual(0, len(l1._channel._rx_ring))
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
self.assertEqual(0, len(l1._channel._rx_ring))
# @skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_11_buffer_round_trip(self):
global c_rns
init_rns(self)
print("")
print("Buffer round trip test")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
time.sleep(1)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
buffer = None
received = []
def handle_data(ready_bytes: int):
data = buffer.read(ready_bytes)
received.append(data)
channel = l1.get_channel()
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_data)
buffer.write(b"Hi there")
buffer.flush()
time.sleep(0.5)
self.assertEqual(1 , len(received))
rx_message = received[0].decode("utf-8")
self.assertEqual("Hi there back at you", rx_message)
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
# @skipIf(os.getenv('SKIP_NORMAL_TESTS') != None and os.getenv('RUN_SLOW_TESTS') == None, "Skipping")
def test_12_buffer_round_trip_big(self, local_bitrate = None):
global c_rns, buffer_read_target
init_rns(self)
print("")
print("Buffer round trip test")
local_interface = next(filter(lambda iface: isinstance(iface, LocalClientInterface), RNS.Transport.interfaces), None)
self.assertIsNotNone(local_interface)
original_bitrate = local_interface.bitrate
try:
if local_bitrate is not None:
local_interface.bitrate = local_bitrate
local_interface._force_bitrate = True
print(f"Forcing local bitrate of {local_bitrate} bps ({round(local_bitrate / 8, 0)} B/s)")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest)
# delay a reasonable time for link to come up at current bitrate
link_sleep = max(RNS.Link.MDU * 3 / local_interface.bitrate * 8, 2)
timeout_at = time.time() + link_sleep
print(f"Waiting {round(link_sleep, 1)} sec for link to come up")
while l1.status != RNS.Link.ACTIVE and time.time() < timeout_at:
time.sleep(0.01)
self.assertEqual(l1.status, RNS.Link.ACTIVE)
buffer = None
received = []
def handle_data(ready_bytes: int):
global received_bytes
data = buffer.read(ready_bytes)
received.append(data)
channel = l1.get_channel()
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_data)
# try to make the message big enough to split across packets, but
# small enough to make the test complete in a reasonable amount of time
# seed_text = "0123456789"
# message = seed_text*ceil(min(max(local_interface.bitrate / 8,
# StreamDataMessage.MAX_DATA_LEN * 2 / len(seed_text)),
# 1000))
if local_interface.bitrate < 1000:
target_bytes = 3000
else:
target_bytes = BUFFER_TEST_TARGET
random.seed(154889)
message = random.randbytes(target_bytes)
buffer_read_target = len(message)
# the return message will have an appendage string " back at you"
# for every StreamDataMessage that arrives. To verify, we need
# to insert that string every MAX_DATA_LEN and also at the end.
expected_rx_message = b""
for i in range(0, len(message)):
if i > 0 and (i % StreamDataMessage.MAX_DATA_LEN) == 0:
expected_rx_message += b" back at you"
expected_rx_message += bytes([message[i]])
expected_rx_message += b" back at you"
# since the segments will be received at max length for a
# StreamDataMessage, the appended text will end up in a
# separate packet.
print(f"Sending {len(message)} bytes, receiving {len(expected_rx_message)} bytes, ")
buffer.write(message)
buffer.flush()
timeout = time.time() + 4
while not time.time() > timeout:
time.sleep(1)
print(f"Received {len(received)} chunks so far")
time.sleep(1)
data = bytearray()
for rx in received:
data.extend(rx)
rx_message = data
print(f"Received {len(received)} chunks, totalling {len(rx_message)} bytes")
self.assertEqual(len(expected_rx_message), len(rx_message))
for i in range(0, len(expected_rx_message)):
self.assertEqual(expected_rx_message[i], rx_message[i])
self.assertEqual(expected_rx_message, rx_message)
l1.teardown()
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
finally:
local_interface.bitrate = original_bitrate
local_interface._force_bitrate = False
# Run with
# RUN_SLOW_TESTS=1 python tests/link.py TestLink.test_13_buffer_round_trip_big_slow
# Or
# make RUN_SLOW_TESTS=1 test
@skipIf(os.getenv('RUN_SLOW_TESTS') == None, "Not running slow tests")
def test_13_buffer_round_trip_big_slow(self):
self.test_12_buffer_round_trip_big(local_bitrate=410)
def size_str(self, num, suffix='B'):
units = ['','K','M','G','T','P','E','Z']
last_unit = 'Y'
if suffix == 'b':
num *= 8
units = ['','K','M','G','T','P','E','Z']
last_unit = 'Y'
for unit in units:
if abs(num) < 1000.0:
if unit == "":
return f"{num:.0f} {unit}{suffix}"
else:
return f"{num:.2f} {unit}{suffix}"
num /= 1000.0
return f"{num:.2f}{last_unit}{suffix}"
if __name__ == '__main__':
unittest.main(verbosity=1)
buffer_read_len = 0
def targets(yp=False):
if yp:
import yappi
def resource_started(resource):
print("Resource started")
if yp:
yappi.start()
def resource_concluded(resource):
print("Resource concluded")
if yp:
try:
yappi.stop()
yappi.get_func_stats().save("receiver_main_calls.data", type="pstat")
threads = yappi.get_thread_stats()
for thread in threads:
print(
f"Function stats for ({thread.name}) ({int(thread.id)})"
) # it is the Thread.__class__.__name__
yappi.get_func_stats(ctx_id=thread.id).save(f"receiver_thread_{thread.id}.data", type="pstat")
except Exception as e:
print(f"Error: {e}")
if hasattr(resource.link.attached_interface, "rxptime"):
rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime
print(f"Average RX proccessing rate: {size_str(rx_pr, 'b')}ps")
def link_established(link):
print("Link established")
link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
link.set_resource_started_callback(resource_started)
link.set_resource_concluded_callback(resource_concluded)
channel = link.get_channel()
def handle_message(message):
if isinstance(message, MessageTest):
message.data = f"{message.data} back"
channel.send(message)
channel.register_message_type(MessageTest)
channel.add_message_handler(handle_message)
buffer = None
response_data = []
def handle_buffer(ready_bytes: int):
global buffer_read_len, BUFFER_TEST_TARGET
data = buffer.read(ready_bytes)
buffer_read_len += len(data)
response_data.append(data)
if data == b"Hi there":
RNS.log("Sending response")
for data in response_data:
buffer.write(data + b" back at you")
buffer.flush()
buffer_read_len = 0
if buffer_read_len == BUFFER_TEST_TARGET:
RNS.log("Sending response")
for data in response_data:
buffer.write(data + b" back at you")
buffer.flush()
buffer_read_len = 0
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_buffer)
m_rns = RNS.Reticulum("./tests/rnsconfig", logdest=RNS.LOG_FILE, loglevel=RNS.LOG_EXTREME)
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
d1 = RNS.Destination(id1, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
d1.set_proof_strategy(RNS.Destination.PROVE_ALL)
d1.set_link_established_callback(link_established)
while True:
time.sleep(1)
def targets_profiling(yp=False):
targets(yp)
def profile_resource():
# import cProfile
# import pstats
# from pstats import SortKey
# cProfile.runctx("entry()", {"entry": resource_profiling, "size_str": size_str}, {}, "profile-resource.data")
# p = pstats.Stats("profile-resource.data")
resource_profiling()
def profile_targets():
targets_profiling(yp=True)
# cProfile.runctx("entry()", {"entry": targets_profiling, "size_str": size_str}, {}, "profile-targets.data")
# p = pstats.Stats("profile-targets.data")
# p.strip_dirs().sort_stats(SortKey.TIME, SortKey.CUMULATIVE).print_stats()
def resource_profiling():
init_rns()
print("")
# TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
l1 = RNS.Link(dest)
time.sleep(0.5)
resource_timeout = 120
resource_size = 5*1000*1000
data = os.urandom(resource_size)
print(f"Sending {size_str(resource_size)} resource...")
import yappi
yappi.start()
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
time.sleep(1)
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
t = time.time() - start
print(f"Resource completed at {size_str(resource_size / t, 'b')}ps")
yappi.get_func_stats().save("sender_main_calls.data", type="pstat")
threads = yappi.get_thread_stats()
for thread in threads:
print(
f"Function stats for ({thread.name}) ({int(thread.id)})"
) # it is the Thread.__class__.__name__
yappi.get_func_stats(ctx_id=thread.id).save(f"sender_thread_{thread.id}.data", type="pstat")
# t_pstats = yappi.convert2pstats(tstats)
# t_pstats.save("resource_tstat.data", type="pstat")
if hasattr(resource.link.attached_interface, "rxptime"):
rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime
print(f"Average RX proccessing rate: {size_str(rx_pr, 'b')}ps")
l1.teardown()
time.sleep(0.5)
def size_str(num, suffix='B'):
units = ['','K','M','G','T','P','E','Z']
last_unit = 'Y'
if suffix == 'b':
num *= 8
units = ['','K','M','G','T','P','E','Z']
last_unit = 'Y'
for unit in units:
if abs(num) < 1000.0:
if unit == "":
return f"{num:.0f} {unit}{suffix}"
else:
return f"{num:.2f} {unit}{suffix}"
num /= 1000.0
return f"{num:.2f}{last_unit}{suffix}"