Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 847db19867 | |||
| 0663a5ead7 | |||
| 7ad904a9ae | |||
| 12cf6be402 | |||
| 292845d721 | |||
| 9bc0ccc14d | |||
| c20ab30963 |
@@ -1442,17 +1442,28 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of tx outputs to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
// SRCG: The following code is designed to clean up the STAKE transactions, but it is very poorly written
|
||||
// Since transactions are ALWAYS supposed to be created in order, it stands that they should ALWAYS be
|
||||
// removed in REVERSE ORDER. Yet the following loop starts from the beginning - this is the worst possible
|
||||
// implementation in performance terms, since it will ALWAYS take the longest possible time to remove the
|
||||
// correct TX.
|
||||
|
||||
// RECODE TO START FROM THE END OF THE DATABASE TABLE, AND THROW AN EXCEPTION IF YOU DO NOT MATCH FIRST TIME!
|
||||
|
||||
// Is there yield_tx data to remove?
|
||||
if (tx.type == cryptonote::transaction_type::STAKE) {
|
||||
// Remove any yield_tx data for this transaction
|
||||
MDB_val_set(val_height, m_height);
|
||||
MDB_val v;
|
||||
MDB_cursor_op op = MDB_SET;
|
||||
while (1) {
|
||||
result = mdb_cursor_get(m_cur_yield_txs, &val_height, &v, MDB_SET);
|
||||
if (result == MDB_NOTFOUND)
|
||||
break;
|
||||
else if (result)
|
||||
result = mdb_cursor_get(m_cur_yield_txs, &val_height, &v, op);
|
||||
if (result == MDB_NOTFOUND) {
|
||||
throw1(DB_ERROR("Failed to locate yield tx for removal from db transaction"));
|
||||
} else if (result) {
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate yield_tx data for removal: ", result).c_str()));
|
||||
}
|
||||
op = MDB_NEXT_DUP;
|
||||
const yield_tx_info yti = *(const yield_tx_info*)v.mv_data;
|
||||
if (yti.tx_hash == tx_hash) {
|
||||
result = mdb_cursor_del(m_cur_yield_txs, 0);
|
||||
|
||||
@@ -959,7 +959,7 @@ static void open_db(const std::string &filename, MDB_env **env, MDB_txn **txn, M
|
||||
dbr = mdb_env_set_maxdbs(*env, 1);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
|
||||
const std::string actual_filename = filename;
|
||||
MINFO("Opening monero blockchain at " << actual_filename);
|
||||
MINFO("Opening Salvium blockchain at " << actual_filename);
|
||||
dbr = mdb_env_open(*env, actual_filename.c_str(), flags, 0664);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open rings database file '"
|
||||
+ actual_filename + "': " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace cryptonote
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(key)
|
||||
FIELD(asset_type)
|
||||
FIELD(unlock_time)
|
||||
VARINT_FIELD(unlock_time)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace cryptonote {
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) {
|
||||
static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60");
|
||||
const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
|
||||
const int target = DIFFICULTY_TARGET_V2;
|
||||
const int target_minutes = target / 60;
|
||||
const int emission_speed_factor = EMISSION_SPEED_FACTOR_PER_MINUTE - (target_minutes-1);
|
||||
|
||||
|
||||
@@ -1256,6 +1256,11 @@ namespace cryptonote
|
||||
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
|
||||
<< "expected matching variant types in transaction id=" << get_transaction_hash(tx));
|
||||
}
|
||||
|
||||
// Verify the asset type
|
||||
std::string asset_type;
|
||||
CHECK_AND_ASSERT_MES(cryptonote::get_output_asset_type(o, asset_type), false, "failed to get asset type");
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1790,6 +1795,7 @@ namespace cryptonote
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob");
|
||||
b.invalidate_hashes();
|
||||
b.miner_tx.invalidate_hashes();
|
||||
b.protocol_tx.invalidate_hashes();
|
||||
if (block_hash)
|
||||
{
|
||||
calculate_block_hash(b, *block_hash, &b_blob);
|
||||
|
||||
@@ -577,7 +577,7 @@ namespace cryptonote
|
||||
b.nonce = nonce;
|
||||
crypto::hash h;
|
||||
|
||||
if ((b.major_version >= RX_BLOCK_VERSION) && !rx_set)
|
||||
if (!rx_set)
|
||||
{
|
||||
crypto::rx_set_miner_thread(th_local_index, tools::get_max_concurrency());
|
||||
rx_set = true;
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
// MONEY_SUPPLY - total number coins to be generated
|
||||
#define MONEY_SUPPLY ((uint64_t)(18440000000000000ull)) // 184.4M coins * pow(10, 8)
|
||||
#define EMISSION_SPEED_FACTOR_PER_MINUTE (20)
|
||||
#define EMISSION_SPEED_FACTOR_PER_MINUTE (21)
|
||||
#define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)30000000) // 3 * pow(10, 7)
|
||||
|
||||
#define BURN_LOCK_PERIOD 0
|
||||
@@ -208,15 +208,15 @@
|
||||
#define HF_VERSION_REJECT_SIGS_IN_COINBASE 1
|
||||
#define HF_VERSION_BULLETPROOF_PLUS 1
|
||||
#define HF_VERSION_ENABLE_RETURN 1
|
||||
#define HF_VERSION_VIEW_TAGS 1
|
||||
|
||||
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 2
|
||||
#define HF_VERSION_VIEW_TAGS 2
|
||||
#define HF_VERSION_2021_SCALING 2
|
||||
#define HF_VERSION_ENABLE_CONVERT 2
|
||||
#define HF_VERSION_ENABLE_ORACLE 2
|
||||
#define HF_VERSION_SLIPPAGE_YIELD 2
|
||||
|
||||
#define TESTNET_VERSION 8
|
||||
#define TESTNET_VERSION 11
|
||||
#define STAGENET_VERSION 1
|
||||
|
||||
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
|
||||
@@ -337,7 +337,7 @@ namespace config
|
||||
boost::uuids::uuid const NETWORK_ID = { {
|
||||
0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x82, 0x53, 0x41, 0x4C, 0x00, TESTNET_VERSION
|
||||
} };
|
||||
std::string const GENESIS_TX = "020001ff000180c0d0c7bbbff60302800b6eb882218e901c1c36bce474224456d82226260226d252459dfbadf186f70353414c3c00000000000000210171af115cca70fdcfdac362854ed9de472e242c8be5a3684e8a809d54f5dbdb18010000";
|
||||
std::string const GENESIS_TX = "020001ff000180c0d0c7bbbff60302838f76f69b70bb0d0f1961a12f6082a033d22285c07d4f12ec93c28197ae2a600353414c3c2101009e8b0abce686c417a1b1344eb7337176bdca90cc928b0facec8a9516190645010000";
|
||||
uint32_t const GENESIS_NONCE = 10001;
|
||||
|
||||
const uint64_t STAKE_LOCK_PERIOD = 20;
|
||||
|
||||
@@ -1044,7 +1044,7 @@ size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_heig
|
||||
std::vector<difficulty_type> new_cumulative_difficulties;
|
||||
for (uint64_t height = start_height; height <= top_height; ++height)
|
||||
{
|
||||
size_t target = get_ideal_hard_fork_version(height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
|
||||
size_t target = DIFFICULTY_TARGET_V2;
|
||||
difficulty_type recalculated_diff = next_difficulty(timestamps, difficulties, target);
|
||||
|
||||
boost::multiprecision::uint256_t recalculated_cum_diff_256 = boost::multiprecision::uint256_t(recalculated_diff) + last_cum_diff;
|
||||
@@ -1351,7 +1351,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||
}
|
||||
|
||||
// FIXME: This will fail if fork activation heights are subject to voting
|
||||
size_t target = get_ideal_hard_fork_version(bei.height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
|
||||
size_t target = DIFFICULTY_TARGET_V2;
|
||||
|
||||
// calculate the difficulty target for the block and return it
|
||||
return next_difficulty(timestamps, cumulative_difficulties, target);
|
||||
@@ -3555,7 +3555,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
||||
}
|
||||
}
|
||||
|
||||
// from v15, require view tags on outputs
|
||||
// from v15, require view tags and asset types on outputs
|
||||
if (!check_output_types(tx, hf_version))
|
||||
{
|
||||
tvc.m_invalid_output = true;
|
||||
@@ -5978,7 +5978,7 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui
|
||||
|
||||
uint64_t Blockchain::get_difficulty_target() const
|
||||
{
|
||||
return get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
|
||||
return DIFFICULTY_TARGET_V2;
|
||||
}
|
||||
|
||||
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace cryptonote
|
||||
};
|
||||
static const command_line::arg_descriptor<std::string> arg_check_updates = {
|
||||
"check-updates"
|
||||
, "Check for new versions of monero: [disabled|notify|download|update]"
|
||||
, "Check for new versions of Salvium: [disabled|notify|download|update]"
|
||||
, "disabled"
|
||||
};
|
||||
static const command_line::arg_descriptor<bool> arg_fluffy_blocks = {
|
||||
@@ -504,7 +504,7 @@ namespace cryptonote
|
||||
if (boost::filesystem::exists(old_files / "blockchain.bin"))
|
||||
{
|
||||
MWARNING("Found old-style blockchain.bin in " << old_files.string());
|
||||
MWARNING("Monero now uses a new format. You can either remove blockchain.bin to start syncing");
|
||||
MWARNING("Salvium now uses a new format. You can either remove blockchain.bin to start syncing");
|
||||
MWARNING("the blockchain anew, or use salvium-blockchain-export and salvium-blockchain-import to");
|
||||
MWARNING("convert your existing blockchain.bin to the new format. See README.md for instructions.");
|
||||
return false;
|
||||
@@ -1878,7 +1878,7 @@ namespace cryptonote
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::check_updates()
|
||||
{
|
||||
static const char software[] = "monero";
|
||||
static const char software[] = "salvium";
|
||||
#ifdef BUILD_TAG
|
||||
static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG);
|
||||
static const char subdir[] = "cli"; // because it can never be simple
|
||||
|
||||
@@ -1236,29 +1236,16 @@ namespace cryptonote
|
||||
|
||||
bool get_block_longhash(const Blockchain *pbc, const blobdata& bd, crypto::hash& res, const uint64_t height, const int major_version, const crypto::hash *seed_hash, const int miners)
|
||||
{
|
||||
// block 202612 bug workaround
|
||||
if (height == 202612)
|
||||
crypto::hash hash;
|
||||
if (pbc != NULL)
|
||||
{
|
||||
static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000";
|
||||
epee::string_tools::hex_to_pod(longhash_202612, res);
|
||||
return true;
|
||||
}
|
||||
if (major_version >= RX_BLOCK_VERSION)
|
||||
const uint64_t seed_height = rx_seedheight(height);
|
||||
hash = seed_hash ? *seed_hash : pbc->get_pending_block_id_by_height(seed_height);
|
||||
} else
|
||||
{
|
||||
crypto::hash hash;
|
||||
if (pbc != NULL)
|
||||
{
|
||||
const uint64_t seed_height = rx_seedheight(height);
|
||||
hash = seed_hash ? *seed_hash : pbc->get_pending_block_id_by_height(seed_height);
|
||||
} else
|
||||
{
|
||||
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
|
||||
}
|
||||
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
|
||||
} else {
|
||||
const int pow_variant = major_version >= 7 ? major_version - 6 : 0;
|
||||
crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height);
|
||||
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
|
||||
}
|
||||
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -260,6 +260,13 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the TX type
|
||||
if (tx.type <= cryptonote::transaction_type::UNSET || tx.type > cryptonote::transaction_type::MAX) {
|
||||
LOG_PRINT_L1("Transaction with id= "<< id << " has invalid type " << (uint8_t)tx.type);
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// assume failure during verification steps until success is certain
|
||||
tvc.m_verifivation_failed = true;
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace cryptonote
|
||||
CONVERT = 4,
|
||||
BURN = 5,
|
||||
STAKE = 6,
|
||||
RETURN = 7
|
||||
RETURN = 7,
|
||||
MAX = 7
|
||||
};
|
||||
}
|
||||
|
||||
+2
-10
@@ -234,16 +234,8 @@ namespace cryptonote
|
||||
|
||||
block = is_current ? info.block : info.previous_block;
|
||||
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(nonce);
|
||||
if (block.major_version >= RX_BLOCK_VERSION)
|
||||
{
|
||||
const crypto::hash &seed_hash = is_current ? info.seed_hash : info.previous_seed_hash;
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0;
|
||||
crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash, cn_variant, cryptonote::get_block_height(block));
|
||||
}
|
||||
const crypto::hash &seed_hash = is_current ? info.seed_hash : info.previous_seed_hash;
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data);
|
||||
if (!check_hash(hash, m_diff))
|
||||
{
|
||||
MWARNING("Payment too low");
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace rpc
|
||||
bool is_version_string_valid(const std::string& str)
|
||||
{
|
||||
return std::regex_match(str, std::regex(
|
||||
"^\\d{1,2}(\\.\\d{1,2}){3}(-(release|[0-9a-f]{9}))?$",
|
||||
"^\\d{1,2}(\\.\\d{1,2}){2}(-(release|[0-9a-f]{9}))?.+$",
|
||||
std::regex_constants::nosubs
|
||||
));
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace
|
||||
const char* USAGE_CHECK_SPEND_PROOF("check_spend_proof <txid> <signature_file> [<message>]");
|
||||
const char* USAGE_GET_RESERVE_PROOF("get_reserve_proof (all|<amount>) [<message>]");
|
||||
const char* USAGE_CHECK_RESERVE_PROOF("check_reserve_proof <address> <signature_file> [<message>]");
|
||||
const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|all|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
|
||||
const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|all|pending|failed|pool|coinbase|burnt|staked|yield] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
|
||||
const char* USAGE_UNSPENT_OUTPUTS("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]");
|
||||
const char* USAGE_RESCAN_BC("rescan_bc [hard|soft|keep_ki] [start_height=0]");
|
||||
const char* USAGE_SET_TX_NOTE("set_tx_note <txid> [free text note]");
|
||||
@@ -3222,7 +3222,7 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st
|
||||
message_writer() << tr("\"price_info\" - Display current pricing information for supported assets.");
|
||||
message_writer() << tr("\"supply_info\" - Display circulating supply information.");
|
||||
message_writer() << tr("\"yield_info\" - Display current stats on Salvium staking / yield.");
|
||||
message_writer() << tr("\"show_transfers [in|out|pending|failed|pool|coinbase|burnt|yield]\" - Show transactions.");
|
||||
message_writer() << tr("\"show_transfers [in|out|pending|failed|pool|coinbase|burnt|staked|yield]\" - Show transactions.");
|
||||
message_writer() << tr("\"sweep_all <address>\" - Send whole balance to another wallet.");
|
||||
message_writer() << tr("\"seed\" - Show secret 25 words that can be used to recover this wallet.");
|
||||
message_writer() << tr("\"refresh\" - Synchronize wallet with the Salvium network.");
|
||||
@@ -3606,15 +3606,16 @@ simple_wallet::simple_wallet()
|
||||
// Seemingly broken formatting to compensate for the backslash before the quotes.
|
||||
tr("Show the incoming/outgoing transfers within an optional height range.\n\n"
|
||||
"Output format:\n"
|
||||
"In or Coinbase: Block Number, \"block\"|\"in\", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, \"-\", Note\n"
|
||||
"Out: Block Number, \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, \"-\", Note\n"
|
||||
"Pool: \"pool\", \"in\", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, \"-\", Note, Double Spend Note\n"
|
||||
"Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n"
|
||||
"In or Coinbase: Block Number, \"block\"|\"in\", Time, Amount, Asset Type, Transaction Hash, Payment ID, Subaddress Index, \"-\", Note\n"
|
||||
"Out: Block Number, \"out\", Time, Amount*, Asset Type, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, \"-\", Note\n"
|
||||
"Pool: \"pool\", \"in\", Time, Amount, Asset Type, Transaction Hash, Payment Id, Subaddress Index, \"-\", Note, Double Spend Note\n"
|
||||
"Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n"
|
||||
"Staked or Burnt: Block Number, \"out\", Time, Amount*, Asset Type, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n"
|
||||
"* Excluding change and fee.\n"
|
||||
"** Set of address indices used as inputs in this transfer."));
|
||||
m_cmd_binder.set_handler("export_transfers",
|
||||
boost::bind(&simple_wallet::on_command, this, &simple_wallet::export_transfers, _1),
|
||||
tr("export_transfers [in|out|all|pending|failed|pool|coinbase|burnt|yield] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<filepath>] [option=<with_keys>]"),
|
||||
tr("export_transfers [in|out|all|pending|failed|pool|coinbase|burnt|staked|yield] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<filepath>] [option=<with_keys>]"),
|
||||
tr("Export to CSV the incoming/outgoing transfers within an optional height range."));
|
||||
m_cmd_binder.set_handler("unspent_outputs",
|
||||
boost::bind(&simple_wallet::on_command, this, &simple_wallet::unspent_outputs, _1),
|
||||
@@ -6962,7 +6963,7 @@ bool simple_wallet::transfer_main(
|
||||
fail_msg_writer() << tr("failed to get blockchain height: ") << err;
|
||||
return false;
|
||||
}
|
||||
unlock_block = bc_height + locked_blocks;
|
||||
unlock_block = locked_blocks;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
break;
|
||||
default:
|
||||
@@ -7092,7 +7093,7 @@ bool simple_wallet::transfer_main(
|
||||
if (transfer_type == TransferLocked)
|
||||
{
|
||||
float days = locked_blocks / 720.0f;
|
||||
prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days;
|
||||
prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock in %llu blocks, approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days;
|
||||
}
|
||||
if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
|
||||
return false;
|
||||
@@ -7272,7 +7273,7 @@ bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
|
||||
local_args.pop_back();
|
||||
}
|
||||
|
||||
transfer_main(Transfer, source_asset, source_asset, local_args, false);
|
||||
transfer_main(TransferLocked, source_asset, source_asset, local_args, false);
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -9462,19 +9463,9 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
|
||||
if (!unlocked)
|
||||
{
|
||||
locked_msg = "locked";
|
||||
if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
|
||||
{
|
||||
uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
|
||||
if (bh >= last_block_height)
|
||||
locked_msg = std::to_string(bh - last_block_height) + " blks";
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint64_t adjusted_time = m_wallet->get_daemon_adjusted_time();
|
||||
uint64_t threshold = adjusted_time + (m_wallet->use_fork_rules(2, 0) ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1);
|
||||
if (threshold < pd.m_unlock_time)
|
||||
locked_msg = get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
|
||||
}
|
||||
uint64_t bh = pd.m_block_height + std::max(pd.m_unlock_time, (uint64_t)CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
|
||||
if (bh >= last_block_height)
|
||||
locked_msg = std::to_string(bh - last_block_height) + " blks";
|
||||
}
|
||||
transfers.push_back({
|
||||
type,
|
||||
@@ -9483,7 +9474,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
|
||||
type,
|
||||
true,
|
||||
pd.m_amount,
|
||||
pd.m_asset_type,
|
||||
pd.m_asset_type,
|
||||
pd.m_tx_hash,
|
||||
payment_id,
|
||||
(pd.m_tx_type == cryptonote::transaction_type::STAKE) ? pd.m_fee : 0,
|
||||
@@ -9636,7 +9627,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
||||
std::vector<std::string> local_args = args_;
|
||||
|
||||
if(local_args.size() > 4) {
|
||||
fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
|
||||
fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|pool|coinbase|burnt|staked|yield] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9695,7 +9686,7 @@ bool simple_wallet::export_transfers(const std::vector<std::string>& args_)
|
||||
std::vector<std::string> local_args = args_;
|
||||
|
||||
if(local_args.size() > 6) {
|
||||
fail_msg_writer() << tr("usage: export_transfers [in|out|all|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>] [option=<with_keys>]");
|
||||
fail_msg_writer() << tr("usage: export_transfers [in|out|all|pending|failed|pool|coinbase|burnt|staked|yield] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>] [option=<with_keys>]");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
#define DEF_SALVIUM_VERSION_TAG "7f6b8da"
|
||||
#define DEF_SALVIUM_VERSION "0.3.1"
|
||||
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION "0.3.6"
|
||||
#define DEF_MONERO_VERSION_TAG "release"
|
||||
#define DEF_MONERO_VERSION "0.18.3.3"
|
||||
#define DEF_MONERO_RELEASE_NAME "Zero"
|
||||
#define DEF_MONERO_VERSION_FULL DEF_SALVIUM_VERSION "-" DEF_SALVIUM_VERSION_TAG ", based on Monero " DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
|
||||
|
||||
+95
-48
@@ -2169,8 +2169,8 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
|
||||
if (!m_encrypt_keys_after_refresh)
|
||||
{
|
||||
boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received");
|
||||
THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero"));
|
||||
THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero"));
|
||||
THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming SALs"));
|
||||
THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming SALs"));
|
||||
m_encrypt_keys_after_refresh.reset(new wallet_keys_unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, *pwd));
|
||||
}
|
||||
}
|
||||
@@ -2223,14 +2223,17 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
|
||||
{
|
||||
tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device());
|
||||
}
|
||||
/*
|
||||
// SRCG: The following "if" block was commented out until v0.3.5 - wonder why???
|
||||
if (tx_scan_info.money_transfered == 0)
|
||||
{
|
||||
MERROR("Invalid output amount, skipping");
|
||||
tx_scan_info.error = true;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
// Populate the unlock_time
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_unlock_time(tx.vout[i], tx_scan_info.unlock_time), error::wallet_internal_error, "failed to get output unlock_time");
|
||||
|
||||
outs.push_back(i);
|
||||
THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index][tx_scan_info.asset_type] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered,
|
||||
error::wallet_internal_error, "Overflow in received amounts");
|
||||
@@ -2382,8 +2385,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
if (!miner_tx && !pool)
|
||||
process_unconfirmed(txid, tx, height);
|
||||
|
||||
std::string source_asset = tx.source_asset_type;
|
||||
std::string dest_asset = tx.destination_asset_type;
|
||||
std::string source_asset =
|
||||
(tx.type == cryptonote::transaction_type::MINER) ? "SAL" :
|
||||
(tx.type == cryptonote::transaction_type::PROTOCOL) ? "SAL" :
|
||||
tx.source_asset_type;
|
||||
|
||||
// per receiving subaddress index
|
||||
std::unordered_map<cryptonote::subaddress_index, std::map<std::string, uint64_t>> tx_money_got_in_outs;
|
||||
@@ -2407,7 +2412,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
size_t pk_index = 0;
|
||||
std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
|
||||
std::deque<bool> output_found(tx.vout.size(), false);
|
||||
uint64_t total_received_1 = 0;
|
||||
std::map<std::string, uint64_t> total_received_1;
|
||||
uint64_t td_origin_idx = ((uint64_t)-1);
|
||||
while (!tx.vout.empty())
|
||||
{
|
||||
@@ -2679,7 +2684,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
if (!ignore_callbacks && 0 != m_callback)
|
||||
m_callback->on_money_received(height, txid, tx, td.m_amount, td.asset_type, 0, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time, td.m_td_origin_idx);
|
||||
}
|
||||
total_received_1 += amount;
|
||||
std::string asset_type = m_transfers.back().asset_type;
|
||||
if (total_received_1.count(asset_type))
|
||||
total_received_1[asset_type] += amount;
|
||||
else
|
||||
total_received_1[asset_type] = amount;
|
||||
notify = true;
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE) {
|
||||
@@ -2790,7 +2799,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
if (!ignore_callbacks && 0 != m_callback)
|
||||
m_callback->on_money_received(height, txid, tx, td.m_amount, td.asset_type, burnt, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time, td.m_td_origin_idx);
|
||||
}
|
||||
total_received_1 += extra_amount;
|
||||
std::string asset_type = m_transfers.back().asset_type;
|
||||
if (total_received_1.count(asset_type))
|
||||
total_received_1[asset_type] += extra_amount;
|
||||
else
|
||||
total_received_1[asset_type] = extra_amount;
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
@@ -2975,51 +2988,84 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t total_received_2 = sub_change;
|
||||
std::map<std::string, uint64_t> total_received_2;
|
||||
for (auto& i : tx_money_got_in_outs)
|
||||
total_received_2 += i.second[source_asset];
|
||||
for (auto& asset: i.second)
|
||||
total_received_2[asset.first] += asset.second;
|
||||
|
||||
// only for regular transfers
|
||||
if (source_asset == dest_asset && !miner_tx) {
|
||||
if (total_received_1 != total_received_2)
|
||||
{
|
||||
const el::Level level = el::Level::Warning;
|
||||
MCLOG_RED(level, "global", "**********************************************************************");
|
||||
MCLOG_RED(level, "global", "Consistency failure in amounts received");
|
||||
MCLOG_RED(level, "global", "Check transaction " << txid);
|
||||
MCLOG_RED(level, "global", "**********************************************************************");
|
||||
exit(1);
|
||||
return;
|
||||
if (!miner_tx) {
|
||||
for (auto& asset: total_received_1) {
|
||||
if (asset.second != total_received_2[asset.first]) {
|
||||
//if (source_asset == dest_asset && !miner_tx) {
|
||||
//if (total_received_1 != total_received_2)
|
||||
//{
|
||||
const el::Level level = el::Level::Warning;
|
||||
MCLOG_RED(level, "global", "**********************************************************************");
|
||||
MCLOG_RED(level, "global", "Consistency failure in amounts received");
|
||||
MCLOG_RED(level, "global", "Check transaction " << txid);
|
||||
MCLOG_RED(level, "global", "**********************************************************************");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool all_same = true;
|
||||
for (auto& i : tx_money_got_in_outs)
|
||||
{
|
||||
payment_details payment;
|
||||
payment.m_tx_hash = txid;
|
||||
payment.m_fee = fee;
|
||||
std::vector<payment_details> temp_payments;
|
||||
for (auto& tsi : tx_scan_info) {
|
||||
if (!tsi.received) continue;
|
||||
cryptonote::subaddress_index si = tsi.received->index;
|
||||
bool updated_payment = false;
|
||||
for (auto& payment : temp_payments) {
|
||||
if (payment.m_subaddr_index == si && payment.m_unlock_time == tsi.unlock_time) {
|
||||
// Add to existing payment
|
||||
payment.m_amount += tsi.amount;
|
||||
payment.m_amounts.push_back(tsi.amount);
|
||||
updated_payment = true;
|
||||
}
|
||||
if (updated_payment) break;
|
||||
}
|
||||
if (updated_payment) continue;
|
||||
|
||||
// Create a new payment
|
||||
temp_payments.push_back(payment_details{});
|
||||
payment_details& payment = temp_payments.back();
|
||||
payment.m_tx_hash = txid;
|
||||
payment.m_fee = fee;
|
||||
// SRCG - figure out what this needs to be (pretty sure we should never get here with CONVERT!)
|
||||
payment.m_amount = source_asset == dest_asset ? i.second[dest_asset] : tx.amount_burnt;
|
||||
payment.m_asset_type = (tx.type == cryptonote::transaction_type::PROTOCOL) ? "SAL" : dest_asset;
|
||||
payment.m_amounts = tx_amounts_individual_outs[i.first];
|
||||
payment.m_block_height = height;
|
||||
payment.m_unlock_time = tx.unlock_time; // SRCG: this is incorrect - work out which vout entry it is and query that
|
||||
payment.m_timestamp = ts;
|
||||
payment.m_coinbase = miner_tx;
|
||||
payment.m_subaddr_index = i.first;
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
payment.m_amount = tsi.amount;
|
||||
payment.m_asset_type = tsi.asset_type;
|
||||
payment.m_amounts.push_back(tsi.amount);
|
||||
payment.m_block_height = height;
|
||||
payment.m_unlock_time = tsi.unlock_time;
|
||||
payment.m_timestamp = ts;
|
||||
payment.m_coinbase = miner_tx;
|
||||
payment.m_subaddr_index = si;
|
||||
if (tx.type == cryptonote::transaction_type::MINER) {
|
||||
|
||||
payment.m_unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; // All protocol_tx payments are coinbase
|
||||
payment.m_tx_type = cryptonote::transaction_type::MINER;
|
||||
|
||||
} else if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
|
||||
payment.m_unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; // All protocol_tx payments are coinbase
|
||||
if (td_origin_idx != ((uint64_t)-1)) {
|
||||
// Get the origin TD information
|
||||
payment.m_amount = payment.m_amounts.empty() ? 0 : payment.m_amounts[0];
|
||||
payment.m_tx_type = m_transfers[td_origin_idx].m_tx.type;
|
||||
payment.m_fee = m_transfers[td_origin_idx].m_tx.amount_burnt;
|
||||
payment.m_amount = payment.m_amounts.empty() ? 0 : payment.m_amounts[0];
|
||||
payment.m_tx_type = m_transfers[td_origin_idx].m_tx.type;
|
||||
payment.m_fee = m_transfers[td_origin_idx].m_tx.amount_burnt;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
} else {
|
||||
payment.m_tx_type = tx.type;
|
||||
payment.m_tx_type = tx.type;
|
||||
}
|
||||
}
|
||||
|
||||
// Now iterate over the temp_payment entries
|
||||
for (auto& payment : temp_payments) {
|
||||
if (pool) {
|
||||
if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}))
|
||||
all_same = false;
|
||||
@@ -3030,7 +3076,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
m_payments.emplace(payment_id, payment);
|
||||
LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
|
||||
}
|
||||
|
||||
// if it's a pool tx and we already had it, don't notify again
|
||||
if (pool && all_same)
|
||||
notify = false;
|
||||
@@ -6871,14 +6916,16 @@ std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> wallet2::
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t unlock_height = td.m_block_height + std::max<uint64_t>(CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS);
|
||||
if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height)
|
||||
unlock_height = td.m_tx.unlock_time;
|
||||
if (td.m_tx.type == cryptonote::transaction_type::MINER)
|
||||
uint64_t unlock_height = 0;
|
||||
if (td.m_tx.type == cryptonote::transaction_type::MINER || td.m_tx.type == cryptonote::transaction_type::PROTOCOL)
|
||||
unlock_height = td.m_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
uint64_t unlock_time = td.m_tx.unlock_time >= CRYPTONOTE_MAX_BLOCK_NUMBER ? td.m_tx.unlock_time : 0;
|
||||
blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0;
|
||||
time_to_unlock = unlock_time > now ? unlock_time - now : 0;
|
||||
else {
|
||||
uint64_t unlock_blocks = 0;
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_unlock_time(td.m_tx.vout[td.m_internal_output_index], unlock_blocks), error::wallet_internal_error, "failed to get unlock_time");
|
||||
unlock_height = td.m_block_height + ((unlock_blocks > CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE) ? unlock_blocks : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
|
||||
}
|
||||
blocks_to_unlock = (unlock_height > blockchain_height) ? unlock_height - blockchain_height : 0;
|
||||
time_to_unlock = 0;
|
||||
amount = 0;
|
||||
}
|
||||
auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
|
||||
@@ -14908,7 +14955,7 @@ mms::multisig_wallet_state wallet2::get_multisig_wallet_state() const
|
||||
state.num_transfer_details = m_transfers.size();
|
||||
if (state.multisig)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Monero address not available");
|
||||
THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Salvium address not available");
|
||||
state.address = m_original_address;
|
||||
state.view_secret_key = m_original_view_secret_key;
|
||||
}
|
||||
|
||||
@@ -320,12 +320,13 @@ private:
|
||||
rct::key mask;
|
||||
uint64_t amount;
|
||||
std::string asset_type;
|
||||
uint64_t unlock_time;
|
||||
uint64_t money_transfered;
|
||||
uint64_t origin_idx;
|
||||
bool error;
|
||||
boost::optional<cryptonote::subaddress_receive_info> received;
|
||||
|
||||
tx_scan_info_t(): amount(0), asset_type(""), money_transfered(0), origin_idx((uint64_t)-1), error(true) {}
|
||||
tx_scan_info_t(): amount(0), asset_type(""), unlock_time(0), money_transfered(0), origin_idx((uint64_t)-1), error(true) {}
|
||||
};
|
||||
|
||||
struct transfer_details
|
||||
|
||||
@@ -149,8 +149,7 @@ namespace wallet_args
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
Print(print) << "Salvium '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
|
||||
Print(print) << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
|
||||
"daemon to work correctly.") << ENDL;
|
||||
Print(print) << wallet_args::tr("This is the command line Salvium wallet. It needs to connect to a Salvium daemon to work correctly.") << ENDL;
|
||||
Print(print) << wallet_args::tr("Usage:") << ENDL << " " << usage;
|
||||
Print(print) << desc_all;
|
||||
should_terminate = true;
|
||||
|
||||
@@ -153,15 +153,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads
|
||||
tpool.submit(&waiter, [&, i] {
|
||||
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i);
|
||||
const uint8_t major_version = hashing_blob[0];
|
||||
if (major_version >= RX_BLOCK_VERSION)
|
||||
{
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
|
||||
}
|
||||
else
|
||||
{
|
||||
int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0;
|
||||
crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash[i], cn_variant, height);
|
||||
}
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
|
||||
});
|
||||
}
|
||||
waiter.wait();
|
||||
|
||||
@@ -320,7 +320,7 @@ namespace tools
|
||||
{
|
||||
MINFO("The daemon is not set up to background mine.");
|
||||
MINFO("With background mining enabled, the daemon will mine when idle and not on battery.");
|
||||
MINFO("Enabling this supports the network you are using, and makes you eligible for receiving new monero");
|
||||
MINFO("Enabling this supports the network you are using, and makes you eligible for receiving new SALs");
|
||||
MINFO("Set setup-background-mining to 1 in salvium-wallet-cli to change.");
|
||||
return;
|
||||
}
|
||||
@@ -358,6 +358,7 @@ namespace tools
|
||||
entry.timestamp = pd.m_timestamp;
|
||||
entry.amount = pd.m_amount;
|
||||
entry.amounts = pd.m_amounts;
|
||||
entry.asset_type = pd.m_asset_type;
|
||||
entry.unlock_time = pd.m_unlock_time;
|
||||
entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
|
||||
entry.fee = pd.m_fee;
|
||||
@@ -4780,7 +4781,7 @@ int main(int argc, char** argv) {
|
||||
std::tie(vm, should_terminate) = wallet_args::main(
|
||||
argc, argv,
|
||||
"salvium-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
|
||||
tools::wallet_rpc_server::tr("This is the RPC monero wallet. It needs to connect to a monero\ndaemon to work correctly."),
|
||||
tools::wallet_rpc_server::tr("This is the RPC Salvium wallet. It needs to connect to a Salvium daemon to work correctly."),
|
||||
desc_params,
|
||||
po::positional_options_description(),
|
||||
[](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
|
||||
|
||||
@@ -1439,6 +1439,7 @@ namespace wallet_rpc
|
||||
uint64_t amount;
|
||||
amounts_container amounts;
|
||||
uint64_t fee;
|
||||
std::string asset_type;
|
||||
std::string note;
|
||||
std::list<transfer_destination> destinations;
|
||||
std::string type;
|
||||
@@ -1460,6 +1461,7 @@ namespace wallet_rpc
|
||||
KV_SERIALIZE(amounts);
|
||||
KV_SERIALIZE(fee);
|
||||
KV_SERIALIZE(note);
|
||||
KV_SERIALIZE(asset_type);
|
||||
KV_SERIALIZE(destinations);
|
||||
KV_SERIALIZE(type);
|
||||
KV_SERIALIZE(unlock_time)
|
||||
|
||||
Reference in New Issue
Block a user