1
0
Fork 0
forked from lthn/blockchain

rpc: find_outs_in_recent_blocks RPC implemented

This commit is contained in:
sowle 2024-08-22 15:30:51 +02:00
parent 47a1bd985d
commit 4d6291d7ff
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
3 changed files with 160 additions and 0 deletions

View file

@ -1301,6 +1301,110 @@ namespace currency
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_find_outs_in_recent_blocks(const COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::request& req, COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::response& resp, epee::json_rpc::error& error_resp, connection_context& cntx)
{
#define LOCAL_CHECK(cond, msg) if (!(cond)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; error_resp.message = msg; LOG_PRINT_L1("on_find_outs_in_recent_blocks: " << msg); return false; }
#define LOCAL_CHECK_INT_ERR(cond, msg) if (!(cond)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = msg; LOG_PRINT_L1("on_find_outs_in_recent_blocks: " << msg); return false; }
LOCAL_CHECK(req.address != account_public_address{}, "address is missing");
LOCAL_CHECK(req.viewkey != null_skey, "viewkey is missing");
LOCAL_CHECK(0 <= req.blocks_limit && req.blocks_limit <= 5, "blocks_limit is out of allowed bounds");
// verify addess keys
crypto::point_t view_pk, spend_pk;
LOCAL_CHECK(view_pk.from_public_key(req.address.view_public_key), "cannon load point from address.view_public_key");
LOCAL_CHECK(view_pk.is_in_main_subgroup(), "address.view_public_key isn't in main subgroup");
LOCAL_CHECK(spend_pk.from_public_key(req.address.spend_public_key), "cannon load point from address.spend_public_key");
LOCAL_CHECK(spend_pk.is_in_main_subgroup(), "address.spend_public_key isn't in main subgroup");
// verify viewkey
crypto::scalar_t view_sc = req.viewkey;
LOCAL_CHECK(view_sc.is_reduced(), "viewkey is invalid");
LOCAL_CHECK(view_sc * crypto::c_point_G == view_pk, "viewkey doesn't correspond to the given address");
const blockchain_storage& bcs = m_core.get_blockchain_storage();
resp.blockchain_top_block_height = bcs.get_top_block_height();
resp.blocks_limit = req.blocks_limit;
// get blockchain transactions
std::unordered_map<uint64_t, std::pair<std::vector<crypto::hash>, std::list<transaction>>> blockchain_txs; // block height -> (vector of tx_ids, list of txs)
if (req.blocks_limit > 0)
{
uint64_t start_offset = resp.blockchain_top_block_height - req.blocks_limit + 1;
std::list<block> recent_blocks;
LOCAL_CHECK_INT_ERR(bcs.get_blocks(start_offset, req.blocks_limit, recent_blocks), "cannot get recent blocks");
std::vector<crypto::hash> blockchain_tx_ids, missed_tx;
for(auto& b : recent_blocks)
{
blockchain_tx_ids.insert(blockchain_tx_ids.end(), b.tx_hashes.begin(), b.tx_hashes.end());
uint64_t height = get_block_height(b);
auto& el = blockchain_txs[height];
el.first = b.tx_hashes;
missed_tx.clear();
LOCAL_CHECK_INT_ERR(bcs.get_transactions(b.tx_hashes, el.second, missed_tx), "bcs.get_transactions failed");
LOCAL_CHECK_INT_ERR(missed_tx.empty(), "missed_tx is not empty");
LOCAL_CHECK_INT_ERR(el.first.size() == el.second.size(), "el.first.size() != el.second.size()");
}
}
// get pool transactions
std::list<transaction> pool_txs;
LOCAL_CHECK_INT_ERR(m_core.get_tx_pool().get_transactions(pool_txs), "cannot get txs from pool");
// processor lambda
auto process_tx = [&](const transaction& tx, const crypto::hash& tx_id, const int64_t height) {
crypto::key_derivation derivation{};
LOCAL_CHECK(generate_key_derivation(get_tx_pub_key_from_extra(tx), req.viewkey, derivation), "generate_key_derivation failed");
for(size_t i = 0, sz = tx.vout.size(); i < sz; ++i)
{
const tx_out_v& outv = tx.vout[i];
if (outv.type() != typeid(tx_out_zarcanum))
continue;
uint64_t decoded_amount = 0;
crypto::public_key decoded_asset_id{};
crypto::scalar_t amount_blinding_mask{}, asset_id_blinding_mask{};
if (!is_out_to_acc(req.address, boost::get<tx_out_zarcanum>(outv), derivation, i, decoded_amount, decoded_asset_id, amount_blinding_mask, asset_id_blinding_mask))
continue;
auto& el = resp.outputs.emplace_back();
el.amount = decoded_amount;
el.asset_id = decoded_asset_id;
el.tx_id = tx_id;
el.tx_block_height = height;
el.output_tx_index = i;
}
return true;
};
// process blockchain txs
for(auto& [height, pair] : blockchain_txs)
{
LOCAL_CHECK(pair.first.size() == pair.second.size(), "container size inconsistency");
auto tx_it = pair.second.begin();
for(size_t i = 0, sz = pair.first.size(); i < sz; ++i, ++tx_it)
{
const crypto::hash& tx_id = pair.first[i];
LOCAL_CHECK(process_tx(*tx_it, tx_id, height), "process blockchain tx failed for tx " + crypto::pod_to_hex(tx_id));
}
}
// process pool txs
for(auto& tx : pool_txs)
{
crypto::hash tx_id = get_transaction_hash(tx);
LOCAL_CHECK(process_tx(tx, tx_id, -1), "process pool tx failed for tx " + crypto::pod_to_hex(tx_id));
}
resp.status = resp.outputs.empty() ? API_RETURN_CODE_NOT_FOUND : API_RETURN_CODE_OK;
return true;
#undef LOCAL_CHECK_INT_ERR
#undef LOCAL_CHECK
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_aliases_by_address(const COMMAND_RPC_GET_ALIASES_BY_ADDRESS::request& req, COMMAND_RPC_GET_ALIASES_BY_ADDRESS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx)
{
account_public_address addr = AUTO_VAL_INIT(addr);

View file

@ -94,6 +94,7 @@ namespace currency
bool on_get_alt_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_alt_blocks_details(const COMMAND_RPC_GET_ALT_BLOCKS_DETAILS::request& req, COMMAND_RPC_GET_ALT_BLOCKS_DETAILS::response& res, connection_context& cntx);
bool on_get_est_height_from_date(const COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE::request& req, COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE::response& res, connection_context& cntx);
bool on_find_outs_in_recent_blocks(const COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::request& req, COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_validate_signature(const COMMAND_VALIDATE_SIGNATURE::request& req, COMMAND_VALIDATE_SIGNATURE::response& res, epee::json_rpc::error& er, connection_context& cntx);
@ -133,6 +134,8 @@ namespace currency
MAP_JON_RPC_WE("get_alias_by_address", on_aliases_by_address, COMMAND_RPC_GET_ALIASES_BY_ADDRESS)
MAP_JON_RPC_WE("get_alias_reward", on_get_alias_reward, COMMAND_RPC_GET_ALIAS_REWARD)
MAP_JON_RPC ("get_est_height_from_date", on_get_est_height_from_date, COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE)
MAP_JON_RPC_WE("find_outs_in_recent_blocks", on_find_outs_in_recent_blocks, COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS)
//block explorer api
MAP_JON_RPC ("get_blocks_details", on_rpc_get_blocks_details, COMMAND_RPC_GET_BLOCKS_DETAILS)
MAP_JON_RPC_WE("get_tx_details", on_get_tx_details, COMMAND_RPC_GET_TX_DETAILS)

View file

@ -23,6 +23,7 @@
#include <list>
#include <string>
#include <vector>
#include <currency_core/account.h>
//#include "currency_core/basic_api_response_codes.h"
namespace currency
@ -289,6 +290,58 @@ namespace currency
};
};
//-----------------------------------------------
struct COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS
{
DOC_COMMAND("Retrieves information about outputs in recent blocks that are targeted for the given address with the corresponding secret view key.")
static constexpr uint64_t blocks_limit_default = 5;
struct request
{
account_public_address address;
crypto::secret_key viewkey;
uint64_t blocks_limit = blocks_limit_default;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_ADDRESS_AS_TEXT(address) DOC_DSCR("Target address for which outputs are being searched") DOC_EXMP("ZxCSpsGGeJsS8fwvQ4HktDU3qBeauoJTR6j73jAWWZxFXdF7XTbGm4YfS2kXJmAP4Rf5BVsSQ9iZ45XANXEYsrLN2L2W77dH7") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(viewkey) DOC_DSCR("Secret view key corresponding to the given address.") DOC_EXMP("5fa8eaaf231a305053260ff91d69c6ef1ecbd0f5") DOC_END
KV_SERIALIZE(blocks_limit) DOC_DSCR("Block count limit. If 0, only the transaction pool will be searched. Maximum and default is " + epee::string_tools::num_to_string_fast(blocks_limit_default) + ".") DOC_EXMP(1711021795) DOC_END
END_KV_SERIALIZE_MAP()
};
struct out_entry
{
uint64_t amount;
crypto::public_key asset_id;
crypto::hash tx_id;
int64_t tx_block_height;
uint64_t output_tx_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount) DOC_DSCR("The amount of the output.") DOC_EXMP(1000000000000) DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset ID of the output.") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Transaction ID where the output is present, if found.") DOC_EXMP("a6e8da986858e6825fce7a192097e6afae4e889cabe853a9c29b964985b23da8") DOC_END
KV_SERIALIZE(tx_block_height) DOC_DSCR("Block height where the transaction is present.") DOC_EXMP(2555000) DOC_END
KV_SERIALIZE(output_tx_index) DOC_DSCR("Index of the output in the transaction.") DOC_EXMP(2) DOC_END
END_KV_SERIALIZE_MAP()
};
struct response
{
std::vector<out_entry> outputs;
uint64_t blockchain_top_block_height;
uint64_t blocks_limit;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outputs) DOC_DSCR("List of found outputs.") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE(blockchain_top_block_height) DOC_DSCR("Height of the most recent block in the blockchain.") DOC_EXMP(2555000) DOC_END
KV_SERIALIZE(blocks_limit) DOC_DSCR("Used limit for block count.") DOC_EXMP(5) DOC_END
KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END
END_KV_SERIALIZE_MAP()
};
};
//-----------------------------------------------
struct COMMAND_RPC_GET_TX_POOL
{
DOC_COMMAND("Retreives transactions from tx pool (and other information).")