From ab733aa7074cf1992f605c203c3d6a7923602705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex?= Date: Thu, 18 Jun 2026 15:31:34 +0000 Subject: [PATCH] feat: result (#40) --- .clang-format | 2 + src/logos_blockchain_module.cpp | 353 ++++++++++++----------- src/logos_blockchain_module.h | 73 +++-- tests/test_blockchain.cpp | 490 ++++++++++++++++---------------- 4 files changed, 489 insertions(+), 429 deletions(-) diff --git a/.clang-format b/.clang-format index b2b9dca..10bd3e0 100644 --- a/.clang-format +++ b/.clang-format @@ -23,4 +23,6 @@ AllowShortLoopsOnASingleLine: false BreakAfterReturnType: None PenaltyReturnTypeOnItsOwnLine: 1000 +BreakTemplateDeclarations: Yes + NamespaceIndentation: All diff --git a/src/logos_blockchain_module.cpp b/src/logos_blockchain_module.cpp index aa591e6..e6b9bf0 100644 --- a/src/logos_blockchain_module.cpp +++ b/src/logos_blockchain_module.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -18,12 +17,30 @@ using json = nlohmann::json; // Define static member LogosBlockchainModule* LogosBlockchainModule::s_instance = nullptr; +// Shorthands for building StdLogosResult values. +namespace result { + StdLogosResult ok() { + return {true}; + } + + template + StdLogosResult ok(T value) { // NOLINT(performance-unnecessary-value-param) + return {true, std::move(value)}; + } + + StdLogosResult err(std::string message) { + return {false, {}, std::move(message)}; + } +} // namespace result + namespace { // Rust `File::open` / `deserialize_config_at_path` only accept real filesystem paths. QML often // passes `file:///...` URLs; strip to a local path when applicable. std::string localPathFromFileUrl(const std::string& s) { - if (s.size() >= 7 && s.substr(0, 7) == "file://") return s.substr(7); - if (s.size() >= 5 && s.substr(0, 5) == "file:") return s.substr(5); + if (s.size() >= 7 && s.substr(0, 7) == "file://") + return s.substr(7); + if (s.size() >= 5 && s.substr(0, 5) == "file:") + return s.substr(5); return s; } @@ -69,19 +86,20 @@ namespace { } } - std::string bytes_to_hex(const uint8_t* data, size_t len) { + std::string bytes_to_hex(const uint8_t* data, const size_t len) { std::string out; out.reserve(len * 2); boost::algorithm::hex_lower(data, data + len, std::back_inserter(out)); return out; } - // Map a "ed25519" / "zk" string (case-insensitive) to the C KeyType enum. + // Maps an `ed25519`/`zk` string (case-insensitive) to the C KeyType enum. bool parse_key_type(const std::string& s, KeyType& out) { std::string lower = s; boost::algorithm::trim(lower); - std::transform(lower.begin(), lower.end(), lower.begin(), - [](unsigned char c) { return std::tolower(c); }); + std::transform(lower.begin(), lower.end(), lower.begin(), [](const unsigned char c) { + return std::tolower(c); + }); if (lower == "ed25519") { out = KeyType::Ed25519; return true; @@ -211,19 +229,24 @@ namespace environment { bool is_circuits_path_valid(const std::string& path) { std::error_code ec; - if (!fs::is_directory(path, ec)) return false; - for (const auto& entry : fs::directory_iterator(path, ec)) { - if (entry.is_regular_file()) return true; + if (!fs::is_directory(path, ec)) + return false; + for (const auto& entry : fs::directory_iterator(path, ec)) { // NOLINT(*-use-anyofallof) + if (entry.is_regular_file()) + return true; } return false; } void setup_circuits_path(const std::string& module_path) { - fs::path circuits_path = fs::path(module_path) / "circuits"; - std::string circuits_str = circuits_path.string(); + const fs::path circuits_path = fs::path(module_path) / "circuits"; + const std::string circuits_str = circuits_path.string(); if (!is_circuits_path_valid(circuits_str)) { - fprintf(stderr, "FATAL: The LOGOS_BLOCKCHAIN_CIRCUITS environment variable is not set or does not contain any files.\n"); + fprintf( + stderr, + "FATAL: The LOGOS_BLOCKCHAIN_CIRCUITS environment variable is not set or does not contain any files.\n" + ); return; } @@ -251,7 +274,7 @@ LogosBlockchainModule::LogosBlockchainModule() { LogosBlockchainModule::~LogosBlockchainModule() { s_instance = nullptr; if (node) { - stop(); + (void)stop(); } } @@ -259,13 +282,13 @@ LogosBlockchainModule::~LogosBlockchainModule() { // Lifecycle -std::string LogosBlockchainModule::generate_user_config(const std::string& json_args) { +StdLogosResult LogosBlockchainModule::generate_user_config(const std::string& json_args) { json parsed_args; try { parsed_args = json::parse(json_args); } catch (const json::parse_error& e) { fprintf(stderr, "Failed to parse JSON args: %s\n", e.what()); - return "1"; + return result::err(std::string("Failed to parse JSON args: ") + e.what()); } const OwnedGenerateConfigArgs owned_args(parsed_args); @@ -273,23 +296,23 @@ std::string LogosBlockchainModule::generate_user_config(const std::string& json_ const OperationStatus status = ::generate_user_config(owned_args.ffi_args); if (!is_ok(&status)) { fprintf(stderr, "Failed to generate user config. Error: %d\n", status); - return "1"; + return result::err("Failed to generate user config."); } - return "0"; + return result::ok(); } -std::string LogosBlockchainModule::start(const std::string& config_path, const std::string& deployment) { +StdLogosResult LogosBlockchainModule::start(const std::string& config_path, const std::string& deployment) { if (node) { fprintf(stderr, "Could not execute the operation: The node is already running.\n"); - return "1"; + return result::err("The node is already running."); } const char* module_path_env = std::getenv("LOGOS_MODULE_PATH"); if (module_path_env && *module_path_env) { environment::setup_circuits_path(module_path_env); } else { - fprintf(stderr, "Warning: LOGOS_MODULE_PATH not set, skipping circuits path setup.\n"); + fprintf(stderr, "Warning: LOGOS_MODULE_PATH not set, skipping circuits' path setup.\n"); } std::string effective_config_path = config_path; @@ -301,7 +324,7 @@ std::string LogosBlockchainModule::start(const std::string& config_path, const s fprintf(stderr, "Using config from LB_CONFIG_PATH: %s\n", effective_config_path.c_str()); } else { fprintf(stderr, "Config path was not specified and LB_CONFIG_PATH is not set.\n"); - return "3"; + return result::err("Config path was not specified and LB_CONFIG_PATH is not set."); } } @@ -315,7 +338,7 @@ std::string LogosBlockchainModule::start(const std::string& config_path, const s fprintf(stderr, "Start node returned with value and error.\n"); if (!is_ok(&error)) { fprintf(stderr, "Failed to start the node. Error: %d\n", error); - return "4"; + return result::err("Failed to start the node."); } node = value; @@ -323,23 +346,23 @@ std::string LogosBlockchainModule::start(const std::string& config_path, const s if (!node) { fprintf(stderr, "Could not subscribe to block events: The node is not running.\n"); - return "4"; + return result::err("Could not subscribe to block events: the node is not running."); } s_instance = this; const OperationStatus subscribe_status = subscribe_to_new_blocks(node, on_new_block_callback); if (!is_ok(&subscribe_status)) { fprintf(stderr, "Failed to subscribe to new blocks. Error: %d\n", subscribe_status); - return "5"; + return result::err("Failed to subscribe to new blocks."); } - return "0"; + return result::ok(); } -std::string LogosBlockchainModule::stop() { +StdLogosResult LogosBlockchainModule::stop() { if (!node) { fprintf(stderr, "Could not execute the operation: The node is not running.\n"); - return "1"; + return result::err("The node is not running."); } s_instance = nullptr; @@ -352,36 +375,42 @@ std::string LogosBlockchainModule::stop() { } node = nullptr; - return "0"; + return result::ok(); } // Config management -std::string LogosBlockchainModule::update_user_config(const std::string& user_config_path, const std::string& keystore_path) { +StdLogosResult LogosBlockchainModule::update_user_config( + const std::string& user_config_path, + const std::string& keystore_path +) { const std::string config = localPathFromFileUrl(user_config_path); const std::string keystore = localPathFromFileUrl(keystore_path); const OperationStatus status = ::update_user_config(config.c_str(), keystore.c_str()); if (!is_ok(&status)) { fprintf(stderr, "Failed to update user config. Error: %d\n", status); - return "1"; + return result::err("Failed to update user config."); } - return "0"; + return result::ok(); } -std::string LogosBlockchainModule::migrate_user_config(const std::string& output_path, const std::string& keystore_path) { +StdLogosResult LogosBlockchainModule::migrate_user_config( + const std::string& output_path, + const std::string& keystore_path +) { const std::string output = localPathFromFileUrl(output_path); const std::string keystore = localPathFromFileUrl(keystore_path); const OperationStatus status = ::migrate_user_config(output.c_str(), keystore.c_str()); if (!is_ok(&status)) { fprintf(stderr, "Failed to migrate user config. Error: %d\n", status); - return "1"; + return result::err("Failed to migrate user config."); } - return "0"; + return result::ok(); } -std::string LogosBlockchainModule::migrate_user_config_0_1_2( +StdLogosResult LogosBlockchainModule::migrate_user_config_0_1_2( const std::string& new_config_path, const std::string& old_config_path, const std::string& keystore_path @@ -394,12 +423,12 @@ std::string LogosBlockchainModule::migrate_user_config_0_1_2( ::migrate_user_config_0_1_2(new_config.c_str(), old_config.c_str(), keystore.c_str()); if (!is_ok(&status)) { fprintf(stderr, "Failed to migrate 0.1.2 config. Error: %d\n", status); - return "1"; + return result::err("Failed to migrate 0.1.2 config."); } - return "0"; + return result::ok(); } -std::string LogosBlockchainModule::participate( +StdLogosResult LogosBlockchainModule::participate( const std::string& config_path, const std::string& keystore_path, const std::string& output_dir, @@ -414,14 +443,14 @@ std::string LogosBlockchainModule::participate( ::participate(config.c_str(), keystore.c_str(), output.c_str(), external_address_ptr); if (!is_ok(&status)) { fprintf(stderr, "Failed to generate participation data. Error: %d\n", status); - return "1"; + return result::err("Failed to generate participation data."); } - return "0"; + return result::ok(); } // Keystore -std::string LogosBlockchainModule::generate_key( +StdLogosResult LogosBlockchainModule::generate_key( const std::string& user_config_path, const std::string& keystore_path, const std::string& key_type, @@ -429,7 +458,7 @@ std::string LogosBlockchainModule::generate_key( ) { KeyType type{}; if (!parse_key_type(key_type, type)) { - return "Error: Invalid key_type (expected \"ed25519\" or \"zk\")."; + return result::err(R"(Invalid key_type (expected "ed25519" or "zk").)"); } const std::string config = localPathFromFileUrl(user_config_path); @@ -439,18 +468,18 @@ std::string LogosBlockchainModule::generate_key( auto [value, error] = ::generate_key(config.c_str(), keystore.c_str(), type, key_title_ptr); if (!is_ok(&error)) { fprintf(stderr, "Failed to generate key. Error: %d\n", error); - return "Error: Failed to generate key: " + std::to_string(error); + return result::err("Failed to generate key: " + std::to_string(error)); } - std::string result(value); + const std::string out(value); const OperationStatus free_status = free_cstring(value); if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free key id string. Error: %d\n", free_status); } - return result; + return result::ok(out); } -std::string LogosBlockchainModule::add_key( +StdLogosResult LogosBlockchainModule::add_key( const std::string& user_config_path, const std::string& keystore_path, const std::string& key_type, @@ -460,23 +489,22 @@ std::string LogosBlockchainModule::add_key( KeyType type{}; if (!parse_key_type(key_type, type)) { fprintf(stderr, "Invalid key_type (expected \"ed25519\" or \"zk\").\n"); - return "1"; + return result::err(R"(Invalid key_type (expected "ed25519" or "zk").)"); } const std::string config = localPathFromFileUrl(user_config_path); const std::string keystore = localPathFromFileUrl(keystore_path); const char* key_title_ptr = key_title.empty() ? nullptr : key_title.c_str(); - const OperationStatus status = - ::add_key(config.c_str(), keystore.c_str(), type, key_hex.c_str(), key_title_ptr); + const OperationStatus status = ::add_key(config.c_str(), keystore.c_str(), type, key_hex.c_str(), key_title_ptr); if (!is_ok(&status)) { fprintf(stderr, "Failed to add key. Error: %d\n", status); - return "1"; + return result::err("Failed to add key."); } - return "0"; + return result::ok(); } -std::string LogosBlockchainModule::remove_key( +StdLogosResult LogosBlockchainModule::remove_key( const std::string& user_config_path, const std::string& keystore_path, const std::string& key_title @@ -487,60 +515,60 @@ std::string LogosBlockchainModule::remove_key( const OperationStatus status = ::remove_key(config.c_str(), keystore.c_str(), key_title.c_str()); if (!is_ok(&status)) { fprintf(stderr, "Failed to remove key. Error: %d\n", status); - return "1"; + return result::err("Failed to remove key."); } - return "0"; + return result::ok(); } // Identity -std::string LogosBlockchainModule::get_peer_id(const std::string& config_path) { +StdLogosResult LogosBlockchainModule::get_peer_id(const std::string& config_path) { const std::string config = localPathFromFileUrl(config_path); auto [value, error] = ::get_peer_id(config.c_str()); if (!is_ok(&error)) { fprintf(stderr, "Failed to get peer id. Error: %d\n", error); - return "Error: Failed to get peer id: " + std::to_string(error); + return result::err("Failed to get peer id: " + std::to_string(error)); } - std::string result(value); + const std::string out(value); const OperationStatus free_status = free_cstring(value); if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free peer id string. Error: %d\n", free_status); } - return result; + return result::ok(out); } // Wallet -std::string LogosBlockchainModule::wallet_get_balance(const std::string& address_hex) { +StdLogosResult LogosBlockchainModule::wallet_get_balance(const std::string& address_hex) const { fprintf(stderr, "wallet_get_balance: address_hex=%s\n", address_hex.c_str()); if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } const std::vector bytes = parse_address_hex(address_hex); if (bytes.empty() || static_cast(bytes.size()) != ADDRESS_BYTES) { - return "Error: Address must be 64 hex characters (32 bytes)."; + return result::err("Address must be 64 hex characters (32 bytes)."); } auto [value, error] = get_balance(node, bytes.data(), nullptr); if (!is_ok(&error)) { - return "Error: Failed to get balance: " + std::to_string(error); + return result::err("Failed to get balance: " + std::to_string(error)); } - return std::to_string(value); + return result::ok(std::to_string(value)); } -std::string LogosBlockchainModule::wallet_transfer_funds( +StdLogosResult LogosBlockchainModule::wallet_transfer_funds( const std::string& change_public_key, const std::vector& sender_addresses, const std::string& recipient_address, const std::string& amount, const std::string& optional_tip_hex -) { +) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } std::string amount_trimmed = amount; @@ -548,25 +576,25 @@ std::string LogosBlockchainModule::wallet_transfer_funds( uint64_t amount_val = 0; auto [ptr, ec] = std::from_chars(amount_trimmed.data(), amount_trimmed.data() + amount_trimmed.size(), amount_val); if (ec != std::errc{} || ptr != amount_trimmed.data() + amount_trimmed.size() || amount_trimmed.empty()) { - return "Error: Invalid amount (positive integer required)."; + return result::err("Invalid amount (positive integer required)."); } const std::vector change_bytes = parse_address_hex(change_public_key); if (change_bytes.empty() || static_cast(change_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid change_public_key (64 hex characters required)."; + return result::err("Invalid change_public_key (64 hex characters required)."); } const std::vector recipient_bytes = parse_address_hex(recipient_address); if (recipient_bytes.empty() || static_cast(recipient_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid recipient_address (64 hex characters required)."; + return result::err("Invalid recipient_address (64 hex characters required)."); } if (sender_addresses.empty()) { - return "Error: At least one sender address required."; + return result::err("At least one sender address is required."); } std::vector> funding_bytes; for (const std::string& hex : sender_addresses) { std::vector b = parse_address_hex(hex); if (b.empty() || static_cast(b.size()) != ADDRESS_BYTES) { - return "Error: Invalid sender address (64 hex characters required)."; + return result::err("Invalid sender address (64 hex characters required)."); } funding_bytes.push_back(std::move(b)); } @@ -579,7 +607,7 @@ std::string LogosBlockchainModule::wallet_transfer_funds( if (!optional_tip_hex.empty()) { tip_bytes = parse_address_hex(optional_tip_hex); if (tip_bytes.empty() || static_cast(tip_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid optional tip (64 hex characters or empty)."; + return result::err("Invalid optional tip (64 hex characters or empty)."); } optional_tip = reinterpret_cast(tip_bytes.data()); } @@ -594,23 +622,24 @@ std::string LogosBlockchainModule::wallet_transfer_funds( auto [value, error] = transfer_funds(node, &args); if (!is_ok(&error)) { - return "Error: Failed to transfer funds: " + std::to_string(error); + return result::err("Failed to transfer funds: " + std::to_string(error)); } - return bytes_to_hex(reinterpret_cast(&value), ADDRESS_BYTES); + return result::ok(bytes_to_hex(reinterpret_cast(&value), ADDRESS_BYTES)); } -std::vector LogosBlockchainModule::wallet_get_known_addresses() { - std::vector out; +StdLogosResult LogosBlockchainModule::wallet_get_known_addresses() const { if (!node) { fprintf(stderr, "Could not execute the operation: The node is not running.\n"); - return out; + return result::err("The node is not running."); } auto [value, error] = get_known_addresses(node); if (!is_ok(&error)) { fprintf(stderr, "Failed to get known addresses. Error: %d\n", error); - return out; + return result::err("Failed to get known addresses: " + std::to_string(error)); } + std::vector out; for (size_t i = 0; i < value.len; ++i) { + // ReSharper disable once CppTooWideScope const uint8_t* ptr = value.addresses[i]; if (ptr) { out.push_back(bytes_to_hex(ptr, ADDRESS_BYTES)); @@ -620,22 +649,26 @@ std::vector LogosBlockchainModule::wallet_get_known_addresses() { if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free known addresses. Error: %d\n", free_status); } - fprintf(stderr, "blockchain lib: known addresses, count=%zu sample:%s\n", - out.size(), out.empty() ? "(none)" : out.front().c_str()); - return out; + fprintf( + stderr, + "blockchain lib: known addresses, count=%zu sample:%s\n", + out.size(), + out.empty() ? "(none)" : out.front().c_str() + ); + return result::ok(std::move(out)); } -std::string LogosBlockchainModule::wallet_get_notes( +StdLogosResult LogosBlockchainModule::wallet_get_notes( const std::string& wallet_address_hex, const std::string& optional_tip_hex -) { +) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } const std::vector address_bytes = parse_address_hex(wallet_address_hex); if (address_bytes.empty() || static_cast(address_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid wallet address (64 hex characters required)."; + return result::err("Invalid wallet address (64 hex characters required)."); } std::vector tip_bytes; @@ -643,25 +676,25 @@ std::string LogosBlockchainModule::wallet_get_notes( if (!optional_tip_hex.empty()) { tip_bytes = parse_address_hex(optional_tip_hex); if (tip_bytes.empty() || static_cast(tip_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid optional tip (64 hex characters or empty)."; + return result::err("Invalid optional tip (64 hex characters or empty)."); } optional_tip = reinterpret_cast(tip_bytes.data()); } auto [value, error] = get_wallet_notes(node, address_bytes.data(), optional_tip); if (!is_ok(&error)) { - return "Error: Failed to get wallet notes: " + std::to_string(error); + return result::err("Failed to get wallet notes: " + std::to_string(error)); } json obj; - obj["tip"] = bytes_to_hex(reinterpret_cast(value.tip), TX_HASH_BYTES); + obj["tip"] = bytes_to_hex(value.tip, TX_HASH_BYTES); json notes = json::array(); for (size_t i = 0; i < value.len; ++i) { - const WalletNote& note = value.notes[i]; + const auto& [note_id, note_value] = value.notes[i]; json n; - n["id"] = bytes_to_hex(reinterpret_cast(note.id), TX_HASH_BYTES); + n["id"] = bytes_to_hex(note_id, TX_HASH_BYTES); // Value is u64; serialized as a string to avoid JSON number precision loss. - n["value"] = std::to_string(note.value); + n["value"] = std::to_string(note_value); notes.push_back(std::move(n)); } obj["notes"] = std::move(notes); @@ -670,33 +703,33 @@ std::string LogosBlockchainModule::wallet_get_notes( if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free wallet notes. Error: %d\n", free_status); } - return obj.dump(); + return result::ok(obj.dump()); } -std::string LogosBlockchainModule::leader_claim() { +StdLogosResult LogosBlockchainModule::leader_claim() const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } auto [value, error] = ::leader_claim(node); if (!is_ok(&error)) { - return "Error: Failed to claim leader rewards: " + std::to_string(error); + return result::err("Failed to claim leader rewards: " + std::to_string(error)); } - return bytes_to_hex(reinterpret_cast(&value), TX_HASH_BYTES); + return result::ok(bytes_to_hex(reinterpret_cast(&value), TX_HASH_BYTES)); } // Channel -std::string LogosBlockchainModule::channel_deposit( +StdLogosResult LogosBlockchainModule::channel_deposit( const std::string& channel_id_hex, const std::string& funding_public_key_hex, const std::string& amount, const std::string& metadata_hex, const std::string& optional_tip_hex -) { +) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } std::string amount_trimmed = amount; @@ -704,25 +737,25 @@ std::string LogosBlockchainModule::channel_deposit( uint64_t amount_val = 0; auto [ptr, ec] = std::from_chars(amount_trimmed.data(), amount_trimmed.data() + amount_trimmed.size(), amount_val); if (ec != std::errc{} || ptr != amount_trimmed.data() + amount_trimmed.size() || amount_trimmed.empty()) { - return "Error: Invalid amount (positive integer required)."; + return result::err("Invalid amount (positive integer required)."); } if (amount_val == 0) { - return "Error: Invalid amount (must be greater than zero)."; + return result::err("Invalid amount (must be greater than zero)."); } const std::vector channel_bytes = parse_address_hex(channel_id_hex); if (channel_bytes.empty() || static_cast(channel_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid channel_id (64 hex characters required)."; + return result::err("Invalid channel_id (64 hex characters required)."); } const std::vector funding_bytes = parse_address_hex(funding_public_key_hex); if (funding_bytes.empty() || static_cast(funding_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid funding_public_key (64 hex characters required)."; + return result::err("Invalid funding_public_key (64 hex characters required)."); } std::vector metadata_bytes; if (!metadata_hex.empty() && !parse_hex_bytes(metadata_hex, metadata_bytes)) { - return "Error: Invalid metadata (even-length hex string required)."; + return result::err("Invalid metadata (even-length hex string required)."); } std::vector tip_bytes; @@ -730,7 +763,7 @@ std::string LogosBlockchainModule::channel_deposit( if (!optional_tip_hex.empty()) { tip_bytes = parse_address_hex(optional_tip_hex); if (tip_bytes.empty() || static_cast(tip_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid optional tip (64 hex characters or empty)."; + return result::err("Invalid optional tip (64 hex characters or empty)."); } optional_tip = reinterpret_cast(tip_bytes.data()); } @@ -745,12 +778,12 @@ std::string LogosBlockchainModule::channel_deposit( auto [value, error] = ::channel_deposit(node, &args); if (!is_ok(&error)) { - return "Error: Failed to deposit into channel: " + std::to_string(error); + return result::err("Failed to deposit into channel: " + std::to_string(error)); } - return bytes_to_hex(reinterpret_cast(&value), ADDRESS_BYTES); + return result::ok(bytes_to_hex(reinterpret_cast(&value), ADDRESS_BYTES)); } -std::string LogosBlockchainModule::channel_deposit_with_notes( +StdLogosResult LogosBlockchainModule::channel_deposit_with_notes( const std::string& channel_id_hex, const std::vector& input_note_id_hexes, const std::string& metadata_hex, @@ -758,18 +791,18 @@ std::string LogosBlockchainModule::channel_deposit_with_notes( const std::vector& funding_public_key_hexes, const std::string& max_tx_fee, const std::string& optional_tip_hex -) { +) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } const std::vector channel_bytes = parse_address_hex(channel_id_hex); if (channel_bytes.empty() || static_cast(channel_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid channel_id (64 hex characters required)."; + return result::err("Invalid channel_id (64 hex characters required)."); } if (input_note_id_hexes.empty()) { - return "Error: At least one input note required."; + return result::err("At least one input note is required."); } // Note IDs are 32-byte values stored contiguously so the buffer can be passed // as a `NoteId` (uint8_t[32]) array. @@ -778,24 +811,24 @@ std::string LogosBlockchainModule::channel_deposit_with_notes( for (const std::string& hex : input_note_id_hexes) { const std::vector b = parse_address_hex(hex); if (b.empty() || static_cast(b.size()) != ADDRESS_BYTES) { - return "Error: Invalid input note id (64 hex characters required)."; + return result::err("Invalid input note id (64 hex characters required)."); } note_ids_flat.insert(note_ids_flat.end(), b.begin(), b.end()); } const std::vector change_bytes = parse_address_hex(change_public_key_hex); if (change_bytes.empty() || static_cast(change_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid change_public_key (64 hex characters required)."; + return result::err("Invalid change_public_key (64 hex characters required)."); } if (funding_public_key_hexes.empty()) { - return "Error: At least one funding public key required."; + return result::err("At least one funding public key is required."); } std::vector> funding_bytes; for (const std::string& hex : funding_public_key_hexes) { std::vector b = parse_address_hex(hex); if (b.empty() || static_cast(b.size()) != ADDRESS_BYTES) { - return "Error: Invalid funding public key (64 hex characters required)."; + return result::err("Invalid funding public key (64 hex characters required)."); } funding_bytes.push_back(std::move(b)); } @@ -809,12 +842,12 @@ std::string LogosBlockchainModule::channel_deposit_with_notes( uint64_t max_tx_fee_val = 0; auto [ptr, ec] = std::from_chars(fee_trimmed.data(), fee_trimmed.data() + fee_trimmed.size(), max_tx_fee_val); if (ec != std::errc{} || ptr != fee_trimmed.data() + fee_trimmed.size() || fee_trimmed.empty()) { - return "Error: Invalid max_tx_fee (non-negative integer required)."; + return result::err("Invalid max_tx_fee (non-negative integer required)."); } std::vector metadata_bytes; if (!metadata_hex.empty() && !parse_hex_bytes(metadata_hex, metadata_bytes)) { - return "Error: Invalid metadata (even-length hex string required)."; + return result::err("Invalid metadata (even-length hex string required)."); } std::vector tip_bytes; @@ -822,7 +855,7 @@ std::string LogosBlockchainModule::channel_deposit_with_notes( if (!optional_tip_hex.empty()) { tip_bytes = parse_address_hex(optional_tip_hex); if (tip_bytes.empty() || static_cast(tip_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid optional tip (64 hex characters or empty)."; + return result::err("Invalid optional tip (64 hex characters or empty)."); } optional_tip = reinterpret_cast(tip_bytes.data()); } @@ -841,19 +874,19 @@ std::string LogosBlockchainModule::channel_deposit_with_notes( auto [value, error] = ::channel_deposit_with_notes(node, &args); if (!is_ok(&error)) { - return "Error: Failed to deposit into channel: " + std::to_string(error); + return result::err("Failed to deposit into channel: " + std::to_string(error)); } - return bytes_to_hex(reinterpret_cast(&value), ADDRESS_BYTES); + return result::ok(bytes_to_hex(reinterpret_cast(&value), ADDRESS_BYTES)); } -std::string LogosBlockchainModule::wallet_get_claimable_vouchers() { +StdLogosResult LogosBlockchainModule::wallet_get_claimable_vouchers() const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } auto [value, error] = get_claimable_vouchers(node, nullptr); if (!is_ok(&error)) { - return "Error: Failed to get claimable vouchers: " + std::to_string(error); + return result::err("Failed to get claimable vouchers: " + std::to_string(error)); } json obj; @@ -861,10 +894,10 @@ std::string LogosBlockchainModule::wallet_get_claimable_vouchers() { obj["vouchers"] = json::array(); for (size_t i = 0; i < value.len; ++i) { - const ClaimableVoucher& voucher = value.vouchers[i]; + const auto& [commitment, nullifier] = value.vouchers[i]; obj["vouchers"].push_back({ - {"commitment", bytes_to_hex(reinterpret_cast(&voucher.commitment), ADDRESS_BYTES)}, - {"nullifier", bytes_to_hex(reinterpret_cast(&voucher.nullifier), ADDRESS_BYTES)}, + {"commitment", bytes_to_hex(reinterpret_cast(&commitment), ADDRESS_BYTES)}, + {"nullifier", bytes_to_hex(reinterpret_cast(&nullifier), ADDRESS_BYTES)}, }); } @@ -873,37 +906,37 @@ std::string LogosBlockchainModule::wallet_get_claimable_vouchers() { fprintf(stderr, "Failed to free claimable vouchers. Error: %d\n", free_status); } - return obj.dump(); + return result::ok(obj.dump()); } // Blend -std::string LogosBlockchainModule::blend_join_as_core_node( +StdLogosResult LogosBlockchainModule::blend_join_as_core_node( const std::string& provider_id_hex, const std::string& zk_id_hex, const std::string& locked_note_id_hex, const std::vector& locators -) { +) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } const std::vector provider_id_bytes = parse_address_hex(provider_id_hex); if (provider_id_bytes.empty() || static_cast(provider_id_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid provider_id_hex (64 hex characters required)."; + return result::err("Invalid provider_id_hex (64 hex characters required)."); } const std::vector zk_id_bytes = parse_address_hex(zk_id_hex); if (zk_id_bytes.empty() || static_cast(zk_id_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid zk_id_hex (64 hex characters required)."; + return result::err("Invalid zk_id_hex (64 hex characters required)."); } const std::vector locked_note_id_bytes = parse_address_hex(locked_note_id_hex); if (locked_note_id_bytes.empty() || static_cast(locked_note_id_bytes.size()) != ADDRESS_BYTES) { - return "Error: Invalid locked_note_id_hex (64 hex characters required)."; + return result::err("Invalid locked_note_id_hex (64 hex characters required)."); } - // locators_ptrs holds raw pointers into the std::strings (valid as long as locators lives). + // locators_ptrs holds raw pointers into the std::strings (valid as long as `locators` lives). std::vector locators_ptrs; locators_ptrs.reserve(locators.size()); for (const std::string& locator : locators) { @@ -919,94 +952,94 @@ std::string LogosBlockchainModule::blend_join_as_core_node( locators_ptrs.size() ); if (!is_ok(&error)) { - return "Error: Failed to join as core node: " + std::to_string(error); + return result::err("Failed to join as core node: " + std::to_string(error)); } std::string declaration_id = bytes_to_hex(reinterpret_cast(&value), sizeof(value)); - fprintf(stderr, "Successfully joined as core node. DeclarationId: %s\n", declaration_id.c_str()); - return declaration_id; + fprintf(stderr, "Successfully joined as a core node. DeclarationId: %s\n", declaration_id.c_str()); + return result::ok(std::move(declaration_id)); } // Explorer -std::string LogosBlockchainModule::get_block(const std::string& header_id_hex) { +StdLogosResult LogosBlockchainModule::get_block(const std::string& header_id_hex) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } const std::vector bytes = parse_address_hex(header_id_hex); if (bytes.empty() || static_cast(bytes.size()) != ADDRESS_BYTES) { - return "Error: Header ID must be 64 hex characters (32 bytes)."; + return result::err("Header ID must be 64 hex characters (32 bytes)."); } auto [value, error] = ::get_block(node, reinterpret_cast(bytes.data())); if (!is_ok(&error)) { fprintf(stderr, "Failed to get block. Error: %d\n", error); - return "Error: Failed to get block: " + std::to_string(error); + return result::err("Failed to get block: " + std::to_string(error)); } - std::string result(value); + std::string out(value); const OperationStatus free_status = free_cstring(value); if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free block string. Error: %d\n", free_status); } - return result; + return result::ok(std::move(out)); } -std::string LogosBlockchainModule::get_blocks(const uint64_t from_slot, const uint64_t to_slot) { +StdLogosResult LogosBlockchainModule::get_blocks(const uint64_t from_slot, const uint64_t to_slot) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } auto [value, error] = ::get_blocks(node, from_slot, to_slot); if (!is_ok(&error)) { fprintf(stderr, "Failed to get blocks. Error: %d\n", error); - return "Error: Failed to get blocks: " + std::to_string(error); + return result::err("Failed to get blocks: " + std::to_string(error)); } - std::string result(value); + std::string out(value); const OperationStatus free_status = free_cstring(value); if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free blocks string. Error: %d\n", free_status); } - return result; + return result::ok(std::move(out)); } -std::string LogosBlockchainModule::get_transaction(const std::string& tx_hash_hex) { +StdLogosResult LogosBlockchainModule::get_transaction(const std::string& tx_hash_hex) const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } const std::vector bytes = parse_address_hex(tx_hash_hex); if (bytes.empty() || static_cast(bytes.size()) != ADDRESS_BYTES) { - return "Error: Transaction hash must be 64 hex characters (32 bytes)."; + return result::err("Transaction hash must be 64 hex characters (32 bytes)."); } auto [value, error] = ::get_transaction(node, reinterpret_cast(bytes.data())); if (!is_ok(&error)) { fprintf(stderr, "Failed to get transaction. Error: %d\n", error); - return "Error: Failed to get transaction: " + std::to_string(error); + return result::err("Failed to get transaction: " + std::to_string(error)); } - std::string result(value); + std::string out(value); const OperationStatus free_status = free_cstring(value); if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free transaction string. Error: %d\n", free_status); } - return result; + return result::ok(std::move(out)); } // Cryptarchia -std::string LogosBlockchainModule::get_cryptarchia_info() { +StdLogosResult LogosBlockchainModule::get_cryptarchia_info() const { if (!node) { - return "Error: The node is not running."; + return result::err("The node is not running."); } auto [value, error] = ::get_cryptarchia_info(node); if (!is_ok(&error)) { fprintf(stderr, "Failed to get cryptarchia info. Error: %d\n", error); - return "Error: Failed to get cryptarchia info: " + std::to_string(error); + return result::err("Failed to get cryptarchia info: " + std::to_string(error)); } json obj; @@ -1020,5 +1053,5 @@ std::string LogosBlockchainModule::get_cryptarchia_info() { if (!is_ok(&free_status)) { fprintf(stderr, "Failed to free cryptarchia info. Error: %d\n", free_status); } - return obj.dump(); + return result::ok(obj.dump()); } diff --git a/src/logos_blockchain_module.h b/src/logos_blockchain_module.h index 3097ddc..df79054 100644 --- a/src/logos_blockchain_module.h +++ b/src/logos_blockchain_module.h @@ -1,10 +1,10 @@ #pragma once -#include #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -17,24 +17,30 @@ extern "C" { class LogosBlockchainModule : public LogosModuleContext { public: LogosBlockchainModule(); - ~LogosBlockchainModule(); + ~LogosBlockchainModule() override; // ---- Node ---- // Lifecycle - std::string generate_user_config(const std::string& json_args); - std::string start(const std::string& config_path, const std::string& deployment); - std::string stop(); + [[nodiscard]] StdLogosResult start(const std::string& config_path, const std::string& deployment); + [[nodiscard]] StdLogosResult stop(); // Config management - std::string update_user_config(const std::string& user_config_path, const std::string& keystore_path); - std::string migrate_user_config(const std::string& output_path, const std::string& keystore_path); - std::string migrate_user_config_0_1_2( + [[nodiscard]] static StdLogosResult generate_user_config(const std::string& json_args); + [[nodiscard]] static StdLogosResult update_user_config( + const std::string& user_config_path, + const std::string& keystore_path + ); + [[nodiscard]] static StdLogosResult migrate_user_config( + const std::string& output_path, + const std::string& keystore_path + ); + [[nodiscard]] static StdLogosResult migrate_user_config_0_1_2( const std::string& new_config_path, const std::string& old_config_path, const std::string& keystore_path ); - std::string participate( + [[nodiscard]] static StdLogosResult participate( const std::string& config_path, const std::string& keystore_path, const std::string& output_dir, @@ -43,48 +49,48 @@ public: // Keystore // key_type is "ed25519" or "zk". key_title may be empty (auto-generated). - std::string generate_key( + [[nodiscard]] static StdLogosResult generate_key( const std::string& user_config_path, const std::string& keystore_path, const std::string& key_type, const std::string& key_title ); - std::string add_key( + [[nodiscard]] static StdLogosResult add_key( const std::string& user_config_path, const std::string& keystore_path, const std::string& key_type, const std::string& key_hex, const std::string& key_title ); - std::string remove_key( + [[nodiscard]] static StdLogosResult remove_key( const std::string& user_config_path, const std::string& keystore_path, const std::string& key_title ); // Identity - std::string get_peer_id(const std::string& config_path); + [[nodiscard]] static StdLogosResult get_peer_id(const std::string& config_path); // Wallet - std::string wallet_get_balance(const std::string& address_hex); - std::string wallet_transfer_funds( + [[nodiscard]] StdLogosResult wallet_get_balance(const std::string& address_hex) const; + [[nodiscard]] StdLogosResult wallet_transfer_funds( const std::string& change_public_key, const std::vector& sender_addresses, const std::string& recipient_address, const std::string& amount, const std::string& optional_tip_hex - ); - std::vector wallet_get_known_addresses(); + ) const; + [[nodiscard]] StdLogosResult wallet_get_known_addresses() const; // Spendable notes (UTXOs) of a wallet address, as a JSON string: // { "tip": "", "notes": [ { "id": "", "value": "" }, ... ] } // optional_tip_hex may be empty to query at the current tip. Note IDs round-trip // into channel_deposit_with_notes. - std::string wallet_get_notes( + [[nodiscard]] StdLogosResult wallet_get_notes( const std::string& wallet_address_hex, const std::string& optional_tip_hex - ); - std::string leader_claim(); - std::string wallet_get_claimable_vouchers(); + ) const; + [[nodiscard]] StdLogosResult leader_claim() const; + [[nodiscard]] StdLogosResult wallet_get_claimable_vouchers() const; // Channel // Amount-based deposit: the binding selects funding notes itself (splitting a @@ -92,19 +98,19 @@ public: // exactly `amount`. funding_public_key owns the funding notes, the deposit note // and any change. metadata_hex may be empty; optional_tip_hex may be empty to // build against the current tip. Returns the transaction hash hex on success. - std::string channel_deposit( + [[nodiscard]] StdLogosResult channel_deposit( const std::string& channel_id_hex, const std::string& funding_public_key_hex, const std::string& amount, const std::string& metadata_hex, const std::string& optional_tip_hex - ); + ) const; // Note-based deposit: the caller supplies the exact notes to consume (their // whole value enters the channel), so amount = sum of the notes' values. Use // wallet_get_notes to obtain note IDs. The gas fee is funded from // funding_public_keys (change to change_public_key), capped at max_tx_fee. // metadata_hex / optional_tip_hex may be empty. Returns the tx hash hex. - std::string channel_deposit_with_notes( + [[nodiscard]] StdLogosResult channel_deposit_with_notes( const std::string& channel_id_hex, const std::vector& input_note_id_hexes, const std::string& metadata_hex, @@ -112,28 +118,33 @@ public: const std::vector& funding_public_key_hexes, const std::string& max_tx_fee, const std::string& optional_tip_hex - ); + ) const; // Blend - std::string blend_join_as_core_node( + [[nodiscard]] StdLogosResult blend_join_as_core_node( const std::string& provider_id_hex, const std::string& zk_id_hex, const std::string& locked_note_id_hex, const std::vector& locators - ); + ) const; // Explorer - std::string get_block(const std::string& header_id_hex); - std::string get_blocks(uint64_t from_slot, uint64_t to_slot); - std::string get_transaction(const std::string& tx_hash_hex); + [[nodiscard]] StdLogosResult get_block(const std::string& header_id_hex) const; + [[nodiscard]] StdLogosResult get_blocks(uint64_t from_slot, uint64_t to_slot) const; + [[nodiscard]] StdLogosResult get_transaction(const std::string& tx_hash_hex) const; // Cryptarchia - std::string get_cryptarchia_info(); + [[nodiscard]] StdLogosResult get_cryptarchia_info() const; + // clang-format off +// Clang-format only handles public/private/protected, so it miss-indents this section. +// Guard kept until https://github.com/llvm/llvm-project/issues/64763 lands. logos_events: // Fired by on_new_block_callback when the Rust node delivers a new block. // blockJson is the full block serialized as JSON. + // ReSharper disable once CppFunctionIsNotImplemented void newBlock(const std::string& blockJson); + // clang-format on private: LogosBlockchainNode* node = nullptr; diff --git a/tests/test_blockchain.cpp b/tests/test_blockchain.cpp index 9444438..961d19c 100644 --- a/tests/test_blockchain.cpp +++ b/tests/test_blockchain.cpp @@ -17,10 +17,6 @@ namespace fs = std::filesystem; static const std::string VALID_HEX(64, 'a'); static const std::string VALID_HEX_WITH_PREFIX = "0x" + std::string(64, 'b'); -static bool starts_with(const std::string& s, const std::string& prefix) { - return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0; -} - static bool contains(const std::string& s, const std::string& sub) { return s.find(sub) != std::string::npos; } @@ -59,8 +55,8 @@ static LogosBlockchainModule* createStartedModule(LogosTestContext& t, TempDir& t.mockCFunction("start_lb_node").returns(1); t.mockCFunction("subscribe_to_new_blocks").returns(0); - std::string rc = module->start(tmpDir.filePath("config.json"), ""); - if (rc != "0") { + StdLogosResult rc = module->start(tmpDir.filePath("config.json"), ""); + if (!rc.success) { delete module; return nullptr; } @@ -77,7 +73,7 @@ LOGOS_TEST(generate_user_config_returns_0_on_success) { t.mockCFunction("generate_user_config").returns(0); - LOGOS_ASSERT_EQ(module.generate_user_config(R"({"output":"/tmp/test-config.json"})"), std::string("0")); + LOGOS_ASSERT_TRUE(module.generate_user_config(R"({"output":"/tmp/test-config.json"})").success); LOGOS_ASSERT(t.cFunctionCalled("generate_user_config")); } @@ -87,7 +83,7 @@ LOGOS_TEST(generate_user_config_returns_1_on_failure) { t.mockCFunction("generate_user_config").returns(1); - LOGOS_ASSERT_EQ(module.generate_user_config("{}"), std::string("1")); + LOGOS_ASSERT_FALSE(module.generate_user_config("{}").success); } LOGOS_TEST(generate_user_config_from_json_string) { @@ -96,7 +92,7 @@ LOGOS_TEST(generate_user_config_from_json_string) { t.mockCFunction("generate_user_config").returns(0); - LOGOS_ASSERT_EQ(module.generate_user_config(R"({"output":"/tmp/out.json"})"), std::string("0")); + LOGOS_ASSERT_TRUE(module.generate_user_config(R"({"output":"/tmp/out.json"})").success); LOGOS_ASSERT(t.cFunctionCalled("generate_user_config")); } @@ -119,7 +115,7 @@ LOGOS_TEST(generate_user_config_with_all_fields) { "kms_file": "/tmp/kms.yaml" })"; - LOGOS_ASSERT_EQ(module.generate_user_config(args), std::string("0")); + LOGOS_ASSERT_TRUE(module.generate_user_config(args).success); } // ============================================================================ @@ -129,94 +125,94 @@ LOGOS_TEST(generate_user_config_with_all_fields) { LOGOS_TEST(stop_without_node_returns_1) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - LOGOS_ASSERT_EQ(module.stop(), std::string("1")); + LOGOS_ASSERT_FALSE(module.stop().success); } LOGOS_TEST(wallet_get_balance_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.wallet_get_balance(VALID_HEX); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "not running")); + StdLogosResult result = module.wallet_get_balance(VALID_HEX); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "not running")); } LOGOS_TEST(wallet_transfer_funds_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "not running")); + StdLogosResult result = module.wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "not running")); } LOGOS_TEST(leader_claim_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.leader_claim(); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "not running")); + StdLogosResult result = module.leader_claim(); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "not running")); } LOGOS_TEST(channel_deposit_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.channel_deposit(VALID_HEX, VALID_HEX, "100", "", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "not running")); + StdLogosResult result = module.channel_deposit(VALID_HEX, VALID_HEX, "100", "", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "not running")); } LOGOS_TEST(channel_deposit_with_notes_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.channel_deposit_with_notes( + StdLogosResult result = module.channel_deposit_with_notes( VALID_HEX, {VALID_HEX}, "", VALID_HEX, {VALID_HEX}, "0", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "not running")); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "not running")); } LOGOS_TEST(wallet_get_notes_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.wallet_get_notes(VALID_HEX, ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "not running")); + StdLogosResult result = module.wallet_get_notes(VALID_HEX, ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "not running")); } -LOGOS_TEST(wallet_get_known_addresses_without_node_returns_empty) { +LOGOS_TEST(wallet_get_known_addresses_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - LOGOS_ASSERT_TRUE(module.wallet_get_known_addresses().empty()); + LOGOS_ASSERT_FALSE(module.wallet_get_known_addresses().success); } LOGOS_TEST(blend_join_as_core_node_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.blend_join_as_core_node(VALID_HEX, VALID_HEX, VALID_HEX, {"locator1"}); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "not running")); + StdLogosResult result = module.blend_join_as_core_node(VALID_HEX, VALID_HEX, VALID_HEX, {"locator1"}); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "not running")); } LOGOS_TEST(get_block_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - LOGOS_ASSERT_TRUE(starts_with(module.get_block(VALID_HEX), "Error:")); + LOGOS_ASSERT_FALSE(module.get_block(VALID_HEX).success); } LOGOS_TEST(get_blocks_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - LOGOS_ASSERT_TRUE(starts_with(module.get_blocks(0, 10), "Error:")); + LOGOS_ASSERT_FALSE(module.get_blocks(0, 10).success); } LOGOS_TEST(get_transaction_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - LOGOS_ASSERT_TRUE(starts_with(module.get_transaction(VALID_HEX), "Error:")); + LOGOS_ASSERT_FALSE(module.get_transaction(VALID_HEX).success); } LOGOS_TEST(get_cryptarchia_info_without_node_returns_error) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - LOGOS_ASSERT_TRUE(starts_with(module.get_cryptarchia_info(), "Error:")); + LOGOS_ASSERT_FALSE(module.get_cryptarchia_info().success); } // ============================================================================ @@ -241,7 +237,7 @@ LOGOS_TEST(start_returns_1_when_already_running) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - LOGOS_ASSERT_EQ(module->start("/tmp/config.json", ""), std::string("1")); + LOGOS_ASSERT_FALSE(module->start("/tmp/config.json", "").success); delete module; } @@ -251,7 +247,7 @@ LOGOS_TEST(stop_succeeds_with_running_node) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - LOGOS_ASSERT_EQ(module->stop(), std::string("0")); + LOGOS_ASSERT_TRUE(module->stop().success); LOGOS_ASSERT(t.cFunctionCalled("stop_node")); delete module; } @@ -268,9 +264,9 @@ LOGOS_TEST(wallet_get_balance_rejects_short_hex) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_get_balance("abcd"); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "64 hex")); + StdLogosResult result = module->wallet_get_balance("abcd"); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "64 hex")); delete module; } @@ -280,8 +276,8 @@ LOGOS_TEST(wallet_get_balance_rejects_long_hex) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_get_balance(std::string(66, 'a')); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module->wallet_get_balance(std::string(66, 'a')); + LOGOS_ASSERT_FALSE(result.success); delete module; } @@ -292,8 +288,8 @@ LOGOS_TEST(wallet_get_balance_rejects_invalid_chars) { LOGOS_ASSERT_TRUE(module != nullptr); std::string hex = std::string(62, 'a') + "zz"; - std::string result = module->wallet_get_balance(hex); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module->wallet_get_balance(hex); + LOGOS_ASSERT_FALSE(result.success); delete module; } @@ -305,9 +301,9 @@ LOGOS_TEST(wallet_transfer_funds_rejects_invalid_amount) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "not_a_number", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "Invalid amount")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "not_a_number", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "Invalid amount")); delete module; } @@ -317,9 +313,9 @@ LOGOS_TEST(wallet_transfer_funds_rejects_invalid_change_key) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_transfer_funds("bad", {VALID_HEX}, VALID_HEX, "100", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "change_public_key")); + StdLogosResult result = module->wallet_transfer_funds("bad", {VALID_HEX}, VALID_HEX, "100", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "change_public_key")); delete module; } @@ -329,9 +325,9 @@ LOGOS_TEST(wallet_transfer_funds_rejects_invalid_recipient) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, "short", "100", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "recipient_address")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, "short", "100", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "recipient_address")); delete module; } @@ -341,9 +337,9 @@ LOGOS_TEST(wallet_transfer_funds_rejects_empty_senders) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_transfer_funds(VALID_HEX, {}, VALID_HEX, "100", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "sender")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {}, VALID_HEX, "100", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "sender")); delete module; } @@ -353,9 +349,9 @@ LOGOS_TEST(wallet_transfer_funds_rejects_invalid_sender_address) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_transfer_funds(VALID_HEX, {"bad_addr"}, VALID_HEX, "100", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "sender")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {"bad_addr"}, VALID_HEX, "100", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "sender")); delete module; } @@ -365,9 +361,9 @@ LOGOS_TEST(wallet_transfer_funds_rejects_invalid_optional_tip) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", "bad_tip"); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "tip")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", "bad_tip"); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "tip")); delete module; } @@ -379,9 +375,9 @@ LOGOS_TEST(blend_join_rejects_invalid_provider_id) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->blend_join_as_core_node("short", VALID_HEX, VALID_HEX, {}); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "provider_id")); + StdLogosResult result = module->blend_join_as_core_node("short", VALID_HEX, VALID_HEX, {}); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "provider_id")); delete module; } @@ -391,9 +387,9 @@ LOGOS_TEST(blend_join_rejects_invalid_zk_id) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->blend_join_as_core_node(VALID_HEX, "short", VALID_HEX, {}); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "zk_id")); + StdLogosResult result = module->blend_join_as_core_node(VALID_HEX, "short", VALID_HEX, {}); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "zk_id")); delete module; } @@ -403,9 +399,9 @@ LOGOS_TEST(blend_join_rejects_invalid_locked_note_id) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->blend_join_as_core_node(VALID_HEX, VALID_HEX, "short", {}); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "locked_note_id")); + StdLogosResult result = module->blend_join_as_core_node(VALID_HEX, VALID_HEX, "short", {}); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "locked_note_id")); delete module; } @@ -417,9 +413,9 @@ LOGOS_TEST(get_block_rejects_invalid_hex) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->get_block("tooshort"); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "64 hex")); + StdLogosResult result = module->get_block("tooshort"); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "64 hex")); delete module; } @@ -429,9 +425,9 @@ LOGOS_TEST(get_transaction_rejects_invalid_hex) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->get_transaction("bad"); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "64 hex")); + StdLogosResult result = module->get_transaction("bad"); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "64 hex")); delete module; } @@ -448,8 +444,9 @@ LOGOS_TEST(wallet_get_balance_accepts_0x_prefix) { t.mockCFunction("get_balance_value").returns(42); t.mockCFunction("get_balance_error").returns(0); - std::string result = module->wallet_get_balance(VALID_HEX_WITH_PREFIX); - LOGOS_ASSERT_EQ(result, std::string("42")); + StdLogosResult result = module->wallet_get_balance(VALID_HEX_WITH_PREFIX); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_EQ(result.value.get(), std::string("42")); delete module; } @@ -468,8 +465,9 @@ LOGOS_TEST(wallet_get_balance_returns_balance_string) { t.mockCFunction("get_balance_value").returns(1000); t.mockCFunction("get_balance_error").returns(0); - std::string result = module->wallet_get_balance(VALID_HEX); - LOGOS_ASSERT_EQ(result, std::string("1000")); + StdLogosResult result = module->wallet_get_balance(VALID_HEX); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_EQ(result.value.get(), std::string("1000")); LOGOS_ASSERT(t.cFunctionCalled("get_balance")); delete module; } @@ -482,8 +480,8 @@ LOGOS_TEST(wallet_get_balance_returns_error_on_ffi_failure) { t.mockCFunction("get_balance_error").returns(1); - std::string result = module->wallet_get_balance(VALID_HEX); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module->wallet_get_balance(VALID_HEX); + LOGOS_ASSERT_FALSE(result.success); delete module; } @@ -495,10 +493,11 @@ LOGOS_TEST(wallet_transfer_funds_returns_tx_hash) { t.mockCFunction("transfer_funds_error").returns(0); - std::string result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "500", ""); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_EQ(static_cast(result.length()), 64); - LOGOS_ASSERT_TRUE(starts_with(result, "ab")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "500", ""); + LOGOS_ASSERT_TRUE(result.success); + std::string hash = result.value.get(); + LOGOS_ASSERT_EQ(static_cast(hash.length()), 64); + LOGOS_ASSERT_TRUE(contains(hash.substr(0, 2), "ab")); LOGOS_ASSERT(t.cFunctionCalled("transfer_funds")); delete module; } @@ -511,9 +510,9 @@ LOGOS_TEST(wallet_transfer_funds_with_optional_tip) { t.mockCFunction("transfer_funds_error").returns(0); - std::string result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", VALID_HEX); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_EQ(static_cast(result.length()), 64); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", VALID_HEX); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_EQ(static_cast(result.value.get().length()), 64); delete module; } @@ -525,8 +524,8 @@ LOGOS_TEST(wallet_transfer_funds_returns_error_on_ffi_failure) { t.mockCFunction("transfer_funds_error").returns(1); - std::string result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", ""); + LOGOS_ASSERT_FALSE(result.success); delete module; } @@ -538,10 +537,11 @@ LOGOS_TEST(leader_claim_returns_tx_hash) { t.mockCFunction("leader_claim_error").returns(0); - std::string result = module->leader_claim(); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_EQ(static_cast(result.length()), 64); - LOGOS_ASSERT_TRUE(starts_with(result, "ef")); + StdLogosResult result = module->leader_claim(); + LOGOS_ASSERT_TRUE(result.success); + std::string hash = result.value.get(); + LOGOS_ASSERT_EQ(static_cast(hash.length()), 64); + LOGOS_ASSERT_TRUE(hash.substr(0, 2) == "ef"); LOGOS_ASSERT(t.cFunctionCalled("leader_claim")); delete module; } @@ -554,9 +554,9 @@ LOGOS_TEST(leader_claim_returns_error_on_ffi_failure) { t.mockCFunction("leader_claim_error").returns(1); - std::string result = module->leader_claim(); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "Failed to claim leader rewards")); + StdLogosResult result = module->leader_claim(); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "Failed to claim leader rewards")); delete module; } @@ -568,10 +568,11 @@ LOGOS_TEST(channel_deposit_returns_tx_hash) { t.mockCFunction("channel_deposit_error").returns(0); - std::string result = module->channel_deposit(VALID_HEX, VALID_HEX, "500", "", ""); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_EQ(static_cast(result.length()), 64); - LOGOS_ASSERT_TRUE(starts_with(result, "bc")); + StdLogosResult result = module->channel_deposit(VALID_HEX, VALID_HEX, "500", "", ""); + LOGOS_ASSERT_TRUE(result.success); + std::string hash = result.value.get(); + LOGOS_ASSERT_EQ(static_cast(hash.length()), 64); + LOGOS_ASSERT_TRUE(hash.substr(0, 2) == "bc"); LOGOS_ASSERT(t.cFunctionCalled("channel_deposit")); delete module; } @@ -584,9 +585,9 @@ LOGOS_TEST(channel_deposit_with_metadata_and_tip) { t.mockCFunction("channel_deposit_error").returns(0); - std::string result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "deadbeef", VALID_HEX); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_EQ(static_cast(result.length()), 64); + StdLogosResult result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "deadbeef", VALID_HEX); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_EQ(static_cast(result.value.get().length()), 64); delete module; } @@ -598,9 +599,9 @@ LOGOS_TEST(channel_deposit_returns_error_on_ffi_failure) { t.mockCFunction("channel_deposit_error").returns(1); - std::string result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "Failed to deposit into channel")); + StdLogosResult result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "Failed to deposit into channel")); delete module; } @@ -610,9 +611,9 @@ LOGOS_TEST(channel_deposit_rejects_invalid_amount) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit(VALID_HEX, VALID_HEX, "not_a_number", "", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "amount")); + StdLogosResult result = module->channel_deposit(VALID_HEX, VALID_HEX, "not_a_number", "", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "amount")); delete module; } @@ -622,9 +623,9 @@ LOGOS_TEST(channel_deposit_rejects_zero_amount) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit(VALID_HEX, VALID_HEX, "0", "", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "amount")); + StdLogosResult result = module->channel_deposit(VALID_HEX, VALID_HEX, "0", "", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "amount")); delete module; } @@ -634,9 +635,9 @@ LOGOS_TEST(channel_deposit_rejects_invalid_channel_id) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit("bad", VALID_HEX, "100", "", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "channel_id")); + StdLogosResult result = module->channel_deposit("bad", VALID_HEX, "100", "", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "channel_id")); delete module; } @@ -646,9 +647,9 @@ LOGOS_TEST(channel_deposit_rejects_invalid_funding_key) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit(VALID_HEX, "short", "100", "", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "funding_public_key")); + StdLogosResult result = module->channel_deposit(VALID_HEX, "short", "100", "", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "funding_public_key")); delete module; } @@ -658,9 +659,9 @@ LOGOS_TEST(channel_deposit_rejects_invalid_metadata) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "xyz", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "metadata")); + StdLogosResult result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "xyz", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "metadata")); delete module; } @@ -670,9 +671,9 @@ LOGOS_TEST(channel_deposit_rejects_invalid_optional_tip) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "", "bad_tip"); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "tip")); + StdLogosResult result = module->channel_deposit(VALID_HEX, VALID_HEX, "100", "", "bad_tip"); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "tip")); delete module; } @@ -685,12 +686,13 @@ LOGOS_TEST(wallet_get_notes_returns_json_on_success) { t.mockCFunction("get_wallet_notes_error").returns(0); t.mockCFunction("get_wallet_notes_count").returns(2); - std::string result = module->wallet_get_notes(VALID_HEX, ""); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "\"tip\"")); - LOGOS_ASSERT_TRUE(contains(result, "\"notes\"")); - LOGOS_ASSERT_TRUE(contains(result, "\"value\":\"100\"")); - LOGOS_ASSERT_TRUE(contains(result, "\"value\":\"200\"")); + StdLogosResult result = module->wallet_get_notes(VALID_HEX, ""); + LOGOS_ASSERT_TRUE(result.success); + std::string json = result.value.get(); + LOGOS_ASSERT_TRUE(contains(json, "\"tip\"")); + LOGOS_ASSERT_TRUE(contains(json, "\"notes\"")); + LOGOS_ASSERT_TRUE(contains(json, "\"value\":\"100\"")); + LOGOS_ASSERT_TRUE(contains(json, "\"value\":\"200\"")); LOGOS_ASSERT(t.cFunctionCalled("get_wallet_notes")); LOGOS_ASSERT(t.cFunctionCalled("free_wallet_notes")); delete module; @@ -705,9 +707,9 @@ LOGOS_TEST(wallet_get_notes_returns_empty_notes_array) { t.mockCFunction("get_wallet_notes_error").returns(0); t.mockCFunction("get_wallet_notes_count").returns(0); - std::string result = module->wallet_get_notes(VALID_HEX, ""); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "\"notes\":[]")); + StdLogosResult result = module->wallet_get_notes(VALID_HEX, ""); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_TRUE(contains(result.value.get(), "\"notes\":[]")); delete module; } @@ -719,9 +721,9 @@ LOGOS_TEST(wallet_get_notes_returns_error_on_ffi_failure) { t.mockCFunction("get_wallet_notes_error").returns(1); - std::string result = module->wallet_get_notes(VALID_HEX, ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "Failed to get wallet notes")); + StdLogosResult result = module->wallet_get_notes(VALID_HEX, ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "Failed to get wallet notes")); delete module; } @@ -731,9 +733,9 @@ LOGOS_TEST(wallet_get_notes_rejects_invalid_address) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->wallet_get_notes("bad", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "wallet address")); + StdLogosResult result = module->wallet_get_notes("bad", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "wallet address")); delete module; } @@ -745,11 +747,12 @@ LOGOS_TEST(channel_deposit_with_notes_returns_tx_hash) { t.mockCFunction("channel_deposit_with_notes_error").returns(0); - std::string result = module->channel_deposit_with_notes( + StdLogosResult result = module->channel_deposit_with_notes( VALID_HEX, {VALID_HEX}, "deadbeef", VALID_HEX, {VALID_HEX}, "1000", ""); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_EQ(static_cast(result.length()), 64); - LOGOS_ASSERT_TRUE(starts_with(result, "de")); + LOGOS_ASSERT_TRUE(result.success); + std::string hash = result.value.get(); + LOGOS_ASSERT_EQ(static_cast(hash.length()), 64); + LOGOS_ASSERT_TRUE(hash.substr(0, 2) == "de"); LOGOS_ASSERT(t.cFunctionCalled("channel_deposit_with_notes")); delete module; } @@ -762,10 +765,10 @@ LOGOS_TEST(channel_deposit_with_notes_returns_error_on_ffi_failure) { t.mockCFunction("channel_deposit_with_notes_error").returns(1); - std::string result = module->channel_deposit_with_notes( + StdLogosResult result = module->channel_deposit_with_notes( VALID_HEX, {VALID_HEX}, "", VALID_HEX, {VALID_HEX}, "0", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "Failed to deposit into channel")); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "Failed to deposit into channel")); delete module; } @@ -775,10 +778,10 @@ LOGOS_TEST(channel_deposit_with_notes_rejects_empty_notes) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit_with_notes( + StdLogosResult result = module->channel_deposit_with_notes( VALID_HEX, {}, "", VALID_HEX, {VALID_HEX}, "0", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "input note")); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "input note")); delete module; } @@ -788,10 +791,10 @@ LOGOS_TEST(channel_deposit_with_notes_rejects_invalid_note_id) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit_with_notes( + StdLogosResult result = module->channel_deposit_with_notes( VALID_HEX, {"bad"}, "", VALID_HEX, {VALID_HEX}, "0", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "input note id")); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "input note id")); delete module; } @@ -801,10 +804,10 @@ LOGOS_TEST(channel_deposit_with_notes_rejects_empty_funding_keys) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit_with_notes( + StdLogosResult result = module->channel_deposit_with_notes( VALID_HEX, {VALID_HEX}, "", VALID_HEX, {}, "0", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "funding public key")); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "funding public key")); delete module; } @@ -814,10 +817,10 @@ LOGOS_TEST(channel_deposit_with_notes_rejects_invalid_max_tx_fee) { auto* module = createStartedModule(t, tmpDir); LOGOS_ASSERT_TRUE(module != nullptr); - std::string result = module->channel_deposit_with_notes( + StdLogosResult result = module->channel_deposit_with_notes( VALID_HEX, {VALID_HEX}, "", VALID_HEX, {VALID_HEX}, "not_a_number", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "max_tx_fee")); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "max_tx_fee")); delete module; } @@ -829,8 +832,8 @@ LOGOS_TEST(wallet_transfer_funds_single_sender_via_vector) { t.mockCFunction("transfer_funds_error").returns(0); - std::string result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", ""); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, {VALID_HEX}, VALID_HEX, "100", ""); + LOGOS_ASSERT_TRUE(result.success); LOGOS_ASSERT(t.cFunctionCalled("transfer_funds")); delete module; } @@ -844,8 +847,8 @@ LOGOS_TEST(wallet_transfer_funds_multiple_senders) { t.mockCFunction("transfer_funds_error").returns(0); std::vector senders = {VALID_HEX, std::string(64, 'b')}; - std::string result = module->wallet_transfer_funds(VALID_HEX, senders, VALID_HEX, "200", ""); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); + StdLogosResult result = module->wallet_transfer_funds(VALID_HEX, senders, VALID_HEX, "200", ""); + LOGOS_ASSERT_TRUE(result.success); delete module; } @@ -858,17 +861,18 @@ LOGOS_TEST(wallet_get_known_addresses_returns_addresses) { t.mockCFunction("get_known_addresses_error").returns(0); t.mockCFunction("get_known_addresses_count").returns(2); - std::vector addrs = module->wallet_get_known_addresses(); - LOGOS_ASSERT_EQ(static_cast(addrs.size()), 2); + StdLogosResult result = module->wallet_get_known_addresses(); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_EQ(static_cast(result.value.size()), 2); // Mock fills addr0 with 0x11 -> hex "1111...11", addr1 with 0x22 -> "2222...22" - LOGOS_ASSERT_EQ(addrs[0], std::string(64, '1')); - LOGOS_ASSERT_EQ(addrs[1], std::string(64, '2')); + LOGOS_ASSERT_EQ(result.value[0].get(), std::string(64, '1')); + LOGOS_ASSERT_EQ(result.value[1].get(), std::string(64, '2')); LOGOS_ASSERT(t.cFunctionCalled("get_known_addresses")); LOGOS_ASSERT(t.cFunctionCalled("free_known_addresses")); delete module; } -LOGOS_TEST(wallet_get_known_addresses_returns_empty_on_ffi_failure) { +LOGOS_TEST(wallet_get_known_addresses_returns_error_on_ffi_failure) { auto t = LogosTestContext("blockchain_module"); TempDir tmpDir; auto* module = createStartedModule(t, tmpDir); @@ -876,8 +880,8 @@ LOGOS_TEST(wallet_get_known_addresses_returns_empty_on_ffi_failure) { t.mockCFunction("get_known_addresses_error").returns(1); - std::vector addrs = module->wallet_get_known_addresses(); - LOGOS_ASSERT_TRUE(addrs.empty()); + StdLogosResult result = module->wallet_get_known_addresses(); + LOGOS_ASSERT_FALSE(result.success); delete module; } @@ -890,12 +894,13 @@ LOGOS_TEST(wallet_get_claimable_vouchers_returns_json) { t.mockCFunction("get_claimable_vouchers_error").returns(0); t.mockCFunction("get_claimable_vouchers_count").returns(2); - std::string result = module->wallet_get_claimable_vouchers(); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "\"vouchers\"")); - LOGOS_ASSERT_TRUE(contains(result, std::string(64, 'a'))); - LOGOS_ASSERT_TRUE(contains(result, std::string(64, '1'))); - LOGOS_ASSERT_TRUE(contains(result, "20202020")); + StdLogosResult result = module->wallet_get_claimable_vouchers(); + LOGOS_ASSERT_TRUE(result.success); + std::string json = result.value.get(); + LOGOS_ASSERT_TRUE(contains(json, "\"vouchers\"")); + LOGOS_ASSERT_TRUE(contains(json, std::string(64, 'a'))); + LOGOS_ASSERT_TRUE(contains(json, std::string(64, '1'))); + LOGOS_ASSERT_TRUE(contains(json, "20202020")); LOGOS_ASSERT(t.cFunctionCalled("get_claimable_vouchers")); LOGOS_ASSERT(t.cFunctionCalled("free_claimable_vouchers")); delete module; @@ -909,9 +914,9 @@ LOGOS_TEST(wallet_get_claimable_vouchers_returns_error_on_ffi_failure) { t.mockCFunction("get_claimable_vouchers_error").returns(1); - std::string result = module->wallet_get_claimable_vouchers(); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "Failed to get claimable vouchers")); + StdLogosResult result = module->wallet_get_claimable_vouchers(); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "Failed to get claimable vouchers")); delete module; } @@ -926,10 +931,12 @@ LOGOS_TEST(blend_join_as_core_node_returns_declaration_id) { t.mockCFunction("blend_join_as_core_node_error").returns(0); std::vector locators = {"locator1", "locator2"}; - std::string result = module->blend_join_as_core_node(VALID_HEX, VALID_HEX, VALID_HEX, locators); + StdLogosResult result = module->blend_join_as_core_node(VALID_HEX, VALID_HEX, VALID_HEX, locators); + LOGOS_ASSERT_TRUE(result.success); // Mock fills hash with 0xCD -> hex "cdcd...cd" (64 chars) - LOGOS_ASSERT_EQ(static_cast(result.length()), 64); - LOGOS_ASSERT_TRUE(starts_with(result, "cd")); + std::string declarationId = result.value.get(); + LOGOS_ASSERT_EQ(static_cast(declarationId.length()), 64); + LOGOS_ASSERT_TRUE(declarationId.substr(0, 2) == "cd"); LOGOS_ASSERT(t.cFunctionCalled("blend_join_as_core_node")); delete module; } @@ -942,8 +949,8 @@ LOGOS_TEST(blend_join_as_core_node_returns_error_on_ffi_failure) { t.mockCFunction("blend_join_as_core_node_error").returns(1); - std::string result = module->blend_join_as_core_node(VALID_HEX, VALID_HEX, VALID_HEX, {}); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module->blend_join_as_core_node(VALID_HEX, VALID_HEX, VALID_HEX, {}); + LOGOS_ASSERT_FALSE(result.success); delete module; } @@ -958,9 +965,11 @@ LOGOS_TEST(get_block_returns_json_on_success) { t.mockCFunction("get_block").returns(R"({"slot":42,"data":"test"})"); t.mockCFunction("get_block_error").returns(0); - std::string result = module->get_block(VALID_HEX); - LOGOS_ASSERT_TRUE(contains(result, "slot")); - LOGOS_ASSERT_TRUE(contains(result, "42")); + StdLogosResult result = module->get_block(VALID_HEX); + LOGOS_ASSERT_TRUE(result.success); + std::string json = result.value.get(); + LOGOS_ASSERT_TRUE(contains(json, "slot")); + LOGOS_ASSERT_TRUE(contains(json, "42")); LOGOS_ASSERT(t.cFunctionCalled("get_block")); LOGOS_ASSERT(t.cFunctionCalled("free_cstring")); delete module; @@ -974,8 +983,8 @@ LOGOS_TEST(get_block_returns_error_on_ffi_failure) { t.mockCFunction("get_block_error").returns(1); - std::string result = module->get_block(VALID_HEX); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module->get_block(VALID_HEX); + LOGOS_ASSERT_FALSE(result.success); delete module; } @@ -988,8 +997,9 @@ LOGOS_TEST(get_blocks_returns_json_on_success) { t.mockCFunction("get_blocks").returns(R"([{"slot":1},{"slot":2}])"); t.mockCFunction("get_blocks_error").returns(0); - std::string result = module->get_blocks(1, 10); - LOGOS_ASSERT_TRUE(contains(result, "slot")); + StdLogosResult result = module->get_blocks(1, 10); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_TRUE(contains(result.value.get(), "slot")); LOGOS_ASSERT(t.cFunctionCalled("get_blocks")); delete module; } @@ -1002,7 +1012,7 @@ LOGOS_TEST(get_blocks_returns_error_on_ffi_failure) { t.mockCFunction("get_blocks_error").returns(1); - LOGOS_ASSERT_TRUE(starts_with(module->get_blocks(0, 10), "Error:")); + LOGOS_ASSERT_FALSE(module->get_blocks(0, 10).success); delete module; } @@ -1015,8 +1025,9 @@ LOGOS_TEST(get_transaction_returns_json_on_success) { t.mockCFunction("get_transaction").returns(R"({"hash":"abc","status":"confirmed"})"); t.mockCFunction("get_transaction_error").returns(0); - std::string result = module->get_transaction(VALID_HEX); - LOGOS_ASSERT_TRUE(contains(result, "confirmed")); + StdLogosResult result = module->get_transaction(VALID_HEX); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_TRUE(contains(result.value.get(), "confirmed")); LOGOS_ASSERT(t.cFunctionCalled("get_transaction")); LOGOS_ASSERT(t.cFunctionCalled("free_cstring")); delete module; @@ -1030,7 +1041,7 @@ LOGOS_TEST(get_transaction_returns_error_on_ffi_failure) { t.mockCFunction("get_transaction_error").returns(1); - LOGOS_ASSERT_TRUE(starts_with(module->get_transaction(VALID_HEX), "Error:")); + LOGOS_ASSERT_FALSE(module->get_transaction(VALID_HEX).success); delete module; } @@ -1047,15 +1058,16 @@ LOGOS_TEST(get_cryptarchia_info_returns_json_on_success) { t.mockCFunction("cryptarchia_height").returns(50); t.mockCFunction("cryptarchia_mode").returns(1); // Online - std::string result = module->get_cryptarchia_info(); - LOGOS_ASSERT_FALSE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "slot")); - LOGOS_ASSERT_TRUE(contains(result, "100")); - LOGOS_ASSERT_TRUE(contains(result, "height")); - LOGOS_ASSERT_TRUE(contains(result, "50")); - LOGOS_ASSERT_TRUE(contains(result, "Online")); - LOGOS_ASSERT_TRUE(contains(result, "lib")); - LOGOS_ASSERT_TRUE(contains(result, "tip")); + StdLogosResult result = module->get_cryptarchia_info(); + LOGOS_ASSERT_TRUE(result.success); + std::string json = result.value.get(); + LOGOS_ASSERT_TRUE(contains(json, "slot")); + LOGOS_ASSERT_TRUE(contains(json, "100")); + LOGOS_ASSERT_TRUE(contains(json, "height")); + LOGOS_ASSERT_TRUE(contains(json, "50")); + LOGOS_ASSERT_TRUE(contains(json, "Online")); + LOGOS_ASSERT_TRUE(contains(json, "lib")); + LOGOS_ASSERT_TRUE(contains(json, "tip")); LOGOS_ASSERT(t.cFunctionCalled("get_cryptarchia_info")); LOGOS_ASSERT(t.cFunctionCalled("free_cryptarchia_info")); delete module; @@ -1070,8 +1082,8 @@ LOGOS_TEST(get_cryptarchia_info_bootstrapping_mode) { t.mockCFunction("get_cryptarchia_info_error").returns(0); t.mockCFunction("cryptarchia_mode").returns(0); // Bootstrapping - std::string result = module->get_cryptarchia_info(); - LOGOS_ASSERT_TRUE(contains(result, "Bootstrapping")); + StdLogosResult result = module->get_cryptarchia_info(); + LOGOS_ASSERT_TRUE(contains(result.value.get(), "Bootstrapping")); delete module; } @@ -1083,7 +1095,7 @@ LOGOS_TEST(get_cryptarchia_info_returns_error_on_ffi_failure) { t.mockCFunction("get_cryptarchia_info_error").returns(1); - LOGOS_ASSERT_TRUE(starts_with(module->get_cryptarchia_info(), "Error:")); + LOGOS_ASSERT_FALSE(module->get_cryptarchia_info().success); delete module; } @@ -1097,7 +1109,7 @@ LOGOS_TEST(update_user_config_returns_0_on_success) { t.mockCFunction("update_user_config").returns(0); - LOGOS_ASSERT_EQ(module.update_user_config("/tmp/config.yaml", "/tmp/keystore.yaml"), std::string("0")); + LOGOS_ASSERT_TRUE(module.update_user_config("/tmp/config.yaml", "/tmp/keystore.yaml").success); LOGOS_ASSERT(t.cFunctionCalled("update_user_config")); } @@ -1107,7 +1119,7 @@ LOGOS_TEST(update_user_config_returns_1_on_failure) { t.mockCFunction("update_user_config").returns(1); - LOGOS_ASSERT_EQ(module.update_user_config("/tmp/config.yaml", "/tmp/keystore.yaml"), std::string("1")); + LOGOS_ASSERT_FALSE(module.update_user_config("/tmp/config.yaml", "/tmp/keystore.yaml").success); } LOGOS_TEST(migrate_user_config_returns_0_on_success) { @@ -1116,7 +1128,7 @@ LOGOS_TEST(migrate_user_config_returns_0_on_success) { t.mockCFunction("migrate_user_config").returns(0); - LOGOS_ASSERT_EQ(module.migrate_user_config("/tmp/out.yaml", "/tmp/keystore.yaml"), std::string("0")); + LOGOS_ASSERT_TRUE(module.migrate_user_config("/tmp/out.yaml", "/tmp/keystore.yaml").success); LOGOS_ASSERT(t.cFunctionCalled("migrate_user_config")); } @@ -1126,7 +1138,7 @@ LOGOS_TEST(migrate_user_config_returns_1_on_failure) { t.mockCFunction("migrate_user_config").returns(1); - LOGOS_ASSERT_EQ(module.migrate_user_config("/tmp/out.yaml", "/tmp/keystore.yaml"), std::string("1")); + LOGOS_ASSERT_FALSE(module.migrate_user_config("/tmp/out.yaml", "/tmp/keystore.yaml").success); } LOGOS_TEST(migrate_user_config_0_1_2_returns_0_on_success) { @@ -1135,7 +1147,7 @@ LOGOS_TEST(migrate_user_config_0_1_2_returns_0_on_success) { t.mockCFunction("migrate_user_config_0_1_2").returns(0); - LOGOS_ASSERT_EQ(module.migrate_user_config_0_1_2("/tmp/new.yaml", "/tmp/old.yaml", "/tmp/keystore.yaml"), std::string("0")); + LOGOS_ASSERT_TRUE(module.migrate_user_config_0_1_2("/tmp/new.yaml", "/tmp/old.yaml", "/tmp/keystore.yaml").success); LOGOS_ASSERT(t.cFunctionCalled("migrate_user_config_0_1_2")); } @@ -1145,7 +1157,7 @@ LOGOS_TEST(migrate_user_config_0_1_2_returns_1_on_failure) { t.mockCFunction("migrate_user_config_0_1_2").returns(1); - LOGOS_ASSERT_EQ(module.migrate_user_config_0_1_2("/tmp/new.yaml", "/tmp/old.yaml", "/tmp/keystore.yaml"), std::string("1")); + LOGOS_ASSERT_FALSE(module.migrate_user_config_0_1_2("/tmp/new.yaml", "/tmp/old.yaml", "/tmp/keystore.yaml").success); } LOGOS_TEST(participate_returns_0_on_success) { @@ -1154,7 +1166,7 @@ LOGOS_TEST(participate_returns_0_on_success) { t.mockCFunction("participate").returns(0); - LOGOS_ASSERT_EQ(module.participate("/tmp/config.yaml", "/tmp/keystore.yaml", "/tmp/out", ""), std::string("0")); + LOGOS_ASSERT_TRUE(module.participate("/tmp/config.yaml", "/tmp/keystore.yaml", "/tmp/out", "").success); LOGOS_ASSERT(t.cFunctionCalled("participate")); } @@ -1164,7 +1176,7 @@ LOGOS_TEST(participate_returns_1_on_failure) { t.mockCFunction("participate").returns(1); - LOGOS_ASSERT_EQ(module.participate("/tmp/config.yaml", "/tmp/keystore.yaml", "/tmp/out", "1.2.3.4"), std::string("1")); + LOGOS_ASSERT_FALSE(module.participate("/tmp/config.yaml", "/tmp/keystore.yaml", "/tmp/out", "1.2.3.4").success); } // ============================================================================ @@ -1178,8 +1190,9 @@ LOGOS_TEST(generate_key_returns_id_on_success) { t.mockCFunction("generate_key").returns("key-abc123"); t.mockCFunction("generate_key_error").returns(0); - std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", ""); - LOGOS_ASSERT_EQ(result, std::string("key-abc123")); + StdLogosResult result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", ""); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_EQ(result.value.get(), std::string("key-abc123")); LOGOS_ASSERT(t.cFunctionCalled("generate_key")); LOGOS_ASSERT(t.cFunctionCalled("free_cstring")); } @@ -1191,17 +1204,17 @@ LOGOS_TEST(generate_key_accepts_zk_type) { t.mockCFunction("generate_key").returns("zk-key"); t.mockCFunction("generate_key_error").returns(0); - std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ZK", "my-title"); - LOGOS_ASSERT_EQ(result, std::string("zk-key")); + StdLogosResult result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ZK", "my-title"); + LOGOS_ASSERT_EQ(result.value.get(), std::string("zk-key")); } LOGOS_TEST(generate_key_rejects_invalid_key_type) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "rsa", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); - LOGOS_ASSERT_TRUE(contains(result, "key_type")); + StdLogosResult result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "rsa", ""); + LOGOS_ASSERT_FALSE(result.success); + LOGOS_ASSERT_TRUE(contains(result.error, "key_type")); LOGOS_ASSERT_FALSE(t.cFunctionCalled("generate_key")); } @@ -1211,8 +1224,8 @@ LOGOS_TEST(generate_key_returns_error_on_ffi_failure) { t.mockCFunction("generate_key_error").returns(1); - std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", ""); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", ""); + LOGOS_ASSERT_FALSE(result.success); } LOGOS_TEST(add_key_returns_0_on_success) { @@ -1221,7 +1234,7 @@ LOGOS_TEST(add_key_returns_0_on_success) { t.mockCFunction("add_key").returns(0); - LOGOS_ASSERT_EQ(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", VALID_HEX, ""), std::string("0")); + LOGOS_ASSERT_TRUE(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", VALID_HEX, "").success); LOGOS_ASSERT(t.cFunctionCalled("add_key")); } @@ -1229,7 +1242,7 @@ LOGOS_TEST(add_key_rejects_invalid_key_type) { auto t = LogosTestContext("blockchain_module"); LogosBlockchainModule module; - LOGOS_ASSERT_EQ(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "bogus", VALID_HEX, ""), std::string("1")); + LOGOS_ASSERT_FALSE(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "bogus", VALID_HEX, "").success); LOGOS_ASSERT_FALSE(t.cFunctionCalled("add_key")); } @@ -1239,7 +1252,7 @@ LOGOS_TEST(add_key_returns_1_on_failure) { t.mockCFunction("add_key").returns(1); - LOGOS_ASSERT_EQ(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "zk", VALID_HEX, "title"), std::string("1")); + LOGOS_ASSERT_FALSE(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "zk", VALID_HEX, "title").success); } LOGOS_TEST(remove_key_returns_0_on_success) { @@ -1248,7 +1261,7 @@ LOGOS_TEST(remove_key_returns_0_on_success) { t.mockCFunction("remove_key").returns(0); - LOGOS_ASSERT_EQ(module.remove_key("/tmp/config.yaml", "/tmp/keystore.yaml", "my-key"), std::string("0")); + LOGOS_ASSERT_TRUE(module.remove_key("/tmp/config.yaml", "/tmp/keystore.yaml", "my-key").success); LOGOS_ASSERT(t.cFunctionCalled("remove_key")); } @@ -1258,7 +1271,7 @@ LOGOS_TEST(remove_key_returns_1_on_failure) { t.mockCFunction("remove_key").returns(1); - LOGOS_ASSERT_EQ(module.remove_key("/tmp/config.yaml", "/tmp/keystore.yaml", "my-key"), std::string("1")); + LOGOS_ASSERT_FALSE(module.remove_key("/tmp/config.yaml", "/tmp/keystore.yaml", "my-key").success); } // ============================================================================ @@ -1272,8 +1285,9 @@ LOGOS_TEST(get_peer_id_returns_id_on_success) { t.mockCFunction("get_peer_id").returns("12D3KooWPeerId"); t.mockCFunction("get_peer_id_error").returns(0); - std::string result = module.get_peer_id("/tmp/config.yaml"); - LOGOS_ASSERT_EQ(result, std::string("12D3KooWPeerId")); + StdLogosResult result = module.get_peer_id("/tmp/config.yaml"); + LOGOS_ASSERT_TRUE(result.success); + LOGOS_ASSERT_EQ(result.value.get(), std::string("12D3KooWPeerId")); LOGOS_ASSERT(t.cFunctionCalled("get_peer_id")); LOGOS_ASSERT(t.cFunctionCalled("free_cstring")); } @@ -1284,6 +1298,6 @@ LOGOS_TEST(get_peer_id_returns_error_on_ffi_failure) { t.mockCFunction("get_peer_id_error").returns(1); - std::string result = module.get_peer_id("/tmp/config.yaml"); - LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + StdLogosResult result = module.get_peer_id("/tmp/config.yaml"); + LOGOS_ASSERT_FALSE(result.success); }