Updated Echo example
This commit is contained in:
parent
0c49ca8458
commit
8082338657
@ -104,7 +104,7 @@ class Destination:
|
||||
def proof_requested_callback(self, callback):
|
||||
self.callbacks.proof_requested = callback
|
||||
|
||||
def setProofStrategy(self, proof_strategy):
|
||||
def set_proof_strategy(self, proof_strategy):
|
||||
if not proof_strategy in Destination.proof_strategies:
|
||||
raise TypeError("Unsupported proof strategy")
|
||||
else:
|
||||
|
@ -99,13 +99,12 @@ class Identity:
|
||||
announced_identity.loadPublicKey(public_key)
|
||||
|
||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||
RNS.log("Announce is valid", RNS.LOG_VERBOSE)
|
||||
RNS.Identity.remember(RNS.Identity.fullHash(packet.raw), destination_hash, public_key)
|
||||
RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_INFO)
|
||||
del announced_identity
|
||||
return True
|
||||
else:
|
||||
RNS.log("Announce is invalid", RNS.LOG_VERBOSE)
|
||||
RNS.log("Received invalid announce", RNS.LOG_DEBUG)
|
||||
del announced_identity
|
||||
return False
|
||||
|
||||
|
@ -253,6 +253,7 @@ class PacketReceipt:
|
||||
if proof_valid:
|
||||
self.status = PacketReceipt.DELIVERED
|
||||
self.proved = True
|
||||
self.concluded_at = time.time()
|
||||
if self.callbacks.delivery != None:
|
||||
self.callbacks.delivery(self)
|
||||
return True
|
||||
@ -267,6 +268,7 @@ class PacketReceipt:
|
||||
if proof_valid:
|
||||
self.status = PacketReceipt.DELIVERED
|
||||
self.proved = True
|
||||
self.concluded_at = time.time()
|
||||
if self.callbacks.delivery != None:
|
||||
self.callbacks.delivery(self)
|
||||
return True
|
||||
@ -275,6 +277,8 @@ class PacketReceipt:
|
||||
else:
|
||||
return False
|
||||
|
||||
def rtt(self):
|
||||
return self.concluded_at - self.sent_at
|
||||
|
||||
def isTimedOut(self):
|
||||
return (self.sent_at+self.timeout < time.time())
|
||||
|
@ -1,5 +1,11 @@
|
||||
##########################################################
|
||||
# This RNS example demonstrates a simple client/server #
|
||||
# echo utility. A client can send an echo request to the #
|
||||
# server, and the server will respond by proving receipt #
|
||||
# of the packet. #
|
||||
##########################################################
|
||||
|
||||
import argparse
|
||||
import time
|
||||
import RNS
|
||||
|
||||
# Let's define an app name. We'll use this for all
|
||||
@ -25,9 +31,16 @@ def server(configpath):
|
||||
# to read it.
|
||||
echo_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
|
||||
|
||||
# We configure the destination to automatically prove all
|
||||
# packets adressed to it. By doing this, RNS will automatically
|
||||
# generate a proof for each incoming packet and transmit it
|
||||
# back to the sender of that packet.
|
||||
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||
|
||||
# Tell the destination which function in our program to
|
||||
# run when a packet is received.
|
||||
echo_destination.setCallback(serverCallback)
|
||||
# run when a packet is received. We do this so we can
|
||||
# print a log message when the server receives a request
|
||||
echo_destination.packet_callback(server_callback)
|
||||
|
||||
# Everything's ready!
|
||||
# Let's Wait for client requests or user input
|
||||
@ -36,58 +49,26 @@ def server(configpath):
|
||||
|
||||
def announceLoop(destination):
|
||||
# Let the user know that everything is ready
|
||||
RNS.log("Echo server "+RNS.prettyhexrep(destination.hash)+" running, hit enter to send announce (Ctrl-C to quit)")
|
||||
RNS.log("Echo server "+RNS.prettyhexrep(destination.hash)+" running, hit enter to manually send an announce (Ctrl-C to quit)")
|
||||
|
||||
# We enter a loop that runs until the users exits.
|
||||
# If the user just hits enter, we will announce our server
|
||||
# destination on the network, which will let clients know
|
||||
# how to create messages directed towards it.
|
||||
# If the user hits enter, we will announce our server
|
||||
# destination on the network, which will let clients
|
||||
# know how to create messages directed towards it.
|
||||
while True:
|
||||
entered = raw_input()
|
||||
destination.announce()
|
||||
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
|
||||
|
||||
|
||||
def serverCallback(message, packet):
|
||||
# We have received am echo request from a client! When
|
||||
# a client sends a request, it will include the hash of
|
||||
# it's identity in the message. Since we know that the
|
||||
# client has created a listening destination using this
|
||||
# identity hash, we can construct an outgoing destination
|
||||
# to direct our response to. The hash is sent in binary
|
||||
# format, so we encode it as printable hexadecimal first,
|
||||
# since aspect names need to in printable text.
|
||||
client_identity_hexhash = message.encode("hex_codec")
|
||||
|
||||
# We can now create a destination that will let us reach
|
||||
# the client which send the echo request.
|
||||
reply_destination = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity_hexhash)
|
||||
|
||||
# Let's encode the reply destination hash in a readable
|
||||
# way, so we can output some info to the user.
|
||||
reply_destination_hexhash = reply_destination.hash.encode("hex_codec")
|
||||
|
||||
def server_callback(message, packet):
|
||||
# Tell the user that we received an echo request, and
|
||||
# that we are going to send a reply to the requester.
|
||||
RNS.log("Received packet from <"+reply_destination_hexhash+">, sending reply")
|
||||
|
||||
# To let the client know that we got the echo request,
|
||||
# we will use the "proof" functions of Reticulum. In most
|
||||
# applications, the proving of packets will occur fully
|
||||
# automatically, but in some cases like this, it can be
|
||||
# beneficial to use the functions manually, since it
|
||||
# neatly provides functionality that can unequivocally
|
||||
# prove the receipt of the request to the client.
|
||||
#
|
||||
# Using the proof functionality is very simple, we just
|
||||
# need to call the "prove" method on the packet we wish
|
||||
# to prove, and specify which destination it should be
|
||||
# directed to.
|
||||
packet.prove(reply_destination)
|
||||
# Sending the proof is handled automatically, since we
|
||||
# set up the destination to prove all incoming packets.
|
||||
RNS.log("Received packet from echo client, proof sent")
|
||||
|
||||
|
||||
# We need a global list to hold sent echo requests
|
||||
sent_requests = []
|
||||
# This initialisation is executed when the users chooses
|
||||
# to run as a client
|
||||
def client(destination_hexhash, configpath):
|
||||
@ -104,36 +85,12 @@ def client(destination_hexhash, configpath):
|
||||
# We must first initialise Reticulum
|
||||
reticulum = RNS.Reticulum(configpath)
|
||||
|
||||
# Randomly create a new identity for our echo server
|
||||
client_identity = RNS.Identity()
|
||||
|
||||
# Let's set up a destination for replies to our echo
|
||||
# requests. This destination will be used by the server
|
||||
# to direct replies to. We're going to use a "plain"
|
||||
# destination, so the server can send replies back
|
||||
# without knowing any public keys of the client. In this
|
||||
# case, such a design is benificial, since any client
|
||||
# can send echo requests directly to the server, without
|
||||
# first having to announce it's destination, or include
|
||||
# public keys in the echo request
|
||||
#
|
||||
# We will use the destination naming convention of:
|
||||
# example_utilities.echo.reply.<IDENTITY_HASH>
|
||||
# where the last part is a hex representation of the hash
|
||||
# of our "client_identity". We need to include this to
|
||||
# create a unique destination for the server to respond to.
|
||||
# If we had used a "single" destination, something equivalent
|
||||
# to this process would have happened automatically.
|
||||
reply_destination = RNS.Destination(client_identity, RNS.Destination.IN, RNS.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity.hexhash)
|
||||
|
||||
# Since we are only expecting packets of the "proof"
|
||||
# type to reach our reply destination, we just set the
|
||||
# proof callback (and in this case not the normal
|
||||
# message callback)
|
||||
reply_destination.setProofCallback(clientProofCallback)
|
||||
# We override the loglevel to provide feedback when
|
||||
# an announce is received
|
||||
RNS.loglevel = RNS.LOG_INFO
|
||||
|
||||
# Tell the user that the client is ready!
|
||||
RNS.log("Echo client "+RNS.prettyhexrep(reply_destination.hash)+" ready, hit enter to send echo request (Ctrl-C to quit)")
|
||||
RNS.log("Echo client ready, hit enter to send echo request to "+destination_hexhash+" (Ctrl-C to quit)")
|
||||
|
||||
# We enter a loop that runs until the user exits.
|
||||
# If the user hits enter, we will try to send an
|
||||
@ -153,76 +110,45 @@ def client(destination_hexhash, configpath):
|
||||
# recall method, so let's create an outgoing
|
||||
# destination. We use the naming convention:
|
||||
# example_utilities.echo.request
|
||||
# Since this is a "single" destination, the identity
|
||||
# hash will be automatically added to the end of
|
||||
# the name.
|
||||
# This matches the naming we specified in the
|
||||
# server part of the code.
|
||||
request_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
|
||||
|
||||
# The destination is ready, so let's create a packet.
|
||||
# We set the destination to the request_destination
|
||||
# that was just created, and the only data we add
|
||||
# is the identity hash of our client identity.
|
||||
# Including that information will let the server
|
||||
# create a destination to send replies to.
|
||||
echo_request = RNS.Packet(request_destination, client_identity.hash)
|
||||
# is a random hash.
|
||||
echo_request = RNS.Packet(request_destination, RNS.Identity.getRandomHash())
|
||||
|
||||
# Send the packet!
|
||||
echo_request.send()
|
||||
# Send the packet! If the packet is successfully
|
||||
# sent, it will return a PacketReceipt instance.
|
||||
packet_receipt = echo_request.send()
|
||||
|
||||
# Add the request to our list of sent packets
|
||||
sent_requests.append(echo_request)
|
||||
# We can then set a delivery callback on the receipt.
|
||||
# This will get automatically called when a proof for
|
||||
# this specific packet is received from the destination.
|
||||
packet_receipt.delivery_callback(packet_delivered)
|
||||
|
||||
# Tell the user that the echo request was sent
|
||||
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
|
||||
else:
|
||||
# If we do not know this destination, tell the
|
||||
# user to wait for an announce to arrive.
|
||||
RNS.log("Destination is not yet known. Wait for an announce to arrive.")
|
||||
RNS.log("Destination is not yet known. Wait for an announce to arrive and try again.")
|
||||
|
||||
# This method is called when our reply destination
|
||||
# receives a proof packet.
|
||||
def clientProofCallback(proof_packet):
|
||||
# We save the current time so we can calculate
|
||||
# round-trip time for the packet
|
||||
now = time.time()
|
||||
|
||||
# Let's look through our list of sent requests,
|
||||
# and see if we can find one that matches the
|
||||
# proof we just received.
|
||||
for unproven_packet in sent_requests:
|
||||
try:
|
||||
# Check that the proof hash matches the
|
||||
# hash of the packet we sent earlier
|
||||
if unproven_packet.packet_hash == proof_packet.data[:32]:
|
||||
# We need to actually calidate the proof.
|
||||
# This is simply done by calling the
|
||||
# validateProofPacket method on the packet
|
||||
# we sent earlier.
|
||||
if unproven_packet.validateProofPacket(proof_packet):
|
||||
# If the proof is valid, we will calculate
|
||||
# the round-trip time, and inform the user.
|
||||
rtt = now - unproven_packet.sent_at
|
||||
if (rtt >= 1):
|
||||
rtt = round(rtt, 3)
|
||||
rttstring = str(rtt)+" seconds"
|
||||
else:
|
||||
rtt = round(rtt*1000, 3)
|
||||
rttstring = str(rtt)+" milliseconds"
|
||||
|
||||
RNS.log(
|
||||
"Valid echo reply, proved by "+RNS.prettyhexrep(unproven_packet.destination.hash)+
|
||||
", round-trip time was "+rttstring
|
||||
)
|
||||
# Perform some cleanup
|
||||
sent_requests.remove(unproven_packet)
|
||||
del unproven_packet
|
||||
else:
|
||||
# If the proof was invalid, we inform
|
||||
# the user of this.
|
||||
RNS.log("Echo reply received, but proof was invalid")
|
||||
except:
|
||||
RNS.log("Proof packet received, but packet contained invalid or unparsable data")
|
||||
def packet_delivered(receipt):
|
||||
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||
rtt = receipt.rtt()
|
||||
if (rtt >= 1):
|
||||
rtt = round(rtt, 3)
|
||||
rttstring = str(rtt)+" seconds"
|
||||
else:
|
||||
rtt = round(rtt*1000, 3)
|
||||
rttstring = str(rtt)+" milliseconds"
|
||||
|
||||
RNS.log("Valid reply received from "+RNS.prettyhexrep(receipt.destination.hash)+", round-trip time is "+rttstring)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -248,6 +174,7 @@ if __name__ == "__main__":
|
||||
print("")
|
||||
parser.print_help()
|
||||
print("")
|
||||
client(args.destination, configarg)
|
||||
else:
|
||||
client(args.destination, configarg)
|
||||
except KeyboardInterrupt:
|
||||
exit()
|
Loading…
Reference in New Issue
Block a user