bool TransactionPrefixImpl::validateInputs() const { return check_inputs_types_supported(m_txPrefix) && check_inputs_overflow(m_txPrefix) && checkInputsKeyimagesDiff(m_txPrefix) && checkMultisignatureInputsDiff(m_txPrefix); }
//--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block) { //#9Protection from big transaction flood if(!kept_by_block && blob_size > m_blockchain.get_current_comulative_blocksize_limit() / 2) { LOG_PRINT_L0("transaction is too big for current transaction flow, tx_id: " << id); tvc.m_verifivation_failed = true; return false; } //TODO: add rule for relay, based on tx size/fee ratio if(!check_inputs_types_supported(tx)) { tvc.m_verifivation_failed = true; return false; } uint64_t inputs_amount = 0; if(!get_inputs_money_amount(tx, inputs_amount)) { tvc.m_verifivation_failed = true; return false; } uint64_t outputs_amount = get_outs_money_amount(tx); if(outputs_amount >= inputs_amount) { LOG_PRINT_L0("transaction use more money then it has: use " << outputs_amount << ", have " << inputs_amount); tvc.m_verifivation_failed = true; return false; } //check key images for transaction if it is not kept by block if(!kept_by_block) { if(have_tx_keyimges_as_spent(tx)) { LOG_ERROR("Transaction with id= "<< id << " used already spent key images"); tvc.m_verifivation_failed = true; return false; } } crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); CRITICAL_REGION_LOCAL(m_transactions_lock); if(!ch_inp_res) { if(kept_by_block) { //anyway add this transaction to pool, because it related to block auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; txd_p.first->second.fee = inputs_amount - outputs_amount; txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_height = 0; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.receive_time = time(nullptr); tvc.m_verifivation_impossible = true; tvc.m_added_to_pool = true; } else { LOG_PRINT_L0("tx used wrong inputs, rejected"); tvc.m_verifivation_failed = true; return false; } } else { //update transactions container auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.fee = inputs_amount - outputs_amount; txd_p.first->second.max_used_block_id = max_used_block_id; txd_p.first->second.max_used_block_height = max_used_block_height; txd_p.first->second.last_failed_height = 0; txd_p.first->second.last_failed_id = null_hash; txd_p.first->second.receive_time = time(nullptr); tvc.m_added_to_pool = true; if(txd_p.first->second.fee > 0) tvc.m_should_be_relayed = true; } tvc.m_verifivation_failed = true; //update image_keys container, here should everything goes ok. BOOST_FOREACH(const auto& in, tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image]; CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: keeped_by_block=" << kept_by_block << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL << "tx_id=" << id ); 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"); } tvc.m_verifivation_failed = false; //succeed return true; }
//--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const transaction &tx, const crypto::hash &id, tx_verification_context& tvc, bool kept_by_block, std::string alias) { size_t blob_size = get_object_blobsize(tx); //#9Protection from big transaction flood if (!kept_by_block && blob_size > currency::get_max_transaction_blob_size(m_blockchain.get_current_blockchain_height())) { LOG_PRINT_L0("transaction is too big (" << blob_size << ")bytes for current transaction flow, tx_id: " << id); tvc.m_verifivation_failed = true; return false; } if (!check_inputs_types_supported(tx)) { tvc.m_verifivation_failed = true; return false; } uint64_t inputs_amount = 0; if (!get_inputs_money_amount(tx, inputs_amount)) { tvc.m_verifivation_failed = true; return false; } uint64_t outputs_amount = get_outs_money_amount(tx); if (outputs_amount >= inputs_amount) { LOG_PRINT_L0("transaction use more money then it has: use " << outputs_amount << ", have " << inputs_amount); tvc.m_verifivation_failed = true; return false; } //check key images for transaction if it is not kept by blockhave_tx_keyimges_as_spent if (!kept_by_block) { if (have_tx_keyimges_as_spent(tx)) { LOG_ERROR("Transaction with id= " << id << " used already spent key images"); tvc.m_verifivation_failed = true; return false; } //transaction spam protection, soft rule if (inputs_amount - outputs_amount < TX_POOL_MINIMUM_FEE) { LOG_ERROR("Transaction with id= " << id << " has too small fee: " << inputs_amount - outputs_amount << ", expected fee: " << DEFAULT_FEE); tvc.m_verifivation_failed = true; return false; } } crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); CRITICAL_REGION_LOCAL(m_transactions_lock); if (!ch_inp_res) { if (kept_by_block) { //if there is a same alias on the block, then delete the tx with the same alias in the pool crypto::hash hash; if (alias.size() && (hash = find_alias(alias)) != null_hash) { transaction tx = AUTO_VAL_INIT(tx); size_t size = 0; uint64_t fee = 0; take_tx(hash, tx, size, fee); LOG_PRINT_L2("Found alias " << alias << " in block, delete pool tx with the same alias: " << id); } //anyway add this transaction to pool, because it related to block auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); if (!txd_p.second) { return false; } //CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; txd_p.first->second.fee = inputs_amount - outputs_amount; txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_height = 0; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.receive_time = time(nullptr); tvc.m_verifivation_impossible = true; tvc.m_added_to_pool = true; } else { LOG_PRINT_L0("tx used wrong inputs, rejected"); tvc.m_verifivation_failed = true; return false; } } else { //check alias repeat or not if (!add_alias_tx_pair(alias, id)) { tvc.m_verifivation_failed = true; tvc.m_added_to_pool = false; return false; } //update transactions container auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); if (!txd_p.second) { return false; } //CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.fee = inputs_amount - outputs_amount; txd_p.first->second.max_used_block_id = max_used_block_id; txd_p.first->second.max_used_block_height = max_used_block_height; txd_p.first->second.last_failed_height = 0; txd_p.first->second.last_failed_id = null_hash; txd_p.first->second.receive_time = time(nullptr); tvc.m_added_to_pool = true; if (txd_p.first->second.fee > 0) tvc.m_should_be_relayed = true; } tvc.m_verifivation_failed = true; //update image_keys container, here should everything goes ok. BOOST_FOREACH(const auto& in, tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image]; CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: keeped_by_block=" << kept_by_block << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL << "tx_id=" << id); 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"); } tvc.m_verifivation_failed = false; //succeed return true; }
//--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block) { if(!check_inputs_types_supported(tx)) { tvc.m_verifivation_failed = true; return false; } uint64_t inputs_amount = 0; if(!get_inputs_money_amount(tx, inputs_amount)) { tvc.m_verifivation_failed = true; return false; } uint64_t outputs_amount = get_outs_money_amount(tx); if(outputs_amount >= inputs_amount) { LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount)); tvc.m_verifivation_failed = true; return false; } uint64_t fee = inputs_amount - outputs_amount; uint64_t needed_fee = blob_size / 1024; needed_fee += (blob_size % 1024) ? 1 : 0; needed_fee *= FEE_PER_KB; if (!kept_by_block && fee < needed_fee /*&& fee < MINING_ALLOWED_LEGACY_FEE*/) { LOG_PRINT_L1("transaction fee is not enough: " << print_money(fee) << ", minumim fee: " << print_money(needed_fee)); tvc.m_verifivation_failed = true; return false; } if (!kept_by_block && blob_size >= TRANSACTION_SIZE_LIMIT) { LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << TRANSACTION_SIZE_LIMIT); tvc.m_verifivation_failed = true; return false; } //check key images for transaction if it is not kept by block if(!kept_by_block) { if(have_tx_keyimges_as_spent(tx)) { LOG_PRINT_L1("Transaction with id= "<< id << " used already spent key images"); tvc.m_verifivation_failed = true; return false; } } crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); CRITICAL_REGION_LOCAL(m_transactions_lock); if(!ch_inp_res) { if(kept_by_block) { //anyway add this transaction to pool, because it related to block auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; txd_p.first->second.fee = inputs_amount - outputs_amount; txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_height = 0; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.receive_time = time(nullptr); tvc.m_verifivation_impossible = true; tvc.m_added_to_pool = true; }else { LOG_PRINT_L1("tx used wrong inputs, rejected"); tvc.m_verifivation_failed = true; return false; } }else { //update transactions container auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.fee = inputs_amount - outputs_amount; txd_p.first->second.max_used_block_id = max_used_block_id; txd_p.first->second.max_used_block_height = max_used_block_height; txd_p.first->second.last_failed_height = 0; txd_p.first->second.last_failed_id = null_hash; txd_p.first->second.receive_time = time(nullptr); tvc.m_added_to_pool = true; if(txd_p.first->second.fee > 0) tvc.m_should_be_relayed = true; } tvc.m_verifivation_failed = true; //update image_keys container, here should everything goes ok. BOOST_FOREACH(const auto& in, tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image]; CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: keeped_by_block=" << kept_by_block << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL << "tx_id=" << id ); 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"); } tvc.m_verifivation_failed = false; m_txs_by_fee.emplace((double)blob_size / fee, id); //succeed return true; }