1
0
Fork 0
forked from lthn/blockchain

Merge branch 'release' into develop

# Conflicts:
#	src/rpc/core_rpc_server.cpp
#	src/version.h.in
This commit is contained in:
sowle 2025-07-15 22:22:36 +03:00
commit 8fb16d207f
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
6 changed files with 153 additions and 11 deletions

View file

@ -4,6 +4,6 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ZANO PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Zano project shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Zano project.
Except as contained in this notice, the name of Zano project shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Zano project.

View file

@ -46,3 +46,4 @@
#define API_RETURN_CODE_HTLC_ORIGIN_HASH_MISSMATCHED "HTLC_ORIGIN_HASH_MISSMATCHED"
#define API_RETURN_CODE_WRAP "WRAP"
#define API_RETURN_CODE_MISSING_ZC_INPUTS "MISSING_ZC_INPUTS"
#define API_RETURN_CODE_ARG_OUT_OF_LIMITS "ARG_OUT_OF_LIMITS"

View file

@ -16,6 +16,23 @@ using namespace epee;
#include "crypto/hash.h"
#include "core_rpc_server_error_codes.h"
#define CHECK_RPC_LIMITS(var, limit) if(var > limit) {res.status = API_RETURN_CODE_ARG_OUT_OF_LIMITS; return true;}
#define RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DIRECT_BLOCK_IDS 4000
#define RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS 500
#define RPC_LIMIT_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_TXIDS 100
#define RPC_LIMIT_COMMAND_RPC_CHECK_KEYIMAGES_IMAGES 1000
#define RPC_LIMIT_COMMAND_RPC_GET_TRANSACTIONS_TXS_HASHES 5000
#define RPC_LIMIT_COMMAND_RPC_GET_OFFERS_EX_LIMIT 1000
#define RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DETAILS_COUNT 4000
#define RPC_LIMIT_COMMAND_RPC_GET_POOL_TXS_DETAILS_IDS 4000
#define RPC_LIMIT_COMMAND_RPC_GET_VOTES 4000
#define RPC_LIMIT_COMMAND_RPC_GET_ASSETS_LIST 1000
#define RPC_LIMIT_COMMAND_RPC_GET_ALT_BLOCKS_DETAILS_COUNT 100
#define RPC_LIMIT_COMMAND_RPC_FORCE_RELAY_RAW_TXS 100
#define RPC_LIMIT_COMMAND_RPC_GET_ALIASES_COUNT 200
namespace currency
@ -25,7 +42,7 @@ namespace currency
const command_line::arg_descriptor<std::string> arg_rpc_bind_ip ("rpc-bind-ip", "", "127.0.0.1");
const command_line::arg_descriptor<std::string> arg_rpc_bind_port ("rpc-bind-port", "", std::to_string(RPC_DEFAULT_PORT));
const command_line::arg_descriptor<bool> arg_rpc_ignore_offline_status ("rpc-ignore-offline", "Let rpc calls despite online/offline status");
const command_line::arg_descriptor<bool> arg_rpc_enable_rpc_api ("rpc-enable-admin-api", "Enable API commands that can alter state of pool or daemon(reset pool, remove txs etc)");
const command_line::arg_descriptor<bool> arg_rpc_enable_admin_api ("rpc-enable-admin-api", "Enable API commands that can alter pool or daemon state (reset pool, remove txs, etc.)");
}
//-----------------------------------------------------------------------------------
void core_rpc_server::init_options(boost::program_options::options_description& desc)
@ -33,7 +50,7 @@ namespace currency
command_line::add_arg(desc, arg_rpc_bind_ip);
command_line::add_arg(desc, arg_rpc_bind_port);
command_line::add_arg(desc, arg_rpc_ignore_offline_status);
command_line::add_arg(desc, arg_rpc_enable_rpc_api);
command_line::add_arg(desc, arg_rpc_enable_admin_api);
}
//------------------------------------------------------------------------------------------------------------------------------
@ -48,14 +65,21 @@ namespace currency
bool core_rpc_server::handle_command_line(const boost::program_options::variables_map& vm)
{
m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip);
if (m_bind_ip == "0.0.0.0")
{
LOG_PRINT_COLOR("!!!!!!IMPORTANT!!!!!!: We strongly advise against binding the server to 0.0.0.0 and exposing it directly" << ENDL
<< "to the public internet. The server is meant solely for internal functionality and lacks the security features" << ENDL
<< "required for publicly facing services. If you need to offer a public API, always place it behind secure" << ENDL
<< "proxies that are properly configured with security measures--such as rate limiting--to protect the service.", LOG_LEVEL_0, LOG_COLOR_RED);
}
m_port = command_line::get_arg(vm, arg_rpc_bind_port);
if (command_line::has_arg(vm, arg_rpc_ignore_offline_status))
{
m_ignore_offline_status = command_line::get_arg(vm, arg_rpc_ignore_offline_status);
}
if (command_line::has_arg(vm, arg_rpc_enable_rpc_api))
if (command_line::has_arg(vm, arg_rpc_enable_admin_api))
{
m_enabled_admin_api = command_line::get_arg(vm, arg_rpc_enable_rpc_api);
m_enabled_admin_api = command_line::get_arg(vm, arg_rpc_enable_admin_api);
}
return true;
@ -289,6 +313,7 @@ namespace currency
bool core_rpc_server::on_get_blocks_direct(const COMMAND_RPC_GET_BLOCKS_DIRECT::request& req, COMMAND_RPC_GET_BLOCKS_DIRECT::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.block_ids.size(), RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DIRECT_BLOCK_IDS);
if (req.block_ids.back() != m_core.get_blockchain_storage().get_block_id_by_height(0))
{
@ -327,6 +352,7 @@ namespace currency
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.block_ids.size(), RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DIRECT_BLOCK_IDS);
LOG_PRINT_L2("[on_get_blocks]: Prevalidating....");
if (req.block_ids.back() != m_core.get_blockchain_storage().get_block_id_by_height(0))
{
@ -377,6 +403,8 @@ namespace currency
bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.amounts.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS);
res.status = API_RETURN_CODE_FAIL;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req_native = AUTO_VAL_INIT(req_native);
@ -428,6 +456,7 @@ namespace currency
bool core_rpc_server::on_get_random_outs1(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.amounts.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS);
res.status = API_RETURN_CODE_FAIL;
if(!m_core.get_random_outs_for_amounts(req, res))
@ -442,6 +471,12 @@ namespace currency
bool core_rpc_server::on_get_random_outs3(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.amounts.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS);
for (const auto& item : req.amounts)
{
CHECK_RPC_LIMITS(item.global_offsets.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS);
}
res.status = API_RETURN_CODE_FAIL;
if (!m_core.get_blockchain_storage().get_random_outs_for_amounts3(req, res))
{
@ -455,6 +490,8 @@ namespace currency
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.txids.size(), RPC_LIMIT_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_TXIDS);
res.tx_global_outs.resize(req.txids.size());
size_t i = 0;
for (auto& txid : req.txids)
@ -506,6 +543,8 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_check_keyimages(const COMMAND_RPC_CHECK_KEYIMAGES::request& req, COMMAND_RPC_CHECK_KEYIMAGES::response& res, connection_context& cntx)
{
CHECK_RPC_LIMITS(req.images.size(), RPC_LIMIT_COMMAND_RPC_CHECK_KEYIMAGES_IMAGES);
m_core.get_blockchain_storage().check_keyimages(req.images, res.images_stat);
res.status = API_RETURN_CODE_OK;
return true;
@ -514,6 +553,8 @@ namespace currency
bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.txs_hashes.size(), RPC_LIMIT_COMMAND_RPC_GET_TRANSACTIONS_TXS_HASHES);
std::vector<crypto::hash> vh;
for(const auto& tx_hex_str : req.txs_hashes)
{
@ -556,6 +597,9 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_offers_ex(const COMMAND_RPC_GET_OFFERS_EX::request& req, COMMAND_RPC_GET_OFFERS_EX::response& res, epee::json_rpc::error& error_resp, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.filter.limit, RPC_LIMIT_COMMAND_RPC_GET_OFFERS_EX_LIMIT);
m_of.get_offers_ex(req.filter, res.offers, res.total_offers, m_core.get_blockchain_storage().get_core_runtime_config().get_core_time());
res.status = API_RETURN_CODE_OK;
return true;
@ -632,6 +676,8 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_rpc_get_blocks_details(const COMMAND_RPC_GET_BLOCKS_DETAILS::request& req, COMMAND_RPC_GET_BLOCKS_DETAILS::response& res, connection_context& cntx)
{
CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DETAILS_COUNT);
m_core.get_blockchain_storage().get_main_blocks_rpc_details(req.height_start, req.count, req.ignore_transactions, res.blocks);
res.status = API_RETURN_CODE_OK;
return true;
@ -691,6 +737,9 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_pool_txs_details(const COMMAND_RPC_GET_POOL_TXS_DETAILS::request& req, COMMAND_RPC_GET_POOL_TXS_DETAILS::response& res, connection_context& cntx)
{
CHECK_RPC_LIMITS(req.ids.size(), RPC_LIMIT_COMMAND_RPC_GET_POOL_TXS_DETAILS_IDS);
if (!req.ids.size())
{
m_core.get_tx_pool().get_all_transactions_details(res.txs);
@ -705,6 +754,7 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_pool_txs_brief_details(const COMMAND_RPC_GET_POOL_TXS_BRIEF_DETAILS::request& req, COMMAND_RPC_GET_POOL_TXS_BRIEF_DETAILS::response& res, connection_context& cntx)
{
CHECK_RPC_LIMITS(req.ids.size(), RPC_LIMIT_COMMAND_RPC_GET_POOL_TXS_DETAILS_IDS);
if (!req.ids.size())
{
m_core.get_tx_pool().get_all_transactions_brief_details(res.txs);
@ -738,6 +788,8 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_votes(const COMMAND_RPC_GET_VOTES::request& req, COMMAND_RPC_GET_VOTES::response& res, connection_context& cntx)
{
CHECK_RPC_LIMITS(req.h_end - req.h_start, RPC_LIMIT_COMMAND_RPC_GET_VOTES);
if (!m_core.get_blockchain_storage().get_pos_votes(req.h_start, req.h_end, res.votes))
{
res.status = API_RETURN_CODE_INTERNAL_ERROR;
@ -762,6 +814,8 @@ namespace currency
bool core_rpc_server::on_get_assets_list(const COMMAND_RPC_GET_ASSETS_LIST::request& req, COMMAND_RPC_GET_ASSETS_LIST::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_ASSETS_LIST);
if (!m_core.get_blockchain_storage().get_assets(req.offset, req.count, res.assets))
{
res.status = API_RETURN_CODE_NOT_FOUND;
@ -865,6 +919,8 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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)
{
CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_ALT_BLOCKS_DETAILS_COUNT);
m_core.get_blockchain_storage().get_alt_blocks_rpc_details(req.offset, req.count, res.blocks);
res.status = API_RETURN_CODE_OK;
return true;
@ -942,7 +998,8 @@ namespace currency
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_force_relaey_raw_txs(const COMMAND_RPC_FORCE_RELAY_RAW_TXS::request& req, COMMAND_RPC_FORCE_RELAY_RAW_TXS::response& res, connection_context& cntx)
{
{
CHECK_RPC_LIMITS(req.txs_as_hex.size(), RPC_LIMIT_COMMAND_RPC_FORCE_RELAY_RAW_TXS);
CHECK_CORE_READY();
NOTIFY_OR_INVOKE_NEW_TRANSACTIONS::request r = AUTO_VAL_INIT(r);
@ -1379,10 +1436,11 @@ namespace currency
res.status = API_RETURN_CODE_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_aliases(const COMMAND_RPC_GET_ALIASES::request& req, COMMAND_RPC_GET_ALIASES::response& res, epee::json_rpc::error& error_resp, connection_context& cntx)
{
CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_ALIASES_COUNT);
if(!check_core_ready())
{
error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY;

View file

@ -1494,7 +1494,7 @@ namespace currency
struct COMMAND_RPC_RESET_TX_POOL
{
DOC_COMMAND("Clears transaction pool.");
DOC_COMMAND("Clears transaction pool. (Admin API, requires CLI option, disabled by default)");
struct request
{
@ -1516,7 +1516,7 @@ namespace currency
struct COMMAND_RPC_REMOVE_TX_FROM_POOL
{
DOC_COMMAND("Removes specified transactions from the transaction pool, typically to clear out transactions that are no longer valid or needed.");
DOC_COMMAND("Removes specified transactions from the transaction pool, typically to clear out transactions that are no longer valid or needed. (Admin API, requires CLI option, disabled by default)");
struct request
{

View file

@ -8,6 +8,6 @@
#define PROJECT_REVISION "8"
#define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION
#define PROJECT_VERSION_BUILD_NO 415
#define PROJECT_VERSION_BUILD_NO 417
#define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO)
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]"

83
utils/JS/JWT/example.js Normal file
View file

@ -0,0 +1,83 @@
const axios = require('axios');
const forge = require('node-forge');
// Your shared JWT secret (must match what the server expects)
const JWT_SECRET = 'hiwejkcddkndspo230XASIijksk123i9x5';
const apiCredentials = {token: JWT_SECRET};
function createJWSToken(payload, secrete_str) {
const header = { alg: 'HS256', typ: 'JWT' };
const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64').replace(/=/g, '');
const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64').replace(/=/g, '');
const signature = forge.hmac.create();
signature.start('sha256', secrete_str);
signature.update(`${encodedHeader}.${encodedPayload}`);
const encodedSignature = forge.util.encode64(signature.digest().getBytes()).replace(/=/g, '');
return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
}
function generateRandomString(length) {
const bytes = forge.random.getBytesSync(Math.ceil(length / 2));
const hexString = forge.util.bytesToHex(bytes);
return hexString.substring(0, length);
}
function generateAccessToken(httpBody) {
if (!apiCredentials?.token) {
throw new Error("No API credentials found, extension is not connected");
}
const md = forge.md.sha256.create();
md.update(httpBody);
const bodyHash = md.digest().toHex();
// Example payload
const payload = {
body_hash: bodyHash,
user: 'zano_extension',
salt: generateRandomString(64),
exp: Math.floor(Date.now() / 1000) + (60), // Expires in 1 minute
};
return createJWSToken(payload, apiCredentials?.token);
}
// JSON-RPC 2.0 request body
const requestData = {
jsonrpc: '2.0',
id: 0,
method: 'getbalance',
params: {},
};
const http_body = JSON.stringify(requestData);
// Create JWT token
const token = generateAccessToken(http_body);
// Send POST request
axios.post('http://127.0.0.1:11111/json_rpc', requestData, {
headers: {
'Content-Type': 'application/json',
'Zano-Access-Token': `${token}`,
},
})
.then(response => {
console.log('Response:', response.data);
})
.catch(error => {
if (error.response) {
console.error('Error Response:', error.response.status, error.response.data);
} else {
console.error('Request Error:', error.message);
}
});