Example #1
0
bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) {
  if (!tx.vin.size()) {
    logger(ERROR) << "tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx);
    return false;
  }

  if (!check_inputs_types_supported(tx)) {
    logger(ERROR) << "unsupported input types for tx id= " << get_transaction_hash(tx);
    return false;
  }

  std::string errmsg;
  if (!check_outs_valid(tx, &errmsg)) {
    logger(ERROR) << "tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx) << ": " << errmsg;
    return false;
  }

  if (!check_money_overflow(tx)) {
    logger(ERROR) << "tx have money overflow, rejected for tx id= " << get_transaction_hash(tx);
    return false;
  }

  uint64_t amount_in = 0;
  get_inputs_money_amount(tx, amount_in);
  uint64_t amount_out = get_outs_money_amount(tx);

  if (amount_in < amount_out) {
    logger(ERROR) << "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()) {
    logger(ERROR) << "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)) {
    logger(ERROR) << "tx has a few inputs with identical keyimages";
    return false;
  }

  if (!checkMultisignatureInputsDiff(tx)) {
    logger(ERROR) << "tx has a few multisignature inputs with identical output indexes";
    return false;
  }

  return true;
}
bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& transaction, TransactionDetails& transactionDetails, uint64_t timestamp) {
  Crypto::Hash hash = getObjectHash(transaction);
  transactionDetails.hash = hash;

  transactionDetails.timestamp = timestamp;

  Crypto::Hash blockHash;
  uint32_t blockHeight;
  if (!core.getBlockContainingTx(hash, blockHash, blockHeight)) {
    transactionDetails.inBlockchain = false;
    transactionDetails.blockHeight = boost::value_initialized<uint32_t>();
    transactionDetails.blockHash = boost::value_initialized<Crypto::Hash>();
  } else {
    transactionDetails.inBlockchain = true;
    transactionDetails.blockHeight = blockHeight;
    transactionDetails.blockHash = blockHash;
    if (timestamp == 0) {
      Block block;
      if (!core.getBlockByHash(blockHash, block)) {
        return false;
      }
      transactionDetails.timestamp = block.timestamp;
    }
  }

  transactionDetails.size = getObjectBinarySize(transaction);
  transactionDetails.unlockTime = transaction.unlockTime;
  transactionDetails.totalOutputsAmount = get_outs_money_amount(transaction);

  uint64_t inputsAmount;
  if (!get_inputs_money_amount(transaction, inputsAmount)) {
    return false;
  }
  transactionDetails.totalInputsAmount = inputsAmount;

  if (transaction.inputs.size() > 0 && transaction.inputs.front().type() == typeid(BaseInput)) {
    //It's gen transaction
    transactionDetails.fee = 0;
    transactionDetails.mixin = 0;
  } else {
    uint64_t fee;
    if (!get_tx_fee(transaction, fee)) {
      return false;
    }
    transactionDetails.fee = fee;
    uint64_t mixin;
    if (!getMixin(transaction, mixin)) {
      return false;
    }
    transactionDetails.mixin = mixin;
  }

  Crypto::Hash paymentId;
  if (getPaymentId(transaction, paymentId)) {
    transactionDetails.paymentId = paymentId;
  } else {
    transactionDetails.paymentId = boost::value_initialized<Crypto::Hash>();
  }

  fillTxExtra(transaction.extra, transactionDetails.extra);

  transactionDetails.signatures.reserve(transaction.signatures.size());
  for (const std::vector<Crypto::Signature>& signatures : transaction.signatures) {
    std::vector<Crypto::Signature> signaturesDetails;
    signaturesDetails.reserve(signatures.size());
    for (const Crypto::Signature& signature : signatures) {
      signaturesDetails.push_back(std::move(signature));
    }
    transactionDetails.signatures.push_back(std::move(signaturesDetails));
  }

  transactionDetails.inputs.reserve(transaction.inputs.size());
  for (const TransactionInput& txIn : transaction.inputs) {
    TransactionInputDetails txInDetails;

    if (txIn.type() == typeid(BaseInput)) {
      TransactionInputGenerateDetails txInGenDetails;
      txInGenDetails.height = boost::get<BaseInput>(txIn).blockIndex;
      txInDetails.amount = 0;
      for (const TransactionOutput& out : transaction.outputs) {
        txInDetails.amount += out.amount;
      }
      txInDetails.input = txInGenDetails;
    } else if (txIn.type() == typeid(KeyInput)) {
      TransactionInputToKeyDetails txInToKeyDetails;
      const KeyInput& txInToKey = boost::get<KeyInput>(txIn);
      std::list<std::pair<Crypto::Hash, size_t>> outputReferences;
      if (!core.scanOutputkeysForIndices(txInToKey, outputReferences)) {
        return false;
      }
      txInDetails.amount = txInToKey.amount;
      txInToKeyDetails.outputIndexes = txInToKey.outputIndexes;
      txInToKeyDetails.keyImage = txInToKey.keyImage;
      txInToKeyDetails.mixin = txInToKey.outputIndexes.size();
      txInToKeyDetails.output.number = outputReferences.back().second;
      txInToKeyDetails.output.transactionHash = outputReferences.back().first;
      txInDetails.input = txInToKeyDetails;
    } else if (txIn.type() == typeid(MultisignatureInput)) {
      TransactionInputMultisignatureDetails txInMultisigDetails;
      const MultisignatureInput& txInMultisig = boost::get<MultisignatureInput>(txIn);
      txInDetails.amount = txInMultisig.amount;
      txInMultisigDetails.signatures = txInMultisig.signatureCount;
      std::pair<Crypto::Hash, size_t> outputReference;
      if (!core.getMultisigOutputReference(txInMultisig, outputReference)) {
        return false;
      }
      txInMultisigDetails.output.number = outputReference.second;
      txInMultisigDetails.output.transactionHash = outputReference.first;
      txInDetails.input = txInMultisigDetails;
    } else {
      return false;
    }
    transactionDetails.inputs.push_back(std::move(txInDetails));
  }

  transactionDetails.outputs.reserve(transaction.outputs.size());
  std::vector<uint32_t> globalIndices;
  globalIndices.reserve(transaction.outputs.size());
  if (!transactionDetails.inBlockchain || !core.get_tx_outputs_gindexs(hash, globalIndices)) {
    for (size_t i = 0; i < transaction.outputs.size(); ++i) {
      globalIndices.push_back(0);
    }
  }

  typedef boost::tuple<TransactionOutput, uint32_t> outputWithIndex;
  auto range = boost::combine(transaction.outputs, globalIndices);
  for (const outputWithIndex& txOutput : range) {
    TransactionOutputDetails txOutDetails;
    txOutDetails.amount = txOutput.get<0>().amount;
    txOutDetails.globalIndex = txOutput.get<1>();

    if (txOutput.get<0>().target.type() == typeid(KeyOutput)) {
      TransactionOutputToKeyDetails txOutToKeyDetails;
      txOutToKeyDetails.txOutKey = boost::get<KeyOutput>(txOutput.get<0>().target).key;
      txOutDetails.output = txOutToKeyDetails;
    } else if (txOutput.get<0>().target.type() == typeid(MultisignatureOutput)) {
      TransactionOutputMultisignatureDetails txOutMultisigDetails;
      MultisignatureOutput txOutMultisig = boost::get<MultisignatureOutput>(txOutput.get<0>().target);
      txOutMultisigDetails.keys.reserve(txOutMultisig.keys.size());
      for (const Crypto::PublicKey& key : txOutMultisig.keys) {
        txOutMultisigDetails.keys.push_back(std::move(key));
      }
      txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatureCount;
      txOutDetails.output = txOutMultisigDetails;
    } else {
      return false;
    }
    transactionDetails.outputs.push_back(std::move(txOutDetails));
  }

  return true;
}
Example #3
0
	//---------------------------------------------------------------------------------
	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;
	}
Example #4
0
//---------------------------------------------------------------------------------
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;
}
Example #5
0
  //---------------------------------------------------------------------------------
  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;
  }