Change donation

This commit is contained in:
Antonio Juarez 2015-10-01 16:27:18 +01:00
parent 6c35e24634
commit 00915e6d34
52 changed files with 943 additions and 400 deletions

View file

@ -31,7 +31,8 @@ const uint32_t WALLET_UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits<uint3
enum class WalletTransactionState : uint8_t {
SUCCEEDED = 0,
FAILED,
CANCELLED
CANCELLED,
CREATED
};
enum WalletEventType {
@ -77,11 +78,37 @@ struct WalletTransaction {
bool isBase;
};
enum class WalletTransferType : uint8_t {
USUAL = 0,
DONATION
};
struct WalletOrder {
std::string address;
uint64_t amount;
};
struct WalletTransfer {
WalletTransferType type;
std::string address;
int64_t amount;
};
struct DonationSettings {
std::string address;
uint64_t threshold = 0;
};
struct TransactionParameters {
std::string sourceAddress;
std::vector<WalletOrder> destinations;
uint64_t fee = 0;
uint64_t mixIn = 0;
std::string extra;
uint64_t unlockTimestamp = 0;
DonationSettings donation;
};
class IWallet {
public:
virtual ~IWallet() {}
@ -113,10 +140,11 @@ public:
virtual size_t getTransactionTransferCount(size_t transactionIndex) const = 0;
virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const = 0;
virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const std::vector<WalletTransfer>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const std::string& sourceAddress, const std::vector<WalletTransfer>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const std::string& sourceAddress, const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const std::string& sourceAddress, const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0;
virtual size_t transfer(const TransactionParameters& sendingTransaction) = 0;
virtual void start() = 0;
virtual void stop() = 0;

View file

@ -78,12 +78,72 @@ private:
const std::function<void(const INode::Callback&)> requestFunc;
};
BlockchainExplorer::PoolUpdateGuard::PoolUpdateGuard() :
m_state(State::NONE) {
}
bool BlockchainExplorer::PoolUpdateGuard::beginUpdate() {
auto state = m_state.load();
for (;;) {
switch (state) {
case State::NONE:
return true;
case State::UPDATING:
if (m_state.compare_exchange_weak(state, State::UPDATE_REQUIRED)) {
return false;
}
case State::UPDATE_REQUIRED:
return false;
default:
assert(false);
return false;
}
}
}
bool BlockchainExplorer::PoolUpdateGuard::endUpdate() {
auto state = m_state.load();
for (;;) {
assert(state != State::NONE);
if (m_state.compare_exchange_weak(state, State::NONE)) {
return state == State::UPDATE_REQUIRED;
}
}
}
class ScopeExitHandler {
public:
ScopeExitHandler(std::function<void()>&& handler) :
m_handler(std::move(handler)),
m_cancelled(false) {
}
~ScopeExitHandler() {
if (!m_cancelled) {
m_handler();
}
}
void reset() {
m_cancelled = true;
}
private:
std::function<void()> m_handler;
bool m_cancelled;
};
BlockchainExplorer::BlockchainExplorer(INode& node, Logging::ILogger& logger) :
node(node),
logger(logger, "BlockchainExplorer"),
state(NOT_INITIALIZED),
synchronized(false),
observersCounter(0) {}
observersCounter(0) {
}
BlockchainExplorer::~BlockchainExplorer() {}
@ -432,6 +492,12 @@ void BlockchainExplorer::poolChanged() {
return;
}
if (!poolUpdateGuard.beginUpdate()) {
return;
}
ScopeExitHandler poolUpdateEndGuard(std::bind(&BlockchainExplorer::poolUpdateEndHandler, this));
std::unique_lock<std::mutex> lock(mutex);
std::shared_ptr<std::vector<std::unique_ptr<ITransactionReader>>> rawNewTransactionsPtr = std::make_shared<std::vector<std::unique_ptr<ITransactionReader>>>();
@ -454,8 +520,11 @@ void BlockchainExplorer::poolChanged() {
);
}
);
request.performAsync(asyncContextCounter,
[this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](std::error_code ec) {
ScopeExitHandler poolUpdateEndGuard(std::bind(&BlockchainExplorer::poolUpdateEndHandler, this));
if (ec) {
logger(ERROR) << "Can't send poolChanged notification because can't get pool symmetric difference: " << ec.message();
return;
@ -503,21 +572,34 @@ void BlockchainExplorer::poolChanged() {
std::placeholders::_1
)
);
request.performAsync(asyncContextCounter,
[this, newTransactionsHashesPtr, newTransactionsPtr, removedTransactionsHashesPtr](std::error_code ec) {
ScopeExitHandler poolUpdateEndGuard(std::bind(&BlockchainExplorer::poolUpdateEndHandler, this));
if (ec) {
logger(ERROR) << "Can't send poolChanged notification because can't get transactions: " << ec.message();
return;
}
if (!newTransactionsPtr->empty() || !removedTransactionsHashesPtr->empty()) {
observerManager.notify(&IBlockchainObserver::poolUpdated, *newTransactionsPtr, *removedTransactionsHashesPtr);
logger(DEBUGGING) << "poolUpdated notification was successfully sent.";
}
}
);
poolUpdateEndGuard.reset();
}
);
poolUpdateEndGuard.reset();
}
void BlockchainExplorer::poolUpdateEndHandler() {
if (poolUpdateGuard.endUpdate()) {
poolChanged();
}
}
void BlockchainExplorer::blockchainSynchronized(uint32_t topHeight) {

View file

@ -74,6 +74,25 @@ public:
typedef WalletAsyncContextCounter AsyncContextCounter;
private:
void poolUpdateEndHandler();
class PoolUpdateGuard {
public:
PoolUpdateGuard();
bool beginUpdate();
bool endUpdate();
private:
enum class State {
NONE,
UPDATING,
UPDATE_REQUIRED
};
std::atomic<State> m_state;
};
enum State {
NOT_INITIALIZED,
INITIALIZED
@ -94,6 +113,6 @@ private:
Logging::LoggerRef logger;
AsyncContextCounter asyncContextCounter;
PoolUpdateGuard poolUpdateGuard;
};
}

View file

@ -158,7 +158,7 @@ void readVarint(IInputStream& in, uint64_t& value) {
throw std::runtime_error("readVarint, value overflow");
}
temp |= static_cast<size_t>(piece & 0x7f) << shift;
temp |= static_cast<uint64_t>(piece & 0x7f) << shift;
if ((piece & 0x80) == 0) {
if (piece == 0 && shift != 0) {
throw std::runtime_error("readVarint, invalid value representation");

View file

@ -164,7 +164,8 @@ const CheckpointData CHECKPOINTS[] = {
{804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"},
{810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"},
{816000, "32b7fdd4e4d715db81f8f09f4ba5e5c78e8113f2804d61a57378baee479ce745"},
{822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"}
{822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"},
{841000, "2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce"}
};
} // CryptoNote

View file

@ -48,18 +48,18 @@ namespace CryptoNote {
core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger);
~core();
bool on_idle();
virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block); //Deprecated. Should be removed with CryptoNoteProtocolHandler.
bool handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block);
virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;}
bool on_idle() override;
virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block) override; //Deprecated. Should be removed with CryptoNoteProtocolHandler.
bool handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) override;
virtual i_cryptonote_protocol* get_protocol() override {return m_pprotocol;}
const Currency& currency() const { return m_currency; }
//-------------------- IMinerHandler -----------------------
virtual bool handle_block_found(Block& b);
virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce);
virtual bool handle_block_found(Block& b) override;
virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce) override;
bool addObserver(ICoreObserver* observer);
bool removeObserver(ICoreObserver* observer);
bool addObserver(ICoreObserver* observer) override;
bool removeObserver(ICoreObserver* observer) override;
miner& get_miner() { return *m_miner; }
static void init_options(boost::program_options::options_description& desc);
@ -93,13 +93,13 @@ namespace CryptoNote {
virtual bool removeMessageQueue(MessageQueue<BlockchainMessage>& messageQueue) override;
uint32_t get_current_blockchain_height();
bool have_block(const Crypto::Hash& id);
bool have_block(const Crypto::Hash& id) override;
std::vector<Crypto::Hash> buildSparseChain() override;
std::vector<Crypto::Hash> buildSparseChain(const Crypto::Hash& startBlockId) override;
void on_synchronized();
void on_synchronized() override;
bool is_ready() override;
virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id);
virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) override;
bool get_blocks(uint32_t start_offset, uint32_t count, std::list<Block>& blocks, std::list<Transaction>& txs);
bool get_blocks(uint32_t start_offset, uint32_t count, std::list<Block>& blocks);
template<class t_ids_container, class t_blocks_container, class t_missed_container>
@ -129,13 +129,13 @@ namespace CryptoNote {
//bool get_outs(uint64_t amount, std::list<Crypto::PublicKey>& pkeys);
virtual std::vector<Crypto::Hash> findBlockchainSupplement(const std::vector<Crypto::Hash>& remoteBlockIds, size_t maxCount,
uint32_t& totalBlockCount, uint32_t& startBlockIndex) override;
bool get_stat_info(core_stat_info& st_inf);
bool get_stat_info(core_stat_info& st_inf) override;
virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector<uint32_t>& indexs);
virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector<uint32_t>& indexs) override;
Crypto::Hash get_tail_id();
virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res);
void pause_mining();
void update_block_template_and_resume_mining();
virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) override;
void pause_mining() override;
void update_block_template_and_resume_mining() override;
//Blockchain& get_blockchain_storage(){return m_blockchain;}
//debug functions
void print_blockchain(uint32_t start_index, uint32_t end_index);

View file

@ -50,8 +50,8 @@ namespace CryptoNote
CryptoNoteProtocolHandler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, IP2pEndpoint* p_net_layout, Logging::ILogger& log);
virtual bool addObserver(ICryptoNoteProtocolObserver* observer);
virtual bool removeObserver(ICryptoNoteProtocolObserver* observer);
virtual bool addObserver(ICryptoNoteProtocolObserver* observer) override;
virtual bool removeObserver(ICryptoNoteProtocolObserver* observer) override;
void set_p2p_endpoint(IP2pEndpoint* p2p);
// ICore& get_core() { return m_core; }
@ -68,8 +68,8 @@ namespace CryptoNote
bool get_payload_sync_data(CORE_SYNC_DATA& hshd);
bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, CryptoNoteConnectionContext& context, bool is_inital);
int handleCommand(bool is_notify, int command, const BinaryArray& in_buff, BinaryArray& buff_out, CryptoNoteConnectionContext& context, bool& handled);
virtual size_t getPeerCount() const;
virtual uint32_t getObservedHeight() const;
virtual size_t getPeerCount() const override;
virtual uint32_t getObservedHeight() const override;
void requestMissingPoolTransactions(const CryptoNoteConnectionContext& context);
private:

View file

@ -41,7 +41,7 @@ const char* getErrorBody(CryptoNote::HttpResponse::HTTP_STATUS status) {
case CryptoNote::HttpResponse::STATUS_404:
return "Requested url is not found\n";
case CryptoNote::HttpResponse::STATUS_500:
return "Internal server error is occured\n";
return "Internal server error is occurred\n";
default:
throw std::runtime_error("Error body for given status is not available");
}

View file

@ -51,9 +51,9 @@ public:
virtual bool addObserver(INodeObserver* observer) override;
virtual bool removeObserver(INodeObserver* observer) override;
virtual size_t getPeerCount() const;
virtual uint32_t getLastLocalBlockHeight() const;
virtual uint32_t getLastKnownBlockHeight() const;
virtual size_t getPeerCount() const override;
virtual uint32_t getLastLocalBlockHeight() const override;
virtual uint32_t getLastKnownBlockHeight() const override;
virtual uint32_t getLocalBlockCount() const override;
virtual uint32_t getKnownBlockCount() const override;
virtual uint64_t getLastLocalBlockTimestamp() const override;

View file

@ -48,25 +48,25 @@ public:
NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort);
virtual ~NodeRpcProxy();
virtual bool addObserver(CryptoNote::INodeObserver* observer);
virtual bool removeObserver(CryptoNote::INodeObserver* observer);
virtual bool addObserver(CryptoNote::INodeObserver* observer) override;
virtual bool removeObserver(CryptoNote::INodeObserver* observer) override;
virtual bool addObserver(CryptoNote::INodeRpcProxyObserver* observer);
virtual bool removeObserver(CryptoNote::INodeRpcProxyObserver* observer);
virtual void init(const Callback& callback);
virtual bool shutdown();
virtual void init(const Callback& callback) override;
virtual bool shutdown() override;
virtual size_t getPeerCount() const;
virtual uint32_t getLastLocalBlockHeight() const;
virtual uint32_t getLastKnownBlockHeight() const;
virtual size_t getPeerCount() const override;
virtual uint32_t getLastLocalBlockHeight() const override;
virtual uint32_t getLastKnownBlockHeight() const override;
virtual uint32_t getLocalBlockCount() const override;
virtual uint32_t getKnownBlockCount() const override;
virtual uint64_t getLastLocalBlockTimestamp() const override;
virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback);
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback);
virtual void getNewBlocks(std::vector<Crypto::Hash>&& knownBlockIds, std::vector<CryptoNote::block_complete_entry>& newBlocks, uint32_t& startHeight, const Callback& callback);
virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override;
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback) override;
virtual void getNewBlocks(std::vector<Crypto::Hash>&& knownBlockIds, std::vector<CryptoNote::block_complete_entry>& newBlocks, uint32_t& startHeight, const Callback& callback) override;
virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices, const Callback& callback) override;
virtual void queryBlocks(std::vector<Crypto::Hash>&& knownBlockIds, uint64_t timestamp, std::vector<BlockShortEntry>& newBlocks, uint32_t& startHeight, const Callback& callback) override;
virtual void getPoolSymmetricDifference(std::vector<Crypto::Hash>&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual,

View file

@ -80,6 +80,7 @@ NetNodeConfig::NetNodeConfig() {
allowLocalIp = false;
hideMyPort = false;
configFolder = Tools::getDefaultDataDirectory();
testnet = false;
}
bool NetNodeConfig::init(const boost::program_options::variables_map& vm)

View file

@ -70,7 +70,7 @@ private:
bool hideMyPort;
std::string configFolder;
std::string p2pStateFilename;
bool testnet = false;
bool testnet;
};
} //namespace nodetool

View file

@ -26,13 +26,13 @@ namespace PaymentService {
class NodeRpcStub: public CryptoNote::INode {
public:
virtual ~NodeRpcStub() {}
virtual bool addObserver(CryptoNote::INodeObserver* observer) { return true; }
virtual bool removeObserver(CryptoNote::INodeObserver* observer) { return true; }
virtual bool addObserver(CryptoNote::INodeObserver* observer) override { return true; }
virtual bool removeObserver(CryptoNote::INodeObserver* observer) override { return true; }
virtual void init(const Callback& callback) { }
virtual bool shutdown() { return true; }
virtual void init(const Callback& callback) override { }
virtual bool shutdown() override { return true; }
virtual size_t getPeerCount() const { return 0; }
virtual size_t getPeerCount() const override { return 0; }
virtual uint32_t getLastLocalBlockHeight() const override { return 0; }
virtual uint32_t getLastKnownBlockHeight() const override { return 0; }
virtual uint32_t getLocalBlockCount() const override { return 0; }
@ -56,7 +56,7 @@ public:
};
virtual void getPoolSymmetricDifference(std::vector<Crypto::Hash>&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual,
std::vector<std::unique_ptr<CryptoNote::ITransactionReader>>& newTxs, std::vector<Crypto::Hash>& deletedTxIds, const Callback& callback) {
std::vector<std::unique_ptr<CryptoNote::ITransactionReader>>& newTxs, std::vector<Crypto::Hash>& deletedTxIds, const Callback& callback) override {
isBcActual = true;
callback(std::error_code());
}

View file

@ -127,6 +127,7 @@ void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
serializer(fee, "fee");
serializer(hash, "hash");
serializer(blockHeight, "block_height");
serializer(state, "state");
serializer(timestamp, "timestamp");
serializer(extra, "extra");
serializer(transfers, "transfers");
@ -154,6 +155,7 @@ void ListTransactionsResponse::serialize(CryptoNote::ISerializer& serializer) {
}
void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
serializer(type, "type");
serializer(address, "address");
serializer(amount, "amount");
}

View file

@ -137,6 +137,7 @@ struct GetTransactionRequest {
};
struct TransferRpcInfo {
uint8_t type;
std::string address;
int64_t amount;
@ -152,6 +153,7 @@ struct TransactionRpcInfo {
uint64_t blockHeight;
uint64_t timestamp;
std::string extra;
uint8_t state;
std::vector<TransferRpcInfo> transfers;
void serialize(CryptoNote::ISerializer& serializer);

View file

@ -240,8 +240,8 @@ void WalletService::loadWallet() {
void WalletService::loadPaymentsCacheAndTransferIndices() {
size_t txCount = wallet->getTransactionCount();
transfersIndices.resize(1);
transfersIndices[0] = 0;
transfersIndices.reserve(txCount + 1);
transfersIndices.push_back(0);
logger(Logging::DEBUGGING) << "seeking for payments among " << txCount << " transactions";
@ -273,18 +273,18 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req
logger(Logging::DEBUGGING) << "Send transaction request came";
try {
std::vector<CryptoNote::WalletTransfer> transfers;
makeTransfers(req.destinations, transfers);
std::vector<CryptoNote::WalletOrder> orders;
makeOrders(req.destinations, orders);
std::string extra;
if (!req.paymentId.empty()) {
addPaymentIdToExtra(req.paymentId, extra);
}
size_t txId = wallet->transfer(transfers, req.fee, req.mixin, extra, req.unlockTime);
size_t txId = wallet->transfer(orders, req.fee, req.mixin, extra, req.unlockTime);
if (txId == CryptoNote::WALLET_INVALID_TRANSACTION_ID) {
logger(Logging::WARNING) << "Unable to send transaction";
throw std::runtime_error("Error occured while sending transaction");
throw std::runtime_error("Error occurred while sending transaction");
}
resp.transactionId = txId;
@ -296,11 +296,11 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req
return std::error_code();
}
void WalletService::makeTransfers(const std::vector<PaymentService::TransferDestination>& destinations, std::vector<CryptoNote::WalletTransfer>& transfers) {
transfers.reserve(destinations.size());
void WalletService::makeOrders(const std::vector<PaymentService::TransferDestination>& destinations, std::vector<CryptoNote::WalletOrder>& orders) {
orders.reserve(destinations.size());
for (auto dest: destinations) {
transfers.push_back( { dest.address, static_cast<int64_t>(dest.amount) } );
orders.push_back( { dest.address, dest.amount } );
}
}
@ -429,7 +429,8 @@ std::error_code WalletService::getTransactionByTransferId(size_t transferId, siz
}
auto nextTxId = std::upper_bound(transfersIndices.begin(), transfersIndices.end(), transferId);
transactionId = (nextTxId - transfersIndices.begin()) - 1;
assert(nextTxId != transfersIndices.begin());
transactionId = std::distance(transfersIndices.begin(), nextTxId) - 1;
return std::error_code();
}
@ -443,9 +444,10 @@ void WalletService::fillTransactionRpcInfo(size_t txId, const CryptoNote::Wallet
rpcInfo.timestamp = tx.timestamp;
rpcInfo.extra = Common::toHex(tx.extra.data(), tx.extra.size());
rpcInfo.hash = Common::podToHex(tx.hash);
rpcInfo.state = static_cast<uint8_t>(tx.state);
for (size_t transferId = 0; transferId < rpcInfo.transferCount; ++transferId) {
auto transfer = wallet->getTransactionTransfer(txId, transferId);
TransferRpcInfo rpcTransfer{ transfer.address, transfer.amount };
TransferRpcInfo rpcTransfer{ static_cast<uint8_t>(transfer.type), transfer.address, transfer.amount };
rpcInfo.transfers.push_back(rpcTransfer);
}
}
@ -510,7 +512,9 @@ std::error_code WalletService::getTransfer(size_t globalTransferId, bool& found,
logger(Logging::DEBUGGING) << "getTransfer request came";
found = false;
try {
size_t txId = (std::upper_bound(transfersIndices.begin(), transfersIndices.end(), globalTransferId) - transfersIndices.begin()) - 1;
auto nextTxIt = std::upper_bound(transfersIndices.begin(), transfersIndices.end(), globalTransferId);
assert(nextTxIt != transfersIndices.begin());
size_t txId = std::distance(transfersIndices.begin(), nextTxIt) - 1;
size_t fakeTxId = transfersIndices.size() - 1;
if (txId == fakeTxId) {
@ -520,6 +524,7 @@ std::error_code WalletService::getTransfer(size_t globalTransferId, bool& found,
auto transferId = globalTransferId - transfersIndices[txId];
auto transfer = wallet->getTransactionTransfer(txId, transferId);
rpcInfo.type = static_cast<uint8_t>(transfer.type);
rpcInfo.address = transfer.address;
rpcInfo.amount = transfer.amount;
found = true;
@ -572,12 +577,22 @@ void WalletService::refresh() {
for (;;) {
auto event = wallet->getEvent();
if (event.type == CryptoNote::TRANSACTION_CREATED || event.type == CryptoNote::TRANSACTION_UPDATED) {
size_t transactionId;
size_t transactionId = event.transactionCreated.transactionIndex;
size_t transferCount = wallet->getTransactionTransferCount(transactionId);
if (event.type == CryptoNote::TRANSACTION_CREATED) {
transactionId = event.transactionCreated.transactionIndex;
transfersIndices.push_back(transfersIndices[transactionId] + wallet->getTransactionTransferCount(transactionId));
assert(transactionId == transfersIndices.size() - 1);
transfersIndices.push_back(transfersIndices[transactionId] + transferCount);
} else {
transactionId = event.transactionUpdated.transactionIndex;
assert(event.type == CryptoNote::TRANSACTION_UPDATED);
assert(transactionId < transfersIndices.size() - 1);
size_t oldTransferCount = transfersIndices[transactionId + 1] - transfersIndices[transactionId];
int change = static_cast<int>(transferCount) - static_cast<int>(oldTransferCount);
if (change != 0) {
for (size_t i = transactionId + 1; i < transfersIndices.size(); ++i) {
transfersIndices[i] = static_cast<size_t>(static_cast<int>(transfersIndices[i]) + change);
}
}
}
auto tx = wallet->getTransaction(transactionId);

View file

@ -83,7 +83,7 @@ private:
void insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed);
void fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo);
void makeTransfers(const std::vector<TransferDestination>& destinations, std::vector<CryptoNote::WalletTransfer>& transfers);
void makeOrders(const std::vector<TransferDestination>& destinations, std::vector<CryptoNote::WalletOrder>& transfers);
struct PaymentItem {
std::string paymentId;

View file

@ -39,13 +39,13 @@ bool ConfigurationManager::init(int argc, char** argv) {
po::options_description confGeneralOptions;
confGeneralOptions.add(cmdGeneralOptions).add_options()
("testnet", po::value<bool>(), "")
("local", po::value<bool>(), "");
("testnet", po::bool_switch(), "")
("local", po::bool_switch(), "");
cmdGeneralOptions.add_options()
("help,h", "produce this help message and exit")
("local", "start with local node (remote is default)")
("testnet", "testnet mode");
("local", po::bool_switch(), "start with local node (remote is default)")
("testnet", po::bool_switch(), "testnet mode");
command_line::add_arg(cmdGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory());
command_line::add_arg(confGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory());
@ -90,10 +90,9 @@ bool ConfigurationManager::init(int argc, char** argv) {
coreConfig.init(confOptions);
remoteNodeConfig.init(confOptions);
if (confOptions.count("local")) {
netNodeConfig.setTestnet(confOptions["testnet"].as<bool>());
startInprocess = confOptions["local"].as<bool>();
}
}
//command line options should override options from config file
gateConfiguration.init(cmdOptions);
@ -101,7 +100,11 @@ bool ConfigurationManager::init(int argc, char** argv) {
coreConfig.init(cmdOptions);
remoteNodeConfig.init(cmdOptions);
if (cmdOptions.count("local")) {
if (cmdOptions["testnet"].as<bool>()) {
netNodeConfig.setTestnet(true);
}
if (cmdOptions["local"].as<bool>()) {
startInprocess = true;
}

View file

@ -159,7 +159,7 @@ void PaymentGateService::runInProcess(Logging::LoggerRef& log) {
node->init([&initPromise, &log](std::error_code ec) {
if (ec) {
log(Logging::INFO) << "Failed to init node: " << ec.message();
log(Logging::WARNING, Logging::YELLOW) << "Failed to init node: " << ec.message();
} else {
log(Logging::INFO) << "node is inited successfully";
}
@ -215,7 +215,7 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency,
try {
service->init();
} catch (std::exception& e) {
Logging::LoggerRef(logger, "run")(Logging::ERROR) << "Failed to init walletService reason: " << e.what();
Logging::LoggerRef(logger, "run")(Logging::ERROR, Logging::BRIGHT_RED) << "Failed to init walletService reason: " << e.what();
return;
}
@ -236,7 +236,7 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency,
try {
service->saveWallet();
} catch (std::exception& ex) {
Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING) << "Couldn't save wallet: " << ex.what();
Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING, Logging::YELLOW) << "Couldn't save wallet: " << ex.what();
}
}
}

View file

@ -75,7 +75,7 @@ void Configuration::init(const boost::program_options::variables_map& options) {
throw ConfigurationError("It's impossible to use both \"register-service\" and \"unregister-service\" at the same time");
}
if (options.count("testnet") != 0) {
if (options["testnet"].as<bool>()) {
testnet = true;
}

View file

@ -59,7 +59,7 @@ std::string GetLastErrorMessage(DWORD errorMessageID)
void __stdcall serviceHandler(DWORD fdwControl) {
if (fdwControl == SERVICE_CONTROL_STOP) {
Logging::LoggerRef log(ppg->getLogger(), "serviceHandler");
log(Logging::INFO) << "Stop signal caught";
log(Logging::INFO, Logging::BRIGHT_YELLOW) << "Stop signal caught";
SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_STOP_PENDING, 0, NO_ERROR, 0, 0, 0 };
SetServiceStatus(serviceStatusHandle, &serviceStatus);
@ -73,26 +73,26 @@ void __stdcall serviceMain(DWORD dwArgc, char **lpszArgv) {
serviceStatusHandle = RegisterServiceCtrlHandler("PaymentGate", serviceHandler);
if (serviceStatusHandle == NULL) {
logRef(Logging::FATAL) << "Couldn't make RegisterServiceCtrlHandler call: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't make RegisterServiceCtrlHandler call: " << GetLastErrorMessage(GetLastError());
return;
}
SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_START_PENDING, 0, NO_ERROR, 0, 1, 3000 };
if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) {
logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError());
return;
}
serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_RUNNING, SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 };
if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) {
logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError());
return;
}
try {
ppg->run();
} catch (std::exception& ex) {
logRef(Logging::FATAL) << "Error occured: " << ex.what();
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Error occurred: " << ex.what();
}
serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_STOPPED, 0, NO_ERROR, 0, 0, 0 };
@ -138,10 +138,14 @@ int runDaemon() {
{ NULL, NULL }
};
Logging::LoggerRef logRef(ppg->getLogger(), "RunService");
if (StartServiceCtrlDispatcher(serviceTable) != TRUE) {
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't start service: " << GetLastErrorMessage(GetLastError());
return 1;
}
logRef(Logging::INFO) << "Service stopped";
return 0;
#else
@ -151,7 +155,7 @@ int runDaemon() {
//parent
return 0;
} else if (daemonResult < 0) {
//error occured
//error occurred
return 1;
}
@ -174,7 +178,7 @@ int registerService() {
for (;;) {
if (GetModuleFileName(NULL, pathBuff, ARRAYSIZE(pathBuff)) == 0) {
logRef(Logging::FATAL) << "GetModuleFileName failed with error: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "GetModuleFileName failed with error: " << GetLastErrorMessage(GetLastError());
ret = 1;
break;
}
@ -186,7 +190,7 @@ int registerService() {
scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (scManager == NULL) {
logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError());
ret = 1;
break;
}
@ -195,7 +199,7 @@ int registerService() {
SERVICE_ERROR_NORMAL, modulePath.c_str(), NULL, NULL, NULL, NULL, NULL);
if (scService == NULL) {
logRef(Logging::FATAL) << "CreateService failed with error: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "CreateService failed with error: " << GetLastErrorMessage(GetLastError());
ret = 1;
break;
}
@ -231,14 +235,14 @@ int unregisterService() {
for (;;) {
scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (scManager == NULL) {
logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError());
ret = 1;
break;
}
scService = OpenService(scManager, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE);
if (scService == NULL) {
logRef(Logging::FATAL) << "OpenService failed with error: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "OpenService failed with error: " << GetLastErrorMessage(GetLastError());
ret = 1;
break;
}
@ -260,12 +264,12 @@ int unregisterService() {
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) {
logRef(Logging::INFO) << SERVICE_NAME << " is stopped";
} else {
logRef(Logging::FATAL) << SERVICE_NAME << " failed to stop" << std::endl;
logRef(Logging::FATAL, Logging::BRIGHT_RED) << SERVICE_NAME << " failed to stop" << std::endl;
}
}
if (!DeleteService(scService)) {
logRef(Logging::FATAL) << "DeleteService failed with error: " << GetLastErrorMessage(GetLastError());
logRef(Logging::FATAL, Logging::BRIGHT_RED) << "DeleteService failed with error: " << GetLastErrorMessage(GetLastError());
ret = 1;
break;
}

View file

@ -28,7 +28,7 @@ public:
BinaryInputStreamSerializer(Common::IInputStream& strm) : stream(strm) {}
virtual ~BinaryInputStreamSerializer() {}
virtual ISerializer::SerializerType type() const;
virtual ISerializer::SerializerType type() const override;
virtual bool beginObject(Common::StringView name) override;
virtual void endObject() override;

View file

@ -28,7 +28,7 @@ public:
BinaryOutputStreamSerializer(Common::IOutputStream& strm) : stream(strm) {}
virtual ~BinaryOutputStreamSerializer() {}
virtual ISerializer::SerializerType type() const;
virtual ISerializer::SerializerType type() const override;
virtual bool beginObject(Common::StringView name) override;
virtual void endObject() override;

View file

@ -29,7 +29,7 @@ public:
JsonInputValueSerializer(Common::JsonValue&& value);
virtual ~JsonInputValueSerializer();
SerializerType type() const;
SerializerType type() const override;
virtual bool beginObject(Common::StringView name) override;
virtual void endObject() override;

View file

@ -28,7 +28,7 @@ public:
JsonOutputStreamSerializer();
virtual ~JsonOutputStreamSerializer();
SerializerType type() const;
SerializerType type() const override;
virtual bool beginObject(Common::StringView name) override;
virtual void endObject() override;

View file

@ -32,7 +32,7 @@ public:
void dump(Common::IOutputStream& target);
virtual ISerializer::SerializerType type() const;
virtual ISerializer::SerializerType type() const override;
virtual bool beginObject(Common::StringView name) override;
virtual void endObject() override;

View file

@ -464,7 +464,8 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C
logManager(log),
logger(log, "simplewallet"),
m_refresh_progress_reporter(*this),
m_initResultPromise(nullptr) {
m_initResultPromise(nullptr),
m_walletSynchronized(false) {
m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [<number_of_threads>] - Start mining in daemon");
m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon");
//m_consoleHandler.setHandler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance");
@ -734,8 +735,21 @@ bool simple_wallet::save(const std::vector<std::string> &args)
}
bool simple_wallet::reset(const std::vector<std::string> &args) {
{
std::unique_lock<std::mutex> lock(m_walletSynchronizedMutex);
m_walletSynchronized = false;
}
m_wallet->reset();
success_msg_writer(true) << "Reset is complete successfully";
success_msg_writer(true) << "Reset completed successfully.";
std::unique_lock<std::mutex> lock(m_walletSynchronizedMutex);
while (!m_walletSynchronized) {
m_walletSynchronizedCV.wait(lock);
}
std::cout << std::endl;
return true;
}
@ -814,10 +828,6 @@ void simple_wallet::initCompleted(std::error_code result) {
}
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::localBlockchainUpdated(uint32_t height) {
m_refresh_progress_reporter.update(height, false);
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::connectionStatusUpdated(bool connected) {
if (connected) {
logger(INFO, GREEN) << "Wallet connected to daemon.";
@ -854,6 +864,19 @@ void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transac
}
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::synchronizationCompleted(std::error_code result) {
std::unique_lock<std::mutex> lock(m_walletSynchronizedMutex);
m_walletSynchronized = true;
m_walletSynchronizedCV.notify_one();
}
void simple_wallet::synchronizationProgressUpdated(uint32_t current, uint32_t total) {
std::unique_lock<std::mutex> lock(m_walletSynchronizedMutex);
if (!m_walletSynchronized) {
m_refresh_progress_reporter.update(current, false);
}
}
bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/) {
success_msg_writer() << "available balance: " << m_currency.formatAmount(m_wallet->actualBalance()) <<
", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance());
@ -1010,6 +1033,15 @@ bool simple_wallet::transfer(const std::vector<std::string> &args) {
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::run() {
{
std::unique_lock<std::mutex> lock(m_walletSynchronizedMutex);
while (!m_walletSynchronized) {
m_walletSynchronizedCV.wait(lock);
}
}
std::cout << std::endl;
std::string addr_start = m_wallet->getAddress().substr(0, 6);
m_consoleHandler.start(false, "[wallet " + addr_start + "]: ", Common::Console::Color::BrightYellow);
return true;

View file

@ -17,8 +17,10 @@
#pragma once
#include <memory>
#include <condition_variable>
#include <future>
#include <memory>
#include <mutex>
#include <boost/program_options/variables_map.hpp>
@ -95,13 +97,11 @@ namespace CryptoNote
void printConnectionError() const;
//---------------- IWalletObserver -------------------------
//---------------- IWalletLegacyObserver -------------------------
virtual void initCompleted(std::error_code result) override;
virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override;
//----------------------------------------------------------
//----------------- INodeObserver --------------------------
virtual void localBlockchainUpdated(uint32_t height) override;
virtual void synchronizationCompleted(std::error_code result) override;
virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override;
//----------------------------------------------------------
//----------------- INodeRpcProxyObserver --------------------------
@ -130,8 +130,7 @@ namespace CryptoNote
m_blockchain_height = (std::max)(m_blockchain_height, height);
}
if (std::chrono::milliseconds(1) < current_time - m_print_time || force)
{
if (std::chrono::milliseconds(1) < current_time - m_print_time || force) {
std::cout << "Height " << height << " of " << m_blockchain_height << '\r';
m_print_time = current_time;
}
@ -140,18 +139,10 @@ namespace CryptoNote
private:
void update_blockchain_height()
{
std::string err;
uint64_t blockchain_height = m_simple_wallet.m_node->getLastLocalBlockHeight();
if (err.empty())
{
m_blockchain_height = blockchain_height;
m_blockchain_height_update_time = std::chrono::system_clock::now();
}
else
{
std::cerr << "Failed to get current blockchain height: " << err;
}
}
private:
CryptoNote::simple_wallet& m_simple_wallet;
@ -182,5 +173,9 @@ namespace CryptoNote
std::unique_ptr<CryptoNote::NodeRpcProxy> m_node;
std::unique_ptr<CryptoNote::IWalletLegacy> m_wallet;
refresh_progress_reporter_t m_refresh_progress_reporter;
bool m_walletSynchronized;
std::mutex m_walletSynchronizedMutex;
std::condition_variable m_walletSynchronizedCV;
};
}

View file

@ -372,7 +372,7 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
lk.unlock();
switch (result) {
case UpdateConsumersResult::errorOccured:
case UpdateConsumersResult::errorOccurred:
if (setFutureStateIf(State::idle, std::bind(
[](State futureState) -> bool {
return futureState != State::stopped;
@ -435,7 +435,7 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons
static_cast<uint32_t>(interval.blocks.size()) - startOffset);
smthChanged = true;
} else {
return UpdateConsumersResult::errorOccured;
return UpdateConsumersResult::errorOccurred;
}
}
}

View file

@ -92,7 +92,7 @@ private:
enum class UpdateConsumersResult {
nothingChanged = 0,
addedNewBlocks = 1,
errorOccured = 2
errorOccurred = 2
};
//void startSync();

View file

@ -45,7 +45,8 @@ enum WalletErrorCodes {
KEY_GENERATION_ERROR,
INDEX_OUT_OF_RANGE,
ADDRESS_ALREADY_EXISTS,
TRACKING_MODE
TRACKING_MODE,
WRONG_PARAMETERS
};
// custom category:
@ -66,7 +67,7 @@ public:
case NOT_INITIALIZED: return "Object was not initialized";
case WRONG_PASSWORD: return "The password is wrong";
case ALREADY_INITIALIZED: return "The object is already initialized";
case INTERNAL_WALLET_ERROR: return "Internal error occured";
case INTERNAL_WALLET_ERROR: return "Internal error occurred";
case MIXIN_COUNT_TOO_BIG: return "MixIn count is too big";
case BAD_ADDRESS: return "Bad address";
case TRANSACTION_SIZE_TOO_BIG: return "Transaction size is too big";
@ -83,6 +84,7 @@ public:
case INDEX_OUT_OF_RANGE: return "Index is out of range";
case ADDRESS_ALREADY_EXISTS: return "Address already exists";
case TRACKING_MODE: return "The wallet is in tracking mode";
case WRONG_PARAMETERS: return "Wrong parameters passed";
default: return "Unknown error";
}
}

View file

@ -153,6 +153,64 @@ size_t getTransactionSize(const ITransactionReader& transaction) {
return transaction.getTransactionData().size();
}
std::vector<WalletTransfer> convertOrdersToTransfer(const std::vector<WalletOrder>& orders) {
std::vector<WalletTransfer> transfers;
transfers.reserve(orders.size());
for (const auto& order: orders) {
WalletTransfer transfer;
if (order.amount > std::numeric_limits<int64_t>::max()) {
throw std::system_error(make_error_code(CryptoNote::error::WRONG_AMOUNT),
"Order amount must not exceed " + std::to_string(std::numeric_limits<decltype(transfer.amount)>::max()));
}
transfer.type = WalletTransferType::USUAL;
transfer.address = order.address;
transfer.amount = static_cast<int64_t>(order.amount);
transfers.emplace_back(std::move(transfer));
}
return transfers;
}
uint64_t calculateDonationAmount(uint64_t freeAmount, uint64_t donationThreshold, uint64_t dustThreshold) {
std::vector<uint64_t> decomposedAmounts;
decomposeAmount(freeAmount, dustThreshold, decomposedAmounts);
std::sort(decomposedAmounts.begin(), decomposedAmounts.end(), std::greater<uint64_t>());
uint64_t donationAmount = 0;
for (auto amount: decomposedAmounts) {
if (amount > donationThreshold - donationAmount) {
continue;
}
donationAmount += amount;
}
assert(donationAmount <= freeAmount);
return donationAmount;
}
uint64_t pushDonationTransferIfPossible(const DonationSettings& donation, uint64_t freeAmount, uint64_t dustThreshold, std::vector<WalletTransfer>& destinations) {
uint64_t donationAmount = 0;
if (!donation.address.empty() && donation.threshold != 0) {
if (donation.threshold > std::numeric_limits<int64_t>::max()) {
throw std::system_error(make_error_code(error::WRONG_AMOUNT),
"Donation threshold must not exceed " + std::to_string(std::numeric_limits<int64_t>::max()));
}
donationAmount = calculateDonationAmount(freeAmount, donation.threshold, dustThreshold);
if (donationAmount != 0) {
destinations.emplace_back(WalletTransfer {WalletTransferType::DONATION, donation.address, static_cast<int64_t>(donationAmount)});
}
}
return donationAmount;
}
}
namespace CryptoNote {
@ -163,7 +221,7 @@ WalletGreen::WalletGreen(System::Dispatcher& dispatcher, const Currency& currenc
m_node(node),
m_blockchainSynchronizer(node, currency.genesisBlockHash()),
m_synchronizer(currency, m_blockchainSynchronizer, node),
m_eventOccured(m_dispatcher),
m_eventOccurred(m_dispatcher),
m_readyEvent(m_dispatcher),
m_transactionSoftLockTime(transactionSoftLockTime)
{
@ -549,9 +607,7 @@ WalletTransfer WalletGreen::getTransactionTransfer(size_t transactionIndex, size
throw std::system_error(std::make_error_code(std::errc::invalid_argument));
}
auto it = bounds.first;
std::advance(it, transferIndex);
return it->second;
return std::next(bounds.first, transferIndex)->second;
}
std::pair<WalletTransfers::const_iterator, WalletTransfers::const_iterator> WalletGreen::getTransactionTransfers(
@ -566,73 +622,111 @@ std::pair<WalletTransfers::const_iterator, WalletTransfers::const_iterator> Wall
return bounds;
}
size_t WalletGreen::transfer(const WalletTransfer& destination,
uint64_t fee,
uint64_t mixIn,
std::string const& extra,
uint64_t unlockTimestamp)
{
std::vector<WalletTransfer> destinations { destination };
return transfer(destinations, fee, mixIn, extra, unlockTimestamp);
}
size_t WalletGreen::transfer(
const std::vector<WalletTransfer>& destinations,
size_t WalletGreen::transfer(const WalletOrder& destination,
uint64_t fee,
uint64_t mixIn,
const std::string& extra,
uint64_t unlockTimestamp) {
System::EventLock lk(m_readyEvent);
TransactionParameters transactionParameters;
transactionParameters.destinations.emplace_back(destination);
transactionParameters.fee = fee;
transactionParameters.mixIn = mixIn;
transactionParameters.extra = extra;
transactionParameters.unlockTimestamp = unlockTimestamp;
throwIfNotInitialized();
throwIfStopped();
throwIfTrackingMode();
return doTransfer(pickWalletsWithMoney(), destinations, fee, mixIn, extra, unlockTimestamp);
return transfer(transactionParameters);
}
size_t WalletGreen::transfer(
const std::string& sourceAddress,
const WalletTransfer& destination,
uint64_t fee,
uint64_t mixIn,
std::string const& extra,
uint64_t unlockTimestamp) {
std::vector<WalletTransfer> destinations { destination };
return transfer(sourceAddress, destinations, fee, mixIn, extra, unlockTimestamp);
}
size_t WalletGreen::transfer(
const std::string& sourceAddress,
const std::vector<WalletTransfer>& destinations,
const std::vector<WalletOrder>& destinations,
uint64_t fee,
uint64_t mixIn,
const std::string& extra,
uint64_t unlockTimestamp) {
TransactionParameters transactionParameters;
transactionParameters.destinations = destinations;
transactionParameters.fee = fee;
transactionParameters.mixIn = mixIn;
transactionParameters.extra = extra;
transactionParameters.unlockTimestamp = unlockTimestamp;
return transfer(transactionParameters);
}
size_t WalletGreen::transfer(
const std::string& sourceAddress,
const WalletOrder& destination,
uint64_t fee,
uint64_t mixIn,
std::string const& extra,
uint64_t unlockTimestamp) {
TransactionParameters transactionParameters;
transactionParameters.sourceAddress = sourceAddress;
transactionParameters.destinations.emplace_back(destination);
transactionParameters.fee = fee;
transactionParameters.mixIn = mixIn;
transactionParameters.extra = extra;
transactionParameters.unlockTimestamp = unlockTimestamp;
return transfer(transactionParameters);
}
size_t WalletGreen::transfer(
const std::string& sourceAddress,
const std::vector<WalletOrder>& destinations,
uint64_t fee,
uint64_t mixIn,
const std::string& extra,
uint64_t unlockTimestamp) {
TransactionParameters transactionParameters;
transactionParameters.sourceAddress = sourceAddress;
transactionParameters.destinations = destinations;
transactionParameters.fee = fee;
transactionParameters.mixIn = mixIn;
transactionParameters.extra = extra;
transactionParameters.unlockTimestamp = unlockTimestamp;
return transfer(transactionParameters);
}
size_t WalletGreen::transfer(const TransactionParameters& transactionParameters) {
System::EventLock lk(m_readyEvent);
throwIfNotInitialized();
throwIfStopped();
throwIfTrackingMode();
throwIfStopped();
WalletOuts wallet = pickWallet(sourceAddress);
std::vector<WalletOuts> wallets;
if (!transactionParameters.sourceAddress.empty()) {
WalletOuts wallet = pickWallet(transactionParameters.sourceAddress);
if (!wallet.outs.empty()) {
wallets.push_back(wallet);
wallets.emplace_back(std::move(wallet));
}
} else {
wallets = pickWalletsWithMoney();
}
return doTransfer(std::move(wallets), destinations, fee, mixIn, extra, unlockTimestamp);
return doTransfer(std::move(wallets),
transactionParameters.destinations,
transactionParameters.fee,
transactionParameters.mixIn,
transactionParameters.extra,
transactionParameters.unlockTimestamp,
transactionParameters.donation);
}
size_t WalletGreen::doTransfer(std::vector<WalletOuts>&& wallets,
const std::vector<WalletTransfer>& destinations,
const std::vector<WalletOrder>& orders,
uint64_t fee,
uint64_t mixIn,
const std::string& extra,
uint64_t unlockTimestamp) {
if (destinations.empty()) {
uint64_t unlockTimestamp,
const DonationSettings& donation) {
if (orders.empty()) {
throw std::system_error(make_error_code(error::ZERO_DESTINATION));
}
@ -640,6 +734,11 @@ size_t WalletGreen::doTransfer(std::vector<WalletOuts>&& wallets,
throw std::system_error(make_error_code(error::FEE_TOO_SMALL));
}
if (donation.address.empty() ^ (donation.threshold == 0)) {
throw std::system_error(make_error_code(error::WRONG_PARAMETERS), "DonationSettings must have both address and threshold parameters filled");
}
std::vector<WalletTransfer> destinations = convertOrdersToTransfer(orders);
validateAddresses(destinations, m_currency);
uint64_t neededMoney = countNeededMoney(destinations, fee);
@ -661,16 +760,19 @@ size_t WalletGreen::doTransfer(std::vector<WalletOuts>&& wallets,
std::vector<InputInfo> keysInfo;
prepareInputs(selectedTransfers, mixinResult, mixIn, keysInfo);
WalletTransfer changeDestination;
changeDestination.address = m_currency.accountAddressAsString({ m_walletsContainer.get<RandomAccessIndex>()[0].spendPublicKey, m_viewPublicKey });
changeDestination.amount = foundMoney - neededMoney;
uint64_t donationAmount = pushDonationTransferIfPossible(donation, foundMoney - neededMoney, m_currency.defaultDustThreshold(), destinations);
uint64_t changeAmount = foundMoney - neededMoney - donationAmount;
std::vector<ReceiverAmounts> decomposedOutputs;
splitDestinations(destinations, changeDestination, m_currency.defaultDustThreshold(), m_currency, decomposedOutputs);
std::vector<ReceiverAmounts> decomposedOutputs = splitDestinations(destinations, m_currency.defaultDustThreshold(), m_currency);
if (changeAmount != 0) {
CryptoNote::AccountPublicAddress changeDestination = { m_walletsContainer.get<RandomAccessIndex>()[0].spendPublicKey, m_viewPublicKey };
auto splittedChange = splitAmount(changeAmount, changeDestination, m_currency.defaultDustThreshold());
decomposedOutputs.emplace_back(std::move(splittedChange));
}
std::unique_ptr<ITransaction> tx = makeTransaction(decomposedOutputs, keysInfo, extra, unlockTimestamp);
size_t txId = insertOutgoingTransaction(tx->getTransactionHash(), -static_cast<int64_t>(neededMoney), fee, tx->getExtra(), unlockTimestamp);
size_t txId = insertOutgoingTransactionAndPushEvent(tx->getTransactionHash(), -static_cast<int64_t>(neededMoney), fee, tx->getExtra(), unlockTimestamp);
pushBackOutgoingTransfers(txId, destinations);
m_fusionTxsCache.emplace(txId, false);
@ -679,34 +781,34 @@ size_t WalletGreen::doTransfer(std::vector<WalletOuts>&& wallets,
try {
sendTransaction(tx.get());
} catch (std::exception&) {
updateTransactionStateAndPushEvent(txId, WalletTransactionState::FAILED);
deleteSpentOutputs(tx->getTransactionHash());
pushEvent(makeTransactionCreatedEvent(txId));
throw;
}
auto txIt = m_transactions.get<RandomAccessIndex>().begin();
std::advance(txIt, txId);
m_transactions.get<RandomAccessIndex>().modify(txIt,
[] (WalletTransaction& tx) { tx.state = WalletTransactionState::SUCCEEDED; });
m_change[tx->getTransactionHash()] = changeDestination.amount;
m_change[tx->getTransactionHash()] = changeAmount;
updateTransactionStateAndPushEvent(txId, WalletTransactionState::SUCCEEDED);
updateUsedWalletsBalances(selectedTransfers);
pushEvent(makeTransactionCreatedEvent(txId));
return txId;
}
void WalletGreen::pushBackOutgoingTransfers(size_t txId, const std::vector<WalletTransfer> &destinations) {
void WalletGreen::pushBackOutgoingTransfers(size_t txId, const std::vector<WalletTransfer>& destinations) {
for (const auto& dest: destinations) {
WalletTransfer d { dest.address, -dest.amount };
WalletTransfer d;
d.type = dest.type;
d.address = dest.address;
d.amount = -dest.amount;
m_transfers.push_back(std::make_pair(txId, d));
}
}
size_t WalletGreen::insertOutgoingTransaction(const Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp) {
size_t WalletGreen::insertOutgoingTransactionAndPushEvent(const Hash& transactionHash,
int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp) {
WalletTransaction insertTx;
insertTx.state = WalletTransactionState::FAILED;
insertTx.state = WalletTransactionState::CREATED;
insertTx.creationTime = static_cast<uint64_t>(time(nullptr));
insertTx.unlockTime = unlockTimestamp;
insertTx.blockHeight = CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT;
@ -720,22 +822,30 @@ size_t WalletGreen::insertOutgoingTransaction(const Hash& transactionHash, int64
size_t txId = m_transactions.get<RandomAccessIndex>().size();
m_transactions.get<RandomAccessIndex>().push_back(std::move(insertTx));
pushEvent(makeTransactionCreatedEvent(txId));
return txId;
}
bool WalletGreen::transactionExists(const Hash& hash) {
auto& hashIndex = m_transactions.get<TransactionIndex>();
auto it = hashIndex.find(hash);
return it != hashIndex.end();
void WalletGreen::updateTransactionStateAndPushEvent(size_t transactionId, WalletTransactionState state) {
auto it = std::next(m_transactions.get<RandomAccessIndex>().begin(), transactionId);
if (it->state != state) {
m_transactions.get<RandomAccessIndex>().modify(it, [state](WalletTransaction& tx) {
tx.state = state;
});
pushEvent(makeTransactionUpdatedEvent(transactionId));
}
}
bool WalletGreen::updateWalletTransactionInfo(const Hash& hash, const CryptoNote::TransactionInformation& info) {
auto& hashIndex = m_transactions.get<TransactionIndex>();
void WalletGreen::updateWalletTransactionInfoAndPushEvent(size_t transactionId, const CryptoNote::TransactionInformation& info, bool transfersUpdated) {
auto& txIdIndex = m_transactions.get<RandomAccessIndex>();
assert(transactionId < txIdIndex.size());
auto it = std::next(txIdIndex.begin(), transactionId);
bool updated = false;
auto it = hashIndex.find(hash);
if (it != hashIndex.end()) {
bool r = hashIndex.modify(it, [&info, &updated] (WalletTransaction& transaction) {
bool r = txIdIndex.modify(it, [&info, &updated](WalletTransaction& transaction) {
if (transaction.blockHeight != info.blockHeight) {
transaction.blockHeight = info.blockHeight;
updated = true;
@ -766,13 +876,13 @@ bool WalletGreen::updateWalletTransactionInfo(const Hash& hash, const CryptoNote
});
assert(r);
return updated;
}
throw std::system_error(make_error_code(std::errc::invalid_argument));
if (updated || transfersUpdated) {
pushEvent(makeTransactionUpdatedEvent(transactionId));
}
}
size_t WalletGreen::insertBlockchainTransaction(const TransactionInformation& info, int64_t txBalance) {
size_t WalletGreen::insertBlockchainTransactionAndPushEvent(const TransactionInformation& info, int64_t txBalance) {
auto& index = m_transactions.get<RandomAccessIndex>();
WalletTransaction tx;
@ -787,8 +897,12 @@ size_t WalletGreen::insertBlockchainTransaction(const TransactionInformation& in
tx.creationTime = info.timestamp;
tx.isBase = info.totalAmountIn == 0;
size_t txId = index.size();
index.push_back(std::move(tx));
return index.size() - 1;
pushEvent(makeTransactionCreatedEvent(txId));
return txId;
}
void WalletGreen::insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount) {
@ -796,7 +910,7 @@ void WalletGreen::insertIncomingTransfer(size_t txId, const std::string& address
return val < a.first;
});
WalletTransfer tr { address, amount };
WalletTransfer tr { WalletTransferType::USUAL, address, amount };
m_transfers.insert(it, std::make_pair(txId, std::move(tr)));
}
@ -805,12 +919,23 @@ std::unique_ptr<CryptoNote::ITransaction> WalletGreen::makeTransaction(const std
std::unique_ptr<ITransaction> tx = createTransaction();
typedef std::pair<const AccountPublicAddress*, uint64_t> AmountToAddress;
std::vector<AmountToAddress> amountsToAddresses;
for (const auto& output: decomposedOutputs) {
for (auto amount: output.amounts) {
tx->addOutput(amount, output.receiver);
amountsToAddresses.emplace_back(AmountToAddress{&output.receiver, amount});
}
}
std::shuffle(amountsToAddresses.begin(), amountsToAddresses.end(), std::default_random_engine{Crypto::rand<std::default_random_engine::result_type>()});
std::sort(amountsToAddresses.begin(), amountsToAddresses.end(), [] (const AmountToAddress& left, const AmountToAddress& right) {
return left.second < right.second;
});
for (const auto& amountToAddress: amountsToAddresses) {
tx->addOutput(amountToAddress.second, *amountToAddress.first);
}
tx->setUnlockTime(unlockTimestamp);
tx->appendExtra(Common::asBinaryArray(extra));
@ -984,26 +1109,30 @@ WalletGreen::WalletOuts WalletGreen::pickWallet(const std::string& address) {
return outs;
}
void WalletGreen::splitDestinations(const std::vector<CryptoNote::WalletTransfer>& destinations,
const CryptoNote::WalletTransfer& changeDestination,
std::vector<CryptoNote::WalletGreen::ReceiverAmounts> WalletGreen::splitDestinations(const std::vector<CryptoNote::WalletTransfer>& destinations,
uint64_t dustThreshold,
const CryptoNote::Currency& currency,
std::vector<ReceiverAmounts>& decomposedOutputs) {
const CryptoNote::Currency& currency) {
std::vector<ReceiverAmounts> decomposedOutputs;
for (const auto& destination: destinations) {
ReceiverAmounts receiverAmounts;
parseAddressString(destination.address, currency, receiverAmounts.receiver);
decomposeAmount(destination.amount, dustThreshold, receiverAmounts.amounts);
decomposedOutputs.push_back(std::move(receiverAmounts));
AccountPublicAddress address;
parseAddressString(destination.address, currency, address);
decomposedOutputs.push_back(splitAmount(destination.amount, address, dustThreshold));
}
ReceiverAmounts changeAmounts;
parseAddressString(changeDestination.address, currency, changeAmounts.receiver);
decomposeAmount(changeDestination.amount, dustThreshold, changeAmounts.amounts);
return decomposedOutputs;
}
decomposedOutputs.push_back(std::move(changeAmounts));
CryptoNote::WalletGreen::ReceiverAmounts WalletGreen::splitAmount(
uint64_t amount,
const AccountPublicAddress& destination,
uint64_t dustThreshold) {
ReceiverAmounts receiverAmounts;
receiverAmounts.receiver = destination;
decomposeAmount(amount, dustThreshold, receiverAmounts.amounts);
return receiverAmounts;
}
void WalletGreen::prepareInputs(
@ -1067,7 +1196,7 @@ void WalletGreen::start() {
void WalletGreen::stop() {
m_stopped = true;
m_eventOccured.set();
m_eventOccurred.set();
}
WalletEvent WalletGreen::getEvent() {
@ -1075,8 +1204,8 @@ WalletEvent WalletGreen::getEvent() {
throwIfStopped();
while(m_events.empty()) {
m_eventOccured.wait();
m_eventOccured.clear();
m_eventOccurred.wait();
m_eventOccurred.clear();
throwIfStopped();
}
@ -1161,25 +1290,23 @@ void WalletGreen::transactionUpdated(ITransfersSubscription* object, const Hash&
int64_t txBalance;
bool found = container->getTransactionInformation(transactionHash, info, txBalance);
assert(found);
bool addTransfer = txBalance > 0;
if (transactionExists(info.transactionHash)) {
bool updated = updateWalletTransactionInfo(info.transactionHash, info);
auto& hashIndex = m_transactions.get<TransactionIndex>();
auto it = hashIndex.find(info.transactionHash);
auto id = getTransactionId(info.transactionHash);
if (updated) {
pushEvent(makeTransactionUpdatedEvent(id));
}
size_t transactionId;
if (it != hashIndex.end()) {
transactionId = std::distance(m_transactions.get<RandomAccessIndex>().begin(), m_transactions.project<RandomAccessIndex>(it));
updateWalletTransactionInfoAndPushEvent(transactionId, info, addTransfer);
} else {
auto id = insertBlockchainTransaction(info, txBalance);
if (txBalance > 0) {
AccountPublicAddress walletAddress{ getWalletRecord(container).spendPublicKey, m_viewPublicKey };
insertIncomingTransfer(id, m_currency.accountAddressAsString(walletAddress), txBalance);
transactionId = insertBlockchainTransactionAndPushEvent(info, txBalance);
m_fusionTxsCache.emplace(transactionId, isFusionTransaction(m_transactions.get<RandomAccessIndex>()[transactionId]));
}
m_fusionTxsCache.emplace(id, isFusionTransaction(m_transactions.get<RandomAccessIndex>()[id]));
pushEvent(makeTransactionCreatedEvent(id));
if (addTransfer) {
AccountPublicAddress walletAddress{ getWalletRecord(container).spendPublicKey, m_viewPublicKey };
insertIncomingTransfer(transactionId, m_currency.accountAddressAsString(walletAddress), txBalance);
}
m_change.erase(transactionHash);
@ -1194,7 +1321,7 @@ void WalletGreen::transactionUpdated(ITransfersSubscription* object, const Hash&
void WalletGreen::pushEvent(const WalletEvent& event) {
m_events.push(event);
m_eventOccured.set();
m_eventOccurred.set();
}
size_t WalletGreen::getTransactionId(const Hash& transactionHash) const {
@ -1393,9 +1520,9 @@ size_t WalletGreen::createFusionTransaction(uint64_t threshold, uint64_t mixin)
System::EventLock lk(m_readyEvent);
throwIfNotInitialized();
throwIfTrackingMode();
throwIfStopped();
//TODO: check if wallet is not in tracking mode
const size_t MAX_FUSION_OUTPUT_COUNT = 4;
if (threshold <= m_currency.defaultDustThreshold()) {
@ -1455,11 +1582,11 @@ size_t WalletGreen::createFusionTransaction(uint64_t threshold, uint64_t mixin)
throw std::runtime_error("Unable to create fusion transaction");
}
size_t transactionId = insertOutgoingTransaction(fusionTransaction->getTransactionHash(), 0, 0, fusionTransaction->getExtra(), 0);
size_t transactionId = insertOutgoingTransactionAndPushEvent(fusionTransaction->getTransactionHash(), 0, 0, fusionTransaction->getExtra(), 0);
m_fusionTxsCache.emplace(transactionId, true);
WalletTransfer destination = {m_currency.accountAddressAsString({m_walletsContainer.get<RandomAccessIndex>().begin()->spendPublicKey,
m_viewPublicKey }), 0};
std::string address = m_currency.accountAddressAsString({m_walletsContainer.get<RandomAccessIndex>().begin()->spendPublicKey, m_viewPublicKey });
WalletTransfer destination = {WalletTransferType::USUAL, address, 0};
pushBackOutgoingTransfers(transactionId, std::vector<WalletTransfer> {destination});
markOutputsSpent(fusionTransaction->getTransactionHash(), fusionInputs);
@ -1467,22 +1594,15 @@ size_t WalletGreen::createFusionTransaction(uint64_t threshold, uint64_t mixin)
try {
sendTransaction(fusionTransaction.get());
} catch (std::exception&) {
updateTransactionStateAndPushEvent(transactionId, WalletTransactionState::FAILED);
deleteSpentOutputs(fusionTransaction->getTransactionHash());
pushEvent(makeTransactionCreatedEvent(transactionId));
throw;
}
auto txIt = m_transactions.get<RandomAccessIndex>().begin();
std::advance(txIt, transactionId);
m_transactions.get<RandomAccessIndex>().modify(txIt,
[] (WalletTransaction& tx) { tx.state = WalletTransactionState::SUCCEEDED; });
m_change[fusionTransaction->getTransactionHash()] = transactionAmount;
updateTransactionStateAndPushEvent(transactionId, WalletTransactionState::SUCCEEDED);
updateUsedWalletsBalances(fusionInputs);
pushEvent(makeTransactionCreatedEvent(transactionId));
return transactionId;
}

View file

@ -66,10 +66,11 @@ public:
virtual size_t getTransactionTransferCount(size_t transactionIndex) const override;
virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const override;
virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const std::vector<WalletTransfer>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const std::string& sourceAddress, const std::vector<WalletTransfer>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const std::string& sourceAddress, const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const std::string& sourceAddress, const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override;
virtual size_t transfer(const TransactionParameters& sendingTransaction) override;
virtual void start() override;
virtual void stop() override;
@ -145,11 +146,12 @@ protected:
bool isFusionTransaction(const WalletTransaction& walletTx) const;
size_t doTransfer(std::vector<WalletOuts>&& wallets,
const std::vector<WalletTransfer>& destinations,
const std::vector<WalletOrder>& orders,
uint64_t fee,
uint64_t mixIn,
const std::string& extra,
uint64_t unlockTimestamp);
uint64_t unlockTimestamp,
const DonationSettings& donation);
void requestMixinOuts(const std::vector<OutputToTransfer>& selectedTransfers,
uint64_t mixIn,
@ -166,18 +168,20 @@ protected:
std::vector<WalletOuts>&& wallets,
std::vector<OutputToTransfer>& selectedTransfers);
void splitDestinations(const std::vector<WalletTransfer>& destinations, const WalletTransfer& changeDestination,
uint64_t dustThreshold, const Currency& currency, std::vector<ReceiverAmounts>& decomposedOutputs);
std::vector<ReceiverAmounts> splitDestinations(const std::vector<WalletTransfer>& destinations,
uint64_t dustThreshold, const Currency& currency);
ReceiverAmounts splitAmount(uint64_t amount, const AccountPublicAddress& destination, uint64_t dustThreshold);
std::unique_ptr<CryptoNote::ITransaction> makeTransaction(const std::vector<ReceiverAmounts>& decomposedOutputs,
std::vector<InputInfo>& keysInfo, const std::string& extra, uint64_t unlockTimestamp);
void sendTransaction(ITransaction* tx);
size_t insertOutgoingTransaction(const Crypto::Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp);
bool transactionExists(const Crypto::Hash& hash);
bool updateWalletTransactionInfo(const Crypto::Hash& hash, const CryptoNote::TransactionInformation& info);
size_t insertBlockchainTransaction(const TransactionInformation& info, int64_t txBalance);
size_t insertBlockchainTransactionAndPushEvent(const TransactionInformation& info, int64_t txBalance);
size_t insertOutgoingTransactionAndPushEvent(const Crypto::Hash& transactionHash,
int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp);
void updateTransactionStateAndPushEvent(size_t transactionId, WalletTransactionState state);
void updateWalletTransactionInfoAndPushEvent(size_t transactionId, const CryptoNote::TransactionInformation& info, bool transfersUpdated);
void insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount);
void pushBackOutgoingTransfers(size_t txId, const std::vector<WalletTransfer> &destinations);
void insertUnlockTransactionJob(const Crypto::Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container);
@ -219,7 +223,7 @@ protected:
BlockchainSynchronizer m_blockchainSynchronizer;
TransfersSyncronizer m_synchronizer;
System::Event m_eventOccured;
System::Event m_eventOccurred;
std::queue<WalletEvent> m_events;
mutable System::Event m_readyEvent;

View file

@ -48,7 +48,7 @@ using CryptoNote::ISerializer;
};
};
struct trnsfer_destination
struct transfer_destination
{
uint64_t amount;
std::string address;
@ -63,7 +63,7 @@ using CryptoNote::ISerializer;
{
struct request
{
std::list<trnsfer_destination> destinations;
std::list<transfer_destination> destinations;
uint64_t fee;
uint64_t mixin;
uint64_t unlock_time;

View file

@ -99,14 +99,18 @@ struct WalletTransactionDto {
//DO NOT CHANGE IT
struct WalletTransferDto {
WalletTransferDto() {}
WalletTransferDto(const CryptoNote::WalletTransfer& tr) {
WalletTransferDto(uint32_t version) : version(version) {}
WalletTransferDto(const CryptoNote::WalletTransfer& tr, uint32_t version) : WalletTransferDto(version) {
address = tr.address;
amount = tr.amount;
type = static_cast<uint8_t>(tr.type);
}
std::string address;
uint64_t amount;
uint8_t type;
uint32_t version;
};
void serialize(WalletRecordDto& value, CryptoNote::ISerializer& serializer) {
@ -156,8 +160,11 @@ void serialize(WalletTransactionDto& value, CryptoNote::ISerializer& serializer)
void serialize(WalletTransferDto& value, CryptoNote::ISerializer& serializer) {
serializer(value.address, "address");
serializer(value.amount, "amount");
}
if (value.version > 2) {
serializer(value.type, "type");
}
}
template <typename Object>
std::string serialize(Object& obj, const std::string& name) {
@ -266,7 +273,7 @@ CryptoNote::WalletTransfer convert(const CryptoNote::WalletLegacyTransfer& tr) {
namespace CryptoNote {
const uint32_t WalletSerializer::SERIALIZATION_VERSION = 2;
const uint32_t WalletSerializer::SERIALIZATION_VERSION = 3;
void CryptoContext::incIv() {
uint64_t * i = reinterpret_cast<uint64_t *>(&iv.data[0]);
@ -503,7 +510,7 @@ void WalletSerializer::saveTransfers(Common::IOutputStream& destination, CryptoC
for (const auto& kv: m_transfers) {
uint64_t txId = kv.first;
WalletTransferDto tr(kv.second);
WalletTransferDto tr(kv.second, SERIALIZATION_VERSION);
serializeEncrypted(txId, "transaction_id", cryptoContext, destination);
cryptoContext.incIv();
@ -519,18 +526,18 @@ void WalletSerializer::load(const std::string& password, Common::IInputStream& s
uint32_t version = loadVersion(source);
if (version == SERIALIZATION_VERSION) {
loadCurrentVersion(source, password);
} else if (version == 1) {
loadWalletV1(source, password);
} else {
if (version > SERIALIZATION_VERSION) {
throw std::system_error(make_error_code(error::WRONG_VERSION));
} else if (version != 1) {
loadWallet(source, password, version);
} else {
loadWalletV1(source, password);
}
s.endObject();
}
void WalletSerializer::loadCurrentVersion(Common::IInputStream& source, const std::string& password) {
void WalletSerializer::loadWallet(Common::IInputStream& source, const std::string& password, uint32_t version) {
CryptoNote::CryptoContext cryptoContext;
bool details = false;
@ -549,7 +556,7 @@ void WalletSerializer::loadCurrentVersion(Common::IInputStream& source, const st
if (details) {
loadTransactions(source, cryptoContext);
loadTransfers(source, cryptoContext);
loadTransfers(source, cryptoContext, version);
}
if (cache) {
@ -869,7 +876,7 @@ void WalletSerializer::loadTransactions(Common::IInputStream& source, CryptoCont
}
}
void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext) {
void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version) {
uint64_t count = 0;
deserializeEncrypted(count, "transfers_count", cryptoContext, source);
cryptoContext.incIv();
@ -881,7 +888,7 @@ void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext
deserializeEncrypted(txId, "transaction_id", cryptoContext, source);
cryptoContext.incIv();
WalletTransferDto dto;
WalletTransferDto dto(version);
deserializeEncrypted(dto, "transfer", cryptoContext, source);
cryptoContext.incIv();
@ -889,6 +896,12 @@ void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext
tr.address = dto.address;
tr.amount = dto.amount;
if (version > 2) {
tr.type = static_cast<WalletTransferType>(dto.type);
} else {
tr.type = WalletTransferType::USUAL;
}
m_transfers.push_back(std::make_pair(txId, tr));
}
}

View file

@ -59,7 +59,7 @@ public:
private:
static const uint32_t SERIALIZATION_VERSION;
void loadCurrentVersion(Common::IInputStream& source, const std::string& password);
void loadWallet(Common::IInputStream& source, const std::string& password, uint32_t version);
void loadWalletV1(Common::IInputStream& source, const std::string& password);
CryptoContext generateCryptoContext(const std::string& password);
@ -95,7 +95,7 @@ private:
void loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadChange(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version);
void loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer);
void loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer);

View file

@ -53,37 +53,37 @@ public:
WalletLegacy(const CryptoNote::Currency& currency, INode& node);
virtual ~WalletLegacy();
virtual void addObserver(IWalletLegacyObserver* observer);
virtual void removeObserver(IWalletLegacyObserver* observer);
virtual void addObserver(IWalletLegacyObserver* observer) override;
virtual void removeObserver(IWalletLegacyObserver* observer) override;
virtual void initAndGenerate(const std::string& password);
virtual void initAndLoad(std::istream& source, const std::string& password);
virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password);
virtual void shutdown();
virtual void reset();
virtual void initAndGenerate(const std::string& password) override;
virtual void initAndLoad(std::istream& source, const std::string& password) override;
virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password) override;
virtual void shutdown() override;
virtual void reset() override;
virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true);
virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) override;
virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword);
virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword) override;
virtual std::string getAddress();
virtual std::string getAddress() override;
virtual uint64_t actualBalance();
virtual uint64_t pendingBalance();
virtual uint64_t actualBalance() override;
virtual uint64_t pendingBalance() override;
virtual size_t getTransactionCount();
virtual size_t getTransferCount();
virtual size_t getTransactionCount() override;
virtual size_t getTransferCount() override;
virtual TransactionId findTransactionByTransferId(TransferId transferId);
virtual TransactionId findTransactionByTransferId(TransferId transferId) override;
virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction);
virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer);
virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) override;
virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) override;
virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0);
virtual TransactionId sendTransaction(const std::vector<WalletLegacyTransfer>& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0);
virtual std::error_code cancelTransaction(size_t transactionId);
virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) override;
virtual TransactionId sendTransaction(const std::vector<WalletLegacyTransfer>& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) override;
virtual std::error_code cancelTransaction(size_t transactionId) override;
virtual void getAccountKeys(AccountKeys& keys);
virtual void getAccountKeys(AccountKeys& keys) override;
private:

View file

@ -34,7 +34,11 @@ static const char _NR[] = {
#include <stddef.h>
#include <time.h>
#include <sys/timeb.h>
#ifdef __APPLE__
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

View file

@ -24,7 +24,7 @@ cn_slow_hash_noaesni
(void *restrict context, const void *restrict data, size_t length, void *restrict hash)
{
#define ctx ((struct cn_ctx *) context)
uint8_t ExpandedKey[256];
ALIGNED_DECL(uint8_t ExpandedKey[256], 16);
size_t i;
__m128i *longoutput, *expkey, *xmminput, b_x;
ALIGNED_DECL(uint64_t a[2], 16);

View file

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0.8.1"
#define PROJECT_VERSION_BUILD_NO "614"
#define PROJECT_VERSION "1.0.8.2"
#define PROJECT_VERSION_BUILD_NO "625"
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"

View file

@ -39,7 +39,7 @@ public:
virtual ~NodeObserver() {
}
virtual void peerCountUpdated(size_t count) {
virtual void peerCountUpdated(size_t count) override {
logger(INFO) << '[' << m_name << "] peerCountUpdated " << count << " = " << m_nodeProxy.getPeerCount();
}

View file

@ -31,16 +31,16 @@ public:
ICoreStub();
ICoreStub(const CryptoNote::Block& genesisBlock);
virtual bool addObserver(CryptoNote::ICoreObserver* observer);
virtual bool removeObserver(CryptoNote::ICoreObserver* observer);
virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id);
virtual bool addObserver(CryptoNote::ICoreObserver* observer) override;
virtual bool removeObserver(CryptoNote::ICoreObserver* observer) override;
virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) override;
virtual std::vector<Crypto::Hash> findBlockchainSupplement(const std::vector<Crypto::Hash>& remoteBlockIds, size_t maxCount,
uint32_t& totalBlockCount, uint32_t& startBlockIndex) override;
virtual bool get_random_outs_for_amounts(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req,
CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res);
virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector<uint32_t>& indexs);
virtual CryptoNote::i_cryptonote_protocol* get_protocol();
virtual bool handle_incoming_tx(CryptoNote::BinaryArray const& tx_blob, CryptoNote::tx_verification_context& tvc, bool keeped_by_block);
CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) override;
virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector<uint32_t>& indexs) override;
virtual CryptoNote::i_cryptonote_protocol* get_protocol() override;
virtual bool handle_incoming_tx(CryptoNote::BinaryArray const& tx_blob, CryptoNote::tx_verification_context& tvc, bool keeped_by_block) override;
virtual std::vector<CryptoNote::Transaction> getPoolTransactions() override;
virtual bool getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector<Crypto::Hash>& knownTxsIds,
std::vector<CryptoNote::Transaction>& addedTxs, std::vector<Crypto::Hash>& deletedTxsIds) override;

View file

@ -34,10 +34,10 @@ public:
virtual bool addObserver(CryptoNote::INodeObserver* observer) override;
virtual bool removeObserver(CryptoNote::INodeObserver* observer) override;
virtual void init(const CryptoNote::INode::Callback& callback) {callback(std::error_code());};
virtual bool shutdown() { return true; };
virtual void init(const CryptoNote::INode::Callback& callback) override {callback(std::error_code());};
virtual bool shutdown() override { return true; };
virtual size_t getPeerCount() const { return 0; };
virtual size_t getPeerCount() const override { return 0; };
virtual uint32_t getLastLocalBlockHeight() const override { return 0; };
virtual uint32_t getLastKnownBlockHeight() const override { return 0; };
virtual uint32_t getLocalBlockCount() const override { return 0; };
@ -56,13 +56,13 @@ public:
virtual void queryBlocks(std::vector<Crypto::Hash>&& knownBlockIds, uint64_t timestamp, std::vector<CryptoNote::BlockShortEntry>& newBlocks,
uint32_t& startHeight, const Callback& callback) override { callback(std::error_code()); };
virtual void getBlocks(const std::vector<uint32_t>& blockHeights, std::vector<std::vector<CryptoNote::BlockDetails>>& blocks, const Callback& callback) { callback(std::error_code()); };
virtual void getBlocks(const std::vector<Crypto::Hash>& blockHashes, std::vector<CryptoNote::BlockDetails>& blocks, const Callback& callback) { callback(std::error_code()); };
virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector<CryptoNote::BlockDetails>& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { callback(std::error_code()); };
virtual void getTransactions(const std::vector<Crypto::Hash>& transactionHashes, std::vector<CryptoNote::TransactionDetails>& transactions, const Callback& callback) { callback(std::error_code()); };
virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector<CryptoNote::TransactionDetails>& transactions, const Callback& callback) { callback(std::error_code()); };
virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector<CryptoNote::TransactionDetails>& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { callback(std::error_code()); };
virtual void isSynchronized(bool& syncStatus, const Callback& callback) { callback(std::error_code()); };
virtual void getBlocks(const std::vector<uint32_t>& blockHeights, std::vector<std::vector<CryptoNote::BlockDetails>>& blocks, const Callback& callback) override { callback(std::error_code()); };
virtual void getBlocks(const std::vector<Crypto::Hash>& blockHashes, std::vector<CryptoNote::BlockDetails>& blocks, const Callback& callback) override { callback(std::error_code()); };
virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector<CryptoNote::BlockDetails>& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) override { callback(std::error_code()); };
virtual void getTransactions(const std::vector<Crypto::Hash>& transactionHashes, std::vector<CryptoNote::TransactionDetails>& transactions, const Callback& callback) override { callback(std::error_code()); };
virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector<CryptoNote::TransactionDetails>& transactions, const Callback& callback) override { callback(std::error_code()); };
virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector<CryptoNote::TransactionDetails>& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) override { callback(std::error_code()); };
virtual void isSynchronized(bool& syncStatus, const Callback& callback) override { callback(std::error_code()); };
virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, CryptoNote::MultisignatureOutput& out, const Callback& callback) override { callback(std::error_code()); }
void updateObservers();
@ -79,16 +79,16 @@ public:
void setGetNewBlocksLimit(size_t maxBlocks) { m_getMaxBlocks = maxBlocks; }
virtual uint32_t getLastLocalBlockHeight() const { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }
virtual uint32_t getLastKnownBlockHeight() const { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }
virtual uint32_t getLastLocalBlockHeight() const override { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }
virtual uint32_t getLastKnownBlockHeight() const override { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }
virtual uint32_t getLocalBlockCount() const override { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size()); }
virtual uint32_t getKnownBlockCount() const override { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size()); }
virtual void getNewBlocks(std::vector<Crypto::Hash>&& knownBlockIds, std::vector<CryptoNote::block_complete_entry>& newBlocks, uint32_t& startHeight, const Callback& callback);
virtual void getNewBlocks(std::vector<Crypto::Hash>&& knownBlockIds, std::vector<CryptoNote::block_complete_entry>& newBlocks, uint32_t& startHeight, const Callback& callback) override;
virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback);
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback);
virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override;
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback) override;
virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices, const Callback& callback) override;
virtual void queryBlocks(std::vector<Crypto::Hash>&& knownBlockIds, uint64_t timestamp, std::vector<CryptoNote::BlockShortEntry>& newBlocks, uint32_t& startHeight, const Callback& callback) override;
virtual void getPoolSymmetricDifference(std::vector<Crypto::Hash>&& known_pool_tx_ids, Crypto::Hash known_block_id, bool& is_bc_actual,

View file

@ -1049,7 +1049,7 @@ TEST_F(BcSTest, checkBlocksRequesting) {
});
uint32_t blocksExpected = 20;
size_t blocksExpected = 20;
generator.generateEmptyBlocks(blocksExpected - 1); //-1 for genesis
m_node.setGetNewBlocksLimit(3);
@ -1085,7 +1085,7 @@ TEST_F(BcSTest, checkConsumerHeightReceived) {
uint32_t firstlySnchronizedHeight = 20;
generator.generateEmptyBlocks(firstlySnchronizedHeight - 1);//-1 for genesis
generator.generateEmptyBlocks(static_cast<size_t>(firstlySnchronizedHeight - 1));//-1 for genesis
m_node.setGetNewBlocksLimit(50);
c.onNewBlocksFunctor = [&](const CompleteBlock*, uint32_t startHeight, size_t) -> bool {

View file

@ -1103,7 +1103,7 @@ TEST_F(BlockchainExplorerTests, getBlocksByTimestampNotInited) {
}
TEST_F(BlockchainExplorerTests, generatedTransactions) {
const uint32_t NUMBER_OF_BLOCKS = 10;
const size_t NUMBER_OF_BLOCKS = 10;
const size_t POOL_TX_NUMBER = 10;
std::vector<uint32_t> blockHeights;
for (uint32_t i = 0; i < NUMBER_OF_BLOCKS + 3; ++i) {

View file

@ -134,11 +134,11 @@ void TestBlockchainGenerator::addMiningBlock() {
m_generatedTransactionsIndex.add(block);
}
void TestBlockchainGenerator::generateEmptyBlocks(uint32_t count)
void TestBlockchainGenerator::generateEmptyBlocks(size_t count)
{
std::unique_lock<std::mutex> lock(m_mutex);
for (uint32_t i = 0; i < count; ++i)
for (size_t i = 0; i < count; ++i)
{
CryptoNote::Block& prev_block = m_blockchain.back();
CryptoNote::Block block;
@ -359,11 +359,11 @@ void TestBlockchainGenerator::addTx(const CryptoNote::Transaction& tx) {
const auto& out = tx.outputs[outIndex];
if (out.target.type() == typeid(KeyOutput)) {
auto& keyOutsContainer = keyOutsIndex[out.amount];
globalIndexes.push_back(keyOutsContainer.size());
globalIndexes.push_back(static_cast<uint32_t>(keyOutsContainer.size()));
keyOutsContainer.push_back({ txHash, outIndex });
} else if (out.target.type() == typeid(MultisignatureOutput)) {
auto& msigOutsContainer = multisignatureOutsIndex[out.amount];
globalIndexes.push_back(msigOutsContainer.size());
globalIndexes.push_back(static_cast<uint32_t>(msigOutsContainer.size()));
msigOutsContainer.push_back({ txHash, outIndex });
}
}

View file

@ -36,7 +36,7 @@ public:
//TODO: get rid of this method
std::vector<CryptoNote::Block>& getBlockchain();
std::vector<CryptoNote::Block> getBlockchainCopy();
void generateEmptyBlocks(uint32_t count);
void generateEmptyBlocks(size_t count);
bool getBlockRewardForAddress(const CryptoNote::AccountPublicAddress& address);
bool generateTransactionsInOneBlock(const CryptoNote::AccountPublicAddress& address, size_t n);
bool getSingleOutputTransaction(const CryptoNote::AccountPublicAddress& address, uint64_t amount);

View file

@ -267,7 +267,7 @@ TEST_F(InProcessNodeTests, getBlocksByHeightEmpty) {
}
TEST_F(InProcessNodeTests, getBlocksByHeightMany) {
const uint32_t NUMBER_OF_BLOCKS = 10;
const size_t NUMBER_OF_BLOCKS = 10;
std::vector<uint32_t> blockHeights;
std::vector<std::vector<CryptoNote::BlockDetails>> actualBlocks;
@ -311,7 +311,7 @@ TEST_F(InProcessNodeTests, getBlocksByHeightMany) {
}
TEST_F(InProcessNodeTests, getBlocksByHeightFail) {
const uint32_t NUMBER_OF_BLOCKS = 10;
const size_t NUMBER_OF_BLOCKS = 10;
std::vector<uint32_t> blockHeights;
std::vector<std::vector<CryptoNote::BlockDetails>> actualBlocks;
@ -366,7 +366,7 @@ TEST_F(InProcessNodeTests, getBlocksByHashEmpty) {
}
TEST_F(InProcessNodeTests, getBlocksByHashMany) {
const uint32_t NUMBER_OF_BLOCKS = 10;
const size_t NUMBER_OF_BLOCKS = 10;
std::vector<Crypto::Hash> blockHashes;
std::vector<CryptoNote::BlockDetails> actualBlocks;
@ -407,7 +407,7 @@ TEST_F(InProcessNodeTests, getBlocksByHashMany) {
}
TEST_F(InProcessNodeTests, getBlocksByHashFail) {
const uint32_t NUMBER_OF_BLOCKS = 10;
const size_t NUMBER_OF_BLOCKS = 10;
std::vector<Crypto::Hash> blockHashes;
std::vector<CryptoNote::BlockDetails> actualBlocks;

View file

@ -437,7 +437,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesError)
block.transactions.push_back(tx);
consumer.addSubscription(subscription);
ASSERT_FALSE(consumer.onNewBlocks(&block, subscription.syncStart.height, 1));
ASSERT_FALSE(consumer.onNewBlocks(&block, static_cast<uint32_t>(subscription.syncStart.height), 1));
}
TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) {
@ -457,7 +457,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) {
block.block->timestamp = subscription.syncStart.timestamp;
block.transactions.push_back(tx);
ASSERT_TRUE(m_consumer.onNewBlocks(&block, subscription.syncStart.height, 1));
ASSERT_TRUE(m_consumer.onNewBlocks(&block, static_cast<uint32_t>(subscription.syncStart.height), 1));
ASSERT_EQ(900, container.balance(ITransfersContainer::IncludeAllLocked));
std::unique_ptr<CompleteBlock[]> blocks(new CompleteBlock[subscription.transactionSpendableAge]);
@ -468,7 +468,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) {
addTestKeyOutput(*tr, 100, i + 1, generateAccountKeys());
}
ASSERT_TRUE(m_consumer.onNewBlocks(blocks.get(), subscription.syncStart.height + 1, subscription.transactionSpendableAge));
ASSERT_TRUE(m_consumer.onNewBlocks(blocks.get(), static_cast<uint32_t>(subscription.syncStart.height + 1), static_cast<uint32_t>(subscription.transactionSpendableAge)));
ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked));
ASSERT_EQ(900, container.balance(ITransfersContainer::IncludeAllUnlocked));
}
@ -792,7 +792,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_manyBlocks) {
}
}
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size()));
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, static_cast<uint32_t>(blocks.size())));
ASSERT_EQ(expectedTransactions, container.transactionsCount());
ASSERT_EQ(expectedAmount, container.balance(ITransfersContainer::IncludeAll));
@ -1031,7 +1031,7 @@ TEST_F(TransfersConsumerPerformanceTest, DISABLED_memory) {
{
AutoPrintTimer t;
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size()));
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, static_cast<uint32_t>(blocks.size())));
}
blocks.clear();
@ -1056,7 +1056,7 @@ TEST_F(TransfersConsumerPerformanceTest, DISABLED_performanceTest) {
std::cout << "Calling onNewBlocks" << std::endl;
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size()));
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, static_cast<uint32_t>(blocks.size())));
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> dur = end - start;

View file

@ -54,9 +54,13 @@ namespace CryptoNote {
case WalletTransactionState::SUCCEEDED:
o << "SUCCEEDED";
break;
case WalletTransactionState::CREATED:
o << "CREATED";
break;
}
return o;
}
std::ostream& operator<<(std::ostream& o, const WalletTransaction& tx) {
o << "WalletTransaction{state=" << tx.state << ", timestamp=" << tx.timestamp
<< ", blockHeight=" << tx.blockHeight << ", hash=" << tx.hash
@ -123,6 +127,10 @@ namespace CryptoNote {
return false;
}
if (lhs.type != rhs.type) {
return false;
}
return true;
}
@ -185,8 +193,10 @@ protected:
size_t sendMoneyToRandomAddressFrom(const std::string& address, uint64_t amount, uint64_t fee);
size_t sendMoneyToRandomAddressFrom(const std::string& address);
size_t sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0);
size_t sendMoney(const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0);
size_t sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0);
size_t sendMoney(const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0);
size_t sendMoneyWithDonation(const std::string& to, uint64_t amount, uint64_t fee,
const std::string& donationAddress, uint64_t donationAmount, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0);
void fillWalletWithDetailsCache();
@ -401,11 +411,11 @@ void WalletApi::generateAddressesWithPendingMoney(size_t count) {
}
size_t WalletApi::sendMoneyToRandomAddressFrom(const std::string& address, uint64_t amount, uint64_t fee) {
CryptoNote::WalletTransfer transfer;
transfer.address = RANDOM_ADDRESS;
transfer.amount = amount;
CryptoNote::WalletOrder order;
order.address = RANDOM_ADDRESS;
order.amount = amount;
return alice.transfer(address, transfer, fee, 0);
return alice.transfer(address, order, fee, 0);
}
size_t WalletApi::sendMoneyToRandomAddressFrom(const std::string& address) {
@ -429,18 +439,33 @@ void WalletApi::fillWalletWithDetailsCache() {
}
}
size_t WalletApi::sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) {
CryptoNote::WalletTransfer transfer;
transfer.address = to;
transfer.amount = amount;
size_t WalletApi::sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) {
CryptoNote::WalletOrder order;
order.address = to;
order.amount = amount;
return wallet.transfer(transfer, fee, mixIn, extra, unlockTimestamp);
return wallet.transfer(order, fee, mixIn, extra, unlockTimestamp);
}
size_t WalletApi::sendMoney(const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) {
size_t WalletApi::sendMoney(const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) {
return sendMoney(alice, to, amount, fee, mixIn, extra, unlockTimestamp);
}
size_t WalletApi::sendMoneyWithDonation(const std::string& to, uint64_t amount, uint64_t fee,
const std::string& donationAddress, uint64_t donationAmount, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) {
TransactionParameters params;
params.destinations.push_back({to, amount});
params.fee = fee;
params.donation.address = donationAddress;
params.donation.threshold = donationAmount;
params.mixIn = mixIn;
params.extra = extra;
params.unlockTimestamp = unlockTimestamp;
return alice.transfer(params);
}
void WalletApi::wait(uint64_t milliseconds) {
System::Timer timer(dispatcher);
timer.sleep(std::chrono::nanoseconds(milliseconds * 1000000));
@ -510,7 +535,7 @@ TEST_F(WalletApi, pendingBalanceUpdatedAfterTransactionGotInBlock) {
auto prevPending = alice.getPendingBalance();
generator.generateEmptyBlocks(TRANSACTION_SOFTLOCK_TIME);
generator.generateEmptyBlocks(static_cast<size_t>(TRANSACTION_SOFTLOCK_TIME));
node.updateObservers();
waitPendingBalanceUpdated(prevPending);
@ -524,7 +549,7 @@ TEST_F(WalletApi, moneyLockedIfTransactionIsSoftLocked) {
bob.initialize("pass2");
sendMoney(bob.createAddress(), SENT, FEE);
generator.generateEmptyBlocks(TRANSACTION_SOFTLOCK_TIME - 1);
generator.generateEmptyBlocks(static_cast<size_t>(TRANSACTION_SOFTLOCK_TIME - 1));
node.updateObservers();
waitPendingBalanceUpdated(bob, 0);
@ -598,7 +623,11 @@ TEST_F(WalletApi, transferFromTwoAddresses) {
}
TEST_F(WalletApi, transferTooBigTransaction) {
CryptoNote::Currency cur = CryptoNote::CurrencyBuilder(logger).blockGrantedFullRewardZone(5).minerTxBlobReservedSize(2).currency();
const size_t testBlockGrantedFullRewardZone = 2000;
const size_t outputSize = 32 + 1;
const size_t bigTxOutputCount = 2 * testBlockGrantedFullRewardZone / outputSize;
CryptoNote::Currency cur = CryptoNote::CurrencyBuilder(logger).blockGrantedFullRewardZone(testBlockGrantedFullRewardZone).currency();
TestBlockchainGenerator gen(cur);
INodeTrivialRefreshStub n(gen);
@ -613,11 +642,12 @@ TEST_F(WalletApi, transferTooBigTransaction) {
n.updateObservers();
waitActualBalanceUpdated(wallet, prev);
CryptoNote::WalletTransfer transfer;
transfer.address = RANDOM_ADDRESS;
transfer.amount = SENT;
std::vector<CryptoNote::WalletOrder> destinations;
for (size_t i = 0; i < bigTxOutputCount; ++i) {
destinations.push_back({ RANDOM_ADDRESS, 1 });
}
ASSERT_ANY_THROW(wallet.transfer(transfer, FEE));
ASSERT_ANY_THROW(wallet.transfer(destinations, FEE));
}
TEST_F(WalletApi, balanceAfterTransfer) {
@ -1272,26 +1302,91 @@ TEST_F(WalletApi, incomingTxTransfer) {
wait(100);
}
TEST_F(WalletApi, walletSendsTransactionUpdatedEventAfterAddingTransfer) {
generateAndUnlockMoney();
CryptoNote::WalletGreen bob(dispatcher, currency, node, TRANSACTION_SOFTLOCK_TIME);
bob.initialize("pass2");
bob.createAddress();
bob.createAddress();
bob.createAddress();
std::vector<CryptoNote::WalletOrder> orders;
orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(0), SENT });
orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(1), SENT });
orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(2), SENT });
alice.transfer(orders, FEE, 0, "", 0);
node.updateObservers();
ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_CREATED, std::chrono::seconds(5)));
ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_UPDATED, std::chrono::seconds(5)));
ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_UPDATED, std::chrono::seconds(5)));
bob.shutdown();
wait(100);
}
TEST_F(WalletApi, walletCreatesTransferForEachTransactionFunding) {
generateAndUnlockMoney();
CryptoNote::WalletGreen bob(dispatcher, currency, node, TRANSACTION_SOFTLOCK_TIME);
bob.initialize("pass2");
bob.createAddress();
bob.createAddress();
std::vector<CryptoNote::WalletOrder> orders;
orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(0), SENT });
orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(1), 2 * SENT });
alice.transfer(orders, FEE, 0, "", 0);
node.updateObservers();
ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_UPDATED, std::chrono::seconds(5)));
ASSERT_EQ(2, bob.getTransactionTransferCount(0));
auto tr1 = bob.getTransactionTransfer(0, 0);
auto tr2 = bob.getTransactionTransfer(0, 1);
ASSERT_TRUE(tr1.address == bob.getAddress(0) || tr1.address == bob.getAddress(1));
ASSERT_TRUE(tr2.address == bob.getAddress(0) || tr2.address == bob.getAddress(1));
ASSERT_NE(tr1.address, tr2.address);
bob.shutdown();
wait(100);
}
size_t getTransactionUsualTransferCount(WalletGreen& wallet, size_t transactionIndex) {
size_t transfersCount = wallet.getTransactionTransferCount(transactionIndex);
size_t usualTransfersCount = 0;
for (size_t i = 0; i < transfersCount; ++i) {
if (wallet.getTransactionTransfer(transactionIndex, i).type == WalletTransferType::USUAL) {
++usualTransfersCount;
}
}
return usualTransfersCount;
}
TEST_F(WalletApi, hybridTxTransfer) {
generateAndUnlockMoney();
alice.createAddress();
alice.createAddress();
CryptoNote::WalletTransfer tr1 { alice.getAddress(1), static_cast<int64_t>(SENT) };
CryptoNote::WalletTransfer tr2 { alice.getAddress(2), static_cast<int64_t>(2 * SENT) };
CryptoNote::WalletOrder tr1 { alice.getAddress(1), SENT };
CryptoNote::WalletOrder tr2 { alice.getAddress(2), 2 * SENT };
alice.transfer({tr1, tr2}, FEE);
node.updateObservers();
dispatcher.yield();
ASSERT_EQ(2, alice.getTransactionTransferCount(1));
ASSERT_EQ(2, getTransactionUsualTransferCount(alice, 1));
EXPECT_EQ(tr1.address, alice.getTransactionTransfer(1, 0).address);
EXPECT_EQ(-tr1.amount, alice.getTransactionTransfer(1, 0).amount);
EXPECT_EQ(-static_cast<int64_t>(tr1.amount), alice.getTransactionTransfer(1, 0).amount);
EXPECT_EQ(WalletTransferType::USUAL, alice.getTransactionTransfer(1, 0).type);
EXPECT_EQ(tr2.address, alice.getTransactionTransfer(1, 1).address);
EXPECT_EQ(-tr2.amount, alice.getTransactionTransfer(1, 1).amount);
EXPECT_EQ(-static_cast<int64_t>(tr2.amount), alice.getTransactionTransfer(1, 1).amount);
EXPECT_EQ(WalletTransferType::USUAL, alice.getTransactionTransfer(1, 1).type);
}
TEST_F(WalletApi, doubleSpendJustSentOut) {
@ -1384,7 +1479,7 @@ TEST_F(WalletApi, DISABLED_loadTest) {
steady_clock::time_point transferStart = steady_clock::now();
for (size_t i = 0; i < TRANSACTIONS_COUNT; ++i) {
CryptoNote::WalletTransfer tr;
CryptoNote::WalletOrder tr;
tr.amount = SENT;
tr.address = RANDOM_ADDRESS;
wallet.transfer(tr, FEE);
@ -1839,10 +1934,10 @@ TEST_F(WalletApi, fusionManagerIsFusionTransactionSpent) {
ASSERT_NE(WALLET_INVALID_TRANSACTION_ID, id);
unlockMoney();
CryptoNote::WalletTransfer transfer;
transfer.address = wallet.getAddress(0);
transfer.amount = alice.getActualBalance() - currency.minimumFee();
alice.transfer(aliceAddress, transfer, currency.minimumFee(), 0);
CryptoNote::WalletOrder order;
order.address = wallet.getAddress(0);
order.amount = alice.getActualBalance() - currency.minimumFee();
alice.transfer(aliceAddress, order, currency.minimumFee(), 0);
auto pending = wallet.getPendingBalance();
node.updateObservers();
@ -1851,3 +1946,123 @@ TEST_F(WalletApi, fusionManagerIsFusionTransactionSpent) {
ASSERT_TRUE(alice.isFusionTransaction(id));
}
size_t findDonationTransferId(const WalletGreen& wallet, size_t transactionId) {
for (size_t i = 0; i < wallet.getTransactionTransferCount(transactionId); ++i) {
if (wallet.getTransactionTransfer(transactionId, i).type == WalletTransferType::DONATION) {
return i;
}
}
return WALLET_INVALID_TRANSFER_ID;
}
TEST_F(WalletApi, donationTransferPresents) {
const uint64_t DONATION_THRESHOLD = 1000000;
generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD);
unlockMoney();
auto transactionId = sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD);
ASSERT_NE(WALLET_INVALID_TRANSACTION_ID, transactionId);
auto donationTransferId = findDonationTransferId(alice, transactionId);
ASSERT_NE(WALLET_INVALID_TRANSFER_ID, donationTransferId);
auto donationTransfer = alice.getTransactionTransfer(transactionId, donationTransferId);
ASSERT_EQ(WalletTransferType::DONATION, donationTransfer.type);
ASSERT_EQ(-static_cast<int64_t>(DONATION_THRESHOLD), donationTransfer.amount);
ASSERT_EQ(RANDOM_ADDRESS, donationTransfer.address);
}
TEST_F(WalletApi, donationDidntHappenIfNotEnoughMoney) {
const uint64_t DONATION_THRESHOLD = 1000000;
generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE);
unlockMoney();
auto transactionId = sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD);
ASSERT_NE(WALLET_INVALID_TRANSACTION_ID, transactionId);
ASSERT_EQ(WALLET_INVALID_TRANSFER_ID, findDonationTransferId(alice, transactionId));
}
TEST_F(WalletApi, donationThrowsIfAddressEmpty) {
const uint64_t DONATION_THRESHOLD = 1000000;
generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD);
unlockMoney();
TransactionParameters params;
params.destinations.push_back({RANDOM_ADDRESS, SENT});
params.fee = FEE;
params.donation.threshold = DONATION_THRESHOLD;
ASSERT_ANY_THROW(alice.transfer(params));
}
TEST_F(WalletApi, donationThrowsIfThresholdZero) {
const uint64_t DONATION_THRESHOLD = 1000000;
generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD);
unlockMoney();
TransactionParameters params;
params.destinations.push_back({RANDOM_ADDRESS, SENT});
params.fee = FEE;
params.donation.address = RANDOM_ADDRESS;
params.donation.threshold = 0;
ASSERT_ANY_THROW(alice.transfer(params));
}
TEST_F(WalletApi, donationTransactionHaveCorrectFee) {
CatchTransactionNodeStub catchNode(generator);
CryptoNote::WalletGreen wallet(dispatcher, currency, catchNode);
wallet.initialize("pass");
wallet.createAddress();
const uint64_t DONATION_THRESHOLD = 1000000;
generator.getSingleOutputTransaction(parseAddress(wallet.getAddress(0)), SENT + FEE + DONATION_THRESHOLD);
unlockMoney(wallet, catchNode);
TransactionParameters params;
params.destinations.push_back({RANDOM_ADDRESS, SENT});
params.fee = FEE;
params.donation.address = RANDOM_ADDRESS;
params.donation.threshold = DONATION_THRESHOLD;
wallet.transfer(params);
ASSERT_TRUE(catchNode.caught);
ASSERT_EQ(FEE, getInputAmount(catchNode.transaction) - getOutputAmount(catchNode.transaction));
wallet.shutdown();
}
TEST_F(WalletApi, donationSerialization) {
const uint64_t DONATION_THRESHOLD = 1000000;
generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD);
unlockMoney();
sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD);
std::stringstream data;
alice.save(data, true, true);
WalletGreen bob(dispatcher, currency, node, TRANSACTION_SOFTLOCK_TIME);
bob.load(data, "pass");
compareWalletsTransactionTransfers(alice, bob);
bob.shutdown();
}
TEST_F(WalletApi, transferThrowsIfDonationThresholdTooBig) {
const uint64_t DONATION_THRESHOLD = static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE);
unlockMoney();
ASSERT_ANY_THROW(sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD));
}

View file

@ -1392,7 +1392,7 @@ TEST_F(WalletLegacyApi, outcommingExternalTransactionTotalAmount) {
wallet.initAndLoad(walletData, "pass");
WaitWalletSync(&walletObserver);
ASSERT_EQ(-(sent + fee), externalTransactionObserver.totalAmount);
ASSERT_EQ(-static_cast<int64_t>(sent + fee), externalTransactionObserver.totalAmount);
wallet.shutdown();
}

View file

@ -701,7 +701,8 @@ const size_t TEST_TX_COUNT_UP_TO_MEDIAN = 10;
const size_t TEST_MAX_TX_COUNT_PER_BLOCK = TEST_TX_COUNT_UP_TO_MEDIAN * 125 / 100;
const size_t TEST_TRANSACTION_SIZE = 2000;
const size_t TEST_FUSION_TX_MAX_SIZE = TEST_FUSION_TX_COUNT_PER_BLOCK * TEST_TRANSACTION_SIZE;
const size_t TEST_MEDIAN_SIZE = TEST_TX_COUNT_UP_TO_MEDIAN * TEST_TRANSACTION_SIZE;
const size_t TEST_MINER_TX_BLOB_RESERVED_SIZE = 600;
const size_t TEST_MEDIAN_SIZE = TEST_TX_COUNT_UP_TO_MEDIAN * TEST_TRANSACTION_SIZE + TEST_MINER_TX_BLOB_RESERVED_SIZE;
Transaction createTestFusionTransaction(const Currency& currency) {
FusionTransactionBuilder builder(currency, 30 * currency.defaultDustThreshold());