Bytecoin v.1.0.7 release

This commit is contained in:
Antonio Juarez 2015-08-11 15:33:19 +01:00
parent 49572fc425
commit deda499fc9
30 changed files with 770 additions and 156 deletions

View file

@ -9,6 +9,8 @@ set(CMAKE_SKIP_INSTALL_RULES ON)
set(CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY ON) set(CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY ON)
set(CMAKE_SUPPRESS_REGENERATION ON) set(CMAKE_SUPPRESS_REGENERATION ON)
enable_testing() enable_testing()
# copy CTestCustom.cmake to build dir to disable long running tests in 'make test'
configure_file(${CMAKE_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_BINARY_DIR})
project(Bytecoin) project(Bytecoin)

11
CTestCustom.cmake Normal file
View file

@ -0,0 +1,11 @@
set(CTEST_CUSTOM_TESTS_IGNORE
CoreTests
IntegrationTestLibrary
TestGenerator
CryptoTests
IntegrationTests
NodeRpcProxyTests
PerformanceTests
TransfersTests
)

View file

@ -1,3 +1,8 @@
Release notes 1.0.7
- Fusion transactions support
- Various simplewallet improvements
Release notes 1.0.6 Release notes 1.0.6
- High-level API update - High-level API update

View file

@ -62,6 +62,10 @@ const uint64_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = 60 * 60 * 24;
const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week
const uint64_t CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL = 7; // CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL * CRYPTONOTE_MEMPOOL_TX_LIVETIME = time to forget tx const uint64_t CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL = 7; // CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL * CRYPTONOTE_MEMPOOL_TX_LIVETIME = time to forget tx
const size_t FUSION_TX_MAX_SIZE = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 30 / 100;
const size_t FUSION_TX_MIN_INPUT_COUNT = 6;
const size_t FUSION_TX_MIN_IN_OUT_COUNT_RATIO = 3;
const uint64_t UPGRADE_HEIGHT = 546602; const uint64_t UPGRADE_HEIGHT = 546602;
const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent
const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
@ -156,7 +160,8 @@ const CheckpointData CHECKPOINTS[] = {
{785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"}, {785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"},
{789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"}, {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"},
{796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"}, {796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"},
{800000, "d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86"} {800000, "d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86"},
{804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"}
}; };
} // CryptoNote } // CryptoNote

View file

@ -23,6 +23,7 @@
namespace CryptoNote { namespace CryptoNote {
const Crypto::Hash NULL_HASH = boost::value_initialized<Crypto::Hash>(); const Crypto::Hash NULL_HASH = boost::value_initialized<Crypto::Hash>();
const Crypto::PublicKey NULL_PUBLIC_KEY = boost::value_initialized<Crypto::PublicKey>(); const Crypto::PublicKey NULL_PUBLIC_KEY = boost::value_initialized<Crypto::PublicKey>();
const Crypto::SecretKey NULL_SECRET_KEY = boost::value_initialized<Crypto::SecretKey>();
KeyPair generateKeyPair(); KeyPair generateKeyPair();

View file

@ -209,6 +209,52 @@ bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alr
return true; return true;
} }
bool Currency::isFusionTransaction(const Transaction& transaction, uint64_t inputAmount, size_t size) const {
assert(getInputAmount(transaction) == inputAmount);
assert(getObjectBinarySize(transaction) == size);
if (size > fusionTxMaxSize()) {
return false;
}
if (transaction.inputs.size() < fusionTxMinInputCount()) {
return false;
}
if (transaction.inputs.size() < transaction.outputs.size() * fusionTxMinInOutCountRatio()) {
return false;
}
std::vector<uint64_t> expectedOutputsAmounts;
expectedOutputsAmounts.reserve(transaction.outputs.size());
decomposeAmount(inputAmount, defaultDustThreshold(), expectedOutputsAmounts);
assert(!expectedOutputsAmounts.empty());
if (expectedOutputsAmounts.size() != transaction.outputs.size()) {
return false;
}
std::sort(expectedOutputsAmounts.begin(), expectedOutputsAmounts.end());
if (expectedOutputsAmounts.front() <= defaultDustThreshold()) {
return false;
}
auto it1 = expectedOutputsAmounts.begin();
auto it2 = transaction.outputs.begin();
for (; it1 != expectedOutputsAmounts.end(); ++it1, ++it2) {
if (*it1 != it2->amount) {
return false;
}
}
return true;
}
bool Currency::isFusionTransaction(const Transaction& transaction) const {
return isFusionTransaction(transaction, getInputAmount(transaction), getObjectBinarySize(transaction));
}
std::string Currency::accountAddressAsString(const AccountBase& account) const { std::string Currency::accountAddressAsString(const AccountBase& account) const {
return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.getAccountKeys().address); return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.getAccountKeys().address);
} }
@ -240,6 +286,16 @@ std::string Currency::formatAmount(uint64_t amount) const {
return s; return s;
} }
std::string Currency::formatAmount(int64_t amount) const {
std::string s = formatAmount(static_cast<uint64_t>(std::abs(amount)));
if (amount < 0) {
s.insert(0, "-");
}
return s;
}
bool Currency::parseAmount(const std::string& str, uint64_t& amount) const { bool Currency::parseAmount(const std::string& str, uint64_t& amount) const {
std::string strAmount = str; std::string strAmount = str;
boost::algorithm::trim(strAmount); boost::algorithm::trim(strAmount);
@ -422,6 +478,10 @@ CurrencyBuilder::CurrencyBuilder(Logging::ILogger& log) : m_currency(log) {
mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME);
numberOfPeriodsToForgetTxDeletedFromPool(parameters::CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL); numberOfPeriodsToForgetTxDeletedFromPool(parameters::CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL);
fusionTxMaxSize(parameters::FUSION_TX_MAX_SIZE);
fusionTxMinInputCount(parameters::FUSION_TX_MIN_INPUT_COUNT);
fusionTxMinInOutCountRatio(parameters::FUSION_TX_MIN_IN_OUT_COUNT_RATIO);
upgradeHeight(parameters::UPGRADE_HEIGHT); upgradeHeight(parameters::UPGRADE_HEIGHT);
upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD);
upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW); upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW);

View file

@ -72,6 +72,10 @@ public:
uint64_t mempoolTxFromAltBlockLiveTime() const { return m_mempoolTxFromAltBlockLiveTime; } uint64_t mempoolTxFromAltBlockLiveTime() const { return m_mempoolTxFromAltBlockLiveTime; }
uint64_t numberOfPeriodsToForgetTxDeletedFromPool() const { return m_numberOfPeriodsToForgetTxDeletedFromPool; } uint64_t numberOfPeriodsToForgetTxDeletedFromPool() const { return m_numberOfPeriodsToForgetTxDeletedFromPool; }
size_t fusionTxMaxSize() const { return m_fusionTxMaxSize; }
size_t fusionTxMinInputCount() const { return m_fusionTxMinInputCount; }
size_t fusionTxMinInOutCountRatio() const { return m_fusionTxMinInOutCountRatio; }
uint64_t upgradeHeight() const { return m_upgradeHeight; } uint64_t upgradeHeight() const { return m_upgradeHeight; }
unsigned int upgradeVotingThreshold() const { return m_upgradeVotingThreshold; } unsigned int upgradeVotingThreshold() const { return m_upgradeVotingThreshold; }
size_t upgradeVotingWindow() const { return m_upgradeVotingWindow; } size_t upgradeVotingWindow() const { return m_upgradeVotingWindow; }
@ -99,11 +103,15 @@ public:
uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx,
const BinaryArray& extraNonce = BinaryArray(), size_t maxOuts = 1, bool penalizeFee = false) const; const BinaryArray& extraNonce = BinaryArray(), size_t maxOuts = 1, bool penalizeFee = false) const;
bool isFusionTransaction(const Transaction& transaction) const;
bool isFusionTransaction(const Transaction& transaction, uint64_t inputAmount, size_t size) const;
std::string accountAddressAsString(const AccountBase& account) const; std::string accountAddressAsString(const AccountBase& account) const;
std::string accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const; std::string accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const;
bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const; bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const;
std::string formatAmount(uint64_t amount) const; std::string formatAmount(uint64_t amount) const;
std::string formatAmount(int64_t amount) const;
bool parseAmount(const std::string& str, uint64_t& amount) const; bool parseAmount(const std::string& str, uint64_t& amount) const;
difficulty_type nextDifficulty(std::vector<uint64_t> timestamps, std::vector<difficulty_type> cumulativeDifficulties) const; difficulty_type nextDifficulty(std::vector<uint64_t> timestamps, std::vector<difficulty_type> cumulativeDifficulties) const;
@ -159,6 +167,10 @@ private:
uint64_t m_mempoolTxFromAltBlockLiveTime; uint64_t m_mempoolTxFromAltBlockLiveTime;
uint64_t m_numberOfPeriodsToForgetTxDeletedFromPool; uint64_t m_numberOfPeriodsToForgetTxDeletedFromPool;
size_t m_fusionTxMaxSize;
size_t m_fusionTxMinInputCount;
size_t m_fusionTxMinInOutCountRatio;
uint64_t m_upgradeHeight; uint64_t m_upgradeHeight;
unsigned int m_upgradeVotingThreshold; unsigned int m_upgradeVotingThreshold;
size_t m_upgradeVotingWindow; size_t m_upgradeVotingWindow;
@ -228,6 +240,10 @@ public:
CurrencyBuilder& mempoolTxFromAltBlockLiveTime(uint64_t val) { m_currency.m_mempoolTxFromAltBlockLiveTime = val; return *this; } CurrencyBuilder& mempoolTxFromAltBlockLiveTime(uint64_t val) { m_currency.m_mempoolTxFromAltBlockLiveTime = val; return *this; }
CurrencyBuilder& numberOfPeriodsToForgetTxDeletedFromPool(uint64_t val) { m_currency.m_numberOfPeriodsToForgetTxDeletedFromPool = val; return *this; } CurrencyBuilder& numberOfPeriodsToForgetTxDeletedFromPool(uint64_t val) { m_currency.m_numberOfPeriodsToForgetTxDeletedFromPool = val; return *this; }
CurrencyBuilder& fusionTxMaxSize(size_t val) { m_currency.m_fusionTxMaxSize = val; return *this; }
CurrencyBuilder& fusionTxMinInputCount(size_t val) { m_currency.m_fusionTxMinInputCount = val; return *this; }
CurrencyBuilder& fusionTxMinInOutCountRatio(size_t val) { m_currency.m_fusionTxMinInOutCountRatio = val; return *this; }
CurrencyBuilder& upgradeHeight(uint64_t val) { m_currency.m_upgradeHeight = val; return *this; } CurrencyBuilder& upgradeHeight(uint64_t val) { m_currency.m_upgradeHeight = val; return *this; }
CurrencyBuilder& upgradeVotingThreshold(unsigned int val); CurrencyBuilder& upgradeVotingThreshold(unsigned int val);
CurrencyBuilder& upgradeVotingWindow(size_t val) { m_currency.m_upgradeVotingWindow = val; return *this; } CurrencyBuilder& upgradeVotingWindow(size_t val) { m_currency.m_upgradeVotingWindow = val; return *this; }

View file

@ -110,7 +110,6 @@ namespace CryptoNote {
m_fee_index(boost::get<1>(m_transactions)), m_fee_index(boost::get<1>(m_transactions)),
logger(log, "txpool") { logger(log, "txpool") {
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(const Transaction &tx, /*const Crypto::Hash& tx_prefix_hash,*/ const Crypto::Hash &id, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { bool tx_memory_pool::add_tx(const Transaction &tx, /*const Crypto::Hash& tx_prefix_hash,*/ const Crypto::Hash &id, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) {
if (!check_inputs_types_supported(tx)) { if (!check_inputs_types_supported(tx)) {
@ -134,7 +133,8 @@ namespace CryptoNote {
} }
const uint64_t fee = inputs_amount - outputs_amount; const uint64_t fee = inputs_amount - outputs_amount;
if (!keptByBlock && fee < m_currency.minimumFee()) { bool isFusionTransaction = fee == 0 && m_currency.isFusionTransaction(tx, inputs_amount, blobSize);
if (!keptByBlock && !isFusionTransaction && fee < m_currency.minimumFee()) {
logger(INFO) << "transaction fee is not enough: " << m_currency.formatAmount(fee) << logger(INFO) << "transaction fee is not enough: " << m_currency.formatAmount(fee) <<
", minimum fee: " << m_currency.formatAmount(m_currency.minimumFee()); ", minimum fee: " << m_currency.formatAmount(m_currency.minimumFee());
tvc.m_verifivation_failed = true; tvc.m_verifivation_failed = true;
@ -212,10 +212,7 @@ namespace CryptoNote {
} }
tvc.m_added_to_pool = true; tvc.m_added_to_pool = true;
tvc.m_should_be_relayed = inputsValid && (fee > 0 || isFusionTransaction);
if (inputsValid && fee > 0)
tvc.m_should_be_relayed = true;
tvc.m_verifivation_failed = true; tvc.m_verifivation_failed = true;
if (!addTransactionInputs(id, tx, keptByBlock)) if (!addTransactionInputs(id, tx, keptByBlock))
@ -360,10 +357,24 @@ namespace CryptoNote {
BlockTemplate blockTemplate; BlockTemplate blockTemplate;
for (auto it = m_fee_index.rbegin(); it != m_fee_index.rend() && it->fee == 0; ++it) {
const auto& txd = *it;
if (m_currency.fusionTxMaxSize() < total_size + txd.blobSize) {
continue;
}
TransactionCheckInfo checkInfo(txd);
if (is_transaction_ready_to_go(txd.tx, checkInfo) && blockTemplate.addTransaction(txd.id, txd.tx)) {
total_size += txd.blobSize;
}
}
for (auto i = m_fee_index.begin(); i != m_fee_index.end(); ++i) { for (auto i = m_fee_index.begin(); i != m_fee_index.end(); ++i) {
const auto& txd = *i; const auto& txd = *i;
if (max_total_size < total_size + txd.blobSize) { size_t blockSizeLimit = (txd.fee == 0) ? median_size : max_total_size;
if (blockSizeLimit < total_size + txd.blobSize) {
continue; continue;
} }
@ -374,7 +385,7 @@ namespace CryptoNote {
m_fee_index.modify(i, [&checkInfo](TransactionCheckInfo& item) { m_fee_index.modify(i, [&checkInfo](TransactionCheckInfo& item) {
item = checkInfo; item = checkInfo;
}); });
if (ready && blockTemplate.addTransaction(txd.id, txd.tx)) { if (ready && blockTemplate.addTransaction(txd.id, txd.tx)) {
total_size += txd.blobSize; total_size += txd.blobSize;
fee += txd.fee; fee += txd.fee;

View file

@ -428,7 +428,7 @@ std::error_code WalletService::getTransactionByTransferId(size_t transferId, siz
return std::make_error_code(std::errc::argument_out_of_domain); return std::make_error_code(std::errc::argument_out_of_domain);
} }
auto nextTxId = std::lower_bound(transfersIndices.begin(), transfersIndices.end(), transferId); auto nextTxId = std::upper_bound(transfersIndices.begin(), transfersIndices.end(), transferId);
transactionId = (nextTxId - transfersIndices.begin()) - 1; transactionId = (nextTxId - transfersIndices.begin()) - 1;
return std::error_code(); return std::error_code();
@ -454,13 +454,15 @@ std::error_code WalletService::getTransaction(size_t txId, bool& found, Transact
logger(Logging::DEBUGGING) << "getTransaction request came"; logger(Logging::DEBUGGING) << "getTransaction request came";
found = false; found = false;
try { try {
auto tx = wallet->getTransaction(txId);
if (txId + 1 >= transfersIndices.size()) { if (txId + 1 >= transfersIndices.size()) {
logger(Logging::WARNING) << "Unable to get transaction " << txId << ": argument out of domain."; logger(Logging::WARNING) << "Unable to get transaction " << txId << ": argument out of domain.";
return std::make_error_code(std::errc::argument_out_of_domain); return std::make_error_code(std::errc::argument_out_of_domain);
} }
auto tx = wallet->getTransaction(txId);
fillTransactionRpcInfo(txId, tx, rpcInfo); fillTransactionRpcInfo(txId, tx, rpcInfo);
found = true; found = true;

0
src/Platform/Windows/System/ErrorMessage.cpp Normal file → Executable file
View file

View file

@ -17,8 +17,10 @@
#include "SimpleWallet.h" #include "SimpleWallet.h"
#include <ctime>
#include <fstream> #include <fstream>
#include <future> #include <future>
#include <iomanip>
#include <thread> #include <thread>
#include <set> #include <set>
#include <sstream> #include <sstream>
@ -31,6 +33,7 @@
#include "Common/CommandLine.h" #include "Common/CommandLine.h"
#include "Common/SignalHandler.h" #include "Common/SignalHandler.h"
#include "Common/StringTools.h"
#include "Common/PathTools.h" #include "Common/PathTools.h"
#include "Common/Util.h" #include "Common/Util.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h" #include "CryptoNoteCore/CryptoNoteFormatUtils.h"
@ -350,9 +353,74 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr<
} }
} }
std::string makeCenteredString(size_t width, const std::string& text) {
if (text.size() >= width) {
return text;
}
size_t offset = (width - text.size() + 1) / 2;
return std::string(offset, ' ') + text + std::string(width - text.size() - offset, ' ');
} }
const size_t TIMESTAMP_MAX_WIDTH = 19;
const size_t HASH_MAX_WIDTH = 64;
const size_t TOTAL_AMOUNT_MAX_WIDTH = 20;
const size_t FEE_MAX_WIDTH = 14;
const size_t BLOCK_MAX_WIDTH = 7;
const size_t UNLOCK_TIME_MAX_WIDTH = 11;
void printListTransfersHeader(LoggerRef& logger) {
std::string header = makeCenteredString(TIMESTAMP_MAX_WIDTH, "timestamp (UTC)") + " ";
header += makeCenteredString(HASH_MAX_WIDTH, "hash") + " ";
header += makeCenteredString(TOTAL_AMOUNT_MAX_WIDTH, "total amount") + " ";
header += makeCenteredString(FEE_MAX_WIDTH, "fee") + " ";
header += makeCenteredString(BLOCK_MAX_WIDTH, "block") + " ";
header += makeCenteredString(UNLOCK_TIME_MAX_WIDTH, "unlock time");
logger(INFO) << header;
logger(INFO) << std::string(header.size(), '-');
}
void printListTransfersItem(LoggerRef& logger, const WalletLegacyTransaction& txInfo, IWalletLegacy& wallet, const Currency& currency) {
std::vector<uint8_t> extraVec = Common::asBinaryArray(txInfo.extra);
Crypto::Hash paymentId;
std::string paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != NULL_HASH ? Common::podToHex(paymentId) : "");
char timeString[TIMESTAMP_MAX_WIDTH + 1];
time_t timestamp = static_cast<time_t>(txInfo.timestamp);
if (std::strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", std::gmtime(&timestamp)) == 0) {
throw std::runtime_error("time buffer is too small");
}
std::string rowColor = txInfo.totalAmount < 0 ? MAGENTA : GREEN;
logger(INFO, rowColor)
<< std::setw(TIMESTAMP_MAX_WIDTH) << timeString
<< " " << std::setw(HASH_MAX_WIDTH) << Common::podToHex(txInfo.hash)
<< " " << std::setw(TOTAL_AMOUNT_MAX_WIDTH) << currency.formatAmount(txInfo.totalAmount)
<< " " << std::setw(FEE_MAX_WIDTH) << currency.formatAmount(txInfo.fee)
<< " " << std::setw(BLOCK_MAX_WIDTH) << txInfo.blockHeight
<< " " << std::setw(UNLOCK_TIME_MAX_WIDTH) << txInfo.unlockTime;
if (!paymentIdStr.empty()) {
logger(INFO, rowColor) << "payment ID: " << paymentIdStr;
}
if (txInfo.totalAmount < 0) {
if (txInfo.transferCount > 0) {
logger(INFO, rowColor) << "transfers:";
for (TransferId id = txInfo.firstTransferId; id < txInfo.firstTransferId + txInfo.transferCount; ++id) {
WalletLegacyTransfer tr;
wallet.getTransfer(id, tr);
logger(INFO, rowColor) << tr.address << " " << std::setw(TOTAL_AMOUNT_MAX_WIDTH) << currency.formatAmount(tr.amount);
}
}
}
logger(INFO, rowColor) << " "; //just to make logger print one endline
}
}
std::string simple_wallet::get_commands_str() std::string simple_wallet::get_commands_str()
{ {
@ -795,6 +863,8 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
} }
bool simple_wallet::listTransfers(const std::vector<std::string>& args) { bool simple_wallet::listTransfers(const std::vector<std::string>& args) {
bool haveTransfers = false;
size_t transactionsCount = m_wallet->getTransactionCount(); size_t transactionsCount = m_wallet->getTransactionCount();
for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) {
WalletLegacyTransaction txInfo; WalletLegacyTransaction txInfo;
@ -803,37 +873,18 @@ bool simple_wallet::listTransfers(const std::vector<std::string>& args) {
continue; continue;
} }
std::string paymentIdStr = ""; if (!haveTransfers) {
std::vector<uint8_t> extraVec; printListTransfersHeader(logger);
extraVec.reserve(txInfo.extra.size()); haveTransfers = true;
std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); });
Crypto::Hash paymentId;
paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != NULL_HASH ? Common::podToHex(paymentId) : "");
std::string address = "-";
if (txInfo.totalAmount < 0) {
if (txInfo.transferCount > 0)
{
WalletLegacyTransfer tr;
m_wallet->getTransfer(txInfo.firstTransferId, tr);
address = tr.address;
}
} }
logger(INFO, txInfo.totalAmount < 0 ? MAGENTA : GREEN) printListTransfersItem(logger, txInfo, *m_wallet, m_currency);
<< txInfo.timestamp }
<< ", " << (txInfo.totalAmount < 0 ? "OUTPUT" : "INPUT")
<< ", " << Common::podToHex(txInfo.hash) if (!haveTransfers) {
<< ", " << (txInfo.totalAmount < 0 ? "-" : "") << m_currency.formatAmount(std::abs(txInfo.totalAmount)) success_msg_writer() << "No transfers";
<< ", " << m_currency.formatAmount(txInfo.fee)
<< ", " << (paymentIdStr.empty() ? std::string("-") : paymentIdStr)
<< ", " << address
<< ", " << txInfo.blockHeight
<< ", " << txInfo.unlockTime;
} }
if (transactionsCount == 0) success_msg_writer() << "No transfers";
return true; return true;
} }

View file

@ -162,7 +162,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint32_t startH
assert(blocks); assert(blocks);
struct Tx { struct Tx {
BlockInfo blockInfo; TransactionBlockInfo blockInfo;
const ITransactionReader* tx; const ITransactionReader* tx;
}; };
@ -193,7 +193,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint32_t startH
continue; continue;
} }
BlockInfo blockInfo; TransactionBlockInfo blockInfo;
blockInfo.height = startHeight + i; blockInfo.height = startHeight + i;
blockInfo.timestamp = block->timestamp; blockInfo.timestamp = block->timestamp;
blockInfo.transactionIndex = 0; // position in block blockInfo.transactionIndex = 0; // position in block
@ -282,7 +282,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint32_t startH
} }
std::error_code TransfersConsumer::onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) { std::error_code TransfersConsumer::onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) {
BlockInfo unconfirmedBlockInfo; TransactionBlockInfo unconfirmedBlockInfo;
unconfirmedBlockInfo.timestamp = 0; unconfirmedBlockInfo.timestamp = 0;
unconfirmedBlockInfo.height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; unconfirmedBlockInfo.height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT;
std::error_code processingError; std::error_code processingError;
@ -325,7 +325,7 @@ void TransfersConsumer::getKnownPoolTxIds(std::vector<Hash>& ids) {
std::error_code createTransfers( std::error_code createTransfers(
const AccountKeys& account, const AccountKeys& account,
const BlockInfo& blockInfo, const TransactionBlockInfo& blockInfo,
const ITransactionReader& tx, const ITransactionReader& tx,
const std::vector<uint32_t>& outputs, const std::vector<uint32_t>& outputs,
const std::vector<uint32_t>& globalIdxs, const std::vector<uint32_t>& globalIdxs,
@ -388,7 +388,7 @@ std::error_code createTransfers(
return std::error_code(); return std::error_code();
} }
std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info) { std::error_code TransfersConsumer::preprocessOutputs(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info) {
std::unordered_map<PublicKey, std::vector<uint32_t>> outputs; std::unordered_map<PublicKey, std::vector<uint32_t>> outputs;
findMyOutputs(tx, m_viewSecret, m_spendKeys, outputs); findMyOutputs(tx, m_viewSecret, m_spendKeys, outputs);
@ -419,7 +419,7 @@ std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo,
return std::error_code(); return std::error_code();
} }
std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx) { std::error_code TransfersConsumer::processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx) {
PreprocessInfo info; PreprocessInfo info;
auto ec = preprocessOutputs(blockInfo, tx, info); auto ec = preprocessOutputs(blockInfo, tx, info);
if (ec) { if (ec) {
@ -430,7 +430,7 @@ std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo
} }
std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) { std::error_code TransfersConsumer::processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) {
std::error_code errorCode; std::error_code errorCode;
std::vector<TransactionOutputInformationIn> emptyOutputs; std::vector<TransactionOutputInformationIn> emptyOutputs;
for (auto& kv : m_subscriptions) { for (auto& kv : m_subscriptions) {
@ -447,7 +447,7 @@ std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo
std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, std::error_code TransfersConsumer::processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub,
const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers, const std::vector<uint32_t>& globalIdxs) { const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers, const std::vector<uint32_t>& globalIdxs) {
if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) {

View file

@ -65,10 +65,10 @@ private:
std::vector<uint32_t> globalIdxs; std::vector<uint32_t> globalIdxs;
}; };
std::error_code preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info); std::error_code preprocessOutputs(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info);
std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx); std::error_code processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx);
std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info); std::error_code processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info);
std::error_code processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, std::error_code processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& outputs, const std::vector<uint32_t>& globalIdxs); const std::vector<TransactionOutputInformationIn>& outputs, const std::vector<uint32_t>& globalIdxs);
std::error_code getGlobalIndices(const Crypto::Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices); std::error_code getGlobalIndices(const Crypto::Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices);

View file

@ -172,7 +172,7 @@ TransfersContainer::TransfersContainer(const Currency& currency, size_t transact
m_transactionSpendableAge(transactionSpendableAge) { m_transactionSpendableAge(transactionSpendableAge) {
} }
bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransactionReader& tx, bool TransfersContainer::addTransaction(const TransactionBlockInfo& block, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfers) { const std::vector<TransactionOutputInformationIn>& transfers) {
std::unique_lock<std::mutex> lock(m_mutex); std::unique_lock<std::mutex> lock(m_mutex);
@ -201,7 +201,7 @@ bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti
/** /**
* \pre m_mutex is locked. * \pre m_mutex is locked.
*/ */
void TransfersContainer::addTransaction(const BlockInfo& block, const ITransactionReader& tx) { void TransfersContainer::addTransaction(const TransactionBlockInfo& block, const ITransactionReader& tx) {
auto txHash = tx.getTransactionHash(); auto txHash = tx.getTransactionHash();
TransactionInformation txInfo; TransactionInformation txInfo;
@ -226,7 +226,7 @@ void TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti
/** /**
* \pre m_mutex is locked. * \pre m_mutex is locked.
*/ */
bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITransactionReader& tx, bool TransfersContainer::addTransactionOutputs(const TransactionBlockInfo& block, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfers) { const std::vector<TransactionOutputInformationIn>& transfers) {
bool outputsAdded = false; bool outputsAdded = false;
@ -281,7 +281,7 @@ bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITr
/** /**
* \pre m_mutex is locked. * \pre m_mutex is locked.
*/ */
bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITransactionReader& tx) { bool TransfersContainer::addTransactionInputs(const TransactionBlockInfo& block, const ITransactionReader& tx) {
bool inputsAdded = false; bool inputsAdded = false;
for (size_t i = 0; i < tx.getInputCount(); ++i) { for (size_t i = 0; i < tx.getInputCount(); ++i) {
@ -365,7 +365,7 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas
} }
} }
bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, bool TransfersContainer::markTransactionConfirmed(const TransactionBlockInfo& block, const Hash& transactionHash,
const std::vector<uint32_t>& globalIndices) { const std::vector<uint32_t>& globalIndices) {
if (block.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { if (block.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) {
throw std::invalid_argument("Block height equals WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT"); throw std::invalid_argument("Block height equals WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT");
@ -479,7 +479,7 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash)
/** /**
* \pre m_mutex is locked. * \pre m_mutex is locked.
*/ */
void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, void TransfersContainer::copyToSpent(const TransactionBlockInfo& block, const ITransactionReader& tx, size_t inputIndex,
const TransactionOutputInformationEx& output) { const TransactionOutputInformationEx& output) {
assert(output.blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); assert(output.blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT);
assert(output.globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); assert(output.globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX);

View file

@ -107,7 +107,7 @@ struct TransactionOutputInformationEx : public TransactionOutputInformationIn {
}; };
struct BlockInfo { struct TransactionBlockInfo {
uint32_t height; uint32_t height;
uint64_t timestamp; uint64_t timestamp;
uint32_t transactionIndex; uint32_t transactionIndex;
@ -120,7 +120,7 @@ struct BlockInfo {
}; };
struct SpentTransactionOutput : TransactionOutputInformationEx { struct SpentTransactionOutput : TransactionOutputInformationEx {
BlockInfo spendingBlock; TransactionBlockInfo spendingBlock;
Crypto::Hash spendingTransactionHash; Crypto::Hash spendingTransactionHash;
uint32_t inputInTransaction; uint32_t inputInTransaction;
@ -153,9 +153,9 @@ public:
TransfersContainer(const CryptoNote::Currency& currency, size_t transactionSpendableAge); TransfersContainer(const CryptoNote::Currency& currency, size_t transactionSpendableAge);
bool addTransaction(const BlockInfo& block, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers); bool addTransaction(const TransactionBlockInfo& block, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers);
bool deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash); bool deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash);
bool markTransactionConfirmed(const BlockInfo& block, const Crypto::Hash& transactionHash, const std::vector<uint32_t>& globalIndices); bool markTransactionConfirmed(const TransactionBlockInfo& block, const Crypto::Hash& transactionHash, const std::vector<uint32_t>& globalIndices);
std::vector<Crypto::Hash> detach(uint32_t height); std::vector<Crypto::Hash> detach(uint32_t height);
bool advanceHeight(uint32_t height); bool advanceHeight(uint32_t height);
@ -258,17 +258,17 @@ private:
> SpentTransfersMultiIndex; > SpentTransfersMultiIndex;
private: private:
void addTransaction(const BlockInfo& block, const ITransactionReader& tx); void addTransaction(const TransactionBlockInfo& block, const ITransactionReader& tx);
bool addTransactionOutputs(const BlockInfo& block, const ITransactionReader& tx, bool addTransactionOutputs(const TransactionBlockInfo& block, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfers); const std::vector<TransactionOutputInformationIn>& transfers);
bool addTransactionInputs(const BlockInfo& block, const ITransactionReader& tx); bool addTransactionInputs(const TransactionBlockInfo& block, const ITransactionReader& tx);
void deleteTransactionTransfers(const Crypto::Hash& transactionHash); void deleteTransactionTransfers(const Crypto::Hash& transactionHash);
bool isSpendTimeUnlocked(uint64_t unlockTime) const; bool isSpendTimeUnlocked(uint64_t unlockTime) const;
bool isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const; bool isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const;
static bool isIncluded(TransactionTypes::OutputType type, uint32_t state, uint32_t flags); static bool isIncluded(TransactionTypes::OutputType type, uint32_t state, uint32_t flags);
void updateTransfersVisibility(const Crypto::KeyImage& keyImage); void updateTransfersVisibility(const Crypto::KeyImage& keyImage);
void copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output); void copyToSpent(const TransactionBlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output);
private: private:
TransactionMultiIndex m_transactions; TransactionMultiIndex m_transactions;

View file

@ -52,7 +52,7 @@ const AccountKeys& TransfersSubscription::getKeys() const {
return subscription.keys; return subscription.keys;
} }
void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, void TransfersSubscription::addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfersList) { const std::vector<TransactionOutputInformationIn>& transfersList) {
bool added = transfers.addTransaction(blockInfo, tx, transfersList); bool added = transfers.addTransaction(blockInfo, tx, transfersList);
if (added) { if (added) {
@ -73,7 +73,7 @@ void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transaction
m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash); m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash);
} }
void TransfersSubscription::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, void TransfersSubscription::markTransactionConfirmed(const TransactionBlockInfo& block, const Hash& transactionHash,
const std::vector<uint32_t>& globalIndices) { const std::vector<uint32_t>& globalIndices) {
transfers.markTransactionConfirmed(block, transactionHash, globalIndices); transfers.markTransactionConfirmed(block, transactionHash, globalIndices);
m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, transactionHash); m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, transactionHash);

View file

@ -32,11 +32,11 @@ public:
void onError(const std::error_code& ec, uint32_t height); void onError(const std::error_code& ec, uint32_t height);
bool advanceHeight(uint32_t height); bool advanceHeight(uint32_t height);
const AccountKeys& getKeys() const; const AccountKeys& getKeys() const;
void addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, void addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfers); const std::vector<TransactionOutputInformationIn>& transfers);
void deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash); void deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash);
void markTransactionConfirmed(const BlockInfo& block, const Crypto::Hash& transactionHash, const std::vector<uint32_t>& globalIndices); void markTransactionConfirmed(const TransactionBlockInfo& block, const Crypto::Hash& transactionHash, const std::vector<uint32_t>& globalIndices);
// ITransfersSubscription // ITransfersSubscription
virtual AccountPublicAddress getAddress() override; virtual AccountPublicAddress getAddress() override;

View file

@ -40,7 +40,8 @@ enum WalletErrorCodes {
TX_CANCELLED, TX_CANCELLED,
OPERATION_CANCELLED, OPERATION_CANCELLED,
TX_TRANSFER_IMPOSSIBLE, TX_TRANSFER_IMPOSSIBLE,
WRONG_VERSION WRONG_VERSION,
FEE_TOO_SMALL
}; };
// custom category: // custom category:
@ -72,7 +73,8 @@ public:
case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later"; case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later";
case OPERATION_CANCELLED: return "The operation you've requested has been cancelled"; case OPERATION_CANCELLED: return "The operation you've requested has been cancelled";
case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible"; case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible";
case WRONG_VERSION: return "Wrong version"; case WRONG_VERSION: return "Wrong version";
case FEE_TOO_SMALL: return "Transaction fee is too small";
default: return "Unknown error"; default: return "Unknown error";
} }
} }

View file

@ -539,6 +539,10 @@ size_t WalletGreen::doTransfer(std::vector<WalletOuts>&& wallets,
throw std::system_error(make_error_code(error::ZERO_DESTINATION)); throw std::system_error(make_error_code(error::ZERO_DESTINATION));
} }
if (fee < m_currency.minimumFee()) {
throw std::system_error(make_error_code(error::FEE_TOO_SMALL));
}
validateAddresses(destinations, m_currency); validateAddresses(destinations, m_currency);
uint64_t neededMoney = countNeededMoney(destinations, fee); uint64_t neededMoney = countNeededMoney(destinations, fee);

View file

@ -137,14 +137,15 @@ void WalletLegacySerializer::deserialize(std::istream& stream, const std::string
MemoryInputStream decryptedStream(plain.data(), plain.size()); MemoryInputStream decryptedStream(plain.data(), plain.size());
CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream); CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream);
try loadKeys(serializer);
{ throwIfKeysMissmatch(account.getAccountKeys().viewSecretKey, account.getAccountKeys().address.viewPublicKey);
loadKeys(serializer);
throwIfKeysMissmatch(account.getAccountKeys().viewSecretKey, account.getAccountKeys().address.viewPublicKey); if (account.getAccountKeys().spendSecretKey != NULL_SECRET_KEY) {
throwIfKeysMissmatch(account.getAccountKeys().spendSecretKey, account.getAccountKeys().address.spendPublicKey); throwIfKeysMissmatch(account.getAccountKeys().spendSecretKey, account.getAccountKeys().address.spendPublicKey);
} } else {
catch (std::exception&) { if (!Crypto::check_key(account.getAccountKeys().address.spendPublicKey)) {
throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD));
}
} }
bool detailsSaved; bool detailsSaved;

View file

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@" #define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0.6.1" #define PROJECT_VERSION "1.0.7"
#define PROJECT_VERSION_BUILD_NO "550" #define PROJECT_VERSION_BUILD_NO "564"
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"

View file

@ -0,0 +1,115 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "gtest/gtest.h"
#include "crypto/crypto.h"
#include "CryptoNoteCore/Currency.h"
#include "CryptoNoteCore/TransactionApi.h"
#include "Logging/ConsoleLogger.h"
#include "TransactionApiHelpers.h"
using namespace CryptoNote;
namespace {
const size_t TEST_FUSION_TX_MAX_SIZE = 6000;
const size_t TEST_FUSION_TX_MIN_INPUT_COUNT = 6;
const size_t TEST_FUSION_TX_MIN_IN_OUT_COUNT_RATIO = 3;
const uint64_t TEST_DUST_THRESHOLD = UINT64_C(1000000);
const uint64_t TEST_AMOUNT = 370 * TEST_DUST_THRESHOLD;
class Currency_isFusionTransactionTest : public ::testing::Test {
public:
Currency_isFusionTransactionTest() :
m_currency(CurrencyBuilder(m_logger).
defaultDustThreshold(TEST_DUST_THRESHOLD).
fusionTxMaxSize(TEST_FUSION_TX_MAX_SIZE).
fusionTxMinInputCount(TEST_FUSION_TX_MIN_INPUT_COUNT).
fusionTxMinInOutCountRatio(TEST_FUSION_TX_MIN_IN_OUT_COUNT_RATIO).
currency()) {
}
protected:
Logging::ConsoleLogger m_logger;
Currency m_currency;
};
}
TEST_F(Currency_isFusionTransactionTest, succeedsOnFusionTransaction) {
auto tx = FusionTransactionBuilder(m_currency, TEST_AMOUNT).buildTx();
ASSERT_TRUE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, succeedsIfFusionTransactionSizeEqMaxSize) {
FusionTransactionBuilder builder(m_currency, TEST_AMOUNT);
auto tx = builder.createFusionTransactionBySize(m_currency.fusionTxMaxSize());
ASSERT_EQ(m_currency.fusionTxMaxSize(), getObjectBinarySize(tx));
ASSERT_TRUE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, failsIfFusionTransactionSizeGreaterThanMaxSize) {
FusionTransactionBuilder builder(m_currency, TEST_AMOUNT);
auto tx = builder.createFusionTransactionBySize(m_currency.fusionTxMaxSize() + 1);
ASSERT_EQ(m_currency.fusionTxMaxSize() + 1, getObjectBinarySize(tx));
ASSERT_FALSE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, failsIfTransactionInputsCountIsNotEnought) {
FusionTransactionBuilder builder(m_currency, TEST_AMOUNT);
builder.setInputCount(m_currency.fusionTxMinInputCount() - 1);
auto tx = builder.buildTx();
ASSERT_EQ(m_currency.fusionTxMinInputCount() - 1, tx.inputs.size());
ASSERT_FALSE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, failsIfTransactionInputOutputCountRatioIsLessThenNecessary) {
FusionTransactionBuilder builder(m_currency, 3710 * m_currency.defaultDustThreshold());
auto tx = builder.buildTx();
ASSERT_EQ(3, tx.outputs.size());
ASSERT_GT(tx.outputs.size() * m_currency.fusionTxMinInOutCountRatio(), tx.inputs.size());
ASSERT_FALSE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, failsIfTransactionHasNotExponentialOutput) {
FusionTransactionBuilder builder(m_currency, TEST_AMOUNT);
builder.setFirstOutput(TEST_AMOUNT);
auto tx = builder.buildTx();
ASSERT_EQ(1, tx.outputs.size());
ASSERT_FALSE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, failsIfTransactionHasOutputsWithTheSameExponent) {
FusionTransactionBuilder builder(m_currency, 130 * m_currency.defaultDustThreshold());
builder.setFirstOutput(70 * m_currency.defaultDustThreshold());
auto tx = builder.buildTx();
ASSERT_EQ(2, tx.outputs.size());
ASSERT_FALSE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, failsIfTransactionHasDustOutput) {
FusionTransactionBuilder builder(m_currency, 37 * m_currency.defaultDustThreshold() / 10);
auto tx = builder.buildTx();
ASSERT_FALSE(m_currency.isFusionTransaction(tx));
}
TEST_F(Currency_isFusionTransactionTest, failsIfTransactionFeeIsNotZero) {
FusionTransactionBuilder builder(m_currency, 370 * m_currency.defaultDustThreshold());
builder.setFee(70 * m_currency.defaultDustThreshold());
auto tx = builder.buildTx();
ASSERT_FALSE(m_currency.isFusionTransaction(tx));
}

View file

@ -50,8 +50,8 @@ namespace {
protected: protected:
BlockInfo blockInfo(uint32_t height) const { TransactionBlockInfo blockInfo(uint32_t height) const {
return BlockInfo{ height, 1000000 }; return TransactionBlockInfo{ height, 1000000 };
} }
std::unique_ptr<ITransactionReader> addTransaction(uint32_t height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, std::unique_ptr<ITransactionReader> addTransaction(uint32_t height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT,
@ -155,7 +155,7 @@ TEST_F(TransfersContainer_addTransaction, addingTransactionTwiceCausesException)
TEST_F(TransfersContainer_addTransaction, addingTwoIdenticalUnconfirmedMultisignatureOutputsDoesNotCauseException) { TEST_F(TransfersContainer_addTransaction, addingTwoIdenticalUnconfirmedMultisignatureOutputsDoesNotCauseException) {
CryptoNote::BlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder tx1; TestTransactionBuilder tx1;
tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
@ -182,7 +182,7 @@ TEST_F(TransfersContainer_addTransaction, addingTwoIdenticalUnconfirmedMultisign
} }
TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIdenticalAnotherUnspentOuputCausesException) { TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIdenticalAnotherUnspentOuputCausesException) {
CryptoNote::BlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 };
TestTransactionBuilder tx1; TestTransactionBuilder tx1;
tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
@ -209,7 +209,7 @@ TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIde
} }
TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIdenticalAnotherSpentOuputCausesException) { TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIdenticalAnotherSpentOuputCausesException) {
CryptoNote::BlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 };
TestTransactionBuilder tx1; TestTransactionBuilder tx1;
tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
auto outInfo1 = tx1.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); auto outInfo1 = tx1.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX);
@ -217,14 +217,14 @@ TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIde
// Spend output // Spend output
{ {
CryptoNote::BlockInfo blockInfo2{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo2{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder tx2; TestTransactionBuilder tx2;
tx2.addTestMultisignatureInput(TEST_OUTPUT_AMOUNT, outInfo1); tx2.addTestMultisignatureInput(TEST_OUTPUT_AMOUNT, outInfo1);
ASSERT_TRUE(container.addTransaction(blockInfo2, *tx2.build(), std::vector<TransactionOutputInformationIn>())); ASSERT_TRUE(container.addTransaction(blockInfo2, *tx2.build(), std::vector<TransactionOutputInformationIn>()));
} }
{ {
CryptoNote::BlockInfo blockInfo3{ TEST_BLOCK_HEIGHT + 3, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo3{ TEST_BLOCK_HEIGHT + 3, 1000000 };
TestTransactionBuilder tx3; TestTransactionBuilder tx3;
tx3.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx3.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
auto outInfo3 = tx3.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); auto outInfo3 = tx3.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX);
@ -240,7 +240,7 @@ TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIde
} }
TEST_F(TransfersContainer_addTransaction, addingConfirmedBlockAndUnconfirmedOutputCausesException) { TEST_F(TransfersContainer_addTransaction, addingConfirmedBlockAndUnconfirmedOutputCausesException) {
CryptoNote::BlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 };
TestTransactionBuilder tx; TestTransactionBuilder tx;
tx.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
@ -252,7 +252,7 @@ TEST_F(TransfersContainer_addTransaction, addingConfirmedBlockAndUnconfirmedOutp
} }
TEST_F(TransfersContainer_addTransaction, addingUnconfirmedBlockAndConfirmedOutputCausesException) { TEST_F(TransfersContainer_addTransaction, addingUnconfirmedBlockAndConfirmedOutputCausesException) {
CryptoNote::BlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder tx; TestTransactionBuilder tx;
tx.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
@ -264,7 +264,7 @@ TEST_F(TransfersContainer_addTransaction, addingUnconfirmedBlockAndConfirmedOutp
} }
TEST_F(TransfersContainer_addTransaction, handlesAddingUnconfirmedOutputToKey) { TEST_F(TransfersContainer_addTransaction, handlesAddingUnconfirmedOutputToKey) {
CryptoNote::BlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder txbuilder; TestTransactionBuilder txbuilder;
txbuilder.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); txbuilder.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
@ -306,7 +306,7 @@ TEST_F(TransfersContainer_addTransaction, handlesAddingUnconfirmedOutputToKey) {
} }
TEST_F(TransfersContainer_addTransaction, handlesAddingConfirmedOutputToKey) { TEST_F(TransfersContainer_addTransaction, handlesAddingConfirmedOutputToKey) {
CryptoNote::BlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 };
TestTransactionBuilder txbuilder; TestTransactionBuilder txbuilder;
txbuilder.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); txbuilder.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
@ -350,7 +350,7 @@ TEST_F(TransfersContainer_addTransaction, handlesAddingConfirmedOutputToKey) {
} }
TEST_F(TransfersContainer_addTransaction, addingEmptyTransactionOuptutsDoesNotChaingeContainer) { TEST_F(TransfersContainer_addTransaction, addingEmptyTransactionOuptutsDoesNotChaingeContainer) {
CryptoNote::BlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder builder; TestTransactionBuilder builder;
builder.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); builder.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
@ -559,7 +559,7 @@ TEST_F(TransfersContainer_deleteUnconfirmedTransaction, deleteUnconfirmedSpendin
auto tx = spendingTx.build(); auto tx = spendingTx.build();
{ {
CryptoNote::BlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
ASSERT_TRUE(container.addTransaction(blockInfo, *tx, {})); ASSERT_TRUE(container.addTransaction(blockInfo, *tx, {}));
} }
@ -669,20 +669,20 @@ TEST_F(TransfersContainer_markTransactionConfirmed, confirmationTxWithNoOutputs)
TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutputIdenticalAnotherUnspentOuputCausesException) { TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutputIdenticalAnotherUnspentOuputCausesException) {
// Add tx1 // Add tx1
CryptoNote::BlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 };
TestTransactionBuilder tx1; TestTransactionBuilder tx1;
tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
auto outInfo1 = tx1.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); auto outInfo1 = tx1.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX);
ASSERT_TRUE(container.addTransaction(blockInfo1, *tx1.build(), {outInfo1})); ASSERT_TRUE(container.addTransaction(blockInfo1, *tx1.build(), {outInfo1}));
// Spend output, add tx2 // Spend output, add tx2
CryptoNote::BlockInfo blockInfo2{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo2{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder tx2; TestTransactionBuilder tx2;
tx2.addTestMultisignatureInput(TEST_OUTPUT_AMOUNT, outInfo1); tx2.addTestMultisignatureInput(TEST_OUTPUT_AMOUNT, outInfo1);
ASSERT_TRUE(container.addTransaction(blockInfo2, *tx2.build(), {})); ASSERT_TRUE(container.addTransaction(blockInfo2, *tx2.build(), {}));
// Add tx3 // Add tx3
CryptoNote::BlockInfo blockInfo3{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo3{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder tx3; TestTransactionBuilder tx3;
tx3.addTestInput(TEST_OUTPUT_AMOUNT + 1, account); tx3.addTestInput(TEST_OUTPUT_AMOUNT + 1, account);
auto outInfo3 = tx3.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); auto outInfo3 = tx3.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX);
@ -703,7 +703,7 @@ TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutp
} }
TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutputIdenticalAnotherSpentOuputCausesException) { TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutputIdenticalAnotherSpentOuputCausesException) {
CryptoNote::BlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 };
TestTransactionBuilder tx1; TestTransactionBuilder tx1;
tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1); tx1.addTestInput(TEST_OUTPUT_AMOUNT + 1);
auto outInfo1 = tx1.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); auto outInfo1 = tx1.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX);
@ -711,7 +711,7 @@ TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutp
container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE);
CryptoNote::BlockInfo blockInfo2{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; CryptoNote::TransactionBlockInfo blockInfo2{ WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 };
TestTransactionBuilder tx2; TestTransactionBuilder tx2;
tx2.addTestInput(TEST_OUTPUT_AMOUNT + 1); tx2.addTestInput(TEST_OUTPUT_AMOUNT + 1);
auto outInfo2 = tx2.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); auto outInfo2 = tx2.addTestMultisignatureOutput(TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX);

View file

@ -62,8 +62,8 @@ namespace {
return getOutputs(flags).size(); return getOutputs(flags).size();
} }
BlockInfo blockInfo(uint32_t height) const { TransactionBlockInfo blockInfo(uint32_t height) const {
return BlockInfo{ height, 1000000 }; return TransactionBlockInfo{ height, 1000000 };
} }
TestTransactionBuilder createTransactionWithFixedKey() { TestTransactionBuilder createTransactionWithFixedKey() {
@ -82,7 +82,7 @@ namespace {
auto outInfo = tx.addTestKeyOutput(amount, outputIndex, account); auto outInfo = tx.addTestKeyOutput(amount, outputIndex, account);
auto finalTx = tx.build(); auto finalTx = tx.build();
EXPECT_TRUE(container.addTransaction(BlockInfo{ height, 1000000, txIndex }, *finalTx, { outInfo })); EXPECT_TRUE(container.addTransaction(TransactionBlockInfo{ height, 1000000, txIndex }, *finalTx, { outInfo }));
return finalTx; return finalTx;
} }

View file

@ -58,7 +58,7 @@ public:
auto tx = std::shared_ptr<ITransactionReader>(b1.build().release()); auto tx = std::shared_ptr<ITransactionReader>(b1.build().release());
std::vector<TransactionOutputInformationIn> outputs = { outInfo }; std::vector<TransactionOutputInformationIn> outputs = { outInfo };
sub.addTransaction(BlockInfo{ height, 100000 }, *tx, outputs); sub.addTransaction(TransactionBlockInfo{ height, 100000 }, *tx, outputs);
return tx; return tx;
} }
@ -87,7 +87,7 @@ TEST_F(TransfersSubscriptionTest, addTransaction) {
// this transaction should not be added, so no notification // this transaction should not be added, so no notification
auto tx = createTransaction(); auto tx = createTransaction();
addTestInput(*tx, 20000); addTestInput(*tx, 20000);
sub.addTransaction(BlockInfo{ 2, 100000 }, *tx, {}); sub.addTransaction(TransactionBlockInfo{ 2, 100000 }, *tx, {});
ASSERT_EQ(2, sub.getContainer().transactionsCount()); ASSERT_EQ(2, sub.getContainer().transactionsCount());
ASSERT_EQ(2, observer.updated.size()); ASSERT_EQ(2, observer.updated.size());
@ -142,7 +142,7 @@ TEST_F(TransfersSubscriptionTest, markTransactionConfirmed) {
ASSERT_EQ(1, sub.getContainer().transactionsCount()); ASSERT_EQ(1, sub.getContainer().transactionsCount());
ASSERT_EQ(1, observer.updated.size()); // added ASSERT_EQ(1, observer.updated.size()); // added
sub.markTransactionConfirmed(BlockInfo{ 10, 100000 }, txHash, { 1 }); sub.markTransactionConfirmed(TransactionBlockInfo{ 10, 100000 }, txHash, { 1 });
ASSERT_EQ(2, observer.updated.size()); // added + updated ASSERT_EQ(2, observer.updated.size()); // added + updated
ASSERT_EQ(txHash, observer.updated[0]); ASSERT_EQ(txHash, observer.updated[0]);

View file

@ -107,7 +107,8 @@ public:
currency(CryptoNote::CurrencyBuilder(logger).currency()), currency(CryptoNote::CurrencyBuilder(logger).currency()),
generator(currency), generator(currency),
node(generator), node(generator),
alice(dispatcher, currency, node) alice(dispatcher, currency, node),
FEE(currency.minimumFee())
{ } { }
virtual void SetUp() override; virtual void SetUp() override;
@ -161,7 +162,7 @@ protected:
std::string aliceAddress; std::string aliceAddress;
const uint64_t SENT = 1122334455; const uint64_t SENT = 1122334455;
const uint64_t FEE = 10000; const uint64_t FEE;
const std::string RANDOM_ADDRESS = "2634US2FAz86jZT73YmM8u5GPCknT2Wxj8bUCKivYKpThFhF2xsjygMGxbxZzM42zXhKUhym6Yy6qHHgkuWtruqiGkDpX6m"; const std::string RANDOM_ADDRESS = "2634US2FAz86jZT73YmM8u5GPCknT2Wxj8bUCKivYKpThFhF2xsjygMGxbxZzM42zXhKUhym6Yy6qHHgkuWtruqiGkDpX6m";
}; };
@ -391,14 +392,13 @@ TEST_F(WalletApi, transferFromTwoAddresses) {
bob.initialize("pass2"); bob.initialize("pass2");
std::string bobAddress = bob.createAddress(); std::string bobAddress = bob.createAddress();
const uint64_t fee = 10000; const uint64_t sent = 2 * TEST_BLOCK_REWARD - 10 * FEE;
const uint64_t sent = 2 * TEST_BLOCK_REWARD - 10 * fee;
auto bobPrev = bob.getPendingBalance(); auto bobPrev = bob.getPendingBalance();
auto alicePendingPrev = alice.getPendingBalance(); auto alicePendingPrev = alice.getPendingBalance();
auto aliceActualPrev = alice.getActualBalance(); auto aliceActualPrev = alice.getActualBalance();
sendMoney(bobAddress, sent, fee); sendMoney(bobAddress, sent, FEE);
node.updateObservers(); node.updateObservers();
@ -409,7 +409,7 @@ TEST_F(WalletApi, transferFromTwoAddresses) {
ASSERT_EQ(0, bob.getActualBalance()); ASSERT_EQ(0, bob.getActualBalance());
ASSERT_EQ(sent, bob.getPendingBalance()); ASSERT_EQ(sent, bob.getPendingBalance());
ASSERT_EQ(2 * TEST_BLOCK_REWARD - sent - fee, alice.getActualBalance() + alice.getPendingBalance()); ASSERT_EQ(2 * TEST_BLOCK_REWARD - sent - FEE, alice.getActualBalance() + alice.getPendingBalance());
bob.shutdown(); bob.shutdown();
wait(100); wait(100);
@ -1039,11 +1039,11 @@ TEST_F(WalletApi, hybridTxTransfer) {
} }
TEST_F(WalletApi, doubleSpendJustSentOut) { TEST_F(WalletApi, doubleSpendJustSentOut) {
generator.getSingleOutputTransaction(parseAddress(aliceAddress), 1000000); generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE);
unlockMoney(); unlockMoney();
sendMoney(RANDOM_ADDRESS, 1000, 100); sendMoney(RANDOM_ADDRESS, SENT, FEE);
ASSERT_ANY_THROW(sendMoney(RANDOM_ADDRESS, 1000, 100)); ASSERT_ANY_THROW(sendMoney(RANDOM_ADDRESS, SENT, FEE));
} }
TEST_F(WalletApi, syncAfterLoad) { TEST_F(WalletApi, syncAfterLoad) {
@ -1139,3 +1139,9 @@ TEST_F(WalletApi, DISABLED_loadTest) {
wallet.shutdown(); wallet.shutdown();
wait(100); wait(100);
} }
TEST_F(WalletApi, transferSmallFeeTransactionThrows) {
generateAndUnlockMoney();
ASSERT_ANY_THROW(sendMoneyToRandomAddressFrom(alice.getAddress(0), SENT, currency.minimumFee() - 1));
}

View file

@ -1818,3 +1818,26 @@ TEST_F(WalletLegacyApi, outdatedUnconfirmedTransactionDeletedOnLoad) {
wallet.removeObserver(&walletObserver); wallet.removeObserver(&walletObserver);
wallet.shutdown(); wallet.shutdown();
} }
TEST_F(WalletLegacyApi, walletLoadsNullSpendSecretKey) {
CryptoNote::AccountKeys accountKeys;
Crypto::generate_keys(accountKeys.address.spendPublicKey, accountKeys.spendSecretKey);
Crypto::generate_keys(accountKeys.address.viewPublicKey, accountKeys.viewSecretKey);
accountKeys.spendSecretKey = CryptoNote::NULL_SECRET_KEY;
alice->initWithKeys(accountKeys, "pass");
WaitWalletSync(aliceWalletObserver.get());
std::stringstream data;
alice->save(data);
WaitWalletSave(aliceWalletObserver.get());
alice->shutdown();
alice->initAndLoad(data, "pass");
WaitWalletSync(aliceWalletObserver.get());
ASSERT_EQ(std::error_code(), aliceWalletObserver->loadResult);
alice->shutdown();
}

View file

@ -43,6 +43,10 @@ PublicKey TestTransactionBuilder::getTransactionPublicKey() const {
return tx->getTransactionPublicKey(); return tx->getTransactionPublicKey();
} }
void TestTransactionBuilder::appendExtra(const BinaryArray& extraData) {
tx->appendExtra(extraData);
}
void TestTransactionBuilder::setUnlockTime(uint64_t time) { void TestTransactionBuilder::setUnlockTime(uint64_t time) {
tx->setUnlockTime(time); tx->setUnlockTime(time);
} }
@ -133,7 +137,7 @@ size_t TestTransactionBuilder::addFakeMultisignatureInput(uint64_t amount, uint3
MultisignatureInput input; MultisignatureInput input;
input.amount = amount; input.amount = amount;
input.outputIndex = globalOutputIndex; input.outputIndex = globalOutputIndex;
input.signatureCount = signatureCount; input.signatureCount = static_cast<uint8_t>(signatureCount);
size_t idx = tx->addInput(input); size_t idx = tx->addInput(input);
std::vector<AccountBase> accs; std::vector<AccountBase> accs;
@ -221,7 +225,108 @@ std::unique_ptr<ITransactionReader> TestTransactionBuilder::build() {
return std::move(tx); return std::move(tx);
} }
Crypto::Hash TestTransactionBuilder::getTransactionHash() const { Crypto::Hash TestTransactionBuilder::getTransactionHash() const {
return transactionHash; return transactionHash;
} }
FusionTransactionBuilder::FusionTransactionBuilder(const Currency& currency, uint64_t amount) :
m_currency(currency),
m_amount(amount),
m_firstOutput(0),
m_fee(0),
m_extraSize(0),
m_inputCount(currency.fusionTxMinInputCount()) {
}
uint64_t FusionTransactionBuilder::getAmount() const {
return m_amount;
}
void FusionTransactionBuilder::setAmount(uint64_t val) {
m_amount = val;
}
uint64_t FusionTransactionBuilder::getFirstOutput() const {
return m_firstOutput;
}
void FusionTransactionBuilder::setFirstOutput(uint64_t val) {
m_firstOutput = val;
}
uint64_t FusionTransactionBuilder::getFee() const {
return m_fee;
}
void FusionTransactionBuilder::setFee(uint64_t val) {
m_fee = val;
}
size_t FusionTransactionBuilder::getExtraSize() const {
return m_extraSize;
}
void FusionTransactionBuilder::setExtraSize(size_t val) {
m_extraSize = val;
}
size_t FusionTransactionBuilder::getInputCount() const {
return m_inputCount;
}
void FusionTransactionBuilder::setInputCount(size_t val) {
m_inputCount = val;
}
std::unique_ptr<ITransactionReader> FusionTransactionBuilder::buildReader() const {
assert(m_inputCount > 0);
TestTransactionBuilder builder;
if (m_extraSize != 0) {
builder.appendExtra(BinaryArray(m_extraSize, 0));
}
builder.addTestInput(m_amount - (m_inputCount - 1) * m_currency.defaultDustThreshold());
for (size_t i = 0; i < m_inputCount - 1; ++i) {
builder.addTestInput(m_currency.defaultDustThreshold());
}
AccountPublicAddress address = generateAddress();
std::vector<uint64_t> outputAmounts;
assert(m_amount >= m_firstOutput + m_fee);
decomposeAmount(m_amount - m_firstOutput - m_fee, m_currency.defaultDustThreshold(), outputAmounts);
std::sort(outputAmounts.begin(), outputAmounts.end());
if (m_firstOutput != 0) {
builder.addOutput(m_firstOutput, address);
}
for (auto outAmount : outputAmounts) {
builder.addOutput(outAmount, address);
}
return builder.build();
}
Transaction FusionTransactionBuilder::buildTx() const {
return convertTx(*buildReader());
}
Transaction FusionTransactionBuilder::createFusionTransactionBySize(size_t targetSize) {
auto tx = buildReader();
size_t realSize = tx->getTransactionData().size();
if (realSize < targetSize) {
setExtraSize(targetSize - realSize);
tx = buildReader();
realSize = tx->getTransactionData().size();
if (realSize > targetSize) {
setExtraSize(getExtraSize() - 1);
tx = buildReader();
}
}
return convertTx(*tx);
}

View file

@ -116,59 +116,93 @@ namespace {
namespace CryptoNote { namespace CryptoNote {
class TestTransactionBuilder { class TestTransactionBuilder {
public: public:
TestTransactionBuilder(); TestTransactionBuilder();
TestTransactionBuilder(const BinaryArray& txTemplate, const Crypto::SecretKey& secretKey); TestTransactionBuilder(const BinaryArray& txTemplate, const Crypto::SecretKey& secretKey);
PublicKey getTransactionPublicKey() const; PublicKey getTransactionPublicKey() const;
void setUnlockTime(uint64_t time); void appendExtra(const BinaryArray& extraData);
void setUnlockTime(uint64_t time);
// inputs // inputs
size_t addTestInput(uint64_t amount, const AccountKeys& senderKeys = generateAccountKeys()); size_t addTestInput(uint64_t amount, const AccountKeys& senderKeys = generateAccountKeys());
size_t addTestInput(uint64_t amount, std::vector<uint32_t> gouts, const AccountKeys& senderKeys = generateAccountKeys()); size_t addTestInput(uint64_t amount, std::vector<uint32_t> gouts, const AccountKeys& senderKeys = generateAccountKeys());
void addTestMultisignatureInput(uint64_t amount, const TransactionOutputInformation& t); void addTestMultisignatureInput(uint64_t amount, const TransactionOutputInformation& t);
size_t addFakeMultisignatureInput(uint64_t amount, uint32_t globalOutputIndex, size_t signatureCount); size_t addFakeMultisignatureInput(uint64_t amount, uint32_t globalOutputIndex, size_t signatureCount);
void addInput(const AccountKeys& senderKeys, const TransactionOutputInformation& t); void addInput(const AccountKeys& senderKeys, const TransactionOutputInformation& t);
// outputs // outputs
TransactionOutputInformationIn addTestKeyOutput(uint64_t amount, uint32_t globalOutputIndex, const AccountKeys& senderKeys = generateAccountKeys()); TransactionOutputInformationIn addTestKeyOutput(uint64_t amount, uint32_t globalOutputIndex, const AccountKeys& senderKeys = generateAccountKeys());
TransactionOutputInformationIn addTestMultisignatureOutput(uint64_t amount, uint32_t globalOutputIndex); TransactionOutputInformationIn addTestMultisignatureOutput(uint64_t amount, uint32_t globalOutputIndex);
TransactionOutputInformationIn addTestMultisignatureOutput(uint64_t amount, std::vector<AccountPublicAddress>& addresses, uint32_t globalOutputIndex); TransactionOutputInformationIn addTestMultisignatureOutput(uint64_t amount, std::vector<AccountPublicAddress>& addresses, uint32_t globalOutputIndex);
size_t addOutput(uint64_t amount, const AccountPublicAddress& to); size_t addOutput(uint64_t amount, const AccountPublicAddress& to);
size_t addOutput(uint64_t amount, const KeyOutput& out); size_t addOutput(uint64_t amount, const KeyOutput& out);
size_t addOutput(uint64_t amount, const MultisignatureOutput& out); size_t addOutput(uint64_t amount, const MultisignatureOutput& out);
// final step // final step
std::unique_ptr<ITransactionReader> build(); std::unique_ptr<ITransactionReader> build();
// get built transaction hash (call only after build) // get built transaction hash (call only after build)
Crypto::Hash getTransactionHash() const; Crypto::Hash getTransactionHash() const;
private: private:
void derivePublicKey(const AccountKeys& reciever, const Crypto::PublicKey& srcTxKey, size_t outputIndex, PublicKey& ephemeralKey) { void derivePublicKey(const AccountKeys& reciever, const Crypto::PublicKey& srcTxKey, size_t outputIndex, PublicKey& ephemeralKey) {
Crypto::KeyDerivation derivation; Crypto::KeyDerivation derivation;
Crypto::generate_key_derivation(srcTxKey, reinterpret_cast<const Crypto::SecretKey&>(reciever.viewSecretKey), derivation); Crypto::generate_key_derivation(srcTxKey, reinterpret_cast<const Crypto::SecretKey&>(reciever.viewSecretKey), derivation);
Crypto::derive_public_key(derivation, outputIndex, Crypto::derive_public_key(derivation, outputIndex,
reinterpret_cast<const Crypto::PublicKey&>(reciever.address.spendPublicKey), reinterpret_cast<const Crypto::PublicKey&>(reciever.address.spendPublicKey),
reinterpret_cast<Crypto::PublicKey&>(ephemeralKey)); reinterpret_cast<Crypto::PublicKey&>(ephemeralKey));
} }
struct MsigInfo { struct MsigInfo {
PublicKey transactionKey; PublicKey transactionKey;
size_t outputIndex; size_t outputIndex;
std::vector<AccountBase> accounts; std::vector<AccountBase> accounts;
};
std::unordered_map<size_t, std::pair<TransactionTypes::InputKeyInfo, KeyPair>> keys;
std::unordered_map<size_t, MsigInfo> msigInputs;
std::unique_ptr<ITransaction> tx;
Crypto::Hash transactionHash;
}; };
std::unordered_map<size_t, std::pair<TransactionTypes::InputKeyInfo, KeyPair>> keys;
std::unordered_map<size_t, MsigInfo> msigInputs;
std::unique_ptr<ITransaction> tx;
Crypto::Hash transactionHash;
};
class FusionTransactionBuilder {
public:
FusionTransactionBuilder(const Currency& currency, uint64_t amount);
uint64_t getAmount() const;
void setAmount(uint64_t val);
uint64_t getFirstOutput() const;
void setFirstOutput(uint64_t val);
uint64_t getFee() const;
void setFee(uint64_t val);
size_t getExtraSize() const;
void setExtraSize(size_t val);
size_t getInputCount() const;
void setInputCount(size_t val);
std::unique_ptr<ITransactionReader> buildReader() const;
Transaction buildTx() const;
Transaction createFusionTransactionBySize(size_t targetSize);
private:
const Currency& m_currency;
uint64_t m_amount;
uint64_t m_firstOutput;
uint64_t m_fee;
size_t m_extraSize;
size_t m_inputCount;
};
} }
namespace CryptoNote { namespace CryptoNote {

View file

@ -31,6 +31,8 @@
#include <Logging/ConsoleLogger.h> #include <Logging/ConsoleLogger.h>
#include <Logging/LoggerGroup.h> #include <Logging/LoggerGroup.h>
#include "TransactionApiHelpers.h"
using namespace CryptoNote; using namespace CryptoNote;
using namespace CryptoNote; using namespace CryptoNote;
@ -656,3 +658,161 @@ TEST_F(tx_pool, RecentlyDeletedTxInfoIsSerializedAndDeserialized) {
ASSERT_EQ(1, pool->get_transactions_count()); ASSERT_EQ(1, pool->get_transactions_count());
} }
TEST_F(tx_pool, TxPoolAcceptsValidFusionTransaction) {
TransactionValidator validator;
FakeTimeProvider timeProvider;
std::unique_ptr<tx_memory_pool> pool(new tx_memory_pool(currency, validator, timeProvider, logger));
ASSERT_TRUE(pool->init(m_configDir.string()));
FusionTransactionBuilder builder(currency, 10 * currency.defaultDustThreshold());
auto tx = builder.buildTx();
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
ASSERT_TRUE(pool->add_tx(tx, tvc, false));
ASSERT_TRUE(tvc.m_added_to_pool);
ASSERT_TRUE(tvc.m_should_be_relayed);
ASSERT_FALSE(tvc.m_verifivation_failed);
ASSERT_FALSE(tvc.m_verifivation_impossible);
}
TEST_F(tx_pool, TxPoolDoesNotAcceptInvalidFusionTransaction) {
TransactionValidator validator;
FakeTimeProvider timeProvider;
std::unique_ptr<tx_memory_pool> pool(new tx_memory_pool(currency, validator, timeProvider, logger));
ASSERT_TRUE(pool->init(m_configDir.string()));
FusionTransactionBuilder builder(currency, 10 * currency.defaultDustThreshold());
builder.setInputCount(currency.fusionTxMinInputCount() - 1);
auto tx = builder.buildTx();
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
ASSERT_FALSE(pool->add_tx(tx, tvc, false));
ASSERT_FALSE(tvc.m_added_to_pool);
ASSERT_FALSE(tvc.m_should_be_relayed);
ASSERT_TRUE(tvc.m_verifivation_failed);
ASSERT_FALSE(tvc.m_verifivation_impossible);
}
namespace {
const size_t TEST_FUSION_TX_COUNT_PER_BLOCK = 3;
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;
Transaction createTestFusionTransaction(const Currency& currency) {
FusionTransactionBuilder builder(currency, 30 * currency.defaultDustThreshold());
return builder.createFusionTransactionBySize(TEST_TRANSACTION_SIZE);
}
Transaction createTestOrdinaryTransactionWithExtra(const Currency& currency, size_t extraSize) {
TestTransactionBuilder builder;
if (extraSize != 0) {
builder.appendExtra(BinaryArray(extraSize, 0));
}
builder.addTestInput(100 * currency.minimumFee());
builder.addTestKeyOutput(99 * currency.minimumFee(), 0);
return convertTx(*builder.build());
}
Transaction createTestOrdinaryTransaction(const Currency& currency) {
auto tx = createTestOrdinaryTransactionWithExtra(currency, 0);
size_t realSize = getObjectBinarySize(tx);
if (realSize < TEST_TRANSACTION_SIZE) {
size_t extraSize = TEST_TRANSACTION_SIZE - realSize;
tx = createTestOrdinaryTransactionWithExtra(currency, extraSize);
realSize = getObjectBinarySize(tx);
if (realSize > TEST_TRANSACTION_SIZE) {
extraSize -= realSize - TEST_TRANSACTION_SIZE;
tx = createTestOrdinaryTransactionWithExtra(currency, extraSize);
}
}
return tx;
}
class TxPool_FillBlockTemplate : public tx_pool {
public:
TxPool_FillBlockTemplate() :
tx_pool() {
currency = CryptoNote::CurrencyBuilder(logger).fusionTxMaxSize(TEST_FUSION_TX_MAX_SIZE).blockGrantedFullRewardZone(TEST_MEDIAN_SIZE).currency();
}
void doTest(size_t poolOrdinaryTxCount, size_t poolFusionTxCount, size_t expectedBlockOrdinaryTxCount, size_t expectedBlockFusionTxCount) {
TransactionValidator validator;
FakeTimeProvider timeProvider;
std::unique_ptr<tx_memory_pool> pool(new tx_memory_pool(currency, validator, timeProvider, logger));
ASSERT_TRUE(pool->init(m_configDir.string()));
std::unordered_map<Crypto::Hash, Transaction> ordinaryTxs;
for (size_t i = 0; i < poolOrdinaryTxCount; ++i) {
auto tx = createTestOrdinaryTransaction(currency);
ordinaryTxs.emplace(getObjectHash(tx), std::move(tx));
}
std::unordered_map<Crypto::Hash, Transaction> fusionTxs;
for (size_t i = 0; i < poolFusionTxCount; ++i) {
auto tx = createTestFusionTransaction(currency);
fusionTxs.emplace(getObjectHash(tx), std::move(tx));
}
for (auto pair : ordinaryTxs) {
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
ASSERT_TRUE(pool->add_tx(pair.second, tvc, false));
}
for (auto pair : fusionTxs) {
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
ASSERT_TRUE(pool->add_tx(pair.second, tvc, false));
}
Block block;
size_t totalSize;
uint64_t totalFee;
ASSERT_TRUE(pool->fill_block_template(block, currency.blockGrantedFullRewardZone(), std::numeric_limits<size_t>::max(), 0, totalSize, totalFee));
size_t fusionTxCount = 0;
size_t ordinaryTxCount = 0;
for (auto txHash : block.transactionHashes) {
if (fusionTxs.count(txHash) > 0) {
++fusionTxCount;
} else {
++ordinaryTxCount;
}
}
ASSERT_EQ(expectedBlockOrdinaryTxCount, ordinaryTxCount);
ASSERT_EQ(expectedBlockFusionTxCount, fusionTxCount);
}
};
}
TEST_F(TxPool_FillBlockTemplate, TxPoolAddsFusionTransactionsToBlockTemplateNoMoreThanLimit) {
ASSERT_NO_FATAL_FAILURE(doTest(TEST_MAX_TX_COUNT_PER_BLOCK,
TEST_MAX_TX_COUNT_PER_BLOCK,
TEST_MAX_TX_COUNT_PER_BLOCK - TEST_FUSION_TX_COUNT_PER_BLOCK,
TEST_FUSION_TX_COUNT_PER_BLOCK));
}
TEST_F(TxPool_FillBlockTemplate, TxPoolAddsFusionTransactionsUpToMedianAfterOrdinaryTransactions) {
static_assert(TEST_MAX_TX_COUNT_PER_BLOCK > 2, "TEST_MAX_TX_COUNT_PER_BLOCK > 2");
ASSERT_NO_FATAL_FAILURE(doTest(2, TEST_MAX_TX_COUNT_PER_BLOCK, 2, TEST_TX_COUNT_UP_TO_MEDIAN - 2));
}
TEST_F(TxPool_FillBlockTemplate, TxPoolAddsFusionTransactionsUpToMedianIfThereAreNoOrdinaryTransactions) {
ASSERT_NO_FATAL_FAILURE(doTest(0, TEST_MAX_TX_COUNT_PER_BLOCK, 0, TEST_TX_COUNT_UP_TO_MEDIAN));
}
TEST_F(TxPool_FillBlockTemplate, TxPoolContinuesToAddOrdinaryTransactionsUpTo125PerCentOfMedianAfterAddingFusionTransactions) {
size_t fusionTxCount = TEST_FUSION_TX_COUNT_PER_BLOCK - 1;
ASSERT_NO_FATAL_FAILURE(doTest(TEST_MAX_TX_COUNT_PER_BLOCK,
fusionTxCount,
TEST_MAX_TX_COUNT_PER_BLOCK - fusionTxCount,
fusionTxCount));
}