Compare commits
11 Commits
v0.9.0-rc2
...
v0.9.0-rc6
| Author | SHA1 | Date | |
|---|---|---|---|
| ab6e23c7b8 | |||
| ac59a40c2d | |||
| 0bd1351750 | |||
| 5fde8021dc | |||
| f9b946e929 | |||
| fee5676d8c | |||
| 97685a0849 | |||
| fa5ec5f12e | |||
| d2a71984cf | |||
| 9f1721261f | |||
| e68841d306 |
@@ -1,6 +1,6 @@
|
||||
# Salvium Zero v0.9.0-rc2
|
||||
# Salvium Zero v0.9.0-rc6
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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,10 +61,6 @@ namespace scanner
|
||||
|
||||
static bool stop_requested = false;
|
||||
|
||||
static const std::vector<std::string> suspect_output_pubkeys = {
|
||||
"1234567890123456789012345678901234567890123456789012345678901234"
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
@@ -87,8 +82,6 @@ int main(int argc, char* argv[])
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
|
||||
const command_line::arg_descriptor<std::string> arg_delimiter = {"delimiter", "\"<string>\"", DELIM};
|
||||
const command_line::arg_descriptor<std::string> arg_stake_mode = {"stake", "\"<string>\"", DEF_STAKE_MODE};
|
||||
const command_line::arg_descriptor<bool> arg_audit = {"audit", "Scan for audit issues", false};
|
||||
const command_line::arg_descriptor<bool> arg_decode_pvk = {"decodepvk", "Attempt to decode private view key data", false};
|
||||
const command_line::arg_descriptor<bool> arg_check_asset_types = {"check-asset-types", "Scan for asset-type issues", false};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
@@ -99,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);
|
||||
|
||||
@@ -141,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.
|
||||
@@ -177,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);
|
||||
@@ -251,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<std::pair<uint64_t, std::pair<crypto::hash, std::string>>> txs;
|
||||
bool seen_closing_tx;
|
||||
std::string enc_view_privkey_str;
|
||||
bool flagged;
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
const std::map<uint8_t, std::pair<std::string, std::string>> 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<crypto::public_key, wallet_summary> wallet_details;
|
||||
|
||||
for (uint64_t h = block_start; h < block_stop; ++h)
|
||||
{
|
||||
cryptonote::blobdata bd = db->get_block_blob_from_height(h);
|
||||
@@ -416,103 +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;
|
||||
|
||||
// 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; n<suspect_output_pubkeys.size(); ++n) {
|
||||
if (ephemeral_pub_str == suspect_output_pubkeys[n]) {
|
||||
ws.txs.back().second.second = "SUSPECT OUTPUT PUBKEY DETECTED : '" + ephemeral_pub_str + "'";
|
||||
ws.flagged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currblks++;
|
||||
@@ -521,57 +381,6 @@ skip:
|
||||
break;
|
||||
}
|
||||
|
||||
// Iterate over all the flagged wallets
|
||||
for (const auto &wallet_entry: wallet_details) {
|
||||
// Was the wallet flagged?
|
||||
if (!wallet_entry.second.flagged) continue;
|
||||
|
||||
// Decrypt the wallet private viewkey
|
||||
crypto::secret_key wallet_private_view_key;
|
||||
bool ok = cryptonote::decrypt_pvk(wallet_entry.second.enc_view_privkey_str, SK, wallet_private_view_key);
|
||||
if (!ok) {
|
||||
// report error
|
||||
throw std::runtime_error("Well shit");
|
||||
}
|
||||
|
||||
// Why was it flagged?
|
||||
for (const auto &tx: wallet_entry.second.txs) {
|
||||
// Check for a reason on this TX
|
||||
if (tx.second.second != "") {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new "view only" wallet file
|
||||
try {
|
||||
// Derive the public view key from the private view key
|
||||
crypto::public_key wallet_public_view_key;
|
||||
bool ok = crypto::secret_key_to_public_key(wallet_private_view_key, wallet_public_view_key);
|
||||
|
||||
// Construct the Monero address with the public keys
|
||||
cryptonote::account_public_address address;
|
||||
address.m_view_public_key = wallet_public_view_key;
|
||||
address.m_spend_public_key = wallet_entry.first;
|
||||
|
||||
std::string daemon_address = (opt_testnet) ? "http://127.0.0.1:29081" : (opt_stagenet) ? "http://127.0.0.1:39081" : "http://127.0.0.1:19081";
|
||||
std::string wallet_password = "1234";
|
||||
std::string wallet_path = epee::string_tools::pod_to_hex(wallet_entry.first) + "_wallet";
|
||||
|
||||
// Initialize the view-only wallet
|
||||
tools::wallet2 w{net_type};
|
||||
w.set_daemon(daemon_address);
|
||||
w.set_refresh_from_block_height(0); // Set scanning from the genesis block
|
||||
|
||||
// Generate the wallet file
|
||||
w.generate(wallet_path, wallet_password, address, wallet_private_view_key, true);
|
||||
|
||||
// Save the wallet file
|
||||
w.store();
|
||||
std::cout << "View-only wallet created successfully at: " << wallet_path << std::endl;
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Error creating view-only wallet: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Stats reporting error", 1);
|
||||
|
||||
@@ -315,15 +315,26 @@ namespace cryptonote
|
||||
}
|
||||
}
|
||||
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: This FAILS under certain circumstances to find the correct subaddress for STAKE returns, and this BREAKS AUDITING
|
||||
boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev);
|
||||
CHECK_AND_ASSERT_MES(subaddr_recv_info, false, "key image helper: given output pubkey doesn't seem to belong to this address");
|
||||
|
||||
/*
|
||||
if (use_origin_data) {
|
||||
// Try something a little special to find the subaddress index
|
||||
crypto::key_derivation recv_derivation_od = AUTO_VAL_INIT(recv_derivation_od);
|
||||
r = hwdev.generate_key_derivation(od.tx_pub_key, ack.m_view_secret_key, recv_derivation_od);
|
||||
boost::optional<subaddress_receive_info> subaddr_recv_info_od = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation_od, additional_recv_derivations, od.output_index,hwdev);
|
||||
}
|
||||
*/
|
||||
// LAND AHOY!!!
|
||||
|
||||
sid.aR = subaddr_recv_info->derivation;
|
||||
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))
|
||||
{
|
||||
@@ -417,6 +428,13 @@ namespace cryptonote
|
||||
crypto::secret_key sk_spend = crypto::null_skey;
|
||||
CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(derivation_P_change_tx, od.output_index, spend_skey, sk_spend), false, "Failed to derive secret key for P_change");
|
||||
|
||||
// 3.5 Handle subaddresses
|
||||
if (!received_index.is_zero()) {
|
||||
crypto::secret_key scalar_step3;
|
||||
hwdev.sc_secret_add(scalar_step3, sk_spend, subaddr_sk);
|
||||
sk_spend = scalar_step3;
|
||||
}
|
||||
|
||||
// 4. Derive the public key from the secret key for verification purposes
|
||||
crypto::public_key change_pk;
|
||||
CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(sk_spend, change_pk), false, "Failed to derive public key for P_change");
|
||||
@@ -435,7 +453,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 {
|
||||
|
||||
@@ -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<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& 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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -161,6 +161,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke
|
||||
std::vector<output_data_t> outputs;
|
||||
|
||||
bool found = false;
|
||||
/*
|
||||
auto it = m_scan_table.find(tx_prefix_hash);
|
||||
if (it != m_scan_table.end())
|
||||
{
|
||||
@@ -171,6 +172,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (!found)
|
||||
{
|
||||
@@ -5970,10 +5972,10 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
// no need to check for duplicate here.
|
||||
// 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<uint64_t> asset_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets);
|
||||
std::vector<uint64_t> absolute_offsets;
|
||||
m_db->get_output_id_from_asset_type_output_index(in_to_key.asset_type, asset_offsets, absolute_offsets);
|
||||
auto absolute_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets);
|
||||
//std::vector<uint64_t> asset_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets);
|
||||
//std::vector<uint64_t> 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);
|
||||
|
||||
@@ -543,13 +543,20 @@ namespace cryptonote
|
||||
std::string encrypted_data = std::string(reinterpret_cast<char*>(&ephemeral_pk), sizeof(ephemeral_pk)) +
|
||||
std::string(reinterpret_cast<char*>(&iv), sizeof(iv)) +
|
||||
ciphertext;
|
||||
return encrypted_data;
|
||||
return epee::string_tools::buff_to_hex_nodelimer(encrypted_data);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
// Decrypt function
|
||||
bool decrypt_pvk(const std::string &encrypted_data, const crypto::secret_key &SK, crypto::secret_key &pvk) {
|
||||
bool decrypt_pvk(const std::string &encrypted_data_hex, const crypto::secret_key &SK, crypto::secret_key &pvk) {
|
||||
//std::string decrypt_pvk(const std::string &encrypted_data, const crypto::secret_key &SK) {
|
||||
// Step 1: Extract ephemeral_pk, iv, and ciphertext from encrypted_data
|
||||
std::string encrypted_data;
|
||||
for (size_t i = 0; i < encrypted_data_hex.length(); i += 2) {
|
||||
std::istringstream iss(encrypted_data_hex.substr(i, 2));
|
||||
int byte;
|
||||
iss >> std::hex >> byte;
|
||||
encrypted_data += static_cast<char>(byte);
|
||||
}
|
||||
const char *data_ptr = encrypted_data.data();
|
||||
crypto::public_key ephemeral_pk;
|
||||
memcpy(&ephemeral_pk, data_ptr, sizeof(ephemeral_pk));
|
||||
@@ -731,6 +738,7 @@ namespace cryptonote
|
||||
rct::salvium_input_data_t sid;
|
||||
const auto& out_key = reinterpret_cast<const crypto::public_key&>(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!");
|
||||
|
||||
@@ -46,9 +46,6 @@ const hardfork_t mainnet_hard_forks[] = {
|
||||
|
||||
// version 5 starts from block 136100, which is on or around the 9th of January, 2025. Fork time finalised on 2025-01-08. No fork voting occurs for the v5 fork.
|
||||
{ 5, 136100, 0, 1736265945 },
|
||||
|
||||
// version 6 starts from block 146146, which is on or around the 23rd of January, 2025. Fork time finalised on 2025-01-17. No fork voting occurs for the v6 fork.
|
||||
//{ 6, 146146, 0, 1736592100 },
|
||||
};
|
||||
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
|
||||
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
@@ -63,14 +60,14 @@ const hardfork_t testnet_hard_forks[] = {
|
||||
// version 3 starts from block 500
|
||||
{ 3, 500, 0, 1729518000 },
|
||||
|
||||
// version 4 starts from block 600
|
||||
// version 4 (full proofs) starts from block 600
|
||||
{ 4, 600, 0, 1734607000 },
|
||||
|
||||
// version 5 starts from block 700
|
||||
{ 5, 700, 0, 1734607005 },
|
||||
// version 5 (TX shutdown) starts from block 800
|
||||
{ 5, 800, 0, 1734607005 },
|
||||
|
||||
// version 5 starts from block 800
|
||||
{ 6, 800, 0, 1734608000 },
|
||||
// version 6 (audit) starts from block 815
|
||||
{ 6, 815, 0, 1734608000 },
|
||||
};
|
||||
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
|
||||
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
|
||||
+12
-2
@@ -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 <shen.noether@gmx.com>
|
||||
//
|
||||
@@ -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::UNSET) {
|
||||
// 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<rv.mixRing[i].size(); ++n) {
|
||||
if (ephemeral_pub == rct::rct2pk(rv.mixRing[i][n].dest)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_THROW_MES(found, "Failed to match ephemeral public key provided in audit data");
|
||||
|
||||
@@ -55,6 +55,7 @@ extern "C" {
|
||||
#include "serialization/binary_archive.h"
|
||||
#include "serialization/json_archive.h"
|
||||
|
||||
#include "cryptonote_protocol/enums.h"
|
||||
|
||||
//Define this flag when debugging to get additional info on the console
|
||||
#ifdef DBG
|
||||
@@ -336,11 +337,19 @@ namespace rct {
|
||||
crypto::key_derivation aR;
|
||||
xmr_amount amount;
|
||||
size_t i;
|
||||
uint8_t origin_tx_type;
|
||||
crypto::key_derivation aR_stake;
|
||||
size_t i_stake;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(aR)
|
||||
VARINT_FIELD(amount)
|
||||
VARINT_FIELD(i)
|
||||
VARINT_FIELD(origin_tx_type)
|
||||
if (origin_tx_type != cryptonote::transaction_type::UNSET) {
|
||||
FIELD(aR_stake)
|
||||
FIELD(i_stake)
|
||||
}
|
||||
END_SERIALIZE()
|
||||
};
|
||||
struct salvium_data_t {
|
||||
|
||||
@@ -1506,7 +1506,12 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& 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::UNSET) {
|
||||
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::UNSET) {
|
||||
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<epee::byte_stream>& dest, const rct::salvium_data_t& salvium_data)
|
||||
|
||||
@@ -6742,11 +6742,6 @@ 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;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6758,7 +6753,9 @@ bool simple_wallet::transfer_main(
|
||||
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
|
||||
return false;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
if (transfer_type == Audit)
|
||||
while (subaddr_indices.size() > 1)
|
||||
subaddr_indices.erase(std::prev(subaddr_indices.end())); }
|
||||
|
||||
uint32_t priority = m_wallet->get_default_priority();
|
||||
if (local_args.size() > 0 && parse_priority(local_args[0], priority))
|
||||
@@ -6796,7 +6793,7 @@ bool simple_wallet::transfer_main(
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1;
|
||||
const size_t min_args = (transfer_type == Audit) ? 0 : (transfer_type == TransferLocked) ? 2 : 1;
|
||||
if(local_args.size() < min_args)
|
||||
{
|
||||
fail_msg_writer() << tr("wrong number of arguments");
|
||||
@@ -8350,7 +8347,7 @@ bool simple_wallet::convert(const std::vector<std::string> &args_)
|
||||
bool simple_wallet::audit(const std::vector<std::string> &args_)
|
||||
{
|
||||
// TODO: add locked versions
|
||||
if (args_.size() != 0)
|
||||
if (args_.size() != 1)
|
||||
{
|
||||
PRINT_USAGE(USAGE_AUDIT);
|
||||
return true;
|
||||
@@ -8362,24 +8359,15 @@ bool simple_wallet::audit(const std::vector<std::string> &args_)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> local_args;
|
||||
local_args.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account,0}));
|
||||
//local_args.insert(local_args.end(), args_.begin(), args_.end());
|
||||
std::vector<std::string> local_args = args_;
|
||||
const std::map<uint8_t, std::pair<std::string, std::string>> audit_hard_forks = get_config(m_wallet->nettype()).AUDIT_HARD_FORKS;
|
||||
const uint8_t hf_version = m_wallet->get_current_hard_fork();
|
||||
if (audit_hard_forks.find(hf_version) != audit_hard_forks.end()) {
|
||||
|
||||
// Get the asset types
|
||||
const std::pair<std::string, std::string> audit_asset_types = audit_hard_forks.at(hf_version);
|
||||
transfer_main(Audit, audit_asset_types.first, audit_asset_types.first, local_args, false);
|
||||
|
||||
// Check to see if the user has a balance of the coins being audited
|
||||
uint64_t unlocked_balance = m_wallet->unlocked_balance_all(true, audit_asset_types.first);
|
||||
if (unlocked_balance > 0) {
|
||||
local_args.push_back(print_money(unlocked_balance));
|
||||
transfer_main(Audit, audit_asset_types.first, audit_asset_types.first, local_args, false);
|
||||
} else {
|
||||
fail_msg_writer() << tr("No coins currently available to audit. Only unlocked coins can be audited.");
|
||||
}
|
||||
} else {
|
||||
fail_msg_writer() << tr("Audit command is not available at this time.");
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION "0.9.0-rc2"
|
||||
#define DEF_SALVIUM_VERSION "0.9.0-rc6"
|
||||
#define DEF_MONERO_VERSION_TAG "release"
|
||||
#define DEF_MONERO_VERSION "0.18.3.3"
|
||||
#define DEF_MONERO_RELEASE_NAME "Zero"
|
||||
|
||||
+32
-8
@@ -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;
|
||||
@@ -2214,6 +2215,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
|
||||
od.tx_pub_key = get_tx_pub_key_from_extra(td_origin.m_tx);
|
||||
od.output_index = td_origin.m_internal_output_index;
|
||||
od.tx_type = td_origin.m_tx.type;
|
||||
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: this is necessary to be able to receive protocol_tx outputs to the correct wallet subaddress
|
||||
tx_scan_info.received->index = td_origin.m_subaddr_index;
|
||||
// LAND AHOY!!!
|
||||
}
|
||||
|
||||
if (m_multisig)
|
||||
@@ -2224,7 +2230,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");
|
||||
@@ -2893,7 +2899,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
crypto::public_key P_change = crypto::null_pkey;
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(tx.vout[0], P_change), error::wallet_internal_error, "Failed to get change output public key");
|
||||
//m_subaddresses[P_change] = {0x50524F54,0x4F434F4C}; /* {PROT,OCOL} - seemed like a good idea at the time, but harder to implement! */
|
||||
m_subaddresses[P_change] = {0,0};
|
||||
m_subaddresses[P_change] = tx_scan_info[o].received->index;//{0,0};
|
||||
//m_subaddresses[P_change] = {0,0};
|
||||
m_salvium_txs.insert({P_change, m_transfers.size()-1});
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
@@ -10123,9 +10130,13 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee);
|
||||
|
||||
uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
|
||||
for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
|
||||
uint32_t subaddr_index = m_transfers[*selected_transfers.begin()].m_subaddr_index.minor;
|
||||
for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) {
|
||||
THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
|
||||
|
||||
if (tx_type == cryptonote::transaction_type::AUDIT) {
|
||||
THROW_WALLET_EXCEPTION_IF(subaddr_index != m_transfers[*i].m_subaddr_index.minor, error::wallet_internal_error, "the AUDIT tx uses funds from multiple subaddresses");
|
||||
}
|
||||
}
|
||||
if (outs.empty())
|
||||
get_outs(outs, selected_transfers, fake_outputs_count, all_rct, valid_public_keys_cache); // may throw
|
||||
|
||||
@@ -10212,11 +10223,20 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
|
||||
change_dts.amount = found_money - needed_money;
|
||||
change_dts.asset_type = source_asset;
|
||||
change_dts.addr = get_subaddress({subaddr_account, 0});
|
||||
change_dts.is_subaddress = subaddr_account != 0;
|
||||
change_dts.addr = get_subaddress({subaddr_account, subaddr_index});
|
||||
change_dts.is_subaddress = subaddr_account != 0 || subaddr_index != 0;
|
||||
change_dts.is_change = true;
|
||||
splitted_dsts.push_back(change_dts);
|
||||
|
||||
account_keys a_keys = m_account.get_keys();
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: add support for auditing of subaddresses
|
||||
if (tx_type == cryptonote::transaction_type::AUDIT && (subaddr_account != 0 || subaddr_index != 0)) {
|
||||
// Overwrite the public spend key and view key
|
||||
a_keys.m_account_address = get_subaddress({subaddr_account, subaddr_index});
|
||||
}
|
||||
// LAND AHOY!!!
|
||||
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
crypto::secret_key multisig_tx_key_entropy;
|
||||
@@ -10260,7 +10280,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
std::vector<std::pair<std::string, std::string>> circ_amounts;
|
||||
THROW_WALLET_EXCEPTION_IF(!get_circulating_supply(circ_amounts), error::wallet_internal_error, "Failed to get circulating supply");
|
||||
// make a normal tx
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(a_keys/*m_account.get_keys()*/, m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
|
||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
|
||||
}
|
||||
@@ -11715,7 +11735,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
// SRCG: should the subaddress be forced to TRUE for _RETURN_ TXs and FALSE for all others?!?!?
|
||||
// add N - 1 outputs for correct initial fee estimation
|
||||
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i) {
|
||||
tx.dsts.push_back(tx_destination_entry(1, address, tx_type == cryptonote::transaction_type::RETURN, tx_type == cryptonote::transaction_type::RETURN));
|
||||
if (tx_type == cryptonote::transaction_type::AUDIT) {
|
||||
tx.dsts.push_back(tx_destination_entry(1, address, tx_type == cryptonote::transaction_type::RETURN, tx_type == cryptonote::transaction_type::RETURN));
|
||||
} else {
|
||||
tx.dsts.push_back(tx_destination_entry(1, address, tx_type == cryptonote::transaction_type::RETURN, tx_type == cryptonote::transaction_type::RETURN));
|
||||
}
|
||||
tx.dsts.back().asset_type = asset_type;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user