From b35a97313dee3dc1b04ca8dc1e9b30889c60d9d1 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Tue, 5 May 2020 14:02:02 +0200 Subject: [PATCH 01/52] Allow partial outputs when syncing --- src/blockchain_db/blockchain_db.h | 5 +++-- src/blockchain_db/lmdb/db_lmdb.cpp | 25 ++++++++++++++++--------- src/blockchain_db/lmdb/db_lmdb.h | 2 +- src/cryptonote_basic/cryptonote_basic.h | 2 ++ tests/unit_tests/hardfork.cpp | 2 +- tests/unit_tests/safex_commands.cpp | 2 +- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index b85b5c797..1412e8c6b 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1438,10 +1438,11 @@ namespace cryptonote * * @param output_type type of output(e.g. staked token output * @param output_index index of the output for selected type + * @param output_id reference to var where output_id will be returned * - * @return list of public keys that can use this output + * @return true if it can find output id, false if it is not found */ - virtual uint64_t get_output_id(const tx_out_type output_type, const uint64_t output_index) const = 0; + virtual bool get_output_id(const tx_out_type output_type, const uint64_t output_index, uint64_t& output_id) const = 0; /** * @brief gets an output's tx hash and index * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 0d27ac2f9..e086017df 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3120,7 +3120,10 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6 if (!(output_type >= cryptonote::tx_out_type::out_advanced && output_type < cryptonote::tx_out_type::out_invalid)) throw0(DB_ERROR("Unknown advanced output type")); - uint64_t output_id = get_output_id(output_type, output_index); + uint64_t output_id; + + if( !get_output_id(output_type, output_index, output_id) ) + throw0(DB_ERROR("Output ID not found!")); check_open(); @@ -3152,7 +3155,7 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6 return output; } - uint64_t BlockchainLMDB::get_output_id(const tx_out_type output_type, const uint64_t output_index) const + bool BlockchainLMDB::get_output_id(const tx_out_type output_type, const uint64_t output_index, uint64_t& output_id) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3166,7 +3169,6 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6 RCURSOR(output_advanced_type); cur_output_advanced_type = m_cur_output_advanced_type; - uint64_t output_id = 0; const uint64_t out_type = static_cast(output_type); MDB_val_set(key, out_type); @@ -3179,13 +3181,13 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6 output_id = okadv->output_id; } else if (result == MDB_NOTFOUND) - throw0(DB_ERROR(lmdb_error("Attemting to get output id from output with index " + std::to_string(output_index) + " but not found: ", result).c_str())); + return false; else throw0(DB_ERROR(lmdb_error("DB error attempting to advanced output blob: ", result).c_str())); TXN_POSTFIX_RDONLY(); - return output_id; + return true; } tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t& output_id) const @@ -4142,8 +4144,9 @@ void BlockchainLMDB::get_amount_output_key(const uint64_t &amount, const std::ve std::vector output_ids; for(const auto output_index: output_indexes){ - uint64_t output_id = get_output_id(output_type, output_index); - output_ids.push_back(output_id); + uint64_t output_id; + if(get_output_id(output_type, output_index, output_id)) + output_ids.push_back(output_id); } TXN_PREFIX_RDONLY(); @@ -5976,7 +5979,9 @@ bool BlockchainLMDB::is_valid_transaction_output_type(const txout_target_v &txou tx_out_type out_type = edited ? tx_out_type::out_safex_offer_update : tx_out_type::out_safex_offer; - uint64_t output_id = get_output_id(out_type, output_index); + uint64_t output_id; + if( !get_output_id(out_type, output_index, output_id)) + throw0(DB_ERROR("Output ID not found!")); MDB_cursor *cur_output_advanced; RCURSOR(output_advanced); @@ -6033,7 +6038,9 @@ bool BlockchainLMDB::is_valid_transaction_output_type(const txout_target_v &txou throw0(DB_ERROR(lmdb_error("DB error attempting to get advanced output data: ", get_result).c_str())); - uint64_t output_id_creation = get_output_id(tx_out_type::out_safex_offer, output_index_creation); + uint64_t output_id_creation; + if(!get_output_id(tx_out_type::out_safex_offer, output_index_creation, output_id_creation)) + throw0(DB_ERROR("Output ID of offer creation not found!")); //Get offer keys MDB_val_set(k_creation, output_id_creation); MDB_val_set(v_blob, blob); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 7d952e2b8..9134f5859 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -284,7 +284,7 @@ class BlockchainLMDB : public BlockchainDB bool allow_partial = false) const; virtual output_advanced_data_t get_output_advanced_data(const tx_out_type output_type, const uint64_t output_index) const; - virtual uint64_t get_output_id(const tx_out_type output_type, const uint64_t output_index) const; + virtual bool get_output_id(const tx_out_type output_type, const uint64_t output_index, uint64_t& output_id) const; virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& output_id) const; virtual void get_output_tx_and_index_from_global(const std::vector &global_indices, diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 347ae77e4..036749f3d 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -570,6 +570,8 @@ namespace cryptonote return tx_out_type::out_safex_price_peg; case safex::command_t::create_feedback: return tx_out_type::out_safex_feedback_token; + case safex::command_t::distribute_network_fee: + return tx_out_type::out_network_fee; case safex::command_t::nop: default: return tx_out_type::out_invalid; diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index f53ee52f2..5f0b37626 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -93,7 +93,7 @@ class TestDB: public BlockchainDB { virtual uint64_t get_indexing_base() const { return 0; } virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, const tx_out_type output_type) const { return output_data_t(); } virtual output_advanced_data_t get_output_advanced_data(const tx_out_type output_type, const uint64_t output_index) const { return output_advanced_data_t{}; } - virtual uint64_t get_output_id(const tx_out_type output_type, const uint64_t output_index) const { return 0; } + virtual bool get_output_id(const tx_out_type output_type, const uint64_t output_index, uint64_t& output_id) const { return 0; } virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index, const tx_out_type output_type) const { return tx_out_index(); } virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices, const tx_out_type output_type) const {} diff --git a/tests/unit_tests/safex_commands.cpp b/tests/unit_tests/safex_commands.cpp index e20d91f84..7b9aadd3e 100644 --- a/tests/unit_tests/safex_commands.cpp +++ b/tests/unit_tests/safex_commands.cpp @@ -188,7 +188,7 @@ class TestBlockchainDB : public cryptonote::BlockchainDB { return cryptonote::output_data_t(); } virtual cryptonote::output_advanced_data_t get_output_advanced_data(const cryptonote::tx_out_type output_type, const uint64_t output_id) const {return cryptonote::output_advanced_data_t{};} - virtual uint64_t get_output_id(const cryptonote::tx_out_type output_type, const uint64_t output_index) const { return 0; } + virtual bool get_output_id(const cryptonote::tx_out_type output_type, const uint64_t output_index, uint64_t& output_id) const { return 0; } virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t &index) const { return cryptonote::tx_out_index(); } From dc286e552c86ecbafc01db821e5a7d6f237aa2f8 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Tue, 5 May 2020 19:18:46 +0200 Subject: [PATCH 02/52] Minor core_test patch --- tests/core_tests/integer_overflow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index bc326a68a..9be881e9c 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -78,6 +78,7 @@ namespace se.real_out_tx_key = get_tx_pub_key_from_extra(tx); se.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(tx); se.real_output_in_tx_index = out_idx; + se.referenced_output_type = cryptonote::tx_out_type::out_token; sources.push_back(se); } @@ -321,4 +322,4 @@ crypto::hash gen_uint_token_overflow_1::get_hash_from_string(const std::string h } const crypto::hash bitcoin_transaction_hash = *reinterpret_cast(expected_bitcoin_hash_data.data()); return bitcoin_transaction_hash; -} \ No newline at end of file +} From f24e1f377dfe947a50fdfcabcbb7aa61d51eb656 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 6 May 2020 20:22:16 +0200 Subject: [PATCH 03/52] Safex command validate returns error instead of throw --- src/safex/command.cpp | 49 ++++++++++++++++++----------- src/safex/command.h | 4 ++- tests/unit_tests/safex_commands.cpp | 2 +- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/safex/command.cpp b/src/safex/command.cpp index 168133c7d..844d10b32 100644 --- a/src/safex/command.cpp +++ b/src/safex/command.cpp @@ -42,8 +42,10 @@ namespace safex execution_status result = execution_status::ok; //per input execution, one input could be less than SAFEX_MINIMUM_TOKEN_STAKE_AMOUNT, all inputs must be SAFEX_MINIMUM_TOKEN_STAKE_AMOUNT - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((tools::is_whole_token_amount(this->get_staked_token_amount())), "Staked input is not whole token amount", this->get_command_type()); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.token_amount == this->get_staked_token_amount()), "Input amount differs from token stake command amount", this->get_command_type()); + if(!tools::is_whole_token_amount(this->get_staked_token_amount())) + result = execution_status::error_wrong_input_params; + if(!(txin.token_amount == this->get_staked_token_amount())) + result = execution_status::error_wrong_input_params; return result; } @@ -74,8 +76,11 @@ namespace safex execution_status result = execution_status::ok; - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.key_offsets.size() == 1), "Only one locked token output could be processed per input", this->get_command_type()); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.key_offsets[0] == this->get_staked_token_output_index()), "Locked token output ID does not match", this->get_command_type()); + if(!(txin.key_offsets.size() == 1)) + result = execution_status::error_wrong_input_params; + + if(!(txin.key_offsets[0] == this->get_staked_token_output_index())) + result = execution_status::error_wrong_input_params; uint64_t staked_token_index = this->get_staked_token_output_index(); const cryptonote::output_advanced_data_t od = blokchainDB.get_output_advanced_data(cryptonote::tx_out_type::out_staked_token, staked_token_index); @@ -88,10 +93,13 @@ namespace safex if(out.target.type() == typeid(cryptonote::txout_to_script) && get_tx_out_type(out.target) == cryptonote::tx_out_type::out_staked_token) if(out.token_amount == txin.token_amount) output_found = true; - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((output_found), "Locked token amount not the same", this->get_command_type()); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((od.height + get_safex_minumum_token_lock_period(blokchainDB.get_net_type()) <= blokchainDB.height()), "Minimum lock period not fulfilled", this->get_command_type()); + if(!output_found) + result = execution_status::error_unstake_token_output_not_found; + if(!(od.height + get_safex_minumum_token_lock_period(blokchainDB.get_net_type()) <= blokchainDB.height())) + result = execution_status::error_unstake_token_minimum_period; + return result; } @@ -118,10 +126,8 @@ namespace safex execution_status token_collect::validate(const cryptonote::BlockchainDB &blokchainDB, const cryptonote::txin_to_script &txin) { - execution_status result = execution_status::ok; - - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.key_offsets.size() == 1), "Only one locked token output could be processed per input", this->get_command_type()); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.key_offsets[0] == this->get_staked_token_output_index()), "Locked token output ID does not match", this->get_command_type()); + //TODO: GRKI Do not allow token_collect for now + execution_status result = execution_status::invalid; //todo Get data about locked token output from database using its index //todo check if db output amount is same as txin amount @@ -150,8 +156,10 @@ namespace safex execution_status result = execution_status::ok; - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.amount > 0), "Amount to donate must be greater than zero ", this->get_command_type()); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.token_amount == 0), "Tokens could not be donated to network ", this->get_command_type()); + if(!(txin.amount > 0)) + return execution_status::error_wrong_input_params; + if(txin.token_amount != 0) + return execution_status::error_wrong_input_params; return result; }; @@ -215,9 +223,10 @@ namespace safex if(sfx_price * cmd->quantity > cmd->price) return execution_status::error_purchase_not_enough_funds; - - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.amount > 0), "Purchase amount must be greater than zero ", this->get_command_type()); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.token_amount == 0), "Could not purchase with tokens ", this->get_command_type()); + if(!(txin.amount > 0)) + return execution_status::error_wrong_input_params; + if(txin.token_amount != 0) + return execution_status::error_wrong_input_params; return result; }; @@ -239,8 +248,10 @@ namespace safex { execution_status result = execution_status::ok; - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.amount > 0), "Amount to donate must be greater than zero ", this->get_command_type()); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.token_amount == 0), "Tokens could not be donated to network ", this->get_command_type()); + if(!(txin.amount > 0)) + return execution_status::error_wrong_input_params; + if(txin.token_amount != 0) + return execution_status::error_wrong_input_params; return result; }; @@ -260,8 +271,8 @@ namespace safex execution_status create_account::validate(const cryptonote::BlockchainDB &blokchainDB, const cryptonote::txin_to_script &txin) { - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((txin.token_amount > 0), "Create account must reference at least one token output and in total is "+ - std::to_string(SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE)+" tokens needed for locking", this->get_command_type()); + if(txin.token_amount == 0) + return execution_status::error_wrong_input_params; std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(txin.script); diff --git a/src/safex/command.h b/src/safex/command.h index 69d07929c..83e0a672b 100644 --- a/src/safex/command.h +++ b/src/safex/command.h @@ -56,7 +56,9 @@ namespace safex error_price_peg_data_too_big = 23, error_price_peg_not_existant = 24, error_feedback_data_too_big = 25, - error_price_peg_rate_zero = 26 + error_price_peg_rate_zero = 26, + error_unstake_token_output_not_found = 27, + error_unstake_token_minimum_period = 28 }; struct execution_result diff --git a/tests/unit_tests/safex_commands.cpp b/tests/unit_tests/safex_commands.cpp index 7b9aadd3e..a9549c393 100644 --- a/tests/unit_tests/safex_commands.cpp +++ b/tests/unit_tests/safex_commands.cpp @@ -506,7 +506,7 @@ TEST_F(SafexCommandExecution, TokenLockExceptions) } catch (safex::command_exception &exception) { - ASSERT_STREQ(std::string("Staked input is not whole token amount").c_str(), std::string(exception.what()).c_str()); + } catch (std::exception &exception) { From 70de443b2de83f9c9005c069d161b975848b2001 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Thu, 7 May 2020 01:20:04 +0200 Subject: [PATCH 04/52] *sigh* Validate func should validate the command --- src/safex/command.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/safex/command.cpp b/src/safex/command.cpp index 844d10b32..debc9ee77 100644 --- a/src/safex/command.cpp +++ b/src/safex/command.cpp @@ -562,20 +562,20 @@ namespace safex bool validate_safex_command(const cryptonote::BlockchainDB &blokchainDB, const cryptonote::txin_to_script &txin) { - //parse command and execute it + //parse command and validate it try { std::unique_ptr cmd = safex_command_serializer::parse_safex_object(txin.script, txin.command_type); - std::shared_ptr result{cmd->execute(blokchainDB, txin)}; - if (result->status != execution_status::ok) + execution_status result{cmd->validate(blokchainDB, txin)}; + if (result != execution_status::ok) { - LOG_ERROR("Execution of safex command failed, status:" << static_cast(result->status)); + LOG_ERROR("Validation of safex command failed, status:" << static_cast(result)); return false; } } catch (command_exception &ex) { - LOG_ERROR("Error in safex command execution:" << ex.what()); + LOG_ERROR("Error in safex command validation:" << ex.what()); return false; } From 11d93d5f0c3335bd89e9fbd086a8fda2fd138b2f Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Thu, 7 May 2020 01:21:47 +0200 Subject: [PATCH 05/52] Throw exception if tx is not valid when adding block (probably due to some other tx in the block) Don't return tx to the pool if that tx caused the exception. --- src/blockchain_db/blockchain_db.cpp | 12 ++++++++++-- src/blockchain_db/blockchain_db.h | 15 +++++++++++++++ src/cryptonote_core/blockchain.cpp | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 01646b668..7336ea0b8 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -140,6 +140,14 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti tx_hash = *tx_hash_ptr; } + for (const txin_v& tx_input : tx.vin) + if(tx_input.type() == typeid(txin_to_script)){ + const cryptonote::txin_to_script &txin = boost::get(tx_input); + if (!safex::validate_safex_command(*this, txin)) { + throw SAFEX_TX_CONFLICT(tx_hash); + } + } + for (const txin_v& tx_input : tx.vin) { @@ -149,7 +157,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti { auto k_image_opt = boost::apply_visitor(key_image_visitor(), tx_input); if (!k_image_opt) - DB_ERROR("Output does not have proper key image"); + throw DB_ERROR("Output does not have proper key image"); const crypto::key_image &k_image = *k_image_opt; add_spent_key(k_image); } @@ -164,7 +172,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti //mark key image as spent auto k_image_opt = boost::apply_visitor(key_image_visitor(), tx_input); if (!k_image_opt) - DB_ERROR("Output does not have proper key image"); + throw DB_ERROR("Output does not have proper key image"); const crypto::key_image &k_image = *k_image_opt; add_spent_key(k_image); diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 1412e8c6b..8253eab2d 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -403,6 +403,21 @@ namespace cryptonote {} }; + /** + * @brief thrown when a transaction cannot be added due to conflict with another tx in the block + */ + class SAFEX_TX_CONFLICT : public DB_EXCEPTION + { + public: + SAFEX_TX_CONFLICT() : DB_EXCEPTION("The safex transaction verification failed!") + {} + + SAFEX_TX_CONFLICT(const crypto::hash& _tx_hash) : DB_EXCEPTION("The safex transaction verification failed!"), tx_hash(_tx_hash) + {} + + crypto::hash tx_hash; + }; + /*********************************** * End of Exception Definitions ***********************************/ diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index df6b3d31d..0563a9de2 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4887,6 +4887,20 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& return_tx_to_pool(txs); return false; } + catch (const SAFEX_TX_CONFLICT& e){ + + for(auto tx: txs){ + cryptonote::transaction tmp; + if(m_db->get_tx(tx.hash,tmp)) + m_db->revert_transaction(tx.hash); + } + auto it = find_if(txs.begin(),txs.end(),[e](transaction& tx){ return tx.hash == e.tx_hash; }); + txs.erase(it); + LOG_ERROR("Error adding block with hash: " << id << " to blockchain, what = " << e.what()); + bvc.m_verifivation_failed = true; + return_tx_to_pool(txs); + return false; + } catch (const std::exception& e) { From 0289e576a5b07318e6ef93117ef0c03b03183542 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Thu, 7 May 2020 02:01:31 +0200 Subject: [PATCH 06/52] Changes for unit tests to pass --- src/safex/command.cpp | 2 +- tests/unit_tests/safex_blockchain_db.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/safex/command.cpp b/src/safex/command.cpp index debc9ee77..515ca15b5 100644 --- a/src/safex/command.cpp +++ b/src/safex/command.cpp @@ -97,7 +97,7 @@ namespace safex if(!output_found) result = execution_status::error_unstake_token_output_not_found; - if(!(od.height + get_safex_minumum_token_lock_period(blokchainDB.get_net_type()) <= blokchainDB.height())) + if(od.height + get_safex_minumum_token_lock_period(blokchainDB.get_net_type()) > blokchainDB.height()) result = execution_status::error_unstake_token_minimum_period; return result; diff --git a/tests/unit_tests/safex_blockchain_db.cpp b/tests/unit_tests/safex_blockchain_db.cpp index 9ace89ab1..650422fba 100644 --- a/tests/unit_tests/safex_blockchain_db.cpp +++ b/tests/unit_tests/safex_blockchain_db.cpp @@ -52,7 +52,7 @@ using epee::string_tools::pod_to_hex; namespace { // anonymous namespace - const int NUMBER_OF_BLOCKS = 23; + const int NUMBER_OF_BLOCKS = 53; const uint64_t default_miner_fee = ((uint64_t) 500000000); const std::string bitcoin_tx_hashes_str[6] = {"3b7ac2a66eded32dcdc61f0fec7e9ddb30ccb3c6f5f06c0743c786e979130c5f", "3c904e67190d2d8c5cc93147c1a3ead133c61fc3fa578915e9bf95544705e63c", "2d825e690c4cb904556285b74a6ce565f16ba9d2f09784a7e5be5f7cdb05ae1d", "89352ec1749c872146eabddd56cd0d1492a3be6d2f9df98f6fbbc0d560120182", @@ -161,7 +161,7 @@ namespace std::cout << "tx 15 hash: " << epee::string_tools::pod_to_hex(get_transaction_hash(tx)) << std::endl; m_txmap[get_transaction_hash(tx)] = tx; } - else if (i == 17) + else if (i == 47) { //token unlock transaction tx_list.resize(tx_list.size() + 1); @@ -169,7 +169,7 @@ namespace construct_token_unstake_transaction(m_txmap, m_blocks, tx, m_users_acc[1], m_users_acc[1], 100 * SAFEX_TOKEN, default_miner_fee, 0); //unlock 100 m_txmap[get_transaction_hash(tx)] = tx; } - else if (i == 19) + else if (i == 49) { //token stake transaction tx_list.resize(tx_list.size() + 1); @@ -177,7 +177,7 @@ namespace construct_token_stake_transaction(m_txmap, m_blocks, tx, m_users_acc[1], m_users_acc[1], 200 * SAFEX_TOKEN, default_miner_fee, 0); m_txmap[get_transaction_hash(tx)] = tx; } - else if (i == 21) + else if (i == 51) { //token unlock transaction tx_list.resize(tx_list.size() + 1); @@ -397,7 +397,7 @@ namespace data = this->m_db->get_token_stake_expiry_outputs(SAFEX_DEFAULT_TOKEN_STAKE_EXPIRY_PERIOD + 15); ASSERT_EQ(data.size(), 0); - data = this->m_db->get_token_stake_expiry_outputs(SAFEX_DEFAULT_TOKEN_STAKE_EXPIRY_PERIOD + 19); + data = this->m_db->get_token_stake_expiry_outputs(SAFEX_DEFAULT_TOKEN_STAKE_EXPIRY_PERIOD + 49); ASSERT_EQ(data.size(), 1); uint64_t test_output_id = data[0]; //first tx in 11 block @@ -412,8 +412,8 @@ namespace bool match = false; crypto::hash matching_tx_hash; - //find pkey key in transaction output of block 19 - for (transaction& tx: this->m_txs[19]) + //find pkey key in transaction output of block 49 + for (transaction& tx: this->m_txs[49]) { for (tx_out out: tx.vout) { From a20ae6fdc44ff1f579775cbb3cd90199c5c18b63 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 8 May 2020 02:00:33 +0200 Subject: [PATCH 07/52] Add signature checks for purchase and feedback --- src/cryptonote_core/blockchain.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0563a9de2..b4bf41cc9 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3880,9 +3880,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, if (txin.type() == typeid(txin_token_migration)) { tpool.submit(&waiter, boost::bind(&Blockchain::check_migration_signature, this, std::cref(tx_prefix_hash), std::cref(tx.signatures[sig_index][0]), std::ref(results[sig_index]))); } - else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::distribute_network_fee || - boost::get(txin).command_type == safex::command_t::simple_purchase || - boost::get(txin).command_type == safex::command_t::create_feedback)) { + else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::distribute_network_fee)) { //todo atana nothing to do here results[sig_index] = true; } @@ -3936,9 +3934,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { if (txin.type() == typeid(txin_token_migration)) { check_migration_signature(tx_prefix_hash, tx.signatures[sig_index][0], results[sig_index]); - } else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::distribute_network_fee || - boost::get(txin).command_type == safex::command_t::simple_purchase || - boost::get(txin).command_type == safex::command_t::create_feedback)) { + } else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::distribute_network_fee) ) { //todo atana nothing to do here results[sig_index] = true; } else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::edit_account)) { From 8b3745c136008a796b541eafa10500137cebf680 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Tue, 12 May 2020 15:45:24 +0200 Subject: [PATCH 08/52] On create safex account, one output token amount must be SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b4bf41cc9..0b3a38cf9 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3220,7 +3220,7 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & return false; } } - if (vout.target.type() == typeid(txout_token_to_key) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_token) + if (vout.target.type() == typeid(txout_token_to_key) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_token && vout.token_amount == SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE) { total_locked_tokens += vout.token_amount; } From 62127a415be4c1743baf57f00b4523e92a27b039 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 13 May 2020 15:21:50 +0200 Subject: [PATCH 09/52] Better parsing of prices in sallet cli --- src/simplewallet/simplewallet_safex.cpp | 33 ++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet_safex.cpp b/src/simplewallet/simplewallet_safex.cpp index fa925db94..6760561e7 100644 --- a/src/simplewallet/simplewallet_safex.cpp +++ b/src/simplewallet/simplewallet_safex.cpp @@ -260,8 +260,15 @@ namespace cryptonote try{ - price = stold(local_args[2])*SAFEX_CASH_COIN; - quantity = stoi(local_args[3]); + bool ok = cryptonote::parse_amount(price, local_args[2]); + if(!ok || 0 == price) + { + fail_msg_writer() << tr("amount is wrong: ") << local_args[2] << + ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); + return true; + } + + quantity = stoull(local_args[3]); long double check_price = stold(local_args[2]); long double check_quantity = stold(local_args[3]); @@ -1098,13 +1105,27 @@ namespace cryptonote std::string prompt = "Enter price in "+currency+" : "; std::string price_str = input_line(tr(prompt.c_str())); - uint64_t new_price = stold(price_str); - new_price*=SAFEX_CASH_COIN; + uint64_t new_price; + + bool ok = cryptonote::parse_amount(new_price, price_str); + if(!ok || 0 == new_price) + { + fail_msg_writer() << tr("amount is wrong: ") << price_str << + ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); + return false; + } prompt = "Enter minimum SFX price : "; std::string min_price_str = input_line(tr(prompt.c_str())); - uint64_t min_price = stold(min_price_str); - min_price*=SAFEX_CASH_COIN; + uint64_t min_price; + + ok = cryptonote::parse_amount(min_price, min_price_str); + if(!ok || 0 == min_price) + { + fail_msg_writer() << tr("amount is wrong: ") << min_price_str << + ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); + return false; + } sfx_offer.set_price_peg(price_peg_id,new_price,min_price); return true; From cb0221d2856d84dc26e0e9a3a759887660420b12 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 13 May 2020 15:46:09 +0200 Subject: [PATCH 10/52] Add message that digits are also avaliable for safex account name --- src/simplewallet/simplewallet_safex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet_safex.cpp b/src/simplewallet/simplewallet_safex.cpp index 6760561e7..42889598b 100644 --- a/src/simplewallet/simplewallet_safex.cpp +++ b/src/simplewallet/simplewallet_safex.cpp @@ -1344,7 +1344,7 @@ namespace cryptonote for(auto ch: username){ if (!(std::islower(ch) || std::isdigit(ch)) && ch!='_' && ch!='-') { - fail_msg_writer() << tr("safex account username can only have lowercase letters, _ and -"); + fail_msg_writer() << tr("safex account username can only have lowercase letters, digits, _ and -"); return true; } } From 9d5a7eeb384d4ed2f30015197c83be253910dc75 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 13 May 2020 15:53:08 +0200 Subject: [PATCH 11/52] Show error if wrong number of arguments for "safex_account keys" are sent --- src/simplewallet/simplewallet_safex.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/simplewallet/simplewallet_safex.cpp b/src/simplewallet/simplewallet_safex.cpp index 42889598b..b0670e552 100644 --- a/src/simplewallet/simplewallet_safex.cpp +++ b/src/simplewallet/simplewallet_safex.cpp @@ -1416,6 +1416,10 @@ namespace cryptonote } else if (command == "keys") { + if(local_args.size() != 1){ + fail_msg_writer() << tr("One Safex account username was not given"); + return true; + } const std::string &username = local_args[0]; if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } From 36f4157be779e8d8f340a148101c87960d1a5d8d Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 13 May 2020 16:13:23 +0200 Subject: [PATCH 12/52] Show message when trying to remove safex account that does not exist --- src/simplewallet/simplewallet_safex.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/simplewallet/simplewallet_safex.cpp b/src/simplewallet/simplewallet_safex.cpp index b0670e552..9b223ec75 100644 --- a/src/simplewallet/simplewallet_safex.cpp +++ b/src/simplewallet/simplewallet_safex.cpp @@ -1374,6 +1374,12 @@ namespace cryptonote { const std::string &username = local_args[0]; + safex::safex_account sfx_acc; + if (!m_wallet->get_safex_account(username, sfx_acc)) { + fail_msg_writer() << tr("Safex account does not exist: ") << username; + return true; + } + auto pass = get_and_verify_password(); From 71b2e338c3222158b94917876affadbd5f4a44c0 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 13 May 2020 16:20:24 +0200 Subject: [PATCH 13/52] Small refactor for error handling --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 40f88b086..fb51684ed 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4439,7 +4439,7 @@ size_t wallet::pop_best_value_from(const transfer_container &transfers, std::vec } - THROW_WALLET_EXCEPTION_IF(candidates.empty(), error::safex_unknown_account); + THROW_WALLET_EXCEPTION_IF(candidates.empty(), error::safex_unknown_id); int idx = -1; for (size_t n = 0; n < candidates.size(); ++n) From 72be464369da94998c576e311aa5987ccf29f602 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 13 May 2020 17:31:19 +0200 Subject: [PATCH 14/52] Small refactor of storing safex account keys --- src/simplewallet/simplewallet_safex.cpp | 2 -- src/wallet/api/wallet.cpp | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet_safex.cpp b/src/simplewallet/simplewallet_safex.cpp index 9b223ec75..8ea4c63c0 100644 --- a/src/simplewallet/simplewallet_safex.cpp +++ b/src/simplewallet/simplewallet_safex.cpp @@ -1387,7 +1387,6 @@ namespace cryptonote return true; if (m_wallet->remove_safex_account(username) && save_safex(pass->password()) ) { - save_safex(pass->password()); success_msg_writer() << tr("Account removed"); } else { fail_msg_writer() << tr("Failed to remove account ") << username; @@ -1414,7 +1413,6 @@ namespace cryptonote return true; if (m_wallet->recover_safex_account(username, skey) && save_safex(pass->password()) ) { - save_safex(pass->password()); success_msg_writer() << tr("Account recovered"); } else { fail_msg_writer() << tr("Failed to recover account ") << username; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c3369fda3..c7095fb18 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1567,8 +1567,10 @@ bool WalletImpl::recoverSafexAccount(const std::string& username, const std::str bool WalletImpl::removeSafexAccount(const std::string& username){ - if (m_wallet->remove_safex_account(username)) + if (m_wallet->remove_safex_account(username)) { + m_wallet->store_safex(m_password); return true; + } return false; } From 2084fb8c8a6c81003a58f57c1b8d179ae6aa9ff9 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 13 May 2020 17:52:36 +0200 Subject: [PATCH 15/52] Store safex account keys file even if no account is present --- src/wallet/wallet.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fb51684ed..76474baef 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2763,8 +2763,6 @@ bool wallet::store_keys(const std::string& keys_file_name, const epee::wipeable_ */ bool wallet::store_safex_keys(const std::string& safex_keys_file_name, const epee::wipeable_string& password) { - if(m_safex_accounts.empty()) - return true; std::string safex_keys_data; bool r; From a498262f048a5ed89646bb86b8b354a120abb1e6 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 13:53:32 +0200 Subject: [PATCH 16/52] Small refactor in check_safex_tx --- src/cryptonote_core/blockchain.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0b3a38cf9..97b4f500c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3022,16 +3022,15 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & if (tx.version == 1) return true; - std::vector input_commands_to_execute; + std::vector input_commands_to_execute; //Transaction must have commands of only one type: safex::command_t command_type = safex::command_t::invalid_command; - for (size_t i = 0; i < tx.vin.size(); i++) + for (auto txin: tx.vin) { - const txin_v &txin = tx.vin[i]; if ((txin.type() == typeid(txin_to_script))) { - const txin_to_script &txin_script = boost::get(tx.vin[i]); + const txin_to_script &txin_script = boost::get(txin); safex::command_t tmp = txin_script.command_type; //multiple different commands on input, error if ((command_type == safex::command_t::token_unstake && tmp == safex::command_t::distribute_network_fee) || @@ -3047,7 +3046,7 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & command_type = tmp; } - input_commands_to_execute.push_back(&txin_script); + input_commands_to_execute.push_back(txin_script); } } @@ -3058,8 +3057,8 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & } //validate all command logic - for (const txin_to_script* pcmd: input_commands_to_execute) - if (!safex::validate_safex_command(*m_db, *pcmd)) { + for (const txin_to_script cmd: input_commands_to_execute) + if (!safex::validate_safex_command(*m_db, cmd)) { tvc.m_safex_command_execution_failed = true; return false; } From 4e29ff9035bc6bbccdad3fd3fea9e9bd31afee76 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 15:10:27 +0200 Subject: [PATCH 17/52] Limit tx input script to: 1 for any command 2 if commands are token_unstake and distribute_network_fee 1 or more if commands are token_stake or donate_network_fee --- src/cryptonote_core/blockchain.cpp | 42 +++++++++++++++++++----------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 97b4f500c..eff2bbb88 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3026,34 +3026,46 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & //Transaction must have commands of only one type: safex::command_t command_type = safex::command_t::invalid_command; + + bool unstake_seen = false; + bool network_fee_seen = false; + bool only_donate_seen = true; + bool only_stake_seen = true; + for (auto txin: tx.vin) { if ((txin.type() == typeid(txin_to_script))) { const txin_to_script &txin_script = boost::get(txin); - safex::command_t tmp = txin_script.command_type; - //multiple different commands on input, error - if ((command_type == safex::command_t::token_unstake && tmp == safex::command_t::distribute_network_fee) || - (command_type == safex::command_t::distribute_network_fee && tmp == safex::command_t::token_unstake)) { - //this is ok - } - else if (command_type != safex::command_t::invalid_command && command_type != tmp) { - tvc.m_safex_verification_failed = true; - return false; - } + + if(txin_script.command_type == safex::command_t::token_unstake) + unstake_seen = true; + + if(txin_script.command_type == safex::command_t::distribute_network_fee) + network_fee_seen = true; + + if(txin_script.command_type != safex::command_t::donate_network_fee) + only_donate_seen = false; + + if(txin_script.command_type != safex::command_t::token_stake) + only_stake_seen = false; if (command_type == safex::command_t::invalid_command) { - command_type = tmp; + command_type = txin_script.command_type; } input_commands_to_execute.push_back(txin_script); } } - //there is no valid command found on input - if (command_type == safex::command_t::invalid_command) { - tvc.m_safex_invalid_command = true; - return false; + // Per TX there can be : + // * 1 command for all types + // * 2 commands if they are 1 unstake token and 1 distribute network fee + // * >1 commands if they are all stake token or donate_network_fee + if (!(input_commands_to_execute.size() == 1 || (input_commands_to_execute.size() == 2 && unstake_seen && network_fee_seen) + || (input_commands_to_execute.size() > 1 && (only_donate_seen || only_stake_seen)))) { + tvc.m_safex_invalid_command = true; + return false; } //validate all command logic From 9fc74108123d1627df31286aa809686d8d7d99bc Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 16:13:53 +0200 Subject: [PATCH 18/52] Check tx for all commands in the input --- src/cryptonote_core/blockchain.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index eff2bbb88..c4b3fc365 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3024,9 +3024,6 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & std::vector input_commands_to_execute; - //Transaction must have commands of only one type: - safex::command_t command_type = safex::command_t::invalid_command; - bool unstake_seen = false; bool network_fee_seen = false; bool only_donate_seen = true; @@ -3050,10 +3047,6 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & if(txin_script.command_type != safex::command_t::token_stake) only_stake_seen = false; - if (command_type == safex::command_t::invalid_command) { - command_type = txin_script.command_type; - } - input_commands_to_execute.push_back(txin_script); } } @@ -3069,13 +3062,13 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & } //validate all command logic - for (const txin_to_script cmd: input_commands_to_execute) + for (const txin_to_script cmd: input_commands_to_execute){ if (!safex::validate_safex_command(*m_db, cmd)) { tvc.m_safex_command_execution_failed = true; return false; } - + safex::command_t command_type = cmd.command_type; if (command_type == safex::command_t::token_stake) @@ -3507,7 +3500,7 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & tvc.m_safex_invalid_command = true; return false; } - + } return true; } From 99c34807b8a1ef08776f05bbbfab193686fd304d Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 16:34:56 +0200 Subject: [PATCH 19/52] Move safex command tx verification to separate function --- src/cryptonote_core/blockchain.cpp | 740 ++++++++++++++--------------- src/cryptonote_core/blockchain.h | 10 + 2 files changed, 371 insertions(+), 379 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c4b3fc365..07b46a0f1 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3063,446 +3063,428 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & //validate all command logic for (const txin_to_script cmd: input_commands_to_execute){ - if (!safex::validate_safex_command(*m_db, cmd)) { - tvc.m_safex_command_execution_failed = true; - return false; + + if (!safex::validate_safex_command(*m_db, cmd)) { + tvc.m_safex_command_execution_failed = true; + return false; } - safex::command_t command_type = cmd.command_type; + if (!check_safex_tx_command(tx, cmd.command_type)){ + tvc.m_safex_invalid_input = true; + return false; + } + } - if (command_type == safex::command_t::token_stake) - { - /* Find amount of output staked tokens */ - uint64_t outputs_staked_token_amount = 0; - for (const auto &vout: tx.vout) - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_staked_token) - { - const txout_to_script &out = boost::get(vout.target); - if (out.output_type == static_cast(tx_out_type::out_staked_token)) - outputs_staked_token_amount += vout.token_amount; - } + return true; +} +//------------------------------------------------------------------ +bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::command_t &command_type){ - /* Check if minumum amount of tokens is staked */ - if (outputs_staked_token_amount < safex::get_minimum_token_stake_amount(m_nettype)) + if (command_type == safex::command_t::token_stake) { - MERROR("Safex token stake amount too small, must be at least "<< cryptonote::print_money(safex::get_minimum_token_stake_amount(m_nettype))); - tvc.m_safex_invalid_command_params = true; - return false; - } - } - else if (command_type == safex::command_t::token_unstake) - { - //Check if tokens are staked long enough - for (const txin_v &txin: tx.vin) - { - if (txin.type() == typeid(txin_to_script)) - { - //TODO: Grki check if absolute is needed - const txin_to_script &in = boost::get(txin); - for (auto index: in.key_offsets) { - output_advanced_data_t out = this->m_db->get_output_advanced_data(tx_out_type::out_staked_token, index); - if (out.height+safex::get_safex_minumum_token_lock_period(m_nettype) > m_db->height()) { - MERROR("Safex token stake period not expired at height"<height()); - tvc.m_safex_invalid_command_params = true; + /* Find amount of output staked tokens */ + uint64_t outputs_staked_token_amount = 0; + for (const auto &vout: tx.vout) + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_staked_token) + { + const txout_to_script &out = boost::get(vout.target); + if (out.output_type == static_cast(tx_out_type::out_staked_token)) + outputs_staked_token_amount += vout.token_amount; + } + + /* Check if minumum amount of tokens is staked */ + if (outputs_staked_token_amount < safex::get_minimum_token_stake_amount(m_nettype)) + { + MERROR("Safex token stake amount too small, must be at least "<< cryptonote::print_money(safex::get_minimum_token_stake_amount(m_nettype))); return false; - } } + } + else if (command_type == safex::command_t::token_unstake) + { + //Check if tokens are staked long enough + for (const txin_v &txin: tx.vin) + { + if (txin.type() == typeid(txin_to_script)) + { + //TODO: Grki check if absolute is needed + const txin_to_script &in = boost::get(txin); + for (auto index: in.key_offsets) { + output_advanced_data_t out = this->m_db->get_output_advanced_data(tx_out_type::out_staked_token, index); + if (out.height+safex::get_safex_minumum_token_lock_period(m_nettype) > m_db->height()) { + MERROR("Safex token stake period not expired at height"<height()); + return false; + } + } - } + } + } } - } - else if (command_type == safex::command_t::donate_network_fee) - { - /* Find cash amount on output that is donated */ - uint64_t outputs_donated_cash_amount = 0; - for (const auto &vout: tx.vout) + else if (command_type == safex::command_t::donate_network_fee) { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_network_fee) - { - const txout_to_script &out = boost::get(vout.target); - if (out.output_type == static_cast(tx_out_type::out_network_fee)) - outputs_donated_cash_amount += vout.amount; - } - } + /* Find cash amount on output that is donated */ + uint64_t outputs_donated_cash_amount = 0; + for (const auto &vout: tx.vout) + { + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_network_fee) + { + const txout_to_script &out = boost::get(vout.target); + if (out.output_type == static_cast(tx_out_type::out_network_fee)) + outputs_donated_cash_amount += vout.amount; + } + } - uint64_t input_cash_amount = 0; - for (const auto &txin: tx.vin) - { - input_cash_amount += get_tx_input_cash_amount(txin); - } + uint64_t input_cash_amount = 0; + for (const auto &txin: tx.vin) + { + input_cash_amount += get_tx_input_cash_amount(txin); + } - /* Check if donated cash amount matches */ - if (outputs_donated_cash_amount >= input_cash_amount) - { - MERROR("Invalid safex cash input amount"); - tvc.m_safex_invalid_input = true; - return false; + /* Check if donated cash amount matches */ + if (outputs_donated_cash_amount >= input_cash_amount) + { + MERROR("Invalid safex cash input amount"); + return false; + } } - } - else if (command_type == safex::command_t::distribute_network_fee) - { - /* Find cash and token amount that is distributed, check if they match */ - uint64_t distributed_cash_amount = 0; - uint64_t unstaked_token_amount = 0; - uint64_t expected_interest = 0; - for (const auto &txin: tx.vin) + else if (command_type == safex::command_t::distribute_network_fee) { - if (txin.type() == typeid(txin_to_script)) - { - const txin_to_script &stxin = boost::get(txin); - if (stxin.command_type == safex::command_t::token_unstake) + /* Find cash and token amount that is distributed, check if they match */ + uint64_t distributed_cash_amount = 0; + uint64_t unstaked_token_amount = 0; + uint64_t expected_interest = 0; + for (const auto &txin: tx.vin) { - unstaked_token_amount += stxin.token_amount; - expected_interest += calculate_staked_token_interest_for_output(stxin, m_db->height()); + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::token_unstake) + { + unstaked_token_amount += stxin.token_amount; + expected_interest += calculate_staked_token_interest_for_output(stxin, m_db->height()); + } + else if (stxin.command_type == safex::command_t::distribute_network_fee) + { + distributed_cash_amount += stxin.amount; + + if (stxin.key_offsets.size() != 1) + { + MERROR("Interest should be distributed for particular token stake output"); + return false; + } + } + } } - else if (stxin.command_type == safex::command_t::distribute_network_fee) + /* Check if donated cash amount matches */ + if (distributed_cash_amount > expected_interest) { - distributed_cash_amount += stxin.amount; - - if (stxin.key_offsets.size() != 1) - { - MERROR("Interest should be distributed for particular token stake output"); - tvc.m_safex_invalid_input = true; + MERROR("Token unstake interest too high"); return false; - } } - } } - /* Check if donated cash amount matches */ - if (distributed_cash_amount > expected_interest) + else if (command_type == safex::command_t::create_account) { - MERROR("Token unstake interest too high"); - tvc.m_safex_invalid_input = true; - return false; - } - } - else if (command_type == safex::command_t::create_account) - { - uint64_t total_locked_tokens = 0; + uint64_t total_locked_tokens = 0; - for (const auto &vout: tx.vout) - { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_account) - { - const txout_to_script &out = boost::get(vout.target); - safex::create_account_data account; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, account); - //check username for uniqueness - crypto::public_key temppkey{}; - if (get_safex_account_public_key(safex::account_username{account.username}, temppkey)) + for (const auto &vout: tx.vout) { - std::string username(std::begin(account.username), std::end(account.username)); - MERROR("Account with username "+username+" already exists"); - tvc.m_safex_invalid_input = true; - return false; - } + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_account) + { + const txout_to_script &out = boost::get(vout.target); + safex::create_account_data account; + const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(accblob, account); + //check username for uniqueness + crypto::public_key temppkey{}; + if (get_safex_account_public_key(safex::account_username{account.username}, temppkey)) + { + std::string username(std::begin(account.username), std::end(account.username)); + MERROR("Account with username "+username+" already exists"); + return false; + } + + //check if account pkey is valid + if (!crypto::check_key(account.pkey)) + { + MERROR("Account public key not valid"); + return false; + } + + if (account.username.size() > SAFEX_ACCOUNT_USERNAME_MAX_SIZE) + { + MERROR("Account username is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_USERNAME_MAX_SIZE)); + return false; + } + + if (account.account_data.size() > SAFEX_ACCOUNT_DATA_MAX_SIZE) + { + MERROR("Account data is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_DATA_MAX_SIZE)); + return false; + } + } + if (vout.target.type() == typeid(txout_token_to_key) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_token && vout.token_amount == SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE) + { + total_locked_tokens += vout.token_amount; + } - //check if account pkey is valid - if (!crypto::check_key(account.pkey)) - { - MERROR("Account public key not valid"); - tvc.m_safex_invalid_input = true; - return false; } - if (account.username.size() > SAFEX_ACCOUNT_USERNAME_MAX_SIZE) - { - MERROR("Account username is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_USERNAME_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; + if(total_locked_tokens < SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE){ + MERROR("Not enough tokens given as output. Needed: " + std::to_string(SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE) + ", actual sent: "+std::to_string(total_locked_tokens) ); + return false; } - if (account.account_data.size() > SAFEX_ACCOUNT_DATA_MAX_SIZE) + } + else if (command_type == safex::command_t::edit_account) + { + + for (const auto &vout: tx.vout) { - MERROR("Account data is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_DATA_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_account_update) + { + const txout_to_script &out = boost::get(vout.target); + safex::edit_account_data account; + const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(accblob, account); + //check username for uniqueness + crypto::public_key temppkey{}; + if (!m_db->get_account_key(safex::account_username{account.username}, temppkey)) + { + std::string username(std::begin(account.username), std::end(account.username)); + MERROR("Account with username "+username+" does not exists"); + return false; + } + + if (account.username.size() > SAFEX_ACCOUNT_USERNAME_MAX_SIZE) + { + MERROR("Account username is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_USERNAME_MAX_SIZE)); + return false; + } + + //check account new data size + if (account.account_data.size() > SAFEX_ACCOUNT_DATA_MAX_SIZE) + { + MERROR("Account data is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_DATA_MAX_SIZE)); + return false; + } + } } - } - if (vout.target.type() == typeid(txout_token_to_key) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_token && vout.token_amount == SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE) + } + else if (command_type == safex::command_t::create_offer) + { + //todo check for signature of account owner + //TODO: Make additional checks + for (const auto &vout: tx.vout) { - total_locked_tokens += vout.token_amount; + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer) + { + const txout_to_script &out = boost::get(vout.target); + safex::create_offer_data offer; + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + //check username for uniqueness + crypto::public_key temppkey{}; + if (!m_db->get_account_key(safex::account_username{offer.seller}, temppkey)) + { + std::string username(std::begin(offer.seller), std::end(offer.seller)); + MERROR("Account with username "+username+" does not exists"); + return false; + } + + if (offer.title.size() > SAFEX_OFFER_NAME_MAX_SIZE) + { + MERROR("Offer title is bigger than max allowed " + std::to_string(SAFEX_OFFER_NAME_MAX_SIZE)); + return false; + } + + //check offer data size + if (offer.description.size() > SAFEX_OFFER_DATA_MAX_SIZE) + { + MERROR("Offer data is bigger than max allowed " + std::to_string(SAFEX_OFFER_DATA_MAX_SIZE)); + return false; + } + } } - } - - if(total_locked_tokens < SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE){ - MERROR("Not enough tokens given as output. Needed: " + std::to_string(SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE) + ", actual sent: "+std::to_string(total_locked_tokens) ); - tvc.m_safex_invalid_input = true; - return false; - } - - } - else if (command_type == safex::command_t::edit_account) - { - - for (const auto &vout: tx.vout) + else if (command_type == safex::command_t::edit_offer) { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_account_update) - { - const txout_to_script &out = boost::get(vout.target); - safex::edit_account_data account; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, account); - //check username for uniqueness - crypto::public_key temppkey{}; - if (!m_db->get_account_key(safex::account_username{account.username}, temppkey)) + //todo check for signature of account owner + //TODO: Make additional checks + for (const auto &vout: tx.vout) { - std::string username(std::begin(account.username), std::end(account.username)); - MERROR("Account with username "+username+" does not exists"); - tvc.m_safex_invalid_input = true; - return false; + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer_update) + { + const txout_to_script &out = boost::get(vout.target); + safex::edit_offer_data offer; + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + //check username for uniqueness + crypto::public_key temppkey{}; + if (!m_db->get_account_key(safex::account_username{offer.seller}, temppkey)) + { + std::string username(std::begin(offer.seller), std::end(offer.seller)); + MERROR("Account with username "+username+" does not exists"); + return false; + } + + if (offer.title.size() > SAFEX_OFFER_NAME_MAX_SIZE) + { + MERROR("Offer title is bigger than max allowed " + std::to_string(SAFEX_OFFER_NAME_MAX_SIZE)); + return false; + } + + //check offer data size + if (offer.description.size() > SAFEX_OFFER_DATA_MAX_SIZE) + { + MERROR("Offer data is bigger than max allowed " + std::to_string(SAFEX_OFFER_DATA_MAX_SIZE)); + return false; + } + } } + } + else if (command_type == safex::command_t::simple_purchase) + { + uint64_t network_fee = 0; + uint64_t product_payment = 0; + uint64_t total_payment = 0; + crypto::secret_key secret_seller_view_key; + crypto::public_key public_seller_spend_key; - if (account.username.size() > SAFEX_ACCOUNT_USERNAME_MAX_SIZE) + if (tx.unlock_time > m_db->height()) { - MERROR("Account username is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_USERNAME_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; + MERROR("Purchase TX should not be locked"); + return false; } - //check account new data size - if (account.account_data.size() > SAFEX_ACCOUNT_DATA_MAX_SIZE) - { - MERROR("Account data is bigger than max allowed " + std::to_string(SAFEX_ACCOUNT_DATA_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; + for (const auto &vout: tx.vout) { + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) + { + const txout_to_script &out = boost::get(vout.target); + safex::safex_offer offer_to_purchase; + safex::create_purchase_data purchase; + const cryptonote::blobdata purchaseblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(purchaseblob, purchase); + total_payment = purchase.price; + get_safex_offer(purchase.offer_id, offer_to_purchase); + + secret_seller_view_key = offer_to_purchase.seller_private_view_key; + public_seller_spend_key = offer_to_purchase.seller_address.m_spend_public_key; + } } - } - } - } - else if (command_type == safex::command_t::create_offer) - { - //todo check for signature of account owner - //TODO: Make additional checks - for (const auto &vout: tx.vout) - { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer) - { - const txout_to_script &out = boost::get(vout.target); - safex::create_offer_data offer; - const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(offerblob, offer); - //check username for uniqueness - crypto::public_key temppkey{}; - if (!m_db->get_account_key(safex::account_username{offer.seller}, temppkey)) - { - std::string username(std::begin(offer.seller), std::end(offer.seller)); - MERROR("Account with username "+username+" does not exists"); - tvc.m_safex_invalid_input = true; - return false; - } - - if (offer.title.size() > SAFEX_OFFER_NAME_MAX_SIZE) - { - MERROR("Offer title is bigger than max allowed " + std::to_string(SAFEX_OFFER_NAME_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; - } - - //check offer data size - if (offer.description.size() > SAFEX_OFFER_DATA_MAX_SIZE) - { - MERROR("Offer data is bigger than max allowed " + std::to_string(SAFEX_OFFER_DATA_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; - } - } - } - } - else if (command_type == safex::command_t::edit_offer) - { - //todo check for signature of account owner - //TODO: Make additional checks - for (const auto &vout: tx.vout) - { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer_update) - { - const txout_to_script &out = boost::get(vout.target); - safex::edit_offer_data offer; - const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(offerblob, offer); - //check username for uniqueness - crypto::public_key temppkey{}; - if (!m_db->get_account_key(safex::account_username{offer.seller}, temppkey)) - { - std::string username(std::begin(offer.seller), std::end(offer.seller)); - MERROR("Account with username "+username+" does not exists"); - tvc.m_safex_invalid_input = true; - return false; - } - - if (offer.title.size() > SAFEX_OFFER_NAME_MAX_SIZE) - { - MERROR("Offer title is bigger than max allowed " + std::to_string(SAFEX_OFFER_NAME_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; - } - - //check offer data size - if (offer.description.size() > SAFEX_OFFER_DATA_MAX_SIZE) - { - MERROR("Offer data is bigger than max allowed " + std::to_string(SAFEX_OFFER_DATA_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; - } - } - } - } - else if (command_type == safex::command_t::simple_purchase) - { - uint64_t network_fee = 0; - uint64_t product_payment = 0; - uint64_t total_payment = 0; - crypto::secret_key secret_seller_view_key; - crypto::public_key public_seller_spend_key; - - if (tx.unlock_time > m_db->height()) - { - MERROR("Purchase TX should not be locked"); - tvc.m_safex_invalid_input = true; - return false; - } - - for (const auto &vout: tx.vout) { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) - { - const txout_to_script &out = boost::get(vout.target); - safex::safex_offer offer_to_purchase; - safex::create_purchase_data purchase; - const cryptonote::blobdata purchaseblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(purchaseblob, purchase); - total_payment = purchase.price; - get_safex_offer(purchase.offer_id, offer_to_purchase); - - secret_seller_view_key = offer_to_purchase.seller_private_view_key; - public_seller_spend_key = offer_to_purchase.seller_address.m_spend_public_key; - } - } - std::vector seller_outs= is_safex_purchase_right_address(secret_seller_view_key, public_seller_spend_key, tx); + std::vector seller_outs= is_safex_purchase_right_address(secret_seller_view_key, public_seller_spend_key, tx); - for (const auto &vout: tx.vout) - { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_network_fee) - { - network_fee += vout.amount; - } - else if (vout.target.type() == typeid(txout_to_key) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_cash) - { - const crypto::public_key &out = *boost::apply_visitor(destination_public_key_visitor(), vout.target); - auto it = std::find(seller_outs.begin(),seller_outs.end(),out); - if(it!=seller_outs.end()) - product_payment += vout.amount; - } - } - - //check network fee payment - if (total_payment*5/100 > network_fee) - { - MERROR("Not enough cash given for network fee"); - tvc.m_safex_invalid_input = true; - return false; - } - //check purchase cash payment - if (total_payment*95/100 > product_payment) - { - MERROR("Not enough cash given for product payment"); - tvc.m_safex_invalid_input = true; - return false; - } - } - else if (command_type == safex::command_t::create_feedback) - { - //TODO: Make additional checks - for (const auto &vout: tx.vout) - { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_feedback) - { - const txout_to_script &out = boost::get(vout.target); - safex::create_feedback_data feedback; - const cryptonote::blobdata feedbackblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(feedbackblob, feedback); - //TODO: check if OfferID exists - } - } - } - else if (command_type == safex::command_t::create_price_peg) - { - //todo check for signature of account owner - //TODO: Make additional checks - for (const auto &vout: tx.vout) - { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg) - { - const txout_to_script &out = boost::get(vout.target); - safex::create_price_peg_data price_peg; - const cryptonote::blobdata price_peg_blob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(price_peg_blob, price_peg); - //check username for uniqueness - crypto::public_key temppkey{}; - if (!m_db->get_account_key(safex::account_username{price_peg.creator}, temppkey)) + for (const auto &vout: tx.vout) { - std::string username(std::begin(price_peg.creator), std::end(price_peg.creator)); - MERROR("Account with username "+username+" does not exists"); - tvc.m_safex_invalid_input = true; - return false; + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_network_fee) + { + network_fee += vout.amount; + } + else if (vout.target.type() == typeid(txout_to_key) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_cash) + { + const crypto::public_key &out = *boost::apply_visitor(destination_public_key_visitor(), vout.target); + auto it = std::find(seller_outs.begin(),seller_outs.end(),out); + if(it!=seller_outs.end()) + product_payment += vout.amount; + } } - if (price_peg.title.size() > SAFEX_PRICE_PEG_NAME_MAX_SIZE) + //check network fee payment + if (total_payment*5/100 > network_fee) { - MERROR("Price peg title is bigger than max allowed " + std::to_string(SAFEX_PRICE_PEG_NAME_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; + MERROR("Not enough cash given for network fee"); + return false; } - - if (price_peg.currency.size() > SAFEX_PRICE_PEG_CURRENCY_MAX_SIZE) + //check purchase cash payment + if (total_payment*95/100 > product_payment) { - MERROR("Price peg currency name is bigger than max allowed " + std::to_string(SAFEX_PRICE_PEG_CURRENCY_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; + MERROR("Not enough cash given for product payment"); + return false; } - - //check price peg data size - if (price_peg.description.size() > SAFEX_PRICE_PEG_DATA_MAX_SIZE) + } + else if (command_type == safex::command_t::create_feedback) + { + //TODO: Make additional checks + for (const auto &vout: tx.vout) { - MERROR("Price peg data is bigger than max allowed " + std::to_string(SAFEX_PRICE_PEG_DATA_MAX_SIZE)); - tvc.m_safex_invalid_input = true; - return false; + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_feedback) + { + const txout_to_script &out = boost::get(vout.target); + safex::create_feedback_data feedback; + const cryptonote::blobdata feedbackblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(feedbackblob, feedback); + //TODO: check if OfferID exists + } } - } } - } - else if (command_type == safex::command_t::update_price_peg) - { - //todo check for signature of account owner - //TODO: Make additional checks - for (const auto &vout: tx.vout) + else if (command_type == safex::command_t::create_price_peg) { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) - { - const txout_to_script &out = boost::get(vout.target); - safex::update_price_peg_data price_peg; - const cryptonote::blobdata price_peg_blob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(price_peg_blob, price_peg); + //todo check for signature of account owner + //TODO: Make additional checks + for (const auto &vout: tx.vout) + { + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg) + { + const txout_to_script &out = boost::get(vout.target); + safex::create_price_peg_data price_peg; + const cryptonote::blobdata price_peg_blob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(price_peg_blob, price_peg); + //check username for uniqueness + crypto::public_key temppkey{}; + if (!m_db->get_account_key(safex::account_username{price_peg.creator}, temppkey)) + { + std::string username(std::begin(price_peg.creator), std::end(price_peg.creator)); + MERROR("Account with username "+username+" does not exists"); + return false; + } + + if (price_peg.title.size() > SAFEX_PRICE_PEG_NAME_MAX_SIZE) + { + MERROR("Price peg title is bigger than max allowed " + std::to_string(SAFEX_PRICE_PEG_NAME_MAX_SIZE)); + return false; + } + + if (price_peg.currency.size() > SAFEX_PRICE_PEG_CURRENCY_MAX_SIZE) + { + MERROR("Price peg currency name is bigger than max allowed " + std::to_string(SAFEX_PRICE_PEG_CURRENCY_MAX_SIZE)); + return false; + } + + //check price peg data size + if (price_peg.description.size() > SAFEX_PRICE_PEG_DATA_MAX_SIZE) + { + MERROR("Price peg data is bigger than max allowed " + std::to_string(SAFEX_PRICE_PEG_DATA_MAX_SIZE)); + return false; + } + } + } + } + else if (command_type == safex::command_t::update_price_peg) + { + //todo check for signature of account owner + //TODO: Make additional checks + for (const auto &vout: tx.vout) + { + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) + { + const txout_to_script &out = boost::get(vout.target); + safex::update_price_peg_data price_peg; + const cryptonote::blobdata price_peg_blob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(price_peg_blob, price_peg); - } + } + } + } + else + { + MERROR("Unsupported safex command"); + return false; } - } - else - { - MERROR("Unsupported safex command"); - tvc.m_safex_invalid_command = true; - return false; - } - } - return true; + return true; } //------------------------------------------------------------------ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index e46cc3edb..7f6138dab 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -623,6 +623,16 @@ namespace cryptonote */ bool check_safex_tx(const transaction &tx, tx_verification_context &tvc); + /** + * @brief check if transaction outputs and inputs satisfy command type restrictions + * + * @param tx the transaction to validate + * @param command_type command type that is being checked + * + * @return returns false if tx does not hold safex related restrictions, otherwise true + */ + bool check_safex_tx_command(const transaction &tx, const safex::command_t& command_type); + bool are_safex_tokens_unlocked(const std::vector &tx_vin); /** From d2be5ac174792b6980cab093376ae004830a5a95 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 17:21:24 +0200 Subject: [PATCH 20/52] Check if output staked token amount is not higher than input staked tokens --- src/cryptonote_core/blockchain.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 07b46a0f1..adde5414d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3083,6 +3083,15 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm if (command_type == safex::command_t::token_stake) { + /* Find amount of input staked tokens */ + uint64_t inputs_staked_token_amount = 0; + for(const auto &vin: tx.vin) + if ((vin.type() == typeid(txin_to_script))){ + const txin_to_script &txin_script = boost::get(vin); + if(txin_script.command_type == safex::command_t::token_stake) + inputs_staked_token_amount += txin_script.token_amount; + } + /* Find amount of output staked tokens */ uint64_t outputs_staked_token_amount = 0; for (const auto &vout: tx.vout) @@ -3099,6 +3108,12 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm MERROR("Safex token stake amount too small, must be at least "<< cryptonote::print_money(safex::get_minimum_token_stake_amount(m_nettype))); return false; } + /* Check if amount of staked tokens in the output is less or equal to the amount in the input*/ + if (inputs_staked_token_amount < outputs_staked_token_amount) + { + MERROR("Safex token stake output amount higher than input amount"); + return false; + } } else if (command_type == safex::command_t::token_unstake) { From ee4b03b8e519af0fdba299ce627038402ccf370f Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 18:51:56 +0200 Subject: [PATCH 21/52] Check whole tx once per command type --- src/cryptonote_core/blockchain.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index adde5414d..c9a977e00 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3023,6 +3023,7 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & if (tx.version == 1) return true; std::vector input_commands_to_execute; + std::set input_commands_to_check; bool unstake_seen = false; bool network_fee_seen = false; @@ -3048,6 +3049,7 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & only_stake_seen = false; input_commands_to_execute.push_back(txin_script); + input_commands_to_check.insert(txin_script.command_type); } } @@ -3062,14 +3064,15 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & } //validate all command logic - for (const txin_to_script cmd: input_commands_to_execute){ - + for (const txin_to_script cmd: input_commands_to_execute) if (!safex::validate_safex_command(*m_db, cmd)) { tvc.m_safex_command_execution_failed = true; return false; - } + } - if (!check_safex_tx_command(tx, cmd.command_type)){ + //check all commands tx restrictions + for (const safex::command_t cmd_type: input_commands_to_check){ + if (!check_safex_tx_command(tx, cmd_type)){ tvc.m_safex_invalid_input = true; return false; } @@ -3122,7 +3125,6 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm { if (txin.type() == typeid(txin_to_script)) { - //TODO: Grki check if absolute is needed const txin_to_script &in = boost::get(txin); for (auto index: in.key_offsets) { output_advanced_data_t out = this->m_db->get_output_advanced_data(tx_out_type::out_staked_token, index); From b608559871a9820acbbadb8cbc78f02c25bafdad Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 19:13:51 +0200 Subject: [PATCH 22/52] Allow only 1 create_account output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 35 +++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c9a977e00..b1c329b36 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3133,10 +3133,7 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm return false; } } - - } - } } else if (command_type == safex::command_t::donate_network_fee) @@ -3205,15 +3202,47 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm { uint64_t total_locked_tokens = 0; + bool create_account_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::create_account) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_object(command.script, command.command_type); + std::unique_ptr result(dynamic_cast(cmd->execute(*m_db, command))); + if (result->status != safex::execution_status::ok) + { + LOG_ERROR("Execution of create account command failed, status:" << static_cast(result->status)); + return false; + } for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_account) { + if(create_account_seen) + { + MERROR("Multiple Safex account creation outputs"); + return false; + } + create_account_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::create_account_data account; const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(accblob, account); + + if(result->username != account.username || result->pkey != account.pkey || result->account_data != account.account_data){ + MERROR("Output data not matching input command data"); + return false; + } + //check username for uniqueness crypto::public_key temppkey{}; if (get_safex_account_public_key(safex::account_username{account.username}, temppkey)) From 8bb581de973d7a303857c2756d4fbaf05c3ff609 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 19:23:56 +0200 Subject: [PATCH 23/52] Allow only 1 edit_account output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b1c329b36..860874945 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3286,15 +3286,47 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } else if (command_type == safex::command_t::edit_account) { + bool edit_account_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::edit_account) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_object(command.script, command.command_type); + std::unique_ptr result(dynamic_cast(cmd->execute(*m_db, command))); + if (result->status != safex::execution_status::ok) + { + LOG_ERROR("Execution of edit account command failed, status:" << static_cast(result->status)); + return false; + } for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_account_update) { + if(edit_account_seen) + { + MERROR("Multiple Safex account edit outputs"); + return false; + } + edit_account_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::edit_account_data account; const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(accblob, account); + + if(result->username != account.username || result->account_data != account.account_data){ + MERROR("Output data not matching input command data"); + return false; + } + //check username for uniqueness crypto::public_key temppkey{}; if (!m_db->get_account_key(safex::account_username{account.username}, temppkey)) From 60d40e6d147feea5d94e11a2df656ac946a64612 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 19:43:34 +0200 Subject: [PATCH 24/52] Use cmd fields instead of execute results when comparing input and output of advanced_tx --- src/cryptonote_core/blockchain.cpp | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 860874945..6f79fc439 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3214,13 +3214,7 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } } } - std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_object(command.script, command.command_type); - std::unique_ptr result(dynamic_cast(cmd->execute(*m_db, command))); - if (result->status != safex::execution_status::ok) - { - LOG_ERROR("Execution of create account command failed, status:" << static_cast(result->status)); - return false; - } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); for (const auto &vout: tx.vout) { @@ -3237,8 +3231,10 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm safex::create_account_data account; const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(accblob, account); + std::string account_username(std::begin(account.username), std::end(account.username)); - if(result->username != account.username || result->pkey != account.pkey || result->account_data != account.account_data){ + + if(cmd->get_username() != account_username || cmd->get_account_key() != account.pkey || cmd->get_account_data() != account.account_data){ MERROR("Output data not matching input command data"); return false; } @@ -3298,13 +3294,8 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } } } - std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_object(command.script, command.command_type); - std::unique_ptr result(dynamic_cast(cmd->execute(*m_db, command))); - if (result->status != safex::execution_status::ok) - { - LOG_ERROR("Execution of edit account command failed, status:" << static_cast(result->status)); - return false; - } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); + for (const auto &vout: tx.vout) { @@ -3321,8 +3312,10 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm safex::edit_account_data account; const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(accblob, account); + std::string account_username(std::begin(account.username), std::end(account.username)); + - if(result->username != account.username || result->account_data != account.account_data){ + if(cmd->get_username() != account_username || cmd->get_new_account_data() != account.account_data){ MERROR("Output data not matching input command data"); return false; } From 1ac61ad1213519b866f3246599e9d7aab6e5c1b5 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 19:58:11 +0200 Subject: [PATCH 25/52] Allow only 1 create_offer output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 34 ++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 6f79fc439..3b3293fb1 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3346,16 +3346,46 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } else if (command_type == safex::command_t::create_offer) { - //todo check for signature of account owner - //TODO: Make additional checks + bool create_offer_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::create_offer) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); + + for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer) { + if(create_offer_seen) + { + MERROR("Multiple Safex offer create outputs"); + return false; + } + create_offer_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::create_offer_data offer; const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(offerblob, offer); + + if(cmd->get_offerid() != offer.offer_id || cmd->get_price_peg_id() != offer.price_peg_id || cmd->get_seller() != offer.seller + || cmd->get_title() != offer.title || cmd->get_price() != offer.price || cmd->get_min_sfx_price() != offer.min_sfx_price + || cmd->get_quantity() != offer.quantity || cmd->get_active() != offer.active || cmd->get_price_peg_used() != offer.price_peg_used + || cmd->get_description() != offer.description || cmd->get_seller_private_view_key() != offer.seller_private_view_key + || cmd->get_seller_address() != offer.seller_address){ + MERROR("Output data not matching input command data"); + return false; + } + //check username for uniqueness crypto::public_key temppkey{}; if (!m_db->get_account_key(safex::account_username{offer.seller}, temppkey)) From aecca3ba52a6394edb9fb814fd193a58567e5118 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 15 May 2020 20:01:39 +0200 Subject: [PATCH 26/52] Allow only 1 create_offer output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 32 ++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3b3293fb1..c9313e2a6 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3412,16 +3412,44 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } else if (command_type == safex::command_t::edit_offer) { - //todo check for signature of account owner - //TODO: Make additional checks + bool edit_offer_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::edit_offer) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); + for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer_update) { + if(edit_offer_seen) + { + MERROR("Multiple Safex offer edit outputs"); + return false; + } + edit_offer_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::edit_offer_data offer; const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(offerblob, offer); + + if(cmd->get_offerid() != offer.offer_id || cmd->get_price_peg_id() != offer.price_peg_id || cmd->get_seller() != offer.seller + || cmd->get_title() != offer.title || cmd->get_price() != offer.price || cmd->get_min_sfx_price() != offer.min_sfx_price + || cmd->get_quantity() != offer.quantity || cmd->get_active() != offer.active || cmd->get_price_peg_used() != offer.price_peg_used + || cmd->get_description() != offer.description){ + MERROR("Output data not matching input command data"); + return false; + } + //check username for uniqueness crypto::public_key temppkey{}; if (!m_db->get_account_key(safex::account_username{offer.seller}, temppkey)) From 89e4865c1582534f17064a51492558886125cac3 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 16 May 2020 12:03:09 +0200 Subject: [PATCH 27/52] Allow only 1 safex_purchase output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 30 ++++++++++++++++++++++++++++++ src/safex/command.h | 4 ++++ 2 files changed, 34 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c9313e2a6..4037b002e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3481,6 +3481,20 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm uint64_t total_payment = 0; crypto::secret_key secret_seller_view_key; crypto::public_key public_seller_spend_key; + bool purchase_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::simple_purchase) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); + if (tx.unlock_time > m_db->height()) { @@ -3491,11 +3505,27 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) { + if(purchase_seen) + { + MERROR("Multiple Safex purchase outputs"); + return false; + } + purchase_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::safex_offer offer_to_purchase; safex::create_purchase_data purchase; const cryptonote::blobdata purchaseblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(purchaseblob, purchase); + + if(cmd->get_offerid() != purchase.offer_id + || cmd->get_price() != purchase.price + || cmd->get_quantity() != purchase.quantity + || cmd->get_shipping() != purchase.shipping){ + MERROR("Output data not matching input command data"); + return false; + } + total_payment = purchase.price; get_safex_offer(purchase.offer_id, offer_to_purchase); diff --git a/src/safex/command.h b/src/safex/command.h index 83e0a672b..65e6152d8 100644 --- a/src/safex/command.h +++ b/src/safex/command.h @@ -752,6 +752,10 @@ struct create_price_peg_result : public execution_result simple_purchase() : command(0, command_t::simple_purchase) {} + crypto::hash get_offerid(){ return offer_id; } + uint64_t get_quantity(){ return quantity; } + uint64_t get_price(){ return price; } + bool get_shipping() { return shipping; } virtual simple_purchase_result* execute(const cryptonote::BlockchainDB &blockchain, const cryptonote::txin_to_script &txin) override; virtual execution_status validate(const cryptonote::BlockchainDB &blokchain, const cryptonote::txin_to_script &txin) override; From 8f4d1cb1151dec9b94636509167f4c644a774a4a Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 16 May 2020 12:08:31 +0200 Subject: [PATCH 28/52] Allow only 1 safex_feedback output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 4037b002e..1537721a7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3566,16 +3566,42 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } else if (command_type == safex::command_t::create_feedback) { - //TODO: Make additional checks + bool feedback_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::create_feedback) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); + for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_feedback) { + if(feedback_seen) + { + MERROR("Multiple Safex feedback outputs"); + return false; + } + feedback_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::create_feedback_data feedback; const cryptonote::blobdata feedbackblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(feedbackblob, feedback); - //TODO: check if OfferID exists + + if(cmd->get_offerid() != feedback.offer_id + || cmd->get_stars_given() != feedback.stars_given + || cmd->get_comment() != feedback.comment){ + MERROR("Output data not matching input command data"); + return false; + } } } } From ae62c18cc78f126780b3080bc271604a47b828ce Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 16 May 2020 12:13:47 +0200 Subject: [PATCH 29/52] Allow only 1 create_price_peg output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 34 ++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 1537721a7..62901820e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3607,16 +3607,46 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } else if (command_type == safex::command_t::create_price_peg) { - //todo check for signature of account owner - //TODO: Make additional checks + bool create_price_peg_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::create_price_peg) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); + for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg) { + if(create_price_peg_seen) + { + MERROR("Multiple Safex create price peg outputs"); + return false; + } + create_price_peg_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::create_price_peg_data price_peg; const cryptonote::blobdata price_peg_blob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(price_peg_blob, price_peg); + + if(cmd->get_title() != price_peg.title + || cmd->get_price_peg_id() != price_peg.price_peg_id + || cmd->get_creator() != price_peg.creator + || cmd->get_description() != price_peg.description + || cmd->get_currency() != price_peg.currency + || cmd->get_rate() != price_peg.rate){ + MERROR("Output data not matching input command data"); + return false; + } + //check username for uniqueness crypto::public_key temppkey{}; if (!m_db->get_account_key(safex::account_username{price_peg.creator}, temppkey)) From fbc6a1aaa8921b19f443645c5b53945045857cc2 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 16 May 2020 12:17:08 +0200 Subject: [PATCH 30/52] Allow only 1 update_price_peg output and check if output data matches input script data --- src/cryptonote_core/blockchain.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 62901820e..1dd2e512b 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3679,17 +3679,42 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } else if (command_type == safex::command_t::update_price_peg) { - //todo check for signature of account owner - //TODO: Make additional checks + bool update_price_peg_seen = false; + txin_to_script command; + for(auto txin: tx.vin){ + if (txin.type() == typeid(txin_to_script)) + { + const txin_to_script &stxin = boost::get(txin); + if (stxin.command_type == safex::command_t::update_price_peg) + { + command = stxin; + } + } + } + std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(command.script); + for (const auto &vout: tx.vout) { if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) { + if(update_price_peg_seen) + { + MERROR("Multiple Safex update price peg outputs"); + return false; + } + update_price_peg_seen = true; + const txout_to_script &out = boost::get(vout.target); safex::update_price_peg_data price_peg; const cryptonote::blobdata price_peg_blob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(price_peg_blob, price_peg); + if(cmd->get_price_peg_id() != price_peg.price_peg_id + || cmd->get_rate() != price_peg.rate){ + MERROR("Output data not matching input command data"); + return false; + } + } } } From ab28966e9bd65088d459ce5ad5a3749acd02121f Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Mon, 18 May 2020 10:57:40 +0200 Subject: [PATCH 31/52] Add restictions to tx_pool when updating price peg --- src/cryptonote_core/tx_pool.cpp | 44 ++++++++++++++++++--------------- src/cryptonote_core/tx_pool.h | 12 ++++++++- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 99fa1b874..dc40161ae 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -504,13 +504,11 @@ namespace cryptonote m_safex_accounts_in_use.push_back(username); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) { - //TODO: GRKI check this case as we do not have creator in update price peg -// const txout_to_script &out = boost::get(vout.target); -// safex::update_price_peg_data price_peg; -// const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); -// cryptonote::parse_and_validate_from_blob(accblob, price_peg); -// std::string username{price_peg..begin(),price_peg.creator.end()}; -// m_safex_accounts_in_use.push_back(username); + const txout_to_script &out = boost::get(vout.target); + safex::update_price_peg_data price_peg; + const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(accblob, price_peg); + m_safex_price_peg_update_in_progress.push_back(price_peg.price_peg_id); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) { const txout_to_script &out = boost::get(vout.target); @@ -622,12 +620,13 @@ namespace cryptonote m_safex_accounts_in_use.erase(it); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) { - //TODO: GRKI check this case as we do not have creator in update price peg - // const txout_to_script &out = boost::get(vout.target); - // safex::update_price_peg_data price_peg; - // const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - // cryptonote::parse_and_validate_from_blob(accblob, price_peg); - // std::string username{price_peg.creator.begin(),price_peg.creator.end()}; + const txout_to_script &out = boost::get(vout.target); + safex::update_price_peg_data price_peg; + const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(accblob, price_peg); + auto it = std::find(m_safex_price_peg_update_in_progress.begin(), m_safex_price_peg_update_in_progress.end(), price_peg.price_peg_id); + CHECK_AND_ASSERT_MES(it != m_safex_price_peg_update_in_progress.end(), false, "failed to find safex restriction for type out_safex_price_peg_update" << ENDL << "transaction id = " << get_transaction_hash(tx)); + m_safex_price_peg_update_in_progress.erase(it); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) { const txout_to_script &out = boost::get(vout.target); @@ -1177,13 +1176,12 @@ namespace cryptonote return true; } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) { - //TODO: GRKI check this case as we do not have creator in update price peg - // const txout_to_script &out = boost::get(vout.target); - // safex::update_price_peg_data price_peg; - // const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - // cryptonote::parse_and_validate_from_blob(accblob, price_peg); - // std::string username{price_peg..begin(),price_peg.creator.end()}; - // m_safex_accounts_in_use.push_back(username); + const txout_to_script &out = boost::get(vout.target); + safex::update_price_peg_data price_peg; + const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(accblob, price_peg); + if(have_tx_safex_price_peg_in_use(price_peg.price_peg_id)) + return true; } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) { const txout_to_script &out = boost::get(vout.target); @@ -1215,6 +1213,12 @@ namespace cryptonote return m_safex_purchase_in_progress.end() != std::find(m_safex_purchase_in_progress.begin(), m_safex_purchase_in_progress.end(), offer_id); } //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx_safex_price_peg_in_use(const crypto::hash &price_peg_id) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + return m_safex_price_peg_update_in_progress.end() != std::find(m_safex_price_peg_update_in_progress.begin(), m_safex_price_peg_update_in_progress.end(), price_peg_id); + } + //--------------------------------------------------------------------------------- void tx_memory_pool::lock() const { m_transactions_lock.lock(); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index acf0baad0..d708cee52 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -431,7 +431,7 @@ namespace cryptonote bool insert_key_images(const transaction &tx, bool kept_by_block); /** - * @brief insert safex data into m_safex_accounts_in_use and m_safex_purchase_in_progress + * @brief insert safex data into m_safex_accounts_in_use, m_safex_purchase_in_progress and m_safex_price_peg_update_in_progress * * @return true on success, false on error */ @@ -475,6 +475,15 @@ namespace cryptonote */ bool have_tx_safex_purchase_in_progress(const crypto::hash& offer_id) const; + /** + * @brief check if a transaction in the pool has a safex price peg usage + * + * @param price_peg_id Price peg ID that is in use + * + * @return true if the safex price peg is in use already, otherwise false + */ + bool have_tx_safex_price_peg_in_use(const crypto::hash& price_peg_id) const; + /** * @brief check if any spent key image in a transaction is in the pool * @@ -593,6 +602,7 @@ namespace cryptonote // Safex related members std::vector m_safex_accounts_in_use; std::vector m_safex_purchase_in_progress; + std::vector m_safex_price_peg_update_in_progress; //TODO: this time should be a named constant somewhere, not hard-coded //! interval on which to check for stale/"stuck" transactions From 4a0ff56ab5d37434f7c897828dc9e5179db1045a Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Mon, 18 May 2020 11:16:24 +0200 Subject: [PATCH 32/52] Check if safex tokens are locked when they are again used for safex account creation --- src/cryptonote_core/blockchain.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 1dd2e512b..21e13d854 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -6620,7 +6620,24 @@ bool Blockchain::are_safex_tokens_unlocked(const std::vector &tx_vin) { output_token_fee.height + safex::get_safex_minumum_account_create_period(m_nettype) > m_db->height()) return false; } - } + } else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::create_account)) + { + const txin_to_script &in = boost::get(txin); + if(in.token_amount != SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE) + return true; + const std::vector absolute = cryptonote::relative_output_offsets_to_absolute(in.key_offsets); + + // Now we search the offsets and find their txs + for (auto index: absolute) { + tx_out_index toi = this->m_db->get_output_tx_and_index(in.token_amount, index, tx_out_type::out_token); + auto output_token_fee = this->m_db->get_output_key(in.token_amount, index, tx_out_type::out_token); + cryptonote::transaction tx = m_db->get_tx(toi.first); + //Now we search for script input + if(is_create_safex_account_token_fee(tx.vout,output_token_fee.pubkey) && + output_token_fee.height + safex::get_safex_minumum_account_create_period(m_nettype) > m_db->height()) + return false; + } + } } return true; From 442344c9f432049197a1bd81823ca22e55fab480 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Mon, 18 May 2020 14:15:39 +0200 Subject: [PATCH 33/52] Don't include locked token outputs when creating safex account --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 76474baef..be77a1734 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -8884,7 +8884,7 @@ std::vector wallet::create_transactions_advanced(safex::comm } ++num_nondust_staked_token_outputs; } - else if (td.token_amount() > 0 && td.m_output_type == cryptonote::tx_out_type::out_token) + else if (td.token_amount() > 0 && td.m_output_type == cryptonote::tx_out_type::out_token && is_token_transfer_unlocked(td)) { auto found = std::find_if(unused_token_transfers_indices_per_subaddr.begin(), unused_token_transfers_indices_per_subaddr.end(), find_predicate); if (found == unused_token_transfers_indices_per_subaddr.end()) From 6fabe84162e57139719e159c5a879dc7f0ff9611 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Mon, 18 May 2020 14:16:24 +0200 Subject: [PATCH 34/52] Limit create_safex_account token locking to 10 blocks for testnet --- src/cryptonote_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index f9f17e286..6b59bf049 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -178,7 +178,7 @@ #define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE ((uint64_t)100*SAFEX_TOKEN) #define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD_FAKECHAIN ((uint64_t)1) -#define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD_TESTNET ((uint64_t)150) +#define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD_TESTNET ((uint64_t)10) #define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD ((uint64_t)150)// TBD #define SAFEX_ACCOUNT_USERNAME_MAX_SIZE 32 From ffcade23eff12b43f93a28580a9083bd9f45276c Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Mon, 18 May 2020 22:34:43 +0200 Subject: [PATCH 35/52] Add API calls for staked token balance --- src/wallet/api/wallet.cpp | 10 ++++++++++ src/wallet/api/wallet.h | 2 ++ src/wallet/api/wallet_api.h | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c7095fb18..5d3007033 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1036,6 +1036,16 @@ uint64_t WalletImpl::unlockedTokenBalance(uint32_t accountIndex) const return m_wallet->unlocked_token_balance(accountIndex); } +uint64_t WalletImpl::stakedTokenBalance(uint32_t accountIndex) const +{ + return m_wallet->staked_token_balance(accountIndex); +} + +uint64_t WalletImpl::unlockedStakedTokenBalance(uint32_t accountIndex) const +{ + return m_wallet->unlocked_staked_token_balance(accountIndex); +} + uint64_t WalletImpl::blockChainHeight() const { if(m_wallet->light_wallet()) { diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 9feb72bc9..a785b9d4b 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -104,6 +104,8 @@ class WalletImpl : public Wallet uint64_t unlockedBalance(uint32_t accountIndex = 0) const; uint64_t tokenBalance(uint32_t accountIndex = 0) const; uint64_t unlockedTokenBalance(uint32_t accountIndex = 0) const; + uint64_t stakedTokenBalance(uint32_t accountIndex = 0) const; + uint64_t unlockedStakedTokenBalance(uint32_t accountIndex = 0) const; uint64_t blockChainHeight() const; uint64_t approximateBlockChainHeight() const; uint64_t daemonBlockChainHeight() const; diff --git a/src/wallet/api/wallet_api.h b/src/wallet/api/wallet_api.h index 6196c607b..56c25f873 100644 --- a/src/wallet/api/wallet_api.h +++ b/src/wallet/api/wallet_api.h @@ -819,6 +819,22 @@ struct Wallet return result; } + virtual uint64_t stakedTokenBalance(uint32_t accountIndex = 0) const = 0; + uint64_t stakedTokenBalanceAll() const { + uint64_t result = 0; + for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) + result += stakedTokenBalance(i); + return result; + } + + virtual uint64_t unlockedStakedTokenBalance(uint32_t accountIndex = 0) const = 0; + uint64_t unlockedStakedTokenBalanceAll() const { + uint64_t result = 0; + for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) + result += unlockedStakedTokenBalance(i); + return result; + } + /** * @brief watchOnly - checks if wallet is watch only * @return - true if watch only From 1c6607090871d681157b793c4e3fc3a854bf06b4 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Tue, 19 May 2020 19:32:46 +0200 Subject: [PATCH 36/52] Small refactor. Set create feedback as normal use case. --- src/cryptonote_core/cryptonote_tx_utils.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 117c8da87..446c722d8 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -1679,10 +1679,6 @@ namespace cryptonote //todo Atana, figure out how to handle this case MCINFO("construct_tx", "donation " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str()); } - else if (src_entr.referenced_output_type == tx_out_type::out_safex_feedback_token && src_entr.command_type == safex::command_t::create_feedback) { - //todo Atana, figure out how to handle this case - crypto::generate_ring_signature(tx_prefix_hash, k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); - } else { crypto::generate_ring_signature(tx_prefix_hash, k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); } From 652bf27b918e07e9467ad62114d832678281a92b Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Tue, 19 May 2020 23:53:21 +0200 Subject: [PATCH 37/52] Remove distribute_network_fee input and calculate amount in the script input of token_unstake --- src/blockchain_db/lmdb/db_lmdb.cpp | 4 - src/cryptonote_basic/cryptonote_basic.h | 2 - .../cryptonote_format_utils.cpp | 2 +- src/cryptonote_core/blockchain.cpp | 88 +++++++------------ src/cryptonote_core/cryptonote_core.cpp | 5 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 30 +------ src/safex/command.cpp | 25 ------ src/safex/command.h | 39 -------- src/safex/safex_core.h | 1 - src/wallet/wallet.cpp | 20 +---- tests/core_tests/chaingen.cpp | 22 +---- 11 files changed, 41 insertions(+), 197 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index e086017df..b82ba9a60 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1586,10 +1586,6 @@ void BlockchainLMDB::process_command_input(const cryptonote::txin_to_script &txi else if (txin.command_type == safex::command_t::donate_network_fee) { //network_fee_sum is updated at place of output processing - } - else if (txin.command_type == safex::command_t::distribute_network_fee) - { - } else if (txin.command_type == safex::command_t::create_account) { diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 036749f3d..347ae77e4 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -570,8 +570,6 @@ namespace cryptonote return tx_out_type::out_safex_price_peg; case safex::command_t::create_feedback: return tx_out_type::out_safex_feedback_token; - case safex::command_t::distribute_network_fee: - return tx_out_type::out_network_fee; case safex::command_t::nop: default: return tx_out_type::out_invalid; diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index f4b1553b5..ae2f046dc 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -660,7 +660,7 @@ namespace cryptonote if (vin.type() == typeid(txin_to_script)) { const txin_to_script& in = boost::get(vin); - if (in.command_type == safex::command_t::distribute_network_fee) { + if (in.command_type == safex::command_t::token_unstake) { network_fee += in.amount; } } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 21e13d854..a629382e0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -338,9 +338,6 @@ bool Blockchain::scan_outputkeys_for_indexes input_commands_to_execute; std::set input_commands_to_check; - bool unstake_seen = false; - bool network_fee_seen = false; bool only_donate_seen = true; bool only_stake_seen = true; @@ -3036,12 +3031,6 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & { const txin_to_script &txin_script = boost::get(txin); - if(txin_script.command_type == safex::command_t::token_unstake) - unstake_seen = true; - - if(txin_script.command_type == safex::command_t::distribute_network_fee) - network_fee_seen = true; - if(txin_script.command_type != safex::command_t::donate_network_fee) only_donate_seen = false; @@ -3055,9 +3044,8 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context & // Per TX there can be : // * 1 command for all types - // * 2 commands if they are 1 unstake token and 1 distribute network fee // * >1 commands if they are all stake token or donate_network_fee - if (!(input_commands_to_execute.size() == 1 || (input_commands_to_execute.size() == 2 && unstake_seen && network_fee_seen) + if (!(input_commands_to_execute.size() == 1 || (input_commands_to_execute.size() > 1 && (only_donate_seen || only_stake_seen)))) { tvc.m_safex_invalid_command = true; return false; @@ -3135,36 +3123,9 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } } } - } - else if (command_type == safex::command_t::donate_network_fee) - { - /* Find cash amount on output that is donated */ - uint64_t outputs_donated_cash_amount = 0; - for (const auto &vout: tx.vout) - { - if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_network_fee) - { - const txout_to_script &out = boost::get(vout.target); - if (out.output_type == static_cast(tx_out_type::out_network_fee)) - outputs_donated_cash_amount += vout.amount; - } - } - uint64_t input_cash_amount = 0; - for (const auto &txin: tx.vin) - { - input_cash_amount += get_tx_input_cash_amount(txin); - } + //TODO: GRKI Refactor this - /* Check if donated cash amount matches */ - if (outputs_donated_cash_amount >= input_cash_amount) - { - MERROR("Invalid safex cash input amount"); - return false; - } - } - else if (command_type == safex::command_t::distribute_network_fee) - { /* Find cash and token amount that is distributed, check if they match */ uint64_t distributed_cash_amount = 0; uint64_t unstaked_token_amount = 0; @@ -3178,9 +3139,6 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm { unstaked_token_amount += stxin.token_amount; expected_interest += calculate_staked_token_interest_for_output(stxin, m_db->height()); - } - else if (stxin.command_type == safex::command_t::distribute_network_fee) - { distributed_cash_amount += stxin.amount; if (stxin.key_offsets.size() != 1) @@ -3198,6 +3156,33 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm return false; } } + else if (command_type == safex::command_t::donate_network_fee) + { + /* Find cash amount on output that is donated */ + uint64_t outputs_donated_cash_amount = 0; + for (const auto &vout: tx.vout) + { + if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_network_fee) + { + const txout_to_script &out = boost::get(vout.target); + if (out.output_type == static_cast(tx_out_type::out_network_fee)) + outputs_donated_cash_amount += vout.amount; + } + } + + uint64_t input_cash_amount = 0; + for (const auto &txin: tx.vin) + { + input_cash_amount += get_tx_input_cash_amount(txin); + } + + /* Check if donated cash amount matches */ + if (outputs_donated_cash_amount >= input_cash_amount) + { + MERROR("Invalid safex cash input amount"); + return false; + } + } else if (command_type == safex::command_t::create_account) { @@ -3831,7 +3816,7 @@ bool Blockchain::check_advanced_tx_input(const txin_to_script &txin, tx_verifica } else if (txin.command_type == safex::command_t::token_unstake) { - if (txin.amount > 0 || txin.token_amount == 0) + if (txin.token_amount == 0) return false; } else if (txin.command_type == safex::command_t::donate_network_fee) @@ -3839,12 +3824,6 @@ bool Blockchain::check_advanced_tx_input(const txin_to_script &txin, tx_verifica if (txin.amount == 0 || txin.token_amount > 0) return false; } - else if (txin.command_type == safex::command_t::distribute_network_fee) - { - //todo atana calculate if interest amount matches - if (txin.amount == 0 || txin.token_amount > 0) - return false; - } else if (txin.command_type == safex::command_t::create_account) { if (txin.amount != 0 || txin.token_amount == 0) //create account input references (spends some of token outputs), in total SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE @@ -4106,10 +4085,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, if (txin.type() == typeid(txin_token_migration)) { tpool.submit(&waiter, boost::bind(&Blockchain::check_migration_signature, this, std::cref(tx_prefix_hash), std::cref(tx.signatures[sig_index][0]), std::ref(results[sig_index]))); } - else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::distribute_network_fee)) { - //todo atana nothing to do here - results[sig_index] = true; - } else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::edit_account)) { std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(boost::get(txin).script); crypto::public_key account_pkey{}; @@ -4160,9 +4135,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { if (txin.type() == typeid(txin_token_migration)) { check_migration_signature(tx_prefix_hash, tx.signatures[sig_index][0], results[sig_index]); - } else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::distribute_network_fee) ) { - //todo atana nothing to do here - results[sig_index] = true; } else if ((txin.type() == typeid(txin_to_script)) && (boost::get(txin).command_type == safex::command_t::edit_account)) { std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(boost::get(txin).script); crypto::public_key account_pkey{}; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 68b37faf6..f4ce0d962 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1176,10 +1176,7 @@ namespace cryptonote } else if (in.type() == typeid(const txin_to_script)) { const txin_to_script &txin = boost::get(in); - if (txin.command_type == safex::command_t::distribute_network_fee) { - // todo atana: check if this is necessary - LOG_PRINT_L2("skip key image validation of distributed network fee"); - } else if (txin.command_type == safex::command_t::edit_account) { + if (txin.command_type == safex::command_t::edit_account) { //todo Atana optimize somehow key image validation, so many conversions const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(txin.script); diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 446c722d8..eee136792 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -635,25 +635,6 @@ namespace cryptonote safex::donate_fee cmd{SAFEX_COMMAND_PROTOCOL_VERSION, src_entr.amount}; safex::safex_command_serializer::serialize_safex_object(cmd, input.script); } - else if (src_entr.command_type == safex::command_t::distribute_network_fee) - { - input.amount = src_entr.amount; - input.k_image = AUTO_VAL_INIT(input.k_image); - //we will set kimage as output id of token stake output that is unstaked in this transaction - uint64_t temp = src_entr.outputs[0].first; - memcpy((void*)(&input.k_image), (char *)(&temp), sizeof(temp)); - - - //fill outputs array and use relative offsets - for (const tx_source_entry::output_entry &out_entry: src_entr.outputs) - input.key_offsets.push_back(out_entry.first); - - input.key_offsets = absolute_output_offsets_to_relative(input.key_offsets); - - //here, prepare data of transaction command execution and serialize command - safex::distribute_fee cmd{SAFEX_COMMAND_PROTOCOL_VERSION, src_entr.amount}; - safex::safex_command_serializer::serialize_safex_object(cmd, input.script); - } else if (src_entr.command_type == safex::command_t::create_account) { input.k_image = img; @@ -818,7 +799,7 @@ namespace cryptonote tx_destination_entry dst_entr{}; //add interest output for fee distribution - if (input_txin_to_script.command_type == safex::command_t::distribute_network_fee) { + if (input_txin_to_script.command_type == safex::command_t::token_unstake) { //find staked token amount matching to this interest uint64_t input_token_staked_amount = 0; uint64_t output_token_amount = 0; @@ -1271,9 +1252,10 @@ namespace cryptonote tx.vin.push_back(input_txin_to_script); //adhoc add destination for interest based on input distribute newtork fee command - if (input_txin_to_script.command_type == safex::command_t::distribute_network_fee) { + if (input_txin_to_script.command_type == safex::command_t::token_unstake) { tx_destination_entry dst_interest = adjust_advanced_outputs(sources, src_entr, input_txin_to_script, destinations); - destinations.push_back(dst_interest); + if(dst_interest.amount > 0) + destinations.push_back(dst_interest); } } @@ -1675,10 +1657,6 @@ namespace cryptonote crypto::generate_signature(tx_prefix_hash, sfx_acc_keys.m_public_key, sfx_acc_keys.m_secret_key, *sigs.data()); MCINFO("construct_tx", "sfx account advanced_output_id="<< src_entr.real_output); } - else if (src_entr.referenced_output_type == tx_out_type::out_network_fee && src_entr.command_type == safex::command_t::distribute_network_fee) { - //todo Atana, figure out how to handle this case - MCINFO("construct_tx", "donation " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str()); - } else { crypto::generate_ring_signature(tx_prefix_hash, k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); } diff --git a/src/safex/command.cpp b/src/safex/command.cpp index 515ca15b5..1a72d83d4 100644 --- a/src/safex/command.cpp +++ b/src/safex/command.cpp @@ -231,31 +231,6 @@ namespace safex return result; }; - - distribute_fee_result* distribute_fee::execute(const cryptonote::BlockchainDB &blokchainDB, const cryptonote::txin_to_script &txin) - { - execution_status result = validate(blokchainDB, txin); - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES(result == execution_status::ok, "Failed to validate distribute fee command", this->get_command_type()); - - distribute_fee_result *cr = new distribute_fee_result{}; - cr->amount = txin.amount; - cr->valid = true; - cr->status = execution_status::ok; - return cr; - }; - - execution_status distribute_fee::validate(const cryptonote::BlockchainDB &blokchainDB, const cryptonote::txin_to_script &txin) - { - - execution_status result = execution_status::ok; - if(!(txin.amount > 0)) - return execution_status::error_wrong_input_params; - if(txin.token_amount != 0) - return execution_status::error_wrong_input_params; - - return result; - }; - create_account_result* create_account::execute(const cryptonote::BlockchainDB &blokchainDB, const cryptonote::txin_to_script &txin) { diff --git a/src/safex/command.h b/src/safex/command.h index 65e6152d8..287ece083 100644 --- a/src/safex/command.h +++ b/src/safex/command.h @@ -117,11 +117,6 @@ namespace safex END_SERIALIZE() }; - struct distribute_fee_result : public execution_result - { - uint64_t amount = 0; //cash amount do donate to newtork token holders - }; - struct create_account_result : public execution_result { @@ -777,37 +772,6 @@ struct create_price_peg_result : public execution_result bool shipping{}; }; - - class distribute_fee : public command - { - public: - friend class safex_command_serializer; - - /** - * @param _version Safex command protocol version - * @param _donate_amount //amount of safex cash that will be distributed to token holders that unstake tokens - * */ - distribute_fee(const uint32_t _version, const uint64_t _donation_safex_cash_amount) : command(_version, command_t::distribute_network_fee), - safex_cash_amount(_donation_safex_cash_amount) {} - - distribute_fee() : command(0, command_t::distribute_network_fee), safex_cash_amount(0) {} - - uint64_t get_staked_token_output_index() const { return safex_cash_amount; } - - virtual distribute_fee_result* execute(const cryptonote::BlockchainDB &blokchain, const cryptonote::txin_to_script &txin) override; - virtual execution_status validate(const cryptonote::BlockchainDB &blokchain, const cryptonote::txin_to_script &txin) override; - - BEGIN_SERIALIZE_OBJECT() - FIELDS(*static_cast(this)) - CHECK_COMMAND_TYPE(this->get_command_type(), command_t::distribute_network_fee); - VARINT_FIELD(safex_cash_amount) - END_SERIALIZE() - - private: - - uint64_t safex_cash_amount; - }; - class create_account : public command { public: @@ -1159,9 +1123,6 @@ class update_price_peg : public command case safex::command_t::token_collect: return std::unique_ptr(parse_safex_object(buffer)); break; - case safex::command_t::distribute_network_fee: - return std::unique_ptr(parse_safex_object(buffer)); - break; case safex::command_t::donate_network_fee: return std::unique_ptr(parse_safex_object(buffer)); break; diff --git a/src/safex/safex_core.h b/src/safex/safex_core.h index 842493fe5..a74bf24a9 100644 --- a/src/safex/safex_core.h +++ b/src/safex/safex_core.h @@ -63,7 +63,6 @@ namespace safex token_unstake = 0x02, token_collect = 0x03, donate_network_fee = 0x04, /* Donate safex cash to newtork token holders */ - distribute_network_fee = 0x05, /* Distribute collected newtork fee to token holders */ simple_purchase = 0x06, create_account = 0x0A, /* Create Safex account */ edit_account = 0x0B, /* Edit Safex account */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index be77a1734..f173e0cf1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -6752,24 +6752,8 @@ void wallet::transfer_advanced(safex::command_t command_type, const std::vector< { src.command_type = safex::command_t::token_unstake; - //also, create additional source for interest distribution - cryptonote::tx_source_entry src_interest = AUTO_VAL_INIT(src_interest); - src_interest.command_type = safex::command_t::distribute_network_fee; - src_interest.referenced_output_type = tx_out_type::out_network_fee; - src_interest.real_output_in_tx_index = src.real_output_in_tx_index; //reference same token output - //******************************************************************************************************/ - //todo atana check if this is safe, if we can use same public key for interest, as ring size is only 1 - //******************************************************************************************************/ - src_interest.real_out_tx_key = src.real_out_tx_key; // here just for completion, does not actually used for check - src_interest.outputs = src.outputs; - src_interest.real_output = src.real_output; - src_interest.amount = get_interest_for_transfer(td); - - // add source and destinations - if (src_interest.amount > 0) - { - additional_sources.push_back(src_interest); - } + src.amount = get_interest_for_transfer(td); + } else if (command_type == safex::command_t::create_account && (!command_input_creted)) { diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 8c339b259..c897e5807 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -538,9 +538,6 @@ bool create_network_token_lock_interest_map(const std::vector if (in.command_type == safex::command_t::token_unstake) { currently_locked_tokens -= in.token_amount; } - else if (in.command_type == safex::command_t::distribute_network_fee) { - //nothing to do?? - } } @@ -916,23 +913,10 @@ bool fill_unstake_token_sources(std::vector &sources, const std if (sources_found) { - cryptonote::tx_source_entry ts_interest = AUTO_VAL_INIT(ts_interest); - ts_interest.referenced_output_type = cryptonote::tx_out_type::out_network_fee; - ts_interest.command_type = safex::command_t::distribute_network_fee; - ts_interest.amount = calculate_token_holder_interest_for_output(oi.blk_height, current_height, interest_map, oi.token_amount); - ts_interest.real_output_in_tx_index = oi.out_no; //reference same token output - //******************************************************************************************************/ - //todo atana check if this is safe, if we can use same public key for interest, as ring size is only 1 - //******************************************************************************************************/ - ts_interest.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // here just for completion, does not actually used for check - //ts_interest.real_out_tx_key = AUTO_VAL_INIT(ts_interest.real_out_tx_key); //not used - ts_interest.outputs = ts.outputs; - ts_interest.real_output = realOutput; - + ts.amount = calculate_token_holder_interest_for_output(oi.blk_height, current_height, interest_map, oi.token_amount); sources.push_back(ts); - if (ts_interest.amount > 0) - sources.push_back(ts_interest); + } @@ -1218,7 +1202,7 @@ void fill_token_unstake_tx_sources_and_destinations(const std::vector 0) { de_interest = create_interest_destination(to, source.amount); } } From 707b821e147096d7c87a46b44052a32a95e4e985 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 20 May 2020 19:50:08 +0200 Subject: [PATCH 38/52] Add check for input amount if network fee that is being collected is valid --- src/blockchain_db/blockchain_db.h | 9 +++++++ src/blockchain_db/lmdb/db_lmdb.cpp | 38 +++++++++++++++++++++++++++++ src/blockchain_db/lmdb/db_lmdb.h | 1 + src/cryptonote_core/blockchain.cpp | 35 +------------------------- src/safex/command.cpp | 4 +++ src/safex/command.h | 3 ++- tests/unit_tests/hardfork.cpp | 1 + tests/unit_tests/safex_commands.cpp | 1 + 8 files changed, 57 insertions(+), 35 deletions(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 8253eab2d..8d1117f88 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1770,6 +1770,15 @@ namespace cryptonote virtual bool get_interval_interest_map(const uint64_t start_height, const uint64_t end_height, safex::map_interval_interest &map) const = 0; + /** + * Returns accumulated interest for given output + * + * + * @param txin script of unstake command + * @param unlock_height height of the Blockchain when the command is called + * @return Total SFX network fee for given output + */ + virtual uint64_t calculate_staked_token_interest_for_output(const txin_to_script &txin, const uint64_t unlock_height) const = 0; /** * Get safex account public key diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index b82ba9a60..ee932c107 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -4859,6 +4859,44 @@ bool BlockchainLMDB::is_valid_transaction_output_type(const txout_target_v &txou return true; }; + uint64_t BlockchainLMDB::calculate_staked_token_interest_for_output(const txin_to_script &txin, const uint64_t unlock_height) const + { + + if (txin.command_type != safex::command_t::token_unstake) { + MERROR("Invalid command for interest calculation"); + return 0; + } + + output_advanced_data_t output_data = get_output_advanced_data(tx_out_type::out_staked_token, txin.key_offsets[0]); + + if (output_data.height == 0) { + MERROR("Invalid output lock height"); + return 0; + } + + uint64_t starting_interval = safex::calculate_interval_for_height(output_data.height, m_nettype) + 1; + uint64_t end_interval = safex::calculate_interval_for_height(unlock_height, m_nettype) - 1; + + if (starting_interval > end_interval) { + MERROR("Calculating interest for invalid intervals"); + return 0; + } + + safex::map_interval_interest interest_map; + if (!get_interval_interest_map(starting_interval, end_interval, interest_map)) { + MERROR("Could not get interval map"); + return 0; + } + + uint64_t interest = 0; + for (uint64_t i=starting_interval;i<=end_interval;++i) { + interest += interest_map[i]*(txin.token_amount/SAFEX_TOKEN); + } + + return interest; + } + + void BlockchainLMDB::add_safex_account(const safex::account_username &username, const blobdata &blob) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 9134f5859..338270479 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -320,6 +320,7 @@ class BlockchainLMDB : public BlockchainDB virtual uint64_t get_network_fee_sum_for_interval(const uint64_t interval) const override; virtual std::vector get_token_stake_expiry_outputs(const uint64_t block_height) const override; virtual bool get_interval_interest_map(const uint64_t start_interval, const uint64_t end_interval, safex::map_interval_interest &map) const override; + virtual uint64_t calculate_staked_token_interest_for_output(const txin_to_script &txin, const uint64_t unlock_height) const override; virtual bool get_account_key(const safex::account_username &username, crypto::public_key &pkey) const; virtual bool get_account_data(const safex::account_username &username, std::vector &data) const; virtual bool get_offer(const crypto::hash offer_id, safex::safex_offer &offer) const; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index a629382e0..21edd0cdf 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -6286,40 +6286,7 @@ uint64_t Blockchain::calculate_staked_token_interest(const uint64_t token_amount uint64_t Blockchain::calculate_staked_token_interest_for_output(const txin_to_script &txin, const uint64_t unlock_height) const { - uint64_t ret = 0; - - if (txin.command_type != safex::command_t::token_unstake) { - MERROR("Invalid command for interest calculation"); - return 0; - } - - output_advanced_data_t output_data = m_db->get_output_advanced_data(tx_out_type::out_staked_token, txin.key_offsets[0]); - - if (output_data.height == 0) { - MERROR("Invalid output lock height"); - return 0; - } - - uint64_t starting_interval = safex::calculate_interval_for_height(output_data.height, m_nettype) + 1; - uint64_t end_interval = safex::calculate_interval_for_height(unlock_height, m_nettype) - 1; - - if (starting_interval > end_interval) { - MERROR("Calculating interest for invalid intervals"); - return 0; - } - - safex::map_interval_interest interest_map; - if (!m_db->get_interval_interest_map(starting_interval, end_interval, interest_map)) { - MERROR("Could not get interval map"); - return 0; - } - - uint64_t interest = 0; - for (uint64_t i=starting_interval;i<=end_interval;++i) { - interest += interest_map[i]*(txin.token_amount/SAFEX_TOKEN); - } - - return interest; + return m_db->calculate_staked_token_interest_for_output(txin, unlock_height); } std::map Blockchain::get_interest_map(uint64_t begin_interval, uint64_t end_interval) diff --git a/src/safex/command.cpp b/src/safex/command.cpp index 1a72d83d4..5b062cc16 100644 --- a/src/safex/command.cpp +++ b/src/safex/command.cpp @@ -94,6 +94,10 @@ namespace safex if(out.token_amount == txin.token_amount) output_found = true; + uint64_t expected_interest = blokchainDB.calculate_staked_token_interest_for_output(txin, blokchainDB.height()); + + if(txin.amount > expected_interest) + result = execution_status::error_unstake_token_network_fee_not_matching; if(!output_found) result = execution_status::error_unstake_token_output_not_found; diff --git a/src/safex/command.h b/src/safex/command.h index 287ece083..15aa4913a 100644 --- a/src/safex/command.h +++ b/src/safex/command.h @@ -58,7 +58,8 @@ namespace safex error_feedback_data_too_big = 25, error_price_peg_rate_zero = 26, error_unstake_token_output_not_found = 27, - error_unstake_token_minimum_period = 28 + error_unstake_token_minimum_period = 28, + error_unstake_token_network_fee_not_matching = 29 }; struct execution_result diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 5f0b37626..dde225107 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -158,6 +158,7 @@ class TestDB: public BlockchainDB { virtual uint64_t get_network_fee_sum_for_interval(const uint64_t interval) const override {return 0;} virtual std::vector get_token_stake_expiry_outputs(const uint64_t block_height) const override {return std::vector{};} virtual bool get_interval_interest_map(const uint64_t start_height, const uint64_t end_height, safex::map_interval_interest &map) const override {return true;} + virtual uint64_t calculate_staked_token_interest_for_output(const txin_to_script &txin, const uint64_t unlock_height) const override { return 0; } virtual void add_block( const block& blk , const size_t& block_size diff --git a/tests/unit_tests/safex_commands.cpp b/tests/unit_tests/safex_commands.cpp index a9549c393..d9ca15f02 100644 --- a/tests/unit_tests/safex_commands.cpp +++ b/tests/unit_tests/safex_commands.cpp @@ -304,6 +304,7 @@ class TestBlockchainDB : public cryptonote::BlockchainDB virtual uint64_t get_network_fee_sum_for_interval(const uint64_t interval) const override {return 0;} virtual std::vector get_token_stake_expiry_outputs(const uint64_t block_height) const override {return std::vector{};} virtual bool get_interval_interest_map(const uint64_t start_height, const uint64_t end_height, safex::map_interval_interest &map) const override {return true;} + virtual uint64_t calculate_staked_token_interest_for_output(const cryptonote::txin_to_script &txin, const uint64_t unlock_height) const override { return 0; }; virtual bool get_account_key(const safex::account_username &username, crypto::public_key &pkey) const { return true;} virtual bool get_account_data(const safex::account_username &username, std::vector &data) const { return true;} virtual bool get_offer(const crypto::hash offer_id, safex::safex_offer &offer) const { return true;} From 4d25904922045e4f726270c5b3a9c28fcd4b6b2e Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 20 May 2020 20:43:04 +0200 Subject: [PATCH 39/52] Refactor check_safex_tx_command for unstake_token --- src/cryptonote_core/blockchain.cpp | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 21edd0cdf..ec958bf4e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3108,25 +3108,6 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } else if (command_type == safex::command_t::token_unstake) { - //Check if tokens are staked long enough - for (const txin_v &txin: tx.vin) - { - if (txin.type() == typeid(txin_to_script)) - { - const txin_to_script &in = boost::get(txin); - for (auto index: in.key_offsets) { - output_advanced_data_t out = this->m_db->get_output_advanced_data(tx_out_type::out_staked_token, index); - if (out.height+safex::get_safex_minumum_token_lock_period(m_nettype) > m_db->height()) { - MERROR("Safex token stake period not expired at height"<height()); - return false; - } - } - } - } - - //TODO: GRKI Refactor this - - /* Find cash and token amount that is distributed, check if they match */ uint64_t distributed_cash_amount = 0; uint64_t unstaked_token_amount = 0; uint64_t expected_interest = 0; @@ -3137,6 +3118,7 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm const txin_to_script &stxin = boost::get(txin); if (stxin.command_type == safex::command_t::token_unstake) { + // Find cash and token amount that is distributed, check if they match unstaked_token_amount += stxin.token_amount; expected_interest += calculate_staked_token_interest_for_output(stxin, m_db->height()); distributed_cash_amount += stxin.amount; @@ -3146,6 +3128,14 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm MERROR("Interest should be distributed for particular token stake output"); return false; } + // Check if tokens are staked long enough + for (auto index: stxin.key_offsets) { + output_advanced_data_t out = this->m_db->get_output_advanced_data(tx_out_type::out_staked_token, index); + if (out.height+safex::get_safex_minumum_token_lock_period(m_nettype) > m_db->height()) { + MERROR("Safex token stake period not expired at height"<height()); + return false; + } + } } } } From dd1e1eb0108edcecb9ad8a91323ff0f9944b97dd Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 20 May 2020 21:18:01 +0200 Subject: [PATCH 40/52] Remove pkey from edit_account_result --- src/safex/command.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/safex/command.h b/src/safex/command.h index 15aa4913a..5982728c8 100644 --- a/src/safex/command.h +++ b/src/safex/command.h @@ -144,12 +144,10 @@ namespace safex username{_username}, account_data{_account_data} { } std::vector username{}; - crypto::public_key pkey{}; std::vector account_data{}; BEGIN_SERIALIZE_OBJECT() FIELD(username) - FIELD(pkey) FIELD(account_data) END_SERIALIZE() }; From 169c704b91b69ea4f4dbc041a11a187d6c30a1b4 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 22 May 2020 00:52:08 +0200 Subject: [PATCH 41/52] Add function to check if key image verification is needed As advanced inputs that are always ring size 1 do not use key_image for signature and nothing is being actually spent, we remove key_image checks for several advanced inputs. This will prevent double spend error, as previous key images were created by hashing script data so edit of account or offer was not possible. Also, as we are not storing key images, we will save some data space. --- src/safex/safex_core.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/safex/safex_core.h b/src/safex/safex_core.h index a74bf24a9..1a5e382c8 100644 --- a/src/safex/safex_core.h +++ b/src/safex/safex_core.h @@ -105,6 +105,24 @@ namespace safex #define SAFEX_COMMAND_ASSERT_MES_AND_THROW(message, command_type) {LOG_ERROR(message); std::stringstream ss; ss << message; throw safex::command_exception(command_type, ss.str());} #define SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES(expr, message, command_type) do {if(!(expr)) SAFEX_COMMAND_ASSERT_MES_AND_THROW(message, command_type);} while(0) +/** +* Returns if input needs key_image verification +* +* +* @return true if it needs key_image verification, false otherwise +*/ + inline uint64_t is_safex_key_image_verification_needed(const safex::command_t& command_type) +{ + + if(command_type == safex::command_t::edit_account + || command_type == safex::command_t::create_offer + || command_type == safex::command_t::edit_offer + || command_type == safex::command_t::create_price_peg + || command_type == safex::command_t::update_price_peg) + return false; + else + return true; +} /** * Returns number of blocks in interval From 1aed5b840124b73ff948e5b5d50bb7d78339e546 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 22 May 2020 00:57:23 +0200 Subject: [PATCH 42/52] Dont check key image validation for specific command types --- src/cryptonote_core/cryptonote_core.cpp | 50 +------------------------ 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index f4ce0d962..e84e24052 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1176,55 +1176,7 @@ namespace cryptonote } else if (in.type() == typeid(const txin_to_script)) { const txin_to_script &txin = boost::get(in); - if (txin.command_type == safex::command_t::edit_account) { - //todo Atana optimize somehow key image validation, so many conversions - const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); - std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(txin.script); - safex::edit_account_data account(cmd->get_username(), cmd->get_new_account_data()); - crypto::hash cmd_hash{}; - get_object_hash(account, cmd_hash); - if (memcmp(cmd_hash.data, k_image.data, sizeof(k_image.data)) != 0) - return false; - } else if (txin.command_type == safex::command_t::create_offer) { - //todo Atana optimize somehow key image validation, so many conversions - const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); - std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(txin.script); - safex::create_offer_data offer(cmd->get_offerid(),cmd->get_seller(),cmd->get_title(),cmd->get_quantity(),cmd->get_price(),cmd->get_description(),cmd->get_active(),cmd->get_seller_address(),cmd->get_seller_private_view_key(),cmd->get_price_peg_id(),cmd->get_min_sfx_price(),cmd->get_price_peg_used()); - crypto::hash cmd_hash{}; - get_object_hash(offer, cmd_hash); - if (memcmp(cmd_hash.data, k_image.data, sizeof(k_image.data)) != 0) - return false; - } else if (txin.command_type == safex::command_t::edit_offer) { - //todo Atana optimize somehow key image validation, so many conversions - const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); - std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(txin.script); - safex::edit_offer_data offer(cmd->get_offerid(),cmd->get_seller(),cmd->get_title(),cmd->get_quantity(),cmd->get_price(),cmd->get_description(),cmd->get_active(),cmd->get_price_peg_id(),cmd->get_min_sfx_price(),cmd->get_price_peg_used()); - crypto::hash cmd_hash{}; - get_object_hash(offer, cmd_hash); - if (memcmp(cmd_hash.data, k_image.data, sizeof(k_image.data)) != 0) - return false; - } else if (txin.command_type == safex::command_t::create_price_peg) { - //todo Atana optimize somehow key image validation, so many conversions - const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); - std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(txin.script); - - safex::create_price_peg_data price_peg(cmd->get_title(),cmd->get_price_peg_id(),cmd->get_creator(),cmd->get_description(),cmd->get_currency(),cmd->get_rate()); - crypto::hash cmd_hash{}; - get_object_hash(price_peg, cmd_hash); - if (memcmp(cmd_hash.data, k_image.data, sizeof(k_image.data)) != 0) - return false; - } else if (txin.command_type == safex::command_t::update_price_peg) { - //todo Atana optimize somehow key image validation, so many conversions - const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); - std::unique_ptr cmd = safex::safex_command_serializer::parse_safex_command(txin.script); - - safex::update_price_peg_data price_peg(cmd->get_price_peg_id(),cmd->get_rate()); - crypto::hash cmd_hash{}; - get_object_hash(price_peg, cmd_hash); - if (memcmp(cmd_hash.data, k_image.data, sizeof(k_image.data)) != 0) - return false; - } - else { + if (safex::is_safex_key_image_verification_needed(txin.command_type)){ const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); // invalid key_image if (!(rct::scalarmultKey(rct::ki2rct(k_image), rct::curveOrder()) == rct::identity())) From a0197a524cd712313c5cb1f5e3d67793fdde8d37 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 22 May 2020 00:58:57 +0200 Subject: [PATCH 43/52] Do key image operations only if key_image is used for given command --- src/blockchain_db/blockchain_db.cpp | 22 ++++++++++++++++----- src/cryptonote_core/blockchain.cpp | 30 ++++++++++++++++++++++++----- src/cryptonote_core/tx_pool.cpp | 21 ++++++++++++++++++++ 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 7336ea0b8..ec1e578d9 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -174,7 +174,9 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti if (!k_image_opt) throw DB_ERROR("Output does not have proper key image"); const crypto::key_image &k_image = *k_image_opt; - add_spent_key(k_image); + + if(is_safex_key_image_verification_needed(txin.command_type)) + add_spent_key(k_image); } @@ -189,14 +191,22 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti { if ((tx_input.type() == typeid(txin_to_key)) || (tx_input.type() == typeid(txin_token_to_key)) - || (tx_input.type() == typeid(txin_token_migration)) - || (tx_input.type() == typeid(txin_to_script)) - ) + || (tx_input.type() == typeid(txin_token_migration))) { auto k_image_opt = boost::apply_visitor(key_image_visitor(), tx_input); if (!k_image_opt) continue; const crypto::key_image &k_image = *k_image_opt; remove_spent_key(k_image); + } else if (tx_input.type() == typeid(txin_to_script)){ + auto input = boost::get(tx_input); + + if(safex::is_safex_key_image_verification_needed(input.command_type)) + { + auto k_image_opt = boost::apply_visitor(key_image_visitor(), tx_input); + if (!k_image_opt) continue; + const crypto::key_image &k_image = *k_image_opt; + remove_spent_key(k_image); + } } } return; @@ -329,7 +339,9 @@ void BlockchainDB::remove_transaction(const crypto::hash& tx_hash) auto input = boost::get(tx_input); if(input.command_type == safex::command_t::token_unstake) remove_unstake_token(tx_hash, tx); - remove_spent_key(boost::get(tx_input).k_image); + + if(safex::is_safex_key_image_verification_needed(input.command_type)) + remove_spent_key(boost::get(tx_input).k_image); } } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index ec958bf4e..15f538b68 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3713,6 +3713,13 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const CHECK_AND_ASSERT_MES(k_image_opt, true, "key image is not available in input"); const crypto::key_image &k_image = *k_image_opt; + if (in.type() == typeid(txin_to_script)) + { + const txin_to_script& in_to_script = boost::get(in); + if(!safex::is_safex_key_image_verification_needed(in_to_script.command_type)) + continue; + } + if(have_tx_keyimg_as_spent(k_image)) return true; } else { @@ -4021,13 +4028,26 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, // make sure tx output has key offset(s) (is signed to be used) CHECK_AND_ASSERT_MES(is_valid_txin_key_offsets(txin), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); - const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), txin); //key image of currently checked input - if (have_tx_keyimg_as_spent(k_image)) + + if (txin.type() == typeid(txin_to_script)) { - MERROR_VER("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(k_image)); - tvc.m_double_spend = true; - return false; + const txin_to_script& in_to_script = boost::get(txin); + if(safex::is_safex_key_image_verification_needed(in_to_script.command_type)){ + if (have_tx_keyimg_as_spent(k_image)) + { + MERROR_VER("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(k_image)); + tvc.m_double_spend = true; + return false; + } + } + } else { + if (have_tx_keyimg_as_spent(k_image)) + { + MERROR_VER("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(k_image)); + tvc.m_double_spend = true; + return false; + } } diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index dc40161ae..b15520093 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -447,8 +447,16 @@ namespace cryptonote CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: kept_by_block=" << kept_by_block << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << k_image << ENDL << "tx_id=" << id ); + + if (in.type() == typeid(txin_to_script)){ + auto input = boost::get(in); + + if(!safex::is_safex_key_image_verification_needed(input.command_type)) + continue; + } auto ins_res = kei_image_set.insert(id); CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set"); + } else { LOG_ERROR("wrong input variant type: " << in.type().name() << ", expected " << typeid(txin_to_key).name() << ", " << typeid(txin_token_to_key).name() << " or " << typeid(txin_token_migration).name()); return false; @@ -535,6 +543,12 @@ namespace cryptonote { if (cryptonote::is_valid_transaction_input_type(vi, tx.version)) { const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), vi); + if (vi.type() == typeid(const txin_to_script)){ + auto input = boost::get(vi); + + if(!safex::is_safex_key_image_verification_needed(input.command_type)) + continue; + } auto it = m_spent_key_images.find(k_image); CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << k_image << ENDL << "transaction id = " << get_transaction_hash(tx)); @@ -1113,6 +1127,13 @@ namespace cryptonote { if (cryptonote::is_valid_transaction_input_type(in, tx.version)) { const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in); + + if (in.type() == typeid(const txin_to_script)){ + auto input = boost::get(in); + + if(!safex::is_safex_key_image_verification_needed(input.command_type)) + continue; + } if(have_tx_keyimg_as_spent(k_image)) return true; } else { From 6a28af1eb7348394bf3e094ee009312f939723ec Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 22 May 2020 01:03:16 +0200 Subject: [PATCH 44/52] Do not generate key image if it is not needed --- src/cryptonote_core/cryptonote_tx_utils.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index eee136792..3d6aaf04b 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -1196,8 +1196,7 @@ namespace cryptonote keypair &in_ephemeral = in_contexts.back().in_ephemeral; crypto::key_image img{}; const auto &out_key = reinterpret_cast(src_entr.outputs[src_entr.real_output].second.dest); - if (src_entr.referenced_output_type == tx_out_type::out_safex_account || src_entr.referenced_output_type == tx_out_type::out_safex_offer - || src_entr.referenced_output_type == tx_out_type::out_safex_price_peg) + if (!safex::is_safex_key_image_verification_needed(src_entr.command_type)) { if (!crypto::check_key(out_key)) { @@ -1205,9 +1204,7 @@ namespace cryptonote return false; } - //todo Atana check if there is better way to generate key image, currently it is hash of input command crypto::hash cmd_hash{}; - get_blob_hash(src_entr.command_safex_data, cmd_hash); memcpy(img.data, cmd_hash.data, sizeof(img.data)); } else { From 52c43278a91e98b111b8fd01b4cffd766a696ebd Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 23 May 2020 13:48:24 +0200 Subject: [PATCH 45/52] Refactor network fee calculation to use dedicated function --- src/cryptonote_core/blockchain.cpp | 5 +++-- src/cryptonote_core/cryptonote_tx_utils.cpp | 2 +- src/safex/safex_core.h | 2 +- src/simplewallet/simplewallet_safex.cpp | 9 +++++++-- src/wallet/api/wallet.cpp | 10 ++++++++-- src/wallet/wallet_rpc_server.cpp | 5 +++-- tests/core_tests/chain_split_safex.cpp | 4 ++-- tests/core_tests/chaingen.cpp | 4 ++-- tests/core_tests/safex_purchase.cpp | 4 ++-- tests/unit_tests/safex_test_common.cpp | 4 ++-- tests/unit_tests/simple_purchase.cpp | 2 +- 11 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 15f538b68..33183652f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3526,14 +3526,15 @@ bool Blockchain::check_safex_tx_command(const transaction &tx, const safex::comm } } + uint64_t calculated_network_fee = calculate_safex_network_fee(total_payment, m_nettype, command_type); //check network fee payment - if (total_payment*5/100 > network_fee) + if (calculated_network_fee > network_fee) { MERROR("Not enough cash given for network fee"); return false; } //check purchase cash payment - if (total_payment*95/100 > product_payment) + if (total_payment - calculated_network_fee > product_payment) { MERROR("Not enough cash given for product payment"); return false; diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 3d6aaf04b..f69830001 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -897,7 +897,7 @@ namespace cryptonote if(txin->command_type == safex::command_t::donate_network_fee) amount_to_donate += txin->amount; if(txin->command_type == safex::command_t::simple_purchase) - amount_to_donate += txin->amount*5/100; + amount_to_donate += calculate_safex_network_fee(txin->amount, network_type::MAINNET, txin->command_type); } // SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES(amount_to_donate >= dst_entr.amount, "Not enough safex cash to donate", safex::command_t::donate_network_fee); diff --git a/src/safex/safex_core.h b/src/safex/safex_core.h index 1a5e382c8..1239dc139 100644 --- a/src/safex/safex_core.h +++ b/src/safex/safex_core.h @@ -253,7 +253,7 @@ namespace safex uint64_t fee = 0; //todo handle multiplication that overflows - SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((cash_amount * SAFEX_DEFAULT_NETWORK_FEE_PERCENTAGE) < cash_amount, "Overflow calculating transaction fee", safex::command_t::token_stake); + SAFEX_COMMAND_CHECK_AND_ASSERT_THROW_MES((cash_amount * SAFEX_DEFAULT_NETWORK_FEE_PERCENTAGE) >= cash_amount, "Overflow calculating transaction fee", command_type); switch (nettype) { default: diff --git a/src/simplewallet/simplewallet_safex.cpp b/src/simplewallet/simplewallet_safex.cpp index 8ea4c63c0..68c09b836 100644 --- a/src/simplewallet/simplewallet_safex.cpp +++ b/src/simplewallet/simplewallet_safex.cpp @@ -368,12 +368,17 @@ namespace cryptonote uint64_t sfx_price; bool res = m_wallet->calculate_sfx_price(*offer_to_purchase, sfx_price); + if(!res) { + fail_msg_writer() << tr("Error calculating SFX price for purchase!!"); + return true; + } uint64_t total_sfx_to_pay = quantity_to_purchase*sfx_price; - de.amount = total_sfx_to_pay * 95 / 100; + safex_network_fee = calculate_safex_network_fee(total_sfx_to_pay, m_wallet->nettype(), safex::command_t::simple_purchase); + + de.amount = total_sfx_to_pay - safex_network_fee; de.output_type = tx_out_type::out_cash; - safex_network_fee += total_sfx_to_pay * 5 / 100; cryptonote::address_parse_info info = AUTO_VAL_INIT(info); cryptonote::tx_destination_entry de_purchase = AUTO_VAL_INIT(de_purchase); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 282edd64e..895fae0b9 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1968,12 +1968,18 @@ PendingTransaction * WalletImpl::createAdvancedTransaction(const string &dst_add uint64_t sfx_price; bool res = m_wallet->calculate_sfx_price(*offer_to_purchase, sfx_price); + if(!res) { + m_status = Status_Error; + m_errorString = tr("Error calculating SFX price for purchase!!"); + break; + } uint64_t total_sfx_to_pay = safexPurchase.m_quantity_to_purchase*sfx_price; - de.amount = total_sfx_to_pay * 95 / 100; + safex_network_fee = calculate_safex_network_fee(total_sfx_to_pay, m_wallet->nettype(), safex::command_t::simple_purchase); + + de.amount = total_sfx_to_pay - safex_network_fee; de.output_type = tx_out_type::out_cash; - safex_network_fee += total_sfx_to_pay * 5 / 100; cryptonote::tx_destination_entry de_purchase = AUTO_VAL_INIT(de_purchase); //Purchase diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 58cc8f20d..e79ce9f27 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -804,8 +804,9 @@ bool wallet_rpc_server::validate_transfer_advanced( } // Allow to collect outputs for regular SFX transaction. else if(cmd_type == safex::command_t::simple_purchase) { - de.amount = destination.amount * 95 / 100; - safex_network_fee += destination.amount * 5 / 100; + uint64_t network_fee = calculate_safex_network_fee(destination.amount, m_wallet->nettype(), safex::command_t::simple_purchase); + safex_network_fee += network_fee; + de.amount = destination.amount - network_fee; } dsts.push_back(de); diff --git a/tests/core_tests/chain_split_safex.cpp b/tests/core_tests/chain_split_safex.cpp index 1e5e1e8e1..b93be034f 100644 --- a/tests/core_tests/chain_split_safex.cpp +++ b/tests/core_tests/chain_split_safex.cpp @@ -127,14 +127,14 @@ gen_simple_chain_split_safex::gen_simple_chain_split_safex() expected_alice_balance = 0; expected_bob_balance = 0; - expected_network_fee = safex_bob_purchase_from_alice.price*5/100; + expected_network_fee = calculate_safex_network_fee(safex_bob_purchase_from_alice.price, FAKECHAIN, safex::command_t::simple_purchase); expected_alice_balance += MK_TOKENS(10000)*AIRDROP_TOKEN_TO_CASH_REWARD_RATE; expected_alice_balance -= 7*TESTS_DEFAULT_FEE; expected_alice_balance_before_purchase = expected_alice_balance; - expected_alice_balance += safex_bob_purchase_from_alice.price*95/100; + expected_alice_balance += safex_bob_purchase_from_alice.price - expected_network_fee; expected_alice_balance_after_unstake = expected_alice_balance + expected_network_fee - TESTS_DEFAULT_FEE; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index c897e5807..b8077a626 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1482,14 +1482,14 @@ void fill_create_purchase_tx_sources_and_destinations(const std::vector Date: Sat, 23 May 2020 16:39:04 +0200 Subject: [PATCH 46/52] Use constexpr instead of macros for Safex constants. * Modern problems require modern solutions * --- src/cryptonote_config.h | 56 +++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6b59bf049..72885eb6b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -169,30 +169,38 @@ #define HF_VERSION_STOP_COUNTERFEIT_TOKENS 6 //Safex related constants -#define SAFEX_COMMAND_PROTOCOL_VERSION 1 -#define SAFEX_MINIMUM_TOKEN_STAKE_AMOUNT 10000 * SAFEX_TOKEN -#define SAFEX_DEFAULT_TOKEN_STAKE_EXPIRY_PERIOD 500000 -#define SAFEX_DEFAULT_INTERVAL_PERIOD 1000 //blocks -#define SAFEX_DEFAULT_MINUMUM_TOKEN_STAKE_PERIOD SAFEX_DEFAULT_INTERVAL_PERIOD*10 //blocks -#define SAFEX_DEFAULT_NETWORK_FEE_PERCENTAGE ((uint64_t)5) -#define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE ((uint64_t)100*SAFEX_TOKEN) - -#define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD_FAKECHAIN ((uint64_t)1) -#define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD_TESTNET ((uint64_t)10) -#define SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD ((uint64_t)150)// TBD - -#define SAFEX_ACCOUNT_USERNAME_MAX_SIZE 32 -#define SAFEX_ACCOUNT_DATA_MAX_SIZE 2048 - -#define SAFEX_OFFER_NAME_MAX_SIZE 80 -#define SAFEX_OFFER_DATA_MAX_SIZE 2048 - - -#define SAFEX_PRICE_PEG_NAME_MAX_SIZE 60 -#define SAFEX_PRICE_PEG_CURRENCY_MAX_SIZE 8 -#define SAFEX_PRICE_PEG_DATA_MAX_SIZE 2048 - -#define SAFEX_FEEDBACK_DATA_MAX_SIZE 2048 +constexpr uint64_t SAFEX_COMMAND_PROTOCOL_VERSION = 1; + +//Safex token stake constants +constexpr uint64_t SAFEX_MINIMUM_TOKEN_STAKE_AMOUNT = 10000 * SAFEX_TOKEN; +constexpr uint64_t SAFEX_DEFAULT_TOKEN_STAKE_EXPIRY_PERIOD = 500000; +constexpr uint64_t SAFEX_DEFAULT_INTERVAL_PERIOD = 1000; //blocks +constexpr uint64_t SAFEX_DEFAULT_MINUMUM_TOKEN_STAKE_PERIOD = SAFEX_DEFAULT_INTERVAL_PERIOD*10; //blocks + +//Safex network fee constants +constexpr uint64_t SAFEX_DEFAULT_NETWORK_FEE_PERCENTAGE = 5; + +//Safex create account token lock constants +constexpr uint64_t SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_FEE = 100*SAFEX_TOKEN; +constexpr uint64_t SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD_FAKECHAIN = 1; +constexpr uint64_t SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD_TESTNET = 10; +constexpr uint64_t SAFEX_CREATE_ACCOUNT_TOKEN_LOCK_PERIOD = 150;// TBD + +//Safex account constants +constexpr uint64_t SAFEX_ACCOUNT_USERNAME_MAX_SIZE = 32; +constexpr uint64_t SAFEX_ACCOUNT_DATA_MAX_SIZE = 2048; + +//Safex offer constants +constexpr uint64_t SAFEX_OFFER_NAME_MAX_SIZE = 80; +constexpr uint64_t SAFEX_OFFER_DATA_MAX_SIZE = 2048; + +//Safex price peg constants +constexpr uint64_t SAFEX_PRICE_PEG_NAME_MAX_SIZE = 60; +constexpr uint64_t SAFEX_PRICE_PEG_CURRENCY_MAX_SIZE = 8; +constexpr uint64_t SAFEX_PRICE_PEG_DATA_MAX_SIZE = 2048; + +//Safex feedback constants +constexpr uint64_t SAFEX_FEEDBACK_DATA_MAX_SIZE = 2048; #define DEFAULT_MIX 6 //default wallet mix for transactions From 83e043205af67d03126a5d872228ecd65064ac1e Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 23 May 2020 17:27:03 +0200 Subject: [PATCH 47/52] Set minimum SFX price for offers --- src/cryptonote_config.h | 1 + src/safex/command.cpp | 8 +++++ src/safex/command.h | 39 +++++++++++++++---------- src/simplewallet/simplewallet_safex.cpp | 36 +++++++++++++++++++++-- src/wallet/api/wallet.cpp | 14 ++++++++- 5 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 72885eb6b..0e8e292e3 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -193,6 +193,7 @@ constexpr uint64_t SAFEX_ACCOUNT_DATA_MAX_SIZE = 2048; //Safex offer constants constexpr uint64_t SAFEX_OFFER_NAME_MAX_SIZE = 80; constexpr uint64_t SAFEX_OFFER_DATA_MAX_SIZE = 2048; +constexpr uint64_t SAFEX_OFFER_MINIMUM_PRICE = SAFEX_CASH_COIN/1000; // 0.001 SFX //Safex price peg constants constexpr uint64_t SAFEX_PRICE_PEG_NAME_MAX_SIZE = 60; diff --git a/src/safex/command.cpp b/src/safex/command.cpp index 5b062cc16..0daf7dce4 100644 --- a/src/safex/command.cpp +++ b/src/safex/command.cpp @@ -348,6 +348,10 @@ namespace safex result = execution_status::error_account_non_existant; } + if(cmd->get_min_sfx_price() < SAFEX_OFFER_MINIMUM_PRICE){ + result = execution_status::error_offer_price_too_small; + } + if(cmd->get_price() > MONEY_SUPPLY) { result = execution_status::error_offer_price_too_big; } @@ -395,6 +399,10 @@ namespace safex result = execution_status::error_account_non_existant; } + if(cmd->get_min_sfx_price() < SAFEX_OFFER_MINIMUM_PRICE){ + result = execution_status::error_offer_price_too_small; + } + if(cmd->get_price() > MONEY_SUPPLY) { result = execution_status::error_offer_price_too_big; } diff --git a/src/safex/command.h b/src/safex/command.h index 5982728c8..b25108940 100644 --- a/src/safex/command.h +++ b/src/safex/command.h @@ -40,26 +40,33 @@ namespace safex ok = 0, invalid = 1, error_wrong_input_params = 1, + // Safex account error_account_data_too_big = 10, error_account_already_exists = 11, error_invalid_account_name = 12, error_account_non_existant = 13, - error_offer_non_existant = 14, - error_purchase_out_of_stock = 15, - error_purchase_not_enough_funds = 16, - error_purchase_offer_not_active = 17, - error_offer_price_too_big = 18, - error_feedback_invalid_rating = 19, - error_offer_price_peg_not_existant = 20, - error_price_peg_bad_currency_format = 21, - error_offer_data_too_big = 22, - error_price_peg_data_too_big = 23, - error_price_peg_not_existant = 24, - error_feedback_data_too_big = 25, - error_price_peg_rate_zero = 26, - error_unstake_token_output_not_found = 27, - error_unstake_token_minimum_period = 28, - error_unstake_token_network_fee_not_matching = 29 + // Safex purchase + error_offer_non_existant = 20, + error_purchase_out_of_stock = 21, + error_purchase_not_enough_funds = 23, + error_purchase_offer_not_active = 24, + // Safex offer + error_offer_price_too_big = 30, + error_offer_price_too_small = 31, + error_offer_data_too_big = 32, + error_offer_price_peg_not_existant = 33, + // Safex feedback + error_feedback_invalid_rating = 40, + error_feedback_data_too_big = 41, + // Safex price peg + error_price_peg_bad_currency_format = 51, + error_price_peg_data_too_big = 52, + error_price_peg_not_existant = 53, + error_price_peg_rate_zero = 54, + // Safex unstake token + error_unstake_token_output_not_found = 60, + error_unstake_token_minimum_period = 61, + error_unstake_token_network_fee_not_matching = 62 }; struct execution_result diff --git a/src/simplewallet/simplewallet_safex.cpp b/src/simplewallet/simplewallet_safex.cpp index 68c09b836..8c3ef0834 100644 --- a/src/simplewallet/simplewallet_safex.cpp +++ b/src/simplewallet/simplewallet_safex.cpp @@ -261,7 +261,7 @@ namespace cryptonote try{ bool ok = cryptonote::parse_amount(price, local_args[2]); - if(!ok || 0 == price) + if(!ok || price == 0) { fail_msg_writer() << tr("amount is wrong: ") << local_args[2] << ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); @@ -294,6 +294,11 @@ namespace cryptonote if(!attach_price_peg(sfx_offer)) return true; } + if(sfx_offer.min_sfx_price < SAFEX_OFFER_MINIMUM_PRICE){ + fail_msg_writer() << tr("Minimum price is wrong: ") << print_money(sfx_offer.min_sfx_price) << + ", " << tr("expected number from ") << print_money(SAFEX_OFFER_MINIMUM_PRICE) << tr(" to ") << print_money(std::numeric_limits::max()); + return true; + } cryptonote::tx_destination_entry de_offer = create_safex_offer_destination(info.address, sfx_offer); dsts.push_back(de_offer); @@ -308,11 +313,30 @@ namespace cryptonote uint64_t price; uint64_t quantity; bool active; + + try { - price = stold(local_args[3])*SAFEX_CASH_COIN; - quantity = stoi(local_args[4]); + + bool ok = cryptonote::parse_amount(price, local_args[3]); + if(!ok || price == 0) + { + fail_msg_writer() << tr("amount is wrong: ") << local_args[3] << + ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); + return true; + } + + quantity = stoull(local_args[4]); active = stoi(local_args[5]); + long double check_price = stold(local_args[3]); + long double check_quantity = stold(local_args[4]); + int check_active = stoi(local_args[5]); + + if(check_price < 0 || check_quantity < 0 || (check_active != 1 && check_active != 0) ){ + fail_msg_writer() << tr("Negative amount, quantity, or active not 1 or 0 entered"); + return true; + } + } catch(std::invalid_argument& e){ fail_msg_writer() << tr("One of the arguments is missing. Please check needed arguments again."); @@ -332,6 +356,12 @@ namespace cryptonote return true; } + if(sfx_offer.min_sfx_price < SAFEX_OFFER_MINIMUM_PRICE){ + fail_msg_writer() << tr("Minimum price is wrong: ") << print_money(sfx_offer.min_sfx_price) << + ", " << tr("expected number from ") << print_money(SAFEX_OFFER_MINIMUM_PRICE) << tr(" to ") << print_money(std::numeric_limits::max()); + return true; + } + cryptonote::tx_destination_entry de_offer_update = edit_safex_offer_destination(info.address, sfx_offer); dsts.push_back(de_offer_update); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 895fae0b9..60bb4e8b8 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1764,7 +1764,7 @@ PendingTransaction * WalletImpl::createAdvancedTransaction(const string &dst_add m_status = Status_Error; m_errorString = tr("Unknown safex account username"); break; - }; + } if (!crypto::check_key(my_safex_account.pkey)) { m_status = Status_Error; m_errorString = tr("Invalid account public key"); @@ -1786,6 +1786,12 @@ PendingTransaction * WalletImpl::createAdvancedTransaction(const string &dst_add sfx_offer.set_price_peg(price_peg_id, sfxOffer.m_price, sfxOffer.m_min_sfx_price); } + if (sfxOffer.m_min_sfx_price < SAFEX_OFFER_MINIMUM_PRICE) { + m_status = Status_Error; + m_errorString = tr("Wrong minimum SFX price"); + break; + } + cryptonote::tx_destination_entry de_offer = create_safex_offer_destination(info.address, sfx_offer); dsts.push_back(de_offer); @@ -1829,6 +1835,12 @@ PendingTransaction * WalletImpl::createAdvancedTransaction(const string &dst_add sfx_offer.set_price_peg(price_peg_id, sfxOffer.m_price, sfxOffer.m_min_sfx_price); } + if (sfxOffer.m_min_sfx_price < SAFEX_OFFER_MINIMUM_PRICE) { + m_status = Status_Error; + m_errorString = tr("Wrong minimum SFX price"); + break; + } + cryptonote::tx_destination_entry de_offer_update = edit_safex_offer_destination(info.address, sfx_offer); dsts.push_back(de_offer_update); From 5d8fe3527fe0069426752fa8de7a9c534e198688 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 23 May 2020 18:30:27 +0200 Subject: [PATCH 48/52] Add restrictions for offer/price_peg instead of just safex account --- src/cryptonote_core/tx_pool.cpp | 94 ++++++++++++++++----------------- src/cryptonote_core/tx_pool.h | 12 ++++- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index b15520093..1dd953e0a 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -490,32 +490,29 @@ namespace cryptonote { const txout_to_script &out = boost::get(vout.target); safex::create_offer_data offer; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, offer); - std::string username{offer.seller.begin(),offer.seller.end()}; - m_safex_accounts_in_use.push_back(username); + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + m_safex_offers_in_use.push_back(offer.offer_id); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer_update) { const txout_to_script &out = boost::get(vout.target); safex::edit_offer_data offer; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, offer); - std::string username{offer.seller.begin(),offer.seller.end()}; - m_safex_accounts_in_use.push_back(username); + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + m_safex_offers_in_use.push_back(offer.offer_id); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg) { const txout_to_script &out = boost::get(vout.target); safex::create_price_peg_data price_peg; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, price_peg); - std::string username{price_peg.creator.begin(),price_peg.creator.end()}; - m_safex_accounts_in_use.push_back(username); + const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); + m_safex_price_peg_update_in_progress.push_back(price_peg.price_peg_id); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) { const txout_to_script &out = boost::get(vout.target); safex::update_price_peg_data price_peg; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, price_peg); + const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); m_safex_price_peg_update_in_progress.push_back(price_peg.price_peg_id); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) { @@ -606,38 +603,35 @@ namespace cryptonote { const txout_to_script &out = boost::get(vout.target); safex::create_offer_data offer; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, offer); - std::string username{offer.seller.begin(),offer.seller.end()}; - auto it = std::find(m_safex_accounts_in_use.begin(), m_safex_accounts_in_use.end(), username); - CHECK_AND_ASSERT_MES(it != m_safex_accounts_in_use.end(), false, "failed to find safex restriction for type out_safex_offer" << ENDL << "transaction id = " << get_transaction_hash(tx)); - m_safex_accounts_in_use.erase(it); + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + auto it = std::find(m_safex_offers_in_use.begin(), m_safex_offers_in_use.end(), offer.offer_id); + CHECK_AND_ASSERT_MES(it != m_safex_offers_in_use.end(), false, "failed to find safex restriction for type out_safex_offer" << ENDL << "transaction id = " << get_transaction_hash(tx)); + m_safex_offers_in_use.erase(it); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer_update) { const txout_to_script &out = boost::get(vout.target); safex::edit_offer_data offer; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, offer); - std::string username{offer.seller.begin(),offer.seller.end()}; - auto it = std::find(m_safex_accounts_in_use.begin(), m_safex_accounts_in_use.end(), username); - CHECK_AND_ASSERT_MES(it != m_safex_accounts_in_use.end(), false, "failed to find safex restriction for type out_safex_offer_update" << ENDL << "transaction id = " << get_transaction_hash(tx)); - m_safex_accounts_in_use.erase(it); + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + auto it = std::find(m_safex_offers_in_use.begin(), m_safex_offers_in_use.end(), offer.offer_id); + CHECK_AND_ASSERT_MES(it != m_safex_offers_in_use.end(), false, "failed to find safex restriction for type out_safex_offer" << ENDL << "transaction id = " << get_transaction_hash(tx)); + m_safex_offers_in_use.erase(it); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg) { const txout_to_script &out = boost::get(vout.target); safex::create_price_peg_data price_peg; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, price_peg); - std::string username{price_peg.creator.begin(),price_peg.creator.end()}; - auto it = std::find(m_safex_accounts_in_use.begin(), m_safex_accounts_in_use.end(), username); - CHECK_AND_ASSERT_MES(it != m_safex_accounts_in_use.end(), false, "failed to find safex restriction for type out_safex_price_peg" << ENDL << "transaction id = " << get_transaction_hash(tx)); - m_safex_accounts_in_use.erase(it); + const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); + auto it = std::find(m_safex_price_peg_update_in_progress.begin(), m_safex_price_peg_update_in_progress.end(), price_peg.price_peg_id); + CHECK_AND_ASSERT_MES(it != m_safex_price_peg_update_in_progress.end(), false, "failed to find safex restriction for type out_safex_price_peg_update" << ENDL << "transaction id = " << get_transaction_hash(tx)); + m_safex_price_peg_update_in_progress.erase(it); } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) { const txout_to_script &out = boost::get(vout.target); safex::update_price_peg_data price_peg; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, price_peg); + const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); auto it = std::find(m_safex_price_peg_update_in_progress.begin(), m_safex_price_peg_update_in_progress.end(), price_peg.price_peg_id); CHECK_AND_ASSERT_MES(it != m_safex_price_peg_update_in_progress.end(), false, "failed to find safex restriction for type out_safex_price_peg_update" << ENDL << "transaction id = " << get_transaction_hash(tx)); m_safex_price_peg_update_in_progress.erase(it); @@ -1172,35 +1166,32 @@ namespace cryptonote { const txout_to_script &out = boost::get(vout.target); safex::create_offer_data offer; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, offer); - std::string username{offer.seller.begin(),offer.seller.end()}; - if(have_tx_safex_account_in_use(username)) + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + if(have_tx_safex_offer_in_use(offer.offer_id)) return true; } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_offer_update) { const txout_to_script &out = boost::get(vout.target); safex::edit_offer_data offer; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, offer); - std::string username{offer.seller.begin(),offer.seller.end()}; - if(have_tx_safex_account_in_use(username)) + const cryptonote::blobdata offerblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(offerblob, offer); + if(have_tx_safex_offer_in_use(offer.offer_id)) return true; } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg) { const txout_to_script &out = boost::get(vout.target); safex::create_price_peg_data price_peg; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, price_peg); - std::string username{price_peg.creator.begin(),price_peg.creator.end()}; - if(have_tx_safex_account_in_use(username)) + const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); + if(have_tx_safex_price_peg_in_use(price_peg.price_peg_id)) return true; } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_price_peg_update) { const txout_to_script &out = boost::get(vout.target); safex::update_price_peg_data price_peg; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, price_peg); + const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); if(have_tx_safex_price_peg_in_use(price_peg.price_peg_id)) return true; } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) @@ -1228,6 +1219,12 @@ namespace cryptonote return m_safex_accounts_in_use.end() != std::find(m_safex_accounts_in_use.begin(), m_safex_accounts_in_use.end(), username); } //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx_safex_offer_in_use(const crypto::hash& offer_id) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + return m_safex_offers_in_use.end() != std::find(m_safex_offers_in_use.begin(), m_safex_offers_in_use.end(), offer_id); + } + //--------------------------------------------------------------------------------- bool tx_memory_pool::have_tx_safex_purchase_in_progress(const crypto::hash &offer_id) const { CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -1617,6 +1614,7 @@ namespace cryptonote m_txs_by_fee_and_receive_time.clear(); m_spent_key_images.clear(); m_safex_accounts_in_use.clear(); + m_safex_offers_in_use.clear(); m_safex_purchase_in_progress.clear(); m_txpool_size = 0; std::vector remove; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index d708cee52..801a1add8 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -431,7 +431,7 @@ namespace cryptonote bool insert_key_images(const transaction &tx, bool kept_by_block); /** - * @brief insert safex data into m_safex_accounts_in_use, m_safex_purchase_in_progress and m_safex_price_peg_update_in_progress + * @brief insert safex data into m_safex_accounts_in_use, m_safex_offers_in_use, m_safex_purchase_in_progress and m_safex_price_peg_update_in_progress * * @return true on success, false on error */ @@ -466,6 +466,15 @@ namespace cryptonote */ bool have_tx_safex_account_in_use(const std::string& username) const; + /** + * @brief check if a transaction in the pool has a safex offer usage + * + * @param username Offer ID of the offer that is in use + * + * @return true if the safex offer is in use already, otherwise false + */ + bool have_tx_safex_offer_in_use(const crypto::hash& offer_id) const; + /** * @brief check if a transaction in the pool is purchase for particular offer * @@ -601,6 +610,7 @@ namespace cryptonote // Safex related members std::vector m_safex_accounts_in_use; + std::vector m_safex_offers_in_use; std::vector m_safex_purchase_in_progress; std::vector m_safex_price_peg_update_in_progress; From 41075f5a4f4626e4f5d57e8b8e839b306866a855 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 23 May 2020 18:39:58 +0200 Subject: [PATCH 49/52] Limit purchase in the pool only if the offer is edited at the moment --- src/cryptonote_core/tx_pool.cpp | 29 +++-------------------------- src/cryptonote_core/tx_pool.h | 13 +------------ 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 1dd953e0a..a39450912 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -514,13 +514,6 @@ namespace cryptonote const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); m_safex_price_peg_update_in_progress.push_back(price_peg.price_peg_id); - } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) - { - const txout_to_script &out = boost::get(vout.target); - safex::create_purchase_data purchase; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, purchase); - m_safex_purchase_in_progress.push_back(purchase.offer_id); } } return true; @@ -635,15 +628,6 @@ namespace cryptonote auto it = std::find(m_safex_price_peg_update_in_progress.begin(), m_safex_price_peg_update_in_progress.end(), price_peg.price_peg_id); CHECK_AND_ASSERT_MES(it != m_safex_price_peg_update_in_progress.end(), false, "failed to find safex restriction for type out_safex_price_peg_update" << ENDL << "transaction id = " << get_transaction_hash(tx)); m_safex_price_peg_update_in_progress.erase(it); - } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) - { - const txout_to_script &out = boost::get(vout.target); - safex::create_purchase_data purchase; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, purchase); - auto it = std::find(m_safex_purchase_in_progress.begin(), m_safex_purchase_in_progress.end(), purchase.offer_id); - CHECK_AND_ASSERT_MES(it != m_safex_purchase_in_progress.end(), false, "failed to find safex restriction for type out_safex_purchase" << ENDL << "transaction id = " << get_transaction_hash(tx)); - m_safex_purchase_in_progress.erase(it); } } return true; @@ -1198,9 +1182,9 @@ namespace cryptonote { const txout_to_script &out = boost::get(vout.target); safex::create_purchase_data purchase; - const cryptonote::blobdata accblob(std::begin(out.data), std::end(out.data)); - cryptonote::parse_and_validate_from_blob(accblob, purchase); - if(have_tx_safex_purchase_in_progress(purchase.offer_id)) + const cryptonote::blobdata purchaseblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(purchaseblob, purchase); + if(have_tx_safex_offer_in_use(purchase.offer_id)) return true; } } @@ -1225,12 +1209,6 @@ namespace cryptonote return m_safex_offers_in_use.end() != std::find(m_safex_offers_in_use.begin(), m_safex_offers_in_use.end(), offer_id); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx_safex_purchase_in_progress(const crypto::hash &offer_id) const - { - CRITICAL_REGION_LOCAL(m_transactions_lock); - return m_safex_purchase_in_progress.end() != std::find(m_safex_purchase_in_progress.begin(), m_safex_purchase_in_progress.end(), offer_id); - } - //--------------------------------------------------------------------------------- bool tx_memory_pool::have_tx_safex_price_peg_in_use(const crypto::hash &price_peg_id) const { CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -1615,7 +1593,6 @@ namespace cryptonote m_spent_key_images.clear(); m_safex_accounts_in_use.clear(); m_safex_offers_in_use.clear(); - m_safex_purchase_in_progress.clear(); m_txpool_size = 0; std::vector remove; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 801a1add8..db68bb0a5 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -431,7 +431,7 @@ namespace cryptonote bool insert_key_images(const transaction &tx, bool kept_by_block); /** - * @brief insert safex data into m_safex_accounts_in_use, m_safex_offers_in_use, m_safex_purchase_in_progress and m_safex_price_peg_update_in_progress + * @brief insert safex data into m_safex_accounts_in_use, m_safex_offers_in_use, and m_safex_price_peg_update_in_progress * * @return true on success, false on error */ @@ -475,15 +475,6 @@ namespace cryptonote */ bool have_tx_safex_offer_in_use(const crypto::hash& offer_id) const; - /** - * @brief check if a transaction in the pool is purchase for particular offer - * - * @param offer_id Offer ID of the offer that is already purchased in the pool - * - * @return true if the purchase with offer ID is in the pool already, otherwise false - */ - bool have_tx_safex_purchase_in_progress(const crypto::hash& offer_id) const; - /** * @brief check if a transaction in the pool has a safex price peg usage * @@ -515,7 +506,6 @@ namespace cryptonote * in any of the transactions in the transaction pool. * * @note see tx_pool::have_tx_safex_account_in_use - * @note see tx_poo::have_tx_safex_purchase_in_progress * * @param tx the transaction to check safex restrictions * @@ -611,7 +601,6 @@ namespace cryptonote // Safex related members std::vector m_safex_accounts_in_use; std::vector m_safex_offers_in_use; - std::vector m_safex_purchase_in_progress; std::vector m_safex_price_peg_update_in_progress; //TODO: this time should be a named constant somewhere, not hard-coded From 41260585f606940e1dbad0b7a7147db1f4e6103f Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sat, 23 May 2020 19:03:44 +0200 Subject: [PATCH 50/52] Small fix for tests to pass --- tests/core_tests/safex_offer.cpp | 2 +- tests/unit_tests/safex_offer.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/core_tests/safex_offer.cpp b/tests/core_tests/safex_offer.cpp index b12bb35e9..20b859186 100644 --- a/tests/core_tests/safex_offer.cpp +++ b/tests/core_tests/safex_offer.cpp @@ -112,7 +112,7 @@ gen_safex_offer_001::gen_safex_offer_001() safex_price_peg_bob = safex::safex_price_peg("TestPricePeg",safex_account_bob.username,"USD","Best price my man",3.5*COIN); - safex_offer_bob.set_price_peg(safex_price_peg_bob.price_peg_id,800,1000); + safex_offer_bob.set_price_peg(safex_price_peg_bob.price_peg_id,800,MK_COINS(1000)); if (!expected_data_fields_intialized) { diff --git a/tests/unit_tests/safex_offer.cpp b/tests/unit_tests/safex_offer.cpp index 20cf941e3..3603d29b8 100644 --- a/tests/unit_tests/safex_offer.cpp +++ b/tests/unit_tests/safex_offer.cpp @@ -99,13 +99,13 @@ namespace data1_new = std::vector(data1_new_str.begin(), data1_new_str.end()); - m_safex_offer[0] = safex::safex_offer("Apple",10,100,"This is an apple", m_safex_account1.username,m_users_acc[0].get_keys().m_view_secret_key,m_users_acc[0].get_keys().m_account_address); - m_safex_offer[1] = safex::safex_offer("Barbie",30,500,"This is a Barbie", m_safex_account2.username,m_users_acc[1].get_keys().m_view_secret_key,m_users_acc[1].get_keys().m_account_address); - m_safex_offer[2] = safex::safex_offer("Car",1,1000,"This is a car", m_safex_account1.username,m_users_acc[0].get_keys().m_view_secret_key,m_users_acc[0].get_keys().m_account_address); + m_safex_offer[0] = safex::safex_offer("Apple",10,100*COIN,"This is an apple", m_safex_account1.username,m_users_acc[0].get_keys().m_view_secret_key,m_users_acc[0].get_keys().m_account_address); + m_safex_offer[1] = safex::safex_offer("Barbie",30,500*COIN,"This is a Barbie", m_safex_account2.username,m_users_acc[1].get_keys().m_view_secret_key,m_users_acc[1].get_keys().m_account_address); + m_safex_offer[2] = safex::safex_offer("Car",1,1000*COIN,"This is a car", m_safex_account1.username,m_users_acc[0].get_keys().m_view_secret_key,m_users_acc[0].get_keys().m_account_address); m_safex_price_peg = safex::safex_price_peg("USD price peg",m_safex_account1.username, "USD", "xcalibra USD price peg", 30); - m_safex_offer[2].set_price_peg(m_safex_price_peg.price_peg_id,100,1000); + m_safex_offer[2].set_price_peg(m_safex_price_peg.price_peg_id,100,1000*COIN); std::string new_str_desc{"Now without worms!!"}; std::vector new_desc{new_str_desc.begin(),new_str_desc.end()}; From 0ec2501b70a0efe0cbe5d38746872919945795a7 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sun, 24 May 2020 21:23:17 +0200 Subject: [PATCH 51/52] Set minimum SFX price to 0.0001 --- src/cryptonote_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 0e8e292e3..c506dd824 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -193,7 +193,7 @@ constexpr uint64_t SAFEX_ACCOUNT_DATA_MAX_SIZE = 2048; //Safex offer constants constexpr uint64_t SAFEX_OFFER_NAME_MAX_SIZE = 80; constexpr uint64_t SAFEX_OFFER_DATA_MAX_SIZE = 2048; -constexpr uint64_t SAFEX_OFFER_MINIMUM_PRICE = SAFEX_CASH_COIN/1000; // 0.001 SFX +constexpr uint64_t SAFEX_OFFER_MINIMUM_PRICE = SAFEX_CASH_COIN/10000; // 0.0001 SFX //Safex price peg constants constexpr uint64_t SAFEX_PRICE_PEG_NAME_MAX_SIZE = 60; From 1a63ee6e6f41cac6a8826d7c010d73dcb03576de Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Sun, 24 May 2020 21:24:32 +0200 Subject: [PATCH 52/52] Add testnet 6.6 configs --- src/checkpoints/checkpoints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index f0259f890..69e791eaf 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -165,7 +165,7 @@ namespace cryptonote if (nettype == TESTNET) { ADD_CHECKPOINT(1, "753e1454ebc44ffb18d7b390f7393a66ce7f3a8aaaf1e4e857af9df4e80d5784"); - ADD_CHECKPOINT(100000,"35f7c0edccf0721fc2022d8bc43ae4ad086649f7778a6f564a1e7c9238f53cd9"); + ADD_CHECKPOINT(100000,"e255e1515acb9cccca8ebe9ce57718726c27c6e647878e6a18879dd42bb28434"); return true; }