From edd1b7e74d4fea842f0e8d8dc29d5e0a28787ecd Mon Sep 17 00:00:00 2001 From: Jay Lorch Date: Wed, 11 Aug 2021 17:05:51 -0700 Subject: [PATCH] Secure Ironfleet networking (#17) * Dafny crypto networking for lock server * CreateIronServiceCert and TestIoFramework * Better support for IPv6, better error messages * Use different class for server and client sender threads * More certificate validation * Make IronLock work with secure network * Include left-out file * Have NetClient ensure it satisfies restrictions on key sizes * Use seq in Receive API so that freshness guarantee isn't violated * Build CreateIronServiceCert and TestIoFramework in SConstruct * Move serialization of crypto to IoFramework * Update host interface * IronRSL * Don't put local addr and port in private key file * Progress on SHT * Progress on SHT proof * Finish SHT proof * Update SHT client and server programs * Fix some issues with workload f of SHT client * Fix some networking/crypto issues * Fixed SHT client workload f * Have RSL server delete its private key file * Don't require service= and private= on command line * Friendlier error messages if wrong file name is provided * Update README to reflect new crypto-based communication * Greater control of output from command line * Remove outdated paragraph from README * Clarification of README * Only print about inaccessible keys if verbose=true * Build all binaries into bin/ * Only report receive failures if verbose=true * Implement safeguard=false for RSL * Rename CreateIronServiceCert to CreateIronServiceCerts * Use nuget for IronRSLClient, v1.0.3 * Update .gitignore --- ironfleet/.gitignore | 1 + ironfleet/README.md | 207 +++-- ironfleet/SConstruct | 742 +++++++-------- .../src/CreateIronServiceCerts/.gitignore | 2 + .../CreateIronServiceCerts.csproj | 24 + .../CreateIronServiceCerts.sln | 25 + .../src/CreateIronServiceCerts/Params.cs | 178 ++++ .../src/CreateIronServiceCerts/Program.cs | 83 ++ .../Common/Framework/DistributedSystem.s.dfy | 29 +- .../Distributed/Common/Framework/Host.s.dfy | 46 +- .../Distributed/Common/Framework/Main.s.dfy | 11 +- .../Dafny/Distributed/Common/Native/Io.s.dfy | 110 +-- .../Distributed/Common/Native/IoFramework.cs | 873 ++++++++++++++---- .../Distributed/Common/Native/IoNative.cs | 133 +-- .../Impl/Common/CmdLineParser.i.dfy | 242 +---- .../Distributed/Impl/Common/NetClient.i.dfy | 12 +- .../Impl/Common/NodeIdentity.i.dfy | 205 +--- .../Dafny/Distributed/Impl/Common/Util.i.dfy | 12 +- .../Impl/LiveSHT/CmdLineParser.i.dfy | 181 ++-- .../Dafny/Distributed/Impl/LiveSHT/Host.i.dfy | 65 +- .../Distributed/Impl/LiveSHT/NetSHT.i.dfy | 49 +- .../Impl/LiveSHT/SchedulerImpl.i.dfy | 90 +- .../Distributed/Impl/LiveSHT/Unsendable.i.dfy | 6 +- .../Distributed/Impl/Lock/CmdLineParser.i.dfy | 148 +-- .../Dafny/Distributed/Impl/Lock/Host.i.dfy | 66 +- .../Dafny/Distributed/Impl/Lock/NetLock.i.dfy | 33 +- .../Dafny/Distributed/Impl/Lock/Node.i.dfy | 2 +- .../Distributed/Impl/Lock/NodeImpl.i.dfy | 61 +- .../Distributed/Impl/Lock/PacketParsing.i.dfy | 4 +- .../Distributed/Impl/RSL/AcceptorModel.i.dfy | 1 - .../Impl/RSL/CMessageRefinements.i.dfy | 21 +- .../Impl/RSL/CPaxosConfiguration.i.dfy | 6 +- .../Dafny/Distributed/Impl/RSL/CTypes.i.dfy | 34 +- .../Distributed/Impl/RSL/CmdLineParser.i.dfy | 81 +- .../Distributed/Impl/RSL/ElectionModel.i.dfy | 1 - .../Distributed/Impl/RSL/ExecutorModel.i.dfy | 40 +- .../src/Dafny/Distributed/Impl/RSL/Host.i.dfy | 60 +- .../Distributed/Impl/RSL/LearnerModel.i.dfy | 7 +- .../Distributed/Impl/RSL/MinCQuorumSize.i.dfy | 4 +- .../Dafny/Distributed/Impl/RSL/NetRSL.i.dfy | 69 +- .../Distributed/Impl/RSL/PacketParsing.i.dfy | 96 +- .../Distributed/Impl/RSL/ProposerModel.i.dfy | 6 +- .../Impl/RSL/ReplicaImplClass.i.dfy | 63 +- .../Impl/RSL/ReplicaImplMain.i.dfy | 12 +- .../RSL/ReplicaImplProcessPacketNoClock.i.dfy | 38 +- .../Impl/RSL/ReplicaImplReadClock.i.dfy | 10 +- .../Distributed/Impl/SHT/ConstantsState.i.dfy | 1 + .../Distributed/Impl/SHT/Delegations.i.dfy | 25 +- .../Distributed/Impl/SHT/HostModel.i.dfy | 7 +- .../Distributed/Impl/SHT/HostState.i.dfy | 5 + .../Distributed/Impl/SHT/PacketParsing.i.dfy | 72 +- .../Impl/SHT/SHTConcreteConfiguration.i.dfy | 17 +- .../Impl/SHT/SingleDeliveryModel.i.dfy | 4 +- .../Impl/SHT/SingleDeliveryState.i.dfy | 4 +- .../Protocol/RSL/DistributedSystem.i.dfy | 4 +- .../Dafny/Distributed/Protocol/SHT/Host.i.dfy | 9 +- .../SHT/RefinementProof/InvProof.i.dfy | 2 + .../SHT/RefinementProof/RefinementProof.i.dfy | 2 + .../Distributed/Services/Lock/Main.i.dfy | 11 +- .../Dafny/Distributed/Services/RSL/Main.i.dfy | 10 +- .../Dafny/Distributed/Services/RSL/Program.cs | 214 ++++- .../Services/SHT/AbstractService.s.dfy | 15 +- .../Dafny/Distributed/Services/SHT/Main.i.dfy | 24 +- .../Distributed/Services/SHT/Marshall.i.dfy | 365 +++----- ironfleet/src/IronLockServer/.gitignore | 1 + ironfleet/src/IronLockServer/Params.cs | 101 ++ ironfleet/src/IronLockServer/Program.cs | 64 +- ironfleet/src/IronRSLClient/.gitignore | 2 + .../src/IronRSLClient/IronRSLClient.csproj | 2 +- ironfleet/src/IronRSLClient/RSLClient.cs | 67 +- ironfleet/src/IronRSLCounterClient/Client.cs | 16 +- .../IronRSLCounterClient.csproj | 2 +- ironfleet/src/IronRSLCounterClient/Params.cs | 121 +-- ironfleet/src/IronRSLCounterClient/Program.cs | 47 +- ironfleet/src/IronRSLCounterServer/Service.cs | 2 +- ironfleet/src/IronRSLKVClient/Client.cs | 26 +- .../IronRSLKVClient/IronRSLKVClient.csproj | 2 +- ironfleet/src/IronRSLKVClient/Params.cs | 155 ++-- ironfleet/src/IronRSLKVClient/Program.cs | 38 +- ironfleet/src/IronRSLKVServer/Service.cs | 2 +- ironfleet/src/IronSHTClient/Client.cs | 273 +++--- .../src/IronSHTClient/IronfleetClient.sln | 22 - ironfleet/src/IronSHTClient/Params.cs | 172 ++-- ironfleet/src/IronSHTClient/Program.cs | 56 +- ironfleet/src/IronSHTServer/Params.cs | 116 +++ ironfleet/src/IronSHTServer/Program.cs | 63 +- ironfleet/src/TestIoFramework/.gitignore | 2 + ironfleet/src/TestIoFramework/Params.cs | 101 ++ ironfleet/src/TestIoFramework/Program.cs | 125 +++ .../TestIoFramework/TestIoFramework.csproj | 24 + .../src/TestIoFramework/TestIoFramework.sln | 25 + 91 files changed, 3717 insertions(+), 2890 deletions(-) create mode 100644 ironfleet/src/CreateIronServiceCerts/.gitignore create mode 100644 ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.csproj create mode 100644 ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.sln create mode 100644 ironfleet/src/CreateIronServiceCerts/Params.cs create mode 100644 ironfleet/src/CreateIronServiceCerts/Program.cs create mode 100644 ironfleet/src/IronLockServer/Params.cs create mode 100644 ironfleet/src/IronRSLClient/.gitignore delete mode 100755 ironfleet/src/IronSHTClient/IronfleetClient.sln create mode 100644 ironfleet/src/IronSHTServer/Params.cs create mode 100644 ironfleet/src/TestIoFramework/.gitignore create mode 100644 ironfleet/src/TestIoFramework/Params.cs create mode 100644 ironfleet/src/TestIoFramework/Program.cs create mode 100644 ironfleet/src/TestIoFramework/TestIoFramework.csproj create mode 100644 ironfleet/src/TestIoFramework/TestIoFramework.sln diff --git a/ironfleet/.gitignore b/ironfleet/.gitignore index f316a5ce..bc6c8874 100755 --- a/ironfleet/.gitignore +++ b/ironfleet/.gitignore @@ -34,3 +34,4 @@ nohup.out *.suo *~ port* +certs/ diff --git a/ironfleet/README.md b/ironfleet/README.md index b15ec643..43eab3a2 100755 --- a/ironfleet/README.md +++ b/ironfleet/README.md @@ -63,37 +63,64 @@ limit 120 seconds instead of the default 60 seconds. Running scons will produce the following executables: ``` - src/IronLockServer/bin/Release/net5.0/IronLockServer.dll - src/IronRSLCounterServer/bin/Release/net5.0/IronRSLCounterServer.dll - src/IronRSLCounterClient/bin/Release/net5.0/IronRSLCounterClient.dll - src/IronRSLKVServer/bin/Release/net5.0/IronRSLKVServer.dll - src/IronRSLKVClient/bin/Release/net5.0/IronRSLKVClient.dll - src/IronSHTServer/bin/Release/net5.0/IronSHTServer.dll - src/IronSHTClient/bin/Release/net5.0/IronSHTClient.dll + bin/CreateIronServiceCerts.dll + bin/TestIoFramework.dll + bin/IronLockServer.dll + bin/IronRSLCounterServer.dll + bin/IronRSLCounterClient.dll + bin/IronRSLKVServer.dll + bin/IronRSLKVClient.dll + bin/IronSHTServer.dll + bin/IronSHTClient.dll ``` To produce these executables without performing verification, use `--no-verify`. -To avoid hampering performance, we've turned off most hosts' output. To make -hosts collect and print profile information, change `false` to `true` in -`ShouldPrintProfilingInfo` in `./src/Dafny/Distributed/Impl/Common/Util.i.dfy`. -To make hosts print information about their progress, change `false` to `true` -in `ShouldPrintProgress` in the same file. - # Running +## Creating certificates + +Ironfleet servers identify themselves using certificates. So, before running +any Ironfleet services, you need to generate certificates for the service by +running `CreateIronServiceCerts`. On the command line you'll specify the name +and type of the service and, for each server, its public address and port. Each +such address can be a hostname like `www.myservice.com` or an IP address like +`127.0.0.1` or `2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF`. + +For instance, you can run the following command: +``` + dotnet bin/CreateIronServiceCerts.dll outputdir=certs name=MyService type=TestService addr1=server1.com port1=6000 addr2=server2.com port2=7000 +``` +This will create three files in the directory `certs`. Two of these files, +`MyService.TestService.server1.private.txt` and +`MyService.TestService.server2.private.txt`, are the private key files for the +two servers. The third, `MyService.TestService.service.txt`, contains the +service identity, including the public keys of the two servers. + +You'll distribute the service file to all servers and all clients. But, +you should only copy a private key file to the server corresponding to that +private key, and after copying it you should delete your local copy. So, in +this example, you'd copy `MyService.TestService.server1.private.txt` only to +server1.com. + ## IronLock IronLock is the simplest of the protocols we've verified, so it may be a good starting point. It consists of N processes passing around a lock. To run it, -you need to supply each process with the IP-port pairs of all processes, as -well as its own IP-pair. Also, make sure your firewall isn't blocking the TCP -ports you use. Here's an example configuration with three processes: +make sure your firewall isn't blocking the TCP ports you use. Here's an example +configuration with three processes: +Create the service with: ``` - dotnet src/IronLockServer/bin/Release/net5.0/IronLockServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4002 - dotnet src/IronLockServer/bin/Release/net5.0/IronLockServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4003 - dotnet src/IronLockServer/bin/Release/net5.0/IronLockServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4001 + dotnet bin/CreateIronServiceCerts.dll outputdir=certs name=MyLock type=IronLock addr1=127.0.0.1 port1=4001 addr2=127.0.0.1 port2=4002 addr3=127.0.0.1 port3=4003 +``` + +Run the service by executing the following three commands in three different +windows: +``` + dotnet bin/IronLockServer.dll certs/MyLock.IronLock.service.txt certs/MyLock.IronLock.server2.private.txt + dotnet bin/IronLockServer.dll certs/MyLock.IronLock.service.txt certs/MyLock.IronLock.server3.private.txt + dotnet bin/IronLockServer.dll certs/MyLock.IronLock.service.txt certs/MyLock.IronLock.server1.private.txt ``` It's important that you start the "first" process last (as in the above @@ -109,28 +136,45 @@ can, printing a message everytime they accept or grant the lock. To run the counter service replicated with IronRSL, you should ideally use four different machines, but in a pinch you can use four separate windows on -the same machine. The server executable expects a list of IP-port pairs that -identifies all of the replicas in the system (in this example we're using 3, -but more is feasible). Each server instance also needs to be told which -IP-port pair belongs to it. +the same machine. The client has reasonable defaults that you can override with key=value -command-line arguments. Run the client with `--help` to get detailed usage +command-line arguments. Run the client with no arguments to get detailed usage information. Make sure your firewall isn't blocking the TCP ports you use. -For example, to test the IronRSL counter on a single machine, you can run each -of the following four commands in a different console: +To test the IronRSL counter on a single machine, you can do the following. +First, create certificates with: ``` - dotnet src/IronRSLCounterServer/bin/Release/net5.0/IronRSLCounterServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4001 - dotnet src/IronRSLCounterServer/bin/Release/net5.0/IronRSLCounterServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4002 - dotnet src/IronRSLCounterServer/bin/Release/net5.0/IronRSLCounterServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4003 - dotnet src/IronRSLCounterClient/bin/Release/net5.0/IronRSLCounterClient.dll nthreads=10 duration=30 clientport=6000 verbose=true + dotnet bin/CreateIronServiceCerts.dll outputdir=certs name=MyCounter type=IronRSLCounter addr1=127.0.0.1 port1=4001 addr2=127.0.0.1 port2=4002 addr3=127.0.0.1 port3=4003 ``` -The first three are the RSL servers, and the latter is the client. If you use -`verbose=false`, the client's output will primarily consist of reports of the -form `#req `. +Then, run each of the following three server commands, each in a different window. +``` + dotnet bin/IronRSLCounterServer.dll certs/MyCounter.IronRSLCounter.service.txt certs/MyCounter.IronRSLCounter.server1.private.txt + dotnet bin/IronRSLCounterServer.dll certs/MyCounter.IronRSLCounter.service.txt certs/MyCounter.IronRSLCounter.server2.private.txt + dotnet bin/IronRSLCounterServer.dll certs/MyCounter.IronRSLCounter.service.txt certs/MyCounter.IronRSLCounter.server3.private.txt +``` + +Finally, run this client command in yet another window: +``` + dotnet bin/IronRSLCounterClient.dll certs/MyCounter.IronRSLCounter.service.txt nthreads=10 duration=30 print=true +``` + +If you don't want the client to print the counter values it receives in replies, +remove `print=true` from the client command. In that case, its output will +primarily consist of reports of the form `#req +`. + +You can run the client as many times as you want. But, you can only run each +server once since we haven't implemented crash recovery. To prevent you from +accidentally running a server multiple times, the server program deletes its +private key file right after reading it. + +Fortunately, `IronRSLCounter` can deal with the failure of fewer than half its +servers. But, if half of them or more fail, you'll have to create a new +service. That is, you'll have to start over by running `CreateIronServiceCerts`, +and that new service's counter will start at 0. Note that the servers use non-blocking network receives, so they may be slow to respond to Ctrl-C. @@ -139,28 +183,44 @@ to respond to Ctrl-C. To run the key-value service replicated with IronRSL, you should ideally use four different machines, but in a pinch you can use four separate windows on -the same machine. The server executable expects a list of IP-port pairs that -identifies all of the replicas in the system (in this example we're using 3, -but more is feasible). Each server instance also needs to be told which -IP-port pair belongs to it. +the same machine. The client has reasonable defaults that you can override with key=value -command-line arguments. Run the client with `--help` to get detailed usage +command-line arguments. Run the client with no arguments to get detailed usage information. Make sure your firewall isn't blocking the TCP ports you use. -For example, to test the IronRSL key-value store on a single machine, you can -run each of the following four commands in a different console: +To test the IronRSL key-value store on a single machine, you can do the following. +First, create certificates with: +``` + dotnet bin/CreateIronServiceCerts.dll outputdir=certs name=MyKV type=IronRSLKV addr1=127.0.0.1 port1=4001 addr2=127.0.0.1 port2=4002 addr3=127.0.0.1 port3=4003 +``` +Then, run each of the following three server commands, each in a different window: ``` - dotnet src/IronRSLKVServer/bin/Release/net5.0/IronRSLKVServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4001 - dotnet src/IronRSLKVServer/bin/Release/net5.0/IronRSLKVServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4002 - dotnet src/IronRSLKVServer/bin/Release/net5.0/IronRSLKVServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4003 - dotnet src/IronRSLKVClient/bin/Release/net5.0/IronRSLKVClient.dll nthreads=10 duration=30 clientport=6000 setfraction=0.25 deletefraction=0.05 verbose=true + dotnet bin/IronRSLKVServer.dll certs/MyKV.IronRSLKV.service.txt certs/MyKV.IronRSLKV.server1.private.txt + dotnet bin/IronRSLKVServer.dll certs/MyKV.IronRSLKV.service.txt certs/MyKV.IronRSLKV.server2.private.txt + dotnet bin/IronRSLKVServer.dll certs/MyKV.IronRSLKV.service.txt certs/MyKV.IronRSLKV.server3.private.txt ``` -The first three are the RSL servers, and the latter is the client. If you use -`verbose=false`, the client's output will primarily consist of reports of the -form `#req `. +Finally, run this client command in yet another window: +``` + dotnet bin/IronRSLKVClient.dll certs/MyKV.IronRSLKV.service.txt nthreads=10 duration=30 setfraction=0.25 deletefraction=0.05 print=true +``` + +If you don't want the client to print the requests it sends and the replies it +receives, remove `print=true` from the client command. In that case, its output +will primarily consist of reports of the form `#req +`. + +You can run the client as many times as you want. But, you can only run each +server once since we haven't implemented crash recovery. To prevent you from +accidentally running a server multiple times, the server program deletes its +private key file right after reading it. + +Fortunately, `IronRSLKV` can deal with the failure of fewer than half its +servers. But, if half of them or more fail, you'll have to create a new +service. That is, you'll have to start over by running `CreateIronServiceCerts`, +and that new service's key-value store will start out empty. Note that the servers use non-blocking network receives, so they may be slow to respond to Ctrl-C. @@ -169,31 +229,34 @@ to respond to Ctrl-C. To run IronSHT (our sharded hash table), you should ideally use multiple different machines, but in a pinch you can use separate windows on the same -machine. Like IronRSL, IronSHT server executables require a list of IP-port -pairs, and the IronSHT client takes command-line arguments of the form -key=value. Make sure your firewall isn't blocking the TCP ports you use. - -For example, you can run each of the following four commands in a different -console: -``` - dotnet src/IronSHTServer/bin/Release/net5.0/IronSHTServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4001 - dotnet src/IronSHTServer/bin/Release/net5.0/IronSHTServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4002 - dotnet src/IronSHTServer/bin/Release/net5.0/IronSHTServer.dll localhost:4001 localhost:4002 localhost:4003 localhost:4003 - dotnet src/IronSHTClient/bin/Release/net5.0/IronSHTClient.dll nthreads=10 duration=30 workload=g numkeys=10000 clientport=6000 verbose=true -``` - -The client will print its output to standard output. If you use -`verbose=false`, the client's output will primarily consist of reports of the -form `#req `. - -Note that until you stop all the SHT servers, each client endpoint is expected -to use strictly increasing sequence numbers, starting with 1. Since the test -client program always starts with 1, you should never reuse the same client -endpoint until you restart the SHT servers, and you should keep in mind that -the client uses `nthreads+1` consecutive ports: one for setup and `nthreads` -for experiments. So, for instance, if you use `nthreads=10 -client=localhost:6000`, then the next time you run you could use -`client=localhost:6011`. +machine. + +The client has reasonable defaults that you can override with key=value +command-line arguments. Run the client with no arguments to get detailed usage +information. Make sure your firewall isn't blocking the TCP ports you use. + +To test the IronSHT sharded hash table on a single machine, you can do the following. +First, create certificates with: +``` + dotnet bin/CreateIronServiceCerts.dll outputdir=certs name=MySHT type=IronSHT addr1=127.0.0.1 port1=4001 addr2=127.0.0.1 port2=4002 addr3=127.0.0.1 port3=4003 +``` + +Then, run each of the following three server commands, each in a different window: +``` + dotnet bin/IronSHTServer.dll certs/MySHT.IronSHT.service.txt certs/MySHT.IronSHT.server1.private.txt + dotnet bin/IronSHTServer.dll certs/MySHT.IronSHT.service.txt certs/MySHT.IronSHT.server2.private.txt + dotnet bin/IronSHTServer.dll certs/MySHT.IronSHT.service.txt certs/MySHT.IronSHT.server3.private.txt +``` + +Finally, run this client command in yet another window: +``` + dotnet bin/IronSHTClient.dll certs/MySHT.IronSHT.service.txt nthreads=10 duration=30 workload=g numkeys=1000 +``` +The client's output will primarily consist of reports of the form `#req + `. + +We haven't implemented crash recovery, so if you restart a server its state will +be empty. # Custom Replicated Services diff --git a/ironfleet/SConstruct b/ironfleet/SConstruct index 0f127d47..1ccf5baa 100755 --- a/ironfleet/SConstruct +++ b/ironfleet/SConstruct @@ -1,369 +1,373 @@ -# -*- python -*- -import atexit -import os, os.path -import re -import shutil -import subprocess -import sys -import SCons.Util -import threading - -Import("*") - -env = Environment(ENV=os.environ) - -# Retrieve tool-specific command overrides passed in by the user -AddOption('--dafny-path', - dest='dafny_path', - type='string', - default=None, - action='store', - help='Specify the path to Dafny tool binaries') - -AddOption('--no-verify', - dest='no_verify', - default=False, - action='store_true', - help="Don't verify, just build executables") - -AddOption('--time-limit', - dest='time_limit', - type='int', - default=60, - action='store', - help='Specify the time limit to use for each verification') - -dafny_path = GetOption('dafny_path') -if dafny_path is None: - sys.stderr.write("ERROR: Missing --dafny-path on command line\n") - exit(-1) - -if sys.platform == "win32" or sys.platform == "cygwin": - dafny_exe = os.path.join(dafny_path, 'Dafny.exe') - if not os.path.exists(dafny_exe): - print("ERROR: Could not find Dafny executable in " + dafny_path) - exit(-1) - dafny_invocation = [dafny_exe] -else: - dafny_exe = os.path.join(dafny_path, 'Dafny.dll') - if not os.path.exists(dafny_exe): - dafny_exe = os.path.join(dafny_path, 'dafny.dll') - if not os.path.exists(dafny_exe): - print("ERROR: Could not find Dafny executable in " + dafny_path) - exit(-1) - dafny_invocation = ["dotnet", dafny_exe] - -# Useful Dafny command lines -dafny_basic_args = ['/compile:0', '/timeLimit:' + str(GetOption('time_limit')), '/trace'] -dafny_default_args = dafny_basic_args + ['/arith:5', '/noCheating:1'] -dafny_args_nlarith = dafny_basic_args + ['/arith:2', '/noCheating:1'] -dafny_spec_args = dafny_basic_args - -#################################################################### -# -# General routines -# -#################################################################### - -def recursive_glob(env, pattern, strings=False): - matches = [] - split = os.path.split(pattern) # [0] is the directory, [1] is the actual pattern - platform_directory = split[0] #os.path.normpath(split[0]) - for d in os.listdir(platform_directory): - if os.path.isdir(os.path.join(platform_directory, d)): - newpattern = os.path.join(split[0], d, split[1]) - matches.append(recursive_glob(env, newpattern, strings)) - - files = env.Glob(pattern, strings=strings) - matches.append(files) - return Flatten(matches) - -#################################################################### -# -# Make table of special cases requiring non-default arguments -# -#################################################################### - -source_to_args = [ - ('src/Dafny/Distributed/Protocol/Lock/RefinementProof/RefinementProof\.i\.dfy', dafny_default_args + ['/noNLarith']), - ('.*nonlinear\.i\.dfy', dafny_args_nlarith), - ('.*\.s\.dfy', dafny_spec_args), - ('.*\.dfy', dafny_default_args), -] - -#################################################################### -# -# Dafny-specific utilities -# -#################################################################### - -dafny_include_re = re.compile(r'include\s+"(\S+)"', re.M) -single_line_comments_re = re.compile(r'//.*\n') -multiline_comments_re = re.compile(r'/\*(([^/\*])|(\*[^/])|(/[^\*]))*\*/') - -def remove_dafny_comments(contents): - # Strip out multi-line comments, using a loop to deal with nested comments - while True: - (contents, substitutions_made) = re.subn(multiline_comments_re, ' ', contents) - if substitutions_made == 0: - break - - # Strip out single-line comments - contents = re.sub(single_line_comments_re, '\n', contents) - return contents - -# helper to look up Dafny command-line arguments matching a srcpath, from the -# source_to_args[] dictionary, dealing with POSIX and Windows pathnames, and -# falling back on a default if no specific override is present. -def get_dafny_command_line_args(srcpath): - srcpath = os.path.normpath(srcpath) # normalize the path, which, on Windows, switches to \\ separators - srcpath = srcpath.replace('\\', '/') # switch to posix path separators - for entry in source_to_args: - pattern, args = entry - if re.search(pattern, srcpath, flags=re.IGNORECASE): - return args - - return dafny_default_args - -dependencies_by_file = dict() -already_verified_files = set() -already_printed_files = set() - -# Scan a .dfy file to discover its transitive dependencies, and store a -# list of them in dependencies_by_file[fullpath]. -def recursively_scan_for_dependencies(fullpath, depth): - if fullpath in dependencies_by_file: - return - contents = File(fullpath).get_text_contents() - dirname = os.path.dirname(fullpath) - filename = os.path.basename(fullpath) - contents = remove_dafny_comments(contents) - includes = dafny_include_re.findall(contents) - extra_files = [os.path.abspath(os.path.join(dirname, i)) for i in includes] - transitive_dependencies = set(extra_files) - for srcpath in extra_files: - recursively_scan_for_dependencies(srcpath, depth + 1) - transitive_dependencies.update(dependencies_by_file[srcpath]) - all_dependencies = sorted(list(transitive_dependencies)) - dependencies_by_file[fullpath] = all_dependencies - - -# Scan a .dfy file to discover its dependencies, and add .vdfy targets for each. -def scan_for_more_targets(target, source, env): - node = source[0] - fullpath = str(node) - recursively_scan_for_dependencies(fullpath, 0) - dependencies = dependencies_by_file[fullpath] - for srcpath in dependencies: - if srcpath not in already_verified_files: - f = os.path.splitext(srcpath)[0] + '.vdfy' - env.DafnyVerify(f, [srcpath, dafny_exe]) - already_verified_files.add(srcpath) - return target, source + dependencies - -#################################################################### -# -# Dafny routines -# -#################################################################### - -def check_dafny(lines): - for line in lines: - if re.search("[Oo]ut of resource", line): - sys.stderr.write("Dafny reported an out-of-resource error\n") - raise Exception() - if re.search("proof obligations\]\s+errors", line): - sys.stderr.write("Dafny reported errors not in summary\n") - raise Exception() - -def check_and_print_tail(filename): - with open(filename, 'r') as fh: - lines = fh.readlines() - check_dafny(lines) - sys.stdout.write(lines[-1]) - sys.stdout.write('Full check of Dafny output succeeded\n') - -CheckAndPrintTail = SCons.Action.ActionFactory(check_and_print_tail, lambda x: "Checking " + x) - -def generate_dafny_verifier_actions(source, target, env, for_signature): - abs_source = File(source[0]).abspath - abs_target = File(target[0]).abspath - source_name = str(source[0]) - temp_target_file = re.sub(r'\.dfy$', '.tmp', source_name) - args = get_dafny_command_line_args(abs_source) - return [ - dafny_invocation + args + [source_name, ">", temp_target_file], - CheckAndPrintTail(temp_target_file), - Move(abs_target, temp_target_file) - ] - -# Add env.DafnyVerify(), to generate Dafny verifier actions -def add_dafny_verifier_builder(env): - dafny_verifier = Builder(generator = generate_dafny_verifier_actions, - suffix = '.vdfy', - src_suffix = '.dfy', - chdir=0, - emitter = scan_for_more_targets, - ) - env.Append(BUILDERS = {'DafnyVerify' : dafny_verifier}) - -# Verify a set of Dafny files by creating verification targets for each, -# which in turn causes a dependency scan to verify all of their dependencies. -def verify_dafny_files(env, files): - for f in files: - target = os.path.splitext(f)[0] + '.vdfy' - env.DafnyVerify(target, [f, dafny_exe]) - -# Verify *.dfy files in a list of directories. This enumerates -# all files in those trees, and creates verification targets for each, -# which in turn causes a dependency scan to verify all of their dependencies. -def verify_files_in(env, directories): - for d in directories: - files = recursive_glob(env, d+'/*.dfy', strings=True) - verify_dafny_files(env, files) - -def verify_dafny_file(source): - if GetOption('no_verify'): - return - target = re.sub(r"\.dfy$", ".vdfy", source) - env.DafnyVerify(target, [source, dafny_exe]) - -#################################################################### -# -# Dafny compilation -# -#################################################################### - -def generate_dafny_compile_actions(source, target, env, for_signature): - return [ - dafny_invocation + ['/compile:0', '/spillTargetCode:3', '/noVerify', str(source[0])], - ] - -def get_dafny_compile_dependencies(target, source, env): - source_name = str(source[0]) - recursively_scan_for_dependencies(source_name, 0) - verification_dependencies = dependencies_by_file[source_name] - extra_dependencies = verification_dependencies - if not GetOption('no_verify'): - extra_dependencies.extend([re.sub('\.dfy$', '.vdfy', f) for f in verification_dependencies if re.search('\.dfy$', f)]) - return target, source + extra_dependencies - -# Add env.DafnyCompile(), to generate dafny_compile build actions -def add_dafny_compiler_builder(env): - client_builder = Builder(generator = generate_dafny_compile_actions, - chdir=0, - emitter=get_dafny_compile_dependencies) - env.Append(BUILDERS = {'DafnyCompile' : client_builder}) - -#################################################################### -# -# .NET binaries -# -#################################################################### - -def generate_dotnet_actions(source, target, env, for_signature): - return [ - ["dotnet", "build", "--configuration", "Release", str(source[0])] - ] - -def get_dotnet_dependencies(target, source, env): - csproj_file = str(source[0]) - source_dir = os.path.dirname(csproj_file) - extra_dependencies = [os.path.join(source_dir, f) for f in os.listdir(source_dir) if re.search('\.cs$', f)] - with open(csproj_file, 'r') as fh: - for line in fh.readlines(): - m = re.search(r'', line) - if m: - raw_file_name = re.sub(r'\\', '/', m.group(1)) - file_name = os.path.normpath(os.path.join(source_dir, raw_file_name)) - extra_dependencies.append(file_name) - return target, source + extra_dependencies - -# Add env.DotnetBuild(), to generate dotnet build actions -def add_dotnet_builder(env): - client_builder = Builder(generator = generate_dotnet_actions, - chdir=0, - emitter=get_dotnet_dependencies) - env.Append(BUILDERS = {'DotnetBuild' : client_builder}) - - -#################################################################### -# -# Extract verification failure information -# -#################################################################### - -# extract a string filename out of a build failure -def bf_to_filename(bf): - import SCons.Errors - if bf is None: # unknown targets product None in list - return '(unknown tgt)' - elif isinstance(bf, SCons.Errors.StopError): - return str(bf) - elif bf.node: - return str(bf.node) - elif bf.filename: - return bf.filename - return '(unknown failure)' - -def report_verification_failures(): - from SCons.Script import GetBuildFailures - bf = GetBuildFailures() - if bf: - # bf is normally a list of build failures; if an element is None, - # it's because of a target that scons doesn't know anything about. - for x in bf: - if x is not None: - filename = bf_to_filename(x) - if filename.endswith('.vdfy'): - file_to_print = os.path.splitext(filename)[0] + '.tmp' - if os.path.isfile(file_to_print): - sys.stdout.write('\n##### Verification error. Printing contents of ' + file_to_print + ' #####\n\n') - with open (file_to_print, 'r') as myfile: - sys.stdout.write(myfile.read()) - else: - print("ERROR: Verification error, but cannot print output since file %s doesn't exist" % (file_to_print)) - else: - print("Build failure for %s" % (filename)) - - -def display_build_status(): - report_verification_failures() - -#################################################################### -# -# Put it all together -# -#################################################################### - -add_dafny_verifier_builder(env) -add_dafny_compiler_builder(env) -add_dotnet_builder(env) -env.AddMethod(verify_files_in, "VerifyFilesIn") -env.AddMethod(verify_dafny_files, "VerifyDafnyFiles") -atexit.register(display_build_status) - -#################################################################### -# -# Create dependencies -# -#################################################################### - -verify_dafny_file('src/Dafny/Distributed/Services/Lock/Main.i.dfy') -verify_dafny_file('src/Dafny/Distributed/Services/SHT/Main.i.dfy') -verify_dafny_file('src/Dafny/Distributed/Services/RSL/Main.i.dfy') -verify_dafny_file('src/Dafny/Distributed/Protocol/RSL/LivenessProof/LivenessProof.i.dfy') -verify_dafny_file('src/Dafny/Distributed/Protocol/LiveSHT/LivenessProof/LivenessProof.i.dfy') - -env.DafnyCompile('src/Dafny/Distributed/Services/RSL/Main.i.cs', 'src/Dafny/Distributed/Services/RSL/Main.i.dfy') -env.DotnetBuild('src/IronRSLCounterServer/bin/Release/net5.0/IronRSLCounterServer.dll', 'src/IronRSLCounterServer/IronRSLCounterServer.csproj') -env.DotnetBuild('src/IronRSLCounterClient/bin/Release/net5.0/IronRSLCounterClient.dll', 'src/IronRSLCounterClient/IronRSLCounterClient.csproj') -env.DotnetBuild('src/IronRSLKVServer/bin/Release/net5.0/IronRSLKVServer.dll', 'src/IronRSLKVServer/IronRSLKVServer.csproj') -env.DotnetBuild('src/IronRSLKVClient/bin/Release/net5.0/IronRSLKVClient.dll', 'src/IronRSLKVClient/IronRSLKVClient.csproj') - -env.DafnyCompile('src/Dafny/Distributed/Services/SHT/Main.i.cs', 'src/Dafny/Distributed/Services/SHT/Main.i.dfy') -env.DotnetBuild('src/IronSHTServer/bin/Release/net5.0/IronSHTServer.dll', 'src/IronSHTServer/IronSHTServer.csproj') -env.DotnetBuild('src/IronSHTClient/bin/Release/net5.0/IronSHTClient.dll', 'src/IronSHTClient/IronSHTClient.csproj') - -env.DafnyCompile('src/Dafny/Distributed/Services/Lock/Main.i.cs', 'src/Dafny/Distributed/Services/Lock/Main.i.dfy') -env.DotnetBuild('src/IronLockServer/bin/Release/net5.0/IronLockServer.dll', 'src/IronLockServer/IronLockServer.csproj') +# -*- python -*- +import atexit +import os, os.path +import re +import shutil +import subprocess +import sys +import SCons.Util +import threading + +Import("*") + +env = Environment(ENV=os.environ) + +# Retrieve tool-specific command overrides passed in by the user +AddOption('--dafny-path', + dest='dafny_path', + type='string', + default=None, + action='store', + help='Specify the path to Dafny tool binaries') + +AddOption('--no-verify', + dest='no_verify', + default=False, + action='store_true', + help="Don't verify, just build executables") + +AddOption('--time-limit', + dest='time_limit', + type='int', + default=60, + action='store', + help='Specify the time limit to use for each verification') + +dafny_path = GetOption('dafny_path') +if dafny_path is None: + sys.stderr.write("ERROR: Missing --dafny-path on command line\n") + exit(-1) + +if sys.platform == "win32" or sys.platform == "cygwin": + dafny_exe = os.path.join(dafny_path, 'Dafny.exe') + if not os.path.exists(dafny_exe): + print("ERROR: Could not find Dafny executable in " + dafny_path) + exit(-1) + dafny_invocation = [dafny_exe] +else: + dafny_exe = os.path.join(dafny_path, 'Dafny.dll') + if not os.path.exists(dafny_exe): + dafny_exe = os.path.join(dafny_path, 'dafny.dll') + if not os.path.exists(dafny_exe): + print("ERROR: Could not find Dafny executable in " + dafny_path) + exit(-1) + dafny_invocation = ["dotnet", dafny_exe] + +# Useful Dafny command lines +dafny_basic_args = ['/compile:0', '/timeLimit:' + str(GetOption('time_limit')), '/trace'] +dafny_default_args = dafny_basic_args + ['/arith:5', '/noCheating:1'] +dafny_args_nlarith = dafny_basic_args + ['/arith:2', '/noCheating:1'] +dafny_spec_args = dafny_basic_args + +#################################################################### +# +# General routines +# +#################################################################### + +def recursive_glob(env, pattern, strings=False): + matches = [] + split = os.path.split(pattern) # [0] is the directory, [1] is the actual pattern + platform_directory = split[0] #os.path.normpath(split[0]) + for d in os.listdir(platform_directory): + if os.path.isdir(os.path.join(platform_directory, d)): + newpattern = os.path.join(split[0], d, split[1]) + matches.append(recursive_glob(env, newpattern, strings)) + + files = env.Glob(pattern, strings=strings) + matches.append(files) + return Flatten(matches) + +#################################################################### +# +# Make table of special cases requiring non-default arguments +# +#################################################################### + +source_to_args = [ + ('src/Dafny/Distributed/Protocol/Lock/RefinementProof/RefinementProof\.i\.dfy', dafny_default_args + ['/noNLarith']), + ('.*nonlinear\.i\.dfy', dafny_args_nlarith), + ('.*\.s\.dfy', dafny_spec_args), + ('.*\.dfy', dafny_default_args), +] + +#################################################################### +# +# Dafny-specific utilities +# +#################################################################### + +dafny_include_re = re.compile(r'include\s+"(\S+)"', re.M) +single_line_comments_re = re.compile(r'//.*\n') +multiline_comments_re = re.compile(r'/\*(([^/\*])|(\*[^/])|(/[^\*]))*\*/') + +def remove_dafny_comments(contents): + # Strip out multi-line comments, using a loop to deal with nested comments + while True: + (contents, substitutions_made) = re.subn(multiline_comments_re, ' ', contents) + if substitutions_made == 0: + break + + # Strip out single-line comments + contents = re.sub(single_line_comments_re, '\n', contents) + return contents + +# helper to look up Dafny command-line arguments matching a srcpath, from the +# source_to_args[] dictionary, dealing with POSIX and Windows pathnames, and +# falling back on a default if no specific override is present. +def get_dafny_command_line_args(srcpath): + srcpath = os.path.normpath(srcpath) # normalize the path, which, on Windows, switches to \\ separators + srcpath = srcpath.replace('\\', '/') # switch to posix path separators + for entry in source_to_args: + pattern, args = entry + if re.search(pattern, srcpath, flags=re.IGNORECASE): + return args + + return dafny_default_args + +dependencies_by_file = dict() +already_verified_files = set() +already_printed_files = set() + +# Scan a .dfy file to discover its transitive dependencies, and store a +# list of them in dependencies_by_file[fullpath]. +def recursively_scan_for_dependencies(fullpath, depth): + if fullpath in dependencies_by_file: + return + contents = File(fullpath).get_text_contents() + dirname = os.path.dirname(fullpath) + filename = os.path.basename(fullpath) + contents = remove_dafny_comments(contents) + includes = dafny_include_re.findall(contents) + extra_files = [os.path.abspath(os.path.join(dirname, i)) for i in includes] + transitive_dependencies = set(extra_files) + for srcpath in extra_files: + recursively_scan_for_dependencies(srcpath, depth + 1) + transitive_dependencies.update(dependencies_by_file[srcpath]) + all_dependencies = sorted(list(transitive_dependencies)) + dependencies_by_file[fullpath] = all_dependencies + + +# Scan a .dfy file to discover its dependencies, and add .vdfy targets for each. +def scan_for_more_targets(target, source, env): + node = source[0] + fullpath = str(node) + recursively_scan_for_dependencies(fullpath, 0) + dependencies = dependencies_by_file[fullpath] + for srcpath in dependencies: + if srcpath not in already_verified_files: + f = os.path.splitext(srcpath)[0] + '.vdfy' + env.DafnyVerify(f, [srcpath, dafny_exe]) + already_verified_files.add(srcpath) + return target, source + dependencies + +#################################################################### +# +# Dafny routines +# +#################################################################### + +def check_dafny(lines): + for line in lines: + if re.search("[Oo]ut of resource", line): + sys.stderr.write("Dafny reported an out-of-resource error\n") + raise Exception() + if re.search("proof obligations\]\s+errors", line): + sys.stderr.write("Dafny reported errors not in summary\n") + raise Exception() + +def check_and_print_tail(filename): + with open(filename, 'r') as fh: + lines = fh.readlines() + check_dafny(lines) + sys.stdout.write(lines[-1]) + sys.stdout.write('Full check of Dafny output succeeded\n') + +CheckAndPrintTail = SCons.Action.ActionFactory(check_and_print_tail, lambda x: "Checking " + x) + +def generate_dafny_verifier_actions(source, target, env, for_signature): + abs_source = File(source[0]).abspath + abs_target = File(target[0]).abspath + source_name = str(source[0]) + temp_target_file = re.sub(r'\.dfy$', '.tmp', source_name) + args = get_dafny_command_line_args(abs_source) + return [ + dafny_invocation + args + [source_name, ">", temp_target_file], + CheckAndPrintTail(temp_target_file), + Move(abs_target, temp_target_file) + ] + +# Add env.DafnyVerify(), to generate Dafny verifier actions +def add_dafny_verifier_builder(env): + dafny_verifier = Builder(generator = generate_dafny_verifier_actions, + suffix = '.vdfy', + src_suffix = '.dfy', + chdir=0, + emitter = scan_for_more_targets, + ) + env.Append(BUILDERS = {'DafnyVerify' : dafny_verifier}) + +# Verify a set of Dafny files by creating verification targets for each, +# which in turn causes a dependency scan to verify all of their dependencies. +def verify_dafny_files(env, files): + for f in files: + target = os.path.splitext(f)[0] + '.vdfy' + env.DafnyVerify(target, [f, dafny_exe]) + +# Verify *.dfy files in a list of directories. This enumerates +# all files in those trees, and creates verification targets for each, +# which in turn causes a dependency scan to verify all of their dependencies. +def verify_files_in(env, directories): + for d in directories: + files = recursive_glob(env, d+'/*.dfy', strings=True) + verify_dafny_files(env, files) + +def verify_dafny_file(source): + if GetOption('no_verify'): + return + target = re.sub(r"\.dfy$", ".vdfy", source) + env.DafnyVerify(target, [source, dafny_exe]) + +#################################################################### +# +# Dafny compilation +# +#################################################################### + +def generate_dafny_compile_actions(source, target, env, for_signature): + return [ + dafny_invocation + ['/compile:0', '/spillTargetCode:3', '/noVerify', str(source[0])], + ] + +def get_dafny_compile_dependencies(target, source, env): + source_name = str(source[0]) + recursively_scan_for_dependencies(source_name, 0) + verification_dependencies = dependencies_by_file[source_name] + extra_dependencies = verification_dependencies + if not GetOption('no_verify'): + extra_dependencies.extend([re.sub('\.dfy$', '.vdfy', f) for f in verification_dependencies if re.search('\.dfy$', f)]) + return target, source + extra_dependencies + +# Add env.DafnyCompile(), to generate dafny_compile build actions +def add_dafny_compiler_builder(env): + client_builder = Builder(generator = generate_dafny_compile_actions, + chdir=0, + emitter=get_dafny_compile_dependencies) + env.Append(BUILDERS = {'DafnyCompile' : client_builder}) + +#################################################################### +# +# .NET binaries +# +#################################################################### + +def generate_dotnet_actions(source, target, env, for_signature): + target_dir = os.path.dirname(str(target[0])) + return [ + ["dotnet", "build", "--configuration", "Release", "--output", target_dir, str(source[0])] + ] + +def get_dotnet_dependencies(target, source, env): + csproj_file = str(source[0]) + source_dir = os.path.dirname(csproj_file) + extra_dependencies = [os.path.join(source_dir, f) for f in os.listdir(source_dir) if re.search('\.cs$', f)] + with open(csproj_file, 'r') as fh: + for line in fh.readlines(): + m = re.search(r'', line) + if m: + raw_file_name = re.sub(r'\\', '/', m.group(1)) + file_name = os.path.normpath(os.path.join(source_dir, raw_file_name)) + extra_dependencies.append(file_name) + return target, source + extra_dependencies + +# Add env.DotnetBuild(), to generate dotnet build actions +def add_dotnet_builder(env): + client_builder = Builder(generator = generate_dotnet_actions, + chdir=0, + emitter=get_dotnet_dependencies) + env.Append(BUILDERS = {'DotnetBuild' : client_builder}) + + +#################################################################### +# +# Extract verification failure information +# +#################################################################### + +# extract a string filename out of a build failure +def bf_to_filename(bf): + import SCons.Errors + if bf is None: # unknown targets product None in list + return '(unknown tgt)' + elif isinstance(bf, SCons.Errors.StopError): + return str(bf) + elif bf.node: + return str(bf.node) + elif bf.filename: + return bf.filename + return '(unknown failure)' + +def report_verification_failures(): + from SCons.Script import GetBuildFailures + bf = GetBuildFailures() + if bf: + # bf is normally a list of build failures; if an element is None, + # it's because of a target that scons doesn't know anything about. + for x in bf: + if x is not None: + filename = bf_to_filename(x) + if filename.endswith('.vdfy'): + file_to_print = os.path.splitext(filename)[0] + '.tmp' + if os.path.isfile(file_to_print): + sys.stdout.write('\n##### Verification error. Printing contents of ' + file_to_print + ' #####\n\n') + with open (file_to_print, 'r') as myfile: + sys.stdout.write(myfile.read()) + else: + print("ERROR: Verification error, but cannot print output since file %s doesn't exist" % (file_to_print)) + else: + print("Build failure for %s" % (filename)) + + +def display_build_status(): + report_verification_failures() + +#################################################################### +# +# Put it all together +# +#################################################################### + +add_dafny_verifier_builder(env) +add_dafny_compiler_builder(env) +add_dotnet_builder(env) +env.AddMethod(verify_files_in, "VerifyFilesIn") +env.AddMethod(verify_dafny_files, "VerifyDafnyFiles") +atexit.register(display_build_status) + +#################################################################### +# +# Create dependencies +# +#################################################################### + +verify_dafny_file('src/Dafny/Distributed/Services/Lock/Main.i.dfy') +verify_dafny_file('src/Dafny/Distributed/Services/SHT/Main.i.dfy') +verify_dafny_file('src/Dafny/Distributed/Services/RSL/Main.i.dfy') +verify_dafny_file('src/Dafny/Distributed/Protocol/RSL/LivenessProof/LivenessProof.i.dfy') +verify_dafny_file('src/Dafny/Distributed/Protocol/LiveSHT/LivenessProof/LivenessProof.i.dfy') + +env.DafnyCompile('src/Dafny/Distributed/Services/RSL/Main.i.cs', 'src/Dafny/Distributed/Services/RSL/Main.i.dfy') +env.DotnetBuild('bin/IronRSLCounterServer.dll', 'src/IronRSLCounterServer/IronRSLCounterServer.csproj') +env.DotnetBuild('bin/IronRSLCounterClient.dll', 'src/IronRSLCounterClient/IronRSLCounterClient.csproj') +env.DotnetBuild('bin/IronRSLKVServer.dll', 'src/IronRSLKVServer/IronRSLKVServer.csproj') +env.DotnetBuild('bin/IronRSLKVClient.dll', 'src/IronRSLKVClient/IronRSLKVClient.csproj') + +env.DafnyCompile('src/Dafny/Distributed/Services/SHT/Main.i.cs', 'src/Dafny/Distributed/Services/SHT/Main.i.dfy') +env.DotnetBuild('bin/IronSHTServer.dll', 'src/IronSHTServer/IronSHTServer.csproj') +env.DotnetBuild('bin/IronSHTClient.dll', 'src/IronSHTClient/IronSHTClient.csproj') + +env.DafnyCompile('src/Dafny/Distributed/Services/Lock/Main.i.cs', 'src/Dafny/Distributed/Services/Lock/Main.i.dfy') +env.DotnetBuild('bin/IronLockServer.dll', 'src/IronLockServer/IronLockServer.csproj') + +env.DotnetBuild('bin/CreateIronServiceCerts.dll', 'src/CreateIronServiceCerts/CreateIronServiceCerts.csproj') +env.DotnetBuild('bin/TestIoFramework.dll', 'src/TestIoFramework/TestIoFramework.csproj') diff --git a/ironfleet/src/CreateIronServiceCerts/.gitignore b/ironfleet/src/CreateIronServiceCerts/.gitignore new file mode 100644 index 00000000..a4588fea --- /dev/null +++ b/ironfleet/src/CreateIronServiceCerts/.gitignore @@ -0,0 +1,2 @@ +.vs +Properties/ diff --git a/ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.csproj b/ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.csproj new file mode 100644 index 00000000..185e10aa --- /dev/null +++ b/ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.csproj @@ -0,0 +1,24 @@ + + + + Exe + net5.0 + + + + 1701;1702;162;164;168;183;219;436;1717;1718 + + + + CreateIronServiceCerts.Program + + + + + + + + + + + diff --git a/ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.sln b/ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.sln new file mode 100644 index 00000000..55f167f0 --- /dev/null +++ b/ironfleet/src/CreateIronServiceCerts/CreateIronServiceCerts.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31005.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreateIronServiceCerts", "CreateIronServiceCerts.csproj", "{80249939-7D35-43CA-891A-F4C73EC6D959}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {80249939-7D35-43CA-891A-F4C73EC6D959}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80249939-7D35-43CA-891A-F4C73EC6D959}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80249939-7D35-43CA-891A-F4C73EC6D959}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80249939-7D35-43CA-891A-F4C73EC6D959}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3EDDD386-2BE8-48A4-8188-FFDA2034F16D} + EndGlobalSection +EndGlobal diff --git a/ironfleet/src/CreateIronServiceCerts/Params.cs b/ironfleet/src/CreateIronServiceCerts/Params.cs new file mode 100644 index 00000000..450a5e50 --- /dev/null +++ b/ironfleet/src/CreateIronServiceCerts/Params.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text.RegularExpressions; + +namespace CreateIronServiceCerts +{ + public class Params + { + private static int MAX_SERVER_COUNT = 1000; + private string serviceName; + private string serviceType; + private int maxServerIndex; + private string outputDir; + private Dictionary serverAddrs; + private Dictionary serverPorts; + private bool verbose; + + public Params() + { + serviceName = "MyIronfleetService"; + serviceType = "IronRSLKV"; + maxServerIndex = 0; + outputDir = "."; + serverAddrs = new Dictionary(); + serverPorts = new Dictionary(); + verbose = false; + } + + public int MaxServerIndex { get { return maxServerIndex; } } + public string ServiceName { get { return serviceName; } } + public string ServiceType { get { return serviceType; } } + public string OutputDir { get { return outputDir; } } + public bool Verbose { get { return verbose; } } + + public bool GetServerData (int serverIndex, out string addr, out int port) + { + addr = ""; + port = 0; + if (!serverAddrs.TryGetValue(serverIndex, out addr)) { + return false; + } + if (!serverPorts.TryGetValue(serverIndex, out port)) { + return false; + } + return true; + } + + public bool UseServerIndex(int serverIndex) + { + if (serverIndex >= MAX_SERVER_COUNT) { + Console.WriteLine("ERROR - Server #{0} too big -- must be less than {1}", serverIndex, MAX_SERVER_COUNT); + return false; + } + if (serverIndex == 0) { + Console.WriteLine("ERROR - Server indices should start at 1, not 0. So, don't use addr0 or port0."); + return false; + } + + maxServerIndex = Math.Max(maxServerIndex, serverIndex); + return true; + } + + public bool Validate() + { + if (maxServerIndex == 0) + { + Console.WriteLine("ERROR - No server data supplied. You need to provide at least addr1 and port1."); + return false; + } + + for (int serverIndex = 1; serverIndex <= maxServerIndex; ++serverIndex) { + if (!serverAddrs.ContainsKey(serverIndex)) { + Console.WriteLine("ERROR - Missing addr{0}", serverIndex); + return false; + } + if (!serverPorts.ContainsKey(serverIndex)) { + Console.WriteLine("ERROR - Missing port{0}", serverIndex); + return false; + } + } + return true; + } + + public bool ProcessCommandLineArgument(string arg) + { + var pos = arg.IndexOf("="); + if (pos < 0) { + Console.WriteLine("ERROR - Invalid argument {0}", arg); + return false; + } + var key = arg.Substring(0, pos).ToLower(); + var value = arg.Substring(pos + 1); + return SetValue(key, value); + } + + private bool SetValue(string key, string value) + { + if (key == "name") { + serviceName = value; + return true; + } + + if (key == "type") { + serviceType = value; + return true; + } + + if (key == "verbose") { + if (value == "false") { + verbose = false; + return true; + } + if (value == "true") { + verbose = true; + return true; + } + Console.WriteLine("ERROR - Invalid verbose value {0} - should be false or true", value); + return false; + } + + if (key == "outputdir") { + outputDir = value; + try { + Directory.CreateDirectory(outputDir); + } + catch (Exception e) { + Console.WriteLine("ERROR - Can't create requested output directory {0}", outputDir); + return false; + } + return true; + } + + Match m = Regex.Match(key, @"^addr(\d+)$"); + if (m.Success) { + if (value.Length == 0) { + Console.WriteLine("ERROR - Address {0} cannot be empty", key); + return false; + } + int serverIndex = Convert.ToInt32(m.Groups[1].Value); + if (!UseServerIndex(serverIndex)) { + return false; + } + serverAddrs[serverIndex] = value; + maxServerIndex = Math.Max(maxServerIndex, serverIndex); + return true; + } + + m = Regex.Match(key, @"^port(\d+)$"); + if (m.Success) { + int port; + try { + port = Convert.ToInt32(value); + } + catch (Exception e) { + Console.WriteLine("ERROR - Invalid port number {0} given for key {1}", value, key); + return false; + } + if (port == 0 || port > 65535) { + Console.WriteLine("ERROR - Invalid port number {0} given for key {1}", value, key); + return false; + } + + int serverIndex = Convert.ToInt32(m.Groups[1].Value); + if (!UseServerIndex(serverIndex)) { + return false; + } + + serverPorts[serverIndex] = port; + return true; + } + + Console.WriteLine("ERROR - Invalid argument key {0}", key); + return false; + } + } +} diff --git a/ironfleet/src/CreateIronServiceCerts/Program.cs b/ironfleet/src/CreateIronServiceCerts/Program.cs new file mode 100644 index 00000000..e972edcc --- /dev/null +++ b/ironfleet/src/CreateIronServiceCerts/Program.cs @@ -0,0 +1,83 @@ +using IronfleetIoFramework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Numerics; +using System.Threading; + +namespace CreateIronServiceCerts +{ + class Program + { + static void usage() + { + Console.Write(@" +Usage: dotnet CreateIronServiceCerts.dll [key=value]... + +Allowed keys: + name - friendly name of the service (default ""MyIronfleetService"") + type - service type (default ""IronRSLKV"") + outputdir - directory to write certificates into (default ""."") + addr - public address (host name or IP) of server # + port - public port of server # + verbose - print verbose output (false or true, default false) +"); + } + + static void Main(string[] args) + { + Params ps = new Params(); + + foreach (var arg in args) + { + if (!ps.ProcessCommandLineArgument(arg)) { + usage(); + return; + } + } + + if (!ps.Validate()) { + return; + } + + List serverPublicIdentities = new List(); + + for (int serverIndex = 1; serverIndex <= ps.MaxServerIndex; ++serverIndex) { + string addr; + int port; + if (!ps.GetServerData(serverIndex, out addr, out port)) { + Console.WriteLine("ERROR - Missing data for server #{0}", serverIndex); + return; + } + + PublicIdentity publicIdentity; + PrivateIdentity privateIdentity; + string serverName = string.Format("{0}.{1}.server{2}", ps.ServiceName, ps.ServiceType, serverIndex); + IronfleetCrypto.CreateNewIdentity(serverName, addr, port, out publicIdentity, out privateIdentity); + + var privateKeyFileName = Path.Join(ps.OutputDir, string.Format("{0}.private.txt", serverName)); + if (!privateIdentity.WriteToFile(privateKeyFileName)) { + return; + } + Console.WriteLine("Successfully wrote private key for server {0} to {1}", serverIndex, privateKeyFileName); + + serverPublicIdentities.Add(publicIdentity); + } + + var serviceIdentity = new ServiceIdentity { + FriendlyName = ps.ServiceName, + ServiceType = ps.ServiceType, + Servers = serverPublicIdentities + }; + var serviceFileName = Path.Join(ps.OutputDir, string.Format("{0}.{1}.service.txt", ps.ServiceName, ps.ServiceType)); + if (!serviceIdentity.WriteToFile(serviceFileName)) { + return; + } + Console.WriteLine("Successfully wrote service description to {0}", serviceFileName); + + Console.WriteLine("DONE - SUCCESS"); + } + } +} diff --git a/ironfleet/src/Dafny/Distributed/Common/Framework/DistributedSystem.s.dfy b/ironfleet/src/Dafny/Distributed/Common/Framework/DistributedSystem.s.dfy index 0c4cccae..6de4a262 100755 --- a/ironfleet/src/Dafny/Distributed/Common/Framework/DistributedSystem.s.dfy +++ b/ironfleet/src/Dafny/Distributed/Common/Framework/DistributedSystem.s.dfy @@ -11,30 +11,8 @@ import opened Native__NativeTypes_s ///////////////////////////////////////// // PHYSICAL ENVIRONMENT -// -// TODO - Move this stuff to Io.s -// ///////////////////////////////////////// -predicate ValidPhysicalAddress(endPoint:EndPoint) -{ - && |endPoint.addr| == 4 - && 0 <= endPoint.port <= 65535 -} - -predicate ValidPhysicalPacket(p:LPacket>) -{ - && ValidPhysicalAddress(p.src) - && ValidPhysicalAddress(p.dst) - && |p.msg| < 0x1_0000_0000_0000_0000 -} - -predicate ValidPhysicalIo(io:LIoOp>) -{ - && (io.LIoOpReceive? ==> ValidPhysicalPacket(io.r)) - && (io.LIoOpSend? ==> ValidPhysicalPacket(io.s)) -} - predicate ValidPhysicalEnvironmentStep(step:LEnvStep>) { step.LEnvStepHostIos? ==> forall io{:trigger io in step.ios}{:trigger ValidPhysicalIo(io)} :: io in step.ios ==> ValidPhysicalIo(io) @@ -47,15 +25,15 @@ predicate ValidPhysicalEnvironmentStep(step:LEnvStep>) datatype DS_State = DS_State( config:H_s.ConcreteConfiguration, environment:LEnvironment>, - servers:map, - clients:set + servers:map ) predicate DS_Init(s:DS_State, config:H_s.ConcreteConfiguration) reads * { && s.config == config - && H_s.ConcreteConfigInit(s.config, mapdomain(s.servers), s.clients) + && H_s.ConcreteConfigToServers(s.config) == mapdomain(s.servers) + && H_s.ConcreteConfigInit(s.config) && LEnvironment_Init(s.environment) && (forall id :: id in s.servers ==> H_s.HostInit(s.servers[id], config, id)) } @@ -73,7 +51,6 @@ predicate DS_Next(s:DS_State, s':DS_State) reads * { && s'.config == s.config - && s'.clients == s.clients && LEnvironment_Next(s.environment, s'.environment) && ValidPhysicalEnvironmentStep(s.environment.nextStep) && if s.environment.nextStep.LEnvStepHostIos? && s.environment.nextStep.actor in s.servers then diff --git a/ironfleet/src/Dafny/Distributed/Common/Framework/Host.s.dfy b/ironfleet/src/Dafny/Distributed/Common/Framework/Host.s.dfy index b7610154..ac311bb2 100755 --- a/ironfleet/src/Dafny/Distributed/Common/Framework/Host.s.dfy +++ b/ironfleet/src/Dafny/Distributed/Common/Framework/Host.s.dfy @@ -14,45 +14,36 @@ predicate HostInit(host_state:HostState, config:ConcreteConfiguration, id:EndPoi reads * predicate HostNext(host_state:HostState, host_state':HostState, ios:seq>>) reads * -predicate ConcreteConfigInit(config:ConcreteConfiguration, servers:set, clients:set) + +predicate ConcreteConfigInit(config:ConcreteConfiguration) +function ConcreteConfigToServers(config:ConcreteConfiguration) : set predicate HostStateInvariants(host_state:HostState, env:HostEnvironment) reads * -predicate ConcreteConfigurationInvariants(config:ConcreteConfiguration) -function ResolveCommandLine(args:seq>) : (args':seq>) - ensures |args'| >= |args| -function ParseCommandLineConfiguration(args:seq>) : (ConcreteConfiguration, set, set) -function ParseCommandLineId(ip:seq, port:seq) : EndPoint +function ParseCommandLineConfiguration(args:seq>) : ConcreteConfiguration predicate ArbitraryObject(o:object) { true } -// TODO: Prohibit HostInitImpl from sending (and receiving?) packets -method HostInitImpl(ghost env:HostEnvironment) returns ( +method HostInitImpl(ghost env:HostEnvironment, netc:NetClient, args:seq>) returns ( ok:bool, - host_state:HostState, - config:ConcreteConfiguration, - ghost servers:set, - ghost clients:set, - id:EndPoint + host_state:HostState ) requires env.Valid() requires env.ok.ok() - requires |env.constants.CommandLineArgs()| >= 2 + requires netc.IsOpen() + requires netc.env == env + requires ValidPhysicalAddress(EndPoint(netc.MyPublicKey())) modifies set x:object | ArbitraryObject(x) // Everything! ensures ok ==> env.Valid() && env.ok.ok() - ensures ok ==> |env.constants.CommandLineArgs()| >= 2 ensures ok ==> HostStateInvariants(host_state, env) - ensures ok ==> ConcreteConfigurationInvariants(config) - ensures ok ==> var args := ResolveCommandLine(env.constants.CommandLineArgs()); - var (parsed_config, parsed_servers, parsed_clients) := ParseCommandLineConfiguration(args[0..|args|-2]); - && config == parsed_config - && servers == parsed_servers - && clients == parsed_clients - && ConcreteConfigInit(parsed_config, parsed_servers, parsed_clients); - ensures ok ==> var args := ResolveCommandLine(env.constants.CommandLineArgs()); - && id == ParseCommandLineId(args[|args|-2], args[|args|-1]) - && HostInit(host_state, config, id); + ensures ok ==> var id := EndPoint(netc.MyPublicKey()); + var config := ParseCommandLineConfiguration(args); + && id in ConcreteConfigToServers(config) + && ConcreteConfigInit(config) + && HostInit(host_state, config, id) + ensures env.net.history() == old(env.net.history()) // Prohibit HostInitImpl from sending (and receiving) packets + method HostNextImpl(ghost env:HostEnvironment, host_state:HostState) returns ( @@ -67,17 +58,16 @@ method HostNextImpl(ghost env:HostEnvironment, host_state:HostState) requires HostStateInvariants(host_state, env) modifies set x:object | ArbitraryObject(x) // Everything! ensures ok <==> env.Valid() && env.ok.ok() - // TODO: Even when !ok, if sent is non-empty, we need to return valid set of sent packets too ensures ok ==> HostStateInvariants(host_state', env) ensures ok ==> HostNext(host_state, host_state', ios) // Connect the low-level IO events to the spec-level IO events ensures ok ==> recvs + clocks + sends == ios // These obligations enable us to apply reduction - ensures ok ==> env.net.history() == old(env.net.history()) + (recvs + clocks + sends) + // Even when !ok, if sent is non-empty, we need to return valid set of sent packets too + ensures (ok || |sends| > 0) ==> env.net.history() == old(env.net.history()) + (recvs + clocks + sends) ensures forall e :: && (e in recvs ==> e.LIoOpReceive?) && (e in clocks ==> e.LIoOpReadClock? || e.LIoOpTimeoutReceive?) && (e in sends ==> e.LIoOpSend?) ensures |clocks| <= 1 } - diff --git a/ironfleet/src/Dafny/Distributed/Common/Framework/Main.s.dfy b/ironfleet/src/Dafny/Distributed/Common/Framework/Main.s.dfy index 80a4eb70..3ae5acac 100755 --- a/ironfleet/src/Dafny/Distributed/Common/Framework/Main.s.dfy +++ b/ironfleet/src/Dafny/Distributed/Common/Framework/Main.s.dfy @@ -6,18 +6,23 @@ include "../Collections/Seqs.s.dfy" abstract module Main_s { import opened Native__Io_s +import opened Native__NativeTypes_s import opened DS_s : DistributedSystem_s import opened AS_s : AbstractService_s import opened Collections__Seqs_s -method {:main} Main(ghost env:HostEnvironment) +method IronfleetMain(ghost env:HostEnvironment, netc:NetClient, args:seq>) requires env.Valid() && env.ok.ok() requires env.net.history() == [] - requires |env.constants.CommandLineArgs()| >= 2 + requires netc.IsOpen() + requires netc.env == env + requires ValidPhysicalAddress(EndPoint(netc.MyPublicKey())) modifies set x:object | DS_s.H_s.ArbitraryObject(x) // Everything! decreases * { - var ok, host_state, config, servers, clients, id := DS_s.H_s.HostInitImpl(env); + var ok, host_state := DS_s.H_s.HostInitImpl(env, netc, args); + var config := DS_s.H_s.ParseCommandLineConfiguration(args); + var id := EndPoint(netc.MyPublicKey()); assert ok ==> DS_s.H_s.HostInit(host_state, config, id); while (ok) diff --git a/ironfleet/src/Dafny/Distributed/Common/Native/Io.s.dfy b/ironfleet/src/Dafny/Distributed/Common/Native/Io.s.dfy index 48a97929..acd51fd9 100755 --- a/ironfleet/src/Dafny/Distributed/Common/Native/Io.s.dfy +++ b/ironfleet/src/Dafny/Distributed/Common/Native/Io.s.dfy @@ -7,7 +7,6 @@ import opened Environment_s class HostEnvironment { - ghost var constants:HostConstants; ghost var ok:OkState; ghost var now:NowState; ghost var net:NetState; @@ -23,36 +22,25 @@ class HostEnvironment } ////////////////////////////////////////////////////////////////////////////// -// Per-host constants +// Failure ////////////////////////////////////////////////////////////////////////////// -class HostConstants +// not failed; IO operations only allowed when ok() == true +class OkState { constructor{:axiom} () requires false - - function{:axiom} LocalAddress():seq reads this // REVIEW: Do we need this anymore? We now allow different NetClients to have different addresses anyway. - function{:axiom} CommandLineArgs():seq> reads this // result of C# System.Environment.GetCommandLineArgs(); argument 0 is name of executable - - static method{:axiom} NumCommandLineArgs(ghost env:HostEnvironment) returns(n:uint32) - requires env.Valid() - ensures n as int == |env.constants.CommandLineArgs()| - - static method{:axiom} GetCommandLineArg(i:uint64, ghost env:HostEnvironment) returns(arg:array) - requires env.Valid() - requires 0 <= i as int < |env.constants.CommandLineArgs()| - ensures fresh(arg) - ensures arg[..] == env.constants.CommandLineArgs()[i] + function{:axiom} ok():bool reads this } ////////////////////////////////////////////////////////////////////////////// -// Failure +// Print parameters ////////////////////////////////////////////////////////////////////////////// -// not failed; IO operations only allowed when ok() == true -class OkState +class PrintParams { constructor{:axiom} () requires false - function{:axiom} ok():bool reads this + static function method{:axiom} ShouldPrintProfilingInfo() : bool + static function method{:axiom} ShouldPrintProgress() : bool } ////////////////////////////////////////////////////////////////////////////// @@ -81,71 +69,53 @@ class Time ensures AdvanceTime(old(env.now.now()), env.now.now(), 0) ensures env.net.history() == old(env.net.history()) + [LIoOpReadClock(t as int)] - // Used for performance debugging - static method{:axiom} GetDebugTimeTicks() returns(t:uint64) - static method{:axiom} RecordTiming(name:array, time:uint64) + // Used for performance debugging + static method{:axiom} GetDebugTimeTicks() returns(t:uint64) + static method{:axiom} RecordTiming(name:array, time:uint64) } ////////////////////////////////////////////////////////////////////////////// // Networking ////////////////////////////////////////////////////////////////////////////// -datatype EndPoint = EndPoint(addr:seq, port:uint16) +datatype EndPoint = EndPoint(public_key:seq) // NetPacket_ctor has silly name to ferret out backwards calls type NetPacket = LPacket> type NetEvent = LIoOp> -class NetState +function MaxPacketSize() : int { 0xFFFF_FFFF_FFFF_FFFF } + +predicate ValidPhysicalAddress(endPoint:EndPoint) { - constructor{:axiom} () requires false - function{:axiom} history():seq reads this + |endPoint.public_key| < 0x10_0000 // < 1 MB +} + +predicate ValidPhysicalPacket(p:LPacket>) +{ + && ValidPhysicalAddress(p.src) + && ValidPhysicalAddress(p.dst) + && |p.msg| <= MaxPacketSize() +} + +predicate ValidPhysicalIo(io:LIoOp>) +{ + && (io.LIoOpReceive? ==> ValidPhysicalPacket(io.r)) + && (io.LIoOpSend? ==> ValidPhysicalPacket(io.s)) } -class IPEndPoint +class NetState { - ghost var env:HostEnvironment - function{:axiom} Address():seq reads this - function{:axiom} Port():uint16 reads this - function EP():EndPoint reads this { EndPoint(Address(), Port()) } constructor{:axiom} () requires false - - method{:axiom} GetAddress() returns(addr:array) - ensures fresh(addr) - ensures addr[..] == Address() - ensures addr.Length == 4 // Encoding current IPv4 assumption - - function method{:axiom} GetPort():uint16 reads this - ensures GetPort() == Port() - - static method{:axiom} Construct(ipAddress:array, port:uint16, ghost env:HostEnvironment) returns(ok:bool, ep:IPEndPoint) - requires env.Valid() - modifies env.ok - ensures env.ok.ok() == ok - ensures ok ==> fresh(ep) && ep.env == env && ep.Address() == ipAddress[..] && ep.Port() == port - - static function method{:axiom} DnsResolve(name:seq):(resolved_name:seq) + function{:axiom} history():seq reads this } -function MaxPacketSize() : int { 0xFFFF_FFFF_FFFF_FFFF } - class NetClient { ghost var env:HostEnvironment - function{:axiom} LocalEndPoint():EndPoint reads this + function method{:axiom} MyPublicKey():seq reads this function{:axiom} IsOpen():bool reads this constructor{:axiom} () requires false - static method{:axiom} Construct(localEP:IPEndPoint, ghost env:HostEnvironment) - returns(ok:bool, net:NetClient?) - requires env.Valid() - requires env.ok.ok() - modifies env.ok - ensures env.ok.ok() == ok - ensures ok ==> && fresh(net) - && net.env == env - && net.IsOpen() - && net.LocalEndPoint() == localEP.EP() - method{:axiom} Close() returns(ok:bool) requires env.Valid() requires env.ok.ok() @@ -155,7 +125,7 @@ class NetClient ensures env == old(env) ensures env.ok.ok() == ok - method{:axiom} Receive(timeLimit:int32) returns(ok:bool, timedOut:bool, remote:IPEndPoint, buffer:array) + method{:axiom} Receive(timeLimit:int32) returns(ok:bool, timedOut:bool, remote:seq, buffer:array) requires env.Valid() requires env.ok.ok() requires IsOpen() @@ -168,17 +138,17 @@ class NetClient ensures env == old(env) ensures env.ok.ok() == ok ensures AdvanceTime(old(env.now.now()), env.now.now(), timeLimit as int) - ensures LocalEndPoint() == old(LocalEndPoint()) + ensures MyPublicKey() == old(MyPublicKey()) ensures ok ==> IsOpen() ensures ok ==> timedOut ==> env.net.history() == old(env.net.history()) + [LIoOpTimeoutReceive()] ensures ok ==> !timedOut ==> - && fresh(remote) && fresh(buffer) && env.net.history() == old(env.net.history()) + - [LIoOpReceive(LPacket(LocalEndPoint(), remote.EP(), buffer[..]))] - && buffer.Length < 0x1_0000_0000_0000_0000; + [LIoOpReceive(LPacket(EndPoint(MyPublicKey()), EndPoint(remote), buffer[..]))] + && ValidPhysicalAddress(EndPoint(remote)) + && buffer.Length <= MaxPacketSize() - method{:axiom} Send(remote:IPEndPoint, buffer:array) returns(ok:bool) + method{:axiom} Send(remote:seq, buffer:array) returns(ok:bool) requires env.Valid() requires env.ok.ok() requires IsOpen() @@ -188,9 +158,9 @@ class NetClient modifies env.net ensures env == old(env) ensures env.ok.ok() == ok - ensures LocalEndPoint() == old(LocalEndPoint()) + ensures MyPublicKey() == old(MyPublicKey()) ensures ok ==> IsOpen() - ensures ok ==> env.net.history() == old(env.net.history()) + [LIoOpSend(LPacket(remote.EP(), LocalEndPoint(), buffer[..]))] + ensures ok ==> env.net.history() == old(env.net.history()) + [LIoOpSend(LPacket(EndPoint(remote), EndPoint(MyPublicKey()), buffer[..]))] } // jonh temporarily neutered this because the opaque type can't be compiled diff --git a/ironfleet/src/Dafny/Distributed/Common/Native/IoFramework.cs b/ironfleet/src/Dafny/Distributed/Common/Native/IoFramework.cs index fdef3425..31dde0c3 100755 --- a/ironfleet/src/Dafny/Distributed/Common/Native/IoFramework.cs +++ b/ironfleet/src/Dafny/Distributed/Common/Native/IoFramework.cs @@ -1,15 +1,201 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Net.Security; using System.Net.Sockets; +using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks.Dataflow; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; namespace IronfleetIoFramework { + public class PrivateIdentity + { + public string FriendlyName { get; set; } + public byte[] Pkcs12 { get; set; } + public string HostNameOrAddress { get; set; } + public int Port { get; set; } + + public bool WriteToFile (string fileName) + { + string json; + + try { + json = JsonSerializer.Serialize(this); + } + catch (Exception e) { + Console.Error.WriteLine("Could not serialize private key data for {0}. Exception:\n{1}", FriendlyName, e); + return false; + } + + try { + File.WriteAllText(fileName, json); + } + catch (Exception e) { + Console.Error.WriteLine("Could not create file {0}. Exception:\n{1}", fileName, e); + return false; + } + + return true; + } + + public static PrivateIdentity ReadFromFile(string fileName) + { + string json; + + try { + json = File.ReadAllText(fileName); + } + catch (Exception) { + Console.Error.WriteLine("ERROR - Could not read contents of private key file {0}", fileName); + return null; + } + + PrivateIdentity privateIdentity; + try { + privateIdentity = JsonSerializer.Deserialize(json); + } + catch (Exception e) { + Console.Error.WriteLine("Could not deserialize contents of private key file {0}. Exception:\n{1}", fileName, e); + return null; + } + + return privateIdentity; + } + } + + public class PublicIdentity + { + public string FriendlyName { get; set; } + public byte[] PublicKey { get; set; } + public string HostNameOrAddress { get; set; } + public int Port { get; set; } + } + + public class ServiceIdentity + { + public string FriendlyName { get; set; } + public string ServiceType { get; set; } + public List Servers { get; set; } + + public bool WriteToFile(string fileName) + { + string json; + + try { + json = JsonSerializer.Serialize(this); + } + catch (Exception e) { + Console.Error.WriteLine("Could not serialize service identity. Exception:\n{0}", e); + return false; + } + + try { + File.WriteAllText(fileName, json); + } + catch (Exception e) { + Console.Error.WriteLine("Could not write service identity to file {0}. Exception:\n{1}", fileName, e); + return false; + } + + return true; + } + + public static ServiceIdentity ReadFromFile(string fileName) + { + string json; + + try { + json = File.ReadAllText(fileName); + } + catch (Exception) { + Console.Error.WriteLine("ERROR - Could not read contents of service file {0}", fileName); + return null; + } + + ServiceIdentity serviceIdentity; + try { + serviceIdentity = JsonSerializer.Deserialize(json); + } + catch (Exception e) { + Console.Error.WriteLine("Could not deserialize contents of service file {0}. Exception:\n{1}", fileName, e); + return null; + } + + return serviceIdentity; + } + } + + public class ByteArrayComparer : IEqualityComparer + { + private static ByteArrayComparer staticDefault; + + public static ByteArrayComparer Default() + { + if (staticDefault == null) { + staticDefault = new ByteArrayComparer(); + } + return staticDefault; + } + + public bool Equals (byte[] a1, byte[] a2) + { + return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2); + } + + public int GetHashCode(byte[] a) + { + return StructuralComparisons.StructuralEqualityComparer.GetHashCode(a); + } + } + + public class IronfleetCrypto + { + public static void CreateNewIdentity(string friendlyName, string hostNameOrAddress, int port, + out PublicIdentity publicIdentity, out PrivateIdentity privateIdentity) + { + var key = RSA.Create(4096); + var subject = string.Format("CN = {0}", friendlyName); + var req = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); + var now = DateTime.Now; + var expiry = now.AddYears(10); + var cert = req.CreateSelfSigned(now, expiry); + var pkcs12 = cert.Export(X509ContentType.Pkcs12, "" /* empty password */); + + publicIdentity = new PublicIdentity { + FriendlyName = friendlyName, + PublicKey = IoScheduler.GetCertificatePublicKey(cert), + HostNameOrAddress = hostNameOrAddress, + Port = port + }; + privateIdentity = new PrivateIdentity { + FriendlyName = friendlyName, + Pkcs12 = pkcs12, + HostNameOrAddress = hostNameOrAddress, + Port = port + }; + } + + public static X509Certificate2 CreateTransientClientIdentity () + { + var key = RSA.Create(2048); + var req = new CertificateRequest("CN = client", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); + var now = DateTime.Now; + var expiry = now.AddYears(1); + var cert = req.CreateSelfSigned(now, expiry); + // On Linux, it's OK to just return cert. But on Windows, we need the following + // code to allow it to be used to authenticate a client. + return new X509Certificate2(cert.Export(X509ContentType.Pkcs12)); + } + } + public class IoEncoder { private static int MAX_IO_SIZE = 0x80_0000 /* 8 MB */; @@ -143,47 +329,125 @@ public static bool ReadInt32(Stream stream, out Int32 value) } } - public class Packet + public class ReceivedPacket { - public IPEndPoint ep; - public byte[] message; + private X509Certificate2 senderCert; + private byte[] message; - public Packet(IPEndPoint i_ep, byte[] i_message) + public ReceivedPacket(X509Certificate2 i_senderCert, byte[] i_message) { - ep = i_ep; + senderCert = i_senderCert; message = i_message; } + + public X509Certificate2 SenderCert { get { return senderCert; } } + public byte[] Message { get { return message; } } } public class SendTask { - public IPEndPoint ep; - public byte[] message; - public int numTriesSoFar; + private byte[] destinationPublicKey; + private byte[] message; + private int numTriesSoFar; - public SendTask(IPEndPoint i_ep, byte[] i_message, int i_numTriesSoFar) + public SendTask(byte[] i_destinationPublicKey, byte[] i_message) { - ep = i_ep; + destinationPublicKey = i_destinationPublicKey; message = i_message; - numTriesSoFar = i_numTriesSoFar; + numTriesSoFar = 0; + } + + public byte[] DestinationPublicKey { get { return destinationPublicKey; } } + public byte[] Message { get { return message; } } + + public int IncrementNumTriesSoFar() + { + numTriesSoFar++; + return numTriesSoFar; + } + } + + public class CertificateValidator + { + private IoScheduler scheduler; + private PublicIdentity expectedPublicIdentity; + + public CertificateValidator(IoScheduler i_scheduler, PublicIdentity i_expectedPublicIdentity = null) + { + scheduler = i_scheduler; + expectedPublicIdentity = i_expectedPublicIdentity; + } + + public bool ValidateSSLCertificate(object sender, X509Certificate certificate, X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + const SslPolicyErrors ignoredErrors = SslPolicyErrors.RemoteCertificateChainErrors; + + if ((sslPolicyErrors & ~ignoredErrors) != SslPolicyErrors.None) { + Console.Error.WriteLine("Could not validate SSL certificate for {0} due to errors {1}", + IoScheduler.GetCertificatePublicKey(certificate as X509Certificate2), + sslPolicyErrors & ~ignoredErrors); + return false; + } + + var cert2 = certificate as X509Certificate2; + + // If we were expecting a specific public identity, check that + // the key in the certificate matches what we were expecting. + + if (expectedPublicIdentity != null) { + if (!ByteArrayComparer.Default().Equals(IoScheduler.GetCertificatePublicKey(cert2), expectedPublicIdentity.PublicKey)) { + Console.Error.WriteLine("Connected to {0} expecting public key {1} but found public key {2}, so disconnecting.", + IoScheduler.PublicIdentityToString(expectedPublicIdentity), + IoScheduler.PublicKeyToString(expectedPublicIdentity.PublicKey), + IoScheduler.PublicKeyToString(IoScheduler.GetCertificatePublicKey(cert2))); + return false; + } + + if (cert2.SubjectName.Name != "CN=" + expectedPublicIdentity.FriendlyName) { + Console.Error.WriteLine("Connected to {0} expecting subject CN={1} but found {2}, so disconnecting.", + IoScheduler.PublicIdentityToString(expectedPublicIdentity), + expectedPublicIdentity.FriendlyName, + cert2.SubjectName.Name); + return false; + } + } + else { + // If we weren't expecting any particular public identity, + // consider the expected public identity to be the known one + // matching the public key in the certificate we got. If + // there is no known one, then this is just an anonymous + // client, which is fine. Otherwise, check that the subject + // matches what we expect. This is just a paranoid check; it + // should never fail. + + expectedPublicIdentity = scheduler.LookupPublicKey(IoScheduler.GetCertificatePublicKey(cert2)); + if (expectedPublicIdentity != null) { + if (cert2.SubjectName.Name != "CN=" + expectedPublicIdentity.FriendlyName) { + Console.Error.WriteLine("Received a certificate we expected to have subject CN={1} but found {2}, so disconnecting.", + IoScheduler.PublicIdentityToString(expectedPublicIdentity), + expectedPublicIdentity.FriendlyName, + cert2.SubjectName.Name); + return false; + } + } + } + + return true; } } public class ReceiverThread { private IoScheduler scheduler; - private TcpClient conn; - private IPEndPoint otherEndpoint; - private bool asServer; - private NetworkStream stream; + private SslStream stream; + private X509Certificate2 remoteCert; - private ReceiverThread(IoScheduler i_scheduler, TcpClient i_conn, IPEndPoint i_otherEndpoint, bool i_asServer) + private ReceiverThread(IoScheduler i_scheduler, SslStream i_stream) { scheduler = i_scheduler; - conn = i_conn; - otherEndpoint = i_otherEndpoint; - asServer = i_asServer; - stream = conn.GetStream(); + stream = i_stream; + remoteCert = stream.RemoteCertificate as X509Certificate2; } public void Run() @@ -194,13 +458,13 @@ public void Run() } catch (Exception e) { - scheduler.ReportException(e, "receiving from", otherEndpoint); + scheduler.ReportException(e, "receiving from " + IoScheduler.GetCertificatePublicKey(remoteCert)); } } - public static ReceiverThread Create(IoScheduler scheduler, TcpClient conn, IPEndPoint otherEndpoint, bool asServer) + public static ReceiverThread Create(IoScheduler scheduler, SslStream stream) { - ReceiverThread receiverThread = new ReceiverThread(scheduler, conn, otherEndpoint, asServer); + ReceiverThread receiverThread = new ReceiverThread(scheduler, stream); Thread t = new Thread(receiverThread.Run); t.Start(); return receiverThread; @@ -211,29 +475,7 @@ private void ReceiveLoop() bool success; if (scheduler.Verbose) { - Console.WriteLine("Starting receive loop with other endpoint {0}", IoScheduler.EndpointToString(otherEndpoint)); - } - - // The first thing sent by the client is its effective port - // number. - - if (asServer) { - Int32 portNumber; - success = IoEncoder.ReadInt32(stream, out portNumber); - if (!success) - { - Console.Error.WriteLine("Failed to receive port number from {0}", IoScheduler.EndpointToString(otherEndpoint)); - return; - } - if (scheduler.Verbose) { - Console.WriteLine("Received effective port number {0} from {1}", portNumber, IoScheduler.EndpointToString(otherEndpoint)); - } - otherEndpoint.Port = portNumber; - - // Now that we know who we're talking to, we can create the - // sender thread. - - SenderThread senderThread = SenderThread.Create(scheduler, conn, otherEndpoint, asServer); + Console.WriteLine("Starting receive loop with remote identified as {0}", IoScheduler.CertificateToString(remoteCert)); } while (true) @@ -243,62 +485,78 @@ private void ReceiveLoop() UInt64 messageSize; success = IoEncoder.ReadUInt64(stream, out messageSize); if (!success) { - Console.Error.WriteLine("Failed to receive message size from {0}", IoScheduler.EndpointToString(otherEndpoint)); + if (scheduler.Verbose) { + Console.Error.WriteLine("Failed to receive message size from {0}", IoScheduler.CertificateToString(remoteCert)); + } return; } if (scheduler.Verbose) { - Console.WriteLine("Received message size {0} from {1}", messageSize, IoScheduler.EndpointToString(otherEndpoint)); + Console.WriteLine("Received message size {0} from {1}", messageSize, IoScheduler.CertificateToString(remoteCert)); } byte[] messageBuf = new byte[messageSize]; success = IoEncoder.ReadBytes(stream, messageBuf, 0, messageSize); if (!success) { - Console.Error.WriteLine("Failed to receive message of size {0} from {1}", - messageSize, IoScheduler.EndpointToString(otherEndpoint)); + if (scheduler.Verbose) { + Console.Error.WriteLine("Failed to receive message of size {0} from {1}", + messageSize, IoScheduler.CertificateToString(remoteCert)); + } return; } if (scheduler.Verbose) { - Console.WriteLine("Received message of size {0} from {1}", messageSize, IoScheduler.EndpointToString(otherEndpoint)); + Console.WriteLine("Received message of size {0} from {1}", messageSize, IoScheduler.CertificateToString(remoteCert)); } - Packet packet = new Packet(otherEndpoint, messageBuf); + ReceivedPacket packet = new ReceivedPacket(remoteCert, messageBuf); scheduler.NoteReceivedPacket(packet); } } } - public class SenderThread + public abstract class SenderThread { - private IoScheduler scheduler; - private TcpClient conn; - private IPEndPoint otherEndpoint; - private bool asServer; - private NetworkStream stream; + protected IoScheduler scheduler; + protected byte[] destinationPublicKey; + protected SslStream stream; + protected X509Certificate2 remoteCert; private BufferBlock sendQueue; private SendTask currentSendTask; - private SenderThread(IoScheduler i_scheduler, TcpClient i_conn, IPEndPoint i_otherEndpoint, bool i_asServer) + protected SenderThread(IoScheduler i_scheduler, byte[] i_destinationPublicKey, SslStream i_stream, + X509Certificate2 i_remoteCert) { scheduler = i_scheduler; - conn = i_conn; - otherEndpoint = i_otherEndpoint; - asServer = i_asServer; + destinationPublicKey = i_destinationPublicKey; + stream = i_stream; + remoteCert = i_remoteCert; sendQueue = new BufferBlock(); currentSendTask = null; } - public void Run() + protected abstract string EndpointDescription(); + protected abstract bool Connect(); + + public void Start() + { + scheduler.RegisterSender(destinationPublicKey, this); + Thread t = new Thread(this.Run); + t.Start(); + } + + private void Run() { try { - SendLoop(); + if (Connect()) { + SendLoop(); + } } catch (Exception e) { - scheduler.ReportException(e, "sending to", otherEndpoint); + scheduler.ReportException(e, "sending to " + EndpointDescription()); } - scheduler.UnregisterSender(otherEndpoint, this); + scheduler.UnregisterSender(destinationPublicKey, this); // If we crashed in the middle of sending a packet, re-queue it // for sending by another sender thread. @@ -317,62 +575,10 @@ public void Run() } } - public static SenderThread Create(IoScheduler scheduler, TcpClient conn, IPEndPoint otherEndpoint, bool asServer) - { - if (scheduler.Verbose) { - Console.WriteLine("Creating sender thread for endpoint {0}", IoScheduler.EndpointToString(otherEndpoint)); - } - - SenderThread senderThread = new SenderThread(scheduler, conn, otherEndpoint, asServer); - scheduler.RegisterSender(otherEndpoint, senderThread); - Thread t = new Thread(senderThread.Run); - t.Start(); - return senderThread; - } - private void SendLoop() { if (scheduler.Verbose) { - Console.WriteLine("Starting send loop with other endpoint {0}", IoScheduler.EndpointToString(otherEndpoint)); - } - - // If we're not the server, we need to initiate the connection. - - if (!asServer) { - var zeroEndpoint = new IPEndPoint(scheduler.MyEndpoint.Address, 0); - - try { - conn = new TcpClient(zeroEndpoint); - } - catch (Exception e) - { - Console.Error.WriteLine("Could not bind to address {0} to send to {1}, causing exception:\n{2}", - zeroEndpoint.Address, IoScheduler.EndpointToString(otherEndpoint), e); - throw; - } - - if (scheduler.Verbose) { - Console.WriteLine("Starting connection to {0}", IoScheduler.EndpointToString(otherEndpoint)); - } - conn.Connect(otherEndpoint); - - // Now that the connection is successful, create a thread to - // receive packets on it. - - ReceiverThread receiverThread = ReceiverThread.Create(scheduler, conn, otherEndpoint, asServer); - } - - stream = conn.GetStream(); - - // The first thing sent by the client is its effective port - // number. - - if (!asServer) { - Int32 myPortNumber = scheduler.MyEndpoint.Port; - if (scheduler.Verbose) { - Console.WriteLine("Sending my effective port number {0} to {1}", myPortNumber, IoScheduler.EndpointToString(otherEndpoint)); - } - IoEncoder.WriteInt32(stream, myPortNumber); + Console.WriteLine("Starting send loop with {0}", EndpointDescription()); } while (true) @@ -383,17 +589,17 @@ private void SendLoop() // Send its length as an 8-byte value. - UInt64 messageSize = (UInt64)currentSendTask.message.Length; + UInt64 messageSize = (UInt64)currentSendTask.Message.Length; IoEncoder.WriteUInt64(stream, messageSize); if (scheduler.Verbose) { - Console.WriteLine("Sent message size {0} to {1}", messageSize, IoScheduler.EndpointToString(otherEndpoint)); + Console.WriteLine("Sent message size {0} to {1}", messageSize, EndpointDescription()); } // Send its contents. - IoEncoder.WriteBytes(stream, currentSendTask.message, 0, messageSize); + IoEncoder.WriteBytes(stream, currentSendTask.Message, 0, messageSize); if (scheduler.Verbose) { - Console.WriteLine("Sent message of size {0} to {1}", messageSize, IoScheduler.EndpointToString(otherEndpoint)); + Console.WriteLine("Sent message of size {0} to {1}", messageSize, EndpointDescription()); } // Set the currentSendTask to null so we know we don't have to @@ -409,14 +615,139 @@ public void EnqueueSendTask(SendTask sendTask) } } + public class ServerSenderThread : SenderThread + { + private ServerSenderThread(IoScheduler i_scheduler, byte[] i_destinationPublicKey, SslStream i_stream, + X509Certificate2 i_remoteCert) : + base(i_scheduler, i_destinationPublicKey, i_stream, i_remoteCert) + { + } + + public static ServerSenderThread Create(IoScheduler scheduler, SslStream stream) + { + var remoteCert = stream.RemoteCertificate as X509Certificate2; + var destinationPublicKey = IoScheduler.GetCertificatePublicKey(remoteCert); + + if (scheduler.Verbose) { + Console.WriteLine("Creating sender thread to send to remote certified as {0}", + IoScheduler.CertificateToString(remoteCert)); + } + + ServerSenderThread senderThread = new ServerSenderThread(scheduler, destinationPublicKey, stream, remoteCert); + senderThread.Start(); + return senderThread; + } + + protected override bool Connect() + { + // There's nothing to do since server sender threads start out connected. + return true; + } + + protected override string EndpointDescription() + { + return IoScheduler.CertificateToString(remoteCert); + } + } + + public class ClientSenderThread : SenderThread + { + private ClientSenderThread(IoScheduler i_scheduler, byte[] i_destinationPublicKey) : + base(i_scheduler, i_destinationPublicKey, null, null) + { + } + + public static ClientSenderThread Create(IoScheduler scheduler, byte[] destinationPublicKey) + { + if (scheduler.Verbose) { + Console.WriteLine("Creating sender thread to send to remote public key {0}", + scheduler.LookupPublicKeyAsString(destinationPublicKey)); + } + + ClientSenderThread senderThread = new ClientSenderThread(scheduler, destinationPublicKey); + senderThread.Start(); + return senderThread; + } + + protected override string EndpointDescription() + { + return scheduler.LookupPublicKeyAsString(destinationPublicKey); + } + + protected override bool Connect() + { + var destinationPublicIdentity = scheduler.LookupPublicKey(destinationPublicKey); + if (destinationPublicIdentity == null) { + if (scheduler.Verbose) { + Console.Error.WriteLine("Could not connect to destination public key {0} because we don't know its address.", + IoScheduler.PublicKeyToString(destinationPublicKey)); + } + return false; + } + + if (scheduler.Verbose) { + Console.WriteLine("Starting connection to {0}", IoScheduler.PublicIdentityToString(destinationPublicIdentity)); + } + + TcpClient client; + try + { + client = new TcpClient(destinationPublicIdentity.HostNameOrAddress, destinationPublicIdentity.Port); + } + catch (Exception e) + { + scheduler.ReportException(e, "connecting to " + IoScheduler.PublicIdentityToString(destinationPublicIdentity)); + return false; + } + + var myCertificateCollection = new X509CertificateCollection(); + myCertificateCollection.Add(scheduler.MyCert); + var myValidator = new CertificateValidator(scheduler, destinationPublicIdentity); + + try { + stream = new SslStream(client.GetStream(), leaveInnerStreamOpen: false, myValidator.ValidateSSLCertificate); + stream.AuthenticateAsClient(destinationPublicIdentity.FriendlyName, myCertificateCollection, + checkCertificateRevocation: false); + } + catch (Exception e) { + scheduler.ReportException(e, "authenticating connection to " + IoScheduler.PublicIdentityToString(destinationPublicIdentity)); + return false; + } + + remoteCert = stream.RemoteCertificate as X509Certificate2; + + if (!ByteArrayComparer.Default().Equals(IoScheduler.GetCertificatePublicKey(remoteCert), destinationPublicKey)) { + Console.Error.WriteLine("Connected to {0} expecting public key {1} but found public key {2}, so disconnecting.", + IoScheduler.PublicIdentityToString(destinationPublicIdentity), + IoScheduler.PublicKeyToString(destinationPublicKey), + IoScheduler.PublicKeyToString(IoScheduler.GetCertificatePublicKey(remoteCert))); + return false; + } + + if (scheduler.Verbose) { + Console.WriteLine("Successfully connected to {0} and got certificate identifying it as {1}", + IoScheduler.PublicIdentityToString(destinationPublicIdentity), + IoScheduler.CertificateToString(remoteCert)); + } + + // Now that the connection is successful, create a thread to + // receive packets on it. + + ReceiverThread receiverThread = ReceiverThread.Create(scheduler, stream); + return true; + } + } + public class ListenerThread { private IoScheduler scheduler; private TcpListener listener; + private IPEndPoint myEndpoint; - public ListenerThread(IoScheduler i_scheduler) + public ListenerThread(IoScheduler i_scheduler, IPEndPoint i_myEndpoint) { scheduler = i_scheduler; + myEndpoint = i_myEndpoint; } public void Run() @@ -436,25 +767,34 @@ public void Run() private void ListenLoop() { - listener = new TcpListener(scheduler.MyEndpoint); + if (scheduler.Verbose) { + Console.WriteLine("Starting to listen on {0}", myEndpoint); + } + + listener = new TcpListener(myEndpoint); + listener.ExclusiveAddressUse = true; listener.Start(); while (true) { if (scheduler.Verbose) { - Console.WriteLine("Waiting for the next incoming connection."); + Console.WriteLine("Waiting for the next incoming connection"); } + TcpClient client = listener.AcceptTcpClient(); - if (client.Client.RemoteEndPoint.AddressFamily != AddressFamily.InterNetwork) - { - Console.Error.WriteLine("Ignoring incoming connection from non-IPv4 source"); - continue; - } - IPEndPoint clientEndpoint = (IPEndPoint)client.Client.RemoteEndPoint; + + CertificateValidator myValidator = new CertificateValidator(scheduler); + SslStream sslStream = new SslStream(client.GetStream(), leaveInnerStreamOpen: false, myValidator.ValidateSSLCertificate); + sslStream.AuthenticateAsServer(scheduler.MyCert, clientCertificateRequired: true, checkCertificateRevocation: false); + + var remoteCert = sslStream.RemoteCertificate as X509Certificate2; + if (scheduler.Verbose) { - Console.WriteLine("Received an incoming connection from {0}", IoScheduler.EndpointToString(clientEndpoint)); + Console.WriteLine("Received an incoming connection from remote certified as {0}", + IoScheduler.CertificateToString(remoteCert)); } - ReceiverThread receiverThread = ReceiverThread.Create(scheduler, client, clientEndpoint, true /* as server */); + ReceiverThread.Create(scheduler, sslStream); + ServerSenderThread.Create(scheduler, sslStream); } } } @@ -490,20 +830,20 @@ private void SendDispatchLoop() while (true) { if (scheduler.Verbose) { - Console.WriteLine("Waiting for the next send to dispatch."); + Console.WriteLine("Waiting for the next send to dispatch"); } SendTask sendTask = sendQueue.Receive(); if (scheduler.Verbose) { Console.WriteLine("Dispatching send of message of size {0} to {1}", - sendTask.message.Length, IoScheduler.EndpointToString(sendTask.ep)); + sendTask.Message.Length, IoScheduler.PublicKeyToString(sendTask.DestinationPublicKey)); } - SenderThread senderThread = scheduler.FindSenderForEndpoint(sendTask.ep); + SenderThread senderThread = scheduler.FindSenderForDestinationPublicKey(sendTask.DestinationPublicKey); if (senderThread == null) { - senderThread = SenderThread.Create(scheduler, null, sendTask.ep, false); + senderThread = ClientSenderThread.Create(scheduler, sendTask.DestinationPublicKey); } senderThread.EnqueueSendTask(sendTask); @@ -518,81 +858,174 @@ public void EnqueueSendTask(SendTask sendTask) public class IoScheduler { - private IPEndPoint myEndpoint; + private X509Certificate2 myCert; private bool onlyClient; private bool verbose; private int maxSendTries; - private BufferBlock receiveQueue; - private Dictionary> endpointToSenderMap; + private BufferBlock receiveQueue; + private Dictionary> destinationPublicKeyToSenderThreadMap; + private Dictionary publicKeyToPublicIdentityMap; private ListenerThread listenerThread; private SendDispatchThread sendDispatchThread; - public IoScheduler(IPEndPoint i_myEndpoint, bool i_onlyClient = false, bool i_verbose = false, int i_maxSendTries = 3) + private IoScheduler(PrivateIdentity myIdentity, string localHostNameOrAddress, int localPort, + List knownIdentities, bool i_verbose = false, int i_maxSendTries = 3) { - myEndpoint = i_myEndpoint; - onlyClient = i_onlyClient; verbose = i_verbose; maxSendTries = i_maxSendTries; + receiveQueue = new BufferBlock(); + destinationPublicKeyToSenderThreadMap = new Dictionary>(ByteArrayComparer.Default()); + publicKeyToPublicIdentityMap = new Dictionary(ByteArrayComparer.Default()); + + foreach (var knownIdentity in knownIdentities) { + publicKeyToPublicIdentityMap[knownIdentity.PublicKey] = knownIdentity; + } + + if (myIdentity == null) { + StartClient(); + } + else { + StartServer(myIdentity, localHostNameOrAddress, localPort); + } + } + + public static IoScheduler CreateServer(PrivateIdentity myIdentity, string localHostNameOrAddress, int localPort, + List knownIdentities, bool verbose = false, + int maxSendTries = 3) + { + return new IoScheduler(myIdentity, localHostNameOrAddress, localPort, knownIdentities, verbose, maxSendTries); + } + + public static IoScheduler CreateClient(List serverIdentities, bool verbose = false, + bool connectToAllServers = true, int maxSendTries = 3) + { + var scheduler = new IoScheduler(null, null, 0, serverIdentities, verbose, maxSendTries); + if (connectToAllServers) { + foreach (var serverIdentity in serverIdentities) { + scheduler.Connect(serverIdentity.PublicKey); + } + } + return scheduler; + } + + private void StartServer(PrivateIdentity myIdentity, string localHostNameOrAddress, int localPort) + { + onlyClient = false; + + try { + myCert = new X509Certificate2(myIdentity.Pkcs12, "" /* empty password */, X509KeyStorageFlags.Exportable); + } + catch (Exception e) { + Console.Error.WriteLine("Could not import private key. Exception:{0}", e); + throw new Exception("Can't start server because private key not decryptable"); + } + + // The `local` parameters override the parameters in + // `myIdentity`, unless they're empty or zero. + + if (localHostNameOrAddress == null || localHostNameOrAddress.Length == 0) { + localHostNameOrAddress = myIdentity.HostNameOrAddress; + } + if (localPort == 0) { + localPort = myIdentity.Port; + } + + var address = LookupHostNameOrAddress(localHostNameOrAddress); + if (address == null) { + Console.Error.WriteLine("ERROR: Could not find any addresses when resolving {0}, which I'm supposed to bind to."); + throw new Exception("Can't resolve binding address"); + } + var myEndpoint = new IPEndPoint(address, localPort); + if (verbose) { - Console.WriteLine("Starting I/O scheduler with endpoint {0}, onlyClient = {1}, maxSendTries = {2}", - EndpointToString(i_myEndpoint), onlyClient, maxSendTries); + Console.WriteLine("Starting I/O scheduler as server listening to {0} certified as {1}", + myEndpoint, IoScheduler.CertificateToString(myCert)); } - receiveQueue = new BufferBlock(); - endpointToSenderMap = new Dictionary>(); - Start(); + + sendDispatchThread = new SendDispatchThread(this); + Thread st = new Thread(sendDispatchThread.Run); + st.Start(); + + // Start a thread to listen on my binding endpoint. + + listenerThread = new ListenerThread(this, myEndpoint); + Thread lt = new Thread(listenerThread.Run); + lt.Start(); } - private void Start() + private void StartClient() { + onlyClient = true; + + myCert = IronfleetCrypto.CreateTransientClientIdentity(); + + if (verbose) { + Console.WriteLine("Starting I/O scheduler as client with certificate {0}", + IoScheduler.CertificateToString(myCert)); + } + sendDispatchThread = new SendDispatchThread(this); Thread st = new Thread(sendDispatchThread.Run); st.Start(); + } - // Start a thread to listen on a port bound to MyEndpoint. But, - // if we're only supposed to connect as a client, don't do this. + private static IPAddress LookupHostNameOrAddress(string hostNameOrAddress) + { + var addresses = Dns.GetHostAddresses(hostNameOrAddress); + if (addresses.Length < 1) { + return null; + } + + // Return the first IPv4 address in the list. - if (!onlyClient) { - listenerThread = new ListenerThread(this); - Thread lt = new Thread(listenerThread.Run); - lt.Start(); + foreach (var address in addresses) { + if (address.AddressFamily == AddressFamily.InterNetwork) { + return address; + } } + + // If there was no IPv4 address, return the first address in the + // list. + + return addresses[0]; } - public IPEndPoint MyEndpoint { get { return myEndpoint; } } public bool Verbose { get { return verbose; } } public bool OnlyClient { get { return onlyClient; } } + public X509Certificate2 MyCert { get { return myCert; } } ///////////////////////////////////// // SENDING ///////////////////////////////////// - public void RegisterSender(IPEndPoint ep, SenderThread senderThread) + public void RegisterSender(byte[] destinationPublicKey, SenderThread senderThread) { - lock (endpointToSenderMap) + lock (destinationPublicKeyToSenderThreadMap) { - if (endpointToSenderMap.ContainsKey(ep)) { - endpointToSenderMap[ep].Insert(0, senderThread); + if (destinationPublicKeyToSenderThreadMap.ContainsKey(destinationPublicKey)) { + destinationPublicKeyToSenderThreadMap[destinationPublicKey].Insert(0, senderThread); } else { - endpointToSenderMap[ep] = new List { senderThread }; + destinationPublicKeyToSenderThreadMap[destinationPublicKey] = new List { senderThread }; } } } - public void UnregisterSender(IPEndPoint ep, SenderThread senderThread) + public void UnregisterSender(byte[] destinationPublicKey, SenderThread senderThread) { - lock (endpointToSenderMap) + lock (destinationPublicKeyToSenderThreadMap) { - endpointToSenderMap[ep].Remove(senderThread); + destinationPublicKeyToSenderThreadMap[destinationPublicKey].Remove(senderThread); } } - public SenderThread FindSenderForEndpoint(IPEndPoint ep) + public SenderThread FindSenderForDestinationPublicKey(byte[] destinationPublicKey) { - lock (endpointToSenderMap) + lock (destinationPublicKeyToSenderThreadMap) { - if (endpointToSenderMap.ContainsKey(ep) && endpointToSenderMap[ep].Count > 0) { - return endpointToSenderMap[ep][0]; + if (destinationPublicKeyToSenderThreadMap.ContainsKey(destinationPublicKey) && + destinationPublicKeyToSenderThreadMap[destinationPublicKey].Count > 0) { + return destinationPublicKeyToSenderThreadMap[destinationPublicKey][0]; } } @@ -603,7 +1036,7 @@ public SenderThread FindSenderForEndpoint(IPEndPoint ep) // RECEIVING ///////////////////////////////////// - public void NoteReceivedPacket(Packet packet) + public void NoteReceivedPacket(ReceivedPacket packet) { receiveQueue.Post(packet); } @@ -612,39 +1045,86 @@ public void NoteReceivedPacket(Packet packet) // UTILITY METHODS ///////////////////////////////////// - public static string EndpointToString(IPEndPoint ep) + public static byte[] GetCertificatePublicKey(X509Certificate2 cert) + { + return cert.PublicKey.EncodedKeyValue.RawData; + } + + public static string PublicKeyToString(byte[] destinationPublicKey) + { + return System.Convert.ToBase64String(destinationPublicKey).Substring(12, 8); + } + + public static string PublicIdentityToString(PublicIdentity id) + { + return string.Format("{0} (key {1}) @ {2}:{3}", id.FriendlyName, PublicKeyToString(id.PublicKey), + id.HostNameOrAddress, id.Port); + } + + public static string CertificateToString(X509Certificate2 cert) { - return string.Format("{0}:{1}", ep.Address, ep.Port); + return string.Format("{0} (key {1})", + cert.SubjectName.Name, PublicKeyToString(IoScheduler.GetCertificatePublicKey(cert))); } - public void ReportException(Exception e, string activity, IPEndPoint otherEndpoint) + public PublicIdentity LookupPublicKey(byte[] publicKey) + { + PublicIdentity publicIdentity; + if (!publicKeyToPublicIdentityMap.TryGetValue(publicKey, out publicIdentity)) { + return null; + } + else { + return publicIdentity; + } + } + + public string LookupPublicKeyAsString(byte[] destinationPublicKey) + { + var publicIdentity = LookupPublicKey(destinationPublicKey); + if (publicIdentity == null) { + return PublicKeyToString(destinationPublicKey); + } + else { + return PublicIdentityToString(publicIdentity); + } + } + + public void ReportException(Exception e, string activity) { if (e is IOException ioe) { e = ioe.InnerException; } if (e is SocketException se) { if (se.SocketErrorCode == SocketError.ConnectionReset) { - Console.WriteLine("Stopped {0} {1} because of a connection reset. Will try again later if necessary.", - activity, otherEndpoint); + if (verbose) { + Console.WriteLine("Stopped {0} because of a connection reset. Will try again later if necessary.", activity); + } return; } if (se.SocketErrorCode == SocketError.ConnectionRefused) { - Console.WriteLine("Stopped {0} {1} because the connection was refused. Will try again later if necessary.", - activity, otherEndpoint); + if (verbose) { + Console.WriteLine("Stopped {0} because the connection was refused. Will try again later if necessary.", activity); + } + return; + } + if (se.SocketErrorCode == SocketError.Shutdown) { + if (verbose) { + Console.WriteLine("Stopped {0} because the connection was shut down. Will try again later if necessary.", activity); + } return; } } - Console.WriteLine("Stopped {0} {1} because of the following exception, but will try again later if necessary:\n{2}", - activity, otherEndpoint, e); + Console.WriteLine("Stopped {0} because of the following exception, but will try again later if necessary:\n{1}", + activity, e); } /////////////////////////////////// // API for IoNative.cs /////////////////////////////////// - public void ReceivePacket(Int32 timeLimit, out bool ok, out bool timedOut, out IPEndPoint remote, out byte[] message) + public void ReceivePacket(Int32 timeLimit, out bool ok, out bool timedOut, out byte[] remotePublicKey, out byte[] message) { - Packet packet; + ReceivedPacket packet; try { if (timeLimit == 0) { @@ -658,40 +1138,44 @@ public void ReceivePacket(Int32 timeLimit, out bool ok, out bool timedOut, out I ok = true; if (timedOut) { - remote = null; + remotePublicKey = null; message = null; } else { - remote = new IPEndPoint(packet.ep.Address, packet.ep.Port); - message = packet.message; + remotePublicKey = IoScheduler.GetCertificatePublicKey(packet.SenderCert); + message = packet.Message; if (verbose) { - Console.WriteLine("Dequeueing a packet of size {0} from {1}", packet.message.Length, EndpointToString(packet.ep)); + Console.WriteLine("Dequeueing a packet of size {0} from {1}", + message.Length, CertificateToString(packet.SenderCert)); } } } catch (TimeoutException) { - remote = null; + remotePublicKey = null; message = null; ok = true; timedOut = true; } catch (Exception e) { Console.Error.WriteLine("Unexpected error trying to read packet from packet queue. Exception:\n{0}", e); - remote = null; + remotePublicKey = null; message = null; ok = false; timedOut = false; } } - public bool SendPacket(IPEndPoint remote, byte[] message) + public bool SendPacket(byte[] remotePublicKey, byte[] message) { try { byte[] messageCopy = new byte[message.Length]; Array.Copy(message, messageCopy, message.Length); - SendTask sendTask = new SendTask(remote, messageCopy, 0); + byte[] remotePublicKeyCopy = new byte[remotePublicKey.Length]; + Array.Copy(remotePublicKey, remotePublicKeyCopy, remotePublicKey.Length); + SendTask sendTask = new SendTask(remotePublicKeyCopy, messageCopy); if (verbose) { - Console.WriteLine("Enqueueing send of a message of size {0} to {1}", message.Length, EndpointToString(remote)); + Console.WriteLine("Enqueueing send of a message of size {0} to {1}", message.Length, + PublicKeyToString(remotePublicKey)); } sendDispatchThread.EnqueueSendTask(sendTask); return true; @@ -704,8 +1188,7 @@ public bool SendPacket(IPEndPoint remote, byte[] message) public void ResendPacket(SendTask sendTask) { - sendTask.numTriesSoFar++; - if (sendTask.numTriesSoFar < maxSendTries) { + if (sendTask.IncrementNumTriesSoFar() < maxSendTries) { sendDispatchThread.EnqueueSendTask(sendTask); } } @@ -714,11 +1197,11 @@ public void ResendPacket(SendTask sendTask) // Extra API calls for client /////////////////////////////////// - public void Connect(IPEndPoint otherEndpoint) + public void Connect(byte[] destinationPublicKey) { - SenderThread senderThread = FindSenderForEndpoint(otherEndpoint); + SenderThread senderThread = FindSenderForDestinationPublicKey(destinationPublicKey); if (senderThread == null) { - senderThread = SenderThread.Create(this, null, otherEndpoint, false); + senderThread = ClientSenderThread.Create(this, destinationPublicKey); } } } diff --git a/ironfleet/src/Dafny/Distributed/Common/Native/IoNative.cs b/ironfleet/src/Dafny/Distributed/Common/Native/IoNative.cs index 9e955985..eb8e98b0 100755 --- a/ironfleet/src/Dafny/Distributed/Common/Native/IoNative.cs +++ b/ironfleet/src/Dafny/Distributed/Common/Native/IoNative.cs @@ -11,84 +11,18 @@ namespace Native____Io__s_Compile { - public partial class HostConstants + public partial class PrintParams { - public static uint NumCommandLineArgs() - { - return (uint)System.Environment.GetCommandLineArgs().Length; - } + internal static bool shouldPrintProfilingInfo = false; + internal static bool shouldPrintProgress = false; - public static ushort[] GetCommandLineArg(ulong i) - { - return Array.ConvertAll(System.Environment.GetCommandLineArgs()[i].ToCharArray(), c => (ushort)c); - } - } + public static bool ShouldPrintProfilingInfo() { return shouldPrintProfilingInfo; } + public static bool ShouldPrintProgress() { return shouldPrintProgress; } - public partial class IPEndPoint - { - internal System.Net.IPEndPoint endpoint; - internal IPEndPoint(System.Net.IPEndPoint endpoint) { this.endpoint = endpoint; } - - public byte[] GetAddress() + public static void SetParameters(bool i_shouldPrintProfilingInfo, bool i_shouldPrintProgress) { - // no exceptions thrown: - return (byte[])(endpoint.Address.GetAddressBytes().Clone()); - } - - public ushort GetPort() - { - // no exceptions thrown: - return (ushort)endpoint.Port; - } - - public static void Construct(byte[] ipAddress, ushort port, out bool ok, out IPEndPoint endpoint) - { - try - { - ipAddress = (byte[])(ipAddress.Clone()); - endpoint = new IPEndPoint(new System.Net.IPEndPoint(new System.Net.IPAddress(ipAddress), port)); - ok = true; - } - catch (Exception e) - { - System.Console.Error.WriteLine(e); - endpoint = null; - ok = false; - } - } - - // DnsResolve is a Dafny function, which must be deterministic, so remember lookup results - private static System.Collections.Generic.Dictionary dns = - new System.Collections.Generic.Dictionary(); - - public static Dafny.ISequence DnsResolve(Dafny.ISequence name) - { - var str_name = new String(Array.ConvertAll(name.Elements, c => (char)c)); - try - { - if (dns.ContainsKey(str_name)) - { - return Dafny.Sequence.FromArray(Array.ConvertAll(dns[str_name].ToCharArray(), c => (ushort)c)); - } - foreach (var addr in System.Net.Dns.GetHostEntry(str_name).AddressList) - { - if (addr.AddressFamily == AddressFamily.InterNetwork) - { - dns.Add(str_name, addr.ToString()); - return Dafny.Sequence.FromArray(Array.ConvertAll(addr.ToString().ToCharArray(), c => (ushort)c)); - } - } - } - catch (Exception e) - { - System.Console.Error.WriteLine("Error: DNS lookup failed for " + str_name); - System.Console.Error.WriteLine(e); - dns.Add(str_name, str_name); - return name; - } - System.Console.Error.WriteLine("Error: could not find IPv4 address for " + str_name); - dns.Add(str_name, str_name); - return name; + shouldPrintProfilingInfo = i_shouldPrintProfilingInfo; + shouldPrintProgress = i_shouldPrintProgress; } } @@ -100,39 +34,54 @@ internal NetClient(IoScheduler i_scheduler) { scheduler = i_scheduler; } + + public static int MaxPublicKeySize { get { return 0xFFFFF; } } + + public Dafny.ISequence MyPublicKey() + { + return Dafny.Sequence.FromArray(IoScheduler.GetCertificatePublicKey(scheduler.MyCert)); + } - public static void Construct(IPEndPoint localEP, out bool ok, out NetClient net) + public static NetClient Create(PrivateIdentity myIdentity, string localHostNameOrAddress, int localPort, + List knownIdentities, bool verbose, int maxSendRetries = 3) { try { - IoScheduler scheduler = new IoScheduler(localEP.endpoint, false /* only client */, false /* verbose */); - net = new NetClient(scheduler); - ok = true; + var scheduler = IoScheduler.CreateServer(myIdentity, localHostNameOrAddress, localPort, knownIdentities, + verbose, maxSendRetries); + var myPublicKey = IoScheduler.GetCertificatePublicKey(scheduler.MyCert); + if (myPublicKey.Length > MaxPublicKeySize) { + System.Console.Error.WriteLine("ERROR: The provided public key for my identity is too big ({0} > {1} bytes)", + myPublicKey.Length, MaxPublicKeySize); + return null; + } + return new NetClient(scheduler); } catch (Exception e) { System.Console.Error.WriteLine(e); - net = null; - ok = false; + return null; } } - public void Close(out bool ok) - { - scheduler = null; - ok = true; - } - - public void Receive(int timeLimit, out bool ok, out bool timedOut, out IPEndPoint remote, out byte[] buffer) + public void Receive(int timeLimit, out bool ok, out bool timedOut, out Dafny.ISequence remote, out byte[] buffer) { - System.Net.IPEndPoint remoteEp; - scheduler.ReceivePacket(timeLimit, out ok, out timedOut, out remoteEp, out buffer); - remote = (remoteEp == null) ? null : new IPEndPoint(remoteEp); + byte[] remoteBytes; + scheduler.ReceivePacket(timeLimit, out ok, out timedOut, out remoteBytes, out buffer); + if (ok && !timedOut && remoteBytes != null && remoteBytes.Length > MaxPublicKeySize) { + timedOut = true; + } + if (ok && !timedOut) { + remote = Dafny.Sequence.FromArray(remoteBytes); + } + else { + remote = Dafny.Sequence.Empty; + } } - public bool Send(IPEndPoint remote, byte[] buffer) + public bool Send(Dafny.ISequence remote, byte[] buffer) { - return scheduler.SendPacket(remote.endpoint, buffer); + return scheduler.SendPacket(remote.Elements, buffer); } } diff --git a/ironfleet/src/Dafny/Distributed/Impl/Common/CmdLineParser.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Common/CmdLineParser.i.dfy index 55411978..f9e0073a 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Common/CmdLineParser.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Common/CmdLineParser.i.dfy @@ -11,151 +11,18 @@ import opened Math__power_s import opened Common__SeqIsUniqueDef_i import opened Common__NetClient_i -function method ascii_to_int(short:uint16) : (bool, byte) - ensures var tuple := ascii_to_int(short); - tuple.0 ==> 0 <= tuple.1 <= 9 -{ - if 48 <= short <= 57 then - (true, (short - 48) as byte) - else - (false, 0) -} - -method power10(e:nat) returns (val:int) - ensures val == power(10, e) -{ - reveal power(); - if e == 0 { - return 1; - } else { - var tmp := power10(e-1); - return 10 * tmp; - } -} - -function method shorts_to_bytes(shorts:seq) : (bool, seq) -{ - if |shorts| == 0 then (true, []) - else - var tuple := shorts_to_bytes(shorts[1..]); - var ok, rest := tuple.0, tuple.1; - var tuple' := ascii_to_int(shorts[0]); - var ok', a_byte := tuple'.0, tuple'.1; - if ok && ok' then - (true, [a_byte] + rest) - else - (false, []) -} - -function method bytes_to_decimal(bytes:seq) : nat -{ - if |bytes| == 0 then 0 - else (bytes[|bytes|-1] as int) + 10 * bytes_to_decimal(bytes[0..|bytes|-1]) -} - -function method shorts_to_nat(shorts:seq) : (bool, int) -{ - if |shorts| == 0 then (false, 0) - else - var tuple := shorts_to_bytes(shorts); - var ok, bytes := tuple.0, tuple.1; - if !ok then (false, 0) - else - (true, bytes_to_decimal(bytes)) -} - -function method shorts_to_byte(shorts:seq) : (bool, byte) -{ - var tuple := shorts_to_nat(shorts); - var ok, val := tuple.0, tuple.1; - if 0 <= val < 0x100 then - (true, val as byte) - else - (false, 0) -} - -function method shorts_to_uint16(shorts:seq) : (bool, uint16) -{ - var tuple := shorts_to_nat(shorts); - var ok, val := tuple.0, tuple.1; - if 0 <= val < 0x10000 then - (true, val as uint16) - else - (false, 0) -} - -function method shorts_to_uint32(shorts:seq) : (bool, uint32) -{ - var tuple := shorts_to_nat(shorts); - var ok, val := tuple.0, tuple.1; - if 0 <= val < 0x1_0000_0000 then - (true, val as uint32) - else - (false, 0) -} - -function method is_ascii_period(short:uint16) : (bool) -{ - short == 46 -} - -function method parse_ip_addr_helper(ip_shorts:seq, current_octet_shorts:seq) : (bool, seq) -{ - if |ip_shorts| == 0 then - var tuple := shorts_to_byte(current_octet_shorts); - var okay, b := tuple.0, tuple.1; - if !okay then (false, []) else (true, [b]) - else - if is_ascii_period(ip_shorts[0]) then - var tuple := shorts_to_byte(current_octet_shorts); - var okay, b := tuple.0, tuple.1; - if !okay then - (false, []) - else - var tuple' := parse_ip_addr_helper(ip_shorts[1..], []); - var ok, ip_bytes := tuple'.0, tuple'.1; - if !ok then - (false, []) - else - (true, [b] + ip_bytes) - else - parse_ip_addr_helper(ip_shorts[1..], current_octet_shorts + [ip_shorts[0]]) -} - -function method parse_ip_addr(ip_shorts:seq) : (bool, seq) -{ - var tuple := parse_ip_addr_helper(ip_shorts, []); - var ok, ip_bytes := tuple.0, tuple.1; - if ok && |ip_bytes| == 4 then - (true, ip_bytes) - else - (false, []) -} - - -function method {:opaque} parse_end_point(ip_shorts:seq, port_shorts:seq) : (bool, EndPoint) - ensures var tuple := parse_end_point(ip_shorts,port_shorts); +function method parse_end_point(public_key:seq) : (bool, EndPoint) + ensures var tuple := parse_end_point(public_key); var ok, ep := tuple.0, tuple.1; - ok ==> EndPointIsValidIPV4(ep) + ok ==> EndPointIsValidPublicKey(ep) { - var tuple := parse_ip_addr(ip_shorts); - var okay, ip_bytes := tuple.0, tuple.1; - - if !okay then - //print("Failed to parse_ip_addr\n"); - (false, EndPoint([0,0,0,0], 0)) + if |public_key| < 0x10_0000 then + (true, EndPoint(public_key)) else - var tuple' := shorts_to_uint16(port_shorts); - var okay', port := tuple'.0, tuple'.1; - if !okay' then - //print("Failed to parse port\n"); - (false, EndPoint([0,0,0,0], 0)) - else - (true, EndPoint(ip_bytes, port)) - + (false, EndPoint(public_key)) } -method test_unique'(endpoints:seq) returns (unique:bool) +method test_unique(endpoints:seq) returns (unique:bool) ensures unique <==> SeqIsUnique(endpoints) { unique := true; @@ -184,16 +51,15 @@ method test_unique'(endpoints:seq) returns (unique:bool) reveal SeqIsUnique(); } -function method parse_end_points(args:seq>) : (bool, seq) - requires |args| % 2 == 0 +function method parse_end_points(args:seq>) : (bool, seq) ensures var (ok, endpoints) := parse_end_points(args); - ok ==> (forall e :: e in endpoints ==> EndPointIsValidIPV4(e)) + ok ==> (forall e :: e in endpoints ==> EndPointIsValidPublicKey(e)) { if |args| == 0 then (true, []) else - var (ok1, ep) := parse_end_point(args[0], args[1]); - var (ok2, rest) := parse_end_points(args[2..]); + var (ok1, ep) := parse_end_point(args[0]); + var (ok2, rest) := parse_end_points(args[1..]); if !(ok1 && ok2) then (false, []) @@ -201,69 +67,41 @@ function method parse_end_points(args:seq>) : (bool, seq) (true, [ep] + rest) } -method collect_cmd_line_args(ghost env:HostEnvironment) returns (args:seq>) - requires HostEnvironmentIsValid(env) - ensures |env.constants.CommandLineArgs()| == |args| - ensures forall i :: 0 <= i < |env.constants.CommandLineArgs()| ==> args[i] == env.constants.CommandLineArgs()[i] +method GetHostIndex(host:EndPoint, hosts:seq) returns (found:bool, index:uint64) + requires EndPointIsValidPublicKey(host) + requires SeqIsUnique(hosts) + requires |hosts| < 0x1_0000_0000_0000_0000 + requires forall h :: h in hosts ==> EndPointIsValidPublicKey(h) + ensures found ==> 0 <= index as int < |hosts| && hosts[index] == host + ensures !found ==> !(host in hosts) { - var num_args := HostConstants.NumCommandLineArgs(env); - var i := 0; - args := []; + var i:uint64 := 0; - while (i < num_args) - invariant 0 <= i <= num_args - invariant |env.constants.CommandLineArgs()[0..i]| == |args| - invariant forall j :: 0 <= j < i ==> args[j] == env.constants.CommandLineArgs()[j]; + while i < (|hosts| as uint64) + invariant i as int <= |hosts|; + invariant forall j :: 0 <= j < i ==> hosts[j] != host; { - var arg := HostConstants.GetCommandLineArg(i as uint64, env); - args := args + [arg[..]]; - i := i + 1; - } -} - -// "foo:bar:10 --> ("foo:bar", "10") -// "foo" -> ("foo", "") -function method split_at_last_colon(str:seq, n:nat) : (seq, seq) - requires n <= |str| -{ - if n == 0 then - (str, []) - else if str[n - 1] == ':' as uint16 then - (str[.. n - 1], str[n ..]) - else - split_at_last_colon(str, n - 1) -} + if host == hosts[i] { + found := true; + index := i; + + calc ==> { + true; + { reveal_SeqIsUnique(); } + forall j :: 0 <= j < |hosts| && j != i as int ==> hosts[j] != host; + } -function method resolve_ip_address(str:seq) : (seq) -{ - if parse_ip_addr(str).0 then - // already an IP address - str - else - // resolve - IPEndPoint.DnsResolve(str) -} + return; + } -function method resolve_cmd_line_args_rec(args:seq>) : (args':seq>) - ensures |args'| >= |args| -{ - if |args| == 0 then - args - else - var (s1, s2) := split_at_last_colon(args[0], |args[0]|); - if |s2| == 0 then - [s1] + resolve_cmd_line_args_rec(args[1..]) - else - [resolve_ip_address(s1), s2] + resolve_cmd_line_args_rec(args[1..]) -} + if i == (|hosts| as uint64) - 1 { + found := false; + return; + } -function method resolve_cmd_line_args(args:seq>) : (args':seq>) - ensures |args'| >= |args| -{ - if |args| == 0 then - args - else - [args[0]] + resolve_cmd_line_args_rec(args[1..]) + i := i + 1; + } + found := false; } } diff --git a/ironfleet/src/Dafny/Distributed/Impl/Common/NetClient.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Common/NetClient.i.dfy index 2fc741f7..9c6b9c4a 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Common/NetClient.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Common/NetClient.i.dfy @@ -10,7 +10,6 @@ function Workaround_CastHostEnvironmentToObject(env:HostEnvironment) : object {e function Workaround_CastOkStateToObject(okState:OkState) : object {okState} function Workaround_CastNowStateToObject(nowState:NowState) : object {nowState} function Workaround_CastNetStateToObject(netState:NetState) : object {netState} -function Workaround_CastIPEndPointToObject(ip:IPEndPoint) : object {ip} function Workaround_CastNetClientToObject(netc:NetClient?) : object? {netc} function HostEnvironmentDefaultFrame(env:HostEnvironment) : set @@ -48,10 +47,9 @@ predicate NetClientOk(netc:NetClient?) && netc.env.ok.ok() } -function method EndPointIsValidIPV4(endPoint:EndPoint) : bool +function method EndPointIsValidPublicKey(endPoint:EndPoint) : bool { - && |endPoint.addr| == 4 - && 0 <= endPoint.port <= 65535 + && |endPoint.public_key| < 0x10_0000 // < 1 MB } predicate NetClientIsValid(netc:NetClient?) @@ -61,12 +59,12 @@ predicate NetClientIsValid(netc:NetClient?) && netc != null && netc.IsOpen() && HostEnvironmentIsValid(netc.env) - && EndPointIsValidIPV4(netc.LocalEndPoint()) + && EndPointIsValidPublicKey(EndPoint(netc.MyPublicKey())) } -predicate EndPointsAreValidIPV4(eps:seq) +predicate EndPointsAreValidPublicKeys(eps:seq) { - forall i :: 0 <= i < |eps| ==> EndPointIsValidIPV4(eps[i]) + forall i :: 0 <= i < |eps| ==> EndPointIsValidPublicKey(eps[i]) } diff --git a/ironfleet/src/Dafny/Distributed/Impl/Common/NodeIdentity.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Common/NodeIdentity.i.dfy index 6d771312..bbe43df4 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Common/NodeIdentity.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Common/NodeIdentity.i.dfy @@ -87,7 +87,7 @@ lemma lemma_AbstractifySeqOfUint64sToSetOfInts_append(original_seq:seq, predicate EndPointIsAbstractable(endpoint:EndPoint) { - EndPointIsValidIPV4(endpoint) + true } function AbstractifyEndPointToNodeIdentity(endpoint:EndPoint) : NodeIdentity @@ -95,18 +95,13 @@ function AbstractifyEndPointToNodeIdentity(endpoint:EndPoint) : NodeIdentity endpoint } -predicate Uint64IsAbstractableToNodeIdentity(id:uint64) -{ - EndPointUint64Representation(id) -} - predicate SeqOfEndPointsIsAbstractable(endPoints:seq) { - forall e :: e in endPoints ==> EndPointIsValidIPV4(e) + forall e :: e in endPoints ==> EndPointIsAbstractable(e) } function {:opaque} AbstractifyEndPointsToNodeIdentities(endPoints:seq) : seq - requires forall e :: e in endPoints ==> EndPointIsValidIPV4(e) + requires forall e :: e in endPoints ==> EndPointIsAbstractable(e) ensures |AbstractifyEndPointsToNodeIdentities(endPoints)| == |endPoints| ensures forall i :: 0<=i<|endPoints| ==> AbstractifyEndPointToNodeIdentity(endPoints[i]) == AbstractifyEndPointsToNodeIdentities(endPoints)[i] { @@ -114,183 +109,17 @@ function {:opaque} AbstractifyEndPointsToNodeIdentities(endPoints:seq) else [AbstractifyEndPointToNodeIdentity(endPoints[0])] + AbstractifyEndPointsToNodeIdentities(endPoints[1..]) } -predicate EndPointSeqRepresentation(s:seq) -{ - |s| == 8 && s[0]==0 && s[1]==0 -} - -predicate EndPointUint64Representation(u:uint64) -{ - u <= 0xffffffffffff -} - -lemma EndPointRepresentations() - ensures forall u :: EndPointUint64Representation(u) ==> EndPointSeqRepresentation(Uint64ToSeqByte(u)) -{ -} - -function method {:opaque} ConvertEndPointToSeqByte(e:EndPoint) : seq - requires EndPointIsValidIPV4(e) - ensures EndPointSeqRepresentation(ConvertEndPointToSeqByte(e)) -{ - [0, 0] + e.addr + Uint16ToSeqByte(e.port) -} - -function method {:opaque} ConvertSeqByteToEndPoint(s:seq) : EndPoint - requires EndPointSeqRepresentation(s) - ensures EndPointIsValidIPV4(ConvertSeqByteToEndPoint(s)) // trivially true with current defn -{ - EndPoint(s[2..6], SeqByteToUint16(s[6..])) -} - -lemma{:timeLimitMultiplier 3} EndPointSeqRepresentations() - ensures forall s :: EndPointSeqRepresentation(s) ==> ConvertEndPointToSeqByte(ConvertSeqByteToEndPoint(s)) == s - ensures forall e :: EndPointIsValidIPV4(e) ==> ConvertSeqByteToEndPoint(ConvertEndPointToSeqByte(e)) == e -{ - forall s | EndPointSeqRepresentation(s) - ensures ConvertEndPointToSeqByte(ConvertSeqByteToEndPoint(s)) == s - { - reveal ConvertEndPointToSeqByte(); - reveal ConvertSeqByteToEndPoint(); - var e := ConvertSeqByteToEndPoint(s); - assert e == EndPoint(s[2..6], SeqByteToUint16(s[6..])); - var s' := [0, 0] + e.addr + Uint16ToSeqByte(e.port); - assert{:split_here} true; - assert s'[0] == 0 == s[0]; - assert s'[1] == 0 == s[1]; - assert s'[2..6] == e.addr; - assert |e.addr| == 4; - assert s'[6..] == Uint16ToSeqByte(e.port); - assert s' == s; // OBSERVE seq - } - - forall e | EndPointIsValidIPV4(e) - ensures ConvertSeqByteToEndPoint(ConvertEndPointToSeqByte(e)) == e - { - var s := [0, 0] + e.addr + Uint16ToSeqByte(e.port); - calc { - ConvertSeqByteToEndPoint(ConvertEndPointToSeqByte(e)); - { reveal ConvertEndPointToSeqByte(); } - ConvertSeqByteToEndPoint(s); - { reveal ConvertSeqByteToEndPoint(); } - EndPoint(s[2..6], SeqByteToUint16(s[6..])); - e; - } - } -} - -function method {:opaque} ConvertEndPointToUint64(e:EndPoint) : uint64 - requires EndPointIsValidIPV4(e) - ensures EndPointUint64Representation(ConvertEndPointToUint64(e)) -{ - SeqByteToUint64(ConvertEndPointToSeqByte(e)) -} - -function method {:opaque} ConvertUint64ToEndPoint(u:uint64) : EndPoint - requires EndPointUint64Representation(u) - ensures EndPointIsValidIPV4(ConvertUint64ToEndPoint(u)) -{ - EndPointRepresentations(); - ConvertSeqByteToEndPoint(Uint64ToSeqByte(u)) -} - -lemma lemma_ConvertUint64ToNodeIdentity_injective_forall() - ensures forall u1, u2 :: - && EndPointUint64Representation(u1) - && EndPointUint64Representation(u2) - && AbstractifyUint64ToNodeIdentity(u1) == AbstractifyUint64ToNodeIdentity(u2) - ==> u1==u2 -{ - forall u1, u2 | - && EndPointUint64Representation(u1) - && EndPointUint64Representation(u2) - && AbstractifyUint64ToNodeIdentity(u1) == AbstractifyUint64ToNodeIdentity(u2) - ensures u1==u2; - { - lemma_ConvertUint64ToNodeIdentity_injective(u1, u2); - } -} - -lemma Uint64EndPointRelationships() - ensures forall u :: EndPointUint64Representation(u) ==> EndPointIsValidIPV4(ConvertUint64ToEndPoint(u)) && ConvertEndPointToUint64(ConvertUint64ToEndPoint(u)) == u - ensures forall e :: EndPointIsValidIPV4(e) ==> ConvertUint64ToEndPoint(ConvertEndPointToUint64(e)) == e -{ - var pv := power2(8); - lemma_2toX(); - reveal ConvertUint64ToEndPoint(); - EndPointSeqRepresentations(); - - forall u | EndPointUint64Representation(u) - ensures ConvertEndPointToUint64(ConvertUint64ToEndPoint(u)) == u - { - reveal ConvertEndPointToUint64(); - lemma_BEByteSeqToInt_BEUintToSeqByte_invertability(); - } - - forall e | EndPointIsValidIPV4(e) - ensures ConvertUint64ToEndPoint(ConvertEndPointToUint64(e)) == e - { - reveal ConvertEndPointToUint64(); - var s := ConvertEndPointToSeqByte(e); - lemma_BEByteSeqToInt_BEUintToSeqByte_invertability(); - } -} - -lemma lemma_Uint64EndPointRelationships() - ensures forall u {:trigger ConvertEndPointToUint64(ConvertUint64ToEndPoint(u))} :: EndPointUint64Representation(u) ==> EndPointIsValidIPV4(ConvertUint64ToEndPoint(u)) && ConvertEndPointToUint64(ConvertUint64ToEndPoint(u)) == u - ensures forall e {:trigger ConvertUint64ToEndPoint(ConvertEndPointToUint64(e))} :: EndPointIsValidIPV4(e) ==> ConvertUint64ToEndPoint(ConvertEndPointToUint64(e)) == e -{ - reveal ConvertUint64ToEndPoint(); - Uint64EndPointRelationships(); -} - -////////////////////////////////////////////////////////////////////////////// - -function AbstractifyUint64ToNodeIdentity(u:uint64) : NodeIdentity - requires EndPointUint64Representation(u) -{ - reveal ConvertUint64ToEndPoint(); - AbstractifyEndPointToNodeIdentity(ConvertUint64ToEndPoint(u)) -} - -//lemma lemma_ConvertUint64ToEndPoint_injective(u1:uint64, u2:uint64) -// requires EndPointUint64Representation(u1) && EndPointUint64Representation(u2) -// requires ConvertUint64ToEndPoint(u1) == ConvertUint64ToEndPoint(u2) -// ensures u1==u2 -//{ -// Uint64EndPointRelationships(); -//} - -lemma lemma_ConvertUint64ToNodeIdentity_injective(u1:uint64, u2:uint64) - requires EndPointUint64Representation(u1) && EndPointUint64Representation(u2) - requires AbstractifyUint64ToNodeIdentity(u1) == AbstractifyUint64ToNodeIdentity(u2) - ensures u1==u2 -{ - reveal ConvertSeqByteToEndPoint(); - reveal ConvertUint64ToEndPoint(); - Uint64EndPointRelationships(); -} - lemma lemma_AbstractifyEndPointToNodeIdentity_injective(e1:EndPoint, e2:EndPoint) - requires EndPointIsValidIPV4(e1) && EndPointIsValidIPV4(e2) requires AbstractifyEndPointToNodeIdentity(e1) == AbstractifyEndPointToNodeIdentity(e2) ensures e1==e2 { - reveal ConvertSeqByteToEndPoint(); - var u1 := ConvertEndPointToUint64(e1); - var u2 := ConvertEndPointToUint64(e2); - lemma_ConvertUint64ToNodeIdentity_injective(u1, u2); - Uint64EndPointRelationships(); } lemma lemma_AbstractifyEndPointToNodeIdentity_injective_forall() ensures forall e1, e2 {:trigger AbstractifyEndPointToNodeIdentity(e1),AbstractifyEndPointToNodeIdentity(e2)} :: - (EndPointIsValidIPV4(e1) && EndPointIsValidIPV4(e2) - && AbstractifyEndPointToNodeIdentity(e1) == AbstractifyEndPointToNodeIdentity(e2)) - ==> e1 == e2; + AbstractifyEndPointToNodeIdentity(e1) == AbstractifyEndPointToNodeIdentity(e2) ==> e1 == e2; { - forall e1, e2 | (EndPointIsValidIPV4(e1) && EndPointIsValidIPV4(e2) - && AbstractifyEndPointToNodeIdentity(e1) == AbstractifyEndPointToNodeIdentity(e2)) + forall e1, e2 | AbstractifyEndPointToNodeIdentity(e1) == AbstractifyEndPointToNodeIdentity(e2) ensures e1 == e2 { lemma_AbstractifyEndPointToNodeIdentity_injective(e1, e2); @@ -307,7 +136,7 @@ lemma lemma_seqs_set_cardinality_EndPoint(Q:seq, S:set) } lemma lemma_sets_cardinality_EndPoint(S:set, T:set) - requires forall e :: e in S ==> EndPointIsValidIPV4(e) + requires forall e :: e in S ==> EndPointIsAbstractable(e) requires T == set e | e in S :: AbstractifyEndPointToNodeIdentity(e) ensures |S| == |T| decreases |S| @@ -329,9 +158,9 @@ lemma lemma_AbstractifyEndPointsToNodeIdentities_properties(endpoints:seq AbstractifyEndPointToNodeIdentity(e) in AbstractifyEndPointsToNodeIdentities(endpoints) - ensures forall e :: EndPointIsValidIPV4(e) ==> (e in endpoints <==> AbstractifyEndPointToNodeIdentity(e) in AbstractifyEndPointsToNodeIdentities(endpoints)) + ensures forall e :: EndPointIsAbstractable(e) ==> (e in endpoints <==> AbstractifyEndPointToNodeIdentity(e) in AbstractifyEndPointsToNodeIdentities(endpoints)) { - forall e | EndPointIsValidIPV4(e) + forall e | EndPointIsAbstractable(e) ensures e in endpoints <==> AbstractifyEndPointToNodeIdentity(e) in AbstractifyEndPointsToNodeIdentities(endpoints) { if e in endpoints { @@ -346,8 +175,8 @@ lemma lemma_AbstractifyEndPointsToNodeIdentities_properties(endpoints:seq, s2:seq) - requires forall e :: e in s1 ==> EndPointIsValidIPV4(e) - requires forall e :: e in s2 ==> EndPointIsValidIPV4(e) + requires forall e :: e in s1 ==> EndPointIsAbstractable(e) + requires forall e :: e in s2 ==> EndPointIsAbstractable(e) requires AbstractifyEndPointsToNodeIdentities(s1) == AbstractifyEndPointsToNodeIdentities(s2) ensures forall e :: e in s1 <==> e in s2 { @@ -356,8 +185,8 @@ lemma lemma_AbstractifyEndPointsToNodeIdentities_injective_elements(s1:seq, s2:seq) - requires forall e :: e in s1 ==> EndPointIsValidIPV4(e) - requires forall e :: e in s2 ==> EndPointIsValidIPV4(e) + requires forall e :: e in s1 ==> EndPointIsAbstractable(e) + requires forall e :: e in s2 ==> EndPointIsAbstractable(e) requires AbstractifyEndPointsToNodeIdentities(s1) == AbstractifyEndPointsToNodeIdentities(s2) ensures s1 == s2; { @@ -371,19 +200,15 @@ lemma lemma_AbstractifyEndPointsToNodeIdentities_injective(s1:seq, s2: predicate NodeIdentityIsRefinable(id:NodeIdentity) { - exists ep :: EndPointIsValidIPV4(ep) && AbstractifyEndPointToNodeIdentity(ep) == id + true } // Give Dafny a symbol handle for this choose (:|) expression function{:opaque} RefineNodeIdentityToEndPoint(id:NodeIdentity) : EndPoint - // requires NodeIdentityIsRefinable(id) - ensures NodeIdentityIsRefinable(id) ==> EndPointIsValidIPV4(RefineNodeIdentityToEndPoint(id)) + ensures NodeIdentityIsRefinable(id) ==> EndPointIsAbstractable(RefineNodeIdentityToEndPoint(id)) ensures NodeIdentityIsRefinable(id) ==> AbstractifyEndPointToNodeIdentity(RefineNodeIdentityToEndPoint(id)) == id { - if(NodeIdentityIsRefinable(id)) then - (var ep :| EndPointIsValidIPV4(ep) && AbstractifyEndPointToNodeIdentity(ep) == id; ep) - else - var e:EndPoint :| (true); e + id } diff --git a/ironfleet/src/Dafny/Distributed/Impl/Common/Util.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Common/Util.i.dfy index 347234c0..9efc9b35 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Common/Util.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Common/Util.i.dfy @@ -10,16 +10,6 @@ import opened Math__power2_s import opened Math__power2_i import opened Math__div_i -function method ShouldPrintProfilingInfo() : bool -{ - false // Make this "true" to make hosts collect and print profile information -} - -function method ShouldPrintProgress() : bool -{ - false // Make this "true" to make hosts print messages about their progress -} - // Uses BigIntegers. If you can, consider using the Opt versions below method seqToArray_slow(s:seq) returns(a:array) ensures a[..] == s @@ -103,7 +93,7 @@ method seqIntoArrayChar(s:seq, a:array) method RecordTimingSeq(name:seq, start:uint64, end:uint64) requires 0 < |name| < 0x1_0000_0000_0000_0000 { - if ShouldPrintProfilingInfo() { + if PrintParams.ShouldPrintProfilingInfo() { var name_array := new char[|name|]; seqIntoArrayChar(name, name_array); diff --git a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/CmdLineParser.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/CmdLineParser.i.dfy index eba28457..e3f8075a 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/CmdLineParser.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/CmdLineParser.i.dfy @@ -11,142 +11,99 @@ import opened Common__NetClient_i import opened Common__SeqIsUniqueDef_i import opened Common__NodeIdentity_i -function method EndPointNull() : EndPoint { EndPoint([0, 0, 0, 0], 0) } - -function ConstantsStateNull () : ConstantsState +function EndPointNull () : EndPoint { - ConstantsState(EndPointNull(), [], StaticParams()) + EndPoint([]) } -function sht_config_parsing(args:seq>) : ConstantsState +function ConstantsStateNull () : ConstantsState { - if args != [] && |args[1..]| % 2 == 0 then - var (ok, endpoints) := parse_end_points(args[1..]); - ConstantsState(if |endpoints| > 0 then endpoints[0] else EndPointNull(), // Root is the first endpoint in the list - endpoints, - StaticParams()) - else - ConstantsStateNull() + ConstantsState(EndPointNull(), [], StaticParams()) } -function sht_parse_id(ip:seq, port:seq) : EndPoint +function sht_cmd_line_parsing(args:seq>) : ConstantsState { - var (ok, ep) := parse_end_point(ip, port); - ep + var (ok, endpoints) := parse_end_points(args); + if !ok then + ConstantsStateNull() + else + ConstantsState(if |endpoints| > 0 then endpoints[0] else EndPointNull(), // Root is the first endpoint in the list + endpoints, + StaticParams()) } -function sht_cmd_line_parsing(env:HostEnvironment) : (ConstantsState, EndPoint) - reads env; - reads env.constants; +function sht_parse_id(arg:seq) : EndPoint { - var args := resolve_cmd_line_args(env.constants.CommandLineArgs()); - if |args| < 2 then - (ConstantsStateNull(), EndPointNull()) - else - var penultimate_arg, final_arg := args[|args|-2], args[|args|-1]; - var config := sht_config_parsing(args[..|args|-2]); - var me := sht_parse_id(penultimate_arg, final_arg); - (config, me) + var (ok, ep) := parse_end_point(arg); + ep } method GetHostIndex(host:EndPoint, hosts:seq) returns (found:bool, index:uint64) - requires EndPointIsValidIPV4(host); - requires SeqIsUnique(hosts); - requires |hosts| < 0x1_0000_0000_0000_0000; - requires forall h :: h in hosts ==> EndPointIsValidIPV4(h); - ensures found ==> 0 <= index as int < |hosts| && hosts[index] == host; - ensures !found ==> !(host in hosts); - ensures !found ==> !(AbstractifyEndPointToNodeIdentity(host) in AbstractifyEndPointsToNodeIdentities(hosts)); + requires EndPointIsValidPublicKey(host) + requires SeqIsUnique(hosts) + requires |hosts| < 0x1_0000_0000_0000_0000 + requires forall h :: h in hosts ==> EndPointIsValidPublicKey(h) + ensures found ==> 0 <= index as int < |hosts| && hosts[index] == host + ensures !found ==> !(host in hosts) + ensures !found ==> !(AbstractifyEndPointToNodeIdentity(host) in AbstractifyEndPointsToNodeIdentities(hosts)) { - var i:uint64 := 0; - lemma_AbstractifyEndPointsToNodeIdentities_properties(hosts); - - while i < |hosts| as uint64 - invariant i as int <= |hosts|; - invariant forall j :: 0 <= j < i ==> hosts[j] != host; - { - if host == hosts[i] { - found := true; - index := i; + var i:uint64 := 0; + lemma_AbstractifyEndPointsToNodeIdentities_properties(hosts); + + while i < |hosts| as uint64 + invariant i as int <= |hosts|; + invariant forall j :: 0 <= j < i ==> hosts[j] != host; + { + if host == hosts[i] { + found := true; + index := i; - calc ==> { - true; - { reveal_SeqIsUnique(); } - forall j :: 0 <= j < |hosts| && j != i as int ==> hosts[j] != host; - } - - return; - } + calc ==> { + true; + { reveal_SeqIsUnique(); } + forall j :: 0 <= j < |hosts| && j != i as int ==> hosts[j] != host; + } - if i == |hosts| as uint64 - 1 { - found := false; - return; - } + return; + } - i := i + 1; + if i == |hosts| as uint64 - 1 { + found := false; + return; } - found := false; + + i := i + 1; + } + found := false; } -method parse_cmd_line(ghost env:HostEnvironment) returns (ok:bool, config:ConstantsState, my_index:uint64) - requires HostEnvironmentIsValid(env); - ensures ok ==> ConstantsStateIsValid(config); - ensures ok ==> |config.hostIds| > 0; - ensures ok ==> 0 <= my_index as int < |config.hostIds|; - //ensures (config, my_index) == sht_cmd_line_parsing(env); - ensures var (config', my_ep') := sht_cmd_line_parsing(env); - ok ==> config == config' && config.hostIds[my_index] == my_ep'; +method parse_cmd_line(id:EndPoint, args:seq>) returns (ok:bool, config:ConstantsState, my_index:uint64) + requires EndPointIsValidPublicKey(id) + ensures ok ==> && ConstantsStateIsValid(config) + && 0 <= my_index as int < |config.hostIds| + && config == sht_cmd_line_parsing(args) + && config.hostIds[my_index] == id { + var tuple1 := parse_end_points(args); + ok := tuple1.0; + var endpoints := tuple1.1; + if !ok || |endpoints| >= 0xffff_ffff_ffff_ffff { ok := false; - var num_args := HostConstants.NumCommandLineArgs(env); - var args := collect_cmd_line_args(env); - assert args == env.constants.CommandLineArgs(); - - args := resolve_cmd_line_args(args); - - if |args| < 4 || |args| % 2 != 1 { - print "Incorrect number of command line arguments.\n"; - print "Expected: ./Main.exe [name:port]+ [name:port]\n"; - print " or: ./Main.exe [IP port]+ [IP port]\n"; - print " where the final argument is one of the IP-port pairs provided earlier \n"; - print "Note that the first IP-port pair indicates the root identity\n"; - return; - } - - var tuple1 := parse_end_points(args[1..|args|-2]); - ok := tuple1.0; - var endpoints := tuple1.1; - if !ok || |endpoints| >= 0xffff_ffff_ffff_ffff { - ok := false; - return; - } - - var tuple2 := parse_end_point(args[|args|-2], args[|args|-1]); - ok := tuple2.0; - if !ok { - return; - } + return; + } - var unique := test_unique'(endpoints); - if !unique { - ok := false; - return; - } - - ok, my_index := GetHostIndex(tuple2.1, endpoints); - if !ok { - return; - } - var root_identity := endpoints[0]; - var hosts := endpoints; - var me := endpoints[my_index]; + var unique := test_unique(endpoints); + if !unique { + ok := false; + return; + } - config := ConstantsState(root_identity, hosts, StaticParams()); + ok, my_index := GetHostIndex(id, endpoints); + if !ok { + return; + } + var root_identity := endpoints[0]; - ghost var ghost_tuple := sht_cmd_line_parsing(env); - ghost var config', my_ep' := ghost_tuple.0, ghost_tuple.1; - assert endpoints == config'.hostIds; - assert config == config'; - assert me == my_ep'; + config := ConstantsState(root_identity, endpoints, StaticParams()); } } diff --git a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Host.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Host.i.dfy index aa6c7ffa..119919c8 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Host.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Host.i.dfy @@ -19,8 +19,8 @@ module Host_i refines Host_s { provides Native__Io_s, Environment_s, Native__NativeTypes_s provides HostState provides ConcreteConfiguration - provides HostInit, HostNext, ConcreteConfigInit, HostStateInvariants, ConcreteConfigurationInvariants - provides ResolveCommandLine, ParseCommandLineConfiguration, ParseCommandLineId, ArbitraryObject + provides HostInit, HostNext, ConcreteConfigInit, HostStateInvariants, ConcreteConfigToServers + provides ParseCommandLineConfiguration, ArbitraryObject provides HostInitImpl, HostNextImpl export All reveals * @@ -30,11 +30,6 @@ module Host_i refines Host_s { type HostState = CScheduler type ConcreteConfiguration = ConstantsState - predicate ConcreteConfigurationInvariants(config:ConcreteConfiguration) - { - ConstantsStateIsValid(config) - } - predicate HostStateInvariants(host_state:HostState, env:HostEnvironment) { host_state.scheduler_impl != null @@ -63,41 +58,37 @@ module Host_i refines Host_s { || HostNextIgnoreUnsendable(host_state.sched, host_state'.sched, ios)) } - predicate ConcreteConfigInit(config:ConcreteConfiguration, servers:set, clients:set) + predicate ConcreteConfigInit(config:ConcreteConfiguration) { ConstantsStateIsValid(config) && config.rootIdentity in config.hostIds //&& (forall i :: 0 <= i < |config.hostIds| ==> c - && MapSeqToSet(config.hostIds, x=>x) == servers - && (forall e :: e in servers ==> EndPointIsAbstractable(e)) - && (forall e :: e in clients ==> EndPointIsAbstractable(e)) } - function ResolveCommandLine(args:seq>) : seq> + function ConcreteConfigToServers(config:ConcreteConfiguration) : set { - resolve_cmd_line_args(args) + MapSeqToSet(config.hostIds, x=>x) } - function ParseCommandLineConfiguration(args:seq>) : (ConcreteConfiguration, set, set) + function ParseCommandLineConfiguration(args:seq>) : ConcreteConfiguration { - var sht_config := sht_config_parsing(args); - var endpoints_set := (set e{:trigger e in sht_config.hostIds} | e in sht_config.hostIds); - (sht_config, endpoints_set, {}) - } - - function ParseCommandLineId(ip:seq, port:seq) : EndPoint - { - sht_parse_id(ip, port) + sht_cmd_line_parsing(args) } - method {:timeLimitMultiplier 4} HostInitImpl(ghost env:HostEnvironment) returns (ok:bool, host_state:HostState, config:ConcreteConfiguration, ghost servers:set, ghost clients:set, id:EndPoint) - { - var init_config:ConstantsState, my_index; - ok, init_config, my_index := parse_cmd_line(env); + method {:timeLimitMultiplier 4} HostInitImpl( + ghost env:HostEnvironment, + netc:NetClient, + args:seq> + ) returns ( + ok:bool, + host_state:HostState + ) + { + var config:ConstantsState, my_index:uint64; + var id := EndPoint(netc.MyPublicKey()); + ok, config, my_index := parse_cmd_line(id, args); if !ok { return; } - assert env.constants == old(env.constants); - id := init_config.hostIds[my_index]; - config := init_config; + assert config.hostIds[my_index] in config.hostIds; var scheduler := new SchedulerImpl(); // calc { @@ -108,24 +99,12 @@ module Host_i refines Host_s { assert env.Valid() && env.ok.ok(); - ok := scheduler.Host_Init_Impl(config, id, env); + ok := scheduler.Host_Init_Impl(config, my_index, id, netc, env); if !ok { return; } host_state := CScheduler(scheduler.AbstractifyToLScheduler(), scheduler); - servers := set e | e in config.hostIds; - clients := {}; - assert env.constants == old(env.constants); - ghost var args := resolve_cmd_line_args(env.constants.CommandLineArgs()); - ghost var tuple := ParseCommandLineConfiguration(args[0..|args|-2]); - ghost var parsed_config, parsed_servers, parsed_clients := tuple.0, tuple.1, tuple.2; - //assert config.config == parsed_config.config; - assert config.params == parsed_config.params; - //assert config == parsed_config[me := parsed_config.hostIds[my_index]]; - assert servers == parsed_servers; - assert clients == parsed_clients; - assert ConcreteConfigInit(parsed_config, parsed_servers, parsed_clients); + assert ConcreteConfigInit(config); assert HostInit(host_state, config, id); - assert config == parsed_config && servers == parsed_servers && clients == parsed_clients && ConcreteConfigInit(parsed_config, parsed_servers, parsed_clients); } predicate EventsConsistent(recvs:seq, clocks:seq, sends:seq) diff --git a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/NetSHT.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/NetSHT.i.dfy index 43ff48bd..d3c508fc 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/NetSHT.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/NetSHT.i.dfy @@ -75,23 +75,14 @@ predicate OnlySentMarshallableData(rawlog:seq) datatype ReceiveResult = RRFail() | RRTimeout() | RRPacket(cpacket:CPacket) -method GetEndPoint(ipe:IPEndPoint) returns (ep:EndPoint) - ensures ep == ipe.EP(); - ensures EndPointIsValidIPV4(ep); -{ - var addr := ipe.GetAddress(); - var port := ipe.GetPort(); - ep := EndPoint(addr[..], port); -} - method Receive(netClient:NetClient, localAddr:EndPoint) returns (rr:ReceiveResult, ghost netEvent:NetEvent) requires NetClientIsValid(netClient); - requires netClient.LocalEndPoint() == localAddr; + requires EndPoint(netClient.MyPublicKey()) == localAddr; //requires KnownSendersMatchConfig(config); //requires SHTConcreteConfigurationIsValid(config); modifies NetClientRepr(netClient); ensures netClient.env == old(netClient.env); - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()); ensures NetClientOk(netClient) <==> !rr.RRFail?; ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient); ensures !rr.RRFail? ==> @@ -103,7 +94,7 @@ method Receive(netClient:NetClient, localAddr:EndPoint) returns (rr:ReceiveResul netEvent.LIoOpReceive? && NetPacketIsAbstractable(netEvent.r) && CPacketIsAbstractable(rr.cpacket) - && EndPointIsValidIPV4(rr.cpacket.src) + && EndPointIsValidPublicKey(rr.cpacket.src) && AbstractifyCPacketToShtPacket(rr.cpacket) == AbstractifyNetPacketToShtPacket(netEvent.r) && rr.cpacket.msg == SHTDemarshallData(netEvent.r.msg) && (rr.cpacket.dst == localAddr) @@ -124,17 +115,17 @@ method Receive(netClient:NetClient, localAddr:EndPoint) returns (rr:ReceiveResul var start_time := Time.GetDebugTimeTicks(); var cmessage := SHTDemarshallDataMethod(buffer); var end_time := Time.GetDebugTimeTicks(); - var srcEp := GetEndPoint(remote); + var srcEp := EndPoint(remote); var cpacket := CPacket(localAddr, srcEp, cmessage); rr := RRPacket(cpacket); - netEvent := LIoOpReceive(LPacket(netClient.LocalEndPoint(), remote.EP(), buffer[..])); + netEvent := LIoOpReceive(LPacket(EndPoint(netClient.MyPublicKey()), srcEp, buffer[..])); forall () ensures AbstractifyCPacketToShtPacket(rr.cpacket) == AbstractifyNetPacketToShtPacket(netEvent.r); //ensures SHTEndPointIsValid(rr.cpacket.src, config); { // Uint64EndPointRelationships(); // assert ConvertEndPointToUint64(srcEp) == rr.cpacket.src; // OBSERVE trigger - assert EndPointIsValidIPV4(netClient.LocalEndPoint()); // OBSERVE trigger + assert EndPointIsValidPublicKey(EndPoint(netClient.MyPublicKey())); // OBSERVE trigger } } } @@ -151,7 +142,7 @@ method ReadClock(netClient:NetClient) returns (clock:CBoundedClock, ghost clockE ensures clock.min as int <= clockEvent.time as int <= clock.max as int; ensures NetClientIsValid(netClient); ensures NetEventIsAbstractable(clockEvent); - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()); ensures clock.min==clock.max==clockEvent.time; // silly // ensures clockEvent.ClockEvent(clock_min, clock_max); // TODO we're going to call GetTime, which returns a single value. @@ -269,12 +260,12 @@ lemma lemma_NetEventLogAppend(broadcast:CBroadcast, netEventLog:seq, n method SendBroadcast(netClient:NetClient, broadcast:CBroadcast, ghost localAddr_:EndPoint) returns (ok:bool, ghost netEventLog:seq) requires NetClientIsValid(netClient); requires CBroadcastIsValid(broadcast); - requires netClient.LocalEndPoint() == localAddr_; + requires EndPoint(netClient.MyPublicKey()) == localAddr_; requires broadcast.CBroadcast? ==> broadcast.src == localAddr_; modifies NetClientRepr(netClient); ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient); ensures netClient.env == old(netClient.env); - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()); ensures NetClientOk(netClient) <==> ok; ensures ok ==> ( NetClientIsValid(netClient) @@ -309,7 +300,7 @@ method SendBroadcast(netClient:NetClient, broadcast:CBroadcast, ghost localAddr_ invariant |netEventLog| == i as int; invariant NetClientRepr(netClient) == old(NetClientRepr(netClient)); invariant netClient.env == old(netClient.env); - invariant netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + invariant netClient.MyPublicKey() == old(netClient.MyPublicKey()); invariant NetClientIsValid(netClient); invariant NetClientOk(netClient); invariant old(netClient.env.net.history()) + netEventLog == netClient.env.net.history(); @@ -324,13 +315,13 @@ method SendBroadcast(netClient:NetClient, broadcast:CBroadcast, ghost localAddr_ var dstEp:EndPoint := broadcast.dsts[i]; var dstAddrAry := seqToArrayOpt(dstEp.addr); var remote; - ok, remote := IPEndPoint.Construct(dstAddrAry, dstEp.port, netClient.env); + ok, remote := CryptoEndPoint.Construct(dstAddrAry, dstEp.port, netClient.env); if (!ok) { return; } ok := netClient.Send(remote, buffer); if (!ok) { return; } - ghost var netEvent := NetSendEvent(NetPacket_ctor(remote.EP(), netClient.LocalEndPoint(), buffer[..])); + ghost var netEvent := NetSendEvent(NetPacket_ctor(remote.EP(), EndPoint(netClient.MyPublicKey()), buffer[..])); netEventLog := netEventLog + [netEvent]; lemma_NetEventLogAppend(broadcast, netEventLog_old, netEvent); @@ -346,12 +337,12 @@ method SendBroadcast(netClient:NetClient, broadcast:CBroadcast, ghost localAddr_ method SendPacketSeq(netClient:NetClient, cpackets:seq, ghost localAddr_:EndPoint) returns (ok:bool, ghost netEventLog:seq) requires NetClientIsValid(netClient); requires OutboundPacketsSeqIsValid(cpackets); - requires netClient.LocalEndPoint() == localAddr_; + requires EndPoint(netClient.MyPublicKey()) == localAddr_; requires OutboundPacketsSeqHasCorrectSrc(cpackets, localAddr_); modifies NetClientRepr(netClient); ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient); ensures netClient.env == old(netClient.env); - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()); ensures NetClientOk(netClient) <==> ok; ensures ok ==> ( NetClientIsValid(netClient) && netClient.IsOpen()); ensures ok ==> old(netClient.env.net.history()) + netEventLog == netClient.env.net.history(); @@ -368,7 +359,7 @@ method SendPacketSeq(netClient:NetClient, cpackets:seq, ghost localAddr while (i < |cpackets|) invariant old(NetClientRepr(netClient)) == NetClientRepr(netClient); invariant netClient.env == old(netClient.env); - invariant netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + invariant netClient.MyPublicKey() == old(netClient.MyPublicKey()); invariant NetClientOk(netClient) <==> ok; invariant ok ==> ( NetClientIsValid(netClient) && netClient.IsOpen()); invariant ok ==> netClientEnvHistory_old + netEventLog == netClient.env.net.history(); @@ -385,12 +376,6 @@ method SendPacketSeq(netClient:NetClient, cpackets:seq, ghost localAddr assert cpacket in cpackets; assert OutboundPacketsIsValid(cpacket); - - var dstAddrAry := seqToArrayOpt(dstEp.addr); - var remote; - ok, remote := IPEndPoint.Construct(dstAddrAry, dstEp.port, netClient.env); - if (!ok) { return; } - assert CSingleMessageIsAbstractable(cpacket.msg); assert CSingleMessageMarshallable(cpacket.msg); @@ -399,10 +384,10 @@ method SendPacketSeq(netClient:NetClient, cpackets:seq, ghost localAddr ghost var data := buffer[..]; assert BufferRefinementAgreesWithMessageRefinement(cpacket.msg, data); - ok := netClient.Send(remote, buffer); + ok := netClient.Send(dstEp.public_key, buffer); if (!ok) { return; } - ghost var netEvent := LIoOpSend(LPacket(remote.EP(), netClient.LocalEndPoint(), buffer[..])); + ghost var netEvent := LIoOpSend(LPacket(dstEp, EndPoint(netClient.MyPublicKey()), buffer[..])); ghost var net := netEvent.s; calc { diff --git a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/SchedulerImpl.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/SchedulerImpl.i.dfy index f2ac446d..46f34034 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/SchedulerImpl.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/SchedulerImpl.i.dfy @@ -30,6 +30,7 @@ import opened LiveSHT__NetSHT_i import opened LiveSHT__SchedulerModel_i import opened LiveSHT__Unsendable_i import opened LiveSHT__Environment_i +import opened Common__GenericMarshalling_i import opened Common__NetClient_i import opened Common__NodeIdentity_i import opened Common__Util_i @@ -57,8 +58,8 @@ class SchedulerImpl && (0 <= nextActionIndex as int < LHost_NumActions()) && (0 <= resendCount as int < 100000000) && NetClientIsValid(netClient) - && netClient.LocalEndPoint() == localAddr - && netClient.LocalEndPoint() == host.me + && EndPoint(netClient.MyPublicKey()) == localAddr + && EndPoint(netClient.MyPublicKey()) == host.me && HostStateIsValid(host) && Repr == { this } + NetClientRepr(netClient) && CSingleDeliveryAccountIsValid(host.sd, host.constants.params) @@ -86,58 +87,38 @@ class SchedulerImpl nextActionIndex as int, resendCount as int) } - - method ConstructNetClient(constants:ConstantsState, me:EndPoint, ghost env_:HostEnvironment) returns (ok:bool, client:NetClient?) - requires env_.Valid() && env_.ok.ok(); - requires ConstantsStateIsValid(constants); - requires EndPointIsAbstractable(me); - modifies env_.ok; - ensures ok ==> NetClientIsValid(client) - && client.LocalEndPoint() == me - && client.env == env_; - { - var my_ep := me; - var ip_byte_array := new byte[|my_ep.addr|]; - assert EndPointIsValidIPV4(my_ep); - seqIntoArrayOpt(my_ep.addr, ip_byte_array); - var ip_endpoint; - ok, ip_endpoint := IPEndPoint.Construct(ip_byte_array, my_ep.port, env_); - if !ok { return; } - ok, client := NetClient.Construct(ip_endpoint, env_); - if ok { - calc { - client.LocalEndPoint(); - ip_endpoint.EP(); - my_ep; - } - } - } - - method {:timeLimitMultiplier 2} Host_Init_Impl(constants:ConstantsState, me:EndPoint, ghost env_:HostEnvironment) returns (ok:bool) - requires env_.Valid() && env_.ok.ok(); - requires ConstantsStateIsValid(constants); - requires EndPointIsAbstractable(me); - modifies this, netClient; - modifies env_.ok; - ensures ok ==> - Valid() + method {:timeLimitMultiplier 2} Host_Init_Impl( + constants:ConstantsState, + my_index:uint64, + me:EndPoint, + nc:NetClient, + ghost env_:HostEnvironment + ) returns ( + ok:bool + ) + requires env_.Valid() && env_.ok.ok() + requires ConstantsStateIsValid(constants) + requires EndPointIsValidPublicKey(me) + requires NetClientIsValid(nc) + requires EndPoint(nc.MyPublicKey()) == me + requires 0 <= my_index as int < |constants.hostIds| + requires EndPoint(nc.MyPublicKey()) == constants.hostIds[my_index] + requires nc.env == env_ + modifies this + ensures ok ==> + Valid() && Env() == env_ && LScheduler_Init(AbstractifyToLScheduler(), AbstractifyEndPointToNodeIdentity(me), AbstractifyEndPointToNodeIdentity(constants.rootIdentity), AbstractifyEndPointsToNodeIdentities(constants.hostIds), AbstractifyCParametersToParameters(constants.params)) - && host.constants == constants; + && host.constants == constants { - ok, netClient := ConstructNetClient(constants, me, env_); - - if (ok) - { - - host := InitHostState(constants, me); - nextActionIndex := 0; - resendCount := 0; - localAddr := host.me; - Repr := { this } + NetClientRepr(netClient); - - } + netClient := nc; + host := InitHostState(constants, me); + nextActionIndex := 0; + resendCount := 0; + localAddr := host.me; + Repr := { this } + NetClientRepr(netClient); + ok := true; } static method rollActionIndex(a:uint64) returns (a':uint64) @@ -231,7 +212,7 @@ class SchedulerImpl //requires SHTConcreteConfigurationIsValid(host.constants.all.config); { CPacketIsSendable(cpacket) - && EndPointIsValidIPV4(host.me) + && EndPointIsValidPublicKey(host.me) && io0.LIoOpReceive? && NetEventIsAbstractable(netEvent0) && io0 == AbstractifyNetEventToLSHTIo(netEvent0) @@ -363,6 +344,7 @@ class SchedulerImpl requires rr.RRPacket?; requires receive_event.LIoOpReceive?; requires CPacketIsAbstractable(rr.cpacket); + requires ValidPhysicalAddress(rr.cpacket.src); requires NetPacketIsAbstractable(receive_event.r); //requires CSingleMessageMarshallable(rr.cpacket.msg); requires !rr.cpacket.msg.CInvalidMessage? && CSingleMessageIs64Bit(rr.cpacket.msg); @@ -398,7 +380,7 @@ class SchedulerImpl assert Env() == old(Env()); assert Valid(); - assert netClient.LocalEndPoint() == host.me; + assert EndPoint(netClient.MyPublicKey()) == host.me; ok, log_tail, ios_tail := DeliverOutboundPackets(sent_packets); if (!ok) { return; } @@ -510,6 +492,7 @@ class SchedulerImpl netEventLog := [netEvent0]; ghost var receive_io := LIoOpReceive(AbstractifyNetPacketToLSHTPacket(netEvent0.r)); ios := [receive_io]; + assert IosReflectIgnoringUnDemarshallable(netEventLog); } else { //assert CPacketIsAbstractable(cpacket) && CSingleMessageMarshallable(cpacket.msg); ok, netEventLog, ios := HostNextReceivePacket(old(Env().net.history()), rr, netEvent0); @@ -533,7 +516,7 @@ class SchedulerImpl } assert Env() == old(Env()); assert Valid(); - assert netClient.LocalEndPoint() == host.me; + assert EndPoint(netClient.MyPublicKey()) == host.me; ok, log_tail, ios_tail := DeliverOutboundPackets(sent_packets); if (!ok) { return; } @@ -625,7 +608,6 @@ class SchedulerImpl var b; if (host.receivedPacket.Some?) { - b := ShouldProcessReceivedMessageImpl(host); if (b) { var cpacket := host.receivedPacket.v; diff --git a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Unsendable.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Unsendable.i.dfy index 6d5f7ce4..8a5c741c 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Unsendable.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/LiveSHT/Unsendable.i.dfy @@ -16,7 +16,8 @@ module LiveSHT__Unsendable_i { { |ios| == 1 && ios[0].LIoOpReceive? - && !Demarshallable(ios[0].r.msg, CSingleMessage_grammar()) +// && !Demarshallable(ios[0].r.msg, CSingleMessage_grammar()) + && SHTDemarshallData(ios[0].r.msg).CInvalidMessage? } @@ -27,7 +28,8 @@ module LiveSHT__Unsendable_i { && s.host.receivedPacket.v.msg.SingleMessage? && s.host.receivedPacket.v.msg.m.Delegate? && var msg := s.host.receivedPacket.v.msg.m; - !(ValidKeyRange(msg.range) && ValidHashtable(msg.h) && !EmptyKeyRange(msg.range))) + !(ValidKeyRange(msg.range) && ValidHashtable(msg.h) && !EmptyKeyRange(msg.range) + && ValidPhysicalAddress(s.host.receivedPacket.v.msg.dst))) } predicate HostNextIgnoreUnsendableReceive(s:LScheduler, s':LScheduler, ios:seq>>) diff --git a/ironfleet/src/Dafny/Distributed/Impl/Lock/CmdLineParser.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Lock/CmdLineParser.i.dfy index 0162c283..e4a1254b 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Lock/CmdLineParser.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Lock/CmdLineParser.i.dfy @@ -9,130 +9,44 @@ import opened CmdLineParser_i import opened Common__NetClient_i import opened Common__SeqIsUniqueDef_i -function method EndPointNull() : EndPoint { EndPoint([0, 0, 0, 0], 0) } - -function lock_config_parsing(args:seq>) : seq -{ - if args != [] && |args[1..]| % 2 == 0 then - var (ok, endpoints) := parse_end_points(args[1..]); - if ok && |endpoints| > 0 && |endpoints| < 0x1_0000_0000_0000_0000 then - endpoints - else - [] - else - [] -} - -function lock_parse_id(ip:seq, port:seq) : EndPoint -{ - var (ok, ep) := parse_end_point(ip, port); - ep -} - -function lock_cmd_line_parsing(env:HostEnvironment) : (seq, EndPoint) - reads env; - reads env.constants -{ - var args := resolve_cmd_line_args(env.constants.CommandLineArgs()); - if |args| < 2 then - ([], EndPointNull()) - else - var penultimate_arg, final_arg := args[|args|-2], args[|args|-1]; - var config := lock_config_parsing(args[..|args|-2]); - var me := lock_parse_id(penultimate_arg, final_arg); - (config, me) -} - -method GetHostIndex(host:EndPoint, hosts:seq) returns (found:bool, index:uint64) - requires EndPointIsValidIPV4(host); - requires SeqIsUnique(hosts); - requires |hosts| < 0x1_0000_0000_0000_0000; - requires forall h :: h in hosts ==> EndPointIsValidIPV4(h); - ensures found ==> 0 <= index as int < |hosts| && hosts[index] == host; - ensures !found ==> !(host in hosts); +function lock_config_parsing(args:seq>) : seq { - var i:uint64 := 0; - - while i < (|hosts| as uint64) - invariant i as int <= |hosts|; - invariant forall j :: 0 <= j < i ==> hosts[j] != host; - { - if host == hosts[i] { - found := true; - index := i; - - calc ==> { - true; - { reveal_SeqIsUnique(); } - forall j :: 0 <= j < |hosts| && j != i as int ==> hosts[j] != host; - } - - return; - } - - if i == (|hosts| as uint64) - 1 { - found := false; - return; - } - - i := i + 1; - } - found := false; + parse_end_points(args).1 } -method ParseCmdLine(ghost env:HostEnvironment) returns (ok:bool, host_ids:seq, my_index:uint64) - requires HostEnvironmentIsValid(env); - ensures ok ==> |host_ids| > 0; - ensures ok ==> 0 <= my_index as int < |host_ids|; - ensures var (host_ids', my_ep') := lock_cmd_line_parsing(env); - ok ==> host_ids == host_ids' && host_ids[my_index] == my_ep'; - ensures ok ==> SeqIsUnique(host_ids); +method ParseCmdLine(id:EndPoint, args:seq>) + returns (ok:bool, host_ids:seq, my_index:uint64) + requires EndPointIsValidPublicKey(id) + ensures ok ==> && 0 <= my_index as int < |host_ids| < 0x1_0000_0000_0000_0000 + && host_ids == lock_config_parsing(args) + && host_ids[my_index] == id + && SeqIsUnique(host_ids) + && (forall h :: h in host_ids ==> EndPointIsValidPublicKey(h)) { + var tuple1 := parse_end_points(args); + ok := tuple1.0; + if !ok { + return; + } + host_ids := tuple1.1; + if |host_ids| == 0 || |host_ids| >= 0x1_0000_0000_0000_0000 { ok := false; - var num_args := HostConstants.NumCommandLineArgs(env); - var args := collect_cmd_line_args(env); - assert args == env.constants.CommandLineArgs(); - - args := resolve_cmd_line_args(args); + return; + } - if |args| < 4 || |args| % 2 != 1 { - print "Incorrect number of command line arguments.\n"; - print "Expected: ./Main.exe [name:port]+ [name:port]\n"; - print " or: ./Main.exe [IP port]+ [IP port]\n"; - print " where the final argument is one of the two IP-port pairs provided earlier \n"; - return; - } - - var tuple1 := parse_end_points(args[1..|args|-2]); - ok := tuple1.0; - var endpoints := tuple1.1; - if !ok || |endpoints| == 0 || |endpoints| >= 0x1_0000_0000_0000_0000 { - ok := false; - return; - } - - var tuple2 := parse_end_point(args[|args|-2], args[|args|-1]); - ok := tuple2.0; - if !ok { - return; - } - - var unique := test_unique'(endpoints); - if !unique { - ok := false; - return; - } + var unique := test_unique(host_ids); + if !unique { + ok := false; + return; + } - ok, my_index := GetHostIndex(tuple2.1, endpoints); - if !ok { - return; - } - host_ids := endpoints; - var me := endpoints[my_index]; + ok, my_index := GetHostIndex(id, host_ids); + if !ok { + return; + } - ghost var ghost_tuple := lock_cmd_line_parsing(env); - ghost var config', my_ep' := ghost_tuple.0, ghost_tuple.1; - assert endpoints == config'; - assert me == my_ep'; + ghost var ghost_host_ids := lock_config_parsing(args); + assert host_ids == ghost_host_ids; } + } diff --git a/ironfleet/src/Dafny/Distributed/Impl/Lock/Host.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Lock/Host.i.dfy index 579483c7..463f311e 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Lock/Host.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Lock/Host.i.dfy @@ -16,8 +16,8 @@ module Host_i refines Host_s { provides Native__Io_s, Environment_s, Native__NativeTypes_s provides HostState provides ConcreteConfiguration - provides HostInit, HostNext, ConcreteConfigInit, HostStateInvariants, ConcreteConfigurationInvariants - provides ResolveCommandLine, ParseCommandLineConfiguration, ParseCommandLineId, ArbitraryObject + provides HostInit, HostNext, ConcreteConfigInit, HostStateInvariants + provides ConcreteConfigToServers, ParseCommandLineConfiguration, ArbitraryObject provides HostInitImpl, HostNextImpl export All reveals * @@ -26,73 +26,67 @@ module Host_i refines Host_s { type HostState = CScheduler type ConcreteConfiguration = Config - predicate ConcreteConfigurationInvariants(config:ConcreteConfiguration) - { - ValidConfig(config) - } - predicate HostStateInvariants(host_state:HostState, env:HostEnvironment) { - && host_state.node_impl.Valid() - && host_state.node_impl.Env() == env - && host_state.node == AbstractifyCNode(host_state.node_impl.node) + && host_state.node_impl.Valid() + && host_state.node_impl.Env() == env + && host_state.node == AbstractifyCNode(host_state.node_impl.node) } predicate HostInit(host_state:HostState, config:ConcreteConfiguration, id:EndPoint) { - && host_state.node_impl.Valid() - && host_state.node_impl.node.config == config - && host_state.node_impl.node.config[host_state.node_impl.node.my_index] == id - && NodeInit(host_state.node, + && host_state.node_impl.Valid() + && host_state.node_impl.node.config == config + && host_state.node_impl.node.config[host_state.node_impl.node.my_index] == id + && NodeInit(host_state.node, host_state.node_impl.node.my_index as int, config) } predicate HostNext(host_state:HostState, host_state':HostState, ios:seq>>) { - NodeNext(host_state.node, host_state'.node, AbstractifyRawLogToIos(ios)) + && NodeNext(host_state.node, host_state'.node, AbstractifyRawLogToIos(ios)) && OnlySentMarshallableData(ios) } - predicate ConcreteConfigInit(config:ConcreteConfiguration, servers:set, clients:set) - { - ValidConfig(config) - && MapSeqToSet(config, x=>x) == servers - } - - function ResolveCommandLine(args:seq>) : seq> + predicate ConcreteConfigInit(config:ConcreteConfiguration) { - resolve_cmd_line_args(args) + ValidConfig(config) } - function ParseCommandLineConfiguration(args:seq>) : (ConcreteConfiguration, set, set) + function ConcreteConfigToServers(config:ConcreteConfiguration) : set { - var lock_config := lock_config_parsing(args); - var endpoints_set := (set e{:trigger e in lock_config} | e in lock_config); - (lock_config, endpoints_set, {}) + MapSeqToSet(config, x=>x) } - function ParseCommandLineId(ip:seq, port:seq) : EndPoint + function ParseCommandLineConfiguration(args:seq>) : ConcreteConfiguration { - lock_parse_id(ip, port) + lock_config_parsing(args) } - method HostInitImpl(ghost env:HostEnvironment) returns (ok:bool, host_state:HostState, config:ConcreteConfiguration, ghost servers:set, ghost clients:set, id:EndPoint) + method HostInitImpl( + ghost env:HostEnvironment, + netc:NetClient, + args:seq> + ) returns ( + ok:bool, + host_state:HostState + ) { var my_index; var node_impl := new NodeImpl(); host_state := CScheduler(AbstractifyCNode(node_impl.node), node_impl); - ok, config, my_index := ParseCmdLine(env); + var id := EndPoint(netc.MyPublicKey()); + var config; + ok, config, my_index := ParseCmdLine(id, args); if !ok { return; } - id := config[my_index]; - - ok := node_impl.InitNode(config, my_index, env); + assert id in config; + + ok := node_impl.InitNode(config, my_index, netc, env); if !ok { return; } host_state := CScheduler(AbstractifyCNode(node_impl.node), node_impl); - servers := set e | e in config; - clients := {}; } predicate EventsConsistent(recvs:seq, clocks:seq, sends:seq) diff --git a/ironfleet/src/Dafny/Distributed/Impl/Lock/NetLock.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Lock/NetLock.i.dfy index 63cede35..cf4904d7 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Lock/NetLock.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Lock/NetLock.i.dfy @@ -54,22 +54,13 @@ predicate OnlySentMarshallableData(rawlog:seq) datatype ReceiveResult = RRFail() | RRTimeout() | RRPacket(cpacket:CLockPacket) -method GetEndPoint(ipe:IPEndPoint) returns (ep:EndPoint) - ensures ep == ipe.EP(); - ensures EndPointIsValidIPV4(ep); -{ - var addr := ipe.GetAddress(); - var port := ipe.GetPort(); - ep := EndPoint(addr[..], port); -} - method Receive(netClient:NetClient, localAddr:EndPoint) returns (rr:ReceiveResult, ghost netEvent:NetEvent) requires NetClientIsValid(netClient); - requires netClient.LocalEndPoint() == localAddr; + requires EndPoint(netClient.MyPublicKey()) == localAddr; modifies NetClientRepr(netClient); ensures netClient.env == old(netClient.env); - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()); ensures NetClientOk(netClient) <==> !rr.RRFail?; ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient); ensures !rr.RRFail? ==> @@ -78,7 +69,7 @@ method Receive(netClient:NetClient, localAddr:EndPoint) ensures rr.RRTimeout? ==> netEvent.LIoOpTimeoutReceive?; ensures rr.RRPacket? ==> netEvent.LIoOpReceive? - && EndPointIsValidIPV4(rr.cpacket.src) + && EndPointIsValidPublicKey(rr.cpacket.src) && AbstractifyCLockPacket(rr.cpacket) == AbstractifyNetPacket(netEvent.r) && rr.cpacket.msg == DemarshallData(netEvent.r.msg) { @@ -97,11 +88,11 @@ method Receive(netClient:NetClient, localAddr:EndPoint) return; } - netEvent := LIoOpReceive(LPacket(netClient.LocalEndPoint(), remote.EP(), buffer[..])); + var remoteEp:EndPoint := EndPoint(remote); + netEvent := LIoOpReceive(LPacket(EndPoint(netClient.MyPublicKey()), remoteEp, buffer[..])); var cmessage := DemarshallDataMethod(buffer); - var srcEp := GetEndPoint(remote); - var cpacket := LPacket(localAddr, srcEp, cmessage); + var cpacket := LPacket(localAddr, remoteEp, cmessage); rr := RRPacket(cpacket); } @@ -121,13 +112,13 @@ predicate SendLogReflectsPacket(netEventLog:seq, packet:Option, ghost localAddr:EndPoint) returns (ok:bool, ghost netEventLog:seq) requires NetClientIsValid(netClient); - requires netClient.LocalEndPoint() == localAddr; + requires EndPoint(netClient.MyPublicKey()) == localAddr; requires OptionCLockPacketValid(opt_packet); requires opt_packet.Some? ==> opt_packet.v.src == localAddr; modifies NetClientRepr(netClient); ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient); ensures netClient.env == old(netClient.env); - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()); + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()); ensures NetClientOk(netClient) <==> ok; ensures ok ==> ( NetClientIsValid(netClient) && netClient.IsOpen() @@ -145,19 +136,15 @@ method SendPacket(netClient:NetClient, opt_packet:Option, ghost loc // Construct the remote address var dstEp:EndPoint := cpacket.dst; - var dstAddrAry := seqToArrayOpt(dstEp.addr); - var remote; - ok, remote := IPEndPoint.Construct(dstAddrAry, dstEp.port, netClient.env); - if (!ok) { return; } // Marshall the message var buffer := MarshallLockMessage(cpacket.msg); // Send the packet off - ok := netClient.Send(remote, buffer); + ok := netClient.Send(dstEp.public_key, buffer); if (!ok) { return; } - ghost var netEvent := LIoOpSend(LPacket(remote.EP(), netClient.LocalEndPoint(), buffer[..])); + ghost var netEvent := LIoOpSend(LPacket(dstEp, EndPoint(netClient.MyPublicKey()), buffer[..])); netEventLog := [netEvent]; } } diff --git a/ironfleet/src/Dafny/Distributed/Impl/Lock/Node.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Lock/Node.i.dfy index bf107375..189f60a9 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Lock/Node.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Lock/Node.i.dfy @@ -21,7 +21,7 @@ datatype CNode = CNode(held:bool, epoch:uint64, my_index:uint64, config:Config) predicate ValidConfig(c:Config) { 0 < |c| < 0x1_0000_0000_0000_0000 - && (forall e :: e in c ==> EndPointIsValidIPV4(e)) + && (forall e :: e in c ==> EndPointIsValidPublicKey(e)) && SeqIsUnique(c) } diff --git a/ironfleet/src/Dafny/Distributed/Impl/Lock/NodeImpl.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Lock/NodeImpl.i.dfy index d2e4535d..2d1a6693 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Lock/NodeImpl.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Lock/NodeImpl.i.dfy @@ -31,8 +31,8 @@ class NodeImpl { CNodeValid(node) && NetClientIsValid(netClient) - && netClient.LocalEndPoint() == localAddr - && netClient.LocalEndPoint() == node.config[node.my_index] + && EndPoint(netClient.MyPublicKey()) == localAddr + && localAddr == node.config[node.my_index] && Repr == { this } + NetClientRepr(netClient) } @@ -41,56 +41,27 @@ class NodeImpl { if netClient!=null then netClient.env else null } - - method ConstructNetClient(me:EndPoint, ghost env_:HostEnvironment) - returns (ok:bool, client:NetClient?) - requires env_.Valid() && env_.ok.ok(); - requires EndPointIsValidIPV4(me); - modifies env_.ok; - ensures ok ==> NetClientIsValid(client) - && client.LocalEndPoint() == me - && client.env == env_; - { - client := null; - var my_ep := me; - var ip_byte_array := new byte[|my_ep.addr|]; - seqIntoArrayOpt(my_ep.addr, ip_byte_array); - - var ip_endpoint; - ok, ip_endpoint := IPEndPoint.Construct(ip_byte_array, my_ep.port, env_); - if !ok { return; } - ok, client := NetClient.Construct(ip_endpoint, env_); - if ok { - calc { - client.LocalEndPoint(); - ip_endpoint.EP(); - my_ep; - } - } - } - - method InitNode(config:Config, my_index:uint64, ghost env_:HostEnvironment) returns (ok:bool) - requires env_.Valid() && env_.ok.ok(); - requires ValidConfig(config) && ValidConfigIndex(config, my_index); - modifies this, netClient; - modifies env_.ok; + method InitNode(config:Config, my_index:uint64, nc:NetClient, ghost env_:HostEnvironment) returns (ok:bool) + requires env_.Valid() && env_.ok.ok() + requires ValidConfig(config) && ValidConfigIndex(config, my_index) + requires NetClientIsValid(nc) + requires EndPoint(nc.MyPublicKey()) == config[my_index] + requires nc.env == env_ + modifies this ensures ok ==> Valid() && Env() == env_ && NodeInit(AbstractifyCNode(node), my_index as int, config) && node.config == config - && node.my_index == my_index; + && node.my_index == my_index { - ok, netClient := ConstructNetClient(config[my_index], env_); - - if (ok) { - node := NodeInitImpl(my_index, config); - assert node.my_index == my_index; - localAddr := node.config[my_index]; - Repr := { this } + NetClientRepr(netClient); - - } + netClient := nc; + node := NodeInitImpl(my_index, config); + assert node.my_index == my_index; + localAddr := node.config[my_index]; + Repr := { this } + NetClientRepr(netClient); + ok := true; } method NodeNextGrant() returns (ok:bool, ghost netEventLog:seq, ghost ios:seq) diff --git a/ironfleet/src/Dafny/Distributed/Impl/Lock/PacketParsing.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/Lock/PacketParsing.i.dfy index 2ef50901..f5ad37ad 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/Lock/PacketParsing.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/Lock/PacketParsing.i.dfy @@ -146,8 +146,8 @@ function AbstractifyNetPacket(net:NetPacket) : LockPacket predicate CLockPacketValid(p:CLockPacket) { - EndPointIsValidIPV4(p.src) - && EndPointIsValidIPV4(p.dst) + EndPointIsValidPublicKey(p.src) + && EndPointIsValidPublicKey(p.dst) && !p.msg.CInvalid? } diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/AcceptorModel.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/AcceptorModel.i.dfy index d5ea6cf5..24cc25cc 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/AcceptorModel.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/AcceptorModel.i.dfy @@ -282,7 +282,6 @@ method {:timeLimitMultiplier 1} NextAcceptorState_ProcessHeartbeat(acceptor:Acce acceptor' := acceptor; lemma_AbstractifyEndPointsToNodeIdentities_properties(acceptor.constants.all.config.replica_ids); lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); - lemma_Uint64EndPointRelationships(); var found:bool, index:uint64 := CGetReplicaIndex(sender, acceptor.constants.all.config); if (!found) { //print("Acceptor ignoring heartbeat from", sender, "\n"); diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/CMessageRefinements.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/CMessageRefinements.i.dfy index b647cac5..40ebf47f 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/CMessageRefinements.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/CMessageRefinements.i.dfy @@ -56,8 +56,8 @@ function AbstractifyCMessageToRslMessage(msg:CMessage) : RslMessage function AbstractifyCMessageToRslPacket(sentTo:EndPoint, sentFrom:EndPoint, msg:CMessage) : RslPacket requires CMessageIsAbstractable(msg) - requires EndPointIsValidIPV4(sentTo) - requires EndPointIsValidIPV4(sentFrom) + requires EndPointIsValidPublicKey(sentTo) + requires EndPointIsValidPublicKey(sentFrom) { LPacket(AbstractifyEndPointToNodeIdentity(sentTo), AbstractifyEndPointToNodeIdentity(sentFrom), AbstractifyCMessageToRslMessage(msg)) } @@ -65,8 +65,8 @@ function AbstractifyCMessageToRslPacket(sentTo:EndPoint, sentFrom:EndPoint, msg: predicate CPacketIsAbstractable(cp:CPacket) { && CMessageIsAbstractable(cp.msg) - && EndPointIsValidIPV4(cp.src) - && EndPointIsValidIPV4(cp.dst) + && EndPointIsValidPublicKey(cp.src) + && EndPointIsValidPublicKey(cp.dst) } predicate CPacketsIsAbstractable(cps:set) @@ -244,12 +244,11 @@ lemma lemma_AbstractifySetOfCPacketsToSetOfRslPackets_properties(cps:set, src:EndPoint) requires CPacketsIsAbstractable(cps) - requires EndPointIsValidIPV4(src) + requires EndPointIsValidPublicKey(src) requires forall p :: p in AbstractifySetOfCPacketsToSetOfRslPackets(cps) ==> p.src == AbstractifyEndPointToNodeIdentity(src) ensures forall cp :: cp in cps ==> cp.src == src { reveal AbstractifySetOfCPacketsToSetOfRslPackets(); - lemma_Uint64EndPointRelationships(); lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); forall cp | cp in cps ensures cp.src == src @@ -261,7 +260,7 @@ lemma lemma_AbstractifyCPacketToRslPacket_src(cps:set, src:EndPoint) lemma lemma_AbstractifySetOfCPacketsToSetOfRslPackets_srcMembershipNeg(cps:set, src:EndPoint) requires CPacketsIsAbstractable(cps) - requires EndPointIsValidIPV4(src) + requires EndPointIsValidPublicKey(src) requires !(forall p :: p in cps ==> p.src != src) ensures !(forall p :: p in AbstractifySetOfCPacketsToSetOfRslPackets(cps) ==> p.src != AbstractifyEndPointToNodeIdentity(src)) { @@ -275,7 +274,7 @@ lemma lemma_AbstractifySetOfCPacketsToSetOfRslPackets_srcMembershipNeg(cps:set, src:EndPoint) requires CPacketsIsAbstractable(cps) - requires EndPointIsValidIPV4(src) + requires EndPointIsValidPublicKey(src) requires forall p :: p in cps ==> p.src != src ensures forall p :: p in AbstractifySetOfCPacketsToSetOfRslPackets(cps) ==> p.src != AbstractifyEndPointToNodeIdentity(src) { @@ -292,7 +291,7 @@ lemma lemma_AbstractifySetOfCPacketsToSetOfRslPackets_srcMembershipPos(cps:set, src:EndPoint) requires CPacketsIsAbstractable(cps) - requires EndPointIsValidIPV4(src) + requires EndPointIsValidPublicKey(src) ensures (forall p :: p in cps ==> p.src != src) <==> (forall p :: p in AbstractifySetOfCPacketsToSetOfRslPackets(cps) ==> p.src != AbstractifyEndPointToNodeIdentity(src)) { var b := (forall p :: p in cps ==> p.src != src); @@ -317,8 +316,8 @@ predicate CBroadcastIsAbstractable(broadcast:CBroadcast) { || broadcast.CBroadcastNop? || (&& broadcast.CBroadcast? - && EndPointIsValidIPV4(broadcast.src) - && (forall i :: 0 <= i < |broadcast.dsts| ==> EndPointIsValidIPV4(broadcast.dsts[i])) + && EndPointIsValidPublicKey(broadcast.src) + && (forall i :: 0 <= i < |broadcast.dsts| ==> EndPointIsValidPublicKey(broadcast.dsts[i])) && CMessageIsAbstractable(broadcast.msg)) } diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/CPaxosConfiguration.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/CPaxosConfiguration.i.dfy index c8b29425..e3cf1165 100644 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/CPaxosConfiguration.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/CPaxosConfiguration.i.dfy @@ -19,7 +19,7 @@ datatype CPaxosConfiguration = CPaxosConfiguration(replica_ids:seq) predicate CPaxosConfigurationIsAbstractable(config:CPaxosConfiguration) { - && (forall e :: e in config.replica_ids ==> EndPointIsValidIPV4(e)) + && (forall e :: e in config.replica_ids ==> EndPointIsValidPublicKey(e)) && SeqIsUnique(config.replica_ids) } @@ -34,7 +34,7 @@ predicate CPaxosConfigurationIsValid(config:CPaxosConfiguration) function method PaxosEndPointIsValid(endPoint:EndPoint, config:CPaxosConfiguration) : bool requires CPaxosConfigurationIsValid(config) { - EndPointIsValidIPV4(endPoint) + EndPointIsValidPublicKey(endPoint) } @@ -110,7 +110,7 @@ lemma lemma_MinQuorumSizeLessThanReplicaCount(config:CPaxosConfiguration) method CGetReplicaIndex(replica:EndPoint, config:CPaxosConfiguration) returns (found:bool, index:uint64) requires CPaxosConfigurationIsValid(config) - requires EndPointIsValidIPV4(replica) + requires EndPointIsValidPublicKey(replica) ensures found ==> ReplicaIndexValid(index, config) && config.replica_ids[index] == replica ensures found ==> GetReplicaIndex(AbstractifyEndPointToNodeIdentity(replica), AbstractifyCPaxosConfigurationToConfiguration(config)) == index as int ensures !found ==> !(replica in config.replica_ids) diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/CTypes.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/CTypes.i.dfy index 488bc7b2..6ddd1553 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/CTypes.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/CTypes.i.dfy @@ -158,12 +158,12 @@ datatype CRequest = CRequest(client:EndPoint, seqno:uint64, request:CAppRequest) predicate method ValidRequest(c:CRequest) { - c.CRequest? ==> EndPointIsValidIPV4(c.client) && CAppRequestMarshallable(c.request) + c.CRequest? ==> EndPointIsValidPublicKey(c.client) && CAppRequestMarshallable(c.request) } predicate CRequestIsAbstractable(c:CRequest) { - EndPointIsValidIPV4(c.client) && CAppRequestIsAbstractable(c.request) + EndPointIsAbstractable(c.client) && CAppRequestIsAbstractable(c.request) } function AbstractifyCRequestToRequest(c:CRequest) : Request @@ -177,7 +177,6 @@ lemma lemma_AbstractifyCRequestToRequest_isInjective() { // assert forall u1:uint64, u2:uint64 :: u1 as int == u2 as int ==> u1 == u2; lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); - lemma_Uint64EndPointRelationships(); } @@ -294,12 +293,12 @@ datatype CReply = CReply (client:EndPoint, seqno:uint64, reply:CAppReply) predicate method ValidReply(c:CReply) { - c.CReply? ==> EndPointIsValidIPV4(c.client) && CAppReplyMarshallable(c.reply) + c.CReply? ==> EndPointIsValidPublicKey(c.client) && CAppReplyMarshallable(c.reply) } predicate CReplyIsAbstractable(c:CReply) { - EndPointIsValidIPV4(c.client) && CAppReplyIsAbstractable(c.reply) + EndPointIsAbstractable(c.client) && CAppReplyIsAbstractable(c.reply) } function AbstractifyCReplyToReply(c:CReply) : Reply @@ -313,7 +312,6 @@ lemma lemma_AbstractifyCReplyToReply_isInjective() { // assert forall u1:uint64, u2:uint64 :: u1 as int == u2 as int ==> u1 == u2; lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); - lemma_Uint64EndPointRelationships(); } predicate CReplySeqIsAbstractable(creplies:seq) @@ -339,7 +337,7 @@ type CReplyCache = map predicate CReplyCacheIsAbstractable(m:CReplyCache) { - forall e {:trigger EndPointIsValidIPV4(e)} :: e in m ==> EndPointIsValidIPV4(e) && CReplyIsAbstractable(m[e]) + forall e {:trigger EndPointIsValidPublicKey(e)} :: e in m ==> EndPointIsValidPublicKey(e) && CReplyIsAbstractable(m[e]) } function max_reply_cache_size() : int { 256 } // 0x1_0000_0000 @@ -353,7 +351,7 @@ predicate ValidReplyCache(m:CReplyCache) function {:opaque} AbstractifyCReplyCacheToReplyCache(m:CReplyCache) : ReplyCache requires CReplyCacheIsAbstractable(m) { - assert forall e :: e in m ==> EndPointIsValidIPV4(e); + assert forall e :: e in m ==> EndPointIsValidPublicKey(e); lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); // var test:CReply->Reply := (AbstractifyCReplyToReply as CReply->Reply); AbstractifyMap(m, AbstractifyEndPointToNodeIdentity, AbstractifyCReplyToReply, RefineNodeIdentityToEndPoint) @@ -362,22 +360,22 @@ function {:opaque} AbstractifyCReplyCacheToReplyCache(m:CReplyCache) : ReplyCach lemma lemma_AbstractifyCReplyCacheToReplyCache_properties(m:CReplyCache) requires CReplyCacheIsAbstractable(m) ensures m == map [] ==> AbstractifyCReplyCacheToReplyCache(m) == map [] - ensures forall e {:trigger e in m}{:trigger e in AbstractifyCReplyCacheToReplyCache(m)} :: (e in m <==> EndPointIsValidIPV4(e) && e in AbstractifyCReplyCacheToReplyCache(m)) - ensures forall e {:trigger e in m, EndPointIsValidIPV4(e)}{:trigger e in AbstractifyCReplyCacheToReplyCache(m)} :: (e !in m && EndPointIsValidIPV4(e) ==> e !in AbstractifyCReplyCacheToReplyCache(m)) + ensures forall e {:trigger e in m}{:trigger e in AbstractifyCReplyCacheToReplyCache(m)} :: (e in m <==> EndPointIsValidPublicKey(e) && e in AbstractifyCReplyCacheToReplyCache(m)) + ensures forall e {:trigger e in m, EndPointIsValidPublicKey(e)}{:trigger e in AbstractifyCReplyCacheToReplyCache(m)} :: (e !in m && EndPointIsValidPublicKey(e) ==> e !in AbstractifyCReplyCacheToReplyCache(m)) ensures forall e {:trigger AbstractifyCReplyToReply(m[e])}{:trigger AbstractifyCReplyCacheToReplyCache(m)[e]} :: e in m ==> AbstractifyCReplyCacheToReplyCache(m)[e] == AbstractifyCReplyToReply(m[e]) ensures forall re :: re in AbstractifyCReplyCacheToReplyCache(m) ==> re in m - ensures forall e, r {:trigger EndPointIsValidIPV4(e), ValidReply(r)} :: EndPointIsValidIPV4(e) && ValidReply(r) ==> + ensures forall e, r {:trigger EndPointIsValidPublicKey(e), ValidReply(r)} :: EndPointIsValidPublicKey(e) && ValidReply(r) ==> var rm := AbstractifyCReplyCacheToReplyCache(m); var rm' := AbstractifyCReplyCacheToReplyCache(m[e := r]); rm' == AbstractifyCReplyCacheToReplyCache(m)[AbstractifyEndPointToNodeIdentity(e) := AbstractifyCReplyToReply(r)] ensures forall e {:trigger RemoveElt(m,e)} :: - (EndPointIsValidIPV4(e) && NodeIdentityIsRefinable(AbstractifyEndPointToNodeIdentity(e)) + (EndPointIsValidPublicKey(e) && NodeIdentityIsRefinable(AbstractifyEndPointToNodeIdentity(e)) && RefineNodeIdentityToEndPoint(AbstractifyEndPointToNodeIdentity(e)) == e && e in m) ==> var rm := AbstractifyCReplyCacheToReplyCache(m); var rm' := AbstractifyCReplyCacheToReplyCache(RemoveElt(m, e)); rm' == RemoveElt(rm, e) { - assert forall e :: e in m ==> EndPointIsValidIPV4(e); + assert forall e :: e in m ==> EndPointIsValidPublicKey(e); reveal AbstractifyCReplyCacheToReplyCache(); lemma_AbstractifyMap_properties(m, AbstractifyEndPointToNodeIdentity, AbstractifyCReplyToReply, RefineNodeIdentityToEndPoint); } @@ -389,7 +387,7 @@ lemma lemma_AbstractifyCReplyCacheToReplyCache_properties(m:CReplyCache) predicate MapOfSeqNumsIsAbstractable(m:map) { - forall e :: e in m ==> EndPointIsValidIPV4(e) + forall e :: e in m ==> EndPointIsValidPublicKey(e) } function {:opaque} AbstractifyMapOfSeqNums(m:map) : map @@ -402,16 +400,16 @@ function {:opaque} AbstractifyMapOfSeqNums(m:map) : map) requires MapOfSeqNumsIsAbstractable(m) ensures m == map [] ==> AbstractifyMapOfSeqNums(m) == map [] - ensures forall e :: (e in m <==> EndPointIsValidIPV4(e) && AbstractifyEndPointToNodeIdentity(e) in AbstractifyMapOfSeqNums(m)) - ensures forall e :: (e !in m && EndPointIsValidIPV4(e) ==> AbstractifyEndPointToNodeIdentity(e) !in AbstractifyMapOfSeqNums(m)) + ensures forall e :: (e in m <==> EndPointIsValidPublicKey(e) && AbstractifyEndPointToNodeIdentity(e) in AbstractifyMapOfSeqNums(m)) + ensures forall e :: (e !in m && EndPointIsValidPublicKey(e) ==> AbstractifyEndPointToNodeIdentity(e) !in AbstractifyMapOfSeqNums(m)) ensures forall e :: e in m ==> AbstractifyMapOfSeqNums(m)[AbstractifyEndPointToNodeIdentity(e)] == m[e] as int - ensures forall e, u {:trigger AbstractifyMapOfSeqNums(m[e := u])} :: EndPointIsValidIPV4(e) ==> + ensures forall e, u {:trigger AbstractifyMapOfSeqNums(m[e := u])} :: EndPointIsValidPublicKey(e) ==> var rm := AbstractifyMapOfSeqNums(m); var rm' := AbstractifyMapOfSeqNums(m[e := u]); rm' == AbstractifyMapOfSeqNums(m)[AbstractifyEndPointToNodeIdentity(e) := u as int] { lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); - assert forall ck1, ck2 :: EndPointIsValidIPV4(ck1) && EndPointIsValidIPV4(ck2) && AbstractifyEndPointToNodeIdentity(ck1) == AbstractifyEndPointToNodeIdentity(ck2) ==> ck1 == ck2; + assert forall ck1, ck2 :: EndPointIsValidPublicKey(ck1) && EndPointIsValidPublicKey(ck2) && AbstractifyEndPointToNodeIdentity(ck1) == AbstractifyEndPointToNodeIdentity(ck2) ==> ck1 == ck2; assert forall ck1, ck2 :: AbstractifyEndPointToNodeIdentity.requires(ck1) && AbstractifyEndPointToNodeIdentity.requires(ck2) && AbstractifyEndPointToNodeIdentity(ck1) == AbstractifyEndPointToNodeIdentity(ck2) ==> ck1 == ck2; reveal AbstractifyMapOfSeqNums(); diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/CmdLineParser.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/CmdLineParser.i.dfy index b5a481ca..8edbbe88 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/CmdLineParser.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/CmdLineParser.i.dfy @@ -9,80 +9,47 @@ import opened CmdLineParser_i import opened Common__NetClient_i import opened LiveRSL__CPaxosConfiguration_i -function paxos_config_parsing(args:seq>) : CPaxosConfiguration +function paxos_config_parsing(args:seq>) : CPaxosConfiguration { - if args != [] && |args[1..]| % 2 == 0 then - var (ok, endpoints) := parse_end_points(args[1..]); - CPaxosConfiguration(endpoints) - else - CPaxosConfiguration([]) + var (_, endpoints) := parse_end_points(args); + CPaxosConfiguration(endpoints) } -function paxos_parse_id(ip:seq, port:seq) : EndPoint +function paxos_parse_id(arg:seq) : EndPoint { - var (ok, ep) := parse_end_point(ip, port); + var (_, ep) := parse_end_point(arg); ep } -function paxos_cmd_line_parsing(env:HostEnvironment) : (CPaxosConfiguration, EndPoint) - reads env - reads env.constants +method parse_cmd_line(id:EndPoint, args:seq>) returns (ok:bool, config:CPaxosConfiguration, my_index:uint64) + requires EndPointIsValidPublicKey(id) + ensures ok ==> && CPaxosConfigurationIsValid(config) + && |config.replica_ids| > 0 + && 0 <= my_index as int < |config.replica_ids| + && config == paxos_config_parsing(args) + && config.replica_ids[my_index] == id { - var args := resolve_cmd_line_args(env.constants.CommandLineArgs()); - if |args| < 2 then - (CPaxosConfiguration([]), EndPoint([0,0,0,0], 0)) - else - var penultimate_arg, final_arg := args[|args|-2], args[|args|-1]; - var config := paxos_config_parsing(args[..|args|-2]); - var me := paxos_parse_id(penultimate_arg, final_arg); - (config, me) -} - -method parse_cmd_line(ghost env:HostEnvironment) returns (ok:bool, config:CPaxosConfiguration, my_index:uint64) - requires HostEnvironmentIsValid(env) - ensures ok ==> CPaxosConfigurationIsValid(config) - ensures ok ==> |config.replica_ids| > 0 - ensures ok ==> 0 <= my_index as int < |config.replica_ids| - ensures var (config', my_ep') := paxos_cmd_line_parsing(env); - ok ==> config == config' && config.replica_ids[my_index] == my_ep' -{ - ok := false; - var num_args := HostConstants.NumCommandLineArgs(env); - var args := collect_cmd_line_args(env); - assert args == env.constants.CommandLineArgs(); - - args := resolve_cmd_line_args(args); - - if |args| < 4 || |args| % 2 != 1 { - print "Incorrect number of command line arguments.\n"; - print "Expected: ./Main.exe [name:port]+ [name:port]\n"; - print " or: ./Main.exe [IP port]+ [IP port]\n"; - print " where the final argument is one of the IP-port pairs provided earlier \n"; - return; - } - - var tuple1 := parse_end_points(args[1..|args|-2]); + var tuple1 := parse_end_points(args); ok := tuple1.0; - var endpoints := tuple1.1; if !ok { print "Error encountered while processing command-line arguments"; return; } + var endpoints := tuple1.1; - if |endpoints| >= 0xffff_ffff_ffff_ffff { - print "Internal error: impossibly many endpoints.\n"; + if |endpoints| < 1 { + print "Must have at least one replica.\n"; ok := false; return; } - var tuple2 := parse_end_point(args[|args|-2], args[|args|-1]); - ok := tuple2.0; - if !ok { - print "Error: Could not parse command-line arguments.\n"; + if |endpoints| >= 0xffff_ffff_ffff_ffff { + print "Internal error: impossibly many endpoints.\n"; + ok := false; return; } - var unique := test_unique'(endpoints); + var unique := test_unique(endpoints); if !unique { print "Error: Each endpoint must be unique.\n"; ok := false; @@ -92,17 +59,11 @@ method parse_cmd_line(ghost env:HostEnvironment) returns (ok:bool, config:CPaxos config := CPaxosConfiguration(endpoints); lemma_MinQuorumSizeLessThanReplicaCount(config); - ok, my_index := CGetReplicaIndex(tuple2.1, config); + ok, my_index := CGetReplicaIndex(id, config); if !ok { print "Error: Could not find local endpoint (last command-line endpoint) in list of preceding endpoints\n"; return; } - - ghost var ghost_tuple := paxos_cmd_line_parsing(env); - ghost var config', my_ep' := ghost_tuple.0, ghost_tuple.1; - assert endpoints == config'.replica_ids; - assert config == config'; - assert config.replica_ids[my_index] == my_ep'; } } diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/ElectionModel.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/ElectionModel.i.dfy index a17f24d2..c74f0569 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/ElectionModel.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/ElectionModel.i.dfy @@ -573,7 +573,6 @@ method {:timeLimitMultiplier 3} ElectionProcessHeartbeat(ces:CElectionState, cp: ghost var es := AbstractifyCElectionStateToElectionState(ces); ghost var es':ElectionState; lemma_AbstractifySeqOfUint64sToSetOfInts_properties(ces.current_view_suspectors); - lemma_Uint64EndPointRelationships(); if !found { es' := es; diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/ExecutorModel.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/ExecutorModel.i.dfy index 1f66b856..5944de71 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/ExecutorModel.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/ExecutorModel.i.dfy @@ -73,7 +73,7 @@ lemma lemma_CReplyCacheUpdate(batch:CRequestBatch, reply_cache:CReplyCache, repl lemma_AbstractifyCReplyCacheToReplyCache_properties(newReplyCache); assert exists e :: e in newReplyCache && r_client == AbstractifyEndPointToNodeIdentity(e); var client :| client in newReplyCache && AbstractifyEndPointToNodeIdentity(client) == r_client; - assert EndPointIsValidIPV4(client); + assert EndPointIsValidPublicKey(client); if client in reply_cache && newReplyCache[client] == reply_cache[client] { lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); assert r_client in r_replyCache; @@ -106,7 +106,7 @@ method {:timeLimitMultiplier 2} HandleRequestBatchImpl( requires ValidReplyCache(reply_cache) requires ValidRequestBatch(batch) requires CReplyCacheIsAbstractable(reply_cache) - requires forall req :: req in batch ==> EndPointIsValidIPV4(req.client) + requires forall req :: req in batch ==> EndPointIsValidPublicKey(req.client) requires MutableMap.MapOf(reply_cache_mutable) == reply_cache modifies reply_cache_mutable modifies state @@ -261,7 +261,7 @@ method {:timeLimitMultiplier 2} HandleRequestBatchImpl( } method {:timeLimitMultiplier 6} UpdateReplyCache(ghost reply_cache:CReplyCache, reply_cache_mutable:MutableMap, ep:EndPoint, newReply:CReply, reply:CAppReply, i:uint64, batch:CRequestBatch, ghost replies:seq) returns (ghost newReplyCache:CReplyCache) - requires EndPointIsValidIPV4(ep) + requires EndPointIsValidPublicKey(ep) requires ValidReply(newReply) requires CReplyIsAbstractable(newReply) requires 0 <= i as int < |batch| @@ -298,24 +298,24 @@ method {:timeLimitMultiplier 6} UpdateReplyCache(ghost reply_cache:CReplyCache, } lemma_AbstractifyCReplyCacheToReplyCache_properties(slimReplyCache); assert ValidReplyCache(slimReplyCache); - forall e {:trigger EndPointIsValidIPV4(e)} | e in slimReplyCache - ensures EndPointIsValidIPV4(e) && CReplyIsAbstractable(slimReplyCache[e]) + forall e {:trigger EndPointIsValidPublicKey(e)} | e in slimReplyCache + ensures EndPointIsValidPublicKey(e) && CReplyIsAbstractable(slimReplyCache[e]) { } newReplyCache := slimReplyCache[ep := newReply]; reply_cache_mutable.Set(ep, newReply); - forall e {:trigger EndPointIsValidIPV4(e)} | e in newReplyCache - ensures EndPointIsValidIPV4(e) && CReplyIsAbstractable(newReplyCache[e]) + forall e {:trigger EndPointIsValidPublicKey(e)} | e in newReplyCache + ensures EndPointIsValidPublicKey(e) && CReplyIsAbstractable(newReplyCache[e]) { if (e == ep) { } } -// assert forall e {:trigger EndPointIsValidIPV4(e)} :: e in newReplyCache ==> EndPointIsValidIPV4(e) && CReplyIsAbstractable(newReplyCache[e]); +// assert forall e {:trigger EndPointIsValidPublicKey(e)} :: e in newReplyCache ==> EndPointIsValidPublicKey(e) && CReplyIsAbstractable(newReplyCache[e]); assert CReplyCacheIsAbstractable(newReplyCache); lemma_AbstractifyCReplyCacheToReplyCache_properties(newReplyCache); assert ep in newReplyCache; - assert EndPointIsValidIPV4(ep); + assert EndPointIsValidPublicKey(ep); assert CReplyCacheIsAbstractable(newReplyCache); assert ValidReplyCache(newReplyCache); ghost var r_newReplyCache := AbstractifyCReplyCacheToReplyCache(newReplyCache); @@ -355,7 +355,7 @@ method {:timeLimitMultiplier 6} UpdateReplyCache(ghost reply_cache:CReplyCache, forall client | client in newReplyCache ensures ReplyCacheUpdated(client, reply_cache, newReplyCache, batch[..i+1], replies) { - assert EndPointIsValidIPV4(client); // OBSERVE: Needed b/c someone put an oddly strict trigger on lemma_AbstractifyCReplyCacheToReplyCache_properties + assert EndPointIsValidPublicKey(client); // OBSERVE: Needed b/c someone put an oddly strict trigger on lemma_AbstractifyCReplyCacheToReplyCache_properties lemma_AbstractifyCReplyCacheToReplyCache_properties(newReplyCache); assert AbstractifyEndPointToNodeIdentity(client) in r_newReplyCache; lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); @@ -490,7 +490,7 @@ method GetPacketsFromRepliesImpl(me:EndPoint, requests:CRequestBatch, replies:se requires |requests| == |replies| < 0x1_0000_0000_0000_0000 requires forall r :: r in requests ==> ValidRequest(r) requires forall r :: r in replies ==> ValidReply(r) && CReplyIsAbstractable(r) - requires EndPointIsValidIPV4(me) + requires EndPointIsValidPublicKey(me) ensures CPacketSeqIsAbstractable(cout_seq) ensures |cout_seq| == |replies| ensures forall p :: p in cout_seq ==> p.src == me && p.msg.CMessage_Reply? && CPacketIsSendable(p) @@ -510,11 +510,9 @@ method GetPacketsFromRepliesImpl(me:EndPoint, requests:CRequestBatch, replies:se { assert ValidRequest(requests[i]) && ValidReply(replies[i]); var cmsg := CMessage_Reply(requests[i].seqno, replies[i].reply); - if ShouldPrintProgress() { + if PrintParams.ShouldPrintProgress() { print("Sending reply to client "); - print(requests[i].client.addr); - print(":"); - print(requests[i].client.port); + print(requests[i].client); print(" with sequence number "); print(requests[i].seqno); print("\n"); @@ -644,8 +642,8 @@ method {:timeLimitMultiplier 4} ExecutorExecute(cs:ExecutorState, reply_cache_mu assert cme in cs.constants.all.config.replica_ids; assert ReplicaConstantsState_IsValid(cs.constants); assert CPaxosConfigurationIsValid(cs.constants.all.config); - assert forall r :: r in cs.constants.all.config.replica_ids ==> EndPointIsValidIPV4(r); - assert EndPointIsValidIPV4(cme); + assert forall r :: r in cs.constants.all.config.replica_ids ==> EndPointIsValidPublicKey(r); + assert EndPointIsValidPublicKey(cme); var start_time_get_packets := Time.GetDebugTimeTicks(); var packets := GetPacketsFromRepliesImpl(cme, cv, creplies); @@ -753,7 +751,6 @@ method ExecutorProcessAppStateRequest(cs:ExecutorState, cinp:CPacket, reply_cach cs' := cs; reveal AbstractifySetOfCPacketsToSetOfRslPackets(); - lemma_Uint64EndPointRelationships(); lemma_AbstractifyEndPointsToNodeIdentities_properties(cs.constants.all.config.replica_ids); if cinp.src in cs.constants.all.config.replica_ids && CBallotIsNotGreaterThan(cs.max_bal_reflected, cinp.msg.bal_state_req) && cs.ops_complete.n >= cinp.msg.opn_state_req.n { @@ -793,7 +790,6 @@ method ExecutorProcessStartingPhase2(cs:ExecutorState, cinp:CPacket) returns(cs' cs' := cs; reveal AbstractifySetOfCPacketsToSetOfRslPackets(); - lemma_Uint64EndPointRelationships(); lemma_AbstractifyEndPointsToNodeIdentities_properties(cs.constants.all.config.replica_ids); if cinp.src in cs.constants.all.config.replica_ids && copn.n > cs.ops_complete.n { @@ -841,11 +837,9 @@ method ExecutorProcessRequest(cs:ExecutorState, cinp:CPacket, cachedReply:CReply assert cinp.msg.seqno <= cachedReply.seqno; var cr := cachedReply; var msg := CMessage_Reply(cr.seqno, cr.reply); - if ShouldPrintProgress() { + if PrintParams.ShouldPrintProgress() { print("Sending cached reply to client "); - print(cr.client.addr); - print(":"); - print(cr.client.port); + print(cr.client); print(" with sequence number "); print(cr.seqno); print("\n"); diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/Host.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/Host.i.dfy index 8375e67b..7a41a58b 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/Host.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/Host.i.dfy @@ -29,11 +29,16 @@ datatype CScheduler = CScheduler(ghost sched:LScheduler, replica_impl:ReplicaImp type HostState = CScheduler type ConcreteConfiguration = ConstantsState -predicate ConcreteConfigurationInvariants(config:ConcreteConfiguration) +predicate ConcreteConfigInit(config:ConcreteConfiguration) { ConstantsStateIsValid(config) } +function ConcreteConfigToServers(config:ConcreteConfiguration) : set +{ + MapSeqToSet(config.config.replica_ids, x=>x) +} + predicate HostStateInvariants(host_state:HostState, env:HostEnvironment) { && host_state.replica_impl.Valid() @@ -58,44 +63,32 @@ predicate HostNext(host_state:HostState, host_state':HostState, ios:seq, clients:set) -{ - && ConstantsStateIsValid(config) - && MapSeqToSet(config.config.replica_ids, x=>x) == servers - && (forall e :: e in servers ==> EndPointIsAbstractable(e)) - && (forall e :: e in clients ==> EndPointIsAbstractable(e)) -} - -function ResolveCommandLine(args:seq>) : seq> -{ - resolve_cmd_line_args(args) -} - -function ParseCommandLineConfiguration(args:seq>) : (ConcreteConfiguration, set, set) +function ParseCommandLineConfiguration(args:seq>) : ConcreteConfiguration { var paxos_config := paxos_config_parsing(args); var params := StaticParams(); - var endpoints_set := (set e{:trigger e in paxos_config.replica_ids} | e in paxos_config.replica_ids); - (ConstantsState(paxos_config, params), endpoints_set, {}) -} - -function ParseCommandLineId(ip:seq, port:seq) : EndPoint -{ - paxos_parse_id(ip, port) + ConstantsState(paxos_config, params) } -method {:timeLimitMultiplier 4} HostInitImpl(ghost env:HostEnvironment) returns (ok:bool, host_state:HostState, config:ConcreteConfiguration, ghost servers:set, ghost clients:set, id:EndPoint) +method {:timeLimitMultiplier 4} HostInitImpl( + ghost env:HostEnvironment, + netc:NetClient, + args:seq> + ) returns ( + ok:bool, + host_state:HostState + ) { var pconfig:CPaxosConfiguration, my_index; - ok, pconfig, my_index := parse_cmd_line(env); + var id := EndPoint(netc.MyPublicKey()); + ok, pconfig, my_index := parse_cmd_line(id, args); var lschedule:LScheduler; var repImpl:ReplicaImpl := new ReplicaImpl(); host_state := CScheduler(lschedule,repImpl); if !ok { return; } - assert env.constants == old(env.constants); - id := pconfig.replica_ids[my_index]; + assert id == pconfig.replica_ids[my_index]; var scheduler := new ReplicaImpl(); var constants := InitReplicaConstantsState(id, pconfig); //SystemConfiguration(me_ep); @@ -111,22 +104,9 @@ method {:timeLimitMultiplier 4} HostInitImpl(ghost env:HostEnvironment) returns assert ReplicaConstantsState_IsValid(constants); assert WellFormedLConfiguration(AbstractifyReplicaConstantsStateToLReplicaConstants(constants).all.config); - ok := scheduler.Replica_Init(constants, env); + ok := scheduler.Replica_Init(constants, netc, env); if !ok { return; } host_state := CScheduler(scheduler.AbstractifyToLScheduler(), scheduler); - config := constants.all; - servers := set e | e in constants.all.config.replica_ids; - clients := {}; - assert env.constants == old(env.constants); - ghost var args := resolve_cmd_line_args(env.constants.CommandLineArgs()); - ghost var tuple := ParseCommandLineConfiguration(args[0..|args|-2]); - ghost var parsed_config, parsed_servers, parsed_clients := tuple.0, tuple.1, tuple.2; - assert config.config == parsed_config.config; - assert config.params == parsed_config.params; - assert config == parsed_config; - assert servers == parsed_servers; - assert clients == parsed_clients; - assert ConcreteConfigInit(parsed_config, parsed_servers, parsed_clients); } predicate EventsConsistent(recvs:seq, clocks:seq, sends:seq) diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/LearnerModel.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/LearnerModel.i.dfy index 18c78c44..d673d4d2 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/LearnerModel.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/LearnerModel.i.dfy @@ -48,7 +48,6 @@ lemma lemma_Received2bPacketsSameSizeAsAbstraction(l_learner_tuple:CLearnerTuple assert src in l_learner_tuple.received_2b_message_senders; forall x | x in l_received_2b_message_senders' - ensures EndPointIsValidIPV4(x) ensures AbstractifyEndPointToNodeIdentity(x) in h_learner_tuple'.received_2b_message_senders { var idx :| 0 <= idx < |l_received_2b_message_senders'| && x == l_received_2b_message_senders'[idx]; @@ -58,7 +57,7 @@ lemma lemma_Received2bPacketsSameSizeAsAbstraction(l_learner_tuple:CLearnerTuple assert AbstractifyEndPointToNodeIdentity(x) in SeqToSet(AbstractifyEndPointsToNodeIdentities(l_learner_tuple.received_2b_message_senders[1..])); } - forall x | EndPointIsValidIPV4(x) && AbstractifyEndPointToNodeIdentity(x) in h_learner_tuple'.received_2b_message_senders + forall x | EndPointIsValidPublicKey(x) && AbstractifyEndPointToNodeIdentity(x) in h_learner_tuple'.received_2b_message_senders ensures x in l_received_2b_message_senders' { var idx :| 0 <= idx < |l_learner_tuple'.received_2b_message_senders| && AbstractifyEndPointToNodeIdentity(x) == AbstractifyEndPointToNodeIdentity(l_learner_tuple'.received_2b_message_senders[idx]); @@ -98,7 +97,7 @@ lemma lemma_Received2bPacketsSameSizeAsAbstraction(l_learner_tuple:CLearnerTuple lemma lemma_AbstractifyCLearnerTupleOfOneSource(l_tup:CLearnerTuple, h_tup:LearnerTuple, src:EndPoint) requires l_tup.received_2b_message_senders == [src] - requires EndPointIsValidIPV4(src) + requires EndPointIsValidPublicKey(src) requires LearnerTupleIsAbstractable(l_tup) requires h_tup.received_2b_message_senders == {AbstractifyEndPointToNodeIdentity(src)} requires h_tup.candidate_learned_value == AbstractifyCRequestBatchToRequestBatch(l_tup.candidate_learned_value) @@ -127,7 +126,7 @@ lemma lemma_AbstractifyCLearnerTupleOfOneSource(l_tup:CLearnerTuple, h_tup:Learn lemma lemma_AddingSourceToSequenceAddsToSet(source:EndPoint, sseq1:seq, sset1:set, sseq2:seq, sset2:set) - requires EndPointIsValidIPV4(source) + requires EndPointIsValidPublicKey(source) requires SeqOfEndPointsIsAbstractable(sseq1) requires SeqOfEndPointsIsAbstractable(sseq2) requires sset1 == SeqToSet(AbstractifyEndPointsToNodeIdentities(sseq1)) diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/MinCQuorumSize.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/MinCQuorumSize.i.dfy index 0164d512..810845be 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/MinCQuorumSize.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/MinCQuorumSize.i.dfy @@ -20,9 +20,9 @@ method MinCQuorumSize(config:CPaxosConfiguration) returns (quorumSize:uint64) lemma_div_basics_forall(); // Needed to prove the operation below is within uint64 bounds quorumSize := (|config.replica_ids| as uint64)/2+1; ghost var c := AbstractifyCPaxosConfigurationToConfiguration(config); - assert EndPointsAreValidIPV4(config.replica_ids); + assert EndPointsAreValidPublicKeys(config.replica_ids); forall ep1, ep2 | ep1 in config.replica_ids && ep2 in config.replica_ids - //&& EndPointIsValidIPV4(ep1) && EndPointIsValidIPV4(ep2) + //&& EndPointIsValidPublicKey(ep1) && EndPointIsValidPublicKey(ep2) && AbstractifyEndPointToNodeIdentity(ep1) == AbstractifyEndPointToNodeIdentity(ep2) ensures ep1 == ep2 { diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/NetRSL.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/NetRSL.i.dfy index 3ef456b2..cd9ffc5a 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/NetRSL.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/NetRSL.i.dfy @@ -83,24 +83,15 @@ predicate OnlySentMarshallableData(rawlog:seq) datatype ReceiveResult = RRFail() | RRTimeout() | RRPacket(cpacket:CPacket) -method GetEndPoint(ipe:IPEndPoint) returns (ep:EndPoint) - ensures ep == ipe.EP() - ensures EndPointIsValidIPV4(ep) -{ - var addr := ipe.GetAddress(); - var port := ipe.GetPort(); - ep := EndPoint(addr[..], port); -} - method{:timeLimitMultiplier 2} Receive(netClient:NetClient, localAddr:EndPoint, config:CPaxosConfiguration, msg_grammar:G) returns (rr:ReceiveResult, ghost netEvent:NetEvent) requires NetClientIsValid(netClient) - requires netClient.LocalEndPoint() == localAddr + requires EndPoint(netClient.MyPublicKey()) == localAddr //requires KnownSendersMatchConfig(config) requires CPaxosConfigurationIsValid(config) requires msg_grammar == CMessage_grammar() modifies NetClientRepr(netClient) ensures netClient.env == old(netClient.env) - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()) + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()) ensures NetClientOk(netClient) <==> !rr.RRFail? ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient) ensures !rr.RRFail? ==> @@ -112,7 +103,7 @@ method{:timeLimitMultiplier 2} Receive(netClient:NetClient, localAddr:EndPoint, && NetPacketIsAbstractable(netEvent.r) && CPacketIsAbstractable(rr.cpacket) && CMessageIs64Bit(rr.cpacket.msg) - && EndPointIsValidIPV4(rr.cpacket.src) + && EndPointIsValidPublicKey(rr.cpacket.src) && AbstractifyCPacketToRslPacket(rr.cpacket) == AbstractifyNetPacketToRslPacket(netEvent.r) && rr.cpacket.msg == PaxosDemarshallData(netEvent.r.msg) && PaxosEndPointIsValid(rr.cpacket.src, config) @@ -132,7 +123,8 @@ method{:timeLimitMultiplier 2} Receive(netClient:NetClient, localAddr:EndPoint, return; } - netEvent := LIoOpReceive(LPacket(netClient.LocalEndPoint(), remote.EP(), buffer[..])); + var srcEp:EndPoint := EndPoint(remote); + netEvent := LIoOpReceive(LPacket(EndPoint(netClient.MyPublicKey()), srcEp, buffer[..])); assert netClient.env.net.history() == old_net_history + [netEvent]; var start_time := Time.GetDebugTimeTicks(); lemma_CMessageGrammarValid(); @@ -140,7 +132,6 @@ method{:timeLimitMultiplier 2} Receive(netClient:NetClient, localAddr:EndPoint, var end_time := Time.GetDebugTimeTicks(); RecordTimingSeq("PaxosDemarshallDataMethod", start_time, end_time); - var srcEp := GetEndPoint(remote); var cpacket := CPacket(localAddr, srcEp, cmessage); rr := RRPacket(cpacket); assert netClient.env.net.history() == old_net_history + [netEvent]; @@ -169,7 +160,7 @@ method{:timeLimitMultiplier 2} Receive(netClient:NetClient, localAddr:EndPoint, RecordTimingSeq("DemarshallMessage_StartingPhase2", start_time, end_time); } - assert EndPointIsValidIPV4(netClient.LocalEndPoint()); + assert EndPointIsValidPublicKey(EndPoint(netClient.MyPublicKey())); assert PaxosEndPointIsValid(rr.cpacket.src, config); assert AbstractifyCPacketToRslPacket(rr.cpacket) == AbstractifyNetPacketToRslPacket(netEvent.r); @@ -180,7 +171,7 @@ method{:timeLimitMultiplier 2} Receive(netClient:NetClient, localAddr:EndPoint, { // lemma_Uint64EndPointRelationships(); // assert ConvertEndPointToUint64(srcEp) == rr.cpacket.src; // OBSERVE trigger - assert EndPointIsValidIPV4(netClient.LocalEndPoint()); // OBSERVE trigger + assert EndPointIsValidPublicKey(netClient.LocalEndPoint()); // OBSERVE trigger } */ } @@ -196,7 +187,7 @@ method ReadClock(netClient:NetClient) returns (clock:CClockReading, ghost clockE ensures clock.t as int == clockEvent.t ensures NetClientIsValid(netClient) ensures NetEventIsAbstractable(clockEvent) - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()) + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()) // TODO we're going to call GetTime, which returns a single value. { var t := Time.GetTime(netClient.env); @@ -309,12 +300,12 @@ lemma lemma_NetEventLogAppend(broadcast:CBroadcast, netEventLog:seq, n method SendBroadcast(netClient:NetClient, broadcast:CBroadcast, ghost localAddr_:EndPoint) returns (ok:bool, ghost netEventLog:seq) requires NetClientIsValid(netClient) requires CBroadcastIsValid(broadcast) - requires netClient.LocalEndPoint() == localAddr_ + requires EndPoint(netClient.MyPublicKey()) == localAddr_ requires broadcast.CBroadcast? ==> broadcast.src == localAddr_ modifies NetClientRepr(netClient) ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient) ensures netClient.env == old(netClient.env) - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()) + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()) ensures NetClientOk(netClient) <==> ok ensures ok ==> && NetClientIsValid(netClient) @@ -351,7 +342,7 @@ method SendBroadcast(netClient:NetClient, broadcast:CBroadcast, ghost localAddr_ invariant |netEventLog| == i as int invariant NetClientRepr(netClient) == old(NetClientRepr(netClient)) invariant netClient.env == old(netClient.env) - invariant netClient.LocalEndPoint() == old(netClient.LocalEndPoint()) + invariant netClient.MyPublicKey() == old(netClient.MyPublicKey()) invariant NetClientIsValid(netClient) invariant NetClientOk(netClient) invariant old(netClient.env.net.history()) + netEventLog == netClient.env.net.history() @@ -367,20 +358,15 @@ method SendBroadcast(netClient:NetClient, broadcast:CBroadcast, ghost localAddr_ // Construct the remote address -- TODO: Only do this once per replica! var dstEp:EndPoint := broadcast.dsts[i]; //var construct_start_time := Time.GetDebugTimeTicks(); - var dstAddrAry := seqToArrayOpt(dstEp.addr); - var remote; - ok, remote := IPEndPoint.Construct(dstAddrAry, dstEp.port, netClient.env); - if (!ok) { return; } - //var construct_end_time := Time.GetDebugTimeTicks(); - //RecordTimingSeq("SendBroadcast_IPEndPointConstruct", construct_start_time, construct_end_time); + //RecordTimingSeq("SendBroadcast", construct_start_time, construct_end_time); //var send_start_time := Time.GetDebugTimeTicks(); - ok := netClient.Send(remote, buffer); + ok := netClient.Send(dstEp.public_key, buffer); if (!ok) { return; } //var send_end_time := Time.GetDebugTimeTicks(); //RecordTimingSeq("SendBroadcast_Send", send_start_time, send_end_time); - ghost var netEvent := LIoOpSend(LPacket(remote.EP(), netClient.LocalEndPoint(), buffer[..])); + ghost var netEvent := LIoOpSend(LPacket(dstEp, EndPoint(netClient.MyPublicKey()), buffer[..])); netEventLog := netEventLog + [netEvent]; lemma_NetEventLogAppend(broadcast, netEventLog_old, netEvent); @@ -395,12 +381,12 @@ method SendPacket(netClient:NetClient, packets:OutboundPackets, ghost localAddr_ requires NetClientIsValid(netClient) requires packets.OutboundPacket? requires OutboundPacketsIsValid(packets) - requires netClient.LocalEndPoint() == localAddr_ + requires EndPoint(netClient.MyPublicKey()) == localAddr_ requires OutboundPacketsHasCorrectSrc(packets, localAddr_) modifies NetClientRepr(netClient) ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient) ensures netClient.env == old(netClient.env) - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()) + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()) ensures NetClientOk(netClient) <==> ok ensures ok ==> && NetClientIsValid(netClient) && netClient.IsOpen() @@ -422,10 +408,6 @@ method SendPacket(netClient:NetClient, packets:OutboundPackets, ghost localAddr_ // Construct the remote address var dstEp:EndPoint := cpacket.dst; - var dstAddrAry := seqToArrayOpt(dstEp.addr); - var remote; - ok, remote := IPEndPoint.Construct(dstAddrAry, dstEp.port, netClient.env); - if (!ok) { return; } assert CMessageIsAbstractable(cpacket.msg); assert Marshallable(cpacket.msg); @@ -437,10 +419,10 @@ method SendPacket(netClient:NetClient, packets:OutboundPackets, ghost localAddr_ ghost var data := buffer[..]; assert BufferRefinementAgreesWithMessageRefinement(cpacket.msg, data); - ok := netClient.Send(remote, buffer); + ok := netClient.Send(dstEp.public_key, buffer); if (!ok) { return; } - ghost var netEvent := LIoOpSend(LPacket(remote.EP(), netClient.LocalEndPoint(), buffer[..])); + ghost var netEvent := LIoOpSend(LPacket(dstEp, EndPoint(netClient.MyPublicKey()), buffer[..])); ghost var net := netEvent.s; calc { @@ -461,12 +443,12 @@ method{:timeLimitMultiplier 2} SendPacketSequence(netClient:NetClient, packets:O requires NetClientIsValid(netClient) requires OutboundPacketsIsValid(packets) requires packets.PacketSequence? - requires netClient.LocalEndPoint() == localAddr_ + requires EndPoint(netClient.MyPublicKey()) == localAddr_ requires OutboundPacketsHasCorrectSrc(packets, localAddr_) modifies NetClientRepr(netClient) ensures old(NetClientRepr(netClient)) == NetClientRepr(netClient) ensures netClient.env == old(netClient.env) - ensures netClient.LocalEndPoint() == old(netClient.LocalEndPoint()) + ensures netClient.MyPublicKey() == old(netClient.MyPublicKey()) ensures NetClientOk(netClient) <==> ok ensures ok ==> && NetClientIsValid(netClient) @@ -487,7 +469,7 @@ method{:timeLimitMultiplier 2} SendPacketSequence(netClient:NetClient, packets:O while i < |cpackets| as uint64 invariant old(NetClientRepr(netClient)) == NetClientRepr(netClient) invariant netClient.env == old(netClient.env) - invariant netClient.LocalEndPoint() == old(netClient.LocalEndPoint()) + invariant netClient.MyPublicKey() == old(netClient.MyPublicKey()) invariant NetClientOk(netClient) <==> ok invariant ok ==> ( NetClientIsValid(netClient) && netClient.IsOpen()) invariant ok ==> netClientEnvHistory_old + netEventLog == netClient.env.net.history() @@ -503,11 +485,6 @@ method{:timeLimitMultiplier 2} SendPacketSequence(netClient:NetClient, packets:O assert cpacket in cpackets; assert OutboundPacketsIsValid(packets); - var dstAddrAry := seqToArrayOpt(dstEp.addr); - var remote; - ok, remote := IPEndPoint.Construct(dstAddrAry, dstEp.port, netClient.env); - if (!ok) { return; } - assert CMessageIsAbstractable(cpacket.msg); assert Marshallable(cpacket.msg); @@ -516,10 +493,10 @@ method{:timeLimitMultiplier 2} SendPacketSequence(netClient:NetClient, packets:O ghost var data := buffer[..]; assert BufferRefinementAgreesWithMessageRefinement(cpacket.msg, data); - ok := netClient.Send(remote, buffer); + ok := netClient.Send(dstEp.public_key, buffer); if (!ok) { return; } - ghost var netEvent := LIoOpSend(LPacket(remote.EP(), netClient.LocalEndPoint(), buffer[..])); + ghost var netEvent := LIoOpSend(LPacket(dstEp, EndPoint(netClient.MyPublicKey()), buffer[..])); ghost var net := netEvent.s; calc { diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/PacketParsing.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/PacketParsing.i.dfy index fcb434b7..5d1fd1ca 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/PacketParsing.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/PacketParsing.i.dfy @@ -31,7 +31,7 @@ import opened Math__mul_nonlinear_i //////////////////////////////////////////////////////////////////// // Grammars for the Paxos types //////////////////////////////////////////////////////////////////// -function method EndPoint_grammar() : G { GUint64 } +function method EndPoint_grammar() : G { GByteArray } function method CRequest_grammar() : G { GTuple([EndPoint_grammar(), GUint64, GByteArray]) } function method CRequestBatch_grammar() : G { GArray(CRequest_grammar()) } function method CReply_grammar() : G { GTuple([EndPoint_grammar(), GUint64, GByteArray]) } @@ -117,10 +117,7 @@ function method parse_EndPoint(val:V) : EndPoint requires ValInGrammar(val, EndPoint_grammar()) ensures EndPointIsAbstractable(parse_EndPoint(val)) { - if val.u <= 0xffffffffffff then - ConvertUint64ToEndPoint(val.u) - else - EndPoint([0,0,0,0], 0) + EndPoint(val.b) } function method parse_Request(val:V) : CRequest @@ -593,7 +590,7 @@ method DetermineIfValidVote(vote:CVote) returns (b:bool) invariant forall i :: 0 <= i < pos ==> ValidRequest(vote.max_val[i]) { var c := vote.max_val[pos]; - if c.CRequest? && !CAppRequestMarshallable(c.request) { + if !CAppRequestMarshallable(c.request) || !EndPointIsValidPublicKey(c.client) { assert !ValidRequest(c); assert !ValidRequestBatch(vote.max_val); b := false; @@ -705,13 +702,12 @@ method DetermineIfMessageMarshallable(msg:CMessage) returns (b:bool) //////////////////////////////////////////////////////////////////// method MarshallEndPoint(c:EndPoint) returns (val:V) - requires EndPointIsValidIPV4(c) + requires EndPointIsValidPublicKey(c) ensures ValInGrammar(val, EndPoint_grammar()) ensures ValidVal(val) ensures parse_EndPoint(val) == c { - val := VUint64(ConvertEndPointToUint64(c)); - lemma_Uint64EndPointRelationships(); + val := VByteArray(c.public_key); } method MarshallRequest(c:CRequest) returns (val:V) @@ -810,7 +806,7 @@ method{:timeLimitMultiplier 3} MarshallVotes(c:CVotes) returns (val:V) ensures ValidVal(val) ensures parse_Votes(val) == c //ensures val == fun_MarshallVotes(c) - ensures SeqSum(val.a) <= |c.v| * (8 + (8 + 8) + (8 + (24 + MaxAppRequestSize())*RequestBatchSizeLimit())) + ensures SeqSum(val.a) <= |c.v| * (8 + (8 + 8) + (8 + (0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit())) { if |c.v| == 0 { val := VArray([]); @@ -846,18 +842,18 @@ method{:timeLimitMultiplier 3} MarshallVotes(c:CVotes) returns (val:V) { reveal SeqSum(); } SizeOfV(val.a[0]) + SeqSum(val.a[1..]); <= - SizeOfV(val.a[0]) + |remainder| * (8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); - //SizeOfV(val.a[0]) + |remainder| * (8 + (8 + 8) + (24 + MaxAppRequestSize())); + SizeOfV(val.a[0]) + |remainder| * (8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + //SizeOfV(val.a[0]) + |remainder| * (8 + (8 + 8) + (0x10_0018 + MaxAppRequestSize())); { lemma_SeqSum2(val.a[0]); } - SizeOfV(val.a[0].t[0]) + SizeOfV(val.a[0].t[1]) + |remainder| * (8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + SizeOfV(val.a[0].t[0]) + SizeOfV(val.a[0].t[1]) + |remainder| * (8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); <= { lemma_VoteValValid(c.v[op], val.a[0].t[1]); lemma_VoteBound(c.v[op], val.a[0].t[1]); } - 8 + (8 + 8) + 8 + ((24 + MaxAppRequestSize())*|val.a[0].t[1].t[1].a|) + |remainder| * (8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); - <= { assert |val.a[0].t[1].t[1].a| <= RequestBatchSizeLimit(); lemma_mul_upper_bound(24 + MaxAppRequestSize(), 24 + MaxAppRequestSize(), |val.a[0].t[1].t[1].a|, RequestBatchSizeLimit());} - 8 + (8 + 8) + 8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()) + |remainder| * (8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); - 1*(8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))) + |remainder| * (8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); - { lemma_mul_is_distributive((8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))), 1, |remainder|); } - (1+|remainder|) * (8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); - |c.v| * (8 + (8 + 8) + (8 + ((24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + 8 + (8 + 8) + 8 + ((0x10_0018 + MaxAppRequestSize())*|val.a[0].t[1].t[1].a|) + |remainder| * (8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + <= { assert |val.a[0].t[1].t[1].a| <= RequestBatchSizeLimit(); lemma_mul_upper_bound(0x10_0018 + MaxAppRequestSize(), 0x10_0018 + MaxAppRequestSize(), |val.a[0].t[1].t[1].a|, RequestBatchSizeLimit());} + 8 + (8 + 8) + 8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()) + |remainder| * (8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + 1*(8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))) + |remainder| * (8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + { lemma_mul_is_distributive((8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))), 1, |remainder|); } + (1+|remainder|) * (8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + |c.v| * (8 + (8 + 8) + (8 + ((0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); } } } @@ -905,10 +901,10 @@ method MarshallMessage_1b(c:CMessage) returns (val:V) SizeOfV(val); 16 + 8 + SizeOfV(val.t[2]); <= - 16 + 8 + 8 + (|c.votes.v| * (8 + (8 + 8) + (8 + (24 + MaxAppRequestSize())*RequestBatchSizeLimit()))); - 32 + (|c.votes.v| * (32 + (24 + MaxAppRequestSize()) * 100)); - < { lemma_mul_strict_inequality(|c.votes.v|, 8, (32 + (24 + MaxAppRequestSize()) * 100)); } - 32 + (8 * (32 + (24 + MaxAppRequestSize()) * 100)); + 16 + 8 + 8 + (|c.votes.v| * (8 + (8 + 8) + (8 + (0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit()))); + 32 + (|c.votes.v| * (32 + (0x10_0018 + MaxAppRequestSize()) * 100)); + < { lemma_mul_strict_inequality(|c.votes.v|, 8, (32 + (0x10_0018 + MaxAppRequestSize()) * 100)); } + 32 + (8 * (32 + (0x10_0018 + MaxAppRequestSize()) * 100)); < MaxPacketSize() - 8; } @@ -1188,7 +1184,7 @@ lemma lemma_CRequestBound(c:CRequest, val:V) requires ValInGrammar(val, CRequest_grammar()) requires ValidRequest(c) requires parse_Request(val) == c - ensures SizeOfV(val) <= 24 + MaxAppRequestSize() + ensures SizeOfV(val) <= 0x10_0018 + MaxAppRequestSize() { ghost var gtuple := GTuple([EndPoint_grammar(), GUint64, GByteArray]); assert ValInGrammar(val, gtuple); @@ -1196,7 +1192,7 @@ lemma lemma_CRequestBound(c:CRequest, val:V) lemma_SeqSum3(val); assert ValInGrammar(val.t[0], gtuple.t[0]); assert ValInGrammar(val.t[1], gtuple.t[1]); - assert SizeOfV(val.t[0]) == 8; + assert SizeOfV(val.t[0]) <= 0x10_0008; assert SizeOfV(val.t[1]) == 8; assert SizeOfV(val.t[2]) <= 8 + MaxAppRequestSize(); } @@ -1206,20 +1202,20 @@ lemma lemma_CRequestBatchBound(c:CRequestBatch, val:V) requires ValidRequestBatch(c) requires parse_RequestBatch(val) == c decreases |c| - ensures SeqSum(val.a) <= (24 + MaxAppRequestSize())*|val.a| + ensures SeqSum(val.a) <= (0x10_0018 + MaxAppRequestSize())*|val.a| { //ghost var gtuple := GTuple([EndPoint_grammar(), GUint64, GByteArray]); ghost var garray := GArray(CRequest_grammar()); assert ValInGrammar(val, garray); reveal SeqSum(); if |val.a| == 0 { - assert SeqSum(val.a) <= (24 + MaxAppRequestSize())*|val.a|; + assert SeqSum(val.a) <= (0x10_0018 + MaxAppRequestSize())*|val.a|; } else { var req := parse_Request(val.a[0]); var restVal:V := VArray(val.a[1..]); var rest := parse_RequestBatch(restVal); assert c == [req] + rest; - var x := 24 + MaxAppRequestSize(); + var x := 0x10_0018 + MaxAppRequestSize(); var N := |val.a|; lemma_CRequestBatchBound(rest, restVal); assert SeqSum(val.a[1..]) <= (x)*(N-1); @@ -1236,14 +1232,14 @@ lemma lemma_ReplyBound(c:CReply, val:V) requires ValInGrammar(val, CReply_grammar()) requires ValidReply(c) requires parse_Reply(val) == c - ensures SizeOfV(val) <= 24 + MaxAppRequestSize() + ensures SizeOfV(val) <= 0x10_0018 + MaxAppRequestSize() { ghost var gtuple := GTuple([EndPoint_grammar(), GUint64, GByteArray]); assert ValInGrammar(val, gtuple); lemma_SeqSum3(val); assert ValInGrammar(val.t[0], gtuple.t[0]); assert ValInGrammar(val.t[1], gtuple.t[1]); - assert SizeOfV(val.t[0]) == 8; + assert SizeOfV(val.t[0]) <= 0x10_0008; assert SizeOfV(val.t[1]) == 8; assert SizeOfV(val.t[2]) <= 8 + MaxAppRequestSize(); } @@ -1286,16 +1282,18 @@ lemma {:timeLimitMultiplier 2} lemma_VoteValValid(c:CVote, val:V) ghost var garray := GArray(CRequest_grammar()); assert ValInGrammar(val.t[1], garray); ghost var gtuple := GTuple([EndPoint_grammar(), GUint64, GByteArray]); - forall i, v | 0 <= i < |val.t[1].a|&& v == val.t[1].a[i] + forall i, v | 0 <= i < |val.t[1].a| && v == val.t[1].a[i] ensures ValidVal(v) { + assert c.max_val[i] in c.max_val; // OBSERVE antecedent to determine that ValidRequest(c.max_val[i]) + assert ValidRequest(c.max_val[i]); assert ValInGrammar(v, gtuple); assert ValInGrammar(v.t[0], gtuple.t[0]); assert ValInGrammar(v.t[1], gtuple.t[1]); assert ValInGrammar(v.t[2], gtuple.t[2]); + assert |v.t[0].b| < 0x1_0000_0000_0000_0000; assert ValidVal(v.t[0]); assert ValidVal(v.t[1]); - assert c.max_val[i] in c.max_val; // OBSERVE antecedent to determine that ValidRequest(c.max_val[i]) assert ValidVal(v.t[2]); assert ValidVal(v); } @@ -1320,7 +1318,7 @@ lemma lemma_VoteBound(c:CVote, val:V) requires ValidVal(val) requires ValidVote(c) requires parse_Vote(val) == c - ensures SizeOfV(val) <= (8 + 8) + 8 + ((24 + MaxAppRequestSize())*|val.t[1].a|) + ensures SizeOfV(val) <= (8 + 8) + 8 + ((0x10_0018 + MaxAppRequestSize())*|val.t[1].a|) { lemma_SeqSum2(val); assert ValInGrammar(val.t[0], CBallot_grammar()); // OBSERVE @@ -1366,12 +1364,12 @@ lemma lemma_MarshallableBound(c:CMessage, val:V) SizeOfV(val.val.t[2]); 8 + SeqSum(val.val.t[2].a); <= - 8 + (24 + MaxAppRequestSize())*|val.val.t[2].a|; + 8 + (0x10_0018 + MaxAppRequestSize())*|val.val.t[2].a|; <= { lemma_mul_is_commutative(|val.val.t[2].a|, RequestBatchSizeLimit()); - lemma_mul_is_commutative(|val.val.t[2].a|, 24 + MaxAppRequestSize()); - lemma_mul_inequality(|val.val.t[2].a|, RequestBatchSizeLimit(), 24 + MaxAppRequestSize()); + lemma_mul_is_commutative(|val.val.t[2].a|, 0x10_0018 + MaxAppRequestSize()); + lemma_mul_inequality(|val.val.t[2].a|, RequestBatchSizeLimit(), 0x10_0018 + MaxAppRequestSize()); } - 8 + (24 + MaxAppRequestSize())*RequestBatchSizeLimit(); + 8 + (0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit(); } } else if c.CMessage_2b? { lemma_SeqSum3(val.val); @@ -1386,12 +1384,12 @@ lemma lemma_MarshallableBound(c:CMessage, val:V) SizeOfV(val.val.t[2]); 8 + SeqSum(val.val.t[2].a); <= - 8 + (24 + MaxAppRequestSize())*|val.val.t[2].a|; + 8 + (0x10_0018 + MaxAppRequestSize())*|val.val.t[2].a|; <= { lemma_mul_is_commutative(|val.val.t[2].a|, RequestBatchSizeLimit()); - lemma_mul_is_commutative(|val.val.t[2].a|, 24 + MaxAppRequestSize()); - lemma_mul_inequality(|val.val.t[2].a|, RequestBatchSizeLimit(), 24 + MaxAppRequestSize()); + lemma_mul_is_commutative(|val.val.t[2].a|, 0x10_0018 + MaxAppRequestSize()); + lemma_mul_inequality(|val.val.t[2].a|, RequestBatchSizeLimit(), 0x10_0018 + MaxAppRequestSize()); } - 8 + (24 + MaxAppRequestSize())*RequestBatchSizeLimit(); + 8 + (0x10_0018 + MaxAppRequestSize())*RequestBatchSizeLimit(); } } else if c.CMessage_Heartbeat? { lemma_SeqSum3(val.val); @@ -1426,8 +1424,8 @@ lemma lemma_MarshallableBound(c:CMessage, val:V) // since they depend on PaxosDemarshallData //////////////////////////////////////////////////////////////////////// function AbstractifyBufferToRslPacket(src:EndPoint, dst:EndPoint, data:seq) : RslPacket - requires EndPointIsValidIPV4(src) - requires EndPointIsValidIPV4(dst) + requires EndPointIsValidPublicKey(src) + requires EndPointIsValidPublicKey(dst) { LPacket(AbstractifyEndPointToNodeIdentity(dst), AbstractifyEndPointToNodeIdentity(src), @@ -1438,7 +1436,7 @@ predicate BufferRefinementAgreesWithMessageRefinement(msg:CMessage, marshalled:s requires CMessageIsAbstractable(msg) requires CMessageIsAbstractable(msg) { - forall src, dst :: (EndPointIsValidIPV4(src) && EndPointIsValidIPV4(dst)) ==> + forall src, dst :: (EndPointIsValidPublicKey(src) && EndPointIsValidPublicKey(dst)) ==> (AbstractifyBufferToRslPacket(src, dst, marshalled) == LPacket(AbstractifyEndPointToNodeIdentity(dst), AbstractifyEndPointToNodeIdentity(src), AbstractifyCMessageToRslMessage(msg))) @@ -1452,8 +1450,8 @@ function AbstractifyNetPacketToRslPacket(net:NetPacket) : RslPacket predicate NetPacketIsAbstractable(net:NetPacket) { - && EndPointIsValidIPV4(net.src) - && EndPointIsValidIPV4(net.dst) + && EndPointIsValidPublicKey(net.src) + && EndPointIsValidPublicKey(net.dst) } predicate NetPacketsIsAbstractable(netps:set) @@ -1519,7 +1517,7 @@ method PaxosMarshall(msg:CMessage) returns (data:array) RecordTimingSeq("GenericMarshallMessage_StartingPhase2", generic_marshall_start_time, generic_marshall_end_time); } - forall src, dst | EndPointIsValidIPV4(src) && EndPointIsValidIPV4(dst) + forall src, dst | EndPointIsValidPublicKey(src) && EndPointIsValidPublicKey(dst) ensures AbstractifyBufferToRslPacket(src, dst, data[..]) == LPacket(AbstractifyEndPointToNodeIdentity(dst), AbstractifyEndPointToNodeIdentity(src), AbstractifyCMessageToRslMessage(msg)); { @@ -1543,7 +1541,7 @@ predicate CPacketIsSendable(cpacket:CPacket) { && CMessageIsValid(cpacket.msg) && CPacketIsAbstractable(cpacket) - && EndPointIsValidIPV4(cpacket.src) + && EndPointIsValidPublicKey(cpacket.src) } predicate CPacketSetIsSendable(cps:set) diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/ProposerModel.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/ProposerModel.i.dfy index 7d21d32f..8225f8a9 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/ProposerModel.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/ProposerModel.i.dfy @@ -136,8 +136,8 @@ method {:timeLimitMultiplier 2} ProposerProcessRequest(proposer:ProposerState, p ghost var ref_val := AbstractifyCRequestToRequest(val); ghost var ref_myOutstandingProposedValues := AbstractifyMapOfSeqNums(proposer.highest_seqno_requested_by_client_this_view); lemma_AbstractifyMapOfSeqNums_properties(proposer.highest_seqno_requested_by_client_this_view); - assert forall e :: (e !in proposer.highest_seqno_requested_by_client_this_view && EndPointIsValidIPV4(e) ==> AbstractifyEndPointToNodeIdentity(e) !in AbstractifyMapOfSeqNums(proposer.highest_seqno_requested_by_client_this_view)); - assert EndPointIsValidIPV4(packet.src); + assert forall e :: (e !in proposer.highest_seqno_requested_by_client_this_view && EndPointIsValidPublicKey(e) ==> AbstractifyEndPointToNodeIdentity(e) !in AbstractifyMapOfSeqNums(proposer.highest_seqno_requested_by_client_this_view)); + assert EndPointIsValidPublicKey(packet.src); assert packet.src !in proposer.highest_seqno_requested_by_client_this_view ==> AbstractifyEndPointToNodeIdentity(packet.src) !in AbstractifyMapOfSeqNums(proposer.highest_seqno_requested_by_client_this_view); assert packet.src in proposer.highest_seqno_requested_by_client_this_view ==> (packet.msg.seqno > proposer.highest_seqno_requested_by_client_this_view[packet.src] <==> @@ -245,7 +245,7 @@ method ProposerMaybeEnterNewViewAndSend1a(proposer:ProposerState) returns (propo method ProposerProcess1b(proposer:ProposerState, packet:CPacket) returns (proposer':ProposerState) requires ProposerIsValid(proposer) requires proposer.current_state == 1 - requires EndPointIsValidIPV4(packet.src) + requires EndPointIsValidPublicKey(packet.src) requires packet.src in proposer.constants.all.config.replica_ids requires packet.msg.CMessage_1b? requires packet.msg.bal_1b == proposer.max_ballot_i_sent_1a diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplClass.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplClass.i.dfy index 9019f17b..02767c93 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplClass.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplClass.i.dfy @@ -71,7 +71,7 @@ class ReplicaImpl COperationNumber(0), COperationNumber(0)); var acceptor_state := AcceptorState(rcs, CBallot(0, 0), CVotes(map []), [], COperationNumber(0), COperationNumber(0)); - var ep := EndPoint([], 0); + var ep := EndPoint([]); var learner_state := CLearnerState(rcs, CBallot(0, 0), map [], false, COperationNumber(0), false, CPacket(ep, ep, CMessage_Invalid())); var app_state := AppStateMachine.Initialize(); var executor_state := ExecutorState(rcs, app_state, COperationNumber(0), CBallot(0, 0), COutstandingOpUnknown(), map[]); @@ -90,8 +90,8 @@ class ReplicaImpl && (0 <= nextActionIndex as int < 10) && netClient != null && NetClientIsValid(netClient) - && netClient.LocalEndPoint() == localAddr - && netClient.LocalEndPoint() == replica.constants.all.config.replica_ids[replica.constants.my_index] + && EndPoint(netClient.MyPublicKey()) == localAddr + && EndPoint(netClient.MyPublicKey()) == replica.constants.all.config.replica_ids[replica.constants.my_index] && ReplicaStateIsValid(replica) && Repr == { this } + NetClientRepr(netClient) && cur_req_set != prev_req_set @@ -126,55 +126,34 @@ class ReplicaImpl nextActionIndex as int) } - method ConstructNetClient(constants:ReplicaConstantsState, ghost env_:HostEnvironment) returns (ok:bool, client:NetClient?) - requires env_.Valid() && env_.ok.ok() - requires ReplicaConstantsState_IsValid(constants) - modifies env_.ok - ensures ok ==> && client != null - && NetClientIsValid(client) - && client.LocalEndPoint() == constants.all.config.replica_ids[constants.my_index] - && client.env == env_ - { - var my_ep := constants.all.config.replica_ids[constants.my_index]; - var ip_byte_array := new byte[|my_ep.addr|]; - assert EndPointIsValidIPV4(my_ep); - seqIntoArrayOpt(my_ep.addr, ip_byte_array); - var ip_endpoint; - ok, ip_endpoint := IPEndPoint.Construct(ip_byte_array, my_ep.port, env_); - if !ok { return; } - ok, client := NetClient.Construct(ip_endpoint, env_); - if ok { - calc { - client.LocalEndPoint(); - ip_endpoint.EP(); - my_ep; - } - } - } - - method {:timeLimitMultiplier 7} Replica_Init(constants:ReplicaConstantsState, ghost env_:HostEnvironment) returns (ok:bool) + method Replica_Init( + constants:ReplicaConstantsState, + nc:NetClient, + ghost env_:HostEnvironment + ) returns ( + ok:bool + ) requires env_.Valid() && env_.ok.ok() requires ReplicaConstantsState_IsValid(constants) requires WellFormedLConfiguration(AbstractifyReplicaConstantsStateToLReplicaConstants(constants).all.config) + requires NetClientIsValid(nc) + requires EndPoint(nc.MyPublicKey()) == constants.all.config.replica_ids[constants.my_index] + requires nc.env == env_ //requires KnownSendersMatchConfig(constants.all.config) - modifies this, netClient - modifies env_.ok + modifies this ensures ok ==> && Valid() && Env() == env_ && this.replica.constants == constants && LSchedulerInit(AbstractifyToLScheduler(), AbstractifyReplicaConstantsStateToLReplicaConstants(constants)) { - ok, netClient := ConstructNetClient(constants, env_); - - if (ok) - { - replica, cur_req_set, prev_req_set, reply_cache_mutable := InitReplicaState(constants); - nextActionIndex := 0; - localAddr := replica.constants.all.config.replica_ids[replica.constants.my_index]; - Repr := { this } + NetClientRepr(netClient); - this.msg_grammar := CMessage_grammar(); - } + netClient := nc; + replica, cur_req_set, prev_req_set, reply_cache_mutable := InitReplicaState(constants); + nextActionIndex := 0; + localAddr := replica.constants.all.config.replica_ids[replica.constants.my_index]; + Repr := { this } + NetClientRepr(netClient); + this.msg_grammar := CMessage_grammar(); + ok := true; } predicate ReceivedPacketProperties(cpacket:CPacket, netEvent0:NetEvent, io0:RslIo) diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplMain.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplMain.i.dfy index b19c9474..329cbab8 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplMain.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplMain.i.dfy @@ -74,7 +74,7 @@ method {:timeLimitMultiplier 2} ReplicaNextMainProcessPacketX(r:ReplicaImpl) // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); ghost var replica := r.AbstractifyToLReplica(); @@ -84,7 +84,7 @@ method {:timeLimitMultiplier 2} ReplicaNextMainProcessPacketX(r:ReplicaImpl) // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); assert r.Valid(); @@ -137,7 +137,7 @@ method ReplicaNextMainNoClock(r:ReplicaImpl) // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); ghost var replica := r.AbstractifyToLReplica(); @@ -148,7 +148,7 @@ method ReplicaNextMainNoClock(r:ReplicaImpl) // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); assert r.Valid(); @@ -194,7 +194,7 @@ method ReplicaNextMainReadClock(r:ReplicaImpl) // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); ghost var replica := r.AbstractifyToLReplica(); @@ -205,7 +205,7 @@ method ReplicaNextMainReadClock(r:ReplicaImpl) // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); assert r.Valid(); diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplProcessPacketNoClock.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplProcessPacketNoClock.i.dfy index 9abfa2fb..faabe634 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplProcessPacketNoClock.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplProcessPacketNoClock.i.dfy @@ -198,11 +198,9 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, && r.Env() == old(r.Env()) && old_net_history + netEventLog == r.Env().net.history() { - if ShouldPrintProgress() { + if PrintParams.ShouldPrintProgress() { print("Received request from client "); - print(cpacket.src.addr); - print(":"); - print(cpacket.src.port); + print(cpacket.src); print(" with sequence number "); print(cpacket.msg.seqno); print("\n"); @@ -214,7 +212,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets; @@ -223,7 +221,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcessRequestPostconditions(replica_old, r.replica, cpacket, sent_packets); @@ -296,7 +294,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets; @@ -305,7 +303,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcess1aPostconditions(replica_old, r.replica, cpacket, sent_packets); @@ -378,7 +376,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets; @@ -387,7 +385,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcess1bPostconditions(replica_old, r.replica, cpacket, sent_packets); @@ -459,7 +457,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets; @@ -468,7 +466,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcessStartingPhase2Postconditions(replica_old, r.replica, cpacket, sent_packets); @@ -541,7 +539,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets; @@ -550,7 +548,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcess2aPostconditions(replica_old, r.replica, cpacket, sent_packets); @@ -623,7 +621,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets; @@ -632,7 +630,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcess2bPostconditions(replica_old, r.replica, cpacket, sent_packets); @@ -754,7 +752,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets; @@ -763,7 +761,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcessAppStateRequestPostconditions(replica_old, r.replica, cpacket, sent_packets); @@ -836,7 +834,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the old heap. ghost var net_client_old := r.netClient; - ghost var net_addr_old := r.netClient.LocalEndPoint(); + ghost var net_addr_old := r.netClient.MyPublicKey(); assert NetClientIsValid(net_client_old); var sent_packets, replicaChanged; @@ -845,7 +843,7 @@ method {:fuel AbstractifyReplicaStateToLReplica,0,0} {:fuel ReplicaStateIsValid, // Mention unchanged predicates over mutable state in the new heap. assert net_client_old == r.netClient; assert NetClientIsValid(r.netClient); - assert net_addr_old == r.netClient.LocalEndPoint(); + assert net_addr_old == r.netClient.MyPublicKey(); lemma_RevealQFromReplicaNextProcessAppStateSupplyPostconditions(replica_old, r.replica, cpacket, sent_packets); diff --git a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplReadClock.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplReadClock.i.dfy index 4d745f1b..958e9064 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplReadClock.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/RSL/ReplicaImplReadClock.i.dfy @@ -20,6 +20,7 @@ import opened LiveRSL__CMessageRefinements_i import opened LiveRSL__CPaxosConfiguration_i import opened LiveRSL__CTypes_i import opened LiveRSL__Environment_i +import opened LiveRSL__PacketParsing_i import opened LiveRSL__QRelations_i import opened LiveRSL__Replica_i import opened LiveRSL__ReplicaModel_i @@ -33,7 +34,7 @@ import opened LiveRSL__NetRSL_i import opened Environment_s import opened Common__NetClient_i -lemma lemma_ReplicaNextReadClockAndProcessPacketHelper( +lemma {:timeLimitMultiplier 2} lemma_ReplicaNextReadClockAndProcessPacketHelper( old_history:seq, pre_clock_history:seq, pre_delivery_history:seq, @@ -92,6 +93,13 @@ lemma lemma_ReplicaNextReadClockAndProcessPacketHelper( { assert ([receive_event, clock_event] + send_events) == all_events; } old_history + all_events; } + + forall io | io in all_events && io.LIoOpSend? + ensures NetPacketBound(io.s.msg) + ensures Marshallable(PaxosDemarshallData(io.s.msg)) + { + assert io in send_events; + } } method {:fuel ReplicaStateIsValid,0,0} {:timeLimitMultiplier 3} Replica_Next_ReadClockAndProcessPacket( diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/ConstantsState.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/ConstantsState.i.dfy index 413c391f..3a8b6857 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/ConstantsState.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/ConstantsState.i.dfy @@ -29,5 +29,6 @@ predicate ConstantsStateIsValid(constants:ConstantsState) { ConstantsStateIsAbstractable(constants) && CParametersIsValid(constants.params) && SeqIsUnique(constants.hostIds) + && ValidPhysicalAddress(constants.rootIdentity) } } diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/Delegations.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/Delegations.i.dfy index 832c9126..2dda6978 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/Delegations.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/Delegations.i.dfy @@ -70,7 +70,7 @@ predicate CDelegationMapIsComplete(m:CDelegationMap) predicate CDelegationMapHasValidEndPoints(lows:seq) { - forall m :: m in lows ==> EndPointIsAbstractable(m.id) + forall m :: m in lows ==> EndPointIsValidPublicKey(m.id) } predicate CDelegationMapIsValid(m:CDelegationMap) @@ -105,7 +105,7 @@ function KeyRangesFromCDelegationMap(m:CDelegationMap) : set } function method {:opaque} CDM_IndexForKey_helper(m:CDelegationMap, k:KeyPlus, index:uint64) : uint64 - requires CDelegationMapIsValid(m); + requires CDelegationMapIsAbstractable(m); requires forall i :: 0 <= i <= index as int && i < |m.lows| ==> KeyPlusLe(m.lows[i].klo, k); decreases |m.lows| - index as int; ensures 0 <= CDM_IndexForKey_helper(m, k, index) as int < |m.lows|; @@ -159,7 +159,7 @@ lemma CDM_Partitioned(m:CDelegationMap, k:KeyPlus, index:int) function method CDM_IndexForKey(m:CDelegationMap, k:KeyPlus) : uint64 requires 0<|m.lows|; - requires CDelegationMapIsValid(m); + requires CDelegationMapIsAbstractable(m) ensures 0 <= CDM_IndexForKey(m, k) as int < |m.lows|; ensures !k.KeyInf? ==> KeyRangeContains(CDM_IndexToKeyRange(m, CDM_IndexForKey(m, k) as int), k); ensures k.KeyInf? ==> CDM_IndexForKey(m, k) as int == |m.lows| - 1; @@ -179,19 +179,20 @@ function CDM_IndexForKeyRange(m:CDelegationMap, kr:KeyRange) : uint64 predicate CDelegationMapIsAbstractable(m:CDelegationMap) { - CDelegationMapIsValid(m) + && (forall low :: low in m.lows ==> EndPointIsAbstractable(low.id)) + && CDelegationMapIsComplete(m) } function RefineToDelegationMapEntry(m:CDelegationMap, k:Key) : NodeIdentity requires CDelegationMapIsAbstractable(m); - requires forall low :: low in m.lows ==> EndPointIsValidIPV4(low.id); + requires forall low :: low in m.lows ==> EndPointIsAbstractable(low.id); { AbstractifyEndPointToNodeIdentity(m.lows[CDM_IndexForKey(m,KeyPlus(k))].id) } function AbstractifyCDelegationMapToDelegationMap(m:CDelegationMap) : DelegationMap requires CDelegationMapIsAbstractable(m); - requires forall low :: low in m.lows ==> EndPointIsValidIPV4(low.id); + requires forall low :: low in m.lows ==> EndPointIsAbstractable(low.id); { imap k:Key {:trigger CDM_IndexForKey(m,KeyPlus(k))} :: RefineToDelegationMapEntry(m, k) } @@ -343,7 +344,7 @@ lemma CDM_IndexForKey_Ordering(m:CDelegationMap) lemma lemma_UpdateCDelegationMap_Part2_Helper(m:CDelegationMap, m':CDelegationMap, newkr:KeyRange, id:EndPoint) requires CDelegationMapIsValid(m); requires CDelegationMapIsValid(m'); - requires EndPointIsValidIPV4(id); + requires EndPointIsValidPublicKey(id); requires !EmptyKeyRange(newkr); requires forall k:Key :: k in AbstractifyCDelegationMapToDelegationMap(m') <==> k in UpdateDelegationMap(AbstractifyCDelegationMapToDelegationMap(m), newkr, AbstractifyEndPointToNodeIdentity(id)); requires forall k:Key :: true ==> AbstractifyCDelegationMapToDelegationMap(m')[k] == UpdateDelegationMap(AbstractifyCDelegationMapToDelegationMap(m), newkr, AbstractifyEndPointToNodeIdentity(id))[k]; @@ -354,7 +355,7 @@ lemma lemma_UpdateCDelegationMap_Part2_Helper(m:CDelegationMap, m':CDelegationMa lemma {:timeLimitMultiplier 4} {:induction false} UpdateCDelegationMap_Part2(m:CDelegationMap, newkr:KeyRange, id:EndPoint, m':CDelegationMap, left_index:int, right_index:int, new_left:seq, new_right:seq) requires CDelegationMapIsValid(m); - requires EndPointIsValidIPV4(id); + requires EndPointIsValidPublicKey(id); requires !EmptyKeyRange(newkr); requires left_index == CDM_IndexForKey(m, newkr.klo) as int; requires right_index == CDM_IndexForKey(m, newkr.khi) as int; @@ -470,7 +471,7 @@ lemma {:timeLimitMultiplier 16} UpdateCDelegationMap_RHS_Helper(m:CDelegationMap left_index:int, right_index:int, new_left:seq, new_right:seq, k:Key, new_index:int) requires CDelegationMapIsValid(m); - requires EndPointIsValidIPV4(id); + requires EndPointIsValidPublicKey(id); requires !EmptyKeyRange(newkr); requires !KeyRangeContains(newkr, KeyPlus(k)); requires left_index == CDM_IndexForKey(m, newkr.klo) as int; @@ -538,7 +539,7 @@ lemma {:timeLimitMultiplier 4} UpdateCDelegationMap_RHS(m:CDelegationMap, newkr: left_index:int, right_index:int, new_left:seq, new_right:seq, k:Key, new_index:int) requires CDelegationMapIsValid(m); - requires EndPointIsValidIPV4(id); + requires EndPointIsValidPublicKey(id); requires !EmptyKeyRange(newkr); requires !KeyRangeContains(newkr, KeyPlus(k)); requires left_index == CDM_IndexForKey(m, newkr.klo) as int; @@ -598,7 +599,7 @@ lemma {:timeLimitMultiplier 2} UpdateCDelegationMap_Part1( left_index:int, right_index:int, new_left:seq, new_right:seq ) requires CDelegationMapIsValid(m); - requires EndPointIsValidIPV4(id); + requires EndPointIsValidPublicKey(id); requires !EmptyKeyRange(newkr); requires left_index == CDM_IndexForKey(m, newkr.klo) as int; requires right_index == CDM_IndexForKey(m, newkr.khi) as int; @@ -710,7 +711,7 @@ lemma {:timeLimitMultiplier 2} UpdateCDelegationMap_Part1( // TODO: Need to convert ok check into an invariant that we don't grow too large! method {:induction false} {:timeLimitMultiplier 4} UpdateCDelegationMap(m:CDelegationMap, newkr:KeyRange, id:EndPoint) returns (ok:bool, m':CDelegationMap) requires CDelegationMapIsValid(m); - requires EndPointIsValidIPV4(id); + requires EndPointIsValidPublicKey(id); requires !EmptyKeyRange(newkr); ensures |m.lows| as uint64 < 0xFFFF_FFFF_FFFF_FFFF - 2 ==> ok == true; ensures ok ==> CDelegationMapIsValid(m'); diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/HostModel.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/HostModel.i.dfy index 67524dc1..a3c513cd 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/HostModel.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/HostModel.i.dfy @@ -85,7 +85,7 @@ method InitHostState(constants:ConstantsState, me:EndPoint) returns (host:HostSt method DelegateForKeyImpl(m:CDelegationMap, k:Key) returns (ep:EndPoint) requires 0<|m.lows|; requires CDelegationMapIsValid(m); - ensures EndPointIsValidIPV4(ep); + ensures EndPointIsValidPublicKey(ep); ensures AbstractifyCDelegationMapToDelegationMap(m)[k] == AbstractifyEndPointToNodeIdentity(ep); ensures AbstractifyEndPointToNodeIdentity(ep) == DelegateForKey(AbstractifyCDelegationMapToDelegationMap(m), k); { @@ -235,7 +235,8 @@ predicate HostIgnoringUnParseable(host:Host, host':Host, packets:set) && host.receivedPacket.v.msg.SingleMessage? && host.receivedPacket.v.msg.m.Delegate? && var msg := host.receivedPacket.v.msg.m; - !(ValidKeyRange(msg.range) && ValidHashtable(msg.h) && !EmptyKeyRange(msg.range)) + !(ValidKeyRange(msg.range) && ValidHashtable(msg.h) && !EmptyKeyRange(msg.range) + && ValidPhysicalAddress(host.receivedPacket.v.msg.dst)) } method {:timeLimitMultiplier 4} HostModelNextDelegate(host:HostState, cpacket:CPacket) returns (host':HostState, sent_packets:seq) @@ -456,7 +457,7 @@ method {:timeLimitMultiplier 2} HostModelSpontaneouslyRetransmit(host:HostState) ghost var sent_packets' := AbstractifyOutboundPacketsToSeqOfLSHTPackets(sent_packets); lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); - assert forall p :: p in sent_packets ==> CPacketIsAbstractable(p) && EndPointIsValidIPV4(p.dst) && EndPointIsValidIPV4(p.src); + assert forall p :: p in sent_packets ==> CPacketIsAbstractable(p) && EndPointIsAbstractable(p.dst) && EndPointIsAbstractable(p.src); assert forall p :: p in sent_packets ==> LPacket(AbstractifyEndPointToNodeIdentity(p.dst), AbstractifyEndPointToNodeIdentity(p.src), AbstractifyCSingleMessageToSingleMessage(p.msg)) in sent_packets'; assert forall p':: p' in sent_packets' ==> exists p :: p in sent_packets && LPacket(AbstractifyEndPointToNodeIdentity(p.dst), AbstractifyEndPointToNodeIdentity(p.src), AbstractifyCSingleMessageToSingleMessage(p.msg)) == p'; diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/HostState.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/HostState.i.dfy index 50d26e35..3510bf3d 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/HostState.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/HostState.i.dfy @@ -59,6 +59,7 @@ function AbstractifyHostStateToHost(host:HostState) : Host predicate HostStateIsValid(host:HostState) { HostStateIsAbstractable(host) + && CDelegationMapIsValid(host.delegationMap) && (forall k :: k in host.h ==> ValidKey(k)) && (forall k :: k in host.h ==> ValidValue(host.h[k])) && CSingleDeliveryAccountIsValid(host.sd, host.constants.params) @@ -70,6 +71,7 @@ predicate HostStateIsValid(host:HostState) && ConstantsStateIsValid(host.constants) && host.numDelegations < host.constants.params.max_delegations && |host.delegationMap.lows| <= 2 * host.numDelegations as int + && (host.receivedPacket.Some? ==> ValidPhysicalAddress(host.receivedPacket.v.src)) } @@ -88,6 +90,7 @@ predicate HostState_common_preconditions(host:HostState, cpacket:CPacket) HostStateIsAbstractable(host) && CPacketIsAbstractable(cpacket) && HostStateIsValid(host) + && ValidPhysicalAddress(cpacket.src) } predicate HostState_common_postconditions(host:HostState, cpacket:CPacket, host':HostState, sent_packets:seq) @@ -110,6 +113,7 @@ predicate NextGetRequestPreconditions(host:HostState, cpacket:CPacket) && CPacketIsAbstractable(cpacket) && cpacket.msg.CSingleMessage? && cpacket.msg.m.CGetRequest? + && EndPointIsValidPublicKey(cpacket.src) //&& ValidKey(cpacket.msg.m.k_getrequest) //&& CSingleMessageMarshallable(cpacket.msg) && CSingleDeliveryAccountIsValid(host.sd, host.constants.params) @@ -161,6 +165,7 @@ predicate NextDelegatePreconditions(host:HostState, cpacket:CPacket) && (forall k :: k in cpacket.msg.m.h ==> ValidValue(cpacket.msg.m.h[k]))*/ //&& CSingleMessageMarshallable(cpacket.msg) && HostState_common_preconditions(host, cpacket) + && ValidPhysicalAddress(host.me) //&& host.numDelegations < host.constants.params.max_delegations - 2 } diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/PacketParsing.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/PacketParsing.i.dfy index 5bd6bafe..6bf0697e 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/PacketParsing.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/PacketParsing.i.dfy @@ -31,7 +31,7 @@ function method OptionalValue_grammar() : G { GTaggedUnion([Value_grammar(), GTu function method KeyPlus_grammar() : G { GTaggedUnion([Key_grammar(), GUint64]) } function method KeyRange_grammar() : G { GTuple([KeyPlus_grammar(), KeyPlus_grammar()]) } function method Hashtable_grammar() : G { GArray(GTuple([Key_grammar(), Value_grammar()])) } -function method EndPoint_grammar() : G { GUint64 } +function method EndPoint_grammar() : G { GByteArray } //////////////////////////////////////////////////////////////////// // Grammars for the SHT messages @@ -141,10 +141,7 @@ function method parse_EndPoint(val:V) : EndPoint requires ValInGrammar(val, EndPoint_grammar()); ensures EndPointIsAbstractable(parse_EndPoint(val)); { - if val.u <= 0xffffffffffff then - ConvertUint64ToEndPoint(val.u) - else - EndPoint([0,0,0,0], 0) + EndPoint(val.b) } function method parse_Message_GetRequest(val:V) : CMessage @@ -224,11 +221,16 @@ function method {:fuel ValidVal,2} parse_CSingleMessage(val:V) : CSingleMessage CAck(val.val.u) } -function SHTDemarshallData(data:seq) : CSingleMessage +function SHTDemarshallData(data:seq) : (result:CSingleMessage) + ensures result.CSingleMessage? ==> EndPointIsValidPublicKey(result.dst) { if Demarshallable(data, CSingleMessage_grammar()) then - var val := DemarshallFunc(data, CSingleMessage_grammar()); - parse_CSingleMessage(val) + var val := DemarshallFunc(data, CSingleMessage_grammar()); + var result := parse_CSingleMessage(val); + if result.CSingleMessage? && !EndPointIsValidPublicKey(result.dst) then + CInvalidMessage() + else + result else CInvalidMessage() } @@ -246,7 +248,9 @@ method SHTDemarshallDataMethod(data:array) returns (msg:CSingleMessage) if success { assert ValInGrammar(val, CSingleMessage_grammar()); msg := parse_CSingleMessage(val); - assert !msg.CInvalidMessage?; + if msg.CSingleMessage? && !EndPointIsValidPublicKey(msg.dst) { + msg := CInvalidMessage(); + } } else { msg := CInvalidMessage(); } @@ -285,14 +289,14 @@ predicate MessageMarshallable(msg:CMessage) case CGetRequest(k) => ValidKey(k) case CSetRequest(k, v) => ValidKey(k) && ValidOptionalValue(v) case CReply(k, v) => ValidKey(k) && ValidOptionalValue(v) - case CRedirect(k, id) => ValidKey(k) && EndPointIsAbstractable(id) - case CShard(kr, id) => ValidKeyRange(kr) && EndPointIsAbstractable(id) && !EmptyKeyRange(msg.kr) + case CRedirect(k, id) => ValidKey(k) && EndPointIsValidPublicKey(id) + case CShard(kr, id) => ValidKeyRange(kr) && EndPointIsValidPublicKey(id) && !EmptyKeyRange(msg.kr) case CDelegate(kr, h) => ValidKeyRange(kr) && ValidHashtable(h) && !EmptyKeyRange(msg.range) } predicate CSingleMessageMarshallable(msg:CSingleMessage) { - msg.CAck? || (msg.CSingleMessage? && EndPointIsAbstractable(msg.dst) && MessageMarshallable(msg.m)) + msg.CAck? || (msg.CSingleMessage? && EndPointIsValidPublicKey(msg.dst) && MessageMarshallable(msg.m)) } method IsValidKeyPlus(kp:KeyPlus) returns (b:bool) @@ -382,10 +386,10 @@ method IsMessageMarshallable(msg:CMessage) returns (b:bool) } case CRedirect(k, id) => b := IsKeyValid(k); - b := b && (|id.addr| as uint64 == 4 && 0 <= id.port <= 65535); + b := b && (|id.public_key| < 0x10_0000); case CShard(kr, id) => b := IsValidKeyRange(kr); - b := b && (|id.addr| as uint64 == 4 && 0 <= id.port <= 65535); + b := b && (|id.public_key| < 0x10_0000); if b { b := IsEmptyKeyRange(kr); b := !b; @@ -413,7 +417,7 @@ method IsCSingleMessageMarshallable(msg:CSingleMessage) returns (b:bool) } else { assert msg.CSingleMessage?; - if !(|msg.dst.addr| as uint64 == 4 && 0 <= msg.dst.port <= 65535) { + if !(|msg.dst.public_key| < 0x10_0000) { b := false; return; } @@ -580,13 +584,13 @@ method MarshallHashtable(c:Hashtable) returns (val:V) } method MarshallEndPoint(c:EndPoint) returns (val:V) - requires EndPointIsAbstractable(c); - ensures ValInGrammar(val, EndPoint_grammar()); - ensures ValidVal(val); - ensures parse_EndPoint(val) == c; + requires EndPointIsValidPublicKey(c) + ensures ValInGrammar(val, EndPoint_grammar()) + ensures ValidVal(val) + ensures parse_EndPoint(val) == c + ensures 0 <= SizeOfV(val) < 0x10_0008 { - val := VUint64(ConvertEndPointToUint64(c)); - Uint64EndPointRelationships(); + val := VByteArray(c.public_key); } method MarshallMessage_GetRequest(c:CMessage) returns (val:V) @@ -595,7 +599,7 @@ method MarshallMessage_GetRequest(c:CMessage) returns (val:V) ensures ValInGrammar(val, CMessage_GetRequest_grammar()); ensures ValidVal(val); ensures parse_Message_GetRequest(val) == c; - ensures 0 <= SizeOfV(val) < MaxPacketSize() - 32; + ensures 0 <= SizeOfV(val) < MaxPacketSize() - 0x10_0020; { val := MarshallKey(c.k_getrequest); } @@ -606,7 +610,7 @@ method MarshallMessage_SetRequest(c:CMessage) returns (val:V) ensures ValInGrammar(val, CMessage_SetRequest_grammar()); ensures ValidVal(val); ensures parse_Message_SetRequest(val) == c; - ensures 0 <= SizeOfV(val) < MaxPacketSize() - 32; + ensures 0 <= SizeOfV(val) < MaxPacketSize() - 0x10_0020; { var k_setrequest := MarshallKey(c.k_setrequest); var v_setrequest := MarshallOptionalValue(c.v_setrequest); @@ -620,7 +624,7 @@ method MarshallMessage_Reply(c:CMessage) returns (val:V) ensures ValInGrammar(val, CMessage_Reply_grammar()); ensures ValidVal(val); ensures parse_Message_Reply(val) == c; - ensures 0 <= SizeOfV(val) < MaxPacketSize() - 32; + ensures 0 <= SizeOfV(val) < MaxPacketSize() - 0x10_0020; { var k_reply := MarshallKey(c.k_reply); var v := MarshallOptionalValue(c.v); @@ -634,7 +638,7 @@ method MarshallMessage_Redirect(c:CMessage) returns (val:V) ensures ValInGrammar(val, CMessage_Redirect_grammar()); ensures ValidVal(val); ensures parse_Message_Redirect(val) == c; - ensures 0 <= SizeOfV(val) < MaxPacketSize() - 32; + ensures 0 <= SizeOfV(val) < MaxPacketSize() - 0x10_0020; { var k_redirect := MarshallKey(c.k_redirect); var ep := MarshallEndPoint(c.id); @@ -648,7 +652,7 @@ method MarshallMessage_Shard(c:CMessage) returns (val:V) ensures ValInGrammar(val, CMessage_Shard_grammar()); ensures ValidVal(val); ensures parse_Message_Shard(val) == c; - ensures 0 <= SizeOfV(val) < MaxPacketSize() - 32; + ensures 0 <= SizeOfV(val) < MaxPacketSize() - 0x10_0020; { var k_redirect := MarshallKeyRange(c.kr); var ep := MarshallEndPoint(c.recipient); @@ -662,7 +666,7 @@ method MarshallMessage_Delegate(c:CMessage) returns (val:V) ensures ValInGrammar(val, CMessage_Delegate_grammar()); ensures ValidVal(val); ensures parse_Message_Delegate(val) == c; - ensures 0 <= SizeOfV(val) < MaxPacketSize() - 32; + ensures 0 <= SizeOfV(val) < MaxPacketSize() - 0x10_0020; { var range := MarshallKeyRange(c.range); var h := MarshallHashtable(c.h); @@ -681,7 +685,7 @@ method MarshallMessage_Delegate(c:CMessage) returns (val:V) { lemma_mul_is_distributive_add_forall(); } (16 + max_key_len()) + (16 + max_key_len()) + 8 + |c.h| * 8 + |c.h| * max_key_len() + |c.h| * 8 + |c.h| * max_val_len(); < - MaxPacketSize() - 32; + MaxPacketSize() - 0x10_0020; } } @@ -691,7 +695,7 @@ method MarshallMessage(c:CMessage) returns (val:V) ensures ValInGrammar(val, CMessage_grammar()); ensures ValidVal(val); ensures parse_Message(val) == c; - ensures 0 <= SizeOfV(val) < MaxPacketSize() - 24; + ensures 0 <= SizeOfV(val) < MaxPacketSize() - 0x10_0018; { if c.CGetRequest? { var msg := MarshallMessage_GetRequest(c); @@ -742,8 +746,6 @@ method MarshallCSingleMessage(c:CSingleMessage) returns (val:V) //////////////////////////////////////////////////////////////////////// function AbstractifyBufferToLSHTPacket(src:EndPoint, dst:EndPoint, data:seq) : LSHTPacket - requires EndPointIsValidIPV4(src); - requires EndPointIsValidIPV4(dst); { LPacket(AbstractifyEndPointToNodeIdentity(dst), AbstractifyEndPointToNodeIdentity(src), @@ -754,7 +756,7 @@ predicate BufferRefinementAgreesWithMessageRefinement(msg:CSingleMessage, marsha requires CSingleMessageIsAbstractable(msg); requires CSingleMessageIsAbstractable(msg); { - forall src, dst :: (EndPointIsValidIPV4(src) && EndPointIsValidIPV4(dst)) ==> + forall src, dst :: (EndPointIsValidPublicKey(src) && EndPointIsValidPublicKey(dst)) ==> (AbstractifyBufferToLSHTPacket(src, dst, marshalled) == LPacket(AbstractifyEndPointToNodeIdentity(dst), AbstractifyEndPointToNodeIdentity(src), AbstractifyCSingleMessageToSingleMessage(msg))) @@ -782,8 +784,8 @@ function AbstractifyNetPacketToShtPacket(net:NetPacket) : Packet predicate NetPacketIsAbstractable(net:NetPacket) { - EndPointIsValidIPV4(net.src) - && EndPointIsValidIPV4(net.dst) + EndPointIsAbstractable(net.src) + && EndPointIsAbstractable(net.dst) } predicate NetPacketsIsAbstractable(netps:set) @@ -822,7 +824,7 @@ method SHTMarshall(msg:CSingleMessage) returns (data:array) lemma_CSingleMessage_grammar_valid(); data := Marshall(val, CSingleMessage_grammar()); - forall src, dst | EndPointIsValidIPV4(src) && EndPointIsValidIPV4(dst) + forall src, dst | EndPointIsValidPublicKey(src) && EndPointIsValidPublicKey(dst) ensures AbstractifyBufferToLSHTPacket(src, dst, data[..]) diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/SHTConcreteConfiguration.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/SHTConcreteConfiguration.i.dfy index 32a8d99e..ed6330a1 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/SHTConcreteConfiguration.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/SHTConcreteConfiguration.i.dfy @@ -24,24 +24,22 @@ datatype SHTConcreteConfiguration = SHTConcreteConfiguration( predicate SHTConcreteConfigurationIsAbstractable(config:SHTConcreteConfiguration) { - (forall e :: e in config.hostIds ==> EndPointIsValidIPV4(e)) - && SeqIsUnique(config.hostIds) - && EndPointIsValidIPV4(config.rootIdentity) - && CParametersIsValid(config.params) + (forall e :: e in config.hostIds ==> EndPointIsAbstractable(e)) + && EndPointIsAbstractable(config.rootIdentity) } predicate SHTConcreteConfigurationIsValid(config:SHTConcreteConfiguration) - ensures SHTConcreteConfigurationIsValid(config) ==> SeqIsUnique(config.hostIds); { 0 < |config.hostIds| < 0xffff_ffff_ffff_ffff && SHTConcreteConfigurationIsAbstractable(config) + && SeqIsUnique(config.hostIds) && CParametersIsValid(config.params) } function method SHTEndPointIsValid(endPoint:EndPoint, config:SHTConcreteConfiguration) : bool requires SHTConcreteConfigurationIsValid(config); { - EndPointIsValidIPV4(endPoint) + EndPointIsValidPublicKey(endPoint) } @@ -64,12 +62,14 @@ predicate ReplicaIndicesValid(indices:seq, config:SHTConcreteConfigurati lemma lemma_WFSHTConcreteConfiguration(config:SHTConcreteConfiguration) ensures SHTConcreteConfigurationIsAbstractable(config) && 0 < |config.hostIds| + && SeqIsUnique(config.hostIds) && config.rootIdentity in config.hostIds ==> SHTConcreteConfigurationIsAbstractable(config) && WFSHTConfiguration(AbstractifyToConfiguration(config)); { if (SHTConcreteConfigurationIsAbstractable(config) - && 0 < |config.hostIds|) + && 0 < |config.hostIds| + && SeqIsUnique(config.hostIds)) { //lemma_CardinalityNonEmpty(config.hostIds); var e := config.hostIds[0]; @@ -103,12 +103,13 @@ predicate WFSHTConcreteConfiguration(config:SHTConcreteConfiguration) lemma_WFSHTConcreteConfiguration(config); SHTConcreteConfigurationIsAbstractable(config) && 0 < |config.hostIds| + && SeqIsUnique(config.hostIds) && config.rootIdentity in config.hostIds } method CGetReplicaIndex(replica:EndPoint, config:SHTConcreteConfiguration) returns (found:bool, index:uint64) requires SHTConcreteConfigurationIsValid(config); - requires EndPointIsValidIPV4(replica); + requires EndPointIsValidPublicKey(replica); ensures found ==> ReplicaIndexValid(index, config) && config.hostIds[index] == replica; ensures found ==> GetHostIndex(AbstractifyEndPointToNodeIdentity(replica), AbstractifyToConfiguration(config)) == index as int; ensures !found ==> !(replica in config.hostIds); diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryModel.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryModel.i.dfy index fb3cde8a..ac3c5bdb 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryModel.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryModel.i.dfy @@ -229,12 +229,12 @@ method ReceiveSingleMessageImpl(acct:CSingleDeliveryAcct, pkt:CPacket, ghost par } -method SendSingleCMessage(acct:CSingleDeliveryAcct, m:CMessage, dst:EndPoint, params:CParameters) +method {:timeLimitMultiplier 3} SendSingleCMessage(acct:CSingleDeliveryAcct, m:CMessage, dst:EndPoint, params:CParameters) returns (acct':CSingleDeliveryAcct, sm:CSingleMessage, shouldSend:bool) requires CSingleDeliveryAccountIsValid(acct, params); requires CMessageIsAbstractable(m); requires MessageMarshallable(m); - requires EndPointIsAbstractable(dst); + requires ValidPhysicalAddress(dst); requires CParametersIsValid(params); ensures CSingleDeliveryAccountIsValid(acct', params); ensures CSingleMessageIsAbstractable(sm); diff --git a/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryState.i.dfy b/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryState.i.dfy index 219703c9..44298531 100755 --- a/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryState.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Impl/SHT/SingleDeliveryState.i.dfy @@ -38,8 +38,8 @@ predicate CTombstoneTableIsAbstractable(ts:CTombstoneTable) function AbstractifyCTombstoneTableToTombstoneTable(ts:CTombstoneTable) : TombstoneTable requires CTombstoneTableIsAbstractable(ts); { - lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); - AbstractifyMap(ts, AbstractifyEndPointToNodeIdentity, uint64_to_nat_t, RefineNodeIdentityToEndPoint) + lemma_AbstractifyEndPointToNodeIdentity_injective_forall(); + AbstractifyMap(ts, AbstractifyEndPointToNodeIdentity, uint64_to_nat_t, RefineNodeIdentityToEndPoint) } ////////////////////////////////////////////////////////////////////////////// diff --git a/ironfleet/src/Dafny/Distributed/Protocol/RSL/DistributedSystem.i.dfy b/ironfleet/src/Dafny/Distributed/Protocol/RSL/DistributedSystem.i.dfy index 7efd3575..e5c416ae 100755 --- a/ironfleet/src/Dafny/Distributed/Protocol/RSL/DistributedSystem.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Protocol/RSL/DistributedSystem.i.dfy @@ -17,8 +17,7 @@ import opened Environment_s datatype RslState = RslState( constants:LConstants, environment:LEnvironment, - replicas:seq, - clients:set + replicas:seq ) predicate RslMapsComplete(ps:RslState) @@ -29,7 +28,6 @@ predicate RslMapsComplete(ps:RslState) predicate RslConstantsUnchanged(ps:RslState, ps':RslState) { && |ps'.replicas| == |ps.replicas| - && ps'.clients == ps.clients && ps'.constants == ps.constants } diff --git a/ironfleet/src/Dafny/Distributed/Protocol/SHT/Host.i.dfy b/ironfleet/src/Dafny/Distributed/Protocol/SHT/Host.i.dfy index ce8ae322..b2c3d9a5 100755 --- a/ironfleet/src/Dafny/Distributed/Protocol/SHT/Host.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Protocol/SHT/Host.i.dfy @@ -8,6 +8,7 @@ include "../../Services/SHT/AbstractService.s.dfy" include "../Common/NodeIdentity.i.dfy" module SHT__Host_i { +import opened Native__Io_s import opened Collections__Maps2_s import opened SHT__SingleDelivery_i import opened SHT__Delegations_i @@ -66,8 +67,13 @@ function ExtractPacketsFromLSHTPackets(seqPackets:seq) : set MapSeqToSet(seqPackets, LSHTPacketToPacket) } +predicate DelegationMap_InitTrigger(k:Key) +{ + true +} + function DelegationMap_Init(rootIdentity:NodeIdentity) : DelegationMap { - imap k {:auto_trigger} :: rootIdentity + imap k {:trigger DelegationMap_InitTrigger(k)} :: rootIdentity } function method HashtableLookup(h:Hashtable, k:Key) : OptionalValue @@ -206,6 +212,7 @@ predicate NextShard_Wrapper(s:Host, s':Host, pkt:Packet, out:set) pkt.msg.m.Shard? && if ( pkt.msg.m.recipient == s.me || !ValidKeyRange(pkt.msg.m.kr) + || !ValidPhysicalAddress(pkt.msg.m.recipient) || EmptyKeyRange(pkt.msg.m.kr) || pkt.msg.m.recipient !in s.constants.hostIds || !DelegateForKeyRangeIsHost(s.delegationMap, pkt.msg.m.kr, s.me) diff --git a/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/InvProof.i.dfy b/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/InvProof.i.dfy index 0f0411ad..574fc06a 100755 --- a/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/InvProof.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/InvProof.i.dfy @@ -1,6 +1,7 @@ include "InvDefs.i.dfy" module SHT__InvProof_i { +import opened Native__Io_s import opened SHT__SHT_i import opened Concrete_NodeIdentity_i`Spec import opened SHT__Network_i @@ -1659,6 +1660,7 @@ lemma NextInv_Process_Message(s:SHT_State, s':SHT_State, id:NodeIdentity, recv:s } else if (NextShard_Wrapper(h, h', rpkt, out)) { if ( rpkt.msg.m.recipient == h.me || !ValidKeyRange(rpkt.msg.m.kr) + || !ValidPhysicalAddress(rpkt.msg.m.recipient) || EmptyKeyRange(rpkt.msg.m.kr) || rpkt.msg.m.recipient !in h.constants.hostIds || !DelegateForKeyRangeIsHost(h.delegationMap, rpkt.msg.m.kr, h.me) diff --git a/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/RefinementProof.i.dfy b/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/RefinementProof.i.dfy index 3782d046..6f7ccb26 100755 --- a/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/RefinementProof.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Protocol/SHT/RefinementProof/RefinementProof.i.dfy @@ -4,6 +4,7 @@ include "Refinement.i.dfy" include "InvProof.i.dfy" module SHT__RefinementProof_i { +import opened Native__Io_s import opened Logic__Option_i import opened Collections__Maps2_s import opened Collections__Maps2_i @@ -1568,6 +1569,7 @@ lemma {:timeLimitMultiplier 12} Next_Process_Message_Refines(s:SHT_State, s':SHT reveal_HiddenRefinement(); if ( rpkt.msg.m.recipient == h.me || !ValidKeyRange(rpkt.msg.m.kr) + || !ValidPhysicalAddress(rpkt.msg.m.recipient) || EmptyKeyRange(rpkt.msg.m.kr) || rpkt.msg.m.recipient !in h.constants.hostIds || !DelegateForKeyRangeIsHost(h.delegationMap, rpkt.msg.m.kr, h.me) diff --git a/ironfleet/src/Dafny/Distributed/Services/Lock/Main.i.dfy b/ironfleet/src/Dafny/Distributed/Services/Lock/Main.i.dfy index d267076e..b6000165 100644 --- a/ironfleet/src/Dafny/Distributed/Services/Lock/Main.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Services/Lock/Main.i.dfy @@ -1,4 +1,5 @@ include "../../Common/Framework/Main.s.dfy" +include "../../Common/Native/Io.s.dfy" include "LockDistributedSystem.i.dfy" include "../../Common/Framework/Environment.s.dfy" include "../../Protocol/Common/NodeIdentity.i.dfy" @@ -11,7 +12,6 @@ include "../../Protocol/Lock/RefinementProof/RefinementProof.i.dfy" include "Marshall.i.dfy" module Main_i refines Main_s { - import opened Native__NativeTypes_s import opened DS_s = Lock_DistributedSystem_i import opened Environment_s import opened Types_i @@ -33,8 +33,8 @@ module Main_i refines Main_s { import opened Common__SeqIsUniqueDef_i import opened Impl_Node_i export - provides DS_s, Native__Io_s - provides Main + provides DS_s, Native__Io_s, Native__NativeTypes_s + provides IronfleetMain predicate IsValidBehavior(config:ConcreteConfiguration, db:seq) reads *; @@ -118,11 +118,6 @@ module Main_i refines Main_s { rest[replica_order[0] := replicas[replica_order[0]].node] } - function AbstractifyConcreteClients(clients:set) : set - { - set e | e in clients :: e - } - predicate DsStateIsAbstractable(ds:DS_State) { ValidConfig(ds.config) diff --git a/ironfleet/src/Dafny/Distributed/Services/RSL/Main.i.dfy b/ironfleet/src/Dafny/Distributed/Services/RSL/Main.i.dfy index f1f67370..c55305fb 100755 --- a/ironfleet/src/Dafny/Distributed/Services/RSL/Main.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Services/RSL/Main.i.dfy @@ -8,7 +8,6 @@ include "Marshall.i.dfy" module Main_i refines Main_s { -import opened Native__NativeTypes_s import opened Host = Host_i import opened DS_s = RSL_DistributedSystem_i import opened DirectRefinement__Refinement_i @@ -112,11 +111,6 @@ function AbstractifyConcreteReplicas(replicas:map, replica_o [replicas[replica_order[0]].sched] + AbstractifyConcreteReplicas(replicas, replica_order[1..]) } -function AbstractifyConcreteClients(clients:set) : set -{ - set e | e in clients :: e -} - predicate DsStateIsAbstractable(ds:DS_State) { && ConstantsStateIsAbstractable(ds.config) @@ -129,8 +123,7 @@ function AbstractifyDsState(ds:DS_State) : RslState { RslState(AbstractifyConstantsStateToLConstants(ds.config), AbstractifyConcreteEnvironment(ds.environment), - AbstractifyConcreteReplicas(ds.servers, ds.config.config.replica_ids), - AbstractifyConcreteClients(ds.clients)) + AbstractifyConcreteReplicas(ds.servers, ds.config.config.replica_ids)) } lemma lemma_DeduceTransitionFromDsBehavior( @@ -159,7 +152,6 @@ lemma lemma_DsConsistency(config:ConcreteConfiguration, db:seq, i:int) requires 0 <= i < |db| ensures db[i].config == config ensures mapdomain(db[i].servers) == mapdomain(db[0].servers) - ensures db[i].clients == db[0].clients { if i == 0 { } else { diff --git a/ironfleet/src/Dafny/Distributed/Services/RSL/Program.cs b/ironfleet/src/Dafny/Distributed/Services/RSL/Program.cs index b2b604c3..b8cac638 100755 --- a/ironfleet/src/Dafny/Distributed/Services/RSL/Program.cs +++ b/ironfleet/src/Dafny/Distributed/Services/RSL/Program.cs @@ -1,4 +1,5 @@ using IronfleetCommon; +using IronfleetIoFramework; using IronRSL; using MathNet.Numerics.Distributions; using System; @@ -9,15 +10,224 @@ namespace IronRSLServer { + public class Params + { + private string serviceFileName; + private string privateKeyFileName; + private string localHostNameOrAddress; + private int localPort; + private bool profile; + private bool progress; + private bool verbose; + private bool safeguard; + + public Params() + { + serviceFileName = ""; + privateKeyFileName = ""; + localHostNameOrAddress = ""; + localPort = 0; + profile = false; + progress = false; + verbose = false; + safeguard = true; + } + + public string ServiceFileName { get { return serviceFileName; } } + public string PrivateKeyFileName { get { return privateKeyFileName; } } + public string LocalHostNameOrAddress { get { return localHostNameOrAddress; } } + public int LocalPort { get { return localPort; } } + public bool Profile { get { return profile; } } + public bool Progress { get { return progress; } } + public bool Verbose { get { return verbose; } } + public bool Safeguard { get { return safeguard; } } + + public bool Validate() + { + if (serviceFileName.Length == 0) { + Console.WriteLine("ERROR - Missing service parameter"); + return false; + } + if (privateKeyFileName.Length == 0) { + Console.WriteLine("ERROR - Missing private parameter"); + return false; + } + return true; + } + + public bool ProcessCommandLineArgument(string arg) + { + var pos = arg.IndexOf("="); + if (pos < 0) { + if (serviceFileName.Length == 0) { + serviceFileName = arg; + return true; + } + else if (privateKeyFileName.Length == 0) { + privateKeyFileName = arg; + return true; + } + else { + Console.WriteLine("ERROR - Invalid argument {0}", arg); + return false; + } + } + var key = arg.Substring(0, pos).ToLower(); + var value = arg.Substring(pos + 1); + return SetValue(key, value); + } + + private bool SetBoolValue(string key, string value, ref bool p) + { + if (value == "false") { + p = false; + return true; + } + else if (value == "true") { + p = true; + return true; + } + else { + Console.WriteLine("ERROR - Invalid {0} value {1} - should be false or true", key, value); + return false; + } + } + + private bool SetValue(string key, string value) + { + if (key == "addr") { + localHostNameOrAddress = value; + return true; + } + + if (key == "port") { + try { + localPort = Convert.ToInt32(value); + return true; + } + catch (Exception e) { + Console.WriteLine("ERROR - Could not convert port {0} to a number. Exception:\n{1}", value, e); + return false; + } + } + + if (key == "profile") { + return SetBoolValue(key, value, ref profile); + } + + if (key == "progress") { + return SetBoolValue(key, value, ref progress); + } + + if (key == "verbose") { + return SetBoolValue(key, value, ref verbose); + } + + if (key == "safeguard") { + return SetBoolValue(key, value, ref safeguard); + } + + Console.WriteLine("ERROR - Invalid argument key {0}", key); + return false; + } + } + class Program { + static void usage() + { + Console.Write(@" +Usage: dotnet IronRSL{0}Server.dll [key=value]... + + - file path of the service description + - file path of the private key + +Allowed keys: + addr - local host name or address to listen to (default: + whatever's specified in the private key file) + port - port to listen to (default: whatever's specified + in the private key file) + profile - print profiling info (false or true, default: false) + progress - print progress (false or true, default: false) + verbose - use verbose output (false or true, default: false) + safeguard - delete the private key file after reading it to + prevent running the same instance twice (false or + true, default: true [see below]) + +You should only run an instance of this server once, since we haven't +implemented crash recovery. To prevent you from accidentally running +it multiple times, this program deletes its private key file right +after reading it. You can override this behavior with +safeguard=false, but this is a VERY UNSAFE thing to do. + +Fortunately, IronRSL can deal with the failure of fewer than half its +servers. But, if half of them or more fail, you'll have to create a +new service. That is, you'll have to start over by running +CreateIronServiceCerts, and that new service will be in its initial +state. +", Service.Name); + } + static void Main(string[] args) { + Console.WriteLine("IronRSL{0}Server program started", Service.Name); + + Console.WriteLine("Processing command-line arguments"); + + Params ps = new Params(); + + foreach (var arg in args) + { + if (!ps.ProcessCommandLineArgument(arg)) { + usage(); + return; + } + } + + if (!ps.Validate()) { + usage(); + return; + } + + ServiceIdentity serviceIdentity = ServiceIdentity.ReadFromFile(ps.ServiceFileName); + if (serviceIdentity == null) { + return; + } + if (serviceIdentity.ServiceType != "IronRSL" + Service.Name) { + Console.Error.WriteLine("Provided service identity has type {0}, not IronRSL{1}.", + serviceIdentity.ServiceType, Service.Name); + return; + } + + PrivateIdentity privateIdentity = PrivateIdentity.ReadFromFile(ps.PrivateKeyFileName); + if (privateIdentity == null) { + return; + } + + Native____Io__s_Compile.PrintParams.SetParameters(ps.Profile, ps.Progress); + + if (ps.Safeguard) { + File.Delete(ps.PrivateKeyFileName); + Console.WriteLine("Deleted private key file after reading it since RSL servers should never run twice."); + } + else { + Console.WriteLine(@" + *** DANGER: Because you specified safeguard=false, we didn't delete the *** + *** private key file to prevent you from running the RSL server twice. *** + *** Hopefully, you're just testing things. *** +"); + } + + var nc = Native____Io__s_Compile.NetClient.Create(privateIdentity, ps.LocalHostNameOrAddress, ps.LocalPort, + serviceIdentity.Servers, ps.Verbose); + Dafny.ISequence[] serverPublicKeys = + serviceIdentity.Servers.Select(server => Dafny.Sequence.FromArray(server.PublicKey)).ToArray(); + var ironArgs = Dafny.Sequence>.FromArray(serverPublicKeys); + Profiler.Initialize(); Native____Io__s_Compile.Time.Initialize(); - Console.WriteLine("IronRSL replica of {0} service started.", Service.Name); Console.WriteLine("[[READY]]"); - Main__i_Compile.__default._Main(); + Main__i_Compile.__default.IronfleetMain(nc, ironArgs); Console.WriteLine("[[EXIT]]"); } } diff --git a/ironfleet/src/Dafny/Distributed/Services/SHT/AbstractService.s.dfy b/ironfleet/src/Dafny/Distributed/Services/SHT/AbstractService.s.dfy index 3cfc2418..55c8bd15 100755 --- a/ironfleet/src/Dafny/Distributed/Services/SHT/AbstractService.s.dfy +++ b/ironfleet/src/Dafny/Distributed/Services/SHT/AbstractService.s.dfy @@ -58,9 +58,10 @@ predicate Service_Next(s:ServiceState, s':ServiceState) function MarshallServiceGetRequest(app:AppRequest, reserved:seq) : seq requires app.AppGetRequest? { - if 0 <= app.g_seqno < 0x1_0000_0000_0000_0000 then + if 0 <= app.g_seqno < 0x1_0000_0000_0000_0000 && |reserved| < 0x10_0000 then [ 0, 0, 0, 0, 0, 0, 0, 0] // CSingleMessage_grammar magic number + Uint64ToBytes(app.g_seqno as uint64) + + Uint64ToBytes(|reserved| as uint64) + reserved + [ 0, 0, 0, 0, 0, 0, 0, 0] // CMessage_GetRequest_grammar magic number + MarshallSHTKey(app.g_k) @@ -71,9 +72,10 @@ function MarshallServiceGetRequest(app:AppRequest, reserved:seq) : seq) : seq requires app.AppSetRequest? { - if 0 <= app.s_seqno < 0x1_0000_0000_0000_0000 then + if 0 <= app.s_seqno < 0x1_0000_0000_0000_0000 && |reserved| < 0x10_0000 then [ 0, 0, 0, 0, 0, 0, 0, 0] // CSingleMessage_grammar magic number + Uint64ToBytes(app.s_seqno as uint64) + + Uint64ToBytes(|reserved| as uint64) + reserved + [ 0, 0, 0, 0, 0, 0, 0, 1] // CMessage_SetRequest_grammar magic number + MarshallSHTKey(app.s_k) @@ -88,9 +90,10 @@ function MarshallServiceSetRequest(app:AppRequest, reserved:seq) : seq) : seq { - if 0 <= app.seqno < 0x1_0000_0000_0000_0000 then + if 0 <= app.seqno < 0x1_0000_0000_0000_0000 && |reserved| < 0x10_0000 then [ 0, 0, 0, 0, 0, 0, 0, 0] // CSingleMessage_grammar magic number + Uint64ToBytes(app.seqno as uint64) + + Uint64ToBytes(|reserved| as uint64) + reserved + [ 0, 0, 0, 0, 0, 0, 0, 2] // CMessage_Reply_grammar magic number + MarshallSHTKey(app.k) @@ -109,16 +112,16 @@ predicate Service_Correspondence(concretePkts:set>>, p in concretePkts && p.src in serviceState.serverAddresses && p.msg == MarshallServiceReply(reply, reserved_bytes) - && |reserved_bytes| == 8 + && |reserved_bytes| < 0x10_0000 ==> reply in serviceState.replies) && (forall req :: req in serviceState.requests && req.AppGetRequest? ==> exists p, reserved_bytes :: p in concretePkts && p.dst in serviceState.serverAddresses && p.msg == MarshallServiceGetRequest(req, reserved_bytes) - && |reserved_bytes| == 8) + && |reserved_bytes| < 0x10_0000) && (forall req :: req in serviceState.requests && req.AppSetRequest? ==> exists p, reserved_bytes :: p in concretePkts && p.dst in serviceState.serverAddresses && p.msg == MarshallServiceSetRequest(req, reserved_bytes) - && |reserved_bytes| == 8) + && |reserved_bytes| < 0x10_0000) } } diff --git a/ironfleet/src/Dafny/Distributed/Services/SHT/Main.i.dfy b/ironfleet/src/Dafny/Distributed/Services/SHT/Main.i.dfy index 9573bfc8..cb51ad8c 100755 --- a/ironfleet/src/Dafny/Distributed/Services/SHT/Main.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Services/SHT/Main.i.dfy @@ -12,7 +12,6 @@ module Main_i refines Main_s { import opened AS_s = AbstractServiceSHT_s`Spec import opened DS_s = SHT_DistributedSystem_i import opened DS_s.H_s - import opened Native__NativeTypes_s import opened Math__mod_auto_i import opened Collections__Sets_i import opened Collections__Maps2_s @@ -44,11 +43,12 @@ module Main_i refines Main_s { import opened LiveSHT__Unsendable_i import opened LiveSHT__SHTRefinement_i import opened Common__GenericMarshalling_i + import opened Common__NetClient_i import opened Common__NodeIdentity_i - + export - provides DS_s, Native__Io_s - provides Main + provides DS_s, Native__Io_s, Native__NativeTypes_s + provides IronfleetMain predicate IsValidBehavior(config:ConcreteConfiguration, db:seq) reads *; @@ -112,7 +112,7 @@ module Main_i refines Main_s { } function AbstractifyConcreteConfiguration(ds_config:ConcreteConfiguration) : SHTConfiguration - requires ConstantsStateIsValid(ds_config); + requires ConstantsStateIsAbstractable(ds_config); { AbstractifyToConfiguration( SHTConcreteConfiguration( @@ -134,11 +134,6 @@ module Main_i refines Main_s { [replicas[replica_order[0]].sched] + AbstractifyConcreteReplicas(replicas, replica_order[1..]) } - function AbstractifyConcreteClients(clients:set) : set - { - set e | e in clients :: e - } - predicate DsStateIsAbstractable(ds:DS_State) { ConstantsStateIsValid(ds.config) @@ -180,7 +175,6 @@ module Main_i refines Main_s { requires 0 <= i < |db|; ensures db[i].config == config; ensures Collections__Maps2_s.mapdomain(db[i].servers) == Collections__Maps2_s.mapdomain(db[0].servers); - ensures db[i].clients == db[0].clients; { if i == 0 { } else { @@ -820,7 +814,7 @@ module Main_i refines Main_s { if Demarshallable(p.msg, g) { var cmsg := parse_CSingleMessage(DemarshallFunc(p.msg, g)); if cmsg.CSingleMessage? { - assert !EndPointIsAbstractable(cmsg.dst) || !MessageMarshallable(cmsg.m); + assert !EndPointIsValidPublicKey(cmsg.dst) || !MessageMarshallable(cmsg.m); } } } @@ -1447,7 +1441,7 @@ module Main_i refines Main_s { p in concretePkts && p.src in serviceState.serverAddresses && p.msg == MarshallServiceReply(reply, reserved_bytes) - && |reserved_bytes| == 8 + && |reserved_bytes| < 0x10_0000 ensures reply in serviceState.replies; { var lsht_packet := AbstractifyConcretePacket(p); @@ -1486,7 +1480,7 @@ module Main_i refines Main_s { forall req | req in serviceState.requests && req.AppGetRequest? ensures exists p, reserved_bytes :: p in concretePkts && p.dst in serviceState.serverAddresses && p.msg == MarshallServiceGetRequest(req, reserved_bytes) - && |reserved_bytes| == 8; + && |reserved_bytes| < 0x10_0000; { assert serviceState == Refinement(sht_state); @@ -1519,7 +1513,7 @@ module Main_i refines Main_s { forall req | req in serviceState.requests && req.AppSetRequest? ensures exists p, reserved_bytes :: p in concretePkts && p.dst in serviceState.serverAddresses && p.msg == MarshallServiceSetRequest(req, reserved_bytes) - && |reserved_bytes| == 8; + && |reserved_bytes| < 0x10_0000; { assert serviceState == Refinement(sht_state); var h,req_index :| h in maprange(sht_state.hosts) && 0 <= req_index < |h.receivedRequests| && req == h.receivedRequests[req_index]; diff --git a/ironfleet/src/Dafny/Distributed/Services/SHT/Marshall.i.dfy b/ironfleet/src/Dafny/Distributed/Services/SHT/Marshall.i.dfy index e947e07d..910d8c6c 100755 --- a/ironfleet/src/Dafny/Distributed/Services/SHT/Marshall.i.dfy +++ b/ironfleet/src/Dafny/Distributed/Services/SHT/Marshall.i.dfy @@ -7,6 +7,7 @@ include "AppInterface.i.dfy" module MarshallProof_i { import opened Native__NativeTypes_s + import opened Native__Io_s import opened Bytes_s import opened Math__power2_i import opened AbstractServiceSHT_s`All @@ -31,6 +32,8 @@ module MarshallProof_i { ensures rest == parse_Uint64(data).1; ensures |rest| >= len as int; ensures val == VByteArray(rest[0..len]); + ensures parse_Val(data, g) == parse_ByteArray(data) + ensures parse_ByteArray(data).1 == rest[len..] { reveal_parse_Val(); len := parse_Uint64(data).0.v.u; @@ -53,6 +56,7 @@ module MarshallProof_i { ensures val == parse_Val(rest, g.cases[caseId]).0.v ensures v == VCase(caseId, val); ensures ValInGrammar(val, g.cases[caseId]); + ensures parse_Val(data, g) == parse_Case(data, g.cases) { reveal_parse_Val(); caseId := parse_Uint64(data).0.v.u; @@ -184,130 +188,58 @@ module MarshallProof_i { ensures u == parse_Uint64(data).0.v.u; ensures v == VUint64(u); ensures rest == parse_Val(data, g).1; + ensures rest == parse_Uint64(data).1; { reveal_parse_Val(); u := parse_Uint64(data).0.v.u; rest := parse_Uint64(data).1; } - lemma {:fuel ValInGrammar,3} {:fuel SizeOfV,4} lemma_SizeOfGetRequest(v:V) - requires ValInGrammar(v, CSingleMessage_grammar()); - requires ValInGrammar(v.val, GTuple([GUint64, EndPoint_grammar(), CMessage_grammar()])); - requires ValInGrammar(v.val.t[2].val, CMessage_GetRequest_grammar()); - ensures SizeOfV(v) == 40; - { - lemma_SeqSum_3(v.val); - reveal_SeqSum(); - } - - lemma {:fuel ValInGrammar,4} {:fuel SizeOfV,5} lemma_SizeOfSetRequest(v:V, cmsg:CSingleMessage) - requires ValInGrammar(v, CSingleMessage_grammar()); - requires ValInGrammar(v.val, GTuple([GUint64, EndPoint_grammar(), CMessage_grammar()])); - requires ValInGrammar(v.val.t[2].val, CMessage_SetRequest_grammar()); - requires parse_CSingleMessage(v) == cmsg; - requires cmsg.m.CSetRequest?; - ensures SizeOfV(v) == if cmsg.m.v_setrequest.ValuePresent? then 56 + |cmsg.m.v_setrequest.v| else 48; - { - lemma_SeqSum_2(v.val.t[2].val); - lemma_SeqSum_3(v.val); - reveal_SeqSum(); - calc { - SizeOfV(v); - 8 + SizeOfV(v.val); - 8 + 8 + 8 + SizeOfV(v.val.t[2]); - 8 + 8 + 8 + 8 + SizeOfV(v.val.t[2].val); - 8 + 8 + 8 + 8 + 8 + SizeOfV(v.val.t[2].val.t[1]); - 8 + 8 + 8 + 8 + 8 + 8 + SizeOfV(v.val.t[2].val.t[1].val); - 48 + SizeOfV(v.val.t[2].val.t[1].val); - } - if cmsg.m.v_setrequest.ValueAbsent? { - assert SizeOfV(v.val.t[2].val.t[1].val) == 0; - } else { - assert SizeOfV(v.val.t[2].val.t[1].val) == 8 + |v.val.t[2].val.t[1].val.b|; - } - - } - - lemma {:fuel ValInGrammar,4} {:fuel SizeOfV,5} lemma_SizeOfReply(v:V, cmsg:CSingleMessage) - requires ValInGrammar(v, CSingleMessage_grammar()); - requires ValInGrammar(v.val, GTuple([GUint64, EndPoint_grammar(), CMessage_grammar()])); - requires ValInGrammar(v.val.t[2].val, CMessage_Reply_grammar()); - requires parse_CSingleMessage(v) == cmsg; - requires cmsg.m.CReply?; - ensures SizeOfV(v) == if cmsg.m.v.ValuePresent? then 56 + |cmsg.m.v.v| else 48; - { - lemma_SeqSum_2(v.val.t[2].val); - lemma_SeqSum_3(v.val); - reveal_SeqSum(); - calc { - SizeOfV(v); - 8 + SizeOfV(v.val); - 8 + 8 + 8 + SizeOfV(v.val.t[2]); - 8 + 8 + 8 + 8 + SizeOfV(v.val.t[2].val); - 8 + 8 + 8 + 8 + 8 + SizeOfV(v.val.t[2].val.t[1]); - 8 + 8 + 8 + 8 + 8 + 8 + SizeOfV(v.val.t[2].val.t[1].val); - 48 + SizeOfV(v.val.t[2].val.t[1].val); - } - if cmsg.m.v.ValueAbsent? { - assert SizeOfV(v.val.t[2].val.t[1].val) == 0; - } else { - assert SizeOfV(v.val.t[2].val.t[1].val) == 8 + |v.val.t[2].val.t[1].val.b|; - } - - } -/* - lemma {:fuel ValInGrammar,3} {:fuel SizeOfV,3} lemma_SizeOfCMessageRequest1(v:V) - requires ValInGrammar(v, CMessage_grammar()); - requires ValInGrammar(v.val, CMessage_Request_grammar()); - requires v.val.t[1].c == 1; - ensures SizeOfV(v) == 32; - { - lemma_SeqSum2(v.val); - reveal_SeqSum(); - } - - lemma {:fuel ValInGrammar,3} {:fuel SizeOfV,3} lemma_SizeOfCMessageRequest(v:V) - requires ValInGrammar(v, CMessage_grammar()); - requires ValInGrammar(v.val, CMessage_Request_grammar()); - requires v.val.t[1].c == 0 || v.val.t[1].c == 2; - ensures SizeOfV(v) == 24; - { - lemma_SeqSum2(v.val); - reveal_SeqSum(); - } -*/ - - lemma lemma_ParseUint64Offset(s1:seq, s2:seq, i:int, j:int) - requires 0 <= i < j <= |s2|; - requires |s1| < 0x1_0000_0000_0000_0000; - requires s2 == parse_Val(s1, GUint64).1; - ensures j+8 <= |s1|; - ensures s2[i..j] == s1[i+8..j+8]; + lemma lemma_SeqOffset(s1:seq, s2:seq, offset:int, i:int, j:int) + requires 0 <= offset <= |s1| + requires s2 == s1[offset..] + requires 0 <= i <= j <= |s2| + ensures s2[i..j] == s1[i+offset..j+offset] { - reveal_parse_Val(); } - lemma {:fuel ValInGrammar,3} lemma_ParseMarshallGetRequest(bytes:seq, msg:SingleMessage) returns (reserved_bytes:seq) + lemma {:fuel ValInGrammar,3} lemma_ParseMarshallGetRequest(bytes:seq, msg:SingleMessage) + returns (reserved_bytes:seq) requires msg.SingleMessage? && msg.m.GetRequest?; requires CSingleMessageIsAbstractable(SHTDemarshallData(bytes)); requires AbstractifyCSingleMessageToSingleMessage(SHTDemarshallData(bytes)) == msg; - ensures |reserved_bytes| == 8; + ensures |reserved_bytes| < 0x10_0000; ensures bytes == MarshallServiceGetRequest(AppGetRequest(msg.seqno, msg.m.k_getrequest), reserved_bytes); { var cmsg := SHTDemarshallData(bytes); assert cmsg.CSingleMessage?; + assert ValidPhysicalAddress(cmsg.dst); var data := bytes; var g := CSingleMessage_grammar(); var v := DemarshallFunc(data, g); - lemma_SizeOfGetRequest(v); - assert SizeOfV(v) == 40; + assert cmsg.dst == msg.dst; + assert SHTDemarshallData(bytes) == parse_CSingleMessage(v); + var cs := parse_CSingleMessage(v); + assert v.c == 0; + assert cs == CSingleMessage(v.val.t[0].u, parse_EndPoint(v.val.t[1]), parse_Message(v.val.t[2])); + assert AbstractifyCSingleMessageToSingleMessage(cs) == msg; + assert cs.dst == parse_EndPoint(v.val.t[1]); + assert cs.dst == EndPoint(v.val.t[1].b); + assert cs.dst == msg.dst; + assert |v.val.t[1].b| < 0x10_0000; + + reserved_bytes := v.val.t[1].b; // Walk through the generic parsing process var msgCaseId, msgCaseVal, rest0 := lemma_ParseValCorrectVCase(data, v, g); var seqnoVal, dstVal, msgVal, rest1, rest2 := lemma_ParseValCorrectTuple3(rest0, msgCaseVal, g.cases[msgCaseId]); var mCaseId, mCaseVal, rest3 := lemma_ParseValCorrectVCase(rest2, msgVal, g.cases[msgCaseId].t[2]); + assert v.val.t[0] == seqnoVal; + assert v.val.t[1] == dstVal; + assert v.val.t[2] == msgVal; + // Prove that the first 8 bytes are correct assert msgCaseId == 0; assert 0 == SeqByteToUint64(bytes[..8]); @@ -321,60 +253,37 @@ module MarshallProof_i { lemma_BEByteSeqToInt_BEUintToSeqByte_invertability(); assert rest0[..8] == Uint64ToSeqByte(msg.seqno as uint64); assert data[8..16] == rest0[..8]; + assert rest == rest0[8..]; + assert rest1 == rest0[8..]; + + // Prove that the bytes of dst are correct + var len_dst, val_dst, rest_dst := lemma_ParseValCorrectVByteArray(rest1, dstVal, EndPoint_grammar()); + assert val_dst == VByteArray(dstVal.b); + assert rest1[..8] == Uint64ToSeqByte(len_dst); + assert rest1[..8] == rest0[8..16]; + assert data[16..24] == rest1[..8]; + assert data[24..24+len_dst] == val_dst.b; + assert len_dst as int == |cmsg.dst.public_key|; - // Prove that the 8 bytes of dst are correct - var u_dst, rest_dst := lemma_ParseValCorrectVUint64(rest1, dstVal, GUint64); - - assert SeqByteToUint64(rest1[..8]) == u_dst; - assert Uint64ToSeqByte(u_dst) == Uint64ToBytes(u_dst); - lemma_BEByteSeqToInt_BEUintToSeqByte_invertability(); - assert rest1[..8] == Uint64ToSeqByte(u_dst); - reveal_parse_Val(); - calc { - data[16..24]; - rest0[8..16]; - { assert rest1 == parse_Val(rest0, GUint64).1; reveal_parse_Val(); } - rest1[..8]; - } - reserved_bytes := data[16..24]; - //lemma_ParseEndPoint(msg.dst, u_dst, data[16..24]); - - // Prove that the 8 bytes of the case ID for CMessage is handled correctly assert mCaseId == 0; - calc { - SeqByteToUint64(bytes[24..32]); - { lemma_ParseUint64Offset(data, rest0, 16, 24); } - SeqByteToUint64(rest0[16..24]); - { assert rest0[16..24] == rest1[8..16]; } - SeqByteToUint64(rest1[8..16]); - SeqByteToUint64(rest2[..8]); - 0; - } - //assert bytes[24..32] == [ 0, 0, 0, 0, 0, 0, 0, 0]; + assert rest2 == rest1[8 + len_dst..] == data[24 + len_dst ..]; + assert rest2[..8] == data[24 + len_dst .. 32 + len_dst]; + assert mCaseId == SeqByteToUint64(rest2[..8]) == SeqByteToUint64(data[24 + len_dst .. 32 + len_dst]); + assert data[24 + len_dst .. 32 + len_dst] == Uint64ToSeqByte(mCaseId); var key, rest_key := lemma_ParseValCorrectVUint64(rest3, mCaseVal, GUint64); - - assert data[..40] == [ 0, 0, 0, 0, 0, 0, 0, 0] - + Uint64ToSeqByte(msg.seqno as uint64) - + reserved_bytes - + [ 0, 0, 0, 0, 0, 0, 0, 0] - + Uint64ToSeqByte(msg.m.k_getrequest); - assert Uint64ToSeqByte(msg.m.k_getrequest) == MarshallSHTKey(msg.m.k_getrequest); - if |data| > 40 { - assert data[0..|data|] == data[..]; - lemma_parse_Val_view_specific_size(data, v, CSingleMessage_grammar(), 0, |data|); - lemma_parse_Val_view_specific(data, v, CSingleMessage_grammar(), 0, |data|); - assert false; - } - assert |data| == 40; + assert data[32 + len_dst .. 40 + len_dst] == Uint64ToSeqByte(msg.m.k_getrequest); + + assert |data| == 40 + len_dst as int; } - lemma {:fuel ValInGrammar,5} lemma_ParseMarshallSetRequest(bytes:seq, msg:SingleMessage) returns (reserved_bytes:seq) + lemma {:fuel ValInGrammar,5} {:timeLimitMultiplier 2} lemma_ParseMarshallSetRequest(bytes:seq, msg:SingleMessage) + returns (reserved_bytes:seq) requires msg.SingleMessage? && msg.m.SetRequest?; requires CSingleMessageIsAbstractable(SHTDemarshallData(bytes)); requires AbstractifyCSingleMessageToSingleMessage(SHTDemarshallData(bytes)) == msg; - ensures |reserved_bytes| == 8; + ensures |reserved_bytes| < 0x10_0000 ensures bytes == MarshallServiceSetRequest(AppSetRequest(msg.seqno, msg.m.k_setrequest, msg.m.v_setrequest), reserved_bytes); { var cmsg := SHTDemarshallData(bytes); @@ -383,8 +292,17 @@ module MarshallProof_i { var g := CSingleMessage_grammar(); var v := DemarshallFunc(data, g); - lemma_SizeOfSetRequest(v, cmsg); - assert SizeOfV(v) >= 48; + assert cmsg.dst == msg.dst; + assert SHTDemarshallData(bytes) == parse_CSingleMessage(v); + var cs := parse_CSingleMessage(v); + assert v.c == 0; + assert cs == CSingleMessage(v.val.t[0].u, parse_EndPoint(v.val.t[1]), parse_Message(v.val.t[2])); + assert AbstractifyCSingleMessageToSingleMessage(cs) == msg; + assert cs.dst == parse_EndPoint(v.val.t[1]); + assert cs.dst == EndPoint(v.val.t[1].b); + assert cs.dst == msg.dst; + assert |v.val.t[1].b| < 0x10_0000; + reserved_bytes := v.val.t[1].b; // Walk through the generic parsing process var msgCaseId, msgCaseVal, rest0 := lemma_ParseValCorrectVCase(data, v, g); @@ -406,64 +324,23 @@ module MarshallProof_i { lemma_BEByteSeqToInt_BEUintToSeqByte_invertability(); assert rest0[..8] == Uint64ToSeqByte(msg.seqno as uint64); assert data[8..16] == rest0[..8]; + assert rest == rest0[8..]; + assert rest1 == rest0[8..]; + + // Prove that the bytes of dst are correct + var len_dst, val_dst, rest_dst := lemma_ParseValCorrectVByteArray(rest1, dstVal, EndPoint_grammar()); + assert val_dst == VByteArray(dstVal.b); + assert rest1[..8] == Uint64ToSeqByte(len_dst); + assert rest1[..8] == rest0[8..16]; + assert data[16..24] == rest1[..8]; + assert data[24..24+len_dst] == val_dst.b; + assert len_dst as int == |cmsg.dst.public_key|; - // Prove that the 8 bytes of dst are correct - var u_dst, rest_dst := lemma_ParseValCorrectVUint64(rest1, dstVal, GUint64); - - assert SeqByteToUint64(rest1[..8]) == u_dst; - assert Uint64ToSeqByte(u_dst) == Uint64ToBytes(u_dst); - lemma_BEByteSeqToInt_BEUintToSeqByte_invertability(); - assert rest1[..8] == Uint64ToSeqByte(u_dst); - reveal_parse_Val(); - calc { - data[16..24]; - rest0[8..16]; - { assert rest1 == parse_Val(rest0, GUint64).1; reveal_parse_Val(); } - rest1[..8]; - } - reserved_bytes := data[16..24]; - - // Prove some length relationships to show that our indices are within bounds - calc ==> { - true; - rest0 == parse_Uint64(data).1; - |rest0| == |data| - 8; - } - calc ==> { - true; - rest1 == parse_Uint64(rest0).1; - |rest1| == |rest0| - 8; - } - calc ==> { - true; - rest2 == parse_Uint64(rest1).1; - |rest2| == |rest1| - 8; - } - calc ==> { - true; - rest3 == parse_Uint64(rest2).1; - |rest3| == |rest2| - 8; - } - - // Prove that the 8 bytes of the case ID for CMessage is handled correctly assert mCaseId == 1; - calc { - SeqByteToUint64(bytes[24..32]); - { reveal_parse_Val(); lemma_ParseUint64Offset(data, rest0, 16, 24); } - SeqByteToUint64(rest0[16..24]); - SeqByteToUint64(rest1[8..16]); - SeqByteToUint64(rest2[..8]); - 1; - } - - // Prove that the key is handled correctly - var key, rest_key := lemma_ParseValCorrectVUint64(rest3, keyVal, GUint64); - - assert data[..40] == [ 0, 0, 0, 0, 0, 0, 0, 0] - + Uint64ToSeqByte(msg.seqno as uint64) - + reserved_bytes - + [ 0, 0, 0, 0, 0, 0, 0, 1] - + Uint64ToSeqByte(msg.m.k_setrequest); + assert rest2 == rest1[8 + len_dst..] == data[24 + len_dst ..]; + assert rest2[..8] == data[24 + len_dst .. 32 + len_dst]; + assert mCaseId == SeqByteToUint64(rest2[..8]) == SeqByteToUint64(data[24 + len_dst .. 32 + len_dst]); + assert data[24 + len_dst .. 32 + len_dst] == Uint64ToSeqByte(mCaseId); assert Uint64ToSeqByte(msg.m.k_setrequest) == MarshallSHTKey(msg.m.k_setrequest); @@ -474,58 +351,65 @@ module MarshallProof_i { assert rest4[..8] == [ 0, 0, 0, 0, 0, 0, 0, 0]; calc { rest4[..8]; + { lemma_SeqOffset(rest3, rest4, 8, 0, 8); } rest3[8..16]; + { lemma_SeqOffset(rest2, rest3, 8, 8, 16); } rest2[16..24]; - rest1[24..32]; - rest0[32..40]; - data[40..48]; + { lemma_SeqOffset(rest1, rest2, 8 + len_dst as int, 16, 24); } + rest1[24+len_dst as int..32+len_dst as int]; + { lemma_SeqOffset(rest0, rest1, 8, 24 + len_dst as int, 32 + len_dst as int); } + rest0[32+len_dst as int..40+len_dst as int]; + { lemma_SeqOffset(data, rest0, 8, 32 + len_dst as int, 40 + len_dst as int); } + data[40+len_dst as int..48+len_dst as int]; } var byteLen, bytesVal, rest6 := lemma_ParseValCorrectVByteArray(rest5, valCaseVal, GByteArray); - assert data[..56+byteLen] == + assert data[..56+(len_dst as int)+(byteLen as int)] == [ 0, 0, 0, 0, 0, 0, 0, 0] + Uint64ToSeqByte(msg.seqno as uint64) + + Uint64ToSeqByte(|reserved_bytes| as uint64) + reserved_bytes + [ 0, 0, 0, 0, 0, 0, 0, 1] + Uint64ToSeqByte(msg.m.k_setrequest) + [ 0, 0, 0, 0, 0, 0, 0, 0] + Uint64ToSeqByte(byteLen) + bytesVal.b; - assert SizeOfV(v) == 56 + byteLen as int; - if |data| > 56 + byteLen as int { + if |data| > 56 + (len_dst as int) + (byteLen as int) { assert data[0..|data|] == data[..]; lemma_parse_Val_view_specific_size(data, v, CSingleMessage_grammar(), 0, |data|); lemma_parse_Val_view_specific(data, v, CSingleMessage_grammar(), 0, |data|); assert false; } - assert |data| == 56 + byteLen as int; + assert |data| == 56 + (len_dst as int) + (byteLen as int); + assert bytes == MarshallServiceSetRequest(AppSetRequest(msg.seqno, msg.m.k_setrequest, msg.m.v_setrequest), reserved_bytes); } else { assert cmsg.m.v_setrequest.ValueAbsent?; assert valCaseId == 1; assert 1 == SeqByteToUint64(rest4[..8]); assert rest4[..8] == [ 0, 0, 0, 0, 0, 0, 0, 1]; - assert data[..48] == [ 0, 0, 0, 0, 0, 0, 0, 0] + assert data[..48+len_dst as int] == [ 0, 0, 0, 0, 0, 0, 0, 0] + Uint64ToSeqByte(msg.seqno as uint64) + + Uint64ToSeqByte(|reserved_bytes| as uint64) + reserved_bytes + [ 0, 0, 0, 0, 0, 0, 0, 1] + Uint64ToSeqByte(msg.m.k_setrequest) + [ 0, 0, 0, 0, 0, 0, 0, 1]; - - if |data| > 48 { + if |data| > 48 + len_dst as int { assert data[0..|data|] == data[..]; lemma_parse_Val_view_specific_size(data, v, CSingleMessage_grammar(), 0, |data|); lemma_parse_Val_view_specific(data, v, CSingleMessage_grammar(), 0, |data|); assert false; } - assert |data| == 48; + assert |data| == 48 + len_dst as int; + assert bytes == MarshallServiceSetRequest(AppSetRequest(msg.seqno, msg.m.k_setrequest, msg.m.v_setrequest), reserved_bytes); } } - lemma {:fuel ValInGrammar,5} lemma_ParseMarshallReply(bytes:seq, reply:AppReply, msg:SingleMessage, reserved_bytes:seq) + lemma {:fuel ValInGrammar,5} {:timeLimitMultiplier 4} lemma_ParseMarshallReply(bytes:seq, reply:AppReply, msg:SingleMessage, reserved_bytes:seq) requires CSingleMessageIsAbstractable(SHTDemarshallData(bytes)); requires AbstractifyCSingleMessageToSingleMessage(SHTDemarshallData(bytes)) == msg; requires CSingleMessageMarshallable(SHTDemarshallData(bytes)); requires bytes == MarshallServiceReply(reply, reserved_bytes); - requires |reserved_bytes| == 8; + requires |reserved_bytes| < 0x10_0000; ensures msg.SingleMessage?; ensures msg.seqno == reply.seqno; ensures msg.m.Reply?; @@ -565,8 +449,9 @@ module MarshallProof_i { assert msg.seqno == u as int; assert reply.seqno == msg.seqno; + var len_dst, val_dst, rest_dst := lemma_ParseValCorrectVByteArray(rest1, dstVal, EndPoint_grammar()); + // Prove some length relationships to show that our indices are within bounds - reveal_parse_Val(); calc ==> { true; rest0 == parse_Uint64(data).1; @@ -579,8 +464,7 @@ module MarshallProof_i { } calc ==> { true; - rest2 == parse_Uint64(rest1).1; - |rest2| == |rest1| - 8; + |rest2| == |rest1| - 8 - len_dst as int; } calc ==> { true; @@ -588,21 +472,29 @@ module MarshallProof_i { |rest3| == |rest2| - 8; } - // Prove that the 8 bytes of the case ID for CMessage is handled correctly - calc { - SeqByteToUint64(bytes[24..32]); - { reveal_parse_Val(); lemma_ParseUint64Offset(data, rest0, 16, 24); } - SeqByteToUint64(rest0[16..24]); - { reveal_parse_Val(); lemma_ParseUint64Offset(rest0, rest1, 8, 16); } - SeqByteToUint64(rest1[8..16]); - SeqByteToUint64(rest2[..8]); - 2; + assert rest0 == data[8..]; + assert rest1 == rest0[8..] == data[16..]; + assert rest2 == rest1[8 + len_dst..] == data[24 + len_dst..]; + assert rest3 == rest2[8..] == data[32 + len_dst..]; + + assert data[16..24] == Uint64ToSeqByte(|reserved_bytes| as uint64); + assert SeqByteToUint64(data[16..24]) == |reserved_bytes| as uint64 by { + lemma_BEByteSeqToInt_BEUintToSeqByte_invertability(); } + assert len_dst == SeqByteToUint64(rest1[..8]) == SeqByteToUint64(data[16..24]) == |reserved_bytes| as uint64; + assert len_dst as int == |reserved_bytes|; + + assert val_dst.b == rest1[8 .. 8 + len_dst]; + assert val_dst.b == data[24 .. 24 + len_dst] == reserved_bytes; + + assert rest2[..8] == data[24 + len_dst .. 32 + len_dst] == [0, 0, 0, 0, 0, 0, 0, 2]; assert mCaseId == 2; var keyVal, optValueVal, rest4 := lemma_ParseValCorrectTuple2(rest3, mCaseVal, g.cases[msgCaseId].t[2].cases[mCaseId]); var valCaseId, valCaseVal, rest5 := lemma_ParseValCorrectVCase(rest4, optValueVal, OptionalValue_grammar()); + assert rest4 == rest3[8..] == data[40 + len_dst..]; + // Prove that the key is handled correctly var key, rest_key := lemma_ParseValCorrectVUint64(rest3, keyVal, GUint64); calc { @@ -618,17 +510,23 @@ module MarshallProof_i { reply.k; } + calc { + rest4[..8]; + { lemma_SeqOffset(rest3, rest4, 8, 0, 8); } + rest3[8..16]; + { lemma_SeqOffset(rest2, rest3, 8, 8, 16); } + rest2[16..24]; + { lemma_SeqOffset(rest1, rest2, 8 + len_dst as int, 16, 24); } + rest1[24+len_dst as int..32+len_dst as int]; + { lemma_SeqOffset(rest0, rest1, 8, 24 + len_dst as int, 32 + len_dst as int); } + rest0[32+len_dst as int..40+len_dst as int]; + { lemma_SeqOffset(data, rest0, 8, 32 + len_dst as int, 40 + len_dst as int); } + data[40+len_dst as int..48+len_dst as int]; + } + // Handle the two subcases of OptionalValue if reply.ov.ValuePresent? { assert rest4[..8] == [ 0, 0, 0, 0, 0, 0, 0, 0]; - calc { - rest4[..8]; - rest3[8..16]; - rest2[16..24]; - rest1[24..32]; - rest0[32..40]; - data[40..48]; - } var byteLen, bytesVal, rest6 := lemma_ParseValCorrectVByteArray(rest5, valCaseVal, GByteArray); calc { byteLen; @@ -650,7 +548,6 @@ module MarshallProof_i { } else { assert rest4[..8] == [ 0, 0, 0, 0, 0, 0, 0, 1]; } - } else { assert bytes == [1]; reveal_parse_Val(); diff --git a/ironfleet/src/IronLockServer/.gitignore b/ironfleet/src/IronLockServer/.gitignore index 360ea127..a4588fea 100755 --- a/ironfleet/src/IronLockServer/.gitignore +++ b/ironfleet/src/IronLockServer/.gitignore @@ -1 +1,2 @@ .vs +Properties/ diff --git a/ironfleet/src/IronLockServer/Params.cs b/ironfleet/src/IronLockServer/Params.cs new file mode 100644 index 00000000..b49caee1 --- /dev/null +++ b/ironfleet/src/IronLockServer/Params.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text.RegularExpressions; + +namespace IronLockServer +{ + public class Params + { + private string serviceFileName; + private string privateKeyFileName; + private string localHostNameOrAddress; + private int localPort; + private bool verbose; + + public Params() + { + serviceFileName = ""; + privateKeyFileName = ""; + localHostNameOrAddress = ""; + localPort = 0; + } + + public string ServiceFileName { get { return serviceFileName; } } + public string PrivateKeyFileName { get { return privateKeyFileName; } } + public string LocalHostNameOrAddress { get { return localHostNameOrAddress; } } + public int LocalPort { get { return localPort; } } + public bool Verbose { get { return verbose; } } + + public bool Validate() + { + if (serviceFileName.Length == 0) { + Console.WriteLine("ERROR - Missing service parameter"); + return false; + } + if (privateKeyFileName.Length == 0) { + Console.WriteLine("ERROR - Missing private parameter"); + return false; + } + return true; + } + + public bool ProcessCommandLineArgument(string arg) + { + var pos = arg.IndexOf("="); + if (pos < 0) { + if (serviceFileName.Length == 0) { + serviceFileName = arg; + return true; + } + else if (privateKeyFileName.Length == 0) { + privateKeyFileName = arg; + return true; + } + else { + Console.WriteLine("ERROR - Invalid argument {0}", arg); + return false; + } + } + var key = arg.Substring(0, pos).ToLower(); + var value = arg.Substring(pos + 1); + return SetValue(key, value); + } + + private bool SetValue(string key, string value) + { + if (key == "addr") { + localHostNameOrAddress = value; + return true; + } + + if (key == "port") { + try { + localPort = Convert.ToInt32(value); + return true; + } + catch (Exception e) { + Console.WriteLine("ERROR - Could not convert port {0} to a number. Exception:\n{1}", value, e); + return false; + } + } + + if (key == "verbose") { + if (value == "false") { + verbose = false; + return true; + } + if (value == "true") { + verbose = true; + return true; + } + Console.WriteLine("ERROR - Invalid verbose value {0} - should be false or true", value); + return false; + } + + Console.WriteLine("ERROR - Invalid argument key {0}", key); + return false; + } + } +} diff --git a/ironfleet/src/IronLockServer/Program.cs b/ironfleet/src/IronLockServer/Program.cs index d7af2573..be5928d4 100755 --- a/ironfleet/src/IronLockServer/Program.cs +++ b/ironfleet/src/IronLockServer/Program.cs @@ -1,21 +1,81 @@ using IronfleetCommon; +using IronfleetIoFramework; using MathNet.Numerics.Distributions; using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Numerics; +using System.Text.Json; using System.Threading; namespace IronLockServer { class Program { + static void usage() + { + Console.Write(@" +Usage: dotnet IronLockServer.dll [key=value]... + + - file path of the service description + - file path of the private key + +Allowed keys: + addr - local host name or address to listen to (default: + whatever's specified in the private key file) + port - port to listen to (default: whatever's specified + in the private key file) + verbose - use verbose output (false or true, default: false) +"); + } + static void Main(string[] args) { + Console.WriteLine("IronLockServer program started"); + + Console.WriteLine("Processing command-line arguments"); + + Params ps = new Params(); + + foreach (var arg in args) + { + if (!ps.ProcessCommandLineArgument(arg)) { + usage(); + return; + } + } + + if (!ps.Validate()) { + usage(); + return; + } + + ServiceIdentity serviceIdentity = ServiceIdentity.ReadFromFile(ps.ServiceFileName); + if (serviceIdentity == null) { + return; + } + if (serviceIdentity.ServiceType != "IronLock") { + Console.Error.WriteLine("ERROR - Service described by {0} is of type {1}, not IronLock", ps.ServiceFileName, + serviceIdentity.ServiceType); + return; + } + + PrivateIdentity privateIdentity = PrivateIdentity.ReadFromFile(ps.PrivateKeyFileName); + if (privateIdentity == null) { + return; + } + + var nc = Native____Io__s_Compile.NetClient.Create(privateIdentity, ps.LocalHostNameOrAddress, ps.LocalPort, + serviceIdentity.Servers, ps.Verbose); + Dafny.ISequence[] serverPublicKeys = + serviceIdentity.Servers.Select(server => Dafny.Sequence.FromArray(server.PublicKey)).ToArray(); + var ironArgs = Dafny.Sequence>.FromArray(serverPublicKeys); + Profiler.Initialize(); Native____Io__s_Compile.Time.Initialize(); - Console.WriteLine("IronFleet program started."); Console.WriteLine("[[READY]]"); - Main__i_Compile.__default._Main(); + Main__i_Compile.__default.IronfleetMain(nc, ironArgs); Console.WriteLine("[[EXIT]]"); } } diff --git a/ironfleet/src/IronRSLClient/.gitignore b/ironfleet/src/IronRSLClient/.gitignore new file mode 100644 index 00000000..31e85980 --- /dev/null +++ b/ironfleet/src/IronRSLClient/.gitignore @@ -0,0 +1,2 @@ +.vs +Properties diff --git a/ironfleet/src/IronRSLClient/IronRSLClient.csproj b/ironfleet/src/IronRSLClient/IronRSLClient.csproj index a39c8a27..455ed178 100755 --- a/ironfleet/src/IronRSLClient/IronRSLClient.csproj +++ b/ironfleet/src/IronRSLClient/IronRSLClient.csproj @@ -3,7 +3,7 @@ net5.0 IronRSLClient - 1.0.2 + 1.0.3 Jay Lorch and Chris Hawblitzel Microsoft Corporation true diff --git a/ironfleet/src/IronRSLClient/RSLClient.cs b/ironfleet/src/IronRSLClient/RSLClient.cs index 7753cba0..9bd0b82a 100755 --- a/ironfleet/src/IronRSLClient/RSLClient.cs +++ b/ironfleet/src/IronRSLClient/RSLClient.cs @@ -9,40 +9,26 @@ namespace IronRSLClient { public class RSLClient { - IPEndPoint[] serverEps; - int myPort; - IoScheduler scheduler; + ServiceIdentity serviceIdentity; + byte[][] serverPublicKeys; + bool verbose; UInt64 nextSeqNum; int primaryServerIndex; + IoScheduler scheduler; - public RSLClient(IEnumerable i_serverEps, int i_myPort) - { - serverEps = Enumerable.ToArray(i_serverEps); - myPort = i_myPort; - var myEp = new IPEndPoint(IPAddress.Any, myPort); - scheduler = new IoScheduler(myEp, true, false); // onlyClient = true, verbose = false - primaryServerIndex = 0; - Start(); - } - - private void Start() + public RSLClient(ServiceIdentity i_serviceIdentity, string serviceName, bool i_verbose = false) { - // Create a random 64-bit initial sequence number. - - Random rng = new Random(); - byte[] seqNumBytes = new byte[8]; - rng.NextBytes(seqNumBytes); - nextSeqNum = IoEncoder.ExtractUInt64(seqNumBytes, 0); - - // Create connections to all endpoints, so that if any of them - // sends a reply we can receive it. Since we're in "only - // client" mode, we aren't listening on any port so we have to - // rely on outgoing connections for all communication. - - foreach (var serverEp in serverEps) - { - scheduler.Connect(serverEp); + serviceIdentity = i_serviceIdentity; + if (serviceIdentity.ServiceType != "IronRSL" + serviceName) { + Console.Error.WriteLine("Provided service identity has type {0}, not IronRSL{1}.", + serviceIdentity.ServiceType, serviceName); + throw new Exception("Wrong service type"); } + serverPublicKeys = serviceIdentity.Servers.Select(server => server.PublicKey).ToArray(); + verbose = i_verbose; + nextSeqNum = 0; + primaryServerIndex = 0; + scheduler = IoScheduler.CreateClient(serviceIdentity.Servers, verbose); } public byte[] SubmitRequest (byte[] request, bool verbose = false, int timeBeforeServerSwitchMs = 1000) @@ -58,15 +44,16 @@ public byte[] SubmitRequest (byte[] request, bool verbose = false, int timeBefor requestMessage = memStream.ToArray(); } - scheduler.SendPacket(serverEps[primaryServerIndex], requestMessage); + scheduler.SendPacket(serverPublicKeys[primaryServerIndex], requestMessage); if (verbose) { - Console.WriteLine("Sending a request with sequence number {0} to {1}", seqNum, serverEps[primaryServerIndex]); + Console.WriteLine("Sending a request with sequence number {0} to {1}", + seqNum, serviceIdentity.Servers[primaryServerIndex]); } while (true) { bool ok, timedOut; - IPEndPoint remote; + byte[] remote; byte[] replyBytes; scheduler.ReceivePacket(timeBeforeServerSwitchMs, out ok, out timedOut, out remote, out replyBytes); @@ -75,11 +62,11 @@ public byte[] SubmitRequest (byte[] request, bool verbose = false, int timeBefor } if (timedOut) { - primaryServerIndex = (primaryServerIndex + 1) % serverEps.Count(); + primaryServerIndex = (primaryServerIndex + 1) % serviceIdentity.Servers.Count(); if (verbose) { Console.WriteLine("#timeout; rotating to server {0}", primaryServerIndex); } - scheduler.SendPacket(serverEps[primaryServerIndex], requestMessage); + scheduler.SendPacket(serverPublicKeys[primaryServerIndex], requestMessage); continue; } @@ -93,18 +80,6 @@ public byte[] SubmitRequest (byte[] request, bool verbose = false, int timeBefor } UInt64 replySeqNum = IoEncoder.ExtractUInt64(replyBytes, 8); - if (seqNum < replySeqNum && (replySeqNum < 10 || seqNum > replySeqNum - 10)) { - // We apparently got unlucky and started with a sequence - // number that was too close to the last sequence number we - // used last time. So, advance past that sequence number - // and try again. - if (verbose) { - Console.WriteLine("Got reply for later sequence number {0}, apparently from an earlier run, so advancing from {1} to {2}", - replySeqNum, seqNum, replySeqNum + 1); - } - nextSeqNum = replySeqNum + 1; - return SubmitRequest(request, verbose, timeBeforeServerSwitchMs); - } if (replySeqNum != seqNum) { // This is a retransmission of a reply for an old sequence // number. Ignore it. diff --git a/ironfleet/src/IronRSLCounterClient/Client.cs b/ironfleet/src/IronRSLCounterClient/Client.cs index 670f304e..5efea8df 100755 --- a/ironfleet/src/IronRSLCounterClient/Client.cs +++ b/ironfleet/src/IronRSLCounterClient/Client.cs @@ -47,18 +47,20 @@ public class Client { public int id; public Params ps; + public ServiceIdentity serviceIdentity; - private Client(int i_id, Params i_ps) + private Client(int i_id, Params i_ps, ServiceIdentity i_serviceIdentity) { id = i_id; ps = i_ps; + serviceIdentity = i_serviceIdentity; } - static public IEnumerable StartThreads(Params ps) + static public IEnumerable StartThreads(Params ps, ServiceIdentity serviceIdentity) { - for (int i = 0; i < ps.numThreads; ++i) + for (int i = 0; i < ps.NumThreads; ++i) { - Client c = new Client(i, ps); + Client c = new Client(i, ps, serviceIdentity); Thread t = new Thread(c.Run); t.Start(); yield return t; @@ -67,7 +69,7 @@ static public IEnumerable StartThreads(Params ps) private void Run() { - RSLClient rslClient = new RSLClient(ps.serverEps, ps.clientPort + (int)id); + RSLClient rslClient = new RSLClient(serviceIdentity, "Counter", ps.Verbose); Thread.Sleep(3000); @@ -76,10 +78,10 @@ private void Run() var request = new IncrementRequest(); byte[] requestBytes = request.Encode(); var startTime = HiResTimer.Ticks; - byte[] replyBytes = rslClient.SubmitRequest(requestBytes, ps.verbose); + byte[] replyBytes = rslClient.SubmitRequest(requestBytes, ps.Verbose); var endTime = HiResTimer.Ticks; var reply = IncrementReply.Decode(replyBytes); - if (ps.verbose) { + if (ps.PrintReplies) { Console.WriteLine("Received increment reply with counter {0}", reply.counterValue); } Console.WriteLine("#req {0} {1} {2}", diff --git a/ironfleet/src/IronRSLCounterClient/IronRSLCounterClient.csproj b/ironfleet/src/IronRSLCounterClient/IronRSLCounterClient.csproj index 2bc74948..5fd6bc37 100755 --- a/ironfleet/src/IronRSLCounterClient/IronRSLCounterClient.csproj +++ b/ironfleet/src/IronRSLCounterClient/IronRSLCounterClient.csproj @@ -7,7 +7,7 @@ - + diff --git a/ironfleet/src/IronRSLCounterClient/Params.cs b/ironfleet/src/IronRSLCounterClient/Params.cs index 93a4c153..956a8dd2 100755 --- a/ironfleet/src/IronRSLCounterClient/Params.cs +++ b/ironfleet/src/IronRSLCounterClient/Params.cs @@ -5,85 +5,98 @@ namespace IronRSLCounterClient { public class Params { - public int seqNumReservationSize; - public int numThreads; - public ulong experimentDuration; - public IPEndPoint[] serverEps; - public int clientPort; - public bool verbose; + private string serviceFileName; + private int numThreads; + private ulong experimentDuration; + private bool verbose; + private bool printReplies; public Params() { - seqNumReservationSize = 1000; + serviceFileName = ""; numThreads = 1; experimentDuration = 60; - serverEps = new IPEndPoint[3] { IPEndPoint.Parse("127.0.0.1:4001"), - IPEndPoint.Parse("127.0.0.1:4002"), - IPEndPoint.Parse("127.0.0.1:4003") }; - clientPort = 6000; verbose = false; + printReplies = false; + } + + public string ServiceFileName { get { return serviceFileName; } } + public int NumThreads { get { return numThreads; } } + public ulong ExperimentDuration { get { return experimentDuration; } } + public bool PrintReplies { get { return printReplies; } } + public bool Verbose { get { return verbose; } } + + public bool Validate() + { + if (serviceFileName.Length == 0) { + Console.WriteLine("ERROR - Missing service parameter"); + return false; + } + return true; } public bool ProcessCommandLineArgument(string arg) { var pos = arg.IndexOf("="); if (pos < 0) { - Console.WriteLine("Invalid argument {0}", arg); - return false; + if (serviceFileName.Length == 0) { + serviceFileName = arg; + return true; + } + else { + Console.WriteLine("Invalid argument {0}", arg); + return false; + } } var key = arg.Substring(0, pos).ToLower(); var value = arg.Substring(pos + 1); return SetValue(key, value); } - private bool SetValue(string key, string value) + private bool SetBoolValue(string key, string value, ref bool p) { - try { - switch (key) { - case "clientport" : - clientPort = Convert.ToInt32(value); - return true; - - case "server1" : - serverEps[0] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[0] != null; - - case "server2" : - serverEps[1] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[1] != null; - - case "server3" : - serverEps[2] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[2] != null; + if (value == "false") { + p = false; + return true; + } + else if (value == "true") { + p = true; + return true; + } + else { + Console.WriteLine("ERROR - Invalid {0} value {1} - should be false or true", key, value); + return false; + } + } - case "nthreads" : - numThreads = Convert.ToInt32(value); - if (numThreads < 1) { - Console.WriteLine("Number of threads must be at least 1, so can't be {0}", numThreads); - return false; - } - return true; + private bool SetValue(string key, string value) + { + if (key == "verbose") { + return SetBoolValue(key, value, ref verbose); + } - case "duration" : - experimentDuration = Convert.ToUInt64(value); - return true; + if (key == "print") { + return SetBoolValue(key, value, ref printReplies); + } - case "verbose" : - if (value == "false") { - verbose = false; - return true; - } - if (value == "true") { - verbose = true; - return true; - } - Console.WriteLine("Invalid verbose value {0} - should be false or true", value); + if (key == "nthreads") { + try { + numThreads = Convert.ToInt32(value); + if (numThreads < 1) { + Console.WriteLine("Number of threads must be at least 1, so can't be {0}", numThreads); return false; + } } + catch (Exception e) { + Console.WriteLine("Could not parse number of threads {0} as a number. Exception:\n{1}", value, e); + return false; + } + return true; } - catch (Exception e) { - Console.WriteLine("Invalid value {0} for key {1}, leading to exception:\n{2}", value, key, e); - return false; + + if (key == "duration") { + experimentDuration = Convert.ToUInt64(value); + return true; } Console.WriteLine("Invalid argument key {0}", key); diff --git a/ironfleet/src/IronRSLCounterClient/Program.cs b/ironfleet/src/IronRSLCounterClient/Program.cs index 8fd1c659..029c0bed 100755 --- a/ironfleet/src/IronRSLCounterClient/Program.cs +++ b/ironfleet/src/IronRSLCounterClient/Program.cs @@ -1,4 +1,5 @@ using IronfleetCommon; +using IronfleetIoFramework; using System; using System.Linq; using System.Numerics; @@ -14,26 +15,15 @@ class Program static void usage() { Console.Write(@" -Usage: dotnet IronRSLCounterClient.dll [key=value]... +Usage: dotnet IronRSLCounterClient.dll [key=value]... -Allowed keys: - clientport - Port this client should bind to (default 6000) - server1 - IP address+port of first server (default 127.0.0.1:4001) - server2 - IP address+port of second server (default 127.0.0.1:4002) - server3 - IP address+port of third server (default 127.0.0.1:4003) - nthreads - number of client threads to run (default 1) - duration - duration of experiment in seconds (default 60) - verbose - print verbose output (false or true, default false) - -If nthreads > 1, then each thread will use a different port number, -using consecutive port numbers starting with clientport. + - file path of the service description -NOTE: Each client endpoint is expected to use strictly increasing -sequence numbers. So if you run this program multiple times, either: -(1) use a different clientip, (2) use a clientport that causes -different ports to be used, or (3) use an initialseqno greater than -any sequence number seen in previous runs (e.g., if the previous run -output #req100, use at least initialseqno=101) +Allowed keys: + nthreads - number of client threads to run (default 1) + duration - duration of experiment in seconds (default 60) + print - print replies (false or true, default false) + verbose - print verbose output (false or true, default false) "); } @@ -48,24 +38,35 @@ static void Main(string[] args) return; } } + + if (!ps.Validate()) + { + usage(); + return; + } + + var serviceIdentity = ServiceIdentity.ReadFromFile(ps.ServiceFileName); + if (serviceIdentity == null) { + return; + } HiResTimer.Initialize(); - if (ps.verbose) { - Console.WriteLine("Client process starting {0} threads running for {1} s...", ps.numThreads, ps.experimentDuration); + if (ps.Verbose) { + Console.WriteLine("Client process starting {0} threads running for {1} s...", ps.NumThreads, ps.ExperimentDuration); } Console.WriteLine("[[READY]]"); // Start the experiment - var threads = Client.StartThreads(ps).ToArray(); + var threads = Client.StartThreads(ps, serviceIdentity).ToArray(); - if (ps.experimentDuration == 0) + if (ps.ExperimentDuration == 0) { threads[0].Join(); } else { - Thread.Sleep((int)ps.experimentDuration * 1000); + Thread.Sleep((int)ps.ExperimentDuration * 1000); Console.Out.WriteLine("[[DONE]]"); Console.Out.Flush(); Environment.Exit(0); diff --git a/ironfleet/src/IronRSLCounterServer/Service.cs b/ironfleet/src/IronRSLCounterServer/Service.cs index f2fddf7f..bacbc580 100755 --- a/ironfleet/src/IronRSLCounterServer/Service.cs +++ b/ironfleet/src/IronRSLCounterServer/Service.cs @@ -13,7 +13,7 @@ private Service(UInt64 i_counter) counter = i_counter; } - public static string Name { get { return "counter"; } } + public static string Name { get { return "Counter"; } } public static Service Initialize() { diff --git a/ironfleet/src/IronRSLKVClient/Client.cs b/ironfleet/src/IronRSLKVClient/Client.cs index 641372bf..ed03ca1d 100755 --- a/ironfleet/src/IronRSLKVClient/Client.cs +++ b/ironfleet/src/IronRSLKVClient/Client.cs @@ -16,18 +16,20 @@ public class Client { public int id; public Params ps; + public ServiceIdentity serviceIdentity; - private Client(int i_id, Params i_ps) + private Client(int i_id, Params i_ps, ServiceIdentity i_serviceIdentity) { id = i_id; ps = i_ps; + serviceIdentity = i_serviceIdentity; } - static public IEnumerable StartThreads(Params ps) + static public IEnumerable StartThreads(Params ps, ServiceIdentity serviceIdentity) { - for (int i = 0; i < ps.numThreads; ++i) + for (int i = 0; i < ps.NumThreads; ++i) { - Client c = new Client(i, ps); + Client c = new Client(i, ps, serviceIdentity); Thread t = new Thread(c.Run); t.Start(); yield return t; @@ -45,7 +47,7 @@ private static KVRequest GetRandomRequest(Random rng, Params ps) string key = keyBuilder.ToString(); int reqTypeSelector = rng.Next(); - if (reqTypeSelector < ps.setFraction * Int32.MaxValue) { + if (reqTypeSelector < ps.SetFraction * Int32.MaxValue) { char v = (char)('A' + keySelector); StringBuilder valBuilder = new StringBuilder(); valBuilder.Append(v); @@ -54,19 +56,19 @@ private static KVRequest GetRandomRequest(Random rng, Params ps) valBuilder.Append(v); valBuilder.Append(rng.Next(100000)); string val = valBuilder.ToString(); - if (ps.verbose) { + if (ps.PrintRequestsAndReplies) { Console.WriteLine("Submitting set request for {0} => {1}", key, val); } return new KVSetRequest(key, val); } - else if (reqTypeSelector < (ps.setFraction + ps.deleteFraction) * Int32.MaxValue) { - if (ps.verbose) { + else if (reqTypeSelector < (ps.SetFraction + ps.DeleteFraction) * Int32.MaxValue) { + if (ps.PrintRequestsAndReplies) { Console.WriteLine("Submitting delete request for {0}", key); } return new KVDeleteRequest(key); } else { - if (ps.verbose) { + if (ps.PrintRequestsAndReplies) { Console.WriteLine("Submitting get request for {0}", key); } return new KVGetRequest(key); @@ -75,7 +77,7 @@ private static KVRequest GetRandomRequest(Random rng, Params ps) private void Run() { - RSLClient rslClient = new RSLClient(ps.serverEps, ps.clientPort + (int)id); + RSLClient rslClient = new RSLClient(serviceIdentity, "KV", ps.Verbose); Thread.Sleep(3000); @@ -86,11 +88,11 @@ private void Run() KVRequest request = GetRandomRequest(rng, ps); byte[] requestBytes = request.Encode(); var startTime = HiResTimer.Ticks; - byte[] replyBytes = rslClient.SubmitRequest(requestBytes, ps.verbose); + byte[] replyBytes = rslClient.SubmitRequest(requestBytes, ps.Verbose); var endTime = HiResTimer.Ticks; KVReply reply = KVReply.Decode(replyBytes, 0); - if (ps.verbose) { + if (ps.PrintRequestsAndReplies) { Console.WriteLine("Received reply of type {0}", reply.ReplyType); if (reply is KVGetFoundReply gfr) { Console.WriteLine("Value obtained for get was {0}", gfr.Val); diff --git a/ironfleet/src/IronRSLKVClient/IronRSLKVClient.csproj b/ironfleet/src/IronRSLKVClient/IronRSLKVClient.csproj index 0668de65..52d7e1b7 100755 --- a/ironfleet/src/IronRSLKVClient/IronRSLKVClient.csproj +++ b/ironfleet/src/IronRSLKVClient/IronRSLKVClient.csproj @@ -7,8 +7,8 @@ - + diff --git a/ironfleet/src/IronRSLKVClient/Params.cs b/ironfleet/src/IronRSLKVClient/Params.cs index 24c4f571..58435b3c 100755 --- a/ironfleet/src/IronRSLKVClient/Params.cs +++ b/ironfleet/src/IronRSLKVClient/Params.cs @@ -5,97 +5,126 @@ namespace IronRSLKVClient { public class Params { - public int seqNumReservationSize; - public int numThreads; - public ulong experimentDuration; - public IPEndPoint[] serverEps; - public int clientPort; - public double setFraction; - public double deleteFraction; - public bool verbose; + private string serviceFileName; + private int numThreads; + private ulong experimentDuration; + private double setFraction; + private double deleteFraction; + private bool verbose; + private bool printRequestsAndReplies; public Params() { - seqNumReservationSize = 1000; + serviceFileName = ""; numThreads = 1; experimentDuration = 60; - serverEps = new IPEndPoint[3] { IPEndPoint.Parse("127.0.0.1:4001"), - IPEndPoint.Parse("127.0.0.1:4002"), - IPEndPoint.Parse("127.0.0.1:4003") }; - clientPort = 6000; setFraction = 0.05; deleteFraction = 0.25; verbose = false; + printRequestsAndReplies = false; + } + + public string ServiceFileName { get { return serviceFileName; } } + public int NumThreads { get { return numThreads; } } + public ulong ExperimentDuration { get { return experimentDuration; } } + public double SetFraction { get { return setFraction; } } + public double DeleteFraction { get { return deleteFraction; } } + public bool PrintRequestsAndReplies { get { return printRequestsAndReplies; } } + public bool Verbose { get { return verbose; } } + + public bool Validate() + { + if (serviceFileName.Length == 0) { + Console.WriteLine("ERROR - Missing service parameter"); + return false; + } + return true; } public bool ProcessCommandLineArgument(string arg) { var pos = arg.IndexOf("="); if (pos < 0) { - Console.WriteLine("Invalid argument {0}", arg); - return false; + if (serviceFileName.Length == 0) { + serviceFileName = arg; + return true; + } + else { + Console.WriteLine("Invalid argument {0}", arg); + return false; + } } var key = arg.Substring(0, pos).ToLower(); var value = arg.Substring(pos + 1); return SetValue(key, value); } - private bool SetValue(string key, string value) + private bool SetBoolValue(string key, string value, ref bool p) { - try { - switch (key) { - case "clientport" : - clientPort = Convert.ToInt32(value); - return true; - - case "server1" : - serverEps[0] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[0] != null; - - case "server2" : - serverEps[1] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[1] != null; - - case "server3" : - serverEps[2] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[2] != null; + if (value == "false") { + p = false; + return true; + } + else if (value == "true") { + p = true; + return true; + } + else { + Console.WriteLine("ERROR - Invalid {0} value {1} - should be false or true", key, value); + return false; + } + } - case "nthreads" : - numThreads = Convert.ToInt32(value); - if (numThreads < 1) { - Console.WriteLine("Number of threads must be at least 1, so can't be {0}", numThreads); - return false; - } - return true; + private bool SetValue(string key, string value) + { + if (key == "verbose") { + return SetBoolValue(key, value, ref verbose); + } - case "duration" : - experimentDuration = Convert.ToUInt64(value); - return true; + if (key == "print") { + return SetBoolValue(key, value, ref printRequestsAndReplies); + } - case "setfraction" : - setFraction = Convert.ToDouble(value); - return true; + if (key == "nthreads") { + try { + numThreads = Convert.ToInt32(value); + if (numThreads < 1) { + Console.WriteLine("Number of threads must be at least 1, so can't be {0}", numThreads); + return false; + } + return true; + } + catch (Exception e) { + Console.WriteLine("Could not parse number of threads {0} as a number. Exception:\n{1}", value, e); + return false; + } + } - case "deletefraction" : - deleteFraction = Convert.ToDouble(value); - return true; + if (key == "duration") { + experimentDuration = Convert.ToUInt64(value); + return true; + } - case "verbose" : - if (value == "false") { - verbose = false; - return true; - } - if (value == "true") { - verbose = true; - return true; - } - Console.WriteLine("Invalid verbose value {0} - should be false or true", value); - return false; + if (key == "setfraction") { + try { + setFraction = Convert.ToDouble(value); + return true; + } + catch (Exception e) { + Console.WriteLine("Could not parse set fraction {0} as a number. Exception:\n{1}", value, e); + return false; } } - catch (Exception e) { - Console.WriteLine("Invalid value {0} for key {1}, leading to exception:\n{2}", value, key, e); - return false; + + if (key == "deletefraction") { + try { + deleteFraction = Convert.ToDouble(value); + return true; + } + catch (Exception e) { + Console.WriteLine("Could not parse delete fraction {0} as a number. Exception:\n{1}", value, e); + return false; + } } Console.WriteLine("Invalid argument key {0}", key); diff --git a/ironfleet/src/IronRSLKVClient/Program.cs b/ironfleet/src/IronRSLKVClient/Program.cs index 0b8aceb2..800f6794 100755 --- a/ironfleet/src/IronRSLKVClient/Program.cs +++ b/ironfleet/src/IronRSLKVClient/Program.cs @@ -1,4 +1,5 @@ using IronfleetCommon; +using IronfleetIoFramework; using System; using System.Linq; using System.Numerics; @@ -14,26 +15,17 @@ class Program static void usage() { Console.Write(@" -Usage: dotnet IronRSLKVClient.dll [key=value]... +Usage: dotnet IronRSLKVClient.dll [key=value]... + + - file path of the service description Allowed keys: - clientport - Port this client should bind to (default 6000) - server1 - IP address+port of first server (default 127.0.0.1:4001) - server2 - IP address+port of second server (default 127.0.0.1:4002) - server3 - IP address+port of third server (default 127.0.0.1:4003) nthreads - number of client threads to run (default 1) duration - duration of experiment in seconds (default 60) setfraction - fraction of requests that are sets (default 0.25) deletefraction - fraction of requests that are deletes (default 0.05) + print - print requests and replies (false or true, default false) verbose - print verbose output (false or true, default false) - -If nthreads > 1, then each thread will use a different port number, -using consecutive port numbers starting with clientport. - -NOTE: Each client endpoint is expected to use strictly increasing -sequence numbers, which it tracks by writing files named port. -So if you run this program multiple times using the same client -address and port, make sure you run from the same directory. "); } @@ -49,23 +41,33 @@ static void Main(string[] args) } } + if (!ps.Validate()) { + usage(); + return; + } + + var serviceIdentity = ServiceIdentity.ReadFromFile(ps.ServiceFileName); + if (serviceIdentity == null) { + return; + } + HiResTimer.Initialize(); - if (ps.verbose) { - Console.WriteLine("Client process starting {0} threads running for {1} s...", ps.numThreads, ps.experimentDuration); + if (ps.Verbose) { + Console.WriteLine("Client process starting {0} threads running for {1} s...", ps.NumThreads, ps.ExperimentDuration); } Console.WriteLine("[[READY]]"); // Start the experiment - var threads = Client.StartThreads(ps).ToArray(); + var threads = Client.StartThreads(ps, serviceIdentity).ToArray(); - if (ps.experimentDuration == 0) + if (ps.ExperimentDuration == 0) { threads[0].Join(); } else { - Thread.Sleep((int)ps.experimentDuration * 1000); + Thread.Sleep((int)ps.ExperimentDuration * 1000); Console.Out.WriteLine("[[DONE]]"); Console.Out.Flush(); Environment.Exit(0); diff --git a/ironfleet/src/IronRSLKVServer/Service.cs b/ironfleet/src/IronRSLKVServer/Service.cs index 95f87b72..fbcc36b9 100755 --- a/ironfleet/src/IronRSLKVServer/Service.cs +++ b/ironfleet/src/IronRSLKVServer/Service.cs @@ -12,7 +12,7 @@ public class Service { Dictionary kvStore; - public static string Name { get { return "key-value store"; } } + public static string Name { get { return "KV"; } } private Service() { diff --git a/ironfleet/src/IronSHTClient/Client.cs b/ironfleet/src/IronSHTClient/Client.cs index ea3b3420..12d2836f 100755 --- a/ironfleet/src/IronSHTClient/Client.cs +++ b/ironfleet/src/IronSHTClient/Client.cs @@ -66,13 +66,13 @@ public class GetRequestMessage : MessageBase { public byte[] Value { get; set; } public ulong seqNum; - public ulong myaddr; + public byte[] myPublicKey; public ulong key; - public GetRequestMessage(ulong seqNum, ulong myaddr, ulong key) : base(0) + public GetRequestMessage(ulong seqNum, byte[] myPublicKey, ulong key) : base(0) { this.seqNum = seqNum; - this.myaddr = myaddr; + this.myPublicKey = myPublicKey; this.key = key; } @@ -92,7 +92,7 @@ private byte[] Encode(bool retrans = false) { this.EncodeUlong(memStream, (ulong)0); // case for CSingleMessage this.EncodeUlong(memStream, (ulong)seqNum); // field one in CSingleMessage - this.EncodeUlong(memStream, (ulong)this.myaddr); // field two in CSingleMessage + this.EncodeBytes(memStream, myPublicKey); // field two in CSingleMessage this.EncodeUlong(memStream, (ulong)0); // case for GetRequest this.EncodeUlong(memStream, key); // field one in GetRequest @@ -104,15 +104,15 @@ private byte[] Encode(bool retrans = false) public class SetRequestMessage : MessageBase { public ulong seqNum; - public ulong myaddr; + public byte[] myPublicKey; public ulong key; public ulong sizeValue; public Random rnd; - public SetRequestMessage(ulong seqNum, ulong myaddr, ulong key, ulong sizeValue) : base(0) + public SetRequestMessage(ulong seqNum, byte[] myPublicKey, ulong key, ulong sizeValue) : base(0) { this.seqNum = seqNum; - this.myaddr = myaddr; + this.myPublicKey = myPublicKey; this.key = key; this.sizeValue = sizeValue; rnd = new Random(); @@ -138,7 +138,7 @@ private byte[] Encode(bool retrans = false) this.EncodeUlong(memStream, (ulong)0); // case for CSingleMessage this.EncodeUlong(memStream, (ulong)seqNum); // field one in CSingleMessage - this.EncodeUlong(memStream, (ulong)this.myaddr); // field two in CSingleMessage + this.EncodeBytes(memStream, this.myPublicKey); // field two in CSingleMessage this.EncodeUlong(memStream, (ulong)1); // case for SetRequest this.EncodeUlong(memStream, key); // field one in SetRequest this.EncodeUlong(memStream, (ulong)0); // case for OptionalValue @@ -151,14 +151,14 @@ private byte[] Encode(bool retrans = false) public class ShardRequestMessage : MessageBase { public ulong seqNum; - public ulong myaddr; + public byte[] myPublicKey; public ulong k_lo, k_hi; - public ulong recipient; + public byte[] recipient; - public ShardRequestMessage(ulong seqNum, ulong myaddr, ulong k_lo, ulong k_hi, ulong recipient) : base(0) + public ShardRequestMessage(ulong seqNum, byte[] myPublicKey, ulong k_lo, ulong k_hi, byte[] recipient) : base(0) { this.seqNum = seqNum; - this.myaddr = myaddr; + this.myPublicKey = myPublicKey; this.k_lo = k_lo; this.k_hi = k_hi; this.recipient = recipient; @@ -180,7 +180,7 @@ private byte[] Encode(bool retrans = false) { this.EncodeUlong(memStream, (ulong)0); // case for CSingleMessage this.EncodeUlong(memStream, (ulong)seqNum); // field one in CSingleMessage - this.EncodeUlong(memStream, (ulong)this.myaddr); // field two in CSingleMessage + this.EncodeBytes(memStream, this.myPublicKey); // field two in CSingleMessage this.EncodeUlong(memStream, (ulong)4); // case for ShardRequest @@ -191,7 +191,7 @@ private byte[] Encode(bool retrans = false) this.EncodeUlong(memStream, (ulong)k_hi); // khi // encode the recipient - this.EncodeUlong(memStream, this.recipient); + this.EncodeBytes(memStream, this.recipient); return memStream.ToArray(); } @@ -202,12 +202,10 @@ public class AckMessage : MessageBase { public byte[] Value { get; set; } public ulong seqNum; - public ulong myaddr; - public AckMessage(ulong seqNum, ulong myaddr) : base(0) + public AckMessage(ulong seqNum) : base(0) { this.seqNum = seqNum; - this.myaddr = myaddr; } public override byte[] ToBigEndianByteArray() @@ -248,39 +246,41 @@ public class Client public int id; public Params ps; public IoScheduler scheduler; + public ServiceIdentity serviceIdentity; - private Client(int i_id, Params i_ps) + private Client(int i_id, Params i_ps, ServiceIdentity i_serviceIdentity) { id = i_id; ps = i_ps; + serviceIdentity = i_serviceIdentity; } - static public IEnumerable StartSetupThreads(Params ps) + static public IEnumerable StartSetupThreads(Params ps, ServiceIdentity serviceIdentity) { - if (ps.numThreads < 0) + if (ps.NumThreads < 0) { throw new ArgumentException("count is less than 1", "count"); } - for (int i = 0; i < ps.numSetupThreads; ++i) + for (int i = 0; i < ps.NumSetupThreads; ++i) { - var c = new Client(i, ps); + var c = new Client(i, ps, serviceIdentity); Thread t = new Thread(c.Setup); t.Start(); yield return t; } } - static public IEnumerable StartExperimentThreads(Params ps) + static public IEnumerable StartExperimentThreads(Params ps, ServiceIdentity serviceIdentity) { - if (ps.numThreads < 0) + if (ps.NumThreads < 0) { throw new ArgumentException("count is less than 1", "count"); } - for (int i = 0; i < ps.numThreads; ++i) + for (int i = 0; i < ps.NumThreads; ++i) { - var c = new Client(i, ps); + var c = new Client(i, ps, serviceIdentity); Thread t = new Thread(c.Experiment); t.Start(); yield return t; @@ -295,24 +295,22 @@ public string ByteArrayToString(byte[] ba) public void Setup() { - IPEndPoint myEndpoint = new IPEndPoint(IPAddress.Any, ps.clientPort + (int)id); - scheduler = new IoScheduler(myEndpoint, false /* only client */, false /* verbose */); - - ulong myaddr = EncodeIpPort(myEndpoint); + scheduler = IoScheduler.CreateClient(serviceIdentity.Servers, ps.Verbose); + byte[] myPublicKey = IoScheduler.GetCertificatePublicKey(scheduler.MyCert); int serverIdx = 0; ulong seqNum = 0; ulong requestKey; - for (requestKey = 0; requestKey < (ulong)ps.numKeys; ++requestKey) + for (requestKey = 0; requestKey < (ulong)ps.NumKeys; ++requestKey) { seqNum++; - var msg = new SetRequestMessage(seqNum, myaddr, requestKey, (ulong)ps.valueSize); + var msg = new SetRequestMessage(seqNum, myPublicKey, requestKey, (ulong)ps.ValueSize); - if (ps.verbose) { + if (ps.Verbose) { Console.WriteLine("Sending set request message with seq {0}, key {1} to server {2}", seqNum, requestKey, serverIdx); } - this.Send(msg, ps.serverEps[serverIdx]); + this.Send(msg, serviceIdentity.Servers[serverIdx].PublicKey); // Wait for the reply var receivedReply = false; @@ -321,9 +319,9 @@ public void Setup() byte[] bytes = Receive(); var endTime = HiResTimer.Ticks; if (bytes == null) { - //serverIdx = (serverIdx + 1) % ps.serverEps.Count(); + //serverIdx = (serverIdx + 1) % serviceIdentity.Servers.Count(); Console.WriteLine("#timeout; retrying {0}", serverIdx); - this.Send(msg, ps.serverEps[serverIdx]); + this.Send(msg, serviceIdentity.Servers[serverIdx].PublicKey); continue; } //Trace("Got the following reply:" + ByteArrayToString(bytes)); @@ -331,43 +329,51 @@ public void Setup() if (bytes.Length == 16) { //Ignore acks - if (ps.verbose) { + if (ps.Verbose) { Console.WriteLine("Received ack"); } } else if (bytes.Length >= 48) { var replySeqNum = ExtractBE64(bytes, offset: 8); - if (ps.verbose) { + if (ps.Verbose) { Console.WriteLine("Reply sequence number : {0}", replySeqNum); } - var ack_msg = new AckMessage(replySeqNum, myaddr); + var ack_msg = new AckMessage(replySeqNum); - if (ps.verbose) { + if (ps.Verbose) { Console.Out.WriteLine("Client {0}: Sending an ack with sequence number {1} to {2}", - id, replySeqNum, ps.serverEps[serverIdx]); - } - this.Send(ack_msg, ps.serverEps[serverIdx]); - - var replyKey = ExtractBE64(bytes, offset: 32); - // Need to send an ack - if (ps.verbose) { - Console.WriteLine("Request key : {0}", requestKey); - Console.WriteLine("Reply key : {0}", replyKey); - Console.WriteLine("Got packet length: {0}", bytes.Length); + id, replySeqNum, serviceIdentity.Servers[serverIdx]); } + this.Send(ack_msg, serviceIdentity.Servers[serverIdx].PublicKey); - if (replyKey == requestKey) - { - receivedReply = true; + int publicKeyLength = Convert.ToInt32(ExtractBE64(bytes, offset: 16)); + if (bytes.Length < publicKeyLength + 40) { + Console.WriteLine("ERROR - Received too-short message (size {0} not long enough for public key of length {1})", + bytes.Length, publicKeyLength); + } + else { + var replyKey = ExtractBE64(bytes, offset: 32 + publicKeyLength); + // Need to send an ack + if (ps.Verbose) { + Console.WriteLine("Request key : {0}", requestKey); + Console.WriteLine("Reply key : {0}", replyKey); + Console.WriteLine("Got packet length: {0}", bytes.Length); + } + + if (replyKey == requestKey) + { + receivedReply = true; + } } } } } } - private void ReceiveReply(int serverIdx, ulong myaddr, ulong requestKey, bool receiveOnlyAcks) + private void ReceiveReply(int serverIdx, byte[] myPublicKey, ulong requestKey, bool receiveOnlyAcks, + bool expectRedirect = false) { var receivedReply = false; while (!receivedReply) @@ -381,7 +387,7 @@ private void ReceiveReply(int serverIdx, ulong myaddr, ulong requestKey, bool re if (bytes.Length == 16) { var replySeqNum = ExtractBE64(bytes, offset: 8); - if (ps.verbose) { + if (ps.Verbose) { Console.WriteLine("Received an ack for sequence number {0}", replySeqNum); } @@ -393,27 +399,34 @@ private void ReceiveReply(int serverIdx, ulong myaddr, ulong requestKey, bool re else if (bytes.Length >= 48) { var replySeqNum = ExtractBE64(bytes, offset: 8); - var ack_msg = new AckMessage(replySeqNum, myaddr); - this.Send(ack_msg, ps.serverEps[serverIdx]); + var ack_msg = new AckMessage(replySeqNum); + this.Send(ack_msg, serviceIdentity.Servers[serverIdx].PublicKey); - var cmessage_case = ExtractBE64(bytes, offset: 24); - if (ps.verbose) { - Console.WriteLine("Received Message Case {0}", cmessage_case); + int publicKeyLength = Convert.ToInt32(ExtractBE64(bytes, offset: 16)); + if (bytes.Length < publicKeyLength + 40) { + Console.WriteLine("ERROR - Received too-short message (size {0} not long enough for public key of length {1})", + bytes.Length, publicKeyLength); } - - var replyKey = ExtractBE64(bytes, offset: 32); - if (ps.verbose) { - if (cmessage_case == 2) { - Console.WriteLine("Received a reply with key {0}", replyKey); + else { + var cmessage_case = ExtractBE64(bytes, offset: 24 + publicKeyLength); + if (ps.Verbose) { + Console.WriteLine("Received Message Case {0}", cmessage_case); } - else if (cmessage_case == 3) { - Console.WriteLine("Received a redirect for key {0}", replyKey); + + var replyKey = ExtractBE64(bytes, offset: 32 + publicKeyLength); + if (ps.Verbose) { + if (cmessage_case == 2) { + Console.WriteLine("Received a reply with key {0}", replyKey); + } + else if (cmessage_case == 3) { + Console.WriteLine("Received a redirect for key {0}", replyKey); + } } - } - if (replyKey == requestKey && cmessage_case == 2) - { - receivedReply = true; + if (replyKey == requestKey && (expectRedirect ? cmessage_case == 3 : cmessage_case == 2)) + { + receivedReply = true; + } } } } @@ -421,48 +434,55 @@ private void ReceiveReply(int serverIdx, ulong myaddr, ulong requestKey, bool re public void Experiment() { - ulong requestKey = 150; + ulong requestKey; int serverIdx = 0; - IPEndPoint myEndpoint = new IPEndPoint(IPAddress.Any, ps.clientPort + ps.numSetupThreads + (int)id); - scheduler = new IoScheduler(myEndpoint, false /* only client */, false /* verbose */); - ulong myaddr = EncodeIpPort(myEndpoint); + scheduler = IoScheduler.CreateClient(serviceIdentity.Servers, ps.Verbose); + + byte[] myPublicKey = IoScheduler.GetCertificatePublicKey(scheduler.MyCert); ulong seqNum = 0; // Test the functionality of the Sharding - if (ps.workload == 'f') + if (ps.Workload == 'f') { - ulong k_lo = 100; - ulong k_hi = 200; - var recipient = EncodeIpPort(ps.serverEps[(serverIdx + 1) % ps.serverEps.Count()]); + // A delegation can delegate at most 61 keys, so make sure + // there can't be that many keys in the range by having the + // range be smaller than 61. + ulong k_lo = 125; + ulong k_hi = 175; + requestKey = 150; + var recipient = serviceIdentity.Servers[(serverIdx + 1) % serviceIdentity.Servers.Count()]; seqNum++; - var msg = new GetRequestMessage(seqNum, myaddr, requestKey); - this.Send(msg, ps.serverEps[serverIdx]); - ReceiveReply(serverIdx, myaddr, requestKey, false); + var msg = new GetRequestMessage(seqNum, myPublicKey, requestKey); + this.Send(msg, serviceIdentity.Servers[serverIdx].PublicKey); + ReceiveReply(serverIdx, myPublicKey, requestKey, false); seqNum++; Console.WriteLine("Sending a Shard request with a sequence number {0}", seqNum); - var shardMessage = new ShardRequestMessage(seqNum, myaddr, k_lo, k_hi, recipient); - this.Send(shardMessage, ps.serverEps[serverIdx]); - ReceiveReply(serverIdx, myaddr, requestKey, true); + var shardMessage = new ShardRequestMessage(seqNum, myPublicKey, k_lo, k_hi, recipient.PublicKey); + this.Send(shardMessage, serviceIdentity.Servers[serverIdx].PublicKey); + ReceiveReply(serverIdx, myPublicKey, requestKey, true); Thread.Sleep(5000); Console.WriteLine("Sending a GetRequest after a Shard, expect a redirect"); seqNum++; - msg = new GetRequestMessage(seqNum, myaddr, requestKey); - this.Send(msg, ps.serverEps[(serverIdx + 0) % ps.serverEps.Count()]); - ReceiveReply(serverIdx, myaddr, requestKey, false); + msg = new GetRequestMessage(seqNum, myPublicKey, requestKey); + this.Send(msg, serviceIdentity.Servers[(serverIdx + 0) % serviceIdentity.Servers.Count()].PublicKey); + ReceiveReply(serverIdx, myPublicKey, requestKey, false, expectRedirect: true); Thread.Sleep(5000); Console.WriteLine("Sending a GetRequest after a Shard to the second host, expect a reply"); - seqNum++; - msg = new GetRequestMessage(seqNum, myaddr, requestKey); - this.Send(msg, ps.serverEps[(serverIdx + 1) % ps.serverEps.Count()]); - ReceiveReply(serverIdx, myaddr, requestKey, false); + // Must use sequence number 1 since this is the first message + // to this server. + msg = new GetRequestMessage(1, myPublicKey, requestKey); + this.Send(msg, serviceIdentity.Servers[(serverIdx + 1) % serviceIdentity.Servers.Count()].PublicKey); + ReceiveReply((serverIdx + 1) % serviceIdentity.Servers.Count(), myPublicKey, requestKey, false); + + Console.WriteLine("Successfully received reply"); return; } @@ -472,20 +492,20 @@ public void Experiment() { seqNum++; var receivedReply = false; - requestKey = seqNum % (ulong)ps.numKeys; + requestKey = seqNum % (ulong)ps.NumKeys; MessageBase msg; - if (ps.workload == 'g') + if (ps.Workload == 'g') { - msg = new GetRequestMessage(seqNum, myaddr, requestKey); + msg = new GetRequestMessage(seqNum, myPublicKey, requestKey); } else { - msg = new SetRequestMessage(seqNum, myaddr, requestKey, (ulong)ps.valueSize); + msg = new SetRequestMessage(seqNum, myPublicKey, requestKey, (ulong)ps.ValueSize); } var startTime = HiResTimer.Ticks; - this.Send(msg, ps.serverEps[serverIdx]); + this.Send(msg, serviceIdentity.Servers[serverIdx].PublicKey); // Wait for the reply @@ -493,48 +513,59 @@ public void Experiment() { byte[] bytes = Receive(); if (bytes == null) { - //serverIdx = (serverIdx + 1) % ps.serverEps.Count(); + //serverIdx = (serverIdx + 1) % serviceIdentity.Servers.Count(); //Console.WriteLine("#timeout; rotating to server {0}", serverIdx); Console.WriteLine("#timeout; retrying {0}", serverIdx); - this.Send(msg, ps.serverEps[serverIdx]); + this.Send(msg, serviceIdentity.Servers[serverIdx].PublicKey); continue; } var endTime = HiResTimer.Ticks; - + if (bytes.Length == 16) { //Ignore acks } - else if (bytes.Length >= 56) + else if (bytes.Length >= 56) { var replySeqNum = ExtractBE64(bytes, offset: 8); - if (ps.verbose) { + if (ps.Verbose) + { Console.WriteLine("Reply sequence number : {0}", replySeqNum); Console.WriteLine("Client {0}: Sending an ack with sequence number {1} to {2}", - id, replySeqNum, ps.serverEps[serverIdx]); + id, replySeqNum, serviceIdentity.Servers[serverIdx]); } if (seqNum % 100 == 0) { - var ack_msg = new AckMessage(replySeqNum, myaddr); - this.Send(ack_msg, ps.serverEps[serverIdx]); + var ack_msg = new AckMessage(replySeqNum); + this.Send(ack_msg, serviceIdentity.Servers[serverIdx].PublicKey); } - var replyKey = ExtractBE64(bytes, offset: 32); - // Need to send an ack - if (ps.verbose) { - Console.WriteLine("Request key : {0}", requestKey); - Console.WriteLine("Reply key : {0}", replyKey); - Console.WriteLine("Got packet length: {0}", bytes.Length); + int publicKeyLength = Convert.ToInt32(ExtractBE64(bytes, offset: 16)); + if (bytes.Length < publicKeyLength + 40) + { + Console.WriteLine("ERROR - Received too-short message (size {0} not long enough for public key of length {1})", + bytes.Length, publicKeyLength); } - - // key is the same as the sequence number - if (replyKey == requestKey) + else { - receivedReply = true; - Console.WriteLine("#req {0} {1} {2}", - id, - seqNum, - HiResTimer.TicksToMilliseconds(endTime - startTime)); + var replyKey = ExtractBE64(bytes, offset: 32 + publicKeyLength); + // Need to send an ack + if (ps.Verbose) + { + Console.WriteLine("Request key : {0}", requestKey); + Console.WriteLine("Reply key : {0}", replyKey); + Console.WriteLine("Got packet length: {0}", bytes.Length); + } + + // key is the same as the sequence number + if (replyKey == requestKey) + { + receivedReply = true; + Console.WriteLine("#req {0} {1} {2}", + id, + seqNum, + HiResTimer.TicksToMilliseconds(endTime - startTime)); + } } } else { @@ -544,7 +575,7 @@ public void Experiment() } } - private void Send(MessageBase msg, System.Net.IPEndPoint remote) + private void Send(MessageBase msg, byte[] remote) { var a = msg.ToBigEndianByteArray(); if (!scheduler.SendPacket(remote, a)) @@ -557,7 +588,7 @@ private byte[] Receive() { bool ok; bool timedOut; - IPEndPoint remote; + byte[] remote; byte[] buffer; scheduler.ReceivePacket(1000, out ok, out timedOut, out remote, out buffer); return buffer; diff --git a/ironfleet/src/IronSHTClient/IronfleetClient.sln b/ironfleet/src/IronSHTClient/IronfleetClient.sln deleted file mode 100755 index 6d867f7a..00000000 --- a/ironfleet/src/IronSHTClient/IronfleetClient.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronfleetClient", "IronfleetClient\IronfleetClient.csproj", "{C43F5717-69F7-465E-887D-6FC956E854FC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C43F5717-69F7-465E-887D-6FC956E854FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C43F5717-69F7-465E-887D-6FC956E854FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C43F5717-69F7-465E-887D-6FC956E854FC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C43F5717-69F7-465E-887D-6FC956E854FC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/ironfleet/src/IronSHTClient/Params.cs b/ironfleet/src/IronSHTClient/Params.cs index f773881e..67309154 100755 --- a/ironfleet/src/IronSHTClient/Params.cs +++ b/ironfleet/src/IronSHTClient/Params.cs @@ -5,43 +5,57 @@ namespace IronSHTClient { public class Params { - public int seqNumReservationSize; - public int numSetupThreads; - public int numThreads; - public ulong experimentDuration; - public int clientPort; - public IPEndPoint[] serverEps; - public ulong initialSeqNum; - public double setFraction; - public double deleteFraction; - public char workload; - public int numKeys; - public int valueSize; - public bool verbose; + private string serviceFileName; + private int numSetupThreads; + private int numThreads; + private ulong experimentDuration; + private char workload; + private int numKeys; + private int valueSize; + private bool verbose; public Params() { - seqNumReservationSize = 1000; + serviceFileName = ""; numSetupThreads = 1; numThreads = 1; experimentDuration = 60; - serverEps = new IPEndPoint[3] { IPEndPoint.Parse("127.0.0.1:4001"), - IPEndPoint.Parse("127.0.0.1:4002"), - IPEndPoint.Parse("127.0.0.1:4003") }; - clientPort = 6000; - initialSeqNum = 0; workload = 's'; numKeys = 1000; valueSize = 1000; verbose = false; } + public string ServiceFileName { get { return serviceFileName; } } + public int NumSetupThreads { get { return numSetupThreads; } } + public int NumThreads { get { return numThreads; } } + public ulong ExperimentDuration { get { return experimentDuration; } } + public char Workload { get { return workload; } } + public int NumKeys { get { return numKeys; } } + public int ValueSize { get { return valueSize; } } + public bool Verbose { get { return verbose; } } + + public bool Validate() + { + if (serviceFileName.Length == 0) { + Console.WriteLine("ERROR - Missing service parameter"); + return false; + } + return true; + } + public bool ProcessCommandLineArgument(string arg) { var pos = arg.IndexOf("="); if (pos < 0) { - Console.WriteLine("Invalid argument {0}", arg); - return false; + if (serviceFileName.Length == 0) { + serviceFileName = arg; + return true; + } + else { + Console.WriteLine("Invalid argument {0}", arg); + return false; + } } var key = arg.Substring(0, pos).ToLower(); var value = arg.Substring(pos + 1); @@ -50,76 +64,64 @@ public bool ProcessCommandLineArgument(string arg) private bool SetValue(string key, string value) { - try { - switch (key) { - case "clientport" : - clientPort = Convert.ToInt32(value); - return true; - - case "server1" : - serverEps[0] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[0] != null; - - case "server2" : - serverEps[1] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[1] != null; - - case "server3" : - serverEps[2] = IronfleetCommon.Networking.ResolveIPEndpoint(value); - return serverEps[2] != null; - - case "nthreads" : - numThreads = Convert.ToInt32(value); - if (numThreads < 1) { - Console.WriteLine("Number of threads must be at least 1, so can't be {0}", numThreads); - return false; - } - return true; - - case "duration" : - experimentDuration = Convert.ToUInt64(value); - return true; - - case "initialseqno" : - initialSeqNum = Convert.ToUInt64(value); - return true; + if (key == "verbose") { + if (value == "false") { + verbose = false; + return true; + } + if (value == "true") { + verbose = true; + return true; + } + Console.WriteLine("ERROR - Invalid verbose value {0} - should be false or true", value); + return false; + } - case "workload" : - if (value != "g" && value != "s" && value != "f") { - Console.WriteLine("Workload must be 'g', 's', or 'f', but you specified {0}", value); - return false; - } - workload = value[0]; - return true; + if (key == "nthreads") { + try { + numThreads = Convert.ToInt32(value); + if (numThreads < 1) { + Console.WriteLine("Number of threads must be at least 1, so can't be {0}", numThreads); + return false; + } + } + catch (Exception e) { + Console.WriteLine("Could not parse number of threads {0} as a number. Exception:\n{1}", value, e); + return false; + } + return true; + } - case "numkeys" : - numKeys = Convert.ToInt32(value); - return true; + if (key == "duration") { + experimentDuration = Convert.ToUInt64(value); + return true; + } - case "valuesize" : - valueSize = Convert.ToInt32(value); - if (valueSize < 0 || valueSize >= 1024) { - Console.WriteLine("Value size must be non-negative and less than 1024, but you specified {0}", valueSize); - return false; - } - return true; + if (key == "workload") { + if (value != "g" && value != "s" && value != "f") { + Console.WriteLine("Workload must be 'g', 's', or 'f', but you specified {0}", value); + return false; + } + workload = value[0]; + return true; + } - case "verbose" : - if (value == "false") { - verbose = false; - return true; - } - if (value == "true") { - verbose = true; - return true; - } - Console.WriteLine("Invalid verbose value {0} - should be false or true", value); - return false; + if (key == "numkeys") { + numKeys = Convert.ToInt32(value); + if (numKeys < 1) { + Console.WriteLine("Number of keys must be greater than zero, not {0}", value); + return false; } + return true; } - catch (Exception e) { - Console.WriteLine("Invalid value {0} for key {1}, leading to exception:\n{2}", value, key, e); - return false; + + if (key == "valuesize") { + valueSize = Convert.ToInt32(value); + if (valueSize < 0 || valueSize >= 1024) { + Console.WriteLine("Value size must be non-negative and less than 1024, but you specified {0}", valueSize); + return false; + } + return true; } Console.WriteLine("Invalid argument key {0}", key); diff --git a/ironfleet/src/IronSHTClient/Program.cs b/ironfleet/src/IronSHTClient/Program.cs index 2fc67466..31937957 100755 --- a/ironfleet/src/IronSHTClient/Program.cs +++ b/ironfleet/src/IronSHTClient/Program.cs @@ -1,4 +1,5 @@ using IronfleetCommon; +using IronfleetIoFramework; using System; using System.Collections.Generic; using System.IO; @@ -14,31 +15,18 @@ class Program static void usage() { Console.Write(@" -Usage: dotnet IronSHTClient.dll [key=value]... +Usage: dotnet IronSHTClient.dll [key=value]... -Allowed keys: - client - IP address+port of client (default 127.0.0.1:6000) - server1 - IP address+port of first server (default 127.0.0.1:4001) - server2 - IP address+port of second server (default 127.0.0.1:4002) - server3 - IP address+port of third server (default 127.0.0.1:4003) - nthreads - number of experiment client threads to run, not - counting the setup thread (default 1) - duration - duration of experiment in seconds (default 60) - initialseqno - first sequence number each thread uses (default 0) - workload - g for Gets, s for Sets, f for Sharding (default s) - numkeys - number of keys (default 1000) - valsize - number of bytes in each value (default 1024) - verbose - print verbose output (false or true, default false) - -Each thread will use a different port number, using consecutive port -numbers starting with the one in client. For instance, if nthreads=3 -and client=127.0.0.1:6000, then the setup thread will use port 6000 -and the 3 experiment threads will use ports 6001-6003. + - file path of the service description -NOTE: Each client endpoint is expected to follow a certain stateful -protocol. So if you run this program multiple times, either: (1) use -a different clientip or (2) use a clientport that causes different -ports to be used. +Allowed keys: + nthreads - number of experiment client threads to run, not + counting the setup thread (default 1) + duration - duration of experiment in seconds (default 60) + workload - g for Gets, s for Sets, f for Sharding (default s) + numkeys - number of keys (default 1000) + valsize - number of bytes in each value (default 1024) + verbose - print verbose output (false or true, default false) "); } @@ -54,28 +42,38 @@ static void Main(string[] args) } } + var serviceIdentity = ServiceIdentity.ReadFromFile(ps.ServiceFileName); + if (serviceIdentity == null) { + return; + } + if (serviceIdentity.ServiceType != "IronSHT") { + Console.Error.WriteLine("ERROR - Service described by {0} is of type {1}, not IronSHT", ps.ServiceFileName, + serviceIdentity.ServiceType); + return; + } + HiResTimer.Initialize(); - if (ps.verbose) { - Console.WriteLine("Client process starting {0} threads running for {1} s...", ps.numThreads, ps.experimentDuration); + if (ps.Verbose) { + Console.WriteLine("Client process starting {0} threads running for {1} s...", ps.NumThreads, ps.ExperimentDuration); } Console.WriteLine("[[READY]]"); // Setup the system - var setupThreads = Client.StartSetupThreads(ps).ToArray(); + var setupThreads = Client.StartSetupThreads(ps, serviceIdentity).ToArray(); setupThreads[0].Join(); Console.WriteLine("[[SETUP COMPLETE]]"); // Start the experiment - var threads = Client.StartExperimentThreads(ps).ToArray(); + var threads = Client.StartExperimentThreads(ps, serviceIdentity).ToArray(); - if (ps.experimentDuration == 0) + if (ps.ExperimentDuration == 0) { threads[0].Join(); } else { - Thread.Sleep((int)ps.experimentDuration * 1000); + Thread.Sleep((int)ps.ExperimentDuration * 1000); Console.Out.WriteLine("[[DONE]]"); Console.Out.Flush(); Environment.Exit(0); diff --git a/ironfleet/src/IronSHTServer/Params.cs b/ironfleet/src/IronSHTServer/Params.cs new file mode 100644 index 00000000..7dd640db --- /dev/null +++ b/ironfleet/src/IronSHTServer/Params.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text.RegularExpressions; + +namespace IronSHTServer +{ + public class Params + { + private string serviceFileName; + private string privateKeyFileName; + private string localHostNameOrAddress; + private int localPort; + private bool profile; + private bool verbose; + + public Params() + { + serviceFileName = ""; + privateKeyFileName = ""; + localHostNameOrAddress = ""; + localPort = 0; + profile = false; + verbose = false; + } + + public string ServiceFileName { get { return serviceFileName; } } + public string PrivateKeyFileName { get { return privateKeyFileName; } } + public string LocalHostNameOrAddress { get { return localHostNameOrAddress; } } + public int LocalPort { get { return localPort; } } + public bool Profile { get { return profile; } } + public bool Verbose { get { return verbose; } } + + public bool Validate() + { + if (serviceFileName.Length == 0) { + Console.WriteLine("ERROR - Missing service parameter"); + return false; + } + if (privateKeyFileName.Length == 0) { + Console.WriteLine("ERROR - Missing private parameter"); + return false; + } + return true; + } + + public bool ProcessCommandLineArgument(string arg) + { + var pos = arg.IndexOf("="); + if (pos < 0) { + if (serviceFileName.Length == 0) { + serviceFileName = arg; + return true; + } + else if (privateKeyFileName.Length == 0) { + privateKeyFileName = arg; + return true; + } + else { + Console.WriteLine("ERROR - Invalid argument {0}", arg); + return false; + } + } + var key = arg.Substring(0, pos).ToLower(); + var value = arg.Substring(pos + 1); + return SetValue(key, value); + } + + private bool SetBoolValue(string key, string value, ref bool p) + { + if (value == "false") { + p = false; + return true; + } + else if (value == "true") { + p = true; + return true; + } + else { + Console.WriteLine("ERROR - Invalid {0} value {1} - should be false or true", key, value); + return false; + } + } + + private bool SetValue(string key, string value) + { + if (key == "addr") { + localHostNameOrAddress = value; + return true; + } + + if (key == "port") { + try { + localPort = Convert.ToInt32(value); + return true; + } + catch (Exception e) { + Console.WriteLine("ERROR - Could not convert port {0} to a number. Exception:\n{1}", value, e); + return false; + } + } + + if (key == "profile") { + return SetBoolValue(key, value, ref profile); + } + + if (key == "verbose") { + return SetBoolValue(key, value, ref verbose); + } + + Console.WriteLine("ERROR - Invalid argument key {0}", key); + return false; + } + } +} diff --git a/ironfleet/src/IronSHTServer/Program.cs b/ironfleet/src/IronSHTServer/Program.cs index 85c58694..5bac3a14 100755 --- a/ironfleet/src/IronSHTServer/Program.cs +++ b/ironfleet/src/IronSHTServer/Program.cs @@ -1,4 +1,5 @@ using IronfleetCommon; +using IronfleetIoFramework; using MathNet.Numerics.Distributions; using System; using System.Linq; @@ -9,13 +10,73 @@ namespace IronSHTServer { class Program { + static void usage() + { + Console.Write(@" +Usage: dotnet IronSHTServer.dll [key=value]... + + - file path of the service description + - file path of the private key + +Allowed keys: + addr - local host name or address to listen to (default: + whatever's specified in the private key file) + port - port to listen to (default: whatever's specified + in the private key file) + profile - print profiling info (false or true, default: false) + verbose - use verbose output (false or true, default: false) +"); + } + static void Main(string[] args) { + Console.WriteLine("IronSHTServer program started"); + + Console.WriteLine("Processing command-line arguments"); + + Params ps = new Params(); + + foreach (var arg in args) + { + if (!ps.ProcessCommandLineArgument(arg)) { + usage(); + return; + } + } + + if (!ps.Validate()) { + usage(); + return; + } + + ServiceIdentity serviceIdentity = ServiceIdentity.ReadFromFile(ps.ServiceFileName); + if (serviceIdentity == null) { + return; + } + if (serviceIdentity.ServiceType != "IronSHT") { + Console.Error.WriteLine("ERROR - Service described by {0} is of type {1}, not IronSHT", ps.ServiceFileName, + serviceIdentity.ServiceType); + return; + } + + PrivateIdentity privateIdentity = PrivateIdentity.ReadFromFile(ps.PrivateKeyFileName); + if (privateIdentity == null) { + return; + } + + Native____Io__s_Compile.PrintParams.SetParameters(ps.Profile, i_shouldPrintProgress: false); + + var nc = Native____Io__s_Compile.NetClient.Create(privateIdentity, ps.LocalHostNameOrAddress, ps.LocalPort, + serviceIdentity.Servers, ps.Verbose); + Dafny.ISequence[] serverPublicKeys = + serviceIdentity.Servers.Select(server => Dafny.Sequence.FromArray(server.PublicKey)).ToArray(); + var ironArgs = Dafny.Sequence>.FromArray(serverPublicKeys); + Profiler.Initialize(); Native____Io__s_Compile.Time.Initialize(); Console.WriteLine("IronFleet program started."); Console.WriteLine("[[READY]]"); - Main__i_Compile.__default._Main(); + Main__i_Compile.__default.IronfleetMain(nc, ironArgs); Console.WriteLine("[[EXIT]]"); } } diff --git a/ironfleet/src/TestIoFramework/.gitignore b/ironfleet/src/TestIoFramework/.gitignore new file mode 100644 index 00000000..a4588fea --- /dev/null +++ b/ironfleet/src/TestIoFramework/.gitignore @@ -0,0 +1,2 @@ +.vs +Properties/ diff --git a/ironfleet/src/TestIoFramework/Params.cs b/ironfleet/src/TestIoFramework/Params.cs new file mode 100644 index 00000000..5a71be54 --- /dev/null +++ b/ironfleet/src/TestIoFramework/Params.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text.RegularExpressions; + +namespace TestIoFramework +{ + public class Params + { + private string serviceFileName; + private string privateKeyFileName; + private string localHostNameOrAddress; + private int localPort; + private bool verbose; + + public Params() + { + serviceFileName = ""; + privateKeyFileName = ""; + localHostNameOrAddress = ""; + localPort = 0; + } + + public string ServiceFileName { get { return serviceFileName; } } + public string PrivateKeyFileName { get { return privateKeyFileName; } } + public string LocalHostNameOrAddress { get { return localHostNameOrAddress; } } + public int LocalPort { get { return localPort; } } + public bool Verbose { get { return verbose; } } + + public bool Validate() + { + if (serviceFileName.Length == 0) { + Console.WriteLine("ERROR - Missing service parameter"); + return false; + } + if (privateKeyFileName.Length == 0) { + Console.WriteLine("ERROR - Missing private parameter"); + return false; + } + return true; + } + + public bool ProcessCommandLineArgument(string arg) + { + var pos = arg.IndexOf("="); + if (pos < 0) { + if (serviceFileName.Length == 0) { + serviceFileName = arg; + return true; + } + else if (privateKeyFileName.Length == 0) { + privateKeyFileName = arg; + return true; + } + else { + Console.WriteLine("ERROR - Invalid argument {0}", arg); + return false; + } + } + var key = arg.Substring(0, pos).ToLower(); + var value = arg.Substring(pos + 1); + return SetValue(key, value); + } + + private bool SetValue(string key, string value) + { + if (key == "addr") { + localHostNameOrAddress = value; + return true; + } + + if (key == "port") { + try { + localPort = Convert.ToInt32(value); + return true; + } + catch (Exception e) { + Console.WriteLine("ERROR - Could not convert port {0} to a number. Exception:\n{1}", value, e); + return false; + } + } + + if (key == "verbose") { + if (value == "false") { + verbose = false; + return true; + } + if (value == "true") { + verbose = true; + return true; + } + Console.WriteLine("ERROR - Invalid verbose value {0} - should be false or true", value); + return false; + } + + Console.WriteLine("ERROR - Invalid argument key {0}", key); + return false; + } + } +} diff --git a/ironfleet/src/TestIoFramework/Program.cs b/ironfleet/src/TestIoFramework/Program.cs new file mode 100644 index 00000000..5c8c2b2d --- /dev/null +++ b/ironfleet/src/TestIoFramework/Program.cs @@ -0,0 +1,125 @@ +using IronfleetIoFramework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Numerics; +using System.Text; +using System.Text.Json; +using System.Threading; + +namespace TestIoFramework +{ + class Runner + { + private ServiceIdentity serviceIdentity; + private PrivateIdentity privateIdentity; + private IoScheduler scheduler; + + public Runner(ServiceIdentity i_serviceIdentity, PrivateIdentity i_privateIdentity, Params ps) + { + serviceIdentity = i_serviceIdentity; + privateIdentity = i_privateIdentity; + scheduler = IoScheduler.CreateServer(privateIdentity, ps.LocalHostNameOrAddress, ps.LocalPort, + serviceIdentity.Servers, ps.Verbose); + } + + public void Run() + { + Thread t = new Thread(this.SenderThread); + t.Start(); + this.ReceiverThread(); + } + + private void SenderThread() + { + Random rng = new Random(); + while (true) { + int serverIndex = rng.Next(serviceIdentity.Servers.Count); + PublicIdentity serverIdentity = serviceIdentity.Servers[serverIndex]; + byte[] serverPublicKey = serverIdentity.PublicKey; + + int randomNumber = rng.Next(10000); + string message = string.Format("Hello {0}", randomNumber); + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + + Console.WriteLine("Sending message {0} to {1}", message, IoScheduler.PublicKeyToString(serverPublicKey)); + + scheduler.SendPacket(serverPublicKey, messageBytes); + Thread.Sleep(1000); + } + } + + private void ReceiverThread() + { + while (true) { + bool ok; + bool timedOut; + byte[] remote; + byte[] messageBytes; + scheduler.ReceivePacket(0, out ok, out timedOut, out remote, out messageBytes); + if (!ok) { + Console.WriteLine("Not OK, so terminating receiver thread"); + return; + } + if (timedOut) { + Thread.Sleep(100); + continue; + } + string message = Encoding.UTF8.GetString(messageBytes); + Console.WriteLine("Received message {0} from {1}", message, IoScheduler.PublicKeyToString(remote)); + } + } + } + + class Program + { + static void usage() + { + Console.Write(@" +Usage: dotnet TestIoFramework.dll [key=value]... + + - file path of the service description + - file path of the private key + +Allowed keys: + addr - local host name or address to listen to (default: + whatever's specified in the private key file) + port - port to listen to (default: whatever's specified + in the private key file) + verbose - use verbose output (default: false) +"); + } + + static void Main(string[] args) + { + Params ps = new Params(); + + foreach (var arg in args) + { + if (!ps.ProcessCommandLineArgument(arg)) { + usage(); + return; + } + } + + if (!ps.Validate()) { + return; + } + + ServiceIdentity serviceIdentity = ServiceIdentity.ReadFromFile(ps.ServiceFileName); + if (serviceIdentity == null) { + return; + } + + PrivateIdentity privateIdentity = PrivateIdentity.ReadFromFile(ps.PrivateKeyFileName); + if (privateIdentity == null) { + return; + } + + var runner = new Runner(serviceIdentity, privateIdentity, ps); + runner.Run(); + } + } +} diff --git a/ironfleet/src/TestIoFramework/TestIoFramework.csproj b/ironfleet/src/TestIoFramework/TestIoFramework.csproj new file mode 100644 index 00000000..e723b995 --- /dev/null +++ b/ironfleet/src/TestIoFramework/TestIoFramework.csproj @@ -0,0 +1,24 @@ + + + + Exe + net5.0 + + + + 1701;1702;162;164;168;183;219;436;1717;1718 + + + + TestIoFramework.Program + + + + + + + + + + + diff --git a/ironfleet/src/TestIoFramework/TestIoFramework.sln b/ironfleet/src/TestIoFramework/TestIoFramework.sln new file mode 100644 index 00000000..3bf62766 --- /dev/null +++ b/ironfleet/src/TestIoFramework/TestIoFramework.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31005.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestIoFramework", "TestIoFramework.csproj", "{80249939-7D35-43CA-891A-F4C73EC6D959}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {80249939-7D35-43CA-891A-F4C73EC6D959}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80249939-7D35-43CA-891A-F4C73EC6D959}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80249939-7D35-43CA-891A-F4C73EC6D959}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80249939-7D35-43CA-891A-F4C73EC6D959}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3EDDD386-2BE8-48A4-8188-FFDA2034F16D} + EndGlobalSection +EndGlobal