Bytecoin v.1.0.7 release
This commit is contained in:
parent
49572fc425
commit
deda499fc9
30 changed files with 770 additions and 156 deletions
|
@ -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
11
CTestCustom.cmake
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
set(CTEST_CUSTOM_TESTS_IGNORE
|
||||||
|
CoreTests
|
||||||
|
IntegrationTestLibrary
|
||||||
|
TestGenerator
|
||||||
|
CryptoTests
|
||||||
|
IntegrationTests
|
||||||
|
NodeRpcProxyTests
|
||||||
|
PerformanceTests
|
||||||
|
TransfersTests
|
||||||
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
0
src/Platform/Windows/System/ErrorMessage.cpp
Normal file → Executable 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(×tamp)) == 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 ")"
|
||||||
|
|
115
tests/UnitTests/TestCurrency.cpp
Normal file
115
tests/UnitTests/TestCurrency.cpp
Normal 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));
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue