1
0
Fork 0
forked from lthn/blockchain

Merge branch 'release' into develop

# Conflicts:
#	src/wallet/wallet2.cpp
This commit is contained in:
sowle 2025-07-17 15:39:00 +03:00
commit ee883a690c
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
5 changed files with 197 additions and 92 deletions

View file

@ -59,6 +59,7 @@ namespace epee
storage_entry load_storage_entry();
void read(section& sec);
void read(std::string& str);
void read(array_entry &ae);
private:
struct recursuion_limitation_guard
{
@ -114,6 +115,7 @@ namespace epee
void throwable_buffer_reader::read(t_pod_type& pod_val)
{
RECURSION_LIMITATION();
static_assert(std::is_pod<t_pod_type>::value, "POD type expected");
read(&pod_val, sizeof(pod_val));
}
@ -277,5 +279,11 @@ namespace epee
m_ptr+=len;
m_count -= len;
}
inline
void throwable_buffer_reader::read(array_entry &ae)
{
RECURSION_LIMITATION();
CHECK_AND_ASSERT_THROW_MES(false, "Reading array entry is not supported");
}
}
}

View file

@ -28,6 +28,8 @@
#include "parserse_base_utils.h"
#include "file_io_utils.h"
#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100
namespace epee
{
namespace serialization
@ -54,9 +56,10 @@ namespace epee
ASSERT_MES_AND_THROW("json parse error");
}*/
template<class t_storage>
inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg)
inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg, unsigned int recursion)
{
CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL,
"Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded");
std::string::const_iterator sub_element_start;
std::string name;
typename t_storage::harray h_array = nullptr;
@ -167,7 +170,7 @@ namespace epee
//sub section here
typename t_storage::hsection new_sec = stg.open_section(name, current_section, true);
CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end));
run_handler(new_sec, it, buf_end, stg);
run_handler(new_sec, it, buf_end, stg, recursion + 1);
state = match_state_wonder_after_value;
}else if(*it == '[')
{//array of something
@ -196,7 +199,7 @@ namespace epee
typename t_storage::hsection new_sec = nullptr;
h_array = stg.insert_first_section(name, new_sec, current_section);
CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section");
run_handler(new_sec, it, buf_end, stg);
run_handler(new_sec, it, buf_end, stg, recursion + 1);
state = match_state_array_after_value;
array_md = array_mode_sections;
}else if(*it == '"')
@ -270,7 +273,7 @@ namespace epee
typename t_storage::hsection new_sec = NULL;
bool res = stg.insert_next_section(h_array, new_sec);
CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section");
run_handler(new_sec, it, buf_end, stg);
run_handler(new_sec, it, buf_end, stg, recursion + 1);
state = match_state_array_after_value;
}else CHECK_ISSPACE();
break;
@ -372,7 +375,7 @@ namespace epee
std::string::const_iterator sec_buf_begin = buff_json.begin();
try
{
run_handler(nullptr, sec_buf_begin, buff_json.end(), stg);
run_handler(nullptr, sec_buf_begin, buff_json.end(), stg, 0);
return true;
}
catch(const std::exception& ex)

View file

@ -3832,6 +3832,9 @@ bool wallet2::balance(std::unordered_map<crypto::public_key, wallet_public::asse
}
if (balances.empty())
balances[currency::native_coin_asset_id] = wallet_public::asset_balance_entry_base{};
return true;
}
//----------------------------------------------------------------------------------------------------
@ -3882,64 +3885,33 @@ bool wallet2::balance(std::list<wallet_public::asset_balance_entry>& balances, u
balances.clear();
std::unordered_map<crypto::public_key, wallet_public::asset_balance_entry_base> balances_map;
this->balance(balances_map, mined);
std::unordered_map<crypto::public_key, currency::asset_descriptor_base> custom_assets_local = m_custom_assets;
for (auto& own_asset : m_own_asset_descriptors)
for (const auto& [asset_id, balance_entry] : balances_map)
{
if (m_whitelisted_assets.find(own_asset.first) == m_whitelisted_assets.end())
{
custom_assets_local[own_asset.first] = own_asset.second;
}
}
asset_descriptor_base asset_info{};
uint32_t asset_flags = 0;
if (!get_asset_info(asset_id, asset_info, asset_flags))
continue;
asset_descriptor_base native_asset_info = AUTO_VAL_INIT(native_asset_info);
native_asset_info.full_name = CURRENCY_NAME_SHORT_BASE;
native_asset_info.ticker = CURRENCY_NAME_ABR;
native_asset_info.decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT;
custom_assets_local[currency::native_coin_asset_id] = native_asset_info;
if (!m_use_assets_whitelisting)
asset_flags &= ~aif_whitelisted;
for (const auto& item : balances_map)
{
asset_descriptor_base asset_info = AUTO_VAL_INIT(asset_info);
//check if asset is whitelisted or customly added
if ((asset_flags & (aif_native_coin | aif_custom | aif_whitelisted)) == 0)
continue;
//check if it custom asset
auto it_cust = custom_assets_local.find(item.first);
if (it_cust == custom_assets_local.end())
{
if (!m_use_assets_whitelisting)
continue;
auto it_local = m_whitelisted_assets.find(item.first);
if (it_local == m_whitelisted_assets.end())
{
WLT_LOG_YELLOW("WARNING: unknown asset " << item.first << " found and skipped; it's NOT included in balance", LOG_LEVEL_1);
continue;
}
else
{
asset_info = it_local->second;
}
}
else
{
asset_info = it_cust->second;
custom_assets_local.erase(it_cust);
}
balances.push_back(wallet_public::asset_balance_entry());
wallet_public::asset_balance_entry& new_item = balances.back();
static_cast<wallet_public::asset_balance_entry_base&>(new_item) = item.second;
new_item.asset_info.asset_id = item.first;
wallet_public::asset_balance_entry& new_item = balances.emplace_back();
static_cast<wallet_public::asset_balance_entry_base&>(new_item) = balance_entry;
new_item.asset_info.asset_id = asset_id;
static_cast<currency::asset_descriptor_base&>(new_item.asset_info) = asset_info;
}
//manually added assets should be always present, at least as zero balanced items
for (auto& asset : custom_assets_local)
for (const auto& [asset_id, custom_asset_entry] : m_custom_assets)
{
balances.push_back(wallet_public::asset_balance_entry());
wallet_public::asset_balance_entry& new_item = balances.back();
new_item.asset_info.asset_id = asset.first;
static_cast<currency::asset_descriptor_base&>(new_item.asset_info) = asset.second;
if (std::find_if(balances.begin(), balances.end(), [&](wallet_public::asset_balance_entry& e){ return e.asset_info.asset_id == asset_id; }) != balances.end())
continue;
wallet_public::asset_balance_entry& new_item = balances.emplace_back();
new_item.asset_info.asset_id = asset_id;
static_cast<currency::asset_descriptor_base&>(new_item.asset_info) = custom_asset_entry;
}
return true;
@ -3961,7 +3933,6 @@ bool wallet2::get_asset_info(const crypto::public_key& asset_id, currency::asset
{
asset_info = it_own->second;
asset_flags |= aif_own;
return true;
}
// whitelisted?
@ -3970,7 +3941,6 @@ bool wallet2::get_asset_info(const crypto::public_key& asset_id, currency::asset
{
asset_info = it_white->second;
asset_flags |= aif_whitelisted;
return true;
}
// custom asset?
@ -3979,7 +3949,6 @@ bool wallet2::get_asset_info(const crypto::public_key& asset_id, currency::asset
{
asset_info = it_cust->second;
asset_flags |= aif_custom;
return true;
}
if (ask_daemon_for_unknown)
@ -3987,11 +3956,10 @@ bool wallet2::get_asset_info(const crypto::public_key& asset_id, currency::asset
if (daemon_get_asset_info(asset_id, asset_info))
{
asset_flags |= aif_unknown;
return true;
}
}
return false;
return asset_flags != aif_none;
}
//----------------------------------------------------------------------------------------------------
size_t wallet2::get_asset_decimal_point(const crypto::public_key& asset_id, size_t result_if_not_found /* = 0 */) const

View file

@ -962,7 +962,7 @@ typedef boost::variant<crypto::public_key, crypto::eth_public_key> public_key_v;
template<typename t_response>
bool sign_signature_with_keys(const t_response& rsp_with_data, tools::wallet_rpc_server& attached_wallet_rpc, const secret_key_v& signer_v, const public_key_v& verifier)
bool ext_sign_and_send_asset_tx(const t_response& rsp_with_data, tools::wallet_rpc_server& attached_wallet_rpc, const secret_key_v& signer_v, const public_key_v& verifier)
{
bool r = false;
tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request send_signed_req = AUTO_VAL_INIT(send_signed_req);
@ -993,15 +993,15 @@ bool sign_signature_with_keys(const t_response& rsp_with_data, tools::wallet_rpc
return false;
}
send_signed_req.unsigned_tx = rsp_with_data.data_for_external_signing->unsigned_tx;
send_signed_req.expected_tx_id = rsp_with_data.tx_id;
send_signed_req.finalized_tx = rsp_with_data.data_for_external_signing->finalized_tx;
send_signed_req.unsigned_tx = rsp_with_data.data_for_external_signing->unsigned_tx;
send_signed_req.expected_tx_id = rsp_with_data.tx_id;
send_signed_req.finalized_tx = rsp_with_data.data_for_external_signing->finalized_tx;
send_signed_req.unlock_transfers_on_fail = true;
tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response send_signed_resp{};
r = invoke_text_json_for_rpc(attached_wallet_rpc, "send_ext_signed_asset_tx", send_signed_req, send_signed_resp);
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: ");
CHECK_AND_ASSERT_MES(send_signed_resp.status == API_RETURN_CODE_OK, false, "RPC send_ext_signed_asset_tx failed: ");
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed");
CHECK_AND_ASSERT_MES(send_signed_resp.status == API_RETURN_CODE_OK, false, "RPC send_ext_signed_asset_tx failed: " << send_signed_resp.status);
return true;
}
@ -1025,7 +1025,6 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const
r = mine_next_pow_blocks_in_playtime(alice_wlt->get_account().get_public_address(), c, 3);
r = mine_next_pow_blocks_in_playtime(bob_wlt->get_account().get_public_address(), c, 3);
r = mine_next_pow_blocks_in_playtime(carol_wlt->get_account().get_public_address(), c, 3);
//r = mine_next_pow_blocks_in_playtime(custody_wlt->get_account().get_public_address(), c, 3);
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
@ -1054,7 +1053,10 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const
return false;
}
const crypto::public_key asset_id = resp.new_asset_id;
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
// core RPC server
currency::t_currency_protocol_handler<currency::core> cprotocol(c, NULL);
@ -1104,26 +1106,34 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const
return false;
}
r = sign_signature_with_keys(emm_resp, alice_wlt_rpc, miner_wlt->get_account().get_keys().spend_secret_key, miner_wlt->get_account().get_keys().account_address.spend_public_key);
CHECK_AND_ASSERT_MES(r, false, "failed to call sign_signature_with_keys");
r = ext_sign_and_send_asset_tx(emm_resp, alice_wlt_rpc, miner_wlt->get_account().get_keys().spend_secret_key, miner_wlt->get_account().get_keys().account_address.spend_public_key);
CHECK_AND_ASSERT_MES(r, false, "failed to call ext_sign_and_send_asset_tx");
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3);
//check bob wallet
tools::wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request wtl_req = AUTO_VAL_INIT(wtl_req);
tools::wallet_public::COMMAND_ASSETS_WHITELIST_ADD::response wtl_resp = AUTO_VAL_INIT(wtl_resp);
wtl_req.asset_id = resp.new_asset_id;
tools::wallet_rpc_server bob_wlt_rpc(bob_wlt);
r = invoke_text_json_for_rpc(bob_wlt_rpc, "assets_whitelist_add", wtl_req, wtl_resp);
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: ");
CHECK_AND_ASSERT_MES(wtl_resp.status == API_RETURN_CODE_OK, false, "RPC status failed");
bob_wlt->refresh();
tools::wallet_public::COMMAND_RPC_GET_BALANCE::request balance_req = AUTO_VAL_INIT(balance_req);
tools::wallet_public::COMMAND_RPC_GET_BALANCE::response balance_resp = AUTO_VAL_INIT(balance_resp);
r = invoke_text_json_for_rpc(bob_wlt_rpc, "getbalance", balance_req, balance_resp);
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: ");
CHECK_AND_ASSERT_MES(r, false, "RPC getbalance failed");
r = std::find_if(balance_resp.balances.begin(), balance_resp.balances.end(), [&](tools::wallet_public::asset_balance_entry& e){ return e.asset_info.asset_id == asset_id; }) == balance_resp.balances.end();
CHECK_AND_ASSERT_MES(r, false, "found asset " << resp.new_asset_id << " which is unexpected");
tools::wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request wtl_req = AUTO_VAL_INIT(wtl_req);
tools::wallet_public::COMMAND_ASSETS_WHITELIST_ADD::response wtl_resp = AUTO_VAL_INIT(wtl_resp);
wtl_req.asset_id = resp.new_asset_id;
r = invoke_text_json_for_rpc(bob_wlt_rpc, "assets_whitelist_add", wtl_req, wtl_resp);
CHECK_AND_ASSERT_MES(r, false, "RPC assets_whitelist_add failed");
CHECK_AND_ASSERT_MES(wtl_resp.status == API_RETURN_CODE_OK, false, "RPC status failed");
bob_wlt->refresh();
balance_req = AUTO_VAL_INIT(balance_req);
balance_resp = AUTO_VAL_INIT(balance_resp);
r = invoke_text_json_for_rpc(bob_wlt_rpc, "getbalance", balance_req, balance_resp);
CHECK_AND_ASSERT_MES(r, false, "RPC getbalance failed");
bool found_asset = false;
@ -1132,14 +1142,14 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const
if (bal.asset_info.asset_id == resp.new_asset_id)
{
found_asset = true;
CHECK_AND_ASSERT_MES(bal.total == COINS_TO_TRANSFER, false, "Amount is unexpected");
CHECK_EQ(bal.total, COINS_TO_TRANSFER);
}
}
CHECK_AND_ASSERT_MES(found_asset, false, "Asset not found ");
CHECK_AND_ASSERT_MES(found_asset, false, "Asset with id " << resp.new_asset_id << " was not found");
//transfer ownership of the asset to new address
//transfer ownership of the asset to an ETH key
//let's change the owner to ecdsa
crypto::eth_secret_key eth_sk_2{};
crypto::eth_public_key eth_pk_2{};
@ -1160,8 +1170,8 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const
return false;
}
r = sign_signature_with_keys(res_own, alice_wlt_rpc, miner_wlt->get_account().get_keys().spend_secret_key, miner_wlt->get_account().get_keys().account_address.spend_public_key);
CHECK_AND_ASSERT_MES(r, false, "failed to call sign_signature_with_keys");
r = ext_sign_and_send_asset_tx(res_own, alice_wlt_rpc, miner_wlt->get_account().get_keys().spend_secret_key, miner_wlt->get_account().get_keys().account_address.spend_public_key);
CHECK_AND_ASSERT_MES(r, false, "failed to call ext_sign_and_send_asset_tx");
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3);
@ -1169,15 +1179,11 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const
//now make another tx and see of ownership got changed
emm_resp = AUTO_VAL_INIT(emm_resp);
r = invoke_text_json_for_rpc(alice_wlt_rpc, "emit_asset", emm_req, emm_resp);
CHECK_AND_ASSERT_MES(r, false, "failed to call");
if (!emm_resp.data_for_external_signing)
{
LOG_ERROR("Missing data_for_external_signing");
return false;
}
CHECK_AND_ASSERT_MES(r, false, "invoke_text_json_for_rpc failed");
CHECK_AND_ASSERT_MES(res_own.data_for_external_signing, false, "data_for_external_signing is missing");
r = sign_signature_with_keys(emm_resp, alice_wlt_rpc, eth_sk_2, eth_pk_2);
CHECK_AND_ASSERT_MES(r, false, "failed to call sign_signature_with_keys");
r = ext_sign_and_send_asset_tx(emm_resp, alice_wlt_rpc, eth_sk_2, eth_pk_2);
CHECK_AND_ASSERT_MES(r, false, "failed to call ext_sign_and_send_asset_tx");
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3);
@ -1197,6 +1203,66 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const
CHECK_AND_ASSERT_MES(found_asset, false, "Asset not found ");
// Transfer ownership to Carol (standard owner pub key)
tools::wallet_rpc_server carol_wlt_rpc(carol_wlt);
req_own = AUTO_VAL_INIT(req_own);
res_own = AUTO_VAL_INIT(res_own);
req_own.asset_id = asset_id;
req_own.new_owner = carol_acc.get_public_address().spend_public_key;
alice_wlt->refresh();
r = invoke_text_json_for_rpc(alice_wlt_rpc, "transfer_asset_ownership", req_own, res_own);
CHECK_AND_ASSERT_MES(r, false, "invoke_text_json_for_rpc failed");
CHECK_AND_ASSERT_MES(res_own.data_for_external_signing, false, "data_for_external_signing is missing");
// externally sign and send transfer ownership tx
r = ext_sign_and_send_asset_tx(res_own, alice_wlt_rpc, eth_sk_2, eth_pk_2);
CHECK_AND_ASSERT_MES(r, false, "ext_sign_and_send_asset_tx failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "unexpected pool txs count: " << c.get_pool_transactions_count());
// Miner still has some asset coins, transfer them to Carol
miner_wlt->refresh();
tools::wallet_public::COMMAND_RPC_TRANSFER::request miner_tr_req = AUTO_VAL_INIT(miner_tr_req);
tools::wallet_public::COMMAND_RPC_TRANSFER::request miner_tr_res = AUTO_VAL_INIT(miner_tr_res);
miner_tr_req.fee = TESTS_DEFAULT_FEE;
miner_tr_req.destinations.push_back({COINS_TO_TRANSFER * 7ull, carol_acc.get_public_address_str(), asset_id});
r = invoke_text_json_for_rpc(miner_wlt_rpc, "transfer", miner_tr_req, miner_tr_res);
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 2, false, "unexpected pool txs count: " << c.get_pool_transactions_count());
// confirm it
CHECK_AND_ASSERT_MES(mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW), false, "");
// make sure the pool is now empty
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "unexpected pool txs count: " << c.get_pool_transactions_count());
// Carol shouldn't have this asset whitelisted and present in balance
carol_wlt->refresh();
balance_req = AUTO_VAL_INIT(balance_req);
balance_resp = AUTO_VAL_INIT(balance_resp);
r = invoke_text_json_for_rpc(carol_wlt_rpc, "getbalance", balance_req, balance_resp);
CHECK_AND_ASSERT_MES(r, false, "RPC getbalance failed");
CHECK_EQ(balance_resp.balance, 0);
r = std::find_if(balance_resp.balances.begin(), balance_resp.balances.end(), [&](tools::wallet_public::asset_balance_entry& e){ return e.asset_info.asset_id == asset_id; }) == balance_resp.balances.end();
CHECK_AND_ASSERT_MES(r, false, "asset was found, which in unexpected");
// make sure she has asset_id among own assets in spite of that
auto& carol_own_assets = carol_wlt->get_own_assets();
CHECK_EQ(carol_own_assets.size(), 1);
CHECK_EQ(carol_own_assets.count(asset_id), 1);
// whitelist and re-check
wtl_req = AUTO_VAL_INIT(wtl_req);
wtl_resp = AUTO_VAL_INIT(wtl_resp);
wtl_req.asset_id = asset_id;
CHECK_AND_ASSERT_MES(invoke_text_json_for_rpc(carol_wlt_rpc, "assets_whitelist_add", wtl_req, wtl_resp), false, "");
CHECK_AND_ASSERT_MES(wtl_resp.status == API_RETURN_CODE_OK, false, "RPC failed");
// now the asset must show up in the balance
balance_req = AUTO_VAL_INIT(balance_req);
balance_resp = AUTO_VAL_INIT(balance_resp);
r = invoke_text_json_for_rpc(carol_wlt_rpc, "getbalance", balance_req, balance_resp);
CHECK_AND_ASSERT_MES(r, false, "RPC getbalance failed");
CHECK_EQ(balance_resp.balance, 0); // the balance for native coin is still zero
auto it = std::find_if(balance_resp.balances.begin(), balance_resp.balances.end(), [&](tools::wallet_public::asset_balance_entry& e){ return e.asset_info.asset_id == asset_id; });
CHECK_AND_ASSERT_MES(it != balance_resp.balances.end(), false, "asset was not found, which in unexpected");
CHECK_EQ(it->total, COINS_TO_TRANSFER * 7);
return true;
}

View file

@ -12,7 +12,9 @@
#include "net/levin_protocol_handler_async.h"
#include "net/net_utils_base.h"
#include "unit_tests_utils.h"
#include "storages/parserse_base_utils.h"
#include "storages/portable_storage_base.h"
#include "storages/portable_storage.h"
namespace
{
struct test_levin_connection_context : public epee::net_utils::connection_context_base
@ -504,3 +506,61 @@ TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_unexpe
ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size()));
}
using epee::serialization::portable_storage;
using epee::serialization::array_entry;
using epee::serialization::section;
using epee::serialization::throwable_buffer_reader;
/**
* Purpose:
* Verify what the deserialization of array_entry no longer uses memcpy to
* overwrite the boost::variant memory directly. Instead, an unsupported-array-entry
* path should throw an exception indicating array_entry deserialization isn't supported.
*/
TEST(levin_protocol_variant_memcpy, memcpy_variant_verify)
{
std::string buf; // raw buffer simulating an array_entry section
buf.push_back(static_cast<char>(SERIALIZE_FLAG_ARRAY | SERIALIZE_TYPE_ARRAY));
buf.push_back(static_cast<char>(1 << 2));
buf.append(sizeof(array_entry), char(0x41));
throwable_buffer_reader reader(reinterpret_cast<const uint8_t*>(buf.data()), buf.size());
EXPECT_THROW(
reader.load_storage_array_entry(SERIALIZE_TYPE_ARRAY),
std::runtime_error
) << "Expected load_storage_array_entry to throw due to array_entry";
}
/**
* Purpose:
* Construct a JSON string nested deeper than the built-in recursion limit (100 levels).
*/
TEST(json_parse_deep, parser_deep)
{
const int depth = 200;
std::string json;
json.reserve(depth * 10);
// Build a deeply nested JSON
// {"level": {"level": { ... {"level":1} ... }}}
for (int i = 0; i < depth; ++i)
{
json += '{';
json += "\"level\":";
}
json += '1';
for (int i = 0; i < depth; ++i)
{
json += '}';
}
portable_storage storage;
bool ok = epee::serialization::json::load_from_json(json, storage);
EXPECT_FALSE(ok) << "Expected load_from_json to fail when depth " << depth
<< " exceeds the 100-level recursion limit.";
}