diff --git a/README.md b/README.md index 04cadfd9c..2f083847f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Salvium Zero v0.8.0 +# Salvium Zero v0.9.0-rc9 Copyright (c) 2023-2024, Salvium Portions Copyright (c) 2014-2023, The Monero Project @@ -172,7 +172,7 @@ invokes cmake commands as needed. ```bash cd salvium - git checkout v0.8.0 + git checkout v0.9.0 make ``` @@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ```bash git clone https://github.com/salvium/salvium cd salvium - git checkout v0.8.0 + git checkout v0.9.0 ``` * Build: @@ -370,10 +370,10 @@ application. cd salvium ``` -* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.8.0'. If you don't care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.0'. If you don't care about the version and just want binaries from master, skip this step: ```bash - git checkout v0.8.0 + git checkout v0.9.0 ``` * If you are on a 64-bit system, run: diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 0a8057849..1982ee1c8 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -269,6 +269,7 @@ uint64_t BlockchainDB::add_block( const std::pair& blck , const std::vector>& txs , const cryptonote::network_type nettype , cryptonote::yield_block_info& ybi + , cryptonote::audit_block_info& abi ) { const block &blk = blck.first; @@ -309,7 +310,7 @@ uint64_t BlockchainDB::add_block( const std::pair& blck } std::map slippage_counts; - uint64_t yield_total = 0; + uint64_t audit_total = 0, yield_total = 0; if (blk.protocol_tx.version == 2) { num_rct_outs += blk.protocol_tx.vout.size(); @@ -356,7 +357,12 @@ uint64_t BlockchainDB::add_block( const std::pair& blck slippage_counts[asset_type] += tx.first.amount_burnt; } - // Is this a YIELD TX? + // Is this an AUDIT TX? + if (tx.first.type == cryptonote::transaction_type::AUDIT) { + audit_total += tx.first.amount_burnt; + } + + // Is this a STAKE TX? if (tx.first.type == cryptonote::transaction_type::STAKE) { yield_total += tx.first.amount_burnt; } @@ -410,7 +416,7 @@ uint64_t BlockchainDB::add_block( const std::pair& blck // call out to subclass implementation to add the block & metadata time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, nettype, ybi); + add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, nettype, ybi, abi); TIME_MEASURE_FINISH(time1); time_add_block1 += time1; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 1c570aa88..dd54f301f 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -430,8 +430,10 @@ private: const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, + uint64_t audit_total, const cryptonote::network_type nettype, - cryptonote::yield_block_info& ybi + cryptonote::yield_block_info& ybi, + cryptonote::audit_block_info& abi ) = 0; /** @@ -886,6 +888,7 @@ public: , const std::vector>& txs , const cryptonote::network_type nettype , cryptonote::yield_block_info& ybi + , cryptonote::audit_block_info& abi ); /** @@ -1915,6 +1918,9 @@ public: */ virtual uint64_t get_database_size() const = 0; + virtual int get_audit_block_info(const uint64_t height, audit_block_info& abi) const = 0; + virtual int get_audit_tx_info(const uint64_t height, std::vector& ati_container) const = 0; + virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const = 0; virtual int get_yield_tx_info(const uint64_t height, std::vector& yti_container) const = 0; diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 168070e53..e290832ab 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -215,6 +215,9 @@ namespace * yield_block_data block height {slippage_coins, locked_coins, lc_total, network_health} * yield_tx_data block height {txn hash, locked_coins, return_address} * + * audit_data block height {locked_coins, lc_total} + * audit_tx_data block height {txn hash, locked_coins, return_address} + * * Note: where the data items are of uniform size, DUPFIXED tables have * been used to save space. In most of these cases, a dummy "zerokval" * key is used when accessing the table; the Key listed above will be @@ -289,6 +292,8 @@ const char* const LMDB_CIRC_SUPPLY_TALLY = "circ_supply_tally"; */ const char* const LMDB_YIELD_TXS = "yield_txs"; const char* const LMDB_YIELD_BLOCKS = "yield_blocks"; +const char* const LMDB_AUDIT_TXS = "audit_txs"; +const char* const LMDB_AUDIT_BLOCKS = "audit_blocks"; const char zerokey[8] = {0}; const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey }; @@ -800,6 +805,75 @@ estim: return threshold_size; } +int BlockchainLMDB::get_audit_block_info(const uint64_t height, audit_block_info& abi) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + // Clear the ABI, just in case + std::memset(&abi, 0, sizeof(struct audit_block_info)); + + // Query for the matured AUDIT_BLOCK_INFO information + TXN_PREFIX_RDONLY(); + RCURSOR(audit_blocks); + + MDB_val v; + MDB_val_set(k, height); + int ret = mdb_cursor_get(m_cur_audit_blocks, &k, &v, MDB_SET); + if (ret == MDB_NOTFOUND) { + LOG_ERROR("Failed to locate ABI for block height " << height); + return ret; + } + if (ret) + throw0(DB_ERROR(lmdb_error("Failed to enumerate audit block info: ", ret).c_str())); + + audit_block_info *p = (audit_block_info*)v.mv_data; + abi = *p; + + TXN_POSTFIX_RDONLY(); + + // Return success to caller + return ret; +} + +int BlockchainLMDB::get_audit_tx_info(const uint64_t height, std::vector& ati_container) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + // Clear the container + ati_container.clear(); + + // Query for the (presumably matured) AUDIT_TX_INFO information (we actually reuse YIELD_TX_INFO for this) + TXN_PREFIX_RDONLY(); + RCURSOR(audit_txs); + + MDB_val v; + MDB_val_set(k, height); + MDB_cursor_op op = MDB_SET; + while (1) + { + int ret = mdb_cursor_get(m_cur_audit_txs, &k, &v, op); + op = MDB_NEXT_DUP; + if (ret == MDB_NOTFOUND) + break; + if (ret) + throw0(DB_ERROR(lmdb_error("Failed to enumerate audit TX info: ", ret).c_str())); + + // Get the data + yield_tx_info *p = (yield_tx_info*)v.mv_data; + // Push result back into the container + ati_container.emplace_back(*p); + // Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish) + ati_container.back().block_height = height; + } + + TXN_POSTFIX_RDONLY(); + + // Return success to caller + return 0; +} + int BlockchainLMDB::get_yield_block_info(const uint64_t height, yield_block_info& ybi) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -855,9 +929,12 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector k(m_height - 1); MDB_val h = k; if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH))) @@ -1031,6 +1120,11 @@ void BlockchainLMDB::remove_block() throw1(BLOCK_DNE(lmdb_error("Attempting to remove yield block info that's not in the db: ", result).c_str())); if ((result = mdb_cursor_del(m_cur_yield_blocks, 0))) throw1(DB_ERROR(lmdb_error("Failed to add removal of yield block info to db transaction: ", result).c_str())); + + // Is the block within an audit window? + if ((result = mdb_cursor_get(m_cur_audit_blocks, &k2, NULL, MDB_SET)) == 0) + if ((result = mdb_cursor_del(m_cur_audit_blocks, 0))) + throw1(DB_ERROR(lmdb_error("Failed to add removal of audit block info to db transaction: ", result).c_str())); } boost::multiprecision::int128_t @@ -1116,6 +1210,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons CURSOR(circ_supply) CURSOR(circ_supply_tally) CURSOR(yield_txs) + CURSOR(audit_txs) MDB_val_set(val_tx_id, tx_id); MDB_val_set(val_h, tx_hash); @@ -1183,13 +1278,19 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons throw0(DB_ERROR(lmdb_error("Failed to add prunable tx prunable hash to db transaction: ", result).c_str())); } + const uint8_t hf_version = m_hardfork->get_ideal_version(m_height); if (tx.type == cryptonote::transaction_type::MINER) { // Update the circulating supply tally because of potentially burnt block_reward proportion - MDB_val_copy source_idx(cryptonote::asset_id_from_type("SAL")); + std::string miner_asset_type = "SAL"; + if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) { + miner_asset_type = "SAL1"; + } + + MDB_val_copy source_idx(cryptonote::asset_id_from_type(miner_asset_type)); boost::multiprecision::int128_t source_tally = 0; result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); - if (result && (m_height>0 || result != MDB_NOTFOUND)) + if (result && result != MDB_NOTFOUND) throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); boost::multiprecision::int128_t final_source_tally = source_tally; for (const auto& out: tx.vout) { @@ -1203,20 +1304,44 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons } write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); LOG_PRINT_L1("tx ID " << tx_id << "\n\tTally before burn = " << source_tally.str() << "\n\tTally after burn = " << final_source_tally.str()); + + MDB_val_copy burn_idx(cryptonote::asset_id_from_type("BURN")); + boost::multiprecision::int128_t burn_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally); + if (result && result != MDB_NOTFOUND) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); + // Sanity check - prevent overflow + if (burn_tally > burn_tally + tx.amount_burnt) + throw0(DB_ERROR("burn overflow detected when adding miner_tx for db transaction")); + boost::multiprecision::int128_t final_burn_tally = burn_tally + tx.amount_burnt; + write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally); } - if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE) { + if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::TRANSFER) { // Get the current tally value for the source currency type MDB_val_copy source_idx(cryptonote::asset_id_from_type(tx.source_asset_type)); boost::multiprecision::int128_t source_tally = 0; result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); boost::multiprecision::int128_t final_source_tally = source_tally - tx.amount_burnt - tx.rct_signatures.txnFee; - boost::multiprecision::int128_t coinbase = get_block_already_generated_coins(m_height-1); if (result) throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); + // Sanity check - prevent underflow + if (source_tally < final_source_tally) + throw0(DB_ERROR("numeric underflow detected when processing C/B/S/A/T for db transaction")); write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); LOG_PRINT_L1("tx ID " << tx_id << "\n\tTally before burn = " << source_tally.str() << "\n\tTally after burn = " << final_source_tally.str()); + + MDB_val_copy burn_idx(cryptonote::asset_id_from_type("BURN")); + boost::multiprecision::int128_t burn_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally); + if (result && /*(m_height>0 ||*/ result != MDB_NOTFOUND/*)*/) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); + boost::multiprecision::int128_t final_burn_tally = burn_tally + tx.amount_burnt + tx.rct_signatures.txnFee; + // Sanity check - prevent underflow + if (burn_tally > final_burn_tally) + throw0(DB_ERROR("burn overflow detected when adding tx for db transaction")); + write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally); } if (tx.type == cryptonote::transaction_type::PROTOCOL) { @@ -1240,17 +1365,25 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons MDB_val_copy source_idx(asset.first); boost::multiprecision::int128_t source_tally = 0; result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); - if (result) + if (result == MDB_NOTFOUND) + throw0(DB_ERROR("minted asset not found")); + else if (result) throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); + if (source_tally > source_tally + asset.second) + throw0(DB_ERROR("add_transaction_data() - mint overflow")); boost::multiprecision::int128_t final_source_tally = source_tally + asset.second; - boost::multiprecision::int128_t coinbase = get_block_already_generated_coins(m_height-1); - if (source_tally == 0 && result == MDB_NOTFOUND) { - if (tx.source_asset_type == "SAL") { - final_source_tally += coinbase; - } - } write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); LOG_PRINT_L1("tx ID " << tx_id << "\n\tAsset Type = " << cryptonote::asset_type_from_id(asset.first) << "\n\tTally before burn =" << source_tally.str() << "\n\tTally after burn =" << final_source_tally.str()); + + MDB_val_copy burn_idx(cryptonote::asset_id_from_type("BURN")); + boost::multiprecision::int128_t burn_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally); + if (result && result != MDB_NOTFOUND) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); + if (burn_tally < asset.second) + throw0(DB_ERROR("add_transaction_data() - burn underflow")); + boost::multiprecision::int128_t final_burn_tally = burn_tally - asset.second; + write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally); } } @@ -1308,6 +1441,60 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons throw0(DB_ERROR( lmdb_error("Failed to add tx yield data to db transaction: ", result).c_str() )); } + // Is there audit_tx data to add? + if (tx.type == cryptonote::transaction_type::AUDIT) { + + // Create the object we are going to write to the database + yield_tx_info audit_data; + audit_data.block_height = m_height; + audit_data.tx_hash = tx_hash; + if (tx.version == TRANSACTION_VERSION_2_OUTS) { + if (tx.return_address == crypto::null_pkey) + throw0(DB_ERROR("missing return_address entry (needed to create audit data for PROTOCOL_TX) - v2 STAKE")); + audit_data.return_address = tx.return_address; + } else if (tx.version >= TRANSACTION_VERSION_N_OUTS) { + if (tx.return_address_list.empty()) + throw0(DB_ERROR("no return_address_list entry (needed to create audit data for the PROTOCOL_TX)")); + else if (tx.return_address_list.size() > 1) + throw0(DB_ERROR("too many return_address_list entries provided (only one needed to create audit data for the PROTOCOL_TX)")); + audit_data.return_address = tx.return_address_list[0]; + } + audit_data.locked_coins = tx.amount_burnt; + if (tx.vin.empty()) + throw0(DB_ERROR("tx.vin is empty (needed to create audit data for the PROTOCOL_TX)")); + if (tx.vin[0].type() != typeid(cryptonote::txin_to_key)) + throw0(DB_ERROR("tx.vin[0] is wrong type (needed to create audit data for the PROTOCOL_TX)")); + audit_data.return_pubkey = tx.return_pubkey; + if (tx.vout.size() != 1) + throw0(DB_ERROR("tx.vout is wrong size (needed to create audit data for the PROTOCOL_TX)")); + if (!cryptonote::get_output_public_key(tx.vout[0], audit_data.P_change)) + throw0(DB_ERROR("failed to get P_change from tx.vout[0] (needed to create audit data for the PROTOCOL_TX)")); + + // Because LMDB is shockingly bad at handling duplicates, we have resorted to using a counter of elements + // in the first element of the struct. + MDB_val data; + MDB_val_set(val_height, m_height); + result = mdb_cursor_get(m_cur_audit_txs, &val_height, &data, MDB_SET); + if (!result) + { + mdb_size_t num_elems = 0; + result = mdb_cursor_count(m_cur_audit_txs, &num_elems); + if (result) + throw0(DB_ERROR(std::string("Failed to get number of audit TXs for height: ").append(mdb_strerror(result)).c_str())); + audit_data.block_height = num_elems; + } + else if (result != MDB_NOTFOUND) + throw0(DB_ERROR(lmdb_error("Failed to get output amount in db transaction: ", result).c_str())); + else + audit_data.block_height = 0; + + // Now we know how many there are, write out the data to the DB + MDB_val_set(val_audit_tx_data, audit_data); + result = mdb_cursor_put(m_cur_audit_txs, &val_height, &val_audit_tx_data, MDB_APPENDDUP); + if (result) + throw0(DB_ERROR( lmdb_error("Failed to add tx audit data to db transaction: ", result).c_str() )); + } + return tx_id; } @@ -1330,6 +1517,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const CURSOR(tx_outputs) CURSOR(circ_supply_tally) CURSOR(yield_txs) + CURSOR(audit_txs) MDB_val_set(val_h, tx_hash); @@ -1373,42 +1561,70 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const throw1(DB_ERROR(lmdb_error("Failed to add removal of prunable hash tx to db transaction: ", result).c_str())); } + const uint8_t hf_version = m_hardfork->get_ideal_version(m_height); if (tx.type == cryptonote::transaction_type::MINER) { // Update the circulating supply tally because of potentially burnt block_reward proportion - MDB_val_copy source_idx(cryptonote::asset_id_from_type("SAL")); + std::string miner_asset_type = "SAL"; + if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) { + miner_asset_type = "SAL1"; + } + + MDB_val_copy source_idx(cryptonote::asset_id_from_type(miner_asset_type)); boost::multiprecision::int128_t source_tally = 0; result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); - if (result && (m_height>0 || result != MDB_NOTFOUND)) + if (result && result != MDB_NOTFOUND) throw0(DB_ERROR(lmdb_error("remove_transaction_data() - Failed to get circulating supply tally when removing db transaction: ", result).c_str())); boost::multiprecision::int128_t final_source_tally = source_tally; for (const auto& out: tx.vout) { // Sanity check - prevent underflow if (final_source_tally < final_source_tally - out.amount) - throw0(DB_ERROR("remove_transaction_data() - numeric underflow detected when removing miner_tx for db transaction")); + throw0(DB_ERROR("numeric underflow detected when removing miner_tx for db transaction")); // Fetch the amount for this output final_source_tally -= out.amount; } write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\n\tTally before burn = " << source_tally.str() << "\n\tTally after burn = " << final_source_tally.str()); + + MDB_val_copy burn_idx(cryptonote::asset_id_from_type("BURN")); + boost::multiprecision::int128_t burn_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally); + if (result && result != MDB_NOTFOUND) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); + // Sanity check - prevent underflow + if (burn_tally < tx.amount_burnt) + throw0(DB_ERROR("burn underflow detected when removing miner_tx for db transaction")); + boost::multiprecision::int128_t final_burn_tally = burn_tally - tx.amount_burnt; + write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally); } - if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE) { + if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::TRANSFER) { // Get the current tally value for the source currency type MDB_val_copy source_idx(cryptonote::asset_id_from_type(tx.source_asset_type)); boost::multiprecision::int128_t source_tally = 0; result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); - if (result == MDB_NOTFOUND) - throw0(DB_ERROR("remove_transaction_data() - minted asset not found")); - // Sanity check - prevent overflow - if (source_tally > source_tally + tx.amount_burnt + tx.rct_signatures.txnFee) - throw0(DB_ERROR("remove_transaction_data() - numeric overflow detected when processing C/B/S for db transaction")); boost::multiprecision::int128_t final_source_tally = source_tally + tx.amount_burnt + tx.rct_signatures.txnFee; + if (result) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when removing db transaction: ", result).c_str())); + // Sanity check - prevent overflow + if (source_tally > final_source_tally) + throw0(DB_ERROR("numeric overflow detected when processing C/B/S/A/T for db transaction")); write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\n\tTally before remint =" << source_tally.str() << "\n\tTally after remint =" << final_source_tally.str()); + + MDB_val_copy burn_idx(cryptonote::asset_id_from_type("BURN")); + boost::multiprecision::int128_t burn_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally); + if (result && /*(m_height>0 ||*/ result != MDB_NOTFOUND/*)*/) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str())); + boost::multiprecision::int128_t final_burn_tally = burn_tally - tx.amount_burnt - tx.rct_signatures.txnFee; + // Sanity check - prevent underflow + if (burn_tally < (tx.amount_burnt + tx.rct_signatures.txnFee)) + throw0(DB_ERROR("burn underflow detected when removing tx for db transaction")); + write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally); } if (tx.type == cryptonote::transaction_type::PROTOCOL) { @@ -1422,7 +1638,6 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const bool ok = cryptonote::get_output_asset_type(out, asset_type); if (!ok) throw0(DB_ERROR("failed to get output asset type (needed to update the circulating supply data for the PROTOCOL_TX)")); - minted_amounts[cryptonote::asset_id_from_type(asset_type)] += out.amount; } @@ -1434,12 +1649,24 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const boost::multiprecision::int128_t source_tally = 0; result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); if (result == MDB_NOTFOUND) - throw0(DB_ERROR("remove_transaction_data() - minted asset not found")); + throw0(DB_ERROR("minted asset not found")); + else if (result) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when removing db transaction: ", result).c_str())); if (source_tally < asset.second) throw0(DB_ERROR("remove_transaction_data() - mint underflow")); boost::multiprecision::int128_t final_source_tally = source_tally - asset.second; write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\n\tAsset Type = " << cryptonote::asset_type_from_id(asset.first) << "\n\tTally before undoing mint =" << source_tally.str() << "\n\tTally after undoing mint =" << final_source_tally.str()); + + MDB_val_copy burn_idx(cryptonote::asset_id_from_type("BURN")); + boost::multiprecision::int128_t burn_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when removing db transaction: ", result).c_str())); + if (burn_tally > burn_tally + asset.second) + throw0(DB_ERROR("remove_transaction_data() - burn overflow")); + boost::multiprecision::int128_t final_burn_tally = burn_tally + asset.second; + write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally); } } remove_tx_outputs(tip->data.tx_id, tx); @@ -1456,7 +1683,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const throw1(DB_ERROR(lmdb_error("Failed to add removal of tx outputs to db transaction: ", result).c_str())); } - // SRCG: The following code is designed to clean up the STAKE transactions, but it is very poorly written + // SRCG: The following code is designed to clean up AUDIT+STAKE transactions, but it is very poorly written // Since transactions are ALWAYS supposed to be created in order, it stands that they should ALWAYS be // removed in REVERSE ORDER. Yet the following loop starts from the beginning - this is the worst possible // implementation in performance terms, since it will ALWAYS take the longest possible time to remove the @@ -1464,6 +1691,30 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const // RECODE TO START FROM THE END OF THE DATABASE TABLE, AND THROW AN EXCEPTION IF YOU DO NOT MATCH FIRST TIME! + // Is there audit_tx data to remove? + if (tx.type == cryptonote::transaction_type::AUDIT) { + // Remove any yield_tx data for this transaction + MDB_val_set(val_height, m_height); + MDB_val v; + MDB_cursor_op op = MDB_SET; + while (1) { + result = mdb_cursor_get(m_cur_audit_txs, &val_height, &v, op); + if (result == MDB_NOTFOUND) { + throw1(DB_ERROR("Failed to locate audit tx for removal from db transaction")); + } else if (result) { + throw1(DB_ERROR(lmdb_error("Failed to locate audit_tx data for removal: ", result).c_str())); + } + op = MDB_NEXT_DUP; + const yield_tx_info ati = *(const yield_tx_info*)v.mv_data; + if (ati.tx_hash == tx_hash) { + result = mdb_cursor_del(m_cur_audit_txs, 0); + if (result) + throw1(DB_ERROR(lmdb_error("Failed to add removal of audit_tx data to db transaction: ", result).c_str())); + break; + } + } + } + // Is there yield_tx data to remove? if (tx.type == cryptonote::transaction_type::STAKE) { // Remove any yield_tx data for this transaction @@ -1970,6 +2221,9 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) lmdb_db_open(txn, LMDB_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_yield_txs, "Failed to open db handle for m_yield_txs"); lmdb_db_open(txn, LMDB_YIELD_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_yield_blocks, "Failed to open db handle for m_yield_blocks"); + lmdb_db_open(txn, LMDB_AUDIT_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_audit_txs, "Failed to open db handle for m_audit_txs"); + lmdb_db_open(txn, LMDB_AUDIT_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_audit_blocks, "Failed to open db handle for m_audit_blocks"); + mdb_set_dupsort(txn, m_spent_keys, compare_hash32); mdb_set_dupsort(txn, m_block_heights, compare_hash32); mdb_set_dupsort(txn, m_tx_indices, compare_hash32); @@ -1993,6 +2247,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) mdb_set_compare(txn, m_circ_supply_tally, compare_uint64); mdb_set_dupsort(txn, m_yield_txs, compare_uint64); + mdb_set_dupsort(txn, m_audit_txs, compare_uint64); if (!(mdb_flags & MDB_RDONLY)) { @@ -2174,6 +2429,10 @@ void BlockchainLMDB::reset() throw0(DB_ERROR(lmdb_error("Failed to drop m_yield_txs: ", result).c_str())); if (auto result = mdb_drop(txn, m_yield_blocks, 0)) throw0(DB_ERROR(lmdb_error("Failed to drop m_yield_blocks: ", result).c_str())); + if (auto result = mdb_drop(txn, m_audit_txs, 0)) + throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_txs: ", result).c_str())); + if (auto result = mdb_drop(txn, m_audit_blocks, 0)) + throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_blocks: ", result).c_str())); // init with current version MDB_val_str(k, "version"); @@ -3516,7 +3775,7 @@ std::map BlockchainLMDB::get_circulating_supply() const // Check for SAL - we need to adjust the total for them if (currency_type == 0) { // Get the current circulating supply for SAL - amount += m_coinbase; + //amount += m_coinbase; } circulating_supply[currency_label] = amount.convert_to(); @@ -3527,12 +3786,13 @@ std::map BlockchainLMDB::get_circulating_supply() const // NEAC: check for empty supply tally - only happens prior to first conversion on chain if (circulating_supply.empty()) { circulating_supply["SAL"] = m_coinbase; + circulating_supply["SAL1"] = 0; + circulating_supply["BURN"] = 0; } // Adjust the supply to account for the staked coins circulating_supply["STAKE"] = staked_coins; - - circulating_supply["BURN"] = m_coinbase - circulating_supply["SAL"] - circulating_supply["STAKE"]; + return circulating_supply; } @@ -4724,7 +4984,7 @@ void BlockchainLMDB::block_rtxn_abort() const } uint64_t BlockchainLMDB::add_block(const std::pair& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, - const std::vector>& txs, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi) + const std::vector>& txs, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -4742,7 +5002,7 @@ uint64_t BlockchainLMDB::add_block(const std::pair& blk, size_t try { - BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, nettype, ybi); + BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, nettype, ybi, abi); } catch (const DB_ERROR_TXN_START& e) { @@ -5239,8 +5499,38 @@ uint64_t BlockchainLMDB::get_database_size() const #define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global")) +void BlockchainLMDB::migrate_2_3() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + uint64_t i; + int result; + mdb_txn_safe txn(false); + MDB_val k, v; + char *ptr; + + MGINFO_YELLOW("Migrating blockchain from DB version 2 to 3 - this may take a while:"); + + // Create the missing (and empty) "audit_block_info" records for all blocks + do { + } while(0); + + uint32_t version = VERSION; + v.mv_data = (void *)&version; + v.mv_size = sizeof(version); + MDB_val_str(vk, "version"); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + result = mdb_put(txn, m_properties, &vk, &v, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str())); + txn.commit(); +} + void BlockchainLMDB::migrate(const uint32_t oldversion) { + //if (oldversion < 3) + // migrate_2_3(); } } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index befd6c56e..451c2fb60 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -78,6 +78,8 @@ typedef struct mdb_txn_cursors MDB_cursor *m_txc_circ_supply_tally; MDB_cursor *m_txc_yield_txs; MDB_cursor *m_txc_yield_blocks; + MDB_cursor *m_txc_audit_txs; + MDB_cursor *m_txc_audit_blocks; } mdb_txn_cursors; @@ -104,6 +106,8 @@ typedef struct mdb_txn_cursors #define m_cur_circ_supply_tally m_cursors->m_txc_circ_supply_tally #define m_cur_yield_txs m_cursors->m_txc_yield_txs #define m_cur_yield_blocks m_cursors->m_txc_yield_blocks +#define m_cur_audit_txs m_cursors->m_txc_audit_txs +#define m_cur_audit_blocks m_cursors->m_txc_audit_blocks typedef struct mdb_rflags { @@ -131,6 +135,8 @@ typedef struct mdb_rflags bool m_rf_circ_supply_tally; bool m_rf_yield_txs; bool m_rf_yield_blocks; + bool m_rf_audit_txs; + bool m_rf_audit_blocks; } mdb_rflags; typedef struct mdb_threadinfo @@ -343,6 +349,7 @@ public: , const std::vector>& txs , const cryptonote::network_type nettype , cryptonote::yield_block_info& ybi + , cryptonote::audit_block_info& abi ); virtual void set_batch_transactions(bool batch_transactions); @@ -400,8 +407,10 @@ private: const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, + uint64_t audit_total, const cryptonote::network_type nettype, - cryptonote::yield_block_info& ybi + cryptonote::yield_block_info& ybi, + cryptonote::audit_block_info& abi ); virtual void remove_block(); @@ -455,10 +464,14 @@ private: // migrate from older DB version to current void migrate(const uint32_t oldversion); - // migrate from DB version 0 to 1 - //void migrate_0_1(); + // migrate from DB version 2 to 3 + void migrate_2_3(); + void cleanup_batch(); + virtual int get_audit_block_info(const uint64_t height, audit_block_info& abi) const; + virtual int get_audit_tx_info(const uint64_t height, std::vector& ati_container) const; + virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const; virtual int get_yield_tx_info(const uint64_t height, std::vector& yti_container) const; @@ -499,6 +512,9 @@ private: MDB_dbi m_yield_txs; MDB_dbi m_yield_blocks; + MDB_dbi m_audit_txs; + MDB_dbi m_audit_blocks; + mutable uint64_t m_cum_size; // used in batch size estimation mutable unsigned int m_cum_count; std::string m_folder; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 4021a70b2..429e39168 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -140,6 +140,9 @@ public: virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const override { return false; } virtual uint64_t get_database_size() const override { return 0; } + virtual int get_audit_block_info(const uint64_t height, audit_block_info& abi) const override { return 0; } + virtual int get_audit_tx_info(const uint64_t height, std::vector& ati_container) const override { return 0; } + virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const override { return 0; } virtual int get_yield_tx_info(const uint64_t height, std::vector& yti_container) const override { return 0; } @@ -156,8 +159,10 @@ public: const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, + uint64_t audit_total, const cryptonote::network_type nettype, - cryptonote::yield_block_info& ybi + cryptonote::yield_block_info& ybi, + cryptonote::audit_block_info& abi ) override { } virtual cryptonote::block get_block_from_height(const uint64_t& height) const override { return cryptonote::block(); } virtual void set_hard_fork_version(uint64_t height, uint8_t version) override {} diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 18282fd5c..2be65129c 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -488,8 +488,9 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path try { cryptonote::yield_block_info ybi; // This just gets discarded because we aren't looking to maintain a cache of YBI data in the import utility + cryptonote::audit_block_info abi; // This just gets discarded because we aren't looking to maintain a cache of ABI data in the import utility uint64_t long_term_block_weight = core.get_blockchain_storage().get_next_long_term_block_weight(block_weight); - core.get_blockchain_storage().get_db().add_block(std::make_pair(b, block_to_blob(b)), block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, ybi); + core.get_blockchain_storage().get_db().add_block(std::make_pair(b, block_to_blob(b)), block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, ybi, abi); } catch (const std::exception& e) { diff --git a/src/blockchain_utilities/blockchain_scanner.cpp b/src/blockchain_utilities/blockchain_scanner.cpp index c62261705..dd065994d 100644 --- a/src/blockchain_utilities/blockchain_scanner.cpp +++ b/src/blockchain_utilities/blockchain_scanner.cpp @@ -29,6 +29,8 @@ #include #include #include "common/command_line.h" +#include "common/i18n.h" +#include "common/password.h" #include "common/varint.h" #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_core/tx_pool.h" @@ -43,11 +45,20 @@ #define MONERO_DEFAULT_LOG_CATEGORY "bcutil" #define DELIM "|" +#define DEF_STAKE_MODE "all" namespace po = boost::program_options; using namespace epee; using namespace cryptonote; +namespace scanner +{ + const char* tr(const char* str) + { + return i18n_translate(str, "tools::scanner"); + } +} + static bool stop_requested = false; int main(int argc, char* argv[]) @@ -70,6 +81,8 @@ int main(int argc, char* argv[]) const command_line::arg_descriptor 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_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 +91,8 @@ 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_check_asset_types); command_line::add_arg(desc_cmd_only, command_line::arg_help); po::options_description desc_options("Allowed options"); @@ -116,6 +131,8 @@ 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_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. @@ -158,7 +175,7 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Error opening database: " << e.what()); return 1; } - r = core_storage->blockchain.init(db, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET); + r = core_storage->blockchain.init(db, net_type); CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); LOG_PRINT_L0("Source blockchain storage initialized OK"); @@ -203,6 +220,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' uint32_t txhr[24] = {0}; unsigned int i; + 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; + for (uint64_t h = block_start; h < block_stop; ++h) { cryptonote::blobdata bd = db->get_block_blob_from_height(h); @@ -234,34 +254,60 @@ skip: std::map> used_tx_versions; used_assets.insert("SAL"); + uint8_t hf_version = blk.major_version; + // Check TX versions if (blk.miner_tx.version != 2) { std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.miner_tx.hash << "" << delimiter << "invalid miner TX version detected" << delimiter << "version:" << blk.miner_tx.version << std::endl; } - if (blk.protocol_tx.version != 2) { + if (blk.protocol_tx.version != 2 && h>0) { std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "invalid protocol TX version detected" << delimiter << "version:" << blk.protocol_tx.version << std::endl; } // Get the miner_tx assets + std::set used_keys; for (const auto& miner_tx_vout : blk.miner_tx.vout) { std::string asset_type; if (!cryptonote::get_output_asset_type(miner_tx_vout, asset_type)) { throw std::runtime_error("Aborting: failed to get output asset type from miner_tx"); - } else if (asset_type != "SAL") { - throw std::runtime_error("Aborting: invalid output asset type from miner_tx"); + } else if (blk.major_version >= HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL1") { + throw std::runtime_error("Aborting: invalid output asset type from miner_tx: " + asset_type); + } else if (blk.major_version < HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL") { + throw std::runtime_error("Aborting: invalid output asset type from miner_tx: " + asset_type + ", HF:" + std::to_string(blk.major_version)); } miner_tx_assets.insert(asset_type); + if (miner_tx_vout.amount > 13500000000 && h>0) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.miner_tx.hash << "" << delimiter << "invalid miner TX amount detected" << delimiter << "amount:" << miner_tx_vout.amount << std::endl; + } + crypto::public_key key; + cryptonote::get_output_public_key(miner_tx_vout, key); + if (used_keys.count(key)) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.miner_tx.hash << "" << delimiter << "invalid miner TX - duplicate output detected" << delimiter << "pubkey:" << key << std::endl; + } + used_keys.insert(key); } // Get the protocol_tx assets + used_keys.clear(); for (const auto& protocol_tx_vout : blk.protocol_tx.vout) { std::string asset_type; if (!cryptonote::get_output_asset_type(protocol_tx_vout, asset_type)) { throw std::runtime_error("Aborting: failed to get output asset type from protocol_tx"); - } else if (asset_type != "SAL") { - throw std::runtime_error("Aborting: invalid output asset type from protocol_tx"); + } else if (blk.major_version >= HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL1") { + throw std::runtime_error("Aborting: invalid output asset type from protocol_tx: " + asset_type); + } else if (blk.major_version < HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL") { + throw std::runtime_error("Aborting: invalid output asset type from protocol_tx: " + asset_type + ", HF:" + std::to_string(blk.major_version)); } protocol_tx_assets.insert(asset_type); + if (protocol_tx_vout.amount > 25000000000000) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "large protocol TX amount detected from height " << (h-audit_lock_period) << delimiter << "amount:" << protocol_tx_vout.amount << std::endl; + } + crypto::public_key key; + cryptonote::get_output_public_key(protocol_tx_vout, key); + if (used_keys.count(key)) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "invalid protocol TX - duplicate output detected" << delimiter << "pubkey:" << key << std::endl; + } + used_keys.insert(key); } for (const auto& tx_id : blk.tx_hashes) @@ -285,207 +331,49 @@ skip: currsz += bd.size(); currtxs++; - if (tx.version != 2) { + if (tx.type != cryptonote::transaction_type::TRANSFER && + tx.type != cryptonote::transaction_type::BURN && + tx.type != cryptonote::transaction_type::STAKE && + tx.type != cryptonote::transaction_type::AUDIT) { + std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX type detected" << delimiter << "type:" << (uint8_t)tx.type << std::endl; + } + + if ((tx.version != 2 && hf_version < HF_VERSION_ENABLE_N_OUTS) || (tx.version != 3 && hf_version < HF_VERSION_ENABLE_N_OUTS && 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; } - - /* - 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; + + 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 (!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; - } - */ - - // 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; + 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 { - - // 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; + 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); } - */ } - - /* - // 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_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index bcb2417be..bbe2bfbdf 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -510,6 +510,18 @@ namespace cryptonote return boost::apply_visitor(txin_signature_size_visitor(), tx_in); } + struct audit_block_info { + uint64_t block_height; + uint64_t locked_coins_this_block; + uint64_t locked_coins_tally; + + BEGIN_SERIALIZE() + VARINT_FIELD(block_height) + VARINT_FIELD(locked_coins_this_block) + VARINT_FIELD(locked_coins_tally) + END_SERIALIZE() + }; + struct yield_block_info { uint64_t block_height; uint64_t slippage_total_this_block; diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index bcbb4c7ff..3acf7e33a 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -358,6 +358,28 @@ namespace boost a & x.z2; } + template + inline void serialize(Archive &a, rct::salvium_input_data_t &x, const boost::serialization::version_type ver) + { + a & x.aR; + a & x.i; + } + + template + inline void serialize(Archive &a, rct::salvium_data_t &x, const boost::serialization::version_type ver) + { + a & x.salvium_data_type; + a & x.pr_proof; + a & x.sa_proof; + if (x.salvium_data_type == rct::SalviumAudit) + { + a & x.cz_proof; + a & x.input_verification_data; + a & x.spend_pubkey; + a & x.enc_view_privkey_str; + } + } + template inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver) { @@ -411,7 +433,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus && x.type != rct::RCTTypeFullProofs) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus && x.type != rct::RCTTypeFullProofs && x.type != rct::RCTTypeSalviumOne) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -421,9 +443,11 @@ namespace boost serializeOutPk(a, x.outPk, ver); a & x.txnFee; a & x.p_r; - if (x.type == rct::RCTTypeFullProofs) { - a & x.pr_proof; - a & x.sa_proof; + if (x.type == rct::RCTTypeSalviumOne) { + a & x.salvium_data; + } else if (x.type == rct::RCTTypeFullProofs) { + a & x.salvium_data.pr_proof; + a & x.salvium_data.sa_proof; } } @@ -450,7 +474,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus && x.type != rct::RCTTypeFullProofs) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus && x.type != rct::RCTTypeFullProofs && x.type != rct::RCTTypeSalviumOne) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -460,9 +484,11 @@ namespace boost serializeOutPk(a, x.outPk, ver); a & x.txnFee; a & x.p_r; - if (x.type == rct::RCTTypeFullProofs) { - a & x.pr_proof; - a & x.sa_proof; + if (x.type == rct::RCTTypeSalviumOne) { + a & x.salvium_data; + } else if (x.type == rct::RCTTypeFullProofs) { + a & x.salvium_data.pr_proof; + a & x.salvium_data.sa_proof; } //-------------- a & x.p.rangeSigs; @@ -475,7 +501,7 @@ namespace boost a & x.p.MGs; if (ver >= 1u) a & x.p.CLSAGs; - if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus || x.type == rct::RCTTypeFullProofs) + if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus || x.type == rct::RCTTypeFullProofs || x.type == rct::RCTTypeSalviumOne) a & x.p.pseudoOuts; } @@ -517,5 +543,5 @@ namespace boost } BOOST_CLASS_VERSION(rct::rctSigPrunable, 2) -BOOST_CLASS_VERSION(rct::rctSig, 3) +BOOST_CLASS_VERSION(rct::rctSig, 4) BOOST_CLASS_VERSION(rct::multisig_out, 1) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 12ce3491a..ea24adab4 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -290,7 +290,7 @@ namespace cryptonote return is_v1_tx(blobdata_ref{tx_blob.data(), tx_blob.size()}); } //--------------------------------------------------------------- - 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) + 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) { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); @@ -317,11 +317,13 @@ namespace cryptonote boost::optional 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"); - - 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.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, 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)) { @@ -400,7 +402,7 @@ namespace cryptonote // SRCG: This is a confusing one - for some reason I was using the line below, and it _seemed_ to work... // ... but I think it was luck! the "od.output_index" would only work for the TD_ORIGIN data, of course... //hwdev.derive_subaddress_public_key(out_key, recv_derivation, od.output_index, P_change); - if (od.tx_type == cryptonote::transaction_type::CONVERT || od.tx_type == cryptonote::transaction_type::STAKE) { + if (od.tx_type == cryptonote::transaction_type::CONVERT || od.tx_type == cryptonote::transaction_type::STAKE || od.tx_type == cryptonote::transaction_type::AUDIT) { hwdev.derive_subaddress_public_key(out_key, recv_derivation, 0, P_change); } else { hwdev.derive_subaddress_public_key(out_key, recv_derivation, real_output_index, P_change); @@ -415,13 +417,20 @@ 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"); CHECK_AND_ASSERT_MES(P_change == change_pk, false, "derived P_change public key does not match P_change"); // 5. Calculate the secret spend key "x_return" - if (od.tx_type == cryptonote::transaction_type::CONVERT || od.tx_type == cryptonote::transaction_type::STAKE) { + if (od.tx_type == cryptonote::transaction_type::CONVERT || od.tx_type == cryptonote::transaction_type::STAKE || od.tx_type == cryptonote::transaction_type::AUDIT) { CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(recv_derivation, 0, sk_spend, scalar_step1), false, "Failed to derive one-time output secret key 'x_return'"); } else { CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(recv_derivation, real_output_index, sk_spend, scalar_step1), false, "Failed to derive one-time output secret key 'x_return'"); @@ -433,7 +442,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 { @@ -967,8 +980,8 @@ namespace cryptonote switch (asset_type_id) { case 0x53414C00: return "SAL"; - case 0x56534400: - return "VSD"; + case 0x53414C31: + return "SAL1"; case 0x4255524E: return "BURN"; case 0x00000000: @@ -984,8 +997,8 @@ namespace cryptonote { if (asset_type == "SAL") { return 0x53414C00; - } else if (asset_type == "VSD") { - return 0x56534400; + } else if (asset_type == "SAL1") { + return 0x53414C31; } else if (asset_type == "BURN") { return 0x4255524E; } else if (asset_type == "") { @@ -1260,7 +1273,24 @@ namespace cryptonote // Verify the asset type std::string asset_type; CHECK_AND_ASSERT_MES(cryptonote::get_output_asset_type(o, asset_type), false, "failed to get asset type"); - CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type); + if (hf_version < HF_VERSION_SALVIUM_ONE_PROOFS) { + // Prior to the first audit, ONLY SAL was supported + CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type); + } else if (hf_version == HF_VERSION_SALVIUM_ONE_PROOFS) { + if (tx.type == cryptonote::transaction_type::AUDIT) { + // The CHANGE for an AUDIT TX must be SAL (and 0 value, and unspendable, and to the origin wallet, and ...) + CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type); + } else if (tx.type == cryptonote::transaction_type::PROTOCOL) { + // PROTOCOL TXs are responsible for paying out SAL and SAL1 during the AUDIT + CHECK_AND_ASSERT_MES(asset_type == "SAL1" || asset_type == "SAL", false, "wrong output asset type:" << asset_type); + } else { + // All other TX types must only spend + create SAL1 (MINER, TRANSFER) + CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type); + } + } else { + // After the first AUDIT, only SAL1 is supported + CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type); + } } return true; } @@ -1338,15 +1368,13 @@ namespace cryptonote CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations"); if (out_can_be_to_acc(view_tag_opt, additional_derivations[output_index], output_index, &hwdev)) { - // HERE BE DRAGONS!!! // SRCG: This is NOT going to work for PROTOCOL_TX except where there is only a single output CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); - // LAND AHOY!!! auto found = subaddresses.find(subaddress_spendkey); if (found != subaddresses.end()) return subaddress_receive_info{ found->second, additional_derivations[output_index] }; - // If we get here, odds are that it is a PROTOCOL_TX (rare for other TX types to have additional derivations!) + // If we get here, odds are that it is a PROTOCOL_TX if (output_index != 0) { // Try the derivation with a 0 index as an override - CONVERT / YIELD TXs cannot know their index in the PROTOCOL_TX, so they use 0 in all cases CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], 0, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 5843853bd..5dbfdd73c 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -104,8 +104,8 @@ namespace cryptonote bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered); 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); - 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(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, 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 c87cebec2..af48252b9 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -31,6 +31,7 @@ #pragma once #include +#include #include #include #include @@ -177,6 +178,9 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 +#define SECRET_ENCRYPTION_PK_STR "5e860406bf9221dba6409faa6eb8fecd6f34acc4935634e76b64b90bf2b6d6a6" + + /* #define HF_VERSION_DYNAMIC_FEE 4 #define HF_VERSION_MIN_MIXIN_4 6 @@ -224,12 +228,15 @@ #define HF_VERSION_SHUTDOWN_USER_TXS 5 +#define HF_VERSION_AUDIT1 6 +#define HF_VERSION_SALVIUM_ONE_PROOFS 6 + #define HF_VERSION_REQUIRE_VIEW_TAGS 255 #define HF_VERSION_ENABLE_CONVERT 255 #define HF_VERSION_ENABLE_ORACLE 255 #define HF_VERSION_SLIPPAGE_YIELD 255 -#define TESTNET_VERSION 12 +#define TESTNET_VERSION 14 #define STAGENET_VERSION 1 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 @@ -280,10 +287,13 @@ namespace config uint32_t const GENESIS_NONCE = 10000; + const std::map> AUDIT_HARD_FORKS = { {HF_VERSION_AUDIT1, {"SAL", "SAL1"}} }; + const uint64_t STAKE_LOCK_PERIOD = 30*24*30; + const uint64_t AUDIT_LOCK_PERIOD = 30*24*10; std::string const TREASURY_ADDRESS = "SaLvdZR6w1A21sf2Wh6jYEh1wzY4GSbT7RX6FjyPsnLsffWLrzFQeXUXJcmBLRWDzZC2YXeYe5t7qKsnrg9FpmxmEcxPHsEYfqA"; - + // Hash domain separators const char HASH_KEY_BULLETPROOF_EXPONENT[] = "bulletproof"; const char HASH_KEY_BULLETPROOF_PLUS_EXPONENT[] = "bulletproof_plus"; @@ -354,6 +364,7 @@ namespace config uint32_t const GENESIS_NONCE = 10001; const uint64_t STAKE_LOCK_PERIOD = 20; + const uint64_t AUDIT_LOCK_PERIOD = 30; std::array const ORACLE_URLS = {{"oracle.salvium.io:8443", "oracle.salvium.io:8443", "oracle.salvium.io:8443"}}; @@ -380,6 +391,7 @@ namespace config uint32_t const GENESIS_NONCE = 10002; const uint64_t STAKE_LOCK_PERIOD = 20; + const uint64_t AUDIT_LOCK_PERIOD = 30; std::array const ORACLE_URLS = {{"oracle.salvium.io:8443", "oracle.salvium.io:8443", "oracle.salvium.io:8443"}}; @@ -415,7 +427,9 @@ namespace cryptonote uint32_t const GENESIS_NONCE; std::array const ORACLE_URLS; std::string const ORACLE_PUBLIC_KEY; - uint64_t STAKE_LOCK_PERIOD; + uint64_t const STAKE_LOCK_PERIOD; + uint64_t const AUDIT_LOCK_PERIOD; + std::map> const AUDIT_HARD_FORKS; std::string TREASURY_ADDRESS; }; inline const config_t& get_config(network_type nettype) @@ -433,6 +447,8 @@ namespace cryptonote ::config::ORACLE_URLS, ::config::ORACLE_PUBLIC_KEY, ::config::STAKE_LOCK_PERIOD, + ::config::AUDIT_LOCK_PERIOD, + ::config::AUDIT_HARD_FORKS, ::config::TREASURY_ADDRESS }; static const config_t testnet = { @@ -448,6 +464,8 @@ namespace cryptonote ::config::testnet::ORACLE_URLS, ::config::testnet::ORACLE_PUBLIC_KEY, ::config::testnet::STAKE_LOCK_PERIOD, + ::config::testnet::AUDIT_LOCK_PERIOD, + ::config::AUDIT_HARD_FORKS, ::config::testnet::TREASURY_ADDRESS }; static const config_t stagenet = { @@ -463,6 +481,8 @@ namespace cryptonote ::config::stagenet::ORACLE_URLS, ::config::stagenet::ORACLE_PUBLIC_KEY, ::config::stagenet::STAKE_LOCK_PERIOD, + ::config::stagenet::AUDIT_LOCK_PERIOD, + ::config::AUDIT_HARD_FORKS, ::config::stagenet::TREASURY_ADDRESS }; switch (nettype) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index ce5c0016c..c5aab80e5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -155,12 +155,13 @@ 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; + /* 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) { @@ -233,7 +235,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke output_index = m_db->get_output_key(tx_in_to_key.amount, i); // call to the passed boost visitor to grab the public key for the output - if (!vis.handle_output(output_index.unlock_time, tx_in_to_key.asset_type, output_index.pubkey, output_index.commitment)) + if (!vis.handle_output(output_index.unlock_time, cryptonote::asset_type_from_id(output_index.asset_type), output_index.pubkey, output_index.commitment)) { MERROR_VER("Failed to handle_output for output no = " << count << ", with absolute offset " << i); return false; @@ -1450,9 +1452,11 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version) { LOG_PRINT_L3("Blockchain::" << __func__); - if (!b.protocol_tx.vin.size()) { - // Nothing is created by this TX - check no money is included - CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "void protocol transaction in the block has outputs"); + if (height == 0) { + // Genesis block exception + CHECK_AND_ASSERT_MES(b.protocol_tx.version == 1, false, "Invalid genesis protocol transaction version"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 0, false, "genesis protocol transaction in the block has inputs"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "genesis protocol transaction in the block has outputs"); return true; } CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); @@ -1500,6 +1504,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl case HF_VERSION_FULL_PROOFS: case HF_VERSION_ENFORCE_FULL_PROOFS: case HF_VERSION_SHUTDOWN_USER_TXS: + case HF_VERSION_SALVIUM_ONE_PROOFS: if (b.miner_tx.amount_burnt > 0) { CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt"); money_in_use += b.miner_tx.amount_burnt; @@ -1532,176 +1537,148 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } //------------------------------------------------------------------ // SRCG -bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, std::vector>& txs, uint8_t hf_version) +bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version) { LOG_PRINT_L3("Blockchain::" << __func__); - CHECK_AND_ASSERT_MES(b.tx_hashes.size() == txs.size(), false, "Invalid number of TXs / hashes supplied"); - if (!b.protocol_tx.vin.size()) { - // Nothing is created by this TX - check no money is included - CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "void protocol transaction in the block has outputs"); + if (height == 0) { + // Genesis block exception + CHECK_AND_ASSERT_MES(b.protocol_tx.version == 1, false, "Invalid genesis protocol transaction version"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 0, false, "genesis protocol transaction in the block has inputs"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "genesis protocol transaction in the block has outputs"); return true; } - - if (!b.protocol_tx.vout.size()) { - // No money is minted, nothing to verify - bail out + + // if nothing is created by this TX - check no money is included + CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); + size_t vout_size = b.protocol_tx.vout.size(); + if (vout_size == 0) { + LOG_PRINT_L2("coinbase protocol transaction in the block has no outputs"); return true; } - // Get the circulating supply so we can verify - std::map circ_supply; - if (hf_version >= HF_VERSION_ENABLE_CONVERT) { - circ_supply = get_db().get_circulating_supply(); - } - - // Build a map of outputs from the protocol_tx - std::map> outputs; - for (auto& o : b.protocol_tx.vout) { - if (o.target.type() == typeid(txout_to_key)) { - txout_to_key out = boost::get(o.target); - CHECK_AND_ASSERT_MES(out.unlock_time == CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, false, "Invalid unlock time on protocol_tx output"); - CHECK_AND_ASSERT_MES(outputs.count(out.key) == 0, false, "Output duplicated in protocol_tx"); - outputs[out.key] = std::make_tuple(out.asset_type, o.amount, out.unlock_time); - } else if (o.target.type() == typeid(txout_to_tagged_key)) { - txout_to_tagged_key out = boost::get(o.target); - CHECK_AND_ASSERT_MES(out.unlock_time == CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, false, "Invalid unlock time on protocol_tx output"); - CHECK_AND_ASSERT_MES(outputs.count(out.key) == 0, false, "Output duplicated in protocol_tx"); - outputs[out.key] = std::make_tuple(out.asset_type, o.amount, out.unlock_time); - } else { - MERROR("Block at height: " << height << " attempting to add protocol transaction with invalid type " << o.target.type().name()); - return false; - } - } - CHECK_AND_ASSERT_MES(outputs.size() == b.protocol_tx.vout.size(), false, "Mismatch between vout and outputs for protocol_tx - aborting"); - - // Maintain a count of outputs that we have verified - std::vector outputs_verified; - - size_t tx_index = 0; - // Iterate over the block's transaction hashes, grabbing each - // from the tx_pool and validating them. - for (const auto& tx_tmp : txs) - { - // Get a ptr to the TX to simplify coding - const transaction* tx = &tx_tmp.first; - - // Check to see if the TX exists in the DB - this probably duplicates the effort in our caller, but better to be sure - if (m_db->tx_exists(tx->hash)) - { - MERROR("Block at height: " << height << " attempting to add transaction already in blockchain with id: " << tx->hash); - return false; - } - - if (hf_version >= HF_VERSION_ENABLE_CONVERT) { - - // Check to see if the TX is a conversion or not - if (tx->type != cryptonote::transaction_type::CONVERT) { - // Only conversion (and failed conversion, aka refund) TXs need to be verified - skip this TX - continue; - } - - // Verify that the TX has an output in the protocol_tx to verify - if (outputs.count(tx->return_address) != 1) { - LOG_ERROR("Block at height: " << height << " - Failed to locate output for conversion TX id " << tx->hash << " - rejecting block"); - return false; - } - - // Get the output information - std::string output_asset_type; - uint64_t output_amount; - uint64_t output_unlock_time; - std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[tx->return_address]; - - // Verify the asset_type - if (tx->source_asset_type == output_asset_type) { - // Check the amount for REFUND - if (tx->amount_burnt != output_amount) { - LOG_ERROR("Block at height: " << height << " - Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block"); - return false; - } - - // Verified the refund successfully - outputs_verified.push_back(tx->return_address); - - } else if (tx->destination_asset_type == output_asset_type) { - // Check the amount for CONVERT - - // Verify the amount of the conversion - uint64_t amount_minted_check = 0, amount_slippage_check = 0; - bool ok = cryptonote::calculate_conversion(tx->source_asset_type, tx->destination_asset_type, tx->amount_burnt, tx->amount_slippage_limit, amount_minted_check, amount_slippage_check, circ_supply, b.pricing_record, hf_version); - if (!ok) { - LOG_ERROR("Block at height: " << height << " - Failed to calculate conversion for TX id " << tx->hash << " - rejecting block"); - return false; - } - if (amount_minted_check != output_amount) { - LOG_ERROR("Block at height: " << height << " - Output amount does not match amount minted for converted TX id " << tx->hash << " - rejecting block"); - return false; - } - - // Verified the conversion successfully - outputs_verified.push_back(tx->return_address); - - } else { - LOG_ERROR("Block at height: " << height << " - Output asset type incorrect: source " << tx->source_asset_type << ", dest " << tx->destination_asset_type << ", got " << output_asset_type << " - rejecting block"); - return false; - } - } - } - // Can we have matured STAKE transactions yet? uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; - if (height > stake_lock_period) { + if (height <= stake_lock_period) { + return false; + } - // Yes - Get the staking data for the block that matured this time - cryptonote::yield_block_info ybi_matured; - uint64_t matured_height = height - stake_lock_period - 1; - bool ok = get_ybi_entry(matured_height, ybi_matured); - if (ok && ybi_matured.locked_coins_this_block > 0) { - - // Iterate over the cached data for block yield, calculating the yield payouts due - std::vector> yield_payouts; - if (!calculate_yield_payouts(matured_height, yield_payouts)) { - LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting"); + // Get the staking data for the block that matured this time + cryptonote::yield_block_info ybi_matured; + std::vector> yield_payouts; + uint64_t matured_height = height - stake_lock_period - 1; + bool ok = get_ybi_entry(matured_height, ybi_matured); + if (!ok) { + LOG_ERROR("Block at height: " << height << " - Failed to obtain yield block information - aborting"); + return false; + } else if (ybi_matured.locked_coins_this_block == 0) { + LOG_PRINT_L1("Block at height: " << height << " - no yield payouts due - skipping"); + } else { + // Iterate over the cached data for block yield, calculating the yield payouts due + if (!calculate_yield_payouts(matured_height, yield_payouts)) { + LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting"); + return false; + } + } + + // Get the audit data for the block that matures at this height + std::vector> audit_payouts; + uint64_t audit_lock_period = get_config(m_nettype).AUDIT_LOCK_PERIOD; + uint64_t matured_audit_height = height - audit_lock_period - 1; + uint8_t hf = m_hardfork->get_ideal_version(matured_audit_height); + const std::map> audit_hard_forks = get_config(m_nettype).AUDIT_HARD_FORKS; + if (audit_hard_forks.find(hf) != audit_hard_forks.end()) { + // Maturing height was during an audit - process accordingly + cryptonote::audit_block_info abi_matured; + ok = get_abi_entry(matured_audit_height, abi_matured); + if (!ok) { + LOG_PRINT_L1("Block at height: " << height << " - failed to obtain audit block information - aborting"); + return false; + } else if (abi_matured.locked_coins_this_block == 0) { + LOG_PRINT_L1("Block at height: " << height << " - no audit payouts due - skipping"); + } else { + // Iterate over the cached data for audits, calculating the audit payouts due + if (!calculate_audit_payouts(matured_audit_height, audit_payouts)) { + LOG_ERROR("Block at height: " << height << " - Failed to obtain audit payout information - aborting"); return false; } - - // Iterate the yield payouts, verifying as we go - for (const auto& payout: yield_payouts) { - - // Do we have a singular matching output in tx.vout? - if (outputs.count(payout.first.return_address) != 1) { - LOG_ERROR("Block at height: " << height << " - Failed to locate output for matured TX id " << payout.first.tx_hash << " - rejecting block"); - return false; - } - - // Get the output information - std::string output_asset_type; - uint64_t output_amount; - uint64_t output_unlock_time; - std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[payout.first.return_address]; - - // Verify the asset type - must be SAL - if (output_asset_type != "SAL") { - LOG_ERROR("Block at height: " << height << " - Incorrect output asset type for matured TX id " << payout.first.tx_hash << " - rejecting block"); - return false; - } - - // Verify the amount - if (output_amount != payout.second) { - LOG_ERROR("Block at height: " << height << " - Incorrect output amount for matured TX id " << payout.first.tx_hash << " - rejecting block"); - return false; - } - - // Amount and return_address match our expectation - outputs_verified.push_back(payout.first.return_address); - } } } - // All candidates have been evaluated - make sure there are no other outputs that have not been catered for - if (outputs.size() != outputs_verified.size()) { - LOG_ERROR("Block at height: " << height << " - Incorrect number of outputs - expected " << outputs_verified.size() << " but received " << outputs.size() << " - rejecting block"); - return false; + // Check we have the correct number of entries + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting"); + + // go through each vout and validate + std::set used_keys; + for (auto& o : b.protocol_tx.vout) { + // gather the output data + uint64_t out_amount; + uint64_t out_unlock_time; + std::string out_asset_type; + crypto::public_key out_key; + if (o.target.type() == typeid(txout_to_key)) { + txout_to_key out = boost::get(o.target); + out_unlock_time = out.unlock_time; + out_asset_type = out.asset_type; + out_key = out.key; + out_amount = o.amount; + } else if (o.target.type() == typeid(txout_to_tagged_key)) { + txout_to_tagged_key out = boost::get(o.target); + out_unlock_time = out.unlock_time; + out_asset_type = out.asset_type; + out_key = out.key; + out_amount = o.amount; + } else { + MERROR("Block at height: " << height << " attempting to add protocol transaction with invalid type " << o.target.type().name()); + return false; + } + + // Check if key has already been seen + if (used_keys.count(out_key) != 0) { + LOG_ERROR("Block at height: " << height << " - Duplicated output key " << out_key << " for protocol TX - aborting"); + return false; + } + + // Add key to list of already-seen + used_keys.insert(out_key); + + // check if there is entry in the yield payouts or audit payouts for this output + std::string expected_output_asset_type = "SAL"; + auto found_yield = std::find_if(yield_payouts.begin(), yield_payouts.end(), [&](const std::pair& p) { + return p.first.return_address == out_key; + }); + auto found_audit = std::find_if(audit_payouts.begin(), audit_payouts.end(), [&](const std::pair& p) { + return p.first.return_address == out_key; + }); + if (found_yield == yield_payouts.end() && found_audit == audit_payouts.end()) { + MERROR("Block at height: " << height << " - Failed to locate output for protocol TX - rejecting block"); + return false; + } else if (found_audit == audit_payouts.end()) { + + // Found a YIELD entry + CHECK_AND_ASSERT_MES(out_amount == found_yield->second, false, "Incorrect value for protocol TX YIELD amount"); + 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"); + 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 { + + // Duplicate entry in yield + audit?!?!? + MERROR("Block at height: " << height << " - Duplicated YIELD and AUDIT keys found for protocol TX - rejecting block"); + return false; + } + + // check other fields + CHECK_AND_ASSERT_MES(out_unlock_time == CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, false, "Invalid unlock time on protocol_tx output"); + CHECK_AND_ASSERT_MES(expected_output_asset_type == out_asset_type, false, "Incorrect asset type detected for protocol TX ouput - rejecting block"); } // Everything checks out @@ -1956,46 +1933,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, size_t txs_weight; uint64_t fee; - /** - * Here is where the magic happens - determination of the payments for the protocol_tx - * - * We need to know the following: - * - address to send the funds to ("return_address") - * - asset_type being burnt - * - amount being burnt - * - asset_type being minted - * - * (All of this information should be provided by the txpool_tx_meta_t object) - * - * From that little lot, we can hopefully work out the slippage on all of the TXs, and - * therefore the amount to be minted for each TX, and who to pay it to, etc, etc. - */ - std::vector protocol_metadata; - std::vector protocol_entries; - if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version, b.pricing_record, circ_supply, protocol_metadata)) + if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version)) { return false; } - - // Clone the txpool_tx_meta_t data into a more useable format - for (auto& meta: protocol_metadata) { - cryptonote::protocol_data_entry entry; - entry.amount_burnt = meta.amount_burnt; - entry.amount_minted = 0; - entry.amount_slippage_limit = meta.amount_slippage_limit; - entry.source_asset = asset_type_from_id(meta.source_asset_id); - entry.destination_asset = asset_type_from_id(meta.destination_asset_id); - entry.return_address = meta.return_address; - entry.type = meta.tx_type; - entry.P_change = meta.one_time_public_key; - entry.return_pubkey = meta.return_pubkey; - protocol_entries.push_back(entry); - } // Check to see if there are any matured YIELD TXs uint64_t yield_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; uint64_t start_height = (height > yield_lock_period) ? height - yield_lock_period - 1 : 0; + std::vector protocol_entries; cryptonote::yield_block_info ybi_matured; bool ok = get_ybi_entry(start_height, ybi_matured); if (ok && ybi_matured.locked_coins_this_block > 0) { @@ -2007,14 +1954,22 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, return false; } + // Work out what the asset_type should be based on height of submission + uint8_t hf_submitted = m_hardfork->get_ideal_version(start_height); + // Create the protocol_metadata entries here for (const auto& yield_entry: yield_payouts) { cryptonote::protocol_data_entry entry; entry.amount_burnt = yield_entry.second; entry.amount_minted = 0; entry.amount_slippage_limit = 0; - entry.source_asset = "SAL"; - entry.destination_asset = "SAL"; + if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) { + entry.source_asset = "SAL1"; + entry.destination_asset = "SAL1"; + } else { + entry.source_asset = "SAL"; + entry.destination_asset = "SAL"; + } entry.return_address = yield_entry.first.return_address; entry.type = cryptonote::transaction_type::STAKE; entry.P_change = yield_entry.first.P_change; @@ -2022,13 +1977,57 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, protocol_entries.push_back(entry); } } + + // Get the audit data for the block that matures at this height + std::vector> audit_payouts; + uint64_t audit_lock_period = get_config(m_nettype).AUDIT_LOCK_PERIOD; + uint64_t matured_audit_height = height - audit_lock_period - 1; + if (height > audit_lock_period) { + uint8_t hf = m_hardfork->get_ideal_version(matured_audit_height); + const std::map> audit_hard_forks = get_config(m_nettype).AUDIT_HARD_FORKS; + if (audit_hard_forks.find(hf) != audit_hard_forks.end()) { + // Maturing height was during an audit - process accordingly + cryptonote::audit_block_info abi_matured; + ok = get_abi_entry(matured_audit_height, abi_matured); + if (!ok) { + LOG_PRINT_L1("Block at height: " << height << " - failed to obtain audit block information - aborting"); + return false; + } else if (abi_matured.locked_coins_this_block == 0) { + LOG_PRINT_L1("Block at height: " << height << " - no audit payouts due - skipping"); + } else { + // Iterate over the cached data for audits, calculating the audit payouts due + if (!calculate_audit_payouts(matured_audit_height, audit_payouts)) { + LOG_ERROR("Block at height: " << height << " - Failed to obtain audit payout information - aborting"); + return false; + } + + // Get the asset types + const std::pair audit_asset_types = audit_hard_forks.at(hf); + + // Create the protocol_metadata entries here + for (const auto& audit_entry: audit_payouts) { + cryptonote::protocol_data_entry entry; + entry.amount_burnt = audit_entry.second; + entry.amount_minted = 0; + entry.amount_slippage_limit = 0; + entry.source_asset = audit_asset_types.first; + entry.destination_asset = audit_asset_types.second; + entry.return_address = audit_entry.first.return_address; + entry.type = cryptonote::transaction_type::AUDIT; + entry.P_change = audit_entry.first.P_change; + entry.return_pubkey = audit_entry.first.return_pubkey; + protocol_entries.push_back(entry); + } + } + } + } // Time to construct the protocol_tx uint64_t protocol_fee = 0; address_parse_info treasury_address_info; ok = cryptonote::get_account_address_from_str(treasury_address_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS); CHECK_AND_ASSERT_MES(ok, false, "Failed to obtain treasury address info"); - ok = construct_protocol_tx(height, protocol_fee, b.protocol_tx, protocol_entries, circ_supply, b.pricing_record, miner_address, treasury_address_info.address, b.major_version); + ok = construct_protocol_tx(height, b.protocol_tx, protocol_entries, b.major_version); CHECK_AND_ASSERT_MES(ok, false, "Failed to construct protocol tx"); pool_cookie = m_tx_pool.cookie(); @@ -3623,8 +3622,24 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } */ - // from v4, only allow bulletproofs plus _with_ full proofs on RCT transactions - if (hf_version >= HF_VERSION_ENFORCE_FULL_PROOFS) { + if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) { + // for v5, force the audit and the new SalviumOne RCT data + if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT) { + if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) { + MERROR_VER("FullProofs plus Audit data required after v" + std::to_string(HF_VERSION_SALVIUM_ONE_PROOFS)); + tvc.m_invalid_output = true; + return false; + } + } else { + if (tx.rct_signatures.type != rct::RCTTypeNull) { + MERROR_VER("NULL RCT required for coinbase TXs after v" + std::to_string(HF_VERSION_SALVIUM_ONE_PROOFS)); + tvc.m_invalid_output = true; + return false; + } + } + + } else if (hf_version >= HF_VERSION_ENFORCE_FULL_PROOFS) { + // from v4, only allow bulletproofs plus _with_ full proofs on RCT transactions if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT) { if (tx.rct_signatures.type != rct::RCTTypeFullProofs) { MERROR_VER("FullProofs required after v" + std::to_string(HF_VERSION_FULL_PROOFS)); @@ -3733,7 +3748,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumOne) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys.size()); @@ -3774,7 +3789,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs) + else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumOne) { if (!tx.pruned) { @@ -3915,8 +3930,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - // from v7, sorted ins - if (hf_version >= 7) { + // from v1, sorted ins + if (hf_version >= 1) { const crypto::key_image *last_key_image = NULL; for (size_t n = 0; n < tx.vin.size(); ++n) { @@ -3928,6 +3943,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { MERROR_VER("transaction has unsorted inputs"); tvc.m_verifivation_failed = true; + assert(false); return false; } last_key_image = &in_to_key.k_image; @@ -3956,6 +3972,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, // Make sure the user isn't trying to spend BURNt coins CHECK_AND_ASSERT_MES(in_to_key.asset_type not_eq "BURN", false, "trying to spend BURNt coins"); + // Make sure only a single asset_type is being spent, and that is the one set on the TX + CHECK_AND_ASSERT_MES(in_to_key.asset_type == tx.source_asset_type, false, "trying to spend " << in_to_key.asset_type << " coins in a TX with " << tx.source_asset_type << " source asset type"); + // make sure tx output has key offset(s) (is signed to be used) CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); @@ -4021,7 +4040,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, false, "Transaction spends at least one output which is too young"); // Warn that new RCT types are present, and thus the cache is not being used effectively - static constexpr const std::uint8_t RCT_CACHE_TYPE = rct::RCTTypeFullProofs; + static constexpr const std::uint8_t RCT_CACHE_TYPE = rct::RCTTypeSalviumOne; if (tx.rct_signatures.type > RCT_CACHE_TYPE) { MWARNING("RCT cache is not caching new verification results. Please update RCT_CACHE_TYPE!"); @@ -4054,10 +4073,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, const rct::rctSig &rv = tx.rct_signatures; // Check that after full proofs are enabled, the RCT version is set to enforce full proofs - if (hf_version >= HF_VERSION_ENFORCE_FULL_PROOFS) - { - if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeFullProofs) - { + if (hf_version == HF_VERSION_SALVIUM_ONE_PROOFS) { + if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) { + MERROR_VER("Unsupported rct type (full proofs (with audit data) are required): " << rv.type); + return false; + } + } else if (hf_version >= HF_VERSION_ENFORCE_FULL_PROOFS) { + if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeFullProofs) { MERROR_VER("Unsupported rct type (full proofs are required): " << rv.type); return false; } @@ -4076,6 +4098,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, case rct::RCTTypeCLSAG: case rct::RCTTypeBulletproofPlus: case rct::RCTTypeFullProofs: + case rct::RCTTypeSalviumOne: { if (!ver_rct_non_semantics_simple_cached(tx, pubkeys, m_rct_ver_cache, RCT_CACHE_TYPE, hf_version)) { @@ -4458,6 +4481,57 @@ uint64_t Blockchain::get_adjusted_time(uint64_t height) const return (adjusted_current_block_ts < median_ts ? adjusted_current_block_ts : median_ts); } //------------------------------------------------------------------ +bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vector>& audit_container) +{ + LOG_PRINT_L3("Blockchain::" << __func__); + + // Clear the audit payout amounts + audit_container.clear(); + + // Get the AUDIT TX information for matured staked coins + std::vector audit_entries; + // We get the audit_tx_info from the block where they entered the chain + int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries); + if (!audit_entries.size()) { + + // Report error and abort + LOG_ERROR("calculate_audit_payouts() called, but no audit TXs found at height " << start_height << " - aborting"); + return false; + } + + // Build a blacklist of staking TXs _not_ to pay out for + const std::set txs_blacklist = {}; + + // Get the ABI information for the 21,600 blocks that the matured TX(s), we can calculate audit + for (const auto& entry: audit_entries) { + // Check to see if this entry is in the blacklist + if (txs_blacklist.find(epee::string_tools::pod_to_hex(entry.tx_hash)) != txs_blacklist.end()) { + LOG_ERROR("calculate_audit_payouts() found blacklisted TX at height " << start_height << " - skipping payout"); + continue; + } + audit_container.emplace_back(std::make_pair(entry, entry.locked_coins)); + } + + // Return success to caller + return true; +} +//------------------------------------------------------------------ +bool Blockchain::get_abi_entry(const uint64_t height, cryptonote::audit_block_info& abi) +{ + LOG_PRINT_L3("Blockchain::" << __func__); + + // Clear the provided container + std::memset(&abi, 0, sizeof(struct cryptonote::audit_block_info)); + + int result = m_db->get_audit_block_info(height, abi); + if (result) { + // Request failed - report error and bail out + LOG_ERROR("failed to retrieve ABI entry for height " << height << " - aborting"); + return false; + } + return true; +} +//------------------------------------------------------------------ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector>& yield_container) { LOG_PRINT_L3("Blockchain::" << __func__); @@ -4475,9 +4549,17 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto LOG_ERROR("calculate_yield_payouts() called, but no yield TXs found at height " << start_height << " - aborting"); return false; } - + + // Build a blacklist of staking TXs _not_ to pay out for + const std::set txs_blacklist = {}; + // Get the YBI information for the 21,600 blocks that the matured TX(s), we can calculate yield for (const auto& entry: yield_entries) { + // Check to see if this entry is in the blacklist + if (txs_blacklist.find(epee::string_tools::pod_to_hex(entry.tx_hash)) != txs_blacklist.end()) { + LOG_ERROR("calculate_yield_payouts() found blacklisted TX at height " << start_height << " - skipping payout"); + continue; + } yield_container.emplace_back(std::make_pair(entry, entry.locked_coins)); } @@ -4775,17 +4857,6 @@ leave: } TIME_MEASURE_FINISH(t2); - TIME_MEASURE_START(t2point5); - - // make sure that block is allowed TXs (prevented during pre-audit period) - if (hf_version == HF_VERSION_SHUTDOWN_USER_TXS && bl.tx_hashes.size()) - { - MERROR_VER("Block with id: " << id << std::endl << "contains " << bl.tx_hashes.size() << " illicit user transactions"); - bvc.m_verifivation_failed = true; - goto leave; - } - - TIME_MEASURE_FINISH(t2point5); //check proof of work TIME_MEASURE_START(target_calculating_time); @@ -5039,7 +5110,7 @@ leave: TIME_MEASURE_FINISH(vmt); TIME_MEASURE_START(vpt); - if(!validate_protocol_transaction(bl, blockchain_height, txs, m_hardfork->get_current_version())) + if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version())) { MERROR_VER("Block with id: " << id << " has incorrect protocol transaction"); bvc.m_verifivation_failed = true; @@ -5076,8 +5147,10 @@ leave: uint64_t long_term_block_weight = get_next_long_term_block_weight(block_weight); cryptonote::blobdata bd = cryptonote::block_to_blob(bl); yield_block_info new_ybi; + audit_block_info new_abi; std::memset(&new_ybi, 0, sizeof(struct yield_block_info)); - new_height = m_db->add_block(std::make_pair(std::move(bl), std::move(bd)), block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs, m_nettype, new_ybi); + std::memset(&new_abi, 0, sizeof(struct audit_block_info)); + new_height = m_db->add_block(std::make_pair(std::move(bl), std::move(bd)), block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs, m_nettype, new_ybi, new_abi); // Update the YBI cache data uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD; @@ -5905,7 +5978,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector (txin); // 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 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/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 9afbbfefc..57f71b212 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1160,6 +1160,13 @@ namespace cryptonote */ uint64_t get_adjusted_time(uint64_t height) const; + /** + * calculate the audit payouts + * + * @return TRUE if the payouts were calculated successfully, FALSE otherwise + */ + bool calculate_audit_payouts(const uint64_t start_height, std::vector>& audit_payouts); + /** * calculate the yield payouts * @@ -1167,6 +1174,15 @@ namespace cryptonote */ bool calculate_yield_payouts(const uint64_t start_height, std::vector>& yield_payouts); + /** + * @brief get the ABI entry for a particular height from the cache + * + * Retrieve the ABI entry for the specified height from the local cache. + * + * @return TRUE if the entry was located and returned, FALSE otherwise + */ + bool get_abi_entry(const uint64_t height, cryptonote::audit_block_info& ybi); + /** * @brief get the complete YBI cache * @@ -1529,12 +1545,11 @@ namespace cryptonote * * @param b the block containing the miner transaction to be validated * @param height the blockchain's weight - * @param txs a vector containing all the TXs and their blobs, needed to obtain tx_types, asset_types and burnt amounts * @param version hard fork version for that transaction * * @return false if anything is found wrong with the protocol transaction, otherwise true */ - bool validate_protocol_transaction(const block& b, uint64_t height, std::vector>& txs, uint8_t hf_version); + bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version); /** * @brief reverts the blockchain to its previous state following a failed switch diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index d10e836e6..6cb1a3ecb 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -924,7 +924,15 @@ namespace cryptonote continue; const rct::rctSig &rv = tx_info[n].tx->rct_signatures; const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version(); - if (hf_version >= HF_VERSION_ENFORCE_FULL_PROOFS) { + if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) { + if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) { + MERROR_VER("Invalid RCT type provided"); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + return false; + } + } else if (hf_version >= HF_VERSION_ENFORCE_FULL_PROOFS) { if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeFullProofs) { MERROR_VER("Invalid RCT type provided"); set_semantics_failed(tx_info[n].tx_hash); @@ -946,6 +954,7 @@ namespace cryptonote tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt : tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt : tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt : + tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt : 0 )) { @@ -981,6 +990,7 @@ namespace cryptonote break; case rct::RCTTypeBulletproofPlus: case rct::RCTTypeFullProofs: + case rct::RCTTypeSalviumOne: if (!is_canonical_bulletproof_plus_layout(rv.p.bulletproofs_plus)) { MERROR_VER("Bulletproof_plus does not have canonical form"); @@ -1007,12 +1017,13 @@ namespace cryptonote { if (!tx_info[n].result) continue; - if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus && tx_info[n].tx->rct_signatures.type != rct::RCTTypeFullProofs) + if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus && tx_info[n].tx->rct_signatures.type != rct::RCTTypeFullProofs && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumOne) continue; if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures, tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt : tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt : tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt : + tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt : 0 )) { diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index e9de4e231..6ad024fe5 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -208,7 +208,7 @@ namespace cryptonote // We are done here - return to caller return true; - } else if (tx_type == cryptonote::transaction_type::STAKE) { + } else if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::AUDIT) { // CONVERT / YIELD Semantics // From this point forward, we are departing from the original "return address" scheme @@ -259,6 +259,7 @@ namespace cryptonote // Not implemented yet return false; } + /* //--------------------------------------------------------------- bool get_conversion_rate(const oracle::pricing_record& pr, const std::string& from_asset, const std::string& to_asset, uint64_t& rate) { // Check for burns @@ -332,19 +333,14 @@ namespace cryptonote return true; } + */ //--------------------------------------------------------------- - bool construct_protocol_tx(const size_t height, - uint64_t& protocol_fee, - transaction& tx, - std::vector& protocol_data, - std::map circ_supply, - const oracle::pricing_record& pr, - const account_public_address &miner_address, - const account_public_address &treasury_address, - const uint8_t hf_version) { - - // A vector to contain all of the additional _tx_secret_keys_ - //std::vector& additional_tx_keys; + bool construct_protocol_tx( + const size_t height, + transaction& tx, + std::vector& protocol_data, + const uint8_t hf_version + ) { // Clear the TX contents tx.set_null(); @@ -358,174 +354,33 @@ namespace cryptonote if (!sort_tx_extra(tx.extra, tx.extra)) return false; - // Update the circulating_supply information, while keeping a count of amount to be created using txin_gen - std::map txin_gen_totals; - uint64_t txin_gen_final = 0; - for (auto const& entry: protocol_data) { - if (!circ_supply.count(entry.source_asset)) { - LOG_ERROR("Circulating supply does not have " << entry.source_asset << " balance - invalid source_asset"); - return false; - } - // Deduct the amount_burnt from the circulating_supply balance - circ_supply[entry.source_asset] -= entry.amount_burnt; - } - - // Calculate the slippage for the output amounts LOG_PRINT_L2("Creating protocol_tx..."); - uint64_t slippage_total = 0; std::vector additional_tx_public_keys; for (auto const& entry: protocol_data) { - if (entry.destination_asset == "BURN") { - // BURN TX - no slippage, no money minted - skip - continue; - } - // CONVERT TX - /* - // Create a secret TX key (= s) - crypto::secret_key s = keypair::generate(hw::get_device("default")).sec; - //additional_tx_keys.push_back(s); - - // Now add the correct TX public key (= sP_change) - // This has to be done using smK() call because of g_k_d() performing a torsion clear - crypto::public_key txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(entry.P_change), rct::sk2rct(s))); - additional_tx_public_keys.push_back(txkey_pub); - - // Calculate the actual return address, because the field we already have is actually the TX pubkey to use - // return address = Hs(syF || i)G + P_change = Hs(saP_change || i)G + P_change - // Generate the uniqueness for the input - size_t output_index = tx.vout.size(); - - // y = Hs(uniqueness) - ec_scalar y; - CHECK_AND_ASSERT_MES(cryptonote::calculate_uniqueness((cryptonote::transaction_type)(entry.type), entry.input_k_image, height, 0, y), false, "while creating protocol_tx outs: failed to calculate uniqueness"); - - rct::key key_y = (rct::key&)(y); - rct::key key_F = (rct::key&)(entry.return_address); - crypto::public_key syF = rct::rct2pk(rct::scalarmultKey(rct::scalarmultKey(key_F, key_y), rct::sk2rct(s))); - crypto::key_derivation derivation_syF = AUTO_VAL_INIT(derivation_syF); - std::memcpy(derivation_syF.data, syF.data, sizeof(crypto::key_derivation)); - - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::derive_public_key(derivation_syF, output_index, entry.P_change, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating protocol_tx outs: failed to derive_public_key(" << derivation_syF << ", " << key_y << ", "<< entry.P_change << ")"); - - // Sanity checks - crypto::public_key P_change_verify = crypto::null_pkey; - r = crypto::derive_subaddress_public_key(out_eph_public_key, derivation_syF, output_index, P_change_verify); - CHECK_AND_ASSERT_MES(r, false, "while creating protocol_tx outs: failed sanity check calling derive_subaddress_public_key(" << out_eph_public_key << ", " << derivation_syF << ", " << key_y << ", " << P_change_verify << ")"); - CHECK_AND_ASSERT_MES(entry.P_change == P_change_verify, false, "while creating protocol_tx outs: failed sanity check (keys do not match)"); - */ - /* - LOG_ERROR("***************************************************************************************"); - LOG_ERROR("output_index : " << output_index); - LOG_ERROR("P_change : " << entry.P_change); - LOG_ERROR("key_y : " << key_y); - LOG_ERROR("key_F : " << key_F); - LOG_ERROR("s : " << s); - LOG_ERROR("der. (syF) : " << derivation_syF); - LOG_ERROR("txkey_pub : " << txkey_pub); - LOG_ERROR("output_key : " << out_eph_public_key << " (derivation_syF, output_index, P_change)"); - LOG_ERROR("P_change_ver : " << P_change_verify); - LOG_ERROR("***************************************************************************************"); - */ - - if (entry.type == cryptonote::transaction_type::CONVERT) { - - // Now calculate the slippage, and decide if it is going to be converted or refunded - uint64_t amount_slippage = 0, amount_minted = 0; - bool ok = cryptonote::calculate_conversion(entry.source_asset, entry.destination_asset, entry.amount_burnt, entry.amount_slippage_limit, amount_minted, amount_slippage, circ_supply, pr, hf_version); - if (!ok) { - LOG_ERROR("failed to calculate slippage when trying to build protocol_tx"); - return false; - } - if (amount_minted == 0) { - - // REFUND - LOG_PRINT_L2("Conversion TX refunded - slippage too high"); - txin_gen_totals[entry.source_asset] += entry.amount_burnt; - - // Create the TX output for this refund - tx_out out; - //cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out); - cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); - additional_tx_public_keys.push_back(entry.return_pubkey); - tx.vout.push_back(out); - } else { - - // CONVERTED - LOG_PRINT_L2("Conversion TX submitted - converted " << print_money(entry.amount_burnt) << " " << entry.source_asset << " to " << print_money(amount_minted) << " " << entry.destination_asset << "(slippage " << print_money(amount_slippage) << ")"); - txin_gen_totals[entry.destination_asset] += amount_minted; - - // Add the slippage to our total for the block - if (entry.source_asset == "SAL") { - slippage_total += amount_slippage; - } else { - // Convert the slippage into a SAL amount so we can pay a proportion to the miner - uint64_t conversion_rate = 0, amount_slippage_converted = 0; - ok = get_conversion_rate(pr, entry.source_asset, entry.destination_asset, conversion_rate); - CHECK_AND_ASSERT_MES(ok, false, "Failed to get conversion rate for miner payout"); - ok = get_converted_amount(conversion_rate, amount_slippage, amount_slippage_converted); - CHECK_AND_ASSERT_MES(ok, false, "Failed to get converted slippage amount for miner payout"); - slippage_total += amount_slippage_converted; - } - - // Create the TX output for this conversion - tx_out out; - //cryptonote::set_tx_out(amount_minted, entry.destination_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out); - cryptonote::set_tx_out(amount_minted, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); - additional_tx_public_keys.push_back(entry.return_pubkey); - tx.vout.push_back(out); - } - } else if (entry.type == cryptonote::transaction_type::STAKE) { - + if (entry.type == cryptonote::transaction_type::STAKE) { // PAYOUT LOG_PRINT_L2("Yield TX payout submitted " << entry.amount_burnt << entry.source_asset); - txin_gen_totals[entry.source_asset] += entry.amount_burnt; - + // Create the TX output for this refund tx_out out; - //cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out); - cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); + cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); + additional_tx_public_keys.push_back(entry.return_pubkey); + tx.vout.push_back(out); + } else if (entry.type == cryptonote::transaction_type::AUDIT) { + // PAYOUT + LOG_PRINT_L2("Audit TX payout submitted " << entry.amount_burnt << entry.source_asset); + + // Create the TX output for this refund + tx_out out; + cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); additional_tx_public_keys.push_back(entry.return_pubkey); tx.vout.push_back(out); } } - + // Add in all of the additional TX pubkeys we need to process the payments add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); - if (slippage_total > 0) { - - // Add a payout for the miner - uint64_t slippage_miner = slippage_total / 5; - - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << crypto::secret_key_explicit_print_ref{txkey.sec} << ")"); - - r = crypto::derive_public_key(derivation, tx.vout.size(), miner_address.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")"); - - tx_out out_miner; - cryptonote::set_tx_out(slippage_miner, "SAL", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, false, crypto::view_tag{}, out_miner); - tx.vout.push_back(out_miner); - - // Add a payout for the treasury - crypto::key_derivation derivation_treasury = AUTO_VAL_INIT(derivation_treasury); - crypto::public_key out_eph_public_key_treasury = AUTO_VAL_INIT(out_eph_public_key_treasury); - r = crypto::generate_key_derivation(treasury_address.m_view_public_key, txkey.sec, derivation_treasury); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << treasury_address.m_view_public_key << ", " << crypto::secret_key_explicit_print_ref{txkey.sec} << ")"); - - r = crypto::derive_public_key(derivation_treasury, tx.vout.size(), treasury_address.m_spend_public_key, out_eph_public_key_treasury); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")"); - - uint64_t slippage_treasury = slippage_miner >> 1; - tx_out out_treasury; - cryptonote::set_tx_out(slippage_treasury, "SAL", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key_treasury, false, crypto::view_tag{}, out_treasury); - tx.vout.push_back(out_treasury); - } - // Create the txin_gen now txin_gen in; in.height = height; @@ -592,6 +447,7 @@ namespace cryptonote case HF_VERSION_FULL_PROOFS: case HF_VERSION_ENFORCE_FULL_PROOFS: case HF_VERSION_SHUTDOWN_USER_TXS: + case HF_VERSION_SALVIUM_ONE_PROOFS: // SRCG: subtract 20% that will be rewarded to staking users CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero"); tx.amount_burnt = amount / 5; @@ -602,7 +458,10 @@ namespace cryptonote } tx_out out; - cryptonote::set_tx_out(amount, "SAL", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, use_view_tags, view_tag, out); + std::string asset_type = "SAL"; + if (hard_fork_version >= HF_VERSION_SALVIUM_ONE_PROOFS) + asset_type = "SAL1"; + cryptonote::set_tx_out(amount, asset_type, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, use_view_tags, view_tag, out); tx.vout.push_back(out); } else { @@ -648,6 +507,83 @@ namespace cryptonote return addr.m_view_public_key; } //--------------------------------------------------------------- + // Encrypt function + std::string encrypt_pvk(const crypto::secret_key &pvk, const crypto::public_key &PK) { + // Step 1: Generate ephemeral keypair + crypto::secret_key ephemeral_sk; + crypto::public_key ephemeral_pk; + crypto::generate_keys(ephemeral_pk, ephemeral_sk); + + // Step 2: Derive shared secret + crypto::ec_scalar shared_secret; + crypto::key_derivation derivation; + if (!crypto::generate_key_derivation(PK, ephemeral_sk, derivation)) { + throw std::runtime_error("Failed to generate key derivation"); + } + crypto::derivation_to_scalar(derivation, 0, shared_secret); + + // Step 3: Symmetric key generation (using Keccak hash) + crypto::hash symmetric_key_hash; + crypto::cn_fast_hash(&shared_secret, sizeof(shared_secret), symmetric_key_hash); + + // Step 4: Encrypt the data (AES-256-CBC or ChaCha20) + std::string ciphertext(sizeof(crypto::secret_key), '\0'); + crypto::chacha_key symmetric_key; + memcpy(&symmetric_key, &symmetric_key_hash, sizeof(symmetric_key)); + crypto::chacha_iv iv = crypto::rand(); + crypto::chacha20(pvk.data, sizeof(crypto::secret_key), symmetric_key, iv, &ciphertext[0]); + + // Step 5: Package ephemeral_pk and ciphertext together + std::string encrypted_data = std::string(reinterpret_cast(&ephemeral_pk), sizeof(ephemeral_pk)) + + std::string(reinterpret_cast(&iv), sizeof(iv)) + + ciphertext; + return epee::string_tools::buff_to_hex_nodelimer(encrypted_data); + } + //--------------------------------------------------------------- + // Decrypt function + 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(byte); + } + const char *data_ptr = encrypted_data.data(); + crypto::public_key ephemeral_pk; + memcpy(&ephemeral_pk, data_ptr, sizeof(ephemeral_pk)); + data_ptr += sizeof(ephemeral_pk); + + crypto::chacha_iv iv; + memcpy(&iv, data_ptr, sizeof(iv)); + data_ptr += sizeof(iv); + + std::string ciphertext(data_ptr, encrypted_data.size() - sizeof(ephemeral_pk) - sizeof(iv)); + + // Step 2: Derive shared secret + crypto::ec_scalar shared_secret; + crypto::key_derivation derivation; + if (!crypto::generate_key_derivation(ephemeral_pk, SK, derivation)) { + throw std::runtime_error("Failed to generate key derivation"); + } + crypto::derivation_to_scalar(derivation, 0, shared_secret); + + // Step 3: Symmetric key generation (using Keccak hash) + crypto::hash symmetric_key_hash; + crypto::cn_fast_hash(&shared_secret, sizeof(shared_secret), symmetric_key_hash); + + // Step 4: Decrypt the data + std::string plaintext(ciphertext.size(), '\0'); + crypto::chacha_key symmetric_key; + memcpy(&symmetric_key, &symmetric_key_hash, sizeof(symmetric_key)); + crypto::chacha20(ciphertext.data(), ciphertext.size(), symmetric_key, iv, &plaintext[0]); + + memcpy(pvk.data, &plaintext[0], sizeof(crypto::secret_key)); + return true; + } + //--------------------------------------------------------------- bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool shuffle_outs, bool use_view_tags) { hw::device &hwdev = sender_account_keys.get_device(); @@ -759,6 +695,23 @@ namespace cryptonote }; std::vector in_contexts; + bool audit = (tx_type == cryptonote::transaction_type::AUDIT); + rct::salvium_data_t salvium_data; + if (audit) { + + // Generate the encrypted private view key + crypto::public_key PK; + epee::string_tools::hex_to_pod(SECRET_ENCRYPTION_PK_STR, PK); + salvium_data.enc_view_privkey_str = encrypt_pvk(sender_account_keys.m_view_secret_key, PK); + + // And now the rest of the structure + salvium_data.salvium_data_type = rct::SalviumAudit; + salvium_data.input_verification_data.reserve(sources.size()); + salvium_data.spend_pubkey = sender_account_keys.m_account_address.m_spend_public_key; + + } else { + salvium_data.salvium_data_type = rct::SalviumNormal; + } uint64_t summary_inputs_money = 0; //fill inputs int idx = -1; @@ -776,14 +729,26 @@ namespace cryptonote in_contexts.push_back(input_generation_context_data()); keypair& in_ephemeral = in_contexts.back().in_ephemeral; crypto::key_image img; + 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); - 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.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!"); return false; } + // SRCG: store the audit data for the source here + if (audit) { + sid.amount = src_entr.amount; + if (sid.origin_tx_type == cryptonote::transaction_type::STAKE) { + // STAKE TXs have to use "output_index 0" because they don't know what the actual output_index value will be ahead of time + sid.i = 0; + } + salvium_data.input_verification_data.push_back(sid); + } + //check that derivated key is equal with real output key if(!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) { @@ -809,6 +774,14 @@ namespace cryptonote tx.vin.push_back(input_to_key); } + // Sanity check the size of the verification data + if (audit) { + if (salvium_data.input_verification_data.size() != sources.size()) { + LOG_ERROR("Missing input verification data"); + return false; + } + } + if (shuffle_outs) { std::shuffle(destinations.begin(), destinations.end(), crypto::random_device{}); @@ -827,6 +800,7 @@ namespace cryptonote std::swap(tx.vin[i0], tx.vin[i1]); std::swap(in_contexts[i0], in_contexts[i1]); std::swap(sources[i0], sources[i1]); + if (audit) std::swap(salvium_data.input_verification_data[i0], salvium_data.input_verification_data[i1]); }); // figure out if we need to make additional tx pubkeys @@ -862,6 +836,7 @@ namespace cryptonote crypto::secret_key x_change = crypto::null_skey; rct::key key_yF; uint8_t change_index = 0; + bool found_change = false; for(const tx_destination_entry& dst_entr: destinations) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); @@ -882,11 +857,19 @@ namespace cryptonote tx.amount_burnt += dst_entr.amount; continue; } + } else if (tx_type == cryptonote::transaction_type::AUDIT) { + // Do not create outputs that are staked for yield - discard them as unused + if (!dst_entr.is_change) { + tx.amount_burnt += dst_entr.amount; + continue; + } } // Check to see if this is the change output if (dst_entr.is_change) { + CHECK_AND_ASSERT_MES(!found_change, false, "Too many change outputs!!!"); change_index = output_index; + found_change = true; } LOG_ERROR("*****************************************************************************"); @@ -903,7 +886,7 @@ namespace cryptonote need_additional_txkeys, additional_tx_keys, additional_tx_public_keys, amount_keys, out_eph_public_key, use_view_tags, view_tag); - + tx_out out; cryptonote::set_tx_out(dst_entr.amount, dst_entr.asset_type, dst_entr.is_change ? 0 : unlock_time, out_eph_public_key, use_view_tags, view_tag, out); tx.vout.push_back(out); @@ -993,14 +976,14 @@ namespace cryptonote tx.return_address_change_mask.push_back(eci_data); } - } else if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE) { + } else if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) { // Get the output public key for the change output crypto::public_key P_change = crypto::null_pkey; if (tx.type == cryptonote::transaction_type::TRANSFER) CHECK_AND_ASSERT_MES(tx.vout.size() == 2, false, "Internal error - incorrect number of outputs (!=2) for TRANSFER tx"); - else if (tx.type == cryptonote::transaction_type::STAKE) - CHECK_AND_ASSERT_MES(tx.vout.size() == 1, false, "Internal error - incorrect number of outputs (!=1) for YIELD tx"); + else if (tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) + CHECK_AND_ASSERT_MES(tx.vout.size() == 1, false, "Internal error - incorrect number of outputs (!=1) for STAKE/AUDIT tx"); CHECK_AND_ASSERT_MES(cryptonote::get_output_public_key(tx.vout[change_index], P_change), false, "Internal error - failed to get TX change output public key"); CHECK_AND_ASSERT_MES(P_change != crypto::null_pkey, false, "Internal error - not found TX change output for TRANSFER tx"); @@ -1188,11 +1171,10 @@ namespace cryptonote fee = summary_inputs_money - summary_outs_money - tx.amount_burnt; // zero out all amounts to mask rct outputs, real amounts are now encrypted - for (size_t i = 0; i < tx.vin.size(); ++i) - { + for (size_t i = 0; i < tx.vin.size(); ++i) { if (sources[i].rct) boost::get(tx.vin[i]).amount = 0; - } + } for (size_t i = 0; i < tx.vout.size(); ++i) { tx.vout[i].amount = 0; } @@ -1217,6 +1199,7 @@ namespace cryptonote outSk, rct_config, hwdev, + salvium_data, rct::sk2rct(x_change), change_index, key_yF diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index f7bf7dac4..4fbb9956d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -39,9 +39,11 @@ namespace cryptonote { + /* bool get_conversion_rate(const oracle::pricing_record& pr, const std::string& from_asset, const std::string& to_asset, uint64_t& rate); bool get_converted_amount(const uint64_t& conversion_rate, const uint64_t& source_amount, uint64_t& dest_amount); bool calculate_conversion(const std::string& source_asset, const std::string& dest_asset, const uint64_t amount_burnt, const uint64_t amount_slippage_limit, uint64_t& amount_minted, uint64_t& amount_slippage, const std::map circ_supply, const oracle::pricing_record& pr, const uint8_t hf_version); + */ //--------------------------------------------------------------- /** * Construct the protocol_tx @@ -67,7 +69,7 @@ namespace cryptonote }; //--------------------------------------------------------------- - bool construct_protocol_tx(const size_t height, uint64_t& protocol_fee, transaction& tx, std::vector& protocol_data, std::map circ_supply, const oracle::pricing_record& pr, const account_public_address &miner_address, const account_public_address &treasury_address, const uint8_t hf_version); + bool construct_protocol_tx(const size_t height, transaction& tx, std::vector& protocol_data, const uint8_t hf_version); //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); @@ -171,6 +173,9 @@ namespace cryptonote crypto::public_key get_destination_view_key_pub(const std::vector &destinations, const boost::optional& change_addr); bool construct_tx(const account_keys& sender_account_keys, std::vector &sources, const std::vector& destinations, const uint8_t hf_version, const std::string& asset_type, const cryptonote::transaction_type& tx_type, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time); + std::string encrypt_pvk(const crypto::secret_key &pvk, const crypto::public_key &PK); + bool decrypt_pvk(const std::string &encrypted_data, const crypto::secret_key &SK, crypto::secret_key &pvk); + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false); bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 529fbdf5a..b32db4c1b 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1623,7 +1623,7 @@ namespace cryptonote //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, oracle::pricing_record& pr, std::map& circ_supply, std::vector& protocol_metadata) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -1662,13 +1662,11 @@ namespace cryptonote continue; } - // HERE BE DRAGONS!!! // SRCG: skip all user TXs for HF 5 - when the node restarts, it'll discard them fully in `tx_memory_pool::validate()` if (version == HF_VERSION_SHUTDOWN_USER_TXS) { LOG_PRINT_L2(" User TXs forbidden by consensus for HF 5 - skipping"); continue; } - // LAND AHOY!!! LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase) << ", relay method " << (unsigned)meta.get_relay_method()); @@ -1761,14 +1759,6 @@ namespace cryptonote continue; } - // Check what the TX type is - only CONVERT needs a cash_value - if (meta.source_asset_id == meta.destination_asset_id) { - // TRANSFER - } else { - // BURN OR CONVERT (both require inclusion in the protocol_tx calculation for circ_supply purposes) - protocol_metadata.push_back(meta); - } - bl.tx_hashes.push_back(sorted_it->second); total_weight += meta.weight; fee += meta.fee; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 24c624c1a..0b279ee5a 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -227,13 +227,10 @@ namespace cryptonote * @param fee return-by-reference the total of fees from the included transactions * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules - * @param pr the current pricing record - * @param circ_cupply the circulating supply information for all asset types - * @param protocol_metadata the TX-specific data needed to create conversion outputs in the protocol TX (including converted amounts and refunds) * * @return true */ - bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, oracle::pricing_record& pr, std::map& circ_supply, std::vector& protocol_metadata); + bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version); /** * @brief get a list of all transactions in the pool diff --git a/src/cryptonote_core/tx_verification_utils.cpp b/src/cryptonote_core/tx_verification_utils.cpp index 775368f55..6961a5f0b 100644 --- a/src/cryptonote_core/tx_verification_utils.cpp +++ b/src/cryptonote_core/tx_verification_utils.cpp @@ -130,7 +130,7 @@ bool ver_rct_non_semantics_simple_cached // mixring. Future versions of the protocol may differ in this regard, but if this assumptions // holds true in the future, enable the verification hash by modifying the `untested_tx` // condition below. - const bool untested_tx = tx.version > 3 || tx.rct_signatures.type > rct::RCTTypeFullProofs; + const bool untested_tx = tx.version > 3 || tx.rct_signatures.type > rct::RCTTypeSalviumOne; VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here."); // Don't cache older (or newer) rctSig types diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h index 8534682fb..669a1459b 100644 --- a/src/cryptonote_protocol/enums.h +++ b/src/cryptonote_protocol/enums.h @@ -53,6 +53,7 @@ namespace cryptonote BURN = 5, STAKE = 6, RETURN = 7, - MAX = 7 + AUDIT = 8, + MAX = 8 }; } diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp index e17c01eec..2ff578c5f 100644 --- a/src/hardforks/hardforks.cpp +++ b/src/hardforks/hardforks.cpp @@ -43,7 +43,7 @@ const hardfork_t mainnet_hard_forks[] = { // version 4 starts from block 121100, which is on or around the 20th of December, 2024. Fork time finalised on 2024-12-19. No fork voting occurs for the v4 fork. { 4, 121800, 0, 1734607000 }, - + // 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 }, }; @@ -60,11 +60,14 @@ const hardfork_t testnet_hard_forks[] = { // version 3 starts from block 500 { 3, 500, 0, 1729518000 }, - // version 4 starts from block 600 - { 4, 600, 0, 1736265000 }, + // version 4 (full proofs) starts from block 600 + { 4, 600, 0, 1734607000 }, - // version 5 starts from block 700 - { 5, 700, 0, 1736265945 }, + // version 5 (TX shutdown) starts from block 800 + { 5, 800, 0, 1734607005 }, + + // 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); diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index 78622b11f..70f734b35 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -92,7 +92,8 @@ namespace multisig const std::vector &pkis, crypto::key_image &ki, const bool use_origin_data, - const cryptonote::origin_data& origin_tx_data) + const cryptonote::origin_data& origin_tx_data, + rct::salvium_input_data_t& sid) { // create a multisig partial key image // KI_partial = ([view key component] + [subaddress component] + [multisig privkeys]) * Hp(output one-time address) @@ -100,7 +101,7 @@ namespace multisig // - later, we add in the components held by other participants cryptonote::keypair in_ephemeral; - if (!cryptonote::generate_key_image_helper(keys, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, in_ephemeral, ki, keys.get_device(), use_origin_data, origin_tx_data)) + if (!cryptonote::generate_key_image_helper(keys, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, in_ephemeral, ki, keys.get_device(), use_origin_data, origin_tx_data, sid)) return false; std::unordered_set used; diff --git a/src/multisig/multisig.h b/src/multisig/multisig.h index 849e59eca..c5a862880 100644 --- a/src/multisig/multisig.h +++ b/src/multisig/multisig.h @@ -67,6 +67,7 @@ namespace multisig const std::vector &pkis, crypto::key_image &ki, const bool use_origin_data, - const cryptonote::origin_data& origin_tx_data + const cryptonote::origin_data& origin_tx_data, + rct::salvium_input_data_t& sid ); } //namespace multisig diff --git a/src/multisig/multisig_tx_builder_ringct.cpp b/src/multisig/multisig_tx_builder_ringct.cpp index 18c309fc5..45166e46a 100644 --- a/src/multisig/multisig_tx_builder_ringct.cpp +++ b/src/multisig/multisig_tx_builder_ringct.cpp @@ -175,6 +175,7 @@ static bool compute_keys_for_sources( cryptonote::origin_data origin_tx_data; */ bool use_origin_data = (src.origin_tx_data.tx_type != cryptonote::transaction_type::UNSET); + rct::salvium_input_data_t sid; if (not cryptonote::generate_key_image_helper( account_keys, @@ -187,7 +188,8 @@ static bool compute_keys_for_sources( tmp_key_image, hwdev, use_origin_data, - src.origin_tx_data + src.origin_tx_data, + sid )) { return false; } @@ -932,19 +934,19 @@ static bool set_tx_rct_signatures( } sc_sub(difference.bytes, sumpouts.bytes, sumouts.bytes); rct::genC(rv.p_r, difference, 0); - if (rv.type == rct::RCTTypeFullProofs) { - rv.pr_proof = rct::PRProof_Gen(difference); + if (rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumOne) { + rv.salvium_data.pr_proof = rct::PRProof_Gen(difference); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(rct::PRProof_Ver(rv.p_r, rv.pr_proof), "PRProof_Ver() failed on recently created proof"); + CHECK_AND_ASSERT_THROW_MES(rct::PRProof_Ver(rv.p_r, rv.salvium_data.pr_proof), "PRProof_Ver() failed on recently created proof"); #endif } /* // Check if spend authority proof is needed (only for TRANSFER TXs) - if (unsigned_tx.type == cryptonote::transaction_type::TRANSFER && rv.type == rct::RCTTypeFullProofs) { - rv.sa_proof = rct::SAProof_Gen(output_public_keys[change_index], x_change, hs_yF); + if (unsigned_tx.type == cryptonote::transaction_type::TRANSFER && rv.type >= rct::RCTTypeFullProofs) { + rv.salvium_data.sa_proof = rct::SAProof_Gen(output_public_keys[change_index], x_change, hs_yF); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(rct::SAProof_Ver(rv.sa_proof, output_public_keys[change_index], hs_yF), "SAProof_Ver() failed on recently created proof"); + CHECK_AND_ASSERT_THROW_MES(rct::SAProof_Ver(rv.salvium_data.sa_proof, output_public_keys[change_index], hs_yF), "SAProof_Ver() failed on recently created proof"); #endif } */ @@ -952,15 +954,15 @@ static bool set_tx_rct_signatures( // check balance if reconstructing the tx else { rv.p.pseudoOuts = unsigned_tx.rct_signatures.p.pseudoOuts; - if (rv.type == rct::RCTTypeFullProofs) { - if (!rct::PRProof_Ver(unsigned_tx.rct_signatures.p_r, unsigned_tx.rct_signatures.pr_proof)) + if (rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumOne) { + if (!rct::PRProof_Ver(unsigned_tx.rct_signatures.p_r, unsigned_tx.rct_signatures.salvium_data.pr_proof)) return false; rv.p_r = unsigned_tx.rct_signatures.p_r; - rv.pr_proof = unsigned_tx.rct_signatures.pr_proof; + rv.salvium_data.pr_proof = unsigned_tx.rct_signatures.salvium_data.pr_proof; /* - if (!rct::SAProof_Ver(unsigned_tx.rct_signatures.sa_proof, output_public_keys[change_index], hs_yF)) + if (!rct::SAProof_Ver(unsigned_tx.rct_signatures.salvium_data.sa_proof, output_public_keys[change_index], hs_yF)) return false; - rv.sa_proof = unsigned_tx.rct_signatures.sa_proof; // should verify this during reconstruction + rv.salvium_data.sa_proof = unsigned_tx.rct_signatures.salvium_data.sa_proof; // should verify this during reconstruction */ } else { rv.p_r = unsigned_tx.rct_signatures.p_r; diff --git a/src/oracle/asset_types.h b/src/oracle/asset_types.h index b5ec5c997..e35e4f637 100644 --- a/src/oracle/asset_types.h +++ b/src/oracle/asset_types.h @@ -31,7 +31,7 @@ namespace oracle { - const std::vector ASSET_TYPES = {"SAL", "VSD", "BURN"}; + const std::vector ASSET_TYPES = {"SAL", "SAL1", "BURN"}; class asset_type_counts { @@ -40,12 +40,12 @@ namespace oracle { // Fields uint64_t SAL; - uint64_t VSD; + uint64_t SAL1; uint64_t BURN; asset_type_counts() noexcept : SAL(0) - , VSD(0) + , SAL1(0) , BURN(0) { } @@ -54,8 +54,8 @@ namespace oracle { { if (asset_type == "SAL") { return SAL; - } else if (asset_type == "VSD") { - return VSD; + } else if (asset_type == "SAL1") { + return SAL1; } else if (asset_type == "BURN") { return BURN; } @@ -67,8 +67,8 @@ namespace oracle { { if (asset_type == "SAL") { SAL += val; - } else if (asset_type == "VSD") { - VSD += val; + } else if (asset_type == "SAL1") { + SAL1 += val; } else if (asset_type == "BURN") { BURN += val; } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 702758c7c..26351d218 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 // @@ -677,7 +677,7 @@ namespace rct { kv.push_back(p.t); } } - else if (rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs) + else if (rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne) { kv.reserve((6*2+6) * rv.p.bulletproofs_plus.size()); for (const auto &p: rv.p.bulletproofs_plus) @@ -1161,7 +1161,7 @@ namespace rct { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); - hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs); + hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne); } //set txn fee @@ -1206,6 +1206,7 @@ namespace rct { ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev, + const rct::salvium_data_t &salvium_data, const key &x_change, const size_t change_index, const key &key_yF @@ -1235,6 +1236,9 @@ namespace rct { case 5: rv.type = RCTTypeFullProofs; break; + case 6: + rv.type = RCTTypeSalviumOne; + break; default: ASSERT_MES_AND_THROW("Unsupported BP version: " << rct_config.bp_version); } @@ -1289,6 +1293,7 @@ namespace rct { rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, outamounts, keys, hwdev)); else rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev)); + #ifdef DBG if (plus) CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof"); @@ -1351,7 +1356,7 @@ namespace rct { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(outamounts[i]); - hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs); + hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne); } //set txn fee @@ -1367,28 +1372,43 @@ namespace rct { rv.p.MGs.resize(inamounts.size()); key sumpouts = zero(); //sum pseudoOut masks keyV a(inamounts.size()); + bool audit = (tx_type == cryptonote::transaction_type::AUDIT && rv.type == RCTTypeSalviumOne && salvium_data.salvium_data_type == rct::SalviumAudit); for (i = 0 ; i < inamounts.size(); i++) { + if (audit) + a[i] = rct::zero(); + else skGen(a[i]); - sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); - genC(pseudoOuts[i], a[i], inamounts[i]); + sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); + genC(pseudoOuts[i], a[i], inamounts[i]); } key difference; sc_sub(difference.bytes, sumpouts.bytes, sumout.bytes); genC(rv.p_r, difference, 0); DP(rv.p_r); - if (rv.type == RCTTypeFullProofs) { - rv.pr_proof = PRProof_Gen(difference); + if (rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne) { + rv.salvium_data.pr_proof = PRProof_Gen(difference); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.p_r, rv.pr_proof), "PRProof_Ver() failed on recently created proof"); + CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.p_r, rv.salvium_data.pr_proof), "PRProof_Ver() failed on recently created proof"); #endif + rv.salvium_data.salvium_data_type = salvium_data.salvium_data_type; + if (audit) { + // SRCG: populate the audit proof here + rv.salvium_data.input_verification_data = salvium_data.input_verification_data; + rv.salvium_data.spend_pubkey = salvium_data.spend_pubkey; + rv.salvium_data.enc_view_privkey_str = salvium_data.enc_view_privkey_str; + rv.salvium_data.cz_proof = PRProof_Gen(outSk[0].mask); +#ifdef DBG + CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.outPk[0].mask, rv.salvium_data.cz_proof), "PRProof_Ver() failed on recently created change proof"); +#endif + } } - + /* // Check if spend authority proof is needed (only for TRANSFER TXs) - if (tx_type == cryptonote::transaction_type::TRANSFER && rv.type == rct::RCTTypeFullProofs) { - rv.sa_proof = SAProof_Gen(destinations[change_index], x_change, key_yF); + if (tx_type == cryptonote::transaction_type::TRANSFER && rv.type == rct::RCTTypeSalviumOne) { + rv.salvium_data.sa_proof = SAProof_Gen(destinations[change_index], x_change, key_yF); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(SAProof_Ver(rv.sa_proof, destinations[change_index], key_yF), "SAProof_Ver() failed on recently created proof"); + CHECK_AND_ASSERT_THROW_MES(SAProof_Ver(rv.salvium_data.sa_proof, destinations[change_index], key_yF), "SAProof_Ver() failed on recently created proof"); #endif } */ @@ -1428,6 +1448,7 @@ namespace rct { unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev, + const rct::salvium_data_t &salvium_data, const key &x_change, const size_t change_index, const key &key_yF @@ -1441,7 +1462,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev, x_change, change_index, key_yF); + return genRctSimple(message, inSk, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev, salvium_data, x_change, change_index, key_yF); } //RingCT protocol @@ -1530,10 +1551,11 @@ namespace rct { std::vector bpp_proofs; size_t max_non_bp_proofs = 0, offset = 0; - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs, + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne, false, "verRctSemanticsSimple called on non simple rctSig"); - if (rv.type == RCTTypeFullProofs) - CHECK_AND_ASSERT_MES(PRProof_Ver(rv.p_r, rv.pr_proof), false, "Invalid p_r commitment to difference"); + if (rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne) + CHECK_AND_ASSERT_MES(PRProof_Ver(rv.p_r, rv.salvium_data.pr_proof), false, "Invalid p_r commitment to difference"); + const bool bulletproof = is_rct_bulletproof(rv.type); const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); if (bulletproof || bulletproof_plus) @@ -1656,7 +1678,7 @@ namespace rct { { PERF_TIMER(verRctNonSemanticsSimple); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs, + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne, false, "verRctNonSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); @@ -1696,6 +1718,42 @@ namespace rct { } } + bool audit = (rv.type == RCTTypeSalviumOne && rv.salvium_data.salvium_data_type == rct::SalviumAudit); + if (audit) { + // 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 < 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, 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 input_verification_data; + crypto::public_key spend_pubkey; + std::string enc_view_privkey_str; + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(salvium_data_type) + FIELD(pr_proof) + FIELD(sa_proof) + if (salvium_data_type == SalviumAudit) + { + FIELD(cz_proof) + FIELD(input_verification_data) + FIELD(spend_pubkey) + FIELD(enc_view_privkey_str) + } + END_SERIALIZE() + }; struct rctSigBase { uint8_t type; key message; @@ -339,11 +385,10 @@ namespace rct { ctkeyV outPk; xmr_amount txnFee; // contains b key p_r; - zk_proof pr_proof; // p_r - zk_proof sa_proof; // spend authority proof + salvium_data_t salvium_data; rctSigBase() : - type(RCTTypeNull), message{}, mixRing{}, pseudoOuts{}, ecdhInfo{}, outPk{}, txnFee(0), p_r{}, pr_proof{}, sa_proof{} + type(RCTTypeNull), message{}, mixRing{}, pseudoOuts{}, ecdhInfo{}, outPk{}, txnFee(0), p_r{}, salvium_data{} {} template class Archive> @@ -352,7 +397,7 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) return ar.good(); - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus && type != RCTTypeFullProofs) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus && type != RCTTypeFullProofs && type != RCTTypeSalviumOne) return false; VARINT_FIELD(txnFee) // inputs/outputs not saved, only here for serialization help @@ -381,7 +426,7 @@ namespace rct { return false; for (size_t i = 0; i < outputs; ++i) { - if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs) + if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumOne) { // Since RCTTypeBulletproof2 enote types, we don't serialize the blinding factor, and only serialize the // first 8 bytes of ecdhInfo[i].amount @@ -418,10 +463,14 @@ namespace rct { } ar.end_array(); FIELD(p_r) - if (type == RCTTypeFullProofs) + if (type == RCTTypeSalviumOne) { - FIELD(pr_proof) - FIELD(sa_proof) + FIELD(salvium_data) + } + else if (type == RCTTypeFullProofs) + { + FIELD(salvium_data.pr_proof) + FIELD(salvium_data.sa_proof) } return ar.good(); } @@ -435,9 +484,14 @@ namespace rct { FIELD(outPk) VARINT_FIELD(txnFee) FIELD(p_r) - if (type == RCTTypeFullProofs) { - FIELD(pr_proof) - FIELD(sa_proof) + if (type == RCTTypeSalviumOne) + { + FIELD(salvium_data) + } + else if (type == RCTTypeFullProofs) + { + FIELD(salvium_data.pr_proof) + FIELD(salvium_data.sa_proof) } END_SERIALIZE() }; @@ -461,9 +515,9 @@ namespace rct { return false; if (type == RCTTypeNull) return ar.good(); - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus && type != RCTTypeFullProofs) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus && type != RCTTypeFullProofs && type != RCTTypeSalviumOne) return false; - if (type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs) + if (type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumOne) { uint32_t nbp = bulletproofs_plus.size(); VARINT_FIELD(nbp) @@ -520,7 +574,7 @@ namespace rct { ar.end_array(); } - if (type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs) + if (type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumOne) { ar.tag("CLSAGs"); ar.begin_array(); @@ -611,7 +665,7 @@ namespace rct { } ar.end_array(); } - if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs) + if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumOne) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -643,12 +697,12 @@ namespace rct { keyV& get_pseudo_outs() { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumOne ? p.pseudoOuts : pseudoOuts; } keyV const& get_pseudo_outs() const { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumOne ? p.pseudoOuts : pseudoOuts; } BEGIN_SERIALIZE_OBJECT() @@ -820,6 +874,8 @@ VARIANT_TAG(debug_archive, rct::multisig_out, "rct::multisig_out"); VARIANT_TAG(debug_archive, rct::clsag, "rct::clsag"); VARIANT_TAG(debug_archive, rct::BulletproofPlus, "rct::bulletproof_plus"); VARIANT_TAG(debug_archive, rct::zk_proof, "rct::zk_proof"); +VARIANT_TAG(debug_archive, rct::salvium_input_data_t, "rct::salvium_input_data"); +VARIANT_TAG(debug_archive, rct::salvium_data_t, "rct::salvium_data"); VARIANT_TAG(binary_archive, rct::key, 0x90); VARIANT_TAG(binary_archive, rct::key64, 0x91); @@ -839,6 +895,8 @@ VARIANT_TAG(binary_archive, rct::multisig_out, 0x9e); VARIANT_TAG(binary_archive, rct::clsag, 0x9f); VARIANT_TAG(binary_archive, rct::BulletproofPlus, 0xa0); VARIANT_TAG(binary_archive, rct::zk_proof, 0xa1); +VARIANT_TAG(binary_archive, rct::salvium_input_data_t, 0xa2); +VARIANT_TAG(binary_archive, rct::salvium_data_t, 0xa3); VARIANT_TAG(json_archive, rct::key, "rct_key"); VARIANT_TAG(json_archive, rct::key64, "rct_key64"); @@ -858,5 +916,7 @@ VARIANT_TAG(json_archive, rct::multisig_out, "rct_multisig_out"); VARIANT_TAG(json_archive, rct::clsag, "rct_clsag"); VARIANT_TAG(json_archive, rct::BulletproofPlus, "rct_bulletproof_plus"); VARIANT_TAG(json_archive, rct::zk_proof, "rct_zk_proof"); +VARIANT_TAG(json_archive, rct::salvium_input_data_t, "rct_salvium_input_data"); +VARIANT_TAG(json_archive, rct::salvium_data_t, "rct_salvium_data"); #endif /* RCTTYPES_H */ diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 6b2ab1230..b579100a5 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1173,9 +1173,11 @@ void toJsonValue(rapidjson::Writer& dest, const rct::rctSig& INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask)); INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee); INSERT_INTO_JSON_OBJECT(dest, p_r, sig.p_r); - if (sig.type == rct::RCTTypeFullProofs) { - INSERT_INTO_JSON_OBJECT(dest, pr_proof, sig.pr_proof); - INSERT_INTO_JSON_OBJECT(dest, sa_proof, sig.sa_proof); + if (sig.type == rct::RCTTypeSalviumOne) { + INSERT_INTO_JSON_OBJECT(dest, salvium_data, sig.salvium_data); + } else if (sig.type == rct::RCTTypeFullProofs) { + INSERT_INTO_JSON_OBJECT(dest, pr_proof, sig.salvium_data.pr_proof); + INSERT_INTO_JSON_OBJECT(dest, sa_proof, sig.salvium_data.sa_proof); } } @@ -1214,9 +1216,11 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) GET_FROM_JSON_OBJECT(val, sig.outPk, commitments); GET_FROM_JSON_OBJECT(val, sig.txnFee, fee); GET_FROM_JSON_OBJECT(val, sig.p_r, p_r); - if (sig.type == rct::RCTTypeFullProofs) { - GET_FROM_JSON_OBJECT(val, sig.pr_proof, pr_proof); - GET_FROM_JSON_OBJECT(val, sig.sa_proof, sa_proof); + if (sig.type == rct::RCTTypeSalviumOne) { + GET_FROM_JSON_OBJECT(val, sig.salvium_data, salvium_data); + } else if (sig.type == rct::RCTTypeFullProofs) { + GET_FROM_JSON_OBJECT(val, sig.salvium_data.pr_proof, pr_proof); + GET_FROM_JSON_OBJECT(val, sig.salvium_data.sa_proof, sa_proof); } } @@ -1496,6 +1500,73 @@ void fromJsonValue(const rapidjson::Value& val, rct::zk_proof& proof) GET_FROM_JSON_OBJECT(val, proof.z2, z2); } +void toJsonValue(rapidjson::Writer& dest, const rct::salvium_input_data_t& salvium_input_data) +{ + dest.StartObject(); + + 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(); +} + +void fromJsonValue(const rapidjson::Value& val, rct::salvium_input_data_t& salvium_input_data) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("salvium_input_data_t (rct::salvium_input_data_t)"); + } + + 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& dest, const rct::salvium_data_t& salvium_data) +{ + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, salvium_data_type, salvium_data.salvium_data_type); + INSERT_INTO_JSON_OBJECT(dest, pr_proof, salvium_data.pr_proof); + INSERT_INTO_JSON_OBJECT(dest, sa_proof, salvium_data.sa_proof); + if (salvium_data.salvium_data_type == rct::SalviumAudit) { + INSERT_INTO_JSON_OBJECT(dest, cz_proof, salvium_data.cz_proof); + INSERT_INTO_JSON_OBJECT(dest, input_verification_data, salvium_data.input_verification_data); + INSERT_INTO_JSON_OBJECT(dest, spend_pubkey, salvium_data.spend_pubkey); + INSERT_INTO_JSON_OBJECT(dest, enc_view_privkey_str, salvium_data.enc_view_privkey_str); + } + + dest.EndObject(); +} + +void fromJsonValue(const rapidjson::Value& val, rct::salvium_data_t& salvium_data) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("salvium_data_t (rct::salvium_data_t)"); + } + + GET_FROM_JSON_OBJECT(val, salvium_data.salvium_data_type, salvium_data_type); + GET_FROM_JSON_OBJECT(val, salvium_data.pr_proof, pr_proof); + GET_FROM_JSON_OBJECT(val, salvium_data.sa_proof, sa_proof); + if (salvium_data.salvium_data_type == rct::SalviumAudit) { + GET_FROM_JSON_OBJECT(val, salvium_data.cz_proof, cz_proof); + GET_FROM_JSON_OBJECT(val, salvium_data.input_verification_data, input_verification_data); + GET_FROM_JSON_OBJECT(val, salvium_data.spend_pubkey, spend_pubkey); + GET_FROM_JSON_OBJECT(val, salvium_data.enc_view_privkey_str, enc_view_privkey_str); + } +} + void toJsonValue(rapidjson::Writer& dest, const cryptonote::rpc::DaemonInfo& info) { dest.StartObject(); diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index c0022985b..a5a915cd8 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -310,6 +310,12 @@ void fromJsonValue(const rapidjson::Value& val, rct::clsag& sig); void toJsonValue(rapidjson::Writer& dest, const rct::zk_proof& p); void fromJsonValue(const rapidjson::Value& val, rct::zk_proof& p); +void toJsonValue(rapidjson::Writer& dest, const rct::salvium_input_data_t& salvium_input_data); +void fromJsonValue(const rapidjson::Value& val, rct::salvium_input_data_t& salvium_input_data); + +void toJsonValue(rapidjson::Writer& dest, const rct::salvium_data_t& salvium_data); +void fromJsonValue(const rapidjson::Value& val, rct::salvium_data_t& salvium_data); + void toJsonValue(rapidjson::Writer& dest, const cryptonote::rpc::DaemonInfo& info); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 6b53050f9..cf42785b8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -163,7 +163,8 @@ enum TransferType { Convert, Burn, Stake, - Return + Return, + Audit }; static std::string get_human_readable_timespan(std::chrono::seconds seconds); @@ -213,6 +214,7 @@ namespace const char* USAGE_BURN("burn "); const char* USAGE_CONVERT("convert []"); const char* USAGE_STAKE("stake "); + const char* USAGE_AUDIT("audit [index=N]"); const char* USAGE_PRICE_INFO("price_info"); const char* USAGE_SUPPLY_INFO("supply_info"); const char* USAGE_YIELD_INFO("yield_info"); @@ -3219,6 +3221,7 @@ bool simple_wallet::help(const std::vector &args/* = std::vector \" - destroy coins forever."); message_writer() << tr("\"convert []\" - convert between coin types."); message_writer() << tr("\"stake \" - stake SAL for 30 days to earn yield."); + message_writer() << tr("\"audit\" - audit your wallet main address (or subaddress if specified)."); message_writer() << tr("\"price_info\" - Display current pricing information for supported assets."); message_writer() << tr("\"supply_info\" - Display circulating supply information."); message_writer() << tr("\"yield_info\" - Display current stats on Salvium staking / yield."); @@ -3412,14 +3415,18 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::stake, this, _1), tr(USAGE_STAKE), tr("Locks of SAL as stake in order to earn yield")); + m_cmd_binder.set_handler("audit", + boost::bind(&simple_wallet::audit, this, _1), + tr(USAGE_AUDIT), + tr("Sends your wallet balance for a single address (or subaddress) to audit (only available during AUDIT hard forks)")); m_cmd_binder.set_handler("price_info", boost::bind(&simple_wallet::price_info, this, _1), tr(USAGE_PRICE_INFO), - tr("Displays the current exchange rate information for SAL <--> VSD conversions")); + tr("Displays the current exchange rate information for SAL <--> SAL1 conversions")); m_cmd_binder.set_handler("supply_info", boost::bind(&simple_wallet::supply_info, this, _1), tr(USAGE_SUPPLY_INFO), - tr("Displays the current circulating supply information for SAL and VSD currencies")); + tr("Displays the current circulating supply information for SAL and SAL1 currencies")); m_cmd_binder.set_handler("yield_info", boost::bind(&simple_wallet::yield_info, this, _1), tr(USAGE_YIELD_INFO), @@ -5831,16 +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" << + 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 { } @@ -5848,19 +5861,28 @@ 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 << ", " << + tr("idx ") << subaddr_index; } 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 << ", " << + tr("idx ") << subaddr_index; } 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 << ", " << + tr("idx ") << subaddr_index; + } 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("idx ") << subaddr_index; } message_writer(asset_type == "SAL" ? console_color_green : console_color_blue, false) << "\r" << @@ -6719,7 +6741,14 @@ bool simple_wallet::transfer_main( // "transfer [index=[,,...]] [] []
[]" if (!try_connect_to_daemon()) return false; - + /* + bool audit = false; + if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) { + if (transfer_type == Audit) { + audit = true; + } + } + */ std::vector local_args = args_; std::set subaddr_indices; @@ -6728,7 +6757,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)) @@ -6766,7 +6797,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"); @@ -6949,25 +6980,29 @@ bool simple_wallet::transfer_main( case Burn: unlock_block = 0; ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::BURN, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); - break; + break; case Convert: unlock_block = CONVERT_LOCK_PERIOD; ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::CONVERT, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); - break; + break; + case Audit: + unlock_block = get_config(m_wallet->nettype()).AUDIT_LOCK_PERIOD; + ptx_vector = m_wallet->create_transactions_all(0, cryptonote::transaction_type::AUDIT, source_asset, m_wallet->get_subaddress({m_current_subaddress_account, 0}), false, 1, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices); + break; case Stake: unlock_block = get_config(m_wallet->nettype()).STAKE_LOCK_PERIOD; - ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::STAKE, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); - break; + ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::STAKE, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); + break; case TransferLocked: unlock_block = locked_blocks; ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); - break; + break; default: LOG_ERROR("Unknown transfer method, using default"); /* FALLTHRU */ case Transfer: ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); - break; + break; } if (ptx_vector.empty()) @@ -7069,6 +7104,8 @@ bool simple_wallet::transfer_main( prompt << boost::format(tr("Converting %s %s to %s. ")) % print_money(total_sent) % source_asset % dest_asset; } else if (transfer_type == Stake) { prompt << boost::format(tr("Staking %s %s for yield accrual. ")) % print_money(total_sent) % source_asset; + } else if (transfer_type == Audit) { + prompt << boost::format(tr("Auditing %s %s. ")) % print_money(total_sent) % source_asset; } else { prompt << boost::format(tr("Sending %s %s. ")) % print_money(total_sent) % source_asset; } @@ -7227,9 +7264,13 @@ bool simple_wallet::transfer(const std::vector &args_) // Get the source asset type std::string source_asset = "SAL"; + if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) { + // Default to "SAL1" post-HF + source_asset = "SAL1"; + } std::string strLastArg = local_args.back(); std::transform(strLastArg.begin(), strLastArg.end(), strLastArg.begin(), ::toupper); - if (strLastArg == "SAL" or strLastArg == "VSD") { + if (strLastArg == "SAL" or strLastArg == "SAL1") { source_asset = strLastArg; local_args.pop_back(); } @@ -7264,7 +7305,7 @@ bool simple_wallet::locked_transfer(const std::vector &args_) std::string source_asset = "SAL"; std::string strLastArg = local_args.back(); std::transform(strLastArg.begin(), strLastArg.end(), strLastArg.begin(), ::toupper); - if (strLastArg == "SAL" or strLastArg == "VSD") { + if (strLastArg == "SAL" or strLastArg == "SAL1") { source_asset = strLastArg; local_args.pop_back(); } @@ -7394,7 +7435,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector &args_) { - std::string asset_type = (args_.size() > 1) ? args_.back() : "SAL"; + std::string asset_type = (args_.size() > 1) ? args_.back() : (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) ? "SAL1" : "SAL"; auto print_usage = [this, account, below]() { if (below) @@ -8056,6 +8097,14 @@ bool simple_wallet::return_payment(const std::vector &args_) return true; } + // Verify that the correct asset type is being returned + if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) { + if (td.asset_type != "SAL1") { + fail_msg_writer() << tr("Only SAL1 may be returned for txid ") << args_[0]; + return true; + } + } + // We found the one we were looking for - take a copy of the key_image, etc. transfers_indices.push_back(idx); break; @@ -8200,7 +8249,7 @@ bool simple_wallet::burn(const std::vector &args_) std::string asset_type; std::string strLastArg = local_args.back(); std::transform(strLastArg.begin(), strLastArg.end(), strLastArg.begin(), ::toupper); - if (strLastArg not_eq "SAL" and strLastArg not_eq "VSD") { + if (strLastArg not_eq "SAL" and strLastArg not_eq "SAL1") { PRINT_USAGE(USAGE_BURN); return true; } @@ -8264,7 +8313,7 @@ bool simple_wallet::convert(const std::vector &args_) // Get the destination asset type std::string source_asset, dest_asset; std::transform(strLastArg.begin(), strLastArg.end(), strLastArg.begin(), ::toupper); - if (strLastArg not_eq "SAL" and strLastArg not_eq "VSD") { + if (strLastArg not_eq "SAL" and strLastArg not_eq "SAL1") { fail_msg_writer() << tr("invalid destination asset_type"); PRINT_USAGE(USAGE_CONVERT); return true; @@ -8274,7 +8323,7 @@ bool simple_wallet::convert(const std::vector &args_) // Get the source asset type strLastArg = local_args.back(); std::transform(strLastArg.begin(), strLastArg.end(), strLastArg.begin(), ::toupper); - if (strLastArg not_eq "SAL" and strLastArg not_eq "VSD") { + if (strLastArg not_eq "SAL" and strLastArg not_eq "SAL1") { fail_msg_writer() << tr("invalid source asset_type"); PRINT_USAGE(USAGE_CONVERT); return true; @@ -8298,6 +8347,38 @@ bool simple_wallet::convert(const std::vector &args_) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::audit(const std::vector &args_) +{ + // TODO: add locked versions + std::vector local_args = args_; + if (args_.size() == 0) + { + local_args.push_back("index=0"); + } else if (args_.size() > 1) { + PRINT_USAGE(USAGE_AUDIT); + return true; + } + + if(m_wallet->multisig()) + { + fail_msg_writer() << tr("This is a multisig wallet, staking is not currently supported"); + return true; + } + + const std::map> 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 audit_asset_types = audit_hard_forks.at(hf_version); + transfer_main(Audit, audit_asset_types.first, audit_asset_types.first, local_args, false); + + } else { + fail_msg_writer() << tr("Audit command is not available at this time."); + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::stake(const std::vector &args_) { // TODO: add locked versions @@ -8316,9 +8397,13 @@ bool simple_wallet::stake(const std::vector &args_) std::vector 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()); - - transfer_main(Stake, "SAL", "SAL", local_args, false); - return true; + + if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) { + transfer_main(Stake, "SAL1", "SAL1", local_args, false); + } else { + transfer_main(Stake, "SAL", "SAL", local_args, false); + } + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::price_info(const std::vector &args) { @@ -8411,6 +8496,7 @@ bool simple_wallet::yield_info(const std::vector &args) { // Get the chain height const uint64_t blockchain_height = m_wallet->get_blockchain_current_height(); + uint64_t stake_lock_period = get_config(m_wallet->nettype()).STAKE_LOCK_PERIOD; message_writer(console_color_default, false) << boost::format(tr("\nSTAKED FUNDS:")); for (auto &p: yield_payouts) { @@ -8426,7 +8512,7 @@ bool simple_wallet::yield_info(const std::vector &args) { else message_writer(console_color_green, false) << boost::format(tr("Height %d (matures %d), txid %s, staked %s SAL, %s SAL accrued so far")) % height - % (height + 21601) + % (height + stake_lock_period) % txid % print_money(burnt) % print_money(yield); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index b467125b6..afe811505 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -185,6 +185,7 @@ namespace cryptonote bool return_payment(const std::vector &args); bool burn(const std::vector &args); bool convert(const std::vector &args); + bool audit(const std::vector &args); bool stake(const std::vector &args); bool price_info(const std::vector &args); bool supply_info(const std::vector &args); diff --git a/src/version.cpp.in b/src/version.cpp.in index a5dd9d5ad..00e19f1e5 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.8.0" +#define DEF_SALVIUM_VERSION "0.9.0-rc9" #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 f65d38145..486019a2b 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); @@ -2198,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; @@ -2208,6 +2215,9 @@ 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; + + // 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; } if (m_multisig) @@ -2218,7 +2228,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"); @@ -2434,7 +2444,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 +2496,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 +2554,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 +2753,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 +2887,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 @@ -2887,13 +2897,14 @@ 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) { - // 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 +7955,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 +9741,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 +9764,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 +9919,11 @@ void wallet2::transfer_selected(const std::vector additional_tx_keys; crypto::secret_key multisig_tx_key_entropy; @@ -10230,7 +10276,7 @@ void wallet2::transfer_selected_rct(std::vector> 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="< wallet2::create_transactions_2(std::vector valid_public_keys_cache; @@ -10695,7 +10742,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 +11411,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 +11477,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 +11658,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(); @@ -11666,7 +11731,11 @@ std::vector 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; } @@ -12291,6 +12360,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 +12369,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 +12585,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 +12599,12 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt received += amount; } } + + // 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; + } } 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 +12858,9 @@ 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); + // 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.")); - + // concatenate all signature strings for (size_t i = 0; i < num_sigs; ++i) sig_str += @@ -13032,11 +13109,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 +14337,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 +14348,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 +14447,7 @@ size_t wallet2::import_outputs(const std::tupleget_db().add_block(std::make_pair(b, ""), 300000, 300000, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(b, ""), 300000, 300000, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi, abi); if (!bc->update_next_cumulative_weight_limit()) { fprintf(stderr, "Failed to update cumulative weight limit 1\n"); @@ -187,8 +190,9 @@ static void test(test_t t, uint64_t blocks) cryptonote::block b; b.major_version = HF_VERSION_2021_SCALING; b.minor_version = HF_VERSION_2021_SCALING; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; - bc->get_db().add_block(std::make_pair(std::move(b), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(std::move(b), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi, abi); if (!bc->update_next_cumulative_weight_limit()) { diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 3f752c316..919ce2e2e 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -91,8 +91,10 @@ namespace const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, + uint64_t audit_total, const cryptonote::network_type nettype, - cryptonote::yield_block_info& ybi + cryptonote::yield_block_info& ybi, + cryptonote::audit_block_info& abi ) override { blocks.push_back({blk, blk_hash}); @@ -178,7 +180,8 @@ static std::unique_ptr init_blockchain(const std: auto blk_hash = get_block_hash(*blk); oracle::asset_type_counts num_rct_outs_by_asset_type; cryptonote::yield_block_info ybi; - bdb->add_block(*blk, 1, 1, 1, 0, 0, num_rct_outs_by_asset_type, blk_hash, 0, 0, cryptonote::FAKECHAIN, ybi); + cryptonote::audit_block_info abi; + bdb->add_block(*blk, 1, 1, 1, 0, 0, num_rct_outs_by_asset_type, blk_hash, 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); } bool r = bap->blockchain.init(bdb, nettype, true, test_options, 2, nullptr); @@ -492,7 +495,8 @@ bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, std::unordered_map subaddresses; subaddresses[from.get_keys().m_account_address.m_spend_public_key] = {0,0}; cryptonote::origin_data od{3, crypto::null_pkey, 0}; - generate_key_image_helper(from.get_keys(), subaddresses, out_key, get_tx_pub_key_from_extra(*oi.p_tx), get_additional_tx_pub_keys_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img, hw::get_device(("default")), false, od); + rct::salvium_input_data_t sid; + generate_key_image_helper(from.get_keys(), subaddresses, out_key, get_tx_pub_key_from_extra(*oi.p_tx), get_additional_tx_pub_keys_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img, hw::get_device(("default")), false, od, sid); // lookup for this key image in the events vector BOOST_FOREACH(auto& tx_pair, mtx) { diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index e4ee63b30..6a4c7e5d5 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -246,6 +246,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector kLRkis; std::unordered_set used_L; const cryptonote::origin_data origin_tx_data{3,crypto::null_pkey, 0}; + rct::salvium_input_data_t sid; for (size_t tdidx = 0; tdidx < inputs; ++tdidx) { kLRkis.push_back(rct::multisig_kLRki()); @@ -254,13 +255,13 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector subaddresses; subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0}; auto& out_key = reinterpret_cast(src_entr.outputs[src_entr.real_output].second.dest); + rct::salvium_input_data_t sid; const cryptonote::origin_data od{3, crypto::null_pkey, src_entr.real_output}; - 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, hw::get_device(("default")), false, od); + 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, hw::get_device(("default")), false, od, sid); // put key image into tx input txin_to_key input_to_key; diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index f880d3798..2308bb84a 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -50,6 +50,7 @@ public: subaddresses[m_bob.get_keys().m_account_address.m_spend_public_key] = {0,0}; crypto::public_key out_key = boost::get(m_tx.vout[0].target).key; cryptonote::origin_data od{3,crypto::null_pkey,0}; - return cryptonote::generate_key_image_helper(m_bob.get_keys(), subaddresses, out_key, m_tx_pub_key, m_additional_tx_pub_keys, 0, in_ephemeral, ki, hw::get_device("default"), false, od); + rct::salvium_input_data_t sid; + return cryptonote::generate_key_image_helper(m_bob.get_keys(), subaddresses, out_key, m_tx_pub_key, m_additional_tx_pub_keys, 0, in_ephemeral, ki, hw::get_device("default"), false, od, sid); } }; diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index e82d79a5e..8809de8e6 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -274,9 +274,10 @@ TYPED_TEST(BlockchainDBTest, AddBlock) // no blocks have been added yet (because genesis has no parent). //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE); + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], cryptonote::FAKECHAIN, ybi)); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1], cryptonote::FAKECHAIN, ybi)); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], cryptonote::FAKECHAIN, ybi, abi)); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1], cryptonote::FAKECHAIN, ybi, abi)); block b; ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0].first))); @@ -289,7 +290,7 @@ TYPED_TEST(BlockchainDBTest, AddBlock) ASSERT_TRUE(compare_blocks(this->m_blocks[0].first, b)); // assert that we can't add the same block twice - ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], cryptonote::FAKECHAIN, ybi), TX_EXISTS); + ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], cryptonote::FAKECHAIN, ybi, abi), TX_EXISTS); for (auto& h : this->m_blocks[0].first.tx_hashes) { @@ -315,15 +316,16 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData) db_wtxn_guard guard(this->m_db); + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], cryptonote::FAKECHAIN, ybi)); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], cryptonote::FAKECHAIN, ybi, abi)); ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0)); ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0)); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1], cryptonote::FAKECHAIN, ybi)); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1], cryptonote::FAKECHAIN, ybi, abi)); ASSERT_EQ(t_diffs[1] - t_diffs[0], this->m_db->get_block_difficulty(1)); ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), this->m_db->get_block_hash_from_height(0)); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 4d39a9b47..6a42b7cdb 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -138,7 +138,8 @@ TEST(bulletproofs, multi_splitting) for (size_t i = 0; i < destinations.size(); ++i) destination_asset_types.push_back("SAL"); - rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, available, mixRing, amount_keys, index, outSk, rct_config, hw::get_device("default")); + rct::salvium_data_t salvium_data; + rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, available, mixRing, amount_keys, index, outSk, rct_config, hw::get_device("default"), salvium_data); ASSERT_TRUE(rct::verRctSimple(s)); for (size_t i = 0; i < n_outputs; ++i) { diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 4fe550778..c19960f99 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -57,8 +57,10 @@ public: , const crypto::hash& blk_hash , uint64_t slippage_total , uint64_t yield_total + , uint64_t audit_total , const cryptonote::network_type nettype , cryptonote::yield_block_info& ybi + , cryptonote::audit_block_info& abi ) override { blocks.push_back(blk); } @@ -103,6 +105,7 @@ TEST(major, Only) TestDB db; HardFork hf(db, 1, 0, 0, 0, 1, 0); // no voting oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; // v h t @@ -114,20 +117,20 @@ TEST(major, Only) ASSERT_FALSE(hf.add(mkblock(0, 2), 0)); ASSERT_FALSE(hf.add(mkblock(2, 2), 0)); ASSERT_TRUE(hf.add(mkblock(1, 2), 0)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); // block height 1, only version 1 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 1)); ASSERT_FALSE(hf.add(mkblock(2, 2), 1)); ASSERT_TRUE(hf.add(mkblock(1, 2), 1)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); // block height 2, only version 2 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 2)); ASSERT_FALSE(hf.add(mkblock(1, 2), 2)); ASSERT_FALSE(hf.add(mkblock(3, 2), 2)); ASSERT_TRUE(hf.add(mkblock(2, 2), 2)); - db.add_block(mkblock(2, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(2, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); } TEST(empty_hardforks, Success) @@ -135,6 +138,7 @@ TEST(empty_hardforks, Success) TestDB db; HardFork hf(db); oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; ASSERT_TRUE(hf.add_fork(1, 0, 0)); @@ -143,7 +147,7 @@ TEST(empty_hardforks, Success) ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); for (uint64_t h = 0; h <= 10; ++h) { - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } ASSERT_EQ(hf.get(0), 1); @@ -170,6 +174,7 @@ TEST(check_for_height, Success) TestDB db; HardFork hf(db, 1, 0, 0, 0, 1, 0); // no voting oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; ASSERT_TRUE(hf.add_fork(1, 0, 0)); @@ -179,14 +184,14 @@ TEST(check_for_height, Success) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_TRUE(hf.check_for_height(mkblock(1, 1), h)); ASSERT_FALSE(hf.check_for_height(mkblock(2, 2), h)); // block version is too high - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 10; ++h) { ASSERT_FALSE(hf.check_for_height(mkblock(1, 1), h)); // block version is too low ASSERT_TRUE(hf.check_for_height(mkblock(2, 2), h)); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -196,6 +201,7 @@ TEST(get, next_version) TestDB db; HardFork hf(db); oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; ASSERT_TRUE(hf.add_fork(1, 0, 0)); @@ -205,19 +211,19 @@ TEST(get, next_version) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_EQ(2, hf.get_next_version()); - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 9; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 10; h <= 15; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -251,6 +257,7 @@ TEST(steps_asap, Success) TestDB db; HardFork hf(db, 1,0,1,1,1); oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; // v h t @@ -261,7 +268,7 @@ TEST(steps_asap, Success) hf.init(); for (uint64_t h = 0; h < 10; ++h) { - db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -282,6 +289,7 @@ TEST(steps_1, Success) TestDB db; HardFork hf(db, 1,0,1,1,1); oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; ASSERT_TRUE(hf.add_fork(1, 0, 0)); @@ -290,7 +298,7 @@ TEST(steps_1, Success) hf.init(); for (uint64_t h = 0 ; h < 10; ++h) { - db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -305,6 +313,7 @@ TEST(reorganize, Same) TestDB db; HardFork hf(db, 1, 0, 1, 1, history, 100); oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; // v h t @@ -317,7 +326,7 @@ TEST(reorganize, Same) // index 0 1 2 3 4 5 6 7 8 9 static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 20; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -336,6 +345,7 @@ TEST(reorganize, Changed) TestDB db; HardFork hf(db, 1, 0, 1, 1, 4, 100); oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; // v h t @@ -350,7 +360,7 @@ TEST(reorganize, Changed) static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE (hf.add(db.get_block_from_height(h), h)); } @@ -370,7 +380,7 @@ TEST(reorganize, Changed) ASSERT_EQ(db.height(), 3); hf.reorganize_from_block_height(2); for (uint64_t h = 3; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ (ret, h < 15); } @@ -384,6 +394,7 @@ TEST(reorganize, Changed) TEST(voting, threshold) { oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; for (int threshold = 87; threshold <= 88; ++threshold) { TestDB db; @@ -396,7 +407,7 @@ TEST(voting, threshold) for (uint64_t h = 0; h <= 8; ++h) { uint8_t v = 1 + !!(h % 8); - db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); bool ret = hf.add(db.get_block_from_height(h), h); if (h >= 8 && threshold == 87) { // for threshold 87, we reach the treshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1 @@ -415,6 +426,7 @@ TEST(voting, threshold) TEST(voting, different_thresholds) { oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; for (int threshold = 87; threshold <= 88; ++threshold) { TestDB db; @@ -432,7 +444,7 @@ TEST(voting, different_thresholds) static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 }; for (uint64_t h = 0; h < sizeof(block_versions) / sizeof(block_versions[0]); ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ(ret, true); } @@ -447,6 +459,7 @@ TEST(voting, info) TestDB db; HardFork hf(db, 1, 0, 1, 1, 4, 50); // window size 4, default threshold 50% oracle::asset_type_counts num_rct_outs_by_asset_type; + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; // v h ts @@ -487,7 +500,7 @@ TEST(voting, info) ASSERT_EQ(expected_thresholds[h], threshold); ASSERT_EQ(4, voting); - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -551,8 +564,9 @@ TEST(reorganize, changed) do { \ cryptonote::block b = mkblock(hf, h, v); \ oracle::asset_type_counts num_rct_outs_by_asset_type; \ + cryptonote::audit_block_info abi; \ cryptonote::yield_block_info ybi; \ - db.add_block(b, 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, cryptonote::FAKECHAIN, ybi); \ + db.add_block(b, 0, 0, 0, 0, 0, num_rct_outs_by_asset_type, crypto::hash(), 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi); \ ASSERT_##a(hf.add(b, h)); \ } while(0) #define ADD_TRUE(v, h) ADD(v, h, TRUE) diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp index 0ba4a79b5..216e7ed75 100644 --- a/tests/unit_tests/long_term_block_weight.cpp +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -61,8 +61,10 @@ public: , const crypto::hash& blk_hash , uint64_t slippage_total , uint64_t yield_total + , uint64_t audit_total , const cryptonote::network_type nettype , cryptonote::yield_block_info& ybi + , cryptonote::audit_block_info& abi ) override { blocks.push_back({block_weight, long_term_block_weight}); } @@ -114,6 +116,7 @@ static uint32_t lcg() #define PREFIX_WINDOW(hf_version,window) \ cryptonote::BlockchainAndPool bap; \ cryptonote::Blockchain *bc = &bap.blockchain; \ + cryptonote::audit_block_info abi; \ cryptonote::yield_block_info ybi; \ struct get_test_options { \ const std::pair hard_forks[3]; \ @@ -146,7 +149,7 @@ TEST(long_term_block_weight, identical_before_fork) { size_t w = h < CRYPTONOTE_REWARD_BLOCKS_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } for (uint64_t h = 0; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) @@ -163,7 +166,7 @@ TEST(long_term_block_weight, identical_after_fork_before_long_term_window) { size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) @@ -180,7 +183,7 @@ TEST(long_term_block_weight, ceiling_at_30000000) { size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), 15000000); @@ -195,7 +198,7 @@ TEST(long_term_block_weight, multi_pop) { size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } @@ -207,7 +210,7 @@ TEST(long_term_block_weight, multi_pop) { size_t w = bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } @@ -229,7 +232,7 @@ TEST(long_term_block_weight, multiple_updates) { size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); @@ -253,7 +256,7 @@ TEST(long_term_block_weight, pop_invariant_max) { size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } @@ -281,7 +284,7 @@ TEST(long_term_block_weight, pop_invariant_max) { size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } @@ -303,7 +306,7 @@ TEST(long_term_block_weight, pop_invariant_random) uint32_t r = lcg(); size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); } @@ -338,7 +341,7 @@ TEST(long_term_block_weight, pop_invariant_random) uint32_t r = lcg(); size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); @@ -366,7 +369,7 @@ TEST(long_term_block_weight, long_growth_spike_and_drop) { size_t w = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); } ASSERT_EQ(long_term_effective_median_block_weight, 300000); @@ -378,7 +381,7 @@ TEST(long_term_block_weight, long_growth_spike_and_drop) float t = h / float(365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000); size_t w = 300000 + t * 30000; uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); } ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); @@ -389,7 +392,7 @@ TEST(long_term_block_weight, long_growth_spike_and_drop) { size_t w = bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); } ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); @@ -400,7 +403,7 @@ TEST(long_term_block_weight, long_growth_spike_and_drop) { size_t w = bc->get_current_cumulative_block_weight_median() * .25; uint64_t ltw = bc->get_next_long_term_block_weight(w); - bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}, cryptonote::FAKECHAIN, ybi, abi); ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); } ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 5ebe9cb2a..ca9f57e4d 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -433,6 +433,7 @@ TEST(cryptonote_protocol_handler, race_condition) const block_t &block, const stat::chain &stat ){ + cryptonote::audit_block_info abi; cryptonote::yield_block_info ybi; core.get_blockchain_storage().get_db().batch_start({}, {}); core.get_blockchain_storage().get_db().add_block( @@ -445,7 +446,8 @@ TEST(cryptonote_protocol_handler, race_condition) stat.reward, {}, cryptonote::FAKECHAIN, - ybi + ybi, + abi ); core.get_blockchain_storage().get_db().batch_stop(); }; diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 6dc2061e8..cff59de0c 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -351,7 +351,8 @@ TEST(ringct, range_proofs) destination_asset_types.push_back("SAL"); //compute rct data with mixin 3 - rctSig s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config, hw::get_device("default")); + rct::salvium_data_t salvium_data; + rctSig s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config, hw::get_device("default"), salvium_data); //verify rct data ASSERT_TRUE(verRctSimple(s)); @@ -368,7 +369,7 @@ TEST(ringct, range_proofs) //compute rct data with mixin 3 - s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config, hw::get_device("default")); + s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config, hw::get_device("default"), salvium_data); //verify rct data ASSERT_FALSE(verRctSimple(s)); @@ -422,7 +423,8 @@ TEST(ringct, range_proofs_with_fee) destination_asset_types.push_back("SAL"); //compute rct data with mixin 3 - rctSig s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 1, 3, rct_config, hw::get_device("default")); + rct::salvium_data_t salvium_data; + rctSig s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 1, 3, rct_config, hw::get_device("default"), salvium_data); //verify rct data ASSERT_TRUE(verRctSimple(s)); @@ -439,7 +441,7 @@ TEST(ringct, range_proofs_with_fee) //compute rct data with mixin 3 - s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 500, 3, rct_config, hw::get_device("default")); + s = genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 500, 3, rct_config, hw::get_device("default"), salvium_data); //verify rct data ASSERT_FALSE(verRctSimple(s)); @@ -504,7 +506,8 @@ TEST(ringct, simple) for (size_t i = 0; i < destinations.size(); ++i) destination_asset_types.push_back("SAL"); - rctSig s = genRctSimple(message, sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, amount_keys, txnfee, 2, rct_config, hw::get_device("default")); + rct::salvium_data_t salvium_data; + rctSig s = genRctSimple(message, sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, amount_keys, txnfee, 2, rct_config, hw::get_device("default"), salvium_data); //verify ring ct signature ASSERT_TRUE(verRctSimple(s)); @@ -572,7 +575,8 @@ static rct::rctSig make_sample_simple_rct_sig(int n_inputs, const uint64_t input for (size_t i = 0; i < destinations.size(); ++i) destination_asset_types.push_back("SAL"); - return genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, amount_keys, fee, 3, rct_config, hw::get_device("default")); + rct::salvium_data_t salvium_data; + return genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, amount_keys, fee, 3, rct_config, hw::get_device("default"), salvium_data); } static bool range_proof_test(bool expected_valid, diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 9331ec013..9bfb86310 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -613,7 +613,8 @@ TEST(Serialization, serializes_ringct_types) for (size_t i = 0; i < destinations.size(); ++i) destination_asset_types.push_back("SAL"); - s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config, hw::get_device("default")); + rct::salvium_data_t salvium_data; + s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config, hw::get_device("default"), salvium_data); ASSERT_FALSE(s0.p.MGs.empty()); ASSERT_TRUE(s0.p.CLSAGs.empty()); @@ -638,7 +639,7 @@ TEST(Serialization, serializes_ringct_types) ASSERT_EQ(bp0, bp1); const rct::RCTConfig rct_config_clsag{ rct::RangeProofPaddedBulletproof, 3 }; - s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config_clsag, hw::get_device("default")); + s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config_clsag, hw::get_device("default"), salvium_data); ASSERT_FALSE(s0.p.CLSAGs.empty()); ASSERT_TRUE(s0.p.MGs.empty());