2015-05-27 12:08:46 +00:00
|
|
|
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
|
2014-08-13 10:38:35 +00:00
|
|
|
//
|
|
|
|
// This file is part of Bytecoin.
|
|
|
|
//
|
|
|
|
// Bytecoin is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// Bytecoin is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
|
|
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2014-08-13 10:51:37 +00:00
|
|
|
#include "cryptonote_core/account.h"
|
2015-04-06 16:13:07 +00:00
|
|
|
#include "cryptonote_core/cryptonote_format_utils.h"
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
#include "WalletTransactionSender.h"
|
|
|
|
#include "WalletUtils.h"
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
#include "cryptonote_core/cryptonote_basic_impl.h"
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
#include <Logging/LoggerGroup.h>
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
#include <random>
|
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
namespace {
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
using namespace CryptoNote;
|
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
uint64_t countNeededMoney(uint64_t fee, const std::vector<CryptoNote::Transfer>& transfers) {
|
|
|
|
uint64_t needed_money = fee;
|
|
|
|
for (auto& transfer: transfers) {
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::throwIf(transfer.amount == 0, CryptoNote::error::ZERO_DESTINATION);
|
|
|
|
CryptoNote::throwIf(transfer.amount < 0, CryptoNote::error::WRONG_AMOUNT);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
|
|
|
needed_money += transfer.amount;
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::throwIf(static_cast<int64_t>(needed_money) < transfer.amount, CryptoNote::error::SUM_OVERFLOW);
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return needed_money;
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
void createChangeDestinations(const CryptoNote::AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, CryptoNote::tx_destination_entry& changeDts) {
|
2014-06-25 17:21:42 +00:00
|
|
|
if (neededMoney < foundMoney) {
|
|
|
|
changeDts.addr = address;
|
|
|
|
changeDts.amount = foundMoney - neededMoney;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
void constructTx(const CryptoNote::account_keys keys, const std::vector<CryptoNote::tx_source_entry>& sources, const std::vector<CryptoNote::tx_destination_entry>& splittedDests,
|
|
|
|
const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, CryptoNote::Transaction& tx) {
|
2014-06-25 17:21:42 +00:00
|
|
|
std::vector<uint8_t> extraVec;
|
|
|
|
extraVec.reserve(extra.size());
|
|
|
|
std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);});
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
Logging::LoggerGroup nullLog;
|
|
|
|
bool r = CryptoNote::construct_tx(keys, sources, splittedDests, extraVec, tx, unlockTimestamp, nullLog);
|
|
|
|
|
|
|
|
CryptoNote::throwIf(!r, CryptoNote::error::INTERNAL_WALLET_ERROR);
|
|
|
|
CryptoNote::throwIf(CryptoNote::get_object_blobsize(tx) >= sizeLimit, CryptoNote::error::TRANSACTION_SIZE_TOO_BIG);
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
void fillTransactionHash(const CryptoNote::Transaction& tx, CryptoNote::TransactionHash& hash) {
|
|
|
|
crypto::hash h = CryptoNote::get_transaction_hash(tx);
|
2014-06-25 17:21:42 +00:00
|
|
|
memcpy(hash.data(), reinterpret_cast<const uint8_t *>(&h), hash.size());
|
|
|
|
}
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
std::shared_ptr<WalletEvent> makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) {
|
|
|
|
transactionCache.updateTransactionSendingState(transactionId, ec);
|
|
|
|
return std::make_shared<WalletSendTransactionCompletedEvent>(transactionId, ec);
|
|
|
|
}
|
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
} //namespace
|
|
|
|
|
|
|
|
namespace CryptoNote {
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
WalletTransactionSender::WalletTransactionSender(const CryptoNote::Currency& currency, WalletUserTransactionsCache& transactionsCache, CryptoNote::account_keys keys, ITransfersContainer& transfersContainer) :
|
2015-04-06 16:13:07 +00:00
|
|
|
m_currency(currency),
|
|
|
|
m_transactionsCache(transactionsCache),
|
|
|
|
m_isStoping(false),
|
|
|
|
m_keys(keys),
|
|
|
|
m_transferDetails(transfersContainer),
|
|
|
|
m_upperTransactionSizeLimit(m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize()) {}
|
2014-06-25 17:21:42 +00:00
|
|
|
|
|
|
|
void WalletTransactionSender::stop() {
|
|
|
|
m_isStoping = true;
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:51:37 +00:00
|
|
|
bool WalletTransactionSender::validateDestinationAddress(const std::string& address) {
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::AccountPublicAddress ignore;
|
2014-08-13 10:51:37 +00:00
|
|
|
return m_currency.parseAccountAddressString(address, ignore);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletTransactionSender::validateTransfersAddresses(const std::vector<Transfer>& transfers) {
|
|
|
|
for (const Transfer& tr: transfers) {
|
|
|
|
if (!validateDestinationAddress(tr.address)) {
|
2015-05-27 12:08:46 +00:00
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS));
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
std::shared_ptr<WalletRequest> WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque<std::shared_ptr<WalletEvent> >& events,
|
|
|
|
const std::vector<Transfer>& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) {
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
using namespace CryptoNote;
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
throwIf(transfers.empty(), CryptoNote::error::ZERO_DESTINATION);
|
2014-08-13 10:51:37 +00:00
|
|
|
validateTransfersAddresses(transfers);
|
|
|
|
uint64_t neededMoney = countNeededMoney(fee, transfers);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2014-08-13 10:51:37 +00:00
|
|
|
std::shared_ptr<SendTransactionContext> context = std::make_shared<SendTransactionContext>();
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
context->foundMoney = selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers);
|
2015-05-27 12:08:46 +00:00
|
|
|
throwIf(context->foundMoney < neededMoney, CryptoNote::error::WRONG_AMOUNT);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
transactionId = m_transactionsCache.addNewTransaction(neededMoney, fee, extra, transfers, unlockTimestamp);
|
|
|
|
context->transactionId = transactionId;
|
2014-06-25 17:21:42 +00:00
|
|
|
context->mixIn = mixIn;
|
|
|
|
|
|
|
|
if(context->mixIn) {
|
|
|
|
std::shared_ptr<WalletRequest> request = makeGetRandomOutsRequest(context);
|
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
|
|
|
return doSendTransaction(context, events);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<WalletRequest> WalletTransactionSender::makeGetRandomOutsRequest(std::shared_ptr<SendTransactionContext> context) {
|
|
|
|
uint64_t outsCount = context->mixIn + 1;// add one to make possible (if need) to skip real output key
|
|
|
|
std::vector<uint64_t> amounts;
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
for (const auto& td : context->selectedTransfers) {
|
|
|
|
amounts.push_back(td.amount);
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::make_shared<WalletGetRandomOutsByAmountsRequest>(amounts, outsCount, context, std::bind(&WalletTransactionSender::sendTransactionRandomOutsByAmount,
|
|
|
|
this, context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr<SendTransactionContext> context, std::deque<std::shared_ptr<WalletEvent> >& events,
|
|
|
|
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec) {
|
2015-04-06 16:13:07 +00:00
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
if (m_isStoping) {
|
2015-05-27 12:08:46 +00:00
|
|
|
ec = make_error_code(CryptoNote::error::TX_CANCELLED);
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ec) {
|
2015-04-06 16:13:07 +00:00
|
|
|
events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec));
|
2014-06-25 17:21:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(),
|
2015-05-27 12:08:46 +00:00
|
|
|
[&] (CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;});
|
2014-06-25 17:21:42 +00:00
|
|
|
|
|
|
|
if (scanty_it != context->outs.end()) {
|
2015-05-27 12:08:46 +00:00
|
|
|
events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG)));
|
2014-06-25 17:21:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<WalletRequest> req = doSendTransaction(context, events);
|
|
|
|
if (req)
|
|
|
|
nextRequest = req;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<WalletRequest> WalletTransactionSender::doSendTransaction(std::shared_ptr<SendTransactionContext> context, std::deque<std::shared_ptr<WalletEvent> >& events) {
|
|
|
|
if (m_isStoping) {
|
2015-05-27 12:08:46 +00:00
|
|
|
events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::TX_CANCELLED)));
|
2014-06-25 17:21:42 +00:00
|
|
|
return std::shared_ptr<WalletRequest>();
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2014-08-13 10:51:37 +00:00
|
|
|
TransactionInfo& transaction = m_transactionsCache.getTransaction(context->transactionId);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
std::vector<CryptoNote::tx_source_entry> sources;
|
2014-06-25 17:21:42 +00:00
|
|
|
prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn);
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::tx_destination_entry changeDts;
|
|
|
|
changeDts.amount = 0;
|
2015-04-06 16:13:07 +00:00
|
|
|
uint64_t totalAmount = -transaction.totalAmount;
|
|
|
|
createChangeDestinations(m_keys.m_account_address, totalAmount, context->foundMoney, changeDts);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
std::vector<CryptoNote::tx_destination_entry> splittedDests;
|
2014-06-25 17:21:42 +00:00
|
|
|
splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests);
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::Transaction tx;
|
2015-04-06 16:13:07 +00:00
|
|
|
constructTx(m_keys, sources, splittedDests, transaction.extra, transaction.unlockTime, m_upperTransactionSizeLimit, tx);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
|
|
|
fillTransactionHash(tx, transaction.hash);
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
m_transactionsCache.updateTransaction(context->transactionId, tx, totalAmount, context->selectedTransfers);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
notifyBalanceChanged(events);
|
|
|
|
|
|
|
|
return std::make_shared<WalletRelayTransactionRequest>(tx, std::bind(&WalletTransactionSender::relayTransactionCallback, this, context,
|
|
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
catch(std::system_error& ec) {
|
2015-04-06 16:13:07 +00:00
|
|
|
events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code()));
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
catch(std::exception&) {
|
2015-05-27 12:08:46 +00:00
|
|
|
events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)));
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::shared_ptr<WalletRequest>();
|
|
|
|
}
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
void WalletTransactionSender::relayTransactionCallback(std::shared_ptr<SendTransactionContext> context, std::deque<std::shared_ptr<WalletEvent> >& events,
|
2014-06-25 17:21:42 +00:00
|
|
|
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec) {
|
2015-04-06 16:13:07 +00:00
|
|
|
if (m_isStoping) {
|
|
|
|
return;
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec));
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
void WalletTransactionSender::splitDestinations(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& changeDts,
|
|
|
|
const TxDustPolicy& dustPolicy, std::vector<CryptoNote::tx_destination_entry>& splittedDests) {
|
2014-06-25 17:21:42 +00:00
|
|
|
uint64_t dust = 0;
|
|
|
|
|
|
|
|
digitSplitStrategy(firstTransferId, transfersCount, changeDts, dustPolicy.dustThreshold, splittedDests, dust);
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
throwIf(dustPolicy.dustThreshold < dust, CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2014-06-25 17:21:42 +00:00
|
|
|
if (0 != dust && !dustPolicy.addToFee) {
|
2015-05-27 12:08:46 +00:00
|
|
|
splittedDests.push_back(CryptoNote::tx_destination_entry(dust, dustPolicy.addrForDust));
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, size_t transfersCount,
|
2015-05-27 12:08:46 +00:00
|
|
|
const CryptoNote::tx_destination_entry& change_dst, uint64_t dust_threshold,
|
|
|
|
std::vector<CryptoNote::tx_destination_entry>& splitted_dsts, uint64_t& dust) {
|
2014-06-25 17:21:42 +00:00
|
|
|
splitted_dsts.clear();
|
|
|
|
dust = 0;
|
|
|
|
|
|
|
|
for (TransferId idx = firstTransferId; idx < firstTransferId + transfersCount; ++idx) {
|
|
|
|
Transfer& de = m_transactionsCache.getTransfer(idx);
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::AccountPublicAddress addr;
|
2014-08-13 10:51:37 +00:00
|
|
|
if (!m_currency.parseAccountAddressString(de.address, addr)) {
|
2015-05-27 12:08:46 +00:00
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS));
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::decompose_amount_into_digits(de.amount, dust_threshold,
|
|
|
|
[&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, addr)); },
|
|
|
|
[&](uint64_t a_dust) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(a_dust, addr)); } );
|
2014-06-25 17:21:42 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::decompose_amount_into_digits(change_dst.amount, dust_threshold,
|
|
|
|
[&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, change_dst.addr)); },
|
2014-06-25 17:21:42 +00:00
|
|
|
[&](uint64_t a_dust) { dust = a_dust; } );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
void WalletTransactionSender::prepareInputs(
|
|
|
|
const std::list<TransactionOutputInformation>& selectedTransfers,
|
2015-05-27 12:08:46 +00:00
|
|
|
std::vector<CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& outs,
|
|
|
|
std::vector<CryptoNote::tx_source_entry>& sources, uint64_t mixIn) {
|
2015-04-06 16:13:07 +00:00
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
size_t i = 0;
|
2015-04-06 16:13:07 +00:00
|
|
|
|
|
|
|
for (const auto& td: selectedTransfers) {
|
2014-06-25 17:21:42 +00:00
|
|
|
sources.resize(sources.size()+1);
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::tx_source_entry& src = sources.back();
|
2015-04-06 16:13:07 +00:00
|
|
|
|
|
|
|
src.amount = td.amount;
|
2014-06-25 17:21:42 +00:00
|
|
|
|
|
|
|
//paste mixin transaction
|
|
|
|
if(outs.size()) {
|
2015-05-27 12:08:46 +00:00
|
|
|
outs[i].outs.sort([](const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;});
|
2014-06-25 17:21:42 +00:00
|
|
|
for (auto& daemon_oe: outs[i].outs) {
|
|
|
|
if(td.globalOutputIndex == daemon_oe.global_amount_index)
|
|
|
|
continue;
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::tx_source_entry::output_entry oe;
|
2014-06-25 17:21:42 +00:00
|
|
|
oe.first = daemon_oe.global_amount_index;
|
|
|
|
oe.second = daemon_oe.out_key;
|
|
|
|
src.outputs.push_back(oe);
|
|
|
|
if(src.outputs.size() >= mixIn)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//paste real transaction to the random index
|
2015-05-27 12:08:46 +00:00
|
|
|
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const CryptoNote::tx_source_entry::output_entry& a) { return a.first >= td.globalOutputIndex; });
|
2014-06-25 17:21:42 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CryptoNote::tx_source_entry::output_entry real_oe;
|
2014-06-25 17:21:42 +00:00
|
|
|
real_oe.first = td.globalOutputIndex;
|
2015-04-06 16:13:07 +00:00
|
|
|
real_oe.second = reinterpret_cast<const crypto::public_key&>(td.outputKey);
|
2014-06-25 17:21:42 +00:00
|
|
|
|
|
|
|
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
src.real_out_tx_key = reinterpret_cast<const crypto::public_key&>(td.transactionPublicKey);
|
2014-06-25 17:21:42 +00:00
|
|
|
src.real_output = interted_it - src.outputs.begin();
|
2015-04-06 16:13:07 +00:00
|
|
|
src.real_output_in_tx_index = td.outputInTransaction;
|
2014-06-25 17:21:42 +00:00
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletTransactionSender::notifyBalanceChanged(std::deque<std::shared_ptr<WalletEvent> >& events) {
|
2015-04-06 16:13:07 +00:00
|
|
|
uint64_t unconfirmedOutsAmount = m_transactionsCache.unconfrimedOutsAmount();
|
|
|
|
uint64_t change = unconfirmedOutsAmount - m_transactionsCache.unconfirmedTransactionsAmount();
|
|
|
|
|
|
|
|
uint64_t actualBalance = m_transferDetails.balance(ITransfersContainer::IncludeKeyUnlocked) - unconfirmedOutsAmount;
|
|
|
|
uint64_t pendingBalance = m_transferDetails.balance(ITransfersContainer::IncludeKeyNotUnlocked) + change;
|
2014-06-25 17:21:42 +00:00
|
|
|
|
|
|
|
events.push_back(std::make_shared<WalletActualBalanceUpdatedEvent>(actualBalance));
|
|
|
|
events.push_back(std::make_shared<WalletPendingBalanceUpdatedEvent>(pendingBalance));
|
|
|
|
}
|
|
|
|
|
2015-04-06 16:13:07 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
template<typename URNG, typename T>
|
|
|
|
T popRandomValue(URNG& randomGenerator, std::vector<T>& vec) {
|
2015-07-15 12:23:00 +00:00
|
|
|
assert(!vec.empty());
|
|
|
|
|
|
|
|
if (vec.empty()) {
|
|
|
|
return T();
|
|
|
|
}
|
2015-04-06 16:13:07 +00:00
|
|
|
|
|
|
|
std::uniform_int_distribution<size_t> distribution(0, vec.size() - 1);
|
|
|
|
size_t idx = distribution(randomGenerator);
|
|
|
|
|
|
|
|
T res = vec[idx];
|
|
|
|
if (idx + 1 != vec.size()) {
|
|
|
|
vec[idx] = vec.back();
|
|
|
|
}
|
|
|
|
vec.resize(vec.size() - 1);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t WalletTransactionSender::selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list<TransactionOutputInformation>& selectedTransfers) {
|
|
|
|
|
|
|
|
std::vector<size_t> unusedTransfers;
|
|
|
|
std::vector<size_t> unusedDust;
|
|
|
|
|
|
|
|
std::vector<TransactionOutputInformation> outputs;
|
|
|
|
m_transferDetails.getOutputs(outputs, ITransfersContainer::IncludeKeyUnlocked);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < outputs.size(); ++i) {
|
|
|
|
const auto& out = outputs[i];
|
|
|
|
if (!m_transactionsCache.isUsed(out)) {
|
|
|
|
if (dust < out.amount)
|
|
|
|
unusedTransfers.push_back(i);
|
|
|
|
else
|
|
|
|
unusedDust.push_back(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::default_random_engine randomGenerator(crypto::rand<std::default_random_engine::result_type>());
|
|
|
|
bool selectOneDust = addDust && !unusedDust.empty();
|
|
|
|
uint64_t foundMoney = 0;
|
|
|
|
|
|
|
|
while (foundMoney < neededMoney && (!unusedTransfers.empty() || !unusedDust.empty())) {
|
|
|
|
size_t idx;
|
|
|
|
if (selectOneDust) {
|
|
|
|
idx = popRandomValue(randomGenerator, unusedDust);
|
|
|
|
selectOneDust = false;
|
|
|
|
} else {
|
|
|
|
idx = !unusedTransfers.empty() ? popRandomValue(randomGenerator, unusedTransfers) : popRandomValue(randomGenerator, unusedDust);
|
|
|
|
}
|
|
|
|
|
|
|
|
selectedTransfers.push_back(outputs[idx]);
|
|
|
|
foundMoney += outputs[idx].amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return foundMoney;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-25 17:21:42 +00:00
|
|
|
} /* namespace CryptoNote */
|