// Mock implementation of the wallet_ffi C functions. // Replaces the real lssa wallet library at link time during unit tests. // Return codes and out-parameter contents are controlled via LogosCMockStore. // // Conventions: // - Functions returning WalletFfiError use LOGOS_CMOCK_RETURN(int, ""); an // unset mock defaults to 0 (SUCCESS), so the happy path needs no setup. // - Out-parameters are filled with deterministic bytes only on success so tests // can assert on the resulting hex/JSON. #include extern "C" { #include } #include #include namespace { // A single non-null sentinel handle handed back by create_new / open. char g_fakeWallet = 0; // Backing storage for list_accounts (returned by pointer to the caller). FfiAccountListEntry g_accountEntries[8]; // Fill a transfer result based on the mocked error code for `key`. WalletFfiError fillTransferResult(const char* key, FfiTransferResult* out_result) { const int err = LogosCMockStore::instance().getReturn(key); if (out_result) { out_result->success = (err == 0); if (err == 0) { const char* tx = LogosCMockStore::instance().getReturnString("transfer_tx_hash"); // getReturnString yields "" (non-null) when unset; fall back to a default. out_result->tx_hash = strdup((tx && *tx) ? tx : "0xmocktxhash"); } else { out_result->tx_hash = nullptr; } } return static_cast(err); } } // namespace extern "C" { // === Lifecycle === WalletHandle* wallet_ffi_create_new(const char*, const char*, const char*) { LOGOS_CMOCK_RECORD("wallet_ffi_create_new"); const int ok = LOGOS_CMOCK_RETURN(int, "wallet_ffi_create_new"); return ok ? reinterpret_cast(&g_fakeWallet) : nullptr; } WalletHandle* wallet_ffi_open(const char*, const char*) { LOGOS_CMOCK_RECORD("wallet_ffi_open"); const int ok = LOGOS_CMOCK_RETURN(int, "wallet_ffi_open"); return ok ? reinterpret_cast(&g_fakeWallet) : nullptr; } int wallet_ffi_save(WalletHandle*) { LOGOS_CMOCK_RECORD("wallet_ffi_save"); return LOGOS_CMOCK_RETURN(int, "wallet_ffi_save"); } void wallet_ffi_destroy(WalletHandle*) { LOGOS_CMOCK_RECORD("wallet_ffi_destroy"); } // === Account management === WalletFfiError wallet_ffi_create_account_public(WalletHandle*, FfiBytes32* out_id) { LOGOS_CMOCK_RECORD("wallet_ffi_create_account_public"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_create_account_public"); if (err == 0 && out_id) { memset(out_id->data, 0xAB, sizeof(out_id->data)); } return static_cast(err); } WalletFfiError wallet_ffi_create_account_private(WalletHandle*, FfiBytes32* out_id) { LOGOS_CMOCK_RECORD("wallet_ffi_create_account_private"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_create_account_private"); if (err == 0 && out_id) { memset(out_id->data, 0xCD, sizeof(out_id->data)); } return static_cast(err); } WalletFfiError wallet_ffi_list_accounts(WalletHandle*, FfiAccountList* out_list) { LOGOS_CMOCK_RECORD("wallet_ffi_list_accounts"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_list_accounts"); if (!out_list) { return static_cast(err); } if (err == 0) { int count = LOGOS_CMOCK_RETURN(int, "list_accounts_count"); if (count < 0) count = 0; if (count > 8) count = 8; for (int i = 0; i < count; ++i) { memset(g_accountEntries[i].account_id.data, 0x10 + i, sizeof(g_accountEntries[i].account_id.data)); g_accountEntries[i].is_public = (i % 2 == 0); } out_list->entries = g_accountEntries; out_list->count = static_cast(count); } else { out_list->entries = nullptr; out_list->count = 0; } return static_cast(err); } void wallet_ffi_free_account_list(FfiAccountList* list) { LOGOS_CMOCK_RECORD("wallet_ffi_free_account_list"); if (list) { list->entries = nullptr; list->count = 0; } } // === Account queries === WalletFfiError wallet_ffi_get_balance(WalletHandle*, const FfiBytes32*, bool, uint8_t (*out_balance)[16]) { LOGOS_CMOCK_RECORD("wallet_ffi_get_balance"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_get_balance"); if (err == 0 && out_balance) { const uint64_t value = static_cast(LOGOS_CMOCK_RETURN(int, "get_balance_value")); memset(*out_balance, 0, 16); for (int i = 0; i < 8; ++i) { (*out_balance)[i] = static_cast((value >> (i * 8)) & 0xFF); } } return static_cast(err); } static WalletFfiError fillAccount(const char* key, FfiAccount* out_account) { const int err = LogosCMockStore::instance().getReturn(key); if (err == 0 && out_account) { memset(out_account->program_owner.data, 0xAA, sizeof(out_account->program_owner.data)); memset(out_account->balance.data, 0, sizeof(out_account->balance.data)); out_account->balance.data[0] = 0x07; memset(out_account->nonce.data, 0, sizeof(out_account->nonce.data)); out_account->nonce.data[0] = 0x01; out_account->data = nullptr; out_account->data_len = 0; } return static_cast(err); } WalletFfiError wallet_ffi_get_account_public(WalletHandle*, const FfiBytes32*, FfiAccount* out_account) { LOGOS_CMOCK_RECORD("wallet_ffi_get_account_public"); return fillAccount("wallet_ffi_get_account_public", out_account); } WalletFfiError wallet_ffi_get_account_private(WalletHandle*, const FfiBytes32*, FfiAccount* out_account) { LOGOS_CMOCK_RECORD("wallet_ffi_get_account_private"); return fillAccount("wallet_ffi_get_account_private", out_account); } void wallet_ffi_free_account_data(FfiAccount* account) { LOGOS_CMOCK_RECORD("wallet_ffi_free_account_data"); if (account && account->data) { free(account->data); account->data = nullptr; account->data_len = 0; } } WalletFfiError wallet_ffi_get_public_account_key(WalletHandle*, const FfiBytes32*, FfiPublicAccountKey* out_key) { LOGOS_CMOCK_RECORD("wallet_ffi_get_public_account_key"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_get_public_account_key"); if (err == 0 && out_key) { memset(out_key->public_key.data, 0xBE, sizeof(out_key->public_key.data)); } return static_cast(err); } WalletFfiError wallet_ffi_get_private_account_keys(WalletHandle*, const FfiBytes32*, FfiPrivateAccountKeys* out_keys) { LOGOS_CMOCK_RECORD("wallet_ffi_get_private_account_keys"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_get_private_account_keys"); if (err == 0 && out_keys) { memset(out_keys->nullifier_public_key.data, 0xEF, sizeof(out_keys->nullifier_public_key.data)); out_keys->viewing_public_key = nullptr; out_keys->viewing_public_key_len = 0; } return static_cast(err); } void wallet_ffi_free_private_account_keys(FfiPrivateAccountKeys* keys) { LOGOS_CMOCK_RECORD("wallet_ffi_free_private_account_keys"); if (keys && keys->viewing_public_key) { free(keys->viewing_public_key); keys->viewing_public_key = nullptr; keys->viewing_public_key_len = 0; } } // === Account encoding === char* wallet_ffi_account_id_to_base58(const FfiBytes32*) { LOGOS_CMOCK_RECORD("wallet_ffi_account_id_to_base58"); const char* str = LOGOS_CMOCK_RETURN_STRING("wallet_ffi_account_id_to_base58"); return strdup((str && *str) ? str : "MockBase58Address"); } WalletFfiError wallet_ffi_account_id_from_base58(const char*, FfiBytes32* out_id) { LOGOS_CMOCK_RECORD("wallet_ffi_account_id_from_base58"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_account_id_from_base58"); if (err == 0 && out_id) { memset(out_id->data, 0x5A, sizeof(out_id->data)); } return static_cast(err); } void wallet_ffi_free_string(char* s) { LOGOS_CMOCK_RECORD("wallet_ffi_free_string"); if (s) { free(s); } } // === Blockchain synchronisation === int wallet_ffi_sync_to_block(WalletHandle*, uint64_t) { LOGOS_CMOCK_RECORD("wallet_ffi_sync_to_block"); return LOGOS_CMOCK_RETURN(int, "wallet_ffi_sync_to_block"); } WalletFfiError wallet_ffi_get_last_synced_block(WalletHandle*, uint64_t* out_block_id) { LOGOS_CMOCK_RECORD("wallet_ffi_get_last_synced_block"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_get_last_synced_block"); if (err == 0 && out_block_id) { *out_block_id = static_cast(LOGOS_CMOCK_RETURN(int, "last_synced_block_value")); } return static_cast(err); } WalletFfiError wallet_ffi_get_current_block_height(WalletHandle*, uint64_t* out_block_height) { LOGOS_CMOCK_RECORD("wallet_ffi_get_current_block_height"); const int err = LOGOS_CMOCK_RETURN(int, "wallet_ffi_get_current_block_height"); if (err == 0 && out_block_height) { *out_block_height = static_cast(LOGOS_CMOCK_RETURN(int, "current_block_height_value")); } return static_cast(err); } // === Pinata claiming === WalletFfiError wallet_ffi_claim_pinata( WalletHandle*, const FfiBytes32*, const FfiBytes32*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_claim_pinata"); return fillTransferResult("wallet_ffi_claim_pinata", out_result); } WalletFfiError wallet_ffi_claim_pinata_private_owned_already_initialized( WalletHandle*, const FfiBytes32*, const FfiBytes32*, const uint8_t (*)[16], uintptr_t, const uint8_t (*)[32], uintptr_t, FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_claim_pinata_private_owned_already_initialized"); return fillTransferResult("wallet_ffi_claim_pinata_private_owned_already_initialized", out_result); } WalletFfiError wallet_ffi_claim_pinata_private_owned_not_initialized( WalletHandle*, const FfiBytes32*, const FfiBytes32*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_claim_pinata_private_owned_not_initialized"); return fillTransferResult("wallet_ffi_claim_pinata_private_owned_not_initialized", out_result); } // === Transfers / registration === WalletFfiError wallet_ffi_transfer_public( WalletHandle*, const FfiBytes32*, const FfiBytes32*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_transfer_public"); return fillTransferResult("wallet_ffi_transfer_public", out_result); } WalletFfiError wallet_ffi_transfer_shielded( WalletHandle*, const FfiBytes32*, const FfiPrivateAccountKeys*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_transfer_shielded"); return fillTransferResult("wallet_ffi_transfer_shielded", out_result); } WalletFfiError wallet_ffi_transfer_deshielded( WalletHandle*, const FfiBytes32*, const FfiBytes32*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_transfer_deshielded"); return fillTransferResult("wallet_ffi_transfer_deshielded", out_result); } WalletFfiError wallet_ffi_transfer_private( WalletHandle*, const FfiBytes32*, const FfiPrivateAccountKeys*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_transfer_private"); return fillTransferResult("wallet_ffi_transfer_private", out_result); } WalletFfiError wallet_ffi_transfer_shielded_owned( WalletHandle*, const FfiBytes32*, const FfiBytes32*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_transfer_shielded_owned"); return fillTransferResult("wallet_ffi_transfer_shielded_owned", out_result); } WalletFfiError wallet_ffi_transfer_private_owned( WalletHandle*, const FfiBytes32*, const FfiBytes32*, const uint8_t (*)[16], FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_transfer_private_owned"); return fillTransferResult("wallet_ffi_transfer_private_owned", out_result); } WalletFfiError wallet_ffi_register_public_account(WalletHandle*, const FfiBytes32*, FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_register_public_account"); return fillTransferResult("wallet_ffi_register_public_account", out_result); } WalletFfiError wallet_ffi_register_private_account(WalletHandle*, const FfiBytes32*, FfiTransferResult* out_result) { LOGOS_CMOCK_RECORD("wallet_ffi_register_private_account"); return fillTransferResult("wallet_ffi_register_private_account", out_result); } void wallet_ffi_free_transfer_result(FfiTransferResult* result) { LOGOS_CMOCK_RECORD("wallet_ffi_free_transfer_result"); if (result && result->tx_hash) { free(result->tx_hash); result->tx_hash = nullptr; } } // === Configuration === char* wallet_ffi_get_sequencer_addr(WalletHandle*) { LOGOS_CMOCK_RECORD("wallet_ffi_get_sequencer_addr"); const char* addr = LOGOS_CMOCK_RETURN_STRING("wallet_ffi_get_sequencer_addr"); return strdup((addr && *addr) ? addr : "127.0.0.1:3000"); } } // extern "C"