Compare commits

...

442 Commits

Author SHA1 Message Date
Jedrzej Stuczynski fe88e20350 Merge branch 'release/v0.3.2' 2020-01-17 13:15:31 +00:00
Jedrzej Stuczynski aaa8e54c80 Updated changelogs 2020-01-17 13:15:20 +00:00
Jedrzej Stuczynski 766dbee75a Updated version numbers 2020-01-17 13:12:22 +00:00
Dave Hrycyszyn 2e2c90983a Merge pull request #59 from nymtech/feature/separate_presence_address
Feature/separate presence address
2020-01-17 13:10:53 +00:00
Jedrzej Stuczynski 518244f6bf presence client: conversion of MixNodePresence into topology::MixNode returns a Result 2020-01-17 13:07:54 +00:00
Jedrzej Stuczynski 809db67efd nym-client: resolving dns names to ips 2020-01-17 12:35:04 +00:00
Jedrzej Stuczynski ac79a0da69 mixnode: changed announce address from SocketAddr to String 2020-01-17 12:21:26 +00:00
Jedrzej Stuczynski 1769fa8a5a mixnode: added announce-host and announce-port arguments 2020-01-17 11:59:59 +00:00
Jedrzej Stuczynski 4cc7fc309b Fixed run local network script 2020-01-17 11:44:24 +00:00
Jedrzej Stuczynski f966df4fb1 Additional healthcheck logging 2020-01-17 11:44:08 +00:00
Jedrzej Stuczynski 781c4c551d Fixed incorrect hashmap reads in healtcheck 2020-01-16 18:14:53 +00:00
Jedrzej Stuczynski ac0566aa17 Merge tag '0.3.1' into develop
0.3.1
2020-01-16 15:04:12 +00:00
Jedrzej Stuczynski 66b2429b2e Merge branch 'release/0.3.1' 2020-01-16 15:04:03 +00:00
Jedrzej Stuczynski 325670e540 Changelogs 2020-01-16 15:03:55 +00:00
Jedrzej Stuczynski 8d84523c32 Increased version numbers in cargo files 2020-01-16 15:01:34 +00:00
Jedrzej Stuczynski 851082dc6e Cargo.lock updates 2020-01-16 14:59:04 +00:00
Jedrzej Stuczynski dcb548ee8e Updated sample validator config to point to the qa mixnet 2020-01-16 14:58:55 +00:00
Dave Hrycyszyn 330e7d8d29 Merge pull request #58 from nymtech/bugfix/presence_client_crash
Bugfix/presence client crash
2020-01-16 14:55:58 +00:00
Jedrzej Stuczynski 3ab4b94a8a Provider no longer crashing when failed to send presence 2020-01-16 13:44:39 +00:00
Jedrzej Stuczynski 09e88da1a9 Mixnode no longer crashing when failed to send metrics or presence 2020-01-16 13:42:50 +00:00
Jedrzej Stuczynski 773fbceff7 Unused import 2020-01-16 13:37:12 +00:00
Jedrzej Stuczynski 84e2292ed0 Moved mix presence reporting from std::thread to tokio task 2020-01-16 13:29:00 +00:00
Jedrzej Stuczynski 59ea4306b8 Merge branch 'release/0.3.0' 2020-01-14 15:04:39 +00:00
Jedrzej Stuczynski b1edf54403 Updated version numbers 2020-01-14 15:04:17 +00:00
Jedrzej Stuczynski a0654b9b59 Updated changelogs 2020-01-14 15:04:00 +00:00
Jedrzej Stuczynski fe35bcbe1c Printing binding warning for provider 2020-01-14 14:57:24 +00:00
Dave Hrycyszyn fcf11a1c36 Merge pull request #54 from nymtech/feature/client_topology_filtering
Feature/client topology filtering
2020-01-14 14:40:57 +00:00
Dave Hrycyszyn 05db16677d clients: knocking 0-scored nodes out of the effective topology 2020-01-14 14:40:37 +00:00
Dave Hrycyszyn 330cfdc259 rustfmt 2020-01-14 14:39:25 +00:00
Dave Hrycyszyn 6cad7bcbef cargo: health check added to cargo.lock 2020-01-14 14:39:15 +00:00
Dave Hrycyszyn bf9463b24a Merge pull request #52 from nymtech/feature/mix_warning
Showing binding warning on binding to localhost, 0.0.0.0 or 127.0.0.1
2020-01-14 13:58:00 +00:00
Jędrzej Stuczyński d69603f87b Merge pull request #53 from mileschet/client-pubkey
print public key for nym client tools
2020-01-14 13:55:32 +00:00
Jedrzej Stuczynski 173daa7305 Fixed broken test 2020-01-14 13:47:47 +00:00
Jedrzej Stuczynski b9c37f2845 Removed unused typ() method on NodeScore 2020-01-14 12:50:30 +00:00
Roberto Santacroce Martins 8237f5d885 changed the place for print the public key 2020-01-14 13:48:08 +01:00
Jedrzej Stuczynski 76432cf253 Client obtaining healthy topology on startup 2020-01-14 12:41:38 +00:00
Jedrzej Stuczynski c1c41ebde1 Moved built_info module definition to top of file 2020-01-14 12:40:46 +00:00
Jedrzej Stuczynski 9082e93826 Helper function to determine if any path can be constructred through topology 2020-01-14 12:40:25 +00:00
Jedrzej Stuczynski 63cebd27cd Filtering topology by node version 2020-01-14 12:40:04 +00:00
Jedrzej Stuczynski 505a0bd0b0 Ability to filter topolgy by predefined node score threshold 2020-01-14 12:39:37 +00:00
Jedrzej Stuczynski fb6ec68046 Calculating node score in separate method 2020-01-14 12:38:54 +00:00
Jedrzej Stuczynski 5121e2fb0c Added node type enum to NodeScore 2020-01-14 12:38:40 +00:00
Jedrzej Stuczynski 25c9ee5b3c Defined std::error::Error for HealthCheckerError 2020-01-14 12:35:20 +00:00
Jedrzej Stuczynski a6014a6ad5 Formatting 2020-01-14 12:33:31 +00:00
Jedrzej Stuczynski ea7a5cf044 Fixed bug in converting MixNode to MixNodePresence 2020-01-14 12:33:13 +00:00
Jedrzej Stuczynski cef1568e33 Removed nym-client lib definition 2020-01-14 12:30:53 +00:00
Jedrzej Stuczynski 315ece38ae Temporarily moved healthcheck from validator to separate common crate 2020-01-14 10:44:05 +00:00
Jedrzej Stuczynski 6fbc6c6680 Renamed version filtering 2020-01-14 10:30:20 +00:00
Jedrzej Stuczynski 7895eb8c38 Initial topology version filtering 2020-01-14 10:28:48 +00:00
Jedrzej Stuczynski 164ebf47be From nymtopology nodes for prence topology 2020-01-14 10:26:54 +00:00
Roberto Santacroce Martins 1c85ba266f print public key for nym client tools 2020-01-14 11:19:11 +01:00
Jedrzej Stuczynski e34a55c60a Separated versions for mixes, providers and coco nodes for filtering 2020-01-14 10:12:26 +00:00
Jedrzej Stuczynski 9bd4c49be9 Default implementation for filtering topology by node versions 2020-01-14 10:10:55 +00:00
Jedrzej Stuczynski c1eb553cc0 Derived clone trait for Topology node types 2020-01-14 10:10:29 +00:00
Jedrzej Stuczynski 04f11ecafa Added version to topology for coconodes 2020-01-14 10:10:05 +00:00
Jedrzej Stuczynski 2d7fff06fe Showing binding warning on binding to localhost, 0.0.0.0 or 127.0.0.1 2020-01-14 09:54:16 +00:00
Dave Hrycyszyn 1d2481e558 whitespace 2020-01-13 18:26:45 +00:00
Dave Hrycyszyn 19dff546b4 readme: typo fix 2020-01-13 18:26:37 +00:00
Dave Hrycyszyn 507e910e72 Pulled nym-client binary out to top-level 2020-01-13 18:25:05 +00:00
Jędrzej Stuczyński 6b68998e93 Merge pull request #51 from nymtech/feature/validator-config-sample
validator: moving sample config files into sample configs directory
2020-01-13 18:02:51 +00:00
Dave Hrycyszyn aad70d65b8 validator: moving sample config files into sample configs directory 2020-01-13 18:00:35 +00:00
Dave Hrycyszyn f670c7f58f Merge pull request #50 from nymtech/feature/validator_health_checker
Feature/validator health checker
2020-01-13 17:57:26 +00:00
Dave Hrycyszyn 3ec3b4a793 Merge branch 'develop' into feature/validator_health_checker 2020-01-13 17:57:06 +00:00
Dave Hrycyszyn d92cee59a0 validator: removing scary comment in main() 2020-01-13 17:36:30 +00:00
Dave Hrycyszyn 350eeae8da README: adding a note about .env file 2020-01-13 17:35:39 +00:00
Dave Hrycyszyn 40fee72161 .env: ignoring the .env file and providing a sample 2020-01-13 17:33:33 +00:00
Jedrzej Stuczynski 51d802d1c7 Provider related compiler warnings 2020-01-13 17:03:32 +00:00
Jedrzej Stuczynski ee32b97fbb Compiler warning 2020-01-13 17:01:05 +00:00
Jedrzej Stuczynski ee1a0033ac Sorting NodeScores before printing 2020-01-13 16:54:58 +00:00
Jedrzej Stuczynski a615dacf64 Defined Ord (+ required traits) for NodeScore 2020-01-13 16:54:40 +00:00
Jedrzej Stuczynski 34de9cc10b Another sphinx revision update 2020-01-13 16:50:10 +00:00
Jedrzej Stuczynski 03d04d5aa7 Updated used sphinx revision 2020-01-13 15:49:55 +00:00
Jedrzej Stuczynski 05ca3e147f Using the new updated healthchecker 2020-01-13 15:26:12 +00:00
Jedrzej Stuczynski 0b1bae6ea1 Formatting 2020-01-13 15:25:52 +00:00
Jedrzej Stuczynski c056485471 resolving all available paths 2020-01-13 15:25:31 +00:00
Jedrzej Stuczynski d152d0fd12 Sending all tests packets before trying to resolve them 2020-01-13 15:25:02 +00:00
Jedrzej Stuczynski 5bc5974bdb Conversion of vec of pub keys into unique path id and vice verse 2020-01-13 15:23:56 +00:00
Jedrzej Stuczynski 5e870e4c8a ibid. 2020-01-13 15:23:20 +00:00
Jedrzej Stuczynski 8178d505c3 PathChecker holding mapping of paths to their statuses 2020-01-13 15:23:06 +00:00
Jedrzej Stuczynski 78a52df0ec Assertion on maximum number of iterations 2020-01-13 15:22:26 +00:00
Jedrzej Stuczynski 0ecab55959 Added itertools 2020-01-13 15:21:28 +00:00
Jedrzej Stuczynski b3f056b933 More informative error message 2020-01-13 15:20:47 +00:00
Jedrzej Stuczynski 520973b578 Using resolution timeout as part of healtchecker 2020-01-13 15:20:33 +00:00
Jedrzej Stuczynski d4ab7f96f1 Separated increasing packet count into explicit sent and received 2020-01-13 15:19:47 +00:00
Jedrzej Stuczynski 911ab8ff4c Added resolution timeout to healthcheck config 2020-01-13 10:58:55 +00:00
Jedrzej Stuczynski 9596060972 Provider no longer printing string message that its storing 2020-01-10 16:56:32 +00:00
Jedrzej Stuczynski 4b7971899c Actually sending test packets through network (not retrieving status yet) 2020-01-10 16:54:49 +00:00
Jedrzej Stuczynski 6eeedf858d decreased default logging level 2020-01-10 16:54:28 +00:00
Jedrzej Stuczynski 7045d76039 Using correct case for existence in hashmap 2020-01-10 16:09:22 +00:00
Jedrzej Stuczynski 90266175a1 Removed some unused imports 2020-01-10 16:06:41 +00:00
Jedrzej Stuczynski c810d0d957 Async registration at all available providers 2020-01-10 16:05:55 +00:00
Jedrzej Stuczynski a4990f1944 Added to bytes conversion of node keys for topology 2020-01-10 16:05:31 +00:00
Jedrzej Stuczynski 52054f8b46 PathChecker constructor returning result type 2020-01-10 15:25:07 +00:00
Jedrzej Stuczynski 489c806292 Split healthchecker into multiple files 2020-01-10 15:20:49 +00:00
Jędrzej Stuczyński db69105813 Merge pull request #49 from nymtech/feature/better-socket-errors
Improving websocket connection errors
2020-01-10 15:05:15 +00:00
Dave Hrycyszyn d39e532e4b Improving websocket connection errors 2020-01-10 15:01:36 +00:00
Jedrzej Stuczynski 25575b4598 Running healthcheck specified number of times 2020-01-10 15:01:12 +00:00
Jedrzej Stuczynski 769598d40a Added layer information to healthcheck 2020-01-10 14:56:50 +00:00
Jedrzej Stuczynski 227f69befb Fixed compilation errors 2020-01-10 14:50:30 +00:00
Jedrzej Stuczynski 9d760a515a Updated sphinx revision 2020-01-10 14:42:10 +00:00
Dave Hrycyszyn 26663100b4 Removed duplicate build instructions in favour of a pointer to the docs site. 2020-01-10 14:41:17 +00:00
Jedrzej Stuczynski 7f6ec5ecdf Merge branch 'develop' into feature/validator_health_checker 2020-01-10 14:38:59 +00:00
Dave Hrycyszyn a58c3dbb25 travis: it's no longer necessary to grab Sphinx separately 2020-01-10 14:23:34 +00:00
Dave Hrycyszyn be0616b524 cargo: pinning Sphinx to a specific rev to get rid of build flapping 2020-01-10 14:23:09 +00:00
Dave Hrycyszyn 92390f9ad7 cargo: lockfile change 2020-01-10 14:18:19 +00:00
Dave Hrycyszyn 3e9221f6af Merge pull request #48 from nymtech/feature/crypto_crate
Feature/crypto crate
2020-01-10 14:16:10 +00:00
Jedrzej Stuczynski 1ad651fe72 Updated comments regarding DummyMix pair 2020-01-10 14:14:59 +00:00
Jedrzej Stuczynski e4501852d5 Made curve generator private 2020-01-10 14:12:29 +00:00
Jedrzej Stuczynski 402b226080 Updated depenencies 2020-01-10 13:53:10 +00:00
Jedrzej Stuczynski c772b9a4df Same for request handling + removed dependency on entiry nym-client 2020-01-10 13:53:01 +00:00
Jedrzej Stuczynski 95cd4123b5 Ibid for provider presence 2020-01-10 13:52:38 +00:00
Jedrzej Stuczynski 4164491bc2 Provider using new key types 2020-01-10 13:52:28 +00:00
Jedrzej Stuczynski 0492a21bcf Easier conversion of keypairs from bytes 2020-01-10 13:51:50 +00:00
Jedrzej Stuczynski 50d54d44c7 ibid. for sockets 2020-01-10 13:51:32 +00:00
Jedrzej Stuczynski 1cc1d2ddf9 client init using new key structure 2020-01-10 13:51:20 +00:00
Jedrzej Stuczynski 0dda60cd81 ibid 2020-01-10 13:51:03 +00:00
Jedrzej Stuczynski 114c927028 removed identity client crate 2020-01-10 13:50:57 +00:00
Jedrzej Stuczynski e18ed7e0fd More generic client pem store 2020-01-10 13:50:47 +00:00
Jedrzej Stuczynski 3e533cdee2 ibid for identity keys 2020-01-10 13:50:32 +00:00
Jedrzej Stuczynski 6e3b2c36fe encryption keys now need to implement PemStorable 2020-01-10 13:49:44 +00:00
Jedrzej Stuczynski 73028f85a9 Defined trait for returning key pem type 2020-01-10 13:49:00 +00:00
Jedrzej Stuczynski d0d894a309 Implementing said traits on dummy struct using x25519 2020-01-10 11:50:48 +00:00
Jedrzej Stuczynski db92f898d3 MixnetIdentity Key traits 2020-01-10 11:50:30 +00:00
Jedrzej Stuczynski 740a2fb2ff X25519 impl of said traits 2020-01-10 11:50:12 +00:00
Jedrzej Stuczynski 84e34e5490 MixnetEncryption Key traits 2020-01-10 11:50:03 +00:00
Jedrzej Stuczynski 2a05d77d94 Initial crypto crate 2020-01-10 11:49:49 +00:00
Dave Hrycyszyn 97d7f6d7cb README: drying up the build and usage instructions a bit 2020-01-10 11:35:05 +00:00
Jedrzej Stuczynski 4f23d7e58b Fixed author strings for other common crates 2020-01-10 10:09:18 +00:00
Jedrzej Stuczynski ccf1e6fe1a Fixed root cargo file 2020-01-10 10:07:01 +00:00
Jedrzej Stuczynski 7068dc7089 Fixed compilation errors due to changes in sphinx crate 2020-01-10 10:05:01 +00:00
Jedrzej Stuczynski 7f6f366e51 Logic for calculating health 2020-01-09 18:01:28 +00:00
Jedrzej Stuczynski 4d997ef03b Using new methods on NodeAddressBytes from sphinx 2020-01-09 17:17:50 +00:00
Jedrzej Stuczynski 65042e7210 Storing node key as bytes rather than string 2020-01-09 16:42:15 +00:00
Jedrzej Stuczynski 89e17b0ffa Added number of test packets to healthcheck config 2020-01-09 16:30:56 +00:00
Jedrzej Stuczynski 6e52afef79 Method for modyfing node score count of packets sent and received 2020-01-09 16:26:01 +00:00
Jedrzej Stuczynski a37d9aedc4 Changed NodeScore constructors 2020-01-09 16:24:29 +00:00
Jedrzej Stuczynski fbd1f05eb9 Moved score calculation to HealthCheckResult 2020-01-09 16:21:11 +00:00
Jedrzej Stuczynski 3efbea8263 error log on failure to format addresses 2020-01-09 16:17:47 +00:00
Jedrzej Stuczynski 582c8dbc98 Printing formatted zero health node status 2020-01-09 16:16:42 +00:00
Jedrzej Stuczynski 1e0d84fb8b Healthcheck getting new topology every run 2020-01-09 15:07:23 +00:00
Jedrzej Stuczynski ca2faf0095 Required dependency for ibid. 2020-01-09 15:06:59 +00:00
Jedrzej Stuczynski e0c110ee55 Generating all possible paths from topology 2020-01-09 15:06:44 +00:00
Jedrzej Stuczynski db2f803023 Making use of the code simplification 2020-01-09 15:03:24 +00:00
Jedrzej Stuczynski d7f444c659 simplified mix_route by using the traits 2020-01-09 15:02:31 +00:00
Jedrzej Stuczynski 3b7f5c7c68 semicolon 2020-01-09 15:01:59 +00:00
Jedrzej Stuczynski 3072d56417 Into SphinxNode trait for ProviderNode 2020-01-09 15:01:41 +00:00
Jedrzej Stuczynski 59bff7a0d5 Into SphinxNode trait for MixNode 2020-01-09 15:01:27 +00:00
Jedrzej Stuczynski fadd781fc3 comment regarding future work 2020-01-09 15:00:24 +00:00
Jedrzej Stuczynski b5518babac Constructor for directory client config 2020-01-09 14:35:41 +00:00
Jędrzej Stuczyński cd28f8051b Merge pull request #43 from nymtech/feature/chill-log-messages
Feature/chill log messages
2020-01-09 13:15:47 +00:00
Dave Hrycyszyn 4966d8ecef Update README.md 2020-01-09 12:36:18 +00:00
Dave Hrycyszyn 3b7d22e675 clients: set up a logger to chill out output 2020-01-09 12:30:35 +00:00
Jędrzej Stuczyński 6ceec8e737 Merge pull request #42 from nymtech/feature/improve-pemstore-errors
persistence: improving PEM file reading and parsing failure messages
2020-01-09 12:27:55 +00:00
Dave Hrycyszyn 077c59f761 sfw-provider: moving test filesystem output to /tmp 2020-01-09 11:39:56 +00:00
Dave Hrycyszyn fd9829bedd persistence: improving PEM file reading and parsing failure messages 2020-01-09 10:48:36 +00:00
Jędrzej Stuczyński 1d79c43782 Merge pull request #39 from nymtech/feature/remove-run
Removing the run command from code and documentation
2020-01-09 10:18:51 +00:00
Dave Hrycyszyn c029117674 Merge branch 'develop' into feature/remove-run 2020-01-09 10:03:55 +00:00
Jędrzej Stuczyński ac78e160df Merge pull request #41 from nymtech/feature/better-error-message-for-bad-topology
Providing a nicer error than "failed on unwrap()" when topology retri…
2020-01-09 10:01:58 +00:00
Jędrzej Stuczyński eb75362307 Merge pull request #40 from nymtech/feature/provider-banner
Prettying up sfw-provider start sequence a bit.
2020-01-09 10:01:28 +00:00
Dave Hrycyszyn 6c3e7b6256 nym-client: taking run right out of circulation 2020-01-08 19:33:14 +00:00
Dave Hrycyszyn 7a3d61374f Providing a nicer error than "failed on unwrap()" when topology retrieval fails 2020-01-08 18:53:12 +00:00
Dave Hrycyszyn ec78c45ea0 Prettying up sfw-provider start sequence a bit. 2020-01-08 18:32:41 +00:00
Dave Hrycyszyn 1337f93c33 Removing the run command from code and documentation 2020-01-08 18:00:07 +00:00
Jedrzej Stuczynski 1f3875f39c Using ibid. in client code 2020-01-08 17:17:25 +00:00
Jedrzej Stuczynski f735befd52 Create 'route_to' function 2020-01-08 17:17:10 +00:00
Jedrzej Stuczynski 2a9e21538f Moved make layered topology to separate function + validation 2020-01-08 17:16:26 +00:00
Jedrzej Stuczynski 8ac1e58ef6 Fixed teammate's typo :) 2020-01-08 17:15:09 +00:00
Jedrzej Stuczynski c97b1b5edf Merge branch 'develop' into feature/validator_health_checker 2020-01-08 16:41:13 +00:00
Dave Hrycyszyn e773b251b9 Fixed tests. 2020-01-08 16:33:12 +00:00
Dave Hrycyszyn 9c8347aa72 Fixing compilation to handle new provider listener split fields 2020-01-08 16:25:16 +00:00
Dave Hrycyszyn fcaa23fd77 Re-arranged client directories 2020-01-08 16:22:03 +00:00
Jędrzej Stuczyński 5fa3eb35e4 Merge pull request #37 from nymtech/feature/script/localnet
Feature/script/localnet
2020-01-08 15:56:17 +00:00
Dave Hrycyszyn 67a900701e Updated script copyright date 2020-01-08 15:56:01 +00:00
Dave Hrycyszyn 8314d647d9 script: proper invocation for testnet runnign 2020-01-08 15:53:27 +00:00
Dave Hrycyszyn b88ca3ed91 script: killing old testnet processes in case they're already running 2020-01-08 15:53:11 +00:00
Dave Hrycyszyn ee43e82235 Removing unused import 2020-01-08 15:52:51 +00:00
Dave Hrycyszyn ce866bd987 Setting a 0.1 second average delay time. 2020-01-08 15:52:35 +00:00
Dave Hrycyszyn 95f103b4fd scripts: local network now runs 2020-01-08 15:13:30 +00:00
Dave Hrycyszyn 5ad6db9232 removed commented logging code 2020-01-08 14:58:18 +00:00
Dave Hrycyszyn c1cd6557eb whitespace 2020-01-08 14:58:03 +00:00
Dave Hrycyszyn 8c3cbe6646 Split provider host/ports for mixnet listener and clients listener 2020-01-08 14:57:55 +00:00
Dave Hrycyszyn add94ed70a Merge branch 'develop' of github.com:nymtech/nym into feature/script/localnet 2020-01-08 14:18:57 +00:00
Dave Hrycyszyn 6fe9dc899b Merge pull request #36 from nymtech/feature/arguments_fix
Feature/arguments fix
2020-01-08 14:17:54 +00:00
Jedrzej Stuczynski 6fedba093b Explicitly required values for mixHost and clientHost by clap 2020-01-08 14:17:16 +00:00
Jedrzej Stuczynski fb15c636eb Correct version for mix 2020-01-08 14:17:02 +00:00
Dave Hrycyszyn 043403b1e0 Merge branch 'develop' into feature/script/localnet 2020-01-08 14:10:49 +00:00
Dave Hrycyszyn 13eda7f68f Merge pull request #34 from nymtech/feature/crates_cleanup
Feature/crates cleanup
2020-01-08 13:56:04 +00:00
Jedrzej Stuczynski 9b94b1d72b Fixed broken directory client tests 2020-01-08 13:05:01 +00:00
Jedrzej Stuczynski 07db413f2c Possibly gotten rid of compiler warnings 2020-01-08 12:59:29 +00:00
Jedrzej Stuczynski db67744b19 Further topology cleanup by defining NymTopology trait 2020-01-08 12:55:40 +00:00
Jedrzej Stuczynski 1292cdb145 Moved topology to separate crate 2020-01-08 12:07:34 +00:00
Jedrzej Stuczynski 7fa42d16eb Moved addressing to separate crate and removed mixnode dependency on entire client crate 2020-01-08 12:02:18 +00:00
Jedrzej Stuczynski a673f9f206 Fixed authors strings 2020-01-08 11:50:58 +00:00
Jedrzej Stuczynski 8706f7ed06 Moved provider client to separate crate 2020-01-08 11:48:49 +00:00
Jedrzej Stuczynski 883ce22c5b Moved mix client to separate crate 2020-01-08 11:45:43 +00:00
Jedrzej Stuczynski 9ba1ea203b Moved directory client to separate crate 2020-01-08 11:34:39 +00:00
Jedrzej Stuczynski 2cef804a34 Allowed for calling get_topology from outside the crate 2020-01-08 10:13:26 +00:00
Jedrzej Stuczynski 46ce9eaff7 Made healthcheck async and repeating and action at config-specified interval 2020-01-08 10:06:36 +00:00
Jedrzej Stuczynski d4cba72118 Added interval to healthcheck config 2020-01-08 10:02:13 +00:00
Jedrzej Stuczynski 708d6d5aed Changed default validator config to use localhost directory 2020-01-08 09:57:24 +00:00
Jedrzej Stuczynski ff7af13080 Added futures and tokio to the validator 2020-01-08 09:57:10 +00:00
Jedrzej Stuczynski f325ea6b92 Creating and running healthchecker instance 2020-01-08 09:51:51 +00:00
Dave Hrycyszyn d42a75cea9 scripts: run_local_network.sh port of the old Go code
Currently non-functional
2020-01-07 19:10:46 +00:00
Jedrzej Stuczynski f4d4556ced initial changelog (to be updated before actual release) 2020-01-07 17:49:15 +00:00
Jedrzej Stuczynski d79d57fcaa updated cargo.lock 2020-01-07 17:49:04 +00:00
Jedrzej Stuczynski 9712322597 sample config 2020-01-07 17:48:54 +00:00
Jedrzej Stuczynski e3038b2b57 missing imports + dependencies 2020-01-07 17:48:47 +00:00
Jedrzej Stuczynski ba1ebd18e0 initial healthcheck 2020-01-07 17:48:33 +00:00
Jedrzej Stuczynski 9c70c64825 missing build.rs file 2020-01-07 17:48:14 +00:00
Jedrzej Stuczynski ddc988a10d reading .env file with logging level 2020-01-07 17:48:04 +00:00
Jedrzej Stuczynski aa4d759c38 logging 2020-01-07 17:47:45 +00:00
Jedrzej Stuczynski d94dcea7ee actual config definition 2020-01-07 17:47:18 +00:00
Jedrzej Stuczynski 3d98ed7b6c obtaining config file location from arguments and parsing 2020-01-07 17:46:52 +00:00
Dave Hrycyszyn 18ac7e90ed travis: moving the build image down a bit 2020-01-07 15:49:13 +00:00
Dave Hrycyszyn b04b62ed27 travis: adding build status 2020-01-07 15:48:06 +00:00
Dave Hrycyszyn 4066b4db42 travis: even more cowbell 2020-01-07 15:44:29 +00:00
Dave Hrycyszyn 037f05f323 travis: more cowbell 2020-01-07 15:41:19 +00:00
Dave Hrycyszyn 3f358a6929 travis: more travis 2020-01-07 15:39:49 +00:00
Dave Hrycyszyn e79a63588a more travis experimentation 2020-01-07 15:38:17 +00:00
Dave Hrycyszyn 60f15d57bd Let's see what's going on 2020-01-07 15:36:26 +00:00
Dave Hrycyszyn e422ce0a13 travis: maybe a branch problem? 2020-01-07 15:33:22 +00:00
Dave Hrycyszyn 598d68a822 travis: fixing Sphinx dir path 2020-01-07 15:30:53 +00:00
Dave Hrycyszyn f3900f3659 travis: clone Sphinx first to make the build work 2020-01-07 15:26:31 +00:00
Dave Hrycyszyn fa2d881442 Linking the Sphinx repo properly 2020-01-07 15:19:48 +00:00
Dave Hrycyszyn 435b7ae7e9 Adding a Travis build file 2020-01-07 15:14:06 +00:00
Dave Hrycyszyn 950a1ec0f8 Merge pull request #25 from nymtech/features/version_number_to_presence
Features/version number to presence
2020-01-07 15:11:23 +00:00
aniampio b483e15d5d Add version for the provider 2020-01-07 14:34:04 +00:00
Jedrzej Stuczynski 4d67d11a39 Initialised crate for the validator with health checker 2020-01-07 14:29:22 +00:00
Jedrzej Stuczynski 1e719a8861 Updated Readme 2020-01-07 13:57:58 +00:00
Jedrzej Stuczynski d856911a1d Created top level cargo workspace 2020-01-07 13:57:49 +00:00
aniampio d50666df0c Add version to MixNodePresence struct 2020-01-07 13:46:14 +00:00
Dave Hrycyszyn f6bf239cc2 Merge branch 'develop' of github.com:nymtech/nym into develop 2020-01-07 13:36:26 +00:00
Dave Hrycyszyn bd5c1dd357 Small docs change. 2020-01-07 13:35:39 +00:00
Dave Hrycyszyn 8a558746fe Merge pull request #7 from nymtech/feature/minor-client-changes
Feature/minor client changes
2020-01-07 12:49:32 +00:00
Jedrzej Stuczynski df23148ccf Changes in cargo.lock files 2020-01-07 12:39:51 +00:00
Jedrzej Stuczynski cf62a7ba12 Killing application on possible yet undetected bug + minor improvements in select! macro loop 2020-01-07 12:39:18 +00:00
Jedrzej Stuczynski bfad7d1087 Temporarily increased recursion limit 2020-01-07 12:38:11 +00:00
Jedrzej Stuczynski 71d23f44c2 Clap using cargo.toml version 2020-01-07 12:37:40 +00:00
Dave Hrycyszyn f57e485ceb Fleshing out the README 2020-01-07 12:30:43 +00:00
Dave Hrycyszyn 0531b716cc Setting up all paths so that builds work 2020-01-07 12:30:30 +00:00
Dave Hrycyszyn bfb91c58c8 Ditching .idea folder 2020-01-07 12:09:50 +00:00
Dave Hrycyszyn 57eab1bc85 Merge branch 'master' of github.com:nymtech/nym 2020-01-07 12:06:45 +00:00
Dave Hrycyszyn 6fb438a994 Added a README 2020-01-07 12:04:35 +00:00
Dave Hrycyszyn 23e0aee0a1 Moved nym-sfw-provider into sfw-provider directory 2020-01-07 12:03:07 +00:00
Dave Hrycyszyn 2f98568edf Merge remote-tracking branch 'nym-sfw-provider/master' 2020-01-07 12:02:20 +00:00
Dave Hrycyszyn 2b2bd5b688 Moved nym-mixnode into mixnode directory 2020-01-07 12:00:59 +00:00
Dave Hrycyszyn d05924e070 Merge remote-tracking branch 'nym-mixnode/master' 2020-01-07 12:00:16 +00:00
Dave Hrycyszyn e02c90457e Moving nym-client into the client directory 2020-01-07 11:58:42 +00:00
Dave Hrycyszyn 6f3d7b8f49 Initial commit 2020-01-07 11:42:54 +00:00
Dave Hrycyszyn 712ca4bbb2 Merge branch 'release/0.2.0' 2020-01-06 18:01:57 +00:00
Dave Hrycyszyn 62b137b3b9 Fixed up Authors file. Ania next. 2020-01-06 18:01:32 +00:00
Dave Hrycyszyn 6b508e829f Changelog for 0.2.0 2020-01-06 18:01:16 +00:00
Dave Hrycyszyn 4e7bd2d88f Merge pull request #27 from nymtech/feature/arguments_changes
Feature/arguments changes
2020-01-06 17:56:19 +00:00
Dave Hrycyszyn b584316caa Merge branch 'release/0.2.0' 2020-01-06 17:43:35 +00:00
Dave Hrycyszyn 220ef1b0ef Version number bump 2020-01-06 17:43:12 +00:00
Dave Hrycyszyn 38e747104e Added the glorious and talented Jędrzej Stuczyński to authors 2020-01-06 17:42:44 +00:00
Dave Hrycyszyn 4fc2e3bc0d Changelog for 0.2.0 release 2020-01-06 17:42:04 +00:00
Jedrzej Stuczynski 7f3ea6af1a Changelog file 2020-01-06 15:26:43 +00:00
Jedrzej Stuczynski fc7635dc27 Changelog file 2020-01-06 15:26:31 +00:00
Jędrzej Stuczyński e8b6cabfe3 Merge pull request #26 from nymtech/feature/built-versions
Added build info to the provider
2020-01-06 14:57:58 +00:00
Jedrzej Stuczynski d29ea67f9b Explicitly required values for mixHost and clientHost 2020-01-06 14:57:19 +00:00
Jedrzej Stuczynski c241ecfa36 Removed local flag and replaced it with explicit directory server 2020-01-06 14:56:36 +00:00
Dave Hrycyszyn 3553f88909 Added build info to the provider 2020-01-06 14:54:04 +00:00
Dave Hrycyszyn 6f5b44d016 Merge pull request #23 from nymtech/feature/ipv6_addressing
ipv6_addressing
2020-01-06 14:45:18 +00:00
Jedrzej Stuczynski d5a0e7d355 ipv6_addressing 2020-01-06 14:43:19 +00:00
Dave Hrycyszyn c3ce96afe9 Merge pull request #22 from nymtech/feature/flags-cleanup
Feature/flags cleanup
2020-01-06 12:24:24 +00:00
Dave Hrycyszyn f15588f617 Merge pull request #21 from nymtech/mileschet-patch-1
updated readme with dependencies instructions
2020-01-06 12:24:04 +00:00
Jedrzej Stuczynski 0bb6959755 Printing directory server on start 2020-01-06 12:20:08 +00:00
Jedrzej Stuczynski efb4660fe7 Requiring explicit host + printing bound address on startup 2020-01-06 12:12:40 +00:00
Jedrzej Stuczynski 27c61cb194 Printing node version number on startup 2020-01-06 12:04:45 +00:00
mileschet cddc61fe57 updated readme with dependencies instructions
added some information about the dependencies until we fix cargo dependencies.
2019-12-29 17:07:41 +01:00
Dave Hrycyszyn 489ea16749 Merge pull request #20 from 0xjac/develop
runner: print public key on startup
2019-12-29 15:20:06 +00:00
0xjac b59b9fa4a5 runner: print public key on startup
Closes #19
2019-12-29 16:13:32 +01:00
Dave Hrycyszyn 57d2cab98a Merge tag '0.1.0' into develop
Initial release (0.1.0)
2019-12-23 20:39:35 +00:00
Dave Hrycyszyn 5c697a3a4d Merge branch 'release/0.1.0' 2019-12-23 20:39:14 +00:00
Dave Hrycyszyn 21f0c3e2ac Including a pointer to the Rust install docs 2019-12-20 14:57:18 +00:00
Dave Hrycyszyn 42ab21c133 whitespace 2019-12-20 14:57:07 +00:00
Dave Hrycyszyn a39eed123e main: setting a default port (1789) 2019-12-20 14:54:48 +00:00
Dave Hrycyszyn 1954895e16 Building out the README a bit. 2019-12-20 14:53:57 +00:00
Dave Hrycyszyn f6ad70bff9 Upping message size limit 2019-12-19 17:43:11 +00:00
Dave Hrycyszyn b9747669d3 Merge pull request #25 from nymtech/feature/presence_with_clients
Feature/presence with clients
2019-12-19 13:58:06 +00:00
Jedrzej Stuczynski cbac85fbf4 Fixed compiler warnings 2019-12-19 13:49:45 +00:00
Jedrzej Stuczynski 76f503a664 Provider sending registered clients with presence 2019-12-19 13:49:04 +00:00
Jedrzej Stuczynski a8eb5ec4c2 Moved client processing data behind just an arc but put the ledger behind arc+mutex 2019-12-19 13:07:07 +00:00
Jedrzej Stuczynski 56a8118ea3 Sending presence inside tokio reactor 2019-12-19 12:55:18 +00:00
Dave Hrycyszyn 234bf7d0a0 cargo fmt run 2019-12-19 11:50:47 +00:00
Jędrzej Stuczyński ac8f04959e Merge pull request #18 from nymtech/feature/use-keys
Feature/use keys
2019-12-19 11:48:36 +00:00
Dave Hrycyszyn 016f5829a5 Removing required arguments and setting default values 2019-12-19 11:46:30 +00:00
Dave Hrycyszyn d79ab40988 Merge branch 'develop' into feature/use-keys 2019-12-19 11:37:31 +00:00
Dave Hrycyszyn f0a1ce50bc Merge pull request #23 from nymtech/features/auth_token
Features/auth token
2019-12-18 15:52:36 +00:00
Dave Hrycyszyn ef82d4327c Merge pull request #18 from nymtech/feature/presence-fix
presence: removing last-seen from MixNodePresence, it's set by server
2019-12-18 15:49:44 +00:00
Dave Hrycyszyn 8da639cd11 Merge branch 'develop' into feature/presence-fix 2019-12-18 15:49:04 +00:00
Jedrzej Stuczynski 428f202cbc Got rid of most of compiler warnings 2019-12-18 14:39:54 +00:00
Jedrzej Stuczynski 073ef2776e Returning result from delete_file function + some extra notes 2019-12-18 14:28:44 +00:00
Jedrzej Stuczynski 091104be8f Register response using concrete authToken type + defined from_bytes method 2019-12-18 13:25:15 +00:00
Jedrzej Stuczynski 8bbe771229 Potentially working mutex on register request 2019-12-18 13:20:18 +00:00
Jedrzej Stuczynski 0ead20764b Pull request using the mutex 2019-12-18 13:06:51 +00:00
Jedrzej Stuczynski 8f451283dc Put the client processing data behind a mutex 2019-12-18 12:39:16 +00:00
Jedrzej Stuczynski 6ae5142166 Changes required by the merge 2019-12-18 11:43:09 +00:00
Jedrzej Stuczynski a8e46d34df Merge branch 'develop' into features/auth_token 2019-12-18 11:41:13 +00:00
Jedrzej Stuczynski 20fe59b9ba Tests for marshaling/unmarshaling register request 2019-12-18 10:48:44 +00:00
Jedrzej Stuczynski d0902218d4 Fixed ProviderRequest marshaling and unmarshaling 2019-12-18 10:45:17 +00:00
Jedrzej Stuczynski 38517e41ba constructor for registerrequest 2019-12-18 10:38:01 +00:00
Jedrzej Stuczynski 507f37fcec Made AuthToken be type alias for u8;32 + Used correct type alliases for the clients ledger 2019-12-18 10:03:12 +00:00
Jedrzej Stuczynski 06c645cf56 Using destination address type alias rather than just array of 32
( + rustfmt)
2019-12-18 09:41:29 +00:00
aniampio b63494f686 Add check of the token when pulling 2019-12-17 18:14:40 +01:00
aniampio f78ce3f9b6 Remove lost comment 2019-12-17 17:36:01 +01:00
aniampio 671e55f4d5 Change SHA256 to HMAC 2019-12-17 17:29:10 +01:00
aniampio 85d147ef62 Change token from vec to fixed size array 2019-12-17 15:46:55 +01:00
aniampio cb69182e02 Make sure the client is registered only once and there is only one directory for it 2019-12-17 14:49:29 +01:00
Dave Hrycyszyn 8dd8873a03 using new Config object for provider 2019-12-17 13:34:18 +00:00
Dave Hrycyszyn 4e55c6c395 rustfmt 2019-12-17 13:33:59 +00:00
Dave Hrycyszyn 381e85f0bc main: setting default provider parameters 2019-12-17 13:33:42 +00:00
Dave Hrycyszyn 8ef95ac522 main: making provider module public 2019-12-17 13:33:08 +00:00
Dave Hrycyszyn 59133ed862 cargo: adding base64 crate 2019-12-17 13:32:53 +00:00
Dave Hrycyszyn 65aa1ba29d rustfmt 2019-12-17 13:32:29 +00:00
Dave Hrycyszyn c71f494974 presence: comment re directory server 2019-12-17 13:29:59 +00:00
Dave Hrycyszyn ae25f335f4 mix_peer: adding Debug so we can print 2019-12-17 13:29:29 +00:00
aniampio 3101bb6980 Code cleanup 2019-12-17 12:29:21 +01:00
aniampio 4a241bb59a Move AuthToken into requests 2019-12-17 12:28:38 +01:00
aniampio 63f30bf9d0 Move creating inboxes directory to register_client function 2019-12-17 11:49:22 +01:00
Jedrzej Stuczynski b39ccf26b6 Allowing the two provider listeners to run in parallel rather than just concurrently 2019-12-17 10:31:04 +00:00
Dave Hrycyszyn 21834fde41 Merge pull request #17 from nymtech/feature/metrics_reporting
Feature/metrics reporting
2019-12-17 10:13:19 +00:00
Dave Hrycyszyn b6db1bf1af presence: removing last-seen from MixNodePresence, it's set by server 2019-12-17 10:11:14 +00:00
aniampio 0b3ce4739b Represent registered_clients_ledger as HashTable 2019-12-16 21:32:21 +01:00
Jedrzej Stuczynski f262b2924b Fixed some compiler warnings 2019-12-16 17:59:09 +00:00
Jedrzej Stuczynski 724af12e03 Removed debug prints 2019-12-16 17:45:06 +00:00
Jedrzej Stuczynski 3a59f3cef9 Metrics reporting 2019-12-16 17:42:55 +00:00
Dave Hrycyszyn 6aba3f08f1 Merge branch 'develop' of github.com:nymtech/nym-mixnode into develop 2019-12-16 16:29:47 +00:00
Dave Hrycyszyn 8cd2be7edc cargo: mysterious lock action 2019-12-16 16:29:39 +00:00
Jędrzej Stuczyński d768eff303 Merge pull request #16 from nymtech/feature/use-keys
Feature/use keys
2019-12-16 16:27:58 +00:00
Dave Hrycyszyn ddf0d762d7 mix_peer: adding a to_string() definition 2019-12-16 16:26:30 +00:00
Dave Hrycyszyn aa207e2c13 mix_peer: using constant sized byte array for addressing 2019-12-16 16:25:49 +00:00
Dave Hrycyszyn c192afe9eb mix_peer: converting address to strongly typed socket address 2019-12-16 16:25:29 +00:00
aniampio 415ba8185e Add to_bytes function 2019-12-16 16:36:59 +01:00
aniampio fdbb2549f8 Add template for register response 2019-12-16 15:49:03 +01:00
Dave Hrycyszyn 3f5e219b1c whitespace 2019-12-16 14:47:18 +00:00
Dave Hrycyszyn e6350a0ea4 main: removing superseded comment 2019-12-16 14:46:37 +00:00
Dave Hrycyszyn 56858c4109 provider: sending proper public keys to directory server 2019-12-16 14:46:15 +00:00
aniampio 509b222052 Add register function and types 2019-12-16 15:40:05 +01:00
Jędrzej Stuczyński 29e1488714 Merge pull request #15 from nymtech/feature/use-keys
Feature/use keys
2019-12-16 13:55:42 +00:00
Dave Hrycyszyn 37a493439d presence: sending URL-safe public key 2019-12-16 13:54:38 +00:00
Dave Hrycyszyn ab02eec824 Using the node socket address to send to the directory server 2019-12-16 13:54:20 +00:00
Dave Hrycyszyn efb900a538 node: adding a config struct and using it to pass args around 2019-12-16 13:30:18 +00:00
Dave Hrycyszyn 9d969904f2 cargo: adding base64 crate 2019-12-16 13:29:48 +00:00
Dave Hrycyszyn 2b5b61ea26 node: adding a config struct 2019-12-16 12:54:16 +00:00
Dave Hrycyszyn 287d2b31a5 main: deleting unused key_file argument 2019-12-16 12:54:00 +00:00
Jędrzej Stuczyński de63fff1a1 Merge pull request #14 from nymtech/feature/directory-server-option
Feature/directory server option
2019-12-16 12:31:57 +00:00
Jędrzej Stuczyński d2f8ffdc2b Merge pull request #17 from nymtech/feature/directory-server-option
presence: ability to configure local directory server
2019-12-16 12:31:25 +00:00
Dave Hrycyszyn 4143e53598 presence: ability to configure local directory server 2019-12-16 12:27:50 +00:00
Dave Hrycyszyn 265ef22838 main: can now be started with a local directory server for testing purposes 2019-12-16 12:20:48 +00:00
aniampio ca28ce9e19 Add function to generate SHA256 tokens 2019-12-16 12:47:52 +01:00
Dave Hrycyszyn f0cf92b542 cargo: removed ws deps 2019-12-16 11:46:12 +00:00
Dave Hrycyszyn aceb6df848 Merge pull request #16 from nymtech/feature/presence
Feature/presence
2019-12-16 11:21:50 +00:00
Dave Hrycyszyn ec849cfc0c Merge pull request #13 from nymtech/feature/presence
Feature/presence
2019-12-16 11:21:23 +00:00
Dave Hrycyszyn 233dc5ce9d cargo: lockfile including new nym-client dependencies 2019-12-16 10:28:51 +00:00
Dave Hrycyszyn e4cfab537e presence: renamed to non-stutter names 2019-12-14 15:46:53 +00:00
Dave Hrycyszyn 9fc01cee73 provider: move presence notifications into its own module 2019-12-14 15:35:10 +00:00
Dave Hrycyszyn 842c0b10b9 node: moving presence notification into a thread. 2019-12-14 14:44:07 +00:00
Dave Hrycyszyn f10a403045 cargo: re-ordering 2019-12-14 14:43:49 +00:00
Dave Hrycyszyn 3ab41835a2 provider: importing types and code for presence notifications. Not yet working. 2019-12-13 13:45:36 +00:00
Dave Hrycyszyn 0b41badab0 cargo: alphabetizing 2019-12-13 13:28:31 +00:00
Dave Hrycyszyn 028cc66db3 cargo: added nym-client library 2019-12-13 13:27:03 +00:00
Dave Hrycyszyn 60dc3b4650 cargo: newline 2019-12-13 13:26:47 +00:00
Dave Hrycyszyn 3d9181a913 main: removing assert so that the node starts 2019-12-13 13:25:59 +00:00
Dave Hrycyszyn 1633f54f4f node: adding presence notifications (note: doesn't work yet) 2019-12-13 13:19:58 +00:00
Dave Hrycyszyn 86d27b06df cargo: depending on the nym-client library 2019-12-13 13:19:40 +00:00
Dave Hrycyszyn 07bb0a69fa cargo: newline action 2019-12-13 13:19:26 +00:00
Jędrzej Stuczyński 138814f26c Merge pull request #12 from nymtech/feature/routing
mix_peer: now using the real address from the packet
2019-12-12 19:19:03 +00:00
Dave Hrycyszyn 06540f5ce1 node: removing more lifetimes 2019-12-12 19:15:28 +00:00
Dave Hrycyszyn 4feadb6352 mix_peer: now using the real address from the packet 2019-12-12 19:01:53 +00:00
Dave Hrycyszyn fdf49ff804 runner: cleaning up text to prep for release 2019-12-12 17:47:13 +00:00
Dave Hrycyszyn eb49ef1013 node: removing socket mutabilitiy 2019-12-12 17:10:24 +00:00
Dave Hrycyszyn 17deec7720 runner: removing host assertion so default takes effect 2019-12-12 17:10:08 +00:00
Dave Hrycyszyn 76c2eac3bd main: adding usage banner 2019-12-12 16:59:01 +00:00
Dave Hrycyszyn f095dd114e main: breaking the run command out so that it lives with the node code 2019-12-12 16:57:01 +00:00
Jędrzej Stuczyński 5004f6d07a Merge pull request #15 from nymtech/feature/banner-and-commands
main: re-arranged so main() is at the top, and added a banner
2019-12-12 16:50:39 +00:00
Dave Hrycyszyn 31fbe3c265 main: re-arranged so main() is at the top, and added a banner 2019-12-12 16:46:39 +00:00
Dave Hrycyszyn 478fe628ca Merge pull request #12 from nymtech/feature/client_packet_retrieval
Feature/client packet retrieval
2019-12-12 15:25:40 +00:00
Jedrzej Stuczynski fbb675e815 Future comment 2019-12-12 15:14:04 +00:00
Jedrzej Stuczynski 96f5d45877 Removal of retrieved messages 2019-12-12 15:13:04 +00:00
Jedrzej Stuczynski 5d356847fa Some additional cleanup 2019-12-12 15:02:01 +00:00
Jedrzej Stuczynski 16ddb50159 Moved client-related functionalities to separate module 2019-12-12 14:55:23 +00:00
Jedrzej Stuczynski 48bead3ac9 Moved mix-related functionalities to separate module 2019-12-12 14:46:41 +00:00
Jedrzej Stuczynski 35cff8dfcd Moved store related functionalities to separate module 2019-12-12 14:40:10 +00:00
Jedrzej Stuczynski 2850c71317 Sending back messages to the client 2019-12-12 12:37:17 +00:00
Jedrzej Stuczynski 534a4aedc7 Pull messages response + marshaling 2019-12-12 12:27:02 +00:00
Jedrzej Stuczynski 27b55a7e60 Moved requests to separate module 2019-12-12 12:26:33 +00:00
Jedrzej Stuczynski dc5660728e Moved dummy message content to requests crate 2019-12-12 10:45:06 +00:00
Jedrzej Stuczynski 86693ab983 Reading constant number of stored messages and when below limit putting dummy data 2019-12-12 10:43:32 +00:00
Jedrzej Stuczynski 10d31abbf8 Added my test inbox to gitignore... 2019-12-12 10:05:15 +00:00
Jedrzej Stuczynski c4779e3428 printing all files with given clients' messages 2019-12-12 10:04:17 +00:00
Jedrzej Stuczynski 8bb462bda1 Basic structure for client message retrieval 2019-12-11 17:24:56 +00:00
Jedrzej Stuczynski 427b83875a Changed read_to_end to normal read with 1024 buffer 2019-12-11 16:59:46 +00:00
Jedrzej Stuczynski 8393910016 Closing socket to client on being done 2019-12-11 16:42:37 +00:00
Jedrzej Stuczynski e283d162e5 Updated gitignore... 2019-12-11 15:49:26 +00:00
Jedrzej Stuczynski d869d427e4 Removed the build information for the subcrate... 2019-12-11 15:48:57 +00:00
Jedrzej Stuczynski a08bbc98db Moved provider requests to subcrate in sfw provider 2019-12-11 15:48:30 +00:00
Jedrzej Stuczynski c02665f71e Fixed using incorrect address for client listener 2019-12-11 11:26:58 +00:00
Jedrzej Stuczynski 9ddbaf42d5 Actually spawning new thread and calling processing function 2019-12-11 11:25:46 +00:00
Jedrzej Stuczynski bc77ddadfa Opening dedicated client tcp socket 2019-12-11 11:24:59 +00:00
Jedrzej Stuczynski 688376f757 Fixed linter error about unused results 2019-12-11 11:15:13 +00:00
Jedrzej Stuczynski 1c0eab2fa2 Running two concurrent futures 2019-12-11 11:13:48 +00:00
Jedrzej Stuczynski fb456c0bd0 Added client-related host and port arguments 2019-12-11 10:45:32 +00:00
Dave Hrycyszyn 8d0afbd21e Merge pull request #7 from nymtech/feature/provider_type
Provider storing received messages
2019-12-10 16:00:12 +00:00
Jedrzej Stuczynski 528ba87492 Removed unused imports 2019-12-10 15:36:14 +00:00
Jedrzej Stuczynski d08a9965c8 Removed commented code with an experiment on usage of tokio tcp listener 2019-12-10 15:34:58 +00:00
Jedrzej Stuczynski bf360473d5 Removed accidentally added tmp store directory 2019-12-10 15:29:08 +00:00
Jedrzej Stuczynski fd2f565af2 Provider storing received messages 2019-12-10 15:28:51 +00:00
Jedrzej Stuczynski 7a3f16548b Creating full store path 2019-12-10 14:35:44 +00:00
Jedrzej Stuczynski 6fdc4a6434 Calling store_processed_data after processing each sphinx packet 2019-12-10 14:25:30 +00:00
Dave Hrycyszyn b5c5ea3193 Merge pull request #7 from nymtech/feature/removal_of_processing_data_copy
Removed processing data copy on every connection and instead use Arc …
2019-12-10 13:18:39 +00:00
Jedrzej Stuczynski 1371b94599 Removed processing data copy on every connection and instead use Arc with RwLock 2019-12-10 13:12:58 +00:00
Jedrzej Stuczynski 2c986efcb1 Finally won first fight with the borrow checker for concurrent code 2019-12-10 13:05:44 +00:00
Jedrzej Stuczynski 9b8b0bff1d Moved socket processing to separate function 2019-12-09 17:07:42 +00:00
Jedrzej Stuczynski cd94ecde19 Passing entire processing data struct for dealing with sphinx packets 2019-12-09 13:59:22 +00:00
Jedrzej Stuczynski 385e41a920 Added store directory as additional provider argument 2019-12-09 12:57:18 +00:00
Jedrzej Stuczynski 836cff226a Moved listening loop to provider struct impl 2019-12-09 12:47:38 +00:00
Dave Hrycyszyn 86861ccb70 Merge pull request #6 from nymtech/feature/command_line_arguments
Feature/command line arguments
2019-12-09 11:48:11 +00:00
Dave Hrycyszyn 0b4f10cfe6 Merge pull request #6 from nymtech/feature/command_line_arguments
Feature/command line arguments
2019-12-09 11:44:35 +00:00
Jedrzej Stuczynski 8ac76cb152 passing arguments to the service provider startup 2019-12-09 10:44:21 +00:00
Jedrzej Stuczynski 818389839f Moved provider related functionalities to separate module 2019-12-09 10:38:01 +00:00
Jedrzej Stuczynski 3a069f18e2 Adjusted packet content 2019-12-09 10:27:30 +00:00
Jedrzej Stuczynski 31d0aa7fe4 Added layer as mixnode attribute 2019-12-06 16:21:04 +00:00
Jedrzej Stuczynski 256afa3be9 Removed example code 2019-12-06 16:20:26 +00:00
Jedrzej Stuczynski 777c40bd97 Starting mixnode with provided command line arguments 2019-12-06 16:20:07 +00:00
Jedrzej Stuczynski 9deaad8a2a Initial set of mixnode arguments 2019-12-06 16:14:29 +00:00
AniaPiotrowska 1ad60582ba Merge pull request #5 from nymtech/feature/andrew_experiments
Delays + some code reorganisation
2019-12-06 15:57:27 +00:00
Jedrzej Stuczynski 2aaf2465e4 Moved mix code to separate module 2019-12-06 14:47:14 +00:00
Jedrzej Stuczynski 0fb9c352d6 - 2019-12-06 13:30:39 +00:00
Jedrzej Stuczynski ad289e531b Calling start_listening on mix node instance 2019-12-06 13:26:47 +00:00
Jedrzej Stuczynski 2fbf2456ec More async cleanup 2019-12-06 13:18:50 +00:00
Jedrzej Stuczynski 2c2ecfb785 Initial minor cleanup + actually waiting for time specified in the packet 2019-12-06 12:58:51 +00:00
Jedrzej Stuczynski 9478460893 Waiting 2s before forwarding packet 2019-12-05 17:08:46 +00:00
Dave Hrycyszyn 6ae04b7ed3 Removing print statements 2019-12-02 13:50:21 +00:00
Dave Hrycyszyn fff78cfade Updating to work with constant sized packets 2019-11-29 15:54:44 +00:00
Dave Hrycyszyn e8472e07f9 Updating to work with constant sized packets 2019-11-29 15:54:34 +00:00
Jędrzej Stuczyński 2c0a4b11db Added README - git was asking so nicely to do it : ) 2019-11-29 12:32:14 +00:00
Jędrzej Stuczyński 1eee313e6e Added README - git was asking so nicely to do it : ) 2019-11-29 12:31:49 +00:00
Dave Hrycyszyn 14dfa4795a Initial commit 2019-11-29 12:28:25 +00:00
Dave Hrycyszyn d7ed6b5a13 WIP 2019-11-29 12:26:12 +00:00
Dave Hrycyszyn f4a62fd64c Initial commit 2019-11-28 19:33:20 +00:00
107 changed files with 13255 additions and 1362 deletions
+2
View File
@@ -0,0 +1,2 @@
RUST_LOG=info
RUST_BACKTRACE=1
+6 -2
View File
@@ -1,3 +1,7 @@
/target
/targetString
**/*.rs.bk
/.idea/
/*/target
/test_inbox
.idea
target
.env
+9
View File
@@ -0,0 +1,9 @@
language: rust
rust:
- stable
- beta
- nightly
jobs:
allow_failures:
- rust: nightly
fast_finish: true
-12
View File
@@ -1,12 +0,0 @@
# nym-client Changelog
* removed the `--local` flag
* introduced `--directory` argument to support arbitrary directory servers. Leaving it out will point the node at the "https://directory.nymtech.net" alpha testnet server
* IPv6 support
* client version number is now shown at node start
* directory server location is now shown at node start
* decrease default delays
## 0.1.0 - Initial Release
* The bare minimum set of features required by a Nym Client
Generated
+1119 -874
View File
File diff suppressed because it is too large Load Diff
+16 -38
View File
@@ -1,39 +1,17 @@
[package]
build = "build.rs"
name = "nym-client"
version = "0.2.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
[workspace]
[lib]
name = "nym_client"
path = "src/lib.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
clap = "2.33.0"
curve25519-dalek = "1.2.3"
dirs = "2.0.2"
futures = "0.3.1"
hex = "0.4.0"
pem = "0.7.0"
rand = "0.7.2"
rand_distr = "0.2.2"
reqwest = "0.9.22"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.44"
sphinx = { path = "../sphinx" }
sfw-provider-requests = { path = "../nym-sfw-provider/sfw-provider-requests" }
tokio = { version = "0.2", features = ["full"] }
tungstenite = "0.9.2"
# putting this explicitly below everything and most likely, the next time we look into it, it will already have a proper release
tokio-tungstenite = { git = "https://github.com/dbcfd/tokio-tungstenite", rev="6dc2018cbfe8fe7ddd75ff977343086503135b38" }
[build-dependencies]
built = "0.3.2"
[dev-dependencies]
mockito = "0.22.0"
members = [
"common/clients/directory-client",
"common/clients/mix-client",
"common/clients/provider-client",
"common/clients/validator-client",
"common/addressing",
"common/crypto",
"common/healthcheck",
"common/topology",
"mixnode",
"nym-client",
"sfw-provider",
"sfw-provider/sfw-provider-requests",
"validator",
]
+15 -6
View File
@@ -1,10 +1,19 @@
# Nym Client
## The Nym Privacy Platform
The Nym Client communicates with the remote, decentralised nodes which make up the Nym system as a whole.
This repository contains the full Nym platform, written in Rust.
It needs to handle communication with 3 types of remote nodes. Here's the development status of each:
The platform is composed of multiple Rust crates. Top-level crates include:
- [ ] Directory nodes
- [ ] Mix nodes
- [ ] Validator nodes
* client - an executable crate which you can use for interacting with Nym nodes
* mixnode - an executable mixnode crate
* sfw-provider - an executable store-and-forward provider crate. The provider acts sort of like a mailbox for mixnet messages.
[![Build Status](https://travis-ci.com/nymtech/nym.svg?branch=develop)](https://travis-ci.com/nymtech/nym)
### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs/mixnet/installation/).
### Developing
There's a `.env.sample-dev` file provided which you can rename to `.env` if you want convenient logging, backtrace, or other environment variables pre-set. The `.env` file is ignored so you don't need to worry about checking it in.
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "addressing"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
@@ -0,0 +1,17 @@
[package]
name = "directory-client"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = "0.9.22"
serde = { version = "1.0.104", features = ["derive"] }
## internal
topology = {path = "../../topology"}
[dev-dependencies]
mockito = "0.22.0"
@@ -1,22 +1,16 @@
use crate::clients::directory::requests::health_check_get::{
HealthCheckRequester, Request as HealthCheckRequest,
};
use crate::clients::directory::requests::metrics_mixes_get::{
MetricsMixRequester, Request as MetricsMixRequest,
};
use crate::clients::directory::requests::metrics_mixes_post::{
MetricsMixPoster, Request as MetricsMixPost,
};
use crate::clients::directory::requests::presence_coconodes_post::{
use crate::requests::health_check_get::{HealthCheckRequester, Request as HealthCheckRequest};
use crate::requests::metrics_mixes_get::{MetricsMixRequester, Request as MetricsMixRequest};
use crate::requests::metrics_mixes_post::{MetricsMixPoster, Request as MetricsMixPost};
use crate::requests::presence_coconodes_post::{
PresenceCocoNodesPoster, Request as PresenceCocoNodesPost,
};
use crate::clients::directory::requests::presence_mixnodes_post::{
use crate::requests::presence_mixnodes_post::{
PresenceMixNodesPoster, Request as PresenceMixNodesPost,
};
use crate::clients::directory::requests::presence_providers_post::{
use crate::requests::presence_providers_post::{
PresenceMixProviderPoster, Request as PresenceProvidersPost,
};
use crate::clients::directory::requests::presence_topology_get::{
use crate::requests::presence_topology_get::{
PresenceTopologyGetRequester, Request as PresenceTopologyRequest,
};
@@ -28,6 +22,12 @@ pub struct Config {
pub base_url: String,
}
impl Config {
pub fn new(base_url: String) -> Self {
Config { base_url }
}
}
pub trait DirectoryClient {
fn new(config: Config) -> Self;
}
@@ -0,0 +1,244 @@
use crate::requests::presence_topology_get::PresenceTopologyGetRequester;
use crate::{Client, Config, DirectoryClient};
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::io;
use std::net::ToSocketAddrs;
use topology::{CocoNode, MixNode, MixProviderNode, NymTopology};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CocoPresence {
pub host: String,
pub pub_key: String,
pub last_seen: u64,
pub version: String,
}
impl Into<topology::CocoNode> for CocoPresence {
fn into(self) -> topology::CocoNode {
topology::CocoNode {
host: self.host,
pub_key: self.pub_key,
last_seen: self.last_seen,
version: self.version,
}
}
}
impl From<topology::CocoNode> for CocoPresence {
fn from(cn: CocoNode) -> Self {
CocoPresence {
host: cn.host,
pub_key: cn.pub_key,
last_seen: cn.last_seen,
version: cn.version,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MixNodePresence {
pub host: String,
pub pub_key: String,
pub layer: u64,
pub last_seen: u64,
pub version: String,
}
impl TryInto<topology::MixNode> for MixNodePresence {
type Error = io::Error;
fn try_into(self) -> Result<MixNode, Self::Error> {
let resolved_hostname = self.host.to_socket_addrs()?.next();
if resolved_hostname.is_none() {
return Err(io::Error::new(
io::ErrorKind::Other,
"no valid socket address",
));
}
Ok(topology::MixNode {
host: resolved_hostname.unwrap(),
pub_key: self.pub_key,
layer: self.layer,
last_seen: self.last_seen,
version: self.version,
})
}
}
impl From<topology::MixNode> for MixNodePresence {
fn from(mn: MixNode) -> Self {
MixNodePresence {
host: mn.host.to_string(),
pub_key: mn.pub_key,
layer: mn.layer,
last_seen: mn.last_seen,
version: mn.version,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MixProviderPresence {
pub client_listener: String,
pub mixnet_listener: String,
pub pub_key: String,
pub registered_clients: Vec<MixProviderClient>,
pub last_seen: u64,
pub version: String,
}
impl Into<topology::MixProviderNode> for MixProviderPresence {
fn into(self) -> topology::MixProviderNode {
topology::MixProviderNode {
client_listener: self.client_listener.parse().unwrap(),
mixnet_listener: self.mixnet_listener.parse().unwrap(),
pub_key: self.pub_key,
registered_clients: self
.registered_clients
.into_iter()
.map(|c| c.into())
.collect(),
last_seen: self.last_seen,
version: self.version,
}
}
}
impl From<topology::MixProviderNode> for MixProviderPresence {
fn from(mpn: MixProviderNode) -> Self {
MixProviderPresence {
client_listener: mpn.client_listener.to_string(),
mixnet_listener: mpn.mixnet_listener.to_string(),
pub_key: mpn.pub_key,
registered_clients: mpn
.registered_clients
.into_iter()
.map(|c| c.into())
.collect(),
last_seen: mpn.last_seen,
version: mpn.version,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MixProviderClient {
pub pub_key: String,
}
impl Into<topology::MixProviderClient> for MixProviderClient {
fn into(self) -> topology::MixProviderClient {
topology::MixProviderClient {
pub_key: self.pub_key,
}
}
}
impl From<topology::MixProviderClient> for MixProviderClient {
fn from(mpc: topology::MixProviderClient) -> Self {
MixProviderClient {
pub_key: mpc.pub_key,
}
}
}
// Topology shows us the current state of the overall Nym network
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Topology {
pub coco_nodes: Vec<CocoPresence>,
pub mix_nodes: Vec<MixNodePresence>,
pub mix_provider_nodes: Vec<MixProviderPresence>,
}
impl NymTopology for Topology {
fn new(directory_server: String) -> Self {
println!("Using directory server: {:?}", directory_server);
let directory_config = Config {
base_url: directory_server,
};
let directory = Client::new(directory_config);
let topology = directory
.presence_topology
.get()
.expect("Failed to retrieve network topology.");
topology
}
fn new_from_nodes(
mix_nodes: Vec<MixNode>,
mix_provider_nodes: Vec<MixProviderNode>,
coco_nodes: Vec<CocoNode>,
) -> Self {
Topology {
coco_nodes: coco_nodes.into_iter().map(|node| node.into()).collect(),
mix_nodes: mix_nodes.into_iter().map(|node| node.into()).collect(),
mix_provider_nodes: mix_provider_nodes
.into_iter()
.map(|node| node.into())
.collect(),
}
}
fn get_mix_nodes(&self) -> Vec<topology::MixNode> {
self.mix_nodes
.iter()
.filter_map(|x| x.clone().try_into().ok())
.collect()
}
fn get_mix_provider_nodes(&self) -> Vec<topology::MixProviderNode> {
self.mix_provider_nodes
.iter()
.map(|x| x.clone().into())
.collect()
}
fn get_coco_nodes(&self) -> Vec<topology::CocoNode> {
self.coco_nodes.iter().map(|x| x.clone().into()).collect()
}
}
#[cfg(test)]
mod converting_mixnode_presence_into_topology_mixnode {
use super::*;
#[test]
fn it_returns_error_on_unresolvable_hostname() {
let unresolvable_hostname = "foomp.foomp.foomp:1234";
let mix_presence = MixNodePresence {
host: unresolvable_hostname.to_string(),
pub_key: "".to_string(),
layer: 0,
last_seen: 0,
version: "".to_string(),
};
let result: Result<topology::MixNode, io::Error> = mix_presence.try_into();
assert!(result.is_err())
}
#[test]
fn it_returns_resolved_ip_on_resolvable_hostname() {
let resolvable_hostname = "nymtech.net:1234";
let mix_presence = MixNodePresence {
host: resolvable_hostname.to_string(),
pub_key: "".to_string(),
layer: 0,
last_seen: 0,
version: "".to_string(),
};
let result: Result<topology::MixNode, io::Error> = mix_presence.try_into();
assert!(result.is_ok())
}
}
@@ -1,4 +1,4 @@
use crate::clients::directory::metrics::PersistedMixMetric;
use crate::metrics::PersistedMixMetric;
pub struct Request {
base_url: String,
@@ -1,4 +1,4 @@
use crate::clients::directory::metrics::MixMetric;
use crate::metrics::MixMetric;
use reqwest::Response;
pub struct Request {
@@ -69,7 +69,7 @@ mod metrics_get_request {
#[cfg(test)]
mod fixtures {
use crate::clients::directory::metrics::MixMetric;
use crate::metrics::MixMetric;
pub fn new_metric() -> MixMetric {
MixMetric {
@@ -1,4 +1,4 @@
use crate::clients::directory::presence::CocoPresence;
use crate::presence::CocoPresence;
use reqwest::Response;
pub struct Request {
@@ -73,13 +73,14 @@ mod metrics_get_request {
#[cfg(test)]
mod fixtures {
use crate::clients::directory::presence::CocoPresence;
use crate::presence::CocoPresence;
pub fn new_presence() -> CocoPresence {
CocoPresence {
host: "foo.com".to_string(),
pub_key: "abc".to_string(),
last_seen: 666,
version: "0.2.0".to_string(),
}
}
}
@@ -1,4 +1,4 @@
use crate::clients::directory::presence::MixNodePresence;
use crate::presence::MixNodePresence;
use reqwest::Response;
pub struct Request {
@@ -73,7 +73,7 @@ mod metrics_get_request {
#[cfg(test)]
mod fixtures {
use crate::clients::directory::presence::MixNodePresence;
use crate::presence::MixNodePresence;
pub fn new_presence() -> MixNodePresence {
MixNodePresence {
@@ -81,6 +81,7 @@ mod metrics_get_request {
pub_key: "abc".to_string(),
layer: 1,
last_seen: 0,
version: "0.1.0".to_string(),
}
}
}
@@ -1,4 +1,4 @@
use crate::clients::directory::presence::MixProviderPresence;
use crate::presence::MixProviderPresence;
use reqwest::Response;
pub struct Request {
@@ -72,13 +72,16 @@ mod metrics_get_request {
}
#[cfg(test)]
mod fixtures {
use crate::clients::directory::presence::MixProviderPresence;
use crate::presence::MixProviderPresence;
pub fn new_presence() -> MixProviderPresence {
MixProviderPresence {
host: "foo.com".to_string(),
client_listener: "foo.com".to_string(),
mixnet_listener: "foo.com".to_string(),
pub_key: "abc".to_string(),
registered_clients: vec![],
last_seen: 0,
version: "0.1.0".to_string(),
}
}
}
@@ -1,4 +1,4 @@
use crate::clients::directory::presence::Topology;
use crate::presence::Topology;
pub struct Request {
base_url: String,
@@ -75,25 +75,29 @@ mod topology_requests {
"host": "3.8.244.109:4000",
"pubKey": "AAAAAAAAAAEKwAECSqKy8I8KkSYIBSctxRBRxuR61PpAOwK0UQtkeuPRdwusAyaoBbvv1IBWyMEhvbgT4CtgUnGfYH2s06CIJ09lWWvQ0Jkgthq12mG73H9QSTNM8RITlF1X5ax9BV0EK34M5dUncn1uEYzJzcbaLjUarf2bqoy906dtQpppUWDRLJI6ycw7rKKJ4ZNUhgi4KAEGBsSgLqc0zDKs0rArwouZyz4ofoWnY68mdJKrVy6Zqz83DSdc7B2hqqkHX_Bfeb4SwAEFuhRpy4HfcuxwcRI9sIWMo_LVmbk19g1gfMRlBrmZqoEQL6rDApVLZ9eMp-5IQK8WLlZpWf4Zjy7kZolARAyp_rHUQkH4PrDjgoPrKbm6qK_iejYpL7qx28Q3VeInMpwMIMaSbbW9y36sEVtGc2I0Iu5vS0sp8ESiVlQ5NaBz72deZ8oKJJ4IEPPHP99-b0UQX80fVIrNM88mMzKy0bHri9NFlmIG-e0G1cqmw_ry3XWGQkcr1M5RuNa6oX50w5QawAEVxd5FP5bE8bS4x54Csof11sQWUTwMp6Q7_3H7ZCTSlKSqujlOhmfqSHfGPO2sDIYPHDhDzjakZpKAZWWhn_hiR6DfPpomQ01ZYUhVKKSMxz7_VPjsQplP0bZXA2gfnkADUN8UQ0N9g_usIw73r4aZsOviMsRM8oByvsjVfUWc4_HTLSdnQyImFkHz9CiCmrIYL2dYQRePRatWggvBAyeRzntxI4jDqLKiBdi54ZlAKgV6MCRaJ7Bu7BtmLXrtK4sawAED3QYxuvOSZrbZdUr4yG-U9yVvJ9Klkf-5Mo4EYp3qTL2KBB6_LrZepjAQqp486YkZ03mTIezcsZ48EboXVTWKBZ3QnTI5tX-j4gGxQb7klOJc97qJkDxsvpz4F0ChgCUIZhpIItWHia7_R3Gi-b5siLIdQdUho9isn3kiDGm6t0NED2Bgy3ZxxQwzqsBZm4kPr2_fPX4YyvIoP9895YcGjZyE5iiRC_TE41RJmB1GZYdxegTMq3lNDllKgiqaiPgawAEJASDkmZHTwlg9YOev5OWpQD-FnhPkqVNo_QcDyRu9eoGcWSGFp2sYqjG2SpmiXq0VNnAO7AcKxRzDFu7TjfhlU3Kt0uTKIcrWVU1zFNbJNMjYEq90pp50nowwx8INz20IXET2ZNX6kIXYFCsEvPLZFlG2OoL6xg3uQS1qMl3lIS_VxdO_JfVe0rT65WsJ_P4Nkc1jYiuNPHY6d_iFO0BVYqX0sOCX73GC_TT13BR0jnPwDAVw0rGtYHsXBb8TKOsawAEZIClauuT1V3qOZnb7uRZhFXO-PKTxgc1LCzJt2ChOrMZaBpjlkf3IPpJ2UF4JH4kGaDeBf2k_S-FLAs3drK21efbi5P6_a4QTxAiiRimXGoQIyvOg462s6kP_ZRFufo8YYQHS4olaOeqU4564dNskg_uBPsFMz_2GNOhmn_15cJqP1jfkyD49Z16GTS5YLHgVl9bJKqyvLuypsToLbt1BJzipEP0L2OohuRm-_MvqvwwWKyjNQsubgee1K728d9AawAEBkGggcNVCtXyhoSqi3_w0tVxtkAYeud8sBeAtZHGs06me_QL8co0MFLlO-zdkUb4ZBq08rFEbgLOma8_3whleM8NIPaHNISp1q3IsIhB5zdXcZoGsqLixODBFHtID3YEHAlr4f9T_yh11yJ95xGCl_6Y37hpwLQVGyrfSfccM24mVFqnV3TT5Wdq3ile-jesUx1Q2G1yK_xVqc6itmk-kDuBjyZgzYi1-jsIXAjnhM9G7t8J_Bv5yGGZhLK2dCzM=",
"type": "validator",
"lastSeen": 1575915097085539300
"lastSeen": 1575915097085539300,
"version": "0.1.0"
},
{
"host": "3.9.129.61:4000",
"pubKey": "AAAAAAAAAAMKwAECSqKy8I8KkSYIBSctxRBRxuR61PpAOwK0UQtkeuPRdwusAyaoBbvv1IBWyMEhvbgT4CtgUnGfYH2s06CIJ09lWWvQ0Jkgthq12mG73H9QSTNM8RITlF1X5ax9BV0EK34M5dUncn1uEYzJzcbaLjUarf2bqoy906dtQpppUWDRLJI6ycw7rKKJ4ZNUhgi4KAEGBsSgLqc0zDKs0rArwouZyz4ofoWnY68mdJKrVy6Zqz83DSdc7B2hqqkHX_Bfeb4SwAEEv6RMevAQmLGkeK0uJKnMPPAtm8GgXjWSQijYdnxlPh5SJSNeJUbPZKWFFWdk8yIFXKa8jnzETtdGFKgUUt5AVUDpTBmEdwaHCzlFhXrttshy0V5OhPUlV8cGABmxbagMYm0bFPg0r-snSkrB9YG6wqJYQVeIMOCGYCPbHmDA8R_0-h8VkRKWs1d9KvQOK4kShqgZtYN71KJW8uDE4q2jsGDVvxFt1AgmU9b93xsXF17KrpZy5WxlLZ73HtnTD_oawAED4vd_rK-Kx_n8x_OdDiiEOPUlYDlDCQUqenU9XHKH3B6ijfkJ368wd3LDDVStjDwNORrAyUSw_VlSNUpd1XLC8d17gTaIq5ZI2fWuwwZaoN1JCsYU8fQ6USgtIehQX7IPP8EkFuNmuCBCmpr4schtYniGe9J8Q4dsV-TYPr2uLJkdx1r7luzF--I22k7NfQQM14QDci_0kgrgmZ54CJGkjXyOhCppBXg3fqLC6aFvT3ZocfiiXBJt0huGgPMDtYsawAECLh8KUdNsDolERwJ8v04bS5jI_KKf7uUnCHWuCELwbJSUI3OK1ufS1qSpauvSzVQSbrhEzrEfwQn4VtxQxJlX4UdDU-R-hafiZvVC6DLLAbuORBAC3FScn9W58CnezH4DvCp_w7nftDfdxeuungbZT9XaxS3iNC6PnFsWF6WM3DxMwrzOrFe6wEEoTSPe1mcUDrtwM5UksIvJr6MBRAXrdl0IdBTQr7cLwKe_KYi4siwdjfJEJtOh7oxQBxBg2UkawAEJAPZK2Gg2MQwpxdDT24lNQHF7FVfkO_LuhJwn0RbwNDSVeA4P6-tWL5TkCpqr8xYHfwQ6Z3ILfpGCZr8PspwIoRzqZHQ16f8Pq9xnr0hLEI9BOQU0FS2EtuyPgju5iwsAJAfehUzu6kNLphuLGsXoIZdXDG5mbylwh9JzAVXTwgaR0hNqyXVJxgbt7jcYaSEBFcMGV-hjXyVVNzBleE-G9o_noI_KWU4Ce7K-qOMcewMKfy_VEw-gVaD6dHz6AMoawAEE9XuOLwRttvKybAssZ9gsK-_YRUwuFOeRDIr3NX___9bx6pCc18adCIlH_8EJWFwXZ05ZpNNE88mYx7ZQ3aqaArZJRoWeZeKhqH_s05V10xbzkYX71G5cqz--8vr9ZlQRb2BeETF_Tdq_PLk7qbT8WTGIoq7ZwyDRQTgzvkCgyzj_hBLh2o7sSVNgUo38SFUTMn7YtvVFYlSrTDE3WKE-T-nh5SWdDBxgDTc3Bw8JpzNH-WkoJ4Lim7sB4Op1gEUawAEW4-kenlffwsNr_3b3aV0YuusLpxB03sxPzQ5B0CWNiVtbja1Z4tWhKGUUrdq_eUgMV0y5Of-BqNi5FspAQnhJBFSSxtOzRGV1h3qyUTksfZyed9z8zPI-ZPP9XXm7hYgJgDz_kxte-NfS9UG9q5AZetHUN4kGxXutjjzfUQZ9yTvhBKgKgTI2Dp_R_jZrWQ8F1BoWzIJzjddT1K2MvCQEkARYw08isbOeFmCwgVUcjxYZO45WyOmLQA7QJRL9WvA=",
"type": "validator",
"lastSeen": 1575915097388409000
"lastSeen": 1575915097388409000,
"version": "0.1.0"
},
{
"host": "3.9.222.1:4000",
"pubKey": "AAAAAAAAAAQKwAECSqKy8I8KkSYIBSctxRBRxuR61PpAOwK0UQtkeuPRdwusAyaoBbvv1IBWyMEhvbgT4CtgUnGfYH2s06CIJ09lWWvQ0Jkgthq12mG73H9QSTNM8RITlF1X5ax9BV0EK34M5dUncn1uEYzJzcbaLjUarf2bqoy906dtQpppUWDRLJI6ycw7rKKJ4ZNUhgi4KAEGBsSgLqc0zDKs0rArwouZyz4ofoWnY68mdJKrVy6Zqz83DSdc7B2hqqkHX_Bfeb4SwAECh9xcxpjOp1r7kiNIgrI9GgAlvXwgHkTchOxUiyOzTq6FDWdGN64KiC3NDeyGTg8FmzvGzS3jREeJqOdr4G9ZGtWkauAITgLFiH62t-YntRslhr8_1shxlmzKiNKJN_QFflEq79pZIlWtp3N8LIHMvXRtl-zt2DMze4s02XDmEkviyVE4CkQUDtCc-2MfPT4JcmEFqtFIxjrXn18SbYg3c6XUQHsGIkuDrKuCTRlpC8kvmM0uVoIeWdmwDlZk4jUawAEJhRwK5ozjqIWRP1bFzBPS9VhaJnfKU9PeFYtN5beiAHrYr2ylIB3yDfmAQUdKDowDUm5nfJATejEjEnrTGxh70QtfoNV391rSns3F71tBwY62KLaNr8qnVfeSFHV3FcQTMHHF_8mDb5_11Rj6aiMvW0y6eetHo7CDPMdEyDPmok_U2ZM5BzOUnwjT21HtnvcKxKKwHJ_QGfnAHPyDIhNOMgxJCrVazOidLCHeYGpyCLw1ipeTyKOQX0_ByB8dH6AawAEGV1GuF5SSlT67B1ityPJK2ZwXjeeKB4gGdCG3qRtWxLTZfGhVm7YAYm2f5tw_wrsJAZ9FubVhateGg0ZN67NxZtsvOOejXz6743f7ijnQopPgd_8pH-iVf6BEcSO8ZdcHxNRUTayzjVLs99bwMo2zaPevW4X4G_bN4mh---aPkdGYHwaiklzUhqJ-eqycrYAFyjyEXaPBXLQm1rpczqluNvnKbd8Q9LZWukgm7_uWv_HxufIvdWgoq8bAt78UU3oawAEP9VDehhqrQG5-WHMB66XVxo1TgMM8aVV0SwAq3lCRkpiFBz_9kw8T1F9Hx2AiNrEGT1QLbdMkpms1cG_5gBBahQofdt_NmUs1jfTFXY9iyMy1Q7A6ZYaLP8Z6q-orc1cKqySY-BJZQ_CpGFfXS0OVniFDQ6v78ytPK7K-yRgT1PxFgm3rZqrG0Tjbrpsg2PUL5S5fuXfMhUosP0uoLj0D1guWAR9Y7kfFBIXaTSFMoa8fghVBUTRNhK9f72a8SxQawAEOiv71taLjKqaaWQ_QjcDhWbvjG1EnsCyI0toNjGkcF19x4Vk-5NC96_4ioUGz404IC0XN03roRnibRT_78D9vZFVCWCqve9EjdF5TcApx03zIP4JT2g2q0MKIGgGrwt4Pz6LO6yOfMm7B8Yraps8IV-nP1w7K1m9XKP_FvH8egl5GHJe-_omlC2YyL_b28jMLENbxDFD-3KPjZFBhSLrRukX2PlayYTwEiTtokA2R9_11vQvJgP8KFEjGHg6zsAMawAEBn2H_hz2knb8ltnpEA5YSKVcV3nUtojkCNi_WUz7xUKd7efw1oI_lbnKrS7HkyC0JkQUZ1pCWUlSXNmgjMEhsn823a1LFzpV7rOv4vayYvvFX61hB9R78VjpyxJiYpDwRZLiUY3AK4WY8NqFDbjXR7rT4CkFHEf-VhSQQ8ZNvlpod1nmeVQVizHH9e7Tq7wsWz-LWEk3Hx6LmcrgDsL79LZYG9JXU5IdvG8RvLNx9cSwEI8yxcchpISAaot7UoYQ=",
"type": "validator",
"lastSeen": 1575915094734973000
"lastSeen": 1575915094734973000,
"version": "0.1.0"
},
{
"host": "3.9.102.214:4000",
"pubKey": "AAAAAAAAAAIKwAECSqKy8I8KkSYIBSctxRBRxuR61PpAOwK0UQtkeuPRdwusAyaoBbvv1IBWyMEhvbgT4CtgUnGfYH2s06CIJ09lWWvQ0Jkgthq12mG73H9QSTNM8RITlF1X5ax9BV0EK34M5dUncn1uEYzJzcbaLjUarf2bqoy906dtQpppUWDRLJI6ycw7rKKJ4ZNUhgi4KAEGBsSgLqc0zDKs0rArwouZyz4ofoWnY68mdJKrVy6Zqz83DSdc7B2hqqkHX_Bfeb4SwAEOVCUN3EwiVroS5-TOq2o7hYSxphK9X0G23N-IBZ0Tr1Rl8XEiJ-OEy0rqnAKwmhAZJWnx3u8oXqbZtOWIZmzQSpcoxhgwfhdmTZJCqT2RVzZyeFItX4sVeilEP3z2xdsJs8-a1kg6UZnx1s1BNLBo7eZrreZygWojPCIDBn03fSAflXoVc5PpY2CGy5MA_IgWgSYBHDdoZEtigp_amjqK7Us44Db20XpLxMXfbahiqa7WKNnMgi6Ca2H67VtaaD8awAEF3zbE1nZRAa7a8vbU25c80YBYJBaW8P6FwXQI-K0Xk5MakwYeMMnIrm6w6IS_0XAO5YlD453GLqnxY8H1BEnRpfOnT7PE4el9mJ8MuYQMo6R2up0lGCmYM0YA9FORjroM3ng69SEPfJPCReG7LfJkERl_m2U403ertDRBYrlqCDagDfyI500srBcMrjSvV3oNouyyx3yZUrjLQfbHhDteQFsYdmakJs8Y-Q9-5MXCcrz6Qa4xwv522Euv0CCxkHcawAEYjfsU_zDhUZA1ey1aquWXlFOnx-iEALqxW1slDYHwQ1M2SILc-v_E6i1doa5e_bAZHVezBHFAlaNAVedNyHFFJxYAqAK3hbzbvl2glw3Q6h_rTXElymloqtaqVFIJ-oUWWOHsZBmu8EDA-HzvGCiBa_GbRaVfh2lE4ObeMXoJrEm_5dbxxeEic2l3IYeIz40N9ooQQOkQcOZdY4AXWYCavIAwWEJBjLtptJgCLu9a_zM1S5GsiyJHpdDs46WbP0EawAEWZ-95Sf0YAHujxRNLdXgpqe0ZF8loVwzZfvyMvqaxF1Ug274BqHuY_c5NdPAzuqoTwjfEn8NKEoaNqlumM75FUYbaTd7mXvk4WVYWjVnkO40dfQjRB7DYhvj0LBlbndAJ4wJIA2ilPYgjZsXVbNNh3e2j3u9eABd0VaFMbSb8Sz5_31r8HzoWmPJs3HiyuyANGFUA6CvAnMN6K3b-D8BhFZU_nPUTgu80o8_n6LQt-XWbaC_mTHzsnOjzBiPJxlYawAEW3bmOEtStH2T8q7vMkhchImp2-hg9MFYGBmEe9sSByTn3NUf8eksqXOC1dUjHkXoZm298FgUYLkNdnlxWpf993j5mEDoFxjcTB7scBD7k6nu6Nrs_wK0-seS8gsHrx9UK7GwAsi10q82Cm4PFyAtrWjmy_d9WLHuZt6VIOKunTs8cf0FwNUiMcvZsruqIFJcP7iWxdiFdUkh65P_iCz1ZEjJcj2GEZoq4v3a3by1aizGPaaiKc1jd_T-XJg_YpncawAEWnstu5b9WiZv0x8xfsiMk6YRlU0Cnj5svxLLXz_8drvwAa--GBY5yH0ke2EM6udMEi2EPeFcGTe6Sjs0YEhSbY7Uad_8suD2J4tIWJSWBbiyvh7rSqzv57m7BlsVcHfQJn_wNH-UlC9xkx8vg-LwfN8_FlxvHNPTc7XZG3lKYbwpUWlZxAziOYT1VQ-2K2bQQBBMdix-ht_SjccL1Dc2dP5kDazQ8yZV_8xnyeheazEedWe63uutfkHlZRg9YwP8=",
"type": "validator",
"lastSeen": 1575915094967382800
"lastSeen": 1575915094967382800,
"version": "0.1.0"
}
],
"mixNodes": [
@@ -101,42 +105,49 @@ mod topology_requests {
"host": "35.176.155.107:1789",
"pubKey": "zSob16499jT7C3S3ky4GihNOjlU6aLfSRkf1xAxOwV0=",
"layer": 3,
"lastSeen": 1575915096805374500
"lastSeen": 1575915096805374500,
"version": "0.1.0"
},
{
"host": "18.130.86.190:1789",
"pubKey": "vCdpFc0NvW0NSqsuTxtjFtiSY35aXesgT3JNA8sSIXk=",
"layer": 1,
"lastSeen": 1575915097370376000
"lastSeen": 1575915097370376000,
"version": "0.1.0"
},
{
"host": "3.10.22.152:1789",
"pubKey": "OwOqwWjh_IlnaWS2PxO6odnhNahOYpRCkju50beQCTA=",
"layer": 1,
"lastSeen": 1575915097639423500
"lastSeen": 1575915097639423500,
"version": "0.1.0"
},
{
"host": "35.178.213.77:1789",
"pubKey": "nkkrUjgL8UJk05QydvWvFSvtRB6nmeV8RMvH5540J3s=",
"layer": 2,
"lastSeen": 1575915097895166500
"lastSeen": 1575915097895166500,
"version": "0.1.0"
},
{
"host": "52.56.99.196:1789",
"pubKey": "whHuBuEc6zyOZOquKbuATaH4Crml61V_3Y-MztpWhF4=",
"layer": 2,
"lastSeen": 1575915096255174700
"lastSeen": 1575915096255174700,
"version": "0.1.0"
},
{
"host": "3.9.12.238:1789",
"pubKey": "vk5Sr-Xyi0cTbugACv8U42ZJ6hs6cGDox0rpmXY94Fc=",
"layer": 3,
"lastSeen": 1575915096497827600
"lastSeen": 1575915096497827600,
"version": "0.1.0"
}
],
"mixProviderNodes": [
{
"host": "3.8.176.11:1789",
"clientListener": "3.8.176.11:8888",
"mixnetListener": "3.8.176.11:9999",
"pubKey": "54U6krAr-j9nQXFlsHk3io04_p0tctuqH71t7w_usgI=",
"registeredClients": [
{
@@ -248,10 +259,12 @@ mod topology_requests {
"pubKey": "COGdpfhmzNGR6YX820GqJIkjOihL8mr6-h-d3JlTDFA="
}
],
"lastSeen": 1575915097358694100
"lastSeen": 1575915097358694100,
"version": "0.1.0"
},
{
"host": "35.178.212.193:1789",
"clientListener": "3.8.176.12:8888",
"mixnetListener": "3.8.176.12:9999",
"pubKey": "sA-sxi038pEbGy4lgZWG-RdHHDkA6kZzu44G0LUxFSc=",
"registeredClients": [
{
@@ -297,7 +310,8 @@ mod topology_requests {
"pubKey": "w1bfLpnd3rWu5JczB0nQfnE2S6nUCbx2AA7HDE48DQo="
}
],
"lastSeen": 1575915097869025000
"lastSeen": 1575915097869025000,
"version": "0.1.0"
}
]
}"#.to_string()
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "mix-client"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.8"
tokio = { version = "0.2", features = ["full"] }
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
@@ -1,4 +1,4 @@
use sphinx::route::NodeAddressBytes;
use log::*;
use sphinx::SphinxPacket;
use std::net::SocketAddr;
use tokio::prelude::*;
@@ -18,7 +18,7 @@ impl MixClient {
) -> Result<(), Box<dyn std::error::Error>> {
let bytes = packet.to_bytes();
println!("socket addr: {:?}", mix_addr);
info!("socket addr: {:?}", mix_addr);
let mut stream = tokio::net::TcpStream::connect(mix_addr).await?;
stream.write_all(&bytes[..]).await?;
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "provider-client"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
futures = "0.3.1"
log = "0.4.8"
tokio = { version = "0.2", features = ["full"] }
## internal
sfw-provider-requests = { path = "../../../sfw-provider/sfw-provider-requests" }
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
@@ -1,4 +1,5 @@
use futures::io::Error;
use log::info;
use sfw_provider_requests::requests::{ProviderRequest, PullRequest, RegisterRequest};
use sfw_provider_requests::responses::{
ProviderResponse, ProviderResponseError, PullResponse, RegisterResponse,
@@ -51,13 +52,8 @@ impl ProviderClient {
our_address: DestinationAddressBytes,
auth_token: Option<AuthToken>,
) -> Self {
// DH temporary: the provider's client port is not in the topology, but we can't change that
// right now without messing up the existing Go mixnet. So I'm going to hardcode this
// for the moment until the Go mixnet goes away.
let provider_socket = SocketAddr::new(provider_network_address.ip(), 9000);
ProviderClient {
provider_network_address: provider_socket,
provider_network_address,
our_address,
auth_token,
}
@@ -69,7 +65,7 @@ impl ProviderClient {
pub async fn send_request(&self, bytes: Vec<u8>) -> Result<Vec<u8>, ProviderClientError> {
let mut socket = tokio::net::TcpStream::connect(self.provider_network_address).await?;
println!("keep alive: {:?}", socket.keepalive());
info!("keep alive: {:?}", socket.keepalive());
socket.set_keepalive(Some(Duration::from_secs(2))).unwrap();
socket.write_all(&bytes[..]).await?;
if let Err(_e) = socket.shutdown(Shutdown::Write) {
@@ -96,7 +92,7 @@ impl ProviderClient {
let bytes = pull_request.to_bytes();
let response = self.send_request(bytes).await?;
println!("Received the following response: {:?}", response);
info!("Received the following response: {:?}", response);
let parsed_response = PullResponse::from_bytes(&response)?;
Ok(parsed_response.messages)
@@ -0,0 +1,9 @@
[package]
name = "validator-client"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>, David Hrycyszyn <dave@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
@@ -0,0 +1,7 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "crypto"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
curve25519-dalek = "1.2.3"
rand = "0.7.2"
rand_os = "0.1"
+39
View File
@@ -0,0 +1,39 @@
use crate::PemStorable;
pub mod x25519;
pub trait MixnetEncryptionKeyPair<Priv, Pub>
where
Priv: MixnetEncryptionPrivateKey,
Pub: MixnetEncryptionPublicKey,
{
fn new() -> Self;
fn private_key(&self) -> &Priv;
fn public_key(&self) -> &Pub;
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self;
// TODO: encryption related methods
}
pub trait MixnetEncryptionPublicKey:
Sized + PemStorable + for<'a> From<&'a <Self as MixnetEncryptionPublicKey>::PrivateKeyMaterial>
{
// we need to couple public and private keys together
type PrivateKeyMaterial: MixnetEncryptionPrivateKey<PublicKeyMaterial = Self>;
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
pub trait MixnetEncryptionPrivateKey: Sized + PemStorable {
// we need to couple public and private keys together
type PublicKeyMaterial: MixnetEncryptionPublicKey<PrivateKeyMaterial = Self>;
/// Returns the associated public key
fn public_key(&self) -> Self::PublicKeyMaterial {
self.into()
}
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
+98
View File
@@ -0,0 +1,98 @@
use crate::encryption::{
MixnetEncryptionKeyPair, MixnetEncryptionPrivateKey, MixnetEncryptionPublicKey,
};
use crate::PemStorable;
use curve25519_dalek::montgomery::MontgomeryPoint;
use curve25519_dalek::scalar::Scalar;
// TODO: ensure this is a proper name for this considering we are not implementing entire DH here
const CURVE_GENERATOR: MontgomeryPoint = curve25519_dalek::constants::X25519_BASEPOINT;
pub struct KeyPair {
pub(crate) private_key: PrivateKey,
pub(crate) public_key: PublicKey,
}
impl MixnetEncryptionKeyPair<PrivateKey, PublicKey> for KeyPair {
fn new() -> Self {
let mut rng = rand_os::OsRng::new().unwrap();
let private_key_value = Scalar::random(&mut rng);
let public_key_value = CURVE_GENERATOR * private_key_value;
KeyPair {
private_key: PrivateKey(private_key_value),
public_key: PublicKey(public_key_value),
}
}
fn private_key(&self) -> &PrivateKey {
&self.private_key
}
fn public_key(&self) -> &PublicKey {
&self.public_key
}
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self {
KeyPair {
private_key: PrivateKey::from_bytes(priv_bytes),
public_key: PublicKey::from_bytes(pub_bytes),
}
}
}
// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct PrivateKey(pub Scalar);
impl MixnetEncryptionPrivateKey for PrivateKey {
type PublicKeyMaterial = PublicKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
fn from_bytes(b: &[u8]) -> Self {
let mut bytes = [0; 32];
bytes.copy_from_slice(&b[..]);
let key = Scalar::from_canonical_bytes(bytes).unwrap();
Self(key)
}
}
impl PemStorable for PrivateKey {
fn pem_type(&self) -> String {
String::from("X25519 PRIVATE KEY")
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PublicKey(pub MontgomeryPoint);
impl<'a> From<&'a PrivateKey> for PublicKey {
fn from(pk: &'a PrivateKey) -> Self {
PublicKey(CURVE_GENERATOR * pk.0)
}
}
impl MixnetEncryptionPublicKey for PublicKey {
type PrivateKeyMaterial = PrivateKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
fn from_bytes(b: &[u8]) -> Self {
let mut bytes = [0; 32];
bytes.copy_from_slice(&b[..]);
let key = MontgomeryPoint(bytes);
Self(key)
}
}
impl PemStorable for PublicKey {
fn pem_type(&self) -> String {
String::from("X25519 PUBLIC KEY")
}
}
+148
View File
@@ -0,0 +1,148 @@
use crate::encryption::{
MixnetEncryptionKeyPair, MixnetEncryptionPrivateKey, MixnetEncryptionPublicKey,
};
use crate::{encryption, PemStorable};
use curve25519_dalek::scalar::Scalar;
pub trait MixnetIdentityKeyPair<Priv, Pub>
where
Priv: MixnetIdentityPrivateKey,
Pub: MixnetIdentityPublicKey,
{
fn new() -> Self;
fn private_key(&self) -> &Priv;
fn public_key(&self) -> &Pub;
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self;
// TODO: signing related methods
}
pub trait MixnetIdentityPublicKey:
Sized + PemStorable + for<'a> From<&'a <Self as MixnetIdentityPublicKey>::PrivateKeyMaterial>
{
// we need to couple public and private keys together
type PrivateKeyMaterial: MixnetIdentityPrivateKey<PublicKeyMaterial = Self>;
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
pub trait MixnetIdentityPrivateKey: Sized + PemStorable {
// we need to couple public and private keys together
type PublicKeyMaterial: MixnetIdentityPublicKey<PrivateKeyMaterial = Self>;
/// Returns the associated public key
fn public_key(&self) -> Self::PublicKeyMaterial {
self.into()
}
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
// same for validator
// for time being define a dummy identity using x25519 encryption keys (as we've done so far)
// and replace it with proper keys, like ed25519 later on
pub struct DummyMixIdentityKeyPair {
pub private_key: DummyMixIdentityPrivateKey,
pub public_key: DummyMixIdentityPublicKey,
}
impl MixnetIdentityKeyPair<DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey>
for DummyMixIdentityKeyPair
{
fn new() -> Self {
let keypair = encryption::x25519::KeyPair::new();
DummyMixIdentityKeyPair {
private_key: DummyMixIdentityPrivateKey(keypair.private_key),
public_key: DummyMixIdentityPublicKey(keypair.public_key),
}
}
fn private_key(&self) -> &DummyMixIdentityPrivateKey {
&self.private_key
}
fn public_key(&self) -> &DummyMixIdentityPublicKey {
&self.public_key
}
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self {
DummyMixIdentityKeyPair {
private_key: DummyMixIdentityPrivateKey::from_bytes(priv_bytes),
public_key: DummyMixIdentityPublicKey::from_bytes(pub_bytes),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DummyMixIdentityPublicKey(encryption::x25519::PublicKey);
impl MixnetIdentityPublicKey for DummyMixIdentityPublicKey {
type PrivateKeyMaterial = DummyMixIdentityPrivateKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
fn from_bytes(b: &[u8]) -> Self {
Self(encryption::x25519::PublicKey::from_bytes(b))
}
}
impl PemStorable for DummyMixIdentityPublicKey {
fn pem_type(&self) -> String {
format!("DUMMY KEY BASED ON {}", self.0.pem_type())
}
}
impl DummyMixIdentityPublicKey {
pub fn to_b64_string(&self) -> String {
base64::encode_config(&self.to_bytes(), base64::URL_SAFE)
}
#[allow(dead_code)]
fn from_b64_string(val: String) -> Self {
Self::from_bytes(&base64::decode_config(&val, base64::URL_SAFE).unwrap())
}
}
// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct DummyMixIdentityPrivateKey(pub encryption::x25519::PrivateKey);
impl<'a> From<&'a DummyMixIdentityPrivateKey> for DummyMixIdentityPublicKey {
fn from(pk: &'a DummyMixIdentityPrivateKey) -> Self {
let private_ref = &pk.0;
let public: encryption::x25519::PublicKey = private_ref.into();
DummyMixIdentityPublicKey(public)
}
}
impl MixnetIdentityPrivateKey for DummyMixIdentityPrivateKey {
type PublicKeyMaterial = DummyMixIdentityPublicKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
fn from_bytes(b: &[u8]) -> Self {
Self(encryption::x25519::PrivateKey::from_bytes(b))
}
}
// TODO: this will be implemented differently by using the proper trait
impl DummyMixIdentityPrivateKey {
pub fn as_scalar(self) -> Scalar {
let encryption_key = self.0;
encryption_key.0
}
}
impl PemStorable for DummyMixIdentityPrivateKey {
fn pem_type(&self) -> String {
format!("DUMMY KEY BASED ON {}", self.0.pem_type())
}
}
+9
View File
@@ -0,0 +1,9 @@
pub mod encryption;
pub mod identity;
// TODO: this trait will need to be moved elsewhere, probably to some 'persistence' crate
// but since it will need to be used by all identities, it's not really appropriate if it lived in nym-client
pub trait PemStorable {
fn pem_type(&self) -> String;
}
+29
View File
@@ -0,0 +1,29 @@
[package]
name = "healthcheck"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
futures = "0.3.1"
itertools = "0.8.2"
log = "0.4.8"
serde = "1.0.104"
serde_derive = "1.0.104"
tokio = { version = "0.2", features = ["full"] }
## internal
addressing = {path = "../addressing" }
crypto = { path = "../crypto" }
directory-client = { path = "../clients/directory-client" }
mix-client = { path = "../clients/mix-client" }
provider-client = { path = "../clients/provider-client" }
sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" }
topology = {path = "../topology" }
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
[dev-dependencies]
+15
View File
@@ -0,0 +1,15 @@
use serde_derive::Deserialize;
#[derive(Deserialize, Debug)]
pub struct HealthCheck {
#[serde(rename(deserialize = "directory-server"))]
pub directory_server: String,
pub interval: f64, // in seconds
#[serde(rename(deserialize = "resolution-timeout"))]
pub resolution_timeout: f64, // in seconds
#[serde(rename(deserialize = "test-packets-per-node"))]
pub num_test_packets: usize,
}
+95
View File
@@ -0,0 +1,95 @@
use crate::result::HealthCheckResult;
use directory_client::requests::presence_topology_get::PresenceTopologyGetRequester;
use directory_client::DirectoryClient;
use log::{debug, error, info, trace};
use std::fmt::{Error, Formatter};
use std::time::Duration;
use topology::NymTopologyError;
pub mod config;
mod path_check;
mod result;
mod score;
#[derive(Debug)]
pub enum HealthCheckerError {
FailedToObtainTopologyError,
InvalidTopologyError,
}
// required by std::error::Error
impl std::fmt::Display for HealthCheckerError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
// just have implementation equivalent to derived debug
write!(f, "{:?}", self)
}
}
impl std::error::Error for HealthCheckerError {}
impl From<topology::NymTopologyError> for HealthCheckerError {
fn from(_: NymTopologyError) -> Self {
use HealthCheckerError::*;
InvalidTopologyError
}
}
pub struct HealthChecker {
directory_client: directory_client::Client,
interval: Duration,
num_test_packets: usize,
resolution_timeout: Duration,
}
impl HealthChecker {
pub fn new(config: config::HealthCheck) -> Self {
debug!(
"healthcheck will be using the following directory server: {:?}",
config.directory_server
);
let directory_client_config = directory_client::Config::new(config.directory_server);
HealthChecker {
directory_client: directory_client::Client::new(directory_client_config),
interval: Duration::from_secs_f64(config.interval),
resolution_timeout: Duration::from_secs_f64(config.resolution_timeout),
num_test_packets: config.num_test_packets,
}
}
pub async fn do_check(&self) -> Result<HealthCheckResult, HealthCheckerError> {
trace!("going to perform a healthcheck!");
let current_topology = match self.directory_client.presence_topology.get() {
Ok(topology) => topology,
Err(err) => {
error!("failed to obtain topology - {:?}", err);
return Err(HealthCheckerError::FailedToObtainTopologyError);
}
};
trace!("current topology: {:?}", current_topology);
let mut healthcheck_result = HealthCheckResult::calculate(
current_topology,
self.num_test_packets,
self.resolution_timeout,
)
.await;
healthcheck_result.sort_scores();
Ok(healthcheck_result)
}
pub async fn run(self) -> Result<(), HealthCheckerError> {
debug!(
"healthcheck will run every {:?} and will send {} packets to each node",
self.interval, self.num_test_packets
);
loop {
match self.do_check().await {
Ok(health) => info!("current network health: \n{}", health),
Err(err) => error!("failed to perform healthcheck - {:?}", err),
};
tokio::time::delay_for(self.interval).await;
}
}
}
+246
View File
@@ -0,0 +1,246 @@
use crypto::identity::{DummyMixIdentityKeyPair, MixnetIdentityKeyPair, MixnetIdentityPublicKey};
use itertools::Itertools;
use log::{debug, error, trace, warn};
use mix_client::MixClient;
use provider_client::ProviderClient;
use sphinx::header::delays::Delay;
use sphinx::route::{Destination, Node as SphinxNode};
use std::collections::HashMap;
use topology::MixProviderNode;
#[derive(Debug, PartialEq, Clone)]
pub enum PathStatus {
Healthy,
Unhealthy,
Pending,
}
pub(crate) struct PathChecker {
provider_clients: HashMap<[u8; 32], Option<ProviderClient>>,
// currently this is an overkill as MixClient is extremely cheap to create,
// however, once we introduce persistent connection between client and layer one mixes,
// this will be extremely helpful to have
layer_one_clients: HashMap<[u8; 32], Option<MixClient>>,
paths_status: HashMap<Vec<u8>, PathStatus>,
our_destination: Destination,
}
impl PathChecker {
pub(crate) async fn new(
providers: Vec<MixProviderNode>,
ephemeral_keys: DummyMixIdentityKeyPair,
) -> Self {
let mut provider_clients = HashMap::new();
let mut temporary_address = [0u8; 32];
let public_key_bytes = ephemeral_keys.public_key().to_bytes();
temporary_address.copy_from_slice(&public_key_bytes[..]);
for provider in providers {
let mut provider_client =
ProviderClient::new(provider.client_listener, temporary_address, None);
let insertion_result = match provider_client.register().await {
Ok(token) => {
debug!("registered at provider {}", provider.pub_key);
provider_client.update_token(token);
provider_clients.insert(provider.get_pub_key_bytes(), Some(provider_client))
}
Err(err) => {
warn!(
"failed to register at provider {} - {:?}",
provider.pub_key, err
);
provider_clients.insert(provider.get_pub_key_bytes(), None)
}
};
if insertion_result.is_some() {
error!("provider {} already existed!", provider.pub_key);
}
}
PathChecker {
provider_clients,
layer_one_clients: HashMap::new(),
our_destination: Destination::new(temporary_address, Default::default()),
paths_status: HashMap::new(),
}
}
// iteration is used to distinguish packets sent through the same path (as the healthcheck
// may try to send say 10 packets through given path)
fn unique_path_key(path: &Vec<SphinxNode>, iteration: u8) -> Vec<u8> {
std::iter::once(iteration)
.chain(
path.iter()
.map(|node| node.pub_key.to_bytes().to_vec())
.flatten(),
)
.collect()
}
pub(crate) fn path_key_to_node_keys(path_key: Vec<u8>) -> Vec<[u8; 32]> {
assert_eq!(path_key.len() % 32, 1);
path_key
.into_iter()
.skip(1) // remove first byte as it represents the iteration number which we do not care about now
.chunks(32)
.into_iter()
.map(|key_chunk| {
let key_chunk_vec: Vec<_> = key_chunk.collect();
let mut key = [0u8; 32];
key.copy_from_slice(&key_chunk_vec);
key
})
.collect()
}
fn update_path_statuses(&mut self, messages: Vec<Vec<u8>>) {
for msg in messages.into_iter() {
// mark path as healthy
let previous_status = self.paths_status.insert(msg, PathStatus::Healthy);
match previous_status {
None => warn!("we received information about unknown path! - perhaps somebody is messing with healthchecker?"),
Some(status) => {
if status != PathStatus::Pending {
warn!("we received information about path that WASN'T in PENDING state! (it was in {:?}", status);
}
}
}
}
}
// consume path_checker and return all path statuses
pub(crate) fn get_all_statuses(self) -> HashMap<Vec<u8>, PathStatus> {
self.paths_status
}
// pull messages from given provider until there are no more 'real' messages
async fn resolve_pending_provider_checks(
&self,
provider_client: &ProviderClient,
) -> Vec<Vec<u8>> {
// keep getting messages until we encounter the dummy message
let mut provider_messages = Vec::new();
loop {
match provider_client.retrieve_messages().await {
Err(err) => {
error!("failed to fetch provider messages! - {:?}", err);
break;
}
Ok(messages) => {
let mut should_stop = false;
for msg in messages.into_iter() {
trace!("received provider response: {:?}", msg);
if msg == sfw_provider_requests::DUMMY_MESSAGE_CONTENT {
// finish iterating the loop as the messages might not be ordered
should_stop = true;
} else {
provider_messages.push(msg);
}
}
if should_stop {
break;
}
}
}
}
provider_messages
}
pub(crate) async fn resolve_pending_checks(&mut self) {
// not sure how to nicely put it into an iterator due to it being async calls
let mut provider_messages = Vec::new();
for provider_client in self.provider_clients.values() {
// if it was none all associated paths were already marked as unhealthy
if provider_client.is_some() {
let pc = provider_client.as_ref().unwrap();
provider_messages.extend(self.resolve_pending_provider_checks(pc).await);
}
}
self.update_path_statuses(provider_messages);
}
pub(crate) async fn send_test_packet(&mut self, path: &Vec<SphinxNode>, iteration: u8) {
debug!("Checking path: {:?} ({})", path, iteration);
let path_identifier = PathChecker::unique_path_key(path, iteration);
// check if there is even any point in sending the packet
// does provider exist?
let provider_client = self
.provider_clients
.get(&path.last().unwrap().pub_key.to_bytes())
.unwrap();
if provider_client.is_none() {
debug!("we can ignore this path as provider itself is inaccessible");
if self
.paths_status
.insert(path_identifier, PathStatus::Unhealthy)
.is_some()
{
panic!("Overwriting path checks!")
}
return;
}
let layer_one_mix = path.first().unwrap();
let first_node_key = layer_one_mix.pub_key.to_bytes();
let first_node_address =
addressing::socket_address_from_encoded_bytes(layer_one_mix.address.to_bytes());
let first_node_client = self
.layer_one_clients
.entry(first_node_key)
.or_insert(Some(mix_client::MixClient::new()));
if first_node_client.is_none() {
debug!("we can ignore this path as layer one mix is inaccessible");
if self
.paths_status
.insert(path_identifier, PathStatus::Unhealthy)
.is_some()
{
panic!("Overwriting path checks!")
}
return;
}
let first_node_client = first_node_client.as_ref().unwrap();
let delays: Vec<_> = path.iter().map(|_| Delay::new(0)).collect();
let packet = sphinx::SphinxPacket::new(
path_identifier.clone(),
&path[..],
&self.our_destination,
&delays,
)
.unwrap();
debug!("sending test packet to {}", first_node_address);
match first_node_client.send(packet, first_node_address).await {
Err(err) => {
warn!("failed to send packet to {} - {}", first_node_address, err);
if self
.paths_status
.insert(path_identifier, PathStatus::Unhealthy)
.is_some()
{
panic!("Overwriting path checks!")
}
}
Ok(_) => {
if self
.paths_status
.insert(path_identifier, PathStatus::Pending)
.is_some()
{
panic!("Overwriting path checks!")
}
}
}
}
}
+160
View File
@@ -0,0 +1,160 @@
use crate::path_check::{PathChecker, PathStatus};
use crate::score::NodeScore;
use crypto::identity::{DummyMixIdentityKeyPair, MixnetIdentityKeyPair};
use log::{debug, error, info, warn};
use sphinx::route::NodeAddressBytes;
use std::collections::HashMap;
use std::fmt::{Error, Formatter};
use std::time::Duration;
use topology::NymTopology;
#[derive(Debug)]
pub struct HealthCheckResult(Vec<NodeScore>);
impl std::fmt::Display for HealthCheckResult {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "NETWORK HEALTH\n==============\n")?;
self.0
.iter()
.for_each(|score| write!(f, "{}\n", score).unwrap());
Ok(())
}
}
impl HealthCheckResult {
pub fn sort_scores(&mut self) {
self.0.sort();
}
fn zero_score<T: NymTopology>(topology: T) -> Self {
warn!("The network is unhealthy, could not send any packets - returning zero score!");
let mixes = topology.get_mix_nodes();
let providers = topology.get_mix_provider_nodes();
let health = mixes
.into_iter()
.map(|node| NodeScore::from_mixnode(node))
.chain(
providers
.into_iter()
.map(|node| NodeScore::from_provider(node)),
)
.collect();
HealthCheckResult(health)
}
// TODO: that is O(n) so maybe not the most efficient considering it will be called n times...
fn node_score(&self, node_key: NodeAddressBytes) -> Option<f64> {
self.0
.iter()
.find(|&node_score| node_score.pub_key() == node_key)
.map(|node| node.score())
}
pub fn filter_topology_by_score<T: NymTopology>(
&self,
topology: &T,
score_threshold: f64,
) -> T {
let filtered_mix_nodes = topology
.get_mix_nodes()
.into_iter()
.filter(|node| {
match self.node_score(NodeAddressBytes::from_b64_string(node.pub_key.clone())) {
None => {
error!("Unknown node in topology - {:?}", node);
false
}
Some(score) => score > score_threshold,
}
})
.collect();
let filtered_provider_nodes = topology
.get_mix_provider_nodes()
.into_iter()
.filter(|node| {
match self.node_score(NodeAddressBytes::from_b64_string(node.pub_key.clone())) {
None => {
error!("Unknown node in topology - {:?}", node);
false
}
Some(score) => score > score_threshold,
}
})
.collect();
// coco nodes remain unchanged as no healthcheck is being run on them or time being
let filtered_coco_nodes = topology.get_coco_nodes();
T::new_from_nodes(
filtered_mix_nodes,
filtered_provider_nodes,
filtered_coco_nodes,
)
}
pub async fn calculate<T: NymTopology>(
topology: T,
iterations: usize,
resolution_timeout: Duration,
) -> Self {
// currently healthchecker supports only up to 255 iterations - if we somehow
// find we need more, it's relatively easy change
assert!(iterations <= 255);
let all_paths = match topology.all_paths() {
Ok(paths) => paths,
Err(_) => return Self::zero_score(topology),
};
// create entries for all nodes
let mut score_map = HashMap::new();
topology.get_mix_nodes().into_iter().for_each(|node| {
score_map.insert(node.get_pub_key_bytes(), NodeScore::from_mixnode(node));
});
topology
.get_mix_provider_nodes()
.into_iter()
.for_each(|node| {
score_map.insert(node.get_pub_key_bytes(), NodeScore::from_provider(node));
});
let ephemeral_keys = DummyMixIdentityKeyPair::new();
let providers = topology.get_mix_provider_nodes();
let mut path_checker = PathChecker::new(providers, ephemeral_keys).await;
for i in 0..iterations {
debug!("running healthcheck iteration {} / {}", i + 1, iterations);
for path in &all_paths {
path_checker.send_test_packet(&path, i as u8).await;
// increase sent count for each node
for node in path {
let current_node_score = score_map.get_mut(&node.pub_key.0).unwrap();
current_node_score.increase_sent_packet_count();
}
}
}
info!(
"waiting {:?} for pending requests to resolve",
resolution_timeout
);
tokio::time::delay_for(resolution_timeout).await;
path_checker.resolve_pending_checks().await;
let all_statuses = path_checker.get_all_statuses();
for (path_key, status) in all_statuses.into_iter() {
let node_keys = PathChecker::path_key_to_node_keys(path_key);
for node in node_keys {
if status == PathStatus::Healthy {
let current_node_score = score_map.get_mut(&node).unwrap();
current_node_score.increase_received_packet_count();
}
}
}
HealthCheckResult(score_map.into_iter().map(|(_, v)| v).collect())
}
}
+165
View File
@@ -0,0 +1,165 @@
use log::error;
use sphinx::route::NodeAddressBytes;
use std::cmp::Ordering;
use std::fmt::Error;
use std::fmt::Formatter;
use std::net::SocketAddr;
use topology::{MixNode, MixProviderNode};
// TODO: should 'nodetype' really be part of healthcheck::score
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
pub(crate) enum NodeType {
Mix,
MixProvider,
}
impl std::fmt::Display for NodeType {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
NodeType::Mix => write!(f, "Mix"),
NodeType::MixProvider => write!(f, "MixProvider"),
}
}
}
#[derive(Debug, Eq)]
pub(crate) struct NodeScore {
typ: NodeType,
pub_key: NodeAddressBytes,
addresses: Vec<SocketAddr>,
version: String,
layer: String,
packets_sent: u64,
packets_received: u64,
}
impl Ord for NodeScore {
// order by: version, layer, sent, received, pubkey; ignore addresses
fn cmp(&self, other: &Self) -> Ordering {
if self.typ > other.typ {
return Ordering::Greater;
} else if self.typ < other.typ {
return Ordering::Less;
}
if self.version > other.version {
return Ordering::Greater;
} else if self.version < other.version {
return Ordering::Less;
}
if self.layer > other.layer {
return Ordering::Greater;
} else if self.layer < other.layer {
return Ordering::Less;
}
if self.packets_sent > other.packets_sent {
return Ordering::Greater;
} else if self.packets_sent < other.packets_sent {
return Ordering::Less;
}
if self.packets_received > other.packets_received {
return Ordering::Greater;
} else if self.packets_received < other.packets_received {
return Ordering::Less;
}
if self.pub_key > other.pub_key {
return Ordering::Greater;
} else if self.pub_key < other.pub_key {
return Ordering::Less;
}
Ordering::Equal
}
}
impl PartialOrd for NodeScore {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for NodeScore {
fn eq(&self, other: &Self) -> bool {
self.typ == other.typ
&& self.pub_key == other.pub_key
&& self.addresses == other.addresses
&& self.version == other.version
&& self.layer == other.layer
&& self.packets_sent == other.packets_sent
&& self.packets_received == other.packets_received
}
}
impl NodeScore {
pub(crate) fn from_mixnode(node: MixNode) -> Self {
NodeScore {
typ: NodeType::Mix,
pub_key: NodeAddressBytes::from_b64_string(node.pub_key),
addresses: vec![node.host],
version: node.version,
layer: format!("layer {}", node.layer),
packets_sent: 0,
packets_received: 0,
}
}
pub(crate) fn from_provider(node: MixProviderNode) -> Self {
NodeScore {
typ: NodeType::MixProvider,
pub_key: NodeAddressBytes::from_b64_string(node.pub_key),
addresses: vec![node.mixnet_listener, node.client_listener],
version: node.version,
layer: format!("provider"),
packets_sent: 0,
packets_received: 0,
}
}
pub(crate) fn increase_sent_packet_count(&mut self) {
self.packets_sent += 1;
}
pub(crate) fn increase_received_packet_count(&mut self) {
self.packets_received += 1;
}
pub(crate) fn score(&self) -> f64 {
match self.packets_sent {
0 => 0.0,
_ => (self.packets_received as f64 / self.packets_sent as f64) * 100.0,
}
}
pub(crate) fn pub_key(&self) -> NodeAddressBytes {
self.pub_key.clone()
}
}
impl std::fmt::Display for NodeScore {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
let fmtd_addresses = match self.addresses.len() {
1 => format!("{}", self.addresses[0]),
2 => format!("{}, {}", self.addresses[0], self.addresses[1]),
n => {
error!(
"could not format score - node has {} addresses while only 1 or 2 are allowed!",
n
);
return Err(std::fmt::Error);
}
};
let stringified_key = self.pub_key.to_b64_string();
write!(
f,
"({})\t{}/{}\t({}%)\t|| {}\tv{} <{}> - {}",
self.typ,
self.packets_received,
self.packets_sent,
self.score(),
self.layer,
self.version,
fmtd_addresses,
stringified_key,
)
}
}
+21
View File
@@ -0,0 +1,21 @@
[package]
name = "topology"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
curve25519-dalek = "1.2.3"
itertools = "0.8.2"
log = "0.4.8"
rand = "0.7.2"
serde = { version = "1.0.104", features = ["derive"] }
## internal
addressing = {path = "../addressing"}
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
+204
View File
@@ -0,0 +1,204 @@
use addressing;
use curve25519_dalek::montgomery::MontgomeryPoint;
use itertools::Itertools;
use rand::seq::IteratorRandom;
use sphinx::route::{Node as SphinxNode, NodeAddressBytes};
use std::cmp::max;
use std::collections::HashMap;
use std::net::SocketAddr;
#[derive(Debug, Clone)]
pub struct MixNode {
pub host: SocketAddr,
pub pub_key: String,
pub layer: u64,
pub last_seen: u64,
pub version: String,
}
impl Into<SphinxNode> for MixNode {
fn into(self) -> SphinxNode {
let address_bytes = addressing::encoded_bytes_from_socket_address(self.host);
let key_bytes = self.get_pub_key_bytes();
let key = MontgomeryPoint(key_bytes);
SphinxNode::new(NodeAddressBytes::from_bytes(address_bytes), key)
}
}
impl MixNode {
pub fn get_pub_key_bytes(&self) -> [u8; 32] {
let decoded_key_bytes = base64::decode_config(&self.pub_key, base64::URL_SAFE).unwrap();
let mut key_bytes = [0; 32];
key_bytes.copy_from_slice(&decoded_key_bytes[..]);
key_bytes
}
}
#[derive(Debug, Clone)]
pub struct MixProviderClient {
pub pub_key: String,
}
#[derive(Debug, Clone)]
pub struct MixProviderNode {
pub client_listener: SocketAddr,
pub mixnet_listener: SocketAddr,
pub pub_key: String,
pub registered_clients: Vec<MixProviderClient>,
pub last_seen: u64,
pub version: String,
}
impl Into<SphinxNode> for MixProviderNode {
fn into(self) -> SphinxNode {
let address_bytes = addressing::encoded_bytes_from_socket_address(self.mixnet_listener);
let key_bytes = self.get_pub_key_bytes();
let key = MontgomeryPoint(key_bytes);
SphinxNode::new(NodeAddressBytes::from_bytes(address_bytes), key)
}
}
impl MixProviderNode {
pub fn get_pub_key_bytes(&self) -> [u8; 32] {
let decoded_key_bytes = base64::decode_config(&self.pub_key, base64::URL_SAFE).unwrap();
let mut key_bytes = [0; 32];
key_bytes.copy_from_slice(&decoded_key_bytes[..]);
key_bytes
}
}
#[derive(Debug, Clone)]
pub struct CocoNode {
pub host: String,
pub pub_key: String,
pub last_seen: u64,
pub version: String,
}
#[derive(Debug)]
pub enum NymTopologyError {
InvalidMixLayerError,
MissingLayerError(Vec<u64>),
}
pub trait NymTopology: Sized {
fn new(directory_server: String) -> Self;
fn new_from_nodes(
mix_nodes: Vec<MixNode>,
mix_provider_nodes: Vec<MixProviderNode>,
coco_nodes: Vec<CocoNode>,
) -> Self;
fn get_mix_nodes(&self) -> Vec<MixNode>;
fn get_mix_provider_nodes(&self) -> Vec<MixProviderNode>;
fn get_coco_nodes(&self) -> Vec<CocoNode>;
fn make_layered_topology(&self) -> Result<HashMap<u64, Vec<MixNode>>, NymTopologyError> {
let mut layered_topology: HashMap<u64, Vec<MixNode>> = HashMap::new();
let mut highest_layer = 0;
for mix in self.get_mix_nodes() {
// we need to have extra space for provider
if mix.layer > sphinx::constants::MAX_PATH_LENGTH as u64 {
return Err(NymTopologyError::InvalidMixLayerError);
}
highest_layer = max(highest_layer, mix.layer);
let layer_nodes = layered_topology.entry(mix.layer).or_insert(Vec::new());
layer_nodes.push(mix);
}
// verify the topology - make sure there are no gaps and there is at least one node per layer
let mut missing_layers = Vec::new();
for layer in 1..=highest_layer {
if !layered_topology.contains_key(&layer) {
missing_layers.push(layer);
}
if layered_topology[&layer].len() == 0 {
missing_layers.push(layer);
}
}
if missing_layers.len() > 0 {
return Err(NymTopologyError::MissingLayerError(missing_layers));
}
Ok(layered_topology)
}
fn mix_route(&self) -> Result<Vec<SphinxNode>, NymTopologyError> {
let mut layered_topology = self.make_layered_topology()?;
let num_layers = layered_topology.len();
let route = (1..=num_layers as u64)
.map(|layer| layered_topology.remove(&layer).unwrap()) // for each layer
.map(|nodes| nodes.into_iter().choose(&mut rand::thread_rng()).unwrap()) // choose random node
.map(|random_node| random_node.into()) // and convert it into sphinx specific node format
.collect();
Ok(route)
}
// sets a route to specific provider
fn route_to(&self, provider_node: SphinxNode) -> Result<Vec<SphinxNode>, NymTopologyError> {
Ok(self
.mix_route()?
.into_iter()
.chain(std::iter::once(provider_node))
.collect())
}
fn all_paths(&self) -> Result<Vec<Vec<SphinxNode>>, NymTopologyError> {
let mut layered_topology = self.make_layered_topology()?;
let providers = self.get_mix_provider_nodes();
let sorted_layers: Vec<Vec<SphinxNode>> = (1..=layered_topology.len() as u64)
.map(|layer| layered_topology.remove(&layer).unwrap()) // get all nodes per layer
.map(|layer_nodes| layer_nodes.into_iter().map(|node| node.into()).collect()) // convert them into 'proper' sphinx nodes
.chain(std::iter::once(
providers.into_iter().map(|node| node.into()).collect(),
)) // append all providers to the end
.collect();
let all_paths = sorted_layers
.into_iter()
.multi_cartesian_product() // create all possible paths through that
.collect();
Ok(all_paths)
}
fn filter_node_versions(
&self,
mix_version: &str,
provider_version: &str,
coco_version: &str,
) -> Self {
let filtered_mixes = self
.get_mix_nodes()
.iter()
.cloned()
.filter(|mix_node| mix_node.version == mix_version)
.collect();
let filtered_providers = self
.get_mix_provider_nodes()
.iter()
.cloned()
.filter(|provider_node| provider_node.version == provider_version)
.collect();
let filtered_coco_nodes = self
.get_coco_nodes()
.iter()
.cloned()
.filter(|coco_node| coco_node.version == coco_version)
.collect();
Self::new_from_nodes(filtered_mixes, filtered_providers, filtered_coco_nodes)
}
fn can_construct_path_through(&self) -> bool {
match self.make_layered_topology() {
Ok(_) => true,
Err(_) => false,
}
}
}
// TODO: tests...
+35
View File
@@ -0,0 +1,35 @@
# nym-mixnode Changelog
## 0.3.2
* added separate announce address
* allows announcing dns hostname instead of an ip address
## 0.3.1
* Fixed crash when directory server goes down
## 0.3.0
* cleaned up a lot of internal dependencies
* reporting version to the directory server
* printing warning on trying to bind to "localhost", "127.0.0.1" or "0.0.0.0"
* more informative error messages
* generalised identity keys
* generalised Topology handling
* started slow transition to `log` crate by `nym-client`
* start of 'MixMining'
* start of validator node
## 0.2.0
* removed the `--local` flag
* introduced `--directory` argument to support arbitrary directory servers. Leaving it out will point the node at the "https://directory.nymtech.net" alpha testnet server
* the `host` argument is now required
* IPv6 support
* mixnode version number is now shown at node start
* directory server location is now shown at node start
## 0.1.0 - Initial Release
* The bare minimum set of features required by a Nym Mixnode
+2371
View File
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
[package]
build = "build.rs"
name = "nym-mixnode"
version = "0.3.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
clap = "2.33.0"
curve25519-dalek = "1.2.3"
futures = "0.3.1"
log = "0.4.8"
tokio = { version = "0.2", features = ["full"] }
## internal
addressing = {path = "../common/addressing" }
directory-client = { path = "../common/clients/directory-client" }
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
[build-dependencies]
built = "0.3.2"
+11
View File
@@ -0,0 +1,11 @@
# Nym Mixnode
A Rust mixnode implementation.
## Usage
* `nym-mixnode` prints a help message showing usage options
* `nym-mixnode run --help` prints a help message showing usage options for the run command
* `nym-mixnode run --layer 1 --host x.x.x.x` will start the mixnode in layer 1 and bind to the specified host IP address. Coordinate with other people in your network to find out which layer needs coverage.
By default, the Nym Mixnode will start on port 1789. If desired, you can change the port using the `--port` option.
View File
+93
View File
@@ -0,0 +1,93 @@
use clap::{App, Arg, ArgMatches, SubCommand};
use std::process;
mod mix_peer;
mod node;
fn main() {
let arg_matches = App::new("Nym Mixnode")
.version(built_info::PKG_VERSION)
.author("Nymtech")
.about("Implementation of the Loopix-based Mixnode")
.subcommand(
SubCommand::with_name("run")
.about("Starts the mixnode")
.arg(
Arg::with_name("host")
.long("host")
.help("The custom host on which the mixnode will be running")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("port")
.long("port")
.help("The port on which the mixnode will be listening")
.takes_value(true),
)
.arg(
Arg::with_name("layer")
.long("layer")
.help("The mixnet layer of this particular node")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("announce_host")
.long("announce-host")
.help("The host that will be reported to the directory server")
.takes_value(true)
)
.arg(
Arg::with_name("announce_port")
.long("announce-port")
.help("The port that will be reported to the directory server")
.takes_value(true)
)
.arg(
Arg::with_name("directory")
.long("directory")
.help("Address of the directory server the node is sending presence and metrics to")
.takes_value(true),
),
)
.get_matches();
if let Err(e) = execute(arg_matches) {
println!("{}", e);
process::exit(1);
}
}
pub mod built_info {
// The file has been placed there by the build script.
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
fn execute(matches: ArgMatches) -> Result<(), String> {
match matches.subcommand() {
("run", Some(m)) => Ok(node::runner::start(m)),
_ => Err(usage()),
}
}
fn usage() -> String {
banner() + "usage: --help to see available options.\n\n"
}
fn banner() -> String {
format!(
r#"
_ __ _ _ _ __ ___
| '_ \| | | | '_ \ _ \
| | | | |_| | | | | | |
|_| |_|\__, |_| |_| |_|
|___/
(mixnode - version {:})
"#,
built_info::PKG_VERSION
)
}
+32
View File
@@ -0,0 +1,32 @@
use addressing;
use sphinx::route::NodeAddressBytes;
use std::error::Error;
use std::net::SocketAddr;
use tokio::prelude::*;
#[derive(Debug)]
pub struct MixPeer {
connection: SocketAddr,
}
impl MixPeer {
// note that very soon `next_hop_address` will be changed to `next_hop_metadata`
pub fn new(next_hop_address: NodeAddressBytes) -> MixPeer {
let next_hop_socket_address =
addressing::socket_address_from_encoded_bytes(next_hop_address.to_bytes());
MixPeer {
connection: next_hop_socket_address,
}
}
pub async fn send(&self, bytes: Vec<u8>) -> Result<(), Box<dyn Error>> {
let next_hop_address = self.connection.clone();
let mut stream = tokio::net::TcpStream::connect(next_hop_address).await?;
stream.write_all(&bytes).await?;
Ok(())
}
pub fn to_string(&self) -> String {
self.connection.to_string()
}
}
+95
View File
@@ -0,0 +1,95 @@
use directory_client::metrics::MixMetric;
use directory_client::requests::metrics_mixes_post::MetricsMixPoster;
use directory_client::DirectoryClient;
use futures::channel::mpsc;
use futures::lock::Mutex;
use futures::StreamExt;
use log::{debug, error};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
const METRICS_INTERVAL: u64 = 3;
#[derive(Debug)]
pub struct MetricsReporter {
received: u64,
sent: HashMap<String, u64>,
}
impl MetricsReporter {
pub(crate) fn new() -> Self {
MetricsReporter {
received: 0,
sent: HashMap::new(),
}
}
pub(crate) fn add_arc_mutex(self) -> Arc<Mutex<Self>> {
Arc::new(Mutex::new(self))
}
async fn increment_received_metrics(metrics: Arc<Mutex<MetricsReporter>>) {
let mut unlocked = metrics.lock().await;
unlocked.received += 1;
}
pub(crate) async fn run_received_metrics_control(
metrics: Arc<Mutex<MetricsReporter>>,
mut rx: mpsc::Receiver<()>,
) {
while let Some(_) = rx.next().await {
MetricsReporter::increment_received_metrics(metrics.clone()).await;
}
}
async fn increment_sent_metrics(metrics: Arc<Mutex<MetricsReporter>>, sent_to: String) {
let mut unlocked = metrics.lock().await;
let receiver_count = unlocked.sent.entry(sent_to).or_insert(0);
*receiver_count += 1;
}
pub(crate) async fn run_sent_metrics_control(
metrics: Arc<Mutex<MetricsReporter>>,
mut rx: mpsc::Receiver<String>,
) {
while let Some(sent_metric) = rx.next().await {
MetricsReporter::increment_sent_metrics(metrics.clone(), sent_metric).await;
}
}
async fn acquire_and_reset_metrics(
metrics: Arc<Mutex<MetricsReporter>>,
) -> (u64, HashMap<String, u64>) {
let mut unlocked = metrics.lock().await;
let received = unlocked.received;
let sent = std::mem::replace(&mut unlocked.sent, HashMap::new());
unlocked.received = 0;
(received, sent)
}
pub(crate) async fn run_metrics_sender(
metrics: Arc<Mutex<MetricsReporter>>,
cfg: directory_client::Config,
pub_key_str: String,
) {
let delay_duration = Duration::from_secs(METRICS_INTERVAL);
let directory_client = directory_client::Client::new(cfg);
loop {
tokio::time::delay_for(delay_duration).await;
let (received, sent) =
MetricsReporter::acquire_and_reset_metrics(metrics.clone()).await;
match directory_client.metrics_post.post(&MixMetric {
pub_key: pub_key_str.clone(),
received,
sent,
}) {
Err(err) => error!("failed to send metrics - {:?}", err),
Ok(_) => debug!("sent metrics information"),
}
}
}
}
+268
View File
@@ -0,0 +1,268 @@
use crate::mix_peer::MixPeer;
use crate::node;
use crate::node::metrics::MetricsReporter;
use curve25519_dalek::montgomery::MontgomeryPoint;
use curve25519_dalek::scalar::Scalar;
use futures::channel::mpsc;
use futures::lock::Mutex;
use futures::SinkExt;
use sphinx::header::delays::Delay as SphinxDelay;
use sphinx::{ProcessedPacket, SphinxPacket};
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::prelude::*;
use tokio::runtime::Runtime;
mod metrics;
mod presence;
pub mod runner;
pub struct Config {
announce_address: String,
directory_server: String,
layer: usize,
public_key: MontgomeryPoint,
secret_key: Scalar,
socket_address: SocketAddr,
}
impl Config {
pub fn public_key_string(&self) -> String {
let key_bytes = self.public_key.to_bytes().to_vec();
base64::encode_config(&key_bytes, base64::URL_SAFE)
}
}
#[derive(Debug)]
pub enum MixProcessingError {
SphinxRecoveryError,
ReceivedFinalHopError,
}
impl From<sphinx::ProcessingError> for MixProcessingError {
// for time being just have a single error instance for all possible results of sphinx::ProcessingError
fn from(_: sphinx::ProcessingError) -> Self {
use MixProcessingError::*;
SphinxRecoveryError
}
}
struct ForwardingData {
packet: SphinxPacket,
delay: SphinxDelay,
recipient: MixPeer,
sent_metrics_tx: mpsc::Sender<String>,
}
// TODO: this will need to be changed if MixPeer will live longer than our Forwarding Data
impl ForwardingData {
fn new(
packet: SphinxPacket,
delay: SphinxDelay,
recipient: MixPeer,
sent_metrics_tx: mpsc::Sender<String>,
) -> Self {
ForwardingData {
packet,
delay,
recipient,
sent_metrics_tx,
}
}
}
// ProcessingData defines all data required to correctly unwrap sphinx packets
struct ProcessingData {
secret_key: Scalar,
received_metrics_tx: mpsc::Sender<()>,
sent_metrics_tx: mpsc::Sender<String>,
}
impl ProcessingData {
fn new(
secret_key: Scalar,
received_metrics_tx: mpsc::Sender<()>,
sent_metrics_tx: mpsc::Sender<String>,
) -> Self {
ProcessingData {
secret_key,
received_metrics_tx,
sent_metrics_tx,
}
}
fn add_arc_mutex(self) -> Arc<Mutex<Self>> {
Arc::new(Mutex::new(self))
}
}
struct PacketProcessor;
impl PacketProcessor {
pub async fn process_sphinx_data_packet(
packet_data: &[u8],
processing_data: Arc<Mutex<ProcessingData>>,
) -> Result<ForwardingData, MixProcessingError> {
// we received something resembling a sphinx packet, report it!
let processing_data = processing_data.lock().await;
let mut received_sender = processing_data.received_metrics_tx.clone();
received_sender.send(()).await.unwrap();
let packet = SphinxPacket::from_bytes(packet_data.to_vec())?;
let (next_packet, next_hop_address, delay) =
match packet.process(processing_data.secret_key) {
ProcessedPacket::ProcessedPacketForwardHop(packet, address, delay) => {
(packet, address, delay)
}
_ => return Err(MixProcessingError::ReceivedFinalHopError),
};
let next_mix = MixPeer::new(next_hop_address);
let fwd_data = ForwardingData::new(
next_packet,
delay,
next_mix,
processing_data.sent_metrics_tx.clone(),
);
Ok(fwd_data)
}
async fn wait_and_forward(mut forwarding_data: ForwardingData) {
let delay_duration = Duration::from_nanos(forwarding_data.delay.get_value());
tokio::time::delay_for(delay_duration).await;
forwarding_data
.sent_metrics_tx
.send(forwarding_data.recipient.to_string())
.await
.unwrap();
println!("RECIPIENT: {:?}", forwarding_data.recipient);
match forwarding_data
.recipient
.send(forwarding_data.packet.to_bytes())
.await
{
Ok(()) => (),
Err(e) => {
println!(
"failed to write bytes to next mix peer. err = {:?}",
e.to_string()
);
}
}
}
}
// the MixNode will live for whole duration of this program
pub struct MixNode {
directory_server: String,
network_address: SocketAddr,
public_key: MontgomeryPoint,
secret_key: Scalar,
// TODO: use it later to enforce forward travel
// layer: usize,
}
impl MixNode {
pub fn new(config: &Config) -> Self {
MixNode {
directory_server: config.directory_server.clone(),
network_address: config.socket_address,
secret_key: config.secret_key,
public_key: config.public_key,
// layer: config.layer,
}
}
async fn process_socket_connection(
mut socket: tokio::net::TcpStream,
processing_data: Arc<Mutex<ProcessingData>>,
) {
// NOTE: processing_data is copied here!!
let mut buf = [0u8; sphinx::PACKET_SIZE];
// In a loop, read data from the socket and write the data back.
loop {
match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => {
println!("Remote connection closed.");
return;
}
Ok(_) => {
let fwd_data = PacketProcessor::process_sphinx_data_packet(
buf.as_ref(),
processing_data.clone(),
)
.await
.unwrap();
PacketProcessor::wait_and_forward(fwd_data).await;
}
Err(e) => {
println!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the some data back
if let Err(e) = socket.write_all(b"foomp").await {
println!("failed to write reply to socket; err = {:?}", e);
return;
}
}
}
pub fn start(&self, config: node::Config) -> Result<(), Box<dyn std::error::Error>> {
// Create the runtime, probably later move it to MixNode itself?
let mut rt = Runtime::new()?;
let (received_tx, received_rx) = mpsc::channel(1024);
let (sent_tx, sent_rx) = mpsc::channel(1024);
let directory_cfg = directory_client::Config {
base_url: self.directory_server.clone(),
};
let pub_key_str =
base64::encode_config(&self.public_key.to_bytes().to_vec(), base64::URL_SAFE);
rt.spawn({
let presence_notifier = presence::Notifier::new(&config);
presence_notifier.run()
});
let metrics = MetricsReporter::new().add_arc_mutex();
rt.spawn(MetricsReporter::run_received_metrics_control(
metrics.clone(),
received_rx,
));
rt.spawn(MetricsReporter::run_sent_metrics_control(
metrics.clone(),
sent_rx,
));
rt.spawn(MetricsReporter::run_metrics_sender(
metrics,
directory_cfg,
pub_key_str,
));
// Spawn the root task
rt.block_on(async {
let mut listener = tokio::net::TcpListener::bind(self.network_address).await?;
let processing_data =
ProcessingData::new(self.secret_key, received_tx, sent_tx).add_arc_mutex();
loop {
let (socket, _) = listener.accept().await?;
let thread_processing_data = processing_data.clone();
tokio::spawn(async move {
MixNode::process_socket_connection(socket, thread_processing_data).await;
});
}
})
}
}
+47
View File
@@ -0,0 +1,47 @@
use crate::node;
use directory_client::presence::MixNodePresence;
use directory_client::requests::presence_mixnodes_post::PresenceMixNodesPoster;
use directory_client::DirectoryClient;
use log::{debug, error};
use std::time::Duration;
pub struct Notifier {
pub net_client: directory_client::Client,
presence: MixNodePresence,
}
impl Notifier {
pub fn new(node_config: &node::Config) -> Notifier {
let config = directory_client::Config {
base_url: node_config.directory_server.clone(),
};
let net_client = directory_client::Client::new(config);
let presence = MixNodePresence {
host: node_config.announce_address.clone(),
pub_key: node_config.public_key_string(),
layer: node_config.layer as u64,
last_seen: 0,
version: env!("CARGO_PKG_VERSION").to_string(),
};
Notifier {
net_client,
presence,
}
}
pub fn notify(&self) {
match self.net_client.presence_mix_nodes_post.post(&self.presence) {
Err(err) => error!("failed to send presence - {:?}", err),
Ok(_) => debug!("sent presence information"),
}
}
pub async fn run(self) {
let delay_duration = Duration::from_secs(5);
loop {
self.notify();
tokio::time::delay_for(delay_duration).await;
}
}
}
+87
View File
@@ -0,0 +1,87 @@
use crate::banner;
use crate::node;
use crate::node::MixNode;
use clap::ArgMatches;
use std::net::ToSocketAddrs;
fn print_binding_warning(address: &str) {
println!("\n##### WARNING #####");
println!(
"\nYou are trying to bind to {} - you might not be accessible to other nodes",
address
);
println!("\n##### WARNING #####\n");
}
pub fn start(matches: &ArgMatches) {
println!("{}", banner());
println!("Starting mixnode...");
let config = new_config(matches);
println!("Public key: {}", config.public_key_string());
println!("Directory server: {}", config.directory_server);
println!(
"Listening for incoming packets on {}",
config.socket_address
);
println!(
"Announcing the following socket address: {}",
config.announce_address
);
let mix = MixNode::new(&config);
mix.start(config).unwrap();
}
fn new_config(matches: &ArgMatches) -> node::Config {
let host = matches.value_of("host").unwrap();
if host == "localhost" || host == "127.0.0.1" || host == "0.0.0.0" {
print_binding_warning(host);
}
let port = match matches.value_of("port").unwrap_or("1789").parse::<u16>() {
Ok(n) => n,
Err(err) => panic!("Invalid port value provided - {:?}", err),
};
let layer = match matches.value_of("layer").unwrap().parse::<usize>() {
Ok(n) => n,
Err(err) => panic!("Invalid layer value provided - {:?}", err),
};
let socket_address = (host, port)
.to_socket_addrs()
.expect("Failed to combine host and port")
.next()
.expect("Failed to extract the socket address from the iterator");
let announce_host = matches.value_of("announce_host").unwrap_or(host);
let announce_port = matches
.value_of("announce_port")
.map(|port| port.parse::<u16>().unwrap())
.unwrap_or(port);
let _ = (announce_host, announce_port)
.to_socket_addrs()
.expect("Failed to combine announce host and port")
.next()
.expect("Failed to extract the announce socket address from the iterator");
let announce_address = format!("{}:{}", announce_host, announce_port);
let (secret_key, public_key) = sphinx::crypto::keygen();
let directory_server = matches
.value_of("directory")
.unwrap_or("https://directory.nymtech.net")
.to_string();
node::Config {
directory_server,
layer,
public_key,
socket_address,
announce_address,
secret_key,
}
}
+34
View File
@@ -0,0 +1,34 @@
# nym-client Changelog
## 0.3.2
* allows receiving topology with dns hostname instead of an ip address
## 0.3.1
* Version increase for consistency with `nym-mixnode` and `nym-sfw-provider`
## 0.3.0
* cleaned up a lot of internal dependencies
* reporting version to the directory server
* printing warning on trying to bind to "localhost", "127.0.0.1" or "0.0.0.0"
* more informative error messages
* generalised identity keys
* generalised Topology handling
* started slow transition to `log` crate by `nym-client`
* start of 'MixMining'
* start of validator node
## 0.2.0
* removed the `--local` flag
* introduced `--directory` argument to support arbitrary directory servers. Leaving it out will point the node at the "https://directory.nymtech.net" alpha testnet server
* IPv6 support
* client version number is now shown at node start
* directory server location is now shown at node start
* decrease default delays
## 0.1.0 - Initial Release
* The bare minimum set of features required by a Nym Client
+2402
View File
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
[package]
build = "build.rs"
name = "nym-client"
version = "0.3.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
clap = "2.33.0"
curve25519-dalek = "1.2.3"
dirs = "2.0.2"
env_logger = "0.7.1"
futures = "0.3.1"
hex = "0.4.0"
log = "0.4.8"
pem = "0.7.0"
rand = "0.7.2"
rand_distr = "0.2.2"
reqwest = "0.9.22"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.44"
tokio = { version = "0.2", features = ["full"] }
tungstenite = "0.9.2"
## internal
addressing = {path = "../common/addressing" }
crypto = {path = "../common/crypto"}
directory-client = { path = "../common/clients/directory-client" }
healthcheck = { path = "../common/healthcheck" }
mix-client = { path = "../common/clients/mix-client" }
provider-client = { path = "../common/clients/provider-client" }
sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" }
topology = {path = "../common/topology" }
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
# putting this explicitly below everything and most likely, the next time we look into it, it will already have a proper release
tokio-tungstenite = { git = "https://github.com/dbcfd/tokio-tungstenite", rev="6dc2018cbfe8fe7ddd75ff977343086503135b38" }
[build-dependencies]
built = "0.3.2"
+10
View File
@@ -0,0 +1,10 @@
# Nym Client
The Nym Client communicates with the remote, decentralised nodes which make up the Nym system as a whole.
It needs to handle communication with 3 types of remote nodes. Here's the development status of each:
- [ ] Directory nodes
- [ ] Mix nodes
- [ ] Validator nodes
+5
View File
@@ -0,0 +1,5 @@
use built;
fn main() {
built::write_built_file().expect("Failed to acquire build-time information");
}
@@ -1,27 +1,24 @@
use crate::clients::directory::presence::Topology;
use crate::clients::mix::MixClient;
use crate::clients::provider::ProviderClient;
use crate::built_info;
use crate::sockets::tcp;
use crate::sockets::ws;
use crate::utils;
use crate::utils::topology::get_topology;
use directory_client::presence::Topology;
use futures::channel::{mpsc, oneshot};
use futures::join;
use futures::lock::Mutex as FMutex;
use futures::select;
use futures::{SinkExt, StreamExt};
use log::*;
use sfw_provider_requests::AuthToken;
use sphinx::route::{Destination, DestinationAddressBytes};
use sphinx::SphinxPacket;
use std::io;
use std::io::prelude::*;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::runtime::Runtime;
pub mod directory;
pub mod mix;
pub mod provider;
pub mod validator;
use topology::NymTopology;
const LOOP_COVER_AVERAGE_DELAY: f64 = 0.5;
// seconds
@@ -44,16 +41,19 @@ struct MixTrafficController;
impl MixTrafficController {
// this was way more difficult to implement than what this code may suggest...
async fn run(mut rx: mpsc::UnboundedReceiver<MixMessage>) {
let mix_client = MixClient::new();
let mix_client = mix_client::MixClient::new();
while let Some(mix_message) = rx.next().await {
println!(
info!(
"[MIX TRAFFIC CONTROL] - got a mix_message for {:?}",
mix_message.0
);
let send_res = mix_client.send(mix_message.1, mix_message.0).await;
match send_res {
Ok(_) => println!("We successfully sent the message!"),
Err(e) => eprintln!("We failed to send the message :( - {:?}", e),
Ok(_) => {
print!(".");
io::stdout().flush().ok().expect("Could not flush stdout");
}
Err(e) => error!("We failed to send the message :( - {:?}", e),
};
}
}
@@ -77,7 +77,7 @@ impl ReceivedMessagesBuffer {
}
async fn add_new_messages(buf: Arc<FMutex<Self>>, msgs: Vec<Vec<u8>>) {
println!("Adding new messages to the buffer! {:?}", msgs);
info!("Adding new messages to the buffer! {:?}", msgs);
let mut unlocked = buf.lock().await;
unlocked.messages.extend(msgs);
}
@@ -157,7 +157,7 @@ impl NymClient {
topology: Topology,
) {
loop {
println!("[LOOP COVER TRAFFIC STREAM] - next cover message!");
info!("[LOOP COVER TRAFFIC STREAM] - next cover message!");
let delay = utils::poisson::sample(LOOP_COVER_AVERAGE_DELAY);
let delay_duration = Duration::from_secs_f64(delay);
tokio::time::delay_for(delay_duration).await;
@@ -176,30 +176,38 @@ impl NymClient {
topology: Topology,
) {
loop {
println!("[OUT QUEUE] here I will be sending real traffic (or loop cover if nothing is available)");
select! {
info!("[OUT QUEUE] here I will be sending real traffic (or loop cover if nothing is available)");
// TODO: consider replacing select macro with our own proper future definition with polling
let traffic_message = select! {
real_message = input_rx.next() => {
println!("[OUT QUEUE] - we got a real message!");
let real_message = real_message.expect("The channel must have closed! - if the client hasn't crashed, it should have!");
info!("[OUT QUEUE] - we got a real message!");
if real_message.is_none() {
error!("Unexpected 'None' real message!");
std::process::exit(1);
}
let real_message = real_message.unwrap();
println!("real: {:?}", real_message);
let encapsulated_message = utils::sphinx::encapsulate_message(real_message.0, real_message.1, &topology);
mix_tx.send(MixMessage(encapsulated_message.0, encapsulated_message.1)).await.unwrap();
utils::sphinx::encapsulate_message(real_message.0, real_message.1, &topology)
},
default => {
println!("[OUT QUEUE] - no real message - going to send extra loop cover");
let cover_message = utils::sphinx::loop_cover_message(our_info.address, our_info.identifier, &topology);
mix_tx.send(MixMessage(cover_message.0, cover_message.1)).await.unwrap();
info!("[OUT QUEUE] - no real message - going to send extra loop cover");
utils::sphinx::loop_cover_message(our_info.address, our_info.identifier, &topology)
}
};
mix_tx
.send(MixMessage(traffic_message.0, traffic_message.1))
.await
.unwrap();
let delay_duration = Duration::from_secs_f64(MESSAGE_SENDING_AVERAGE_DELAY);
tokio::time::delay_for(delay_duration).await;
}
}
async fn start_provider_polling(
provider_client: ProviderClient,
provider_client: provider_client::ProviderClient,
mut poller_tx: mpsc::UnboundedSender<Vec<Vec<u8>>>,
) {
let loop_message = &utils::sphinx::LOOP_COVER_MESSAGE_PAYLOAD.to_vec();
@@ -207,7 +215,7 @@ impl NymClient {
loop {
let delay_duration = Duration::from_secs_f64(FETCH_MESSAGES_DELAY);
tokio::time::delay_for(delay_duration).await;
println!("[FETCH MSG] - Polling provider...");
info!("[FETCH MSG] - Polling provider...");
let messages = provider_client.retrieve_messages().await.unwrap();
let good_messages = messages
.into_iter()
@@ -219,21 +227,66 @@ impl NymClient {
}
pub fn start(self) -> Result<(), Box<dyn std::error::Error>> {
let score_threshold = 0.0;
println!("Starting nym client");
let mut rt = Runtime::new()?;
let topology = get_topology(self.directory.clone());
// this is temporary and assumes there exists only a single provider.
let provider_address: SocketAddr = topology
.mix_provider_nodes
.first()
.unwrap()
.host
.parse()
.unwrap();
println!("Trying to obtain valid, healthy, topology");
let full_topology = Topology::new(self.directory.clone());
let mut provider_client =
ProviderClient::new(provider_address, self.address, self.auth_token);
// run a healthcheck to determine healthy-ish nodes:
// this is a temporary solution as the healthcheck will eventually be moved to validators
let healthcheck_config = healthcheck::config::HealthCheck {
directory_server: self.directory.clone(),
// those are literally unrelevant when running single check
interval: 100000.0,
resolution_timeout: 5.0,
num_test_packets: 2,
};
let healthcheck = healthcheck::HealthChecker::new(healthcheck_config);
let healthcheck_result = rt.block_on(healthcheck.do_check());
let healthcheck_scores = match healthcheck_result {
Err(err) => {
error!(
"failed to perform healthcheck to determine healthy topology - {:?}",
err
);
return Err(Box::new(err));
}
Ok(scores) => scores,
};
let healthy_topology =
healthcheck_scores.filter_topology_by_score(&full_topology, score_threshold);
// for time being assume same versioning, i.e. if client is running X.Y.Z,
// we're expecting mixes, providers and coconodes to also be running X.Y.Z
let versioned_healthy_topology = healthy_topology.filter_node_versions(
built_info::PKG_VERSION,
built_info::PKG_VERSION,
built_info::PKG_VERSION,
);
// make sure you can still send a packet through the network:
if !versioned_healthy_topology.can_construct_path_through() {
error!("No valid path exists in the topology");
// TODO: replace panic with proper return type
panic!("No valid path exists in the topology");
}
// this is temporary and assumes there exists only a single provider.
let provider_client_listener_address: SocketAddr = versioned_healthy_topology
.get_mix_provider_nodes()
.first()
.expect("Could not get a provider from the supplied network topology, are you using the right directory server?")
.client_listener;
let mut provider_client = provider_client::ProviderClient::new(
provider_client_listener_address,
self.address,
self.auth_token,
);
// registration
rt.block_on(async {
@@ -241,7 +294,7 @@ impl NymClient {
None => {
let auth_token = provider_client.register().await.unwrap();
provider_client.update_token(auth_token);
println!("Obtained new token! - {:?}", auth_token);
info!("Obtained new token! - {:?}", auth_token);
}
Some(token) => println!("Already got the token! - {:?}", token),
}
@@ -270,14 +323,14 @@ impl NymClient {
let loop_cover_traffic_future = rt.spawn(NymClient::start_loop_cover_traffic_stream(
mix_tx.clone(),
Destination::new(self.address, Default::default()),
topology.clone(),
versioned_healthy_topology.clone(),
));
let out_queue_control_future = rt.spawn(NymClient::control_out_queue(
mix_tx,
self.input_rx,
Destination::new(self.address, Default::default()),
topology.clone(),
versioned_healthy_topology.clone(),
));
let provider_polling_future = rt.spawn(NymClient::start_provider_polling(
@@ -292,7 +345,7 @@ impl NymClient {
self.input_tx,
received_messages_buffer_output_tx,
self.address,
topology,
versioned_healthy_topology,
));
}
SocketType::TCP => {
@@ -301,7 +354,7 @@ impl NymClient {
self.input_tx,
received_messages_buffer_output_tx,
self.address,
topology,
versioned_healthy_topology,
));
}
SocketType::None => (),
@@ -1,8 +1,8 @@
use crate::banner;
use crate::identity::mixnet;
use crate::persistence::pathfinder::Pathfinder;
use crate::persistence::pemstore::PemStore;
use clap::ArgMatches;
use crypto::identity::MixnetIdentityKeyPair;
pub fn execute(matches: &ArgMatches) {
println!("{}", banner());
@@ -12,9 +12,9 @@ pub fn execute(matches: &ArgMatches) {
let pathfinder = Pathfinder::new(id);
println!("Writing keypairs to {:?}...", pathfinder.config_dir);
let mix_keys = mixnet::KeyPair::new();
let mix_keys = crypto::identity::DummyMixIdentityKeyPair::new();
let pem_store = PemStore::new(pathfinder);
pem_store.write(mix_keys);
pem_store.write_identity(mix_keys);
println!("Client configuration completed.\n\n\n")
}
@@ -1,4 +1,3 @@
pub mod init;
pub mod run;
pub mod tcpsocket;
pub mod websocket;
@@ -1,9 +1,9 @@
use crate::banner;
use crate::clients::{NymClient, SocketType};
use crate::persistence::pemstore;
use crate::sockets::tcp;
use clap::ArgMatches;
use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey};
use std::net::ToSocketAddrs;
pub fn execute(matches: &ArgMatches) {
@@ -29,11 +29,17 @@ pub fn execute(matches: &ArgMatches) {
.next()
.expect("Failed to extract the socket address from the iterator");
let keypair = pemstore::read_keypair_from_disk(id);
let auth_token = None;
let keypair = pemstore::read_mix_identity_keypair_from_disk(id);
// TODO: reading auth_token from disk (if exists);
println!("Public key: {}", keypair.public_key.to_b64_string());
let mut temporary_address = [0u8; 32];
let public_key_bytes = keypair.public_key().to_bytes();
temporary_address.copy_from_slice(&public_key_bytes[..]);
let auth_token = None;
let client = NymClient::new(
keypair.public_bytes(),
temporary_address,
socket_address.clone(),
directory_server,
auth_token,
@@ -3,6 +3,7 @@ use crate::clients::{NymClient, SocketType};
use crate::persistence::pemstore;
use clap::ArgMatches;
use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey};
use std::net::ToSocketAddrs;
pub fn execute(matches: &ArgMatches) {
@@ -28,11 +29,17 @@ pub fn execute(matches: &ArgMatches) {
.next()
.expect("Failed to extract the socket address from the iterator");
let keypair = pemstore::read_keypair_from_disk(id);
let keypair = pemstore::read_mix_identity_keypair_from_disk(id);
// TODO: reading auth_token from disk (if exists);
println!("Public key: {}", keypair.public_key.to_b64_string());
let mut temporary_address = [0u8; 32];
let public_key_bytes = keypair.public_key().to_bytes();
temporary_address.copy_from_slice(&public_key_bytes[..]);
let auth_token = None;
let client = NymClient::new(
keypair.public_bytes(),
temporary_address,
socket_address,
directory_server,
auth_token,
+13 -26
View File
@@ -1,16 +1,25 @@
#![recursion_limit = "256"]
use clap::{App, Arg, ArgMatches, SubCommand};
use env_logger;
use log::*;
use std::process;
pub mod clients;
mod commands;
mod identity;
mod persistence;
mod sockets;
mod utils;
pub mod utils;
pub mod built_info {
// The file has been placed there by the build script.
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
fn main() {
env_logger::init();
let arg_matches = App::new("Nym Client")
.version("0.1.0")
.version(built_info::PKG_VERSION)
.author("Nymtech")
.about("Implementation of the Nym Client")
.subcommand(
@@ -28,22 +37,6 @@ fn main() {
.takes_value(true)
)
)
.subcommand(
SubCommand::with_name("run")
.about("Run a persistent Nym client process")
.arg(Arg::with_name("id")
.long("id")
.help("Id of the nym-mixnet-client we want to run.")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("directory")
.long("directory")
.help("Address of the directory server the client is getting topology from")
.takes_value(true),
)
)
.subcommand(
SubCommand::with_name("tcpsocket")
.about("Run Nym client that listens for bytes on a TCP socket")
@@ -94,20 +87,14 @@ fn main() {
.get_matches();
if let Err(e) = execute(arg_matches) {
println!("{}", e);
error!("{}", e);
process::exit(1);
}
}
pub mod built_info {
// The file has been placed there by the build script.
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
fn execute(matches: ArgMatches) -> Result<(), String> {
match matches.subcommand() {
("init", Some(m)) => Ok(commands::init::execute(m)),
("run", Some(m)) => Ok(commands::run::execute(m)),
("tcpsocket", Some(m)) => Ok(commands::tcpsocket::execute(m)),
("websocket", Some(m)) => Ok(commands::websocket::execute(m)),
_ => Err(usage()),
+98
View File
@@ -0,0 +1,98 @@
use crate::persistence::pathfinder::Pathfinder;
use pem::{encode, parse, Pem};
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
pub fn read_mix_identity_keypair_from_disk(
id: String,
) -> crypto::identity::DummyMixIdentityKeyPair {
let pathfinder = Pathfinder::new(id);
let pem_store = PemStore::new(pathfinder);
let keypair = pem_store.read_identity();
keypair
}
#[allow(dead_code)]
pub fn read_mix_encryption_keypair_from_disk(_id: String) -> crypto::encryption::x25519::KeyPair {
unimplemented!()
}
pub struct PemStore {
config_dir: PathBuf,
private_mix_key: PathBuf,
public_mix_key: PathBuf,
}
impl PemStore {
pub fn new(pathfinder: Pathfinder) -> PemStore {
PemStore {
config_dir: pathfinder.config_dir,
private_mix_key: pathfinder.private_mix_key,
public_mix_key: pathfinder.public_mix_key,
}
}
pub fn read_identity<IDPair, Priv, Pub>(&self) -> IDPair
where
IDPair: crypto::identity::MixnetIdentityKeyPair<Priv, Pub>,
Priv: crypto::identity::MixnetIdentityPrivateKey,
Pub: crypto::identity::MixnetIdentityPublicKey,
{
let private_pem = self.read_pem_file(self.private_mix_key.clone());
let public_pem = self.read_pem_file(self.public_mix_key.clone());
let key_pair = IDPair::from_bytes(&private_pem.contents, &public_pem.contents);
assert_eq!(key_pair.private_key().pem_type(), private_pem.tag);
assert_eq!(key_pair.public_key().pem_type(), public_pem.tag);
key_pair
}
fn read_pem_file(&self, filepath: PathBuf) -> Pem {
let mut pem_bytes = File::open(filepath).expect("Could not open stored keys from disk.");
let mut buf = Vec::new();
pem_bytes
.read_to_end(&mut buf)
.expect("PEM bytes reading failed.");
let pem = parse(&buf).expect("PEM parsing failed while reading keys");
pem
}
// This should be refactored and made more generic for when we have other kinds of
// KeyPairs that we want to persist (e.g. validator keypairs, or keys for
// signing vs encryption). However, for the moment, it does the job.
pub fn write_identity<IDPair, Priv, Pub>(&self, key_pair: IDPair)
where
IDPair: crypto::identity::MixnetIdentityKeyPair<Priv, Pub>,
Priv: crypto::identity::MixnetIdentityPrivateKey,
Pub: crypto::identity::MixnetIdentityPublicKey,
{
std::fs::create_dir_all(self.config_dir.clone()).unwrap();
let private_key = key_pair.private_key();
let public_key = key_pair.public_key();
self.write_pem_file(
self.private_mix_key.clone(),
private_key.to_bytes(),
private_key.pem_type(),
);
self.write_pem_file(
self.public_mix_key.clone(),
public_key.to_bytes(),
public_key.pem_type(),
);
}
fn write_pem_file(&self, filepath: PathBuf, data: Vec<u8>, tag: String) {
let pem = Pem {
tag,
contents: data,
};
let key = encode(&pem);
let mut file = File::create(filepath).unwrap();
file.write_all(key.as_bytes()).unwrap();
}
}
@@ -1,17 +1,17 @@
use crate::clients::directory::presence::Topology;
use crate::clients::BufferResponse;
use crate::clients::InputMessage;
use directory_client::presence::Topology;
use futures::channel::{mpsc, oneshot};
use futures::future::FutureExt;
use futures::io::Error;
use futures::SinkExt;
use sphinx::route::{Destination, DestinationAddressBytes};
use std::borrow::Borrow;
use std::convert::TryFrom;
use std::io;
use std::net::SocketAddr;
use tokio::prelude::*;
use std::convert::TryFrom;
use std::sync::Arc;
use std::borrow::Borrow;
use tokio::prelude::*;
const SEND_REQUEST_PREFIX: u8 = 1;
const FETCH_REQUEST_PREFIX: u8 = 2;
@@ -42,7 +42,6 @@ impl From<io::Error> for TCPSocketError {
}
}
enum ClientRequest {
Send {
message: Vec<u8>,
@@ -67,7 +66,7 @@ impl TryFrom<&[u8]> for ClientRequest {
FETCH_REQUEST_PREFIX => Ok(ClientRequest::Fetch),
GET_CLIENTS_REQUEST_PREFIX => Ok(ClientRequest::GetClients),
OWN_DETAILS_REQUEST_PREFIX => Ok(ClientRequest::OwnDetails),
_ => Err(UnknownRequestError)
_ => Err(UnknownRequestError),
}
}
}
@@ -95,7 +94,10 @@ impl ClientRequest {
recipient_address: DestinationAddressBytes,
mut input_tx: mpsc::UnboundedSender<InputMessage>,
) -> ServerResponse {
println!("send handle. sending to: {:?}, msg: {:?}", recipient_address, msg);
println!(
"send handle. sending to: {:?}, msg: {:?}",
recipient_address, msg
);
let dummy_surb = [0; 16];
let input_msg = InputMessage(Destination::new(recipient_address, dummy_surb), msg);
@@ -124,9 +126,7 @@ impl ClientRequest {
let messages = messages.unwrap();
println!("fetched {} messages", messages.len());
ServerResponse::Fetch {
messages,
}
ServerResponse::Fetch { messages }
}
async fn handle_get_clients(topology: &Topology) -> ServerResponse {
@@ -148,7 +148,6 @@ impl ClientRequest {
}
}
enum ServerResponse {
Send,
Fetch { messages: Vec<Vec<u8>> },
@@ -161,10 +160,10 @@ impl Into<Vec<u8>> for ServerResponse {
fn into(self) -> Vec<u8> {
match self {
ServerResponse::Send => b"ok".to_vec(),
ServerResponse::Fetch {messages} =>encode_fetched_messages(messages),
ServerResponse::GetClients {clients} => encode_list_of_clients(clients),
ServerResponse::OwnDetails {address} => address,
ServerResponse::Error { message } => message.as_bytes().to_vec()
ServerResponse::Fetch { messages } => encode_fetched_messages(messages),
ServerResponse::GetClients { clients } => encode_list_of_clients(clients),
ServerResponse::OwnDetails { address } => address,
ServerResponse::Error { message } => message.as_bytes().to_vec(),
}
}
}
@@ -204,17 +203,26 @@ impl ServerResponse {
}
}
async fn handle_connection(data: &[u8], request_handling_data: RequestHandlingData) -> Result<ServerResponse, TCPSocketError> {
async fn handle_connection(
data: &[u8],
request_handling_data: RequestHandlingData,
) -> Result<ServerResponse, TCPSocketError> {
let request = ClientRequest::try_from(data)?;
let response = match request {
ClientRequest::Send {
message,
recipient_address
} => ClientRequest::handle_send(message, recipient_address, request_handling_data.msg_input).await,
recipient_address,
} => {
ClientRequest::handle_send(message, recipient_address, request_handling_data.msg_input)
.await
}
ClientRequest::Fetch => ClientRequest::handle_fetch(request_handling_data.msg_query).await,
ClientRequest::GetClients => ClientRequest::handle_get_clients(request_handling_data.topology.borrow()).await,
ClientRequest::OwnDetails => ClientRequest::handle_own_details(request_handling_data.self_address).await,
ClientRequest::GetClients => {
ClientRequest::handle_get_clients(request_handling_data.topology.borrow()).await
}
ClientRequest::OwnDetails => {
ClientRequest::handle_own_details(request_handling_data.self_address).await
}
};
Ok(response)
@@ -262,7 +270,7 @@ async fn accept_connection(
};
match handle_connection(&buf[..n], request_handling_data).await {
Ok(res) => res,
Err(e) => ServerResponse::new_error(format!("{:?}", e))
Err(e) => ServerResponse::new_error(format!("{:?}", e)),
}
}
Err(e) => {
@@ -302,4 +310,4 @@ pub async fn start_tcpsocket(
eprintln!("The tcpsocket went kaput...");
Ok(())
}
}
@@ -1,11 +1,12 @@
use crate::clients::directory::presence::Topology;
use crate::clients::BufferResponse;
use crate::clients::InputMessage;
use directory_client::presence::Topology;
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
use futures::channel::{mpsc, oneshot};
use futures::future::FutureExt;
use futures::io::Error;
use futures::{SinkExt, StreamExt};
use log::*;
use serde::{Deserialize, Serialize};
use sphinx::route::{Destination, DestinationAddressBytes};
use std::io;
@@ -64,7 +65,7 @@ impl From<Message> for ClientRequest {
Message::Close(_) => panic!("todo: handle close!"),
_ => panic!("Other types of messages are also unsupported!"),
};
serde_json::from_str(&text_msg).unwrap()
serde_json::from_str(&text_msg).expect("unable to deserialize From<Message> json")
}
}
@@ -105,6 +106,7 @@ impl ClientRequest {
async fn handle_fetch(mut msg_query: mpsc::UnboundedSender<BufferResponse>) -> ServerResponse {
let (res_tx, res_rx) = oneshot::channel();
if msg_query.send(res_tx).await.is_err() {
warn!("Failed to handle_fetch. msg_query.send() is an error.");
return ServerResponse::Error {
message: "Server failed to receive messages".to_string(),
};
@@ -113,6 +115,7 @@ impl ClientRequest {
let messages = res_rx.map(|msg| msg).await;
if messages.is_err() {
warn!("Failed to handle_fetch. messages is an error");
return ServerResponse::Error {
message: "Server failed to receive messages".to_string(),
};
@@ -196,6 +199,7 @@ async fn accept_connection(
self_address: DestinationAddressBytes,
topology: Topology,
) {
warn!("accept_connection");
let address = stream
.peer_addr()
.expect("connected streams should have a peer address");
@@ -1,5 +1,3 @@
pub mod addressing;
pub mod bytes;
pub mod poisson;
pub mod sphinx;
pub mod topology;
+40
View File
@@ -0,0 +1,40 @@
use addressing;
use sphinx::route::{Destination, DestinationAddressBytes, SURBIdentifier};
use sphinx::SphinxPacket;
use std::net::SocketAddr;
use topology::NymTopology;
pub const LOOP_COVER_MESSAGE_PAYLOAD: &[u8] = b"The cake is a lie!";
pub fn loop_cover_message<T: NymTopology>(
our_address: DestinationAddressBytes,
surb_id: SURBIdentifier,
topology: &T,
) -> (SocketAddr, SphinxPacket) {
let destination = Destination::new(our_address, surb_id);
encapsulate_message(destination, LOOP_COVER_MESSAGE_PAYLOAD.to_vec(), topology)
}
pub fn encapsulate_message<T: NymTopology>(
recipient: Destination,
message: Vec<u8>,
topology: &T,
) -> (SocketAddr, SphinxPacket) {
let mut providers = topology.get_mix_provider_nodes();
let provider = providers.pop().unwrap().into();
let route = topology.route_to(provider).unwrap();
// Set average packet delay to an arbitrary but at least not super-slow value for testing.
let average_delay = 0.1;
let delays = sphinx::header::delays::generate(route.len(), average_delay);
// build the packet
let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays).unwrap();
let first_node_address =
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address.to_bytes());
(first_node_address, packet)
}
@@ -0,0 +1,7 @@
[healthcheck]
#directory-server = "http://localhost:8080"
directory-server = "https://qa-directory.nymtech.net"
interval = 10.0
test-packets-per-node = 2 # in seconds
resolution-timeout = 5 # in seconds
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
#// Copyright 2020 The Nym Mixnet Authors
#//
#// Licensed under the Apache License, Version 2.0 (the "License");
#// you may not use this file except in compliance with the License.
#// You may obtain a copy of the License at
#//
#// http://www.apache.org/licenses/LICENSE-2.0
#//
#// Unless required by applicable law or agreed to in writing, software
#// distributed under the License is distributed on an "AS IS" BASIS,
#// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#// See the License for the specific language governing permissions and
#// limitations under the License.
echo "Killing old testnet processes..."
killall nym-mixnode
killall nym-sfw-provider
echo "Press CTRL-C to stop."
cargo build
MAX_LAYERS=3
NUMMIXES=${1:-3} # Set $NUMMIXES to default of 3, but allow the user to set other values if desired
for (( j=0; j<$NUMMIXES; j++ ))
# Note: to disable logging (or direct it to another output) modify the constant on top of mixnode or provider;
# Will make it later either configurable by flags or config file.
do
let layer=j%MAX_LAYERS+1
$PWD/target/debug/nym-mixnode run --port $((9980+$j)) --host "localhost" --layer $layer --directory http://localhost:8080 &
sleep 1
done
sleep 1
$PWD/target/debug/nym-sfw-provider run --clientHost "localhost" --mixHost "localhost" --mixPort 9997 --clientPort 9998 --directory http://localhost:8080
# trap call ctrl_c()
trap ctrl_c SIGINT SIGTERM SIGTSTP
function ctrl_c() {
echo "** Trapped SIGINT, SIGTERM and SIGTSTP"
for (( j=0; j<$NUMMIXES; j++ ));
do
kill_port $((9980+$j))
done
}
function kill_port() {
PID=$(lsof -t -i:$1)
echo "$PID"
kill -TERM $PID || kill -KILL $PID
}
+34
View File
@@ -0,0 +1,34 @@
# nym-sfw-provider Changelog
## 0.3.2
* Version increase for consistency with `nym-mixnode` and `nym-client`
## 0.3.1
* Fixed crash when directory server goes down
## 0.3.0
* cleaned up a lot of internal dependencies
* reporting version to the directory server
* printing warning on trying to bind to "localhost", "127.0.0.1" or "0.0.0.0"
* more informative error messages
* generalised identity keys
* generalised Topology handling
* started slow transition to `log` crate by `nym-client`
* start of 'MixMining'
* start of validator node
## 0.2.0
* removed the `--local` flag
* introduced `--directory` argument to support arbitrary directory servers. Leaving it out will point the node at the "https://directory.nymtech.net" alpha testnet server
* IPv6 support
* provider version number is now shown at node start
* directory server location is now shown at node start
## 0.1.0 - Initial Release
* The bare minimum set of features required by a Nym Store and Forward Provider
+2378
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
[package]
build = "build.rs"
name = "nym-sfw-provider"
version = "0.3.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
clap = "2.33.0"
curve25519-dalek = "1.2.3"
hex = "0.4.0"
futures = "0.3.1"
log = "0.4.8"
rand = "0.7.2"
tokio = { version = "0.2.4", features = ["full"] }
sha2 = "0.8.0"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.44"
hmac = "0.7.1"
## internal
crypto = {path = "../common/crypto"}
directory-client = { path = "../common/clients/directory-client" }
sfw-provider-requests = { path = "./sfw-provider-requests" }
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
[build-dependencies]
built = "0.3.2"
+2
View File
@@ -0,0 +1,2 @@
# nym-sfw-provider
A store-and-forward provider for Nym, implemented in Rust
+5
View File
@@ -0,0 +1,5 @@
use built;
fn main() {
built::write_built_file().expect("Failed to acquire build-time information");
}
+462
View File
@@ -0,0 +1,462 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aes-ctr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aes-soft"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aesni"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayref"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blake2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-cipher-trait"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "c2-chacha"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"keystream 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clear_on_drop"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ctr"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curve25519-dalek"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "getrandom"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hkdf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hmac"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "keystream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lioness"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"keystream 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ppv-lite86"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_distr"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sfw-provider-requests"
version = "0.1.0"
dependencies = [
"sphinx 0.1.0",
]
[[package]]
name = "sha2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sphinx"
version = "0.1.0"
dependencies = [
"aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lioness 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stream-cipher"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "subtle"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typenum"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasi"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee"
"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862"
"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736"
"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
"checksum hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa08a006102488bd9cd5b8013aabe84955cf5ae22e304c2caf655b633aefae3"
"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
"checksum keystream 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum lioness 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9"
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c"
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941"
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
@@ -0,0 +1,10 @@
[package]
name = "sfw-provider-requests"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <jedrzej.stuczynski@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sphinx = { git = "https://github.com/nymtech/sphinx", rev="1d8cefcb6a0cb8e87d00d89eb1ccf2839e92aa1f" }
@@ -0,0 +1,7 @@
pub mod requests;
pub mod responses;
pub const DUMMY_MESSAGE_CONTENT: &[u8] =
b"[DUMMY MESSAGE] Wanting something does not give you the right to have it.";
pub type AuthToken = [u8; 32];
@@ -0,0 +1,231 @@
use crate::AuthToken;
use sphinx::route::DestinationAddressBytes;
const PULL_REQUEST_MESSAGE_PREFIX: [u8; 2] = [1, 0];
const REGISTER_MESSAGE_PREFIX: [u8; 2] = [0, 1];
// TODO: how to do it more nicely, considering all sfw-provider-requests implement same trait that is exercised here?
#[derive(Debug)]
pub enum ProviderRequests {
PullMessages(PullRequest),
Register(RegisterRequest),
}
impl ProviderRequests {
pub fn to_bytes(&self) -> Vec<u8> {
use ProviderRequests::*;
match self {
PullMessages(pr) => pr.to_bytes(),
Register(pr) => pr.to_bytes(),
}
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError> {
use ProviderRequests::*;
if bytes.len() < 2 {
return Err(ProviderRequestError::UnmarshalError);
}
let mut received_prefix = [0; 2];
received_prefix.copy_from_slice(&bytes[..2]);
match received_prefix {
PULL_REQUEST_MESSAGE_PREFIX => Ok(PullMessages(PullRequest::from_bytes(bytes)?)),
REGISTER_MESSAGE_PREFIX => Ok(Register(RegisterRequest::from_bytes(bytes)?)),
_ => Err(ProviderRequestError::UnmarshalErrorIncorrectPrefix),
}
}
}
#[derive(Debug)]
pub enum ProviderRequestError {
MarshalError,
UnmarshalError,
UnmarshalErrorIncorrectPrefix,
}
pub trait ProviderRequest
where
Self: Sized,
{
fn get_prefix() -> [u8; 2];
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError>;
}
#[derive(Debug)]
pub struct PullRequest {
// TODO: public keys, signatures, tokens, etc. basically some kind of authentication bs
pub auth_token: AuthToken,
pub destination_address: sphinx::route::DestinationAddressBytes,
}
impl PullRequest {
pub fn new(
destination_address: sphinx::route::DestinationAddressBytes,
auth_token: AuthToken,
) -> Self {
PullRequest {
auth_token,
destination_address,
}
}
}
impl ProviderRequest for PullRequest {
fn get_prefix() -> [u8; 2] {
PULL_REQUEST_MESSAGE_PREFIX
}
fn to_bytes(&self) -> Vec<u8> {
Self::get_prefix()
.to_vec()
.into_iter()
.chain(self.destination_address.iter().cloned())
.chain(self.auth_token.iter().cloned())
.collect()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError> {
if bytes.len() != 2 + 32 + 32 {
return Err(ProviderRequestError::UnmarshalError);
}
let mut received_prefix = [0u8; 2];
received_prefix.copy_from_slice(&bytes[..2]);
if received_prefix != Self::get_prefix() {
return Err(ProviderRequestError::UnmarshalErrorIncorrectPrefix);
}
let mut destination_address = [0u8; 32];
destination_address.copy_from_slice(&bytes[2..34]);
let mut auth_token = [0u8; 32];
auth_token.copy_from_slice(&bytes[34..]);
Ok(PullRequest {
auth_token,
destination_address,
})
}
}
#[derive(Debug)]
pub struct RegisterRequest {
pub destination_address: DestinationAddressBytes,
}
impl RegisterRequest {
pub fn new(destination_address: DestinationAddressBytes) -> Self {
RegisterRequest {
destination_address,
}
}
}
impl ProviderRequest for RegisterRequest {
fn get_prefix() -> [u8; 2] {
REGISTER_MESSAGE_PREFIX
}
fn to_bytes(&self) -> Vec<u8> {
Self::get_prefix()
.to_vec()
.into_iter()
.chain(self.destination_address.iter().cloned())
.collect()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError> {
if bytes.len() != 2 + 32 {
return Err(ProviderRequestError::UnmarshalError);
}
let mut received_prefix = [0u8; 2];
received_prefix.copy_from_slice(&bytes[..2]);
if received_prefix != Self::get_prefix() {
return Err(ProviderRequestError::UnmarshalErrorIncorrectPrefix);
}
let mut destination_address = [0u8; 32];
destination_address.copy_from_slice(&bytes[2..]);
Ok(RegisterRequest {
destination_address,
})
}
}
#[cfg(test)]
mod creating_pull_request {
use super::*;
#[test]
fn it_is_possible_to_recover_it_from_bytes() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let auth_token = [1u8; 32];
let pull_request = PullRequest::new(address, auth_token);
let bytes = pull_request.to_bytes();
let recovered = PullRequest::from_bytes(&bytes).unwrap();
assert_eq!(address, recovered.destination_address);
assert_eq!(auth_token, recovered.auth_token);
}
#[test]
fn it_is_possible_to_recover_it_from_bytes_with_enum_wrapper() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let auth_token = [1u8; 32];
let pull_request = PullRequest::new(address, auth_token);
let bytes = pull_request.to_bytes();
let recovered = ProviderRequests::from_bytes(&bytes).unwrap();
match recovered {
ProviderRequests::PullMessages(req) => {
assert_eq!(address, req.destination_address);
assert_eq!(auth_token, req.auth_token);
}
_ => panic!("expected to recover pull request!"),
}
}
}
#[cfg(test)]
mod creating_register_request {
use super::*;
#[test]
fn it_is_possible_to_recover_it_from_bytes() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let register_request = RegisterRequest::new(address);
let bytes = register_request.to_bytes();
let recovered = RegisterRequest::from_bytes(&bytes).unwrap();
assert_eq!(address, recovered.destination_address);
}
#[test]
fn it_is_possible_to_recover_it_from_bytes_with_enum_wrapper() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let register_request = RegisterRequest::new(address);
let bytes = register_request.to_bytes();
let recovered = ProviderRequests::from_bytes(&bytes).unwrap();
match recovered {
ProviderRequests::Register(req) => {
assert_eq!(address, req.destination_address);
}
_ => panic!("expected to recover pull request!"),
}
}
}
@@ -0,0 +1,151 @@
use crate::AuthToken;
use std::convert::TryInto;
#[derive(Debug)]
pub enum ProviderResponseError {
MarshalError,
UnmarshalError,
UnmarshalErrorInvalidLength,
}
pub trait ProviderResponse
where
Self: Sized,
{
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderResponseError>;
}
#[derive(Debug)]
pub struct PullResponse {
pub messages: Vec<Vec<u8>>,
}
#[derive(Debug)]
pub struct RegisterResponse {
pub auth_token: AuthToken,
}
impl PullResponse {
pub fn new(messages: Vec<Vec<u8>>) -> Self {
PullResponse { messages }
}
}
impl RegisterResponse {
pub fn new(auth_token: AuthToken) -> Self {
RegisterResponse { auth_token }
}
}
// TODO: This should go into some kind of utils module/crate
fn read_be_u16(input: &mut &[u8]) -> u16 {
let (int_bytes, rest) = input.split_at(std::mem::size_of::<u16>());
*input = rest;
u16::from_be_bytes(int_bytes.try_into().unwrap())
}
// TODO: currently this allows for maximum 64kB payload - if we go over that in sphinx,
// we need to update this code.
impl ProviderResponse for PullResponse {
// num_msgs || len1 || len2 || ... || msg1 || msg2 || ...
fn to_bytes(&self) -> Vec<u8> {
let num_msgs = self.messages.len() as u16;
let msgs_lens: Vec<u16> = self.messages.iter().map(|msg| msg.len() as u16).collect();
num_msgs
.to_be_bytes()
.to_vec()
.into_iter()
.chain(
msgs_lens
.into_iter()
.flat_map(|len| len.to_be_bytes().to_vec().into_iter()),
)
.chain(self.messages.iter().flat_map(|msg| msg.clone().into_iter()))
.collect()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderResponseError> {
// can we read number of messages?
if bytes.len() < 2 {
return Err(ProviderResponseError::UnmarshalErrorInvalidLength);
}
let mut bytes_copy = bytes.clone();
let num_msgs = read_be_u16(&mut bytes_copy);
// can we read all lengths of messages?
if bytes_copy.len() < (num_msgs * 2) as usize {
return Err(ProviderResponseError::UnmarshalErrorInvalidLength);
}
let msgs_lens: Vec<_> = (0..num_msgs)
.map(|_| read_be_u16(&mut bytes_copy))
.collect();
let required_remaining_len = msgs_lens
.iter()
.fold(0usize, |acc, &len| acc + (len as usize));
// can we read messages themselves?
if bytes_copy.len() != required_remaining_len {
return Err(ProviderResponseError::UnmarshalErrorInvalidLength);
}
let msgs = msgs_lens
.iter()
.scan(0usize, |i, &len| {
let j = *i + (len as usize);
let msg = bytes_copy[*i..j].to_vec();
*i = j;
Some(msg)
})
.collect();
Ok(PullResponse { messages: msgs })
}
}
impl ProviderResponse for RegisterResponse {
fn to_bytes(&self) -> Vec<u8> {
self.auth_token.to_vec()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderResponseError> {
match bytes.len() {
32 => {
let mut auth_token = [0u8; 32];
auth_token.copy_from_slice(&bytes[..32]);
Ok(RegisterResponse { auth_token })
}
_ => Err(ProviderResponseError::UnmarshalErrorInvalidLength),
}
}
}
#[cfg(test)]
mod creating_pull_response {
use super::*;
#[test]
fn it_is_possible_to_recover_it_from_bytes() {
let msg1 = vec![1, 2, 3, 4, 5];
let msg2 = vec![];
let msg3 = vec![
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4,
5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3,
4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2,
3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1,
2, 3, 4, 5, 1, 2, 3, 4, 5,
];
let msg4 = vec![1, 2, 3, 4, 5, 6, 7];
let msgs = vec![msg1, msg2, msg3, msg4];
let pull_response = PullResponse::new(msgs.clone());
let bytes = pull_response.to_bytes();
let recovered = PullResponse::from_bytes(&bytes).unwrap();
assert_eq!(msgs, recovered.messages);
}
}
+194
View File
@@ -0,0 +1,194 @@
use crate::provider::ServiceProvider;
use clap::{App, Arg, ArgMatches, SubCommand};
use crypto::identity::MixnetIdentityKeyPair;
use std::net::ToSocketAddrs;
use std::path::PathBuf;
use std::process;
pub mod provider;
fn main() {
let arg_matches = App::new("Nym Service Provider")
.version(built_info::PKG_VERSION)
.author("Nymtech")
.about("Implementation of the Loopix-based Service Provider")
.subcommand(
SubCommand::with_name("run")
.about("Starts the service provider")
.arg(
Arg::with_name("mixHost")
.long("mixHost")
.help("The custom host on which the service provider will be running for receiving sphinx packets")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("mixPort")
.long("mixPort")
.help("The port on which the service provider will be listening for sphinx packets")
.takes_value(true)
)
.arg(
Arg::with_name("clientHost")
.long("clientHost")
.help("The custom host on which the service provider will be running for receiving client sfw-provider-requests")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("clientPort")
.long("clientPort")
.help("The port on which the service provider will be listening for client sfw-provider-requests")
.takes_value(true)
)
.arg(
Arg::with_name("storeDir")
.short("s")
.long("storeDir")
.help("Directory storing all packets for the clients")
.takes_value(true)
)
.arg(
Arg::with_name("registeredLedger")
.short("r")
.long("registeredLedger")
.help("Directory of the ledger of registered clients")
.takes_value(true)
)
.arg(
Arg::with_name("directory")
.long("directory")
.help("Address of the directory server the node is sending presence and metrics to")
.takes_value(true),
),
)
.get_matches();
if let Err(e) = execute(arg_matches) {
println!("{}", e);
process::exit(1);
}
}
fn print_binding_warning(address: &str) {
println!("\n##### WARNING #####");
println!(
"\nYou are trying to bind to {} - you might not be accessible to other nodes",
address
);
println!("\n##### WARNING #####\n");
}
fn run(matches: &ArgMatches) {
println!("{}", banner());
let config = new_config(matches);
let provider = ServiceProvider::new(config);
provider.start().unwrap()
}
fn new_config(matches: &ArgMatches) -> provider::Config {
let directory_server = matches
.value_of("directory")
.unwrap_or("https://directory.nymtech.net")
.to_string();
let mix_host = matches.value_of("mixHost").unwrap();
let mix_port = match matches.value_of("mixPort").unwrap_or("8085").parse::<u16>() {
Ok(n) => n,
Err(err) => panic!("Invalid mix host port value provided - {:?}", err),
};
let client_host = matches.value_of("clientHost").unwrap();
let client_port = match matches
.value_of("clientPort")
.unwrap_or("9000")
.parse::<u16>()
{
Ok(n) => n,
Err(err) => panic!("Invalid client port value provided - {:?}", err),
};
if mix_host == "localhost" || mix_host == "127.0.0.1" || mix_host == "0.0.0.0" {
print_binding_warning(mix_host);
}
if client_host == "localhost" || client_host == "127.0.0.1" || client_host == "0.0.0.0" {
print_binding_warning(client_host);
}
let key_pair = crypto::identity::DummyMixIdentityKeyPair::new(); // TODO: persist this so keypairs don't change every restart
let store_dir = PathBuf::from(
matches
.value_of("storeDir")
.unwrap_or("/tmp/nym-provider/inboxes"),
);
let registered_client_ledger_dir = PathBuf::from(
matches
.value_of("registeredLedger")
.unwrap_or("/tmp/nym-provider/registered_clients"),
);
println!("store_dir is: {:?}", store_dir);
println!(
"registered_client_ledger_dir is: {:?}",
registered_client_ledger_dir
);
let mix_socket_address = (mix_host, mix_port)
.to_socket_addrs()
.expect("Failed to combine host and port")
.next()
.expect("Failed to extract the socket address from the iterator");
let client_socket_address = (client_host, client_port)
.to_socket_addrs()
.expect("Failed to combine host and port")
.next()
.expect("Failed to extract the socket address from the iterator");
println!("Listening for mixnet packets on {}", mix_socket_address);
println!("Listening for client requests on {}", client_socket_address);
provider::Config {
mix_socket_address,
directory_server,
public_key: key_pair.public_key,
client_socket_address,
secret_key: key_pair.private_key,
store_dir: PathBuf::from(store_dir),
}
}
pub mod built_info {
// The file has been placed there by the build script.
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
fn execute(matches: ArgMatches) -> Result<(), String> {
match matches.subcommand() {
("run", Some(m)) => Ok(run(m)),
_ => Err(usage()),
}
}
fn usage() -> String {
banner() + "usage: --help to see available options.\n\n"
}
fn banner() -> String {
format!(
r#"
_ __ _ _ _ __ ___
| '_ \| | | | '_ \ _ \
| | | | |_| | | | | | |
|_| |_|\__, |_| |_| |_|
|___/
(store-and-forward provider - version {:})
"#,
built_info::PKG_VERSION
)
}
@@ -0,0 +1,270 @@
use crate::provider::storage::{ClientStorage, StoreError};
use crate::provider::ClientLedger;
use crypto::identity::{DummyMixIdentityPrivateKey, MixnetIdentityPrivateKey};
use futures::lock::Mutex as FMutex;
use hmac::{Hmac, Mac};
use sfw_provider_requests::requests::{
ProviderRequestError, ProviderRequests, PullRequest, RegisterRequest,
};
use sfw_provider_requests::responses::{ProviderResponse, PullResponse, RegisterResponse};
use sfw_provider_requests::AuthToken;
use sha2::Sha256;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;
type HmacSha256 = Hmac<Sha256>;
#[derive(Debug)]
pub enum ClientProcessingError {
ClientDoesntExistError,
StoreError,
InvalidRequest,
WrongToken,
IOError,
}
impl From<ProviderRequestError> for ClientProcessingError {
fn from(_: ProviderRequestError) -> Self {
use ClientProcessingError::*;
InvalidRequest
}
}
impl From<StoreError> for ClientProcessingError {
fn from(e: StoreError) -> Self {
match e {
StoreError::ClientDoesntExistError => ClientProcessingError::ClientDoesntExistError,
_ => ClientProcessingError::StoreError,
}
}
}
impl From<io::Error> for ClientProcessingError {
fn from(_: io::Error) -> Self {
use ClientProcessingError::*;
IOError
}
}
#[derive(Debug)]
pub(crate) struct ClientProcessingData {
store_dir: PathBuf,
registered_clients_ledger: Arc<FMutex<ClientLedger>>,
secret_key: DummyMixIdentityPrivateKey,
}
impl ClientProcessingData {
pub(crate) fn new(
store_dir: PathBuf,
registered_clients_ledger: Arc<FMutex<ClientLedger>>,
secret_key: DummyMixIdentityPrivateKey,
) -> Self {
ClientProcessingData {
store_dir,
registered_clients_ledger,
secret_key,
}
}
pub(crate) fn add_arc(self) -> Arc<Self> {
Arc::new(self)
}
}
pub(crate) struct ClientRequestProcessor;
impl ClientRequestProcessor {
pub(crate) async fn process_client_request(
data: &[u8],
processing_data: Arc<ClientProcessingData>,
) -> Result<Vec<u8>, ClientProcessingError> {
let client_request = ProviderRequests::from_bytes(&data)?;
println!("Received the following request: {:?}", client_request);
match client_request {
ProviderRequests::Register(req) => Ok(ClientRequestProcessor::register_new_client(
req,
processing_data,
)
.await?
.to_bytes()),
ProviderRequests::PullMessages(req) => Ok(
ClientRequestProcessor::process_pull_messages_request(req, processing_data)
.await?
.to_bytes(),
),
}
}
async fn process_pull_messages_request(
req: PullRequest,
processing_data: Arc<ClientProcessingData>,
) -> Result<PullResponse, ClientProcessingError> {
// TODO: this lock is completely unnecessary as we're only reading the data.
// Wait for https://github.com/nymtech/nym-sfw-provider/issues/19 to resolve.
let unlocked_ledger = processing_data.registered_clients_ledger.lock().await;
println!("Processing pull!");
if unlocked_ledger.has_token(req.auth_token) {
// drop the mutex so that we could do IO without blocking others wanting to get the lock
drop(unlocked_ledger);
let retrieved_messages = ClientStorage::retrieve_client_files(
req.destination_address,
processing_data.store_dir.as_path(),
)?;
Ok(PullResponse::new(retrieved_messages))
} else {
Err(ClientProcessingError::WrongToken)
}
}
async fn register_new_client(
req: RegisterRequest,
processing_data: Arc<ClientProcessingData>,
) -> Result<RegisterResponse, ClientProcessingError> {
println!("Processing register new client request!");
let mut unlocked_ledger = processing_data.registered_clients_ledger.lock().await;
let auth_token = ClientRequestProcessor::generate_new_auth_token(
req.destination_address.to_vec(),
processing_data.secret_key,
);
if !unlocked_ledger.has_token(auth_token) {
unlocked_ledger.insert_token(auth_token, req.destination_address);
ClientRequestProcessor::create_storage_dir(
req.destination_address,
processing_data.store_dir.as_path(),
)?;
}
Ok(RegisterResponse::new(auth_token))
}
fn create_storage_dir(
client_address: sphinx::route::DestinationAddressBytes,
store_dir: &Path,
) -> io::Result<()> {
let client_dir_name = hex::encode(client_address);
let full_store_dir = store_dir.join(client_dir_name);
std::fs::create_dir_all(full_store_dir)
}
fn generate_new_auth_token(data: Vec<u8>, key: DummyMixIdentityPrivateKey) -> AuthToken {
let mut auth_token_raw =
HmacSha256::new_varkey(&key.to_bytes()).expect("HMAC can take key of any size");
auth_token_raw.input(&data);
let mut auth_token = [0u8; 32];
auth_token.copy_from_slice(&auth_token_raw.result().code().to_vec());
auth_token
}
}
#[cfg(test)]
mod register_new_client {
// use super::*;
// TODO: those tests require being called in async context. we need to research how to test this stuff...
// #[test]
// fn registers_new_auth_token_for_each_new_client() {
// let req1 = RegisterRequest {
// destination_address: [1u8; 32],
// };
// let registered_client_ledger = ClientLedger::new();
// let store_dir = PathBuf::from("./foo/");
// let key = Scalar::from_bytes_mod_order([1u8; 32]);
// let client_processing_data = ClientProcessingData::new(store_dir, registered_client_ledger, key).add_arc_futures_mutex();
//
//
// // need to do async....
// client_processing_data.lock().await;
// assert_eq!(0, registered_client_ledger.0.len());
// ClientRequestProcessor::register_new_client(
// req1,
// client_processing_data.clone(),
// );
//
// assert_eq!(1, registered_client_ledger.0.len());
//
// let req2 = RegisterRequest {
// destination_address: [2u8; 32],
// };
// ClientRequestProcessor::register_new_client(
// req2,
// client_processing_data,
// );
// assert_eq!(2, registered_client_ledger.0.len());
// }
//
// #[test]
// fn registers_given_token_only_once() {
// let req1 = RegisterRequest {
// destination_address: [1u8; 32],
// };
// let registered_client_ledger = ClientLedger::new();
// let store_dir = PathBuf::from("./foo/");
// let key = Scalar::from_bytes_mod_order([1u8; 32]);
// let client_processing_data = ClientProcessingData::new(store_dir, registered_client_ledger, key).add_arc_futures_mutex();
//
// ClientRequestProcessor::register_new_client(
// req1,
// client_processing_data.clone(),
// );
// let req2 = RegisterRequest {
// destination_address: [1u8; 32],
// };
// ClientRequestProcessor::register_new_client(
// req2,
// client_processing_data.clone(),
// );
//
// client_processing_data.lock().await;
//
// assert_eq!(1, registered_client_ledger.0.len())
// }
}
#[cfg(test)]
mod create_storage_dir {
use super::*;
use sphinx::route::DestinationAddressBytes;
#[test]
fn it_creates_a_correct_storage_directory() {
let client_address: DestinationAddressBytes = [1u8; 32];
let store_dir = Path::new("/tmp/");
ClientRequestProcessor::create_storage_dir(client_address, store_dir).unwrap();
}
}
#[cfg(test)]
mod generating_new_auth_token {
use super::*;
#[test]
fn for_the_same_input_generates_the_same_auth_token() {
let data1 = vec![1u8; 55];
let data2 = vec![1u8; 55];
let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_eq!(token1, token2);
}
#[test]
fn for_different_inputs_generates_different_auth_tokens() {
let data1 = vec![1u8; 55];
let data2 = vec![2u8; 55];
let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_ne!(token1, token2);
let data1 = vec![1u8; 50];
let data2 = vec![2u8; 55];
let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_ne!(token1, token2);
}
}
@@ -0,0 +1,83 @@
use crate::provider::storage::StoreData;
use crypto::identity::DummyMixIdentityPrivateKey;
use sphinx::{ProcessedPacket, SphinxPacket};
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
// TODO: this will probably need to be moved elsewhere I imagine
// DUPLICATE WITH MIXNODE CODE!!!
#[derive(Debug)]
pub enum MixProcessingError {
SphinxRecoveryError,
ReceivedForwardHopError,
InvalidPayload,
NonMatchingRecipient,
FileIOFailure,
}
impl From<sphinx::ProcessingError> for MixProcessingError {
// for time being just have a single error instance for all possible results of sphinx::ProcessingError
fn from(_: sphinx::ProcessingError) -> Self {
use MixProcessingError::*;
SphinxRecoveryError
}
}
impl From<std::io::Error> for MixProcessingError {
fn from(_: std::io::Error) -> Self {
use MixProcessingError::*;
FileIOFailure
}
}
// ProcessingData defines all data required to correctly unwrap sphinx packets
#[derive(Debug, Clone)]
pub(crate) struct MixProcessingData {
secret_key: DummyMixIdentityPrivateKey,
pub(crate) store_dir: PathBuf,
}
impl MixProcessingData {
pub(crate) fn new(secret_key: DummyMixIdentityPrivateKey, store_dir: PathBuf) -> Self {
MixProcessingData {
secret_key,
store_dir,
}
}
pub(crate) fn add_arc_rwlock(self) -> Arc<RwLock<Self>> {
Arc::new(RwLock::new(self))
}
}
pub(crate) struct MixPacketProcessor(());
impl MixPacketProcessor {
pub fn process_sphinx_data_packet(
packet_data: &[u8],
processing_data: &RwLock<MixProcessingData>,
) -> Result<StoreData, MixProcessingError> {
let packet = SphinxPacket::from_bytes(packet_data.to_vec())?;
let read_processing_data = processing_data.read().unwrap();
let (client_address, client_surb_id, payload) =
match packet.process(read_processing_data.secret_key.as_scalar()) {
ProcessedPacket::ProcessedPacketFinalHop(client_address, surb_id, payload) => {
(client_address, surb_id, payload)
}
_ => return Err(MixProcessingError::ReceivedForwardHopError),
};
// TODO: should provider try to be recovering plaintext? this would potentially make client retrieve messages of non-constant length,
// perhaps provider should be re-padding them on retrieval or storing full data?
let (payload_destination, message) = payload
.try_recover_destination_and_plaintext()
.ok_or_else(|| MixProcessingError::InvalidPayload)?;
if client_address != payload_destination {
return Err(MixProcessingError::NonMatchingRecipient);
}
Ok(StoreData::new(client_address, client_surb_id, message))
}
}
+314
View File
@@ -0,0 +1,314 @@
use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor};
use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData};
use crate::provider::storage::ClientStorage;
use crypto::identity::{DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey};
use directory_client::presence::MixProviderClient;
use futures::io::Error;
use futures::lock::Mutex as FMutex;
use sfw_provider_requests::AuthToken;
use sphinx::route::DestinationAddressBytes;
use std::collections::HashMap;
use std::net::{Shutdown, SocketAddr};
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::RwLock;
use tokio::prelude::*;
use tokio::runtime::Runtime;
mod client_handling;
mod mix_handling;
pub mod presence;
mod storage;
// TODO: if we ever create config file, this should go there
const STORED_MESSAGE_FILENAME_LENGTH: usize = 16;
const MESSAGE_RETRIEVAL_LIMIT: usize = 5;
pub struct Config {
pub client_socket_address: SocketAddr,
pub directory_server: String,
pub mix_socket_address: SocketAddr,
pub public_key: DummyMixIdentityPublicKey,
pub secret_key: DummyMixIdentityPrivateKey,
pub store_dir: PathBuf,
}
#[derive(Debug)]
pub enum ProviderError {
TcpListenerBindingError,
TcpListenerConnectionError,
TcpListenerUnexpectedEof,
TcpListenerUnknownError,
}
impl From<io::Error> for ProviderError {
fn from(err: Error) -> Self {
use ProviderError::*;
match err.kind() {
io::ErrorKind::ConnectionRefused => TcpListenerConnectionError,
io::ErrorKind::ConnectionReset => TcpListenerConnectionError,
io::ErrorKind::ConnectionAborted => TcpListenerConnectionError,
io::ErrorKind::NotConnected => TcpListenerConnectionError,
io::ErrorKind::AddrInUse => TcpListenerBindingError,
io::ErrorKind::AddrNotAvailable => TcpListenerBindingError,
io::ErrorKind::UnexpectedEof => TcpListenerUnexpectedEof,
_ => TcpListenerUnknownError,
}
}
}
#[derive(Debug)]
pub struct ClientLedger(HashMap<AuthToken, DestinationAddressBytes>);
impl ClientLedger {
fn new() -> Self {
ClientLedger(HashMap::new())
}
fn add_arc_futures_mutex(self) -> Arc<FMutex<Self>> {
Arc::new(FMutex::new(self))
}
fn has_token(&self, auth_token: AuthToken) -> bool {
return self.0.contains_key(&auth_token);
}
fn insert_token(
&mut self,
auth_token: AuthToken,
client_address: DestinationAddressBytes,
) -> Option<DestinationAddressBytes> {
self.0.insert(auth_token, client_address)
}
fn current_clients(&self) -> Vec<MixProviderClient> {
self.0
.iter()
.map(|(_, v)| base64::encode_config(v, base64::URL_SAFE))
.map(|pub_key| MixProviderClient { pub_key })
.collect()
}
#[allow(dead_code)]
fn load(_file: PathBuf) -> Self {
unimplemented!()
}
}
pub struct ServiceProvider {
directory_server: String,
mix_network_address: SocketAddr,
client_network_address: SocketAddr,
public_key: DummyMixIdentityPublicKey,
secret_key: DummyMixIdentityPrivateKey,
store_dir: PathBuf,
registered_clients_ledger: ClientLedger,
}
impl ServiceProvider {
pub fn new(config: Config) -> Self {
ServiceProvider {
mix_network_address: config.mix_socket_address,
client_network_address: config.client_socket_address,
secret_key: config.secret_key,
public_key: config.public_key,
store_dir: PathBuf::from(config.store_dir.clone()),
// TODO: load initial ledger from file
registered_clients_ledger: ClientLedger::new(),
directory_server: config.directory_server.clone(),
}
}
async fn process_mixnet_socket_connection(
mut socket: tokio::net::TcpStream,
processing_data: Arc<RwLock<MixProcessingData>>,
) {
let mut buf = [0u8; sphinx::PACKET_SIZE];
// In a loop, read data from the socket and write the data back.
loop {
match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => {
println!("Remote connection closed.");
return;
}
Ok(_) => {
let store_data = match MixPacketProcessor::process_sphinx_data_packet(
buf.as_ref(),
processing_data.as_ref(),
) {
Ok(sd) => sd,
Err(e) => {
eprintln!("failed to process sphinx packet; err = {:?}", e);
return;
}
};
ClientStorage::store_processed_data(
store_data,
processing_data.read().unwrap().store_dir.as_path(),
)
.unwrap_or_else(|e| {
eprintln!("failed to store processed sphinx message; err = {:?}", e);
return;
});
}
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the some data back
if let Err(e) = socket.write_all(b"foomp").await {
eprintln!("failed to write reply to socket; err = {:?}", e);
return;
}
}
}
async fn send_response(mut socket: tokio::net::TcpStream, data: &[u8]) {
if let Err(e) = socket.write_all(data).await {
eprintln!("failed to write reply to socket; err = {:?}", e)
}
if let Err(e) = socket.shutdown(Shutdown::Write) {
eprintln!("failed to close write part of the socket; err = {:?}", e)
}
}
// TODO: FIGURE OUT HOW TO SET READ_DEADLINES IN TOKIO
async fn process_client_socket_connection(
mut socket: tokio::net::TcpStream,
processing_data: Arc<ClientProcessingData>,
) {
let mut buf = [0; 1024];
// TODO: restore the for loop once we go back to persistent tcp socket connection
let response = match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => {
println!("Remote connection closed.");
Err(())
}
Ok(n) => {
match ClientRequestProcessor::process_client_request(
buf[..n].as_ref(),
processing_data,
)
.await
{
Err(e) => {
eprintln!("failed to process client request; err = {:?}", e);
Err(())
}
Ok(res) => Ok(res),
}
}
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
Err(())
}
};
if let Err(e) = socket.shutdown(Shutdown::Read) {
eprintln!("failed to close read part of the socket; err = {:?}", e)
}
match response {
Ok(res) => {
println!("should send this response! {:?}", res);
ServiceProvider::send_response(socket, &res).await;
}
_ => {
println!("we failed...");
ServiceProvider::send_response(socket, b"bad foomp").await;
}
}
}
async fn start_mixnet_listening(
address: SocketAddr,
secret_key: DummyMixIdentityPrivateKey,
store_dir: PathBuf,
) -> Result<(), ProviderError> {
let mut listener = tokio::net::TcpListener::bind(address).await?;
let processing_data = MixProcessingData::new(secret_key, store_dir).add_arc_rwlock();
loop {
let (socket, _) = listener.accept().await?;
// do note that the underlying data is NOT copied here; arc is incremented and lock is shared
// (if I understand it all correctly)
let thread_processing_data = processing_data.clone();
tokio::spawn(async move {
ServiceProvider::process_mixnet_socket_connection(socket, thread_processing_data)
.await
});
}
}
async fn start_client_listening(
address: SocketAddr,
store_dir: PathBuf,
client_ledger: Arc<FMutex<ClientLedger>>,
secret_key: DummyMixIdentityPrivateKey,
) -> Result<(), ProviderError> {
let mut listener = tokio::net::TcpListener::bind(address).await?;
let processing_data =
ClientProcessingData::new(store_dir, client_ledger, secret_key).add_arc();
loop {
let (socket, _) = listener.accept().await?;
// do note that the underlying data is NOT copied here; arc is incremented and lock is shared
// (if I understand it all correctly)
let thread_processing_data = processing_data.clone();
tokio::spawn(async move {
ServiceProvider::process_client_socket_connection(socket, thread_processing_data)
.await
});
}
}
// Note: this now consumes the provider
pub fn start(self) -> Result<(), Box<dyn std::error::Error>> {
// Create the runtime, probably later move it to Provider struct itself?
// TODO: figure out the difference between Runtime and Handle
let mut rt = Runtime::new()?;
// let mut h = rt.handle();
let initial_client_ledger = self.registered_clients_ledger;
let thread_shareable_ledger = initial_client_ledger.add_arc_futures_mutex();
let presence_notifier = presence::Notifier::new(
self.directory_server,
self.client_network_address.clone(),
self.mix_network_address.clone(),
self.public_key,
thread_shareable_ledger.clone(),
);
let presence_future = rt.spawn(presence_notifier.run());
let mix_future = rt.spawn(ServiceProvider::start_mixnet_listening(
self.mix_network_address,
self.secret_key.clone(),
self.store_dir.clone(),
));
let client_future = rt.spawn(ServiceProvider::start_client_listening(
self.client_network_address,
self.store_dir.clone(),
thread_shareable_ledger,
self.secret_key,
));
// Spawn the root task
rt.block_on(async {
let future_results =
futures::future::join3(mix_future, client_future, presence_future).await;
assert!(future_results.0.is_ok() && future_results.1.is_ok());
});
// this line in theory should never be reached as the runtime should be permanently blocked on listeners
eprintln!("The server went kaput...");
Ok(())
}
}
+70
View File
@@ -0,0 +1,70 @@
use crate::provider::ClientLedger;
use crypto::identity::DummyMixIdentityPublicKey;
use directory_client::presence::MixProviderPresence;
use directory_client::requests::presence_providers_post::PresenceMixProviderPoster;
use directory_client::DirectoryClient;
use futures::lock::Mutex as FMutex;
use log::{debug, error};
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
pub struct Notifier {
pub net_client: directory_client::Client,
client_ledger: Arc<FMutex<ClientLedger>>,
client_listener: String,
mixnet_listener: String,
pub_key: String,
}
impl Notifier {
pub fn new(
directory_server_address: String,
client_listener: SocketAddr,
mixnet_listener: SocketAddr,
pub_key: DummyMixIdentityPublicKey,
client_ledger: Arc<FMutex<ClientLedger>>,
) -> Notifier {
let directory_config = directory_client::Config {
base_url: directory_server_address,
};
let net_client = directory_client::Client::new(directory_config);
Notifier {
net_client,
client_listener: client_listener.to_string(),
mixnet_listener: mixnet_listener.to_string(),
pub_key: pub_key.to_b64_string(),
client_ledger,
}
}
async fn make_presence(&self) -> MixProviderPresence {
let unlocked_ledger = self.client_ledger.lock().await;
MixProviderPresence {
client_listener: self.client_listener.clone(),
mixnet_listener: self.mixnet_listener.clone(),
pub_key: self.pub_key.clone(),
registered_clients: unlocked_ledger.current_clients(),
last_seen: 0,
version: env!("CARGO_PKG_VERSION").to_string(),
}
}
pub fn notify(&self, presence: MixProviderPresence) {
match self.net_client.presence_providers_post.post(&presence) {
Err(err) => error!("failed to send presence - {:?}", err),
Ok(_) => debug!("sent presence information"),
}
}
pub async fn run(self) {
loop {
let presence = self.make_presence().await;
self.notify(presence);
let delay_duration = Duration::from_secs(5);
tokio::time::delay_for(delay_duration).await;
}
}
}
+124
View File
@@ -0,0 +1,124 @@
use crate::provider::{MESSAGE_RETRIEVAL_LIMIT, STORED_MESSAGE_FILENAME_LENGTH};
use rand::Rng;
use sfw_provider_requests::DUMMY_MESSAGE_CONTENT;
use sphinx::route::{DestinationAddressBytes, SURBIdentifier};
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
pub enum StoreError {
ClientDoesntExistError,
FileIOFailure,
}
impl From<std::io::Error> for StoreError {
fn from(_: std::io::Error) -> Self {
use StoreError::*;
FileIOFailure
}
}
pub struct StoreData {
client_address: DestinationAddressBytes,
#[allow(dead_code)]
client_surb_id: SURBIdentifier,
message: Vec<u8>,
}
impl StoreData {
pub(crate) fn new(
client_address: DestinationAddressBytes,
client_surb_id: SURBIdentifier,
message: Vec<u8>,
) -> Self {
StoreData {
client_address,
client_surb_id,
message,
}
}
}
// TODO: replace with database
pub struct ClientStorage(());
// TODO: change it to some generic implementation to inject fs
impl ClientStorage {
pub(crate) fn generate_random_file_name() -> String {
rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(STORED_MESSAGE_FILENAME_LENGTH)
.collect::<String>()
}
fn dummy_message() -> Vec<u8> {
// TODO: should it be padded to constant length?
DUMMY_MESSAGE_CONTENT.to_vec()
}
pub fn store_processed_data(store_data: StoreData, store_dir: &Path) -> io::Result<()> {
let client_dir_name = hex::encode(store_data.client_address);
let full_store_dir = store_dir.join(client_dir_name);
let full_store_path = full_store_dir.join(ClientStorage::generate_random_file_name());
println!(
"going to store: {:?} in file: {:?}",
store_data.message, full_store_path
);
// TODO: what to do with surbIDs??
// we can use normal io here, no need for tokio as it's all happening in one thread per connection
let mut file = File::create(full_store_path)?;
file.write_all(store_data.message.as_ref())?;
Ok(())
}
pub fn retrieve_client_files(
client_address: DestinationAddressBytes,
store_dir: &Path,
) -> Result<Vec<Vec<u8>>, StoreError> {
let client_dir_name = hex::encode(client_address);
let full_store_dir = store_dir.join(client_dir_name);
println!("going to lookup: {:?}!", full_store_dir);
if !full_store_dir.exists() {
return Err(StoreError::ClientDoesntExistError);
}
let msgs: Vec<_> = std::fs::read_dir(full_store_dir)?
.map(|entry| entry.unwrap())
.filter(|entry| {
let is_file = entry.metadata().unwrap().is_file();
if !is_file {
eprintln!(
"potentially corrupted client inbox! - found a non-file - {:?}",
entry.path()
);
}
is_file
})
.map(|entry| {
let content = std::fs::read(entry.path()).unwrap();
ClientStorage::delete_file(entry.path()).unwrap();
content
}) // TODO: THIS MAP IS UNSAFE (BOTH FOR READING AND DELETING)!! - in the case where there are multiple requests from the same client being processed in parallel
.chain(std::iter::repeat(ClientStorage::dummy_message()))
.take(MESSAGE_RETRIEVAL_LIMIT)
.collect();
println!("retrieved the following data: {:?}", msgs);
Ok(msgs)
}
// TODO: THIS NEEDS A LOCKING MECHANISM!!! (or a db layer on top - basically 'ClientStorage' on steroids)
// TODO 2: This should only be called AFTER we sent the reply. Because if client's connection failed after sending request
// the messages would be deleted but he wouldn't have received them
fn delete_file(path: PathBuf) -> io::Result<()> {
println!("Here {:?} will be deleted!", path);
std::fs::remove_file(path) // another argument for db layer -> remove_file is NOT guaranteed to immediately get rid of the file
}
}
-41
View File
@@ -1,41 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CocoPresence {
pub host: String,
pub pub_key: String,
pub last_seen: i64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MixNodePresence {
pub host: String,
pub pub_key: String,
pub layer: u64,
pub last_seen: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MixProviderPresence {
pub host: String,
pub pub_key: String,
pub registered_clients: Vec<MixProviderClient>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MixProviderClient {
pub pub_key: String,
}
// Topology shows us the current state of the overall Nym network
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Topology {
pub coco_nodes: Vec<CocoPresence>,
pub mix_nodes: Vec<MixNodePresence>,
pub mix_provider_nodes: Vec<MixProviderPresence>,
}
-1
View File
@@ -1 +0,0 @@
-17
View File
@@ -1,17 +0,0 @@
use crate::banner;
//use crate::clients::NymClient;
//use crate::persistence::pemstore;
use clap::ArgMatches;
pub fn execute(_matches: &ArgMatches) {
println!("{}", banner());
panic!("For time being this command is deprecated! Please use 'websocket' instead");
//
// let is_local = matches.is_present("local");
// let id = matches.value_of("id").unwrap().to_string();
// println!("Starting client...");
//
// let keypair = pemstore::read_keypair_from_disk(id);
// let client = NymClient::new(keypair.public_bytes(), is_local);
// client.start("127.0.0.1:9000".parse().unwrap()).unwrap();
}
-33
View File
@@ -1,33 +0,0 @@
use curve25519_dalek::montgomery::MontgomeryPoint;
use curve25519_dalek::scalar::Scalar;
// This keypair serves as the user's identity within the Mixnet
pub struct KeyPair {
pub private: Scalar,
pub public: MontgomeryPoint,
}
impl KeyPair {
pub fn new() -> KeyPair {
let (private, public) = sphinx::crypto::keygen();
KeyPair { private, public }
}
pub fn from_bytes(private_bytes: Vec<u8>, public_bytes: Vec<u8>) -> KeyPair {
let mut bytes = [0; 32];
bytes.copy_from_slice(&private_bytes[..]);
let private = Scalar::from_canonical_bytes(bytes).unwrap();
let mut bytes = [0; 32];
bytes.copy_from_slice(&public_bytes[..]);
let public = MontgomeryPoint(bytes);
KeyPair { private, public }
}
pub fn private_bytes(&self) -> [u8; 32] {
self.private.to_bytes()
}
pub fn public_bytes(&self) -> [u8; 32] {
self.public.to_bytes()
}
}
-1
View File
@@ -1 +0,0 @@
pub mod mixnet;
-1
View File
@@ -1 +0,0 @@
// TODO types for Validator keys will go in here once we hook this up.
-5
View File
@@ -1,5 +0,0 @@
pub mod clients;
pub mod identity;
pub mod persistence;
pub mod sockets;
pub mod utils;
-72
View File
@@ -1,72 +0,0 @@
use crate::identity::mixnet::KeyPair;
use crate::persistence::pathfinder::Pathfinder;
use pem::{encode, parse, Pem};
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
pub fn read_keypair_from_disk(id: String) -> KeyPair {
let pathfinder = Pathfinder::new(id);
let pem_store = PemStore::new(pathfinder);
let keypair = pem_store.read();
keypair
}
pub struct PemStore {
config_dir: PathBuf,
private_mix_key: PathBuf,
public_mix_key: PathBuf,
}
impl PemStore {
pub fn new(pathfinder: Pathfinder) -> PemStore {
PemStore {
config_dir: pathfinder.config_dir,
private_mix_key: pathfinder.private_mix_key,
public_mix_key: pathfinder.public_mix_key,
}
}
pub fn read(&self) -> KeyPair {
let private = self.read_file(self.private_mix_key.clone());
let public = self.read_file(self.public_mix_key.clone());
KeyPair::from_bytes(private, public)
}
fn read_file(&self, filepath: PathBuf) -> Vec<u8> {
let mut pem_bytes = File::open(filepath).unwrap();
let mut buf = Vec::new();
pem_bytes.read_to_end(&mut buf).unwrap();
let pem = parse(&buf).unwrap();
pem.contents
}
// This should be refactored and made more generic for when we have other kinds of
// KeyPairs that we want to persist (e.g. validator keypairs, or keys for
// signing vs encryption). However, for the moment, it does the job.
pub fn write(&self, key_pair: KeyPair) {
std::fs::create_dir_all(self.config_dir.clone()).unwrap();
self.write_pem_file(
self.private_mix_key.clone(),
key_pair.private_bytes(),
String::from("SPHINX CURVE25519 PRIVATE KEY"),
);
self.write_pem_file(
self.public_mix_key.clone(),
key_pair.public_bytes(),
String::from("SPHINX CURVE25519 PUBLIC KEY"),
);
}
fn write_pem_file(&self, filepath: PathBuf, data: [u8; 32], tag: String) {
let pem = Pem {
tag,
contents: data.to_vec(),
};
let key = encode(&pem);
let mut file = File::create(filepath).unwrap();
file.write_all(key.as_bytes()).unwrap();
}
}
-48
View File
@@ -1,48 +0,0 @@
use crate::clients::directory::presence::Topology;
use crate::utils::{addressing, bytes, topology};
use curve25519_dalek::montgomery::MontgomeryPoint;
use sphinx::route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, SURBIdentifier};
use sphinx::SphinxPacket;
use std::net::SocketAddr;
pub const LOOP_COVER_MESSAGE_PAYLOAD: &[u8] = b"The cake is a lie!";
pub fn loop_cover_message(
our_address: DestinationAddressBytes,
surb_id: SURBIdentifier,
topology: &Topology,
) -> (SocketAddr, SphinxPacket) {
let destination = Destination::new(our_address, surb_id);
encapsulate_message(destination, LOOP_COVER_MESSAGE_PAYLOAD.to_vec(), topology)
}
pub fn encapsulate_message(
recipient: Destination,
message: Vec<u8>,
topology: &Topology,
) -> (SocketAddr, SphinxPacket) {
let mixes_route = topology::route_from(&topology);
let first_provider = topology.mix_provider_nodes.first().unwrap();
let decoded_key_bytes =
base64::decode_config(&first_provider.pub_key, base64::URL_SAFE).unwrap();
let key_bytes = bytes::zero_pad_to_32(decoded_key_bytes);
let key = MontgomeryPoint(key_bytes);
let provider = Node::new(
addressing::encoded_bytes_from_socket_address(first_provider.host.clone().parse().unwrap()),
key,
);
let route = [mixes_route, vec![provider]].concat();
let delays = sphinx::header::delays::generate(route.len());
// build the packet
let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays).unwrap();
let first_node_address =
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address);
(first_node_address, packet)
}

Some files were not shown because too many files have changed in this diff Show More