Compare commits

...

10 Commits

Author SHA1 Message Date
lza_menace 4513845a19 wowify 11 months ago
moneroexamples d66972065f
Merge pull request #287 from erciccione/patch-1
css: fix overflowing text in cells
1 year ago
ErC b3b84af960
css: fix overflowing text in cells
Long strings without spaces don't get wrapped and will result in the text overflowing outside of the cell and messing up the structure of the page. See for example [the tx_extra field in this transaction](https://xmrchain.net/search?value=1ecce7898a4e1c7334241c834ba4866d6b511ee896c318b3445aea1cc926e40f).

This small patch makes long text wrap inside the given limit, which would be otherwise "ignored".
1 year ago
moneroexamples 6c5d7b17c6
Merge pull request #281 from moneroexamples/devel
Devel
1 year ago
moneroexamples 188d5471b1 readme updated 1 year ago
moneroexamples 5a54019b0e add msg about disabled randomx 1 year ago
moneroexamples f841622349 temp fix: https://github.com/moneroexamples/onion-monero-blockchain-explorer/issues/279
Temprorry disabled randomx code generation till figure out
what exactly is happening
1 year ago
moneroexamples 31490c319d per_kb_fee_estimated added 1 year ago
moneroexamples aa96ce2927
Merge pull request #274 from moneroexamples/devel
For monero release v0.18
2 years ago
moneroexamples dc2d06b141 Update dockerfile 2 years ago

@ -14,7 +14,7 @@ endif()
if (NOT MONERO_DIR)
set(MONERO_DIR ~/monero)
set(MONERO_DIR ~/wownero)
endif()
message(STATUS MONERO_DIR ": ${MONERO_DIR}")

@ -3,7 +3,7 @@ FROM ubuntu:20.04 as builder
# Set Monero branch/tag to be used for monerod compilation
ARG MONERO_BRANCH=release-v18
ARG MONERO_BRANCH=release-v0.18
# Added DEBIAN_FRONTEND=noninteractive to workaround tzdata prompt on installation
ENV DEBIAN_FRONTEND="noninteractive"

@ -72,7 +72,6 @@ The key features of the Onion Monero Blockchain Explorer are:
- can provide total amount of all miner fees,
- decoding encrypted payment id,
- decoding outputs and proving txs sent to sub-address.
- listing RandomX code for each block
## Development branch
@ -244,7 +243,6 @@ xmrblocks, Onion Monero Blockchain Explorer:
-t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain
-s [ --stagenet ] [=arg(=1)] (=0) use stagenet blockchain
--enable-pusher [=arg(=1)] (=0) enable signed transaction pusher
--enable-randomx [=arg(=1)] (=0) enable generation of randomx code
--enable-mixin-details [=arg(=1)] (=0)
enable mixin details for key images,
e.g., timescale, mixin of mixins, in tx

@ -45,7 +45,7 @@ foreach (l ${LIBS})
find_library(Xmr_${L}_LIBRARY
NAMES ${l}
PATHS ${CMAKE_LIBRARY_PATH}
PATH_SUFFIXES "/src/${l}" "/src/" "/external/db_drivers/lib${l}" "/lib" "/src/crypto" "/src/crypto/wallet" "/contrib/epee/src" "/external/easylogging++/" "/external/${l}" "external/miniupnp/miniupnpc"
PATH_SUFFIXES "/src/${l}" "/src/" "/external/db_drivers/lib${l}" "/lib" "/src/crypto" "/src/crypto/wallet" "/contrib/epee/src" "/external/easylogging++/" "/external/${l}" "external/miniupnp/miniupnpc" "/external/randomwow"
NO_DEFAULT_PATH
)
@ -83,7 +83,7 @@ include_directories(
${MONERO_SOURCE_DIR}/src/crypto
${MONERO_SOURCE_DIR}/src/crypto/wallet
${MONERO_SOURCE_DIR}/external
${MONERO_SOURCE_DIR}/external/randomx/src
${MONERO_SOURCE_DIR}/external/randomwow/src
${MONERO_SOURCE_DIR}/build
${MONERO_SOURCE_DIR}/external/easylogging++
${MONERO_SOURCE_DIR}/contrib/epee/include

@ -105,6 +105,12 @@ main(int ac, const char* av[])
bool enable_as_hex {*enable_as_hex_opt};
bool enable_emission_monitor {*enable_emission_monitor_opt};
//temprorary disable randomx
if (enable_randomx == true) {
cout << "Support for randomx code is disabled due to issues with it"<< endl;
enable_randomx = false;
}
// set monero log output level
uint32_t log_level = 0;
mlog_configure("", true);
@ -208,9 +214,9 @@ main(int ac, const char* av[])
string daemon_url {*daemon_url_opt};
if (testnet && daemon_url == "127.0.0.1:18081")
daemon_url = "127.0.0.1:28081";
if (stagenet && daemon_url == "127.0.0.1:18081")
if (testnet && daemon_url == "127.0.0.1:34568")
daemon_url = "127.0.0.1:11181";
if (stagenet && daemon_url == "127.0.0.1:34568")
daemon_url = "127.0.0.1:38081";
uint64_t mempool_info_timeout {5000};
@ -484,7 +490,7 @@ main(int ac, const char* av[])
|| post_body.count("txprvkey") == 0
|| post_body.count("txhash") == 0)
{
return string("xmr address, tx private key or "
return string("wow address, tx private key or "
"tx hash not provided");
}

@ -18,7 +18,7 @@ namespace xmreg
p.add("txhash", -1);
options_description desc(
"xmrblocks, Onion Monero Blockchain Explorer");
"xmrblocks, Onion Wownero Blockchain Explorer");
desc.add_options()
("help,h", value<bool>()->default_value(false)->implicit_value(true),
@ -44,7 +44,7 @@ namespace xmreg
("enable-autorefresh-option", value<bool>()->default_value(false)->implicit_value(true),
"enable users to have the index page on autorefresh")
("enable-emission-monitor", value<bool>()->default_value(false)->implicit_value(true),
"enable Monero total emission monitoring thread")
"enable Wownero total emission monitoring thread")
("port,p", value<string>()->default_value("8081"),
"default explorer port")
("bindaddr,x", value<string>()->default_value("0.0.0.0"),
@ -64,7 +64,7 @@ namespace xmreg
("concurrency,c", value<size_t>()->default_value(0),
"number of threads handling http queries. Default is 0 which means it is based you on the cpu")
("bc-path,b", value<string>(),
"path to lmdb folder of the blockchain, e.g., ~/.bitmonero/lmdb")
"path to lmdb folder of the blockchain, e.g., ~/.wownero/lmdb")
("ssl-crt-file", value<string>(),
"path to crt file for ssl (https) functionality")
("ssl-key-file", value<string>(),
@ -72,7 +72,7 @@ namespace xmreg
("daemon-login", value<string>(),
"Specify username[:password] for daemon RPC client")
("daemon-url,d", value<string>()->default_value("127.0.0.1:18081"),
"Monero daemon url")
"Wownero daemon url")
("enable-mixin-guess", value<bool>()->default_value(false)->implicit_value(true),
"enable guessing real outputs in key images based on viewkey");

@ -297,13 +297,13 @@ CurrentBlockchainStatus::is_thread_running()
return is_running;
}
bf::path CurrentBlockchainStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"};
bf::path CurrentBlockchainStatus::blockchain_path {"/home/mwo/.wownero/lmdb"};
cryptonote::network_type CurrentBlockchainStatus::nettype {cryptonote::network_type::MAINNET};
string CurrentBlockchainStatus::output_file {"emission_amount.txt"};
string CurrentBlockchainStatus::daemon_url {"http:://127.0.0.1:18081"};
string CurrentBlockchainStatus::daemon_url {"http:://127.0.0.1:234568"};
uint64_t CurrentBlockchainStatus::blockchain_chunk_size {10000};

@ -328,8 +328,8 @@ MempoolStatus::is_thread_running()
return is_running;
}
bf::path MempoolStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"};
string MempoolStatus::daemon_url {"http:://127.0.0.1:18081"};
bf::path MempoolStatus::blockchain_path {"/home/mwo/.wownero/lmdb"};
string MempoolStatus::daemon_url {"http:://127.0.0.1:34568"};
cryptonote::network_type MempoolStatus::nettype {cryptonote::network_type::MAINNET};
atomic<bool> MempoolStatus::is_running {false};
boost::thread MempoolStatus::m_thread;

@ -1,4 +1,4 @@
// Copyright (c) 2019, The Monero Project
// Copyright (c) 2019-2022, The Monero Project
//
// All rights reserved.
//
@ -34,6 +34,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include "randomx.h"
#include "c_threads.h"
@ -42,31 +43,38 @@
#define RX_LOGCAT "randomx"
static CTHR_RWLOCK_TYPE main_dataset_lock = CTHR_RWLOCK_INIT;
static CTHR_RWLOCK_TYPE main_cache_lock = CTHR_RWLOCK_INIT;
static randomx_dataset *main_dataset = NULL;
static randomx_cache *main_cache = NULL;
static char main_seedhash[HASH_SIZE];
static int main_seedhash_set = 0;
static CTHR_RWLOCK_TYPE secondary_cache_lock = CTHR_RWLOCK_INIT;
static randomx_cache *secondary_cache = NULL;
static char secondary_seedhash[HASH_SIZE];
static int secondary_seedhash_set = 0;
#if defined(_MSC_VER)
#define THREADV __declspec(thread)
#else
#define THREADV __thread
#endif
typedef struct rx_state {
CTHR_MUTEX_TYPE rs_mutex;
char rs_hash[32];
uint64_t rs_height;
randomx_cache *rs_cache;
} rx_state;
THREADV randomx_vm *main_vm_full = NULL;
static THREADV randomx_vm *main_vm_light = NULL;
static THREADV randomx_vm *secondary_vm_light = NULL;
static CTHR_MUTEX_TYPE rx_mutex = CTHR_MUTEX_INIT;
static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT;
static THREADV uint32_t miner_thread = 0;
static rx_state rx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}};
static randomx_dataset *rx_dataset;
static uint64_t rx_dataset_height;
THREADV randomx_vm *rx_vm = NULL;
static THREADV int rx_toggle;
static bool is_main(const char* seedhash) { return main_seedhash_set && (memcmp(seedhash, main_seedhash, HASH_SIZE) == 0); }
static bool is_secondary(const char* seedhash) { return secondary_seedhash_set && (memcmp(seedhash, secondary_seedhash, HASH_SIZE) == 0); }
static void local_abort(const char *msg)
{
merror(RX_LOGCAT, "%s", msg);
fprintf(stderr, "%s\n", msg);
#ifdef NDEBUG
_exit(1);
@ -75,108 +83,196 @@ static void local_abort(const char *msg)
#endif
}
/**
* @brief uses cpuid to determine if the CPU supports the AES instructions
* @return true if the CPU supports AES, false otherwise
*/
static void hash2hex(const char* hash, char* hex) {
const char* d = "0123456789abcdef";
for (int i = 0; i < HASH_SIZE; ++i) {
const uint8_t b = hash[i];
hex[i * 2 + 0] = d[b >> 4];
hex[i * 2 + 1] = d[b & 15];
}
hex[HASH_SIZE * 2] = '\0';
}
static inline int force_software_aes(void)
{
static int use = -1;
static inline int disabled_flags(void) {
static int flags = -1;
if (use != -1)
return use;
if (flags != -1) {
return flags;
}
const char *env = getenv("MONERO_USE_SOFTWARE_AES");
const char *env = getenv("MONERO_RANDOMX_UMASK");
if (!env) {
use = 0;
}
else if (!strcmp(env, "0") || !strcmp(env, "no")) {
use = 0;
flags = 0;
}
else {
use = 1;
char* endptr;
long int value = strtol(env, &endptr, 0);
if (endptr != env && value >= 0 && value < INT_MAX) {
flags = value;
}
else {
flags = 0;
}
}
return use;
return flags;
}
static void cpuid(int CPUInfo[4], int InfoType)
{
#if defined(__x86_64__)
__asm __volatile__
(
"cpuid":
"=a" (CPUInfo[0]),
"=b" (CPUInfo[1]),
"=c" (CPUInfo[2]),
"=d" (CPUInfo[3]) :
"a" (InfoType), "c" (0)
);
#endif
static inline int enabled_flags(void) {
static int flags = -1;
if (flags != -1) {
return flags;
}
flags = randomx_get_flags();
return flags;
}
static inline int check_aes_hw(void)
#define SEEDHASH_EPOCH_BLOCKS 2048 /* Must be same as BLOCKS_SYNCHRONIZING_MAX_COUNT in cryptonote_config.h */
#define SEEDHASH_EPOCH_LAG 64
static inline int is_power_of_2(uint64_t n) { return n && (n & (n-1)) == 0; }
static int get_seedhash_epoch_lag(void)
{
#if defined(__x86_64__)
int cpuid_results[4];
static int supported = -1;
static unsigned int lag = (unsigned int)-1;
if (lag != (unsigned int)-1)
return lag;
const char *e = getenv("SEEDHASH_EPOCH_LAG");
if (e)
{
lag = atoi(e);
if (lag > SEEDHASH_EPOCH_LAG || !is_power_of_2(lag))
lag = SEEDHASH_EPOCH_LAG;
}
else
{
lag = SEEDHASH_EPOCH_LAG;
}
return lag;
}
if(supported >= 0)
return supported;
static unsigned int get_seedhash_epoch_blocks(void)
{
static unsigned int blocks = (unsigned int)-1;
if (blocks != (unsigned int)-1)
return blocks;
const char *e = getenv("SEEDHASH_EPOCH_BLOCKS");
if (e)
{
blocks = atoi(e);
if (blocks < 2 || blocks > SEEDHASH_EPOCH_BLOCKS || !is_power_of_2(blocks))
blocks = SEEDHASH_EPOCH_BLOCKS;
}
else
{
blocks = SEEDHASH_EPOCH_BLOCKS;
}
return blocks;
}
cpuid(cpuid_results,1);
return supported = cpuid_results[2] & (1 << 25);
#else
return 0;
#endif
uint64_t me_rx_seedheight(const uint64_t height) {
const uint64_t seedhash_epoch_lag = get_seedhash_epoch_lag();
const uint64_t seedhash_epoch_blocks = get_seedhash_epoch_blocks();
uint64_t s_height = (height <= seedhash_epoch_blocks+seedhash_epoch_lag) ? 0 :
(height - seedhash_epoch_lag - 1) & ~(seedhash_epoch_blocks-1);
return s_height;
}
static volatile int use_rx_jit_flag = -1;
void me_rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nextheight) {
*seedheight = me_rx_seedheight(height);
*nextheight = me_rx_seedheight(height + get_seedhash_epoch_lag());
}
static inline int use_rx_jit(void)
static void rx_alloc_dataset(randomx_flags flags, randomx_dataset** dataset, int ignore_env)
{
#if defined(__x86_64__)
if (use_rx_jit_flag != -1)
return use_rx_jit_flag;
if (*dataset) {
return;
}
const char *env = getenv("MONERO_USE_RX_JIT");
if (!env) {
use_rx_jit_flag = 1;
if (disabled_flags() & RANDOMX_FLAG_FULL_MEM) {
static int shown = 0;
if (!shown) {
shown = 1;
minfo(RX_LOGCAT, "RandomX dataset is disabled by MONERO_RANDOMX_UMASK environment variable.");
}
return;
}
else if (!strcmp(env, "0") || !strcmp(env, "no")) {
use_rx_jit_flag = 0;
if (!ignore_env && !getenv("MONERO_RANDOMX_FULL_MEM")) {
static int shown = 0;
if (!shown) {
shown = 1;
minfo(RX_LOGCAT, "RandomX dataset is not enabled by default. Use MONERO_RANDOMX_FULL_MEM environment variable to enable it.");
}
return;
}
else {
use_rx_jit_flag = 1;
*dataset = randomx_alloc_dataset((flags | RANDOMX_FLAG_LARGE_PAGES) & ~disabled_flags());
if (!*dataset) {
mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset using large pages");
*dataset = randomx_alloc_dataset(flags & ~disabled_flags());
if (!*dataset) {
merror(RX_LOGCAT, "Couldn't allocate RandomX dataset");
}
}
return use_rx_jit_flag;
#else
return 0;
#endif
}
#define SEEDHASH_EPOCH_BLOCKS 2048 /* Must be same as BLOCKS_SYNCHRONIZING_MAX_COUNT in cryptonote_config.h */
#define SEEDHASH_EPOCH_LAG 64
static void rx_alloc_cache(randomx_flags flags, randomx_cache** cache)
{
if (*cache) {
return;
}
void me_rx_reorg(const uint64_t split_height) {
int i;
CTHR_MUTEX_LOCK(rx_mutex);
for (i=0; i<2; i++) {
if (split_height < rx_s[i].rs_height)
rx_s[i].rs_height = 1; /* set to an invalid seed height */
*cache = randomx_alloc_cache((flags | RANDOMX_FLAG_LARGE_PAGES) & ~disabled_flags());
if (!*cache) {
mwarning(RX_LOGCAT, "Couldn't allocate RandomX cache using large pages");
*cache = randomx_alloc_cache(flags & ~disabled_flags());
if (!*cache) local_abort("Couldn't allocate RandomX cache");
}
CTHR_MUTEX_UNLOCK(rx_mutex);
}
uint64_t me_rx_seedheight(const uint64_t height) {
uint64_t s_height = (height <= SEEDHASH_EPOCH_BLOCKS+SEEDHASH_EPOCH_LAG) ? 0 :
(height - SEEDHASH_EPOCH_LAG - 1) & ~(SEEDHASH_EPOCH_BLOCKS-1);
return s_height;
static void rx_init_full_vm(randomx_flags flags, randomx_vm** vm)
{
if (*vm || !main_dataset || (disabled_flags() & RANDOMX_FLAG_FULL_MEM)) {
return;
}
if ((flags & RANDOMX_FLAG_JIT) && !miner_thread) {
flags |= RANDOMX_FLAG_SECURE;
}
*vm = randomx_create_vm((flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_FULL_MEM) & ~disabled_flags(), NULL, main_dataset);
if (!*vm) {
mwarning(RX_LOGCAT, "Couldn't allocate RandomX full VM using large pages");
*vm = randomx_create_vm((flags | RANDOMX_FLAG_FULL_MEM) & ~disabled_flags(), NULL, main_dataset);
if (!*vm) {
merror(RX_LOGCAT, "Couldn't allocate RandomX full VM");
}
}
}
void me_rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nextheight) {
*seedheight = me_rx_seedheight(height);
*nextheight = me_rx_seedheight(height + SEEDHASH_EPOCH_LAG);
static void rx_init_light_vm(randomx_flags flags, randomx_vm** vm, randomx_cache* cache)
{
if (*vm) {
randomx_vm_set_cache(*vm, cache);
return;
}
if ((flags & RANDOMX_FLAG_JIT) && !miner_thread) {
flags |= RANDOMX_FLAG_SECURE;
}
flags &= ~RANDOMX_FLAG_FULL_MEM;
*vm = randomx_create_vm((flags | RANDOMX_FLAG_LARGE_PAGES) & ~disabled_flags(), cache, NULL);
if (!*vm) {
mwarning(RX_LOGCAT, "Couldn't allocate RandomX light VM using large pages");
*vm = randomx_create_vm(flags & ~disabled_flags(), cache, NULL);
if (!*vm) local_abort("Couldn't allocate RandomX light VM");
}
}
typedef struct seedinfo {
@ -185,165 +281,232 @@ typedef struct seedinfo {
unsigned long si_count;
} seedinfo;
static CTHR_THREAD_RTYPE rx_seedthread(void *arg) {
static CTHR_THREAD_RTYPE me_rx_seedthread(void *arg) {
seedinfo *si = arg;
randomx_init_dataset(rx_dataset, si->si_cache, si->si_start, si->si_count);
randomx_init_dataset(main_dataset, si->si_cache, si->si_start, si->si_count);
CTHR_THREAD_RETURN;
}
static void rx_initdata(randomx_cache *rs_cache, const int miners, const uint64_t seedheight) {
if (miners > 1) {
unsigned long delta = randomx_dataset_item_count() / miners;
unsigned long start = 0;
int i;
seedinfo *si;
CTHR_THREAD_TYPE *st;
si = malloc(miners * sizeof(seedinfo));
if (si == NULL)
local_abort("Couldn't allocate RandomX mining threadinfo");
st = malloc(miners * sizeof(CTHR_THREAD_TYPE));
if (st == NULL) {
free(si);
local_abort("Couldn't allocate RandomX mining threadlist");
}
for (i=0; i<miners-1; i++) {
si[i].si_cache = rs_cache;
si[i].si_start = start;
si[i].si_count = delta;
start += delta;
}
si[i].si_cache = rs_cache;
static void me_rx_init_dataset(size_t max_threads) {
if (!main_dataset) {
return;
}
// leave 2 CPU cores for other tasks
const size_t num_threads = (max_threads < 4) ? 1 : (max_threads - 2);
seedinfo* si = malloc(num_threads * sizeof(seedinfo));
if (!si) local_abort("Couldn't allocate RandomX mining threadinfo");
const uint32_t delta = randomx_dataset_item_count() / num_threads;
uint32_t start = 0;
const size_t n1 = num_threads - 1;
for (size_t i = 0; i < n1; ++i) {
si[i].si_cache = main_cache;
si[i].si_start = start;
si[i].si_count = randomx_dataset_item_count() - start;
for (i=1; i<miners; i++) {
CTHR_THREAD_CREATE(st[i], rx_seedthread, &si[i]);
}
randomx_init_dataset(rx_dataset, rs_cache, 0, si[0].si_count);
for (i=1; i<miners; i++) {
CTHR_THREAD_JOIN(st[i]);
si[i].si_count = delta;
start += delta;
}
si[n1].si_cache = main_cache;
si[n1].si_start = start;
si[n1].si_count = randomx_dataset_item_count() - start;
CTHR_THREAD_TYPE *st = malloc(num_threads * sizeof(CTHR_THREAD_TYPE));
if (!st) local_abort("Couldn't allocate RandomX mining threadlist");
CTHR_RWLOCK_LOCK_READ(main_cache_lock);
for (size_t i = 0; i < n1; ++i) {
if (!CTHR_THREAD_CREATE(st[i], me_rx_seedthread, &si[i])) {
local_abort("Couldn't start RandomX seed thread");
}
free(st);
free(si);
} else {
randomx_init_dataset(rx_dataset, rs_cache, 0, randomx_dataset_item_count());
}
rx_dataset_height = seedheight;
me_rx_seedthread(&si[n1]);
for (size_t i = 0; i < n1; ++i) CTHR_THREAD_JOIN(st[i]);
CTHR_RWLOCK_UNLOCK_READ(main_cache_lock);
free(st);
free(si);
minfo(RX_LOGCAT, "RandomX dataset initialized");
}
void me_rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length,
char *hash, int miners, int is_alt) {
uint64_t s_height = me_rx_seedheight(mainheight);
int changed = 0;
int toggle = is_alt ? s_height : seedheight;
randomx_flags flags = RANDOMX_FLAG_DEFAULT;
rx_state *rx_sp;
randomx_cache *cache;
toggle = (toggle & SEEDHASH_EPOCH_BLOCKS) != 0;
CTHR_MUTEX_LOCK(rx_mutex);
/* if alt block but with same seed as mainchain, no need for alt cache */
if (is_alt && s_height == seedheight && !memcmp(rx_s[toggle].rs_hash, seedhash, sizeof(rx_s[toggle].rs_hash)))
is_alt = 0;
/* RPC could request an earlier block on mainchain */
if (!is_alt && s_height > seedheight)
is_alt = 1;
toggle ^= (is_alt != 0);
if (toggle != rx_toggle)
changed = 1;
rx_toggle = toggle;
rx_sp = &rx_s[toggle];
CTHR_MUTEX_LOCK(rx_sp->rs_mutex);
CTHR_MUTEX_UNLOCK(rx_mutex);
cache = rx_sp->rs_cache;
if (cache == NULL) {
if (use_rx_jit())
flags |= RANDOMX_FLAG_JIT;
if (cache == NULL) {
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
if (cache == NULL) {
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX cache");
cache = randomx_alloc_cache(flags);
}
if (cache == NULL)
local_abort("Couldn't allocate RandomX cache");
}
typedef struct thread_info {
char seedhash[HASH_SIZE];
size_t max_threads;
} thread_info;
static CTHR_THREAD_RTYPE me_rx_set_main_seedhash_thread(void *arg) {
thread_info* info = arg;
CTHR_RWLOCK_LOCK_WRITE(main_dataset_lock);
CTHR_RWLOCK_LOCK_WRITE(main_cache_lock);
// Double check that seedhash wasn't already updated
if (is_main(info->seedhash)) {
CTHR_RWLOCK_UNLOCK_WRITE(main_cache_lock);
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
free(info);
CTHR_THREAD_RETURN;
}
if (rx_sp->rs_height != seedheight || rx_sp->rs_cache == NULL || memcmp(seedhash, rx_sp->rs_hash, sizeof(rx_sp->rs_hash))) {
randomx_init_cache(cache, seedhash, 32);
rx_sp->rs_cache = cache;
rx_sp->rs_height = seedheight;
memcpy(rx_sp->rs_hash, seedhash, sizeof(rx_sp->rs_hash));
changed = 1;
}
if (rx_vm == NULL) {
randomx_flags flags = RANDOMX_FLAG_DEFAULT;
if (use_rx_jit()) {
flags |= RANDOMX_FLAG_JIT;
if (!miners)
flags |= RANDOMX_FLAG_SECURE;
}
if(!force_software_aes() && check_aes_hw())
flags |= RANDOMX_FLAG_HARD_AES;
if (miners) {
CTHR_MUTEX_LOCK(rx_dataset_mutex);
if (rx_dataset == NULL) {
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
if (rx_dataset == NULL) {
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
memcpy(main_seedhash, info->seedhash, HASH_SIZE);
main_seedhash_set = 1;
char buf[HASH_SIZE * 2 + 1];
hash2hex(main_seedhash, buf);
minfo(RX_LOGCAT, "RandomX new main seed hash is %s", buf);
const randomx_flags flags = enabled_flags() & ~disabled_flags();
rx_alloc_dataset(flags, &main_dataset, 0);
rx_alloc_cache(flags, &main_cache);
randomx_init_cache(main_cache, info->seedhash, HASH_SIZE);
minfo(RX_LOGCAT, "RandomX main cache initialized");
CTHR_RWLOCK_UNLOCK_WRITE(main_cache_lock);
// From this point, rx_slow_hash can calculate hashes in light mode, but dataset is not initialized yet
me_rx_init_dataset(info->max_threads);
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
free(info);
CTHR_THREAD_RETURN;
}
void me_rx_set_main_seedhash(const char *seedhash, size_t max_dataset_init_threads) {
// Early out if seedhash didn't change
if (is_main(seedhash)) {
return;
}
// Update main cache and dataset in the background
thread_info* info = malloc(sizeof(thread_info));
if (!info) local_abort("Couldn't allocate RandomX mining threadinfo");
memcpy(info->seedhash, seedhash, HASH_SIZE);
info->max_threads = max_dataset_init_threads;
CTHR_THREAD_TYPE t;
if (!CTHR_THREAD_CREATE(t, me_rx_set_main_seedhash_thread, info)) {
local_abort("Couldn't start RandomX seed thread");
}
}
void me_rx_slow_hash(const char *seedhash, const void *data, size_t length, char *result_hash) {
const randomx_flags flags = enabled_flags() & ~disabled_flags();
int success = 0;
// Fast path (seedhash == main_seedhash)
// Multiple threads can run in parallel in fast or light mode, 1-2 ms or 10-15 ms per hash per thread
if (is_main(seedhash)) {
// If CTHR_RWLOCK_TRYLOCK_READ fails it means dataset is being initialized now, so use the light mode
if (main_dataset && CTHR_RWLOCK_TRYLOCK_READ(main_dataset_lock)) {
// Double check that main_seedhash didn't change
if (is_main(seedhash)) {
rx_init_full_vm(flags, &main_vm_full);
if (main_vm_full) {
randomx_calculate_hash(main_vm_full, data, length, result_hash);
success = 1;
}
if (rx_dataset != NULL)
rx_initdata(rx_sp->rs_cache, miners, seedheight);
}
if (rx_dataset != NULL)
flags |= RANDOMX_FLAG_FULL_MEM;
else {
miners = 0;
mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner");
CTHR_RWLOCK_UNLOCK_READ(main_dataset_lock);
} else {
CTHR_RWLOCK_LOCK_READ(main_cache_lock);
// Double check that main_seedhash didn't change
if (is_main(seedhash)) {
rx_init_light_vm(flags, &main_vm_light, main_cache);
randomx_calculate_hash(main_vm_light, data, length, result_hash);
success = 1;
}
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
}
rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset);
if(rx_vm == NULL) { //large pages failed
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM");
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
CTHR_RWLOCK_UNLOCK_READ(main_cache_lock);
}
if(rx_vm == NULL) {//fallback if everything fails
flags = RANDOMX_FLAG_DEFAULT | (miners ? RANDOMX_FLAG_FULL_MEM : 0);
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
}
if (success) {
return;
}
char buf[HASH_SIZE * 2 + 1];
// Slow path (seedhash != main_seedhash, but seedhash == secondary_seedhash)
// Multiple threads can run in parallel in light mode, 10-15 ms per hash per thread
if (!secondary_cache) {
CTHR_RWLOCK_LOCK_WRITE(secondary_cache_lock);
if (!secondary_cache) {
hash2hex(seedhash, buf);
minfo(RX_LOGCAT, "RandomX new secondary seed hash is %s", buf);
rx_alloc_cache(flags, &secondary_cache);
randomx_init_cache(secondary_cache, seedhash, HASH_SIZE);
minfo(RX_LOGCAT, "RandomX secondary cache updated");
memcpy(secondary_seedhash, seedhash, HASH_SIZE);
secondary_seedhash_set = 1;
}
if (rx_vm == NULL)
local_abort("Couldn't allocate RandomX VM");
} else if (miners) {
CTHR_MUTEX_LOCK(rx_dataset_mutex);
if (rx_dataset != NULL && rx_dataset_height != seedheight)
rx_initdata(cache, miners, seedheight);
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
} else if (changed) {
randomx_vm_set_cache(rx_vm, rx_sp->rs_cache);
}
/* mainchain users can run in parallel */
if (!is_alt)
CTHR_MUTEX_UNLOCK(rx_sp->rs_mutex);
randomx_calculate_hash(rx_vm, data, length, hash);
/* altchain slot users always get fully serialized */
if (is_alt)
CTHR_MUTEX_UNLOCK(rx_sp->rs_mutex);
}
CTHR_RWLOCK_UNLOCK_WRITE(secondary_cache_lock);
}
CTHR_RWLOCK_LOCK_READ(secondary_cache_lock);
if (is_secondary(seedhash)) {
rx_init_light_vm(flags, &secondary_vm_light, secondary_cache);
randomx_calculate_hash(secondary_vm_light, data, length, result_hash);
success = 1;
}
CTHR_RWLOCK_UNLOCK_READ(secondary_cache_lock);
void me_rx_slow_hash_allocate_state(void) {
if (success) {
return;
}
// Slowest path (seedhash != main_seedhash, seedhash != secondary_seedhash)
// Only one thread runs at a time and updates secondary_seedhash if needed, up to 200-500 ms per hash
CTHR_RWLOCK_LOCK_WRITE(secondary_cache_lock);
if (!is_secondary(seedhash)) {
hash2hex(seedhash, buf);
minfo(RX_LOGCAT, "RandomX new secondary seed hash is %s", buf);
randomx_init_cache(secondary_cache, seedhash, HASH_SIZE);
minfo(RX_LOGCAT, "RandomX secondary cache updated");
memcpy(secondary_seedhash, seedhash, HASH_SIZE);
secondary_seedhash_set = 1;
}
rx_init_light_vm(flags, &secondary_vm_light, secondary_cache);
randomx_calculate_hash(secondary_vm_light, data, length, result_hash);
CTHR_RWLOCK_UNLOCK_WRITE(secondary_cache_lock);
}
void me_rx_slow_hash_free_state(void) {
if (rx_vm != NULL) {
randomx_destroy_vm(rx_vm);
rx_vm = NULL;
void me_rx_set_miner_thread(uint32_t value, size_t max_dataset_init_threads) {
miner_thread = value;
// If dataset is not allocated yet, try to allocate and initialize it
CTHR_RWLOCK_LOCK_WRITE(main_dataset_lock);
if (main_dataset) {
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
return;
}
const randomx_flags flags = enabled_flags() & ~disabled_flags();
rx_alloc_dataset(flags, &main_dataset, 1);
me_rx_init_dataset(max_dataset_init_threads);
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
}
uint32_t me_rx_get_miner_thread() {
return miner_thread;
}
void me_rx_slow_hash_allocate_state() {}
static void me_rx_destroy_vm(randomx_vm** vm) {
if (*vm) {
randomx_destroy_vm(*vm);
*vm = NULL;
}
}
void me_rx_slow_hash_free_state() {
me_rx_destroy_vm(&main_vm_full);
me_rx_destroy_vm(&main_vm_light);
me_rx_destroy_vm(&secondary_vm_light);
}

@ -42,7 +42,7 @@ extern "C" void me_rx_slow_hash(const uint64_t mainheight, const uint64_t seedhe
char *hash, int miners, int is_alt);
//extern "C" void me_rx_reorg(const uint64_t split_height);
extern __thread randomx_vm *rx_vm;
extern __thread randomx_vm *main_vm_full;
#include <algorithm>
#include <limits>
@ -2103,7 +2103,7 @@ show_my_outputs(string tx_hash_str,
{"blk_height" , tx_blk_height_str},
{"tx_size" , fmt::format("{:0.4f}",
static_cast<double>(txd.size) / 1024.0)},
{"tx_fee" , xmreg::xmr_amount_to_str(txd.fee, "{:0.12f}", true)},
{"tx_fee" , xmreg::xmr_amount_to_str(txd.fee, "{:0.11f}", true)},
{"blk_timestamp" , blk_timestamp},
{"delta_time" , age.first},
{"outputs_no" , static_cast<uint64_t>(txd.output_pub_keys.size())},
@ -2684,7 +2684,7 @@ show_my_outputs(string tx_hash_str,
context["show_inputs"] = show_key_images;
context["inputs_no"] = static_cast<uint64_t>(inputs.size());
context["sum_mixin_xmr"] = xmreg::xmr_amount_to_str(
sum_mixin_xmr, "{:0.12f}", false);
sum_mixin_xmr, "{:0.11f}", false);
uint64_t possible_spending {0};
@ -2717,7 +2717,7 @@ show_my_outputs(string tx_hash_str,
}
context["possible_spending"] = xmreg::xmr_amount_to_str(
possible_spending, "{:0.12f}", false);
possible_spending, "{:0.11f}", false);
} // if (enable_mixin_guess)
@ -5695,17 +5695,29 @@ json_networkinfo()
return j_response;
}
uint64_t fee_estimated {0};
uint64_t per_kb_fee_estimated {0};
// get dynamic fee estimate from last 10 blocks
if (!get_dynamic_per_kb_fee_estimate(fee_estimated))
if (!get_dynamic_per_kb_fee_estimate(per_kb_fee_estimated))
{
j_response["status"] = "error";
j_response["message"] = "Cant get dynamic fee esimate";
j_response["message"] = "Cant get per kb dynamic fee esimate";
return j_response;
}
j_info["fee_per_kb"] = fee_estimated;
uint64_t fee_estimated {0};
// get dynamic fee estimate from last 10 blocks
//@todo: make this work
// if (!get_base_fee_estimate(fee_estimated))
// {
// j_response["status"] = "error";
// j_response["message"] = "Cant get dynamic fee esimate";
// return j_response;
// }
j_info["fee_per_kb"] = per_kb_fee_estimated;
j_info["fee_estimate"] = fee_estimated;
j_info["tx_pool_size"] = MempoolStatus::mempool_no.load();
j_info["tx_pool_size_kbytes"] = MempoolStatus::mempool_size.load();
@ -6123,9 +6135,9 @@ construct_tx_context(transaction tx, uint16_t with_ring_signatures = 0)
{"blk_height" , tx_blk_height_str},
{"tx_blk_height" , tx_blk_height},
{"tx_size" , fmt::format("{:0.4f}", tx_size)},
{"tx_fee" , xmreg::xmr_amount_to_str(txd.fee, "{:0.12f}", false)},
{"tx_fee" , xmreg::xmr_amount_to_str(txd.fee, "{:0.11f}", false)},
{"tx_fee_micro" , xmreg::xmr_amount_to_str(txd.fee*1e6, "{:0.4f}", false)},
{"payed_for_kB" , fmt::format("{:0.12f}", payed_for_kB)},
{"payed_for_kB" , fmt::format("{:0.11f}", payed_for_kB)},
{"tx_version" , static_cast<uint64_t>(txd.version)},
{"blk_timestamp" , blk_timestamp},
{"blk_timestamp_uint" , blk.timestamp},
@ -6836,6 +6848,25 @@ get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated)
return true;
}
bool
get_base_fee_estimate(uint64_t& fee_estimated)
{
string error_msg;
if (!rpc.get_base_fee_estimate(
FEE_ESTIMATE_GRACE_BLOCKS,
fee_estimated))
{
cerr << "rpc.get_base_fee_estimate failed" << endl;
return false;
}
(void) error_msg;
return true;
}
bool
are_absolute_offsets_good(
std::vector<uint64_t> const& absolute_offsets,
@ -6947,18 +6978,18 @@ get_randomx_code(uint64_t blk_height,
std::lock_guard<std::mutex> lk {mtx};
if (!rx_vm)
if (!main_vm_full)
{
crypto::hash block_hash;
// this will create rx_vm instance if one
// this will create main_vm_full instance if one
// does not exist
me_get_block_longhash(core_storage, blk, block_hash, blk_height, 0);
if (!rx_vm)
if (!main_vm_full)
{
cerr << "rx_vm is still null!";
cerr << "main_vm_full is still null!";
return {};
}
}
@ -6969,32 +7000,32 @@ get_randomx_code(uint64_t blk_height,
alignas(16) uint64_t tempHash[8];
blake2b(tempHash, sizeof(tempHash), bd.data(), bd.size(), nullptr, 0);
rx_vm->initScratchpad(&tempHash);
rx_vm->resetRoundingMode();
main_vm_full->initScratchpad(&tempHash);
main_vm_full->resetRoundingMode();
for (int chain = 0; chain < RANDOMX_PROGRAM_COUNT - 1; ++chain)
{
rx_vm->run(&tempHash);
main_vm_full->run(&tempHash);
blake2b(tempHash, sizeof(tempHash),
rx_vm->getRegisterFile(),
main_vm_full->getRegisterFile(),
sizeof(randomx::RegisterFile), nullptr, 0);
rx_code.push_back({});
rx_code.back().prog = rx_vm->getProgram();
rx_code.back().reg_file = *(rx_vm->getRegisterFile());
rx_code.back().prog = main_vm_full->getProgram();
rx_code.back().reg_file = *(main_vm_full->getRegisterFile());
}
rx_vm->run(&tempHash);
main_vm_full->run(&tempHash);
rx_code.push_back({});
rx_code.back().prog = rx_vm->getProgram();
rx_code.back().reg_file = *(rx_vm->getRegisterFile());
rx_code.back().prog = main_vm_full->getProgram();
rx_code.back().reg_file = *(main_vm_full->getRegisterFile());
//crypto::hash res2;
//rx_vm->getFinalResult(res2.data, RANDOMX_HASH_SIZE);
//main_vm_full->getFinalResult(res2.data, RANDOMX_HASH_SIZE);
//cout << "pow2: " << pod_to_hex(res2) << endl;
return rx_code;

@ -40,6 +40,33 @@ rpccalls::connect_to_monero_daemon()
return m_http_client.connect(timeout_time_ms);
}
bool
rpccalls::get_base_fee_estimate(uint64_t grace_blocks,
uint64_t& fee_estimate) {
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response res;
req.grace_blocks = grace_blocks;
if (!connect_to_monero_daemon())
{
cerr << "get_base_fee_estimate: not connected to daemon" << endl;
return false;
}
bool r = epee::net_utils::invoke_http_json(
"/get_fee_estimate",
req, res, m_http_client, timeout_time_ms);
fee_estimate = res.fee;
return r;
}
uint64_t
rpccalls::get_current_height()
{

@ -86,7 +86,7 @@ public:
using login_opt = boost::optional<epee::net_utils::http::login>;
rpccalls(string _daemon_url = "http:://127.0.0.1:18081",
rpccalls(string _daemon_url = "http:://127.0.0.1:34568",
login_opt _login = login_opt {},
uint64_t _timeout = 200000);
@ -105,6 +105,9 @@ public:
bool
get_network_info(COMMAND_RPC_GET_INFO::response& info);
bool
get_base_fee_estimate(uint64_t grace_blocks, uint64_t& fee_estimate);
bool
get_hardfork_info( COMMAND_RPC_HARD_FORK_INFO::response& res);

@ -15,7 +15,7 @@
<h4>Output keys for address: {{address}}</h4>
<h4>Viewkey: {{viewkey}}</h4>
{{#has_total_xmr}}
<h4>Total xmr: {{total_xmr}}</h4>
<h4>Total wow: {{total_xmr}}</h4>
{{/has_total_xmr}}
<div class="center">

@ -29,6 +29,7 @@ tr, li, #pages, .info {
td {
text-align: center;
word-break: break-all;
}
a:link {

@ -2,7 +2,7 @@
<h6 style="margin-top:10px">
<a href="https://github.com/moneroexamples/onion-monero-blockchain-explorer">source code</a>
| explorer version (api): {{git_branch_name}}-{{last_git_commit_date}}-{{last_git_commit_hash}} ({{api}})
| monero version: {{monero_version_full}}
| wownero version: {{monero_version_full}}
</h6>
</div>
</body>

@ -18,7 +18,7 @@
<div>
<div class="center">
<h1 class="center"><a href="/">Onion Monero Blockchain Explorer</a></h1>
<h1 class="center"><a href="/">Wownero Blockchain Explorer</a></h1>
<h4 style="font-size: 15px; margin: 0px">(no javascript - no cookies - no web analytics trackers - no images - open sourced)</h4>
</div>

@ -55,7 +55,7 @@
{{#emission}}
<h3 style="font-size: 12px; margin-top: 2px">
Monero emission (fees) is {{amount}} ({{fee_amount}}) as of {{blk_no}} block
Wownero emission is {{amount}} ({{fee_amount}}) as of {{blk_no}} block
</h3>
{{/emission}}

@ -69,7 +69,7 @@
</table>
<h3>
Sum XMR from matched outputs (i.e., incoming XMR):
Sum WOW from matched outputs (i.e., incoming WOW):
{{#found_our_outputs}}
{{sum_xmr}}
{{/found_our_outputs}}
@ -142,7 +142,7 @@
</div>
<h3>
Sum XMR from matched and marked by * ring member's outputs: {{sum_mixin_xmr}}
Sum WOW from matched and marked by * ring member's outputs: {{sum_mixin_xmr}}
<br/>
<span style="font-size: 16px"> Possible spending is:
{{possible_spending}} (tx fee included)
@ -155,7 +155,7 @@
<br/>
<span style="font-size: 14px">
Number of possible our mixins is {{no_all_possible_mixins}}
for {{all_possible_mixins_amount}} xmr
for {{all_possible_mixins_amount}} wow
(amount as uint64).
</span>
</h3>

@ -55,7 +55,7 @@
</table>
<h3>{{outputs_no}} output(s) for total of {{outputs_xmr_sum}} xmr</h3>
<h3>{{outputs_no}} output(s) for total of {{outputs_xmr_sum}} wow</h3>
<div class="center">
<table class="center">
<tr>
@ -83,7 +83,7 @@
<input type="radio" id="tab-1" name="tab-group-1" checked>
<label for="tab-1">Decode outputs</label>
<div class="content tab-1">
<h4 style="margin: 0px">Check which outputs belong to given Monero address/subaddress and viewkey</h4>
<h4 style="margin: 0px">Check which outputs belong to given Wownero address/subaddress and viewkey</h4>
<h5 style="margin: 0px">
For RingCT transactions, outputs' amounts are also decoded
<br/>
@ -91,7 +91,7 @@
</h5>
<form action="/myoutputs" method="post" style="width:100%; margin-top:2px" class="style-1">
<input type="hidden" name="tx_hash" value="{{tx_hash}}"><br/>
<input type="text" name="xmr_address" size="110" placeholder="Monero address/subaddress"><br/>
<input type="text" name="xmr_address" size="110" placeholder="Wownero address/subaddress"><br/>
<input type="text" name="viewkey" size="110" placeholder="Private viewkey" style="margin-top:5px"><br/>
<input type="hidden" name="raw_tx_data" value="{{raw_tx_data}}">
<!--above raw_tx_data field only used when checking raw tx data through tx pusher-->
@ -105,10 +105,10 @@
<label for="tab-2">Prove sending</label>
<div class="content tab-2">
<h4 style="margin: 0px">Prove to someone that you have sent them Monero in this transaction</h4>
<h4 style="margin: 0px">Prove to someone that you have sent them Wownero in this transaction</h4>
<h5 style="margin: 0px">
Tx private key can be obtained using <i>get_tx_key</i>
command in <i>monero-wallet-cli</i> command line tool
command in <i>wownero-wallet-cli</i> command line tool
<br/>
Note: address/subaddress and tx private key are sent to the server, as the calculations are done on the server side
</h5>
@ -142,14 +142,14 @@
{{/enable_mixins_details}}
{{^inputs_xmr_sum_not_zero}}
<h3>{{inputs_no}} input(s) for total of {{inputs_xmr_sum}} xmr</h3>
<h3>{{inputs_no}} input(s) for total of {{inputs_xmr_sum}} wow</h3>
{{/inputs_xmr_sum_not_zero}}
{{#inputs_xmr_sum_not_zero}}
{{^have_any_unknown_amount}}
<h3>{{inputs_no}} inputs(s) for total of {{inputs_xmr_sum}} xmr</h3>
<h3>{{inputs_no}} inputs(s) for total of {{inputs_xmr_sum}} wow</h3>
{{/have_any_unknown_amount}}
{{#have_any_unknown_amount}}
<h3>{{inputs_no}} inputs(s) for total of at least {{inputs_xmr_sum}} xmr</h3>
<h3>{{inputs_no}} inputs(s) for total of at least {{inputs_xmr_sum}} wow</h3>
{{/have_any_unknown_amount}}
{{/inputs_xmr_sum_not_zero}}

@ -9,12 +9,12 @@
<div class="center">
<form action="/checkandpush" method="post" style="width:100%; margin-top:15px" class="style-1">
Paste here either a hex string of raw transaction<br/>
(the <i>tx_blob</i> response in the wallet RPC, or the <i>raw_monero_tx</i>
(the <i>tx_blob</i> response in the wallet RPC, or the <i>raw_wownero_tx</i>
file saved by the wallet CLI with <i>--do-not-relay</i> option specified),<br/>
or base64 encoded, unsigned or signed transaction data<br/>
<br/>
(In Linux, can get the raw tx data: <i>cat raw_monero_tx | xclip -selection clipboard</i>)<br/>
(In Windows, can get the raw tx data: <i>certutil.exe -encode -f raw_monero_tx encoded.txt & type "encoded.txt" | clip</i>)<br/>
(In Linux, can get the raw tx data: <i>cat raw_wownero_tx | xclip -selection clipboard</i>)<br/>
(In Windows, can get the raw tx data: <i>certutil.exe -encode -f raw_wownero_tx encoded.txt & type "encoded.txt" | clip</i>)<br/>
<textarea name="rawtxdata" rows="20" cols="80"></textarea>
<br/>

@ -627,8 +627,6 @@ sum_fees_in_txs(const vector<transaction>& txs)
return fees_sum;
}
vector<output_tuple_with_tag>
get_ouputs(const transaction& tx)
{

@ -8,7 +8,7 @@
#define PATH_SEPARARTOR '/'
#define XMR_AMOUNT(value) \
static_cast<double>(value) / 1e12
static_cast<double>(value) / 1e11
#define REMOVE_HASH_BRAKETS(a_hash) \
a_hash.substr(1, a_hash.size()-2)
@ -228,7 +228,7 @@ get_payment_id(const transaction& tx,
inline double
get_xmr(uint64_t core_amount)
{
return static_cast<double>(core_amount) / 1e12;
return static_cast<double>(core_amount) / 1e11;
}
array<size_t, 5>

Loading…
Cancel
Save