@ -30,6 +30,7 @@
# include <limits>
# include <limits>
# include <ctime>
# include <ctime>
# include <future>
# include <future>
# include <visitor/render_node.hpp>
# define TMPL_DIR ". / templates"
# define TMPL_DIR ". / templates"
@ -66,7 +67,7 @@
# define JS_SHA3 TMPL_DIR " / js / sha3.js"
# define JS_SHA3 TMPL_DIR " / js / sha3.js"
# define ONIONEXPLORER_RPC_VERSION_MAJOR 1
# define ONIONEXPLORER_RPC_VERSION_MAJOR 1
# define ONIONEXPLORER_RPC_VERSION_MINOR 0
# define ONIONEXPLORER_RPC_VERSION_MINOR 1
# define MAKE_ONIONEXPLORER_RPC_VERSION(major,minor) (((major)<<16)|(minor))
# define MAKE_ONIONEXPLORER_RPC_VERSION(major,minor) (((major)<<16)|(minor))
# define ONIONEXPLORER_RPC_VERSION \
# define ONIONEXPLORER_RPC_VERSION \
MAKE_ONIONEXPLORER_RPC_VERSION ( ONIONEXPLORER_RPC_VERSION_MAJOR , ONIONEXPLORER_RPC_VERSION_MINOR )
MAKE_ONIONEXPLORER_RPC_VERSION ( ONIONEXPLORER_RPC_VERSION_MAJOR , ONIONEXPLORER_RPC_VERSION_MINOR )
@ -113,6 +114,71 @@ namespace std
}
}
/**
* visitor to produce json representations of
* values stored in mstch : : node
*/
class mstch_node_to_json : public boost : : static_visitor < nlohmann : : json >
{
public :
// enabled for numeric types
template < typename T >
typename std : : enable_if < std : : is_arithmetic < T > : : value , nlohmann : : json > : : type
operator ( ) ( T const & value ) const {
return nlohmann : : json { value } ;
}
nlohmann : : json operator ( ) ( std : : string const & value ) const {
return nlohmann : : json { value } ;
}
nlohmann : : json operator ( ) ( mstch : : map const & n_map ) const
{
nlohmann : : json j ;
for ( auto const & kv : n_map )
j [ kv . first ] = boost : : apply_visitor ( mstch_node_to_json ( ) , kv . second ) ;
return j ;
}
nlohmann : : json operator ( ) ( mstch : : array const & n_array ) const
{
nlohmann : : json j ;
for ( auto const & v : n_array )
j . push_back ( boost : : apply_visitor ( mstch_node_to_json ( ) , v ) ) ;
return j ;
}
// catch other types that are non-numeric and not listed above
template < typename T >
typename std : : enable_if < ! std : : is_arithmetic < T > : : value , nlohmann : : json > : : type
operator ( ) ( const T & ) const {
return nlohmann : : json { } ;
}
} ;
namespace mstch
{
namespace internal
{
// add conversion from mstch::map to nlohmann::json
void
to_json ( nlohmann : : json & j , mstch : : map const & m )
{
for ( auto const & kv : m )
j [ kv . first ] = boost : : apply_visitor ( mstch_node_to_json ( ) , kv . second ) ;
}
}
}
namespace xmreg
namespace xmreg
{
{
@ -124,6 +190,8 @@ using namespace std;
using epee : : string_tools : : pod_to_hex ;
using epee : : string_tools : : pod_to_hex ;
using epee : : string_tools : : hex_to_pod ;
using epee : : string_tools : : hex_to_pod ;
/**
/**
* @ brief The tx_details struct
* @ brief The tx_details struct
*
*
@ -189,8 +257,8 @@ struct tx_details
mixin_str = std : : to_string ( mixin_no ) ;
mixin_str = std : : to_string ( mixin_no ) ;
fee_str = fmt : : format ( " {:0.6f} " , xmr_amount ) ;
fee_str = fmt : : format ( " {:0.6f} " , xmr_amount ) ;
fee_short_str = fmt : : format ( " {:0. 3 f}" , xmr_amount ) ;
fee_short_str = fmt : : format ( " {:0. 4 f}" , xmr_amount ) ;
payed_for_kB_str = fmt : : format ( " {:0. 3 f}" , payed_for_kB ) ;
payed_for_kB_str = fmt : : format ( " {:0. 4 f}" , payed_for_kB ) ;
}
}
@ -298,6 +366,7 @@ class page
bool enable_mixins_details ;
bool enable_mixins_details ;
bool enable_tx_cache ;
bool enable_tx_cache ;
bool enable_block_cache ;
bool enable_block_cache ;
bool enable_as_hex ;
bool show_cache_times ;
bool show_cache_times ;
@ -348,6 +417,7 @@ public:
cryptonote : : network_type _nettype ,
cryptonote : : network_type _nettype ,
bool _enable_pusher ,
bool _enable_pusher ,
bool _enable_js ,
bool _enable_js ,
bool _enable_as_hex ,
bool _enable_key_image_checker ,
bool _enable_key_image_checker ,
bool _enable_output_key_checker ,
bool _enable_output_key_checker ,
bool _enable_autorefresh_option ,
bool _enable_autorefresh_option ,
@ -367,6 +437,7 @@ public:
nettype { _nettype } ,
nettype { _nettype } ,
enable_pusher { _enable_pusher } ,
enable_pusher { _enable_pusher } ,
enable_js { _enable_js } ,
enable_js { _enable_js } ,
enable_as_hex { _enable_as_hex } ,
enable_key_image_checker { _enable_key_image_checker } ,
enable_key_image_checker { _enable_key_image_checker } ,
enable_output_key_checker { _enable_output_key_checker } ,
enable_output_key_checker { _enable_output_key_checker } ,
enable_autorefresh_option { _enable_autorefresh_option } ,
enable_autorefresh_option { _enable_autorefresh_option } ,
@ -512,12 +583,12 @@ public:
uint64_t local_copy_server_timestamp = server_timestamp ;
uint64_t local_copy_server_timestamp = server_timestamp ;
// number of last blocks to show
uint64_t no_of_last_blocks { no_blocks_on_index + 1 } ;
// get the current blockchain height. Just to check
// get the current blockchain height. Just to check
uint64_t height = core_storage - > get_current_blockchain_height ( ) ;
uint64_t height = core_storage - > get_current_blockchain_height ( ) ;
// number of last blocks to show
uint64_t no_of_last_blocks = std : : min ( no_blocks_on_index + 1 , height ) ;
// initalise page tempate map with basic info about blockchain
// initalise page tempate map with basic info about blockchain
mstch : : map context {
mstch : : map context {
{ " testnet " , testnet } ,
{ " testnet " , testnet } ,
@ -583,7 +654,7 @@ public:
crypto : : hash blk_hash = core_storage - > get_block_id_by_height ( i ) ;
crypto : : hash blk_hash = core_storage - > get_block_id_by_height ( i ) ;
// get block size in kB
// get block size in kB
double blk_size = static_cast < double > ( core_storage - > get_db ( ) . get_block_ size ( i ) ) / 1024.0 ;
double blk_size = static_cast < double > ( core_storage - > get_db ( ) . get_block_ weight ( i ) ) / 1024.0 ;
string blk_size_str = fmt : : format ( " {:0.2f} " , blk_size ) ;
string blk_size_str = fmt : : format ( " {:0.2f} " , blk_size ) ;
@ -716,8 +787,8 @@ public:
// get all transactions in the block found
// get all transactions in the block found
// initialize the first list with transaction for solving
// initialize the first list with transaction for solving
// the block i.e. coinbase.
// the block i.e. coinbase.
list < cryptonote : : transaction > blk_txs { blk . miner_tx } ;
vector < cryptonote : : transaction > blk_txs { blk . miner_tx } ;
list < crypto : : hash > missed_txs ;
vector < crypto : : hash > missed_txs ;
if ( ! core_storage - > get_transactions ( blk . tx_hashes , blk_txs , missed_txs ) )
if ( ! core_storage - > get_transactions ( blk . tx_hashes , blk_txs , missed_txs ) )
{
{
@ -814,14 +885,12 @@ public:
// perapre network info mstch::map for the front page
// perapre network info mstch::map for the front page
string hash_rate ;
string hash_rate ;
if ( testnet | | stagenet )
if ( current_network_info . hash_rate > 1e6 )
{
hash_rate = std : : to_string ( current_network_info . hash_rate ) + " H/s " ;
}
else
{
hash_rate = fmt : : format ( " {:0.3f} MH/s " , current_network_info . hash_rate / 1.0e6 ) ;
hash_rate = fmt : : format ( " {:0.3f} MH/s " , current_network_info . hash_rate / 1.0e6 ) ;
}
else if ( current_network_info . hash_rate > 1e3 )
hash_rate = fmt : : format ( " {:0.3f} kH/s " , current_network_info . hash_rate / 1.0e3 ) ;
else
hash_rate = fmt : : format ( " {:d} H/s " , current_network_info . hash_rate ) ;
pair < string , string > network_info_age = get_age ( local_copy_server_timestamp ,
pair < string , string > network_info_age = get_age ( local_copy_server_timestamp ,
current_network_info . info_timestamp ) ;
current_network_info . info_timestamp ) ;
@ -1157,7 +1226,7 @@ public:
}
}
// get block size in bytes
// get block size in bytes
uint64_t blk_size = core_storage - > get_db ( ) . get_block_ size ( _blk_height ) ;
uint64_t blk_size = core_storage - > get_db ( ) . get_block_ weight ( _blk_height ) ;
// miner reward tx
// miner reward tx
transaction coinbase_tx = blk . miner_tx ;
transaction coinbase_tx = blk . miner_tx ;
@ -1175,6 +1244,10 @@ public:
_blk_height , current_blockchain_height ) ;
_blk_height , current_blockchain_height ) ;
// initalise page tempate map with basic info about blockchain
// initalise page tempate map with basic info about blockchain
string blk_pow_hash_str = pod_to_hex ( get_block_longhash ( blk , _blk_height ) ) ;
uint64_t blk_difficulty = core_storage - > get_db ( ) . get_block_difficulty ( _blk_height ) ;
mstch : : map context {
mstch : : map context {
{ " testnet " , testnet } ,
{ " testnet " , testnet } ,
{ " stagenet " , stagenet } ,
{ " stagenet " , stagenet } ,
@ -1184,6 +1257,7 @@ public:
{ " blk_timestamp_epoch " , blk . timestamp } ,
{ " blk_timestamp_epoch " , blk . timestamp } ,
{ " prev_hash " , prev_hash_str } ,
{ " prev_hash " , prev_hash_str } ,
{ " next_hash " , next_hash_str } ,
{ " next_hash " , next_hash_str } ,
{ " enable_as_hex " , enable_as_hex } ,
{ " have_next_hash " , have_next_hash } ,
{ " have_next_hash " , have_next_hash } ,
{ " have_prev_hash " , have_prev_hash } ,
{ " have_prev_hash " , have_prev_hash } ,
{ " have_txs " , have_txs } ,
{ " have_txs " , have_txs } ,
@ -1192,6 +1266,8 @@ public:
{ " blk_age " , age . first } ,
{ " blk_age " , age . first } ,
{ " delta_time " , delta_time } ,
{ " delta_time " , delta_time } ,
{ " blk_nonce " , blk . nonce } ,
{ " blk_nonce " , blk . nonce } ,
{ " blk_pow_hash " , blk_pow_hash_str } ,
{ " blk_difficulty " , blk_difficulty } ,
{ " age_format " , age . second } ,
{ " age_format " , age . second } ,
{ " major_ver " , std : : to_string ( blk . major_version ) } ,
{ " major_ver " , std : : to_string ( blk . major_version ) } ,
{ " minor_ver " , std : : to_string ( blk . minor_version ) } ,
{ " minor_ver " , std : : to_string ( blk . minor_version ) } ,
@ -1524,6 +1600,152 @@ public:
return mstch : : render ( template_file [ " tx " ] , context , partials ) ;
return mstch : : render ( template_file [ " tx " ] , context , partials ) ;
}
}
string
show_tx_hex ( string tx_hash_str )
{
transaction tx ;
crypto : : hash tx_hash ;
if ( ! get_tx ( tx_hash_str , tx , tx_hash ) )
return string { " Cant get tx: " } + tx_hash_str ;
try
{
return tx_to_hex ( tx ) ;
}
catch ( std : : exception const & e )
{
cerr < < e . what ( ) < < endl ;
return string { " Failed to obtain hex of tx due to: " } + e . what ( ) ;
}
}
string
show_block_hex ( size_t block_height , bool complete_blk )
{
// get transaction
block blk ;
if ( ! mcore - > get_block_by_height ( block_height , blk ) )
{
cerr < < " Cant get block in blockchain: " < < block_height
< < " . \n Check mempool now \n " ;
}
try
{
if ( complete_blk = = false )
{
// get only block data as hex
return epee : : string_tools : : buff_to_hex_nodelimer (
t_serializable_object_to_blob ( blk ) ) ;
}
else
{
// get block_complete_entry (block and its txs) as hex
block_complete_entry complete_block_data ;
if ( ! mcore - > get_block_complete_entry ( blk , complete_block_data ) )
{
cerr < < " Failed to obtain complete block data " < < endl ;
return string { " Failed to obtain complete block data " } ;
}
std : : string complete_block_data_str ;
if ( ! epee : : serialization : : store_t_to_binary (
complete_block_data , complete_block_data_str ) )
{
cerr < < " Failed to serialize complete_block_data \n " ;
return string { " Failed to obtain complete block data " } ;
}
return epee : : string_tools
: : buff_to_hex_nodelimer ( complete_block_data_str ) ;
}
}
catch ( std : : exception const & e )
{
cerr < < e . what ( ) < < endl ;
return string { " Failed to obtain hex of a block due to: " } + e . what ( ) ;
}
}
string
show_ringmembers_hex ( string const & tx_hash_str )
{
transaction tx ;
crypto : : hash tx_hash ;
if ( ! get_tx ( tx_hash_str , tx , tx_hash ) )
return string { " Cant get tx: " } + tx_hash_str ;
vector < txin_to_key > input_key_imgs = xmreg : : get_key_images ( tx ) ;
// key: vector of absolute_offsets and associated amount (last value),
// value: vector of output_info_of_mixins
std : : map < vector < uint64_t > , vector < string > > all_mixin_outputs ;
// make timescale maps for mixins in input
for ( txin_to_key const & in_key : input_key_imgs )
{
// get absolute offsets of mixins
std : : vector < uint64_t > absolute_offsets
= cryptonote : : relative_output_offsets_to_absolute (
in_key . key_offsets ) ;
// get public keys of outputs used in the mixins that
// match to the offests
std : : vector < cryptonote : : output_data_t > mixin_outputs ;
try
{
// before proceeding with geting the outputs based on
// the amount and absolute offset
// check how many outputs there are for that amount
// go to next input if a too large offset was found
if ( are_absolute_offsets_good ( absolute_offsets , in_key )
= = false )
continue ;
core_storage - > get_db ( ) . get_output_key ( in_key . amount ,
absolute_offsets ,
mixin_outputs ) ;
}
catch ( OUTPUT_DNE const & e )
{
cerr < < " get_output_keys: " < < e . what ( ) < < endl ;
continue ;
}
// add accociated amount to these offsets so that we can differentiate
// between same offsets, but for different amounts
absolute_offsets . push_back ( in_key . amount ) ;
for ( auto const & mo : mixin_outputs )
all_mixin_outputs [ absolute_offsets ] . emplace_back ( pod_to_hex ( mo ) ) ;
} // for (txin_to_key const& in_key: input_key_imgs)
if ( all_mixin_outputs . empty ( ) )
return string { " No ring members to serialize " } ;
// archive all_mixin_outputs vector
std : : ostringstream oss ;
boost : : archive : : portable_binary_oarchive archive ( oss ) ;
archive < < all_mixin_outputs ;
// return as all_mixin_outputs vector hex
return epee : : string_tools
: : buff_to_hex_nodelimer ( oss . str ( ) ) ;
}
string
string
show_my_outputs ( string tx_hash_str ,
show_my_outputs ( string tx_hash_str ,
string xmr_address_str ,
string xmr_address_str ,
@ -1726,9 +1948,9 @@ public:
string shortcut_url = domain
string shortcut_url = domain
+ ( tx_prove ? " /prove " : " /myoutputs " )
+ ( tx_prove ? " /prove " : " /myoutputs " )
+ " / " + tx_hash_str
+ ' / ' + tx_hash_str
+ " / " + xmr_address_str
+ ' / ' + xmr_address_str
+ " / " + viewkey_str ;
+ ' / ' + viewkey_str ;
string viewkey_str_partial = viewkey_str ;
string viewkey_str_partial = viewkey_str ;
@ -1771,9 +1993,8 @@ public:
key_derivation derivation ;
key_derivation derivation ;
std : : vector < key_derivation > additional_derivations ( txd . additional_pks . size ( ) ) ;
std : : vector < key_derivation > additional_derivations ( txd . additional_pks . size ( ) ) ;
//cout << multiple_tx_secret_keys.size() << " " << txd.additional_pks.size() + 1 << '\n';
if ( tx_prove & & multiple_tx_secret_keys . size ( )
! = txd . additional_pks . size ( ) + 1 )
if ( tx_prove & & multiple_tx_secret_keys . size ( ) ! = txd . additional_pks . size ( ) + 1 )
{
{
return string ( " This transaction includes additional tx pubkeys whose "
return string ( " This transaction includes additional tx pubkeys whose "
" size doesn't match with the provided tx secret keys " ) ;
" size doesn't match with the provided tx secret keys " ) ;
@ -1813,7 +2034,8 @@ public:
if ( decrypted_payment_id8 ! = null_hash8 )
if ( decrypted_payment_id8 ! = null_hash8 )
{
{
if ( mcore - > get_device ( ) - > decrypt_payment_id ( decrypted_payment_id8 , pub_key , prv_view_key ) )
if ( mcore - > get_device ( ) - > decrypt_payment_id (
decrypted_payment_id8 , pub_key , prv_view_key ) )
{
{
context [ " decrypted_payment_id8 " ] = pod_to_hex ( decrypted_payment_id8 ) ;
context [ " decrypted_payment_id8 " ] = pod_to_hex ( decrypted_payment_id8 ) ;
}
}
@ -1850,7 +2072,8 @@ public:
bool with_additional = false ;
bool with_additional = false ;
if ( ! mine_output & & txd . additional_pks . size ( ) = = txd . output_pub_keys . size ( ) )
if ( ! mine_output & & txd . additional_pks . size ( )
= = txd . output_pub_keys . size ( ) )
{
{
derive_public_key ( additional_derivations [ output_idx ] ,
derive_public_key ( additional_derivations [ output_idx ] ,
output_idx ,
output_idx ,
@ -1875,15 +2098,17 @@ public:
bool r ;
bool r ;
r = decode_ringct ( tx . rct_signatures ,
r = decode_ringct (
with_additional ? additional_derivations [ output_idx ] : derivation ,
tx . rct_signatures ,
with_additional
? additional_derivations [ output_idx ] : derivation ,
output_idx ,
output_idx ,
tx . rct_signatures . ecdhInfo [ output_idx ] . mask ,
tx . rct_signatures . ecdhInfo [ output_idx ] . mask ,
rct_amount ) ;
rct_amount ) ;
if ( ! r )
if ( ! r )
{
{
cerr < < " \n show_my_outputs: Cant decode ringCT! " < < endl ;
cerr < < " \n show_my_outputs: Cant decode RingCT!\n " ;
}
}
outp . second = rct_amount ;
outp . second = rct_amount ;
@ -1927,9 +2152,14 @@ public:
// parefct matches must be equal to number of inputs in a tx.
// parefct matches must be equal to number of inputs in a tx.
uint64_t no_of_matched_mixins { 0 } ;
uint64_t no_of_matched_mixins { 0 } ;
// Hold all possible mixins that we found. This is only used so that
// we get number of all posibilities, and their total xmr amount
// (useful for unit testing)
// public_key , amount
std : : vector < std : : pair < crypto : : public_key , uint64_t > > all_possible_mixins ;
for ( const txin_to_key & in_key : input_key_imgs )
for ( const txin_to_key & in_key : input_key_imgs )
{
{
// get absolute offsets of mixins
// get absolute offsets of mixins
std : : vector < uint64_t > absolute_offsets
std : : vector < uint64_t > absolute_offsets
= cryptonote : : relative_output_offsets_to_absolute (
= cryptonote : : relative_output_offsets_to_absolute (
@ -1941,7 +2171,8 @@ public:
try
try
{
{
// before proceeding with geting the outputs based on the amount and absolute offset
// before proceeding with geting the outputs based on
// the amount and absolute offset
// check how many outputs there are for that amount
// check how many outputs there are for that amount
// go to next input if a too large offset was found
// go to next input if a too large offset was found
if ( are_absolute_offsets_good ( absolute_offsets , in_key ) = = false )
if ( are_absolute_offsets_good ( absolute_offsets , in_key ) = = false )
@ -2009,10 +2240,9 @@ public:
string out_msg = fmt : : format (
string out_msg = fmt : : format (
" Output with amount {:d} and index {:d} does not exist! " ,
" Output with amount {:d} and index {:d} does not exist! " ,
in_key . amount , abs_offset
in_key . amount , abs_offset ) ;
) ;
cerr < < out_msg < < endl ;
cerr < < out_msg < < ' \n ' ;
break ;
break ;
}
}
@ -2028,7 +2258,6 @@ public:
if ( ! mcore - > get_tx ( tx_out_idx . first , mixin_tx ) )
if ( ! mcore - > get_tx ( tx_out_idx . first , mixin_tx ) )
{
{
cerr < < " Cant get tx: " < < tx_out_idx . first < < endl ;
cerr < < " Cant get tx: " < < tx_out_idx . first < < endl ;
break ;
break ;
}
}
@ -2036,33 +2265,35 @@ public:
mixins . push_back ( mstch : : map {
mixins . push_back ( mstch : : map {
{ " mixin_pub_key " , out_pub_key_str } ,
{ " mixin_pub_key " , out_pub_key_str } ,
make_pair < string , mstch : : array > ( " mixin_outputs " , mstch : : array { } ) ,
make_pair < string , mstch : : array > ( " mixin_outputs "
{ " has_mixin_outputs " , false }
, mstch : : array { } ) ,
} ) ;
{ " has_mixin_outputs " , false } } ) ;
mstch : : array & mixin_outputs = boost : : get < mstch : : array > (
mstch : : array & mixin_outputs = boost : : get < mstch : : array > (
boost : : get < mstch : : map > ( mixins . back ( ) ) [ " mixin_outputs " ]
boost : : get < mstch : : map > ( mixins . back ( ) ) [ " mixin_outputs " ] ) ;
) ;
mstch : : node & has_mixin_outputs
mstch : : node & has_mixin_outputs
= boost : : get < mstch : : map > ( mixins . back ( ) ) [ " has_mixin_outputs " ] ;
= boost : : get < mstch : : map > ( mixins . back ( ) ) [ " has_mixin_outputs " ] ;
bool found_something { false } ;
bool found_something { false } ;
public_key mixin_tx_pub_key
public_key mixin_tx_pub_key
= xmreg : : get_tx_pub_key_from_received_outs ( mixin_tx ) ;
= xmreg : : get_tx_pub_key_from_received_outs ( mixin_tx ) ;
std : : vector < public_key > mixin_additional_tx_pub_keys = cryptonote : : get_additional_tx_pub_keys_from_extra ( mixin_tx ) ;
std : : vector < public_key > mixin_additional_tx_pub_keys
= cryptonote : : get_additional_tx_pub_keys_from_extra ( mixin_tx ) ;
string mixin_tx_pub_key_str = pod_to_hex ( mixin_tx_pub_key ) ;
string mixin_tx_pub_key_str = pod_to_hex ( mixin_tx_pub_key ) ;
// public transaction key is combined with our viewkey
// public transaction key is combined with our viewkey
// to create, so called, derived key.
// to create, so called, derived key.
key_derivation derivation ;
key_derivation derivation ;
std : : vector < key_derivation > additional_derivations ( mixin_additional_tx_pub_keys . size ( ) ) ;
if ( ! generate_key_derivation ( mixin_tx_pub_key , prv_view_key , derivation ) )
std : : vector < key_derivation > additional_derivations (
mixin_additional_tx_pub_keys . size ( ) ) ;
if ( ! generate_key_derivation ( mixin_tx_pub_key ,
prv_view_key , derivation ) )
{
{
cerr < < " Cant get derived key for: " < < " \n "
cerr < < " Cant get derived key for: " < < " \n "
< < " pub_tx_key: " < < mixin_tx_pub_key < < " and "
< < " pub_tx_key: " < < mixin_tx_pub_key < < " and "
@ -2072,11 +2303,13 @@ public:
}
}
for ( size_t i = 0 ; i < mixin_additional_tx_pub_keys . size ( ) ; + + i )
for ( size_t i = 0 ; i < mixin_additional_tx_pub_keys . size ( ) ; + + i )
{
{
if ( ! generate_key_derivation ( mixin_additional_tx_pub_keys [ i ] , prv_view_key , additional_derivations [ i ] ) )
if ( ! generate_key_derivation ( mixin_additional_tx_pub_keys [ i ] ,
prv_view_key ,
additional_derivations [ i ] ) )
{
{
cerr < < " Cant get derived key for: " < < " \n "
cerr < < " Cant get derived key for: " < < " \n "
< < " pub_tx_key: " < < mixin_additional_tx_pub_keys [ i ] < < " and "
< < " pub_tx_key: " < < mixin_additional_tx_pub_keys [ i ]
< < " prv_view_key" < < prv_view_key < < endl ;
< < " and prv_view_key" < < prv_view_key < < endl ;
continue ;
continue ;
}
}
@ -2090,23 +2323,28 @@ public:
mixin_outputs . push_back ( mstch : : map {
mixin_outputs . push_back ( mstch : : map {
{ " mix_tx_hash " , mixin_tx_hash_str } ,
{ " mix_tx_hash " , mixin_tx_hash_str } ,
{ " mix_tx_pub_key " , mixin_tx_pub_key_str } ,
{ " mix_tx_pub_key " , mixin_tx_pub_key_str } ,
make_pair < string , mstch : : array > ( " found_outputs " , mstch : : array { } ) ,
make_pair < string , mstch : : array > ( " found_outputs "
, mstch : : array { } ) ,
{ " has_found_outputs " , false }
{ " has_found_outputs " , false }
} ) ;
} ) ;
mstch : : array & found_outputs = boost : : get < mstch : : array > (
mstch : : array & found_outputs = boost : : get < mstch : : array > (
boost : : get < mstch : : map > ( mixin_outputs . back ( ) ) [ " found_outputs " ]
boost : : get < mstch : : map > (
mixin_outputs . back ( ) ) [ " found_outputs " ]
) ;
) ;
mstch : : node & has_found_outputs
mstch : : node & has_found_outputs
= boost : : get < mstch : : map > ( mixin_outputs . back ( ) ) [ " has_found_outputs " ] ;
= boost : : get < mstch : : map > (
mixin_outputs . back ( ) ) [ " has_found_outputs " ] ;
uint64_t ringct_amount { 0 } ;
// for each output in mixin tx, find the one from key_image
// for each output in mixin tx, find the one from key_image
// and check if its ours.
// and check if its ours.
for ( const auto & mix_out : output_pub_keys )
for ( const auto & mix_out : output_pub_keys )
{
{
txout_to_key txout_k = std : : get < 0 > ( mix_out ) ;
txout_to_key const & txout_k = std : : get < 0 > ( mix_out ) ;
uint64_t amount = std : : get < 1 > ( mix_out ) ;
uint64_t amount = std : : get < 1 > ( mix_out ) ;
uint64_t output_idx_in_tx = std : : get < 2 > ( mix_out ) ;
uint64_t output_idx_in_tx = std : : get < 2 > ( mix_out ) ;
@ -2131,14 +2369,19 @@ public:
// check if generated public key matches the current output's key
// check if generated public key matches the current output's key
bool mine_output = ( txout_k . key = = tx_pubkey_generated ) ;
bool mine_output = ( txout_k . key = = tx_pubkey_generated ) ;
bool with_additional = false ;
bool with_additional = false ;
if ( ! mine_output & & mixin_additional_tx_pub_keys . size ( ) = = output_pub_keys . size ( ) )
if ( ! mine_output & & mixin_additional_tx_pub_keys . size ( )
= = output_pub_keys . size ( ) )
{
{
derive_public_key ( additional_derivations [ output_idx_in_tx ] ,
derive_public_key ( additional_derivations [ output_idx_in_tx ] ,
output_idx_in_tx ,
output_idx_in_tx ,
address_info . address . m_spend_public_key ,
address_info . address . m_spend_public_key ,
tx_pubkey_generated ) ;
tx_pubkey_generated ) ;
mine_output = ( txout_k . key = = tx_pubkey_generated ) ;
mine_output = ( txout_k . key = = tx_pubkey_generated ) ;
with_additional = true ;
with_additional = true ;
}
}
@ -2154,16 +2397,17 @@ public:
bool r ;
bool r ;
r = decode_ringct ( mixin_tx . rct_signatures ,
r = decode_ringct (
with_additional ? additional_derivations [ output_idx_in_tx ] : derivation ,
mixin_tx . rct_signatures ,
with_additional
? additional_derivations [ output_idx_in_tx ] : derivation ,
output_idx_in_tx ,
output_idx_in_tx ,
mixin_tx . rct_signatures . ecdhInfo [ output_idx_in_tx ] . mask ,
mixin_tx . rct_signatures . ecdhInfo [ output_idx_in_tx ] . mask ,
rct_amount ) ;
rct_amount ) ;
if ( ! r )
if ( ! r )
{
cerr < < " show_my_outputs: key images: "
cerr < < " show_my_outputs: key images: Cant decode ringCT! " < < endl ;
" Cant decode RingCT! \n " ;
}
amount = rct_amount ;
amount = rct_amount ;
@ -2190,11 +2434,9 @@ public:
} ) ;
} ) ;
//cout << "txout_k.key == output_data.pubkey" << endl;
//cout << "txout_k.key == output_data.pubkey" << endl;
//cout << pod_to_hex(txout_k.key) << " == " << pod_to_hex(output_data.pubkey) << endl;
if ( mine_output )
if ( mine_output )
{
{
found_something = true ;
found_something = true ;
show_key_images = true ;
show_key_images = true ;
@ -2202,9 +2444,7 @@ public:
// public key of an outputs used in ring signature,
// public key of an outputs used in ring signature,
// matches a public key in a mixin_tx
// matches a public key in a mixin_tx
if ( txout_k . key ! = output_data . pubkey )
if ( txout_k . key ! = output_data . pubkey )
{
continue ;
continue ;
}
// sum up only first output matched found in each input
// sum up only first output matched found in each input
if ( no_of_output_matches_found = = 0 )
if ( no_of_output_matches_found = = 0 )
@ -2222,6 +2462,7 @@ public:
else if ( mixin_tx . version = = 2 ) // ringct
else if ( mixin_tx . version = = 2 ) // ringct
{
{
sum_mixin_xmr + = amount ;
sum_mixin_xmr + = amount ;
ringct_amount + = amount ;
}
}
no_of_matched_mixins + + ;
no_of_matched_mixins + + ;
@ -2247,11 +2488,9 @@ public:
// << ", key_img == input_key: " << (key_img == in_key.k_image)
// << ", key_img == input_key: " << (key_img == in_key.k_image)
// << endl;
// << endl;
no_of_output_matches_found + + ;
no_of_output_matches_found + + ;
}
} // if (mine_output)
} // for (const pair<txout_to_key, uint64_t>& mix_out: txd.output_pub_keys)
} // for (const pair<txout_to_key, uint64_t>& mix_out: txd.output_pub_keys)
@ -2259,11 +2498,16 @@ public:
has_mixin_outputs = found_something ;
has_mixin_outputs = found_something ;
+ + count ;
// all_possible_mixins_amount += amount;
} // for (const cryptonote::output_data_t& output_data: mixin_outputs)
if ( found_something )
all_possible_mixins . push_back (
{ mixin_tx_pub_key ,
in_key . amount = = 0 ? ringct_amount : in_key . amount } ) ;
+ + count ;
} // for (const cryptonote::output_data_t& output_data: mixin_outputs)
} // for (const txin_to_key& in_key: input_key_imgs)
} // for (const txin_to_key& in_key: input_key_imgs)
@ -2283,6 +2527,23 @@ public:
uint64_t possible_spending { 0 } ;
uint64_t possible_spending { 0 } ;
//cout << "\nall_possible_mixins: " << all_possible_mixins.size() << '\n';
// useful for unit testing as it provides total xmr sum
// of possible mixins
uint64_t all_possible_mixins_amount1 { 0 } ;
for ( auto & p : all_possible_mixins )
all_possible_mixins_amount1 + = p . second ;
//cout << "\all_possible_mixins_amount: " << all_possible_mixins_amount1 << '\n';
//cout << "\nmixins: " << mix << '\n';
context [ " no_all_possible_mixins " ] = static_cast < uint64_t > ( all_possible_mixins . size ( ) ) ;
context [ " all_possible_mixins_amount " ] = all_possible_mixins_amount1 ;
// show spending only if sum of mixins is more than
// show spending only if sum of mixins is more than
// what we get + fee, and number of perferctly matched
// what we get + fee, and number of perferctly matched
// mixis is equal to number of inputs
// mixis is equal to number of inputs
@ -2657,7 +2918,7 @@ public:
" Its prefix is: {:s} " ,
" Its prefix is: {:s} " ,
data_prefix ) ;
data_prefix ) ;
cout < < msg < < endl ;
cerr < < msg < < endl ;
return string ( msg ) ;
return string ( msg ) ;
}
}
@ -2666,6 +2927,11 @@ public:
crypto : : hash tx_prefix_hash_from_blob ;
crypto : : hash tx_prefix_hash_from_blob ;
cryptonote : : transaction tx_from_blob ;
cryptonote : : transaction tx_from_blob ;
// std::stringstream ss;
// ss << tx_data_blob;
// binary_archive<false> ba(ss);
// serialization::serialize(ba, tx_from_blob);
if ( ! cryptonote : : parse_and_validate_tx_from_blob ( tx_data_blob ,
if ( ! cryptonote : : parse_and_validate_tx_from_blob ( tx_data_blob ,
tx_from_blob ,
tx_from_blob ,
tx_hash_from_blob ,
tx_hash_from_blob ,
@ -3965,6 +4231,8 @@ public:
}
}
/*
/*
* Lets use this json api convention for success and error
* Lets use this json api convention for success and error
* https : //labs.omniti.com/labs/jsend
* https : //labs.omniti.com/labs/jsend
@ -4214,6 +4482,56 @@ public:
return j_response ;
return j_response ;
}
}
json
json_detailedtransaction ( string tx_hash_str )
{
json j_response {
{ " status " , " fail " } ,
{ " data " , json { } }
} ;
json & j_data = j_response [ " data " ] ;
transaction tx ;
bool found_in_mempool { false } ;
uint64_t tx_timestamp { 0 } ;
string error_message ;
if ( ! find_tx_for_json ( tx_hash_str , tx , found_in_mempool , tx_timestamp , error_message ) )
{
j_data [ " title " ] = error_message ;
return j_response ;
}
// get detailed tx information
mstch : : map tx_context = construct_tx_context ( tx , 1 /*full detailed */ ) ;
// remove some page specific and html stuff
tx_context . erase ( " timescales " ) ;
tx_context . erase ( " tx_json " ) ;
tx_context . erase ( " tx_json_raw " ) ;
tx_context . erase ( " enable_mixins_details " ) ;
tx_context . erase ( " with_ring_signatures " ) ;
tx_context . erase ( " show_part_of_inputs " ) ;
tx_context . erase ( " show_more_details_link " ) ;
tx_context . erase ( " max_no_of_inputs_to_show " ) ;
tx_context . erase ( " inputs_xmr_sum_not_zero " ) ;
tx_context . erase ( " have_raw_tx " ) ;
tx_context . erase ( " have_any_unknown_amount " ) ;
tx_context . erase ( " has_error " ) ;
tx_context . erase ( " error_msg " ) ;
tx_context . erase ( " server_time " ) ;
tx_context . erase ( " construction_time " ) ;
j_data = tx_context ;
j_response [ " status " ] = " success " ;
return j_response ;
}
/*
/*
* Lets use this json api convention for success and error
* Lets use this json api convention for success and error
* https : //labs.omniti.com/labs/jsend
* https : //labs.omniti.com/labs/jsend
@ -4293,7 +4611,7 @@ public:
// get block size in bytes
// get block size in bytes
uint64_t blk_size = core_storage - > get_db ( ) . get_block_ size ( block_height ) ;
uint64_t blk_size = core_storage - > get_db ( ) . get_block_ weight ( block_height ) ;
// miner reward tx
// miner reward tx
transaction coinbase_tx = blk . miner_tx ;
transaction coinbase_tx = blk . miner_tx ;
@ -4522,7 +4840,7 @@ public:
}
}
// get block size in bytes
// get block size in bytes
double blk_size = core_storage - > get_db ( ) . get_block_ size ( i ) ;
double blk_size = core_storage - > get_db ( ) . get_block_ weight ( i ) ;
crypto : : hash blk_hash = core_storage - > get_block_id_by_height ( i ) ;
crypto : : hash blk_hash = core_storage - > get_block_id_by_height ( i ) ;
@ -4541,8 +4859,8 @@ public:
json & j_txs = j_blocks . back ( ) [ " txs " ] ;
json & j_txs = j_blocks . back ( ) [ " txs " ] ;
list < cryptonote : : transaction > blk_txs { blk . miner_tx } ;
vector < cryptonote : : transaction > blk_txs { blk . miner_tx } ;
list < crypto : : hash > missed_txs ;
vector < crypto : : hash > missed_txs ;
if ( ! core_storage - > get_transactions ( blk . tx_hashes , blk_txs , missed_txs ) )
if ( ! core_storage - > get_transactions ( blk . tx_hashes , blk_txs , missed_txs ) )
{
{
@ -5107,8 +5425,8 @@ public:
}
}
// get transactions in the given block
// get transactions in the given block
list < cryptonote : : transaction > blk_txs { blk . miner_tx } ;
vector < cryptonote : : transaction > blk_txs { blk . miner_tx } ;
list < crypto : : hash > missed_txs ;
vector < crypto : : hash > missed_txs ;
if ( ! core_storage - > get_transactions ( blk . tx_hashes , blk_txs , missed_txs ) )
if ( ! core_storage - > get_transactions ( blk . tx_hashes , blk_txs , missed_txs ) )
{
{
@ -5225,7 +5543,7 @@ public:
string emission_blk_no = std : : to_string ( current_values . blk_no - 1 ) ;
string emission_blk_no = std : : to_string ( current_values . blk_no - 1 ) ;
string emission_coinbase = xmr_amount_to_str ( current_values . coinbase , " {:0.3f} " ) ;
string emission_coinbase = xmr_amount_to_str ( current_values . coinbase , " {:0.3f} " ) ;
string emission_fee = xmr_amount_to_str ( current_values . fee , " {:0. 3 f}" , false ) ;
string emission_fee = xmr_amount_to_str ( current_values . fee , " {:0. 4 f}" , false ) ;
j_data = json {
j_data = json {
{ " blk_no " , current_values . blk_no - 1 } ,
{ " blk_no " , current_values . blk_no - 1 } ,
@ -5893,6 +6211,7 @@ private:
context [ " inputs_xmr_sum " ] = xmreg : : xmr_amount_to_str ( inputs_xmr_sum ) ;
context [ " inputs_xmr_sum " ] = xmreg : : xmr_amount_to_str ( inputs_xmr_sum ) ;
context [ " server_time " ] = server_time_str ;
context [ " server_time " ] = server_time_str ;
context [ " enable_mixins_details " ] = detailed_view ;
context [ " enable_mixins_details " ] = detailed_view ;
context [ " enable_as_hex " ] = enable_as_hex ;
context [ " show_part_of_inputs " ] = show_part_of_inputs ;
context [ " show_part_of_inputs " ] = show_part_of_inputs ;
context [ " max_no_of_inputs_to_show " ] = max_no_of_inputs_to_show ;
context [ " max_no_of_inputs_to_show " ] = max_no_of_inputs_to_show ;
@ -6140,6 +6459,32 @@ private:
}
}
bool
find_tx_for_json (
string const & tx_hash_str ,
transaction & tx ,
bool & found_in_mempool ,
uint64_t & tx_timestamp ,
string & error_message )
{
// parse tx hash string to hash object
crypto : : hash tx_hash ;
if ( ! xmreg : : parse_str_secret_key ( tx_hash_str , tx_hash ) )
{
error_message = fmt : : format ( " Cant parse tx hash: {:s} " , tx_hash_str ) ;
return false ;
}
if ( ! find_tx ( tx_hash , tx , found_in_mempool , tx_timestamp ) )
{
error_message = fmt : : format ( " Cant find tx hash: {:s} " , tx_hash_str ) ;
return false ;
}
return true ;
}
bool
bool
search_mempool ( crypto : : hash tx_hash ,
search_mempool ( crypto : : hash tx_hash ,
vector < MempoolStatus : : mempool_tx > & found_txs )
vector < MempoolStatus : : mempool_tx > & found_txs )
@ -6344,6 +6689,41 @@ private:
} } ;
} } ;
}
}
bool
get_tx ( string const & tx_hash_str ,
transaction & tx ,
crypto : : hash & tx_hash )
{
if ( ! epee : : string_tools : : hex_to_pod ( tx_hash_str , tx_hash ) )
{
string msg = fmt : : format ( " Cant parse {:s} as tx hash! " , tx_hash_str ) ;
cerr < < msg < < endl ;
return false ;
}
// get transaction
if ( ! mcore - > get_tx ( tx_hash , tx ) )
{
cerr < < " Cant get tx in blockchain: " < < tx_hash
< < " . \n Check mempool now \n " ;
vector < MempoolStatus : : mempool_tx > found_txs ;
search_mempool ( tx_hash , found_txs ) ;
if ( found_txs . empty ( ) )
{
// tx is nowhere to be found :-(
return false ;
}
tx = found_txs . at ( 0 ) . tx ;
}
return true ;
}
void
void
add_js_files ( mstch : : map & context )
add_js_files ( mstch : : map & context )
{
{