diff --git a/README.md b/README.md index 4e6509284..45f3e78c6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Salvium Zero v0.9.0-rc3 +# Salvium Zero v0.9.0-rc4 -Copyright (c) 2023-2024, Salvium +Copyright (c) 2023-2025, Salvium Portions Copyright (c) 2014-2023, The Monero Project Portions Copyright (c) 2012-2013 The Cryptonote developers. diff --git a/src/blockchain_utilities/blockchain_scanner.cpp b/src/blockchain_utilities/blockchain_scanner.cpp index 88c513249..dd065994d 100644 --- a/src/blockchain_utilities/blockchain_scanner.cpp +++ b/src/blockchain_utilities/blockchain_scanner.cpp @@ -40,7 +40,6 @@ #include "blockchain_db/blockchain_db.h" #include "oracle/pricing_record.h" #include "version.h" -#include "wallet/wallet2.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "bcutil" @@ -62,12 +61,6 @@ namespace scanner static bool stop_requested = false; -static const std::vector suspect_output_pubkeys = { - "fde608d92a95f8897836b648f012ef91c36cbb527c4085bfaaf03ff356fa7c68", - "1a0d89c9a8a9303cab47a9a8a841e7b86a8e102f38509e8476be3b42015033b0", - "1234567890123456789012345678901234567890123456789012345678901234" -}; - int main(int argc, char* argv[]) { TRY_ENTRY(); @@ -89,8 +82,6 @@ int main(int argc, char* argv[]) 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_decode_pvk = {"decodepvk", "Attempt to decode private view key data", 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); @@ -101,8 +92,6 @@ int main(int argc, char* argv[]) 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_decode_pvk); command_line::add_arg(desc_cmd_sett, arg_check_asset_types); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -143,8 +132,6 @@ int main(int argc, char* argv[]) 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_decode_pvk = command_line::get_arg(vm, arg_decode_pvk); 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. @@ -179,26 +166,6 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Loading blockchain from folder " << folder << " ..."); const std::string filename = folder.string(); - crypto::secret_key SK = crypto::null_skey; - if (opt_decode_pvk) { - epee::wipeable_string private_key_passphrase; - auto pwd_container = tools::password_container::prompt(true, "Enter passphrase for decoding private keys"); - if (!pwd_container) { - std::cerr << scanner::tr("Failed to read passphrase") << std::endl; - return 1; - } - private_key_passphrase = pwd_container->password(); - crypto::hash_to_scalar(private_key_passphrase.data(), private_key_passphrase.size(), SK); - crypto::public_key PK; - crypto::secret_key_to_public_key(SK, PK); - std::string PK_str = epee::string_tools::pod_to_hex(PK); - const std::string expected_PK_str = "5e860406bf9221dba6409faa6eb8fecd6f34acc4935634e76b64b90bf2b6d6a6"; - if (PK_str != expected_PK_str) { - std::cerr << scanner::tr("Invalid passphrase - PK produced was ") << PK_str << std::endl; - return 1; - } - } - try { db->open(filename, DBF_RDONLY); @@ -253,21 +220,9 @@ 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; - std::string enc_view_privkey_str; - bool flagged; - std::string reason; - }; - const std::map> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS; const uint64_t audit_lock_period = get_config(net_type).AUDIT_LOCK_PERIOD; - // 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); @@ -418,106 +373,6 @@ skip: // 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 commencing - check hard fork version for _this_block_ - if (audit_hard_forks.find(hf_version) == audit_hard_forks.end()) continue; - - // Pre-check - only attempt to verify legitimate AUDIT TXs - if (tx.type != cryptonote::transaction_type::AUDIT) continue; - - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "Considering AUDIT TX" << delimiter << std::endl; - - // Make sure the RCT data is correct - if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) { - std::cerr << "Aborting: Invalid RCT type " << tx.rct_signatures.type << " detected in AUDIT tx:" << tx_id << std::endl; - throw std::runtime_error("Aborting: Invalid RCT type detected in AUDIT tx"); - } - if (tx.rct_signatures.salvium_data.salvium_data_type != rct::SalviumAudit) { - std::cerr << "Aborting: Invalid 'salvium_data_type' " << tx.rct_signatures.salvium_data.salvium_data_type << " detected in AUDIT tx:" << tx_id << std::endl; - throw std::runtime_error("Aborting: Invalid 'salvium_data_type' detected in AUDIT tx"); - } - - // WE ARE AUDITING - RETRIEVE ANY WALLET SUMMARY FOR THIS WALLET - wallet_summary &ws = wallet_details[tx.rct_signatures.salvium_data.spend_pubkey]; - if (ws.txs.size()) { - if (ws.txs.back().first < (h-1)) { - std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "REVIEW: interval detected between audit TXs" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << std::endl; - } - } else { - ws.enc_view_privkey_str = tx.rct_signatures.salvium_data.enc_view_privkey_str; - ws.flagged = false; - ws.seen_closing_tx = false; - } - ws.txs.push_back({h, {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 << "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.txs.back().second.second = "wallet overflow"; - ws.flagged = true; - } - 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 - std::cerr << "invalid source asset_type for Ks:" << tx.rct_signatures.salvium_data.spend_pubkey << std::endl; - //throw std::runtime_error("Aborting: invalid source asset type found in tx"); - ws.txs.back().second.second = "invalid source asset_type '" + tx.source_asset_type + "'"; - ws.flagged = true; - } - - // 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; - ws.txs.back().second.second = "large AUDIT TX detected - amount:" + std::to_string(tx.amount_burnt/100000000) + std::string(" SAL"); - ws.flagged = true; - } 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; - ws.txs.back().second.second = "large AUDIT TX detected - amount:" + std::to_string(tx.amount_burnt/100000000) + std::string(" SAL"); - ws.flagged = true; - } - - // 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; - ws.txs.back().second.second = "large wallet balance detected - amount:" + std::to_string(ws.total_amount/100000000) + std::string(" SAL"); - ws.flagged = true; - } 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; - ws.txs.back().second.second = "large wallet balance detected - amount:" + std::to_string(ws.total_amount/100000000) + std::string(" SAL"); - ws.flagged = true; - } - - // Validate the Salvium audit data - //CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.outPk[0].mask, rv.salvium_data.cz_proof), "PRProof_Ver() failed on change proof"); - //CHECK_AND_ASSERT_THROW_MES(pseudoOuts.size() == rv.salvium_data.input_verification_data.size(), "incorrect number of input verification datasets"); - //CHECK_AND_ASSERT_THROW_MES(rv.salvium_data.spend_pubkey != crypto::null_pkey, "Invalid spend pubkey provided in audit data"); - //CHECK_AND_ASSERT_THROW_MES(rv.salvium_data.enc_view_privkey_str != "", "Invalid encrypted viewkey provided in audit data"); - for (size_t i=0; i < tx.rct_signatures.salvium_data.input_verification_data.size(); ++i) { - - // Recalculate the value of Ks from the Ko value - crypto::public_key ephemeral_pub = crypto::null_pkey; - CHECK_AND_ASSERT_THROW_MES(crypto::derive_public_key(tx.rct_signatures.salvium_data.input_verification_data[i].aR, - tx.rct_signatures.salvium_data.input_verification_data[i].i, - tx.rct_signatures.salvium_data.spend_pubkey, ephemeral_pub), - "Failed to derive ephemeral public key from audit data"); - // Now check this isn't in the list of suspect_output_pubkeys - std::string ephemeral_pub_str = epee::string_tools::pod_to_hex(ephemeral_pub); - for (size_t n=0; nderivation; sid.i = real_output_index; - return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev, use_origin_data, od); + return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev, use_origin_data, od, sid); } //--------------------------------------------------------------- - bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od) + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid) { if (hwdev.compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki)) { @@ -435,7 +435,11 @@ namespace cryptonote // 6. Create the key_image needed to be able to spend the output hwdev.generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); - + + // Update the SID to have the correct derivation for P_change as well + sid.aR_stake = derivation_P_change_tx; + sid.i_stake = od.output_index; + return true; } else { diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 9b802b53a..5dbfdd73c 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -105,7 +105,7 @@ namespace cryptonote bool get_tx_fee(const transaction& tx, uint64_t & fee); uint64_t get_tx_fee(const transaction& tx); bool generate_key_image_helper(const account_keys& ack, const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid); - bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od); + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid); void get_blob_hash(const blobdata& blob, crypto::hash& res); void get_blob_hash(const blobdata_ref& blob, crypto::hash& res); crypto::hash get_blob_hash(const blobdata& blob); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index d914bf9b2..cf7b0a23a 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -236,7 +236,7 @@ #define HF_VERSION_ENABLE_ORACLE 255 #define HF_VERSION_SLIPPAGE_YIELD 255 -#define TESTNET_VERSION 12 +#define TESTNET_VERSION 13 #define STAGENET_VERSION 1 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 6c633d1e6..cec0d1031 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -731,6 +731,7 @@ namespace cryptonote rct::salvium_input_data_t sid; const auto& out_key = reinterpret_cast(src_entr.outputs[src_entr.real_output].second.dest); bool use_origin_data = (src_entr.origin_tx_data.tx_type != cryptonote::transaction_type::UNSET); + sid.origin_tx_type = src_entr.origin_tx_data.tx_type; if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral,img, hwdev, use_origin_data, src_entr.origin_tx_data, sid)) { LOG_ERROR("Key image generation failed!"); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index d80f9c129..7cca4d63e 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2016, Monero Research Labs -// Portions Copyright (c) 2023-2024, Salvium (author: SRCG) +// Portions Copyright (c) 2023-2025, Salvium (author: SRCG) // // Author: Shen Noether // @@ -1726,15 +1726,25 @@ namespace rct { CHECK_AND_ASSERT_THROW_MES(rv.salvium_data.spend_pubkey != crypto::null_pkey, "Invalid spend pubkey provided in audit data"); CHECK_AND_ASSERT_THROW_MES(rv.salvium_data.enc_view_privkey_str != "", "Invalid encrypted viewkey provided in audit data"); for (size_t i=0; i < rv.salvium_data.input_verification_data.size(); ++i) { + + // Check for STAKE origin + crypto::public_key Ks = rv.salvium_data.spend_pubkey; + if (rv.salvium_data.input_verification_data[i].origin_tx_type == cryptonote::transaction_type::STAKE) { + // Verify the origin data provided + CHECK_AND_ASSERT_THROW_MES(crypto::derive_public_key(rv.salvium_data.input_verification_data[i].aR_stake, rv.salvium_data.input_verification_data[i].i_stake, rv.salvium_data.spend_pubkey, Ks), + "Failed to derive ephemeral public key from audit data"); + } + // Recalculate the value of Ks from the Ko value crypto::public_key ephemeral_pub = crypto::null_pkey; - CHECK_AND_ASSERT_THROW_MES(crypto::derive_public_key(rv.salvium_data.input_verification_data[i].aR, rv.salvium_data.input_verification_data[i].i, rv.salvium_data.spend_pubkey, ephemeral_pub), + CHECK_AND_ASSERT_THROW_MES(crypto::derive_public_key(rv.salvium_data.input_verification_data[i].aR, rv.salvium_data.input_verification_data[i].i, Ks, ephemeral_pub), "Failed to derive ephemeral public key from audit data"); // Now find this in the list of mixring entries bool found = false; for (size_t n=0; n& dest, const rct::salvium_ INSERT_INTO_JSON_OBJECT(dest, aR, salvium_input_data.aR); INSERT_INTO_JSON_OBJECT(dest, i, salvium_input_data.i); - + INSERT_INTO_JSON_OBJECT(dest, amount, salvium_input_data.amount); + INSERT_INTO_JSON_OBJECT(dest, origin_tx_type, salvium_input_data.origin_tx_type); + if (salvium_input_data.origin_tx_type == cryptonote::transaction_type::STAKE) { + INSERT_INTO_JSON_OBJECT(dest, aR_stake, salvium_input_data.aR_stake); + INSERT_INTO_JSON_OBJECT(dest, i_stake, salvium_input_data.i_stake); + } dest.EndObject(); } @@ -1519,6 +1524,12 @@ void fromJsonValue(const rapidjson::Value& val, rct::salvium_input_data_t& salvi GET_FROM_JSON_OBJECT(val, salvium_input_data.aR, aR); GET_FROM_JSON_OBJECT(val, salvium_input_data.i, i); + GET_FROM_JSON_OBJECT(val, salvium_input_data.amount, amount); + GET_FROM_JSON_OBJECT(val, salvium_input_data.origin_tx_type, origin_tx_type); + if (salvium_input_data.origin_tx_type == cryptonote::transaction_type::STAKE) { + GET_FROM_JSON_OBJECT(val, salvium_input_data.aR_stake, aR_stake); + GET_FROM_JSON_OBJECT(val, salvium_input_data.i_stake, i_stake); + } } void toJsonValue(rapidjson::Writer& dest, const rct::salvium_data_t& salvium_data) diff --git a/src/version.cpp.in b/src/version.cpp.in index b808d60be..a7390094c 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@" -#define DEF_SALVIUM_VERSION "0.9.0-rc3" +#define DEF_SALVIUM_VERSION "0.9.0-rc4" #define DEF_MONERO_VERSION_TAG "release" #define DEF_MONERO_VERSION "0.18.3.3" #define DEF_MONERO_RELEASE_NAME "Zero" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7cd80ca34..5a8ac9ab8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2204,6 +2204,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons // Flag to indicate this is a TX that uses a return_address bool use_od = false; cryptonote::origin_data od = AUTO_VAL_INIT(od); + rct::salvium_input_data_t sid; auto search = m_salvium_txs.find(pk_change); if (search != m_salvium_txs.end()) { size_t idx = search->second; @@ -2224,7 +2225,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons } else { - bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device(), use_od, od); + bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device(), use_od, od, sid); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != output_public_key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");