diff --git a/flake.lock b/flake.lock index aecdf32..812f17b 100644 --- a/flake.lock +++ b/flake.lock @@ -2716,17 +2716,17 @@ "rust-rapidsnark": "rust-rapidsnark" }, "locked": { - "lastModified": 1785414238, - "narHash": "sha256-SNo5ZvmzB39HMgY+f0a93XoNA92e/T5Ki9Gho62COWY=", + "lastModified": 1785659324, + "narHash": "sha256-xtC9Qksgp3f3ZEDdhLuM0DegelFl4EW20jqSmhqHeHQ=", "owner": "logos-blockchain", "repo": "logos-execution-zone", - "rev": "f58cde1a2251c20cf418a561ad974a8c64ab2393", + "rev": "15144ddb8437f48cdce32908e122dbb7eea68399", "type": "github" }, "original": { "owner": "logos-blockchain", + "ref": "v0.2.1", "repo": "logos-execution-zone", - "rev": "f58cde1a2251c20cf418a561ad974a8c64ab2393", "type": "github" } }, diff --git a/flake.nix b/flake.nix index 529e992..0aeb25f 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,7 @@ inputs = { logos-module-builder.url = "github:logos-co/logos-module-builder"; nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx"; - logos-execution-zone.url = "github:logos-blockchain/logos-execution-zone?rev=f58cde1a2251c20cf418a561ad974a8c64ab2393"; + logos-execution-zone.url = "github:logos-blockchain/logos-execution-zone?ref=v0.2.1"; }; outputs = inputs@{ logos-module-builder, ... }: diff --git a/src/lez_core_module.cpp b/src/lez_core_module.cpp index 11a6bc3..9e88398 100644 --- a/src/lez_core_module.cpp +++ b/src/lez_core_module.cpp @@ -212,28 +212,31 @@ bool jsonToFfiPrivateAccountKeys(const std::string& json, FfiPrivateAccountKeys* if (doc.is_discarded() || !doc.is_object()) return false; - if (doc.contains(JsonKeys::NullifierPublicKey) && doc[JsonKeys::NullifierPublicKey].is_string()) { - if (!hexToBytes32(doc[JsonKeys::NullifierPublicKey].get(), &output_keys->nullifier_public_key)) - return false; - } + // Nullifier public key is mandatory: a missing/wrong-typed value must not fall back to zero. + if (!doc.contains(JsonKeys::NullifierPublicKey) || !doc[JsonKeys::NullifierPublicKey].is_string()) + return false; + if (!hexToBytes32(doc[JsonKeys::NullifierPublicKey].get(), &output_keys->nullifier_public_key)) + return false; + + output_keys->viewing_public_key = nullptr; + output_keys->viewing_public_key_len = 0; + + if (doc.contains(JsonKeys::ViewingPublicKey)) { + if (!doc[JsonKeys::ViewingPublicKey].is_string()) + return false; - if (doc.contains(JsonKeys::ViewingPublicKey) && doc[JsonKeys::ViewingPublicKey].is_string()) { std::vector buffer; if (!hexToBytes(doc[JsonKeys::ViewingPublicKey].get(), buffer)) return false; - if (buffer.empty()) { - output_keys->viewing_public_key = nullptr; - output_keys->viewing_public_key_len = 0; - } else { + if (!buffer.empty()) { auto* data = static_cast(malloc(buffer.size())); + if (!data) + return false; memcpy(data, buffer.data(), buffer.size()); output_keys->viewing_public_key = data; output_keys->viewing_public_key_len = buffer.size(); } - } else { - output_keys->viewing_public_key = nullptr; - output_keys->viewing_public_key_len = 0; } return true; diff --git a/tests/test_lez_core.cpp b/tests/test_lez_core.cpp index bb072f1..3af98ef 100644 --- a/tests/test_lez_core.cpp +++ b/tests/test_lez_core.cpp @@ -288,6 +288,68 @@ LOGOS_TEST(transfer_shielded_invalid_keys_json_error) { LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_shielded")); } +// A missing nullifier_public_key must not succeed with a zero key. +LOGOS_TEST(transfer_shielded_missing_nullifier_key_error) { + auto t = LogosTestContext("logos_execution_zone"); + LEZCoreModule module; + + const nlohmann::json obj = parseObject(module.transfer_shielded(VALID_ID, "{}", VALID_U128)); + LOGOS_ASSERT_FALSE(obj["success"].get()); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_shielded")); +} + +LOGOS_TEST(transfer_shielded_misspelled_nullifier_key_error) { + auto t = LogosTestContext("logos_execution_zone"); + LEZCoreModule module; + + const std::string keysJson = std::string("{\"nullifierPublicKey\":\"") + std::string(64, 'a') + "\"}"; + const nlohmann::json obj = parseObject(module.transfer_shielded(VALID_ID, keysJson, VALID_U128)); + LOGOS_ASSERT_FALSE(obj["success"].get()); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_shielded")); +} + +LOGOS_TEST(transfer_shielded_abbreviated_nullifier_key_error) { + auto t = LogosTestContext("logos_execution_zone"); + LEZCoreModule module; + + const std::string keysJson = std::string("{\"nullifier_pubkey\":\"") + std::string(64, 'a') + "\"}"; + const nlohmann::json obj = parseObject(module.transfer_shielded(VALID_ID, keysJson, VALID_U128)); + LOGOS_ASSERT_FALSE(obj["success"].get()); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_shielded")); +} + +LOGOS_TEST(transfer_shielded_nullifier_key_omitted_viewing_key_only_error) { + auto t = LogosTestContext("logos_execution_zone"); + LEZCoreModule module; + + const std::string keysJson = std::string("{\"viewing_public_key\":\"") + std::string(64, 'a') + "\"}"; + const nlohmann::json obj = parseObject(module.transfer_shielded(VALID_ID, keysJson, VALID_U128)); + LOGOS_ASSERT_FALSE(obj["success"].get()); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_shielded")); +} + +LOGOS_TEST(transfer_shielded_nullifier_key_wrong_type_error) { + auto t = LogosTestContext("logos_execution_zone"); + LEZCoreModule module; + + const std::string keysJson = "{\"nullifier_public_key\": 12345}"; + const nlohmann::json obj = parseObject(module.transfer_shielded(VALID_ID, keysJson, VALID_U128)); + LOGOS_ASSERT_FALSE(obj["success"].get()); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_shielded")); +} + +// A non-string viewing_public_key must be rejected, not treated as absent. +LOGOS_TEST(transfer_shielded_viewing_key_wrong_type_error) { + auto t = LogosTestContext("logos_execution_zone"); + LEZCoreModule module; + + const std::string keysJson = std::string("{\"nullifier_public_key\":\"") + std::string(64, 'a') + + "\",\"viewing_public_key\": 12345}"; + const nlohmann::json obj = parseObject(module.transfer_shielded(VALID_ID, keysJson, VALID_U128)); + LOGOS_ASSERT_FALSE(obj["success"].get()); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_shielded")); +} + LOGOS_TEST(transfer_shielded_success_json) { auto t = LogosTestContext("logos_execution_zone"); LEZCoreModule module; @@ -335,6 +397,16 @@ LOGOS_TEST(transfer_shielded_without_identifier_uses_random_nonzero_identifier) LOGOS_ASSERT_FALSE(memcmp(first, second, sizeof(first)) == 0); } +// transfer_private shares the same parser, so it must reject a missing key too. +LOGOS_TEST(transfer_private_missing_nullifier_key_error) { + auto t = LogosTestContext("logos_execution_zone"); + LEZCoreModule module; + + const nlohmann::json obj = parseObject(module.transfer_private(VALID_ID, "{}", VALID_U128)); + LOGOS_ASSERT_FALSE(obj["success"].get()); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("wallet_ffi_transfer_private")); +} + // transfer_private mirrors transfer_shielded's identifier handling exactly (see above). LOGOS_TEST(transfer_private_with_identifier_forwards_it_unchanged) { auto t = LogosTestContext("logos_execution_zone");