//----------------------------------------------------------------------------------------------- bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { if(!tx.vin.size()) { LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx)); return false; } if(!check_inputs_types_supported(tx)) { LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx)); return false; } if(!check_outs_valid(tx)) { LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx)); return false; } if(!check_money_overflow(tx)) { LOG_PRINT_RED_L0("tx have money overflow, rejected for tx id= " << get_transaction_hash(tx)); return false; } uint64_t amount_in = m_currency.getTransactionAllInputsAmount(tx); uint64_t amount_out = get_outs_money_amount(tx); if(amount_in <= amount_out) { LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx)); return false; } if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) { LOG_PRINT_RED_L0("transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " << (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize())); return false; } //check if tx use different key images if(!check_tx_inputs_keyimages_diff(tx)) { LOG_PRINT_RED_L0("tx has a few inputs with identical keyimages"); return false; } if (!checkMultisignatureInputsDiff(tx)) { LOG_PRINT_RED_L0("tx has a few multisignature inputs with identical output indexes"); return false; } return true; }
//------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_clonetelepod(const wallet_rpc::COMMAND_RPC_CLONETELEPOD::request& req, wallet_rpc::COMMAND_RPC_CLONETELEPOD::response& res, epee::json_rpc::error& er, connection_context& cntx) { currency::transaction tx2 = AUTO_VAL_INIT(tx2); //new destination account currency::account_base acc2 = AUTO_VAL_INIT(acc2); acc2.generate(); if (!build_transaction_from_telepod(req.tpd, acc2.get_keys().m_account_address, tx2, res.status)) { LOG_ERROR("Failed to build_transaction_from_telepod(...)"); return true; } //send transaction to daemon currency::COMMAND_RPC_SEND_RAW_TX::request req_send_raw; req_send_raw.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx2)); currency::COMMAND_RPC_SEND_RAW_TX::response rsp_send_raw; bool r = m_wallet.get_core_proxy()->call_COMMAND_RPC_SEND_RAW_TX(req_send_raw, rsp_send_raw); if (!r || rsp_send_raw.status != CORE_RPC_STATUS_OK) { LOG_ERROR("Problem with construct_tx(....), blobl size os too big: " << get_object_blobsize(tx2)); res.status = "INTERNAL_ERROR"; return true; } res.tpd.basement_tx_id_hex = string_tools::pod_to_hex(currency::get_transaction_hash(tx2)); std::string acc2_buff = epee::serialization::store_t_to_binary(acc2); res.tpd.account_keys_hex = string_tools::buff_to_hex_nodelimer(acc2_buff); res.status = "OK"; LOG_PRINT_GREEN("TELEPOD ISSUED [" << currency::print_money(currency::get_outs_money_amount(tx2)) << "BBR, base_tx_id: ]" << currency::get_transaction_hash(tx2), LOG_LEVEL_0); return true; }
//--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block) { crypto::hash h = null_hash; size_t blob_size = get_object_blobsize(tx); get_transaction_hash(tx, h); return add_tx(tx, h, blob_size, tvc, keeped_by_block); }
//------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_withdrawtelepod(const wallet_rpc::COMMAND_RPC_WITHDRAWTELEPOD::request& req, wallet_rpc::COMMAND_RPC_WITHDRAWTELEPOD::response& res, epee::json_rpc::error& er, connection_context& cntx) { currency::transaction tx2 = AUTO_VAL_INIT(tx2); //parse destination add currency::account_public_address acc_addr = AUTO_VAL_INIT(acc_addr); if (!currency::get_account_address_from_str(acc_addr, req.addr)) { LOG_ERROR("Failed to build_transaction_from_telepod(...)"); res.status = "BAD_ADDRESS"; return true; } if (!build_transaction_from_telepod(req.tpd, acc_addr, tx2, res.status)) { LOG_ERROR("Failed to build_transaction_from_telepod(...)"); return true; } //send transaction to daemon currency::COMMAND_RPC_SEND_RAW_TX::request req_send_raw; req_send_raw.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx2)); currency::COMMAND_RPC_SEND_RAW_TX::response rsp_send_raw; bool r = m_wallet.get_core_proxy()->call_COMMAND_RPC_SEND_RAW_TX(req_send_raw, rsp_send_raw); if (!r || rsp_send_raw.status != CORE_RPC_STATUS_OK) { LOG_ERROR("Problem with construct_tx(....), blobl size os too big: " << get_object_blobsize(tx2)); res.status = "INTERNAL_ERROR"; return true; } res.status = "OK"; LOG_PRINT_GREEN("TELEPOD WITHDRAWN [" << currency::print_money(currency::get_outs_money_amount(tx2)) << "BBR, tx_id: ]" << currency::get_transaction_hash(tx2), LOG_LEVEL_0); return true; }
//------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::build_transaction_from_telepod(const wallet_rpc::telepod& tlp, const currency::account_public_address& acc2, currency::transaction& tx2, std::string& status) { //check if base transaction confirmed currency::COMMAND_RPC_GET_TRANSACTIONS::request get_tx_req = AUTO_VAL_INIT(get_tx_req); currency::COMMAND_RPC_GET_TRANSACTIONS::response get_tx_rsp = AUTO_VAL_INIT(get_tx_rsp); get_tx_req.txs_hashes.push_back(tlp.basement_tx_id_hex); if (!m_wallet.get_core_proxy()->call_COMMAND_RPC_GET_TRANSACTIONS(get_tx_req, get_tx_rsp) || get_tx_rsp.status != CORE_RPC_STATUS_OK || !get_tx_rsp.txs_as_hex.size()) { status = "UNCONFIRMED"; return false; } //extract account keys std::string acc_buff; currency::account_base acc = AUTO_VAL_INIT(acc); if (!string_tools::parse_hexstr_to_binbuff(tlp.account_keys_hex, acc_buff)) { LOG_ERROR("Failed to parse_hexstr_to_binbuff(tlp.account_keys_hex, acc_buff)"); status = "BAD"; return false; } if (!epee::serialization::load_t_from_binary(acc, acc_buff)) { LOG_ERROR("Failed to load_t_from_binary(acc, acc_buff)"); status = "BAD"; return false; } //extract transaction currency::transaction tx = AUTO_VAL_INIT(tx); std::string buff; if (!string_tools::parse_hexstr_to_binbuff(get_tx_rsp.txs_as_hex.back(), buff)) { LOG_ERROR("Failed to parse_hexstr_to_binbuff(get_tx_rsp.txs_as_hex.back(), buff)"); status = "INTERNAL_ERROR"; return false; } if (!currency::parse_and_validate_tx_from_blob(buff, tx)) { LOG_ERROR("Failed to currency::parse_and_validate_tx_from_blob(buff, tx)"); status = "INTERNAL_ERROR"; return false; } crypto::public_key tx_pub_key = currency::get_tx_pub_key_from_extra(tx); if (tx_pub_key == currency::null_pkey) { LOG_ERROR("Failed to currency::get_tx_pub_key_from_extra(tx)"); status = "BAD"; return false; } //get transaction global output indices currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request get_ind_req = AUTO_VAL_INIT(get_ind_req); currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response get_ind_rsp = AUTO_VAL_INIT(get_ind_rsp); get_ind_req.txid = currency::get_transaction_hash(tx); if (!m_wallet.get_core_proxy()->call_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES(get_ind_req, get_ind_rsp) || get_ind_rsp.status != CORE_RPC_STATUS_OK || get_ind_rsp.o_indexes.size() != tx.vout.size()) { LOG_ERROR("Problem with call_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES(....) "); status = "INTERNAL_ERROR"; return false; } //prepare inputs std::vector<currency::tx_source_entry> sources; size_t i = 0; uint64_t amount = 0; for (auto& o : get_ind_rsp.o_indexes) { //check if input is for telepod's address if (currency::is_out_to_acc(acc.get_keys(), boost::get<currency::txout_to_key>(tx.vout[i].target), tx_pub_key, i)) { //income output amount += tx.vout[i].amount; sources.resize(sources.size() + 1); currency::tx_source_entry& tse = sources.back(); tse.amount = tx.vout[i].amount; tse.outputs.push_back(currency::tx_source_entry::output_entry(o, boost::get<currency::txout_to_key>(tx.vout[i].target).key)); tse.real_out_tx_key = tx_pub_key; tse.real_output = 0; tse.real_output_in_tx_index = i; } ++i; } //prepare outputs std::vector<currency::tx_destination_entry> dsts(1); currency::tx_destination_entry& dst = dsts.back(); dst.addr = acc2; dst.amount = amount - DEFAULT_FEE; //generate transaction const std::vector<uint8_t> extra; bool r = currency::construct_tx(acc.get_keys(), sources, dsts, extra, tx2, 0); if (!r) { LOG_ERROR("Problem with construct_tx(....) "); status = "INTERNAL_ERROR"; return false; } if (CURRENCY_MAX_TRANSACTION_BLOB_SIZE <= get_object_blobsize(tx2)) { LOG_ERROR("Problem with construct_tx(....), blobl size os too big: " << get_object_blobsize(tx2)); status = "INTERNAL_ERROR"; return false; } 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; }