From 7f6b8daa497c1606c6f3c2764196e6000bdc8949 Mon Sep 17 00:00:00 2001 From: Some Random Crypto Guy Date: Thu, 9 Nov 2023 18:53:09 +0000 Subject: [PATCH] placeholder checkin because of so many changes having been made - let's use github as a backup site --- README.md | 2 +- src/blockchain_db/blockchain_db.h | 11 +- src/blockchain_db/lmdb/db_lmdb.cpp | 14 +- src/blockchain_db/lmdb/db_lmdb.h | 2 +- src/blockchain_db/testdb.h | 2 +- src/cryptonote_basic/cryptonote_basic.h | 32 ++- .../cryptonote_boost_serialization.h | 16 +- .../cryptonote_format_utils.cpp | 30 +++ .../cryptonote_format_utils.h | 3 + src/cryptonote_config.h | 4 +- src/cryptonote_core/blockchain.cpp | 63 ++++-- src/cryptonote_core/cryptonote_core.cpp | 7 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 211 ++++++++++++++++-- src/cryptonote_core/cryptonote_tx_utils.h | 26 ++- src/cryptonote_core/tx_pool.cpp | 40 +++- src/cryptonote_core/tx_pool.h | 7 +- src/ringct/rctSigs.cpp | 143 ++++++------ src/ringct/rctSigs.h | 5 +- src/rpc/core_rpc_server.cpp | 4 +- src/simplewallet/simplewallet.cpp | 18 +- src/wallet/wallet2.cpp | 4 +- tests/performance_tests/check_tx_signature.h | 7 +- tests/unit_tests/ringct.cpp | 3 +- 23 files changed, 484 insertions(+), 170 deletions(-) diff --git a/README.md b/README.md index b4ac8c4..fde0f2e 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# Fulmo +# Welcome to Fulmo \ No newline at end of file diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index c8da6ff..c6005b3 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -157,13 +157,18 @@ struct txpool_tx_meta_t { crypto::hash max_used_block_id; crypto::hash last_failed_id; + crypto::public_key destination_address; uint64_t weight; uint64_t fee; + uint64_t amount_burnt; + uint64_t amount_slippage_limit; uint64_t max_used_block_height; uint64_t last_failed_height; uint64_t receive_time; uint64_t last_relayed_time; //!< If received over i2p/tor, randomized forward time. If Dandelion++stem, randomized embargo time. Otherwise, last relayed timestamp - // 112 bytes + uint32_t source_asset_id; + uint32_t destination_asset_id; + // 168 bytes uint8_t kept_by_block; uint8_t relayed; uint8_t do_not_relay; @@ -174,7 +179,7 @@ struct txpool_tx_meta_t uint8_t is_forwarding: 1; uint8_t bf_padding: 3; - uint8_t padding[76]; // till 192 bytes + uint8_t padding[20]; // till 192 bytes void set_relay_method(relay_method method) noexcept; relay_method get_relay_method() const noexcept; @@ -1170,7 +1175,7 @@ public: * * @return the current circulating supply tally values */ - virtual std::vector> get_circulating_supply() const = 0; + virtual std::map get_circulating_supply() const = 0; /** diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index e37b40c..9f0d84f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1055,7 +1055,8 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons cs.source_currency_type = std::find(oracle::ASSET_TYPES.begin(), oracle::ASSET_TYPES.end(), strSource) - oracle::ASSET_TYPES.begin(); cs.dest_currency_type = std::find(oracle::ASSET_TYPES.begin(), oracle::ASSET_TYPES.end(), strDest) - oracle::ASSET_TYPES.begin(); cs.amount_burnt = tx.amount_burnt; - cs.amount_minted = tx.amount_minted; + // SRCG - need to work out how to populate this + //cs.amount_minted = tx.amount_minted; MDB_val_set(val_circ_supply, cs); result = mdb_cursor_put(m_cur_circ_supply, &val_tx_id, &val_circ_supply, MDB_APPEND); @@ -1164,7 +1165,8 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const cs.tx_hash = tx_hash; //cs.pricing_record_height = tx.pricing_record_height; cs.amount_burnt = tx.amount_burnt; - cs.amount_minted = tx.amount_minted; + // SRCG - need to work out how to populate this + //cs.amount_minted = tx.amount_minted; cs.source_currency_type = std::find(oracle::ASSET_TYPES.begin(), oracle::ASSET_TYPES.end(), strSource) - oracle::ASSET_TYPES.begin(); cs.dest_currency_type = std::find(oracle::ASSET_TYPES.begin(), oracle::ASSET_TYPES.end(), strDest) - oracle::ASSET_TYPES.begin(); @@ -3183,10 +3185,10 @@ uint64_t BlockchainLMDB::height() const return db_stats.ms_entries; } -std::vector> BlockchainLMDB::get_circulating_supply() const +std::map BlockchainLMDB::get_circulating_supply() const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); - std::vector> circulating_supply; + std::map circulating_supply; uint64_t m_height = height(); if (m_height == 0) { return circulating_supply; @@ -3225,14 +3227,14 @@ std::vector> BlockchainLMDB::get_circulating amount += m_coinbase; } - circulating_supply.emplace_back(std::pair{currency_label, amount.str()}); + circulating_supply[currency_label] = amount.convert_to(); } TXN_POSTFIX_RDONLY(); // NEAC: check for empty supply tally - only happens prior to first conversion on chain if (circulating_supply.empty()) { - circulating_supply.emplace_back(std::pair{"FULM", std::to_string(m_coinbase)}); + circulating_supply["FULM"] = m_coinbase; } return circulating_supply; } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index a91720d..e7ed1c2 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -262,7 +262,7 @@ public: virtual uint64_t height() const; - virtual std::vector> get_circulating_supply() const; + virtual std::map get_circulating_supply() const; virtual bool tx_exists(const crypto::hash& h) const; virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 99d1686..c86fa32 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -170,7 +170,7 @@ public: virtual void drop_alt_blocks() override {} virtual bool for_all_alt_blocks(std::function f, bool include_blob = false) const override { return true; } - virtual std::vector> get_circulating_supply() const override { return std::vector>(); } + virtual std::map get_circulating_supply() const override { return std::map(); } virtual void get_output_id_from_asset_type_output_index(const std::string asset_type, const std::vector &asset_type_output_indices, std::vector &output_indices) const override { } virtual uint64_t get_output_id_from_asset_type_output_index(const std::string asset_type, const uint64_t &asset_type_output_index) const override { return 0; }; }; diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 849c99b..30037fd 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -51,6 +51,7 @@ #include "ringct/rctTypes.h" #include "device/device.hpp" #include "cryptonote_basic/fwd.h" +#include "cryptonote_protocol/enums.h" #include "oracle/pricing_record.h" namespace cryptonote @@ -196,13 +197,18 @@ namespace cryptonote std::vector vout; //extra std::vector extra; - // Block height of PR to use - //uint64_t pricing_record_height; + // TX type + uint8_t type; + // Destination address (encrypted) + crypto::public_key destination_address; + // Source asset type + std::string source_asset_type; + // Destination asset type (this is only necessary for CONVERT transactions) + std::string destination_asset_type; // Circulating supply information uint64_t amount_burnt; - uint64_t amount_minted; - // Slippage tracking - uint64_t amount_slippage; + // Slippage limit + uint64_t amount_slippage_limit; BEGIN_SERIALIZE() VARINT_FIELD(version) @@ -211,10 +217,12 @@ namespace cryptonote FIELD(vin) FIELD(vout) FIELD(extra) - //VARINT_FIELD(pricing_record_height) + VARINT_FIELD(type) + FIELD(destination_address) + FIELD(source_asset_type) + FIELD(destination_asset_type) VARINT_FIELD(amount_burnt) - VARINT_FIELD(amount_minted) - VARINT_FIELD(amount_slippage) + VARINT_FIELD(amount_slippage_limit) END_SERIALIZE() public: @@ -226,10 +234,12 @@ namespace cryptonote vin.clear(); vout.clear(); extra.clear(); - //pricing_record_height = 0; + type = static_cast(cryptonote::transaction_type::TRANSFER); + destination_address = crypto::null_pkey; + source_asset_type.clear(); + destination_asset_type.clear(); amount_burnt = 0; - amount_minted = 0; - amount_slippage = 0; + amount_slippage_limit = 0; } }; diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index a32d425..ecd2784 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -166,10 +166,12 @@ namespace boost a & x.vin; a & x.vout; a & x.extra; - //a & x.pricing_record_height; + a & x.type; + a & x.destination_address; + a & x.source_asset_type; + a & x.destination_asset_type; a & x.amount_burnt; - a & x.amount_minted; - a & x.amount_slippage; + a & x.amount_slippage_limit; } template @@ -180,10 +182,12 @@ namespace boost a & x.vin; a & x.vout; a & x.extra; - //a & x.pricing_record_height; + a & x.type; + a & x.destination_address; + a & x.source_asset_type; + a & x.destination_asset_type; a & x.amount_burnt; - a & x.amount_minted; - a & x.amount_slippage; + a & x.amount_slippage_limit; if (x.version == 1) { a & x.signatures; diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index ef6b987..259135f 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -909,6 +909,36 @@ namespace cryptonote return outputs_amount; } //--------------------------------------------------------------- + std::string asset_type_from_id(const uint32_t asset_type_id) + { + switch (asset_type_id) { + case 0x46554c4d: + return "FULM"; + case 0x46555344: + return "FUSD"; + case 0x00000000: + return ""; + default: + break; + } + // Should probably throw() here + return ""; + } + //--------------------------------------------------------------- + uint32_t asset_id_from_type(const std::string asset_type) + { + if (asset_type == "FULM") { + return 0x46554c4d; + } else if (asset_type == "FUSD") { + return 0x46555344; + } else if (asset_type == "") { + return 0x00000000; + } else { + // Should probably throw() here + return static_cast(-1); + } + } + //--------------------------------------------------------------- bool get_tx_asset_types(const transaction& tx, const crypto::hash &txid, std::string& source, std::string& destination, const bool is_miner_tx) { // Clear the source std::set source_asset_types; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 0b1ac64..ab843a0 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -133,6 +133,9 @@ namespace cryptonote boost::optional get_output_view_tag(const cryptonote::tx_out& out); bool get_output_asset_type(const cryptonote::tx_out& out, std::string& output_asset_type); bool get_output_unlock_time(const cryptonote::tx_out& out, uint64_t& output_unlock_time); + std::string asset_type_from_id(const uint32_t asset_type_id); + uint32_t asset_id_from_type(const std::string asset_type); + bool get_tx_asset_types(const transaction& tx, const crypto::hash &txid, std::string& source, std::string& destination, const bool is_miner_tx); bool check_inputs_types_supported(const transaction& tx); bool check_outs_valid(const transaction& tx); bool parse_amount(uint64_t& amount, const std::string& str_amount); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index ee75477..0c5b335 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -256,8 +256,8 @@ namespace config 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10 } }; // Bender's nightmare - std::string const GENESIS_TX = "023c01ff000180c09e90acbb1402721d160620a58d7a9fe0a55b4a665284b94d40efffa4e5ff3326e8ea5cd26e8d0446554c4d3c0000000000000021012488e4fc617688fad2304293f2be80032b6deac2f1cd8cb49a578c34387ddafc00000000"; - + std::string const GENESIS_TX = "023c01ff000180c09e90acbb140227f8208e379c38ed7a3cf432f11ea684fedcf1d66c04a4276a5d4e484a5841480446554c4d3c00000000000000210116f94b1a0acf7fd7f9a2bfa80bf8d84b2a4a54cede3163ea8d2b422f3ab6d5d10100000000000000000000000000000000000000000000000000000000000000000000000000"; + uint32_t const GENESIS_NONCE = 10000; // Hash domain separators diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0fddcd5..1760f53 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1666,12 +1666,49 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead."); + std::map circ_supply = get_db().get_circulating_supply(); + size_t txs_weight; uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version)) + + /** + * Here is where the magic happens - determination of the payments for the protocol_tx + * + * We need to know the following: + * - address to send the funds to ("destination_address") + * - asset_type being burnt + * - amount being burnt + * - asset_type being minted + * + * (All of this information should be provided by the txpool_tx_meta_t object) + * + * From that little lot, we can hopefully work out the slippage on all of the TXs, and + * therefore the amount to be minted for each TX, and who to pay it to, etc, etc. + */ + std::vector protocol_metadata; + std::vector protocol_entries; + if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version, pr, circ_supply, protocol_metadata)) { return false; } + + // Clone the txpool_tx_meta_t data into a more useable format + for (auto& meta: protocol_metadata) { + cryptonote::protocol_data_entry entry; + entry.amount_burnt = meta.amount_burnt; + entry.amount_minted = 0; + entry.amount_slippage_limit = meta.amount_slippage_limit; + entry.source_asset = asset_type_from_id(meta.source_asset_id); + entry.destination_asset = asset_type_from_id(meta.destination_asset_id); + entry.destination_address = meta.destination_address; + protocol_entries.push_back(entry); + } + + // Time to construct the protocol_tx + uint64_t protocol_fee = 0; + bool ok = construct_protocol_tx(height, protocol_fee, b.protocol_tx, protocol_entries, circ_supply, pr, b.major_version); + CHECK_AND_ASSERT_MES(ok, false, "Failed to construct protocol tx"); + pool_cookie = m_tx_pool.cookie(); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) size_t real_txs_weight = 0; @@ -2444,7 +2481,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc bool Blockchain::get_pricing_record(oracle::pricing_record& pr, uint64_t timestamp) { LOG_PRINT_L1("Requesting pricing record from Oracle - time : " << timestamp); - + /* bool r = false; const uint8_t hf_version = m_hardfork->get_current_version(); @@ -2472,18 +2509,6 @@ bool Blockchain::get_pricing_record(oracle::pricing_record& pr, uint64_t timesta // Copy the PR pr = res.pr; - /* - pr.version = res.pr.version; - pr.timestamp = res.pr.timestamp; - for (auto &asset: res.pr.assets) { - cryptonote::pricing_record_entry entry; - entry.asset_type = asset.asset; - entry.spot = asset.spot; - entry.ma = asset.ma; - pr.assets.push_back(entry); - } - pr.signature = res.pr.signature; - */ // Verify the signature if (pr.verifySignature(get_config(m_nettype).ORACLE_PUBLIC_KEY)) { } else { @@ -2498,6 +2523,11 @@ bool Blockchain::get_pricing_record(oracle::pricing_record& pr, uint64_t timesta sig_hex += ss.str(); } LOG_PRINT_L1("Received pricing record - signature = " << sig_hex); + */ + + uint64_t height = get_current_blockchain_height(); + std::vector prices = {200000000, 150000000, 100000000, 75000000}; + pr.spot = pr.moving_average = prices[height % 4]; return true; } @@ -3358,6 +3388,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, const uint8_t hf_version = m_hardfork->get_current_version(); + /* if (tx.version >= 2) { if (tx.vout.size() < 2) @@ -3367,6 +3398,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } } + */ // from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others // if one output cannot mix with 2 others, we accept at most 1 output that can mix @@ -3493,6 +3525,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at Blockchain::check_tx_inputs"); const txin_to_key& in_to_key = boost::get(txin); + // 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 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)); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c789805..7183b32 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -925,7 +925,7 @@ namespace cryptonote tx_info[n].result = false; break; case rct::RCTTypeSimple: - if (!rct::verRctSemanticsSimple(rv)) + if (!rct::verRctSemanticsSimple(rv, tx_info[n].tx->amount_burnt)) { MERROR_VER("rct signature semantics check failed"); set_semantics_failed(tx_info[n].tx_hash); @@ -976,18 +976,17 @@ namespace cryptonote break; } } - if (!rvv.empty() && !rct::verRctSemanticsSimple(rvv)) + if (!rvv.empty()) { LOG_PRINT_L1("One transaction among this group has bad semantics, verifying one at a time"); ret = false; - const bool assumed_bad = rvv.size() == 1; // if there's only one tx, it must be the bad one for (size_t n = 0; n < tx_info.size(); ++n) { 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) continue; - if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures)) + if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures, tx_info[n].tx->amount_burnt)) { set_semantics_failed(tx_info[n].tx_hash); tx_info[n].tvc.m_verifivation_failed = true; diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 9723de7..c4f0685 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -76,6 +76,165 @@ namespace cryptonote LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses"); } //--------------------------------------------------------------- + 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 + if (to_asset == "") { + LOG_ERROR("Converting to a BURN is nonsensical - aborting"); + rate = std::numeric_limits::max(); + return false; + } + // Check for transfers + if (from_asset == to_asset) { + rate = COIN; + return true; + } + if (from_asset == "FULM") { + // FULM as source + if (to_asset not_eq "FUSD") { + // Invalid conversion - abort + LOG_ERROR("Invalid conversion (" << from_asset << "," << to_asset << ") - aborting"); + return false; + } + // Scale to FUSD + rate = pr["FUSD"]; + } else if (from_asset == "FUSD") { + // FUSD as source + if (to_asset not_eq "FULM") { + // Invalid conversion - abort + LOG_ERROR("Invalid conversion (" << from_asset << "," << to_asset << ") - aborting"); + return false; + } + // Scale to FULM + boost::multiprecision::uint128_t rate_128 = COIN; + rate_128 *= COIN; + rate_128 /= pr["FUSD"]; + rate = rate_128.convert_to(); + rate -= (rate % 10000); + } + return true; + } + //--------------------------------------------------------------- + bool get_converted_amount(const uint64_t& conversion_rate, const uint64_t& source_amount, uint64_t& dest_amount) { + if (!conversion_rate || !source_amount) { + LOG_ERROR("Invalid conversion rate or input amount for conversion (" << conversion_rate << "," << source_amount << ") - aborting"); + return false; + } + boost::multiprecision::uint128_t source_amount_128 = source_amount; + boost::multiprecision::uint128_t conversion_rate_128 = conversion_rate; + boost::multiprecision::uint128_t dest_amount_128 = source_amount_128 * conversion_rate_128; + dest_amount_128 /= COIN; + dest_amount = dest_amount_128.convert_to(); + return true; + } + //--------------------------------------------------------------- + bool calculate_conversion(const std::string& source_asset, const std::string& dest_asset, const uint64_t amount_burnt, const uint64_t amount_slippage_limit, uint64_t& amount_minted, uint64_t& amount_slippage, const std::map circ_supply, const oracle::pricing_record& pr, const uint8_t hf_version) { + + // Sanity check - are the asset types a valid conversion? + CHECK_AND_ASSERT_MES(source_asset != dest_asset, false, "cannot calculate slippage when source and dest assets are identical"); + CHECK_AND_ASSERT_MES(source_asset != "", false, "source_asset not provided"); + CHECK_AND_ASSERT_MES(dest_asset != "", false, "dest_asset not provided (is this a BURN?)"); + /////CHECK_AND_ASSERT_MES(pr.has_rate(dest_asset), false, "missing rate for " << dest_asset <<" in pricing record - cannot calculate conversion"); + CHECK_AND_ASSERT_MES(circ_supply.count(source_asset) != 0, false, "missing circulating_supply data - cannot calculate slippage"); + + // Get the conversion rate for the TX + uint64_t conversion_rate = COIN; + bool ok = get_conversion_rate(pr, source_asset, dest_asset, conversion_rate); + CHECK_AND_ASSERT_MES(ok, false, "Unable to get conversion rate for " << source_asset << " to " << dest_asset); + + if (hf_version >= HF_VERSION_SLIPPAGE_YIELD) { + + // Apply slippage to the burnt amount + amount_slippage = 0; + + // Check that the slippage is acceptable + if (amount_slippage > amount_slippage_limit) { + // Bail out with no conversion + LOG_PRINT_L1("Unable to convert - slippage limit was too low"); + amount_minted = 0; + return true; + } + } + + // Work out the converted amount + ok = get_converted_amount(conversion_rate, amount_burnt - amount_slippage, amount_minted); + CHECK_AND_ASSERT_MES(ok, false, "Unable to get converted amount for " << (amount_burnt - amount_slippage) << ", converting from " << source_asset << " to " << dest_asset); + + return true; + } + //--------------------------------------------------------------- + bool construct_protocol_tx(const size_t height, + uint64_t& protocol_fee, + transaction& tx, + std::vector& protocol_data, + std::map circ_supply, + const oracle::pricing_record& pr, + const uint8_t hf_version) { + + // Clear the TX contents + tx.set_null(); + + keypair txkey = keypair::generate(hw::get_device("default")); + add_tx_pub_key_to_extra(tx, txkey.pub); + if (!sort_tx_extra(tx.extra, tx.extra)) + return false; + + txin_gen in; + in.height = height; + + // Update the circulating_supply information, while keeping a count of amount to be created using txin_gen + std::map txin_gen_totals; + uint64_t txin_gen_final = 0; + for (auto const& entry: protocol_data) { + if (!circ_supply.count(entry.source_asset)) { + LOG_ERROR("Circulating supply does not have " << entry.source_asset << " balance - invalid source_asset"); + return false; + } + // Deduct the amount_burnt from the circulating_supply balance + circ_supply[entry.source_asset] -= entry.amount_burnt; + } + + // Calculate the slippage for the output amounts + for (auto const& entry: protocol_data) { + if (entry.destination_asset == "") { + // BURN TX - no slippage, no money minted - skip + continue; + } + // CONVERT TX - calculate the slippage, and decide if it is going to be converted or refunded + uint64_t amount_slippage = 0, amount_minted = 0; + bool ok = cryptonote::calculate_conversion(entry.source_asset, entry.destination_asset, entry.amount_burnt, entry.amount_slippage_limit, amount_minted, amount_slippage, circ_supply, pr, hf_version); + if (!ok) { + LOG_ERROR("failed to calculate slippage when trying to build protocol_tx"); + return false; + } + if (amount_minted == 0) { + + // REFUND + txin_gen_totals[entry.source_asset] += entry.amount_burnt; + + // Create the TX output for this refund + tx_out out; + cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, 0, entry.destination_address, false, crypto::view_tag{}, out); + tx.vout.push_back(out); + } else { + + // CONVERTED + txin_gen_totals[entry.destination_asset] += amount_minted; + + // Create the TX output for this conversion + tx_out out; + cryptonote::set_tx_out(amount_minted, entry.destination_asset, 0, entry.destination_address, false, crypto::view_tag{}, out); + tx.vout.push_back(out); + } + + } + + // TODO: create the YIELD outputs + + // TODO: sum the txin_gen_totals values so that we know how much we are going to include in the protocol TX + + return false; + } + //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { // Clear the TX contents @@ -111,9 +270,9 @@ namespace cryptonote // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller, // and avoids the quantization. These outputs will be added as rct outputs with identity // masks, to they can be used as rct inputs. - if (hard_fork_version >= 2 && hard_fork_version < 4) { - block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD; - } + //if (hard_fork_version >= 2 && hard_fork_version < 4) { + // block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD; + //} std::vector out_amounts; decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD, @@ -271,6 +430,24 @@ namespace cryptonote tx.extra = extra; crypto::public_key txkey_pub; + transaction_type tx_type; + if (!get_tx_type(source_asset, dest_asset, tx_type)) { + LOG_ERROR("invalid tx type"); + return false; + } + tx.type = static_cast(tx_type); + + // Is this a CONVERT tx? + if (tx_type == cryptonote::transaction_type::CONVERT) { + // Set the destination address to be something only our wallet can identify + // This is where Fulmo gets interesting... + tx.destination_address = get_destination_view_key_pub(destinations, change_addr); + } + + // Set the source and destination asset_type values + tx.source_asset_type = source_asset; + tx.destination_asset_type = dest_asset; + // if we have a stealth payment id, find it and encrypt it with the tx key now std::vector tx_extra_fields; if (parse_tx_extra(tx.extra, tx_extra_fields)) @@ -402,12 +579,6 @@ namespace cryptonote tx.vin.push_back(input_to_key); } - transaction_type tx_type; - if (!get_tx_type(source_asset, dest_asset, tx_type)) { - LOG_ERROR("invalid tx type"); - return false; - } - if (shuffle_outs) { std::shuffle(destinations.begin(), destinations.end(), crypto::random_device{}); @@ -470,19 +641,23 @@ namespace cryptonote additional_tx_public_keys, amount_keys, out_eph_public_key, use_view_tags, view_tag); + // Is this a BURN or CONVERT TX? + if (tx_type == cryptonote::transaction_type::BURN || tx_type == cryptonote::transaction_type::CONVERT) { + // Do not create outputs that are for the destination asset type - discard them as unused + if (dst_entr.asset_type == dest_asset) { + tx.amount_burnt += dst_entr.amount; + amount_keys.pop_back(); + if (tx_type == cryptonote::transaction_type::CONVERT) { + //tx.amount_slippage_limit += dst_entr.amount; + } + continue; + } + } tx_out out; cryptonote::set_tx_out(dst_entr.amount, dst_entr.asset_type, unlock_time, out_eph_public_key, use_view_tags, view_tag, out); tx.vout.push_back(out); output_index++; summary_outs_money += dst_entr.amount; - if (tx_type == cryptonote::transaction_type::BURN || tx_type == cryptonote::transaction_type::CONVERT) { - if (dst_entr.asset_type == dest_asset) { - tx.amount_burnt += dst_entr.amount; - } - if (tx_type == cryptonote::transaction_type::CONVERT) { - tx.amount_minted += dst_entr.amount; - } - } } CHECK_AND_ASSERT_MES(additional_tx_public_keys.size() == additional_tx_keys.size(), false, "Internal error creating additional public keys"); @@ -654,7 +829,7 @@ namespace cryptonote if (!use_simple_rct && amount_in > amount_out) outamounts.push_back(amount_in - amount_out); else - fee = summary_inputs_money - summary_outs_money; + 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) diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 65cc895..93fd232 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -38,8 +38,33 @@ namespace cryptonote { + bool get_conversion_rate(const oracle::pricing_record& pr, const std::string& from_asset, const std::string& to_asset, uint64_t& rate); + bool get_converted_amount(const uint64_t& conversion_rate, const uint64_t& source_amount, uint64_t& dest_amount); + bool calculate_conversion(const std::string& source_asset, const std::string& dest_asset, const uint64_t amount_burnt, const uint64_t amount_slippage_limit, uint64_t& amount_minted, uint64_t& amount_slippage, const std::map circ_supply, const oracle::pricing_record& pr, const uint8_t hf_version); + //--------------------------------------------------------------- + /** + * Construct the protocol_tx + * + * @param height the current blockchain height (for reasons unknown) + * @param protocol_fee a variable that will receive the total supplementary fee due to be assigned to the block reward + * @param tx the actual TX instance to be populated with the necessary txin_gen and txout_to_key entries etc + * @param hf_version the current hard-fork version of the blockchain + * + * @return TRUE on success, FALSE otherwise (duh) + */ + struct protocol_data_entry + { + crypto::public_key destination_address; + uint64_t amount_burnt; + uint64_t amount_minted; + uint64_t amount_slippage_limit; + std::string source_asset; + std::string destination_asset; + }; + bool construct_protocol_tx(const size_t height, uint64_t& protocol_fee, transaction& tx, std::vector& protocol_data, std::map circ_supply, const oracle::pricing_record& pr, const uint8_t hf_version); //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); + bool construct_protocol_tx(size_t height, transaction& tx, size_t max_outs = 999, uint8_t hard_fork_version = 1); struct tx_source_entry { @@ -156,7 +181,6 @@ namespace cryptonote crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const crypto::hash *seed_hash = nullptr, const int miners = 0); void get_altblock_longhash(const block& b, crypto::hash& res, const crypto::hash& seed_hash); - bool get_tx_asset_types(const transaction& tx, const crypto::hash &txid, std::string& source, std::string& destination, const bool is_miner_tx); bool get_tx_type(const std::string& source, const std::string& destination, transaction_type& type); } diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index cdd55aa..d2fb543 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -282,6 +282,14 @@ namespace cryptonote meta.pruned = tx.pruned; meta.bf_padding = 0; memset(meta.padding, 0, sizeof(meta.padding)); + + //SRCG - need to work out how to populate this + meta.destination_address = tx.destination_address; + meta.amount_burnt = tx.amount_burnt; + meta.amount_slippage_limit = tx.amount_slippage_limit; + meta.source_asset_id = cryptonote::asset_id_from_type(tx.source_asset_type); + meta.destination_asset_id = cryptonote::asset_id_from_type(tx.destination_asset_type); + try { if (kept_by_block) @@ -358,6 +366,13 @@ namespace cryptonote meta.bf_padding = 0; memset(meta.padding, 0, sizeof(meta.padding)); + //SRCG - need to work out how to populate this + meta.destination_address = tx.destination_address; + meta.amount_burnt = tx.amount_burnt; + meta.amount_slippage_limit = tx.amount_slippage_limit; + meta.source_asset_id = cryptonote::asset_id_from_type(tx.source_asset_type); + meta.destination_asset_id = cryptonote::asset_id_from_type(tx.destination_asset_type); + if (!insert_key_images(tx, id, tx_relay)) return false; @@ -1492,7 +1507,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, oracle::pricing_record& pr, std::map& circ_supply, std::vector& protocol_metadata) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -1530,6 +1545,7 @@ namespace cryptonote warned = true; continue; } + 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)) @@ -1601,14 +1617,14 @@ namespace cryptonote if (memcmp(&original_meta, &meta, sizeof(meta))) { try - { - m_blockchain.update_txpool_tx(sorted_it->second, meta); - } + { + m_blockchain.update_txpool_tx(sorted_it->second, meta); + } catch (const std::exception &e) - { - MERROR("Failed to update tx meta: " << e.what()); - // continue, not fatal - } + { + MERROR("Failed to update tx meta: " << e.what()); + // continue, not fatal + } } if (!ready) { @@ -1621,6 +1637,14 @@ namespace cryptonote continue; } + // Check what the TX type is - only CONVERT needs a cash_value + if (meta.source_asset_id == meta.destination_asset_id) { + // TRANSFER + } else { + // BURN OR CONVERT (both require inclusion in the protocol_tx calculation for circ_supply purposes) + protocol_metadata.push_back(meta); + } + bl.tx_hashes.push_back(sorted_it->second); total_weight += meta.weight; fee += meta.fee; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 45623fd..5645784 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -52,6 +52,8 @@ #include "rpc/core_rpc_server_commands_defs.h" #include "rpc/message_data_structs.h" +#include "oracle/pricing_record.h" + namespace cryptonote { class Blockchain; @@ -233,10 +235,13 @@ namespace cryptonote * @param fee return-by-reference the total of fees from the included transactions * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules + * @param pr the current pricing record + * @param circ_cupply the circulating supply information for all asset types + * @param protocol_metadata the TX-specific data needed to create conversion outputs in the protocol TX (including converted amounts and refunds) * * @return true */ - bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version); + bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, oracle::pricing_record& pr, std::map& circ_supply, std::vector& protocol_metadata); /** * @brief get a list of all transactions in the pool diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 3433252..ac2c904 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -1120,6 +1120,7 @@ namespace rct { hw::device &hwdev ) { const bool bulletproof_or_plus = rct_config.range_proof_type > RangeProofBorromean; + CHECK_AND_ASSERT_THROW_MES(destination_asset_types.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); @@ -1400,7 +1401,8 @@ namespace rct { //ver RingCT simple //assumes only post-rct style inputs (at least for max anonymity) - bool verRctSemanticsSimple(const std::vector & rvv) { + bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt) + { try { PERF_TIMER(verRctSemanticsSimple); @@ -1412,87 +1414,79 @@ namespace rct { std::vector bpp_proofs; size_t max_non_bp_proofs = 0, offset = 0; - for (const rctSig *rvp: rvv) + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus, + false, "verRctSemanticsSimple called on non simple rctSig"); + const bool bulletproof = is_rct_bulletproof(rv.type); + const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); + if (bulletproof || bulletproof_plus) { - CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL"); - const rctSig &rv = *rvp; - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus, - false, "verRctSemanticsSimple called on non simple rctSig"); - const bool bulletproof = is_rct_bulletproof(rv.type); - const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); - if (bulletproof || bulletproof_plus) + if (bulletproof_plus) + CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_plus_amounts(rv.p.bulletproofs_plus), false, "Mismatched sizes of outPk and bulletproofs_plus"); + else + CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); + if (is_rct_clsag(rv.type)) { - if (bulletproof_plus) - CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_plus_amounts(rv.p.bulletproofs_plus), false, "Mismatched sizes of outPk and bulletproofs_plus"); - else - CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); - if (is_rct_clsag(rv.type)) - { - CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs are not empty for CLSAG"); - CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.CLSAGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.CLSAGs"); - } - else - { - CHECK_AND_ASSERT_MES(rv.p.CLSAGs.empty(), false, "CLSAGs are not empty for MLSAG"); - CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs"); - } - CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty"); + CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs are not empty for CLSAG"); + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.CLSAGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.CLSAGs"); } else { - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); - CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty"); + CHECK_AND_ASSERT_MES(rv.p.CLSAGs.empty(), false, "CLSAGs are not empty for MLSAG"); + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs"); } - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); - - if (!bulletproof && !bulletproof_plus) - max_non_bp_proofs += rv.p.rangeSigs.size(); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty"); } + else + { + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty"); + } + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); + + if (!bulletproof && !bulletproof_plus) + max_non_bp_proofs += rv.p.rangeSigs.size(); results.resize(max_non_bp_proofs); - for (const rctSig *rvp: rvv) + + const keyV &pseudoOuts = bulletproof || bulletproof_plus ? rv.p.pseudoOuts : rv.pseudoOuts; + + rct::keyV masks(rv.outPk.size()); + for (size_t i = 0; i < rv.outPk.size(); i++) { + masks[i] = rv.outPk[i].mask; + } + key sumOutpks = addKeys(masks); + DP(sumOutpks); + const key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + addKeys(sumOutpks, txnFeeKey, sumOutpks); + + const key txnAmountBurntKey = scalarmultH(d2h(amount_burnt)); + addKeys(sumOutpks, txnAmountBurntKey, sumOutpks); + + key sumPseudoOuts = addKeys(pseudoOuts); + DP(sumPseudoOuts); + + //check pseudoOuts vs Outs.. + if (!equalKeys(sumPseudoOuts, sumOutpks)) { + LOG_PRINT_L1("Sum check failed"); + return false; + } + + if (bulletproof_plus) { - const rctSig &rv = *rvp; - - const bool bulletproof = is_rct_bulletproof(rv.type); - const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); - const keyV &pseudoOuts = bulletproof || bulletproof_plus ? rv.p.pseudoOuts : rv.pseudoOuts; - - rct::keyV masks(rv.outPk.size()); - for (size_t i = 0; i < rv.outPk.size(); i++) { - masks[i] = rv.outPk[i].mask; - } - key sumOutpks = addKeys(masks); - DP(sumOutpks); - const key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - addKeys(sumOutpks, txnFeeKey, sumOutpks); - - key sumPseudoOuts = addKeys(pseudoOuts); - DP(sumPseudoOuts); - - //check pseudoOuts vs Outs.. - if (!equalKeys(sumPseudoOuts, sumOutpks)) { - LOG_PRINT_L1("Sum check failed"); - return false; - } - - if (bulletproof_plus) - { - for (size_t i = 0; i < rv.p.bulletproofs_plus.size(); i++) - bpp_proofs.push_back(&rv.p.bulletproofs_plus[i]); - } - else if (bulletproof) - { - for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) - bp_proofs.push_back(&rv.p.bulletproofs[i]); - } - else - { - for (size_t i = 0; i < rv.p.rangeSigs.size(); i++) - tpool.submit(&waiter, [&, i, offset] { results[i+offset] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); - offset += rv.p.rangeSigs.size(); - } + for (size_t i = 0; i < rv.p.bulletproofs_plus.size(); i++) + bpp_proofs.push_back(&rv.p.bulletproofs_plus[i]); + } + else if (bulletproof) + { + for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) + bp_proofs.push_back(&rv.p.bulletproofs[i]); + } + else + { + for (size_t i = 0; i < rv.p.rangeSigs.size(); i++) + tpool.submit(&waiter, [&, i, offset] { results[i+offset] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); + offset += rv.p.rangeSigs.size(); } if (!bpp_proofs.empty() && !verBulletproofPlus(bpp_proofs)) { @@ -1533,11 +1527,6 @@ namespace rct { } } - bool verRctSemanticsSimple(const rctSig & rv) - { - return verRctSemanticsSimple(std::vector(1, &rv)); - } - //ver RingCT simple //assumes only post-rct style inputs (at least for max anonymity) bool verRctNonSemanticsSimple(const rctSig & rv) { diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 40a0e18..34cbe36 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -162,10 +162,9 @@ namespace rct { ); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } - bool verRctSemanticsSimple(const rctSig & rv); - bool verRctSemanticsSimple(const std::vector & rv); + bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt=0); bool verRctNonSemanticsSimple(const rctSig & rv); - static inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); } + static inline bool verRctSimple(const rctSig & rv, const uint64_t amount_burnt=0) { return verRctSemanticsSimple(rv, amount_burnt) && verRctNonSemanticsSimple(rv); } xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 84f14bc..b1c778b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2926,10 +2926,10 @@ namespace cryptonote bool core_rpc_server::on_get_circulating_supply(const COMMAND_RPC_GET_CIRCULATING_SUPPLY::request& req, COMMAND_RPC_GET_CIRCULATING_SUPPLY::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) { PERF_TIMER(on_get_circulating_supply); - std::vector> amounts = m_core.get_blockchain_storage().get_db().get_circulating_supply(); + std::map amounts = m_core.get_blockchain_storage().get_db().get_circulating_supply(); for (const auto &i: amounts) { - COMMAND_RPC_GET_CIRCULATING_SUPPLY::supply_entry se(i.first, i.second); + COMMAND_RPC_GET_CIRCULATING_SUPPLY::supply_entry se(i.first, std::to_string(i.second)); res.supply_tally.push_back(se); } res.status = CORE_RPC_STATUS_OK; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 7c0ee62..6fbc815 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7853,6 +7853,7 @@ bool simple_wallet::convert(const std::vector &args_) // TODO: add locked versions if (args_.size() < 4) { + fail_msg_writer() << tr("missing argument(s)"); PRINT_USAGE(USAGE_CONVERT); return true; } @@ -7875,8 +7876,9 @@ bool simple_wallet::convert(const std::vector &args_) std::string strLastArg = local_args.back(); std::transform(strLastArg.begin(), strLastArg.end(), strLastArg.begin(), ::toupper); if (strLastArg not_eq "FULM" and strLastArg not_eq "FUSD") { - PRINT_USAGE(USAGE_CONVERT); - return true; + fail_msg_writer() << tr("invalid destination asset_type"); + PRINT_USAGE(USAGE_CONVERT); + return true; } dest_asset = strLastArg; local_args.pop_back(); @@ -7884,11 +7886,19 @@ bool simple_wallet::convert(const std::vector &args_) strLastArg = local_args.back(); std::transform(strLastArg.begin(), strLastArg.end(), strLastArg.begin(), ::toupper); if (strLastArg not_eq "FULM" and strLastArg not_eq "FUSD") { - PRINT_USAGE(USAGE_CONVERT); - return true; + fail_msg_writer() << tr("invalid source asset_type"); + PRINT_USAGE(USAGE_CONVERT); + return true; } source_asset = strLastArg; local_args.pop_back(); + + // Sanity check - it isn't a conversion if the asset_type for source and dest are equal + if (source_asset == dest_asset) { + fail_msg_writer() << tr("invalid conversion - asset_type is unchanged"); + PRINT_USAGE(USAGE_CONVERT); + return true; + } transfer_main(Convert, source_asset, dest_asset, local_args, false); return true; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e314e4e..4fc0aa5 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2595,7 +2595,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment_details payment; payment.m_tx_hash = txid; payment.m_fee = fee; - payment.m_amount = source_asset == dest_asset ? i.second[dest_asset] : tx.amount_minted; + // SRCG - figure out what this needs to be (pretty sure we should never get here with CONVERT!) + payment.m_amount = source_asset == dest_asset ? i.second[dest_asset] : tx.amount_burnt; payment.m_asset_type = dest_asset; payment.m_amounts = tx_amounts_individual_outs[i.first]; payment.m_block_height = height; @@ -10766,6 +10767,7 @@ bool wallet2::sanity_check(const std::vector &ptx_vector, s check_tx_proof(ptx.tx, address, r.second.second, "automatic-sanity-check", proof, received); } catch (const std::exception &e) { received = 0; } + received += ptx.tx.amount_burnt; total_received += received; } diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index aae9ddb..579408c 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -161,16 +161,15 @@ public: bool test() { - std::vector rvv; - rvv.reserve(m_txes.size()); for (size_t n = 0; n < m_txes.size(); ++n) { const rct::rctSig &rv = m_txes[n].rct_signatures; if (!rct::verRctNonSemanticsSimple(rv)) return false; - rvv.push_back(&rv); + if (!rct::verRctSemanticsSimple(rv)) + return false; } - return rct::verRctSemanticsSimple(rvv); + return true; } private: diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index fa3b0c7..efd1954 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1288,7 +1288,6 @@ TEST(ringct, aggregated) static const uint64_t outputs[] = {500, 1500}; s[n] = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 0); sp[n] = &s[n]; + ASSERT_TRUE(verRctSemanticsSimple(s[n])); } - - ASSERT_TRUE(verRctSemanticsSimple(sp)); }