diff --git a/README.md b/README.md index 3c83add..281eab3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Building Bitcedi +## Building Citadel ### On *nix: diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 6d69f68..ba61a57 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,9 @@ +Release notes 3.1.0-beta + +- Messages 2.0 core support +- Messages API for simplewallet +- Basic access authentication + Release notes 0.1.0 - New industrial type wallet for services: walletd diff --git a/include/IWalletLegacy.h b/include/IWalletLegacy.h index 8360020..1bf2869 100644 --- a/include/IWalletLegacy.h +++ b/include/IWalletLegacy.h @@ -133,8 +133,8 @@ class IWalletLegacy { virtual bool getDeposit(DepositId depositId, Deposit& deposit) = 0; virtual std::vector getTransactionsByPaymentIds(const std::vector& paymentIds) const = 0; - virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()) = 0; - virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()) = 0; + virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector(), uint64_t ttl = 0) = 0; + virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector(), uint64_t ttl = 0) = 0; virtual TransactionId deposit(uint32_t term, uint64_t amount, uint64_t fee, uint64_t mixIn = 0) = 0; virtual TransactionId withdrawDeposits(const std::vector& depositIds, uint64_t fee) = 0; virtual std::error_code cancelTransaction(size_t transferId) = 0; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a16967..908cf2e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,6 @@ add_definitions(-DSTATICLIB) file(GLOB_RECURSE BlockchainExplorer BlockchainExplorer/*) file(GLOB_RECURSE Common Common/*) -file(GLOB_RECURSE ConnectivityTool ConnectivityTool/*) file(GLOB_RECURSE Crypto crypto/*) file(GLOB_RECURSE CryptoNoteCore CryptoNoteCore/* CryptoNoteConfig.h) file(GLOB_RECURSE CryptoNoteProtocol CryptoNoteProtocol/*) @@ -32,7 +31,7 @@ file(GLOB_RECURSE PaymentGate PaymentGate/*) file(GLOB_RECURSE PaymentGateService PaymentGateService/*) file(GLOB_RECURSE Miner Miner/*) -source_group("" FILES $${Common} ${ConnectivityTool} ${Crypto} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${JsonRpcServer} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet} ${WalletLegacy}) +source_group("" FILES $${Common} ${Crypto} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${JsonRpcServer} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet} ${WalletLegacy}) add_library(BlockchainExplorer ${BlockchainExplorer}) add_library(Common ${Common}) @@ -51,7 +50,6 @@ add_library(Wallet ${Wallet} ${WalletLegacy}) add_library(PaymentGate ${PaymentGate}) add_library(JsonRpcServer ${JsonRpcServer}) -add_executable(ConnectivityTool ${ConnectivityTool}) add_executable(Daemon ${Daemon}) add_executable(SimpleWallet ${SimpleWallet}) add_executable(PaymentGateService ${PaymentGateService}) @@ -61,7 +59,7 @@ if (MSVC) target_link_libraries(System ws2_32) endif () -target_link_libraries(ConnectivityTool CryptoNoteCore Common Logging Crypto P2P Rpc Http Serialization System ${Boost_LIBRARIES}) +target_link_libraries(CryptoNoteCore Common Logging Crypto P2P Rpc Http Serialization System ${Boost_LIBRARIES}) target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) target_link_libraries(SimpleWallet Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES}) target_link_libraries(PaymentGateService PaymentGate JsonRpcServer Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) @@ -69,13 +67,11 @@ target_link_libraries(Miner CryptoNoteCore Rpc Serialization System Http Logging add_dependencies(Rpc version) -add_dependencies(ConnectivityTool version) add_dependencies(Daemon version) add_dependencies(SimpleWallet version) add_dependencies(PaymentGateService version) add_dependencies(P2P version) -set_property(TARGET ConnectivityTool PROPERTY OUTPUT_NAME "connectivity_tool") set_property(TARGET SimpleWallet PROPERTY OUTPUT_NAME "bitcediwallet") set_property(TARGET PaymentGateService PROPERTY OUTPUT_NAME "walletd") set_property(TARGET Miner PROPERTY OUTPUT_NAME "miner") diff --git a/src/Common/Base58.cpp b/src/Common/Base58.cpp index 127b0bf..041e602 100644 --- a/src/Common/Base58.cpp +++ b/src/Common/Base58.cpp @@ -85,15 +85,15 @@ namespace Tools uint64_t res = 0; switch (9 - size) { - case 1: res |= *data++; - case 2: res <<= 8; res |= *data++; - case 3: res <<= 8; res |= *data++; - case 4: res <<= 8; res |= *data++; - case 5: res <<= 8; res |= *data++; - case 6: res <<= 8; res |= *data++; - case 7: res <<= 8; res |= *data++; - case 8: res <<= 8; res |= *data; break; - default: assert(false); + case 1: res |= *data++; /* FALLTHRU */ + case 2: res <<= 8; res |= *data++; /* FALLTHRU */ + case 3: res <<= 8; res |= *data++; /* FALLTHRU */ + case 4: res <<= 8; res |= *data++; /* FALLTHRU */ + case 5: res <<= 8; res |= *data++; /* FALLTHRU */ + case 6: res <<= 8; res |= *data++; /* FALLTHRU */ + case 7: res <<= 8; res |= *data++; /* FALLTHRU */ + case 8: res <<= 8; res |= *data; break; + default: assert(false); } return res; diff --git a/src/Common/ConsoleHandler.cpp b/src/Common/ConsoleHandler.cpp index a9b96f7..a274c0f 100644 --- a/src/Common/ConsoleHandler.cpp +++ b/src/Common/ConsoleHandler.cpp @@ -192,9 +192,40 @@ bool ConsoleHandler::runCommand(const std::vector& cmdAndArgs) { } void ConsoleHandler::handleCommand(const std::string& cmd) { - std::vector args; - boost::split(args, cmd, boost::is_any_of(" "), boost::token_compress_on); - runCommand(args); + bool parseString = false; + std::string arg; + std::vector argList; + + for (auto ch : cmd) { + switch (ch) { + case ' ': + if (parseString) { + arg += ch; + } else if (!arg.empty()) { + argList.emplace_back(std::move(arg)); + arg.clear(); + } + break; + + case '"': + if (!arg.empty()) { + argList.emplace_back(std::move(arg)); + arg.clear(); + } + + parseString = !parseString; + break; + + default: + arg += ch; + } + } + + if (!arg.empty()) { + argList.emplace_back(std::move(arg)); + } + + runCommand(argList); } void ConsoleHandler::handlerThread() { diff --git a/src/Common/JsonValue.cpp b/src/Common/JsonValue.cpp index eecd478..0445c07 100644 --- a/src/Common/JsonValue.cpp +++ b/src/Common/JsonValue.cpp @@ -638,9 +638,8 @@ std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { namespace { char readChar(std::istream& in) { - char c; - - if (!(in >> c)) { + char c = static_cast(in.get()); + if (!in) { throw std::runtime_error("Unable to parse: unexpected end of stream"); } diff --git a/src/CryptoNoteConfig.h b/src/CryptoNoteConfig.h index cc3b163..98c9d5b 100644 --- a/src/CryptoNoteConfig.h +++ b/src/CryptoNoteConfig.h @@ -11,13 +11,13 @@ namespace CryptoNote { namespace parameters { +const uint64_t DIFFICULTY_TARGET = 240; // seconds const uint64_t CRYPTONOTE_MAX_BLOCK_NUMBER = 500000000; const size_t CRYPTONOTE_MAX_BLOCK_BLOB_SIZE = 500000000; const size_t CRYPTONOTE_MAX_TX_SIZE = 1000000000; const uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0xd7; // addresses start with "0xc" const size_t CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW = 6; -const uint64_t CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT = 60 * 60 * 2; - +const uint64_t CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT = DIFFICULTY_TARGET * 3; const size_t BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW = 30; const uint64_t MONEY_SUPPLY = UINT64_C(858986905600000000); @@ -31,10 +31,11 @@ const uint64_t COIN = UINT64_C(10000000 const uint64_t MINIMUM_FEE = UINT64_C(100000); // pow(10, 5) const uint64_t DEFAULT_DUST_THRESHOLD = UINT64_C(100000); // pow(10, 5) //const uint64_t GENESIS_BLOCK_REWARD = UINT64_C(0); +const uint64_t MAX_TX_MIXIN_SIZE = 50; -const uint64_t DIFFICULTY_TARGET = 240; // seconds const uint64_t EXPECTED_NUMBER_OF_BLOCKS_PER_DAY = 24 * 60 * 60 / DIFFICULTY_TARGET; const size_t DIFFICULTY_WINDOW = 240; // blocks +const size_t DIFFICULTY_WINDOW_V2 = 60; // blocks const size_t DIFFICULTY_CUT = 30; // timestamps to cut after sorting const size_t DIFFICULTY_LAG = 15; static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Bad DIFFICULTY_WINDOW or DIFFICULTY_CUT"); @@ -63,7 +64,8 @@ const size_t FUSION_TX_MAX_SIZE = CRYPTONOTE_BLOCK_ const size_t FUSION_TX_MIN_INPUT_COUNT = 12; const size_t FUSION_TX_MIN_IN_OUT_COUNT_RATIO = 4; -const uint64_t UPGRADE_HEIGHT = 1; +const uint32_t UPGRADE_HEIGHT_V2 = 1; +const uint32_t UPGRADE_HEIGHT_V3 = 170500; const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks @@ -83,7 +85,7 @@ const uint64_t START_BLOCK_REWARD = (UINT64_C(80) * p const uint64_t MIN_BLOCK_REWARD = (UINT64_C(5) * parameters::COIN); const uint64_t REWARD_HALVING_INTERVAL = (UINT64_C(66000)); -const char CRYPTONOTE_NAME[] = "bitcedi"; +const char CRYPTONOTE_NAME[] = "citadel"; const char GENESIS_COINBASE_TX_HEX[] = "010601ff000180a0d9e61d029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121019e2d1a633f2a54ff1a415e0051d5a699461d9c95479f67c8568446581c2e3782"; const uint32_t GENESIS_NONCE = 70; @@ -91,6 +93,7 @@ const uint8_t TRANSACTION_VERSION_1 = 1; const uint8_t TRANSACTION_VERSION_2 = 2; const uint8_t BLOCK_MAJOR_VERSION_1 = 1; const uint8_t BLOCK_MAJOR_VERSION_2 = 2; +const uint8_t BLOCK_MAJOR_VERSION_3 = 3; const uint8_t BLOCK_MINOR_VERSION_0 = 0; const uint8_t BLOCK_MINOR_VERSION_1 = 1; @@ -117,9 +120,9 @@ const size_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // const char P2P_STAT_TRUSTED_PUB_KEY[] = "85ae8734f90bc1ee295ceb0ec05a49852d4dbbc9d1c27a619b5f4bdf26a0196e"; const std::initializer_list SEED_NODES = { - "seed0.bitcedi.org:55008", - "seed1.bitcedi.org:55008", - "seed2.bitcedi.org:55008" + "173.230.155.185:55008", + "172.104.240.101:55008", + "45.33.53.50:55008" }; struct CheckpointData { diff --git a/src/CryptoNoteCore/Blockchain.cpp b/src/CryptoNoteCore/Blockchain.cpp index a19618f..b6136cd 100644 --- a/src/CryptoNoteCore/Blockchain.cpp +++ b/src/CryptoNoteCore/Blockchain.cpp @@ -311,7 +311,8 @@ m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_checkpoints(logger), -m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger) { +m_upgradeDetectorV2(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger), +m_upgradeDetectorV3(currency, m_blocks, BLOCK_MAJOR_VERSION_3, logger) { m_outputs.set_deleted_key(0); m_multisignatureOutputs.set_deleted_key(0); @@ -449,7 +450,12 @@ bool Blockchain::init(const std::string& config_folder, bool load_existing) { } } - if (!m_upgradeDetector.init()) { + if (!m_upgradeDetectorV2.init()) { + logger(ERROR, BRIGHT_RED) << "Failed to initialize upgrade detector"; + return false; + } + + if (!m_upgradeDetectorV3.init()) { logger(ERROR, BRIGHT_RED) << "Failed to initialize upgrade detector"; return false; } @@ -651,7 +657,8 @@ difficulty_type Blockchain::getDifficultyForNextBlock() { std::lock_guard lk(m_blockchain_lock); std::vector timestamps; std::vector commulative_difficulties; - size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(m_currency.difficultyBlocksCount())); + uint8_t BlockMajorVersion = m_blocks.size() >= m_upgradeDetectorV3.upgradeHeight() ? m_upgradeDetectorV3.targetVersion() : BLOCK_MAJOR_VERSION_1; + size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast((BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : m_currency.difficultyBlocksCount()))); if (offset == 0) { ++offset; } @@ -661,7 +668,12 @@ difficulty_type Blockchain::getDifficultyForNextBlock() { commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty); } - return m_currency.nextDifficulty(timestamps, commulative_difficulties); + return m_currency.nextDifficulty(BlockMajorVersion, timestamps, commulative_difficulties); +} + +uint64_t Blockchain::getBlockTimestamp(uint32_t height) { + assert(height < m_blocks.size()); + return m_blocks[height].bl.timestamp; } uint64_t Blockchain::getCoinsInCirculation() { @@ -690,7 +702,13 @@ difficulty_type Blockchain::difficultyAtHeight(uint64_t height) { } uint8_t Blockchain::get_block_major_version_for_height(uint64_t height) const { - return height > m_upgradeDetector.upgradeHeight() ? m_upgradeDetector.targetVersion() : BLOCK_MAJOR_VERSION_1; + if (height > m_upgradeDetectorV3.upgradeHeight()) { + return m_upgradeDetectorV3.targetVersion(); + } else if (height > m_upgradeDetectorV2.upgradeHeight()) { + return m_upgradeDetectorV2.targetVersion(); + } else { + return BLOCK_MAJOR_VERSION_1; + } } bool Blockchain::rollback_blockchain_switching(std::list &original_chain, size_t rollback_height) { @@ -797,10 +815,13 @@ bool Blockchain::switch_to_alternative_blockchain(std::list& alt_chain, BlockEntry& bei) { std::vector timestamps; std::vector commulative_difficulties; - if (alt_chain.size() < m_currency.difficultyBlocksCount()) { + uint8_t BlockMajorVersion = m_blocks.size() >= m_upgradeDetectorV3.upgradeHeight() ? m_upgradeDetectorV3.targetVersion() : BLOCK_MAJOR_VERSION_1; + if (alt_chain.size() < (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : m_currency.difficultyBlocksCount())) { std::lock_guard lk(m_blockchain_lock); size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; - size_t main_chain_count = m_currency.difficultyBlocksCount() - std::min(m_currency.difficultyBlocksCount(), alt_chain.size()); + size_t main_chain_count = (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : + m_currency.difficultyBlocksCount()) - std::min((BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : + m_currency.difficultyBlocksCount()), alt_chain.size()); main_chain_count = std::min(main_chain_count, main_chain_stop_offset); size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; @@ -811,30 +832,30 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); } - if (!((alt_chain.size() + timestamps.size()) <= m_currency.difficultyBlocksCount())) { + if (!((alt_chain.size() + timestamps.size()) <= (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : m_currency.difficultyBlocksCount()))) { logger(ERROR, BRIGHT_RED) << "Internal error, alt_chain.size()[" << alt_chain.size() << "] + timestamps.size()[" << timestamps.size() << - "] NOT <= m_currency.difficultyBlocksCount()[" << m_currency.difficultyBlocksCount() << ']'; return false; + "] NOT <= m_currency.difficultyBlocksCount()[" << (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : m_currency.difficultyBlocksCount()) << ']'; return false; } for (auto it : alt_chain) { timestamps.push_back(it->second.bl.timestamp); commulative_difficulties.push_back(it->second.cumulative_difficulty); } } else { - timestamps.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount())); - commulative_difficulties.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount())); + timestamps.resize(std::min(alt_chain.size(), (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : m_currency.difficultyBlocksCount()))); + commulative_difficulties.resize(std::min(alt_chain.size(), (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : m_currency.difficultyBlocksCount()))); size_t count = 0; size_t max_i = timestamps.size() - 1; BOOST_REVERSE_FOREACH(auto it, alt_chain) { timestamps[max_i - count] = it->second.bl.timestamp; commulative_difficulties[max_i - count] = it->second.cumulative_difficulty; count++; - if (count >= m_currency.difficultyBlocksCount()) { + if (count >= (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3 ? m_currency.difficultyBlocksCount2() + 1 : m_currency.difficultyBlocksCount())) { break; } } } - return m_currency.nextDifficulty(timestamps, commulative_difficulties); + return m_currency.nextDifficulty(BlockMajorVersion, timestamps, commulative_difficulties); } bool Blockchain::prevalidate_miner_transaction(const Block& b, uint32_t height) { @@ -1449,6 +1470,12 @@ bool Blockchain::checkTransactionInputs(const Transaction& tx, const Crypto::Has for (const auto& txin : tx.inputs) { assert(inputIndex < tx.signatures.size()); if (txin.type() == typeid(KeyInput)) { + uint64_t txMixin = boost::get(txin).outputIndexes.size(); + if (txMixin > CryptoNote::parameters::MAX_TX_MIXIN_SIZE) { + logger(DEBUGGING, BRIGHT_WHITE) << "Transaction << " << transactionHash << " hast too large mixin count."; + return false; + } + const KeyInput& in_to_key = boost::get(txin); if (!(!in_to_key.outputIndexes.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.outputIndexes in transaction with id " << getObjectHash(tx); return false; } @@ -1790,7 +1817,7 @@ bool Blockchain::pushBlock(const Block& blockData, const std::vector(txin).outputIndexes.size(); + if (txMixin > CryptoNote::parameters::MAX_TX_MIXIN_SIZE) { + logger(ERROR) << "Transaction " << getObjectHash(tx) << " has too large mixin count, rejected"; + return false; + } + } + } + return true; +} + bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { if (!tx.inputs.size()) { logger(ERROR) << "tx with empty inputs, rejected for tx id= " << getObjectHash(tx); @@ -316,15 +332,32 @@ bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficu b = boost::value_initialized(); b.majorVersion = m_blockchain.get_block_major_version_for_height(height); - if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { - b.minorVersion = BLOCK_MINOR_VERSION_1; - } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { + if (b.majorVersion < BLOCK_MAJOR_VERSION_3) { + if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { + b.minorVersion = m_currency.upgradeHeight(BLOCK_MAJOR_VERSION_2) == UpgradeDetectorBase::UNDEF_HEIGHT ? BLOCK_MINOR_VERSION_1 : BLOCK_MINOR_VERSION_0; + } else { + b.minorVersion = m_currency.upgradeHeight(BLOCK_MAJOR_VERSION_3) == UpgradeDetectorBase::UNDEF_HEIGHT ? BLOCK_MINOR_VERSION_1 : BLOCK_MINOR_VERSION_0; + } + } else { b.minorVersion = BLOCK_MINOR_VERSION_0; - } + } b.previousBlockHash = get_tail_id(); b.timestamp = time(NULL); + // Don't generate a block template with invalid timestamp + // Fix by Jagerman - https://github.com/graft-project/GraftNetwork/pull/118/commits + if(height >= m_currency.timestampCheckWindow()) { + std::vector timestamps; + for(size_t offset = height - m_currency.timestampCheckWindow(); offset < height; ++offset) { + timestamps.push_back(m_blockchain.getBlockTimestamp(offset)); + } + uint64_t median_ts = Common::medianValue(timestamps); + if (b.timestamp < median_ts) { + b.timestamp = median_ts; + } + } + median_size = m_blockchain.getCurrentCumulativeBlocksizeLimit() / 2; already_generated_coins = m_blockchain.getCoinsInCirculation(); } @@ -962,6 +995,12 @@ bool core::handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& return false; } + if (!check_tx_mixin(tx)) { + logger(INFO) << "Transaction verification failed: mixin count for transaction " << txHash << " is too large, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + if (!check_tx_semantic(tx, keptByBlock)) { logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << txHash << " semantic, rejected"; tvc.m_verifivation_failed = true; diff --git a/src/CryptoNoteCore/Core.h b/src/CryptoNoteCore/Core.h index b31fe89..10c479a 100644 --- a/src/CryptoNoteCore/Core.h +++ b/src/CryptoNoteCore/Core.h @@ -155,6 +155,9 @@ namespace CryptoNote { //check correct values, amounts and all lightweight checks not related with database bool check_tx_semantic(const Transaction& tx, bool keeped_by_block); //check if tx already in memory pool or in main blockchain + bool check_tx_mixin(const Transaction& tx); + //check if the mixin is not too large + bool is_key_image_spent(const Crypto::KeyImage& key_im); diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp index 758da84..8bc6fd3 100644 --- a/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp @@ -75,6 +75,7 @@ bool constructTransaction( const std::vector& sources, const std::vector& destinations, const std::vector& messages, + uint64_t ttl, std::vector extra, Transaction& tx, uint64_t unlock_time, @@ -197,6 +198,10 @@ bool constructTransaction( } } + if (ttl != 0) { + appendTTLToExtra(tx.extra, ttl); + } + //generate ring signatures Hash tx_prefix_hash; getObjectHash(*static_cast(&tx), tx_prefix_hash); @@ -479,7 +484,7 @@ bool get_block_longhash(cn_context &context, const Block& b, Hash& res) { return false; } - cn_slow_hash(context, bd.data(), bd.size(), res); + cn_slow_hash(context, bd.data(), bd.size(), res, b.majorVersion >= 3 ? 1 : 0); return true; } diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.h b/src/CryptoNoteCore/CryptoNoteFormatUtils.h index 999cff5..81a365a 100644 --- a/src/CryptoNoteCore/CryptoNoteFormatUtils.h +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.h @@ -51,7 +51,7 @@ bool constructTransaction( const std::vector& sources, const std::vector& destinations, const std::vector& messages, - std::vector extra, Transaction& transaction, uint64_t unlock_time, Logging::ILogger& log); + uint64_t ttl, std::vector extra, Transaction& transaction, uint64_t unlock_time, Logging::ILogger& log); inline bool constructTransaction( const AccountKeys& sender_account_keys, @@ -59,7 +59,7 @@ inline bool constructTransaction( const std::vector& destinations, std::vector extra, Transaction& tx, uint64_t unlock_time, Logging::ILogger& log) { - return constructTransaction(sender_account_keys, sources, destinations, std::vector(), extra, tx, unlock_time, log); + return constructTransaction(sender_account_keys, sources, destinations, std::vector(), 0, extra, tx, unlock_time, log); } bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const Crypto::PublicKey& tx_pub_key, size_t keyIndex); diff --git a/src/CryptoNoteCore/CryptoNoteSerialization.cpp b/src/CryptoNoteCore/CryptoNoteSerialization.cpp index a27a6c8..4334e53 100644 --- a/src/CryptoNoteCore/CryptoNoteSerialization.cpp +++ b/src/CryptoNoteCore/CryptoNoteSerialization.cpp @@ -296,7 +296,7 @@ void serialize(MultisignatureOutput& multisignature, ISerializer& serializer) { void serializeBlockHeader(BlockHeader& header, ISerializer& serializer) { serializer(header.majorVersion, "major_version"); - if (header.majorVersion > BLOCK_MAJOR_VERSION_2) { + if (header.majorVersion > BLOCK_MAJOR_VERSION_3) { throw std::runtime_error("Wrong major version"); } diff --git a/src/CryptoNoteCore/CryptoNoteTools.cpp b/src/CryptoNoteCore/CryptoNoteTools.cpp index 3f0319f..1adf492 100644 --- a/src/CryptoNoteCore/CryptoNoteTools.cpp +++ b/src/CryptoNoteCore/CryptoNoteTools.cpp @@ -31,6 +31,19 @@ Crypto::Hash getBinaryArrayHash(const BinaryArray& binaryArray) { return hash; } +uint64_t getInputAmount(const Transaction& transaction) { + uint64_t amount = 0; + for (auto& input : transaction.inputs) { + if (input.type() == typeid(KeyInput)) { + amount += boost::get(input).amount; + } else if (input.type() == typeid(MultisignatureInput)) { + amount += boost::get(input).amount; + } + } + + return amount; +} + std::vector getInputsAmounts(const Transaction& transaction) { std::vector inputsAmounts; inputsAmounts.reserve(transaction.inputs.size()); diff --git a/src/CryptoNoteCore/CryptoNoteTools.h b/src/CryptoNoteCore/CryptoNoteTools.h index 12687a3..e867099 100644 --- a/src/CryptoNoteCore/CryptoNoteTools.h +++ b/src/CryptoNoteCore/CryptoNoteTools.h @@ -107,6 +107,7 @@ Crypto::Hash getObjectHash(const T& object) { return hash; } +uint64_t getInputAmount(const Transaction& transaction); std::vector getInputsAmounts(const Transaction& transaction); uint64_t getOutputAmount(const Transaction& transaction); void decomposeAmount(uint64_t amount, uint64_t dustThreshold, std::vector& decomposedAmounts); diff --git a/src/CryptoNoteCore/Currency.cpp b/src/CryptoNoteCore/Currency.cpp index 478bf1c..3962cd1 100644 --- a/src/CryptoNoteCore/Currency.cpp +++ b/src/CryptoNoteCore/Currency.cpp @@ -60,7 +60,8 @@ bool Currency::init() { } if (isTestnet()) { - m_upgradeHeight = 0; + m_upgradeHeightV2 = 0; + m_upgradeHeightV3 = 11; m_blocksFileName = "testnet_" + m_blocksFileName; m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; @@ -106,6 +107,16 @@ uint64_t Currency::baseRewardFunction(uint64_t alreadyGeneratedCoins, uint32_t h return base_reward; } +uint32_t Currency::upgradeHeight(uint8_t majorVersion) const { + if (majorVersion == BLOCK_MAJOR_VERSION_2) { + return m_upgradeHeightV2; + } else if (majorVersion == BLOCK_MAJOR_VERSION_3) { + return m_upgradeHeightV3; + } else { + return static_cast(-1); + } +} + bool Currency::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint32_t height, uint64_t& reward, int64_t& emissionChange) const { assert(alreadyGeneratedCoins <= m_moneySupply); @@ -445,49 +456,103 @@ bool Currency::parseAmount(const std::string& str, uint64_t& amount) const { return Common::fromString(strAmount, amount); } -difficulty_type Currency::nextDifficulty(std::vector timestamps, +difficulty_type Currency::nextDifficulty(uint8_t blockMajorVersion, std::vector timestamps, std::vector cumulativeDifficulties) const { - assert(m_difficultyWindow >= 2); - - if (timestamps.size() > m_difficultyWindow) { - timestamps.resize(m_difficultyWindow); - cumulativeDifficulties.resize(m_difficultyWindow); - } - - size_t length = timestamps.size(); - assert(length == cumulativeDifficulties.size()); - assert(length <= m_difficultyWindow); - if (length <= 1) { - return 1; - } - - sort(timestamps.begin(), timestamps.end()); - - size_t cutBegin, cutEnd; - assert(2 * m_difficultyCut <= m_difficultyWindow - 2); - if (length <= m_difficultyWindow - 2 * m_difficultyCut) { - cutBegin = 0; - cutEnd = length; - } else { - cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2; - cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut); - } - assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length); - uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin]; - if (timeSpan == 0) { - timeSpan = 1; - } - - difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin]; - assert(totalWork > 0); - - uint64_t low, high; - low = mul128(totalWork, m_difficultyTarget, &high); - if (high != 0 || low + timeSpan - 1 < low) { - return 0; - } - - return (low + timeSpan - 1) / timeSpan; + if (blockMajorVersion >= BLOCK_MAJOR_VERSION_3) { + + // LWMA difficulty algorithm (simplified) + // Copyright (c) 2017-2018 Zawy + // MIT license http://www.opensource.org/licenses/mit-license.php + // set constants: + // N is most recent solved block. + // T= target solvetime, adjust = 0.9989^(500/N), N=int((45*(600/T)^(0.3*(600/T)^0.2) + // timestamp, cumulativedifficulties, and target are vectors of size N+1 + + const int64_t T = static_cast(m_difficultyTarget); + size_t N = CryptoNote::parameters::DIFFICULTY_WINDOW_V2; + const double_t adjust = pow(0.9989, 500 / N); + const double_t k = T * (N + 1) * adjust; + + if (timestamps.size() < 4) { + return 1; + } + // use a smaller N if timestamps and difficulies vectors are less than N+1 + if (timestamps.size() < N + 1) { + N = timestamps.size() - 1; + } + if (timestamps.size() > N + 1) { + timestamps.resize(N + 1); + } + if (cumulativeDifficulties.size() > N + 1) { + cumulativeDifficulties.resize(N + 1); + } + + int64_t L(0); + uint64_t next_D; + + for (size_t i = 1; i <= N; i++) { + L += (int64_t)(std::max(-7 * T, std::min(7 * T, timestamps[i] - timestamps[i - 1])) * i); + } + if (L < 1) + L = 1; + + uint64_t low, high; + low = mul128(cumulativeDifficulties[N] - cumulativeDifficulties[0], static_cast(k), &high); + // blockchain error "Difficulty overhead" if this function returns zero + if (high != 0) { + return 0; + } + + next_D = low / L / 2; + + return next_D; + + } + else { + + assert(m_difficultyWindow >= 2); + + if (timestamps.size() > m_difficultyWindow) { + timestamps.resize(m_difficultyWindow); + cumulativeDifficulties.resize(m_difficultyWindow); + } + + size_t length = timestamps.size(); + assert(length == cumulativeDifficulties.size()); + assert(length <= m_difficultyWindow); + if (length <= 1) { + return 1; + } + + sort(timestamps.begin(), timestamps.end()); + + size_t cutBegin, cutEnd; + assert(2 * m_difficultyCut <= m_difficultyWindow - 2); + if (length <= m_difficultyWindow - 2 * m_difficultyCut) { + cutBegin = 0; + cutEnd = length; + } + else { + cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2; + cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut); + } + assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length); + uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin]; + if (timeSpan == 0) { + timeSpan = 1; + } + + difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin]; + assert(totalWork > 0); + + uint64_t low, high; + low = mul128(totalWork, m_difficultyTarget, &high); + if (high != 0 || low + timeSpan - 1 < low) { + return 0; + } + + return (low + timeSpan - 1) / timeSpan; + } } bool Currency::checkProofOfWork(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, @@ -567,7 +632,8 @@ CurrencyBuilder::CurrencyBuilder(Logging::ILogger& log) : m_currency(log) { mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); numberOfPeriodsToForgetTxDeletedFromPool(parameters::CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL); - upgradeHeight(parameters::UPGRADE_HEIGHT); + upgradeHeightV2(parameters::UPGRADE_HEIGHT_V2); + upgradeHeightV3(parameters::UPGRADE_HEIGHT_V3); upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW); upgradeWindow(parameters::UPGRADE_WINDOW); diff --git a/src/CryptoNoteCore/Currency.h b/src/CryptoNoteCore/Currency.h index 350a458..c46cb46 100644 --- a/src/CryptoNoteCore/Currency.h +++ b/src/CryptoNoteCore/Currency.h @@ -48,6 +48,7 @@ class Currency { size_t difficultyLag() const { return m_difficultyLag; } size_t difficultyCut() const { return m_difficultyCut; } size_t difficultyBlocksCount() const { return m_difficultyWindow + m_difficultyLag; } + size_t difficultyBlocksCount2() const { return CryptoNote::parameters::DIFFICULTY_WINDOW_V2; } uint64_t depositMinAmount() const { return m_depositMinAmount; } uint32_t depositMinTerm() const { return m_depositMinTerm; } @@ -66,13 +67,13 @@ class Currency { uint64_t mempoolTxFromAltBlockLiveTime() const { return m_mempoolTxFromAltBlockLiveTime; } uint64_t numberOfPeriodsToForgetTxDeletedFromPool() const { return m_numberOfPeriodsToForgetTxDeletedFromPool; } - uint64_t upgradeHeight() const { return m_upgradeHeight; } + uint32_t upgradeHeight(uint8_t majorVersion) const; unsigned int upgradeVotingThreshold() const { return m_upgradeVotingThreshold; } - size_t upgradeVotingWindow() const { return m_upgradeVotingWindow; } - size_t upgradeWindow() const { return m_upgradeWindow; } - size_t minNumberVotingBlocks() const { return (m_upgradeVotingWindow * m_upgradeVotingThreshold + 99) / 100; } - uint64_t maxUpgradeDistance() const { return static_cast(m_upgradeWindow); } - uint64_t calculateUpgradeHeight(uint64_t voteCompleteHeight) const { return voteCompleteHeight + m_upgradeWindow; } + uint32_t upgradeVotingWindow() const { return m_upgradeVotingWindow; } + uint32_t upgradeWindow() const { return m_upgradeWindow; } + uint32_t minNumberVotingBlocks() const { return (m_upgradeVotingWindow * m_upgradeVotingThreshold + 99) / 100; } + uint32_t maxUpgradeDistance() const { return 7 * m_upgradeWindow; } + uint32_t calculateUpgradeHeight(uint32_t voteCompleteHeight) const { return voteCompleteHeight + m_upgradeWindow; } size_t fusionTxMaxSize() const { return m_fusionTxMaxSize; } size_t fusionTxMinInputCount() const { return m_fusionTxMinInputCount; } @@ -117,7 +118,7 @@ class Currency { std::string formatAmount(int64_t amount) const; bool parseAmount(const std::string& str, uint64_t& amount) const; - difficulty_type nextDifficulty(std::vector timestamps, std::vector cumulativeDifficulties) const; + difficulty_type nextDifficulty(uint8_t blockMajorVersion, std::vector timestamps, std::vector cumulativeDifficulties) const; bool checkProofOfWork(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const; size_t getApproximateMaximumInputCount(size_t transactionSize, size_t outputCount, size_t mixinCount) const; @@ -176,10 +177,11 @@ class Currency { uint64_t m_mempoolTxFromAltBlockLiveTime; uint64_t m_numberOfPeriodsToForgetTxDeletedFromPool; - uint64_t m_upgradeHeight; + uint32_t m_upgradeHeightV2; + uint32_t m_upgradeHeightV3; unsigned int m_upgradeVotingThreshold; - size_t m_upgradeVotingWindow; - size_t m_upgradeWindow; + uint32_t m_upgradeVotingWindow; + uint32_t m_upgradeWindow; size_t m_fusionTxMaxSize; size_t m_fusionTxMinInputCount; @@ -261,7 +263,8 @@ class CurrencyBuilder : boost::noncopyable { 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& upgradeHeight(uint64_t val) { m_currency.m_upgradeHeight = val; return *this; } + CurrencyBuilder& upgradeHeightV2(uint64_t val) { m_currency.m_upgradeHeightV2 = val; return *this; } + CurrencyBuilder& upgradeHeightV3(uint64_t val) { m_currency.m_upgradeHeightV3 = val; return *this; } CurrencyBuilder& upgradeVotingThreshold(unsigned int val); CurrencyBuilder& upgradeVotingWindow(size_t val) { m_currency.m_upgradeVotingWindow = val; return *this; } CurrencyBuilder& upgradeWindow(size_t val); diff --git a/src/CryptoNoteCore/SwappedMap.h b/src/CryptoNoteCore/SwappedMap.h index 8e005f8..28aa2d6 100644 --- a/src/CryptoNoteCore/SwappedMap.h +++ b/src/CryptoNoteCore/SwappedMap.h @@ -183,7 +183,7 @@ template bool SwappedMap::open(const std::string& it } template void SwappedMap::close() { - std::cout << "SwappedMap cache hits: " << m_cacheHits << ", misses: " << m_cacheMisses << " (" << std::fixed << std::setprecision(2) << static_cast(m_cacheMisses) / (m_cacheHits + m_cacheMisses) * 100 << "%)" << std::endl; + //std::cout << "SwappedMap cache hits: " << m_cacheHits << ", misses: " << m_cacheMisses << " (" << std::fixed << std::setprecision(2) << static_cast(m_cacheMisses) / (m_cacheHits + m_cacheMisses) * 100 << "%)" << std::endl; } template uint64_t SwappedMap::size() const { diff --git a/src/CryptoNoteCore/TransactionExtra.cpp b/src/CryptoNoteCore/TransactionExtra.cpp index 7bd67ef..d986a0f 100644 --- a/src/CryptoNoteCore/TransactionExtra.cpp +++ b/src/CryptoNoteCore/TransactionExtra.cpp @@ -9,6 +9,7 @@ #include "Common/MemoryInputStream.h" #include "Common/StreamTools.h" #include "Common/StringTools.h" +#include "Common/Varint.h" #include "CryptoNoteTools.h" #include "Serialization/BinaryOutputStreamSerializer.h" #include "Serialization/BinaryInputStreamSerializer.h" @@ -81,6 +82,15 @@ bool parseTransactionExtra(const std::vector &transactionExtra, std::ve transactionExtraFields.push_back(message); break; } + + case TX_EXTRA_TTL: { + uint8_t size; + readVarint(iss, size); + TransactionExtraTTL ttl; + readVarint(iss, ttl.ttl); + transactionExtraFields.push_back(ttl); + break; + } } } } catch (std::exception &) { @@ -119,6 +129,11 @@ struct ExtraSerializerVisitor : public boost::static_visitor { bool operator()(const tx_extra_message& t) { return append_message_to_extra(extra, t); } + + bool operator()(const TransactionExtraTTL& t) { + appendTTLToExtra(extra, t.ttl); + return true; + } }; bool writeTransactionExtra(std::vector& tx_extra, const std::vector& tx_extra_fields) { @@ -221,6 +236,16 @@ std::vector get_messages_from_extra(const std::vector &ext return result; } +void appendTTLToExtra(std::vector& tx_extra, uint64_t ttl) { + std::string ttlData = Tools::get_varint_data(ttl); + std::string extraFieldSize = Tools::get_varint_data(ttlData.size()); + + tx_extra.reserve(tx_extra.size() + 1 + extraFieldSize.size() + ttlData.size()); + tx_extra.push_back(TX_EXTRA_TTL); + std::copy(extraFieldSize.begin(), extraFieldSize.end(), std::back_inserter(tx_extra)); + std::copy(ttlData.begin(), ttlData.end(), std::back_inserter(tx_extra)); +} + void setPaymentIdToTransactionExtraNonce(std::vector& extra_nonce, const Hash& payment_id) { extra_nonce.clear(); extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); diff --git a/src/CryptoNoteCore/TransactionExtra.h b/src/CryptoNoteCore/TransactionExtra.h index fa5f2f2..aa6bc61 100644 --- a/src/CryptoNoteCore/TransactionExtra.h +++ b/src/CryptoNoteCore/TransactionExtra.h @@ -19,6 +19,7 @@ #define TX_EXTRA_NONCE 0x02 #define TX_EXTRA_MERGE_MINING_TAG 0x03 #define TX_EXTRA_MESSAGE_TAG 0x04 +#define TX_EXTRA_TTL 0x05 #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 @@ -52,11 +53,15 @@ struct tx_extra_message { bool serialize(ISerializer& serializer); }; +struct TransactionExtraTTL { + uint64_t ttl; +}; + // tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: // varint tag; // varint size; // varint data[]; -typedef boost::variant TransactionExtraField; +typedef boost::variant TransactionExtraField; @@ -83,6 +88,7 @@ bool getPaymentIdFromTransactionExtraNonce(const BinaryArray& extra_nonce, Crypt bool appendMergeMiningTagToExtra(std::vector& tx_extra, const TransactionExtraMergeMiningTag& mm_tag); bool append_message_to_extra(std::vector& tx_extra, const tx_extra_message& message); std::vector get_messages_from_extra(const std::vector& extra, const Crypto::PublicKey &txkey, const Crypto::SecretKey *recepient_secret_key); +void appendTTLToExtra(std::vector& tx_extra, uint64_t ttl); bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); //returns false if payment id is not found or parse error diff --git a/src/CryptoNoteCore/TransactionPool.cpp b/src/CryptoNoteCore/TransactionPool.cpp index 594499e..4df293e 100644 --- a/src/CryptoNoteCore/TransactionPool.cpp +++ b/src/CryptoNoteCore/TransactionPool.cpp @@ -22,6 +22,7 @@ #include "CryptoNoteFormatUtils.h" #include "CryptoNoteTools.h" #include "CryptoNoteConfig.h" +#include "TransactionExtra.h" using namespace Logging; @@ -96,7 +97,9 @@ namespace CryptoNote { m_timeProvider(timeProvider), m_txCheckInterval(60, timeProvider), m_fee_index(boost::get<1>(m_transactions)), - logger(log, "txpool") { + logger(log, "txpool"), + m_paymentIdIndex(), + m_timestampIndex() { } //--------------------------------------------------------------------------------- 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) { @@ -115,9 +118,16 @@ namespace CryptoNote { return false; } + std::vector txExtraFields; + parseTransactionExtra(tx.extra, txExtraFields); + TransactionExtraTTL ttl; + if (!findTransactionExtraFieldByType(txExtraFields, ttl)) { + ttl.ttl = 0; + } + const uint64_t fee = inputs_amount - outputs_amount; bool isFusionTransaction = fee == 0 && m_currency.isFusionTransaction(tx, blobSize); - if (!keptByBlock && !isFusionTransaction && fee < m_currency.minimumFee()) { + if (!keptByBlock && !isFusionTransaction && ttl.ttl == 0 && fee < m_currency.minimumFee()) { logger(INFO) << "transaction fee is not enough: " << m_currency.formatAmount(fee) << ", minimum fee: " << m_currency.formatAmount(m_currency.minimumFee()); tvc.m_verifivation_failed = true; @@ -125,6 +135,25 @@ namespace CryptoNote { return false; } + if (ttl.ttl != 0 && !keptByBlock) { + uint64_t now = static_cast(time(nullptr)); + if (ttl.ttl <= now) { + logger(WARNING, BRIGHT_YELLOW) << "Transaction TTL has already expired: tx = " << id << ", ttl = " << ttl.ttl; + tvc.m_verifivation_failed = true; + return false; + } else if (ttl.ttl - now > m_currency.mempoolTxLiveTime() + m_currency.blockFutureTimeLimit()) { + logger(WARNING, BRIGHT_YELLOW) << "Transaction TTL is out of range: tx = " << id << ", ttl = " << ttl.ttl; + tvc.m_verifivation_failed = true; + return false; + } + + if (fee != 0) { + logger(WARNING, BRIGHT_YELLOW) << "Transaction with TTL has non-zero fee: tx = " << id << ", fee = " << m_currency.formatAmount(fee); + tvc.m_verifivation_failed = true; + return false; + } + } + //check key images for transaction if it is not kept by block if (!keptByBlock) { std::lock_guard lock(m_transactions_lock); @@ -192,10 +221,13 @@ namespace CryptoNote { m_paymentIdIndex.add(txd.tx); m_timestampIndex.add(txd.receiveTime, txd.id); + if (ttl.ttl != 0) { + m_ttlIndex.emplace(std::make_pair(id, ttl.ttl)); + } } tvc.m_added_to_pool = true; - tvc.m_should_be_relayed = inputsValid && (fee > 0 || isFusionTransaction); + tvc.m_should_be_relayed = inputsValid && (fee > 0 || isFusionTransaction || ttl.ttl != 0); tvc.m_verifivation_failed = true; if (!addTransactionInputs(id, tx, keptByBlock)) @@ -328,7 +360,15 @@ namespace CryptoNote { << "max_used_block_id: " << txd.maxUsedBlock.id << std::endl << "last_failed_height: " << txd.lastFailedBlock.height << std::endl << "last_failed_id: " << txd.lastFailedBlock.id << std::endl - << "received: " << std::ctime(&txd.receiveTime) << std::endl; + << "received: " << std::ctime(&txd.receiveTime); + + auto ttlIt = m_ttlIndex.find(txd.id); + if (ttlIt != m_ttlIndex.end()) { + // ctime() returns string that ends with new line + ss << "TTL: " << std::ctime(reinterpret_cast(&ttlIt->second)); + } + + ss << std::endl; } return ss.str(); @@ -349,6 +389,10 @@ namespace CryptoNote { for (auto it = m_fee_index.rbegin(); it != m_fee_index.rend() && it->fee == 0; ++it) { const auto& txd = *it; + if (m_ttlIndex.count(txd.id) > 0) { + continue; + } + if (m_currency.fusionTxMaxSize() < total_size + txd.blobSize) { continue; } @@ -362,6 +406,10 @@ namespace CryptoNote { for (auto i = m_fee_index.begin(); i != m_fee_index.end(); ++i) { const auto& txd = *i; + if (m_ttlIndex.count(txd.id) > 0) { + continue; + } + size_t blockSizeLimit = (txd.fee == 0) ? median_size : max_total_size; if (blockSizeLimit < total_size + txd.blobSize) { continue; @@ -404,6 +452,7 @@ namespace CryptoNote { m_paymentIdIndex.clear(); m_timestampIndex.clear(); + m_ttlIndex.clear(); } else { buildIndices(); } @@ -428,6 +477,7 @@ namespace CryptoNote { m_paymentIdIndex.clear(); m_timestampIndex.clear(); + m_ttlIndex.clear(); return true; } @@ -498,8 +548,16 @@ namespace CryptoNote { uint64_t txAge = now - it->receiveTime; bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); - if (remove) { - logger(TRACE) << "Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge; + auto ttlIt = m_ttlIndex.find(it->id); + bool ttlExpired = (ttlIt != m_ttlIndex.end() && ttlIt->second <= now); + + if (remove || ttlExpired) { + if (ttlExpired) { + logger(TRACE) << "Tx " << it->id << " removed from tx pool due to expired TTL, TTL : " << ttlIt->second; + } else { + logger(TRACE) << "Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge; + } + m_recentlyDeletedTransactions.emplace(it->id, now); it = removeTransaction(it); somethingRemoved = true; @@ -520,6 +578,7 @@ namespace CryptoNote { removeTransactionInputs(i->id, i->tx, i->keptByBlock); m_paymentIdIndex.remove(i->tx); m_timestampIndex.remove(i->receiveTime, i->id); + m_ttlIndex.erase(i->id); return m_transactions.erase(i); } @@ -618,6 +677,15 @@ namespace CryptoNote { for (auto it = m_transactions.begin(); it != m_transactions.end(); it++) { m_paymentIdIndex.add(it->tx); m_timestampIndex.add(it->receiveTime, it->id); + + std::vector txExtraFields; + parseTransactionExtra(it->tx.extra, txExtraFields); + TransactionExtraTTL ttl; + if (findTransactionExtraFieldByType(txExtraFields, ttl)) { + if (ttl.ttl != 0) { + m_ttlIndex.emplace(std::make_pair(it->id, ttl.ttl)); + } + } } } diff --git a/src/CryptoNoteCore/TransactionPool.h b/src/CryptoNoteCore/TransactionPool.h index bfb7f99..966e948 100644 --- a/src/CryptoNoteCore/TransactionPool.h +++ b/src/CryptoNoteCore/TransactionPool.h @@ -75,7 +75,7 @@ namespace CryptoNote { const CryptoNote::Currency& currency, CryptoNote::ITransactionValidator& validator, CryptoNote::ITimeProvider& timeProvider, - Logging::ILogger& log); + Logging::ILogger& log); bool addObserver(ITxPoolObserver* observer); bool removeObserver(ITxPoolObserver* observer); @@ -203,6 +203,7 @@ namespace CryptoNote { PaymentIdIndex m_paymentIdIndex; TimestampTransactionsIndex m_timestampIndex; + std::unordered_map m_ttlIndex; }; } diff --git a/src/CryptoNoteCore/UpgradeDetector.h b/src/CryptoNoteCore/UpgradeDetector.h index 46c840a..e42d977 100644 --- a/src/CryptoNoteCore/UpgradeDetector.h +++ b/src/CryptoNoteCore/UpgradeDetector.h @@ -1,5 +1,5 @@ // Copyright (c) 2011-2016 The Cryptonote developers -// Copyright (c) 2014-2016 SDN developers +// Copyright (c) 2014-2016 XDN-project developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,6 +9,9 @@ #include #include +#include "Common/StringTools.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" #include "CryptoNoteCore/Currency.h" #include "CryptoNoteConfig.h" #include @@ -16,12 +19,12 @@ namespace CryptoNote { class UpgradeDetectorBase { public: - enum : uint64_t { - UNDEF_HEIGHT = static_cast(-1), + enum : uint32_t { + UNDEF_HEIGHT = static_cast(-1), }; }; - static_assert(CryptoNote::UpgradeDetectorBase::UNDEF_HEIGHT == UINT64_C(0xFFFFFFFFFFFFFFFF), "UpgradeDetectorBase::UNDEF_HEIGHT has invalid value"); + static_assert(CryptoNote::UpgradeDetectorBase::UNDEF_HEIGHT == UINT32_C(0xFFFFFFFF), "UpgradeDetectorBase::UNDEF_HEIGHT has invalid value"); template class BasicUpgradeDetector : public UpgradeDetectorBase { @@ -31,10 +34,12 @@ namespace CryptoNote { m_blockchain(blockchain), m_targetVersion(targetVersion), m_votingCompleteHeight(UNDEF_HEIGHT), - logger(log, "upgrade") { } + logger(log, "upgrade") { + } bool init() { - if (m_currency.upgradeHeight() == UNDEF_HEIGHT) { + uint32_t upgradeHeight = m_currency.upgradeHeight(m_targetVersion); + if (upgradeHeight == UNDEF_HEIGHT) { if (m_blockchain.empty()) { m_votingCompleteHeight = UNDEF_HEIGHT; @@ -44,51 +49,73 @@ namespace CryptoNote { } else if (m_targetVersion <= m_blockchain.back().bl.majorVersion) { auto it = std::lower_bound(m_blockchain.begin(), m_blockchain.end(), m_targetVersion, [](const typename BC::value_type& b, uint8_t v) { return b.bl.majorVersion < v; }); - if (!(it != m_blockchain.end() && it->bl.majorVersion == m_targetVersion)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: upgrade height isn't found"; return false; } - uint64_t upgradeHeight = it - m_blockchain.begin(); - m_votingCompleteHeight = findVotingCompleteHeight(upgradeHeight); - if (!(m_votingCompleteHeight != UNDEF_HEIGHT)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: voting complete height isn't found, upgrade height = " << upgradeHeight; return false; } + if (it == m_blockchain.end() || it->bl.majorVersion != m_targetVersion) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: upgrade height isn't found"; + return false; + } + uint32_t upgradeHeight = it - m_blockchain.begin(); + m_votingCompleteHeight = findVotingCompleteHeight(upgradeHeight); + if (m_votingCompleteHeight == UNDEF_HEIGHT) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: voting complete height isn't found, upgrade height = " << upgradeHeight; + return false; + } } else { m_votingCompleteHeight = UNDEF_HEIGHT; } } else if (!m_blockchain.empty()) { - if (m_blockchain.size() <= m_currency.upgradeHeight() + 1) { - if (!(m_blockchain.back().bl.majorVersion == m_targetVersion - 1)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << (m_blockchain.size() - 1) << " has invalid version " << - static_cast(m_blockchain.back().bl.majorVersion) << ", expected " << static_cast(m_targetVersion); return false; } + if (m_blockchain.size() <= upgradeHeight + 1) { + if (m_blockchain.back().bl.majorVersion >= m_targetVersion) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << (m_blockchain.size() - 1) << + " has invalid version " << static_cast(m_blockchain.back().bl.majorVersion) << + ", expected " << static_cast(m_targetVersion - 1) << " or less"; + return false; + } } else { - int blockVersionAtUpgradeHeight = m_blockchain[m_currency.upgradeHeight()].bl.majorVersion; - if (!(blockVersionAtUpgradeHeight == m_targetVersion - 1)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << m_currency.upgradeHeight() << " has invalid version " << - blockVersionAtUpgradeHeight << ", expected " << static_cast(m_targetVersion - 1); return false; } + int blockVersionAtUpgradeHeight = m_blockchain[upgradeHeight].bl.majorVersion; + if (blockVersionAtUpgradeHeight != m_targetVersion - 1) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << upgradeHeight << + " has invalid version " << blockVersionAtUpgradeHeight << + ", expected " << static_cast(m_targetVersion - 1); + return false; + } - int blockVersionAfterUpgradeHeight = m_blockchain[m_currency.upgradeHeight() + 1].bl.majorVersion; - if (!(blockVersionAfterUpgradeHeight == m_targetVersion)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << (m_currency.upgradeHeight() + 1) << " has invalid version " << - blockVersionAfterUpgradeHeight << ", expected " << static_cast(m_targetVersion); return false; } + int blockVersionAfterUpgradeHeight = m_blockchain[upgradeHeight + 1].bl.majorVersion; + if (blockVersionAfterUpgradeHeight != m_targetVersion) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << (upgradeHeight + 1) << + " has invalid version " << blockVersionAfterUpgradeHeight << + ", expected " << static_cast(m_targetVersion); + return false; + } } } return true; } - uint8_t targetVersion() const { return m_targetVersion; } - uint64_t votingCompleteHeight() const { return m_votingCompleteHeight; } + uint8_t targetVersion() const { + return m_targetVersion; + } + uint32_t votingCompleteHeight() const { + return m_votingCompleteHeight; + } - uint64_t upgradeHeight() const { - if (m_currency.upgradeHeight() == UNDEF_HEIGHT) { + uint32_t upgradeHeight() const { + if (m_currency.upgradeHeight(m_targetVersion) == UNDEF_HEIGHT) { return m_votingCompleteHeight == UNDEF_HEIGHT ? UNDEF_HEIGHT : m_currency.calculateUpgradeHeight(m_votingCompleteHeight); } else { - return m_currency.upgradeHeight(); + return m_currency.upgradeHeight(m_targetVersion); } } void blockPushed() { assert(!m_blockchain.empty()); - if (m_currency.upgradeHeight() != UNDEF_HEIGHT) { - if (m_blockchain.size() <= m_currency.upgradeHeight() + 1) { - assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); + if (m_currency.upgradeHeight(m_targetVersion) != UNDEF_HEIGHT) { + if (m_blockchain.size() <= m_currency.upgradeHeight(m_targetVersion) + 1) { + assert(m_blockchain.back().bl.majorVersion <= m_targetVersion - 1); } else { - assert(m_blockchain.back().bl.majorVersion == m_targetVersion); + assert(m_blockchain.back().bl.majorVersion >= m_targetVersion); } } else if (m_votingCompleteHeight != UNDEF_HEIGHT) { @@ -98,33 +125,41 @@ namespace CryptoNote { assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); if (m_blockchain.size() % (60 * 60 / m_currency.difficultyTarget()) == 0) { - logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE is going to happen after height " << upgradeHeight() << "!"; + auto interval = m_currency.difficultyTarget() * (upgradeHeight() - m_blockchain.size() + 2); + time_t upgradeTimestamp = time(nullptr) + static_cast(interval); + struct tm* upgradeTime = localtime(&upgradeTimestamp);; + char upgradeTimeStr[40]; + strftime(upgradeTimeStr, 40, "%H:%M:%S %Y.%m.%d", upgradeTime); + + logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE is going to happen after block index " << upgradeHeight() << " at about " << + upgradeTimeStr << " (in " << Common::timeIntervalToString(interval) << ")! Current last block index " << (m_blockchain.size() - 1) << + ", hash " << get_block_hash(m_blockchain.back().bl); } } else if (m_blockchain.size() == upgradeHeight() + 1) { assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); - logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE has happened! Starting from height " << (upgradeHeight() + 1) << + logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE has happened! Starting from block index " << (upgradeHeight() + 1) << " blocks with major version below " << static_cast(m_targetVersion) << " will be rejected!"; } else { assert(m_blockchain.back().bl.majorVersion == m_targetVersion); } } else { - uint64_t lastBlockHeight = m_blockchain.size() - 1; + uint32_t lastBlockHeight = m_blockchain.size() - 1; if (isVotingComplete(lastBlockHeight)) { m_votingCompleteHeight = lastBlockHeight; - logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE voting complete at height " << m_votingCompleteHeight << - "! UPGRADE is going to happen after height " << upgradeHeight() << "!"; + logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE voting complete at block index " << m_votingCompleteHeight << + "! UPGRADE is going to happen after block index " << upgradeHeight() << "!"; } } } void blockPopped() { if (m_votingCompleteHeight != UNDEF_HEIGHT) { - assert(m_currency.upgradeHeight() == UNDEF_HEIGHT); + assert(m_currency.upgradeHeight(m_targetVersion) == UNDEF_HEIGHT); if (m_blockchain.size() == m_votingCompleteHeight) { - logger(Logging::TRACE, Logging::BRIGHT_YELLOW) << "###### UPGRADE after height " << upgradeHeight() << " has been cancelled!"; + logger(Logging::TRACE, Logging::BRIGHT_YELLOW) << "###### UPGRADE after block index " << upgradeHeight() << " has been canceled!"; m_votingCompleteHeight = UNDEF_HEIGHT; } else { assert(m_blockchain.size() > m_votingCompleteHeight); @@ -132,12 +167,25 @@ namespace CryptoNote { } } + size_t getNumberOfVotes(uint32_t height) { + if (height < m_currency.upgradeVotingWindow() - 1) { + return 0; + } + + size_t voteCounter = 0; + for (size_t i = height + 1 - m_currency.upgradeVotingWindow(); i <= height; ++i) { + const auto& b = m_blockchain[i].bl; + voteCounter += (b.majorVersion == m_targetVersion - 1) && (b.minorVersion == BLOCK_MINOR_VERSION_1) ? 1 : 0; + } + + return voteCounter; + } + private: - uint64_t findVotingCompleteHeight(uint64_t probableUpgradeHeight) { - assert(m_currency.upgradeHeight() == UNDEF_HEIGHT); + uint32_t findVotingCompleteHeight(uint32_t probableUpgradeHeight) { + assert(m_currency.upgradeHeight(m_targetVersion) == UNDEF_HEIGHT); - uint64_t probableVotingCompleteHeight = probableUpgradeHeight > m_currency.maxUpgradeDistance() ? - probableUpgradeHeight - m_currency.maxUpgradeDistance() : 0; + uint32_t probableVotingCompleteHeight = probableUpgradeHeight > m_currency.maxUpgradeDistance() ? probableUpgradeHeight - m_currency.maxUpgradeDistance() : 0; for (size_t i = probableVotingCompleteHeight; i <= probableUpgradeHeight; ++i) { if (isVotingComplete(i)) { return i; @@ -147,21 +195,12 @@ namespace CryptoNote { return UNDEF_HEIGHT; } - bool isVotingComplete(uint64_t height) { - assert(m_currency.upgradeHeight() == UNDEF_HEIGHT); + bool isVotingComplete(uint32_t height) { + assert(m_currency.upgradeHeight(m_targetVersion) == UNDEF_HEIGHT); assert(m_currency.upgradeVotingWindow() > 1); assert(m_currency.upgradeVotingThreshold() > 0 && m_currency.upgradeVotingThreshold() <= 100); - if (height < static_cast(m_currency.upgradeVotingWindow()) - 1) { - return false; - } - - unsigned int voteCounter = 0; - for (size_t i = height + 1 - m_currency.upgradeVotingWindow(); i <= height; ++i) { - const auto& b = m_blockchain[i].bl; - voteCounter += (b.majorVersion == m_targetVersion - 1) && (b.minorVersion == BLOCK_MINOR_VERSION_1) ? 1 : 0; - } - + size_t voteCounter = getNumberOfVotes(height); return m_currency.upgradeVotingThreshold() * m_currency.upgradeVotingWindow() <= 100 * voteCounter; } @@ -170,6 +209,6 @@ namespace CryptoNote { const Currency& m_currency; BC& m_blockchain; uint8_t m_targetVersion; - uint64_t m_votingCompleteHeight; + uint32_t m_votingCompleteHeight; }; } diff --git a/src/Rpc/CoreRpcServerCommandsDefinitions.h b/src/Rpc/CoreRpcServerCommandsDefinitions.h index df3be15..7392923 100644 --- a/src/Rpc/CoreRpcServerCommandsDefinitions.h +++ b/src/Rpc/CoreRpcServerCommandsDefinitions.h @@ -256,6 +256,7 @@ struct COMMAND_RPC_GET_INFO { struct response { std::string status; + std::string version; uint64_t height; uint64_t difficulty; uint64_t tx_count; @@ -271,6 +272,7 @@ struct COMMAND_RPC_GET_INFO { void serialize(ISerializer &s) { KV_MEMBER(status) + KV_MEMBER(version) KV_MEMBER(height) KV_MEMBER(difficulty) KV_MEMBER(tx_count) @@ -299,7 +301,21 @@ struct COMMAND_RPC_STOP_DAEMON { typedef STATUS_STRUCT response; }; -// +//----------------------------------------------- +struct COMMAND_RPC_GET_PEER_LIST { + typedef EMPTY_STRUCT request; + + struct response { + std::vector peers; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(peers) + KV_MEMBER(status) + } + }; +}; + struct COMMAND_RPC_GETBLOCKCOUNT { typedef std::vector request; @@ -441,6 +457,7 @@ struct f_block_short_response { std::string hash; uint64_t tx_count; uint64_t cumul_size; + difficulty_type difficulty; void serialize(ISerializer &s) { KV_MEMBER(timestamp) @@ -448,6 +465,7 @@ struct f_block_short_response { KV_MEMBER(hash) KV_MEMBER(cumul_size) KV_MEMBER(tx_count) + KV_MEMBER(difficulty) } }; @@ -498,71 +516,6 @@ struct f_block_details_response { KV_MEMBER(totalFeeAmount) } }; -struct currency_base_coin { - std::string name; - std::string git; - - void serialize(ISerializer &s) { - KV_MEMBER(name) - KV_MEMBER(git) - } -}; - -struct currency_core { - std::vector SEED_NODES; - uint64_t EMISSION_SPEED_FACTOR; - uint64_t DIFFICULTY_TARGET; - uint64_t CRYPTONOTE_DISPLAY_DECIMAL_POINT; - std::string MONEY_SUPPLY; -//uint64_t GENESIS_BLOCK_REWARD; - uint64_t DEFAULT_DUST_THRESHOLD; - uint64_t MINIMUM_FEE; - uint64_t CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; - uint64_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; -// uint64_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; - uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; - uint64_t P2P_DEFAULT_PORT; - uint64_t RPC_DEFAULT_PORT; - uint64_t MAX_BLOCK_SIZE_INITIAL; - uint64_t EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; - uint64_t UPGRADE_HEIGHT; - uint64_t DIFFICULTY_CUT; - uint64_t DIFFICULTY_LAG; - //std::string BYTECOIN_NETWORK; - std::string CRYPTONOTE_NAME; - std::string GENESIS_COINBASE_TX_HEX; - std::vector CHECKPOINTS; - - void serialize(ISerializer &s) { - KV_MEMBER(SEED_NODES) - KV_MEMBER(EMISSION_SPEED_FACTOR) - KV_MEMBER(DIFFICULTY_TARGET) - KV_MEMBER(CRYPTONOTE_DISPLAY_DECIMAL_POINT) - KV_MEMBER(MONEY_SUPPLY) -// KV_MEMBER(GENESIS_BLOCK_REWARD) - KV_MEMBER(DEFAULT_DUST_THRESHOLD) - KV_MEMBER(MINIMUM_FEE) - KV_MEMBER(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) - KV_MEMBER(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) -// KV_MEMBER(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1) - KV_MEMBER(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) - KV_MEMBER(P2P_DEFAULT_PORT) - KV_MEMBER(RPC_DEFAULT_PORT) - KV_MEMBER(MAX_BLOCK_SIZE_INITIAL) - KV_MEMBER(EXPECTED_NUMBER_OF_BLOCKS_PER_DAY) - KV_MEMBER(UPGRADE_HEIGHT) - KV_MEMBER(DIFFICULTY_CUT) - KV_MEMBER(DIFFICULTY_LAG) -// KV_MEMBER(BYTECOIN_NETWORK) - KV_MEMBER(CRYPTONOTE_NAME) - KV_MEMBER(GENESIS_COINBASE_TX_HEX) - KV_MEMBER(CHECKPOINTS) - } -}; - - - - struct COMMAND_RPC_GET_LAST_BLOCK_HEADER { typedef EMPTY_STRUCT request; @@ -658,24 +611,41 @@ struct F_COMMAND_RPC_GET_TRANSACTION_DETAILS { } }; }; -struct F_COMMAND_RPC_GET_BLOCKCHAIN_SETTINGS { - typedef EMPTY_STRUCT request; - struct response { - currency_base_coin base_coin; - currency_core core; - std::vector extensions; - std::string status; - void serialize(ISerializer &s) { - KV_MEMBER(base_coin) - KV_MEMBER(core) - KV_MEMBER(extensions) - KV_MEMBER(status) - } - }; +//----------------------------------------------- +struct K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID { + struct request { + std::string payment_id; + + void serialize(ISerializer &s) { + KV_MEMBER(payment_id) + } + }; + + struct response { + std::vector transactions; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(transactions) + KV_MEMBER(status) + } + }; }; +struct F_COMMAND_RPC_GET_POOL { + typedef EMPTY_STRUCT request; + struct response { + std::vector transactions; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(transactions) + KV_MEMBER(status) + } + }; +}; struct COMMAND_RPC_QUERY_BLOCKS { struct request { diff --git a/src/Rpc/RpcServer - copy.cpp b/src/Rpc/RpcServer - copy.cpp deleted file mode 100644 index 494c86f..0000000 --- a/src/Rpc/RpcServer - copy.cpp +++ /dev/null @@ -1,651 +0,0 @@ -// Copyright (c) 2011-2016 The Cryptonote developers -// Copyright (c) 2014-2016 SDN developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "RpcServer.h" - -#include -#include - -// CryptoNote -#include "Common/StringTools.h" -#include "CryptoNoteCore/CryptoNoteTools.h" -#include "CryptoNoteCore/Core.h" -#include "CryptoNoteCore/IBlock.h" -#include "CryptoNoteCore/Miner.h" -#include "CryptoNoteCore/TransactionExtra.h" - -#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h" - -#include "P2p/NetNode.h" - -#include "CoreRpcServerErrorCodes.h" -#include "JsonRpc.h" - -#undef ERROR - -using namespace Logging; -using namespace Crypto; -using namespace Common; - -namespace CryptoNote { - -namespace { - -template -RpcServer::HandlerFunction binMethod(bool (RpcServer::*handler)(typename Command::request const&, typename Command::response&)) { - return [handler](RpcServer* obj, const HttpRequest& request, HttpResponse& response) { - - boost::value_initialized req; - boost::value_initialized res; - - if (!loadFromBinaryKeyValue(static_cast(req), request.getBody())) { - return false; - } - - bool result = (obj->*handler)(req, res); - response.setBody(storeToBinaryKeyValue(res.data())); - return result; - }; -} - -template -RpcServer::HandlerFunction jsonMethod(bool (RpcServer::*handler)(typename Command::request const&, typename Command::response&)) { - return [handler](RpcServer* obj, const HttpRequest& request, HttpResponse& response) { - - boost::value_initialized req; - boost::value_initialized res; - - if (!loadFromJson(static_cast(req), request.getBody())) { - return false; - } - - bool result = (obj->*handler)(req, res); - response.setBody(storeToJson(res.data())); - return result; - }; -} - -} - -std::unordered_map> RpcServer::s_handlers = { - - // binary handlers - { "/getblocks.bin", { binMethod(&RpcServer::on_get_blocks), false } }, - { "/queryblocks.bin", { binMethod(&RpcServer::on_query_blocks), false } }, - { "/queryblockslite.bin", { binMethod(&RpcServer::on_query_blocks_lite), false } }, - { "/get_o_indexes.bin", { binMethod(&RpcServer::on_get_indexes), false } }, - { "/getrandom_outs.bin", { binMethod(&RpcServer::on_get_random_outs), false } }, - { "/get_pool_changes.bin", { binMethod(&RpcServer::onGetPoolChanges), false } }, - { "/get_pool_changes_lite.bin", { binMethod(&RpcServer::onGetPoolChangesLite), false } }, - - // json handlers - { "/getinfo", { jsonMethod(&RpcServer::on_get_info), true } }, - { "/getheight", { jsonMethod(&RpcServer::on_get_height), true } }, - { "/gettransactions", { jsonMethod(&RpcServer::on_get_transactions), false } }, - { "/sendrawtransaction", { jsonMethod(&RpcServer::on_send_raw_tx), false } }, - { "/start_mining", { jsonMethod(&RpcServer::on_start_mining), false } }, - { "/stop_mining", { jsonMethod(&RpcServer::on_stop_mining), false } }, - { "/stop_daemon", { jsonMethod(&RpcServer::on_stop_daemon), true } }, - - // json rpc - { "/json_rpc", { std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), true } } -}; - -RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p, const ICryptoNoteProtocolQuery& protocolQuery) : - HttpServer(dispatcher, log), logger(log, "RpcServer"), m_core(c), m_p2p(p2p), m_protocolQuery(protocolQuery) { -} - -void RpcServer::processRequest(const HttpRequest& request, HttpResponse& response) { - auto url = request.getUrl(); - - auto it = s_handlers.find(url); - if (it == s_handlers.end()) { - response.setStatus(HttpResponse::STATUS_404); - return; - } - - if (!it->second.allowBusyCore && !isCoreReady()) { - response.setStatus(HttpResponse::STATUS_500); - response.setBody("Core is busy"); - return; - } - - it->second.handler(this, request, response); -} - -bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& response) { - - using namespace JsonRpc; - - response.addHeader("Content-Type", "application/json"); - - JsonRpcRequest jsonRequest; - JsonRpcResponse jsonResponse; - - try { - logger(TRACE) << "JSON-RPC request: " << request.getBody(); - jsonRequest.parseRequest(request.getBody()); - jsonResponse.setId(jsonRequest.getId()); // copy id - - static std::unordered_map> jsonRpcHandlers = { - { "getblockcount", { makeMemberMethod(&RpcServer::on_getblockcount), true } }, - { "on_getblockhash", { makeMemberMethod(&RpcServer::on_getblockhash), false } }, - { "getblocktemplate", { makeMemberMethod(&RpcServer::on_getblocktemplate), false } }, - { "getcurrencyid", { makeMemberMethod(&RpcServer::on_get_currency_id), true } }, - { "submitblock", { makeMemberMethod(&RpcServer::on_submitblock), false } }, - { "getlastblockheader", { makeMemberMethod(&RpcServer::on_get_last_block_header), false } }, - { "getblockheaderbyhash", { makeMemberMethod(&RpcServer::on_get_block_header_by_hash), false } }, - { "getblockheaderbyheight", { makeMemberMethod(&RpcServer::on_get_block_header_by_height), false } } - }; - - auto it = jsonRpcHandlers.find(jsonRequest.getMethod()); - if (it == jsonRpcHandlers.end()) { - throw JsonRpcError(JsonRpc::errMethodNotFound); - } - - if (!it->second.allowBusyCore && !isCoreReady()) { - throw JsonRpcError(CORE_RPC_ERROR_CODE_CORE_BUSY, "Core is busy"); - } - - it->second.handler(this, jsonRequest, jsonResponse); - - } catch (const JsonRpcError& err) { - jsonResponse.setError(err); - } catch (const std::exception& e) { - jsonResponse.setError(JsonRpcError(JsonRpc::errInternalError, e.what())); - } - - response.setBody(jsonResponse.getBody()); - logger(TRACE) << "JSON-RPC response: " << jsonResponse.getBody(); - return true; -} - -bool RpcServer::isCoreReady() { - return m_core.currency().isTestnet() || m_p2p.get_payload_object().isSynchronized(); -} - -// -// Binary handlers -// - -bool RpcServer::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { - // TODO code duplication see InProcessNode::doGetNewBlocks() - if (req.block_ids.empty()) { - res.status = "Failed"; - return false; - } - - if (req.block_ids.back() != m_core.getBlockIdByHeight(0)) { - res.status = "Failed"; - return false; - } - - uint32_t totalBlockCount; - uint32_t startBlockIndex; - std::vector supplement = m_core.findBlockchainSupplement(req.block_ids, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, totalBlockCount, startBlockIndex); - - res.current_height = totalBlockCount; - res.start_height = startBlockIndex; - - for (const auto& blockId : supplement) { - assert(m_core.have_block(blockId)); - auto completeBlock = m_core.getBlock(blockId); - assert(completeBlock != nullptr); - - res.blocks.resize(res.blocks.size() + 1); - res.blocks.back().block = asString(toBinaryArray(completeBlock->getBlock())); - - res.blocks.back().txs.reserve(completeBlock->getTransactionCount()); - for (size_t i = 0; i < completeBlock->getTransactionCount(); ++i) { - res.blocks.back().txs.push_back(asString(toBinaryArray(completeBlock->getTransaction(i)))); - } - } - - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res) { - uint32_t startHeight; - uint32_t currentHeight; - uint32_t fullOffset; - - if (!m_core.queryBlocks(req.block_ids, req.timestamp, startHeight, currentHeight, fullOffset, res.items)) { - res.status = "Failed to perform query"; - return false; - } - - res.start_height = startHeight; - res.current_height = currentHeight; - res.full_offset = fullOffset; - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request& req, COMMAND_RPC_QUERY_BLOCKS_LITE::response& res) { - uint32_t startHeight; - uint32_t currentHeight; - uint32_t fullOffset; - if (!m_core.queryBlocksLite(req.blockIds, req.timestamp, startHeight, currentHeight, fullOffset, res.items)) { - res.status = "Failed to perform query"; - return false; - } - - res.startHeight = startHeight; - res.currentHeight = currentHeight; - res.fullOffset = fullOffset; - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { - std::vector outputIndexes; - if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) { - res.status = "Failed"; - return true; - } - - res.o_indexes.assign(outputIndexes.begin(), outputIndexes.end()); - res.status = CORE_RPC_STATUS_OK; - logger(TRACE) << "COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"; - return true; -} - -bool RpcServer::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { - res.status = "Failed"; - if (!m_core.get_random_outs_for_amounts(req, res)) { - return true; - } - - res.status = CORE_RPC_STATUS_OK; - - std::stringstream ss; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; - - std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa) { - ss << "[" << ofa.amount << "]:"; - - assert(ofa.outs.size() && "internal error: ofa.outs.size() is empty"); - - std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe) - { - ss << oe.global_amount_index << " "; - }); - ss << ENDL; - }); - std::string s = ss.str(); - logger(TRACE) << "COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s; - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& req, COMMAND_RPC_GET_POOL_CHANGES::response& rsp) { - rsp.status = CORE_RPC_STATUS_OK; - std::vector addedTransactions; - rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, addedTransactions, rsp.deletedTxsIds); - for (auto& tx : addedTransactions) { - BinaryArray txBlob; - if (!toBinaryArray(tx, txBlob)) { - rsp.status = "Internal error"; - break;; - } - - rsp.addedTxs.emplace_back(std::move(txBlob)); - } - return true; -} - - -bool RpcServer::onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request& req, COMMAND_RPC_GET_POOL_CHANGES_LITE::response& rsp) { - rsp.status = CORE_RPC_STATUS_OK; - rsp.isTailBlockActual = m_core.getPoolChangesLite(req.tailBlockId, req.knownTxsIds, rsp.addedTxs, rsp.deletedTxsIds); - - return true; -} - -// -// JSON handlers -// - -bool RpcServer::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { - res.height = m_core.get_current_blockchain_height(); - res.difficulty = m_core.getNextBlockDifficulty(); - res.tx_count = m_core.get_blockchain_total_transactions() - res.height; //without coinbase - res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = m_core.get_alternative_blocks_count(); - uint64_t total_conn = m_p2p.get_connections_count(); - res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); - res.incoming_connections_count = total_conn - res.outgoing_connections_count; - res.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); - res.last_known_block_index = std::max(static_cast(1), m_protocolQuery.getObservedHeight()) - 1; - res.full_deposit_amount = m_core.fullDepositAmount(); - res.full_deposit_interest = m_core.fullDepositInterest(); - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) { - res.height = m_core.get_current_blockchain_height(); - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { - std::vector vh; - for (const auto& tx_hex_str : req.txs_hashes) { - BinaryArray b; - if (!fromHex(tx_hex_str, b)) - { - res.status = "Failed to parse hex representation of transaction hash"; - return true; - } - if (b.size() != sizeof(Hash)) - { - res.status = "Failed, size of data mismatch"; - } - vh.push_back(*reinterpret_cast(b.data())); - } - std::list missed_txs; - std::list txs; - m_core.getTransactions(vh, txs, missed_txs); - - for (auto& tx : txs) { - res.txs_as_hex.push_back(toHex(toBinaryArray(tx))); - } - - for (const auto& miss_tx : missed_txs) { - res.missed_tx.push_back(Common::podToHex(miss_tx)); - } - - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { - BinaryArray tx_blob; - if (!fromHex(req.tx_as_hex, tx_blob)) - { - logger(INFO) << "[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex; - res.status = "Failed"; - return true; - } - - tx_verification_context tvc = boost::value_initialized(); - if (!m_core.handle_incoming_tx(tx_blob, tvc, false)) - { - logger(INFO) << "[on_send_raw_tx]: Failed to process tx"; - res.status = "Failed"; - return true; - } - - if (tvc.m_verifivation_failed) - { - logger(INFO) << "[on_send_raw_tx]: tx verification failed"; - res.status = "Failed"; - return true; - } - - if (!tvc.m_should_be_relayed) - { - logger(INFO) << "[on_send_raw_tx]: tx accepted, but not relayed"; - res.status = "Not relayed"; - return true; - } - - - NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(asString(tx_blob)); - m_core.get_protocol()->relay_transactions(r); - //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) { - AccountPublicAddress adr; - if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) { - res.status = "Failed, wrong address"; - return true; - } - - if (!m_core.get_miner().start(adr, static_cast(req.threads_count))) { - res.status = "Failed, mining not started"; - return true; - } - - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) { - if (!m_core.get_miner().stop()) { - res.status = "Failed, mining not stopped"; - return true; - } - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { - if (m_core.currency().isTestnet()) { - m_p2p.sendStopSignal(); - res.status = CORE_RPC_STATUS_OK; - } else { - res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - return false; - } - return true; -} - -//------------------------------------------------------------------------------------------------------------------------------ -// JSON RPC methods -//------------------------------------------------------------------------------------------------------------------------------ -bool RpcServer::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { - res.count = m_core.get_current_blockchain_height(); - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res) { - if (req.size() != 1) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong parameters, expected height" }; - } - - uint32_t h = static_cast(req[0]); - Crypto::Hash blockId = m_core.getBlockIdByHeight(h); - if (blockId == NULL_HASH) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) - }; - } - - res = Common::podToHex(blockId); - return true; -} - -namespace { - uint64_t slow_memmem(void* start_buff, size_t buflen, void* pat, size_t patlen) - { - void* buf = start_buff; - void* end = (char*)buf + buflen - patlen; - while ((buf = memchr(buf, ((char*)pat)[0], buflen))) - { - if (buf>end) - return 0; - if (memcmp(buf, pat, patlen) == 0) - return (char*)buf - (char*)start_buff; - buf = (char*)buf + 1; - } - return 0; - } -} - -bool RpcServer::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res) { - if (req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE, "To big reserved size, maximum 255" }; - } - - AccountPublicAddress acc = boost::value_initialized(); - - if (!req.wallet_address.size() || !m_core.currency().parseAccountAddressString(req.wallet_address, acc)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS, "Failed to parse wallet address" }; - } - - Block b = boost::value_initialized(); - CryptoNote::BinaryArray blob_reserve; - blob_reserve.resize(req.reserve_size, 0); - if (!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) { - logger(ERROR) << "Failed to create block template"; - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; - } - - BinaryArray block_blob = toBinaryArray(b); - PublicKey tx_pub_key = CryptoNote::getTransactionPublicKeyFromExtra(b.baseTransaction.extra); - if (tx_pub_key == NULL_PUBLIC_KEY) { - logger(ERROR) << "Failed to find tx pub key in coinbase extra"; - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to find tx pub key in coinbase extra" }; - } - - if (0 < req.reserve_size) { - res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); - if (!res.reserved_offset) { - logger(ERROR) << "Failed to find tx pub key in blockblob"; - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; - } - res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) - if (res.reserved_offset + req.reserve_size > block_blob.size()) { - logger(ERROR) << "Failed to calculate offset for reserved bytes"; - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; - } - } else { - res.reserved_offset = 0; - } - - res.blocktemplate_blob = toHex(block_blob); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& /*req*/, COMMAND_RPC_GET_CURRENCY_ID::response& res) { - Hash currencyId = m_core.currency().genesisBlockHash(); - res.currency_id_blob = Common::podToHex(currencyId); - return true; -} - -bool RpcServer::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res) { - if (req.size() != 1) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong param" }; - } - - BinaryArray blockblob; - if (!fromHex(req[0], blockblob)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob" }; - } - - block_verification_context bvc = boost::value_initialized(); - - m_core.handle_incoming_block_blob(blockblob, bvc, true, true); - - if (!bvc.m_added_to_main_chain) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED, "Block not accepted" }; - } - - res.status = CORE_RPC_STATUS_OK; - return true; -} - - -namespace { - uint64_t get_block_reward(const Block& blk) { - uint64_t reward = 0; - for (const TransactionOutput& out : blk.baseTransaction.outputs) { - reward += out.amount; - } - return reward; - } -} - -void RpcServer::fill_block_header_response(const Block& blk, bool orphan_status, uint64_t height, const Hash& hash, block_header_response& responce) { - responce.major_version = blk.majorVersion; - responce.minor_version = blk.minorVersion; - responce.timestamp = blk.timestamp; - responce.prev_hash = Common::podToHex(blk.previousBlockHash); - responce.nonce = blk.nonce; - responce.orphan_status = orphan_status; - responce.height = height; - responce.depth = m_core.get_current_blockchain_height() - height - 1; - responce.hash = Common::podToHex(hash); - m_core.getBlockDifficulty(static_cast(height), responce.difficulty); - responce.reward = get_block_reward(blk); -} - -bool RpcServer::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res) { - uint32_t last_block_height; - Hash last_block_hash; - - m_core.get_blockchain_top(last_block_height, last_block_hash); - - Block last_block; - if (!m_core.getBlockByHash(last_block_hash, last_block)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; - } - - fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header); - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res) { - Hash block_hash; - - if (!parse_hash256(req.hash, block_hash)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse hex representation of block hash. Hex = " + req.hash + '.' }; - } - - Block blk; - if (!m_core.getBlockByHash(block_hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by hash. Hash = " + req.hash + '.' }; - } - - if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: coinbase transaction in the block has the wrong type" }; - } - - uint64_t block_height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; - fill_block_header_response(blk, false, block_height, block_hash, res.block_header); - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res) { - if (m_core.get_current_blockchain_height() <= req.height) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; - } - - Hash block_hash = m_core.getBlockIdByHeight(static_cast(req.height)); - Block blk; - if (!m_core.getBlockByHash(block_hash, blk)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.' }; - } - - fill_block_header_response(blk, false, req.height, block_hash, res.block_header); - res.status = CORE_RPC_STATUS_OK; - return true; -} - - -} diff --git a/src/Rpc/RpcServer.cpp b/src/Rpc/RpcServer.cpp index 9e8f56a..2c9b012 100644 --- a/src/Rpc/RpcServer.cpp +++ b/src/Rpc/RpcServer.cpp @@ -1,9 +1,11 @@ // Copyright (c) 2011-2016 The Cryptonote developers // Copyright (c) 2014-2016 SDN developers +// Copyright (c) 2016 - 2018, The Karbo developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "RpcServer.h" +#include "version.h" #include #include @@ -11,6 +13,7 @@ // CryptoNote #include "Common/StringTools.h" #include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" #include "CryptoNoteCore/Core.h" #include "CryptoNoteCore/IBlock.h" #include "CryptoNoteCore/Miner.h" @@ -88,6 +91,7 @@ std::unordered_map(&RpcServer::on_start_mining), false } }, { "/stop_mining", { jsonMethod(&RpcServer::on_stop_mining), false } }, { "/stop_daemon", { jsonMethod(&RpcServer::on_stop_daemon), true } }, + { "/peers", { jsonMethod(&RpcServer::on_get_peer_list), true } }, // json rpc { "/json_rpc", { std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), true } } @@ -120,6 +124,9 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& using namespace JsonRpc; response.addHeader("Content-Type", "application/json"); + if (!m_cors_domain.empty()) { + response.addHeader("Access-Control-Allow-Origin", m_cors_domain); + } JsonRpcRequest jsonRequest; JsonRpcResponse jsonResponse; @@ -133,7 +140,6 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& { "f_blocks_list_json", { makeMemberMethod(&RpcServer::f_on_blocks_list_json), false } }, { "f_block_json", { makeMemberMethod(&RpcServer::f_on_block_json), false } }, { "f_transaction_json", { makeMemberMethod(&RpcServer::f_on_transaction_json), false } }, - // { "f_get_blockchain_settings", { makeMemberMethod(&RpcServer::f_on_get_blockchain_settings), true } }, { "getblockcount", { makeMemberMethod(&RpcServer::on_getblockcount), true } }, { "on_getblockhash", { makeMemberMethod(&RpcServer::on_getblockhash), false } }, { "getblocktemplate", { makeMemberMethod(&RpcServer::on_getblocktemplate), false } }, @@ -141,7 +147,9 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& { "submitblock", { makeMemberMethod(&RpcServer::on_submitblock), false } }, { "getlastblockheader", { makeMemberMethod(&RpcServer::on_get_last_block_header), false } }, { "getblockheaderbyhash", { makeMemberMethod(&RpcServer::on_get_block_header_by_hash), false } }, - { "getblockheaderbyheight", { makeMemberMethod(&RpcServer::on_get_block_header_by_height), false } } + { "getblockheaderbyheight", { makeMemberMethod(&RpcServer::on_get_block_header_by_height), false } }, + { "f_pool_json", { makeMemberMethod(&RpcServer::f_on_pool_json), false } }, + { "k_transactions_by_payment_id", { makeMemberMethod(&RpcServer::k_on_transactions_by_payment_id), false } } }; auto it = jsonRpcHandlers.find(jsonRequest.getMethod()); @@ -166,6 +174,16 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& return true; } +bool RpcServer::restrictRPC(const bool is_restricted) { + m_restricted_rpc = is_restricted; + return true; +} + +bool RpcServer::enableCors(const std::string domain) { + m_cors_domain = domain; + return true; +} + bool RpcServer::isCoreReady() { return m_core.currency().isTestnet() || m_p2p.get_payload_object().isSynchronized(); } @@ -328,6 +346,7 @@ bool RpcServer::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RP res.last_known_block_index = std::max(static_cast(1), m_protocolQuery.getObservedHeight()) - 1; res.full_deposit_amount = m_core.fullDepositAmount(); res.full_deposit_interest = m_core.fullDepositInterest(); + res.version = PROJECT_VERSION_LONG; res.status = CORE_RPC_STATUS_OK; return true; } @@ -410,6 +429,10 @@ bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMM } bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) { + if (m_restricted_rpc) { + res.status = "Failed, restricted handle"; + return false; + } AccountPublicAddress adr; if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) { res.status = "Failed, wrong address"; @@ -426,6 +449,10 @@ bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, CO } bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) { + if (m_restricted_rpc) { + res.status = "Failed, restricted handle"; + return false; + } if (!m_core.get_miner().stop()) { res.status = "Failed, mining not stopped"; return true; @@ -435,6 +462,10 @@ bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMM } bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { + if (m_restricted_rpc) { + res.status = "Failed, restricted handle"; + return false; + } if (m_core.currency().isTestnet()) { m_p2p.sendStopSignal(); res.status = CORE_RPC_STATUS_OK; @@ -445,6 +476,19 @@ bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMM return true; } +bool RpcServer::on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res) { + std::list pl_wite; + std::list pl_gray; + m_p2p.getPeerlistManager().get_peerlist_full(pl_gray, pl_wite); + for (const auto& pe : pl_wite) { + std::stringstream ss; + ss << pe.adr; + res.peers.push_back(ss.str()); + } + res.status = CORE_RPC_STATUS_OK; + return true; +} + //------------------------------------------------------------------------------------------------------------------------------ // JSON RPC methods //------------------------------------------------------------------------------------------------------------------------------ @@ -472,6 +516,8 @@ bool RpcServer::f_on_blocks_list_json(const F_COMMAND_RPC_GET_BLOCKS_LIST::reque m_core.getBlockSize(block_hash, tx_cumulative_block_size); size_t blokBlobSize = getObjectBinarySize(blk); size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); + difficulty_type blockDiff; + m_core.getBlockDifficulty(static_cast(i), blockDiff); f_block_short_response block_short; block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; @@ -480,6 +526,7 @@ bool RpcServer::f_on_blocks_list_json(const F_COMMAND_RPC_GET_BLOCKS_LIST::reque block_short.hash = Common::podToHex(block_hash); block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; block_short.tx_count = blk.transactionHashes.size() + 1; + block_short.difficulty = blockDiff; res.blocks.push_back(block_short); @@ -494,10 +541,15 @@ bool RpcServer::f_on_blocks_list_json(const F_COMMAND_RPC_GET_BLOCKS_LIST::reque bool RpcServer::f_on_block_json(const F_COMMAND_RPC_GET_BLOCK_DETAILS::request& req, F_COMMAND_RPC_GET_BLOCK_DETAILS::response& res) { Hash hash; - if (!parse_hash256(req.hash, hash)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse hex representation of block hash. Hex = " + req.hash + '.' }; + try { + uint32_t height = boost::lexical_cast(req.hash); + hash = m_core.getBlockIdByHeight(height); + } catch (boost::bad_lexical_cast &) { + if (!parse_hash256(req.hash, hash)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of block hash. Hex = " + req.hash + '.' }; + } } Block blk; @@ -703,6 +755,24 @@ bool RpcServer::f_on_transaction_json(const F_COMMAND_RPC_GET_TRANSACTION_DETAIL return true; } +bool RpcServer::f_on_pool_json(const F_COMMAND_RPC_GET_POOL::request& req, F_COMMAND_RPC_GET_POOL::response& res) { + auto pool = m_core.getPoolTransactions(); + for (const Transaction tx : pool) { + f_transaction_short_response transaction_short; + + uint64_t amount_in = getInputAmount(tx); + uint64_t amount_out = getOutputAmount(tx); + + transaction_short.hash = Common::podToHex(getObjectHash(tx)); + transaction_short.fee = amount_in < amount_out + parameters::MINIMUM_FEE ? parameters::MINIMUM_FEE : amount_in - amount_out; + transaction_short.amount_out = amount_out; + transaction_short.size = getObjectBinarySize(tx); + res.transactions.push_back(transaction_short); + } + res.status = CORE_RPC_STATUS_OK; + return true; +} + bool RpcServer::f_getMixin(const Transaction& transaction, uint64_t& mixin) { mixin = 0; for (const TransactionInput& txin : transaction.inputs) { @@ -717,6 +787,43 @@ bool RpcServer::f_getMixin(const Transaction& transaction, uint64_t& mixin) { return true; } +bool RpcServer::k_on_transactions_by_payment_id(const K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::request& req, K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::response& res) { + if (!req.payment_id.size()) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong parameters, expected payment_id" }; + } + logger(Logging::INFO, Logging::WHITE) << "RPC request came: Search by Payment ID: " << req.payment_id; + + Crypto::Hash paymentId; + std::vector transactions; + + if (!parse_hash256(req.payment_id, paymentId)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse Payment ID: " + req.payment_id + '.' }; + } + + if (!m_core.getTransactionsByPaymentId(paymentId, transactions)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get transactions by Payment ID: " + req.payment_id + '.' }; + } + + for (const Transaction& tx : transactions) { + f_transaction_short_response transaction_short; + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + transaction_short.hash = Common::podToHex(getObjectHash(tx)); + transaction_short.fee = amount_in - amount_out; + transaction_short.amount_out = amount_out; + transaction_short.size = getObjectBinarySize(tx); + res.transactions.push_back(transaction_short); + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} bool RpcServer::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { res.count = m_core.get_current_blockchain_height(); diff --git a/src/Rpc/RpcServer.h b/src/Rpc/RpcServer.h index 90d9bef..1b2a3e4 100644 --- a/src/Rpc/RpcServer.h +++ b/src/Rpc/RpcServer.h @@ -1,5 +1,6 @@ // Copyright (c) 2011-2016 The Cryptonote developers // Copyright (c) 2014-2016 SDN developers +// Copyright (c) 2016 - 2018, The Karbo developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -23,6 +24,8 @@ class RpcServer : public HttpServer { RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p, const ICryptoNoteProtocolQuery& protocolQuery); typedef std::function HandlerFunction; + bool enableCors(const std::string domain); + bool restrictRPC(const bool is_resctricted); private: @@ -56,6 +59,9 @@ class RpcServer : public HttpServer { bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res); bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res); bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res); + bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); + bool f_on_pool_json(const F_COMMAND_RPC_GET_POOL::request& req, F_COMMAND_RPC_GET_POOL::response& res); + bool k_on_transactions_by_payment_id(const K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::request& req, K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::response& res); // json rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); @@ -79,6 +85,8 @@ class RpcServer : public HttpServer { core& m_core; NodeServer& m_p2p; const ICryptoNoteProtocolQuery& m_protocolQuery; + bool m_restricted_rpc; + std::string m_cors_domain; }; } diff --git a/src/SimpleWallet/SimpleWallet.cpp b/src/SimpleWallet/SimpleWallet.cpp index 7211adf..2179908 100644 --- a/src/SimpleWallet/SimpleWallet.cpp +++ b/src/SimpleWallet/SimpleWallet.cpp @@ -137,9 +137,11 @@ struct TransferCommand { std::vector extra; uint64_t fee; std::map> aliases; + std::vector messages; + uint64_t ttl; TransferCommand(const CryptoNote::Currency& currency) : - m_currency(currency), fake_outs_count(0), fee(currency.minimumFee()) { + m_currency(currency), fake_outs_count(0), fee(currency.minimumFee()), ttl(0) { } bool parseArguments(LoggerRef& logger, const std::vector &args) { @@ -155,6 +157,8 @@ struct TransferCommand { return false; } + bool feeFound = false; + bool ttlFound = false; while (!ar.eof()) { auto arg = ar.next(); @@ -169,6 +173,13 @@ struct TransferCommand { return false; } } else if (arg == "-f") { + feeFound = true; + + if (ttlFound) { + logger(ERROR, BRIGHT_RED) << "Transaction with TTL can not have fee"; + return false; + } + bool ok = m_currency.parseAmount(value, fee); if (!ok) { logger(ERROR, BRIGHT_RED) << "Fee value is invalid: " << value; @@ -179,6 +190,23 @@ struct TransferCommand { logger(ERROR, BRIGHT_RED) << "Fee value is less than minimum: " << m_currency.minimumFee(); return false; } + } else if (arg == "-m") { + messages.emplace_back(value); + } else if (arg == "-ttl") { + ttlFound = true; + + if (feeFound) { + logger(ERROR, BRIGHT_RED) << "Transaction with fee can not have TTL"; + return false; + } else { + fee = 0; + } + + if (!Common::fromString(value, ttl) || ttl < 1 || ttl * 60 > m_currency.mempoolTxLiveTime()) { + logger(ERROR, BRIGHT_RED) << "TTL has invalid format: \"" << value << "\", " << + "enter time from 1 to " << (m_currency.mempoolTxLiveTime() / 60) << " minutes"; + return false; + } } } else { WalletLegacyTransfer destination; @@ -1113,6 +1141,18 @@ bool simple_wallet::transfer(const std::vector &args) { } } + std::vector messages; + for (auto dst : cmd.dsts) { + for (auto msg : cmd.messages) { + messages.emplace_back(TransactionMessage{ msg, dst.address }); + } + } + + uint64_t ttl = 0; + if (cmd.ttl != 0) { + ttl = static_cast(time(nullptr)) + cmd.ttl; + } + CryptoNote::WalletHelper::SendCompleteResultObserver sent; std::string extraString; @@ -1120,7 +1160,7 @@ bool simple_wallet::transfer(const std::vector &args) { WalletHelper::IWalletRemoveObserverGuard removeGuard(*m_wallet, sent); - CryptoNote::TransactionId tx = m_wallet->sendTransaction(cmd.dsts, cmd.fee, extraString, cmd.fake_outs_count, 0); + CryptoNote::TransactionId tx = m_wallet->sendTransaction(cmd.dsts, cmd.fee, extraString, cmd.fake_outs_count, 0, messages, ttl); if (tx == WALLET_LEGACY_INVALID_TRANSACTION_ID) { fail_msg_writer() << "Can't send money"; return true; diff --git a/src/Wallet/WalletRpcServer.cpp b/src/Wallet/WalletRpcServer.cpp index b32adfc..80b0afc 100644 --- a/src/Wallet/WalletRpcServer.cpp +++ b/src/Wallet/WalletRpcServer.cpp @@ -24,10 +24,14 @@ namespace Tools { const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = { "rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", 0, true }; const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = { "rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1" }; +const command_line::arg_descriptor wallet_rpc_server::arg_rpc_user = { "rpc-user", "Username to use the rpc server. If authorization is not required, leave it empty", "" }; +const command_line::arg_descriptor wallet_rpc_server::arg_rpc_password = { "rpc-password", "Password to use the rpc server. If authorization is not required, leave it empty", "" }; void wallet_rpc_server::init_options(boost::program_options::options_description& desc) { command_line::add_arg(desc, arg_rpc_bind_ip); command_line::add_arg(desc, arg_rpc_bind_port); + command_line::add_arg(desc, arg_rpc_user); + command_line::add_arg(desc, arg_rpc_password); } //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::wallet_rpc_server( @@ -49,7 +53,7 @@ wallet_rpc_server::wallet_rpc_server( } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run() { - start(m_bind_ip, m_port); + start(m_bind_ip, m_port, m_rpcUser, m_rpcPassword); m_stopComplete.wait(); return true; } @@ -66,6 +70,8 @@ void wallet_rpc_server::send_stop_signal() { bool wallet_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); m_port = command_line::get_arg(vm, arg_rpc_bind_port); + m_rpcUser = command_line::get_arg(vm, arg_rpc_user); + m_rpcPassword = command_line::get_arg(vm, arg_rpc_password); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -93,6 +99,7 @@ void wallet_rpc_server::processRequest(const CryptoNote::HttpRequest& request, C { "getbalance", makeMemberMethod(&wallet_rpc_server::on_getbalance) }, { "transfer", makeMemberMethod(&wallet_rpc_server::on_transfer) }, { "store", makeMemberMethod(&wallet_rpc_server::on_store) }, + { "get_messages", makeMemberMethod(&wallet_rpc_server::on_get_messages) }, { "get_payments", makeMemberMethod(&wallet_rpc_server::on_get_payments) }, { "get_transfers", makeMemberMethod(&wallet_rpc_server::on_get_transfers) }, { "get_height", makeMemberMethod(&wallet_rpc_server::on_get_height) }, @@ -126,11 +133,16 @@ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE: //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res) { std::vector transfers; + std::vector messages; for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { CryptoNote::WalletLegacyTransfer transfer; transfer.address = it->address; transfer.amount = it->amount; transfers.push_back(transfer); + + if (!it->message.empty()) { + messages.emplace_back(CryptoNote::TransactionMessage{ it->message, it->address }); + } } std::vector extra; @@ -151,18 +163,22 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ } } - std::vector messages; for (auto& rpc_message : req.messages) { messages.emplace_back(CryptoNote::TransactionMessage{ rpc_message.message, rpc_message.address }); } + uint64_t ttl = 0; + if (req.ttl != 0) { + ttl = static_cast(time(nullptr)) + req.ttl; + } + std::string extraString; std::copy(extra.begin(), extra.end(), std::back_inserter(extraString)); try { CryptoNote::WalletHelper::SendCompleteResultObserver sent; WalletHelper::IWalletRemoveObserverGuard removeGuard(m_wallet, sent); - CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time, messages); + CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time, messages, ttl); if (tx == WALLET_LEGACY_INVALID_TRANSACTION_ID) { throw std::runtime_error("Couldn't send transaction"); } @@ -194,6 +210,30 @@ bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& r return true; } //------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::on_get_messages(const wallet_rpc::COMMAND_RPC_GET_MESSAGES::request& req, wallet_rpc::COMMAND_RPC_GET_MESSAGES::response& res) { + res.total_tx_count = m_wallet.getTransactionCount(); + + for (uint64_t i = req.first_tx_id; i < res.total_tx_count && res.tx_messages.size() < req.tx_limit; ++i) { + WalletLegacyTransaction tx; + if (!m_wallet.getTransaction(static_cast(i), tx)) { + throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR, "Failed to get transaction"); + } + + if (!tx.messages.empty()) { + wallet_rpc::transaction_messages tx_messages; + tx_messages.tx_hash = Common::podToHex(tx.hash); + tx_messages.tx_id = i; + tx_messages.block_height = tx.blockHeight; + tx_messages.timestamp = tx.timestamp; + std::copy(tx.messages.begin(), tx.messages.end(), std::back_inserter(tx_messages.messages)); + + res.tx_messages.emplace_back(std::move(tx_messages)); + } + } + + return true; +} +//------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res) { PaymentId expectedPaymentId; CryptoNote::BinaryArray payment_id_blob; diff --git a/src/Wallet/WalletRpcServer.h b/src/Wallet/WalletRpcServer.h index ef35a07..a9aed7d 100644 --- a/src/Wallet/WalletRpcServer.h +++ b/src/Wallet/WalletRpcServer.h @@ -41,6 +41,8 @@ namespace Tools static const command_line::arg_descriptor arg_rpc_bind_port; static const command_line::arg_descriptor arg_rpc_bind_ip; + static const command_line::arg_descriptor arg_rpc_user; + static const command_line::arg_descriptor arg_rpc_password; private: @@ -50,6 +52,7 @@ namespace Tools bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res); + bool on_get_messages(const wallet_rpc::COMMAND_RPC_GET_MESSAGES::request& req, wallet_rpc::COMMAND_RPC_GET_MESSAGES::response& res); bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res); bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res); bool on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res); @@ -62,6 +65,8 @@ namespace Tools CryptoNote::INode& m_node; uint16_t m_port; std::string m_bind_ip; + std::string m_rpcUser; + std::string m_rpcPassword; CryptoNote::Currency& m_currency; const std::string m_walletFilename; diff --git a/src/Wallet/WalletRpcServerCommandsDefinitions.h b/src/Wallet/WalletRpcServerCommandsDefinitions.h index e0df18e..9b907fd 100644 --- a/src/Wallet/WalletRpcServerCommandsDefinitions.h +++ b/src/Wallet/WalletRpcServerCommandsDefinitions.h @@ -44,10 +44,12 @@ using CryptoNote::ISerializer; { uint64_t amount; std::string address; + std::string message; void serialize(ISerializer& s) { KV_MEMBER(amount) KV_MEMBER(address) + KV_MEMBER(message) } }; @@ -71,6 +73,7 @@ using CryptoNote::ISerializer; uint64_t unlock_time; std::string payment_id; std::list messages; + uint64_t ttl = 0; void serialize(ISerializer& s) { KV_MEMBER(destinations) @@ -79,6 +82,7 @@ using CryptoNote::ISerializer; KV_MEMBER(unlock_time) KV_MEMBER(payment_id) KV_MEMBER(messages) + KV_MEMBER(ttl) } }; @@ -98,6 +102,44 @@ using CryptoNote::ISerializer; typedef CryptoNote::EMPTY_STRUCT response; }; + struct transaction_messages { + std::string tx_hash; + uint64_t tx_id; + uint32_t block_height; + uint64_t timestamp; + std::list messages; + + void serialize(ISerializer& s) { + KV_MEMBER(tx_hash); + KV_MEMBER(tx_id); + KV_MEMBER(block_height); + KV_MEMBER(timestamp); + KV_MEMBER(messages); + } + }; + + struct COMMAND_RPC_GET_MESSAGES { + struct request { + uint64_t first_tx_id = 0; + uint32_t tx_limit = std::numeric_limits::max(); + + void serialize(ISerializer& s) { + KV_MEMBER(first_tx_id); + KV_MEMBER(tx_limit); + } + }; + + struct response { + uint64_t total_tx_count; + std::list tx_messages; + + void serialize(ISerializer& s) { + KV_MEMBER(total_tx_count); + KV_MEMBER(tx_messages); + } + }; + }; + struct payment_details { std::string tx_hash; diff --git a/src/WalletLegacy/WalletLegacy.cpp b/src/WalletLegacy/WalletLegacy.cpp index b6f9984..9c6132e 100644 --- a/src/WalletLegacy/WalletLegacy.cpp +++ b/src/WalletLegacy/WalletLegacy.cpp @@ -464,12 +464,13 @@ TransactionId WalletLegacy::sendTransaction(const WalletLegacyTransfer& transfer const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp, - const std::vector& messages) { + const std::vector& messages, + uint64_t ttl) { std::vector transfers; transfers.push_back(transfer); throwIfNotInitialised(); - return sendTransaction(transfers, fee, extra, mixIn, unlockTimestamp, messages); + return sendTransaction(transfers, fee, extra, mixIn, unlockTimestamp, messages, ttl); } TransactionId WalletLegacy::sendTransaction(const std::vector& transfers, @@ -477,7 +478,8 @@ TransactionId WalletLegacy::sendTransaction(const std::vector& messages) { + const std::vector& messages, + uint64_t ttl) { TransactionId txId = 0; std::unique_ptr request; std::deque> events; @@ -485,7 +487,7 @@ TransactionId WalletLegacy::sendTransaction(const std::vector lock(m_cacheMutex); - request = m_sender->makeSendRequest(txId, events, transfers, fee, extra, mixIn, unlockTimestamp, messages); + request = m_sender->makeSendRequest(txId, events, transfers, fee, extra, mixIn, unlockTimestamp, messages, ttl); } notifyClients(events); diff --git a/src/WalletLegacy/WalletLegacy.h b/src/WalletLegacy/WalletLegacy.h index 4723139..77c523d 100644 --- a/src/WalletLegacy/WalletLegacy.h +++ b/src/WalletLegacy/WalletLegacy.h @@ -77,13 +77,15 @@ class WalletLegacy : const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, - const std::vector& messages = std::vector()) override; + const std::vector& messages = std::vector(), + uint64_t ttl = 0) override; virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, - const std::vector& messages = std::vector()) override; + const std::vector& messages = std::vector(), + uint64_t ttl = 0) override; virtual TransactionId deposit(uint32_t term, uint64_t amount, uint64_t fee, uint64_t mixIn = 0) override; virtual TransactionId withdrawDeposits(const std::vector& depositIds, uint64_t fee) override; virtual std::error_code cancelTransaction(size_t transactionId) override; diff --git a/src/WalletLegacy/WalletSendTransactionContext.h b/src/WalletLegacy/WalletSendTransactionContext.h index 1c0ea81..2d9131c 100644 --- a/src/WalletLegacy/WalletSendTransactionContext.h +++ b/src/WalletLegacy/WalletSendTransactionContext.h @@ -33,6 +33,7 @@ struct SendTransactionContext TxDustPolicy dustPolicy; uint64_t mixIn; std::vector messages; + uint64_t ttl; uint32_t depositTerm; }; diff --git a/src/WalletLegacy/WalletTransactionSender.cpp b/src/WalletLegacy/WalletTransactionSender.cpp index 9e1815e..e1fab61 100644 --- a/src/WalletLegacy/WalletTransactionSender.cpp +++ b/src/WalletLegacy/WalletTransactionSender.cpp @@ -51,13 +51,13 @@ void createChangeDestinations(const AccountPublicAddress& address, uint64_t need } void constructTx(const AccountKeys keys, const std::vector& sources, const std::vector& splittedDests, - const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, Transaction& tx, const std::vector& messages) { + const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, Transaction& tx, const std::vector& messages, uint64_t ttl) { std::vector extraVec; extraVec.reserve(extra.size()); std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);}); Logging::LoggerGroup nullLog; - bool r = constructTransaction(keys, sources, splittedDests, messages, extraVec, tx, unlockTimestamp, nullLog); + bool r = constructTransaction(keys, sources, splittedDests, messages, ttl, extraVec, tx, unlockTimestamp, nullLog); throwIf(!r, error::INTERNAL_WALLET_ERROR); throwIf(getObjectBinarySize(tx) >= sizeLimit, error::TRANSACTION_SIZE_TOO_BIG); @@ -181,7 +181,8 @@ std::unique_ptr WalletTransactionSender::makeSendRequest(Transact const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp, - const std::vector& messages) { + const std::vector& messages, + uint64_t ttl) { throwIf(transfers.empty(), error::ZERO_DESTINATION); validateTransfersAddresses(transfers); uint64_t neededMoney = countNeededMoney(fee, transfers); @@ -194,6 +195,7 @@ std::unique_ptr WalletTransactionSender::makeSendRequest(Transact transactionId = m_transactionsCache.addNewTransaction(neededMoney, fee, extra, transfers, unlockTimestamp, messages); context->transactionId = transactionId; context->mixIn = mixIn; + context->ttl = ttl; for (const TransactionMessage& message : messages) { AccountPublicAddress address; @@ -331,7 +333,7 @@ std::unique_ptr WalletTransactionSender::doSendTransaction(std::s splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests); Transaction tx; - constructTx(m_keys, sources, splittedDests, transaction.extra, transaction.unlockTime, m_upperTransactionSizeLimit, tx, context->messages); + constructTx(m_keys, sources, splittedDests, transaction.extra, transaction.unlockTime, m_upperTransactionSizeLimit, tx, context->messages, context->ttl); getObjectHash(tx, transaction.hash); diff --git a/src/WalletLegacy/WalletTransactionSender.h b/src/WalletLegacy/WalletTransactionSender.h index 0eacf39..6367060 100644 --- a/src/WalletLegacy/WalletTransactionSender.h +++ b/src/WalletLegacy/WalletTransactionSender.h @@ -32,7 +32,8 @@ class WalletTransactionSender const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, - const std::vector& messages = std::vector()); + const std::vector& messages = std::vector(), + uint64_t ttl = 0); std::unique_ptr makeDepositRequest(TransactionId& transactionId, std::deque>& events, diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index df3a89c..432a138 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -53,7 +53,7 @@ enum { void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash_f(void *, const void *, size_t, void *); +void cn_slow_hash_f(void *, const void *, size_t, void *, int); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index d8a6a65..e51c2e8 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -43,11 +43,11 @@ namespace Crypto { private: void *data; - friend inline void cn_slow_hash(cn_context &, const void *, size_t, Hash &); + friend inline void cn_slow_hash(cn_context &, const void *, size_t, Hash &, int); }; - inline void cn_slow_hash(cn_context &context, const void *data, size_t length, Hash &hash) { - (*cn_slow_hash_f)(context.data, data, length, reinterpret_cast(&hash)); + inline void cn_slow_hash(cn_context &context, const void *data, size_t length, Hash &hash, int variant = 0) { + (*cn_slow_hash_f)(context.data, data, length, reinterpret_cast(&hash), variant); } inline void tree_hash(const Hash *hashes, size_t count, Hash &root_hash) { diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 7d09004..d694c15 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -22,10 +22,10 @@ #include "hash-ops.h" #include "oaes_lib.h" -void (*cn_slow_hash_fp)(void *, const void *, size_t, void *); +void(*cn_slow_hash_fp)(void *, const void *, size_t, void *, int); -void cn_slow_hash_f(void * a, const void * b, size_t c, void * d){ -(*cn_slow_hash_fp)(a, b, c, d); +void cn_slow_hash_f(void * a, const void * b, size_t c, void * d, int v){ + (*cn_slow_hash_fp)(a, b, c, d, v); } #if defined(__GNUC__) diff --git a/src/crypto/slow-hash.inl b/src/crypto/slow-hash.inl index f29d17a..118d4a3 100644 --- a/src/crypto/slow-hash.inl +++ b/src/crypto/slow-hash.inl @@ -3,13 +3,53 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#if defined(WIN32) +#include +#else +#include +#endif + +#define xor64(a, b) *((uint64_t*)a) ^= b + +#define VARIANT1_1(p) \ + do if (variant > 0) \ + { \ + const uint8_t tmp = ((const uint8_t*)(p))[11]; \ + static const uint32_t table = 0x75310; \ + const uint8_t index = (((tmp >> 3) & 6) | (tmp & 1)) << 1; \ + ((uint8_t*)(p))[11] = tmp ^ ((table >> index) & 0x30); \ + } while(0) + +#define VARIANT1_2(p) \ + do if (variant > 0) \ + { \ + xor64(p, tweak1_2); \ + } while(0) + +#define VARIANT1_CHECK() \ + do if (length < 43) \ + { \ + printf("Cryptonight variants need at least 43 bytes of data"); \ + exit(1); \ + } while(0) + +#define NONCE_POINTER (((const uint8_t*)data)+35) + +#define VARIANT1_INIT64() \ + if (variant > 0) \ + { \ + VARIANT1_CHECK(); \ + } \ + const uint64_t tweak1_2 = variant > 0 ? (ctx->state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0 + static void #if defined(AESNI) cn_slow_hash_aesni #else cn_slow_hash_noaesni #endif -(void *restrict context, const void *restrict data, size_t length, void *restrict hash) +(void *restrict context, const void *restrict data, size_t length, void *restrict hash, int variant) { #define ctx ((struct cn_ctx *) context) ALIGNED_DECL(uint8_t ExpandedKey[256], 16); @@ -19,6 +59,9 @@ cn_slow_hash_noaesni hash_process(&ctx->state.hs, (const uint8_t*) data, length); memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE); + + VARIANT1_INIT64(); + #if defined(AESNI) memcpy(ExpandedKey, ctx->state.hs.b, AES_KEY_SIZE); ExpandAESKey256(ExpandedKey); @@ -99,6 +142,8 @@ cn_slow_hash_noaesni b_x = _mm_xor_si128(b_x, c_x); _mm_store_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0], b_x); + VARIANT1_1(&ctx->long_state[a[0] & 0x1FFFF0]); + nextblock = (uint64_t *)&ctx->long_state[c[0] & 0x1FFFF0]; b[0] = nextblock[0]; b[1] = nextblock[1]; @@ -127,6 +172,9 @@ cn_slow_hash_noaesni a[0] ^= b[0]; a[1] ^= b[1]; + + VARIANT1_2(dst + 1); + b_x = c_x; //__builtin_prefetch(&ctx->long_state[a[0] & 0x1FFFF0], 0, 3); } diff --git a/src/version.h.in b/src/version.h.in index 802686f..5e3d5a6 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.1.1" -#define PROJECT_VERSION_BUILD_NO "cedi0010" +#define PROJECT_VERSION "0.1.2" +#define PROJECT_VERSION_BUILD_NO "ctl0011" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/tests/CoreTests/BlockValidation.cpp b/tests/CoreTests/BlockValidation.cpp index dcaa79b..4bc5094 100644 --- a/tests/CoreTests/BlockValidation.cpp +++ b/tests/CoreTests/BlockValidation.cpp @@ -26,7 +26,7 @@ namespace { CryptoNote::Block blk_prev = blk_last; for (size_t i = 0; i < new_block_count; ++i) { CryptoNote::Block blk_next; - CryptoNote::difficulty_type diffic = currency.nextDifficulty(timestamps, cummulative_difficulties); + CryptoNote::difficulty_type diffic = currency.nextDifficulty(block_major_version, timestamps, cummulative_difficulties); if (!generator.constructBlockManually(blk_next, blk_prev, miner_account, test_generator::bf_major_ver | test_generator::bf_timestamp | test_generator::bf_diffic, block_major_version, 0, blk_prev.timestamp, Crypto::Hash(), diffic)) { @@ -190,7 +190,7 @@ bool gen_block_invalid_nonce::generate(std::vector& events) co } // Create invalid nonce - difficulty_type diffic = m_currency.nextDifficulty(timestamps, commulative_difficulties); + difficulty_type diffic = m_currency.nextDifficulty(m_blockMajorVersion, timestamps, commulative_difficulties); assert(1 < diffic); const Block& blk_last = boost::get(events.back()); uint64_t timestamp = blk_last.timestamp; @@ -583,7 +583,7 @@ gen_block_invalid_binary_format::gen_block_invalid_binary_format(uint8_t blockMa m_corrupt_blocks_begin_idx(0), m_blockMajorVersion(blockMajorVersion) { CryptoNote::CurrencyBuilder currencyBuilder(m_logger); - currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : 0); + currencyBuilder.upgradeHeightV2(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : 0); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK("check_all_blocks_purged", gen_block_invalid_binary_format::check_all_blocks_purged); @@ -616,7 +616,7 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev do { blk_last = boost::get(events.back()); - diffic = m_currency.nextDifficulty(timestamps, cummulative_difficulties); + diffic = m_currency.nextDifficulty(m_blockMajorVersion, timestamps, cummulative_difficulties); if (!lift_up_difficulty(m_currency, events, timestamps, cummulative_difficulties, generator, 1, blk_last, miner_account, m_blockMajorVersion)) { return false; @@ -633,7 +633,7 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev std::vector tx_hashes; tx_hashes.push_back(getObjectHash(tx_0)); size_t txs_size = getObjectBinarySize(tx_0); - diffic = m_currency.nextDifficulty(timestamps, cummulative_difficulties); + diffic = m_currency.nextDifficulty(m_blockMajorVersion, timestamps, cummulative_difficulties); if (!generator.constructBlockManually(blk_test, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, m_blockMajorVersion, 0, blk_last.timestamp, Crypto::Hash(), diffic, Transaction(), tx_hashes, txs_size)) diff --git a/tests/CoreTests/BlockValidation.h b/tests/CoreTests/BlockValidation.h index 7618b37..7994b63 100644 --- a/tests/CoreTests/BlockValidation.h +++ b/tests/CoreTests/BlockValidation.h @@ -17,7 +17,7 @@ class CheckBlockPurged : public test_chain_unit_base { assert(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_2); CryptoNote::CurrencyBuilder currencyBuilder(m_logger); - currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + currencyBuilder.upgradeHeightV2(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK("check_block_purged", CheckBlockPurged::check_block_purged); @@ -60,7 +60,7 @@ struct CheckBlockAccepted : public test_chain_unit_base { assert(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_2); CryptoNote::CurrencyBuilder currencyBuilder(m_logger); - currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + currencyBuilder.upgradeHeightV2(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK("check_block_accepted", CheckBlockAccepted::check_block_accepted); @@ -287,7 +287,7 @@ struct gen_block_is_too_big : public CheckBlockPurged gen_block_is_too_big(uint8_t blockMajorVersion) : CheckBlockPurged(1, blockMajorVersion) { CryptoNote::CurrencyBuilder currencyBuilder(m_logger); - currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + currencyBuilder.upgradeHeightV2(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); m_currency = currencyBuilder.currency(); } diff --git a/tests/CoreTests/Deposit.h b/tests/CoreTests/Deposit.h index cf5c81d..0feae81 100644 --- a/tests/CoreTests/Deposit.h +++ b/tests/CoreTests/Deposit.h @@ -14,7 +14,7 @@ namespace DepositTests { struct DepositTestsBase : public test_chain_unit_base { DepositTestsBase() { - m_currency = CryptoNote::CurrencyBuilder(m_logger).upgradeHeight(0).depositMinTerm(10).depositMinTotalRateFactor(100).currency(); + m_currency = CryptoNote::CurrencyBuilder(m_logger).upgradeHeightV2(0).depositMinTerm(10).depositMinTotalRateFactor(100).currency(); from.generate(); to.generate(); REGISTER_CALLBACK_METHOD(DepositTestsBase, mark_invalid_block); @@ -64,7 +64,7 @@ struct DepositIndexTest : public DepositTestsBase { using Core = CryptoNote::core; using Events = std::vector; DepositIndexTest() { - m_currency = CryptoNote::CurrencyBuilder(m_logger).upgradeHeight(0).depositMinTerm(10).depositMinTotalRateFactor(100).mininumFee(1000).currency(); + m_currency = CryptoNote::CurrencyBuilder(m_logger).upgradeHeightV2(0).depositMinTerm(10).depositMinTotalRateFactor(100).mininumFee(1000).currency(); REGISTER_CALLBACK_METHOD(DepositIndexTest, interestZero); REGISTER_CALLBACK_METHOD(DepositIndexTest, interestOneMinimal); REGISTER_CALLBACK_METHOD(DepositIndexTest, interestTwoMininmal); @@ -100,7 +100,7 @@ struct DepositIndexTest : public DepositTestsBase { struct EmissionTest : public DepositTestsBase { EmissionTest() { - m_currency = CryptoNote::CurrencyBuilder(m_logger).upgradeHeight(0).depositMinTerm(10).depositMinTotalRateFactor(100).currency(); + m_currency = CryptoNote::CurrencyBuilder(m_logger).upgradeHeightV2(0).depositMinTerm(10).depositMinTotalRateFactor(100).currency(); REGISTER_CALLBACK_METHOD(EmissionTest, save_emission_before); REGISTER_CALLBACK_METHOD(EmissionTest, save_emission_after); } diff --git a/tests/CoreTests/DoubleSpend.cpp b/tests/CoreTests/DoubleSpend.cpp index bb3a92f..95cb200 100644 --- a/tests/CoreTests/DoubleSpend.cpp +++ b/tests/CoreTests/DoubleSpend.cpp @@ -79,7 +79,7 @@ DoubleSpendBase::DoubleSpendBase() : send_amount(MK_COINS(17)), has_invalid_tx(false) { - m_currency = CurrencyBuilder(m_logger).upgradeHeight(0).currency(); + m_currency = CurrencyBuilder(m_logger).upgradeHeightV2(0).currency(); m_outputTxKey = generateKeyPair(); m_bob_account.generate(); m_alice_account.generate(); diff --git a/tests/CoreTests/TransactionValidation.cpp b/tests/CoreTests/TransactionValidation.cpp index 8bd5d0d..056e6e5 100644 --- a/tests/CoreTests/TransactionValidation.cpp +++ b/tests/CoreTests/TransactionValidation.cpp @@ -701,7 +701,7 @@ bool GenerateTransactionWithZeroFee::generate(std::vector& eve MultiSigTx_OutputSignatures::MultiSigTx_OutputSignatures(size_t givenKeys, uint32_t requiredSignatures, bool shouldSucceed) : m_givenKeys(givenKeys), m_requiredSignatures(requiredSignatures), m_shouldSucceed(shouldSucceed) { - m_currency = CurrencyBuilder(m_logger).upgradeHeight(0).currency(); + m_currency = CurrencyBuilder(m_logger).upgradeHeightV2(0).currency(); for (size_t i = 0; i < m_givenKeys; ++i) { AccountBase acc; @@ -832,7 +832,7 @@ MultiSigTx_Input::MultiSigTx_Input( m_givenSignatures(givenSignatures), m_inputShouldSucceed(inputShouldSucceed) { - m_currency = CurrencyBuilder(m_logger).upgradeHeight(0).currency(); + m_currency = CurrencyBuilder(m_logger).upgradeHeightV2(0).currency(); } bool MultiSigTx_Input::generate(std::vector& events) const { diff --git a/tests/CoreTests/Upgrade.cpp b/tests/CoreTests/Upgrade.cpp index e575bc9..0d542d4 100644 --- a/tests/CoreTests/Upgrade.cpp +++ b/tests/CoreTests/Upgrade.cpp @@ -34,7 +34,8 @@ namespace { gen_upgrade::gen_upgrade() : m_invalidBlockIndex(0), m_checkBlockTemplateVersionCallCounter(0) { CryptoNote::CurrencyBuilder currencyBuilder(m_logger); currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); - currencyBuilder.upgradeHeight(UpgradeDetectorBase::UNDEF_HEIGHT); + currencyBuilder.upgradeHeightV2(UpgradeDetectorBase::UNDEF_HEIGHT); + currencyBuilder.upgradeHeightV3(UpgradeDetectorBase::UNDEF_HEIGHT); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK_METHOD(gen_upgrade, markInvalidBlock); @@ -191,8 +192,8 @@ bool gen_upgrade::checkBlockTemplateVersion(CryptoNote::core& c, uint8_t expecte difficulty_type diff; uint32_t height; CHECK_TEST_CONDITION(c.get_block_template(b, account.getAccountKeys().address, diff, height, BinaryArray())); - CHECK_EQ(b.majorVersion, expectedMajorVersion); - CHECK_EQ(b.minorVersion, expectedMinorVersion); + CHECK_EQ(static_cast(b.majorVersion), static_cast(expectedMajorVersion)); + CHECK_EQ(static_cast(b.minorVersion), static_cast(expectedMinorVersion)); return true; } diff --git a/tests/Difficulty/Difficulty.cpp b/tests/Difficulty/Difficulty.cpp index a54efe1..982a8bc 100644 --- a/tests/Difficulty/Difficulty.cpp +++ b/tests/Difficulty/Difficulty.cpp @@ -45,6 +45,7 @@ int main(int argc, char *argv[]) { begin = end - currency.difficultyWindow(); } uint64_t res = currency.nextDifficulty( + CryptoNote::BLOCK_MAJOR_VERSION_1, vector(timestamps.begin() + begin, timestamps.begin() + end), vector(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end)); if (res != difficulty) { diff --git a/tests/UnitTests/TestUpgradeDetector.cpp b/tests/UnitTests/TestUpgradeDetector.cpp index 43c5ed9..e66a238 100644 --- a/tests/UnitTests/TestUpgradeDetector.cpp +++ b/tests/UnitTests/TestUpgradeDetector.cpp @@ -33,7 +33,8 @@ namespace { currencyBuilder.upgradeVotingThreshold(90); currencyBuilder.upgradeVotingWindow(720); currencyBuilder.upgradeWindow(720); - currencyBuilder.upgradeHeight(upgradeHeight); + currencyBuilder.upgradeHeightV2(upgradeHeight); + currencyBuilder.upgradeHeightV3(UpgradeDetector::UNDEF_HEIGHT); return currencyBuilder.currency(); } @@ -153,14 +154,14 @@ namespace { // Upgrade to v2 is here createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); - createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_1); + createBlocks(blocks, currency.upgradeVotingWindow() * currency.upgradeVotingThreshold() / 100, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_1); uint64_t votingCompleteHeigntV3 = blocks.size() - 1; uint64_t upgradeHeightV3 = currency.calculateUpgradeHeight(votingCompleteHeigntV3); createBlocks(blocks, upgradeHeightV3 - blocks.size(), BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); // Upgrade to v3 is here createBlocks(blocks, 1, BLOCK_V3, BLOCK_MINOR_VERSION_0); - createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_V3, BLOCK_MINOR_VERSION_1); + createBlocks(blocks, currency.upgradeVotingWindow() * currency.upgradeVotingThreshold() / 100, BLOCK_V3, BLOCK_MINOR_VERSION_1); uint64_t votingCompleteHeigntV4 = blocks.size() - 1; uint64_t upgradeHeightV4 = currency.calculateUpgradeHeight(votingCompleteHeigntV4); createBlocks(blocks, upgradeHeightV4 - blocks.size(), BLOCK_V3, BLOCK_MINOR_VERSION_0);