mirror of
				https://github.com/markqvist/reticulum-cpp.git
				synced 2025-10-30 21:08:52 -04:00 
			
		
		
		
	WIP: Initial implementation of persistence
Persistence implemented with the help of the ArduinoJson library using MessagePack format.
This commit is contained in:
		
							parent
							
								
									c39e9e7fee
								
							
						
					
					
						commit
						f9a679ffc2
					
				
					 20 changed files with 1031 additions and 54 deletions
				
			
		
							
								
								
									
										34
									
								
								library.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								library.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| { | ||||
| 	"name": "reticulum-cpp", | ||||
| 	"version": "0.1.1", | ||||
| 	"description": "Port of Reticulum Network Stack to C++ specifically but not exclusively targeting 32-bit and better MCUs", | ||||
| 	"keywords": "reticulum, mcu, esp32", | ||||
| 	"repository": | ||||
| 	{ | ||||
| 		"type": "git", | ||||
| 		"url": "https://github.com/attermann/reticulum-cpp.git" | ||||
| 	}, | ||||
| 	"authors": | ||||
| 	[ | ||||
| 		{ | ||||
| 			"name": "Chad Attermann", | ||||
| 			"email": "attermann@gmail.com", | ||||
| 			"maintainer": true | ||||
| 		} | ||||
| 	], | ||||
| 	"license": "Apache-2.0", | ||||
| 	"homepage": "https://reticulum.network/", | ||||
| 	"dependencies": { | ||||
| 		"ArduinoJson": "~6.21.3", | ||||
| 		"rweather/Crypto": "~0.4.0", | ||||
| 		"sandeepmistry/LoRa": "~0.8.0", | ||||
| 		"WiFi": "~2.0.0" | ||||
| 	}, | ||||
| 	"frameworks": "*", | ||||
| 	"platforms": "*", | ||||
| 	"export": {}, | ||||
| 	"exclude": [ | ||||
| 		".github", | ||||
| 		"src/main.cpp" | ||||
| 	] | ||||
| } | ||||
|  | @ -23,6 +23,7 @@ build_flags = | |||
| 	-Isrc | ||||
| 	-DNATIVE | ||||
| lib_deps =  | ||||
| 	ArduinoJson@^6.21.3 | ||||
| 	rweather/Crypto@^0.4.0 | ||||
| lib_compat_mode = off | ||||
| 
 | ||||
|  | @ -54,6 +55,7 @@ build_flags = | |||
| 	-Wno-format | ||||
| 	-Isrc | ||||
| lib_deps =  | ||||
| 	ArduinoJson@^6.21.3 | ||||
| 	rweather/Crypto@^0.4.0 | ||||
| 	sandeepmistry/LoRa@^0.8.0 | ||||
| 	WiFi@^2.0.0 | ||||
|  |  | |||
							
								
								
									
										14
									
								
								src/Bytes.h
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								src/Bytes.h
									
										
									
									
									
								
							|  | @ -2,6 +2,8 @@ | |||
| 
 | ||||
| #include "Log.h" | ||||
| 
 | ||||
| #include <ArduinoJson.h> | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| #include <string.h> | ||||
|  | @ -257,3 +259,15 @@ inline RNS::Bytes& operator << (RNS::Bytes& lhbytes, uint8_t rhbyte) { | |||
| 	lhbytes.append(rhbyte); | ||||
| 	return lhbytes; | ||||
| } | ||||
| 
 | ||||
| namespace ArduinoJson { | ||||
| 	inline bool convertToJson(const RNS::Bytes& src, JsonVariant dst) { | ||||
| 		return dst.set(src.toHex()); | ||||
| 	} | ||||
| 	inline void convertFromJson(JsonVariantConst src, RNS::Bytes& dst) { | ||||
| 		dst.assignHex(src.as<const char*>()); | ||||
| 	} | ||||
| 	inline bool canConvertFromJson(JsonVariantConst src, const RNS::Bytes&) { | ||||
| 		return src.is<const char*>(); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -36,16 +36,16 @@ void Identity::createKeys() { | |||
| 	_object->_prv_bytes     = _object->_prv->private_bytes(); | ||||
| 	debug("Identity::createKeys: prv bytes:     " + _object->_prv_bytes.toHex()); | ||||
| 
 | ||||
| 	// CRYPTO: create encryption public keys
 | ||||
| 	_object->_pub           = _object->_prv->public_key(); | ||||
| 	_object->_pub_bytes     = _object->_pub->public_bytes(); | ||||
| 	debug("Identity::createKeys: pub bytes:     " + _object->_pub_bytes.toHex()); | ||||
| 
 | ||||
| 	// CRYPTO: create signature private keys
 | ||||
| 	_object->_sig_prv       = Cryptography::Ed25519PrivateKey::generate(); | ||||
| 	_object->_sig_prv_bytes = _object->_sig_prv->private_bytes(); | ||||
| 	debug("Identity::createKeys: sig prv bytes: " + _object->_sig_prv_bytes.toHex()); | ||||
| 
 | ||||
| 	// CRYPTO: create encryption public keys
 | ||||
| 	_object->_pub           = _object->_prv->public_key(); | ||||
| 	_object->_pub_bytes     = _object->_pub->public_bytes(); | ||||
| 	debug("Identity::createKeys: pub bytes:     " + _object->_pub_bytes.toHex()); | ||||
| 
 | ||||
| 	// CRYPTO: create signature public keys
 | ||||
| 	_object->_sig_pub       = _object->_sig_prv->public_key(); | ||||
| 	_object->_sig_pub_bytes = _object->_sig_pub->public_bytes(); | ||||
|  | @ -127,20 +127,58 @@ void Identity::load_public_key(const Bytes& pub_bytes) { | |||
| } | ||||
| 
 | ||||
| bool Identity::load(const char* path) { | ||||
| /*
 | ||||
| 	try: | ||||
| 		with open(path, "rb") as key_file: | ||||
| 			prv_bytes = key_file.read() | ||||
| 			return self.load_private_key(prv_bytes) | ||||
| 		return False | ||||
| 	except Exception as e: | ||||
| 		RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR) | ||||
| 		RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) | ||||
| */ | ||||
| 	// MOCK
 | ||||
| 	return true; | ||||
| 	extreme("Reading identity key from storage..."); | ||||
| 	try { | ||||
| 		Bytes prv_bytes = OS::read_file(path); | ||||
| 		if (prv_bytes) { | ||||
| 			return load_private_key(prv_bytes); | ||||
| 		} | ||||
| 		else { | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	catch (std::exception& e) { | ||||
| 		error("Error while loading identity from " + std::string(path)); | ||||
| 		error("The contained exception was: " + std::string(e.what())); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| Saves the identity to a file. This will write the private key to disk, | ||||
| and anyone with access to this file will be able to decrypt all | ||||
| communication for the identity. Be very careful with this method. | ||||
| 
 | ||||
| :param path: The full path specifying where to save the identity. | ||||
| :returns: True if the file was saved, otherwise False. | ||||
| */ | ||||
| bool Identity::to_file(const char* path) { | ||||
| 	extreme("Writing identity key to storage..."); | ||||
| 	try { | ||||
| 		return OS::write_file(get_private_key(), path); | ||||
| 	} | ||||
| 	catch (std::exception& e) { | ||||
| 		error("Error while saving identity to " + std::string(path)); | ||||
| 		error("The contained exception was: " + std::string(e.what())); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
| Create a new :ref:`RNS.Identity<api-identity>` instance from a file. | ||||
| Can be used to load previously created and saved identities into Reticulum. | ||||
| 
 | ||||
| :param path: The full path to the saved :ref:`RNS.Identity<api-identity>` data | ||||
| :returns: A :ref:`RNS.Identity<api-identity>` instance, or *None* if the loaded data was invalid. | ||||
| */ | ||||
| /*static*/ const Identity Identity::from_file(const char* path) { | ||||
| 	Identity identity(false); | ||||
| 	if (identity.load(path)) { | ||||
| 		return identity; | ||||
| 	} | ||||
| 	return {Type::NONE}; | ||||
| } | ||||
| 
 | ||||
| /*static*/ void Identity::remember(const Bytes& packet_hash, const Bytes& destination_hash, const Bytes& public_key, const Bytes& app_data /*= {Bytes::NONE}*/) { | ||||
| 	if (public_key.size() != Type::Identity::KEYSIZE/8) { | ||||
|  |  | |||
|  | @ -90,6 +90,7 @@ namespace RNS { | |||
| 			_object->_hexhash = _object->_hash.toHex(); | ||||
| 		}; | ||||
| 		bool load(const char* path); | ||||
| 		bool to_file(const char* path); | ||||
| 
 | ||||
| 		inline const Bytes get_salt() const { assert(_object); return _object->_hash; } | ||||
| 		inline const Bytes get_context() const { return {Bytes::NONE}; } | ||||
|  | @ -103,6 +104,7 @@ namespace RNS { | |||
| 		void prove(const Packet& packet, const Destination& destination) const; | ||||
| 		void prove(const Packet& packet) const; | ||||
| 
 | ||||
| 		static const Identity from_file(const char* path); | ||||
| 		static void remember(const Bytes& packet_hash, const Bytes& destination_hash, const Bytes& public_key, const Bytes& app_data = {Bytes::NONE}); | ||||
| 		static Identity recall(const Bytes& destination_hash); | ||||
| 		static Bytes recall_app_data(const Bytes& destination_hash); | ||||
|  |  | |||
|  | @ -73,3 +73,15 @@ void Interface::process_announce_queue() { | |||
| 			RNS.log("The announce queue for this interface has been cleared.", RNS.LOG_ERROR) | ||||
| */ | ||||
| } | ||||
| 
 | ||||
| void ArduinoJson::convertFromJson(JsonVariantConst src, RNS::Interface& dst) { | ||||
| 	if (!src.isNull()) { | ||||
| 		RNS::Bytes hash; | ||||
| 		hash.assignHex(src.as<const char*>()); | ||||
| 		// Query transport for matching interface
 | ||||
| 		dst = Transport::find_interface_from_hash(hash); | ||||
| 	} | ||||
| 	else { | ||||
| 		dst = {RNS::Type::NONE}; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -176,3 +176,16 @@ namespace RNS { | |||
| 	}; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace ArduinoJson { | ||||
| 	inline bool convertToJson(const RNS::Interface& src, JsonVariant dst) { | ||||
| 		if (!src) { | ||||
| 			return dst.set(nullptr); | ||||
| 		} | ||||
| 		return dst.set(src.get_hash().toHex()); | ||||
| 	} | ||||
| 	void convertFromJson(JsonVariantConst src, RNS::Interface& dst); | ||||
| 	inline bool canConvertFromJson(JsonVariantConst src, const RNS::Interface&) { | ||||
| 		return src.is<const char*>() && strlen(src.as<const char*>()) == 64; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -554,3 +554,15 @@ void PacketReceipt::check_timeout() { | |||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ArduinoJson::convertFromJson(JsonVariantConst src, RNS::Packet& dst) { | ||||
| 	if (!src.isNull()) { | ||||
| 		RNS::Bytes hash; | ||||
| 		hash.assignHex(src.as<const char*>()); | ||||
| 		// Query transport for matching interface
 | ||||
| 		dst = Transport::get_cached_packet(hash); | ||||
| 	} | ||||
| 	else { | ||||
| 		dst = {RNS::Type::NONE}; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										13
									
								
								src/Packet.h
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								src/Packet.h
									
										
									
									
									
								
							|  | @ -274,3 +274,16 @@ namespace RNS { | |||
| 	}; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace ArduinoJson { | ||||
| 	inline bool convertToJson(const RNS::Packet& src, JsonVariant dst) { | ||||
| 		if (!src) { | ||||
| 			return dst.set(nullptr); | ||||
| 		} | ||||
| 		return dst.set(src.get_hash().toHex()); | ||||
| 	} | ||||
| 	void convertFromJson(JsonVariantConst src, RNS::Packet& dst); | ||||
| 	inline bool canConvertFromJson(JsonVariantConst src, const RNS::Packet&) { | ||||
| 		return src.is<const char*>() && strlen(src.as<const char*>()) == 64; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| 
 | ||||
| #include "Transport.h" | ||||
| #include "Log.h" | ||||
| #include "Utilities/OS.h" | ||||
| 
 | ||||
| #include <RNG.h> | ||||
| 
 | ||||
|  | @ -13,6 +14,8 @@ | |||
| using namespace RNS; | ||||
| using namespace RNS::Type::Reticulum; | ||||
| 
 | ||||
| /*static*/ std::string Reticulum::storagepath; | ||||
| 
 | ||||
| /*static*/ bool Reticulum::__transport_enabled = false; | ||||
| /*static*/ bool Reticulum::__use_implicit_proof = true; | ||||
| /*static*/ bool Reticulum::__allow_probes = false; | ||||
|  | @ -40,6 +43,8 @@ Reticulum::Reticulum() : _object(new Object()) { | |||
| 	// CBA TEST Transport
 | ||||
| 	__transport_enabled = true; | ||||
| 
 | ||||
| 	Utilities::OS::setup(); | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| 	// Stir in the Ethernet MAC address.
 | ||||
| 	//byte mac[6];
 | ||||
|  | @ -51,9 +56,9 @@ Reticulum::Reticulum() : _object(new Object()) { | |||
| 	//RNG.addNoiseSource(noise);
 | ||||
|  #endif | ||||
| 
 | ||||
| /*
 | ||||
| 	RNS.vendor.platformutils.platform_checks() | ||||
| 	//z RNS.vendor.platformutils.platform_checks()
 | ||||
| 
 | ||||
| /* TODO
 | ||||
| 	if configdir != None: | ||||
| 		Reticulum.configdir = configdir | ||||
| 	else: | ||||
|  | @ -73,7 +78,15 @@ Reticulum::Reticulum() : _object(new Object()) { | |||
| 	Reticulum.cachepath     = Reticulum.configdir+"/storage/cache" | ||||
| 	Reticulum.resourcepath  = Reticulum.configdir+"/storage/resources" | ||||
| 	Reticulum.identitypath  = Reticulum.configdir+"/storage/identities" | ||||
| */ | ||||
| // CBA MOCK
 | ||||
| #ifdef ARDUINO | ||||
| 	storagepath = ""; | ||||
| #else | ||||
| 	storagepath = "."; | ||||
| #endif | ||||
| 
 | ||||
| /* TODO
 | ||||
| 	Reticulum.__transport_enabled = False | ||||
| 	Reticulum.__use_implicit_proof = True | ||||
| 	Reticulum.__allow_probes = False | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include "Type.h" | ||||
| 
 | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <memory> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
|  | @ -12,6 +13,19 @@ namespace RNS { | |||
| 	class Reticulum { | ||||
| 
 | ||||
| 	public: | ||||
| 
 | ||||
| 		//z router           = None
 | ||||
| 		//z config           = None
 | ||||
| 
 | ||||
| 		// The default configuration path will be expanded to a directory
 | ||||
| 		// named ".reticulum" inside the current users home directory
 | ||||
| 		//z userdir          = os.path.expanduser("~")
 | ||||
| 		//z configdir        = None
 | ||||
| 		//z configpath       = ""
 | ||||
| 		//p storagepath      = ""
 | ||||
| 		static std::string storagepath; | ||||
| 		//z cachepath        = ""
 | ||||
| 
 | ||||
| 		static bool __transport_enabled; | ||||
| 		static bool __use_implicit_proof; | ||||
| 		static bool __allow_probes; | ||||
|  |  | |||
|  | @ -1,7 +1,5 @@ | |||
| #include "Test.h" | ||||
| 
 | ||||
| #include "Log.h" | ||||
| 
 | ||||
| void test() { | ||||
| 
 | ||||
| 	testOS(); | ||||
|  | @ -9,5 +7,6 @@ void test() { | |||
| 	testCollections(); | ||||
| 	testReference(); | ||||
| 	testCrypto(); | ||||
| 	testPersistence(); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -6,3 +6,4 @@ void testBytes(); | |||
| void testCollections(); | ||||
| void testReference(); | ||||
| void testCrypto(); | ||||
| void testPersistence(); | ||||
|  |  | |||
							
								
								
									
										502
									
								
								src/Test/TestPersistence.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								src/Test/TestPersistence.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,502 @@ | |||
| //#include <unity.h>
 | ||||
| 
 | ||||
| #include "Transport.h" | ||||
| #include "Interfaces/LoRaInterface.h" | ||||
| #include "Utilities/Persistence.h" | ||||
| #include "Utilities/OS.h" | ||||
| #include "Log.h" | ||||
| #include "Bytes.h" | ||||
| 
 | ||||
| #include <ArduinoJson.h> | ||||
| 
 | ||||
| #include <map> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <assert.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| 
 | ||||
| class Test { | ||||
| public: | ||||
|     Test() {} | ||||
|     Test(const char* foo, const char* fee) : _foo(foo), _fee(fee) {} | ||||
| 	const std::string toString() { return std::string("Test(") + _foo + "," + _fee + ")"; } | ||||
|     std::string _foo; | ||||
|     std::string _fee; | ||||
| }; | ||||
| 
 | ||||
| namespace ArduinoJson { | ||||
| 	template <> | ||||
| 	struct Converter<Test> { | ||||
| 		static bool toJson(const Test& src, JsonVariant dst) { | ||||
| 			dst["foo"] = src._foo; | ||||
| 			dst["fee"] = src._fee; | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		static Test fromJson(JsonVariantConst src) { | ||||
| 			return Test(src["foo"], src["fee"]); | ||||
| 		} | ||||
| 
 | ||||
| 		static bool checkJson(JsonVariantConst src) { | ||||
| 			return src["foo"].is<std::string>() && src["fee"].is<std::string>(); | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| const char test_file_path[] = "/test_file"; | ||||
| #else | ||||
| const char test_file_path[] = "test_file"; | ||||
| #endif | ||||
| const char test_file_data[] = "test data"; | ||||
| 
 | ||||
| void testWrite() { | ||||
| 	RNS::Bytes data(test_file_data); | ||||
| 	if (RNS::Utilities::OS::write_file(data, test_file_path)) { | ||||
| 		RNS::extreme("wrote: " + std::to_string(data.size()) + " bytes"); | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("write failed"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void testRead() { | ||||
| 	RNS::Bytes data = RNS::Utilities::OS::read_file(test_file_path); | ||||
| 	if (data) { | ||||
| 		RNS::extreme("read: " + std::to_string(data.size()) + " bytes"); | ||||
| 		RNS::extreme("data: " + data.toString()); | ||||
| 		assert(data.size() == strlen(test_file_data)); | ||||
| 		assert(memcmp(data.data(), test_file_data, strlen(test_file_data)) == 0); | ||||
| 		//assert(RNS::Utilities::OS::remove_file(test_file_path));
 | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("read failed"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| const char test_object_path[] = "/test_object"; | ||||
| #else | ||||
| const char test_object_path[] = "test_object"; | ||||
| #endif | ||||
| 
 | ||||
| void testSerializeObject() { | ||||
| 
 | ||||
| 	Test test("bar", "fum"); | ||||
| 
 | ||||
| 	StaticJsonDocument<1024> doc; | ||||
| 	doc["foo"] = test._foo; | ||||
| 	doc["fee"] = test._fee; | ||||
| 
 | ||||
| 	RNS::Bytes data; | ||||
| 	size_t size = 1024; | ||||
| 	//size_t length = serializeJson(doc, data.writable(size), size);
 | ||||
| 	size_t length = serializeMsgPack(doc, data.writable(size), size); | ||||
| 	if (length < size) { | ||||
| 		data.resize(length); | ||||
| 	} | ||||
| 	RNS::extreme("serialized " + std::to_string(length) + " bytes"); | ||||
| 	if (length > 0) { | ||||
| 		if (RNS::Utilities::OS::write_file(data, test_object_path)) { | ||||
| 			RNS::extreme("wrote: " + std::to_string(data.size()) + " bytes"); | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("write failed"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("failed to serialize"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void testDeserializeObject() { | ||||
| 
 | ||||
| 	//StaticJsonDocument<8192> doc;
 | ||||
| 	DynamicJsonDocument doc(8192); | ||||
| 
 | ||||
| 	RNS::Bytes data = RNS::Utilities::OS::read_file(test_object_path); | ||||
| 	if (data) { | ||||
| 		RNS::extreme("read: " + std::to_string(data.size()) + " bytes"); | ||||
| 		//RNS::extreme("data: " + data.toString());
 | ||||
| 		//DeserializationError error = deserializeJson(doc, data.data());
 | ||||
| 		DeserializationError error = deserializeMsgPack(doc, data.data()); | ||||
| 		if (!error) { | ||||
| 			JsonObject root = doc.as<JsonObject>(); | ||||
| 			for (JsonPair kv : root) { | ||||
| 				RNS::extreme("key: " + std::string(kv.key().c_str()) + " value: " + kv.value().as<const char*>()); | ||||
| 			} | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("failed to deserialize"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 		//assert(RNS::Utilities::OS::remove_file(test_object_path));
 | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("read failed"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| const char test_vector_path[] = "/test_vector"; | ||||
| #else | ||||
| const char test_vector_path[] = "test_vector"; | ||||
| #endif | ||||
| 
 | ||||
| void testSerializeVector() { | ||||
| 
 | ||||
| 	//std::vector<std::string> vector;
 | ||||
| 	//vector.push_back("foo");
 | ||||
| 	//vector.push_back("bar");
 | ||||
| 	std::vector<Test> vector; | ||||
| 	vector.push_back({"bar1", "fum1"}); | ||||
| 	vector.push_back({"bar2", "fum2"}); | ||||
| 
 | ||||
| 	StaticJsonDocument<4096> doc; | ||||
| 	//copyArray(vector, doc.createNestedArray("vector"));
 | ||||
| 	//doc["vector"] = vector;
 | ||||
| 	//copyArray(vector, doc);
 | ||||
| 	doc = vector; | ||||
| 
 | ||||
| /*
 | ||||
| 	DynamicJsonBuffer jsonBuffer; | ||||
| 	JsonObject& root = jsonBuffer.createObject(); | ||||
| 
 | ||||
| 	//JsonObject& weather = root.createNestedObject("weather");
 | ||||
| 	//weather["temperature"] = 12;
 | ||||
| 	//weather["condition"] = "cloudy";
 | ||||
| 
 | ||||
| 	//JsonArray& coords = root.createNestedArray("coords");
 | ||||
| 	//coords.add(48.7507371, 7);
 | ||||
| 	//coords.add(2.2625587, 7);
 | ||||
| 	JsonArray& arr = root.createNestedArray("vec"); | ||||
| 	for (auto entry : vec) { | ||||
| 		arr.add( | ||||
| 	} | ||||
| */ | ||||
| 
 | ||||
| 	RNS::Bytes data; | ||||
| 	size_t size = 4096; | ||||
| 	size_t length = serializeJson(doc, data.writable(size), size); | ||||
| 	//size_t length = serializeMsgPack(doc, data.writable(size), size);
 | ||||
| 	if (length < size) { | ||||
| 		data.resize(length); | ||||
| 	} | ||||
| 	RNS::extreme("testSerializeVector: serialized " + std::to_string(length) + " bytes"); | ||||
| 	if (length > 0) { | ||||
| 		if (RNS::Utilities::OS::write_file(data, test_vector_path)) { | ||||
| 			RNS::extreme("testSerializeVector: wrote: " + std::to_string(data.size()) + " bytes"); | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("testSerializeVector: write failed"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("testSerializeVector: failed to serialize"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void testDeserializeVector() { | ||||
| 
 | ||||
| 	// Compute the required size
 | ||||
| 	const int capacity = | ||||
| 		JSON_ARRAY_SIZE(2) + | ||||
| 		2*JSON_OBJECT_SIZE(3) + | ||||
| 		4*JSON_OBJECT_SIZE(1); | ||||
| 
 | ||||
| 	//StaticJsonDocument<8192> doc;
 | ||||
| 	DynamicJsonDocument doc(8192); | ||||
| 
 | ||||
| 	RNS::Bytes data = RNS::Utilities::OS::read_file(test_vector_path); | ||||
| 	if (data) { | ||||
| 		RNS::extreme("testDeserializeVector: read: " + std::to_string(data.size()) + " bytes"); | ||||
| 		//RNS::extreme("testDeserializeVector: data: " + data.toString());
 | ||||
| 		DeserializationError error = deserializeJson(doc, data.data()); | ||||
| 		//DeserializationError error = deserializeMsgPack(doc, data.data());
 | ||||
| 		if (!error) { | ||||
| 			RNS::extreme("testDeserializeVector: successfully deserialized"); | ||||
| 
 | ||||
| 			std::vector<Test> vector = doc.as<std::vector<Test>>(); | ||||
| 			for (auto& test : vector) { | ||||
| 				RNS::extreme("testDeserializeVector: entry: " + test.toString()); | ||||
| 			} | ||||
| 
 | ||||
| 			//JsonArray arr = doc.as<JsonArray>();
 | ||||
| 			//for (JsonVariant elem : arr) {
 | ||||
| 			//	RNS::extreme("testDeserializeVector: entry: " + elem.as<Test>().toString());
 | ||||
| 			//}
 | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("testDeserializeVector: failed to deserialize"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 		//assert(RNS::Utilities::OS::remove_file(test_vector_path));
 | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("testDeserializeVector: read failed"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| const char test_map_path[] = "/test_map"; | ||||
| #else | ||||
| const char test_map_path[] = "test_map"; | ||||
| #endif | ||||
| 
 | ||||
| void testSerializeMap() { | ||||
| 
 | ||||
| 	//std::map<std::string, std::string> map;
 | ||||
| 	//map.insert({"foo", "bar"});
 | ||||
| 	//map.insert({"fee", "fum"});
 | ||||
| 	std::map<std::string, Test> map; | ||||
| 	map.insert({"one", {"bar1", "fum1"}}); | ||||
| 	map.insert({"two", {"bar2", "fum2"}}); | ||||
| 
 | ||||
| 	StaticJsonDocument<4096> doc; | ||||
| 	//copyArray(map, doc.createNestedArray("map"));
 | ||||
| 	//doc["map"] = map;
 | ||||
| 	//copyArray(map, doc);
 | ||||
| 	doc = map; | ||||
| 
 | ||||
| 	RNS::Bytes data; | ||||
| 	size_t size = 4096; | ||||
| 	size_t length = serializeJson(doc, data.writable(size), size); | ||||
| 	//size_t length = serializeMsgPack(doc, data.writable(size), size);
 | ||||
| 	if (length < size) { | ||||
| 		data.resize(length); | ||||
| 	} | ||||
| 	RNS::extreme("testSerializeMap: serialized " + std::to_string(length) + " bytes"); | ||||
| 	if (length > 0) { | ||||
| 		if (RNS::Utilities::OS::write_file(data, test_map_path)) { | ||||
| 			RNS::extreme("testSerializeMap: wrote: " + std::to_string(data.size()) + " bytes"); | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("testSerializeMap: write failed"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("testSerializeMap: failed to serialize"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void testDeserializeMap() { | ||||
| 
 | ||||
| 	// Compute the required size
 | ||||
| 	const int capacity = | ||||
| 		JSON_ARRAY_SIZE(2) + | ||||
| 		2*JSON_OBJECT_SIZE(3) + | ||||
| 		4*JSON_OBJECT_SIZE(1); | ||||
| 
 | ||||
| 	//StaticJsonDocument<8192> doc;
 | ||||
| 	DynamicJsonDocument doc(8192); | ||||
| 
 | ||||
| 	RNS::Bytes data = RNS::Utilities::OS::read_file(test_map_path); | ||||
| 	if (data) { | ||||
| 		RNS::extreme("testDeserializeMap: read: " + std::to_string(data.size()) + " bytes"); | ||||
| 		//RNS::extreme("testDeserializeMap: data: " + data.toString());
 | ||||
| 		DeserializationError error = deserializeJson(doc, data.data()); | ||||
| 		//DeserializationError error = deserializeMsgPack(doc, data.data());
 | ||||
| 		if (!error) { | ||||
| 			RNS::extreme("testDeserializeMap: successfully deserialized"); | ||||
| 
 | ||||
| 			std::map<std::string, Test> map(doc.as<std::map<std::string, Test>>()); | ||||
| 			for (auto& [str, test] : map) { | ||||
| 				RNS::extreme("testDeserializeMap: entry: " + str + " = " + test.toString()); | ||||
| 			} | ||||
| 
 | ||||
| 			//JsonObject root = doc.as<JsonObject>();
 | ||||
| 			//for (JsonPair kv : root) {
 | ||||
| 			//	RNS::extreme("testDeserializeMap: entry: " + std::string(kv.key().c_str()) + " = " + kv.value().as<Test>().toString());
 | ||||
| 			//}
 | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("testDeserializeMap: failed to deserialize"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 		//assert(RNS::Utilities::OS::remove_file(test_map_path));
 | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("testDeserializeMap: testDeserializeVector: read failed"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| const char test_destination_table_path[] = "/test_destination_table"; | ||||
| #else | ||||
| const char test_destination_table_path[] = "test_destination_table"; | ||||
| #endif | ||||
| 
 | ||||
| void testSerializeDestinationTable() { | ||||
| 
 | ||||
| 	//RNS::Bytes empty;
 | ||||
| 	//std::set<RNS::Bytes> blobs;
 | ||||
| 	//RNS::Interface interface({RNS::Type::NONE});
 | ||||
| 	RNS::Interfaces::LoRaInterface lora_interface;  | ||||
| 	//RNS::Packet packet({RNS::Type::NONE});
 | ||||
| 	static std::map<RNS::Bytes, RNS::Transport::DestinationEntry> map; | ||||
| 	//DestinationEntry(double time, const Bytes& received_from, uint8_t announce_hops, double expires, const std::set<Bytes>& random_blobs, Interface& receiving_interface, const Packet& packet) :
 | ||||
| 	//RNS::Transport::DestinationEntry entry_one(1.0, empty, 1, 0.0, blobs, interface, packet);
 | ||||
| 	RNS::Bytes received; | ||||
| 	received.assignHex("deadbeef"); | ||||
| 	RNS::Transport::DestinationEntry entry_one; | ||||
| 	entry_one._timestamp = 1.0; | ||||
| 	entry_one._received_from = received; | ||||
| 	entry_one._receiving_interface = lora_interface; | ||||
| 	RNS::Bytes one; | ||||
| 	one.assignHex("1111111111111111"); | ||||
| 	map.insert({one, entry_one}); | ||||
| 	//RNS::Transport::DestinationEntry entry_two(2.0, empty, 1, 0.0, blobs, interface, packet);
 | ||||
| 	RNS::Transport::DestinationEntry entry_two; | ||||
| 	entry_two._timestamp = 2.0; | ||||
| 	entry_two._received_from = received; | ||||
| 	entry_two._receiving_interface = lora_interface; | ||||
| 	RNS::Bytes two; | ||||
| 	two.assignHex("2222222222222222"); | ||||
| 	map.insert({two, entry_two}); | ||||
| 
 | ||||
| 	for (auto& [hash, test] : map) { | ||||
| 		RNS::extreme("testSerializeDestinationTable: entry: " + hash.toHex() + " = (" + std::to_string(test._timestamp) + "," + test._received_from.toHex() + ")"); | ||||
| 	} | ||||
| 
 | ||||
| 	StaticJsonDocument<4096> doc; | ||||
| 	doc = map; | ||||
| 
 | ||||
| 	RNS::Bytes data; | ||||
| 	size_t size = 4096; | ||||
| 	size_t length = serializeJson(doc, data.writable(size), size); | ||||
| 	//size_t length = serializeMsgPack(doc, data.writable(size), size);
 | ||||
| 	if (length < size) { | ||||
| 		data.resize(length); | ||||
| 	} | ||||
| 	RNS::extreme("testSerializeDestinationTable: serialized " + std::to_string(length) + " bytes"); | ||||
| 	if (length > 0) { | ||||
| 		if (RNS::Utilities::OS::write_file(data, test_destination_table_path)) { | ||||
| 			RNS::extreme("testSerializeDestinationTable: wrote: " + std::to_string(data.size()) + " bytes"); | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("testSerializeDestinationTable: write failed"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("testSerializeDestinationTable: failed to serialize"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void testDeserializeDestinationTable() { | ||||
| 
 | ||||
| 	// Compute the required size
 | ||||
| 	const int capacity = | ||||
| 		JSON_ARRAY_SIZE(2) + | ||||
| 		2*JSON_OBJECT_SIZE(3) + | ||||
| 		4*JSON_OBJECT_SIZE(1); | ||||
| 
 | ||||
| 	//StaticJsonDocument<8192> doc;
 | ||||
| 	DynamicJsonDocument doc(8192); | ||||
| 
 | ||||
| 	RNS::Bytes data = RNS::Utilities::OS::read_file(test_destination_table_path); | ||||
| 	if (data) { | ||||
| 		RNS::extreme("testDeserializeDestinationTable: read: " + std::to_string(data.size()) + " bytes"); | ||||
| 		//RNS::extreme("testDeserializeVector: data: " + data.toString());
 | ||||
| 		DeserializationError error = deserializeJson(doc, data.data()); | ||||
| 		//DeserializationError error = deserializeMsgPack(doc, data.data());
 | ||||
| 		if (!error) { | ||||
| 			RNS::extreme("testDeserializeDestinationTable: successfully deserialized"); | ||||
| 
 | ||||
| 			static std::map<RNS::Bytes, RNS::Transport::DestinationEntry> map(doc.as<std::map<RNS::Bytes, RNS::Transport::DestinationEntry>>()); | ||||
| 			for (auto& [hash, test] : map) { | ||||
| 				RNS::extreme("testDeserializeDestinationTable: entry: " + hash.toHex() + " = (" + std::to_string(test._timestamp) + "," + test._received_from.toHex() + ")"); | ||||
| 			} | ||||
| 
 | ||||
| 			//JsonObject root = doc.as<JsonObject>();
 | ||||
| 			//for (JsonPair kv : root) {
 | ||||
| 			//	RNS::extreme("testDeserializeDestinationTable: entry: " + std::string(kv.key().c_str()) + " = " + kv.value().as<Test>().toString());
 | ||||
| 			//}
 | ||||
| 		} | ||||
| 		else { | ||||
| 			RNS::extreme("testDeserializeDestinationTable: failed to deserialize"); | ||||
| 			assert(false); | ||||
| 		} | ||||
| 		//assert(RNS::Utilities::OS::remove_file(test_destination_table_path));
 | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("testDeserializeDestinationTable: read failed"); | ||||
| 		assert(false); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void testPersistence() { | ||||
| 	RNS::head("Running testPersistence...", RNS::LOG_EXTREME); | ||||
| 	testWrite(); | ||||
| 	testRead(); | ||||
| 	testSerializeObject(); | ||||
| 	testDeserializeObject(); | ||||
| 	testSerializeVector(); | ||||
| 	testDeserializeVector(); | ||||
| 	testSerializeMap(); | ||||
| 	testDeserializeMap(); | ||||
| 	testSerializeDestinationTable(); | ||||
| 	testDeserializeDestinationTable(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| void setUp(void) { | ||||
| 	// set stuff up here
 | ||||
| } | ||||
| 
 | ||||
| void tearDown(void) { | ||||
| 	// clean stuff up here
 | ||||
| } | ||||
| 
 | ||||
| int runUnityTests(void) { | ||||
| 	UNITY_BEGIN(); | ||||
| 	RUN_TEST(testCollections); | ||||
| 	return UNITY_END(); | ||||
| } | ||||
| 
 | ||||
| // For native dev-platform or for some embedded frameworks
 | ||||
| int main(void) { | ||||
| 	return runUnityTests(); | ||||
| } | ||||
| 
 | ||||
| // For Arduino framework
 | ||||
| void setup() { | ||||
| 	// Wait ~2 seconds before the Unity test runner
 | ||||
| 	// establishes connection with a board Serial interface
 | ||||
| 	delay(2000); | ||||
| 
 | ||||
| 	runUnityTests(); | ||||
| } | ||||
| void loop() {} | ||||
| 
 | ||||
| // For ESP-IDF framework
 | ||||
| void app_main() { | ||||
| 	runUnityTests(); | ||||
| } | ||||
| */ | ||||
|  | @ -81,15 +81,17 @@ using namespace RNS::Utilities; | |||
| 	_owner = reticulum_instance; | ||||
| 
 | ||||
| 	if (!_identity) { | ||||
| 		//z transport_identity_path = Reticulum::storagepath+"/transport_identity"
 | ||||
| 		//z if (os.path.isfile(transport_identity_path)) {
 | ||||
| 		//z 	identity = Identity.from_file(transport_identity_path);
 | ||||
| 		//z }
 | ||||
| 		std::string transport_identity_path = Reticulum::storagepath + "/transport_identity"; | ||||
| // CBA TEST
 | ||||
| //OS::remove_file(transport_identity_path.c_str());
 | ||||
| 		if (OS::file_exists(transport_identity_path.c_str())) { | ||||
| 			_identity = Identity::from_file(transport_identity_path.c_str()); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!_identity) { | ||||
| 			verbose("No valid Transport Identity in storage, creating..."); | ||||
| 			_identity = new Identity(); | ||||
| 			//z _identity.to_file(transport_identity_path);
 | ||||
| 			_identity = Identity(); | ||||
| 			_identity.to_file(transport_identity_path.c_str()); | ||||
| 		} | ||||
| 		else { | ||||
| 			verbose("Loaded Transport Identity from storage"); | ||||
|  | @ -135,9 +137,8 @@ using namespace RNS::Utilities; | |||
| // TODO
 | ||||
| /*
 | ||||
| 	if RNS.Reticulum.transport_enabled(): | ||||
| 		destination_table_path = RNS.Reticulum.storagepath+"/destination_table" | ||||
| 		tunnel_table_path = RNS.Reticulum.storagepath+"/tunnels" | ||||
| 
 | ||||
| 		destination_table_path = RNS.Reticulum.storagepath+"/destination_table" | ||||
| 		if os.path.isfile(destination_table_path) and not Transport.owner.is_connected_to_shared_instance: | ||||
| 			serialised_destinations = [] | ||||
| 			try: | ||||
|  | @ -183,6 +184,7 @@ using namespace RNS::Utilities; | |||
| 			except Exception as e: | ||||
| 				RNS.log("Could not load destination table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR) | ||||
| 
 | ||||
| 		tunnel_table_path = RNS.Reticulum.storagepath+"/tunnels" | ||||
| 		if os.path.isfile(tunnel_table_path) and not Transport.owner.is_connected_to_shared_instance: | ||||
| 			serialised_tunnels = [] | ||||
| 			try: | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ | |||
| #include "Bytes.h" | ||||
| #include "Type.h" | ||||
| 
 | ||||
| #include <ArduinoJson.h> | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include <list> | ||||
|  | @ -54,11 +56,12 @@ namespace RNS { | |||
|     */ | ||||
| 	class Transport { | ||||
| 
 | ||||
| 	private: | ||||
| 	public: | ||||
| 		// CBA TODO Analyze safety of using Inrerface references here
 | ||||
| 		// CBA TODO Analyze safety of using Packet references here
 | ||||
| 		class DestinationEntry { | ||||
| 		public: | ||||
| 			DestinationEntry() {} | ||||
| 			DestinationEntry(double time, const Bytes& received_from, uint8_t announce_hops, double expires, const std::set<Bytes>& random_blobs, Interface& receiving_interface, const Packet& packet) : | ||||
| 				_timestamp(time), | ||||
| 				_received_from(received_from), | ||||
|  | @ -74,11 +77,11 @@ namespace RNS { | |||
| 			Bytes _received_from; | ||||
| 			uint8_t _hops = 0; | ||||
| 			double _expires = 0; | ||||
| 			std::set<Bytes> _random_blobs; | ||||
| 			const std::set<Bytes> _random_blobs; | ||||
| 			// CBA TODO does this need to be a reference in order for virtual method callbacks to work?
 | ||||
| 			Interface& _receiving_interface; | ||||
| 			Interface _receiving_interface = {Type::NONE}; | ||||
| 			//const Packet& _announce_packet;
 | ||||
| 			Packet _announce_packet; | ||||
| 			const Packet _announce_packet = {Type::NONE}; | ||||
| 		}; | ||||
| 
 | ||||
| 		// CBA TODO Analyze safety of using Inrerface references here
 | ||||
|  | @ -101,17 +104,17 @@ namespace RNS { | |||
| 			double _timestamp = 0; | ||||
| 			double _retransmit_timeout = 0; | ||||
| 			uint8_t _retries = 0; | ||||
| 			Bytes _received_from; | ||||
| 			const Bytes _received_from; | ||||
| 			uint8_t _hops = 0; | ||||
| 			// CBA Storing packet reference causes memory issues, presumably because orignal packet is being destroyed
 | ||||
| 			//  MUST use instance instad of reference!!!
 | ||||
| 			//const Packet& _packet;
 | ||||
| 			Packet _packet; | ||||
| 			const Packet _packet = {Type::NONE}; | ||||
| 			uint8_t _local_rebroadcasts = 0; | ||||
| 			bool _block_rebroadcasts = false; | ||||
| 			// CBA TODO does this need to be a reference in order for virtual method callbacks to work?
 | ||||
| 			//const Interface& _attached_interface;
 | ||||
| 			Interface _attached_interface; | ||||
| 			const Interface _attached_interface = {Type::NONE}; | ||||
| 		}; | ||||
| 
 | ||||
| 		// CBA TODO Analyze safety of using Inrerface references here
 | ||||
|  | @ -131,16 +134,16 @@ namespace RNS { | |||
| 			} | ||||
| 		public: | ||||
| 			double _timestamp = 0; | ||||
| 			Bytes _next_hop; | ||||
| 			const Bytes _next_hop; | ||||
| 			// CBA TODO does this need to be a reference in order for virtual method callbacks to work?
 | ||||
| 			//const Interface& _outbound_interface;
 | ||||
| 			Interface _outbound_interface; | ||||
| 			const Interface _outbound_interface = {Type::NONE}; | ||||
| 			uint8_t _remaining_hops = 0; | ||||
| 			// CBA TODO does this need to be a reference in order for virtual method callbacks to work?
 | ||||
| 			//const Interface& _receiving_interface;
 | ||||
| 			Interface _receiving_interface; | ||||
| 			const Interface _receiving_interface = {Type::NONE}; | ||||
| 			uint8_t _hops = 0; | ||||
| 			Bytes _destination_hash; | ||||
| 			const Bytes _destination_hash; | ||||
| 			bool _validated = false; | ||||
| 			double _proof_timeout = 0; | ||||
| 		}; | ||||
|  | @ -157,10 +160,10 @@ namespace RNS { | |||
| 		public: | ||||
| 			// CBA TODO does this need to be a reference in order for virtual method callbacks to work?
 | ||||
| 			//const Interface& _receiving_interface;
 | ||||
| 			Interface _receiving_interface; | ||||
| 			const Interface _receiving_interface = {Type::NONE}; | ||||
| 			// CBA TODO does this need to be a reference in order for virtual method callbacks to work?
 | ||||
| 			//const Interface& _outbound_interface;
 | ||||
| 			Interface _outbound_interface; | ||||
| 			const Interface _outbound_interface = {Type::NONE}; | ||||
| 			double _timestamp = 0; | ||||
| 		}; | ||||
| 
 | ||||
|  | @ -174,11 +177,11 @@ namespace RNS { | |||
| 			{ | ||||
| 			} | ||||
| 		public: | ||||
| 			Bytes _destination_hash; | ||||
| 			const Bytes _destination_hash; | ||||
| 			double _timeout = 0; | ||||
| 			// CBA TODO does this need to be a reference in order for virtual method callbacks to work?
 | ||||
| 			//const Interface& _requesting_interface;
 | ||||
| 			Interface _requesting_interface; | ||||
| 			const Interface _requesting_interface = {Type::NONE}; | ||||
| 		}; | ||||
| 
 | ||||
| 	public: | ||||
|  | @ -312,4 +315,55 @@ namespace RNS { | |||
| 		static Identity _identity; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
| } | ||||
| 
 | ||||
| namespace ArduinoJson { | ||||
| 
 | ||||
| 	// ArduinoJSON serialization support for RNS::Transport::DestinationEntry
 | ||||
| 	template <> | ||||
| 	struct Converter<RNS::Transport::DestinationEntry> { | ||||
| 		static bool toJson(const RNS::Transport::DestinationEntry& src, JsonVariant dst) { | ||||
| 			dst["timestamp"] = src._timestamp; | ||||
| 			dst["received_from"] = src._received_from; | ||||
| 			dst["announce_hops"] = src._hops; | ||||
| 			dst["expires"] = src._expires; | ||||
| 			//dst["random_blobs"] = src._random_blobs;
 | ||||
| 			dst["receiving_interface"] = src._receiving_interface; | ||||
| 			dst["packet"] = src._announce_packet; | ||||
| 			return true; | ||||
| 		} | ||||
| 		static RNS::Transport::DestinationEntry fromJson(JsonVariantConst src) { | ||||
| /**/ | ||||
| 			RNS::Transport::DestinationEntry dst; | ||||
| 			dst._timestamp = src["timestamp"]; | ||||
| 			dst._received_from = src["received_from"]; | ||||
| 			dst._hops = src["announce_hops"]; | ||||
| 			dst._expires = src["expires"]; | ||||
| 			//dst._random_blobs = src["random_blobs"];
 | ||||
| 			dst._receiving_interface = src["receiving_interface"]; | ||||
| 			//dst._announce_packet = src["packet"];
 | ||||
| /**/ | ||||
| /*
 | ||||
| 			//RNS::Transport::DestinationEntry dst(src["timestamp"], src["received_from"], src["announce_hops"], src["expires"], src["random_blobs"], src["receiving_interface"], src["packet"]);
 | ||||
| 			RNS::Transport::DestinationEntry dst( | ||||
| 				src["timestamp"].as<double>(), | ||||
| 				src["received_from"].as<RNS::Bytes>(), | ||||
| 				src["announce_hops"].as<int>(), | ||||
| 				src["expires"].as<double>(), | ||||
| 				src["random_blobs"].as<std::set<RNS::Bytes>>(), | ||||
| 				src["receiving_interface"].as<RNS::Interface>(), | ||||
| 				src["packet"].as<RNS::Packet>() | ||||
| 			); | ||||
| */ | ||||
| 			return dst; | ||||
| 		} | ||||
| 		static bool checkJson(JsonVariantConst src) { | ||||
| 			return | ||||
| 				src["timestamp"].is<double>() && | ||||
| 				src["received_from"].is<RNS::Bytes>() && | ||||
| 				src["announce_hops"].is<int>() && | ||||
| 				src["expires"].is<double>(); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										144
									
								
								src/Utilities/OS.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/Utilities/OS.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,144 @@ | |||
| #include "OS.h" | ||||
| 
 | ||||
| #include "../Log.h" | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| #include <FS.h> | ||||
| #include <LittleFS.h> | ||||
| #else | ||||
| #include <stdio.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| using namespace RNS; | ||||
| using namespace RNS::Utilities; | ||||
| 
 | ||||
| /*static*/ void OS::setup() { | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| 	// Setup filesystem
 | ||||
|     if (!LittleFS.begin(true)){ | ||||
|         Serial.println("LittleFS Mount Failed"); | ||||
|         return; | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /*static*/ bool OS::file_exists(const char* file_path) { | ||||
| #ifdef ARDUINO | ||||
| 	File file = LittleFS.open(file_path, FILE_READ); | ||||
| 	if (file) { | ||||
| #else | ||||
| 	FILE* file = fopen(file_path, "r"); | ||||
| 	if (file != nullptr) { | ||||
| #endif | ||||
| 		//RNS::extreme("file_exists: file exists, closing file");
 | ||||
| #ifdef ARDUINO | ||||
| 		file.close(); | ||||
| #else | ||||
| 		fclose(file); | ||||
| #endif | ||||
| 		return true; | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("file_exists: failed to open file " + std::string(file_path)); | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*static*/ const Bytes OS::read_file(const char* file_path) { | ||||
|     Bytes data; | ||||
| #ifdef ARDUINO | ||||
| 	File file = LittleFS.open(file_path, FILE_READ); | ||||
| 	if (file) { | ||||
| 		size_t size = file.size(); | ||||
| 		size_t read = file.readBytes((char*)data.writable(size), size); | ||||
| #else | ||||
| 	FILE* file = fopen(file_path, "r"); | ||||
| 	if (file != nullptr) { | ||||
|         fseek(file, 0, SEEK_END); | ||||
|         size_t size = ftell(file); | ||||
|         rewind(file); | ||||
| 		//size_t read = fread(data.writable(size), size, 1, file);
 | ||||
| 		size_t read = fread(data.writable(size), 1, size, file); | ||||
| #endif | ||||
| 		RNS::extreme("read_file: read " + std::to_string(read) + " bytes from file " + std::string(file_path)); | ||||
| 		if (read != size) { | ||||
| 			RNS::extreme("read_file: failed to read file " + std::string(file_path)); | ||||
|             data.clear(); | ||||
| 		} | ||||
| 		//RNS::extreme("read_file: closing input file");
 | ||||
| #ifdef ARDUINO | ||||
| 		file.close(); | ||||
| #else | ||||
| 		fclose(file); | ||||
| #endif | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("read_file: failed to open input file " + std::string(file_path)); | ||||
| 	} | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| /*static*/ bool OS::write_file(const Bytes& data, const char* file_path) { | ||||
|     bool success = false; | ||||
| #ifdef ARDUINO | ||||
| 	File file = LittleFS.open(file_path, FILE_WRITE); | ||||
| 	if (file) { | ||||
| 		size_t wrote = file.write(data.data(), data.size()); | ||||
| #else | ||||
| 	FILE* file = fopen(file_path, "w"); | ||||
| 	if (file != nullptr) { | ||||
|         //size_t wrote = fwrite(data.data(), data.size(), 1, file);
 | ||||
|         size_t wrote = fwrite(data.data(), 1, data.size(), file); | ||||
| #endif | ||||
|         RNS::extreme("write_file: wrote " + std::to_string(wrote) + " bytes to file " + std::string(file_path)); | ||||
|         if (wrote == data.size()) { | ||||
|             success = true; | ||||
|         } | ||||
|         else { | ||||
| 			RNS::extreme("write_file: failed to write file " + std::string(file_path)); | ||||
| 		} | ||||
| 		//RNS::extreme("write_file: closing output file");
 | ||||
| #ifdef ARDUINO | ||||
| 		file.close(); | ||||
| #else | ||||
| 		fclose(file); | ||||
| #endif | ||||
| 	} | ||||
| 	else { | ||||
| 		RNS::extreme("write_file: failed to open output file " + std::string(file_path)); | ||||
| 	} | ||||
|     return success; | ||||
| } | ||||
| 
 | ||||
| /*static*/ bool OS::remove_file(const char* file_path) { | ||||
| #ifdef ARDUINO | ||||
| 	return LittleFS.remove(file_path); | ||||
| #else | ||||
| 	return (remove(file_path) == 0); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*static*/ bool OS::rename_file(const char* from_file_path, const char* to_file_path) { | ||||
| #ifdef ARDUINO | ||||
| 	return LittleFS.rename(from_file_path, to_file_path); | ||||
| #else | ||||
| 	return (rename(from_file_path, to_file_path) == 0); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*static*/ bool OS::create_directory(const char* directory_path) { | ||||
| #ifdef ARDUINO | ||||
| 	return LittleFS.mkdir(directory_path); | ||||
| #else | ||||
| 	struct stat st = {0}; | ||||
| 	if (stat(directory_path, &st) == 0) { | ||||
| 		return true; | ||||
| 	} | ||||
| 	return (mkdir(directory_path, 0700) == 0); | ||||
| #endif | ||||
| } | ||||
|  | @ -1,16 +1,24 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "../Bytes.h" | ||||
| 
 | ||||
| #include <cmath> | ||||
| #include <unistd.h> | ||||
| #include <time.h> | ||||
| #include <stdint.h> | ||||
| #include <sys/time.h> | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| #include "Arduino.h" | ||||
| #endif | ||||
| 
 | ||||
| namespace RNS { namespace Utilities { | ||||
| 
 | ||||
| 	class OS { | ||||
| 
 | ||||
| 	public: | ||||
| 		static void setup(); | ||||
| 
 | ||||
|         // sleep for specified milliseconds
 | ||||
| 		//static inline void sleep(float seconds) { ::sleep(seconds); }
 | ||||
| #ifdef ARDUINO | ||||
|  | @ -30,6 +38,13 @@ namespace RNS { namespace Utilities { | |||
|         //static inline float round(float value, uint8_t precision) { return std::round(value / precision) * precision; }
 | ||||
|         static inline double round(double value, uint8_t precision) { return std::round(value / precision) * precision; } | ||||
| 
 | ||||
| 		static bool file_exists(const char* file_path); | ||||
| 		static const RNS::Bytes read_file(const char* file_path); | ||||
| 		static bool write_file(const RNS::Bytes& data, const char* file_path); | ||||
| 		static bool remove_file(const char* file_path); | ||||
| 		static bool rename_file(const char* from_file_path, const char* to_file_path); | ||||
| 		static bool create_directory(const char* directory_path); | ||||
| 
 | ||||
|     }; | ||||
| 
 | ||||
| } } | ||||
|  |  | |||
							
								
								
									
										89
									
								
								src/Utilities/Persistence.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Utilities/Persistence.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | |||
| 
 | ||||
| #include "Bytes.h" | ||||
| 
 | ||||
| #include <map> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| 
 | ||||
| #include <ArduinoJson.h> | ||||
| 
 | ||||
| namespace ArduinoJson { | ||||
| 
 | ||||
| 	// ArduinoJSON serialization support for std::vector<T>
 | ||||
| 	template <typename T> | ||||
| 		struct Converter<std::vector<T> > { | ||||
| 		static void toJson(const std::vector<T>& src, JsonVariant dst) { | ||||
| 			JsonArray array = dst.to<JsonArray>(); | ||||
| 			for (T item : src) | ||||
| 			array.add(item); | ||||
| 		} | ||||
| 		static std::vector<T> fromJson(JsonVariantConst src) { | ||||
| 			std::vector<T> dst; | ||||
| 			for (T item : src.as<JsonArrayConst>()) | ||||
| 			dst.push_back(item); | ||||
| 			return dst; | ||||
| 		} | ||||
| 		static bool checkJson(JsonVariantConst src) { | ||||
| 			JsonArrayConst array = src; | ||||
| 			bool result = array; | ||||
| 			for (JsonVariantConst item : array) | ||||
| 			result &= item.is<T>(); | ||||
| 			return result; | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	// ArduinoJSON serialization support for std::map<std::string, T>
 | ||||
| 	template <typename T> | ||||
| 	struct Converter<std::map<std::string, T> > { | ||||
| 		static void toJson(const std::map<std::string, T>& src, JsonVariant dst) { | ||||
| 			JsonObject obj = dst.to<JsonObject>(); | ||||
| 			for (const auto& item : src) | ||||
| 			obj[item.first] = item.second; | ||||
| 		} | ||||
| 		static std::map<std::string, T> fromJson(JsonVariantConst src) { | ||||
| 			std::map<std::string, T> dst; | ||||
| 			for (JsonPairConst item : src.as<JsonObjectConst>()) | ||||
| 			dst[item.key().c_str()] = item.value().as<T>(); | ||||
| 			return dst; | ||||
| 		} | ||||
| 		static bool checkJson(JsonVariantConst src) { | ||||
| 			JsonObjectConst obj = src; | ||||
| 			bool result = obj; | ||||
| 			for (JsonPairConst item : obj) | ||||
| 			result &= item.value().is<T>(); | ||||
| 			return result; | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	// ArduinoJSON serialization support for std::map<Bytes, T>
 | ||||
| 	template <typename T> | ||||
| 	struct Converter<std::map<RNS::Bytes, T> > { | ||||
| 		static void toJson(const std::map<RNS::Bytes, T>& src, JsonVariant dst) { | ||||
| 			JsonObject obj = dst.to<JsonObject>(); | ||||
| 			for (const auto& item : src) { | ||||
| 			//obj[item.first] = item.second;
 | ||||
| 			obj[item.first.toHex()] = item.second; | ||||
| 			} | ||||
| 		} | ||||
| 		static std::map<RNS::Bytes, T> fromJson(JsonVariantConst src) { | ||||
| 			std::map<RNS::Bytes, T> dst; | ||||
| 			for (JsonPairConst item : src.as<JsonObjectConst>()) { | ||||
| 			//dst[item.key().c_str()] = item.value().as<T>();
 | ||||
| 			RNS::Bytes key; | ||||
| 			key.assignHex(item.key().c_str()); | ||||
| 			//dst[key] = item.value().as<T>();
 | ||||
| 			dst.insert({key, item.value().as<T>()}); | ||||
| 			} | ||||
| 			return dst; | ||||
| 		} | ||||
| 		static bool checkJson(JsonVariantConst src) { | ||||
| 			JsonObjectConst obj = src; | ||||
| 			bool result = obj; | ||||
| 			for (JsonPairConst item : obj) { | ||||
| 			result &= item.value().is<T>(); | ||||
| 			} | ||||
| 			return result; | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/main.cpp
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								src/main.cpp
									
										
									
									
									
								
							|  | @ -120,9 +120,10 @@ void run_tests() { | |||
| 		RNS::LogLevel loglevel = RNS::loglevel(); | ||||
| 		//RNS::loglevel(RNS::LOG_WARNING);
 | ||||
| 		RNS::loglevel(RNS::LOG_EXTREME); | ||||
| 		test(); | ||||
| 		//test();
 | ||||
| 		//testReference();
 | ||||
| 		//testCrypto();
 | ||||
| 		testPersistence(); | ||||
| 		RNS::loglevel(loglevel); | ||||
| 		RNS::extreme("Finished running tests"); | ||||
| 		//return;
 | ||||
|  | @ -134,6 +135,7 @@ void run_tests() { | |||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if defined(RUN_RETICULUM) | ||||
| void reticulum_announce() { | ||||
| 	if (destination) { | ||||
| 		RNS::head("Announcing destination...", RNS::LOG_EXTREME); | ||||
|  | @ -145,8 +147,7 @@ void reticulum_announce() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| #if defined(RUN_RETICULUM) | ||||
| void setup_reticulum() { | ||||
| void reticulum_setup() { | ||||
| 	RNS::info("Setting up Reticulum..."); | ||||
| 
 | ||||
| 	try { | ||||
|  | @ -177,7 +178,7 @@ void setup_reticulum() { | |||
| 
 | ||||
| #ifdef UDP_INTERFACE | ||||
| 		RNS::head("Starting UDPInterface...", RNS::LOG_EXTREME); | ||||
| 		udp_interface.start("some_ssid", "some_password", 2424); | ||||
| 		udp_interface.start("some_ssid", "some_password"); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef LORA_INTERFACE | ||||
|  | @ -251,13 +252,14 @@ void setup_reticulum() { | |||
| 		destination.receive(recv_packet); | ||||
| #endif | ||||
| 
 | ||||
| 		RNS::head("Ready!", RNS::LOG_EXTREME); | ||||
| 	} | ||||
| 	catch (std::exception& e) { | ||||
| 		RNS::error(std::string("!!! Exception in setup_reticulum: ") + e.what() + " !!!"); | ||||
| 		RNS::error(std::string("!!! Exception in reticulum_setup: ") + e.what() + " !!!"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void teardown_reticulum() { | ||||
| void reticulum_teardown() { | ||||
| 	RNS::info("Tearing down Reticulum..."); | ||||
| 
 | ||||
| 	try { | ||||
|  | @ -275,7 +277,7 @@ void teardown_reticulum() { | |||
| 
 | ||||
| 	} | ||||
| 	catch (std::exception& e) { | ||||
| 		RNS::error(std::string("!!! Exception in teardown_reticulum: ") + e.what() + " !!!"); | ||||
| 		RNS::error(std::string("!!! Exception in reticulum_teardown: ") + e.what() + " !!!"); | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
|  | @ -317,11 +319,11 @@ void setup() { | |||
| #endif | ||||
| 
 | ||||
| #if defined(RUN_RETICULUM) | ||||
| 	setup_reticulum(); | ||||
| 	reticulum_setup(); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef ARDUINO | ||||
| 	Serial.print("Goodbye from T-Beam on PlatformIO!\n"); | ||||
| 	//Serial.print("Goodbye from T-Beam on PlatformIO!\n");
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
|  | @ -380,7 +382,9 @@ int main(void) { | |||
| 		if (ch > 0) { | ||||
| 			switch (ch) { | ||||
| 			case 'a': | ||||
| #if defined(RUN_RETICULUM) | ||||
| 				reticulum_announce(); | ||||
| #endif | ||||
| 				break; | ||||
| 			case 'q': | ||||
| 				run = false; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 attermann
						attermann