Updated Echo example

This commit is contained in:
Mark Qvist 2018-04-25 00:20:57 +02:00
parent 0c49ca8458
commit 8082338657
4 changed files with 58 additions and 128 deletions

View File

@ -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:

View File

@ -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

View File

@ -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())

View File

@ -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
@ -24,10 +30,17 @@ def server(configpath):
# certain that no-one else than this destination was able
# 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()