Compare commits

...

19 Commits

Author SHA1 Message Date
Some Random Crypto Guy ab6e23c7b8 added index= param for auditing 2025-01-28 14:04:24 +00:00
Some Random Crypto Guy ac59a40c2d bumped RC version number 2025-01-28 12:34:17 +00:00
Some Random Crypto Guy 0bd1351750 Merge branch 'subaddress_handling' into develop 2025-01-28 12:33:22 +00:00
Some Random Crypto Guy 5fde8021dc disabled scan table for mining 2025-01-28 11:38:43 +00:00
Some Random Crypto Guy f9b946e929 fixed subaddress indexing on STAKE returns 2025-01-28 10:57:36 +00:00
Some Random Crypto Guy fee5676d8c partial implementation of subaddress handling 2025-01-27 21:22:29 +00:00
Some Random Crypto Guy 97685a0849 updated hard forks for audit testnet; tweaked PVK encryption to use hex format; bumped version number 2025-01-24 11:39:24 +00:00
Some Random Crypto Guy fa5ec5f12e addressed issue with protocol_tx payout validation; bumped version 2025-01-23 14:55:15 +00:00
Some Random Crypto Guy d2a71984cf improved logging; bumped version 2025-01-22 13:24:20 +00:00
Some Random Crypto Guy 9f1721261f Merge branch 'private_viewkey' into develop 2025-01-22 09:52:09 +00:00
Some Random Crypto Guy e68841d306 testing of scanner finding suspect stealth addresses 2025-01-22 09:51:51 +00:00
Some Random Crypto Guy 6571df4c76 added private viewkey support; upgraded scanner; bumped version 2025-01-21 13:49:53 +00:00
Some Random Crypto Guy 89180a6727 removed ability to BURN the SAL coins post-audit 2025-01-20 10:52:55 +00:00
Some Random Crypto Guy 2ccd388153 updated scanner 2025-01-18 22:40:44 +00:00
Some Random Crypto Guy 5757c67e17 updated provisional HF for audit; fixed wallet default asset type pre-fork; fixed circ_supply management (hopefully) 2025-01-17 11:55:34 +00:00
Some Random Crypto Guy 013011cde7 fixed BURN command 2025-01-16 21:09:49 +00:00
Some Random Crypto Guy 400ec099d1 working audit commands 2025-01-16 21:02:16 +00:00
Some Random Crypto Guy 60825ae753 interim checkin of AUDIT command 2025-01-16 12:21:35 +00:00
Some Random Crypto Guy 0c5d6e92e6 updated blockchain scanner to make sure bugs are fixed 2025-01-02 13:02:42 +00:00
36 changed files with 1542 additions and 460 deletions
+6 -6
View File
@@ -1,6 +1,6 @@
# Salvium Zero v0.7.2
# Salvium Zero v0.9.0-rc6
Copyright (c) 2023-2024, Salvium
Copyright (c) 2023-2025, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
Portions Copyright (c) 2012-2013 The Cryptonote developers.
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v0.7.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.7.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.7.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.7.0
git checkout v0.9.0
```
* If you are on a 64-bit system, run:
+9 -3
View File
@@ -269,6 +269,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
, const std::vector<std::pair<transaction, blobdata>>& 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<block, blobdata>& blck
}
std::map<std::string, int64_t> 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<block, blobdata>& 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<block, blobdata>& 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;
+7 -1
View File
@@ -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<std::pair<transaction, blobdata>>& 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<yield_tx_info>& 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<yield_tx_info>& yti_container) const = 0;
+321 -31
View File
@@ -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<yield_tx_info>& 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<yield_t
if (ret)
throw0(DB_ERROR(lmdb_error("Failed to enumerate yield TX info: ", ret).c_str()));
// Push result back into the container
// Get the data
yield_tx_info *p = (yield_tx_info*)v.mv_data;
// Push result back into the container
yti_container.emplace_back(*p);
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
yti_container.back().block_height = height;
}
TXN_POSTFIX_RDONLY();
@@ -866,7 +943,7 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_t
return 0;
}
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi)
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, 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)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -896,6 +973,18 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
int result = 0;
// Create the AUDIT_BLOCK_INFO instance for this block
CURSOR(audit_blocks)
abi.block_height = m_height;
abi.locked_coins_this_block = audit_total;
// Put the YBI into the table
MDB_val_set(key, m_height);
MDB_val_set(abi_val, abi);
result = mdb_cursor_put(m_cur_audit_blocks, &key, &abi_val, MDB_APPEND);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add ABI to db: ", result).c_str()));
CURSOR(yield_blocks)
yield_block_info ybi_matured, ybi_prev;
uint64_t yield_lock_period = cryptonote::get_config(nettype).STAKE_LOCK_PERIOD;
@@ -932,7 +1021,6 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
ybi.network_health_percentage = 100;
// Put the YBI into the table
MDB_val_set(key, m_height);
MDB_val_set(ybi_val, ybi);
result = mdb_cursor_put(m_cur_yield_blocks, &key, &ybi_val, MDB_APPEND);
if (result)
@@ -1004,6 +1092,7 @@ void BlockchainLMDB::remove_block()
CURSOR(blocks)
CURSOR(circ_supply_tally)
CURSOR(yield_blocks)
CURSOR(audit_blocks)
MDB_val_copy<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<std::string,uint64_t> 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<uint64_t>();
@@ -3527,12 +3786,13 @@ std::map<std::string,uint64_t> 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<block, blobdata>& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
const std::vector<std::pair<transaction, blobdata>>& txs, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi)
const std::vector<std::pair<transaction, blobdata>>& 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<block, blobdata>& 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
+19 -3
View File
@@ -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<std::pair<transaction, blobdata>>& 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<yield_tx_info>& 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<yield_tx_info>& 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;
+3
View File
@@ -297,6 +297,9 @@ monero_add_executable(blockchain_scanner
target_link_libraries(blockchain_scanner
PRIVATE
wallet
crypto
cncrypto
cryptonote_core
blockchain_db
version
@@ -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)
{
+85 -197
View File
@@ -29,6 +29,8 @@
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#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<uint64_t> arg_block_start = {"block-start", "start at block number", block_start};
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
const command_line::arg_descriptor<std::string> arg_delimiter = {"delimiter", "\"<string>\"", DELIM};
const command_line::arg_descriptor<std::string> arg_stake_mode = {"stake", "\"<string>\"", DEF_STAKE_MODE};
const command_line::arg_descriptor<bool> arg_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<uint8_t, std::pair<std::string, std::string>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
const uint64_t audit_lock_period = get_config(net_type).AUDIT_LOCK_PERIOD;
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<size_t, std::vector<std::string>> 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<crypto::public_key> 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++;
+12
View File
@@ -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;
@@ -358,6 +358,28 @@ namespace boost
a & x.z2;
}
template <class Archive>
inline void serialize(Archive &a, rct::salvium_input_data_t &x, const boost::serialization::version_type ver)
{
a & x.aR;
a & x.i;
}
template <class Archive>
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 <class Archive>
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)
@@ -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<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od)
bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid)
{
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);
@@ -315,13 +315,26 @@ namespace cryptonote
}
}
// HERE BE DRAGONS!!!
// SRCG: This FAILS under certain circumstances to find the correct subaddress for STAKE returns, and this BREAKS AUDITING
boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev);
CHECK_AND_ASSERT_MES(subaddr_recv_info, false, "key image helper: given output pubkey doesn't seem to belong to this address");
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);
/*
if (use_origin_data) {
// Try something a little special to find the subaddress index
crypto::key_derivation recv_derivation_od = AUTO_VAL_INIT(recv_derivation_od);
r = hwdev.generate_key_derivation(od.tx_pub_key, ack.m_view_secret_key, recv_derivation_od);
boost::optional<subaddress_receive_info> subaddr_recv_info_od = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation_od, additional_recv_derivations, od.output_index,hwdev);
}
*/
// LAND AHOY!!!
sid.aR = subaddr_recv_info->derivation;
sid.i = real_output_index;
return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev, use_origin_data, od, 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 +413,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 +428,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 +453,11 @@ namespace cryptonote
// 6. Create the key_image needed to be able to spend the output
hwdev.generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki);
// Update the SID to have the correct derivation for P_change as well
sid.aR_stake = derivation_P_change_tx;
sid.i_stake = od.output_index;
return true;
} else {
@@ -967,8 +991,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 +1008,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 +1284,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;
}
@@ -104,8 +104,8 @@ namespace cryptonote
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& 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<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od);
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<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid);
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, 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);
+25 -3
View File
@@ -31,6 +31,7 @@
#pragma once
#include <array>
#include <map>
#include <stdexcept>
#include <string>
#include <boost/uuid/uuid.hpp>
@@ -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
@@ -222,12 +226,17 @@
#define HF_VERSION_ENFORCE_FULL_PROOFS 4
#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 13
#define STAGENET_VERSION 1
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
@@ -278,10 +287,13 @@ namespace config
uint32_t const GENESIS_NONCE = 10000;
const std::map<uint8_t, std::pair<std::string, std::string>> 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";
@@ -352,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<std::string, 3> const ORACLE_URLS = {{"oracle.salvium.io:8443", "oracle.salvium.io:8443", "oracle.salvium.io:8443"}};
@@ -378,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<std::string, 3> const ORACLE_URLS = {{"oracle.salvium.io:8443", "oracle.salvium.io:8443", "oracle.salvium.io:8443"}};
@@ -413,7 +427,9 @@ namespace cryptonote
uint32_t const GENESIS_NONCE;
std::array<std::string, 3> 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<uint8_t, std::pair<std::string, std::string>> const AUDIT_HARD_FORKS;
std::string TREASURY_ADDRESS;
};
inline const config_t& get_config(network_type nettype)
@@ -431,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 = {
@@ -446,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 = {
@@ -461,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)
+223 -31
View File
@@ -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<uint64_t> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
//std::vector<uint64_t> absolute_offsets;
//m_db->get_output_id_from_asset_type_output_index(tx_in_to_key.asset_type, asset_offsets, absolute_offsets);
std::vector<uint64_t> asset_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
std::vector<uint64_t> absolute_offsets;
m_db->get_output_id_from_asset_type_output_index(tx_in_to_key.asset_type, asset_offsets, absolute_offsets);
std::vector<output_data_t> outputs;
bool found = false;
/*
auto it = m_scan_table.find(tx_prefix_hash);
if (it != m_scan_table.end())
{
@@ -171,6 +172,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke
found = true;
}
}
*/
if (!found)
{
@@ -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;
@@ -1501,6 +1503,8 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
case HF_VERSION_ENABLE_N_OUTS:
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;
@@ -1546,9 +1550,12 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
}
// if nothing is created by this TX - check no money is included
size_t vout_size = b.protocol_tx.vout.size();
CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs");
CHECK_AND_ASSERT_MES(vout_size != 0, true, "coinbase protocol transaction in the block has no outputs");
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;
}
// Can we have matured STAKE transactions yet?
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
@@ -1558,22 +1565,48 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
// Get the staking data for the block that matured this time
cryptonote::yield_block_info ybi_matured;
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
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) {
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;
}
}
// Iterate over the cached data for block yield, calculating the yield payouts due
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
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<std::pair<yield_tx_info, uint64_t>> 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<uint8_t, std::pair<std::string, std::string>> 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;
}
}
}
// Check we have the correct number of entries
CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting");
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<crypto::public_key> used_keys;
@@ -1609,19 +1642,43 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
// Add key to list of already-seen
used_keys.insert(out_key);
// check if there is entry in the yield payouts for this output
auto found = std::find_if(yield_payouts.begin(), yield_payouts.end(), [&](const std::pair<yield_tx_info, uint64_t>& p) {
// 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<yield_tx_info, uint64_t>& p) {
return p.first.return_address == out_key;
});
if (found == yield_payouts.end()) {
auto found_audit = std::find_if(audit_payouts.begin(), audit_payouts.end(), [&](const std::pair<yield_tx_info, uint64_t>& 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(out_asset_type == "SAL", false, "Incorrect output asset type for protocol TX");
CHECK_AND_ASSERT_MES(out_amount == found->second, false, "Incorrect output amount for protocol TX");
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
@@ -1912,6 +1969,50 @@ 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<std::pair<yield_tx_info, uint64_t>> 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<uint8_t, std::pair<std::string, std::string>> 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<std::string, std::string> 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;
@@ -3513,8 +3614,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));
@@ -3623,7 +3740,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());
@@ -3664,7 +3781,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)
{
@@ -3805,8 +3922,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)
{
@@ -3818,6 +3935,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;
@@ -3846,6 +3964,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));
@@ -3911,7 +4032,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!");
@@ -3944,10 +4065,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;
}
@@ -3966,6 +4090,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))
{
@@ -4348,6 +4473,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<std::pair<yield_tx_info, uint64_t>>& 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<cryptonote::yield_tx_info> 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<std::string> 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<std::pair<yield_tx_info, uint64_t>>& yield_container)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -4365,9 +4541,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<std::string> 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));
}
@@ -4955,8 +5139,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;
@@ -5784,7 +5970,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
{
const txin_to_key &in_to_key = boost::get < txin_to_key > (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<uint64_t> asset_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets);
//std::vector<uint64_t> absolute_offsets;
//m_db->get_output_id_from_asset_type_output_index(in_to_key.asset_type, asset_offsets, absolute_offsets);
// LAND AHOY!!!
for (const auto & offset : absolute_offsets)
offset_map[in_to_key.amount].push_back(offset);
+16
View File
@@ -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<std::pair<yield_tx_info, uint64_t>>& audit_payouts);
/**
* calculate the yield payouts
*
@@ -1167,6 +1174,15 @@ namespace cryptonote
*/
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& 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
*
+13 -2
View File
@@ -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
))
{
+153 -11
View File
@@ -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,6 +333,7 @@ namespace cryptonote
return true;
}
*/
//---------------------------------------------------------------
bool construct_protocol_tx(
const size_t height,
@@ -361,7 +363,22 @@ namespace cryptonote
// Create the TX output for this refund
tx_out out;
cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out);
std::string asset_type = "SAL";
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS)
asset_type = "SAL1";
cryptonote::set_tx_out(entry.amount_burnt, asset_type, 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;
std::string asset_type = "SAL";
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS)
asset_type = "SAL1";
cryptonote::set_tx_out(entry.amount_burnt, asset_type, 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);
}
@@ -435,6 +452,8 @@ namespace cryptonote
case HF_VERSION_ENABLE_N_OUTS:
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;
@@ -445,7 +464,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 {
@@ -491,6 +513,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::chacha_iv>();
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<char*>(&ephemeral_pk), sizeof(ephemeral_pk)) +
std::string(reinterpret_cast<char*>(&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<char>(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<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& 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<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &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();
@@ -602,6 +701,23 @@ namespace cryptonote
};
std::vector<input_generation_context_data> 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;
@@ -619,14 +735,22 @@ 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<const crypto::public_key&>(src_entr.outputs[src_entr.real_output].second.dest);
bool use_origin_data = (src_entr.origin_tx_data.tx_type != cryptonote::transaction_type::UNSET);
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;
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) )
{
@@ -652,6 +776,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{});
@@ -670,6 +802,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
@@ -705,6 +838,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);
@@ -725,11 +859,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("*****************************************************************************");
@@ -746,7 +888,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);
@@ -836,14 +978,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");
@@ -1031,11 +1173,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<txin_to_key>(tx.vin[i]).amount = 0;
}
}
for (size_t i = 0; i < tx.vout.size(); ++i) {
tx.vout[i].amount = 0;
}
@@ -1060,6 +1201,7 @@ namespace cryptonote
outSk,
rct_config,
hwdev,
salvium_data,
rct::sk2rct(x_change),
change_index,
key_yF
@@ -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<std::string, uint64_t> circ_supply, const oracle::pricing_record& pr, const uint8_t hf_version);
*/
//---------------------------------------------------------------
/**
* Construct the protocol_tx
@@ -171,6 +173,9 @@ namespace cryptonote
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& asset_type, const cryptonote::transaction_type& tx_type, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &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<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& 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<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &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<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& 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<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false);
+17
View File
@@ -167,6 +167,15 @@ namespace cryptonote
return false;
}
// Reject ALL TXs except miner + protocol for v5
if (version == HF_VERSION_SHUTDOWN_USER_TXS) {
if (tx.type != cryptonote::transaction_type::MINER && tx.type != cryptonote::transaction_type::PROTOCOL) {
LOG_PRINT_L1("User TXs are not permitted for v" + std::to_string(HF_VERSION_SHUTDOWN_USER_TXS));
tvc.m_verifivation_failed = true;
return false;
}
}
if(!check_inputs_types_supported(tx))
{
tvc.m_verifivation_failed = true;
@@ -1653,6 +1662,14 @@ 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());
if (!meta.matches(relay_category::legacy) && !(m_mine_stem_txes && meta.get_relay_method() == relay_method::stem))
@@ -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
+2 -1
View File
@@ -53,6 +53,7 @@ namespace cryptonote
BURN = 5,
STAKE = 6,
RETURN = 7,
MAX = 7
AUDIT = 8,
MAX = 8
};
}
+12
View File
@@ -43,6 +43,9 @@ 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 },
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
@@ -56,6 +59,15 @@ const hardfork_t testnet_hard_forks[] = {
// version 3 starts from block 500
{ 3, 500, 0, 1729518000 },
// version 4 (full proofs) starts from block 600
{ 4, 600, 0, 1734607000 },
// 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);
+3 -2
View File
@@ -92,7 +92,8 @@ namespace multisig
const std::vector<crypto::key_image> &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<crypto::key_image> used;
+2 -1
View File
@@ -67,6 +67,7 @@ namespace multisig
const std::vector<crypto::key_image> &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
+14 -12
View File
@@ -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;
+7 -7
View File
@@ -31,7 +31,7 @@
namespace oracle {
const std::vector<std::string> ASSET_TYPES = {"SAL", "VSD", "BURN"};
const std::vector<std::string> 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;
}
+79 -21
View File
@@ -1,5 +1,5 @@
// Copyright (c) 2016, Monero Research Labs
// Portions Copyright (c) 2023-2024, Salvium (author: SRCG)
// Portions Copyright (c) 2023-2025, Salvium (author: SRCG)
//
// Author: Shen Noether <shen.noether@gmx.com>
//
@@ -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<const BulletproofPlus*> 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<rv.mixRing[i].size(); ++n) {
if (ephemeral_pub == rct::rct2pk(rv.mixRing[i][n].dest)) {
found = true;
break;
}
}
CHECK_AND_ASSERT_THROW_MES(found, "Failed to match ephemeral public key provided in audit data");
// Verify the individual input commitment
CHECK_AND_ASSERT_THROW_MES(equalKeys(scalarmultH(d2h(rv.salvium_data.input_verification_data[i].amount)), pseudoOuts[i]), "Invalid pseudoOuts entry");
}
}
return true;
}
// we can get deep throws from ge_frombytes_vartime if input isn't valid
@@ -1728,7 +1786,7 @@ namespace rct {
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs);
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
@@ -1752,14 +1810,14 @@ namespace rct {
}
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) {
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, "decodeRct called on non simple rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs);
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus || rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumOne);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
+2
View File
@@ -149,6 +149,7 @@ namespace rct {
unsigned int mixin,
const RCTConfig &rct_config,
hw::device &hwdev,
const rct::salvium_data_t &salvium_data,
const key &x_change = rct::zero(),
const size_t change_index = 0,
const key &key_yF = rct::zero()
@@ -169,6 +170,7 @@ namespace rct {
ctkeyV &outSk,
const RCTConfig &rct_config,
hw::device &hwdev,
const rct::salvium_data_t &salvium_data,
const key &x_change = rct::zero(),
const size_t change_index = 0,
const key &key_yF = rct::zero()
+3
View File
@@ -198,6 +198,7 @@ namespace rct {
case RCTTypeCLSAG:
case RCTTypeBulletproofPlus:
case RCTTypeFullProofs:
case RCTTypeSalviumOne:
return true;
default:
return false;
@@ -223,6 +224,7 @@ namespace rct {
{
case RCTTypeBulletproofPlus:
case RCTTypeFullProofs:
case RCTTypeSalviumOne:
return true;
default:
return false;
@@ -248,6 +250,7 @@ namespace rct {
case RCTTypeCLSAG:
case RCTTypeBulletproofPlus:
case RCTTypeFullProofs:
case RCTTypeSalviumOne:
return true;
default:
return false;
+78 -18
View File
@@ -55,6 +55,7 @@ extern "C" {
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
#include "cryptonote_protocol/enums.h"
//Define this flag when debugging to get additional info on the console
#ifdef DBG
@@ -316,7 +317,8 @@ namespace rct {
RCTTypeBulletproof2 = 4,
RCTTypeCLSAG = 5,
RCTTypeBulletproofPlus = 6,
RCTTypeFullProofs = 7
RCTTypeFullProofs = 7,
RCTTypeSalviumOne = 8
};
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
struct RCTConfig {
@@ -329,6 +331,50 @@ namespace rct {
VARINT_FIELD(bp_version)
END_SERIALIZE()
};
enum SalviumDataType { SalviumNormal=0, SalviumAudit=1 };
struct salvium_input_data_t {
crypto::key_derivation aR;
xmr_amount amount;
size_t i;
uint8_t origin_tx_type;
crypto::key_derivation aR_stake;
size_t i_stake;
BEGIN_SERIALIZE_OBJECT()
FIELD(aR)
VARINT_FIELD(amount)
VARINT_FIELD(i)
VARINT_FIELD(origin_tx_type)
if (origin_tx_type != cryptonote::transaction_type::UNSET) {
FIELD(aR_stake)
FIELD(i_stake)
}
END_SERIALIZE()
};
struct salvium_data_t {
uint8_t salvium_data_type; // flag to indicate what type of data is valid
zk_proof pr_proof; // p_r
zk_proof sa_proof; // spend authority proof
zk_proof cz_proof; // change is zero proof
std::vector<salvium_input_data_t> 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<bool W, template <bool> 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 */
+77 -6
View File
@@ -1173,9 +1173,11 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& 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<epee::byte_stream>& 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<epee::byte_stream>& 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<epee::byte_stream>& dest, const cryptonote::rpc::DaemonInfo& info)
{
dest.StartObject();
+6
View File
@@ -310,6 +310,12 @@ void fromJsonValue(const rapidjson::Value& val, rct::clsag& sig);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::zk_proof& p);
void fromJsonValue(const rapidjson::Value& val, rct::zk_proof& p);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& 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<epee::byte_stream>& dest, const rct::salvium_data_t& salvium_data);
void fromJsonValue(const rapidjson::Value& val, rct::salvium_data_t& salvium_data);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::DaemonInfo& info);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info);
+116 -36
View File
@@ -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 <amount> <asset_type>");
const char* USAGE_CONVERT("convert <source_amount> <source_asset> <dest_asset> [<slippage_limit>]");
const char* USAGE_STAKE("stake <amount>");
const char* USAGE_AUDIT("audit");
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<std::string> &args/* = std::vector<st
message_writer() << tr("\"burn <amount> <asset_type>\" - destroy coins forever.");
message_writer() << tr("\"convert <amount> <source_asset> <dest_asset> [<slippage_limit>]\" - convert between coin types.");
message_writer() << tr("\"stake <amount>\" - stake SAL for 30 days to earn yield.");
message_writer() << tr("\"audit\" - audit your wallet.");
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 <amount> 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 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,24 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid,
if (tx.type == cryptonote::transaction_type::BURN) {
message_writer(console_color_yellow, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
tr("burnt ") << print_money(tx.amount_burnt) << " " << asset_type;
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
tr("burnt ") << print_money(tx.amount_burnt) << " " << asset_type;
} else if (tx.type == cryptonote::transaction_type::CONVERT) {
message_writer(console_color_blue, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
tr("converting ") << print_money(tx.amount_burnt) << " " << asset_type;
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
tr("converting ") << print_money(tx.amount_burnt) << " " << asset_type;
} else if (tx.type == cryptonote::transaction_type::STAKE) {
message_writer(console_color_cyan, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
tr("staked ") << print_money(tx.amount_burnt) << " " << asset_type;
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
tr("staked ") << print_money(tx.amount_burnt) << " " << asset_type;
} else if (tx.type == cryptonote::transaction_type::AUDIT) {
message_writer(console_color_yellow, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
tr("audited ") << print_money(tx.amount_burnt) << " " << asset_type;
}
message_writer(asset_type == "SAL" ? console_color_green : console_color_blue, false) << "\r" <<
@@ -6720,6 +6738,13 @@ bool simple_wallet::transfer_main(
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<std::string> local_args = args_;
std::set<uint32_t> subaddr_indices;
@@ -6728,7 +6753,9 @@ bool simple_wallet::transfer_main(
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
return false;
local_args.erase(local_args.begin());
}
if (transfer_type == Audit)
while (subaddr_indices.size() > 1)
subaddr_indices.erase(std::prev(subaddr_indices.end())); }
uint32_t priority = m_wallet->get_default_priority();
if (local_args.size() > 0 && parse_priority(local_args[0], priority))
@@ -6766,7 +6793,7 @@ bool simple_wallet::transfer_main(
return false;
}
const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1;
const size_t min_args = (transfer_type == Audit) ? 0 : (transfer_type == TransferLocked) ? 2 : 1;
if(local_args.size() < min_args)
{
fail_msg_writer() << tr("wrong number of arguments");
@@ -6944,30 +6971,35 @@ bool simple_wallet::transfer_main(
std::vector<tools::wallet2::pending_tx> ptx_vector;
uint64_t unlock_block = 0;
std::string err;
cryptonote::address_parse_info info;
switch (transfer_type)
{
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 +7101,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 +7261,13 @@ bool simple_wallet::transfer(const std::vector<std::string> &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 +7302,7 @@ bool simple_wallet::locked_transfer(const std::vector<std::string> &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();
}
@@ -8056,6 +8094,14 @@ bool simple_wallet::return_payment(const std::vector<std::string> &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 +8246,7 @@ bool simple_wallet::burn(const std::vector<std::string> &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 +8310,7 @@ bool simple_wallet::convert(const std::vector<std::string> &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 +8320,7 @@ bool simple_wallet::convert(const std::vector<std::string> &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 +8344,36 @@ bool simple_wallet::convert(const std::vector<std::string> &args_)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::audit(const std::vector<std::string> &args_)
{
// TODO: add locked versions
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;
}
std::vector<std::string> local_args = args_;
const std::map<uint8_t, std::pair<std::string, std::string>> audit_hard_forks = get_config(m_wallet->nettype()).AUDIT_HARD_FORKS;
const uint8_t hf_version = m_wallet->get_current_hard_fork();
if (audit_hard_forks.find(hf_version) != audit_hard_forks.end()) {
// Get the asset types
const std::pair<std::string, std::string> audit_asset_types = audit_hard_forks.at(hf_version);
transfer_main(Audit, audit_asset_types.first, audit_asset_types.first, local_args, false);
} else {
fail_msg_writer() << tr("Audit command is not available at this time.");
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::stake(const std::vector<std::string> &args_)
{
// TODO: add locked versions
@@ -8316,9 +8392,13 @@ bool simple_wallet::stake(const std::vector<std::string> &args_)
std::vector<std::string> local_args;
local_args.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account,0}));
local_args.insert(local_args.end(), args_.begin(), args_.end());
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<std::string> &args) {
+1
View File
@@ -185,6 +185,7 @@ namespace cryptonote
bool return_payment(const std::vector<std::string> &args);
bool burn(const std::vector<std::string> &args);
bool convert(const std::vector<std::string> &args);
bool audit(const std::vector<std::string> &args);
bool stake(const std::vector<std::string> &args);
bool price_info(const std::vector<std::string> &args);
bool supply_info(const std::vector<std::string> &args);
+1 -1
View File
@@ -1,5 +1,5 @@
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
#define DEF_SALVIUM_VERSION "0.7.2"
#define DEF_SALVIUM_VERSION "0.9.0-rc6"
#define DEF_MONERO_VERSION_TAG "release"
#define DEF_MONERO_VERSION "0.18.3.3"
#define DEF_MONERO_RELEASE_NAME "Zero"
+131 -41
View File
@@ -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,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
od.tx_pub_key = get_tx_pub_key_from_extra(td_origin.m_tx);
od.output_index = td_origin.m_internal_output_index;
od.tx_type = td_origin.m_tx.type;
// HERE BE DRAGONS!!!
// SRCG: this is necessary to be able to receive protocol_tx outputs to the correct wallet subaddress
tx_scan_info.received->index = td_origin.m_subaddr_index;
// LAND AHOY!!!
}
if (m_multisig)
@@ -2218,7 +2230,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
}
else
{
bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device(), use_od, od);
bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device(), use_od, od, sid);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != output_public_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
@@ -2425,7 +2437,7 @@ bool wallet2::get_yield_summary_info(uint64_t &total_burnt,
for (size_t idx = m_transfers.size()-1; idx>0; --idx) {
const tools::wallet2::transfer_details& td = m_transfers[idx];
//if (td.m_block_height < ybi_data[0].block_height) break;
if (td.m_tx.type == cryptonote::transaction_type::STAKE) {
if (td.m_tx.type == cryptonote::transaction_type::STAKE || td.m_tx.type == cryptonote::transaction_type::AUDIT) {
if (map_payouts.count(idx)) {
payouts.push_back(std::make_tuple(td.m_block_height, epee::string_tools::pod_to_hex(td.m_txid), td.m_tx.amount_burnt, m_transfers[map_payouts[idx]].m_amount - td.m_tx.amount_burnt));
} else {
@@ -2434,7 +2446,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 +2498,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 +2556,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 +2755,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 +2889,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 +2899,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 +7957,14 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
// SRCG: Calculate the correct uniqueness value here
assert(false);
cryptonote::origin_data origin_tx_data;
rct::salvium_input_data_t sid;
// if this output is back to this wallet, we can calculate its key image already
if (!is_out_to_acc_precomp(m_subaddresses, output_public_key, derivation, additional_derivations, i, hwdev, get_output_view_tag(tx.vout[i])))
continue;
crypto::key_image ki;
cryptonote::keypair in_ephemeral;
if (generate_key_image_helper(keys, m_subaddresses, output_public_key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev, false, origin_tx_data))
if (generate_key_image_helper(keys, m_subaddresses, output_public_key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev, false, origin_tx_data, sid))
signed_txes.tx_key_images[output_public_key] = ki;
else
MERROR("Failed to calculate key image");
@@ -9463,11 +9477,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
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 +9743,11 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
"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 +9766,16 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
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 +9921,11 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
"real output not found");
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
// HERE BE DRAGONS!!!
// SRCG: ring tweak to indexed per asset_type - DO NOT COMMIT UNTIL IT IS ALL WORKING
//real_oe.first = td.m_global_output_index;
real_oe.first = td.m_asset_type_output_index;
// LAND AHOY!!!
real_oe.second.dest = rct::pk2rct(td.get_public_key());
real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
*it_to_replace = real_oe;
@@ -10101,9 +10130,13 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee);
uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
uint32_t subaddr_index = m_transfers[*selected_transfers.begin()].m_subaddr_index.minor;
for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) {
THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
if (tx_type == cryptonote::transaction_type::AUDIT) {
THROW_WALLET_EXCEPTION_IF(subaddr_index != m_transfers[*i].m_subaddr_index.minor, error::wallet_internal_error, "the AUDIT tx uses funds from multiple subaddresses");
}
}
if (outs.empty())
get_outs(outs, selected_transfers, fake_outputs_count, all_rct, valid_public_keys_cache); // may throw
@@ -10152,13 +10185,21 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
//paste real transaction to the random index
auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
{
return a.first == td.m_global_output_index;
// HERE BE DRAGONS!!!
// SRCG: ring tweak to indexed per asset_type - DO NOT COMMIT UNTIL IT IS ALL WORKING
//return a.first == td.m_global_output_index;
return a.first == td.m_asset_type_output_index;
// LAND AHOY!!!
});
THROW_WALLET_EXCEPTION_IF(it_to_replace == src.outputs.end(), error::wallet_internal_error,
"real output not found");
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
// HERE BE DRAGONS!!!
// SRCG: ring tweak to indexed per asset_type - DO NOT COMMIT UNTIL IT IS ALL WORKING
//real_oe.first = td.m_global_output_index;
real_oe.first = td.m_asset_type_output_index;
// LAND AHOY!!!
real_oe.second.dest = rct::pk2rct(td.get_public_key());
real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
*it_to_replace = real_oe;
@@ -10182,11 +10223,20 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
change_dts.amount = found_money - needed_money;
change_dts.asset_type = source_asset;
change_dts.addr = get_subaddress({subaddr_account, 0});
change_dts.is_subaddress = subaddr_account != 0;
change_dts.addr = get_subaddress({subaddr_account, subaddr_index});
change_dts.is_subaddress = subaddr_account != 0 || subaddr_index != 0;
change_dts.is_change = true;
splitted_dsts.push_back(change_dts);
account_keys a_keys = m_account.get_keys();
// HERE BE DRAGONS!!!
// SRCG: add support for auditing of subaddresses
if (tx_type == cryptonote::transaction_type::AUDIT && (subaddr_account != 0 || subaddr_index != 0)) {
// Overwrite the public spend key and view key
a_keys.m_account_address = get_subaddress({subaddr_account, subaddr_index});
}
// LAND AHOY!!!
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
crypto::secret_key multisig_tx_key_entropy;
@@ -10230,7 +10280,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
std::vector<std::pair<std::string, std::string>> circ_amounts;
THROW_WALLET_EXCEPTION_IF(!get_circulating_supply(circ_amounts), error::wallet_internal_error, "Failed to get circulating supply");
// make a normal tx
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
bool r = cryptonote::construct_tx_and_get_tx_key(a_keys/*m_account.get_keys()*/, m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
}
@@ -10652,7 +10702,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
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);
std::unordered_set<crypto::public_key> valid_public_keys_cache;
@@ -10695,7 +10746,18 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
//THROW_WALLET_EXCEPTION_IF(!get_circulating_supply(circ_amounts), error::wallet_internal_error, "Failed to get circulating supply");
break;
case transaction_type::STAKE:
THROW_WALLET_EXCEPTION_IF(dest_asset != "SAL", error::wallet_internal_error, "Yield TX must specify 'SAL' destination asset type");
if (use_salviumone_proofs) {
THROW_WALLET_EXCEPTION_IF(source_asset != "SAL1", error::wallet_internal_error, "STAKE TX must specify 'SAL1' source asset type");
THROW_WALLET_EXCEPTION_IF(dest_asset != "SAL1", error::wallet_internal_error, "STAKE TX must specify 'SAL1' destination asset type");
} else {
THROW_WALLET_EXCEPTION_IF(source_asset != "SAL", error::wallet_internal_error, "STAKE TX must specify 'SAL' source asset type");
THROW_WALLET_EXCEPTION_IF(dest_asset != "SAL", error::wallet_internal_error, "STAKE TX must specify 'SAL' destination asset type");
}
THROW_WALLET_EXCEPTION_IF(subaddr_account != 0, error::wallet_internal_error, "Staking is only permitted from main account, not secondary accounts");
break;
case transaction_type::AUDIT:
THROW_WALLET_EXCEPTION_IF(source_asset != "SAL", error::wallet_internal_error, "AUDIT TX must specify 'SAL' source asset type");
THROW_WALLET_EXCEPTION_IF(dest_asset != "SAL", error::wallet_internal_error, "AUDIT TX must specify 'SAL' destination asset type");
THROW_WALLET_EXCEPTION_IF(subaddr_account != 0, error::wallet_internal_error, "Staking is only permitted from main account, not secondary accounts");
break;
default:
@@ -11327,6 +11389,8 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &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 +11415,15 @@ std::vector<wallet2::pending_tx> 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 +11481,7 @@ std::vector<wallet2::pending_tx> 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::pending_tx> 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<uint8_t>& extra)
@@ -11594,7 +11662,8 @@ std::vector<wallet2::pending_tx> 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 +11735,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// SRCG: should the subaddress be forced to TRUE for _RETURN_ TXs and FALSE for all others?!?!?
// add N - 1 outputs for correct initial fee estimation
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i) {
tx.dsts.push_back(tx_destination_entry(1, address, tx_type == cryptonote::transaction_type::RETURN, tx_type == cryptonote::transaction_type::RETURN));
if (tx_type == cryptonote::transaction_type::AUDIT) {
tx.dsts.push_back(tx_destination_entry(1, address, tx_type == cryptonote::transaction_type::RETURN, tx_type == cryptonote::transaction_type::RETURN));
} else {
tx.dsts.push_back(tx_destination_entry(1, address, tx_type == cryptonote::transaction_type::RETURN, tx_type == cryptonote::transaction_type::RETURN));
}
tx.dsts.back().asset_type = asset_type;
}
@@ -12291,6 +12364,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 +12373,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
const std::vector<crypto::public_key> 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 +12589,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 +12603,14 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
received += amount;
}
}
// HERE BE DRAGONS!!!
// SRCG: if this returns 0 received, but it's an AUDIT TX, then that is EXPECTED
bool audit = (tx.rct_signatures.type == rct::RCTTypeSalviumOne && tx.rct_signatures.salvium_data.salvium_data_type == rct::SalviumAudit);
if (audit && received == 0) {
received += tx.amount_burnt;
}
// LAND AHOY!!!
}
void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
@@ -12782,8 +12864,11 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
uint64_t received;
check_tx_key_helper(tx, derivation, additional_derivations, address, received);
// HERE BE DRAGONS!!!
// SRCG: if this returns 0 received, but it's an AUDIT TX, then that is EXPECTED
THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx."));
// LAND AHOY!!!
// concatenate all signature strings
for (size_t i = 0; i < num_sigs; ++i)
sig_str +=
@@ -13032,11 +13117,12 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
// Populate this struct if you want to make use of "get_reserve_proof()" for Salvium!!!
assert(false);
origin_data od;
rct::salvium_input_data_t sid;
// derive ephemeral secret key
crypto::key_image ki;
cryptonote::keypair ephemeral;
const bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, td.get_public_key(), tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, ephemeral, ki, m_account.get_device(), false, od);
const bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, td.get_public_key(), tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, 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(ephemeral.pub != td.get_public_key(), error::wallet_internal_error, "Derived public key doesn't agree with the stored one");
@@ -13209,7 +13295,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::secret_key shared_secret;
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), 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(shared_secret), 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);
amount = rct::h2d(ecdh_info.amount);
}
total += amount;
@@ -13664,11 +13750,12 @@ std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>
// 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 +14345,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<std::string>(i + offset));
crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
@@ -14268,7 +14356,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 +14455,7 @@ size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<
// 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;
// the hot wallet wouldn't have known about key images (except if we already exported them)
cryptonote::keypair in_ephemeral;
@@ -14376,7 +14465,7 @@ size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<
const crypto::public_key& out_key = etd.m_pubkey;
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);
@@ -14585,6 +14674,7 @@ crypto::key_image wallet2::get_multisig_composite_key_image(size_t n) const
// SRCG: work out if we have origin data to use
bool use_origin_data = false;
cryptonote::origin_data origin_tx_data;
rct::salvium_input_data_t sid;
if (td.m_td_origin_idx != (uint64_t)-1) {
// Flag to indicate this is a TX that uses a return_address
@@ -14595,7 +14685,7 @@ crypto::key_image wallet2::get_multisig_composite_key_image(size_t n) const
use_origin_data = true;
}
bool r = multisig::generate_multisig_composite_key_image(get_account().get_keys(), m_subaddresses, td.get_public_key(), tx_key, additional_tx_keys, td.m_internal_output_index, pkis, ki, use_origin_data, origin_tx_data);
bool r = multisig::generate_multisig_composite_key_image(get_account().get_keys(), m_subaddresses, td.get_public_key(), tx_key, additional_tx_keys, td.m_internal_output_index, pkis, ki, use_origin_data, origin_tx_data, sid);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
return ki;
}