From c1f5dd42e874db475eb3f90f3adc1afd107f5ac1 Mon Sep 17 00:00:00 2001 From: Some Random Crypto Guy Date: Tue, 21 Nov 2023 21:22:21 +0000 Subject: [PATCH] change is once again working in the wallet - this only leaves the converted amounts to be paid out, and yield TXs to be supported --- src/blockchain_db/lmdb/db_lmdb.cpp | 214 +++++++++++------- src/cryptonote_basic/cryptonote_basic.h | 2 +- .../cryptonote_format_utils.cpp | 43 ++++ src/cryptonote_config.h | 2 +- src/cryptonote_core/blockchain.cpp | 14 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 90 +++----- src/cryptonote_protocol/enums.h | 10 +- src/oracle/asset_types.h | 8 +- src/rpc/core_rpc_server.cpp | 8 +- src/serialization/json_object.cpp | 17 ++ src/version.cpp.in | 6 +- src/wallet/wallet2.cpp | 41 +++- 12 files changed, 285 insertions(+), 170 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index d5c661f48..583d04786 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -369,9 +369,7 @@ typedef struct outassettype { typedef struct circ_supply { crypto::hash tx_hash; - //uint64_t pricing_record_height; - uint64_t source_currency_type; - uint64_t dest_currency_type; + uint32_t asset_type; uint64_t amount_burnt; uint64_t amount_minted; } circ_supply; @@ -944,25 +942,22 @@ import_tally_from_cst(circ_supply_tally *cst) return tally; } -boost::multiprecision::int128_t -read_circulating_supply_data(MDB_cursor *cur_circ_supply_tally, MDB_val idx) +int read_circulating_supply_data(MDB_cursor *cur_circ_supply_tally, MDB_val idx, boost::multiprecision::int128_t& tally) { MDB_val vcst; circ_supply_tally cst; int result = mdb_cursor_get(cur_circ_supply_tally, &idx, &vcst, MDB_SET); if (result == MDB_NOTFOUND) { LOG_PRINT_L1("Failed to obtain circulating supply - must be first TX with this asset"); - - cst.is_negative = false; - cst.amount_hi = 0; - cst.amount_lo = 0; + tally = 0; } else if (!result) { cst = *(circ_supply_tally*) vcst.mv_data; + tally = import_tally_from_cst(&cst); } else { - throw0(DB_ERROR(lmdb_error("Failed to obtain tally for circulating supply: ", result).c_str())); + throw0(DB_ERROR(lmdb_error("Failed to obtain tally for circulating supply: ", result).c_str())); + tally = -1; } - - return import_tally_from_cst(&cst); + return result; } void write_circulating_supply_data(MDB_cursor *cur_circ_supply_tally, MDB_val idx, boost::multiprecision::int128_t tally) @@ -1078,23 +1073,13 @@ 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())); } - // get tx assets - std::string strSource; - std::string strDest; - if (!get_tx_asset_types(tx, tx_hash, strSource, strDest, miner_tx)) { - throw0(DB_ERROR("Failed to add tx circulating supply to db transaction: get_tx_asset_types fails.")); - } - - if (strSource != strDest) { + if (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::BURN) { // Conversion TX - update our records circ_supply cs; cs.tx_hash = tx_hash; - //cs.pricing_record_height = tx.pricing_record_height; - 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.asset_type = cryptonote::asset_id_from_type(tx.source_asset_type); cs.amount_burnt = tx.amount_burnt; - // SRCG - need to work out how to populate this - //cs.amount_minted = tx.amount_minted; + cs.amount_minted = 0; MDB_val_set(val_circ_supply, cs); result = mdb_cursor_put(m_cur_circ_supply, &val_tx_id, &val_circ_supply, MDB_APPEND); @@ -1102,27 +1087,67 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons throw0(DB_ERROR( lmdb_error("Failed to add tx circulating supply to db transaction: ", result).c_str() )); // Get the current tally value for the source currency type - MDB_val_copy source_idx(cs.source_currency_type); - boost::multiprecision::int128_t source_tally = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx); + MDB_val_copy source_idx(cs.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 - cs.amount_burnt; boost::multiprecision::int128_t coinbase = get_block_already_generated_coins(m_height-1); - if ((strSource == "FULM" && (coinbase + final_source_tally < 0)) || - (strSource != "FULM" && final_source_tally < 0)) { - LOG_ERROR(__func__ << " : mint/burn underflow detected for " << strSource << " : correcting supply tally by " << final_source_tally); - final_source_tally = 0; + if (source_tally == 0 && result == MDB_NOTFOUND) { + if (tx.source_asset_type == "FULM") { + final_source_tally += coinbase; + } else { + throw0(DB_ERROR("burn underflow - asset balance is zero for non-FULM asset")); + } } write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); - - // Get the current tally value for the dest currency type - MDB_val_copy dest_idx(cs.dest_currency_type); - boost::multiprecision::int128_t dest_tally = read_circulating_supply_data(m_cur_circ_supply_tally, dest_idx); - boost::multiprecision::int128_t final_dest_tally = dest_tally + cs.amount_minted; - write_circulating_supply_data(m_cur_circ_supply_tally, dest_idx, final_dest_tally); - - LOG_PRINT_L1("tx ID " << tx_id << "\nSource tally before burn =" << source_tally.str() << "\nSource tally after burn =" << final_source_tally.str() << - "\nDest tally before mint =" << dest_tally.str() << "\nDest tally after mint =" << final_dest_tally.str()); + LOG_PRINT_L1("tx ID " << tx_id << "\n\tTally before burn = " << source_tally.str() << "\n\tTally after burn = " << final_source_tally.str()); } + if (tx.type == cryptonote::transaction_type::PROTOCOL) { + + // Iterate over all of the outputs for a PROTOCOL_TX since they're all MINTED + std::map minted_amounts; + for (const auto& out: tx.vout) { + + // Fetch the amount and output_asset_type for this output + std::string asset_type = ""; + 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)")); + + circ_supply cs; + cs.tx_hash = tx_hash; + cs.asset_type = cryptonote::asset_id_from_type(asset_type); + cs.amount_burnt = 0; + cs.amount_minted = out.amount; + + minted_amounts[cs.asset_type] += out.amount; + + MDB_val_set(val_circ_supply, cs); + result = mdb_cursor_put(m_cur_circ_supply, &val_tx_id, &val_circ_supply, MDB_APPEND); + if (result) + throw0(DB_ERROR( lmdb_error("Failed to add tx circulating supply to db transaction: ", result).c_str() )); + } + + // Now update the overall tally entries + for (const auto& asset: minted_amounts) { + + // Get the current tally value for the source currency type + MDB_val_copy source_idx(asset.first); + boost::multiprecision::int128_t source_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); + 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 == "FULM") { + 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()); + } + } + // Is there yield_tx data to add? if (tx.type == cryptonote::transaction_type::YIELD) { // Create the object we are going to write to the database @@ -1203,55 +1228,64 @@ 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())); } - // get tx assets - std::string strSource; - std::string strDest; - if (!get_tx_asset_types(tx, tx_hash, strSource, strDest, miner_tx)) { - throw0(DB_ERROR("Failed to add tx circulating supply to db transaction: get_tx_asset_types fails.")); + if (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::BURN) { + + // Get the current tally value for the source currency type + MDB_val_copy source_idx(cryptonote::asset_id_from_type(tx.source_asset_type)); + boost::multiprecision::int128_t source_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); + if (result == MDB_NOTFOUND) + throw0(DB_ERROR("remove_transaction_data() - minted asset not found")); + boost::multiprecision::int128_t final_source_tally = source_tally + tx.amount_burnt; + boost::multiprecision::int128_t coinbase = get_block_already_generated_coins(m_height-1); + 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()); } - if (strSource != strDest) - { - // Update the tally table - // Get the current tally value for the source currency type - circ_supply cs; - cs.tx_hash = tx_hash; - //cs.pricing_record_height = tx.pricing_record_height; - cs.amount_burnt = tx.amount_burnt; - // 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(); + if (tx.type == cryptonote::transaction_type::PROTOCOL) { - // Update the tally by increasing the amount by how much we've burnt - MDB_val_copy source_idx(cs.source_currency_type); - boost::multiprecision::int128_t source_tally = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx); - boost::multiprecision::int128_t final_source_tally = source_tally + cs.amount_burnt; - write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally); - - // Update the tally by decreasing the amount by how much we've minted - MDB_val_copy dest_idx(cs.dest_currency_type); - boost::multiprecision::int128_t dest_tally = read_circulating_supply_data(m_cur_circ_supply_tally, dest_idx); - boost::multiprecision::int128_t final_dest_tally = dest_tally - cs.amount_minted; - boost::multiprecision::int128_t coinbase = get_block_already_generated_coins(m_height-1); - if ((strDest == "FULM" && (coinbase + final_dest_tally < 0)) || - (strDest != "FULM" && final_dest_tally < 0)) { - LOG_ERROR(__func__ << " : mint/burn underflow detected for " << strDest << " : correcting supply tally by " << final_dest_tally); - final_dest_tally = 0; + // Iterate over all of the outputs for a PROTOCOL_TX since they're all MINTED + std::map minted_amounts; + for (const auto& out: tx.vout) { + + // Fetch the amount and output_asset_type for this output + std::string asset_type = ""; + 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; } - write_circulating_supply_data(m_cur_circ_supply_tally, dest_idx, final_dest_tally); - // Update the circ_supply table - if ((result = mdb_cursor_get(m_cur_circ_supply, &val_tx_id, NULL, MDB_SET))) + // Now update the overall tally entries + for (const auto& asset: minted_amounts) { + + // Get the current tally value for the source currency type + MDB_val_copy source_idx(asset.first); + boost::multiprecision::int128_t source_tally = 0; + result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally); + if (result == MDB_NOTFOUND) + throw0(DB_ERROR("remove_transaction_data() - minted asset not found")); + 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()); + } + } + + // Update the circ_supply table by deleting all entries for this TX + if ((result = mdb_cursor_get(m_cur_circ_supply, &val_tx_id, NULL, MDB_SET))) { + if (result == MDB_NOTFOUND) { + LOG_PRINT_L1("failed to obtain circulating supply data - no burns / conversions made yet?"); + } else { throw1(DB_ERROR(lmdb_error("Failed to locate circulating supply for removal: ", result).c_str())); + } + } else { result = mdb_cursor_del(m_cur_circ_supply, 0); if (result) throw1(DB_ERROR(lmdb_error("Failed to add removal of circulating supply to db transaction: ", result).c_str())); - - LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\nSource tally before undoing burn =" << source_tally.str() << "\nSource tally after undoing burn =" << final_source_tally.str() << - "\nDest tally before undoing mint =" << dest_tally.str() << "\nDest tally after undoing mint =" << final_dest_tally.str()); } - remove_tx_outputs(tip->data.tx_id, tx); result = mdb_cursor_get(m_cur_tx_outputs, &val_tx_id, NULL, MDB_SET); @@ -1763,6 +1797,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) lmdb_db_open(txn, LMDB_CIRC_SUPPLY, MDB_INTEGERKEY | MDB_CREATE, m_circ_supply, "Failed to open db handle for m_circ_supply"); lmdb_db_open(txn, LMDB_CIRC_SUPPLY_TALLY, MDB_CREATE, m_circ_supply_tally, "Failed to open db handle for m_circ_supply_tally"); + 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"); + 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); @@ -1784,6 +1820,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) mdb_set_compare(txn, m_circ_supply, compare_uint64); mdb_set_compare(txn, m_circ_supply_tally, compare_uint64); + mdb_set_dupsort(txn, m_yield_txs, compare_uint64); + if (!(mdb_flags & MDB_RDONLY)) { result = mdb_drop(txn, m_hf_starting_heights, 1); @@ -1960,6 +1998,8 @@ void BlockchainLMDB::reset() throw0(DB_ERROR(lmdb_error("Failed to drop m_hf_versions: ", result).c_str())); if (auto result = mdb_drop(txn, m_properties, 0)) throw0(DB_ERROR(lmdb_error("Failed to drop m_properties: ", result).c_str())); + if (auto result = mdb_drop(txn, m_yield_txs, 0)) + throw0(DB_ERROR(lmdb_error("Failed to drop m_yield_txs: ", result).c_str())); // init with current version MDB_val_str(k, "version"); @@ -3287,7 +3327,7 @@ std::map BlockchainLMDB::get_circulating_supply() const // Push the data into the circulating supply return struct const uint64_t currency_type = *(const uint64_t*)k.mv_data; circ_supply_tally *cst = (circ_supply_tally*)v.mv_data; - const std::string currency_label = oracle::ASSET_TYPES.at(currency_type); + const std::string currency_label = cryptonote::asset_type_from_id(currency_type); boost::multiprecision::int128_t amount = import_tally_from_cst(cst); // Check for FULM - we need to adjust the total for them @@ -3592,12 +3632,24 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_block_cou if (result) throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str())); } + + // Skip the protocol TX as well? + op = MDB_NEXT; + result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &v, op); + if (result) + throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str())); + if (!pruned) + { + result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, &v, op); + if (result) + throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str())); + } } op = MDB_NEXT; current_block.second.reserve(b.tx_hashes.size()); - num_txes += b.tx_hashes.size() + (skip_coinbase ? 0 : 1); + num_txes += b.tx_hashes.size() + (skip_coinbase ? 0 : 2); for (const auto &tx_hash: b.tx_hashes) { // get pruned data diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index a41bfab2a..46520ef5d 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -234,7 +234,7 @@ namespace cryptonote vin.clear(); vout.clear(); extra.clear(); - type = cryptonote::transaction_type::TRANSFER; + type = cryptonote::transaction_type::UNSET; destination_address = crypto::null_pkey; source_asset_type.clear(); destination_asset_type.clear(); diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 259135f1e..b470f329f 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -916,6 +916,8 @@ namespace cryptonote return "FULM"; case 0x46555344: return "FUSD"; + case 0x4255524E: + return "BURN"; case 0x00000000: return ""; default: @@ -931,6 +933,8 @@ namespace cryptonote return 0x46554c4d; } else if (asset_type == "FUSD") { return 0x46555344; + } else if (asset_type == "BURN") { + return 0x4255524E; } else if (asset_type == "") { return 0x00000000; } else { @@ -939,7 +943,30 @@ namespace cryptonote } } //--------------------------------------------------------------- + /** + * The various scenarios that are permitted for Fulmo are more extensive than + * they are for Zepyhr / Havan. Specifically, we permit: + * + * MINER_TX: (SRCG => all fees are to be paid in FULM?) + * - input txin_gen (FULM) + * - outputs txout_to_key (FULM) / txout_to_tagged_key (FULM) + * + * PROTOCOL_TX: + * - input txin_gen (FULM) --- ONLY if there are outputs + * - input void ("") --- ONLY if there are NO outputs + * - outputs txout_to_key (FULM, FUSD) / txout_to_tagged_key (FULM, FUSD) + * + * BURN: + * + * CONVERT: + * + * TRANSFER: + * + * LOCK_FOR_YIELD: + * + */ 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; source = ""; @@ -961,12 +988,28 @@ namespace cryptonote sat.reserve(source_asset_types.size()); std::copy(source_asset_types.begin(), source_asset_types.end(), std::back_inserter(sat)); + // Check for empty TX + if (sat.size() == 0) { + CHECK_AND_ASSERT_MES(tx.type == cryptonote::transaction_type::PROTOCOL, false, "Only protocol_tx type can have no vin entry"); + CHECK_AND_ASSERT_MES(tx.vin.empty(), false, "Only protocol_tx type can have no vin entry"); + CHECK_AND_ASSERT_MES(tx.vout.empty(), false, "Protocol TX with no inputs cannot have outputs"); + destination = ""; + return true; + } + // Sanity check that we only have 1 source asset type if (sat.size() != 1) { LOG_ERROR("Multiple Source Asset types detected. Rejecting.."); return false; } source = sat[0]; + + // Check for empty TX (no outputs) + if (tx.vout.empty()) { + LOG_PRINT_L1("No TX outputs found - returning empty destination"); + destination = ""; + return true; + } // Clear the destination std::set destination_asset_types; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 1d3cd69ca..73177b293 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -260,7 +260,7 @@ 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 = "023c01ff000180c09e90acbb140227f8208e379c38ed7a3cf432f11ea684fedcf1d66c04a4276a5d4e484a5841480446554c4d3c00000000000000210116f94b1a0acf7fd7f9a2bfa80bf8d84b2a4a54cede3163ea8d2b422f3ab6d5d10100000000000000000000000000000000000000000000000000000000000000000000000000"; + std::string const GENESIS_TX = "023c01ff000180c09e90acbb1402ba4fcfcff0d4a91441c87615e514be3938e173179ac34a7fc98c0286e7320fdc0446554c4d3c0000000000000021019e809607ceda9ae7ae6755e05bcdc9c555acc8e95dcaede402eca1166a032e1a0100000000000000000000000000000000000000000000000000000000000000000000000000"; uint32_t const GENESIS_NONCE = 10000; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 87499173a..e67a32221 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1407,6 +1407,11 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version) { LOG_PRINT_L3("Blockchain::" << __func__); + if (!b.protocol_tx.vin.size()) { + // Nothing is created by this TX - check no money is included + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "void protocol transaction in the block has outputs"); + return true; + } CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type"); CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version"); @@ -1474,6 +1479,12 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, LOG_PRINT_L3("Blockchain::" << __func__); CHECK_AND_ASSERT_MES(b.tx_hashes.size() == txs.size(), false, "Invalid number of TXs / hashes supplied"); + if (!b.protocol_tx.vin.size()) { + // Nothing is created by this TX - check no money is included + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "void protocol transaction in the block has outputs"); + return true; + } + key_images_container keys; uint64_t fee_summary = 0; @@ -1557,7 +1568,8 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, return false; } } - return false; + + return true; } //------------------------------------------------------------------ // get the block weights of the last blocks, and return by reference . diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 1a1bef93a..2e663edf4 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -78,7 +78,7 @@ namespace cryptonote //--------------------------------------------------------------- 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 == "") { + if (to_asset == "BURN") { LOG_ERROR("Converting to a BURN is nonsensical - aborting"); rate = std::numeric_limits::max(); return false; @@ -175,6 +175,7 @@ namespace cryptonote // Clear the TX contents tx.set_null(); + tx.type = cryptonote::transaction_type::PROTOCOL; // Force the TX type to 2 tx.version = 2; @@ -182,9 +183,6 @@ namespace cryptonote // Clear the unlock_time tx.unlock_time = 0; - // Force the TX type to "special" (0) - tx.type = cryptonote::transaction_type::UNSET; - 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)) @@ -205,7 +203,7 @@ namespace cryptonote // Calculate the slippage for the output amounts LOG_PRINT_L2("Creating protocol_tx..."); for (auto const& entry: protocol_data) { - if (entry.destination_asset == "") { + if (entry.destination_asset == "BURN") { // BURN TX - no slippage, no money minted - skip continue; } @@ -241,7 +239,7 @@ namespace cryptonote } // TODO: create the YIELD outputs - + // Create the txin_gen now txin_gen in; in.height = height; @@ -254,6 +252,7 @@ namespace cryptonote // Clear the TX contents tx.set_null(); + tx.type = cryptonote::transaction_type::MINER; keypair txkey = keypair::generate(hw::get_device("default")); add_tx_pub_key_to_extra(tx, txkey.pub); @@ -278,65 +277,29 @@ namespace cryptonote ", fee " << fee); #endif block_reward += fee; - - // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and - // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the - // emission schedule - // 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; - //} - - std::vector out_amounts; - decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD, - [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, - [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); - - CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); - if (height == 0 || hard_fork_version >= 4) - { - // the genesis block was not decomposed, for unknown reasons - while (max_outs < out_amounts.size()) - { - //out_amounts[out_amounts.size() - 2] += out_amounts.back(); - //out_amounts.resize(out_amounts.size() - 1); - out_amounts[1] += out_amounts[0]; - for (size_t n = 1; n < out_amounts.size(); ++n) - out_amounts[n - 1] = out_amounts[n]; - out_amounts.pop_back(); - } - } - else - { - CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded"); - } - uint64_t summary_amounts = 0; - for (size_t no = 0; no < out_amounts.size(); no++) - { - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); + CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); - r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); - - uint64_t amount = out_amounts[no]; - summary_amounts += amount; - - bool use_view_tags = hard_fork_version >= HF_VERSION_VIEW_TAGS; - crypto::view_tag view_tag; - if (use_view_tags) - crypto::derive_view_tag(derivation, no, view_tag); - - tx_out out; - cryptonote::set_tx_out(amount, "FULM", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, use_view_tags, view_tag, out); - - tx.vout.push_back(out); - } + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); + bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")"); + + uint64_t amount = block_reward; + summary_amounts += amount; + + bool use_view_tags = hard_fork_version >= HF_VERSION_VIEW_TAGS; + crypto::view_tag view_tag; + if (use_view_tags) + crypto::derive_view_tag(derivation, 0, view_tag); + + tx_out out; + cryptonote::set_tx_out(amount, "FULM", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, use_view_tags, view_tag, out); + + tx.vout.push_back(out); CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); @@ -937,6 +900,7 @@ namespace cryptonote //genesis block bl = {}; bl.protocol_tx.set_null(); + bl.protocol_tx.type = cryptonote::transaction_type::PROTOCOL; blobdata tx_bl; bool r = string_tools::parse_hexstr_to_binbuff(genesis_tx, tx_bl); diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h index 17f9ec9a5..774b0ff18 100644 --- a/src/cryptonote_protocol/enums.h +++ b/src/cryptonote_protocol/enums.h @@ -46,9 +46,11 @@ namespace cryptonote enum transaction_type { UNSET = 0, - TRANSFER = 1, - CONVERT = 2, - BURN = 3, - YIELD = 4 + MINER = 1, + PROTOCOL = 2, + TRANSFER = 3, + CONVERT = 4, + BURN = 5, + YIELD = 6 }; } diff --git a/src/oracle/asset_types.h b/src/oracle/asset_types.h index b29e53f53..5da714859 100644 --- a/src/oracle/asset_types.h +++ b/src/oracle/asset_types.h @@ -31,7 +31,7 @@ namespace oracle { - const std::vector ASSET_TYPES = {"FULM", "FUSD"}; + const std::vector ASSET_TYPES = {"FULM", "FUSD", "BURN"}; class asset_type_counts { @@ -41,10 +41,12 @@ namespace oracle { // Fields uint64_t FULM; uint64_t FUSD; + uint64_t BURN; asset_type_counts() noexcept : FULM(0) , FUSD(0) + , BURN(0) { } @@ -54,6 +56,8 @@ namespace oracle { return FULM; } else if (asset_type == "FUSD") { return FUSD; + } else if (asset_type == "BURN") { + return BURN; } return 0; @@ -65,6 +69,8 @@ namespace oracle { FULM += val; } else if (asset_type == "FUSD") { FUSD += val; + } else if (asset_type == "BURN") { + BURN += val; } } }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b1c778b0e..0e97e9ea3 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -649,8 +649,8 @@ namespace cryptonote res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); res.asset_type_output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_asset_type_output_indices()); ntxes += bd.second.size(); - res.output_indices.back().indices.reserve(1 + bd.second.size()); - res.asset_type_output_indices.back().indices.reserve(1 + bd.second.size()); + res.output_indices.back().indices.reserve(2 + bd.second.size()); + res.asset_type_output_indices.back().indices.reserve(2 + bd.second.size()); if (req.no_miner_tx) { res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); @@ -665,7 +665,7 @@ namespace cryptonote size += res.blocks.back().txs.back().blob.size(); } - const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1); + const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 2); if (n_txes_to_lookup > 0) { std::vector>> indices; @@ -675,7 +675,7 @@ namespace cryptonote res.status = "Failed"; return true; } - if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0)) + if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 2 : 0)) { res.status = "Failed"; return true; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 2ccba1d0e..54d87efb1 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -260,6 +260,11 @@ void fromJsonValue(const rapidjson::Value& val, long& i) to_int64(val, i); } +void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction_type& type) +{ + to_uint(val, type); +} + void toJsonValue(rapidjson::Writer& dest, const cryptonote::transaction& tx) { dest.StartObject(); @@ -269,6 +274,12 @@ void toJsonValue(rapidjson::Writer& dest, const cryptonote::t INSERT_INTO_JSON_OBJECT(dest, inputs, tx.vin); INSERT_INTO_JSON_OBJECT(dest, outputs, tx.vout); INSERT_INTO_JSON_OBJECT(dest, extra, tx.extra); + INSERT_INTO_JSON_OBJECT(dest, type, static_cast(tx.type)); + INSERT_INTO_JSON_OBJECT(dest, destination_address, tx.destination_address); + INSERT_INTO_JSON_OBJECT(dest, source_asset_type, tx.source_asset_type); + INSERT_INTO_JSON_OBJECT(dest, destination_asset_type, tx.destination_asset_type); + INSERT_INTO_JSON_OBJECT(dest, amount_burnt, tx.amount_burnt); + INSERT_INTO_JSON_OBJECT(dest, amount_slippage_limit, tx.amount_slippage_limit); if (!tx.pruned) { INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures); @@ -291,6 +302,12 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx) GET_FROM_JSON_OBJECT(val, tx.vin, inputs); GET_FROM_JSON_OBJECT(val, tx.vout, outputs); GET_FROM_JSON_OBJECT(val, tx.extra, extra); + GET_FROM_JSON_OBJECT(val, tx.type, type); + GET_FROM_JSON_OBJECT(val, tx.destination_address, destination_address); + GET_FROM_JSON_OBJECT(val, tx.source_asset_type, source_asset_type); + GET_FROM_JSON_OBJECT(val, tx.destination_asset_type, destination_asset_type); + GET_FROM_JSON_OBJECT(val, tx.amount_burnt, amount_burnt); + GET_FROM_JSON_OBJECT(val, tx.amount_slippage_limit, amount_slippage_limit); GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct); const auto& sigs = val.FindMember("signatures"); diff --git a/src/version.cpp.in b/src/version.cpp.in index 76cae29eb..f6f6c356a 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,7 +1,9 @@ +#define DEF_FULMO_VERSION_TAG "7f6b8da" +#define DEF_FULMO_VERSION "0.0.1" #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" #define DEF_MONERO_VERSION "0.18.2.2" -#define DEF_MONERO_RELEASE_NAME "Fluorine Fermi" -#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG +#define DEF_MONERO_RELEASE_NAME "Zero" +#define DEF_MONERO_VERSION_FULL DEF_FULMO_VERSION "-" DEF_FULMO_VERSION_TAG ", based on Monero " DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ #include "version.h" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ebaa01e79..8fc95bce9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2045,12 +2045,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (!miner_tx && !pool) process_unconfirmed(txid, tx, height); - std::string source_asset; - std::string dest_asset; - cryptonote::transaction_type tx_type; - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_tx_asset_types(tx, txid, source_asset, dest_asset, miner_tx), error::wallet_internal_error, "Fail to get asset types"); - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_tx_type(source_asset, dest_asset, tx_type), error::wallet_internal_error, "Failed to get TX type"); - + std::string source_asset = tx.source_asset_type; + std::string dest_asset = tx.destination_asset_type; + cryptonote::transaction_type tx_type_verify = tx.type; + if (!miner_tx) { + THROW_WALLET_EXCEPTION_IF(!cryptonote::get_tx_type(source_asset, dest_asset, tx_type_verify), error::wallet_internal_error, "Failed to get TX type"); + THROW_WALLET_EXCEPTION_IF(tx_type_verify != tx.type, error::wallet_internal_error, "Incorrect TX type"); + } + // per receiving subaddress index std::unordered_map> tx_money_got_in_outs; std::unordered_map tx_amounts_individual_outs; @@ -2699,7 +2701,7 @@ bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) con //---------------------------------------------------------------------------------------------------- void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector &tx_cache_data, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache) { - THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 2 != parsed_block.o_indices.indices.size(), error::wallet_internal_error, "block transactions=" + std::to_string(bche.txs.size()) + " not match with daemon response size=" + std::to_string(parsed_block.o_indices.indices.size())); @@ -2714,12 +2716,17 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry ++tx_cache_data_offset; TIME_MEASURE_FINISH(miner_tx_handle_time); + TIME_MEASURE_START(protocol_tx_handle_time); + process_new_transaction(get_transaction_hash(b.protocol_tx), b.protocol_tx, parsed_block.o_indices.indices[1].indices, parsed_block.asset_type_output_indices.indices[1].indices, height, b.major_version, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache); + ++tx_cache_data_offset; + TIME_MEASURE_FINISH(protocol_tx_handle_time); + TIME_MEASURE_START(txs_handle_time); THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx) { - process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, parsed_block.asset_type_output_indices.indices[idx+1].indices, height, b.major_version, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache); + process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+2].indices, parsed_block.asset_type_output_indices.indices[idx+2].indices, height, b.major_version, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache); } TIME_MEASURE_FINISH(txs_handle_time); m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); @@ -2859,7 +2866,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector tx_cache_data; for (size_t i = 0; i < blocks.size(); ++i) - num_txes += 1 + parsed_blocks[i].txes.size(); + num_txes += 2 + parsed_blocks[i].txes.size(); tx_cache_data.resize(num_txes); size_t txidx = 0; for (size_t i = 0; i < blocks.size(); ++i) @@ -2868,12 +2875,14 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); + const cryptonote::transaction& tx = parsed_blocks[i].block.protocol_tx; + const size_t n_vouts = tx.vout.size(); + if (parsed_blocks[i].block.major_version >= hf_version_view_tags) + geniods.push_back(geniod_params{ tx, n_vouts, txidx }); + else + tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true); + ++txidx; for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j) { THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); @@ -3035,7 +3052,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector