From 400ec099d1fd93902de0dfc0727e027f1d358e1b Mon Sep 17 00:00:00 2001 From: Some Random Crypto Guy Date: Thu, 16 Jan 2025 21:02:16 +0000 Subject: [PATCH] working audit commands --- src/blockchain_db/lmdb/db_lmdb.cpp | 8 +- .../blockchain_scanner.cpp | 299 ++++++------------ src/cryptonote_core/blockchain.cpp | 20 +- src/simplewallet/simplewallet.cpp | 56 ++-- src/wallet/wallet2.cpp | 132 ++++++-- 5 files changed, 236 insertions(+), 279 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index fa37a5cd2..57dd04906 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -929,9 +929,12 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector arg_block_start = {"block-start", "start at block number", block_start}; const command_line::arg_descriptor arg_block_stop = {"block-stop", "Stop at block number", block_stop}; const command_line::arg_descriptor arg_delimiter = {"delimiter", "\"\"", DELIM}; + const command_line::arg_descriptor arg_stake_mode = {"stake", "\"\"", DEF_STAKE_MODE}; + const command_line::arg_descriptor arg_audit = {"audit", "Scan for audit issues", false}; + const command_line::arg_descriptor arg_check_asset_types = {"check-asset-types", "Scan for asset-type issues", false}; command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir); command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); @@ -78,6 +82,9 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_block_start); command_line::add_arg(desc_cmd_sett, arg_block_stop); command_line::add_arg(desc_cmd_sett, arg_delimiter); + command_line::add_arg(desc_cmd_sett, arg_stake_mode); + command_line::add_arg(desc_cmd_sett, arg_audit); + command_line::add_arg(desc_cmd_sett, arg_check_asset_types); command_line::add_arg(desc_cmd_only, command_line::arg_help); po::options_description desc_options("Allowed options"); @@ -116,6 +123,9 @@ int main(int argc, char* argv[]) block_start = command_line::get_arg(vm, arg_block_start); block_stop = command_line::get_arg(vm, arg_block_stop); std::string delimiter = command_line::get_arg(vm, arg_delimiter); + std::string stake_mode = command_line::get_arg(vm, arg_stake_mode); + bool opt_audit = command_line::get_arg(vm, arg_audit); + bool opt_check_asset_types = command_line::get_arg(vm, arg_check_asset_types); // If we wanted to use the memory pool, we would set up a fake_core. @@ -203,6 +213,16 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' uint32_t txhr[24] = {0}; unsigned int i; + struct wallet_summary { + uint64_t total_amount; + std::vector txs; + bool seen_closing_tx; + crypto::public_key viewkey; + }; + + // Create a map of wallet addresses and total amounts in them + std::map wallet_details; + for (uint64_t h = block_start; h < block_stop; ++h) { cryptonote::blobdata bd = db->get_block_blob_from_height(h); @@ -312,227 +332,86 @@ skip: if ((tx.version != 2 && h < 89800) || (tx.version != 3 && h >= 89800 && tx.type == cryptonote::transaction_type::TRANSFER)) { std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX version detected" << delimiter << "version:" << tx.version << std::endl; } + + if (tx.type == cryptonote::transaction_type::STAKE && stake_mode.compare("off")) { + if (stake_mode.compare("all") == 0) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "STAKE TX detected" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << std::endl; + } else if (stake_mode.compare("large") == 0 && tx.amount_burnt > 25000000000000llu) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "large STAKE TX detected" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << std::endl; + } + } - if (tx.type == cryptonote::transaction_type::STAKE && tx.amount_burnt > 25000000000000llu) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "large STAKE TX detected" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << std::endl; + if (opt_check_asset_types) { + if (tx.source_asset_type != "SAL") { + throw std::runtime_error("Aborting: invalid source asset type found in tx"); + } else if (tx.destination_asset_type != "SAL") { + if (tx.destination_asset_type == "BURN") { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "BURN TX detected" << delimiter << "amount:" << tx.amount_burnt << std::endl; + } else { + throw std::runtime_error("Aborting: invalid destination asset type found in tx"); + } + } + + for (const auto& tx_vout : tx.vout) { + std::string asset_type; + if (!cryptonote::get_output_asset_type(tx_vout, asset_type)) { + throw std::runtime_error("Aborting: failed to get output asset type from tx"); + } else if (asset_type != "SAL") { + throw std::runtime_error("Aborting: invalid output asset type from tx"); + } + } + + // Add the source currency to the list of expected ones + used_assets.insert(tx.source_asset_type); } + // Are we auditing? + if (!opt_audit) continue; + + // Audit checks + if (h < 0 || h > 99999999) continue; // Set these limits to match the audit process!!! + + // Pre-check - only attempt to verify legitimate AUDIT TXs + if (tx.type != cryptonote::transaction_type::STAKE) continue; + if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) continue; + if (tx.rct_signatures.salvium_data.salvium_data_type != rct::SalviumAudit) continue; + + // WE ARE AUDITING - RETRIEVE ANY WALLET SUMMARY FOR THIS WALLET + wallet_summary &ws = wallet_details[tx.rct_signatures.salvium_data.spend_pubkey]; + if (!ws.total_amount) { + ws.viewkey = tx.rct_signatures.salvium_data.view_pubkey; + } + ws.txs.push_back(tx_id); + + // Increment the total amount for this wallet that has been audited + if (ws.total_amount + tx.amount_burnt < ws.total_amount) { + std::cerr << "Aborting: overflow in total_amount for Ks:" << tx.rct_signatures.salvium_data.spend_pubkey << std::endl; + throw std::runtime_error("Aborting: overflow in total_amount for Ks"); + } + ws.total_amount += tx.amount_burnt; + + // Check - asset_type of SAL on all inputs if (tx.source_asset_type != "SAL") { + // TX must spend SALs in audit throw std::runtime_error("Aborting: invalid source asset type found in tx"); - } else if (tx.destination_asset_type != "SAL") { - if (tx.destination_asset_type == "BURN") { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "BURN TX detected" << delimiter << "amount:" << tx.amount_burnt << std::endl; - } else { - throw std::runtime_error("Aborting: invalid destination asset type found in tx"); - } - } - - for (const auto& tx_vout : tx.vout) { - std::string asset_type; - if (!cryptonote::get_output_asset_type(tx_vout, asset_type)) { - throw std::runtime_error("Aborting: failed to get output asset type from tx"); - } else if (asset_type != "SAL") { - throw std::runtime_error("Aborting: invalid output asset type from tx"); - } } - /* - std::string source; - std::string dest; - offshore::pricing_record pr; - if (!cryptonote::get_tx_asset_types(tx, source, dest, false)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "At least 1 input or 1 output of the tx was invalid" << delimiter << "get_tx_asset_types() failed : "; - if (source.empty()) { - std::cout << "source is empty" << std::endl; - } - if (dest.empty()) { - std::cout << "dest is empty" << std::endl; - } + // Check - amount in STAKE TX + if (tx.amount_burnt >= 25000000000000llu) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "BLACKLIST: large AUDIT TX detected" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << std::endl; + } else if (tx.amount_burnt >= 10000000000000llu) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "REVIEW: large AUDIT TX detected" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << + std::endl; } - if (!cryptonote::get_tx_type(source, dest, offshore, onshore, offshore_transfer, xusd_to_xasset, xasset_to_xusd, xasset_transfer)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "At least 1 input or 1 output of the tx was invalid" << delimiter << "get_tx_type() failed" << std::endl; + + // Check - total amount for this wallet + if (ws.total_amount >= 25000000000000llu) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "BLACKLIST: large BALANCE detected" << delimiter << "amount:" << (ws.total_amount / 100000000) << std::endl; + } else if (ws.total_amount >= 10000000000000llu) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "REVIEW: large BALANCE TX detected" << delimiter << "amount:" << (ws.total_amount / 100000000) << std::endl; } - */ - // Add the source currency to the list of expected ones - used_assets.insert(tx.source_asset_type); - /* - if ((offshore && !tx.rct_signatures.txnOffshoreFee) || - (onshore && !tx.rct_signatures.txnOffshoreFee_usd) || - (xusd_to_xasset && !tx.rct_signatures.txnOffshoreFee_usd) || - (xasset_to_xusd && !tx.rct_signatures.txnOffshoreFee_xasset)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "Missing conversion fee." << delimiter << "" << - "Source:" << source << ", dest:" << dest << - ", XHV fees:" << tx.rct_signatures.txnFee << "," << tx.rct_signatures.txnOffshoreFee << - ", XUSD fees:" << tx.rct_signatures.txnFee_usd << "," << tx.rct_signatures.txnOffshoreFee_usd << - ", burnt:" << tx.amount_burnt << ", minted:" << tx.amount_minted << std::endl; - } else if ((offshore || onshore || xusd_to_xasset || xasset_to_xusd) && (!tx.amount_burnt || !tx.amount_minted)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "Missing burnt/minted value." << std::endl; - } - */ - /* - // Only run these checks for conversions - if (source != dest) { - - // Check PR record is not too old - if (h > (tx.pricing_record_height + 10)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "pricing record used by tx was too old" << - delimiter << "tx.pricing_record_height = " << tx.pricing_record_height << std::endl; - } - - // Get the PR used by the TX - cryptonote::blobdata bd_pr = db->get_block_blob_from_height(tx.pricing_record_height); - cryptonote::block blk_pr; - if (!cryptonote::parse_and_validate_block_from_blob(bd_pr, blk_pr)) { - LOG_PRINT_L0("Bad block from db"); - return 1; - } - - // Get a more convenient handle on the conversion PR - pr = blk_pr.pricing_record; - - // Verify the fees in 128-bit space - boost::multiprecision::uint128_t burnt_128 = tx.amount_burnt; - boost::multiprecision::uint128_t minted_128 = tx.amount_minted; - - // calculate conversion fees - uint32_t fees_version = (h >= 831700) ? 2 : (h >= 653565) ? 2 : 1; - uint64_t blocks_to_unlock = tx.unlock_time - h + 1; - - boost::multiprecision::uint128_t fee; - if (offshore) { - if (fees_version >= 3) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter - << "invalid fee version " << fees_version << "" << delimiter << "..." << std::endl; - } else if (fees_version == 2) { - - fee = - (blocks_to_unlock >= 5030) ? (tx.amount_burnt / 500) : - (blocks_to_unlock >= 1430) ? (tx.amount_burnt / 20) : - (blocks_to_unlock >= 710) ? (tx.amount_burnt / 10) : - tx.amount_burnt / 5; - - } else { - - // Calculate the priority based on the unlock time - uint64_t priority = - (blocks_to_unlock >= 5030) ? 1 : - (blocks_to_unlock >= 1430) ? 2 : - (blocks_to_unlock >= 710) ? 3 : - 4; - uint64_t unlock_time = 60 * pow(3, 4-priority); - - // abs() implementation for uint64_t's - uint64_t delta = (pr.unused1 > pr.xUSD) ? pr.unused1 - pr.xUSD : pr.xUSD - pr.unused1; - - // Estimate the fee - double scale = exp((M_PI / -1000.0) * (unlock_time - 60) * 1.2); - scale *= delta; - scale *= tx.amount_burnt; - scale /= 1000000000000; - fee = (boost::multiprecision::uint128_t)(scale); - } - - if ((h >= 658500) && (fee != tx.rct_signatures.txnOffshoreFee)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter - << "invalid fee " << tx.rct_signatures.txnOffshoreFee << "" << delimiter << "check:" << fee << std::endl; - } - - } else if (onshore) { - - if (fees_version >= 3) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter - << "invalid fee version " << fees_version << "" << delimiter << "..." << std::endl; - } else if (fees_version == 2) { - - fee = - (blocks_to_unlock >= 5030) ? (tx.amount_burnt / 500) : - (blocks_to_unlock >= 1430) ? (tx.amount_burnt / 20) : - (blocks_to_unlock >= 710) ? (tx.amount_burnt / 10) : - tx.amount_burnt / 5; - - } else { - - // Calculate the priority based on the unlock time - uint64_t priority = - (blocks_to_unlock >= 5030) ? 1 : - (blocks_to_unlock >= 1430) ? 2 : - (blocks_to_unlock >= 710) ? 3 : - 4; - uint64_t unlock_time = 60 * pow(3, 4-priority); - - // abs() implementation for uint64_t's - uint64_t delta = (pr.unused1 > pr.xUSD) ? pr.unused1 - pr.xUSD : pr.xUSD - pr.unused1; - - // Estimate the fee - double scale = exp((M_PI / -1000.0) * (unlock_time - 60) * 1.2); - scale *= delta; - scale *= tx.amount_burnt; - scale /= 1000000000000; - fee = (boost::multiprecision::uint128_t)(scale); - } - - if ((h >= 658500) && (fee != tx.rct_signatures.txnOffshoreFee_usd)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter - << "invalid offshore fee " << tx.rct_signatures.txnOffshoreFee_usd << "" << delimiter << "check:" << fee << std::endl; - } - - } else if (xusd_to_xasset) { - - fee = tx.amount_burnt; - fee *= 3; - fee /= 1000; - - if (fee != tx.rct_signatures.txnOffshoreFee_usd) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter - << "invalid xusd_to_xasset fee " << tx.rct_signatures.txnOffshoreFee_usd << "" << delimiter << "check:" << fee << std::endl; - } - - } else if (xasset_to_xusd) { - - fee = tx.amount_burnt; - fee *= 3; - fee /= 1000; - - if (fee != tx.rct_signatures.txnOffshoreFee_xasset) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter - << "invalid xasset_to_xusd fee " << tx.rct_signatures.txnOffshoreFee_xasset << "" << delimiter << "check:" << fee << std::endl; - } - - } - - // Check for 0 price in the source or destination currency - if (offshore|| xusd_to_xasset) { - if (!pr[dest]) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "0 exchange rate used for dest " << dest << "" << delimiter << "..." << std::endl; - } else if (pr[dest] == 1000000000000) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "1.0000 exchange rate used for dest " << dest << "" << delimiter << "..." << std::endl; - } - } else if (onshore || xasset_to_xusd) { - if (!pr[source]) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "0 exchange rate used for source " << source << "" << delimiter << "..." << std::endl; - } else if (pr[source] == 1000000000000) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "1.0000 exchange rate used for source " << source << "" << delimiter << "..." << std::endl; - } - } - } - */ } - - /* - // compare the asset sets - if (used_assets == miner_tx_assets) { - } else if (used_assets.empty() && (miner_tx_assets.size() == 1) && (miner_tx_assets.count("XHV") == 1)) { - } else { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.miner_tx.hash << "" << delimiter << "Mismatch in miner reward assets detected" << delimiter << "Used assets = { "; - for (auto const &i: used_assets) - std::cout << i << " "; - std::cout << "}, miner_tx claimed { "; - for (auto const &i: miner_tx_assets) - std::cout << i << " "; - std::cout << "}" << std::endl; - } - */ currblks++; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f54732d5a..b49ed316f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -155,9 +155,9 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke // outputs list. that is to say that absolute offset #2 is absolute offset // #1 plus relative offset #2. // TODO: Investigate if this is necessary / why this is done. - std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); - //std::vector absolute_offsets; - //m_db->get_output_id_from_asset_type_output_index(tx_in_to_key.asset_type, asset_offsets, absolute_offsets); + std::vector asset_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); + std::vector absolute_offsets; + m_db->get_output_id_from_asset_type_output_index(tx_in_to_key.asset_type, asset_offsets, absolute_offsets); std::vector outputs; bool found = false; @@ -1655,14 +1655,16 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, // Found a YIELD entry CHECK_AND_ASSERT_MES(out_amount == found_yield->second, false, "Incorrect value for protocol TX YIELD amount"); - if (get_hard_fork_version(found_yield->first.block_height) >= HF_VERSION_SALVIUM_ONE_PROOFS) + uint8_t hf_yield = m_hardfork->get_ideal_version(found_yield->first.block_height); + if (hf_yield >= HF_VERSION_SALVIUM_ONE_PROOFS) expected_output_asset_type = "SAL1"; } else if (found_yield == yield_payouts.end()) { // Found an AUDIT entry CHECK_AND_ASSERT_MES(out_amount == found_audit->second, false, "Incorrect value for protocol TX AUDIT amount"); - if (get_hard_fork_version(found_audit->first.block_height) >= HF_VERSION_SALVIUM_ONE_PROOFS) + uint8_t hf_audit = m_hardfork->get_ideal_version(found_audit->first.block_height); + if (hf_audit >= HF_VERSION_SALVIUM_ONE_PROOFS) expected_output_asset_type = "SAL1"; } else { @@ -5966,7 +5968,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector (txin); // no need to check for duplicate here. - auto absolute_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets); + // HERE BE DRAGONS!!! + // SRCG: ring tweak to indexed per asset_type - DO NOT COMMIT UNTIL IT IS ALL WORKING + //auto absolute_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets); + std::vector asset_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets); + std::vector absolute_offsets; + m_db->get_output_id_from_asset_type_output_index(in_to_key.asset_type, asset_offsets, absolute_offsets); + // LAND AHOY!!! for (const auto & offset : absolute_offsets) offset_map[in_to_key.amount].push_back(offset); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 24fba27c2..64a32bb9d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -5838,18 +5838,22 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, message_writer(console_color_red) << "\r" << "*** CONVERT ***"; } else if (td_origin.m_tx.type == cryptonote::transaction_type::STAKE) { message_writer(console_color_magenta, false) << "\r" << - tr("Height ") << height << ", " << - tr("txid ") << txid << ", " << - tr("stake returned ") << print_money(td_origin.m_tx.amount_burnt) << " " << td_origin.asset_type << " from height " << td_origin.m_block_height << ", " << - tr("idx ") << subaddr_index; + tr("Height ") << height << ", " << + tr("txid ") << txid << ", " << + tr("stake returned ") << print_money(td_origin.m_tx.amount_burnt) << " " << td_origin.asset_type << " from height " << td_origin.m_block_height << ", " << + tr("idx ") << subaddr_index; message_writer(console_color_magenta, false) << "\r" << - tr("Height ") << height << ", " << - tr("txid ") << txid << ", " << - tr("yield earned ") << print_money(amount - td_origin.m_tx.amount_burnt) << " " << asset_type << ", " << - tr("idx ") << subaddr_index; + tr("Height ") << height << ", " << + tr("txid ") << txid << ", " << + tr("yield earned ") << print_money(amount - td_origin.m_tx.amount_burnt) << " " << asset_type << ", " << + tr("idx ") << subaddr_index; } else if (td_origin.m_tx.type == cryptonote::transaction_type::AUDIT) { - message_writer(console_color_red) << "\r" << "*** AUDIT ***"; + message_writer(console_color_red) << "\r" << + tr("Height ") << height << ", " << + tr("txid ") << txid << ", " << + tr("audit returned ") << print_money(td_origin.m_tx.amount_burnt) << " " << asset_type << " from height " << td_origin.m_block_height << ", " << + tr("idx ") << subaddr_index; } else { } @@ -5857,24 +5861,24 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, if (tx.type == cryptonote::transaction_type::BURN) { message_writer(console_color_yellow, false) << "\r" << - tr("Height ") << height << ", " << - tr("txid ") << txid << ", " << - tr("burnt ") << print_money(tx.amount_burnt) << " " << asset_type; + tr("Height ") << height << ", " << + tr("txid ") << txid << ", " << + tr("burnt ") << print_money(tx.amount_burnt) << " " << asset_type; } else if (tx.type == cryptonote::transaction_type::CONVERT) { message_writer(console_color_blue, false) << "\r" << - tr("Height ") << height << ", " << - tr("txid ") << txid << ", " << - tr("converting ") << print_money(tx.amount_burnt) << " " << asset_type; + tr("Height ") << height << ", " << + tr("txid ") << txid << ", " << + tr("converting ") << print_money(tx.amount_burnt) << " " << asset_type; } else if (tx.type == cryptonote::transaction_type::STAKE) { message_writer(console_color_cyan, false) << "\r" << - tr("Height ") << height << ", " << - tr("txid ") << txid << ", " << - tr("staked ") << print_money(tx.amount_burnt) << " " << asset_type; + tr("Height ") << height << ", " << + tr("txid ") << txid << ", " << + tr("staked ") << print_money(tx.amount_burnt) << " " << asset_type; } else if (tx.type == cryptonote::transaction_type::AUDIT) { message_writer(console_color_yellow, false) << "\r" << - tr("Height ") << height << ", " << - tr("txid ") << txid << ", " << - tr("audited ") << print_money(tx.amount_burnt) << " " << asset_type; + tr("Height ") << height << ", " << + tr("txid ") << txid << ", " << + tr("audited ") << print_money(tx.amount_burnt) << " " << asset_type; } message_writer(asset_type == "SAL" ? console_color_green : console_color_blue, false) << "\r" << @@ -6738,9 +6742,11 @@ bool simple_wallet::transfer_main( if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) { if (transfer_type == Audit) { audit = true; +/* } else if (source_asset != "SAL1") { fail_msg_writer() << tr("Only SAL1 may be spent now"); return false; +*/ } } @@ -8396,13 +8402,7 @@ bool simple_wallet::stake(const std::vector &args_) local_args.insert(local_args.end(), args_.begin(), args_.end()); if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) { - // Check to see if the user has a balance of SAL - uint64_t sal_balance = m_wallet->unlocked_balance_all(true, "SAL"); - if (sal_balance > 0) { - transfer_main(Stake, "SAL", "SAL", local_args, false); - } else { - transfer_main(Stake, "SAL1", "SAL1", local_args, false); - } + transfer_main(Stake, "SAL1", "SAL1", local_args, false); } else { transfer_main(Stake, "SAL", "SAL", local_args, false); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f65d38145..7cd80ca34 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -886,6 +886,11 @@ uint8_t get_full_proofs_fork() return HF_VERSION_FULL_PROOFS; } +uint8_t get_salvium_one_proofs_fork() +{ + return HF_VERSION_SALVIUM_ONE_PROOFS; +} + uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_quantization_mask) { if (use_per_byte_fee) @@ -2148,6 +2153,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & case rct::RCTTypeCLSAG: case rct::RCTTypeBulletproofPlus: case rct::RCTTypeFullProofs: + case rct::RCTTypeSalviumOne: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTTypeFull: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); @@ -2425,7 +2431,7 @@ bool wallet2::get_yield_summary_info(uint64_t &total_burnt, for (size_t idx = m_transfers.size()-1; idx>0; --idx) { const tools::wallet2::transfer_details& td = m_transfers[idx]; //if (td.m_block_height < ybi_data[0].block_height) break; - if (td.m_tx.type == cryptonote::transaction_type::STAKE) { + if (td.m_tx.type == cryptonote::transaction_type::STAKE || td.m_tx.type == cryptonote::transaction_type::AUDIT) { if (map_payouts.count(idx)) { payouts.push_back(std::make_tuple(td.m_block_height, epee::string_tools::pod_to_hex(td.m_txid), td.m_tx.amount_burnt, m_transfers[map_payouts[idx]].m_amount - td.m_tx.amount_burnt)); } else { @@ -2434,7 +2440,7 @@ bool wallet2::get_yield_summary_info(uint64_t &total_burnt, } } else if (td.m_tx.type == cryptonote::transaction_type::PROTOCOL) { // Store list of reverse-lookup indices to tell YIELD TXs how much they earned - if (m_transfers[td.m_td_origin_idx].m_tx.type == cryptonote::transaction_type::STAKE) + if (m_transfers[td.m_td_origin_idx].m_tx.type == cryptonote::transaction_type::STAKE || m_transfers[td.m_td_origin_idx].m_tx.type == cryptonote::transaction_type::AUDIT) map_payouts[td.m_td_origin_idx] = idx; } } @@ -2486,7 +2492,7 @@ bool wallet2::verify_spend_authority_proof(const cryptonote::transaction &tx, co // Sanity checks if (tx.type != cryptonote::transaction_type::TRANSFER) return true; if (tx.version < TRANSACTION_VERSION_N_OUTS) return true; - if (tx.rct_signatures.type != rct::RCTTypeFullProofs) return true; + if (tx.rct_signatures.type != rct::RCTTypeFullProofs && tx.rct_signatures.type != rct::RCTTypeSalviumOne) return true; // To verify the spend authority proof, we need to know the y value to process the F value ec_scalar y; @@ -2544,7 +2550,7 @@ bool wallet2::verify_spend_authority_proof(const cryptonote::transaction &tx, co rct::key hs_yF = rct::hash_to_scalar(key_yF); // Now we can verify the proof itself - if (!rct::SAProof_Ver(tx.rct_signatures.sa_proof, key_P_change, hs_yF)) { + if (!rct::SAProof_Ver(tx.rct_signatures.salvium_data.sa_proof, key_P_change, hs_yF)) { return false; } @@ -2743,7 +2749,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate protocol TX origin in m_transfers"); const transfer_details& td_origin = get_transfer_details(td_origin_idx); - THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.type != cryptonote::transaction_type::STAKE, error::wallet_internal_error, "incorrect TX type for protocol_tx origin in m_transfers"); + THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.type != cryptonote::transaction_type::AUDIT && td_origin.m_tx.type != cryptonote::transaction_type::STAKE, error::wallet_internal_error, "incorrect TX type for protocol_tx origin in m_transfers"); // Get the output key for the change entry crypto::public_key pk_locked_coins = crypto::null_pkey; @@ -2877,7 +2883,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote total_received_1[asset_type] = amount; notify = true; - if (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE) { + if (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) { // The CONVERT/YIELD TX was created by us - therefore we need to expect an output in the PROTOCOL_TX // It could be a refund or a conversion @@ -2890,10 +2896,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote m_subaddresses[P_change] = {0,0}; m_salvium_txs.insert({P_change, m_transfers.size()-1}); - if (tx.type == cryptonote::transaction_type::STAKE) { - // Additionally, with YIELD TXs, we need to update our "balance staked" subtotal, because otherwise our balance is out by the staked coins until they mature! + if (tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) { + // Additionally, with STAKE and AUDIT TXs, we need to update our "balance staked" subtotal, because otherwise our balance is out by the staked coins until they mature! // SRCG: must remember to deduct the number of staked coins when they mature!! - LOG_PRINT_L1("***** STAKED COINS : " << tx.amount_burnt << " *****"); + LOG_PRINT_L1("***** STAKED/AUDITED COINS : " << tx.amount_burnt << " *****"); m_locked_coins.insert({P_change, {0, tx.amount_burnt}}); } @@ -7944,13 +7950,14 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector> if (out < num_outs) { MINFO("Using it"); + // HERE BE DRAGONS!!! + // SRCG: ring tweak to indexed per asset_type - DO NOT COMMIT UNTIL IT IS ALL WORKING //req.outputs.push_back({amount, out, true}); // Rings are stored referencing global output IDs - add_output_to_lists({amount, out, true}); + //add_output_to_lists({amount, out, true}); + add_output_to_lists({amount, out, false}); + // LAND AHOY!!! ++num_found; seen_indices.emplace(out); - if (out == td.m_global_output_index) + if (out == td.m_asset_type_output_index) { MINFO("This is the real output"); own_found = true; @@ -9725,7 +9736,11 @@ void wallet2::get_outs(std::vector> "Daemon response did not include the requested real output"); // pick real out first (it will be sorted when done) - outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), mask)); + // HERE BE DRAGONS!!! + // SRCG: DO NOT COMMIT THIS CHANGE UNTIL VERIFIED AS WORKING + //outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), mask)); + outs.back().push_back(std::make_tuple(td.m_asset_type_output_index, td.get_public_key(), mask)); + // LAND AHOY!!! // then pick outs from an existing ring, if any if (td.m_key_image_known && !td.m_key_image_partial) @@ -9744,13 +9759,16 @@ void wallet2::get_outs(std::vector> for (size_t o = 0; o < requested_outputs_count; ++o) { size_t i = base + o; - if (req.outputs[i].index == out && req.outputs[i].is_global_out) + // HERE BE DRAGONS!!! + // SRCG: DO NOT COMMIT THIS CHANGE UNTIL VERIFIED AS WORKING + if (req.outputs[i].index == out /*&& req.outputs[i].is_global_out*/) { LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key << " (from existing ring)"); tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked, valid_public_keys_cache); found = true; break; } + // LAND AHOY!!! } THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Failed to find existing ring output in daemon out data"); } @@ -9896,7 +9914,11 @@ void wallet2::transfer_selected(const std::vector wallet2::create_transactions_2(std::vector valid_public_keys_cache; @@ -10695,7 +10726,18 @@ std::vector wallet2::create_transactions_2(std::vector &ptx_vector, c received += ptx.tx.amount_burnt; else if (ptx.tx.type == cryptonote::transaction_type::STAKE) received += ptx.tx.amount_burnt; + else if (ptx.tx.type == cryptonote::transaction_type::AUDIT) + received += ptx.tx.amount_burnt; total_received += received; } @@ -11351,11 +11395,15 @@ std::vector wallet2::create_transactions_all(uint64_t below const bool bulletproof_plus = true; const bool clsag = true; const bool use_fullproofs = use_fork_rules(get_full_proofs_fork(), 0); - const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, use_fullproofs ? 5 : 4 }; + const bool use_salviumone_proofs = use_fork_rules(get_salvium_one_proofs_fork(), 0); + const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, use_salviumone_proofs ? 6 : use_fullproofs ? 5 : 4 }; const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0); const uint64_t base_fee = get_base_fee(priority); - const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags); - const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags); + // HERE BE DRAGONS!!! + // SRCG: the weight of the TX needs to account for the additional proofs + const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags/*, use_fullproofs, use_salviumone_proofs*/); + const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags/*, use_fullproofs, use_salviumone_proofs*/); + // LAND AHOY!!! THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!"); const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring; const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024); @@ -11413,7 +11461,7 @@ std::vector wallet2::create_transactions_all(uint64_t below } } - return create_transactions_from(address, cryptonote::transaction_type::TRANSFER, "SAL", is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + return create_transactions_from(address, tx_type, asset_type, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) @@ -11594,7 +11642,8 @@ std::vector wallet2::create_transactions_from(const crypton const bool bulletproof_plus = true; const bool clsag = true; const bool use_fullproofs = use_fork_rules(get_full_proofs_fork(), 0); - const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, use_fullproofs ? 5 : 4 }; + const bool use_salviumone_proofs = use_fork_rules(get_salvium_one_proofs_fork(), 0); + const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, use_salviumone_proofs ? 6 : use_fullproofs ? 5 : 4 }; const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0); const uint64_t base_fee = get_base_fee(priority); const uint64_t fee_quantization_mask = get_fee_quantization_mask(); @@ -12291,6 +12340,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string // SRCG: Calculate the correct uniqueness value here assert(false); cryptonote::origin_data origin_tx_data; + rct::salvium_input_data_t sid; // derive the real output keypair const transfer_details& in_td = m_transfers[found->second]; @@ -12299,7 +12349,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string const std::vector in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx); keypair in_ephemeral; crypto::key_image in_img; - THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device(), false, origin_tx_data), + THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device(), false, origin_tx_data, sid), error::wallet_internal_error, "failed to generate key image"); THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch"); @@ -12515,7 +12565,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt crypto::secret_key scalar1; crypto::derivation_to_scalar(found_derivation, n, scalar1); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; - rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus || tx.rct_signatures.type == rct::RCTTypeFullProofs); + rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus || tx.rct_signatures.type == rct::RCTTypeFullProofs || tx.rct_signatures.type == rct::RCTTypeSalviumOne); const rct::key C = tx.rct_signatures.outPk[n].mask; rct::key Ctmp; THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); @@ -12529,6 +12579,14 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt received += amount; } } + + // HERE BE DRAGONS!!! + // SRCG: if this returns 0 received, but it's an AUDIT TX, then that is EXPECTED + bool audit = (tx.rct_signatures.type == rct::RCTTypeSalviumOne && tx.rct_signatures.salvium_data.salvium_data_type == rct::SalviumAudit); + if (audit && received == 0) { + received += tx.amount_burnt; + } + // LAND AHOY!!! } void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) @@ -12782,8 +12840,11 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation"); uint64_t received; check_tx_key_helper(tx, derivation, additional_derivations, address, received); + // HERE BE DRAGONS!!! + // SRCG: if this returns 0 received, but it's an AUDIT TX, then that is EXPECTED THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx.")); - + // LAND AHOY!!! + // concatenate all signature strings for (size_t i = 0; i < num_sigs; ++i) sig_str += @@ -13032,11 +13093,12 @@ std::string wallet2::get_reserve_proof(const boost::optional> // Populate this struct if you want to make use of "import_outputs" for Salvium!!! assert(false); origin_data od; + rct::salvium_input_data_t sid; // generate ephemeral secret key crypto::key_image ki; cryptonote::keypair in_ephemeral; - bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device(), false, od); + bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device(), false, od, sid); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); THROW_WALLET_EXCEPTION_IF(td.m_key_image_known && !td.m_key_image_partial && ki != td.m_key_image, @@ -14258,6 +14321,7 @@ process: // Populate this struct if you want to make use of "import_outputs" for Salvium!!! assert(false); origin_data od; + rct::salvium_input_data_t sid; THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast(i + offset)); crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); @@ -14268,7 +14332,7 @@ process: crypto::public_key out_key = td.get_public_key(); if (should_expand(td.m_subaddr_index)) create_one_off_subaddress(td.m_subaddr_index); - bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device(), false, od); + bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device(), false, od, sid); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); if (should_expand(td.m_subaddr_index)) expand_subaddresses(td.m_subaddr_index); @@ -14367,6 +14431,7 @@ size_t wallet2::import_outputs(const std::tuple