diff --git a/CMakeLists.txt b/CMakeLists.txt index ee04f6d2..c613e272 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,14 @@ # Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. # Licensed under the GNU Lesser General Public License. See LICENSE for details. -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.0) project(bytecoin) set(CMAKE_CXX_STANDARD 14) set(CMAKE_C_STANDARD 11) # message(STATUS "Bytecoind profile: According to cmake, sizeof(void *) == " ${CMAKE_SIZEOF_VOID_P}) option(USE_INSTRUMENTATION "For testing - builds with address sanitizer instrument" OFF) option(WITH_THREAD_SANITIZER "For testing - builds with thread sanitizer instrument, USE_INSTRUMENTATION must be also set" OFF) +option(BETTER_DEBUG "Disables optimizations. We do not use standard debug/realease configurations because they change too much" OFF) option(USE_SSL "Builds with support of https between walletd and bytecoind" ON) if(CMAKE_SIZEOF_VOID_P EQUAL 8) option(USE_SQLITE "Builds with SQLite instead of LMDB. 4x slower, but works on 32-bit and mobile platforms" OFF) @@ -17,12 +18,20 @@ else() set(OPENSSL_ROOT ../openssl32) endif() if(WIN32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") add_definitions(-D_SCL_SECURE_NO_WARNINGS=1 -D_CRT_SECURE_NO_WARNINGS=1 -D_WIN32_WINNT=0x0501) else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes -g -O2 -Wall -Wextra -Werror=return-type -Wno-unused-parameter") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes -g -O2 -Wall -Wextra -Werror=return-type -Wno-unused-parameter") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes -g -Wall -Wextra -Werror=return-type -Wno-unused-parameter") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes -g -Wall -Wextra -Werror=return-type -Wno-unused-parameter") + if(BETTER_DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") + endif() + message(STATUS "Better debug: " ${BETTER_DEBUG}) message(STATUS "Instrumentation usage: " ${USE_INSTRUMENTATION}) message(STATUS "Thread sanitizer usage: " ${WITH_THREAD_SANITIZER}) if(USE_INSTRUMENTATION) @@ -58,32 +67,22 @@ else() message(STATUS "SSL usage: OFF") endif() file(GLOB SRC_CRYPTO - src/crypto/*.cpp src/crypto/*.hpp src/crypto/*.h - src/crypto/blake256.c - src/crypto/chacha8.c - src/crypto/crypto-ops-data.c - src/crypto/crypto-ops.c - src/crypto/crypto-util.c - src/crypto/groestl.c - src/crypto/hash-extra-blake.c - src/crypto/hash-extra-groestl.c - src/crypto/hash-extra-jh.c - src/crypto/hash-extra-skein.c - src/crypto/hash.c - src/crypto/jh.c - src/crypto/keccak.c - src/crypto/oaes_lib.c - src/crypto/random.c - src/crypto/skein.c - src/crypto/slow-hash_x86.c - src/crypto/tree-hash.c + src/crypto/*.cpp src/crypto/*.hpp src/crypto/*.h + src/crypto/*.c src/crypto/*.h + src/crypto/bernstein/*.h src/crypto/bernstein/*.c + src/crypto/blake/*.h src/crypto/blake/*.c + src/crypto/groestl/*.h src/crypto/groestl/*.c + src/crypto/jh/*.h src/crypto/jh/*.c + src/crypto/keccak/*.c + src/crypto/oaes/*.h src/crypto/oaes/*.c + src/crypto/skein/*.h src/crypto/skein/*.c ) file(GLOB SRC_COMMON src/common/*.cpp src/common/*.hpp) file(GLOB SRC_SERIALIZATION src/Serialization/*.cpp src/Serialization/*.hpp) file(GLOB SRC_SERIA src/seria/*.cpp src/seria/*.hpp) file(GLOB SRC_LOGGING src/logging/*.cpp src/logging/*.hpp) file(GLOB SRC_P2P src/p2p/*.cpp src/p2p/*.hpp) -file(GLOB SRC_CORE src/Core/*.cpp src/Core/*.hpp) +file(GLOB SRC_CORE src/Core/*.cpp src/Core/*.hpp src/CryptoNote.hpp src/CryptoNote.cpp src/rpc_api.hpp src/rpc_api.cpp) file(GLOB SRC_HTTP src/http/*.cpp src/http/*.hpp) file(GLOB SRC_PLATFORM src/platform/ExclusiveLock.cpp src/platform/ExclusiveLock.hpp diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 1037401f..b1358fb9 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,5 +1,95 @@ ## Release Notes +### v3.3.0 + +*Consensus update (hard fork)* + +- Voting starts immediately, once 90% of mined blocks contain votes for update, the update height will be automatically selected so that consensus update will happen approximately 2 weeks after that. +- Market fees - any transaction fee including 0 is now legal for all transactions. Miners will increase block size only if it is profitable for them in a short run. + +*General improvements* + +- Better priority and exclusive nodes logic. +- Seed nodes are now contacted approximately once per day (greatly helps to catch up after `bytecoind` is started for users who run it after delay of several weeks or more). +- Binary methods now share single access point `/binary_rpc`. +- Code for consensus upgrade voting correctly counts votes on both main chain and each side chain. +- Limited on incoming connections (default is 100). +- Fixed ignored external port on peer handshake (made connects through exposed non-standard ports to nodes behind NAT impossible). +- Groestl hash function is updated from the official source. +- Keccak permutation function is updated from the official source. +- `bytecoind` never searches for `blocks.bin` and `blockindex.bin` outside the data folder. +- now when you specify `--p2p-bind-address`, but not `--p2p-external-port`, p2p external port will be set to p2p bind port. When you wish NAT tunneling, good practice is to specify both. + +*Command line changes/additions* + +- Paranoid mode to check every byte of blockchain when downloading (usually checks only blocks beyond the last checkpoint). +- *Warning:* Now `walletd` exits by default after `--create-wallet` and `--set-password` operations. This change can break your scripts. If you need the wallet running after those commands, you can add `--launch-after-command` parameter +- Now you can use `--set-password` with `--export-view-only` and `--backup-wallet-data`, to encrypt result wallet with a different password. +- Fixed bug with `walletd` not returning `api::WALLETD_BIND_PORT_IN_USE` error code when the JSON API port is in use and using inproc `bytecoind`. +- Fixed bug with `bytecoind` not returning `api::BYTECOIND_BIND_PORT_IN_USE` error code when the JSON API port is in use. +- `walletd` now prints address after creation of wallet. +- `walletd` now prints deprecation warning when using inproc `bytecoind`. +- `walletd` now binds to port `9070` on testnet and `10070` on stagenet resp. by default. + +*General API improvements* + +- Optimisation of JSON RPC calls (2x speed up on very large responses). +- Made the `jsonrpc` argument mandatory with value "2.0" in all JSON RPC calls according to the spec. +- JSON RPC `id` is required now according to spec. +- JSON RPC error's additional data moved into `data` object inside the `error` object according to spec. +- Now any field in requests that daemons do not understand will be reported as a error. +- Much better error handling, more specific error codes. + +*Specific API improvements* + +- New `get_block_header` method for blockchain structure inspection (to replace `getblockheaderbyhash`, `getblockheaderbyheight`, and `getlastblockheader` legacy methods). +- New `get_wallet_info` method. +- New `VIEW_ONLY_WALLET` (`-304`) error code, returned from `create_transaction`. +- In methods supporting longpoll (`get_statu`s and `get_block_template`) all longpoll arguments are now optional. So, for example, if you are interested in `outgoing_peer_count` only, you can specify only `outgoing_peer_count` in request and get response when `outgoing_peer_count` changes. Changes to other fields will not trigger response to longpoll. +- New `ADDRESS_FAILED_TO_PARSE` (`-4`) and `ADDRESS_NOT_IN_WALLET` (`-1002`) error codes, returned from lots of methods +- New fields in get_addresses request/response to iterate through list of addresses +- Now `top_block_timestamp_median` returned correctly from get_status JSON RPC methods. +- New `need_signatures` fields in APIs returning raw transactions. +- `check_sendproof` now returns values from sendproof in response if proof is valid. +- All methods return new correct values for `block_size` and `transactions_cumulative_size`. +- `get_raw_block` and `get_block_header` now return `orphan_status` and `depth` (consistent with `height_or_depth` fields where top block is `-1`). +- `get_random_amounts` has no more depth limit of 128 block (distribution would be skewed a bit for very large depths). +- `get_statistics` response now includes much more information. +- `submit_block` now returns `block_header` in result. +- All `transfer` objects now have `transaction_hash` field - especially useful when processing `unlocked_transfers` in result of `get_transfers` method. + +*API deprecations (will be removed in version 3.4.0)* + +- In all `output` and `transaction` objects `unlock_time` is deprecated (renamed to `unlock_block_or_timestamp`). +- In all `output` objects `global_index` deprecated (renamed to `index`). +- In `get_random_outputs` request `outs_count` is deprecated (renamed to `output_count`). +- In `get_transfers` request `desired_transactions_count` is deprecated (renamed to `desired_transaction_count`). +- In all `transaction` objects 'binary_size' is deprecated (renamed to `size`). + +*Incompatible API changes* + +- `get_raw_transaction` method now returns json error `-5` if transaction not found. +- Deprecated `prev_hash` field remains only in result of legacy methods (`getblockheaderbyhash`, `getblockheaderbyheight`, and `getlastblockheader` legacy methods), use 'previous_block_hash' instead. +- Deprecated `total_fee_amount` field remains only in result of legacy methods (`getblockheaderbyhash`, `getblockheaderbyheight`, and `getlastblockheader`), use `transactions_fee` instead. +- Deprecated `transactions_cumulative_size` field remains only in result of legacy methods (`getblockheaderbyhash`, `getblockheaderbyheight`, and `getlastblockheader`), use `transactions_size` instead. + +*Incompatible API changes (likely to affect only developers of block explorers)* + +- In all raw block objects `global_indices` renamed to `output_indexes`. +- In all raw transaction objects `vin`, `vout` renamed to `inputs`, `outputs` resp. +- In all raw output objects `key` renamed to `public_key`. +- In all raw output objects `target` object removed and all its fields moved into raw output object. +- In all raw coinbase input objects `block_index` renamed to `height`. +- In all raw header objects (including `parent_block`) `miner_tx`, `base_transaction_branch` renamed to `coinbase_transaction`, `coinbase_transaction_branch` resp. +- In all raw input and raw output objects, `tag`:`ff` renamed to `type`:`coinbase` and `tag`:`02` renamed to `type`:`key`. + +*Testnet/Stagenet related* + +- New command line parameter `--net=test|stage|main` configures daemons for testnet, stagenet, or mainnet resp. +- For testnet time multiplier can now be set to speed up all processes 10x, 100x or even more. +- When participating in testnet or stagenet, `bytecoind` now uses UDP Multicast to announce/discover other bytecoind nodes in local network. Thus in most local networks testnet will self-assemble without seed nodes. In mainnet multicasts are disabled due to anonymity concerns. +- Testnet/Stagenet now have fixed 1MB max block size limit. + ### v3.2.4 - Added the testnet functionality. @@ -97,7 +187,6 @@ - Added early support of JSON-RPC API basic authentification that prevents CSRF attacks. - Added (experimental) support of 32-bit platforms. - ### v3.0.0-beta-20180206 - Project is moved to the new public GitHub repository. diff --git a/src/Core/Archive.cpp b/src/Core/Archive.cpp index 4d857a02..59f31e71 100644 --- a/src/Core/Archive.cpp +++ b/src/Core/Archive.cpp @@ -23,12 +23,12 @@ const std::string Archive::BLOCK("b"); const std::string Archive::TRANSACTION("t"); const std::string Archive::CHECKPOINT("c"); -//static const float DB_COMMIT_PERIOD = 60; // 1 minute sounds good for archive +// static const float DB_COMMIT_PERIOD = 60; // 1 minute sounds good for archive Archive::Archive(bool read_only, const std::string &path) - : read_only(read_only) + : read_only(read_only) // , commit_timer(std::bind(&Archive::db_commit, this)) - { +{ #if !platform_USE_SQLITE try { m_db = std::make_unique(read_only, path); @@ -49,7 +49,7 @@ Archive::Archive(bool read_only, const std::string &path) throw; } #endif -// commit_timer.once(DB_COMMIT_PERIOD); + // commit_timer.once(DB_COMMIT_PERIOD); } // struct Record { @@ -83,17 +83,16 @@ void Archive::db_commit() { if (!m_db || read_only) return; m_db->commit_db_txn(); -// commit_timer.once(DB_COMMIT_PERIOD); + // commit_timer.once(DB_COMMIT_PERIOD); } void Archive::read_archive(api::bytecoind::GetArchive::Request &&req, api::bytecoind::GetArchive::Response &resp) { - if (req.archive_id != unique_id) { - api::bytecoind::GetArchive::Error err; - err.code = api::bytecoind::GetArchive::WRONG_ARCHIVE_ID; - err.message = "Archive id changed"; - err.archive_id = unique_id; - throw err; - } + if (unique_id.empty()) + throw api::bytecoind::GetArchive::Error( + api::bytecoind::GetArchive::ARCHIVE_NOT_ENABLED, "Archive was never enabled on this bytecoind", unique_id); + if (req.archive_id != unique_id) + throw api::bytecoind::GetArchive::Error( + api::bytecoind::GetArchive::WRONG_ARCHIVE_ID, "Archive id changed", unique_id); resp.from_record = req.from_record; if (resp.from_record > next_record_id) resp.from_record = next_record_id; @@ -109,7 +108,7 @@ void Archive::read_archive(api::bytecoind::GetArchive::Request &&req, api::bytec api::bytecoind::GetArchive::ArchiveRecord rec; seria::from_binary(rec, cur.get_value_array()); resp.records.push_back(rec); - if(req.records_only) + if (req.records_only) continue; std::string str_hash = common::pod_to_hex(rec.hash); const auto hash_key = HASHES_PREFIX + DB::to_binary_key(rec.hash.data, sizeof(rec.hash.data)); @@ -124,6 +123,9 @@ void Archive::read_archive(api::bytecoind::GetArchive::Request &&req, api::bytec invariant(block.from_raw_block(raw_block), ""); bl.raw_header = block.header; bl.raw_transactions.reserve(block.transactions.size()); + bl.transaction_binary_sizes.reserve(block.transactions.size() + 1); + auto coinbase_size = static_cast(seria::binary_size(block.header.base_transaction)); + bl.transaction_binary_sizes.push_back(coinbase_size); for (size_t i = 0; i != block.transactions.size(); ++i) { bl.raw_transactions.push_back(static_cast(block.transactions.at(i))); bl.transaction_binary_sizes.push_back(static_cast(raw_block.transactions.at(i).size())); @@ -145,7 +147,7 @@ void Archive::read_archive(api::bytecoind::GetArchive::Request &&req, api::bytec if (resp.checkpoints.count(str_hash) == 0) { BinaryArray data; invariant(m_db->get(hash_key, data), ""); - SignedCheckPoint &ch = resp.checkpoints[str_hash]; + SignedCheckpoint &ch = resp.checkpoints[str_hash]; seria::from_binary(ch, data); } } diff --git a/src/Core/Archive.hpp b/src/Core/Archive.hpp index f1cb6ec0..846ddca0 100644 --- a/src/Core/Archive.hpp +++ b/src/Core/Archive.hpp @@ -15,7 +15,7 @@ class Archive { uint64_t next_record_id = 0; std::string unique_id; -// platform::Timer commit_timer; + // platform::Timer commit_timer; public: explicit Archive(bool read_only, const std::string &path); std::string get_unique_id() const { return unique_id; } diff --git a/src/Core/BlockChain.cpp b/src/Core/BlockChain.cpp index 61883475..5ff7f8e1 100644 --- a/src/Core/BlockChain.cpp +++ b/src/Core/BlockChain.cpp @@ -8,8 +8,8 @@ #include "Config.hpp" #include "CryptoNoteTools.hpp" #include "Currency.hpp" -#include "DifficultyCheck.hpp" #include "TransactionExtra.hpp" +#include "common/Math.hpp" #include "common/StringTools.hpp" #include "common/Varint.hpp" #include "crypto/crypto.hpp" @@ -39,7 +39,7 @@ static const std::string CD_TIPS_PREFIX = "x-tips/"; // We store bid->children counter, with counter=1 default (absent from index) // We store cumulative_difficulty->bid for bids with no children -static const size_t COMMIT_EVERY_N_BLOCKS = 50000; // We do not want to create too large transactions +static const size_t COMMIT_EVERY_N_BLOCKS = 50000; // We do not want to create too large DB transactions static const std::string delete_blockchain_message = "database corrupted, please delete "; @@ -76,28 +76,50 @@ bool Block::to_raw_block(RawBlock &raw_block) const { return true; } -PreparedBlock::PreparedBlock(BinaryArray &&ba, crypto::CryptoNightContext *context) : block_data(std::move(ba)) { - seria::from_binary(raw_block, block_data); - if (block.from_raw_block(raw_block)) - bid = bytecoin::get_block_hash(block.header); - if (block.header.major_version >= 2) - parent_block_size = seria::binary_size(block.header.parent_block); - coinbase_tx_size = seria::binary_size(block.header.base_transaction); - base_transaction_hash = get_transaction_hash(block.header.base_transaction); - if (context) - long_block_hash = bytecoin::get_block_long_hash(block.header, *context); +PreparedBlock::PreparedBlock(BinaryArray &&ba, const Currency ¤cy, crypto::CryptoNightContext *context) + : block_data(std::move(ba)) { + try { + seria::from_binary(raw_block, block_data); + if (!block.from_raw_block(raw_block)) { + error_text = "from_raw_block failed"; + return; + } + auto body_proxy = get_body_proxy_from_template(block.header); + bid = bytecoin::get_block_hash(block.header, body_proxy); + if (block.header.major_version >= 2) + parent_block_size = seria::binary_size(block.header.parent_block); + coinbase_tx_size = seria::binary_size(block.header.base_transaction); + base_transaction_hash = get_transaction_hash(block.header.base_transaction); + if (context) { + auto ba = currency.get_block_long_hashing_data(block.header, body_proxy); + long_block_hash = context->cn_slow_hash(ba.data(), ba.size()); + } + } catch (const std::exception &ex) { + error_text = common::what(ex); + } } -PreparedBlock::PreparedBlock(RawBlock &&rba, crypto::CryptoNightContext *context) : raw_block(rba) { - block_data = seria::to_binary(raw_block); - if (block.from_raw_block(raw_block)) - bid = bytecoin::get_block_hash(block.header); - if (block.header.major_version >= 2) - parent_block_size = seria::binary_size(block.header.parent_block); - coinbase_tx_size = seria::binary_size(block.header.base_transaction); - base_transaction_hash = get_transaction_hash(block.header.base_transaction); - if (context) - long_block_hash = bytecoin::get_block_long_hash(block.header, *context); +PreparedBlock::PreparedBlock(RawBlock &&rba, const Currency ¤cy, crypto::CryptoNightContext *context) + : raw_block(rba) { + try { + block_data = seria::to_binary(raw_block); + if (!block.from_raw_block(raw_block)) { + error_text = "from_raw_block failed"; + return; + } + auto body_proxy = get_body_proxy_from_template(block.header); + bid = bytecoin::get_block_hash(block.header, body_proxy); + if (block.header.major_version >= 2) + parent_block_size = seria::binary_size(block.header.parent_block); + coinbase_tx_size = seria::binary_size(block.header.base_transaction); + base_transaction_hash = get_transaction_hash(block.header.base_transaction); + if (context) { + auto ba = currency.get_block_long_hashing_data(block.header, body_proxy); + long_block_hash = context->cn_slow_hash(ba.data(), ba.size()); + } + } catch (const std::exception &ex) { + error_text = common::what(ex); + } } BlockChain::BlockChain(logging::ILogger &log, const Config &config, const Currency ¤cy, bool read_only) @@ -107,7 +129,7 @@ BlockChain::BlockChain(logging::ILogger &log, const Config &config, const Curren , m_log(log, "BlockChainState") , m_config(config) , m_currency(currency) { - invariant(CheckPointDifficulty{}.size() == currency.get_checkpoint_keys_count(), ""); + invariant(CheckpointDifficulty{}.size() == currency.get_checkpoint_keys_count(), ""); std::string version; if (!m_db.get("$version", version)) { DB::Cursor cur = m_db.begin(std::string()); @@ -144,7 +166,7 @@ BroadcastAction BlockChain::add_block( bool have_header = read_header(pb.bid, info); bool have_block = has_block(pb.bid); if (have_block && have_header) { - if (info->height > m_currency.last_sw_checkpoint().first) + if (info->height > m_currency.last_sw_checkpoint().height) m_archive.add(Archive::BLOCK, pb.block_data, pb.bid, source_address); return BroadcastAction::NOTHING; } @@ -161,29 +183,18 @@ BroadcastAction BlockChain::add_block( info->height = prev_info.height + 1; // Rest fields are filled by check_standalone_consensus std::string check_error = check_standalone_consensus(pb, info, prev_info, true); - Hash first_difficulty_check_hash; - invariant( - common::pod_from_hex(difficulty_check[0].hash, first_difficulty_check_hash), "DifficultyCheck table corrupted"); - if (info->hash == first_difficulty_check_hash && - info->cumulative_difficulty != difficulty_check[0].cumulative_difficulty) { - m_log(logging::ERROR) << "Reached first difficulty checkpoint with wrong cumulative_difficulty " - << info->cumulative_difficulty << ", should be " - << difficulty_check[0].cumulative_difficulty << ", " << delete_blockchain_message - << m_db.get_path() << std::endl; - std::exit(api::BYTECOIND_DATABASE_ERROR); - } if (!check_error.empty()) return BroadcastAction::BAN; // TODO - return check_error if (!add_blod(*info)) { // Has parent that does not pass through last SW checkpoint - if (info->height > m_currency.last_sw_checkpoint().first) + if (info->height > m_currency.last_sw_checkpoint().height) m_archive.add(Archive::BLOCK, pb.block_data, pb.bid, source_address); return BroadcastAction::NOTHING; } try { - if (!have_block) { // have block, but not header during internal_import + if (!have_block) { // have block, but not header during internal_import store_block(pb.bid, pb.block_data); // Do not commit between here and // reorganize_blocks or invariant might be dead - if (info->height > m_currency.last_sw_checkpoint().first) + if (info->height > m_currency.last_sw_checkpoint().height) m_archive.add(Archive::BLOCK, pb.block_data, pb.bid, source_address); } store_header(pb.bid, *info); @@ -197,7 +208,7 @@ BroadcastAction BlockChain::add_block( } check_children_counter(info->cumulative_difficulty, pb.bid, 1); modify_children_counter(info->cumulative_difficulty, pb.bid, -1); // -1 from default 1 gives 0 - if (info->hash == m_currency.last_sw_checkpoint().second) + if (info->hash == m_currency.last_sw_checkpoint().hash) build_blods(); auto tip_check_cd = get_checkpoint_difficulty(get_tip_bid()); auto bid_check_cd = get_checkpoint_difficulty(info->hash); @@ -213,7 +224,7 @@ BroadcastAction BlockChain::add_block( } } catch (const std::exception &ex) { m_log(logging::ERROR) << "Exception while reorganizing blockchain, probably out of disk space ex.what=" - << ex.what() << ", " << delete_blockchain_message << m_db.get_path() << std::endl; + << common::what(ex) << ", " << delete_blockchain_message << m_db.get_path() << std::endl; std::exit(api::BYTECOIND_DATABASE_ERROR); } if (get_tip_height() % COMMIT_EVERY_N_BLOCKS == COMMIT_EVERY_N_BLOCKS - 1) // no commit on genesis @@ -223,19 +234,22 @@ BroadcastAction BlockChain::add_block( void BlockChain::debug_check_transaction_invariants(const RawBlock &raw_block, const Block &block, const api::BlockHeader &info, const Hash &base_transaction_hash) const { + BinaryArray binary_tx; Transaction rtx; Height bhe; Hash bha; size_t iib; - uint32_t bs; - invariant(read_transaction(base_transaction_hash, &rtx, &bhe, &bha, &iib, &bs), "tx index invariant failed 1"); + invariant(read_transaction(base_transaction_hash, &binary_tx, &bhe, &bha, &iib), "tx index invariant failed 1"); + seria::from_binary(rtx, binary_tx); invariant(get_transaction_hash(rtx) == base_transaction_hash && bhe == info.height && bha == info.hash && iib == 0, "tx index invariant failed 2"); for (size_t tx_index = 0; tx_index != block.transactions.size(); ++tx_index) { Hash tid = block.header.transaction_hashes.at(tx_index); - invariant(read_transaction(tid, &rtx, &bhe, &bha, &iib, &bs), "tx index invariant failed 3"); + invariant(read_transaction(tid, &binary_tx, &bhe, &bha, &iib), "tx index invariant failed 3"); + seria::from_binary(rtx, binary_tx); invariant(seria::to_binary(rtx) == raw_block.transactions.at(tx_index) && bhe == info.height && - bha == info.hash && iib == tx_index + 1 && bs == raw_block.transactions.at(tx_index).size(), + bha == info.hash && iib == tx_index + 1 && + binary_tx.size() == raw_block.transactions.at(tx_index).size(), "tx index invariant failed 4"); } } @@ -287,8 +301,9 @@ bool BlockChain::reorganize_blocks(const Hash &switch_to_chain, push_chain(recent_info); for (auto &&tid : recent_pb.block.header.transaction_hashes) undone_transactions.erase(tid); - // debug_check_transaction_invariants(recent_pb.raw_block, recent_pb.block, recent_info, - // recent_pb.base_transaction_hash); + if (m_config.paranoid_checks) + debug_check_transaction_invariants( + recent_pb.raw_block, recent_pb.block, recent_info, recent_pb.base_transaction_hash); } else { BinaryArray block_data; RawBlock raw_block; @@ -309,7 +324,8 @@ bool BlockChain::reorganize_blocks(const Hash &switch_to_chain, push_chain(info); for (auto &&tid : block.header.transaction_hashes) undone_transactions.erase(tid); - // debug_check_transaction_invariants(raw_block, block, info, base_transaction_hash); + if (m_config.paranoid_checks) + debug_check_transaction_invariants(raw_block, block, info, base_transaction_hash); } } on_reorganization(undone_transactions, undone_blocks); @@ -353,18 +369,43 @@ Hash BlockChain::get_common_block( } std::vector BlockChain::get_sparse_chain() const { - std::vector tip_path; + std::vector result; + auto tip_path = get_sparse_chain(m_genesis_bid, m_tip_bid); + for (const auto &el : tip_path) + result.push_back(el.hash); + return result; + // Height jump = 0; + // while (m_tip_height >= jump) { + // tip_path.push_back(read_chain(m_tip_height - jump)); + // if (tip_path.size() <= 10) + // jump += 1; + // else + // jump += (1 << (tip_path.size() - 10)); + // } + // if (tip_path.back() != m_genesis_bid) + // tip_path.push_back(m_genesis_bid); + // return tip_path; +} - uint32_t jump = 0; - while (m_tip_height >= jump) { - tip_path.push_back(read_chain(m_tip_height - jump)); +std::vector BlockChain::get_sparse_chain(Hash start, Hash end) const { + std::vector tip_path; + + api::BlockHeader header_end; + if (!read_header(end, &header_end) || !in_chain(header_end.height, header_end.hash)) + return tip_path; + api::BlockHeader header_start; + if (!read_header(start, &header_start) || !in_chain(header_start.height, header_start.hash)) + return tip_path; + Height jump = 0; + while (header_end.height >= jump + header_start.height) { + tip_path.push_back(SWCheckpoint{header_end.height - jump, read_chain(header_end.height - jump)}); if (tip_path.size() <= 10) jump += 1; else jump += (1 << (tip_path.size() - 10)); } - if (tip_path.back() != m_genesis_bid) - tip_path.push_back(m_genesis_bid); + if (tip_path.back().hash != start) + tip_path.push_back(SWCheckpoint{header_start.height, header_start.hash}); return tip_path; } @@ -391,7 +432,7 @@ uint32_t BlockChain::find_blockchain_supplement(const std::vector &remote_ return 0; // Not possible if genesis blocks match } -Height BlockChain::get_timestamp_lower_bound_block_index(Timestamp ts) const { +Height BlockChain::get_timestamp_lower_bound_height(Timestamp ts) const { auto middle = common::write_varint_sqlite4(ts); DB::Cursor cur = m_db.begin(TIMESTAMP_BLOCK_PREFIX, middle); if (cur.end()) @@ -399,7 +440,7 @@ Height BlockChain::get_timestamp_lower_bound_block_index(Timestamp ts) const { const char *be = cur.get_suffix().data(); const char *en = be + cur.get_suffix().size(); common::read_varint_sqlite4(be, en); // We ignore result, auto actual_ts = - return boost::lexical_cast(common::read_varint_sqlite4(be, en)); + return common::integer_cast(common::read_varint_sqlite4(be, en)); } std::vector BlockChain::get_sync_headers_chain(const std::vector &locator, @@ -412,12 +453,12 @@ std::vector BlockChain::get_sync_headers_chain(const std::vector &lo continue; while (header.height != 0) { Hash ha; - if(read_chain(header.height, &ha) && ha == header.hash) + if (read_chain(header.height, &ha) && ha == header.hash) break; header = read_header(header.previous_block_hash); } uint32_t min_height = header.height; - *start_height = min_height; + *start_height = min_height; for (; result.size() < max_count && min_height <= m_tip_height; min_height += 1) { result.push_back(read_chain(min_height)); } @@ -459,8 +500,8 @@ void ser_members(APITransactionPos &v, ISeria &s) { } } // namespace seria -bool BlockChain::read_transaction(const Hash &tid, Transaction *tx, Height *block_height, Hash *block_hash, - size_t *index_in_block, uint32_t *binary_size) const { +bool BlockChain::read_transaction( + const Hash &tid, BinaryArray *binary_tx, Height *block_height, Hash *block_hash, size_t *index_in_block) const { auto txkey = TRANSATION_PREFIX + DB::to_binary_key(tid.data, sizeof(tid.data)); BinaryArray ba; if (!m_db.get(txkey, ba)) @@ -475,9 +516,7 @@ bool BlockChain::read_transaction(const Hash &tid, Transaction *tx, Height *bloc *block_hash = bid; *block_height = tpos.height; *index_in_block = tpos.index; - *binary_size = tpos.size; - BinaryArray tbody(block_val.data() + tpos.offset, block_val.data() + tpos.offset + tpos.size); - seria::from_binary(*tx, tbody); // TODO save copy + binary_tx->assign(block_val.data() + tpos.offset, block_val.data() + tpos.offset + tpos.size); return true; } @@ -543,7 +582,7 @@ bool BlockChain::read_block(const Hash &bid, BinaryArray *block_data, RawBlock * auto key = BLOCK_PREFIX + DB::to_binary_key(bid.data, sizeof(bid.data)) + BLOCK_SUFFIX; if (!m_db.get(key, rb)) return false; - if(raw_block) + if (raw_block) seria::from_binary(*raw_block, rb); *block_data = std::move(rb); return true; @@ -582,7 +621,7 @@ bool BlockChain::read_header(const Hash &bid, api::BlockHeader *header, Height h *header = cit->second; return true; } - if (header_cache.size() > m_currency.largest_window() * 10) { + if (header_cache.size() > m_currency.largest_window() * 20) { m_log(logging::INFO) << "BlockChain header cache reached max size and cleared" << std::endl; header_cache.clear(); // very simple policy } @@ -596,6 +635,19 @@ bool BlockChain::read_header(const Hash &bid, api::BlockHeader *header, Height h return true; } +void BlockChain::fix_block_sizes(api::BlockHeader *info) const { + DB::Value binary_block; + auto key = BLOCK_PREFIX + DB::to_binary_key(info->hash.data, sizeof(info->hash.data)) + BLOCK_SUFFIX; + invariant(m_db.get(key, binary_block), "Block must be there"); + common::MemoryInputStream stream(binary_block.data(), binary_block.size()); + seria::BinaryInputStream ba_stream(stream); + uint32_t raw_block_block_size = 0; + ser(raw_block_block_size, ba_stream); + auto coinbase_size = info->block_size - info->transactions_size; + info->transactions_size = info->block_size; + info->block_size = info->transactions_size + raw_block_block_size - coinbase_size; +} + api::BlockHeader BlockChain::read_header(const Hash &bid, Height hint) const { api::BlockHeader result; invariant(read_header(bid, &result, hint), "Expected header was not found" + common::pod_to_hex(bid)); @@ -628,7 +680,7 @@ std::vector BlockChain::get_tip_segment( void BlockChain::read_tip() { DB::Cursor cur2 = m_db.rbegin(TIP_CHAIN_PREFIX); - m_tip_height = cur2.end() ? -1 : boost::lexical_cast(common::read_varint_sqlite4(cur2.get_suffix())); + m_tip_height = cur2.end() ? -1 : common::integer_cast(common::read_varint_sqlite4(cur2.get_suffix())); seria::from_binary(m_tip_bid, cur2.get_value_array()); api::BlockHeader tip_header = read_header(m_tip_bid); m_tip_cumulative_difficulty = tip_header.cumulative_difficulty; @@ -753,6 +805,19 @@ bool BlockChain::prune_branch(CumulativeDifficulty cd, Hash bid) { if (bid == m_tip_bid) return false; check_children_counter(cd, bid, 0); + auto checkpoints = get_stable_checkpoints(); + for (auto cit = checkpoints.begin(); cit != checkpoints.end(); ++cit) + if (cit->is_enabled() && cit->hash == bid) + return false; + auto bit = blods.find(bid); + if (bit != blods.end()) { + if (!bit->second.children.empty() || !bit->second.parent) + return false; + auto rit = std::remove(bit->second.parent->children.begin(), bit->second.parent->children.end(), &bit->second); + invariant(rit != bit->second.parent->children.end(), ""); + bit->second.parent->children.erase(rit, bit->second.parent->children.end()); + blods.erase(bit); + } api::BlockHeader me = read_header(bid); api::BlockHeader pa = read_header(me.previous_block_hash); modify_children_counter(cd, bid, 1); @@ -761,17 +826,39 @@ bool BlockChain::prune_branch(CumulativeDifficulty cd, Hash bid) { m_db.del(key, true); auto key2 = HEADER_PREFIX + DB::to_binary_key(bid.data, sizeof(bid.data)) + HEADER_SUFFIX; m_db.del(key2, true); + m_header_tip_window.clear(); + api::BlockHeader tip_header = read_header(m_tip_bid); + m_header_tip_window.push_back(tip_header); + header_cache.erase(bid); return true; } -void BlockChain::test_prune_oldest() { - for (int i = 0; i != 10; ++i) { - CumulativeDifficulty cd{}; +bool BlockChain::test_prune_oldest() { + CumulativeDifficulty cd{}; + Hash bid; + invariant(get_oldest_tip(&cd, &bid), ""); + return prune_branch(cd, bid); +} + +void BlockChain::test_print_tips() const { + CumulativeDifficulty ocd; + Hash obid; + if (get_oldest_tip(&ocd, &obid)) + std::cout << "oldest tip cd=" << ocd << " bid=" << obid << std::endl; + std::cout << "---- BlockTree structure ----" << std::endl; + for (DB::Cursor cur = m_db.begin(CHILDREN_PREFIX); !cur.end(); cur.next()) { Hash bid; - if (!get_oldest_tip(&cd, &bid)) - return; - prune_branch(cd, bid); + DB::from_binary_key(cur.get_suffix(), 0, bid.data, sizeof(bid.data)); + uint32_t counter = 1; + seria::from_binary(counter, cur.get_value_array()); + api::BlockHeader info = read_header(bid); + std::cout << "branch height=" << info.height << " bid=" << bid << " children=" << counter << std::endl; } + for_each_tip([&](CumulativeDifficulty cd, Hash bid) -> bool { + api::BlockHeader info = read_header(bid); + std::cout << "tip height=" << info.height << " bid=" << bid << " CD=" << cd << std::endl; + return true; + }); } void BlockChain::test_print_structure(Height n_confirmations) const { @@ -779,14 +866,14 @@ void BlockChain::test_print_structure(Height n_confirmations) const { Hash obid; if (get_oldest_tip(&ocd, &obid)) std::cout << "oldest tip cd=" << ocd << " bid=" << obid << std::endl; - std::cout << "---- BlockTree tips and forking nodes ----" << std::endl; + std::cout << "---- BlockTree structure + additional info ----" << std::endl; for (DB::Cursor cur = m_db.begin(CHILDREN_PREFIX); !cur.end(); cur.next()) { Hash bid; DB::from_binary_key(cur.get_suffix(), 0, bid.data, sizeof(bid.data)); uint32_t counter = 1; seria::from_binary(counter, cur.get_value_array()); - std::cout << "childrens=" << counter << " bid=" << bid << std::endl; + std::cout << "children=" << counter << " bid=" << bid << std::endl; } size_t total_forked_transactions = 0; size_t total_possible_ds_transactions = 0; @@ -813,12 +900,11 @@ void BlockChain::test_print_structure(Height n_confirmations) const { for (size_t tx_pos = 0; tx_pos != block.header.transaction_hashes.size(); ++tx_pos) { Hash tid = block.header.transaction_hashes.at(tx_pos); total_forked_transactions += 1; - Transaction tx; + BinaryArray binary_tx; Height height = 0; Hash block_hash; size_t index_in_block = 0; - uint32_t binary_size = 0; - if (!read_transaction(tid, &tx, &height, &block_hash, &index_in_block, &binary_size)) { + if (!read_transaction(tid, &binary_tx, &height, &block_hash, &index_in_block)) { Amount input_amount = 0; for (const auto &input : block.transactions.at(tx_pos).inputs) if (input.type() == typeid(KeyInput)) { @@ -846,12 +932,48 @@ void BlockChain::test_print_structure(Height n_confirmations) const { << total_possible_ds_transactions << " total amount=" << total_possible_ds_amount << std::endl; } +void BlockChain::upgrade_5_to_6() { + m_log(logging::INFO) << "Blockchain database needs to recalculate block sizes..." << std::endl; + size_t processed = 0; + std::set hashes; + DB::Cursor cur2 = m_db.rbegin(TIP_CHAIN_PREFIX); + Height tip_height = cur2.end() ? -1 : common::integer_cast(common::read_varint_sqlite4(cur2.get_suffix())); + for (DB::Cursor cur = m_db.rbegin(BLOCK_PREFIX); !cur.end(); cur.next()) { + if (cur.get_suffix().substr(cur.get_suffix().size() - HEADER_SUFFIX.size()) != HEADER_SUFFIX) + continue; + if (processed++ % 50000 == 0) + m_log(logging::INFO) << "Processed " << processed << "/" << tip_height * 11 / 10 << " blocks" << std::endl; + Hash bid; + DB::from_binary_key(cur.get_suffix(), 0, bid.data, sizeof(bid.data)); + api::BlockHeader header; + seria::from_binary(header, cur.get_value_array()); + invariant(bid == header.hash, "DB corrupted"); + invariant(hashes.insert(bid).second, ""); + // RawBlock rb; + // invariant(read_block(bid, &rb), "Block must be there"); + DB::Value binary_block; + auto key = BLOCK_PREFIX + DB::to_binary_key(bid.data, sizeof(bid.data)) + BLOCK_SUFFIX; + invariant(m_db.get(key, binary_block), "Block must be there"); + common::MemoryInputStream stream(binary_block.data(), binary_block.size()); + seria::BinaryInputStream ba_stream(stream); + uint32_t raw_block_block_size = 0; + ser(raw_block_block_size, ba_stream); + auto coinbase_size = header.block_size - header.transactions_size; + header.transactions_size = header.block_size; + header.block_size = header.transactions_size + raw_block_block_size - coinbase_size; + key = HEADER_PREFIX + DB::to_binary_key(bid.data, sizeof(bid.data)) + HEADER_SUFFIX; + BinaryArray ba = seria::to_binary(header); + m_db.put(key, ba, false); + } + m_log(logging::INFO) << "Recalulating block sizes finished" << std::endl; +} + void BlockChain::start_internal_import() { m_log(logging::INFO) << "Blockchain database has old format, preparing for internal block import..." << std::endl; if (m_internal_import_chain.empty()) { const std::vector former_prefixes{ TIP_CHAIN_PREFIX + "B/", TIP_CHAIN_PREFIX + "1/", TIP_CHAIN_PREFIX + "/", TIP_CHAIN_PREFIX}; - for (auto && prefix : former_prefixes) { + for (auto &&prefix : former_prefixes) { std::vector main_chain; for (Height ha = 0;; ha += 1) { BinaryArray ba; @@ -884,7 +1006,7 @@ void BlockChain::start_internal_import() { continue; // block in main chain } BinaryArray block_data; - if( read_block(bid, &block_data, nullptr) ) + if (read_block(bid, &block_data, nullptr)) m_archive.add(Archive::BLOCK, block_data, bid, "start_internal_import"); } cur.erase(); @@ -896,29 +1018,34 @@ void BlockChain::start_internal_import() { bool BlockChain::internal_import() { auto idea_start = std::chrono::high_resolution_clock::now(); - while (true) { - if (get_tip_height() + 1 >= m_internal_import_chain.size()) - break; - const Hash bid = m_internal_import_chain.at(get_tip_height() + 1); - RawBlock rb; - if (!read_block(bid, &rb)) { - m_log(logging::WARNING) << "Block not found during internal import for height=" << get_tip_height() + 1 - << " bid=" << bid << std::endl; - break; - } - PreparedBlock pb(std::move(rb), nullptr); - api::BlockHeader info; - if (add_block(pb, &info, "internal_import") != BroadcastAction::BROADCAST_ALL) { - m_log(logging::WARNING) << "Block corrupted during internal import for height=" << get_tip_height() + 1 - << " bid=" << bid << std::endl; - break; + try { + while (true) { + if (get_tip_height() + 1 >= m_internal_import_chain.size()) + break; + const Hash bid = m_internal_import_chain.at(get_tip_height() + 1); + RawBlock rb; + if (!read_block(bid, &rb)) { + m_log(logging::WARNING) << "Block not found during internal import for height=" << get_tip_height() + 1 + << " bid=" << bid << std::endl; + break; + } + PreparedBlock pb(std::move(rb), m_currency, nullptr); + api::BlockHeader info; + if (add_block(pb, &info, "internal_import") != BroadcastAction::BROADCAST_ALL) { + m_log(logging::WARNING) << "Block corrupted during internal import for height=" << get_tip_height() + 1 + << " bid=" << bid << std::endl; + break; + } + // if (get_tip_height() % COMMIT_EVERY_N_BLOCKS == 0) + // db_commit(); + auto idea_ms = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - idea_start); + if (idea_ms.count() > 200) + return true; // import in chunks of 0.2 seconds } - // if (get_tip_height() % COMMIT_EVERY_N_BLOCKS == 0) - // db_commit(); - auto idea_ms = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - idea_start); - if (idea_ms.count() > 200) - return true; // import in chunks of 0.2 seconds + } catch (const std::exception &ex) { + m_log(logging::WARNING) << "Block corrupted during internal import for height=" << get_tip_height() + 1 + << " exception=" << common::what(ex) << std::endl; } m_log(logging::INFO) << "Finished internal importing of blocks, will continue downloading..." << std::endl; m_internal_import_chain.clear(); @@ -964,27 +1091,26 @@ void BlockChain::test_undo_everything(Height new_tip_height) { } } -std::vector BlockChain::get_latest_checkpoints() const { - std::vector result; +std::vector BlockChain::get_latest_checkpoints() const { + std::vector result; for (DB::Cursor cur = m_db.begin(CHECKPOINT_PREFIX_LATEST); !cur.end(); cur.next()) { - result.push_back(SignedCheckPoint{}); + result.push_back(SignedCheckpoint{}); seria::from_binary(result.back(), cur.get_value_array()); } return result; } -std::vector BlockChain::get_stable_checkpoints() const { - std::vector result; +std::vector BlockChain::get_stable_checkpoints() const { + std::vector result; for (DB::Cursor cur = m_db.begin(CHECKPOINT_PREFIX_STABLE); !cur.end(); cur.next()) { - result.push_back(SignedCheckPoint{}); + result.push_back(SignedCheckpoint{}); seria::from_binary(result.back(), cur.get_value_array()); } return result; } -bool BlockChain::add_checkpoint(const SignedCheckPoint &checkpoint, const std::string &source_address) { - if (checkpoint.height <= m_currency.last_sw_checkpoint().first && - checkpoint.counter != std::numeric_limits::max()) +bool BlockChain::add_checkpoint(const SignedCheckpoint &checkpoint, const std::string &source_address) { + if (checkpoint.height <= m_currency.last_sw_checkpoint().height && checkpoint.is_enabled()) return false; // Height is ignored when disabling key_id PublicKey public_key = m_currency.get_checkpoint_public_key(checkpoint.key_id); // returns empty key if out of range @@ -993,7 +1119,7 @@ bool BlockChain::add_checkpoint(const SignedCheckPoint &checkpoint, const std::s BinaryArray binary_checkpoint = seria::to_binary(checkpoint); BinaryArray ba; if (m_db.get(key_latest, ba)) { - SignedCheckPoint previous_checkpoint; + SignedCheckpoint previous_checkpoint; seria::from_binary(previous_checkpoint, ba); if (checkpoint.counter < previous_checkpoint.counter) return false; @@ -1012,7 +1138,7 @@ bool BlockChain::add_checkpoint(const SignedCheckPoint &checkpoint, const std::s m_db.put(key_latest, binary_checkpoint, false); m_archive.add(Archive::CHECKPOINT, binary_checkpoint, crypto::cn_fast_hash(binary_checkpoint.data(), binary_checkpoint.size()), source_address); - if (checkpoint.counter != std::numeric_limits::max()) { // Disabling key_id + if (checkpoint.is_enabled()) { auto bit = blods.find(checkpoint.hash); if (bit == blods.end()) return true; // orphan checkpoint @@ -1028,7 +1154,7 @@ bool BlockChain::add_checkpoint(const SignedCheckPoint &checkpoint, const std::s RawBlock raw_block; if (!read_block(bid, &raw_block)) return true; - PreparedBlock pb(std::move(raw_block), nullptr); + PreparedBlock pb(std::move(raw_block), m_currency, nullptr); reorganize_blocks(bid, pb, header); tip_check_cd = get_checkpoint_difficulty(get_tip_bid()); return true; @@ -1037,7 +1163,7 @@ bool BlockChain::add_checkpoint(const SignedCheckPoint &checkpoint, const std::s } int BlockChain::compare( - const CheckPointDifficulty &a, CumulativeDifficulty ca, const CheckPointDifficulty &b, CumulativeDifficulty cb) { + const CheckpointDifficulty &a, CumulativeDifficulty ca, const CheckpointDifficulty &b, CumulativeDifficulty cb) { // invariant(a.size() == b.size(), "size mismatch in BlockChain::compare"); for (size_t i = a.size(); i-- > 0;) if (a.at(i) != b.at(i)) @@ -1049,35 +1175,61 @@ int BlockChain::compare( return 0; } -bool BlockChain::add_blod(const api::BlockHeader &header) { - if (blods.empty()) // Allow any blocks if main does not pass through last sw checkpoint yet - return true; +bool BlockChain::add_blod_impl(const api::BlockHeader &header) { auto bit = blods.find(header.hash); if (bit != blods.end()) return true; // Strange, but nop bit = blods.find(header.previous_block_hash); if (bit == blods.end()) return false; - Blod &blod = blods[header.hash]; - blod.height = header.height; - blod.hash = header.hash; - blod.parent = &bit->second; + Blod &blod = blods[header.hash]; + blod.height = header.height; + blod.hash = header.hash; + blod.parent = &bit->second; bit->second.children.push_back(&blod); blod.checkpoint_difficulty = blod.parent->checkpoint_difficulty; + + if (m_currency.upgrade_from_major_version) { + blod.vote_for_upgrade = (header.major_version == m_currency.upgrade_from_major_version && header.minor_version == m_currency.upgrade_indicator_minor_version); + blod.upgrade_decided_height = blod.parent->upgrade_decided_height; + if (!blod.upgrade_decided_height) { + blod.votes_for_upgrade_in_voting_window = blod.parent->votes_for_upgrade_in_voting_window; + blod.votes_for_upgrade_in_voting_window.push_back(blod.vote_for_upgrade); + if (blod.votes_for_upgrade_in_voting_window.size() > m_currency.upgrade_voting_window) + blod.votes_for_upgrade_in_voting_window.pop_front(); + invariant(blod.votes_for_upgrade_in_voting_window.size() <= m_currency.upgrade_voting_window, ""); + size_t count = std::count(blod.votes_for_upgrade_in_voting_window.begin(), + blod.votes_for_upgrade_in_voting_window.end(), 1); + // if(count != 0) + // std::cout << "Votes for version=" << int(m_currency.upgrade_desired_major_version) << " + // height=" + //<< header.height << " votes=" << count << std::endl; + if (count >= m_currency.upgrade_votes_required) { + blod.upgrade_decided_height = blod.height + m_currency.upgrade_blocks_after_voting; + m_log(logging::INFO) << "Consensus upgrade decided on height=" << header.height + << " decided_height=" << blod.upgrade_decided_height << " bid=" << header.hash + << std::endl; + blod.votes_for_upgrade_in_voting_window.clear(); + } + } + } + return true; +} + +bool BlockChain::add_blod(const api::BlockHeader &header) { + if (blods.empty()) // Allow any blocks if main does not pass through last sw checkpoint yet + return true; + add_blod_impl(header); // We inherit from parent and rebuild only if we pass through one of checlpoints for (auto &&ch : get_latest_checkpoints()) - if (ch.counter != std::numeric_limits::max()) { // disabled are made stable in add_checkpoint - if (header.hash == ch.hash) { - auto key_stable = CHECKPOINT_PREFIX_STABLE + common::write_varint_sqlite4(ch.key_id); - m_db.put(key_stable, seria::to_binary(ch), false); - } + if (ch.is_enabled() && header.hash == ch.hash) { // disabled are made stable in add_checkpoint + auto key_stable = CHECKPOINT_PREFIX_STABLE + common::write_varint_sqlite4(ch.key_id); + m_db.put(key_stable, seria::to_binary(ch), false); } for (auto &&ch : get_stable_checkpoints()) - if (ch.counter != std::numeric_limits::max()) { // skip disabled keys - if (header.hash == ch.hash) { - update_key_count_max_heights(); - return true; - } + if (ch.is_enabled() && header.hash == ch.hash) { + update_key_count_max_heights(); + return true; } return true; } @@ -1086,7 +1238,7 @@ void BlockChain::build_blods() { if (!blods.empty()) return; // build only once per daemon launch api::BlockHeader last_sw_checkpoint_header; - if( !read_header(m_currency.last_sw_checkpoint().second, &last_sw_checkpoint_header) ) + if (!read_header(m_currency.last_sw_checkpoint().hash, &last_sw_checkpoint_header)) return; std::set bad_header_hashes; // sidechains that do not pass through last SW checkpoint std::set good_header_hashes; // sidechains that pass through last SW checkpoint @@ -1108,11 +1260,11 @@ void BlockChain::build_blods() { bad_header_hashes.insert(ha.hash); break; } - if (header.height < m_currency.last_sw_checkpoint().first) + if (header.height < m_currency.last_sw_checkpoint().height) break; side_chain.push_back(header); - if (header.height == m_currency.last_sw_checkpoint().first) { - if (header.hash == m_currency.last_sw_checkpoint().second) { + if (header.height == m_currency.last_sw_checkpoint().height) { + if (header.hash == m_currency.last_sw_checkpoint().hash) { std::reverse(side_chain.begin(), side_chain.end()); for (auto &&ha : side_chain) { good_header_hashes.insert(ha.hash); @@ -1129,27 +1281,77 @@ void BlockChain::build_blods() { return true; }); for (auto &&ha : good_headers) { // They are conveniently sorted parent to child and can be applied sequentially - Blod &blod = blods[ha.hash]; - blod.height = ha.height; - blod.hash = ha.hash; - auto bit = blods.find(ha.previous_block_hash); - if (bit == blods.end()) - continue; - blod.parent = &bit->second; - bit->second.children.push_back(&blod); + if (!add_blod_impl(ha)) { + invariant(ha.hash == m_currency.last_sw_checkpoint().hash, ""); + Blod &blod = blods[ha.hash]; + blod.height = ha.height; + blod.hash = ha.hash; + if (m_currency.upgrade_from_major_version) { + blod.vote_for_upgrade = (ha.major_version == m_currency.upgrade_from_major_version && ha.minor_version == m_currency.upgrade_indicator_minor_version); + Height first_height = + m_currency.last_sw_checkpoint().height < m_currency.upgrade_voting_window - 1 + ? 0 + : m_currency.last_sw_checkpoint().height - (m_currency.upgrade_voting_window - 1); + for (Height h = first_height; h != m_currency.last_sw_checkpoint().height; ++h) { + auto header = read_header(read_chain(h), h); + uint8_t vote = (header.major_version == m_currency.upgrade_from_major_version && header.minor_version == m_currency.upgrade_indicator_minor_version); + blod.votes_for_upgrade_in_voting_window.push_back(vote); + } + blod.votes_for_upgrade_in_voting_window.push_back(blod.vote_for_upgrade); + size_t count = std::count(blod.votes_for_upgrade_in_voting_window.begin(), + blod.votes_for_upgrade_in_voting_window.end(), 1); + invariant(count < m_currency.upgrade_votes_required, "Upgrade happened before or on last SW checkpoint"); + } + } } update_key_count_max_heights(); } +void BlockChain::fill_statistics(api::bytecoind::GetStatistics::Response &res) const { + res.checkpoints = get_latest_checkpoints(); + + if (!m_currency.upgrade_from_major_version) + return; + auto bit = blods.find(get_tip_bid()); + if (bit == blods.end()) + return; + res.upgrade_decided_height = bit->second.upgrade_decided_height; + size_t count = std::count(bit->second.votes_for_upgrade_in_voting_window.begin(), + bit->second.votes_for_upgrade_in_voting_window.end(), 1); + res.upgrade_votes_in_top_block = static_cast(count); +} + +bool BlockChain::fill_next_block_versions( + const api::BlockHeader &prev_info, bool cooperative, uint8_t *major, uint8_t *minor) const { + *major = m_currency.get_block_major_version_for_height(prev_info.height + 1); + if( *major == m_currency.upgrade_from_major_version) + *minor = m_currency.upgrade_indicator_minor_version; + else + *minor = 0; + // TODO - for now we only observe voting, do not create new blocks yet + // if( !m_currency.upgrade_desired_major_version ) + // return true; + // if(blods.empty()) + // return true; + // auto bit = blods.find(prev_info.hash); + // if (bit == blods.end()) + // return false; + // if(!bit->second.upgrade_decided_height || prev_info.height + 1 < bit->second.upgrade_decided_height) + // return true; + // *major = m_currency.upgrade_desired_major_version; + // *minor = 0; + return true; +} + void BlockChain::update_key_count_max_heights() { // We use simplest O(n) algo, will optimize later and use this one as a reference for (auto &&bit : blods) { bit.second.checkpoint_key_ids.reset(); - bit.second.checkpoint_difficulty = CheckPointDifficulty{}; + bit.second.checkpoint_difficulty = CheckpointDifficulty{}; } auto checkpoints = get_stable_checkpoints(); for (auto cit = checkpoints.begin(); cit != checkpoints.end(); ++cit) - if (cit->counter != std::numeric_limits::max()) { // skip disabled keys + if (cit->is_enabled()) { auto bit = blods.find(cit->hash); if (bit == blods.end()) continue; @@ -1157,7 +1359,7 @@ void BlockChain::update_key_count_max_heights() { b->checkpoint_key_ids.set(cit->key_id); } std::vector to_visit; - auto bit = blods.find(m_currency.last_sw_checkpoint().second); + auto bit = blods.find(m_currency.last_sw_checkpoint().hash); if (bit != blods.end()) to_visit.push_back(&bit->second); while (!to_visit.empty()) { @@ -1172,9 +1374,9 @@ void BlockChain::update_key_count_max_heights() { } } -BlockChain::CheckPointDifficulty BlockChain::get_checkpoint_difficulty(Hash hash) const { +BlockChain::CheckpointDifficulty BlockChain::get_checkpoint_difficulty(Hash hash) const { auto bit = blods.find(hash); if (bit == blods.end()) - return CheckPointDifficulty{}; + return CheckpointDifficulty{}; return bit->second.checkpoint_difficulty; } diff --git a/src/Core/BlockChain.hpp b/src/Core/BlockChain.hpp index 16957fa6..5dbd73e8 100644 --- a/src/Core/BlockChain.hpp +++ b/src/Core/BlockChain.hpp @@ -38,10 +38,12 @@ struct PreparedBlock { Hash base_transaction_hash; size_t coinbase_tx_size = 0; size_t parent_block_size = 0; - Hash long_block_hash; // only if context != nullptr + Hash long_block_hash; // only if context != nullptr + std::string error_text; // empty when no error - explicit PreparedBlock(BinaryArray &&ba, crypto::CryptoNightContext *context); - explicit PreparedBlock(RawBlock &&rba, crypto::CryptoNightContext *context); // we get raw blocks from p2p + explicit PreparedBlock(BinaryArray &&ba, const Currency ¤cy, crypto::CryptoNightContext *context); + explicit PreparedBlock( + RawBlock &&rba, const Currency ¤cy, crypto::CryptoNightContext *context); // we get raw blocks from p2p PreparedBlock() = default; }; @@ -52,10 +54,12 @@ class BlockChain { explicit BlockChain(logging::ILogger &, const Config &config, const Currency &, bool read_only); virtual ~BlockChain() = default; + const Currency &get_currency() const { return m_currency; } const Hash &get_genesis_bid() const { return m_genesis_bid; } // Read blockchain state Hash get_tip_bid() const { return m_tip_bid; } Height get_tip_height() const { return m_tip_height; } + CumulativeDifficulty get_tip_cumulative_difficulty() const { return m_tip_cumulative_difficulty; } const api::BlockHeader &get_tip() const; template void get_tips(Height, Height, T &) const; @@ -68,49 +72,50 @@ class BlockChain { bool read_chain(Height height, Hash *bid) const; bool in_chain(Height height, Hash bid) const; bool read_block(const Hash &bid, RawBlock *rb) const; - bool read_block(const Hash &bid, BinaryArray *block_data, RawBlock *rb) const; // rb can be null here + bool read_block(const Hash &bid, BinaryArray *block_data, RawBlock *rb) const; // rb can be null here bool has_block(const Hash &bid) const; bool read_header(const Hash &bid, api::BlockHeader *info, Height hint = 0) const; - bool read_transaction(const Hash &tid, Transaction *tx, Height *block_height, Hash *block_hash, - size_t *index_in_block, uint32_t *binary_size) const; - + void fix_block_sizes(api::BlockHeader *info) const; // TODO - remove after correct sizes are in DB + bool read_transaction( + const Hash &tid, BinaryArray *binary_tx, Height *block_height, Hash *block_hash, size_t *index_in_block) const; // Modify blockchain state. bytecoin header does not contain enough info for consensus calcs, so we cannot have // header chain without block chain BroadcastAction add_block(const PreparedBlock &pb, api::BlockHeader *info, const std::string &source_address); // Facilitate sync and download std::vector get_sparse_chain() const; + std::vector get_sparse_chain(Hash start, Hash end) const; std::vector get_sync_headers(const std::vector &sparse_chain, size_t max_count) const; std::vector get_sync_headers_chain( const std::vector &sparse_chain, Height *start_height, size_t max_count) const; Height find_blockchain_supplement(const std::vector &remote_block_ids) const; - Height get_timestamp_lower_bound_block_index(Timestamp) const; + Height get_timestamp_lower_bound_height(Timestamp) const; void test_undo_everything(Height new_tip_height); void test_print_structure(Height n_confirmations) const; - - void test_prune_oldest(); + void test_print_tips() const; + bool test_prune_oldest(); void db_commit(); bool internal_import(); // import some existing blocks from inside DB Height internal_import_known_height() const { return static_cast(m_internal_import_chain.size()); } - std::vector get_latest_checkpoints() const; - std::vector get_stable_checkpoints() const; - bool add_checkpoint(const SignedCheckPoint &checkpoint, const std::string &source_address); + std::vector get_latest_checkpoints() const; + std::vector get_stable_checkpoints() const; + bool add_checkpoint(const SignedCheckpoint &checkpoint, const std::string &source_address); void read_archive(api::bytecoind::GetArchive::Request &&req, api::bytecoind::GetArchive::Response &resp) { m_archive.read_archive(std::move(req), resp); } + virtual void fill_statistics(api::bytecoind::GetStatistics::Response &res) const; - typedef std::array CheckPointDifficulty; // size must be == m_currency.get_checkpoint_keys_count() + typedef std::array CheckpointDifficulty; // size must be == m_currency.get_checkpoint_keys_count() protected: - CumulativeDifficulty get_tip_cumulative_difficulty() const { return m_tip_cumulative_difficulty; } - std::vector m_internal_import_chain; void start_internal_import(); + void upgrade_5_to_6(); // We will perform it on next version change virtual std::string check_standalone_consensus( const PreparedBlock &pb, api::BlockHeader *info, const api::BlockHeader &prev_info, bool check_pow) const = 0; @@ -167,22 +172,29 @@ class BlockChain { void for_each_tip(std::function fun) const; static int compare( - const CheckPointDifficulty &a, CumulativeDifficulty ca, const CheckPointDifficulty &b, CumulativeDifficulty cb); + const CheckpointDifficulty &a, CumulativeDifficulty ca, const CheckpointDifficulty &b, CumulativeDifficulty cb); struct Blod { Hash hash; Height height = 0; Blod *parent = nullptr; std::vector children; std::bitset<64> checkpoint_key_ids; - CheckPointDifficulty checkpoint_difficulty; // (key_count-1)->max_height + CheckpointDifficulty checkpoint_difficulty; // (key_count-1)->max_height + + uint8_t vote_for_upgrade = 0; + std::deque votes_for_upgrade_in_voting_window; + Height upgrade_decided_height = 0; }; std::map blods; void update_key_count_max_heights(); + bool add_blod_impl(const api::BlockHeader &header); bool add_blod(const api::BlockHeader &header); - CheckPointDifficulty get_checkpoint_difficulty(Hash hash) const; + CheckpointDifficulty get_checkpoint_difficulty(Hash hash) const; protected: void build_blods(); + bool fill_next_block_versions( + const api::BlockHeader &prev_info, bool cooperative, uint8_t *major, uint8_t *minor) const; }; } // namespace bytecoin diff --git a/src/Core/BlockChainFileFormat.cpp b/src/Core/BlockChainFileFormat.cpp index d377bcee..20093f10 100644 --- a/src/Core/BlockChainFileFormat.cpp +++ b/src/Core/BlockChainFileFormat.cpp @@ -4,6 +4,7 @@ #include "BlockChainFileFormat.hpp" //#include "crypto/crypto-ops.h" #include "BlockChainState.hpp" +#include "common/Math.hpp" #include "platform/PathTools.hpp" #include "seria/BinaryInputStream.hpp" #include "seria/BinaryOutputStream.hpp" @@ -19,7 +20,10 @@ using namespace bytecoin; // std::cout << "Block tx count=" << pb.block.transactions.size() << std::endl; // } -LegacyBlockChainReader::LegacyBlockChainReader(const std::string &index_file_name, const std::string &item_file_name) { +LegacyBlockChainReader::LegacyBlockChainReader(const Currency ¤cy, + const std::string &index_file_name, + const std::string &item_file_name) + : currency(currency) { try { m_indexes_file = std::make_unique(index_file_name, platform::FileStream::READ_EXISTING); m_items_file = std::make_unique(item_file_name, platform::FileStream::READ_EXISTING); @@ -30,7 +34,7 @@ LegacyBlockChainReader::LegacyBlockChainReader(const std::string &index_file_nam uint64_t max_hei = (m_indexesFileSize - sizeof(uint64_t)) / sizeof(uint32_t); uint64_t read_hei = 0; m_indexes_file->read(reinterpret_cast(&read_hei), sizeof(uint64_t)); - m_count = boost::lexical_cast(std::min(read_hei, max_hei)); + m_count = common::integer_cast(std::min(read_hei, max_hei)); } catch (const std::runtime_error &) { } } @@ -67,7 +71,7 @@ void LegacyBlockChainReader::load_offsets() { BinaryArray LegacyBlockChainReader::get_block_data_by_index(Height i) { load_offsets(); - size_t si = boost::lexical_cast(m_offsets.at(i + 1) - m_offsets.at(i)); + size_t si = common::integer_cast(m_offsets.at(i + 1) - m_offsets.at(i)); m_items_file->seek(m_offsets.at(i), SEEK_SET); BinaryArray data_cache(si); m_items_file->read(reinterpret_cast(data_cache.data()), si); @@ -94,7 +98,7 @@ void LegacyBlockChainReader::thread_run() { to_load = next_load_height++; } BinaryArray rba = get_block_data_by_index(to_load); - PreparedBlock pb(std::move(rba), nullptr); + PreparedBlock pb(std::move(rba), currency, nullptr); { std::unique_lock lock(mu); total_prepared_data_size += pb.block_data.size(); @@ -135,7 +139,7 @@ bool LegacyBlockChainReader::import_blocks(BlockChainState *block_chain) { // size_t bs_count = std::min(block_chain.get_tip_height() + 1 + count, get_block_count()); while (block_chain->get_tip_height() + 1 < get_block_count()) { BinaryArray rba = get_block_data_by_index(block_chain->get_tip_height() + 1); - PreparedBlock pb(std::move(rba), nullptr); + PreparedBlock pb(std::move(rba), currency, nullptr); api::BlockHeader info; if (block_chain->add_block(pb, &info, "blocks_file") != BroadcastAction::BROADCAST_ALL) { std::cout << "block_chain.add_block !BROADCAST_ALL block=" << block_chain->get_tip_height() + 1 @@ -149,7 +153,7 @@ bool LegacyBlockChainReader::import_blocks(BlockChainState *block_chain) { break; } } catch (const std::exception &ex) { - std::cout << "Exception while importing blockchain file, what=" << ex.what() << std::endl; + std::cout << "Exception while importing blockchain file, what=" << common::what(ex) << std::endl; return false; } catch (...) { std::cout << "Unknown exception while importing blockchain file" << std::endl; @@ -161,14 +165,8 @@ bool LegacyBlockChainReader::import_blocks(BlockChainState *block_chain) { bool LegacyBlockChainReader::import_blockchain2(const std::string &coin_folder, BlockChainState *block_chain, Height max_height) { - // std::fstream ts_file("/Users/user/bytecoin/timestamps.txt", - // std::ios::out | std::ios::trunc); - // ts_file << "Block timestamp\tBlock median_timestamp\tBlock - // unlockTimestamp\tTimestamp difference\tMedian timestamp " - // "difference\tMedian - Timestamp" - // << std::endl; - - LegacyBlockChainReader reader(coin_folder + "/blockindexes.bin", coin_folder + "/blocks.bin"); + LegacyBlockChainReader reader( + block_chain->get_currency(), coin_folder + "/blockindexes.bin", coin_folder + "/blocks.bin"); const size_t import_height = std::min(max_height, reader.get_block_count() + 1); if (block_chain->get_tip_height() > import_height) { // std::cout << "Skipping block chain import - we have more blocks than " @@ -219,8 +217,8 @@ LegacyBlockChainWriter::LegacyBlockChainWriter(const std::string &index_file_nam m_indexes_file.write(&count, sizeof(count)); } -void LegacyBlockChainWriter::write_block(const bytecoin::RawBlock &raw_block) { - bytecoin::BinaryArray ba = seria::to_binary(raw_block); +void LegacyBlockChainWriter::write_block(const RawBlock &raw_block) { + BinaryArray ba = seria::to_binary(raw_block); m_items_file.write(ba.data(), ba.size()); uint32_t si = static_cast(ba.size()); m_indexes_file.write(&si, sizeof si); diff --git a/src/Core/BlockChainFileFormat.hpp b/src/Core/BlockChainFileFormat.hpp index dd820919..bb693712 100644 --- a/src/Core/BlockChainFileFormat.hpp +++ b/src/Core/BlockChainFileFormat.hpp @@ -17,6 +17,7 @@ namespace bytecoin { class BlockChainState; // TODO - convert all read/writes to little endian class LegacyBlockChainReader { + const Currency ¤cy; std::unique_ptr m_items_file; std::unique_ptr m_indexes_file; Height m_count = 0; @@ -37,7 +38,8 @@ class LegacyBlockChainReader { public: // No exceptions, just return block count 0 - explicit LegacyBlockChainReader(const std::string &index_file_name, const std::string &item_file_name); + explicit LegacyBlockChainReader( + const Currency ¤cy, const std::string &index_file_name, const std::string &item_file_name); ~LegacyBlockChainReader(); Height get_block_count() const { return m_count; } BinaryArray get_block_data_by_index(Height); @@ -55,7 +57,7 @@ class LegacyBlockChainWriter { public: LegacyBlockChainWriter(const std::string &index_file_name, const std::string &item_file_name, uint64_t count); - void write_block(const bytecoin::RawBlock &raw_block); + void write_block(const RawBlock &raw_block); static bool export_blockchain2(const std::string &export_folder, const BlockChainState &block_chain); }; diff --git a/src/Core/BlockChainState.cpp b/src/Core/BlockChainState.cpp index b3a3e271..c6bc8162 100644 --- a/src/Core/BlockChainState.cpp +++ b/src/Core/BlockChainState.cpp @@ -32,7 +32,7 @@ using namespace platform; namespace seria { void ser_members(IBlockChainState::UnlockTimePublickKeyHeightSpent &v, ISeria &s) { - seria_kv("unlock_time", v.unlock_time, s); + seria_kv("unlock_block_or_timestamp", v.unlock_block_or_timestamp, s); seria_kv("public_key", v.public_key, s); seria_kv("height", v.height, s); seria_kv("spent", v.spent, s); @@ -60,14 +60,14 @@ bool BlockChainState::DeltaState::read_keyimage(const KeyImage &key_image, Heigh } uint32_t BlockChainState::DeltaState::push_amount_output( - Amount amount, UnlockMoment unlock_time, Height block_height, const PublicKey &pk) { + Amount amount, BlockOrTimestamp unlock_time, Height block_height, const PublicKey &pk) { uint32_t pg = m_parent_state->next_global_index_for_amount(amount); auto &ga = m_global_amounts[amount]; ga.push_back(std::make_pair(unlock_time, pk)); return pg + static_cast(ga.size()) - 1; } -void BlockChainState::DeltaState::pop_amount_output(Amount amount, UnlockMoment unlock_time, const PublicKey &pk) { +void BlockChainState::DeltaState::pop_amount_output(Amount amount, BlockOrTimestamp unlock_time, const PublicKey &pk) { std::vector> &el = m_global_amounts[amount]; invariant(!el.empty(), "DeltaState::pop_amount_output underflow"); invariant(el.back().first == unlock_time && el.back().second == pk, "DeltaState::pop_amount_output wrong element"); @@ -89,10 +89,10 @@ bool BlockChainState::DeltaState::read_amount_output( auto git = m_global_amounts.find(amount); if (git == m_global_amounts.end() || global_index >= git->second.size()) return false; - unp->unlock_time = git->second[global_index].first; - unp->public_key = git->second[global_index].second; - unp->height = m_block_height; - unp->spent = false; // Spending just created outputs inside mempool or block is prohibited, simplifying logic + unp->unlock_block_or_timestamp = git->second[global_index].first; + unp->public_key = git->second[global_index].second; + unp->height = m_block_height; + unp->spent = false; // Spending just created outputs inside mempool or block is prohibited, simplifying logic return true; } void BlockChainState::DeltaState::spend_output(Amount amount, uint32_t global_index) { @@ -136,7 +136,7 @@ static std::string validate_semantic(bool generating, const Transaction &tx, uin return "OUTPUT_ZERO_AMOUNT"; if (output.target.type() == typeid(KeyOutput)) { const KeyOutput &key_output = boost::get(output.target); - if (check_output_key && !key_isvalid(key_output.key)) + if (check_output_key && !key_isvalid(key_output.public_key)) return "OUTPUT_INVALID_KEY"; } else return "OUTPUT_UNKNOWN_TYPE"; @@ -202,7 +202,7 @@ BlockChainState::BlockChainState(logging::ILogger &log, const Config &config, co genesis_block.header = currency.genesis_block_template; RawBlock raw_block; invariant(genesis_block.to_raw_block(raw_block), "Genesis block failed to convert into raw block"); - PreparedBlock pb(std::move(raw_block), nullptr); + PreparedBlock pb(std::move(raw_block), m_currency, nullptr); api::BlockHeader info; invariant(add_block(pb, &info, std::string()) != BroadcastAction::BAN, "Genesis block failed to add"); } @@ -210,6 +210,54 @@ BlockChainState::BlockChainState(logging::ILogger &log, const Config &config, co m_log(logging::INFO) << "BlockChainState::BlockChainState height=" << get_tip_height() << " cumulative_difficulty=" << get_tip_cumulative_difficulty() << " bid=" << get_tip_bid() << std::endl; + /* RawBlock rb; + Block bb; + invariant(read_block(common::pfh("ec140124695fbe90929a3f49dbe4d2b88fa0ad5ae271aedf0ebcd9f55b6bd3d7"), + &rb), ""); + invariant(bb.from_raw_block(rb), ""); + Hash ha1 = get_auxiliary_block_header_hash(bb.header); + Hash ha2 = get_block_hash(bb.header); + auto ba3 = currency.get_block_long_hashing_data(bb.header); + auto bbh = seria::to_binary(bb.header); + std::cout << common::to_hex(rb.block.data(), rb.block.size()) << std::endl; + std::cout << common::to_hex(bbh.data(), bbh.size()) << std::endl; + invariant(bbh == rb.block, "");*/ + /* size_t templates_size = 0; + size_t true_headers_size = 0; + Height start = 400000; + Height ha = start; + for(; ha < start + 10000; ++ha ){ + Hash bid; + if(!read_chain(ha, &bid)) + break; + RawBlock rb; + Block bb; + invariant(read_block(bid, &rb), ""); + invariant(bb.from_raw_block(rb), ""); + BinaryArray tba = seria::to_binary(static_cast(bb.header)); + BinaryArray tba2 = seria::to_binary(get_body_proxy_from_template(bb.header)); + templates_size += rb.block.size(); + true_headers_size += tba.size() + tba2.size(); + } + std::cout << "from height " << start << " to " << ha << " block templates size=" << templates_size << " true + headers + body proxies size=" << true_headers_size << std::endl;*/ + /*BlockHeader tr; + tr.major_version = 104; + tr.cm_merkle_branch.push_back(crypto::rand()); + tr.cm_merkle_branch.push_back(crypto::rand()); + tr.cm_merkle_branch.push_back(Hash{}); + tr.cm_merkle_branch.push_back(Hash{}); + tr.cm_merkle_branch.push_back(Hash{}); + tr.cm_merkle_branch.push_back(crypto::rand()); + tr.cm_merkle_branch.push_back(Hash{}); + tr.cm_merkle_branch.push_back(Hash{}); + tr.cm_merkle_branch.push_back(Hash{}); + tr.cm_merkle_branch.push_back(crypto::rand()); + BinaryArray ba = seria::to_binary(tr); + std::cout << seria::to_json_value(tr).to_string() << std::endl; + BlockHeader tr2; + seria::from_binary(tr2, ba); + invariant(tr.cm_merkle_branch == tr2.cm_merkle_branch, "");*/ build_blods(); } @@ -226,9 +274,8 @@ std::string BlockChainState::check_standalone_consensus( if (get_tip_bid() != prev_info.hash) // Optimization for most common case calculate_consensus_values(prev_info, &info->size_median, &info->timestamp_median); - auto next_block_granted_full_reward_zone = m_currency.block_granted_full_reward_zone_by_block_version( - block.header.major_version); // We will check version later in this fun - info->effective_size_median = std::max(info->size_median, next_block_granted_full_reward_zone); + auto next_minimum_size_median = m_currency.get_minimum_size_median(block.header.major_version); + info->effective_size_median = std::max(info->size_median, next_minimum_size_median); size_t cumulative_size = 0; for (size_t i = 0; i != pb.raw_block.transactions.size(); ++i) { @@ -248,8 +295,10 @@ std::string BlockChainState::check_standalone_consensus( if (info->block_size > max_block_cumulative_size) return "CUMULATIVE_BLOCK_SIZE_TOO_BIG"; - // block at UPGRADE_HEIGHT still has old version. - if (block.header.major_version != m_currency.get_block_major_version_for_height(info->height)) + uint8_t should_be_major = 0, might_be_minor = 0; + if (!fill_next_block_versions(prev_info, false, &should_be_major, &might_be_minor)) + return "DOES_NOT_PASS_THROUGH_LAST_SW_CHECKPOINT"; + if (block.header.major_version != should_be_major) return "WRONG_VERSION"; if (block.header.major_version >= 2) { @@ -271,19 +320,19 @@ std::string BlockChainState::check_standalone_consensus( if (block.header.base_transaction.inputs[0].type() != typeid(CoinbaseInput)) return "INPUT_UNEXPECTED_TYPE"; - if (boost::get(block.header.base_transaction.inputs[0]).block_index != info->height) + if (boost::get(block.header.base_transaction.inputs[0]).height != info->height) return "BASE_INPUT_WRONG_BLOCK_INDEX"; - if (block.header.base_transaction.unlock_time != info->height + m_currency.mined_money_unlock_window) + if (block.header.base_transaction.unlock_block_or_timestamp != info->height + m_currency.mined_money_unlock_window) return "WRONG_TRANSACTION_UNLOCK_TIME"; - const bool check_keys = !m_currency.is_in_sw_checkpoint_zone(info->height); + const bool check_keys = m_config.paranoid_checks || !m_currency.is_in_sw_checkpoint_zone(info->height); uint64_t miner_reward = 0; for (const auto &output : block.header.base_transaction.outputs) { // TODO - call validate_semantic if (output.amount == 0) return "OUTPUT_ZERO_AMOUNT"; if (output.target.type() == typeid(KeyOutput)) { - if (check_keys && !key_isvalid(boost::get(output.target).key)) + if (check_keys && !key_isvalid(boost::get(output.target).public_key)) return "OUTPUT_INVALID_KEY"; } else return "OUTPUT_UNKNOWN_TYPE"; @@ -312,24 +361,25 @@ std::string BlockChainState::check_standalone_consensus( if (info->difficulty == 0) return "DIFFICULTY_OVERHEAD"; - Amount cumulative_fee = 0; + Amount transactions_fee = 0; for (const auto &tx : block.transactions) { Amount fee = 0; if (!get_tx_fee(tx, &fee)) return "WRONG_AMOUNT"; - cumulative_fee += fee; + transactions_fee += fee; } int64_t emission_change = 0; auto already_generated_coins = prev_info.already_generated_coins; - if (!m_currency.get_block_reward(block.header.major_version, info->effective_size_median, 0, - already_generated_coins, 0, &info->base_reward, &emission_change) || - !m_currency.get_block_reward(block.header.major_version, info->effective_size_median, info->block_size, - already_generated_coins, cumulative_fee, &info->reward, &emission_change)) { - // log(Logging::WARNING) << "Block " << hash << " has too big cumulative size"; + if(info->block_size > info->effective_size_median * 2) return "CUMULATIVE_BLOCK_SIZE_TOO_BIG"; - } + + m_currency.get_block_reward(block.header.major_version, info->effective_size_median, 0, + already_generated_coins, 0, &info->base_reward, &emission_change); + + m_currency.get_block_reward(block.header.major_version, info->effective_size_median, info->block_size, + already_generated_coins, transactions_fee, &info->reward, &emission_change); if (miner_reward != info->reward) { // log(Logging::WARNING) << "Block reward mismatch for block " << @@ -339,11 +389,11 @@ std::string BlockChainState::check_standalone_consensus( } info->already_generated_coins = prev_info.already_generated_coins + emission_change; info->already_generated_transactions = prev_info.already_generated_transactions + block.transactions.size() + 1; - info->total_fee_amount = cumulative_fee; - info->transactions_cumulative_size = static_cast(cumulative_size); + info->transactions_fee = transactions_fee; + info->transactions_size = static_cast(cumulative_size); for (auto &&tx : pb.block.transactions) { Amount tx_fee = 0; - std::string tx_result = validate_semantic(false, tx, &tx_fee, check_keys); + std::string tx_result = validate_semantic(false, tx, &tx_fee, m_config.paranoid_checks || check_keys); if (!tx_result.empty()) return tx_result; } @@ -352,15 +402,26 @@ std::string BlockChainState::check_standalone_consensus( if (!m_currency.check_sw_checkpoint(info->height, info->hash, is_checkpoint)) return "CHECKPOINT_BLOCK_HASH_MISMATCH"; } else { - if (!check_pow) + if (!check_pow && !m_config.paranoid_checks) return std::string(); - Hash long_hash = pb.long_block_hash != Hash{} ? pb.long_block_hash - : get_block_long_hash(block.header, m_hash_crypto_context); - if (!m_currency.check_proof_of_work(long_hash, block.header, info->difficulty)) + Hash long_hash = pb.long_block_hash; + if (long_hash == Hash{}) { + auto body_proxy = get_body_proxy_from_template(block.header); + auto ba = m_currency.get_block_long_hashing_data(block.header, body_proxy); + long_hash = m_hash_crypto_context.cn_slow_hash(ba.data(), ba.size()); + } + if (!check_hash(long_hash, info->difficulty)) return "PROOF_OF_WORK_TOO_WEAK"; } return std::string(); } +void BlockChainState::fill_statistics(api::bytecoind::GetStatistics::Response &res) const { + BlockChain::fill_statistics(res); + res.transaction_pool_size = m_memory_state_total_size; + res.transaction_pool_max_size = MAX_POOL_SIZE; + Hash minimal_tid; + res.transaction_pool_lowest_fee_per_byte = minimum_pool_fee_per_byte(&minimal_tid); +} void BlockChainState::calculate_consensus_values( const api::BlockHeader &prev_info, uint32_t *next_median_size, Timestamp *next_median_timestamp) const { @@ -396,22 +457,33 @@ void BlockChainState::tip_changed() { calculate_consensus_values(get_tip(), &m_next_median_size, &m_next_median_timestamp); } -bool BlockChainState::create_mining_block_template(BlockTemplate *b, const AccountPublicAddress &adr, - const BinaryArray &extra_nonce, Difficulty *difficulty, Height *height) const { - clear_mining_transactions(); - *height = get_tip_height() + 1; +bool BlockChainState::create_mining_block_template(const AccountPublicAddress &adr, + const BinaryArray &extra_nonce, BlockTemplate *b, Difficulty *difficulty, Height *height) const { + return create_mining_block_template(adr, extra_nonce, b, difficulty, height, get_tip_bid()); +} - *b = BlockTemplate{}; - b->major_version = m_currency.get_block_major_version_for_height(*height); - b->minor_version = m_currency.get_block_minor_version_for_height(*height); +bool BlockChainState::create_mining_block_template(const AccountPublicAddress &adr, + const BinaryArray &extra_nonce, BlockTemplate *b, Difficulty *difficulty, Height * height, Hash parent_bid) const { + api::BlockHeader parent_info; + if (!read_header(parent_bid, &parent_info)) + throw std::runtime_error("Attempt to mine from block we do not have"); + *height = parent_info.height + 1; + *b = BlockTemplate{}; + if (!fill_next_block_versions(parent_info, false, &b->major_version, &b->minor_version)) + throw std::runtime_error( + "Mining of block in chain not passing through last SW checkpoint is not possible (will not be accepted by network anyway)"); + uint32_t next_median_size = 0; + Timestamp next_median_timestamp = 0; + calculate_consensus_values(parent_info, &next_median_size, &next_median_timestamp); + clear_mining_transactions(); // ???? { std::vector timestamps; std::vector difficulties; - Height blocks_count = std::min(get_tip_height(), m_currency.difficulty_blocks_count()); // TODO - excess min + Height blocks_count = std::min(parent_info.height, m_currency.difficulty_blocks_count()); timestamps.reserve(blocks_count); difficulties.reserve(blocks_count); - auto timestamps_window = get_tip_segment(get_tip(), blocks_count, false); + auto timestamps_window = get_tip_segment(parent_info, blocks_count, false); for (auto it = timestamps_window.begin(); it != timestamps_window.end(); ++it) { timestamps.push_back(it->timestamp); difficulties.push_back(it->cumulative_difficulty); @@ -428,35 +500,34 @@ bool BlockChainState::create_mining_block_template(BlockTemplate *b, const Accou b->parent_block.minor_version = 0; b->parent_block.transaction_count = 1; - TransactionExtraMergeMiningTag mm_tag{}; - if (!append_merge_mining_tag_to_extra(b->parent_block.base_transaction.extra, mm_tag)) { - m_log(logging::ERROR) << logging::BrightRed << "Failed to append merge mining tag to extra of " - "the parent block miner transaction"; - return false; - } + extra_add_merge_mining_tag(b->parent_block.base_transaction.extra, TransactionExtraMergeMiningTag{}); } - b->previous_block_hash = get_tip_bid(); - b->timestamp = std::max(platform::now_unix_timestamp(), m_next_median_timestamp); + b->previous_block_hash = parent_bid; + b->parent_block.timestamp = std::max(platform::now_unix_timestamp(), next_median_timestamp); + b->timestamp = b->parent_block.timestamp; - auto next_block_granted_full_reward_zone = - m_currency.block_granted_full_reward_zone_by_block_version(b->major_version); - auto effective_size_median = std::max(m_next_median_size, next_block_granted_full_reward_zone); - Amount already_generated_coins = get_tip().already_generated_coins; + auto next_minimum_size_median = m_currency.get_minimum_size_median(b->major_version); + auto effective_size_median = std::max(next_median_size, next_minimum_size_median); + Amount already_generated_coins = parent_info.already_generated_coins; - auto max_total_size = (125 * effective_size_median) / 100; - auto max_cumulative_size = m_currency.max_block_cumulative_size(*height); - max_total_size = std::min(max_total_size, max_cumulative_size) - m_currency.miner_tx_blob_reserved_size; +// auto max_total_size = (150 * effective_size_median) / 100; + const auto max_consensus_block_size = std::min(m_currency.max_block_cumulative_size(*height), 2 * effective_size_median); + const auto max_txs_size = max_consensus_block_size - m_currency.miner_tx_blob_reserved_size; std::vector pool_hashes; for (auto &&msf : m_memory_state_fee_tx) for (auto &&ha : msf.second) pool_hashes.push_back(ha); size_t txs_size = 0; - Amount fee = 0; + Amount txs_fee = 0; DeltaState memory_state(*height, b->timestamp, this); // will be get_tip().timestamp_unlock after fork - // technically we should give unlock timestamp of next block, but more + // TODO - technically we should give unlock timestamp of next block, but more // conservative also works + Amount base_reward = 0; + SignedAmount emission_change = 0; + m_currency.get_block_reward(b->major_version, effective_size_median, 0, + already_generated_coins, 0, &base_reward, &emission_change); for (; !pool_hashes.empty(); pool_hashes.pop_back()) { auto tit = m_memory_state_tx.find(pool_hashes.back()); @@ -465,23 +536,31 @@ bool BlockChainState::create_mining_block_template(BlockTemplate *b, const Accou assert(false); continue; } - const size_t block_size_limit = max_total_size; - const size_t tx_size = tit->second.binary_tx.size(); - if (txs_size + tx_size > block_size_limit) + const size_t tx_size = tit->second.binary_tx.size(); + const Amount tx_fee = tit->second.fee; + if( txs_size + tx_size > max_txs_size) continue; - Amount single_fee = tit->second.fee; BlockGlobalIndices global_indices; Height conflict_height = 0; const std::string result = redo_transaction_get_error(false, tit->second.tx, &memory_state, &global_indices, &conflict_height, true); if (!result.empty()) { m_log(logging::ERROR) << "Transaction " << tit->first - << " is in pool, but could not be redone result=" << result << std::endl; + << " is in pool, but could not be redone result=" << result + << " Conflict height=" << conflict_height << std::endl; continue; } + if (txs_size + tx_size > effective_size_median){ + Amount reward; + m_currency.get_block_reward(b->major_version, effective_size_median, txs_size + tx_size, + already_generated_coins, txs_fee + tx_fee, &reward, &emission_change); + if(reward < base_reward) + continue; + } txs_size += tx_size; - fee += single_fee; + txs_fee += tx_fee; b->transaction_hashes.emplace_back(tit->first); + m_mining_transactions.erase(tit->first); // We want ot update height to most recent m_mining_transactions.insert(std::make_pair(tit->first, std::make_pair(tit->second.binary_tx, *height))); m_log(logging::TRACE) << "Transaction " << tit->first << " included to block template"; } @@ -494,208 +573,30 @@ bool BlockChainState::create_mining_block_template(BlockTemplate *b, const Accou // expected block size // make blocks coin-base tx looks close to real coinbase tx to get truthful // blob size - bool r = m_currency.construct_miner_tx(b->major_version, *height, effective_size_median, already_generated_coins, - txs_size, fee, adr, &b->base_transaction, extra_nonce, 11); - if (!r) { - m_log(logging::ERROR) << logging::BrightRed << "Failed to construct miner tx, first chance"; - return false; - } - - size_t cumulative_size = txs_size + seria::binary_size(b->base_transaction); - const size_t TRIES_COUNT = 10; - for (size_t try_count = 0; try_count < TRIES_COUNT; ++try_count) { - r = m_currency.construct_miner_tx(b->major_version, *height, effective_size_median, already_generated_coins, - cumulative_size, fee, adr, &b->base_transaction, extra_nonce, 11); - if (!r) { - m_log(logging::ERROR) << logging::BrightRed << "Failed to construct miner tx, second chance"; - return false; - } - - size_t coinbase_blob_size = seria::binary_size(b->base_transaction); - if (coinbase_blob_size > cumulative_size - txs_size) { - cumulative_size = txs_size + coinbase_blob_size; - continue; - } - - if (coinbase_blob_size < cumulative_size - txs_size) { - size_t delta = cumulative_size - txs_size - coinbase_blob_size; - common::append(b->base_transaction.extra, delta, 0); - // here could be 1 byte difference, because of extra field counter is - // varint, and it can become from - // 1-byte len to 2-bytes len. - if (cumulative_size != txs_size + seria::binary_size(b->base_transaction)) { - if (cumulative_size + 1 != txs_size + seria::binary_size(b->base_transaction)) { - m_log(logging::ERROR) - << logging::BrightRed << "unexpected case: cumulative_size=" << cumulative_size - << " + 1 is not equal txs_cumulative_size=" << txs_size - << " + get_object_blobsize(b.base_transaction)=" << seria::binary_size(b->base_transaction); - return false; - } - - b->base_transaction.extra.resize(b->base_transaction.extra.size() - 1); - if (cumulative_size != txs_size + seria::binary_size(b->base_transaction)) { - // ooh, not lucky, -1 makes varint-counter size smaller, in that case - // we continue to grow with - // cumulative_size - m_log(logging::TRACE) - << logging::BrightRed << "Miner tx creation have no luck with delta_extra size = " << delta - << " and " << delta - 1; - cumulative_size += delta - 1; - continue; - } - - m_log(logging::TRACE) << logging::BrightGreen - << "Setting extra for block: " << b->base_transaction.extra.size() - << ", try_count=" << try_count; - } - } - if (cumulative_size != txs_size + seria::binary_size(b->base_transaction)) { - m_log(logging::ERROR) << logging::BrightRed << "unexpected case: cumulative_size=" << cumulative_size - << " is not equal txs_cumulative_size=" << txs_size - << " + get_object_blobsize(b.base_transaction)=" - << seria::binary_size(b->base_transaction); - return false; - } - return true; - } - m_log(logging::ERROR) << logging::BrightRed << "Failed to create_block_template with " << TRIES_COUNT << " tries"; - return false; -} - -bool BlockChainState::create_mining_block_template2(BlockTemplate *b, const AccountPublicAddress &adr, - const BinaryArray &extra_nonce, Difficulty *difficulty, Hash parent_bid) const { - uint32_t next_median_size = 0; - Timestamp next_median_timestamp = 0; - const Hash bid = parent_bid; - api::BlockHeader binfo; - if (!read_header(bid, &binfo)) { - // m_log(logging::ERROR) << logging::BrightRed << "Expected header was not found" << - // common::pod_to_hex(bid); - return false; - } - const Height height = binfo.height + 1; - *b = BlockTemplate{}; - b->major_version = m_currency.get_block_major_version_for_height(height); - b->minor_version = m_currency.get_block_minor_version_for_height(height); - calculate_consensus_values(binfo, &next_median_size, &next_median_timestamp); - clear_mining_transactions(); // ???? - { - std::vector timestamps; - std::vector difficulties; - Height blocks_count = std::min(height - 1, m_currency.difficulty_blocks_count()); - timestamps.reserve(blocks_count); - difficulties.reserve(blocks_count); - auto timestamps_window = get_tip_segment(binfo, blocks_count, false); - for (auto it = timestamps_window.begin(); it != timestamps_window.end(); ++it) { - timestamps.push_back(it->timestamp); - difficulties.push_back(it->cumulative_difficulty); - } - *difficulty = m_currency.next_effective_difficulty(b->major_version, timestamps, difficulties); - } - if (*difficulty == 0) { - m_log(logging::ERROR) << "difficulty overhead in create_mining_block_template." << std::endl; - return false; - } - - if (b->major_version >= 2) { - b->parent_block.major_version = 1; - b->parent_block.minor_version = 0; - b->parent_block.transaction_count = 1; - - TransactionExtraMergeMiningTag mm_tag{}; - if (!append_merge_mining_tag_to_extra(b->parent_block.base_transaction.extra, mm_tag)) { - m_log(logging::ERROR) << logging::BrightRed << "Failed to append merge mining tag to extra of " - "the parent block miner transaction"; - return false; - } - } - - b->previous_block_hash = bid; - b->timestamp = std::max(platform::now_unix_timestamp(), next_median_timestamp); - - auto next_block_granted_full_reward_zone = - m_currency.block_granted_full_reward_zone_by_block_version(b->major_version); - auto effective_size_median = std::max(next_median_size, next_block_granted_full_reward_zone); - Amount already_generated_coins = binfo.already_generated_coins; +// bool r = m_currency.construct_miner_tx(b->major_version, *height, effective_size_median, already_generated_coins, +// txs_size, txs_fee, m_config.mineproof_secret, adr, &b->base_transaction, extra_nonce, 11); +// if (!r) { +// m_log(logging::ERROR) << logging::BrightRed << "Failed to construct miner tx, first chance"; +// return false; +// } - auto max_total_size = (125 * effective_size_median) / 100; - auto max_cumulative_size = m_currency.max_block_cumulative_size(height); - max_total_size = std::min(max_total_size, max_cumulative_size) - m_currency.miner_tx_blob_reserved_size; - - std::vector pool_hashes; - for (auto &&msf : m_memory_state_fee_tx) - for (auto &&ha : msf.second) - pool_hashes.push_back(ha); - size_t txs_size = 0; - Amount fee = 0; - DeltaState memory_state(height, b->timestamp, this); // will be get_tip().timestamp_unlock after fork - // technically we should give unlock timestamp of next block, but more - // conservative also works - - for (; !pool_hashes.empty(); pool_hashes.pop_back()) { - auto tit = m_memory_state_tx.find(pool_hashes.back()); - if (tit == m_memory_state_tx.end()) { - m_log(logging::ERROR) << "Transaction " << common::pod_to_hex(pool_hashes.back()) - << " is in pool index, but not in pool"; - assert(false); - continue; - } - const size_t block_size_limit = max_total_size; - const size_t tx_size = tit->second.binary_tx.size(); - if (txs_size + tx_size > block_size_limit) - continue; - Amount single_fee = tit->second.fee; - BlockGlobalIndices global_indices; - Height conflict_height = 0; - const std::string result = - redo_transaction_get_error(false, tit->second.tx, &memory_state, &global_indices, &conflict_height, true); - if (!result.empty()) { - m_log(logging::ERROR) << "Transaction " << common::pod_to_hex(tit->first) - << " is in pool, but could not be redone result=" << result - << " Conflict height=" << conflict_height << std::endl; - continue; - } - txs_size += tx_size; - fee += single_fee; - b->transaction_hashes.emplace_back(tit->first); - m_mining_transactions.insert(std::make_pair(tit->first, std::make_pair(tit->second.binary_tx, height))); - m_log(logging::TRACE) << "Transaction " << common::pod_to_hex(tit->first) << " included to block template"; - } - - // two-phase miner transaction generation: we don't know exact block size - // until we prepare block, but we don't know - // reward until we know - // block size, so first miner transaction generated with fake amount of money, - // and with phase we know think we know - // expected block size - // make blocks coin-base tx looks close to real coinbase tx to get truthful - // blob size - bool r = m_currency.construct_miner_tx(b->major_version, height, effective_size_median, already_generated_coins, - txs_size, fee, adr, &b->base_transaction, extra_nonce, 11); - if (!r) { - m_log(logging::ERROR) << logging::BrightRed << "Failed to construct miner tx, first chance"; - return false; - } - - size_t cumulative_size = txs_size + seria::binary_size(b->base_transaction); - const size_t TRIES_COUNT = 10; + size_t cumulative_size = txs_size; + const size_t TRIES_COUNT = 11; for (size_t try_count = 0; try_count < TRIES_COUNT; ++try_count) { - r = m_currency.construct_miner_tx(b->major_version, height, effective_size_median, already_generated_coins, - cumulative_size, fee, adr, &b->base_transaction, extra_nonce, 11); - if (!r) { + if (!m_currency.construct_miner_tx(b->major_version, *height, effective_size_median, already_generated_coins, + cumulative_size, txs_fee, m_config.mineproof_secret, adr, &b->base_transaction, extra_nonce, 11)) { m_log(logging::ERROR) << logging::BrightRed << "Failed to construct miner tx, second chance"; return false; } - size_t coinbase_blob_size = seria::binary_size(b->base_transaction); - if (coinbase_blob_size > cumulative_size - txs_size) { + if (coinbase_blob_size + txs_size > cumulative_size) { cumulative_size = txs_size + coinbase_blob_size; continue; } - if (coinbase_blob_size < cumulative_size - txs_size) { - size_t delta = cumulative_size - txs_size - coinbase_blob_size; + if (coinbase_blob_size + txs_size < cumulative_size) { + size_t delta = cumulative_size - (coinbase_blob_size + txs_size); common::append(b->base_transaction.extra, delta, 0); // here could be 1 byte difference, because of extra field counter is // varint, and it can become from @@ -712,18 +613,15 @@ bool BlockChainState::create_mining_block_template2(BlockTemplate *b, const Acco b->base_transaction.extra.resize(b->base_transaction.extra.size() - 1); if (cumulative_size != txs_size + seria::binary_size(b->base_transaction)) { // ooh, not lucky, -1 makes varint-counter size smaller, in that case - // we continue to grow with - // cumulative_size + // we continue to grow with cumulative_size m_log(logging::TRACE) << logging::BrightRed << "Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1; cumulative_size += delta - 1; continue; } - - m_log(logging::TRACE) << logging::BrightGreen - << "Setting extra for block: " << b->base_transaction.extra.size() - << ", try_count=" << try_count; + m_log(logging::TRACE) << logging::BrightGreen << "Setting extra for block: " << + b->base_transaction.extra.size() << ", try_count=" << try_count; } } if (cumulative_size != txs_size + seria::binary_size(b->base_transaction)) { @@ -740,10 +638,10 @@ bool BlockChainState::create_mining_block_template2(BlockTemplate *b, const Acco } uint32_t BlockChainState::get_next_effective_median_size() const { - uint8_t next_major_version = m_currency.get_block_major_version_for_height(get_tip_height() + 1); - auto next_block_granted_full_reward_zone = - m_currency.block_granted_full_reward_zone_by_block_version(next_major_version); - return std::max(m_next_median_size, next_block_granted_full_reward_zone); + uint8_t should_be_major = 0, might_be_minor = 0; + fill_next_block_versions(get_tip(), false, &should_be_major, &might_be_minor); + auto next_minimum_size_median = m_currency.get_minimum_size_median(should_be_major); + return std::max(m_next_median_size, next_minimum_size_median); } BroadcastAction BlockChainState::add_mined_block( @@ -770,14 +668,14 @@ BroadcastAction BlockChainState::add_mined_block( } raw_block->transactions.emplace_back(*binary_tx); } - PreparedBlock pb(std::move(*raw_block), nullptr); + PreparedBlock pb(std::move(*raw_block), m_currency, nullptr); *raw_block = pb.raw_block; return add_block(pb, info, "json_rpc"); } void BlockChainState::clear_mining_transactions() const { for (auto tit = m_mining_transactions.begin(); tit != m_mining_transactions.end();) - if (get_tip_height() > tit->second.second + 3) // Remember txs for 3 blocks + if (get_tip_height() > tit->second.second + 10) // Remember used txs for some number of blocks tit = m_mining_transactions.erase(tit); else ++tit; @@ -865,17 +763,17 @@ AddTransactionResult BlockChainState::add_transaction(const Hash &tid, const Tra if (read_keyimage(in.key_image, conflict_height)) { // std::cout << tid << " " << in.key_image << // std::endl; -// m_log(logging::WARNING) << "OUTPUT_ALREADY_SPENT in transaction " << tid -// << std::endl; // TODO - remove + // m_log(logging::WARNING) << "OUTPUT_ALREADY_SPENT in transaction " << tid + // << std::endl; // TODO - remove return AddTransactionResult::OUTPUT_ALREADY_SPENT; // Already spent in main chain } } } Amount my_fee3 = 0; - const std::string validate_result = validate_semantic(false, tx, &my_fee3, check_sigs); + const std::string validate_result = validate_semantic(false, tx, &my_fee3, m_config.paranoid_checks || check_sigs); if (!validate_result.empty()) { m_log(logging::WARNING) << "add_transaction validation failed " << validate_result << " in transaction " << tid - << std::endl; + << std::endl; return AddTransactionResult::BAN; } DeltaState memory_state(unlock_height, unlock_timestamp, this); @@ -1026,7 +924,7 @@ std::string BlockChainState::redo_transaction_get_error(bool generating, const T DeltaState *delta_state, BlockGlobalIndices *global_indices, Height *conflict_height, bool check_sigs) const { const bool check_outputs = check_sigs; Hash tx_prefix_hash; - if (check_sigs) + if (m_config.paranoid_checks || check_sigs) tx_prefix_hash = get_transaction_prefix_hash(transaction); DeltaState tx_delta(delta_state->get_block_height(), delta_state->get_unlock_timestamp(), delta_state); global_indices->resize(global_indices->size() + 1); @@ -1039,7 +937,7 @@ std::string BlockChainState::redo_transaction_get_error(bool generating, const T if (input.type() == typeid(KeyInput)) { const KeyInput &in = boost::get(input); - if (check_sigs || check_outputs) { + if (m_config.paranoid_checks || check_sigs || check_outputs) { Height height = 0; if (tx_delta.read_keyimage(in.key_image, &height)) { *conflict_height = height; @@ -1060,8 +958,8 @@ std::string BlockChainState::redo_transaction_get_error(bool generating, const T return "INPUT_INVALID_GLOBAL_INDEX"; } *conflict_height = std::max(*conflict_height, unp.height); - if (!m_currency.is_transaction_spend_time_unlocked( - unp.unlock_time, delta_state->get_block_height(), delta_state->get_unlock_timestamp())) + if (!m_currency.is_transaction_spend_time_unlocked(unp.unlock_block_or_timestamp, + delta_state->get_block_height(), delta_state->get_unlock_timestamp())) return "INPUT_SPEND_LOCKED_OUT"; output_keys[i] = unp.public_key; } @@ -1070,9 +968,11 @@ std::string BlockChainState::redo_transaction_get_error(bool generating, const T std::for_each(output_keys.begin(), output_keys.end(), [&output_key_pointers](const PublicKey &key) { output_key_pointers.push_back(&key); }); bool key_corrupted = false; - if (check_sigs && + if ((m_config.paranoid_checks || check_sigs) && !check_ring_signature(tx_prefix_hash, in.key_image, output_key_pointers.data(), - output_key_pointers.size(), transaction.signatures[input_index].data(), true, &key_corrupted)) { + output_key_pointers.size(), transaction.signatures[input_index].data(), + delta_state->get_block_height() >= m_currency.key_image_subgroup_checking_height, + &key_corrupted)) { if (key_corrupted) // TODO - db corrupted return "INPUT_CORRUPTED_SIGNATURES"; return "INPUT_INVALID_SIGNATURES"; @@ -1087,8 +987,8 @@ std::string BlockChainState::redo_transaction_get_error(bool generating, const T for (const auto &output : transaction.outputs) { if (output.target.type() == typeid(KeyOutput)) { const KeyOutput &key_output = boost::get(output.target); - auto global_index = tx_delta.push_amount_output(output.amount, transaction.unlock_time, 0, - key_output.key); // DeltaState ignores unlock point + auto global_index = tx_delta.push_amount_output(output.amount, transaction.unlock_block_or_timestamp, 0, + key_output.public_key); // DeltaState ignores unlock point my_indices.push_back(global_index); } } @@ -1100,7 +1000,8 @@ std::string BlockChainState::redo_transaction_get_error(bool generating, const T void BlockChainState::undo_transaction(IBlockChainState *delta_state, Height, const Transaction &tx) { for (auto oit = tx.outputs.rbegin(); oit != tx.outputs.rend(); ++oit) { if (oit->target.type() == typeid(KeyOutput)) { - delta_state->pop_amount_output(oit->amount, tx.unlock_time, boost::get(oit->target).key); + delta_state->pop_amount_output( + oit->amount, tx.unlock_block_or_timestamp, boost::get(oit->target).public_key); } } for (auto iit = tx.inputs.rbegin(); iit != tx.inputs.rend(); ++iit) { @@ -1135,8 +1036,12 @@ bool BlockChainState::redo_block(const Hash &bhash, const Block &block, const ap DeltaState delta(info.height, info.timestamp, this); BlockGlobalIndices global_indices; global_indices.reserve(block.transactions.size() + 1); - const bool check_sigs = !m_currency.is_in_sw_checkpoint_zone(info.height + 1); - if (check_sigs && !ring_checker.start_work_get_error(this, m_currency, block, info.height, info.timestamp).empty()) + const bool check_sigs = m_config.paranoid_checks || !m_currency.is_in_sw_checkpoint_zone(info.height + 1); + if (check_sigs && + !ring_checker + .start_work_get_error(this, m_currency, block, info.height, info.timestamp, + info.height >= m_currency.key_image_subgroup_checking_height) + .empty()) return false; if (!redo_block(block, info, &delta, &global_indices)) return false; @@ -1154,13 +1059,13 @@ bool BlockChainState::redo_block(const Hash &bhash, const Block &block, const ap // update_first_seen_timestamp(th, 0); // } auto now = std::chrono::steady_clock::now(); - if (m_config.is_testnet || + if (m_config.net != "main" || std::chrono::duration_cast(now - log_redo_block_timestamp).count() > 1000) { log_redo_block_timestamp = now; m_log(logging::INFO) << "redo_block height=" << info.height << " bid=" << bhash << " #tx=" << block.transactions.size() << std::endl; } else { - if (check_sigs) // No point in writing log before checkpoints + if (m_config.paranoid_checks || check_sigs) // No point in writing log before checkpoints m_log(logging::TRACE) << "redo_block height=" << info.height << " bid=" << bhash << " #tx=" << block.transactions.size() << std::endl; } @@ -1194,23 +1099,23 @@ bool BlockChainState::read_block_output_global_indices(const Hash &bid, BlockGlo } std::vector BlockChainState::get_random_outputs( - Amount amount, size_t outs_count, Height height, Timestamp time) const { + Amount amount, size_t output_count, Height confirmed_height, Timestamp time) const { std::vector result; uint32_t total_count = next_global_index_for_amount(amount); // We might need better algorithm if we have lots of locked amounts - if (total_count <= outs_count) { + if (total_count <= output_count) { for (uint32_t i = 0; i != total_count; ++i) { api::Output item; UnlockTimePublickKeyHeightSpent unp; - item.amount = amount; - item.global_index = i; + item.amount = amount; + item.index = i; invariant(read_amount_output(amount, i, &unp), "global amount < total_count not found"); - item.unlock_time = unp.unlock_time; - item.public_key = unp.public_key; - item.height = unp.height; - if (unp.spent || unp.height > height ) + item.unlock_block_or_timestamp = unp.unlock_block_or_timestamp; + item.public_key = unp.public_key; + item.height = unp.height; + if (unp.spent || unp.height > confirmed_height) continue; - if( !m_currency.is_transaction_spend_time_unlocked(item.unlock_time, height, time)) + if (!m_currency.is_transaction_spend_time_unlocked(item.unlock_block_or_timestamp, confirmed_height, time)) continue; result.push_back(item); } @@ -1220,7 +1125,7 @@ std::vector BlockChainState::get_random_outputs( crypto::random_engine generator; std::lognormal_distribution distribution(1.9, 1.0); size_t attempts = 0; - for (; result.size() < outs_count && attempts < outs_count * 20; ++attempts) { // TODO - 20 + for (; result.size() < output_count && attempts < output_count * 20; ++attempts) { // TODO - 20 // uint32_t num = crypto::rand(); // num %= total_count; // 0 handled in if above double sample = distribution(generator); @@ -1232,15 +1137,23 @@ std::vector BlockChainState::get_random_outputs( continue; api::Output item; UnlockTimePublickKeyHeightSpent unp; - item.amount = amount; - item.global_index = num; + item.amount = amount; + item.index = num; invariant(read_amount_output(amount, num, &unp), "num < total_count not found"); - item.unlock_time = unp.unlock_time; - item.public_key = unp.public_key; - item.height = unp.height; - if (unp.spent || unp.height > height ) + item.unlock_block_or_timestamp = unp.unlock_block_or_timestamp; + item.public_key = unp.public_key; + item.height = unp.height; + if (unp.height > confirmed_height) { + if (confirmed_height + 128 < get_tip_height()) + total_count = num; + // heuristic - if confirmed_height is deep, the area under ditribution curve + // with height < confirmed_height might be very small, so we adjust total_count + // to get descent results after small number of attempts + continue; + } + if (unp.spent) continue; - if( !m_currency.is_transaction_spend_time_unlocked(item.unlock_time, height, time)) + if (!m_currency.is_transaction_spend_time_unlocked(item.unlock_block_or_timestamp, confirmed_height, time)) continue; result.push_back(item); } @@ -1271,7 +1184,7 @@ bool BlockChainState::read_keyimage(const KeyImage &key_image, Height *height) c } uint32_t BlockChainState::push_amount_output( - Amount amount, UnlockMoment unlock_time, Height block_height, const PublicKey &pk) { + Amount amount, BlockOrTimestamp unlock_time, Height block_height, const PublicKey &pk) { uint32_t my_gi = next_global_index_for_amount(amount); auto key = AMOUNT_OUTPUT_PREFIX + common::write_varint_sqlite4(amount) + common::write_varint_sqlite4(my_gi); BinaryArray ba = seria::to_binary(UnlockTimePublickKeyHeightSpent{unlock_time, pk, block_height}); @@ -1280,7 +1193,7 @@ uint32_t BlockChainState::push_amount_output( return my_gi; } -void BlockChainState::pop_amount_output(Amount amount, UnlockMoment unlock_time, const PublicKey &pk) { +void BlockChainState::pop_amount_output(Amount amount, BlockOrTimestamp unlock_time, const PublicKey &pk) { uint32_t next_gi = next_global_index_for_amount(amount); invariant(next_gi != 0, "BlockChainState::pop_amount_output underflow"); next_gi -= 1; @@ -1290,7 +1203,7 @@ void BlockChainState::pop_amount_output(Amount amount, UnlockMoment unlock_time, UnlockTimePublickKeyHeightSpent unp; invariant(read_amount_output(amount, next_gi, &unp), "BlockChainState::pop_amount_output element does not exist"); // TODO - check also was_height after upgrade to version 4 ? - invariant(!unp.spent && unp.unlock_time == unlock_time && unp.public_key == pk, + invariant(!unp.spent && unp.unlock_block_or_timestamp == unlock_time && unp.public_key == pk, "BlockChainState::pop_amount_output popping wrong element"); m_db.del(key, true); } @@ -1301,7 +1214,7 @@ uint32_t BlockChainState::next_global_index_for_amount(Amount amount) const { return it->second; std::string prefix = AMOUNT_OUTPUT_PREFIX + common::write_varint_sqlite4(amount); DB::Cursor cur2 = m_db.rbegin(prefix); - uint32_t alt_in = cur2.end() ? 0 : boost::lexical_cast(common::read_varint_sqlite4(cur2.get_suffix())) + 1; + uint32_t alt_in = cur2.end() ? 0 : common::integer_cast(common::read_varint_sqlite4(cur2.get_suffix())) + 1; m_next_gi_for_amount[amount] = alt_in; return alt_in; } @@ -1337,9 +1250,9 @@ void BlockChainState::test_print_outputs() { const char *be = cur.get_suffix().data(); const char *en = be + cur.get_suffix().size(); auto amount = common::read_varint_sqlite4(be, en); - uint32_t global_index = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + uint32_t global_index = common::integer_cast(common::read_varint_sqlite4(be, en)); if (be != en) - std::cout << "Excess value bytes for amount=" << amount << " global_index=" << global_index << std::endl; + std::cout << "Excess value bytes for amount=" << amount << " index=" << global_index << std::endl; if (amount != previous_amount) { if (previous_amount != (Amount)-1) { if (!coins.insert(std::make_pair(previous_amount, next_global_index)).second) { @@ -1351,11 +1264,11 @@ void BlockChainState::test_print_outputs() { next_global_index = 0; } if (global_index != next_global_index) { - std::cout << "Bad output index for amount=" << amount << " global_index=" << global_index << std::endl; + std::cout << "Bad output index for amount=" << amount << " index=" << global_index << std::endl; } next_global_index += 1; if (++total_counter % 2000000 == 0) - std::cout << "Working on amount=" << amount << " global_index=" << global_index << std::endl; + std::cout << "Working on amount=" << amount << " index=" << global_index << std::endl; } total_counter = 0; std::cout << "Total coins=" << total_counter << " total stacks=" << coins.size() << std::endl; @@ -1367,9 +1280,9 @@ void BlockChainState::test_print_outputs() { for (uint32_t i = 0; i != total_count; ++i) { UnlockTimePublickKeyHeightSpent unp; if (!read_amount_output(co.first, i, &unp)) - std::cout << "Failed to read amount=" << co.first << " global_index=" << i << std::endl; + std::cout << "Failed to read amount=" << co.first << " index=" << i << std::endl; if (++total_counter % 1000000 == 0) - std::cout << "Working on amount=" << co.first << " global_index=" << i << std::endl; + std::cout << "Working on amount=" << co.first << " index=" << i << std::endl; } } } diff --git a/src/Core/BlockChainState.hpp b/src/Core/BlockChainState.hpp index d838fbbb..a54ea02b 100644 --- a/src/Core/BlockChainState.hpp +++ b/src/Core/BlockChainState.hpp @@ -17,7 +17,7 @@ class Config; class IBlockChainState { public: struct UnlockTimePublickKeyHeightSpent { - UnlockMoment unlock_time = 0; + BlockOrTimestamp unlock_block_or_timestamp = 0; PublicKey public_key; Height height = 0; bool spent = false; @@ -27,8 +27,8 @@ class IBlockChainState { virtual void delete_keyimage(const KeyImage &) = 0; virtual bool read_keyimage(const KeyImage &, Height *) const = 0; - virtual uint32_t push_amount_output(Amount, UnlockMoment, Height, const PublicKey &) = 0; - virtual void pop_amount_output(Amount, UnlockMoment, const PublicKey &) = 0; + virtual uint32_t push_amount_output(Amount, BlockOrTimestamp, Height, const PublicKey &) = 0; + virtual void pop_amount_output(Amount, BlockOrTimestamp, const PublicKey &) = 0; virtual uint32_t next_global_index_for_amount(Amount) const = 0; virtual bool read_amount_output(Amount, uint32_t global_index, UnlockTimePublickKeyHeightSpent *) const = 0; virtual void spend_output(Amount, uint32_t global_index) = 0; @@ -38,10 +38,9 @@ class BlockChainState : public BlockChain, private IBlockChainState { public: BlockChainState(logging::ILogger &, const Config &, const Currency &, bool read_only); - const Currency &get_currency() const { return m_currency; } uint32_t get_next_effective_median_size() const; - std::vector get_random_outputs(Amount, size_t outs_count, Height, Timestamp) const; + std::vector get_random_outputs(Amount, size_t output_count, Height, Timestamp) const; typedef std::vector> BlockGlobalIndices; bool read_block_output_global_indices(const Hash &bid, BlockGlobalIndices *) const; @@ -64,15 +63,17 @@ class BlockChainState : public BlockChain, private IBlockChainState { const PoolTransMap &get_memory_state_transactions() const { return m_memory_state_tx; } bool create_mining_block_template( - BlockTemplate *, const AccountPublicAddress &, const BinaryArray &extra_nonce, Difficulty *, Height *) const; - bool create_mining_block_template2( - BlockTemplate *, const AccountPublicAddress &, const BinaryArray &extra_nonce, Difficulty *, Hash) const; + const AccountPublicAddress &, const BinaryArray &extra_nonce, BlockTemplate *, Difficulty *, Height *) const; + bool create_mining_block_template( + const AccountPublicAddress &, const BinaryArray &extra_nonce, BlockTemplate *, Difficulty *, Height *, Hash) const; BroadcastAction add_mined_block(const BinaryArray &raw_block_template, RawBlock *, api::BlockHeader *); static api::BlockHeader fill_genesis(Hash genesis_bid, const BlockTemplate &); void test_print_outputs(); + void fill_statistics(api::bytecoind::GetStatistics::Response &res) const override; + protected: virtual std::string check_standalone_consensus(const PreparedBlock &pb, api::BlockHeader *info, const api::BlockHeader &prev_info, bool check_pow) const override; @@ -100,8 +101,8 @@ class BlockChainState : public BlockChain, private IBlockChainState { void delete_keyimage(const KeyImage &) override; bool read_keyimage(const KeyImage &, Height *) const override; - uint32_t push_amount_output(Amount, UnlockMoment, Height, const PublicKey &) override; - void pop_amount_output(Amount, UnlockMoment, const PublicKey &) override; + uint32_t push_amount_output(Amount, BlockOrTimestamp, Height, const PublicKey &) override; + void pop_amount_output(Amount, BlockOrTimestamp, const PublicKey &) override; uint32_t next_global_index_for_amount(Amount) const override; bool read_amount_output(Amount, uint32_t global_index, UnlockTimePublickKeyHeightSpent *) const override; void spend_output(Amount, uint32_t global_index) override; @@ -111,8 +112,8 @@ class BlockChainState : public BlockChain, private IBlockChainState { void delete_keyimage(const KeyImage &) override; bool read_keyimage(const KeyImage &, Height *) const override; - uint32_t push_amount_output(Amount, UnlockMoment, Height, const PublicKey &) override; - void pop_amount_output(Amount, UnlockMoment, const PublicKey &) override; + uint32_t push_amount_output(Amount, BlockOrTimestamp, Height, const PublicKey &) override; + void pop_amount_output(Amount, BlockOrTimestamp, const PublicKey &) override; uint32_t next_global_index_for_amount(Amount) const override; bool read_amount_output(Amount, uint32_t global_index, UnlockTimePublickKeyHeightSpent *) const override; void spend_output(Amount, uint32_t global_index) override; diff --git a/src/Core/Config.cpp b/src/Core/Config.cpp index 5553344f..e7cce891 100644 --- a/src/Core/Config.cpp +++ b/src/Core/Config.cpp @@ -10,81 +10,86 @@ #include "platform/PathTools.hpp" #include "platform/Time.hpp" -static void parse_peer_and_add_to_container(const std::string &str, std::vector &container) { - bytecoin::NetworkAddress na{}; +using namespace common; +using namespace bytecoin; + +static void parse_peer_and_add_to_container(const std::string &str, std::vector &container) { + NetworkAddress na{}; if (!common::parse_ip_address_and_port(str, &na.ip, &na.port)) throw std::runtime_error("Wrong address format " + str + ", should be ip:port"); container.push_back(na); } -static void parse_net(const std::string &str, bool* is_testnet, bool* is_stagenet) { - if (str == "main") { - *is_stagenet = false; - *is_testnet = false; - return; - } - if (str == "stage") { - *is_stagenet = true; - *is_testnet = false; - return; +static std::string get_net(common::CommandLine &cmd) { + std::string net; + if (const char *pa = cmd.get("--net")) { + net = pa; + if (net == "main" || net == "stage" || net == "test") + return net; + throw std::runtime_error("Wrong net value " + net + ", should be 'test', 'stage', or 'main'"); } - if (str == "test") { - *is_stagenet = false; - *is_testnet = true; - return; - } - throw std::runtime_error("Wrong net value " + str + ", should be test, or stage, or main"); + if (cmd.get_bool("--testnet", "use --net=test instead")) + return "test"; + return "main"; } -using namespace common; -using namespace bytecoin; - const static UUID BYTECOIN_NETWORK{{0x11, 0x10, 0x01, 0x11, 0x11, 0x00, 0x01, 0x01, 0x10, 0x11, 0x00, 0x12, 0x10, 0x11, 0x01, 0x10}}; // Bender's nightmare Config::Config(common::CommandLine &cmd) - : is_testnet(false) - , is_stagenet(false) + : net(get_net(cmd)) , is_archive(cmd.get_bool("--archive")) -// , mempool_tx_live_time(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME) - , blocks_file_name(parameters::CRYPTONOTE_BLOCKS_FILENAME) - , block_indexes_file_name(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME) + , blocks_file_name(parameters::BLOCKS_FILENAME) + , block_indexes_file_name(parameters::BLOCKINDEXES_FILENAME) , crypto_note_name(CRYPTONOTE_NAME) , network_id(BYTECOIN_NETWORK) , p2p_bind_port(P2P_DEFAULT_PORT) , p2p_external_port(P2P_DEFAULT_PORT) , p2p_bind_ip("0.0.0.0") + , multicast_address("239.195.17.131") + , multicast_port(P2P_DEFAULT_PORT) + , multicast_period(net == "main" ? 0 : 60.0f) // No multicast in mainnet due to anonymity , bytecoind_bind_port(RPC_DEFAULT_PORT) , bytecoind_bind_ip("127.0.0.1") // Less attack vectors from outside for ordinary uses , bytecoind_remote_port(0) , bytecoind_remote_ip("127.0.0.1") + , db_commit_period_wallet_cache(290) + , db_commit_period_blockchain(310) , walletd_bind_port(WALLET_RPC_DEFAULT_PORT) , walletd_bind_ip("127.0.0.1") // Connection to wallet allows spending - , p2p_local_white_list_limit(P2P_LOCAL_WHITE_PEERLIST_LIMIT) - , p2p_local_gray_list_limit(P2P_LOCAL_GRAY_PEERLIST_LIMIT) + , p2p_local_white_list_limit(1000) + , p2p_local_gray_list_limit(5000) , p2p_default_peers_in_handshake(P2P_DEFAULT_PEERS_IN_HANDSHAKE) - , p2p_default_connections_count(P2P_DEFAULT_CONNECTIONS_COUNT) - , p2p_allow_local_ip(false) - , p2p_whitelist_connections_percent(P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT) + , p2p_max_outgoing_connections(8) + , p2p_max_incoming_connections(100) + , p2p_whitelist_connections_percent(70) , p2p_block_ids_sync_default_count(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT) , p2p_blocks_sync_default_count(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT) - , rpc_get_blocks_fast_max_count(COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT) { - common::pod_from_hex(P2P_STAT_TRUSTED_PUBLIC_KEY, trusted_public_key); - - if (const char *pa = cmd.get("--net")) - parse_net(pa, &is_testnet, &is_stagenet); - if (is_testnet) { + , rpc_get_blocks_fast_max_count(COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT) + , paranoid_checks(cmd.get_bool("--paranoid-checks")) + , trusted_public_key(P2P_STAT_TRUSTED_PUBLIC_KEY) { + if (net == "test") { network_id.data[0] += 1; p2p_bind_port += 1000; p2p_external_port += 1000; bytecoind_bind_port += 1000; - p2p_allow_local_ip = true; + walletd_bind_port += 1000; + multicast_port += 1000; if (const char *pa = cmd.get("--time-multiplier")) platform::set_time_multiplier_for_tests(boost::lexical_cast(pa)); } + if (net == "stage") { + network_id.data[0] += 2; + p2p_bind_port += 2000; + p2p_external_port += 2000; + bytecoind_bind_port += 2000; + walletd_bind_port += 2000; + multicast_port += 2000; + } if (const char *pa = cmd.get("--p2p-bind-address")) { if (!common::parse_ip_address_and_port(pa, &p2p_bind_ip, &p2p_bind_port)) throw std::runtime_error("Wrong address format " + std::string(pa) + ", should be ip:port"); + p2p_external_port = p2p_bind_port; } if (const char *pa = cmd.get("--p2p-external-port")) p2p_external_port = boost::lexical_cast(pa); @@ -107,7 +112,11 @@ Config::Config(common::CommandLine &cmd) #endif } if (const char *pa = cmd.get("--bytecoind-authorization")) { - bytecoind_authorization = common::base64::encode(BinaryArray(pa, pa + strlen(pa))); + bytecoind_authorization = common::base64::encode(BinaryArray(pa, pa + strlen(pa))); + bytecoind_authorization_private = bytecoind_authorization; + } + if (const char *pa = cmd.get("--bytecoind-authorization-private")) { + bytecoind_authorization_private = common::base64::encode(BinaryArray(pa, pa + strlen(pa))); } if (const char *pa = cmd.get("--bytecoind-bind-address")) { if (!common::parse_ip_address_and_port(pa, &bytecoind_bind_ip, &bytecoind_bind_port)) @@ -136,8 +145,7 @@ Config::Config(common::CommandLine &cmd) throw std::runtime_error("Wrong address format " + addr + ", should be ip:port"); } } - if (cmd.get_bool("--allow-local-ip", "Local IPs are automatically allowed for peers from the same private network")) - p2p_allow_local_ip = true; + cmd.get_bool("--allow-local-ip", "Local IPs are automatically allowed for peers from the same private network"); for (auto &&pa : cmd.get_array("--seed-node-address")) parse_peer_and_add_to_container(pa, seed_nodes); for (auto &&pa : cmd.get_array("--seed-node", "Use --seed-node-address instead")) @@ -146,26 +154,35 @@ Config::Config(common::CommandLine &cmd) parse_peer_and_add_to_container(pa, priority_nodes); for (auto &&pa : cmd.get_array("--add-priority-node", "Use --priority-node-address instead")) parse_peer_and_add_to_container(pa, priority_nodes); + std::vector exclusive_nodes_list; for (auto &&pa : cmd.get_array("--exclusive-node-address")) - parse_peer_and_add_to_container(pa, exclusive_nodes); + parse_peer_and_add_to_container(pa, exclusive_nodes_list); for (auto &&pa : cmd.get_array("--add-exclusive-node", "Use --exclusive-node-address instead")) - parse_peer_and_add_to_container(pa, exclusive_nodes); - - if (seed_nodes.empty() && !is_testnet) - for (auto &&sn : bytecoin::SEED_NODES) { + parse_peer_and_add_to_container(pa, exclusive_nodes_list); + if (!priority_nodes.empty() && !exclusive_nodes_list.empty()) + throw std::runtime_error("Priority nodes and exclusive nodes cannot be used together"); + if (!exclusive_nodes_list.empty()) { + exclusive_nodes = true; + priority_nodes = exclusive_nodes_list; + } + if (seed_nodes.empty() && net != "test") + for (auto &&sn : net == "stage" ? SEED_NODES_STAGENET : SEED_NODES) { NetworkAddress addr; if (!common::parse_ip_address_and_port(sn, &addr.ip, &addr.port)) continue; seed_nodes.push_back(addr); } - std::sort(seed_nodes.begin(), seed_nodes.end()); - std::sort(exclusive_nodes.begin(), exclusive_nodes.end()); std::sort(priority_nodes.begin(), priority_nodes.end()); + if (const char *pa = cmd.get("--mineproof-secret")) { + if (!common::pod_from_hex(pa, mineproof_secret)) + throw std::runtime_error("--mineproof-secret must be 64 hex characters"); + } + data_folder = platform::get_app_data_folder(crypto_note_name); - if (is_testnet) - data_folder += "_testnet"; + if (net != "main") + data_folder += "_" + net + "net"; if (const char *pa = cmd.get("--data-folder")) { data_folder = pa; if (!platform::folder_exists(data_folder)) diff --git a/src/Core/Config.hpp b/src/Core/Config.hpp index 8d638c44..316d3a4a 100644 --- a/src/Core/Config.hpp +++ b/src/Core/Config.hpp @@ -17,15 +17,8 @@ class Config { // Consensus does not depend on those parameters public: explicit Config(common::CommandLine &cmd); - bool is_testnet; - bool is_stagenet; + std::string net; bool is_archive; - Timestamp locked_tx_allowed_delta_seconds; - Height locked_tx_allowed_delta_blocks; - - // Timestamp mempool_tx_live_time; - // Timestamp mempool_tx_from_alt_block_live_time; - // size_t number_of_periods_to_forget_tx_deleted_from_pool; std::string blocks_file_name; std::string block_indexes_file_name; @@ -36,14 +29,21 @@ class Config { // Consensus does not depend on those parameters uint16_t p2p_bind_port; uint16_t p2p_external_port; std::string p2p_bind_ip; + std::string multicast_address; + uint16_t multicast_port; + float multicast_period; std::string ssl_certificate_pem_file; boost::optional ssl_certificate_password; std::string bytecoind_authorization; + std::string bytecoind_authorization_private; uint16_t bytecoind_bind_port; std::string bytecoind_bind_ip; uint16_t bytecoind_remote_port; std::string bytecoind_remote_ip; + Hash mineproof_secret; + float db_commit_period_wallet_cache; + float db_commit_period_blockchain; std::string walletd_authorization; uint16_t walletd_bind_port; @@ -52,18 +52,18 @@ class Config { // Consensus does not depend on those parameters size_t p2p_local_white_list_limit; size_t p2p_local_gray_list_limit; size_t p2p_default_peers_in_handshake; - size_t p2p_default_connections_count; - bool p2p_allow_local_ip; + size_t p2p_max_outgoing_connections; + size_t p2p_max_incoming_connections; size_t p2p_whitelist_connections_percent; size_t p2p_block_ids_sync_default_count; size_t p2p_blocks_sync_default_count; size_t rpc_get_blocks_fast_max_count; - std::vector exclusive_nodes; std::vector seed_nodes; - std::vector priority_nodes; // Those nodes have reconnect and ban periods greatly reduced - + std::vector priority_nodes; + bool exclusive_nodes = false; // if true, will connect to priority_nodes only + bool paranoid_checks = false; // Check every byte of blockchain, even before last checkpoint PublicKey trusted_public_key{}; std::string data_folder; diff --git a/src/Core/CryptoNoteTools.cpp b/src/Core/CryptoNoteTools.cpp index 2a94960c..5c5b10ee 100644 --- a/src/Core/CryptoNoteTools.cpp +++ b/src/Core/CryptoNoteTools.cpp @@ -4,6 +4,7 @@ #include "CryptoNoteTools.hpp" #include "Currency.hpp" #include "TransactionExtra.hpp" +#include "common/Varint.hpp" #include "seria/ISeria.hpp" using namespace bytecoin; @@ -21,14 +22,13 @@ Hash bytecoin::get_base_transaction_hash(const BaseTransaction &tx) { return crypto::cn_fast_hash(data.data(), data.size()); } -void bytecoin::fix_merge_mining_tag(BlockTemplate &block) { +void bytecoin::set_solo_mining_tag(BlockTemplate &block) { if (block.major_version >= 2) { - bytecoin::TransactionExtraMergeMiningTag mmTag; + TransactionExtraMergeMiningTag mmTag; mmTag.depth = 0; block.parent_block.base_transaction.extra.clear(); - mmTag.merkle_root = get_auxiliary_block_header_hash(block); - if (!bytecoin::append_merge_mining_tag_to_extra(block.parent_block.base_transaction.extra, mmTag)) - throw std::runtime_error("bytecoin::append_merge_mining_tag_to_extra failed"); + mmTag.merkle_root = get_auxiliary_block_header_hash(block, get_body_proxy_from_template(block)); + extra_add_merge_mining_tag(block.parent_block.base_transaction.extra, mmTag); } } @@ -71,27 +71,28 @@ void decompose_amount_into_digits( void bytecoin::decompose_amount(Amount amount, Amount dust_threshold, std::vector *decomposed_amounts) { decompose_amount_into_digits(amount, dust_threshold, [&](Amount amount) { decomposed_amounts->push_back(amount); }, [&](Amount dust) { + // This code will work relatively well for any dust_threshold <= 6 Amount du0 = dust % 1000; Amount du1 = dust - du0; - if (du0 != 0) - decomposed_amounts->push_back(du0); if (du1 != 0) decomposed_amounts->push_back(du1); + if (du0 != 0) + decomposed_amounts->push_back(du0); }); } size_t bytecoin::get_maximum_tx_size(size_t input_count, size_t output_count, size_t mixin_count) { - const size_t KEY_IMAGE_SIZE = sizeof(crypto::KeyImage); - const size_t OUTPUT_KEY_SIZE = sizeof(crypto::PublicKey); + const size_t KEY_IMAGE_SIZE = sizeof(KeyImage); + const size_t OUTPUT_KEY_SIZE = sizeof(PublicKey); const size_t AMOUNT_SIZE = sizeof(uint64_t) + 2; // varint const size_t GLOBAL_INDEXES_VECTOR_SIZE_SIZE = sizeof(uint8_t); // varint const size_t GLOBAL_INDEXES_INITIAL_VALUE_SIZE = sizeof(uint32_t); // varint const size_t GLOBAL_INDEXES_DIFFERENCE_SIZE = sizeof(uint32_t); // varint - const size_t SIGNATURE_SIZE = sizeof(crypto::Signature); + const size_t SIGNATURE_SIZE = sizeof(Signature); const size_t EXTRA_TAG_SIZE = sizeof(uint8_t); const size_t INPUT_TAG_SIZE = sizeof(uint8_t); const size_t OUTPUT_TAG_SIZE = sizeof(uint8_t); - const size_t PUBLIC_KEY_SIZE = sizeof(crypto::PublicKey); + const size_t PUBLIC_KEY_SIZE = sizeof(PublicKey); const size_t TRANSACTION_VERSION_SIZE = sizeof(uint8_t); const size_t TRANSACTION_UNLOCK_TIME_SIZE = sizeof(uint64_t); @@ -113,15 +114,10 @@ bool bytecoin::get_tx_fee(const TransactionPrefix &tx, uint64_t *fee) { amount_in += boost::get(in).amount; } } - - for (const auto &o : tx.outputs) { + for (const auto &o : tx.outputs) amount_out += o.amount; - } - - if (amount_in < amount_out) { + if (amount_in < amount_out) return false; - } - *fee = amount_in - amount_out; return true; } @@ -133,69 +129,13 @@ uint64_t bytecoin::get_tx_fee(const TransactionPrefix &tx) { return r; } -void seria::ser_members(ParentBlockSerializer &v, ISeria &s) { - seria_kv("major_version", v.m_parent_block.major_version, s); - - seria_kv("minor_version", v.m_parent_block.minor_version, s); - seria_kv("timestamp", v.m_timestamp, s); - seria_kv("previous_block_hash", v.m_parent_block.previous_block_hash, s); - s.object_key("nonce"); - s.binary(&v.m_nonce, sizeof(v.m_nonce)); // TODO - fix endianess - - if (v.m_hashing_serialization) { - crypto::Hash miner_tx_hash = get_base_transaction_hash(v.m_parent_block.base_transaction); - crypto::Hash merkle_root = crypto::tree_hash_from_branch(v.m_parent_block.base_transaction_branch.data(), - v.m_parent_block.base_transaction_branch.size(), miner_tx_hash, 0); - - seria_kv("merkle_root", merkle_root, s); - } - - uint64_t tx_num = static_cast(v.m_parent_block.transaction_count); - seria_kv("transaction_count", tx_num, s); - v.m_parent_block.transaction_count = static_cast(tx_num); - if (v.m_parent_block.transaction_count < 1) - throw std::runtime_error("Wrong transactions number"); - - if (v.m_header_only) { - return; - } - - size_t branch_size = crypto::tree_depth(v.m_parent_block.transaction_count); - if (!s.is_input()) { - if (v.m_parent_block.base_transaction_branch.size() != branch_size) - throw std::runtime_error("Wrong miner transaction branch size"); - } else { - v.m_parent_block.base_transaction_branch.resize(branch_size); - } - - s.object_key("base_transaction_branch"); - size_t btb_size = v.m_parent_block.base_transaction_branch.size(); - s.begin_array(btb_size, true); - for (crypto::Hash &hash : v.m_parent_block.base_transaction_branch) { - s(hash); - } - s.end_array(); - - seria_kv("miner_tx", v.m_parent_block.base_transaction, s); - - TransactionExtraMergeMiningTag mm_tag; - if (!get_merge_mining_tag_from_extra(v.m_parent_block.base_transaction.extra, mm_tag)) - throw std::runtime_error("Can't get extra merge mining tag"); - if (mm_tag.depth > 8 * sizeof(crypto::Hash)) - throw std::runtime_error("Wrong merge mining tag depth"); - - if (!s.is_input()) { - if (mm_tag.depth != v.m_parent_block.blockchain_branch.size()) - throw std::runtime_error("Blockchain branch size must be equal to merge mining tag depth"); - } else { - v.m_parent_block.blockchain_branch.resize(mm_tag.depth); - } - - s.object_key("blockchain_branch"); - btb_size = v.m_parent_block.blockchain_branch.size(); - s.begin_array(btb_size, true); - for (crypto::Hash &hash : v.m_parent_block.blockchain_branch) { - s(hash); - } - s.end_array(); +BlockBodyProxy bytecoin::get_body_proxy_from_template(const BlockTemplate &bt) { + BlockBodyProxy body_proxy; + std::vector transaction_hashes; + transaction_hashes.reserve(bt.transaction_hashes.size() + 1); + transaction_hashes.push_back(get_object_hash(bt.base_transaction)); + transaction_hashes.insert(transaction_hashes.end(), bt.transaction_hashes.begin(), bt.transaction_hashes.end()); + body_proxy.transactions_merkle_root = crypto::tree_hash(transaction_hashes.data(), transaction_hashes.size()); + body_proxy.transaction_count = static_cast(transaction_hashes.size()); + return body_proxy; } diff --git a/src/Core/CryptoNoteTools.hpp b/src/Core/CryptoNoteTools.hpp index e64a668d..40d111c1 100644 --- a/src/Core/CryptoNoteTools.hpp +++ b/src/Core/CryptoNoteTools.hpp @@ -20,7 +20,7 @@ Hash get_object_hash(const T &object, size_t *size = nullptr) { Hash get_base_transaction_hash(const BaseTransaction &tx); -void fix_merge_mining_tag(BlockTemplate &block); // If solo mining, we must still have valid merge mining tag +void set_solo_mining_tag(BlockTemplate &block); // MM headers must still have valid mm_tag if solo mining void decompose_amount(Amount amount, Amount dust_threshold, std::vector *decomposed_amounts); size_t get_maximum_tx_size(size_t input_count, size_t output_count, size_t mixin_count); @@ -28,30 +28,5 @@ size_t get_maximum_tx_size(size_t input_count, size_t output_count, size_t mixin bool get_tx_fee(const TransactionPrefix &tx, uint64_t *fee); uint64_t get_tx_fee(const TransactionPrefix &tx); -struct ParentBlockSerializer { - ParentBlockSerializer( - ParentBlock &parent_block, Timestamp ×tamp, uint32_t &nonce, bool hashing_serialization, bool header_only) - : m_parent_block(parent_block) - , m_timestamp(timestamp) - , m_nonce(nonce) - , m_hashing_serialization(hashing_serialization) - , m_header_only(header_only) {} - ParentBlock &m_parent_block; - Timestamp &m_timestamp; - uint32_t &m_nonce; - bool m_hashing_serialization; - bool m_header_only; -}; - -inline ParentBlockSerializer make_parent_block_serializer( - const BlockTemplate &b, bool hashing_serialization, bool header_only) { - BlockTemplate &block_ref = const_cast(b); - return ParentBlockSerializer( - block_ref.parent_block, block_ref.timestamp, block_ref.nonce, hashing_serialization, header_only); -} -} - -namespace seria { -class ISeria; -void ser_members(bytecoin::ParentBlockSerializer &v, ISeria &s); +BlockBodyProxy get_body_proxy_from_template(const BlockTemplate &bt); } diff --git a/src/Core/Currency.cpp b/src/Core/Currency.cpp index dd2fd002..7efe9e5a 100644 --- a/src/Core/Currency.cpp +++ b/src/Core/Currency.cpp @@ -6,9 +6,11 @@ #include #include #include +#include "CryptoNote.hpp" #include "CryptoNoteConfig.hpp" #include "CryptoNoteTools.hpp" #include "Difficulty.hpp" +#include "TransactionBuilder.hpp" #include "TransactionExtra.hpp" #include "common/Base58.hpp" #include "common/StringTools.hpp" @@ -49,141 +51,160 @@ const std::vector Currency::DECIMAL_PLACES = {1, 10, 100, 1000, 10000, 1 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000ull}; -Currency::Currency(bool is_testnet) - : is_testnet(is_testnet) - , max_block_height(parameters::CRYPTONOTE_MAX_BLOCK_NUMBER) - , max_block_blob_size(parameters::CRYPTONOTE_MAX_BLOCK_BLOB_SIZE) - , max_tx_size(parameters::CRYPTONOTE_MAX_TX_SIZE) - , public_address_base58_prefix(parameters::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) - , mined_money_unlock_window(parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) +Currency::Currency(const std::string &net) + : net(net) + , max_block_height(parameters::MAX_BLOCK_NUMBER) + , max_block_blob_size(parameters::MAX_BLOCK_BLOB_SIZE) + , max_tx_size(parameters::MAX_TX_SIZE) + , public_address_base58_prefix(parameters::PUBLIC_ADDRESS_BASE58_PREFIX) + , mined_money_unlock_window(parameters::MINED_MONEY_UNLOCK_WINDOW) , timestamp_check_window(parameters::BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) - , block_future_time_limit(parameters::CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) + , block_future_time_limit(parameters::BLOCK_FUTURE_TIME_LIMIT) , money_supply(parameters::MONEY_SUPPLY) , emission_speed_factor(parameters::EMISSION_SPEED_FACTOR) - , reward_blocks_window(parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW) - , block_granted_full_reward_zone(parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) - , miner_tx_blob_reserved_size(parameters::CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) - , number_of_decimal_places(parameters::CRYPTONOTE_DISPLAY_DECIMAL_POINT) + , reward_blocks_window(parameters::REWARD_BLOCKS_WINDOW) + , minimum_size_median(parameters::MINIMUM_SIZE_MEDIAN) + , miner_tx_blob_reserved_size(parameters::COINBASE_BLOB_RESERVED_SIZE) + , number_of_decimal_places(parameters::DISPLAY_DECIMAL_POINT) , minimum_fee(parameters::MINIMUM_FEE) , default_dust_threshold(parameters::DEFAULT_DUST_THRESHOLD) , difficulty_target(std::max(1, parameters::DIFFICULTY_TARGET / platform::get_time_multiplier_for_tests())) // multiplier can be != 1 only in testnet - , minimum_difficulty_from_v2(is_testnet ? 2 : parameters::MINIMUM_DIFFICULTY_FROM_V2) - , difficulty_window(parameters::DIFFICULTY_WINDOW(difficulty_target)) + , minimum_difficulty(net == "test" ? 2 : parameters::MINIMUM_DIFFICULTY) + , difficulty_window(expected_blocks_per_day()) , difficulty_lag(parameters::DIFFICULTY_LAG) , difficulty_cut(parameters::DIFFICULTY_CUT) - , max_block_size_initial(parameters::MAX_BLOCK_SIZE_INITIAL) - , max_block_size_growth_speed_numerator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_NUMERATOR) - , max_block_size_growth_speed_denominator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_DENOMINATOR(difficulty_target)) - , locked_tx_allowed_delta_seconds(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS(difficulty_target)) - , locked_tx_allowed_delta_blocks(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS) + , max_block_size_initial(net != "main" ? 1024*1024 : parameters::MAX_BLOCK_SIZE_INITIAL) + , max_block_size_growth_per_year(net != "main" ? 0 : parameters::MAX_BLOCK_SIZE_GROWTH_PER_YEAR) + , locked_tx_allowed_delta_seconds(parameters::LOCKED_TX_ALLOWED_DELTA_SECONDS(difficulty_target)) + , locked_tx_allowed_delta_blocks(parameters::LOCKED_TX_ALLOWED_DELTA_BLOCKS) , upgrade_height_v2(parameters::UPGRADE_HEIGHT_V2) , upgrade_height_v3(parameters::UPGRADE_HEIGHT_V3) + , key_image_subgroup_checking_height(parameters::KEY_IMAGE_SUBGROUP_CHECKING_HEIGHT) + , upgrade_from_major_version(3) + , upgrade_indicator_minor_version(3) + , upgrade_desired_major_version(0) + , upgrade_voting_window(expected_blocks_per_day()) + , upgrade_votes_required(upgrade_voting_window * 9 / 10) + , upgrade_blocks_after_voting(expected_blocks_per_day() * 14) , current_transaction_version(CURRENT_TRANSACTION_VERSION) { - if (is_testnet) { - upgrade_height_v2 = 0; - upgrade_height_v3 = static_cast(-1); + if (net != "main") { + upgrade_height_v2 = 1; + upgrade_height_v3 = 1; } // Hard code coinbase tx in genesis block, because through generating tx use // random, but genesis should be always // the same - std::string genesis_coinbase_tx_hex = - "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f" - "5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; - BinaryArray miner_tx_blob; - - bool r = from_hex(genesis_coinbase_tx_hex, miner_tx_blob); - seria::from_binary(genesis_block_template.base_transaction, miner_tx_blob); - - if (!r) - throw std::runtime_error("Currency failed to parse coinbase tx from hard coded blob"); - + // std::string genesis_coinbase_tx_hex = + // "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f" + // "5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + // BinaryArray miner_tx_blob; + // invariant(from_hex(genesis_coinbase_tx_hex, miner_tx_blob), "Currency failed to parse coinbase tx from hard + // coded blob"); + // seria::from_binary(genesis_block_template.base_transaction, miner_tx_blob); + // Demystified genesis block calculations below genesis_block_template.major_version = 1; genesis_block_template.minor_version = 0; genesis_block_template.timestamp = 0; genesis_block_template.nonce = 70; - if (is_testnet) { - ++genesis_block_template.nonce; - } - genesis_block_hash = get_block_hash(genesis_block_template); -} - -size_t Currency::sw_checkpoint_count() const { return is_testnet ? 1 : sizeof(CHECKPOINTS) / sizeof(*CHECKPOINTS); } - -bool Currency::is_in_sw_checkpoint_zone(Height index) const { - if (is_testnet) - return index == 0; - return index <= CHECKPOINTS[sw_checkpoint_count() - 1].height; -} - -bool Currency::check_sw_checkpoint(Height index, const crypto::Hash &h, bool &is_sw_checkpoint) const { - if (is_testnet || index == 0) { - is_sw_checkpoint = (index == 0); - return index == 0 ? h == genesis_block_hash : true; - } - auto it = std::lower_bound(CHECKPOINTS, CHECKPOINTS + sw_checkpoint_count(), index, - [](const CheckpointData &da, uint32_t ma) { return da.height < ma; }); + PublicKey genesis_output_key = + common::pfh("9b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd088071"); + PublicKey genesis_tx_public_key = + common::pfh("3c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"); + genesis_block_template.base_transaction.version = 1; + genesis_block_template.base_transaction.unlock_block_or_timestamp = mined_money_unlock_window; + genesis_block_template.base_transaction.inputs.push_back(CoinbaseInput{0}); + genesis_block_template.base_transaction.outputs.push_back( + TransactionOutput{money_supply >> emission_speed_factor, KeyOutput{genesis_output_key}}); + extra_add_transaction_public_key(genesis_block_template.base_transaction.extra, genesis_tx_public_key); + + if (net == "test") + genesis_block_template.nonce += 1; + if (net == "stage") + genesis_block_template.nonce += 2; + auto body_proxy = get_body_proxy_from_template(genesis_block_template); + genesis_block_hash = get_block_hash(genesis_block_template, body_proxy); + if (net == "main") { + checkpoints_begin = std::begin(CHECKPOINTS); + checkpoints_end = std::end(CHECKPOINTS); + checkpoint_keys_begin = std::begin(CHECKPOINT_PUBLIC_KEYS); + checkpoint_keys_end = std::end(CHECKPOINT_PUBLIC_KEYS); + } + if (net == "test") { + checkpoints_begin = nullptr; + checkpoints_end = nullptr; + checkpoint_keys_begin = std::begin(CHECKPOINT_PUBLIC_KEYS_TESTNET); + checkpoint_keys_end = std::end(CHECKPOINT_PUBLIC_KEYS_TESTNET); + } + if (net == "stage") { + checkpoints_begin = std::begin(CHECKPOINTS_STAGENET); + checkpoints_end = std::end(CHECKPOINTS_STAGENET); + checkpoint_keys_begin = std::begin(CHECKPOINT_PUBLIC_KEYS_STAGENET); + checkpoint_keys_end = std::end(CHECKPOINT_PUBLIC_KEYS_STAGENET); + } +} + +uint32_t Currency::expected_blocks_per_day() const { + return 24 * 60 * 60 / difficulty_target / platform::get_time_multiplier_for_tests(); +} +uint32_t Currency::expected_blocks_per_year() const { + return 365 * 24 * 60 * 60 / difficulty_target / platform::get_time_multiplier_for_tests(); +} + +bool Currency::is_in_sw_checkpoint_zone(Height height) const { return height <= last_sw_checkpoint().height; } + +bool Currency::check_sw_checkpoint(Height height, const Hash &h, bool &is_sw_checkpoint) const { + if (checkpoints_begin == checkpoints_end) { + is_sw_checkpoint = (height == 0); + return height == 0 ? h == genesis_block_hash : true; + } + auto it = std::lower_bound( + checkpoints_begin, checkpoints_end, height, [](const SWCheckpoint &da, uint32_t ma) { return da.height < ma; }); is_sw_checkpoint = false; - if (it == CHECKPOINTS + sw_checkpoint_count()) + if (it == checkpoints_end) return true; - if (it->height != index) + if (it->height != height) return true; is_sw_checkpoint = true; - return common::pod_to_hex(h) == it->hash; + return h == it->hash; } -std::pair Currency::last_sw_checkpoint() const { - if (is_testnet || sw_checkpoint_count() == 0) - return std::make_pair(0, genesis_block_hash); - auto cp = CHECKPOINTS[sw_checkpoint_count() - 1]; - Hash ha{}; - common::pod_from_hex(cp.hash, ha); - return std::make_pair(cp.height, ha); +SWCheckpoint Currency::last_sw_checkpoint() const { + if (checkpoints_begin == checkpoints_end) + return SWCheckpoint{0, genesis_block_hash}; + return *(checkpoints_end - 1); } PublicKey Currency::get_checkpoint_public_key(uint32_t key_id) const { - PublicKey key{}; - if (key_id < get_checkpoint_keys_count()) { - if (is_testnet) - common::pod_from_hex(CHECKPOINT_PUBLIC_KEYS_TESTNET[key_id], key); - else - common::pod_from_hex(CHECKPOINT_PUBLIC_KEYS[key_id], key); - } - return key; -} - -uint32_t Currency::get_checkpoint_keys_count() const { - if (is_testnet) - return sizeof(CHECKPOINT_PUBLIC_KEYS_TESTNET) / sizeof(*CHECKPOINT_PUBLIC_KEYS_TESTNET); - return sizeof(CHECKPOINT_PUBLIC_KEYS) / sizeof(*CHECKPOINT_PUBLIC_KEYS); + if (key_id >= checkpoint_keys_end - checkpoint_keys_begin) + return PublicKey{}; + return checkpoint_keys_begin[key_id]; } uint8_t Currency::get_block_major_version_for_height(Height height) const { - if (height <= upgrade_height_v2) + if (height < upgrade_height_v2) return 1; - if (height > upgrade_height_v2 && height <= upgrade_height_v3) + if (height >= upgrade_height_v2 && height < upgrade_height_v3) return 2; - return 3; // info.height > currency.upgrade_height_v3 + return 3; // info.height >= currency.upgrade_height_v3 } -uint8_t Currency::get_block_minor_version_for_height(Height height) const { - if (height <= upgrade_height_v2) - return 0; - if (height > upgrade_height_v2 && height <= upgrade_height_v3) - return 0; - return 2; // Signal of checkpoints support +Difficulty Currency::get_minimum_difficulty(uint8_t block_major_version) const { + if (block_major_version == 1) + return parameters::MINIMUM_DIFFICULTY_V1; + return minimum_difficulty; } -uint32_t Currency::block_granted_full_reward_zone_by_block_version(uint8_t block_major_version) const { - if (block_major_version >= 3) - return block_granted_full_reward_zone; +uint32_t Currency::get_minimum_size_median(uint8_t block_major_version) const { + if (block_major_version == 1) + return parameters::MINIMUM_SIZE_MEDIAN_V1; if (block_major_version == 2) - return bytecoin::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; - return bytecoin::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + return parameters::MINIMUM_SIZE_MEDIAN_V2; + return minimum_size_median; } -bool Currency::get_block_reward(uint8_t block_major_version, size_t effective_median_size, size_t current_block_size, +void Currency::get_block_reward(uint8_t block_major_version, size_t effective_median_size, size_t current_block_size, Amount already_generated_coins, Amount fee, Amount *reward, SignedAmount *emission_change) const { assert(already_generated_coins <= money_supply); assert(emission_speed_factor > 0 && emission_speed_factor <= 8 * sizeof(Amount)); @@ -196,8 +217,6 @@ bool Currency::get_block_reward(uint8_t block_major_version, size_t effective_me *emission_change = penalized_base_reward - (fee - penalized_fee); *reward = penalized_base_reward + penalized_fee; - - return true; } Height Currency::largest_window() const { @@ -205,11 +224,12 @@ Height Currency::largest_window() const { } uint32_t Currency::max_block_cumulative_size(Height height) const { - assert(height <= std::numeric_limits::max() / max_block_size_growth_speed_numerator); - uint64_t max_size = static_cast( - max_block_size_initial + - (height * max_block_size_growth_speed_numerator) / max_block_size_growth_speed_denominator); - assert(max_size >= max_block_size_initial); + if( max_block_size_growth_per_year == 0) + return max_block_size_initial; + assert(height <= std::numeric_limits::max() / max_block_size_growth_per_year); + uint64_t max_size = + max_block_size_initial + (uint64_t(height) * max_block_size_growth_per_year) / expected_blocks_per_year(); + assert(max_size < std::numeric_limits::max()); return static_cast(max_size); } @@ -220,30 +240,25 @@ uint32_t Currency::max_transaction_allowed_size(uint32_t effective_block_size_me } bool Currency::construct_miner_tx(uint8_t block_major_version, Height height, size_t effective_median_size, - Amount already_generated_coins, size_t current_block_size, Amount fee, const AccountPublicAddress &miner_address, - Transaction *tx, const BinaryArray &extra_nonce, size_t max_outs) const { + Amount already_generated_coins, size_t current_block_size, Amount fee, Hash mineproof_seed, + const AccountPublicAddress &miner_address, Transaction *tx, const BinaryArray &extra_nonce, size_t max_outs) const { tx->inputs.clear(); tx->outputs.clear(); tx->extra.clear(); - KeyPair txkey = crypto::random_keypair(); - add_transaction_public_key_to_extra(tx->extra, txkey.public_key); - if (!extra_nonce.empty()) { - if (!add_extra_nonce_to_transaction_extra(tx->extra, extra_nonce)) { - return false; - } - } + tx->inputs.push_back(CoinbaseInput{height}); + + KeyPair txkey = mineproof_seed == Hash{} ? crypto::random_keypair() + : TransactionBuilder::deterministic_keys_from_seed(*tx, mineproof_seed); - CoinbaseInput in; - in.block_index = height; + extra_add_transaction_public_key(tx->extra, txkey.public_key); + if (!extra_nonce.empty()) + extra_add_nonce(tx->extra, extra_nonce); Amount block_reward; SignedAmount emission_change; - if (!get_block_reward(block_major_version, effective_median_size, current_block_size, already_generated_coins, fee, - &block_reward, &emission_change)) { - // logger(INFO) << "Block is too big"; - return false; - } + get_block_reward(block_major_version, effective_median_size, current_block_size, already_generated_coins, fee, + &block_reward, &emission_change); std::vector out_amounts; decompose_amount(block_reward, default_dust_threshold, &out_amounts); @@ -257,12 +272,10 @@ bool Currency::construct_miner_tx(uint8_t block_major_version, Height height, si Amount summary_amounts = 0; for (size_t no = 0; no < out_amounts.size(); no++) { - crypto::KeyDerivation derivation{}; - crypto::PublicKey out_ephemeral_pub_key{}; - - bool r = crypto::generate_key_derivation(miner_address.view_public_key, txkey.secret_key, derivation); + KeyDerivation derivation{}; + PublicKey out_ephemeral_pub_key{}; - if (!r) { + if (!crypto::generate_key_derivation(miner_address.view_public_key, txkey.secret_key, derivation)) { // logger(ERROR, BrightRed) // << "while creating outs: failed to generate_key_derivation(" // << miner_address.view_public_key << ", " << txkey.secret_key << @@ -270,9 +283,7 @@ bool Currency::construct_miner_tx(uint8_t block_major_version, Height height, si return false; } - r = crypto::derive_public_key(derivation, no, miner_address.spend_public_key, out_ephemeral_pub_key); - - if (!r) { + if (!crypto::derive_public_key(derivation, no, miner_address.spend_public_key, out_ephemeral_pub_key)) { // logger(ERROR, BrightRed) // << "while creating outs: failed to derive_public_key(" // << derivation << ", " << no << ", " @@ -281,7 +292,7 @@ bool Currency::construct_miner_tx(uint8_t block_major_version, Height height, si } KeyOutput tk; - tk.key = out_ephemeral_pub_key; + tk.public_key = out_ephemeral_pub_key; TransactionOutput out; summary_amounts += out.amount = out_amounts[no]; @@ -289,17 +300,13 @@ bool Currency::construct_miner_tx(uint8_t block_major_version, Height height, si tx->outputs.push_back(out); } - if (summary_amounts != block_reward) { + invariant(summary_amounts == block_reward, ""); // logger(ERROR, BrightRed) << "Failed to construct miner tx, // summary_amounts = " << summary_amounts << " not // equal block_reward = " << block_reward; - return false; - } tx->version = current_transaction_version; - // lock - tx->unlock_time = height + mined_money_unlock_window; - tx->inputs.push_back(in); + tx->unlock_block_or_timestamp = height + mined_money_unlock_window; return true; } @@ -309,12 +316,10 @@ uint64_t Currency::get_penalized_amount(uint64_t amount, size_t median_size, siz assert(median_size <= std::numeric_limits::max()); assert(current_block_size <= std::numeric_limits::max()); - if (amount == 0) { + if (amount == 0) return 0; - } - if (current_block_size <= median_size) { + if (current_block_size <= median_size) return amount; - } uint64_t product_hi; uint64_t product_lo = @@ -367,7 +372,7 @@ bool Currency::parse_account_address_string(const std::string &str, AccountPubli return true; } -static std::string ffw(bytecoin::Amount am, size_t digs) { +static std::string ffw(Amount am, size_t digs) { std::string result = common::to_string(am); if (result.size() < digs) result = std::string(digs - result.size(), '0') + result; @@ -375,8 +380,8 @@ static std::string ffw(bytecoin::Amount am, size_t digs) { } std::string Currency::format_amount(size_t number_of_decimal_places, Amount amount) { - bytecoin::Amount ia = amount / DECIMAL_PLACES.at(number_of_decimal_places); - bytecoin::Amount fa = amount - ia * DECIMAL_PLACES.at(number_of_decimal_places); + Amount ia = amount / DECIMAL_PLACES.at(number_of_decimal_places); + Amount fa = amount - ia * DECIMAL_PLACES.at(number_of_decimal_places); std::string result; while (ia >= 1000) { result = "'" + ffw(ia % 1000, 3) + result; @@ -440,7 +445,8 @@ bool Currency::parse_amount(size_t number_of_decimal_places, const std::string & Difficulty Currency::next_difficulty( std::vector *timestamps, std::vector *cumulative_difficulties) const { - assert(difficulty_window >= 2); + invariant(difficulty_window >= 2, "Bad DIFFICULTY_WINDOW"); + invariant(2 * difficulty_cut <= difficulty_window - 2, "Bad DIFFICULTY_WINDOW or DIFFICULTY_CUT"); if (timestamps->size() > difficulty_window) { timestamps->resize(difficulty_window); @@ -448,16 +454,13 @@ Difficulty Currency::next_difficulty( } size_t length = timestamps->size(); - assert(length == cumulative_difficulties->size()); - assert(length <= difficulty_window); - if (length <= 1) { + invariant(length == cumulative_difficulties->size() && length <= difficulty_window, ""); + if (length <= 1) return 1; - } - sort(timestamps->begin(), timestamps->end()); + std::sort(timestamps->begin(), timestamps->end()); size_t cut_begin, cut_end; - assert(2 * difficulty_cut <= difficulty_window - 2); if (length <= difficulty_window - 2 * difficulty_cut) { cut_begin = 0; cut_end = length; @@ -465,7 +468,7 @@ Difficulty Currency::next_difficulty( cut_begin = (length - (difficulty_window - 2 * difficulty_cut) + 1) / 2; cut_end = cut_begin + (difficulty_window - 2 * difficulty_cut); } - assert(cut_begin + 2 <= cut_end && cut_end <= length); // TODO - why + 2? + invariant(cut_begin + 2 <= cut_end && cut_end <= length, "After difficulty cut at least 2 items should remain"); Timestamp time_span = timestamps->at(cut_end - 1) - timestamps->at(cut_begin); if (time_span == 0) { time_span = 1; @@ -473,8 +476,7 @@ Difficulty Currency::next_difficulty( invariant( cumulative_difficulties->at(cut_end - 1) > cumulative_difficulties->at(cut_begin), "Reversed difficulties"); - CumulativeDifficulty total_work = cumulative_difficulties->at(cut_end - 1) -= - cumulative_difficulties->at(cut_begin); + CumulativeDifficulty total_work = cumulative_difficulties->at(cut_end - 1) - cumulative_difficulties->at(cut_begin); invariant(total_work.hi == 0, "Window difficulty difference too large"); uint64_t low, high; @@ -482,69 +484,65 @@ Difficulty Currency::next_difficulty( if (high != 0 || std::numeric_limits::max() - low < (time_span - 1)) { return 0; } - return (low + time_span - 1) / time_span; } + Difficulty Currency::next_effective_difficulty(uint8_t block_major_version, std::vector timestamps, std::vector cumulative_difficulties) const { Difficulty difficulty = next_difficulty(×tamps, &cumulative_difficulties); - if (difficulty != 0 && block_major_version >= 2 && difficulty < minimum_difficulty_from_v2) - difficulty = minimum_difficulty_from_v2; + if (difficulty != 0 && difficulty < get_minimum_difficulty(block_major_version)) + difficulty = get_minimum_difficulty(block_major_version); return difficulty; } -bool Currency::check_proof_of_work_v1(const Hash &long_block_hash, - const BlockTemplate &block, - Difficulty current_difficulty) const { - if (block.major_version != 1) { - return false; - } - return check_hash(long_block_hash, current_difficulty); -} - -bool Currency::check_proof_of_work_v2(const Hash &long_block_hash, - const BlockTemplate &block, - Difficulty current_difficulty) const { - if (block.major_version < 2) { - return false; - } - TransactionExtraMergeMiningTag mm_tag; - if (!get_merge_mining_tag_from_extra(block.parent_block.base_transaction.extra, mm_tag)) { - // logger(ERROR) << "merge mining tag wasn't found in extra of the parent - // block miner transaction"; - return false; - } - if (8 * sizeof(genesis_block_hash) < block.parent_block.blockchain_branch.size()) { - return false; - } - crypto::Hash aux_blocks_merkle_root = crypto::tree_hash_from_branch(block.parent_block.blockchain_branch.data(), - block.parent_block.blockchain_branch.size(), get_auxiliary_block_header_hash(block), &genesis_block_hash); - - if (aux_blocks_merkle_root != mm_tag.merkle_root) { - // logger(ERROR, BRIGHT_YELLOW) << "Aux block hash wasn't found in merkle - // tree"; - return false; - } - if (!check_hash(long_block_hash, current_difficulty)) { - return false; - } - return true; -} - -bool Currency::check_proof_of_work(const Hash &long_block_hash, - const BlockTemplate &block, - Difficulty current_difficulty) const { - switch (block.major_version) { +BinaryArray Currency::get_block_long_hashing_data(const BlockHeader &bh, const BlockBodyProxy &body_proxy) const { + common::BinaryArray result; + common::VectorOutputStream stream(result); + seria::BinaryOutputStream ba(stream); + ba.begin_object(); + ser_members(const_cast(bh), ba, BlockSeriaType::LONG_BLOCKHASH, body_proxy); + ba.end_object(); + // std::cout << "ba: " << common::to_hex(result.data(), result.size()) << std::endl; + switch (bh.major_version) { case 1: - return check_proof_of_work_v1(long_block_hash, block, current_difficulty); + return result; case 2: - case 3: - return check_proof_of_work_v2(long_block_hash, block, current_difficulty); + case 3: { + TransactionExtraMergeMiningTag mm_tag; + if (!extra_get_merge_mining_tag(bh.parent_block.base_transaction.extra, mm_tag)) { + // logger(ERROR) << "merge mining tag wasn't found in extra of the parent + // block miner transaction"; + return BinaryArray{}; + } + if (mm_tag.depth != bh.parent_block.blockchain_branch.size()) + return BinaryArray{}; + if (bh.parent_block.blockchain_branch.size() > 8 * sizeof(genesis_block_hash)) + return BinaryArray{}; + Hash aux_blocks_merkle_root = crypto::tree_hash_from_branch(bh.parent_block.blockchain_branch.data(), + bh.parent_block.blockchain_branch.size(), get_auxiliary_block_header_hash(bh, body_proxy), + &genesis_block_hash); + + if (aux_blocks_merkle_root != mm_tag.merkle_root) { + // logger(ERROR, BRIGHT_YELLOW) << "Aux block hash wasn't found in merkle + // tree"; + return BinaryArray{}; + } + return result; + } +#if bytecoin_ALLOW_CM + case 104: { + Hash merkle_root_hash = crypto::tree_hash_from_branch(bh.cm_merkle_branch.data(), + bh.cm_merkle_branch.size(), + get_auxiliary_block_header_hash(bh, body_proxy), + genesis_block_hash.data); + BinaryArray long_hashing_array(sizeof(Hash) + 8); + memcpy(long_hashing_array.data(), merkle_root_hash.data, sizeof(Hash)); + common::uint_le_to_bytes(long_hashing_array.data() + sizeof(Hash), 8, bh.nonce); + return long_hashing_array; } - // logger(ERROR, BrightRed) << "Unknown block major version: " << - // block.get_block().major_version << "." << - // block.get_block().minor_version; - return false; +#endif + } + throw std::runtime_error("Unknown block major version."); } bool Currency::is_dust(Amount amount) { @@ -555,68 +553,39 @@ bool Currency::is_dust(Amount amount) { Hash bytecoin::get_transaction_inputs_hash(const TransactionPrefix &tx) { BinaryArray ba = seria::to_binary(tx.inputs); - Hash new_hash = crypto::cn_fast_hash(ba.data(), ba.size()); - return new_hash; + return crypto::cn_fast_hash(ba.data(), ba.size()); } Hash bytecoin::get_transaction_prefix_hash(const TransactionPrefix &tx) { - const TransactionPrefix &prefix = tx; - BinaryArray ba = seria::to_binary(prefix); - Hash new_hash = crypto::cn_fast_hash(ba.data(), ba.size()); - return new_hash; + BinaryArray ba = seria::to_binary(tx); + return crypto::cn_fast_hash(ba.data(), ba.size()); } Hash bytecoin::get_transaction_hash(const Transaction &tx) { BinaryArray ba = seria::to_binary(tx); - Hash new_hash = crypto::cn_fast_hash(ba.data(), ba.size()); - return new_hash; -} - -static Hash get_transaction_tree_hash(const BlockTemplate &bh) { - std::vector transaction_hashes; - transaction_hashes.reserve(bh.transaction_hashes.size() + 1); - transaction_hashes.push_back(get_object_hash(bh.base_transaction)); - transaction_hashes.insert(transaction_hashes.end(), bh.transaction_hashes.begin(), bh.transaction_hashes.end()); - Hash tree_hash = crypto::tree_hash(transaction_hashes.data(), transaction_hashes.size()); - return tree_hash; -} - -static BinaryArray get_block_hashing_binary_array(const BlockTemplate &bh) { - BinaryArray ba = seria::to_binary(static_cast(bh)); - - Hash tree_hash = get_transaction_tree_hash(bh); - append(ba, std::begin(tree_hash.data), std::end(tree_hash.data)); - auto tx_count = common::get_varint_data(bh.transaction_hashes.size() + 1); - append(ba, tx_count.begin(), tx_count.end()); - - return ba; -} - -Hash bytecoin::get_block_hash(const BlockTemplate &bh) { - BinaryArray ba2 = get_block_hashing_binary_array(bh); - - if (bh.major_version >= 2) { - auto serializer = make_parent_block_serializer(bh, true, false); - BinaryArray parent_ba2 = seria::to_binary(serializer); - append(ba2, parent_ba2.begin(), parent_ba2.end()); - } - Hash new_hash2 = get_object_hash(ba2); - return new_hash2; -} - -Hash bytecoin::get_auxiliary_block_header_hash(const BlockTemplate &bh) { - return get_object_hash(get_block_hashing_binary_array(bh)); -} - -Hash bytecoin::get_block_long_hash(const BlockTemplate &bh, crypto::CryptoNightContext &crypto_ctx) { - if (bh.major_version == 1) { - auto raw_hashing_block = get_block_hashing_binary_array(bh); - return crypto_ctx.cn_slow_hash(raw_hashing_block.data(), raw_hashing_block.size()); - } - if (bh.major_version >= 2) { - auto serializer = make_parent_block_serializer(bh, true, true); - BinaryArray raw_hashing_block = seria::to_binary(serializer); - return crypto_ctx.cn_slow_hash(raw_hashing_block.data(), raw_hashing_block.size()); - } - throw std::runtime_error("Unknown block major version."); + return crypto::cn_fast_hash(ba.data(), ba.size()); +} + +Hash bytecoin::get_block_hash(const BlockHeader &bh, const BlockBodyProxy &body_proxy) { +// common::BinaryArray result; +// common::VectorOutputStream stream(result); +// seria::BinaryOutputStream ba(stream); +// ba.begin_object(); +// ser_members(const_cast(bh), ba, BlockSeriaType::BLOCKHASH, body_proxy); +// ba.end_object(); + Hash ha2 = get_object_hash(seria::to_binary(bh, BlockSeriaType::BLOCKHASH, body_proxy)); + // std::cout << "ha: " << ha2 << " ba: " << common::to_hex(result.data(), result.size()) << std::endl; + return ha2; +} + +Hash bytecoin::get_auxiliary_block_header_hash(const BlockHeader &bh, const BlockBodyProxy &body_proxy) { +// common::BinaryArray result; +// common::VectorOutputStream stream(result); +// seria::BinaryOutputStream ba(stream); +// ba.begin_object(); +// ser(const_cast(bh), ba, BlockSeriaType::PREHASH, body_proxy); +// ba.end_object(); + Hash ha2 = get_object_hash(seria::to_binary(bh, BlockSeriaType::PREHASH, body_proxy)); + // std::cout << "ha: " << ha2 << " ba: " << common::to_hex(result.data(), result.size()) << std::endl; + return ha2; } diff --git a/src/Core/Currency.hpp b/src/Core/Currency.hpp index 0bd4ce7f..911cce1d 100644 --- a/src/Core/Currency.hpp +++ b/src/Core/Currency.hpp @@ -17,9 +17,9 @@ class Currency { // Consensus calcs depend on those parameters static const std::vector PRETTY_AMOUNTS; static const std::vector DECIMAL_PLACES; - explicit Currency(bool is_testnet); + explicit Currency(const std::string &net); - bool is_testnet; + std::string net; BlockTemplate genesis_block_template{}; Hash genesis_block_hash{}; @@ -38,7 +38,9 @@ class Currency { // Consensus calcs depend on those parameters unsigned int emission_speed_factor; Height reward_blocks_window; - uint32_t block_granted_full_reward_zone; + uint32_t minimum_size_median; + uint32_t get_minimum_size_median(uint8_t block_major_version) const; + uint32_t miner_tx_blob_reserved_size; size_t number_of_decimal_places; @@ -48,40 +50,50 @@ class Currency { // Consensus calcs depend on those parameters Amount default_dust_threshold; Timestamp difficulty_target; - Difficulty minimum_difficulty_from_v2; + Difficulty minimum_difficulty; + Difficulty get_minimum_difficulty(uint8_t block_major_version) const; Height difficulty_window; Height difficulty_lag; size_t difficulty_cut; Height difficulty_blocks_count() const { return difficulty_window + difficulty_lag; } - uint32_t expected_blocks_per_day() const { return 24 * 60 * 60 / difficulty_target; } - uint64_t max_block_size_initial; - uint64_t max_block_size_growth_speed_numerator; - uint64_t max_block_size_growth_speed_denominator; + uint32_t expected_blocks_per_day() const; + uint32_t expected_blocks_per_year() const; + uint32_t max_block_size_initial; + uint32_t max_block_size_growth_per_year; Timestamp locked_tx_allowed_delta_seconds; Height locked_tx_allowed_delta_blocks; - Height upgrade_height_v2; - Height upgrade_height_v3; + Height upgrade_height_v2; // height of first v2 block + Height upgrade_height_v3; // height of first v3 block + Height key_image_subgroup_checking_height; uint8_t get_block_major_version_for_height(Height) const; - uint8_t get_block_minor_version_for_height(Height) const; + + // upgrade voting threshold must not be reached before or at last sw checkpoint! + uint8_t upgrade_from_major_version; + uint8_t upgrade_indicator_minor_version; + uint8_t upgrade_desired_major_version; + Height upgrade_voting_window; + Height upgrade_votes_required; + Height upgrade_blocks_after_voting; uint8_t current_transaction_version; - size_t sw_checkpoint_count() const; - bool is_in_sw_checkpoint_zone(Height index) const; - bool check_sw_checkpoint(Height index, const crypto::Hash &h, bool &is_sw_checkpoint) const; - std::pair last_sw_checkpoint() const; + size_t sw_checkpoint_count() const { return checkpoints_end - checkpoints_begin; } + bool is_in_sw_checkpoint_zone(Height height) const; + bool check_sw_checkpoint(Height height, const Hash &h, bool &is_sw_checkpoint) const; + SWCheckpoint last_sw_checkpoint() const; PublicKey get_checkpoint_public_key(uint32_t key_id) const; - uint32_t get_checkpoint_keys_count() const; + uint32_t get_checkpoint_keys_count() const { + return static_cast(checkpoint_keys_end - checkpoint_keys_begin); + } - uint32_t block_granted_full_reward_zone_by_block_version(uint8_t block_major_version) const; - bool get_block_reward(uint8_t block_major_version, size_t effective_median_size, size_t current_block_size, + void get_block_reward(uint8_t block_major_version, size_t effective_median_size, size_t current_block_size, Amount already_generated_coins, Amount fee, Amount *reward, SignedAmount *emission_change) const; uint32_t max_block_cumulative_size(Height height) const; uint32_t max_transaction_allowed_size(uint32_t effective_block_size_median) const; bool construct_miner_tx(uint8_t block_major_version, Height height, size_t effective_median_size, - Amount already_generated_coins, size_t current_block_size, Amount fee, + Amount already_generated_coins, size_t current_block_size, Amount fee, Hash mineproof_seed, const AccountPublicAddress &miner_address, Transaction *tx, const BinaryArray &extra_nonce = BinaryArray(), size_t max_outs = 1) const; @@ -99,18 +111,14 @@ class Currency { // Consensus calcs depend on those parameters Difficulty next_effective_difficulty(uint8_t block_major_version, std::vector timestamps, std::vector cumulative_difficulties) const; - bool check_proof_of_work_v1( - const Hash &long_block_hash, const BlockTemplate &block, Difficulty current_difficulty) const; - bool check_proof_of_work_v2( - const Hash &long_block_hash, const BlockTemplate &block, Difficulty current_difficulty) const; - bool check_proof_of_work( - const Hash &long_block_hash, const BlockTemplate &block, Difficulty current_difficulty) const; + BinaryArray get_block_long_hashing_data(const BlockHeader &, const BlockBodyProxy &) const; - bool is_transaction_spend_time(UnlockMoment unlock_time) const { return unlock_time >= max_block_height; } - bool is_transaction_spend_time_block(UnlockMoment unlock_time) const { return unlock_time < max_block_height; } - bool is_transaction_spend_time_unlocked(UnlockMoment unlock_time, Height block_index, Timestamp block_time) const { + bool is_transaction_spend_time(BlockOrTimestamp unlock_time) const { return unlock_time >= max_block_height; } + bool is_transaction_spend_time_block(BlockOrTimestamp unlock_time) const { return unlock_time < max_block_height; } + bool is_transaction_spend_time_unlocked( + BlockOrTimestamp unlock_time, Height block_height, Timestamp block_time) const { if (unlock_time < max_block_height) { // interpret as block index - return block_index + locked_tx_allowed_delta_blocks >= unlock_time; + return block_height + locked_tx_allowed_delta_blocks >= unlock_time; } // else interpret as time return block_time + locked_tx_allowed_delta_seconds >= unlock_time; } @@ -121,6 +129,12 @@ class Currency { // Consensus calcs depend on those parameters static std::string format_amount(size_t number_of_decimal_places, Amount); static std::string format_amount(size_t number_of_decimal_places, SignedAmount); static bool parse_amount(size_t number_of_decimal_places, const std::string &, Amount *); + +private: + const PublicKey *checkpoint_keys_begin = nullptr; + const PublicKey *checkpoint_keys_end = nullptr; + const SWCheckpoint *checkpoints_begin = nullptr; + const SWCheckpoint *checkpoints_end = nullptr; }; // we should probaly find better place for these global funs @@ -128,8 +142,8 @@ Hash get_transaction_inputs_hash(const TransactionPrefix &); Hash get_transaction_prefix_hash(const TransactionPrefix &); Hash get_transaction_hash(const Transaction &); -Hash get_block_hash(const BlockTemplate &); -Hash get_block_long_hash(const BlockTemplate &, crypto::CryptoNightContext &); -Hash get_auxiliary_block_header_hash(const BlockTemplate &); // Without parent block, for merge mining calculations +Hash get_block_hash(const BlockHeader &, const BlockBodyProxy &); +Hash get_auxiliary_block_header_hash(const BlockHeader &, const BlockBodyProxy &); +// Auxilary hash, or prehash - inserted into MM or CM tree } // namespace bytecoin diff --git a/src/Core/Difficulty.cpp b/src/Core/Difficulty.cpp index 8a487326..a7994475 100644 --- a/src/Core/Difficulty.cpp +++ b/src/Core/Difficulty.cpp @@ -9,30 +9,42 @@ #include "Currency.hpp" #include "Difficulty.hpp" +#include "common/Varint.hpp" #include "crypto/hash.hpp" #include "crypto/int-util.h" #include "seria/ISeria.hpp" using namespace bytecoin; -// carry is PLATFORM-DEPENDENT -static bool cadd(uint64_t a, uint64_t b) { return a + b < a; } - +// C99 6.2.5.9 - this is actually good working code by C standard +// static bool cadd(uint64_t a, uint64_t b) { return a + b < a; } static bool cadc(uint64_t a, uint64_t b, bool c) { return a + b < a || (c && a + b == (uint64_t)-1); } bool bytecoin::check_hash(const crypto::Hash &hash, Difficulty difficulty) { - uint64_t low, high, top, cur; + uint64_t hash64[4]; + for (size_t i = 0; i != 4; ++i) + hash64[i] = common::uint_le_from_bytes(hash.data + 8 * i, 8); // First check the highest word, this will most likely fail for a random hash. - top = mul128(swap64le(((const uint64_t *)&hash)[3]), difficulty, &high); - if (high != 0) { + uint64_t r4; + uint64_t r3s = mul128(hash64[3], difficulty, &r4); + // uint64_t low, high, top, cur; // + // top = r3s; // + // high = r4; // + if (r4 != 0) return false; - } - low = mul128(swap64le(((const uint64_t *)&hash)[0]), difficulty, &cur); // TODO - low is not used - low = mul128(swap64le(((const uint64_t *)&hash)[1]), difficulty, &high); - bool carry = cadd(cur, low); - cur = high; - low = mul128(swap64le(((const uint64_t *)&hash)[2]), difficulty, &high); - carry = cadc(cur, low, carry); - carry = cadc(high, top, carry); + // low = mul128(swap64le(((const uint64_t *)&hash)[0]), difficulty, &cur); // + // low = mul128(swap64le(((const uint64_t *)&hash)[1]), difficulty, &high); // + // bool carry = cadd(cur, low); // + // cur = high; // + // low = mul128(swap64le(((const uint64_t *)&hash)[2]), difficulty, &high); // + // carry = cadc(cur, low, carry); // + // carry = cadc(high, top, carry); // + uint64_t r1, r2, r3; + mul128(hash64[0], difficulty, &r1); + uint64_t r1s = mul128(hash64[1], difficulty, &r2); + uint64_t r2s = mul128(hash64[2], difficulty, &r3); + bool carry = cadc(r1, r1s, false); + carry = cadc(r2, r2s, carry); + carry = cadc(r3, r3s, carry); return !carry; } diff --git a/src/Core/Multicore.cpp b/src/Core/Multicore.cpp index dc7de6d9..c8603414 100644 --- a/src/Core/Multicore.cpp +++ b/src/Core/Multicore.cpp @@ -50,7 +50,7 @@ void RingCheckerMulticore::thread_run() { [&output_key_pointers](const PublicKey &key) { output_key_pointers.push_back(&key); }); bool key_corrupted = false; bool result = check_ring_signature(arg.tx_prefix_hash, arg.key_image, output_key_pointers.data(), - output_key_pointers.size(), arg.signatures.data(), true, &key_corrupted); + output_key_pointers.size(), arg.signatures.data(), arg.key_image_subgroup_check, &key_corrupted); { std::unique_lock lock(mu); if (local_work_counter == work_counter) { @@ -71,7 +71,7 @@ void RingCheckerMulticore::cancel_work() { } std::string RingCheckerMulticore::start_work_get_error(IBlockChainState *state, const Currency ¤cy, - const Block &block, Height unlock_height, Timestamp unlock_timestamp) { + const Block &block, Height unlock_height, Timestamp unlock_timestamp, bool key_image_subgroup_check) { { std::unique_lock lock(mu); args.clear(); @@ -89,10 +89,11 @@ std::string RingCheckerMulticore::start_work_get_error(IBlockChainState *state, } else if (input.type() == typeid(KeyInput)) { const KeyInput &in = boost::get(input); RingSignatureArg arg; - arg.tx_prefix_hash = tx_prefix_hash; - arg.key_image = in.key_image; - arg.signatures = transaction.signatures[input_index]; - Height height = 0; + arg.key_image_subgroup_check = key_image_subgroup_check; + arg.tx_prefix_hash = tx_prefix_hash; + arg.key_image = in.key_image; + arg.signatures = transaction.signatures[input_index]; + Height height = 0; if (state->read_keyimage(in.key_image, &height)) return "INPUT_KEYIMAGE_ALREADY_SPENT"; if (in.output_indexes.empty()) @@ -107,7 +108,8 @@ std::string RingCheckerMulticore::start_work_get_error(IBlockChainState *state, IBlockChainState::UnlockTimePublickKeyHeightSpent unp; if (!state->read_amount_output(in.amount, global_indexes[i], &unp)) return "INPUT_INVALID_GLOBAL_INDEX"; - if (!currency.is_transaction_spend_time_unlocked(unp.unlock_time, unlock_height, unlock_timestamp)) + if (!currency.is_transaction_spend_time_unlocked( + unp.unlock_block_or_timestamp, unlock_height, unlock_timestamp)) return "INPUT_SPEND_LOCKED_OUT"; arg.output_keys[i] = unp.public_key; } @@ -156,7 +158,7 @@ WalletPreparatorMulticore::~WalletPreparatorMulticore() { PreparedWalletTransaction::PreparedWalletTransaction(TransactionPrefix &&ttx, const SecretKey &view_secret_key) : tx(std::move(ttx)) { - PublicKey tx_public_key = get_transaction_public_key_from_extra(tx.extra); + PublicKey tx_public_key = extra_get_transaction_public_key(tx.extra); if (!generate_key_derivation(tx_public_key, view_secret_key, derivation)) return; KeyPair tx_keys; @@ -167,7 +169,7 @@ PreparedWalletTransaction::PreparedWalletTransaction(TransactionPrefix &&ttx, co if (output.target.type() == typeid(KeyOutput)) { const KeyOutput &key_output = boost::get(output.target); PublicKey spend_key; - underive_public_key(derivation, key_index, key_output.key, + underive_public_key(derivation, key_index, key_output.public_key, spend_key); // error indicated by spend_key not in our wallet spend_keys.push_back(spend_key); ++key_index; @@ -192,7 +194,7 @@ void WalletPreparatorMulticore::thread_run() { SecretKey view_secret_key; Height height = 0; int local_work_counter = 0; - api::bytecoind::GetRawBlock::Response sync_block; + api::RawBlock sync_block; std::vector> global_indices; { std::unique_lock lock(mu); @@ -210,7 +212,7 @@ void WalletPreparatorMulticore::thread_run() { work.blocks.erase(work.blocks.begin()); } PreparedWalletBlock result(std::move(sync_block.raw_header), std::move(sync_block.raw_transactions), - sync_block.base_transaction_hash, view_secret_key); + sync_block.transactions.at(0).hash, view_secret_key); { std::unique_lock lock(mu); if (local_work_counter == work_counter) { diff --git a/src/Core/Multicore.hpp b/src/Core/Multicore.hpp index 66fedb12..614d828a 100644 --- a/src/Core/Multicore.hpp +++ b/src/Core/Multicore.hpp @@ -21,6 +21,7 @@ class Currency; struct RingSignatureArg { Hash tx_prefix_hash; KeyImage key_image; + bool key_image_subgroup_check = false; std::vector output_keys; std::vector signatures; }; @@ -45,7 +46,7 @@ class RingCheckerMulticore { ~RingCheckerMulticore(); void cancel_work(); std::string start_work_get_error(IBlockChainState *state, const Currency ¤cy, const Block &block, - Height unlock_height, Timestamp unlock_timestamp); // can fail immediately + Height unlock_height, Timestamp unlock_timestamp, bool key_image_subgroup_check); // can fail immediately bool signatures_valid() const; }; diff --git a/src/Core/Node.cpp b/src/Core/Node.cpp index 92df6ba9..b8040714 100644 --- a/src/Core/Node.cpp +++ b/src/Core/Node.cpp @@ -3,6 +3,7 @@ #include "Node.hpp" #include +#include #include #include "Config.hpp" #include "CryptoNoteTools.hpp" @@ -23,34 +24,64 @@ Node::Node(logging::ILogger &log, const Config &config, BlockChainState &block_c , m_config(config) , m_block_chain_was_far_behind(true) , m_log(log, "Node") - , m_peer_db(config) - , m_p2p(log, config, m_peer_db, std::bind(&Node::client_factory, this, _1, _2)) + , m_peer_db(log, config, "peer_db") + , m_p2p(log, config, m_peer_db, std::bind(&Node::client_factory, this, _1)) + , multicast(config.multicast_address, config.multicast_port, std::bind(&Node::on_multicast, this, _1, _2, _3)) + , m_multicast_timer(std::bind(&Node::send_multicast, this)) , m_start_time(m_p2p.get_local_time()) , m_commit_timer(std::bind(&Node::db_commit, this)) - , m_downloader(this, block_chain) { + , m_downloader(this, block_chain) + , m_downloader_v3(this, block_chain) { const std::string old_path = platform::get_default_data_directory(config.crypto_note_name); const std::string new_path = config.get_data_folder(); - if (!config.is_testnet) { - m_block_chain_reader1 = - std::make_unique(new_path + "/blockindexes.bin", new_path + "/blocks.bin"); - if (m_block_chain_reader1->get_block_count() <= block_chain.get_tip_height()) - m_block_chain_reader1.reset(); - if (new_path != old_path) { // Current situation on Linux - m_block_chain_reader2 = - std::make_unique(old_path + "/blockindexes.bin", old_path + "/blocks.bin"); - if (m_block_chain_reader2->get_block_count() <= block_chain.get_tip_height()) - m_block_chain_reader2.reset(); - } - } + m_block_chain_reader1 = std::make_unique( + block_chain.get_currency(), new_path + "/blockindexes.bin", new_path + "/blocks.bin"); + if (m_block_chain_reader1->get_block_count() <= block_chain.get_tip_height()) + m_block_chain_reader1.reset(); if (!config.bytecoind_bind_ip.empty() && config.bytecoind_bind_port != 0) m_api.reset(new http::Server(config.bytecoind_bind_ip, config.bytecoind_bind_port, std::bind(&Node::on_api_http_request, this, _1, _2, _3), std::bind(&Node::on_api_http_disconnect, this, _1), config.ssl_certificate_pem_file, config.ssl_certificate_password ? config.ssl_certificate_password.get() : std::string())); - m_commit_timer.once(DB_COMMIT_PERIOD_BYTECOIND); + m_commit_timer.once(m_config.db_commit_period_blockchain); advance_long_poll(); + send_multicast(); +} + +void Node::send_multicast() { + if (m_config.multicast_period == 0) + return; + std::cout << "sending multicast about node listening on port=" << m_config.p2p_external_port << std::endl; + BinaryArray ha = P2PProtocolNew::create_multicast_announce( + m_block_chain.get_currency().genesis_block_hash, m_config.p2p_external_port); + platform::UDPMulticast::send(m_config.multicast_address, m_config.multicast_port, ha.data(), ha.size()); + m_multicast_timer.once(m_config.multicast_period); +} + +void Node::on_multicast(const std::string &addr, const unsigned char *data, size_t size) { + // std::cout << " on_multicast from=" << addr << " size=" << size << std::endl; + NetworkAddress na; + na.port = P2PProtocolNew::parse_multicast_announce(data, size, m_block_chain.get_currency().genesis_block_hash); + if (!na.port) + return; + if (common::parse_ip_address(addr, &na.ip)) { + std::cout << "* good on_multicast from=" << na << " size=" << size << std::endl; // TODO - remove + if (m_peer_db.add_incoming_peer(na, m_p2p.get_local_time())) + m_log(logging::INFO) << "Adding peer from multicast announce addr=" << na << std::endl; + } + // We do not receive multicast from loopback, so we just guess peer could be from localhost + if (common::parse_ip_address("127.0.0.1", &na.ip)) { + if (m_peer_db.add_incoming_peer(na, m_p2p.get_local_time())) + m_log(logging::INFO) << "Adding local peer from multicast announce addr=" << na << std::endl; + } + m_p2p.peers_updated(); +} + +void Node::db_commit() { + m_block_chain.db_commit(); + m_commit_timer.once(m_config.db_commit_period_blockchain); } bool Node::on_idle() { @@ -72,7 +103,7 @@ bool Node::on_idle() { return true; } -void Node::sync_transactions(P2PClientBytecoin *who) { +void Node::sync_transactions(P2PProtocolBytecoin *who) { NOTIFY_REQUEST_TX_POOL::request msg; auto mytxs = m_block_chain.get_memory_state_transactions(); msg.txs.reserve(mytxs.size()); @@ -83,267 +114,41 @@ void Node::sync_transactions(P2PClientBytecoin *who) { who->send(std::move(raw_msg)); } -void Node::P2PClientBytecoin::on_msg_bytes(size_t, size_t) { // downloaded. uploaded - // node->peers.on_peer_bytes(get_address(), downloaded, uploaded, - // node->p2p.get_local_time()); -} - -CORE_SYNC_DATA -Node::P2PClientBytecoin::get_sync_data() const { - CORE_SYNC_DATA sync_data; - sync_data.current_height = m_node->m_block_chain.get_tip_height(); - sync_data.top_id = m_node->m_block_chain.get_tip_bid(); - return sync_data; -} - -std::vector Node::P2PClientBytecoin::get_peers_to_share() const { - auto result = m_node->m_peer_db.get_peerlist_to_p2p( - get_address(), m_node->m_p2p.get_local_time(), config.p2p_default_peers_in_handshake); - return result; -} - -void Node::P2PClientBytecoin::on_first_message_after_handshake() { - // if we set just seen on handshake, we will keep connecting to seed nodes - // forever - m_node->m_peer_db.set_peer_just_seen( - get_last_received_unique_number(), get_address(), m_node->m_p2p.get_local_time()); -} - -void Node::P2PClientBytecoin::after_handshake() { - m_node->m_p2p.peers_updated(); - m_node->m_downloader.on_connect(this); - m_node->advance_long_poll(); - - auto signed_checkpoints = m_node->m_block_chain.get_latest_checkpoints(); - for (const auto & sck : signed_checkpoints) { - BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_CHECKPOINT::ID, LevinProtocol::encode(sck), false); - send(std::move(raw_msg)); - } -} - -void Node::P2PClientBytecoin::on_msg_handshake(COMMAND_HANDSHAKE::request &&req) { - m_node->m_peer_db.add_incoming_peer(get_address(), req.node_data.peer_id, m_node->m_p2p.get_local_time()); - after_handshake(); -} - -void Node::P2PClientBytecoin::on_msg_handshake(COMMAND_HANDSHAKE::response &&req) { - m_node->m_peer_db.merge_peerlist_from_p2p(req.local_peerlist, m_node->m_p2p.get_local_time()); - after_handshake(); -} - -void Node::P2PClientBytecoin::on_msg_notify_request_chain(NOTIFY_REQUEST_CHAIN::request &&req) { - NOTIFY_RESPONSE_CHAIN_ENTRY::request msg; - msg.m_block_ids = m_node->m_block_chain.get_sync_headers_chain( - req.block_ids, &msg.start_height, config.p2p_block_ids_sync_default_count); - msg.total_height = m_node->m_block_chain.get_tip_height() + 1; - - BinaryArray raw_msg = - LevinProtocol::send_message(NOTIFY_RESPONSE_CHAIN_ENTRY::ID, LevinProtocol::encode(msg), false); - send(std::move(raw_msg)); -} - -void Node::P2PClientBytecoin::on_msg_notify_request_chain(NOTIFY_RESPONSE_CHAIN_ENTRY::request &&req) { - m_node->m_downloader.on_msg_notify_request_chain(this, req); -} - -void Node::P2PClientBytecoin::on_msg_notify_request_objects(NOTIFY_REQUEST_GET_OBJECTS::request &&req) { - NOTIFY_RESPONSE_GET_OBJECTS::request msg; - msg.current_blockchain_height = m_node->m_block_chain.get_tip_height() + 1; - for (auto &&bh : req.blocks) { - RawBlock raw_block; - if (m_node->m_block_chain.read_block(bh, &raw_block)) { - msg.blocks.push_back(RawBlockLegacy{raw_block.block, raw_block.transactions}); - } else - msg.missed_ids.push_back(bh); - } - if (!req.txs.empty()) { - // TODO - remove after we are sure transactions are never asked - throw std::runtime_error( - "Transactions asked in NOTIFY_REQUEST_GET_OBJECTS by " + common::ip_address_to_string(get_address().ip)); - } - BinaryArray raw_msg = - LevinProtocol::send_message(NOTIFY_RESPONSE_GET_OBJECTS::ID, LevinProtocol::encode(msg), false); - send(std::move(raw_msg)); -} - -void Node::P2PClientBytecoin::on_msg_notify_request_objects(NOTIFY_RESPONSE_GET_OBJECTS::request &&req) { - m_node->m_downloader.on_msg_notify_request_objects(this, req); -} - -void Node::P2PClientBytecoin::on_disconnect(const std::string &ban_reason) { - m_node->m_downloader.on_disconnect(this); - - P2PClientBasic::on_disconnect(ban_reason); - m_node->advance_long_poll(); -} - -void Node::P2PClientBytecoin::on_msg_notify_request_tx_pool(NOTIFY_REQUEST_TX_POOL::request &&req) { - NOTIFY_NEW_TRANSACTIONS::request msg; - auto mytxs = m_node->m_block_chain.get_memory_state_transactions(); - msg.txs.reserve(mytxs.size()); - std::sort(req.txs.begin(), req.txs.end()); // Should have been sorted on wire, - // checked here, but alas, legacy - for (auto &&tx : mytxs) { - auto it = std::lower_bound(req.txs.begin(), req.txs.end(), tx.first); - if (it != req.txs.end() && *it == tx.first) - continue; - msg.txs.push_back(tx.second.binary_tx); - } - m_node->m_log(logging::TRACE) << "on_msg_notify_request_tx_pool from " << get_address() - << " peer sent=" << req.txs.size() << " we are relaying=" << msg.txs.size() - << std::endl; - if (msg.txs.empty()) - return; - BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_TRANSACTIONS::ID, LevinProtocol::encode(msg), false); - send(std::move(raw_msg)); -} - -void Node::P2PClientBytecoin::on_msg_timed_sync(COMMAND_TIMED_SYNC::request &&req) { - m_node->m_downloader.advance_download(); -} -void Node::P2PClientBytecoin::on_msg_timed_sync(COMMAND_TIMED_SYNC::response &&req) { - m_node->m_downloader.advance_download(); -} - -void Node::P2PClientBytecoin::on_msg_notify_new_block(NOTIFY_NEW_BLOCK::request &&req) { - RawBlock raw_block{req.b.block, req.b.transactions}; - PreparedBlock pb(std::move(raw_block), nullptr); - api::BlockHeader info; - auto action = m_node->m_block_chain.add_block( - pb, &info, common::ip_address_and_port_to_string(get_address().ip, get_address().port)); - switch (action) { - case BroadcastAction::BAN: - disconnect("NOTIFY_NEW_BLOCK add_block BAN"); - return; - case BroadcastAction::BROADCAST_ALL: { - req.hop += 1; - BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_BLOCK::ID, LevinProtocol::encode(req), false); - m_node->m_p2p.broadcast(this, raw_msg); - - m_node->advance_long_poll(); - break; - } - case BroadcastAction::NOTHING: - break; - } - set_last_received_sync_data(CORE_SYNC_DATA{req.current_blockchain_height - 1, pb.bid}); - // -1 is in legacy protocol - m_node->m_downloader.advance_download(); -} - -void Node::P2PClientBytecoin::on_msg_notify_new_transactions(NOTIFY_NEW_TRANSACTIONS::request &&req) { - if (m_node->m_block_chain_reader1 || m_node->m_block_chain_reader2 || - m_node->m_block_chain.get_tip_height() < m_node->m_block_chain.internal_import_known_height()) - return; // We cannot check tx while downloading anyway - NOTIFY_NEW_TRANSACTIONS::request msg; - Hash any_tid; - for (auto &&raw_tx : req.txs) { - Transaction tx; - try { - seria::from_binary(tx, raw_tx); - } catch (const std::exception &ex) { - disconnect("NOTIFY_NEW_TRANSACTIONS add_transaction BAN from_binary failed " + std::string(ex.what())); - return; - } - const Hash tid = get_transaction_hash(tx); - any_tid = tid; - Height conflict_height = 0; - auto action = m_node->m_block_chain.add_transaction(tid, tx, raw_tx, m_node->m_p2p.get_local_time(), - &conflict_height, common::ip_address_and_port_to_string(get_address().ip, get_address().port)); - switch (action) { - case AddTransactionResult::BAN: - disconnect("NOTIFY_NEW_TRANSACTIONS add_transaction BAN"); - return; - case AddTransactionResult::BROADCAST_ALL: - msg.txs.push_back(raw_tx); - break; - case AddTransactionResult::ALREADY_IN_POOL: - case AddTransactionResult::INCREASE_FEE: - case AddTransactionResult::FAILED_TO_REDO: - case AddTransactionResult::OUTPUT_ALREADY_SPENT: - break; - } - } - m_node->m_log(logging::TRACE) << "on_msg_notify_new_transactions from " << get_address() - << " got=" << req.txs.size() << " relaying=" << msg.txs.size() - << (req.txs.size() > 1 ? " notify_tx_reply (?) " : " ") - << (any_tid == Hash{} ? "" : common::pod_to_hex(any_tid)) << std::endl; - if (msg.txs.empty()) - return; - BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_TRANSACTIONS::ID, LevinProtocol::encode(msg), false); - m_node->m_p2p.broadcast(this, raw_msg); - m_node->advance_long_poll(); -} -void Node::P2PClientBytecoin::on_msg_notify_checkpoint(NOTIFY_CHECKPOINT::request &&req) { - if (!m_node->m_block_chain.add_checkpoint( - req, common::ip_address_and_port_to_string(get_address().ip, get_address().port))) - return; - m_node->m_log(logging::INFO) << "NOTIFY_CHECKPOINT::request height=" << req.height << " hash=" << req.hash - << std::endl; - BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_CHECKPOINT::ID, LevinProtocol::encode(req), false); - m_node->m_p2p.broadcast(nullptr, raw_msg); // nullptr, not this - so a sender sees "reflection" of message - COMMAND_TIMED_SYNC::request ts_req; - ts_req.payload_data = CORE_SYNC_DATA{m_node->m_block_chain.get_tip_height(), m_node->m_block_chain.get_tip_bid()}; - raw_msg = LevinProtocol::send_message(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(ts_req), true); - m_node->m_p2p.broadcast(nullptr, raw_msg); - m_node->advance_long_poll(); -} +bool Node::check_trust(const np::ProofOfTrust &tr) { + uint64_t local_time = platform::now_unix_timestamp(); + uint64_t time_delta = local_time > tr.time ? local_time - tr.time : tr.time - local_time; -#if bytecoin_ALLOW_DEBUG_COMMANDS -void Node::P2PClientBytecoin::on_msg_network_state(COMMAND_REQUEST_NETWORK_STATE::request &&req) { - if (!m_node->check_trust(req.tr)) { - disconnect(std::string()); - return; - } - COMMAND_REQUEST_NETWORK_STATE::response msg; - msg.local_time = m_node->m_p2p.get_local_time(); - msg.my_id = get_unique_number(); - for (auto &&cc : m_node->m_downloader.get_good_clients()) { - connection_entry item; - item.is_income = cc.first->is_incoming(); - item.id = cc.first->get_unique_number(); - item.adr = cc.first->get_address(); - msg.connections_list.push_back(item); - } - BinaryArray raw_msg = LevinProtocol::send_reply(COMMAND_REQUEST_NETWORK_STATE::ID, LevinProtocol::encode(msg), 0); - send(std::move(raw_msg)); -} + if (time_delta > 24 * 60 * 60) + return false; + if (m_last_stat_request_time >= tr.time) + return false; + if (m_p2p.get_unique_number() != tr.peer_id) + return false; -void Node::P2PClientBytecoin::on_msg_stat_info(COMMAND_REQUEST_STAT_INFO::request &&req) { - if (!m_node->check_trust(req.tr)) { - disconnect(std::string()); - return; - } - COMMAND_REQUEST_STAT_INFO::response msg; - msg.incoming_connections_count = m_node->m_p2p.good_clients(true).size(); - msg.connections_count = msg.incoming_connections_count + m_node->m_p2p.good_clients(false).size(); - msg.version = app_version(); - msg.os_version = platform::get_os_version_string(); - msg.payload_info = CoreStatistics{}; - BinaryArray raw_msg = LevinProtocol::send_reply(COMMAND_REQUEST_STAT_INFO::ID, LevinProtocol::encode(msg), 0); - send(std::move(raw_msg)); + Hash h = tr.get_hash(); + if (!crypto::check_signature(h, m_config.trusted_public_key, tr.sign)) + return false; + m_last_stat_request_time = tr.time; + return true; } -#endif - -bool Node::check_trust(const proof_of_trust &tr) { - uint64_t local_time = platform::now_unix_timestamp(); - uint64_t time_delata = local_time > tr.time ? local_time - tr.time : tr.time - local_time; +bool Node::check_trust(const ProofOfTrustLegacy &tr) { + uint64_t local_time = platform::now_unix_timestamp(); + uint64_t time_delta = local_time > tr.time ? local_time - tr.time : tr.time - local_time; - if (time_delata > 24 * 60 * 60) + if (time_delta > 24 * 60 * 60) return false; if (m_last_stat_request_time >= tr.time) return false; if (m_p2p.get_unique_number() != tr.peer_id) return false; - crypto::Hash h = tr.get_hash(); + Hash h = tr.get_hash(); if (!crypto::check_signature(h, m_config.trusted_public_key, tr.sign)) return false; m_last_stat_request_time = tr.time; return true; } - void Node::advance_long_poll() { const auto now = m_p2p.get_local_time(); if (!prevent_sleep && m_block_chain.get_tip().timestamp < now - 86400) @@ -353,14 +158,12 @@ void Node::advance_long_poll() { prevent_sleep = nullptr; if (m_long_poll_http_clients.empty()) return; - api::bytecoind::GetStatus::Response resp = create_status_response3(); - json_rpc::Response last_json_resp; - last_json_resp.set_result(resp); + const api::bytecoind::GetStatus::Response resp = create_status_response(); for (auto lit = m_long_poll_http_clients.begin(); lit != m_long_poll_http_clients.end();) { const bool method_status = lit->original_json_request.get_method() == api::bytecoind::GetStatus::method() || lit->original_json_request.get_method() == api::bytecoind::GetStatus::method2(); - if (method_status && lit->original_get_status == resp) { + if (method_status && !resp.ready_for_longpoll(lit->original_get_status)) { ++lit; continue; } @@ -369,6 +172,7 @@ void Node::advance_long_poll() { ++lit; continue; } + const common::JsonValue &jid = lit->original_json_request.get_id().get(); http::ResponseData last_http_response; last_http_response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); last_http_response.r.status = 200; @@ -376,23 +180,20 @@ void Node::advance_long_poll() { last_http_response.r.http_version_minor = lit->original_request.r.http_version_minor; last_http_response.r.keep_alive = lit->original_request.r.keep_alive; if (method_status) { - last_json_resp.set_id(lit->original_json_request.get_id()); - last_http_response.set_body(last_json_resp.get_body()); + last_http_response.set_body(json_rpc::create_response_body(resp, jid)); } else { - json_rpc::Response gbt_json_resp; try { api::bytecoind::GetBlockTemplate::Request gbt_req; lit->original_json_request.load_params(gbt_req); api::bytecoind::GetBlockTemplate::Response gbt_res; getblocktemplate(std::move(gbt_req), gbt_res); - gbt_json_resp.set_result(gbt_res); - gbt_json_resp.set_id(lit->original_json_request.get_id()); + last_http_response.set_body(json_rpc::create_response_body(gbt_res, jid)); } catch (const json_rpc::Error &err) { - gbt_json_resp.set_error(err); + last_http_response.set_body(json_rpc::create_error_response_body(err, jid)); } catch (const std::exception &e) { - gbt_json_resp.set_error(json_rpc::Error(json_rpc::INTERNAL_ERROR, e.what())); + json_rpc::Error json_err(json_rpc::INTERNAL_ERROR, common::what(e)); + last_http_response.set_body(json_rpc::create_error_response_body(json_err, jid)); } - last_http_response.set_body(gbt_json_resp.get_body()); } lit->original_who->write(std::move(last_http_response)); lit = m_long_poll_http_clients.erase(lit); @@ -411,39 +212,44 @@ static const std::string robots_txt = "User-agent: *\r\nDisallow: /" bool Node::on_api_http_request(http::Client *who, http::RequestData &&request, http::ResponseData &response) { response.r.add_headers_nocache(); - if (request.r.uri == "/" || request.r.uri == "/index.html") { - response.r.headers.push_back({"Content-Type", "text/html; charset=UTF-8"}); - response.r.status = 200; - auto stat = create_status_response3(); - response.set_body(beautiful_index_start + app_version() + " • sync status " + - common::to_string(stat.top_block_height) + "/" + - common::to_string(stat.top_known_block_height) + beautiful_index_finish); - return true; - } if (request.r.uri == "/robots.txt") { response.r.headers.push_back({"Content-Type", "text/plain; charset=UTF-8"}); response.r.status = 200; response.set_body(std::string(robots_txt)); return true; } - auto it = m_http_handlers.find(request.r.uri); - if (it == m_http_handlers.end()) { - auto leg = api::bytecoind::legacy_bin_methods(); - if (std::find(leg.begin(), leg.end(), request.r.uri) != leg.end()) - response.r.status = 410; - else - response.r.status = 404; + bool good_auth = + m_config.bytecoind_authorization.empty() || request.r.basic_authorization == m_config.bytecoind_authorization; + bool good_auth_private = m_config.bytecoind_authorization_private.empty() || + request.r.basic_authorization == m_config.bytecoind_authorization_private; + if (!good_auth && !good_auth_private) // Private methods will check for private authorization again + throw http::ErrorAuthorization("Blockchain"); + if (request.r.uri == "/" || request.r.uri == "/index.html") { + response.r.headers.push_back({"Content-Type", "text/html; charset=UTF-8"}); + response.r.status = 200; + auto stat = create_status_response(); + auto body = beautiful_index_start + app_version() + " • " + m_config.net + "net • sync status " + + common::to_string(stat.top_block_height) + "/" + common::to_string(stat.top_known_block_height) + + beautiful_index_finish; + if (m_config.net != "main") + boost::replace_all(body, "#f04086", "#00afa5"); + response.set_body(std::move(body)); return true; } - if (!m_config.bytecoind_authorization.empty() && - request.r.basic_authorization != m_config.bytecoind_authorization) { - response.r.headers.push_back({"WWW-Authenticate", "Basic realm=\"Blockchain\", charset=\"UTF-8\""}); - response.r.status = 401; + if (request.r.uri == api::bytecoind::url()) { + if (!on_json_rpc(who, std::move(request), response)) + return false; + response.r.status = 200; return true; } - if (!it->second(this, who, std::move(request), response)) - return false; - response.r.status = 200; + if (request.r.uri == api::bytecoind::binary_url()) { + if (!on_binary_rpc(who, std::move(request), response)) + return false; + response.r.status = 200; + return true; + } + response.r.status = 404; + response.set_body("404 Not Found"); return true; } @@ -455,34 +261,9 @@ void Node::on_api_http_disconnect(http::Client *who) { ++lit; } -namespace { - -template -Node::HTTPHandlerFunction bin_method(bool (Node::*handler)(http::Client *who, http::RequestData &&raw_request, - json_rpc::Request &&raw_js_request, CommandRequest &&, CommandResponse &)) { - return [handler](Node *obj, http::Client *who, http::RequestData &&request, http::ResponseData &response) { - - CommandRequest req{}; - CommandResponse res{}; - - seria::from_binary(req, request.body); - - bool result = (obj->*handler)(who, std::move(request), json_rpc::Request(), std::move(req), res); - if (result) { - response.set_body(seria::to_binary_str(res)); - response.r.status = 200; - } - return result; - }; -} -} // anonymous namespace - -const std::unordered_map Node::m_http_handlers = { - - {api::bytecoind::SyncBlocks::bin_method(), bin_method(&Node::on_wallet_sync3)}, - {api::bytecoind::SyncMemPool::bin_method(), bin_method(&Node::on_sync_mempool3)}, - {"/json_rpc", std::bind(&Node::process_json_rpc_request, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3, std::placeholders::_4)}}; +const std::unordered_map Node::m_binaryrpc_handlers = { + {api::bytecoind::SyncBlocks::method(), json_rpc::make_binary_member_method(&Node::on_sync_blocks)}, + {api::bytecoind::SyncMemPool::method(), json_rpc::make_binary_member_method(&Node::on_sync_mempool)}}; std::unordered_map Node::m_jsonrpc_handlers = { {api::bytecoind::GetLastBlockHeaderLegacy::method(), json_rpc::make_member_method(&Node::on_get_last_block_header)}, @@ -496,34 +277,34 @@ std::unordered_map Node::m_jsonrpc_ha {api::bytecoind::GetCurrencyId::method_legacy(), json_rpc::make_member_method(&Node::on_get_currency_id)}, {api::bytecoind::SubmitBlock::method(), json_rpc::make_member_method(&Node::on_submitblock)}, {api::bytecoind::SubmitBlockLegacy::method(), json_rpc::make_member_method(&Node::on_submitblock_legacy)}, - {api::bytecoind::GetRandomOutputs::method(), json_rpc::make_member_method(&Node::on_get_random_outputs3)}, - {api::bytecoind::GetStatus::method(), json_rpc::make_member_method(&Node::on_get_status3)}, - {api::bytecoind::GetStatus::method2(), json_rpc::make_member_method(&Node::on_get_status3)}, + {api::bytecoind::GetRandomOutputs::method(), json_rpc::make_member_method(&Node::on_get_random_outputs)}, + {api::bytecoind::GetStatus::method(), json_rpc::make_member_method(&Node::on_get_status)}, + {api::bytecoind::GetStatus::method2(), json_rpc::make_member_method(&Node::on_get_status)}, {api::bytecoind::GetStatistics::method(), json_rpc::make_member_method(&Node::on_get_statistics)}, {api::bytecoind::GetArchive::method(), json_rpc::make_member_method(&Node::on_get_archive)}, - {api::bytecoind::SendTransaction::method(), json_rpc::make_member_method(&Node::handle_send_transaction3)}, - {api::bytecoind::CheckSendProof::method(), json_rpc::make_member_method(&Node::handle_check_sendproof3)}, -// {api::bytecoind::GetRawBlock::method(), json_rpc::make_member_method(&Node::on_get_raw_block)}, - {api::bytecoind::SyncBlocks::method(), json_rpc::make_member_method(&Node::on_wallet_sync3)}, - {api::bytecoind::GetRawTransaction::method(), json_rpc::make_member_method(&Node::on_get_raw_transaction3)}, - {api::bytecoind::SyncMemPool::method(), json_rpc::make_member_method(&Node::on_sync_mempool3)}}; - -bool Node::on_get_random_outputs3(http::Client *, http::RequestData &&, json_rpc::Request &&, + {api::bytecoind::SendTransaction::method(), json_rpc::make_member_method(&Node::handle_send_transaction)}, + {api::bytecoind::CheckSendproof::method(), json_rpc::make_member_method(&Node::handle_check_sendproof)}, + {api::bytecoind::SyncBlocks::method(), json_rpc::make_member_method(&Node::on_sync_blocks)}, + {api::bytecoind::GetRawBlock::method(), json_rpc::make_member_method(&Node::on_get_raw_block)}, + {api::bytecoind::GetBlockHeader::method(), json_rpc::make_member_method(&Node::on_get_block_header)}, + {api::bytecoind::GetRawTransaction::method(), json_rpc::make_member_method(&Node::on_get_raw_transaction)}, + {api::bytecoind::SyncMemPool::method(), json_rpc::make_member_method(&Node::on_sync_mempool)}}; + +bool Node::on_get_random_outputs(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetRandomOutputs::Request &&request, api::bytecoind::GetRandomOutputs::Response &response) { - if (request.confirmed_height_or_depth < 0) - request.confirmed_height_or_depth = std::max( - 0, static_cast(m_block_chain.get_tip_height()) + 1 + request.confirmed_height_or_depth); + Height confirmed_height_or_depth = api::ErrorWrongHeight::fix_height_or_depth( + request.confirmed_height_or_depth, m_block_chain.get_tip_height(), true, false); api::BlockHeader tip_header = m_block_chain.get_tip(); for (uint64_t amount : request.amounts) { auto random_outputs = m_block_chain.get_random_outputs( - amount, request.outs_count, request.confirmed_height_or_depth, tip_header.timestamp); + amount, request.output_count, confirmed_height_or_depth, tip_header.timestamp); auto &outs = response.outputs[amount]; outs.insert(outs.end(), random_outputs.begin(), random_outputs.end()); } return true; } -api::bytecoind::GetStatus::Response Node::create_status_response3() const { +api::bytecoind::GetStatus::Response Node::create_status_response() const { api::bytecoind::GetStatus::Response res; res.top_block_height = m_block_chain.get_tip_height(); res.top_known_block_height = m_downloader.get_known_block_count(res.top_block_height); @@ -535,11 +316,20 @@ api::bytecoind::GetStatus::Response Node::create_status_response3() const { if (m_block_chain_reader2) res.top_known_block_height = std::max(res.top_known_block_height, m_block_chain_reader2->get_block_count()); - res.incoming_peer_count = static_cast(m_p2p.good_clients(true).size()); - res.outgoing_peer_count = static_cast(m_p2p.good_clients(false).size()); + for (auto &&pb : broadcast_protocols) + if (pb->is_incoming()) + res.incoming_peer_count += 1; + else + res.outgoing_peer_count += 1; + for (auto &&pb : broadcast_protocols_new) + if (pb->is_incoming()) + res.incoming_peer_count += 1; + else + res.outgoing_peer_count += 1; api::BlockHeader tip = m_block_chain.get_tip(); res.top_block_hash = m_block_chain.get_tip_bid(); res.top_block_timestamp = tip.timestamp; + res.top_block_timestamp_median = tip.timestamp_median; res.top_block_difficulty = tip.difficulty; res.top_block_cumulative_difficulty = tip.cumulative_difficulty; res.recommended_fee_per_byte = m_block_chain.get_currency().coin() / 1000000; // TODO - calculate @@ -548,11 +338,53 @@ api::bytecoind::GetStatus::Response Node::create_status_response3() const { return res; } -bool Node::on_get_status3(http::Client *who, http::RequestData &&raw_request, json_rpc::Request &&raw_js_request, +void Node::broadcast(P2PProtocolBytecoin *exclude, const BinaryArray &data) { + for (auto &&p : broadcast_protocols) + if (p != exclude) + p->P2PProtocol::send(BinaryArray(data)); // Move is impossible here +} +void Node::broadcast_new(P2PProtocolBytecoinNew *exclude, const BinaryArray &binary_header) { + np::RelayBlockHeader msg; + msg.binary_header = binary_header; + // TODO - do not forget to check + msg.top_block_desc.cd = m_block_chain.get_tip_cumulative_difficulty(); + msg.top_block_desc.height = m_block_chain.get_tip_height(); + msg.top_block_desc.hash = m_block_chain.get_tip_bid(); + BinaryArray body = seria::to_binary_kv(msg); + BinaryArray header = P2PProtocolBytecoinNew::create_header(np::RelayBlockHeader::ID, body.size()); + for (auto &&p : broadcast_protocols_new) + if (p != exclude) { + p->P2PProtocol::send(BinaryArray(header)); + p->P2PProtocol::send(BinaryArray(body)); + } +} +void Node::broadcast_new(P2PProtocolBytecoinNew *exclude, const std::vector &transaction_descs) { + np::RelayTransactionDescs msg; + msg.transaction_descs = transaction_descs; + // TODO - split into chunks + msg.top_block_desc.cd = m_block_chain.get_tip_cumulative_difficulty(); + msg.top_block_desc.height = m_block_chain.get_tip_height(); + msg.top_block_desc.hash = m_block_chain.get_tip_bid(); + BinaryArray body = seria::to_binary_kv(msg); + BinaryArray header = P2PProtocolBytecoinNew::create_header(np::RelayBlockHeader::ID, body.size()); + for (auto &&p : broadcast_protocols_new) + if (p != exclude) { + p->P2PProtocol::send(BinaryArray(header)); + p->P2PProtocol::send(BinaryArray(body)); + } +} + +// void Node::broadcast_new(P2PProtocolBytecoinNew * exclude, const BinaryArray &data){ +// for(auto && p : broadcast_protocols_new) +// if( p != exclude ) +// p->P2PProtocol::send(BinaryArray(data)); // Move is impossible here +//} + +bool Node::on_get_status(http::Client *who, http::RequestData &&raw_request, json_rpc::Request &&raw_js_request, api::bytecoind::GetStatus::Request &&req, api::bytecoind::GetStatus::Response &res) { - res = create_status_response3(); - if (req == res) { - // m_log(logging::INFO) << "on_get_status3 will long poll, json=" + res = create_status_response(); + if (!res.ready_for_longpoll(req)) { + // m_log(logging::INFO) << "on_get_status will long poll, json=" //<< // raw_request.body << std::endl; LongPollClient lpc; @@ -566,22 +398,86 @@ bool Node::on_get_status3(http::Client *who, http::RequestData &&raw_request, js return true; } -bool Node::on_get_statistics(http::Client *, http::RequestData &&, json_rpc::Request &&, +api::bytecoind::GetStatistics::Response Node::create_statistics_response() const { + api::bytecoind::GetStatistics::Response res; + res.peer_id = m_p2p.get_unique_number(); + for (auto &&p : broadcast_protocols_new) { + np::ConnectionDesc desc; + desc.address = p->get_address(); + desc.is_incoming = p->is_incoming(); + desc.p2p_version = p->get_other_peer_desc().p2p_version; + desc.peer_id = p->get_other_peer_desc().peer_id; + desc.top_block_desc = p->get_other_top_block_desc(); + res.connections.push_back(desc); + } + for (auto &&p : broadcast_protocols) { + np::ConnectionDesc desc; + desc.address = p->get_address(); + desc.is_incoming = p->is_incoming(); + desc.p2p_version = p->get_version(); + desc.peer_id = p->get_last_received_unique_number(); + desc.top_block_desc.hash = p->get_last_received_sync_data().top_id; + desc.top_block_desc.height = p->get_last_received_sync_data().current_height; + res.connections.push_back(desc); + } + res.platform = platform::get_platform_name(); + res.version = bytecoin::app_version(); + res.net = m_config.net; + res.genesis_block_hash = m_block_chain.get_currency().genesis_block_hash; + res.start_time = m_start_time; + m_block_chain.fill_statistics(res); + return res; +} + +bool Node::on_get_statistics(http::Client *, http::RequestData &&http_request, json_rpc::Request &&, api::bytecoind::GetStatistics::Request &&, api::bytecoind::GetStatistics::Response &res) { - res.peer_id = m_p2p.get_unique_number(); - res.platform = platform::get_platform_name(); - res.version = bytecoin::app_version(); - res.start_time = m_start_time; - res.checkpoints = m_block_chain.get_latest_checkpoints(); + bool good_auth_private = m_config.bytecoind_authorization_private.empty() || + http_request.r.basic_authorization == m_config.bytecoind_authorization_private; + if (!good_auth_private) + throw http::ErrorAuthorization("Statistics"); + res = create_statistics_response(); return true; } -bool Node::on_get_archive(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool Node::on_get_archive(http::Client *, http::RequestData &&http_request, json_rpc::Request &&, api::bytecoind::GetArchive::Request &&req, api::bytecoind::GetArchive::Response &resp) { + bool good_auth_private = m_config.bytecoind_authorization_private.empty() || + http_request.r.basic_authorization == m_config.bytecoind_authorization_private; + if (!good_auth_private) + throw http::ErrorAuthorization("Archive"); m_block_chain.read_archive(std::move(req), resp); return true; } -bool Node::on_wallet_sync3(http::Client *, http::RequestData &&, json_rpc::Request &&json_req, + +static void fill_transaction_info(const TransactionPrefix &tx, api::Transaction *api_tx) { + api_tx->unlock_block_or_timestamp = tx.unlock_block_or_timestamp; + api_tx->extra = tx.extra; + api_tx->anonymity = std::numeric_limits::max(); + api_tx->public_key = extra_get_transaction_public_key(tx.extra); + extra_get_payment_id(tx.extra, api_tx->payment_id); + Amount input_amount = 0; + for (const auto &input : tx.inputs) { + if (input.type() == typeid(KeyInput)) { + const KeyInput &in = boost::get(input); + api_tx->anonymity = std::min(api_tx->anonymity, static_cast(in.output_indexes.size() - 1)); + input_amount += in.amount; + } + } + Amount output_amount = 0; + for (const auto &output : tx.outputs) { + if (output.target.type() == typeid(KeyOutput)) { + // const KeyOutput &key_output = boost::get(output.target); + output_amount += output.amount; + } + } + api_tx->amount = output_amount; + if (input_amount >= output_amount) + api_tx->fee = input_amount - output_amount; + if (api_tx->anonymity == std::numeric_limits::max()) + api_tx->anonymity = 0; // No key inputs +} + +bool Node::on_sync_blocks(http::Client *, http::RequestData &&, json_rpc::Request &&json_req, api::bytecoind::SyncBlocks::Request &&req, api::bytecoind::SyncBlocks::Response &res) { if (req.sparse_chain.empty()) throw std::runtime_error("Empty sparse chain - must include at least genesis block"); @@ -590,35 +486,36 @@ bool Node::on_wallet_sync3(http::Client *, http::RequestData &&, json_rpc::Reque "Wrong currency - different genesis block. Must be " + common::pod_to_hex(m_block_chain.get_genesis_bid())); if (req.max_count > api::bytecoind::SyncBlocks::Request::MAX_COUNT) throw std::runtime_error( - "Too big max_count - must be < " + common::to_string(api::bytecoind::SyncBlocks::Request::MAX_COUNT)); + "Too big max_count - must be <= " + common::to_string(api::bytecoind::SyncBlocks::Request::MAX_COUNT)); auto first_block_timestamp = req.first_block_timestamp < m_block_chain.get_currency().block_future_time_limit ? 0 : req.first_block_timestamp - m_block_chain.get_currency().block_future_time_limit; - Height full_offset = m_block_chain.get_timestamp_lower_bound_block_index(first_block_timestamp); - Height start_block_index; - std::vector supplement = - m_block_chain.get_sync_headers_chain(req.sparse_chain, &start_block_index, req.max_count); - if (full_offset >= start_block_index + supplement.size()) { - start_block_index = full_offset; + Height full_offset = m_block_chain.get_timestamp_lower_bound_height(first_block_timestamp); + Height start_height; + std::vector supplement = m_block_chain.get_sync_headers_chain(req.sparse_chain, &start_height, req.max_count); + if (full_offset >= start_height + supplement.size()) { + start_height = full_offset; supplement.clear(); while (supplement.size() < req.max_count) { Hash ha; - if (!m_block_chain.read_chain(start_block_index + static_cast(supplement.size()), &ha)) + if (!m_block_chain.read_chain(start_height + static_cast(supplement.size()), &ha)) break; supplement.push_back(ha); } - } else if (full_offset > start_block_index) { - supplement.erase(supplement.begin(), supplement.begin() + (full_offset - start_block_index)); - start_block_index = full_offset; + } else if (full_offset > start_height) { + supplement.erase(supplement.begin(), supplement.begin() + (full_offset - start_height)); + start_height = full_offset; } - res.start_height = start_block_index; + res.start_height = start_height; res.blocks.resize(supplement.size()); for (size_t i = 0; i != supplement.size(); ++i) { - auto bhash = supplement[i]; + const auto bhash = supplement[i]; + auto &res_block = res.blocks[i]; invariant( - m_block_chain.read_header(bhash, &res.blocks[i].header), "Block header must be there, but it is not there"); - BlockChainState::BlockGlobalIndices global_indices; + m_block_chain.read_header(bhash, &res_block.header), "Block header must be there, but it is not there"); + m_block_chain.fix_block_sizes(&res_block.header); + // BlockChainState::BlockGlobalIndices output_indexes; // if (res.blocks[i].header.timestamp >= req.first_block_timestamp) // // commented out becuase empty Block cannot be serialized { @@ -626,24 +523,42 @@ bool Node::on_wallet_sync3(http::Client *, http::RequestData &&, json_rpc::Reque invariant(m_block_chain.read_block(bhash, &rb), "Block must be there, but it is not there"); Block block; invariant(block.from_raw_block(rb), "RawBlock failed to convert into block"); - res.blocks[i].base_transaction_hash = get_transaction_hash(block.header.base_transaction); - res.blocks[i].raw_header = std::move(block.header); - res.blocks[i].raw_transactions.reserve(block.transactions.size()); - res.blocks[i].transaction_binary_sizes.reserve(block.transactions.size()); + res_block.transactions.resize(block.transactions.size() + 1); + res_block.transactions.at(0).hash = get_transaction_hash(block.header.base_transaction); + res_block.transactions.at(0).size = + static_cast(seria::binary_size(block.header.base_transaction)); + if (req.need_redundant_data) { + fill_transaction_info(block.header.base_transaction, &res_block.transactions.at(0)); + res_block.transactions.at(0).block_height = start_height + static_cast(i); + res_block.transactions.at(0).block_hash = bhash; + res_block.transactions.at(0).coinbase = true; + res_block.transactions.at(0).timestamp = block.header.timestamp; + } + res_block.raw_header = std::move(block.header); + res_block.raw_transactions.reserve(block.transactions.size()); for (size_t tx_index = 0; tx_index != block.transactions.size(); ++tx_index) { - res.blocks[i].raw_transactions.push_back(std::move(block.transactions.at(tx_index))); - res.blocks[i].transaction_binary_sizes.push_back( - static_cast(rb.transactions.at(tx_index).size())); + res_block.transactions.at(tx_index + 1).hash = res_block.raw_header.transaction_hashes.at(tx_index); + res_block.transactions.at(tx_index + 1).size = + static_cast(rb.transactions.at(tx_index).size()); + if (req.need_redundant_data) { + fill_transaction_info(block.transactions.at(tx_index), &res_block.transactions.at(tx_index + 1)); + res_block.transactions.at(tx_index + 1).block_height = start_height + static_cast(i); + res_block.transactions.at(tx_index + 1).block_hash = bhash; + res_block.transactions.at(tx_index + 1).timestamp = res_block.raw_header.timestamp; + } + if (req.need_signatures) + res_block.signatures.push_back(std::move(block.transactions.at(tx_index).signatures)); + res_block.raw_transactions.push_back(std::move(block.transactions.at(tx_index))); } - invariant(m_block_chain.read_block_output_global_indices(bhash, &res.blocks[i].global_indices), + invariant(m_block_chain.read_block_output_global_indices(bhash, &res_block.output_indexes), "Invariant dead - bid is in chain but blockchain has no block indices"); } } - res.status = create_status_response3(); + res.status = create_status_response(); return true; } -bool Node::on_sync_mempool3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool Node::on_sync_mempool(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::SyncMemPool::Request &&req, api::bytecoind::SyncMemPool::Response &res) { const auto &pool = m_block_chain.get_memory_state_transactions(); for (auto &&ex : req.known_hashes) @@ -651,75 +566,131 @@ bool Node::on_sync_mempool3(http::Client *, http::RequestData &&, json_rpc::Requ res.removed_hashes.push_back(ex); for (auto &&tx : pool) if (!std::binary_search(req.known_hashes.begin(), req.known_hashes.end(), tx.first)) { - // res.added_binary_transactions.push_back(seria::to_binary(tx.second)); res.added_raw_transactions.push_back(tx.second.tx); + if (req.need_signatures) + res.added_signatures.push_back(tx.second.tx.signatures); res.added_transactions.push_back(api::Transaction{}); - res.added_transactions.back().hash = tx.first; - res.added_transactions.back().timestamp = tx.second.timestamp; - res.added_transactions.back().fee = tx.second.fee; - res.added_transactions.back().binary_size = static_cast(tx.second.binary_tx.size()); + if (req.need_redundant_data) + fill_transaction_info(tx.second.tx, &res.added_transactions.back()); + res.added_transactions.back().hash = tx.first; + res.added_transactions.back().timestamp = tx.second.timestamp; + res.added_transactions.back().fee = tx.second.fee; + res.added_transactions.back().size = static_cast(tx.second.binary_tx.size()); } - res.status = create_status_response3(); + res.status = create_status_response(); return true; } +bool Node::on_get_block_header(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::bytecoind::GetBlockHeader::Request &&request, api::bytecoind::GetBlockHeader::Response &response) { + if (request.hash != Hash{} && request.height_or_depth != std::numeric_limits::max()) + throw json_rpc::Error( + json_rpc::INVALID_REQUEST, "You cannot specify both hash and height_or_depth to this method"); + if (request.hash != Hash{}) { + if (!m_block_chain.read_header(request.hash, &response.block_header)) + throw api::ErrorHashNotFound("Block not found in either main or side chains", request.hash); + } else { + Height height_or_depth = + api::ErrorWrongHeight::fix_height_or_depth(request.height_or_depth, m_block_chain.get_tip_height(), true, true); + invariant( + m_block_chain.read_chain(height_or_depth, &request.hash), ""); // after fix_height it must always succeed + invariant(m_block_chain.read_header(request.hash, &response.block_header), ""); + } + m_block_chain.fix_block_sizes(&response.block_header); + response.orphan_status = !m_block_chain.in_chain(response.block_header.height, response.block_header.hash); + response.depth = + api::HeightOrDepth(response.block_header.height) - api::HeightOrDepth(m_block_chain.get_tip_height()) - 1; + return true; +} bool Node::on_get_raw_block(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::bytecoind::GetRawBlock::Request && request, api::bytecoind::GetRawBlock::Response & response){ - - if (!m_block_chain.read_header(request.hash, &response.header)) - throw json_rpc::Error(-5, "Block not found by hash"); // TODO - use HASH_NOT_FOUND enum - BlockChainState::BlockGlobalIndices global_indices; + api::bytecoind::GetRawBlock::Request &&request, api::bytecoind::GetRawBlock::Response &response) { + if (request.hash != Hash{} && request.height_or_depth != std::numeric_limits::max()) + throw json_rpc::Error( + json_rpc::INVALID_REQUEST, "You cannot specify both hash and height_or_depth to this method"); + if (request.hash != Hash{}) { + if (!m_block_chain.read_header(request.hash, &response.block.header)) + throw api::ErrorHashNotFound("Block not found in either main or side chains", request.hash); + } else { + Height height_or_depth = + api::ErrorWrongHeight::fix_height_or_depth(request.height_or_depth, m_block_chain.get_tip_height(), true, true); + invariant( + m_block_chain.read_chain(height_or_depth, &request.hash), ""); // after fix_height it must always succeed + invariant(m_block_chain.read_header(request.hash, &response.block.header), ""); + } + m_block_chain.fix_block_sizes(&response.block.header); + // BlockChainState::BlockGlobalIndices output_indexes; RawBlock rb; invariant(m_block_chain.read_block(request.hash, &rb), "Block must be there, but it is not there"); Block block; invariant(block.from_raw_block(rb), "RawBlock failed to convert into block"); - auto coinbase_size = static_cast(seria::binary_size(block.header.base_transaction)); - response.header.transactions_cumulative_size = response.header.block_size; - - response.header.block_size = response.header.transactions_cumulative_size + static_cast(rb.block.size()) - coinbase_size; - response.base_transaction_hash = get_transaction_hash(block.header.base_transaction); - response.raw_header = std::move(block.header); - response.raw_transactions.reserve(block.transactions.size()); - response.transaction_binary_sizes.reserve(block.transactions.size() + 1); - response.transaction_binary_sizes.push_back(coinbase_size); + api::RawBlock &b = response.block; + b.transactions.resize(block.transactions.size() + 1); + b.transactions.at(0).hash = get_transaction_hash(block.header.base_transaction); + b.transactions.at(0).size = static_cast(seria::binary_size(block.header.base_transaction)); + fill_transaction_info(block.header.base_transaction, &b.transactions.at(0)); + b.transactions.at(0).block_height = b.header.height; + b.transactions.at(0).block_hash = b.header.hash; + b.transactions.at(0).coinbase = true; + b.transactions.at(0).timestamp = block.header.timestamp; + b.raw_header = std::move(block.header); + b.raw_transactions.reserve(block.transactions.size()); for (size_t tx_index = 0; tx_index != block.transactions.size(); ++tx_index) { - response.raw_transactions.push_back(std::move(block.transactions.at(tx_index))); - response.transaction_binary_sizes.push_back( - static_cast(rb.transactions.at(tx_index).size())); - } - m_block_chain.read_block_output_global_indices(request.hash, &response.global_indices); + b.transactions.at(tx_index + 1).hash = b.raw_header.transaction_hashes.at(tx_index); + b.transactions.at(tx_index + 1).size = static_cast(rb.transactions.at(tx_index).size()); + fill_transaction_info(block.transactions.at(tx_index), &b.transactions.at(tx_index + 1)); + b.transactions.at(tx_index + 1).block_height = b.header.height; + b.transactions.at(tx_index + 1).block_hash = b.header.hash; + b.transactions.at(tx_index + 1).timestamp = b.raw_header.timestamp; + if (request.need_signatures) + b.signatures.push_back(std::move(block.transactions.at(tx_index).signatures)); + b.raw_transactions.push_back(std::move(block.transactions.at(tx_index))); + } + m_block_chain.read_block_output_global_indices(request.hash, &b.output_indexes); // If block not in main chain - global indices will be empty + response.orphan_status = !m_block_chain.in_chain(b.header.height, b.header.hash); + response.depth = api::HeightOrDepth(b.header.height) - api::HeightOrDepth(m_block_chain.get_tip_height()) - 1; return true; } -bool Node::on_get_raw_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool Node::on_get_raw_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetRawTransaction::Request &&req, api::bytecoind::GetRawTransaction::Response &res) { const auto &pool = m_block_chain.get_memory_state_transactions(); auto tit = pool.find(req.hash); if (tit != pool.end()) { - res.raw_transaction = static_cast(tit->second.tx); + res.raw_transaction = static_cast(tit->second.tx); + if (req.need_signatures) + res.signatures = tit->second.tx.signatures; + fill_transaction_info(tit->second.tx, &res.transaction); res.transaction.fee = tit->second.fee; res.transaction.hash = req.hash; res.transaction.block_height = m_block_chain.get_tip_height() + 1; res.transaction.timestamp = tit->second.timestamp; - res.transaction.binary_size = static_cast(tit->second.binary_tx.size()); + res.transaction.size = static_cast(tit->second.binary_tx.size()); return true; } + BinaryArray binary_tx; Transaction tx; size_t index_in_block = 0; - if (m_block_chain.read_transaction(req.hash, &tx, &res.transaction.block_height, &res.transaction.block_hash, - &index_in_block, &res.transaction.binary_size)) { - res.raw_transaction = static_cast(tx); // TODO - std::move? + if (m_block_chain.read_transaction( + req.hash, &binary_tx, &res.transaction.block_height, &res.transaction.block_hash, &index_in_block)) { + res.transaction.size = static_cast(binary_tx.size()); + seria::from_binary(tx, binary_tx); + res.raw_transaction = static_cast(tx); // TODO - std::move? + if (req.need_signatures) + res.signatures = tx.signatures; + fill_transaction_info(tx, &res.transaction); res.transaction.hash = req.hash; res.transaction.fee = get_tx_fee(res.raw_transaction); // 0 for coinbase return true; } - return true; + throw api::ErrorHashNotFound( + "Transaction not found in main chain. You cannot get transactions from side chains with this method.", + req.hash); } -bool Node::handle_send_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool Node::handle_send_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::SendTransaction::Request &&request, api::bytecoind::SendTransaction::Response &response) { response.send_result = "broadcast"; @@ -730,11 +701,8 @@ bool Node::handle_send_transaction3(http::Client *, http::RequestData &&, json_r try { seria::from_binary(tx, request.binary_transaction); } catch (const std::exception &ex) { - api::bytecoind::SendTransaction::Error err; - err.code = api::bytecoind::SendTransaction::INVALID_TRANSACTION_BINARY_FORMAT; - err.message = ex.what(); - err.conflict_height = conflict_height; - throw err; + std::throw_with_nested(api::bytecoind::SendTransaction::Error( + api::bytecoind::SendTransaction::INVALID_TRANSACTION_BINARY_FORMAT, common::what(ex), conflict_height)); } const Hash tid = get_transaction_hash(tx); auto action = m_block_chain.add_transaction( @@ -747,7 +715,8 @@ bool Node::handle_send_transaction3(http::Client *, http::RequestData &&, json_r msg.txs.push_back(request.binary_transaction); BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_TRANSACTIONS::ID, LevinProtocol::encode(msg), false); - m_p2p.broadcast(nullptr, raw_msg); + broadcast(nullptr, raw_msg); + // broadcast_new(nullptr, ); // TODO - broadcast transaction advance_long_poll(); break; } @@ -755,48 +724,44 @@ bool Node::handle_send_transaction3(http::Client *, http::RequestData &&, json_r break; case AddTransactionResult::INCREASE_FEE: break; - case AddTransactionResult::FAILED_TO_REDO: { - api::bytecoind::SendTransaction::Error err; - err.code = api::bytecoind::SendTransaction::WRONG_OUTPUT_REFERENCE; - err.message = "Transaction references outputs changed during reorganization or signature wrong"; - err.conflict_height = conflict_height; - throw err; - } - case AddTransactionResult::OUTPUT_ALREADY_SPENT: { - api::bytecoind::SendTransaction::Error err; - err.code = api::bytecoind::SendTransaction::OUTPUT_ALREADY_SPENT; - err.message = "One of referenced outputs is already spent"; - err.conflict_height = conflict_height; - throw err; - } + case AddTransactionResult::FAILED_TO_REDO: + throw api::bytecoind::SendTransaction::Error(api::bytecoind::SendTransaction::WRONG_OUTPUT_REFERENCE, + "Transaction references outputs changed during reorganization or signature wrong", conflict_height); + case AddTransactionResult::OUTPUT_ALREADY_SPENT: + throw api::bytecoind::SendTransaction::Error(api::bytecoind::SendTransaction::OUTPUT_ALREADY_SPENT, + "One of referenced outputs is already spent", conflict_height); } return true; } -bool Node::handle_check_sendproof3(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::bytecoind::CheckSendProof::Request &&request, api::bytecoind::CheckSendProof::Response &response) { - Transaction tx; +bool Node::handle_check_sendproof(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::bytecoind::CheckSendproof::Request &&request, api::bytecoind::CheckSendproof::Response &response) { SendProof sp; try { - seria::from_json_value(sp, common::JsonValue::from_string(request.sendproof)); + seria::from_json_value(sp, common::JsonValue::from_string(request.sendproof), m_block_chain.get_currency()); +// seria::JsonInputStreamValue s(); +// s.begin_object(); +// ser_members(sp, s, ); +// s.end_object(); } catch (const std::exception &ex) { - throw api::bytecoind::CheckSendProof::Error(api::bytecoind::CheckSendProof::FAILED_TO_PARSE, - "Failed to parse proof object ex.what=" + std::string(ex.what())); + std::throw_with_nested(api::bytecoind::CheckSendproof::Error(api::bytecoind::CheckSendproof::FAILED_TO_PARSE, + "Failed to parse proof object ex.what=" + common::what(ex))); } + BinaryArray binary_tx; Height height = 0; Hash block_hash; size_t index_in_block = 0; - uint32_t binary_size = 0; - if (!m_block_chain.read_transaction( - sp.transaction_hash, &tx, &height, &block_hash, &index_in_block, &binary_size)) { - throw api::bytecoind::CheckSendProof::Error( - api::bytecoind::CheckSendProof::NOT_IN_MAIN_CHAIN, "Transaction is not in main chain"); + if (!m_block_chain.read_transaction(sp.transaction_hash, &binary_tx, &height, &block_hash, &index_in_block)) { + throw api::bytecoind::CheckSendproof::Error( + api::bytecoind::CheckSendproof::NOT_IN_MAIN_CHAIN, "Transaction is not in main chain"); } - PublicKey tx_public_key = get_transaction_public_key_from_extra(tx.extra); + Transaction tx; + seria::from_binary(tx, binary_tx); + PublicKey tx_public_key = extra_get_transaction_public_key(tx.extra); Hash message_hash = crypto::cn_fast_hash(sp.message.data(), sp.message.size()); if (!crypto::check_sendproof( tx_public_key, sp.address.view_public_key, sp.derivation, message_hash, sp.signature)) { - throw api::bytecoind::CheckSendProof::Error(api::bytecoind::CheckSendProof::WRONG_SIGNATURE, + throw api::bytecoind::CheckSendproof::Error(api::bytecoind::CheckSendproof::WRONG_SIGNATURE, "Proof object does not match transaction or was tampered with"); } Amount total_amount = 0; @@ -806,7 +771,7 @@ bool Node::handle_check_sendproof3(http::Client *, http::RequestData &&, json_rp if (output.target.type() == typeid(KeyOutput)) { const KeyOutput &key_output = boost::get(output.target); PublicKey spend_key; - if (underive_public_key(sp.derivation, key_index, key_output.key, spend_key) && + if (underive_public_key(sp.derivation, key_index, key_output.public_key, spend_key) && spend_key == sp.address.spend_public_key) { total_amount += output.amount; } @@ -815,10 +780,40 @@ bool Node::handle_check_sendproof3(http::Client *, http::RequestData &&, json_rp ++out_index; } if (total_amount == 0) - throw api::bytecoind::CheckSendProof::Error(api::bytecoind::CheckSendProof::ADDRESS_NOT_IN_TRANSACTION, + throw api::bytecoind::CheckSendproof::Error(api::bytecoind::CheckSendproof::ADDRESS_NOT_IN_TRANSACTION, "No outputs found in transaction for the address being proofed"); if (total_amount != sp.amount) - throw api::bytecoind::CheckSendProof::Error(api::bytecoind::CheckSendProof::WRONG_AMOUNT, + throw api::bytecoind::CheckSendproof::Error(api::bytecoind::CheckSendproof::WRONG_AMOUNT, "Wrong amount in outputs, actual amount is " + common::to_string(total_amount)); + response.transaction_hash = sp.transaction_hash; + response.address = m_block_chain.get_currency().account_address_as_string(sp.address); + response.message = sp.message; + response.amount = sp.amount; + return true; +} + +void Node::submit_block(const BinaryArray &blockblob, api::BlockHeader *info) { + BlockTemplate block_template; + seria::from_binary(block_template, blockblob); + RawBlock raw_block; + // api::BlockHeader info; + auto broad = m_block_chain.add_mined_block(blockblob, &raw_block, info); + if (broad == BroadcastAction::BAN) + throw json_rpc::Error{api::bytecoind::SubmitBlock::BLOCK_NOT_ACCEPTED, "Block not accepted"}; + NOTIFY_NEW_BLOCK::request msg; + msg.b = RawBlockLegacy{raw_block.block, raw_block.transactions}; + msg.hop = 1; + msg.current_blockchain_height = m_block_chain.get_tip_height() + 1; // TODO check + BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_BLOCK::ID, LevinProtocol::encode(msg), false); + broadcast(nullptr, raw_msg); + broadcast_new(nullptr, blockblob); + advance_long_poll(); +} + +bool Node::on_submitblock(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::bytecoind::SubmitBlock::Request &&req, api::bytecoind::SubmitBlock::Response &res) { + submit_block(req.blocktemplate_blob, &res.block_header); + res.orphan_status = !m_block_chain.in_chain(res.block_header.height, res.block_header.hash); + res.depth = api::HeightOrDepth(res.block_header.height) - api::HeightOrDepth(m_block_chain.get_tip_height()) - 1; return true; } diff --git a/src/Core/Node.hpp b/src/Core/Node.hpp index b934763c..e4cf38f2 100644 --- a/src/Core/Node.hpp +++ b/src/Core/Node.hpp @@ -10,64 +10,69 @@ #include #include "BlockChainFileFormat.hpp" #include "BlockChainState.hpp" +#include "http/BinaryRpc.hpp" #include "http/JsonRpc.hpp" #include "http/Server.hpp" #include "p2p/P2P.hpp" #include "p2p/P2PClientBasic.hpp" +#include "p2p/P2PClientNew.hpp" #include "platform/PreventSleep.hpp" #include "rpc_api.hpp" namespace bytecoin { // a bit different commit periods to make most commits not simultaneous -static const float DB_COMMIT_PERIOD_WALLET_CACHE = 290; // 5 minutes sounds good compromise -static const float DB_COMMIT_PERIOD_BYTECOIND = 310; // 5 minutes sounds good compromise -static const float SYNC_TIMEOUT = 20; // If sync does not return, select different sync node after -static const int DOWNLOAD_CONCURRENCY = 4; -static const int DOWNLOAD_QUEUE = 10; // number of block requests sent before receiving reply -static const int DOWNLOAD_BLOCK_WINDOW = DOWNLOAD_CONCURRENCY * DOWNLOAD_QUEUE * 2; -static const float RETRY_DOWNLOAD_SECONDS = 10; +static const float SYNC_TIMEOUT = 20; // If sync does not return, select different sync node after +static const int DOWNLOAD_CONCURRENCY = 4; +static const int DOWNLOAD_QUEUE = 10; // number of block requests sent before receiving reply +static const int DOWNLOAD_BLOCK_WINDOW = DOWNLOAD_CONCURRENCY * DOWNLOAD_QUEUE * 2; +static const float RETRY_DOWNLOAD_SECONDS = 10; class Node { public: - typedef std::function HTTPHandlerFunction; - typedef std::function + typedef std::function JSONRPCHandlerFunction; + typedef std::function + BINARYRPCHandlerFunction; explicit Node(logging::ILogger &, const Config &, BlockChainState &); bool on_idle(); // binary method - bool on_wallet_sync3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool on_sync_blocks(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::SyncBlocks::Request &&, api::bytecoind::SyncBlocks::Response &); - bool on_sync_mempool3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool on_sync_mempool(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::SyncMemPool::Request &&, api::bytecoind::SyncMemPool::Response &); - bool on_get_raw_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::bytecoind::GetRawTransaction::Request &&, api::bytecoind::GetRawTransaction::Response &); + + bool on_get_raw_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::bytecoind::GetRawTransaction::Request &&, api::bytecoind::GetRawTransaction::Response &); bool on_get_raw_block(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::bytecoind::GetRawBlock::Request &&, api::bytecoind::GetRawBlock::Response &); + api::bytecoind::GetRawBlock::Request &&, api::bytecoind::GetRawBlock::Response &); + bool on_get_block_header(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::bytecoind::GetBlockHeader::Request &&, api::bytecoind::GetBlockHeader::Response &); - api::bytecoind::GetStatus::Response create_status_response3() const; + api::bytecoind::GetStatus::Response create_status_response() const; + api::bytecoind::GetStatistics::Response create_statistics_response() const; // json_rpc_node - bool on_get_status3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool on_get_status(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetStatus::Request &&, api::bytecoind::GetStatus::Response &); bool on_get_statistics(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetStatistics::Request &&, api::bytecoind::GetStatistics::Response &); bool on_get_archive(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetArchive::Request &&, api::bytecoind::GetArchive::Response &); - bool on_get_random_outputs3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool on_get_random_outputs(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetRandomOutputs::Request &&, api::bytecoind::GetRandomOutputs::Response &); - bool handle_send_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_send_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::SendTransaction::Request &&, api::bytecoind::SendTransaction::Response &); - bool handle_check_sendproof3(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::bytecoind::CheckSendProof::Request &&, api::bytecoind::CheckSendProof::Response &); + bool handle_check_sendproof(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::bytecoind::CheckSendproof::Request &&, api::bytecoind::CheckSendproof::Response &); bool on_getblocktemplate(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetBlockTemplate::Request &&r, api::bytecoind::GetBlockTemplate::Response &); void getblocktemplate( const api::bytecoind::GetBlockTemplate::Request &, api::bytecoind::GetBlockTemplate::Response &); bool on_get_currency_id(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetCurrencyId::Request &&, api::bytecoind::GetCurrencyId::Response &); + void submit_block(const BinaryArray &blockblob, api::BlockHeader *info); bool on_submitblock(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::SubmitBlock::Request &&, api::bytecoind::SubmitBlock::Response &); bool on_submitblock_legacy(http::Client *, http::RequestData &&, json_rpc::Request &&, @@ -80,7 +85,8 @@ class Node { api::bytecoind::GetBlockHeaderByHeightLegacy::Request &&, api::bytecoind::GetBlockHeaderByHeightLegacy::Response &); - bool process_json_rpc_request(http::Client *, http::RequestData &&, http::ResponseData &); + bool on_json_rpc(http::Client *, http::RequestData &&, http::ResponseData &); + bool on_binary_rpc(http::Client *, http::RequestData &&, http::ResponseData &); BlockChainState &m_block_chain; const Config &m_config; @@ -104,19 +110,22 @@ class Node { logging::LoggerRef m_log; PeerDB m_peer_db; P2P m_p2p; + platform::UDPMulticast multicast; + platform::Timer m_multicast_timer; + void send_multicast(); + void on_multicast(const std::string &addr, const unsigned char *data, size_t size); + const Timestamp m_start_time; platform::Timer m_commit_timer; std::unique_ptr prevent_sleep; - void db_commit() { - m_block_chain.db_commit(); - m_commit_timer.once(DB_COMMIT_PERIOD_BYTECOIND); - } + void db_commit(); - bool check_trust(const proof_of_trust &); - uint64_t m_last_stat_request_time = 0; + bool check_trust(const np::ProofOfTrust &); + bool check_trust(const ProofOfTrustLegacy &); + uint64_t m_last_stat_request_time = 0; // TODO - Timestamp type after getting rid of old p2p // Prevent replay attacks by only trusting requests with timestamp > than previous request - class P2PClientBytecoin : public P2PClientBasic { + class P2PProtocolBytecoin : public P2PProtocolBasic { Node *const m_node; void after_handshake(); @@ -125,8 +134,9 @@ class Node { virtual void on_msg_bytes(size_t downloaded, size_t uploaded) override; virtual CORE_SYNC_DATA get_sync_data() const override; - virtual std::vector get_peers_to_share() const override; + virtual std::vector get_peers_to_share() const override; + virtual void on_immediate_protocol_switch(unsigned char first_byte) override; virtual void on_first_message_after_handshake() override; virtual void on_msg_handshake(COMMAND_HANDSHAKE::request &&) override; virtual void on_msg_handshake(COMMAND_HANDSHAKE::response &&) override; @@ -145,23 +155,58 @@ class Node { virtual void on_msg_stat_info(COMMAND_REQUEST_STAT_INFO::request &&) override; #endif public: - explicit P2PClientBytecoin(Node *node, bool incoming, D_handler d_handler) - : P2PClientBasic(node->m_config, node->m_p2p.get_unique_number(), incoming, d_handler), m_node(node) {} + explicit P2PProtocolBytecoin(Node *node, P2PClient *client) + : P2PProtocolBasic(node->m_config, node->m_p2p.get_unique_number(), client), m_node(node) {} + ~P2PProtocolBytecoin(); Node *get_node() const { return m_node; } }; - std::unique_ptr client_factory(bool incoming, P2PClient::D_handler d_handler) { - return std::make_unique(this, incoming, d_handler); + std::unique_ptr client_factory(P2PClient *client) { + return std::make_unique(this, client); } + class P2PProtocolBytecoinNew : public P2PProtocolNew { + Node *const m_node; + void after_handshake(); + + protected: + void on_disconnect(const std::string &ban_reason) override; + + void on_msg_bytes(size_t, size_t) override; + void on_msg_handshake(np::Handshake::Request &&req) override; + void on_msg_handshake(np::Handshake::Response &&req) override; + void on_msg_find_diff(np::FindDiff::Request &&) override; + void on_msg_find_diff(np::FindDiff::Response &&) override; + void on_msg_sync_headers(np::SyncHeaders::Request &&) override; + void on_msg_sync_headers(np::SyncHeaders::Response &&) override; + void on_msg_get_transactions(np::GetTransactions::Request &&) override; + void on_msg_get_transactions(np::GetTransactions::Response &&) override; + void on_msg_get_pool_hashes(np::GetPoolHashes::Request &&) override; + void on_msg_get_pool_hashes(np::GetPoolHashes::Response &&) override; + void on_msg_relay_block_header(np::RelayBlockHeader &&) override; + void on_msg_relay_transaction_desc(np::RelayTransactionDescs &&) override; +#if bytecoin_ALLOW_DEBUG_COMMANDS + void on_msg_get_peer_statistics(np::GetPeerStatistics::Request &&) override; +#endif + void on_first_message_after_handshake() override; + np::TopBlockDesc get_top_block_desc() const override; + std::vector get_peers_to_share() const override; + + public: + explicit P2PProtocolBytecoinNew(Node *node, P2PClient *client) + : P2PProtocolNew( + node->m_config, node->m_block_chain.get_currency(), node->m_p2p.get_unique_number(), client) + , m_node(node) {} + Node *get_node() const { return m_node; } + }; class DownloaderV11 { // torrent-style sync&download from legacy v1 clients Node *const m_node; BlockChainState &m_block_chain; - std::map m_good_clients; // -> # of downloading blocks + std::map m_good_clients; // -> # of downloading blocks size_t total_downloading_blocks = 0; - std::list m_who_downloaded_block; - P2PClientBytecoin *m_chain_client = nullptr; - bool m_chain_request_sent = false; + std::list m_who_downloaded_block; + P2PProtocolBytecoin *m_chain_client = nullptr; + bool m_chain_request_sent = false; platform::Timer m_chain_timer; // If m_chain_client does not respond for long, disconnect it struct DownloadCell { @@ -169,7 +214,7 @@ class Node { Height expected_height = 0; NetworkAddress bid_source; // for banning culprit in case of a problem NetworkAddress block_source; // for banning culprit in case of a problem - P2PClientBytecoin *downloading_client = nullptr; + P2PProtocolBytecoin *downloading_client = nullptr; std::chrono::steady_clock::time_point request_time; RawBlock rb; enum Status { DOWNLOADING, DOWNLOADED, PREPARING, PREPARED } status = DOWNLOADING; @@ -197,7 +242,7 @@ class Node { void add_work(std::tuple &&wo); void thread_run(); - void start_download(DownloadCell &dc, P2PClientBytecoin *who); + void start_download(DownloadCell &dc, P2PProtocolBytecoin *who); void stop_download(DownloadCell &dc, bool success); void on_chain_timer(); void on_download_timer(); @@ -211,22 +256,94 @@ class Node { bool on_idle(); uint32_t get_known_block_count(uint32_t my) const; - void on_connect(P2PClientBytecoin *); - void on_disconnect(P2PClientBytecoin *); - const std::map &get_good_clients() const { return m_good_clients; } - void on_msg_notify_request_chain(P2PClientBytecoin *, const NOTIFY_RESPONSE_CHAIN_ENTRY::request &); - void on_msg_notify_request_objects(P2PClientBytecoin *, const NOTIFY_RESPONSE_GET_OBJECTS::request &); + void on_connect(P2PProtocolBytecoin *); + void on_disconnect(P2PProtocolBytecoin *); + const std::map &get_good_clients() const { return m_good_clients; } + void on_msg_notify_request_chain(P2PProtocolBytecoin *, const NOTIFY_RESPONSE_CHAIN_ENTRY::request &); + void on_msg_notify_request_objects(P2PProtocolBytecoin *, const NOTIFY_RESPONSE_GET_OBJECTS::request &); + void on_msg_timed_sync(const CORE_SYNC_DATA &payload_data); + }; + class DownloaderV3 { // torrent-style sync&download from new v3 clients + Node *const m_node; + BlockChainState &m_block_chain; + + std::map m_good_clients; // -> # of downloading blocks + size_t total_downloading_blocks = 0; + std::list m_who_downloaded_block; + P2PProtocolBytecoinNew *m_find_diff_client = nullptr; + int m_find_diff_iteration = 0; + Hash m_find_diff_bid; + P2PProtocolBytecoinNew *m_sync_headers_client = nullptr; + Hash m_sync_headers_previous_block_hash; + platform::Timer m_chain_timer; // If m_chain_client does not respond for long, disconnect it + + struct DownloadCell { + Hash bid; + Height expected_height = 0; + NetworkAddress block_source; // for banning culprit in case of a problem + P2PProtocolBytecoinNew *downloading_client = nullptr; + std::chrono::steady_clock::time_point request_time; + RawBlock rb; + enum Status { DOWNLOADING, DOWNLOADED, PREPARING, PREPARED } status = DOWNLOADING; + bool protect_from_disconnect = false; + PreparedBlock pb; + }; + std::deque + m_download_chain; // ~20-1000 of blocks we wish to have downloading (depending on current median size) + platform::Timer m_download_timer; + std::chrono::steady_clock::time_point log_request_timestamp; + std::chrono::steady_clock::time_point log_response_timestamp; + + // multicore preparator + std::vector threads; + std::mutex mu; + std::map prepared_blocks; + std::deque> work; + std::condition_variable have_work; + platform::EventLoop *main_loop = nullptr; + bool quit = false; + void add_work(std::tuple &&wo); + void thread_run(); + + void start_download(DownloadCell &dc, P2PProtocolBytecoinNew *who); + void stop_download(DownloadCell &dc, bool success); + void on_chain_timer(); + void on_download_timer(); + void advance_chain(); + + public: + DownloaderV3(Node *node, BlockChainState &block_chain); + ~DownloaderV3(); + + void advance_download(); + bool on_idle(); + + uint32_t get_known_block_count(uint32_t my) const; + void on_connect(P2PProtocolBytecoinNew *); + void on_disconnect(P2PProtocolBytecoinNew *); + const std::map &get_good_clients() const { return m_good_clients; } + + void on_msg_find_diff(P2PProtocolBytecoinNew *, np::FindDiff::Response &&resp); + void on_msg_sync_headers(P2PProtocolBytecoinNew *, np::SyncHeaders::Response &&resp); + void on_msg_get_transactions(P2PProtocolBytecoinNew *, np::GetTransactions::Response &&resp); }; + std::set broadcast_protocols; + void broadcast(P2PProtocolBytecoin *exclude, const BinaryArray &data); + std::set broadcast_protocols_new; + void broadcast_new(P2PProtocolBytecoinNew *exclude, const BinaryArray &binary_header); + void broadcast_new(P2PProtocolBytecoinNew *exclude, const std::vector &transaction_descs); DownloaderV11 m_downloader; + DownloaderV3 m_downloader_v3; bool on_api_http_request(http::Client *, http::RequestData &&, http::ResponseData &); void on_api_http_disconnect(http::Client *); - void sync_transactions(P2PClientBytecoin *); + void sync_transactions(P2PProtocolBytecoin *); + void sync_transactions(P2PProtocolBytecoinNew *) {} - static const std::unordered_map m_http_handlers; static std::unordered_map m_jsonrpc_handlers; + static const std::unordered_map m_binaryrpc_handlers; }; } // namespace bytecoin diff --git a/src/Core/NodeDownloader.cpp b/src/Core/NodeDownloader.cpp index 3043f6eb..6cbd852f 100644 --- a/src/Core/NodeDownloader.cpp +++ b/src/Core/NodeDownloader.cpp @@ -3,6 +3,7 @@ #include #include "Config.hpp" +#include "CryptoNoteTools.hpp" #include "Node.hpp" #include "seria/BinaryInputStream.hpp" #include "seria/BinaryOutputStream.hpp" @@ -62,7 +63,9 @@ void Node::DownloaderV11::thread_run() { wo = std::move(work.front()); work.pop_front(); } - PreparedBlock result(std::move(std::get<2>(wo)), std::get<1>(wo) ? &hash_crypto_context : nullptr); + PreparedBlock result(std::move(std::get<2>(wo)), + m_node->m_block_chain.get_currency(), + std::get<1>(wo) ? &hash_crypto_context : nullptr); { std::unique_lock lock(mu); prepared_blocks[std::get<0>(wo)] = std::move(result); @@ -77,26 +80,26 @@ uint32_t Node::DownloaderV11::get_known_block_count(uint32_t my) const { return my; } -void Node::DownloaderV11::on_connect(P2PClientBytecoin *who) { +void Node::DownloaderV11::on_connect(P2PProtocolBytecoin *who) { if (who->is_incoming()) // Never sync from incoming return; m_node->m_log(logging::TRACE) << "DownloaderV11::on_connect " << who->get_address() << std::endl; - if (who->get_version() == 1) { - m_good_clients.insert(std::make_pair(who, 0)); - if (who->get_last_received_sync_data().current_height == m_block_chain.get_tip_height()) { - m_node->m_log(logging::TRACE) - << "DownloaderV11::on_connect sync_transactions to " << who->get_address() - << " our pool size=" << m_node->m_block_chain.get_memory_state_transactions().size() << std::endl; - m_node->sync_transactions(who); - // If we at same height, sync tx now, otherwise will sync after we reach same height - } - advance_download(); + invariant(m_good_clients.insert(std::make_pair(who, 0)).second, ""); + if (who->get_last_received_sync_data().current_height == m_block_chain.get_tip_height()) { + m_node->m_log(logging::TRACE) << "DownloaderV11::on_connect sync_transactions to " << who->get_address() + << " our pool size=" + << m_node->m_block_chain.get_memory_state_transactions().size() << std::endl; + m_node->sync_transactions(who); + // If we at same height, sync tx now, otherwise will sync after we reach same height } + advance_download(); } -void Node::DownloaderV11::on_disconnect(P2PClientBytecoin *who) { +void Node::DownloaderV11::on_disconnect(P2PProtocolBytecoin *who) { if (who->is_incoming()) return; + if (m_good_clients.count(who) == 0) // Remove only if we have it added + return; m_node->m_log(logging::TRACE) << "DownloaderV11::on_disconnect " << who->get_address() << std::endl; invariant(total_downloading_blocks >= m_good_clients[who], "total_downloading_blocks mismatch in disconnect"); total_downloading_blocks -= m_good_clients[who]; @@ -112,7 +115,7 @@ void Node::DownloaderV11::on_disconnect(P2PClientBytecoin *who) { } if (m_chain_client && m_chain_client == who) { m_chain_timer.cancel(); - m_chain_client = nullptr; + m_chain_client = nullptr; m_chain_request_sent = false; m_node->m_log(logging::TRACE) << "DownloaderV11::on_disconnect m_chain_client reset to 0" << std::endl; } @@ -126,13 +129,13 @@ void Node::DownloaderV11::on_chain_timer() { } } -void Node::DownloaderV11::on_msg_notify_request_chain(P2PClientBytecoin *who, +void Node::DownloaderV11::on_msg_notify_request_chain(P2PProtocolBytecoin *who, const NOTIFY_RESPONSE_CHAIN_ENTRY::request &req) { if (m_chain_client != who || !m_chain_request_sent) return; // TODO - who just sent us chain we did not ask, ban m_chain_request_sent = false; m_chain_timer.cancel(); - m_node->m_log(logging::INFO) << "Downloader received chain from " << who->get_address() + m_node->m_log(logging::INFO) << "DownloaderV11 received chain from " << who->get_address() << " start_height=" << req.start_height << " length=" << req.m_block_ids.size() << std::endl; m_chain_start_height = req.start_height; @@ -148,13 +151,15 @@ void Node::DownloaderV11::on_msg_notify_request_chain(P2PClientBytecoin *who, m_chain_start_height += 1; } // We stop removing as soon as we find new block, because wrong order might prevent us from applying blocks if (req.m_block_ids.size() != m_chain.size() + 1) { - m_node->m_log(logging::INFO) << "Downloader truncated chain length=" << m_chain.size() << std::endl; + m_node->m_log(logging::INFO) << "DownloaderV11 truncated chain length=" << m_chain.size() << std::endl; } - if (req.m_block_ids.empty()){ // Most likely peer is 3.2.0 + if (req.m_block_ids.empty()) { // Most likely peer is 3.2.0 const auto now = m_node->m_p2p.get_local_time(); - m_node->m_log(logging::INFO) << "Downloader truncated chain to zero, delaying connect to " << who->get_address() << std::endl; + m_node->m_log(logging::INFO) << "DownloaderV11 truncated chain to zero, delaying connect to " + << who->get_address() << std::endl; m_node->m_peer_db.delay_connection_attempt(who->get_address(), now); who->disconnect(std::string()); // Will recursively call advance_chain again + return; } advance_download(); } @@ -165,8 +170,8 @@ void Node::DownloaderV11::advance_chain() { if (!m_chain.empty() || !m_download_chain.empty() || m_chain_request_sent) return; m_chain_client = nullptr; - std::vector lagging_clients; - std::vector worth_clients; + std::vector lagging_clients; + std::vector worth_clients; const auto now = m_node->m_p2p.get_local_time(); for (auto &&who : m_good_clients) { if (who.first->get_last_received_sync_data().current_height + GOOD_LAG < m_node->m_block_chain.get_tip_height()) @@ -175,10 +180,11 @@ void Node::DownloaderV11::advance_chain() { if (!m_node->m_block_chain.read_header(who.first->get_last_received_sync_data().top_id, &info)) worth_clients.push_back(who.first); } - if (lagging_clients.size() > m_node->m_config.p2p_default_connections_count / 4) { + if (lagging_clients.size() > m_node->m_config.p2p_max_outgoing_connections / 4) { auto who = lagging_clients.front(); m_node->m_peer_db.delay_connection_attempt(who->get_address(), now); - m_node->m_log(logging::INFO) << "Downloader disconnecting lagging client " << who->get_address() << std::endl; + m_node->m_log(logging::INFO) << "DownloaderV11 disconnecting lagging client " << who->get_address() + << std::endl; who->disconnect(std::string()); // Will recursively call advance_chain again return; } @@ -198,7 +204,7 @@ void Node::DownloaderV11::advance_chain() { m_chain_timer.once(SYNC_TIMEOUT); } -void Node::DownloaderV11::start_download(DownloadCell &dc, P2PClientBytecoin *who) { +void Node::DownloaderV11::start_download(DownloadCell &dc, P2PProtocolBytecoin *who) { auto idea_now = std::chrono::steady_clock::now(); dc.downloading_client = who; dc.block_source = who->get_address(); @@ -235,17 +241,19 @@ void Node::DownloaderV11::stop_download(DownloadCell &dc, bool success) { dc.downloading_client = nullptr; } -void Node::DownloaderV11::on_msg_notify_request_objects(P2PClientBytecoin *who, +void Node::DownloaderV11::on_msg_notify_request_objects(P2PProtocolBytecoin *who, const NOTIFY_RESPONSE_GET_OBJECTS::request &req) { for (auto &&rb : req.blocks) { Hash bid; try { BlockTemplate bheader; seria::from_binary(bheader, rb.block); - bid = bytecoin::get_block_hash(bheader); + auto body_proxy = get_body_proxy_from_template(bheader); + bid = bytecoin::get_block_hash(bheader, body_proxy); } catch (const std::exception &ex) { - m_node->m_log(logging::INFO) << "Exception " << ex.what() << " while parsing returned block, banning " - << who->get_address() << std::endl; + m_node->m_log(logging::INFO) << "Exception " << common::what(ex) + << " while parsing returned block, banning " << who->get_address() + << std::endl; who->disconnect(std::string()); break; } @@ -263,7 +271,7 @@ void Node::DownloaderV11::on_msg_notify_request_objects(P2PClientBytecoin *who, << " (queue=" << total_downloading_blocks << ") from " << who->get_address() << std::endl; } m_node->m_log(logging::TRACE) - << "Downloader received block with height=" << dc.expected_height << " hash=" << dc.bid + << "DownloaderV11 received block with height=" << dc.expected_height << " hash=" << dc.bid << " (queue=" << total_downloading_blocks << ") from " << who->get_address() << std::endl; cell_found = true; if (multicore) { @@ -272,26 +280,27 @@ void Node::DownloaderV11::on_msg_notify_request_objects(P2PClientBytecoin *who, !m_node->m_block_chain.get_currency().is_in_sw_checkpoint_zone(dc.expected_height), std::move(dc.rb))); } else { - dc.pb = PreparedBlock(std::move(dc.rb), nullptr); + dc.pb = PreparedBlock(std::move(dc.rb), m_node->m_block_chain.get_currency(), nullptr); dc.status = DownloadCell::PREPARED; } break; } if (!cell_found) { - m_node->m_log(logging::INFO) << "Downloader received stray block from " << who->get_address() << std::endl; + m_node->m_log(logging::INFO) << "DownloaderV11 received stray block from " << who->get_address() + << std::endl; // who->disconnect(std::string()); // break; } } for (auto &&bid : req.missed_ids) { for (size_t dit_counter = 0; dit_counter != m_download_chain.size(); ++dit_counter) { - auto & dit = m_download_chain.at(dit_counter); + auto &dit = m_download_chain.at(dit_counter); if (dit.status != DownloadCell::DOWNLOADING || dit.downloading_client != who || dit.bid != bid) continue; // downloaded or downloading stop_download(dit, false); if (!m_chain_client || m_chain_client == who) { m_node->m_log(logging::INFO) - << "Downloader cannot download block from any connected client, cleaning chain" << std::endl; + << "DownloaderV11 cannot download block from any connected client, cleaning chain" << std::endl; while (m_download_chain.size() > dit_counter) { stop_download(m_download_chain.back(), false); m_download_chain.pop_back(); @@ -328,7 +337,7 @@ bool Node::DownloaderV11::on_idle() { auto action = m_block_chain.add_block( dc.pb, &info, common::ip_address_and_port_to_string(dc.block_source.ip, dc.block_source.port)); if (action == BroadcastAction::BAN) { - m_node->m_log(logging::INFO) << "Downloader DownloadCell BAN height=" << dc.expected_height + m_node->m_log(logging::INFO) << "DownloaderV11 DownloadCell BAN height=" << dc.expected_height << " wb=" << dc.bid << std::endl; // TODO - ban client who gave us chain // continue; @@ -342,13 +351,17 @@ bool Node::DownloaderV11::on_idle() { // << " cd=" << info.cumulative_difficulty.lo << std::endl; if (m_download_chain.empty()) { // We do not want to broadcast too often during download + m_node->m_log(logging::INFO) << "Added last (from batch) downloaded block height=" << info.height + << " bid=" << info.hash << std::endl; COMMAND_TIMED_SYNC::request req; req.payload_data = CORE_SYNC_DATA{m_node->m_block_chain.get_tip_height(), m_node->m_block_chain.get_tip_bid()}; BinaryArray raw_msg = LevinProtocol::send_message(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(req), true); - m_node->m_p2p.broadcast( + m_node->broadcast( nullptr, raw_msg); // nullptr - we can not always know which connection was block source + // m_node->broadcast_new(nullptr, raw_msg); // TODO nullptr - we can not always know which + // connection was block source } } added_counter += 1; @@ -385,7 +398,7 @@ void Node::DownloaderV11::on_download_timer() { SYNC_TIMEOUT) { auto who = m_download_chain.front().downloading_client; m_node->m_peer_db.delay_connection_attempt(who->get_address(), m_node->m_p2p.get_local_time()); - m_node->m_log(logging::INFO) << "Downloader disconnecting protected slacker " << who->get_address() + m_node->m_log(logging::INFO) << "DownloaderV11 disconnecting protected slacker " << who->get_address() << std::endl; who->disconnect(std::string()); } @@ -409,19 +422,19 @@ void Node::DownloaderV11::advance_download() { while (m_who_downloaded_block.size() > TOTAL_DOWNLOAD_BLOCKS) m_who_downloaded_block.pop_front(); - std::map who_downloaded_counter; + std::map who_downloaded_counter; for (auto lit = m_who_downloaded_block.begin(); lit != m_who_downloaded_block.end(); ++lit) who_downloaded_counter[*lit] += 1; auto idea_now = std::chrono::steady_clock::now(); for (size_t dit_counter = 0; dit_counter != m_download_chain.size(); ++dit_counter) { - auto & dit = m_download_chain.at(dit_counter); + auto &dit = m_download_chain.at(dit_counter); if (dit.status != DownloadCell::DOWNLOADING || dit.downloading_client) continue; // downloaded or downloading if (total_downloading_blocks >= TOTAL_DOWNLOAD_BLOCKS) break; - P2PClientBytecoin *ready_client = nullptr; - size_t ready_counter = std::numeric_limits::max(); - size_t ready_speed = 1; + P2PProtocolBytecoin *ready_client = nullptr; + size_t ready_counter = std::numeric_limits::max(); + size_t ready_speed = 1; for (auto &&who : m_good_clients) { size_t speed = std::max(1, std::min(TOTAL_DOWNLOAD_BLOCKS / 4, who_downloaded_counter[who.first])); diff --git a/src/Core/NodeDownloaderV3.cpp b/src/Core/NodeDownloaderV3.cpp new file mode 100644 index 00000000..7012f4ec --- /dev/null +++ b/src/Core/NodeDownloaderV3.cpp @@ -0,0 +1,613 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include "Config.hpp" +#include "Node.hpp" +#include "seria/BinaryInputStream.hpp" +#include "seria/BinaryOutputStream.hpp" + +using namespace bytecoin; + +static const bool multicore = true; + +Node::DownloaderV3::DownloaderV3(Node *node, BlockChainState &block_chain) + : m_node(node) + , m_block_chain(block_chain) + , m_chain_timer(std::bind(&DownloaderV3::on_chain_timer, this)) + , m_download_timer(std::bind(&DownloaderV3::on_download_timer, this)) + , log_request_timestamp(std::chrono::steady_clock::now()) + , log_response_timestamp(std::chrono::steady_clock::now()) { + if (multicore) { + auto th_count = std::max(2, std::thread::hardware_concurrency() / 2); + // we use more energy but have the same speed when using hyperthreading + // std::cout << "Starting multicore POW checker using " << th_count << "/" << + // std::thread::hardware_concurrency() + // << " cpus" << std::endl; + for (size_t i = 0; i != th_count; ++i) + threads.emplace_back(&DownloaderV3::thread_run, this); + main_loop = platform::EventLoop::current(); + } + m_download_timer.once(SYNC_TIMEOUT / 8); // just several ticks per SYNC_TIMEOUT +} + +Node::DownloaderV3::~DownloaderV3() { + { + std::unique_lock lock(mu); + quit = true; + have_work.notify_all(); + } + for (auto &&th : threads) + th.join(); +} + +void Node::DownloaderV3::add_work(std::tuple &&wo) { + std::unique_lock lock(mu); + work.push_back(std::move(wo)); + have_work.notify_all(); +} + +void Node::DownloaderV3::thread_run() { + crypto::CryptoNightContext hash_crypto_context; + while (true) { + std::tuple wo; + { + std::unique_lock lock(mu); + if (quit) + return; + if (work.empty()) { + have_work.wait(lock); + continue; + } + wo = std::move(work.front()); + work.pop_front(); + } + PreparedBlock result(std::move(std::get<2>(wo)), + m_node->m_block_chain.get_currency(), + std::get<1>(wo) ? &hash_crypto_context : nullptr); + { + std::unique_lock lock(mu); + prepared_blocks[std::get<0>(wo)] = std::move(result); + main_loop->wake(); // so we start processing on_idle + } + } +} + +uint32_t Node::DownloaderV3::get_known_block_count(uint32_t my) const { + for (auto &&gc : m_good_clients) + my = std::max(my, gc.first->get_other_top_block_desc().height); + return my; +} + +void Node::DownloaderV3::on_connect(P2PProtocolBytecoinNew *who) { + if (who->is_incoming()) // Never sync from incoming + return; + m_node->m_log(logging::TRACE) << "DownloaderV3::on_connect " << who->get_address() << std::endl; + invariant(m_good_clients.insert(std::make_pair(who, 0)).second, ""); + // compare height, not hashes. This syncs most good transactions between short splits + if (who->get_other_top_block_desc().height == m_block_chain.get_tip_height()) { + m_node->m_log(logging::TRACE) << "DownloaderV3::on_connect sync_transactions to " << who->get_address() + << " our pool size=" + << m_node->m_block_chain.get_memory_state_transactions().size() << std::endl; + m_node->sync_transactions(who); + // If we at same height, sync tx now, otherwise will sync after we reach same height + } + advance_download(); +} + +void Node::DownloaderV3::on_disconnect(P2PProtocolBytecoinNew *who) { + if (who->is_incoming()) + return; + if (m_good_clients.count(who) == 0) // Remove only if we have it added + return; + m_node->m_log(logging::TRACE) << "DownloaderV3::on_disconnect " << who->get_address() << std::endl; + invariant(total_downloading_blocks >= m_good_clients[who], "total_downloading_blocks mismatch in disconnect"); + total_downloading_blocks -= m_good_clients[who]; + m_good_clients.erase(who); + for (auto lit = m_who_downloaded_block.begin(); lit != m_who_downloaded_block.end();) + if (*lit == who) + lit = m_who_downloaded_block.erase(lit); + else + ++lit; + for (auto &&dc : m_download_chain) { + if (dc.status == DownloadCell::DOWNLOADING && dc.downloading_client == who) + dc.downloading_client = nullptr; + } + if (m_find_diff_client && m_find_diff_client == who) { + m_chain_timer.cancel(); + m_find_diff_client = nullptr; + m_node->m_log(logging::TRACE) << "DownloaderV3::on_disconnect find_diff_client reset to 0" << std::endl; + } + if (m_sync_headers_client && m_sync_headers_client == who) { + m_chain_timer.cancel(); + m_sync_headers_client = nullptr; + m_node->m_log(logging::TRACE) << "DownloaderV3::on_disconnect sync_headers_client reset to 0" << std::endl; + } + advance_download(); +} + +void Node::DownloaderV3::on_chain_timer() { + if (m_find_diff_client) { + m_node->m_log(logging::TRACE) << "DownloaderV3::on_chain_timer find_diff_client disconnect" << std::endl; + m_find_diff_client->disconnect(std::string()); + return; + } + if (m_sync_headers_client) { + m_node->m_log(logging::TRACE) << "DownloaderV3::on_chain_timer m_sync_headers_client disconnect" << std::endl; + m_sync_headers_client->disconnect(std::string()); + return; + } +} + +void Node::DownloaderV3::on_msg_find_diff(P2PProtocolBytecoinNew *who, np::FindDiff::Response &&resp) { + m_chain_timer.cancel(); + if (resp.sparse_chain.size() > np::FindDiff::Response::MAX_SPARSE_CHAIN_LENGTH) + return who->disconnect("MAX_SPARSE_CHAIN_LENGTH violation"); + if (who != m_find_diff_client) + return who->disconnect("Stray FindDiff Response"); + api::BlockHeader desired_header; + api::BlockHeader have_header; + if (resp.sparse_chain.size() < 2) + return who->disconnect("FindDiff sparse_chain length < 2"); + if (resp.sparse_chain.at(0).hash != m_find_diff_bid) + return who->disconnect("FindDiff sparse_chain does not start with desired_bid"); + if (m_node->m_block_chain.read_header(resp.sparse_chain.at(0).hash, &desired_header)) { + m_node->m_log(logging::INFO) << "DownloaderV3::on_msg_find_diff already have desired_bid from " + << m_sync_headers_client->get_address() << " m_find_diff_bid=" << m_find_diff_bid + << " remote height=" << m_sync_headers_client->get_other_top_block_desc().height + << " our height=" << m_block_chain.get_tip_height() << std::endl; + // While we were searching for diff, we got the desired header already + m_find_diff_client = nullptr; + advance_chain(); + return; + } + SWCheckpoint desired_sw = resp.sparse_chain.at(0); + resp.sparse_chain.erase(resp.sparse_chain.begin()); + if (!m_node->m_block_chain.read_header(resp.sparse_chain.back().hash, &have_header)) + return who->disconnect("FindDiff sparse_chain does not contain any bid we have"); + SWCheckpoint have_sw = resp.sparse_chain.back(); + resp.sparse_chain.pop_back(); + while (!resp.sparse_chain.empty() && + !m_node->m_block_chain.read_header(resp.sparse_chain.at(0).hash, &desired_header)) { + desired_sw = resp.sparse_chain.at(0); + resp.sparse_chain.erase(resp.sparse_chain.begin()); + } + while ( + !resp.sparse_chain.empty() && m_node->m_block_chain.read_header(resp.sparse_chain.back().hash, &have_header)) { + have_sw = resp.sparse_chain.back(); + resp.sparse_chain.pop_back(); + } + if (!resp.sparse_chain.empty()) + return who->disconnect("FindDiff sparse_chain with wrong order"); + if (desired_sw.height == have_sw.height + 1) { // Found! + m_sync_headers_client = m_find_diff_client; + m_find_diff_client = nullptr; + m_sync_headers_previous_block_hash = have_sw.hash; + np::SyncHeaders::Request fd; + fd.previous_hash = m_sync_headers_previous_block_hash; + fd.max_count = np::SyncHeaders::Request::GOOD_COUNT; + BinaryArray msg = seria::to_binary_kv(fd); + m_find_diff_client->send(P2PProtocolBytecoinNew::create_header(np::SyncHeaders::Request::ID, msg.size())); + m_find_diff_client->send(std::move(msg)); + m_node->m_log(logging::INFO) << "DownloaderV3::advance_chain SyncHeaders::Request from " + << m_sync_headers_client->get_address() + << " remote height=" << m_sync_headers_client->get_other_top_block_desc().height + << " our height=" << m_block_chain.get_tip_height() << std::endl; + m_chain_timer.once(SYNC_TIMEOUT); + return; + } + m_find_diff_iteration += 1; + np::FindDiff::Request fd; + fd.gap_start.push_back(have_sw.hash); + fd.desired_bid = m_find_diff_bid; + BinaryArray msg = seria::to_binary_kv(fd); + m_find_diff_client->send(P2PProtocolBytecoinNew::create_header(np::FindDiff::Request::ID, msg.size())); + m_find_diff_client->send(std::move(msg)); + m_node->m_log(logging::INFO) << "DownloaderV3::advance_chain next FindDiff::Request from " + << m_find_diff_client->get_address() + << " remote height=" << m_find_diff_client->get_other_top_block_desc().height + << " our height=" << m_block_chain.get_tip_height() << std::endl; + m_chain_timer.once(SYNC_TIMEOUT); +} +void Node::DownloaderV3::on_msg_sync_headers(P2PProtocolBytecoinNew *who, np::SyncHeaders::Response &&resp) { + m_chain_timer.cancel(); + if (resp.binary_headers.size() > np::SyncHeaders::Request::GOOD_COUNT) + return who->disconnect("SyncHeaders binary_headers too much headers returned"); + if (resp.binary_headers.size() == 0) { // Peer switched chain + m_node->m_log(logging::INFO) << "DownloaderV3::on_msg_sync_headers peer switched chains " + << m_sync_headers_client->get_address() + << " m_sync_headers_previous_block_hash=" << m_sync_headers_previous_block_hash + << " remote height=" << m_sync_headers_client->get_other_top_block_desc().height + << " our height=" << m_block_chain.get_tip_height() << std::endl; + m_sync_headers_client = nullptr; + advance_chain(); + return; + } + for (const auto &bh : resp.binary_headers) { + BlockTemplate block_header; + seria::from_binary(block_header, bh); + api::BlockHeader info; + // if (m_block_chain.add_header(block_header, &info) == BroadcastAction::BAN) + // return who->disconnect("SyncHeaders Response header banned"); + if (info.previous_block_hash != m_sync_headers_previous_block_hash) + return who->disconnect("SyncHeaders Response binary_headers do not form chain with requested start"); + m_sync_headers_previous_block_hash = info.previous_block_hash; + } + // TODO - increase probability if reply takes too long + size_t barrier = crypto::rand() % (np::SyncHeaders::Request::GOOD_COUNT * 110 / 100); + if (resp.binary_headers.size() <= barrier) { // switch + m_node->m_log(logging::INFO) << "DownloaderV3::on_msg_sync_headers probability switch " + << m_sync_headers_client->get_address() << " size=" << resp.binary_headers.size() + << " barrier=" << barrier + << " remote height=" << m_sync_headers_client->get_other_top_block_desc().height + << " our height=" << m_block_chain.get_tip_height() << std::endl; + m_sync_headers_client = nullptr; + advance_chain(); + return; + } + np::SyncHeaders::Request fd; + fd.previous_hash = m_sync_headers_previous_block_hash; + fd.max_count = np::SyncHeaders::Request::GOOD_COUNT; + BinaryArray msg = seria::to_binary_kv(fd); + m_find_diff_client->send(P2PProtocolBytecoinNew::create_header(np::SyncHeaders::Request::ID, msg.size())); + m_find_diff_client->send(std::move(msg)); + m_node->m_log(logging::INFO) << "DownloaderV3::advance_chain next SyncHeaders::Request from " + << m_sync_headers_client->get_address() + << " remote height=" << m_sync_headers_client->get_other_top_block_desc().height + << " our height=" << m_block_chain.get_tip_height() << std::endl; + m_chain_timer.once(SYNC_TIMEOUT); +} +void Node::DownloaderV3::on_msg_get_transactions(P2PProtocolBytecoinNew *who, np::GetTransactions::Response &&resp) {} + +/*void Node::DownloaderV3::on_msg_notify_request_chain(P2PProtocolBytecoin *who, + const NOTIFY_RESPONSE_CHAIN_ENTRY::request &req) { + if (m_chain_client != who || !m_chain_request_sent) + return; // TODO - who just sent us chain we did not ask, ban + m_chain_request_sent = false; + m_chain_timer.cancel(); + m_node->m_log(logging::INFO) << "Downloader received chain from " << who->get_address() + << " start_height=" << req.start_height << " length=" << req.m_block_ids.size() + << std::endl; + m_chain_start_height = req.start_height; + chain_source = m_chain_client->get_address(); + m_chain.assign(req.m_block_ids.begin(), req.m_block_ids.end()); + // Hash last_downloaded_block = m_chain.empty() ? Hash{} : m_chain.back(); + std::set downloading_bids; + for (auto &&dc : m_download_chain) + downloading_bids.insert(dc.bid); + while (!m_chain.empty() && + (m_node->m_block_chain.has_block(m_chain.front()) || downloading_bids.count(m_chain.front()) != 0)) { + m_chain.pop_front(); + m_chain_start_height += 1; + } // We stop removing as soon as we find new block, because wrong order might prevent us from applying blocks + if (req.m_block_ids.size() != m_chain.size() + 1) { + m_node->m_log(logging::INFO) << "Downloader truncated chain length=" << m_chain.size() << std::endl; + } + if (req.m_block_ids.empty()){ // Most likely peer is 3.2.0 + const auto now = m_node->m_p2p.get_local_time(); + m_node->m_log(logging::INFO) << "Downloader truncated chain to zero, delaying connect to " << who->get_address() +<< std::endl; + m_node->m_peer_db.delay_connection_attempt(who->get_address(), now); + who->disconnect(std::string()); // Will recursively call advance_chain again + } + advance_download(); +}*/ + +static const size_t GOOD_LAG = 5; // lagging by 5 blocks is ok for us + +void Node::DownloaderV3::advance_chain() { + if (m_find_diff_client || m_sync_headers_client) + return; // TODO - if number of headers we are preparing > some const + std::vector lagging_clients; + std::vector worth_clients; + const auto now = m_node->m_p2p.get_local_time(); + for (auto &&who : m_good_clients) { + if (who.first->get_other_top_block_desc().height + GOOD_LAG < m_node->m_block_chain.get_tip_height()) + lagging_clients.push_back(who.first); + api::BlockHeader info; + if (!m_node->m_block_chain.read_header(who.first->get_other_top_block_desc().hash, &info)) + worth_clients.push_back(who.first); + } + if (lagging_clients.size() > m_node->m_config.p2p_max_outgoing_connections / 4) { + auto who = lagging_clients.front(); + m_node->m_peer_db.delay_connection_attempt(who->get_address(), now); + m_node->m_log(logging::INFO) << "DownloaderV3 disconnecting lagging client " << who->get_address() << std::endl; + who->disconnect(std::string()); // Will recursively call advance_chain again + return; + } + if (worth_clients.empty()) + return; // We hope to get more connections soon + m_find_diff_client = worth_clients.at(crypto::rand() % worth_clients.size()); + m_find_diff_iteration = 0; + m_find_diff_bid = m_find_diff_client->get_other_top_block_desc().hash; + np::FindDiff::Request fd; + fd.gap_start = m_node->m_block_chain.get_sparse_chain(); + fd.desired_bid = m_find_diff_bid; + BinaryArray msg = seria::to_binary_kv(fd); + m_find_diff_client->send(P2PProtocolBytecoinNew::create_header(np::FindDiff::Request::ID, msg.size())); + m_find_diff_client->send(std::move(msg)); + m_node->m_log(logging::INFO) << "DownloaderV3::advance_chain FindDiff::Request from " + << m_find_diff_client->get_address() + << " remote height=" << m_find_diff_client->get_other_top_block_desc().height + << " our height=" << m_block_chain.get_tip_height() << std::endl; + m_chain_timer.once(SYNC_TIMEOUT); +} + +void Node::DownloaderV3::start_download(DownloadCell &dc, P2PProtocolBytecoinNew *who) { + auto idea_now = std::chrono::steady_clock::now(); + dc.downloading_client = who; + dc.block_source = who->get_address(); + dc.request_time = idea_now; + m_good_clients[dc.downloading_client] += 1; + total_downloading_blocks += 1; + NOTIFY_REQUEST_GET_OBJECTS::request msg; + msg.blocks.push_back(dc.bid); + if (std::chrono::duration_cast(idea_now - log_request_timestamp).count() > 1000) { + log_request_timestamp = idea_now; + std::cout << "Requesting block " << dc.expected_height << " from " << dc.downloading_client->get_address() + << std::endl; + } + m_node->m_log(logging::TRACE) << "DownloaderV3::advance_download requesting block " << dc.expected_height + << " hash=" << dc.bid << " from " << dc.downloading_client->get_address() + << std::endl; + BinaryArray raw_msg = + LevinProtocol::send_message(NOTIFY_REQUEST_GET_OBJECTS::ID, LevinProtocol::encode(msg), false); + dc.downloading_client->send(std::move(raw_msg)); +} + +void Node::DownloaderV3::stop_download(DownloadCell &dc, bool success) { + if (dc.status != DownloadCell::DOWNLOADING || !dc.downloading_client) + return; + auto git = m_good_clients.find(dc.downloading_client); + invariant(git != m_good_clients.end() && git->second != 0 && total_downloading_blocks != 0, + "DownloadCell reference to good client not found"); + git->second -= 1; + total_downloading_blocks -= 1; + if (success) { + dc.status = DownloadCell::DOWNLOADED; + m_who_downloaded_block.push_back(dc.downloading_client); + } + dc.downloading_client = nullptr; +} + +/*void Node::DownloaderV3::on_msg_notify_request_objects(P2PProtocolBytecoin *who, + const NOTIFY_RESPONSE_GET_OBJECTS::request &req) { + for (auto &&rb : req.blocks) { + Hash bid; + try { + BlockTemplate bheader; + seria::from_binary(bheader, rb.block); + bid = bytecoin::get_block_hash(bheader); + } catch (const std::exception &ex) { + m_node->m_log(logging::INFO) << "Exception " << common::what(ex) << " while parsing returned block, banning +" + << who->get_address() << std::endl; + who->disconnect(std::string()); + break; + } + bool cell_found = false; + for (auto &&dc : m_download_chain) { + if (dc.status != DownloadCell::DOWNLOADING || dc.downloading_client != who || dc.bid != bid) + continue; // downloaded or downloading + stop_download(dc, true); + dc.rb.block = rb.block; // TODO - std::move + dc.rb.transactions = rb.transactions; // TODO - std::move + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(now - log_response_timestamp).count() > 1000) { + log_response_timestamp = now; + std::cout << "Received block with height=" << dc.expected_height + << " (queue=" << total_downloading_blocks << ") from " << who->get_address() << std::endl; + } + m_node->m_log(logging::TRACE) + << "Downloader received block with height=" << dc.expected_height << " hash=" << dc.bid + << " (queue=" << total_downloading_blocks << ") from " << who->get_address() << std::endl; + cell_found = true; + if (multicore) { + dc.status = DownloadCell::PREPARING; + add_work(std::tuple(dc.bid, + !m_node->m_block_chain.get_currency().is_in_sw_checkpoint_zone(dc.expected_height), + std::move(dc.rb))); + } else { + dc.pb = PreparedBlock(std::move(dc.rb), m_node->m_block_chain.get_currency(), nullptr); + dc.status = DownloadCell::PREPARED; + } + break; + } + if (!cell_found) { + m_node->m_log(logging::INFO) << "Downloader received stray block from " << who->get_address() << std::endl; + // who->disconnect(std::string()); + // break; + } + } + for (auto &&bid : req.missed_ids) { + for (size_t dit_counter = 0; dit_counter != m_download_chain.size(); ++dit_counter) { + auto & dit = m_download_chain.at(dit_counter); + if (dit.status != DownloadCell::DOWNLOADING || dit.downloading_client != who || dit.bid != bid) + continue; // downloaded or downloading + stop_download(dit, false); + if (!m_chain_client || m_chain_client == who) { + m_node->m_log(logging::INFO) + << "Downloader cannot download block from any connected client, cleaning chain" << std::endl; + while (m_download_chain.size() > dit_counter) { + stop_download(m_download_chain.back(), false); + m_download_chain.pop_back(); + } + m_chain.clear(); + advance_download(); + return; + } + start_download(dit, m_chain_client); + } + } + advance_download(); +}*/ + +bool Node::DownloaderV3::on_idle() { + int added_counter = 0; + if (multicore) { + std::unique_lock lock(mu); + for (auto &&pb : prepared_blocks) { + for (auto &&dc : m_download_chain) + if (dc.status == DownloadCell::PREPARING && dc.bid == pb.first) { + dc.pb = std::move(pb.second); + dc.status = DownloadCell::PREPARED; + break; + } + } + prepared_blocks.clear(); + } + auto idea_start = std::chrono::high_resolution_clock::now(); + while (!m_download_chain.empty() && m_download_chain.front().status == DownloadCell::PREPARED) { + DownloadCell dc = std::move(m_download_chain.front()); + m_download_chain.pop_front(); + api::BlockHeader info; + auto action = m_block_chain.add_block( + dc.pb, &info, common::ip_address_and_port_to_string(dc.block_source.ip, dc.block_source.port)); + if (action == BroadcastAction::BAN) { + m_node->m_log(logging::INFO) << "Downloader DownloadCell BAN height=" << dc.expected_height + << " wb=" << dc.bid << std::endl; + // TODO - ban client who gave us chain + // continue; + } + // if (action == BroadcastAction::NOTHING) + // std::cout << "BroadcastAction::NOTHING height=" << info.height << " cd=" << + // info.cumulative_difficulty.lo + // << std::endl; + if (action == BroadcastAction::BROADCAST_ALL) { + // std::cout << "BroadcastAction::BROADCAST_ALL height=" << info.height + // << " cd=" << info.cumulative_difficulty.lo << std::endl; + if (m_download_chain.empty()) { + // We do not want to broadcast too often during download + m_node->m_log(logging::INFO) << "Added last (from batch) downloaded block height=" << info.height + << " bid=" << info.hash << std::endl; + COMMAND_TIMED_SYNC::request req; + req.payload_data = + CORE_SYNC_DATA{m_node->m_block_chain.get_tip_height(), m_node->m_block_chain.get_tip_bid()}; + BinaryArray raw_msg = + LevinProtocol::send_message(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(req), true); + m_node->broadcast( + nullptr, raw_msg); // nullptr - we can not always know which connection was block source + // m_node->broadcast_new(nullptr, raw_msg); // TODO nullptr - we can not always know which + // connection was block source + } + } + added_counter += 1; + auto idea_ms = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - idea_start); + if (idea_ms.count() > 100) + break; + } + if (added_counter) { + m_node->advance_long_poll(); + advance_download(); + if (m_download_chain.empty()) + for (auto &&who : m_good_clients) { + if (who.first->get_other_top_block_desc().height == m_node->m_block_chain.get_tip_height()) { + m_node->m_log(logging::TRACE) + << "DownloaderV3::on_idle sync_transactions to " << who.first->get_address() + << " our pool size=" << m_node->m_block_chain.get_memory_state_transactions().size() + << std::endl; + m_node->sync_transactions(who.first); + break; // TODO - sync with all nodes + } + } + } + + return !m_download_chain.empty() && m_download_chain.front().status == DownloadCell::PREPARED; +} + +void Node::DownloaderV3::on_download_timer() { + m_download_timer.once(SYNC_TIMEOUT / 8); // just several ticks per SYNC_TIMEOUT + auto idea_now = std::chrono::steady_clock::now(); + if (!m_download_chain.empty() && m_download_chain.front().status == DownloadCell::DOWNLOADING && + m_download_chain.front().downloading_client && m_download_chain.front().protect_from_disconnect && + std::chrono::duration_cast(idea_now - m_download_chain.front().request_time).count() > + SYNC_TIMEOUT) { + auto who = m_download_chain.front().downloading_client; + m_node->m_peer_db.delay_connection_attempt(who->get_address(), m_node->m_p2p.get_local_time()); + m_node->m_log(logging::INFO) << "Downloader disconnecting protected slacker " << who->get_address() + << std::endl; + who->disconnect(std::string()); + } +} + +void Node::DownloaderV3::advance_download() { + /* if (m_node->m_block_chain_reader1 || m_node->m_block_chain_reader2 || + m_block_chain.get_tip_height() < m_block_chain.internal_import_known_height()) + return; + const size_t TOTAL_DOWNLOAD_BLOCKS = 400; // TODO - dynamic count + const size_t TOTAL_DOWNLOAD_WINDOW = 2000; // TODO - dynamic count + while (m_download_chain.size() < TOTAL_DOWNLOAD_WINDOW && !m_chain.empty()) { + m_download_chain.push_back(DownloadCell()); + m_download_chain.back().bid = m_chain.front(); + m_download_chain.back().expected_height = m_chain_start_height; + m_download_chain.back().bid_source = chain_source; + m_chain.pop_front(); + m_chain_start_height += 1; + } + advance_chain(); + + while (m_who_downloaded_block.size() > TOTAL_DOWNLOAD_BLOCKS) + m_who_downloaded_block.pop_front(); + std::map who_downloaded_counter; + for (auto lit = m_who_downloaded_block.begin(); lit != m_who_downloaded_block.end(); ++lit) + who_downloaded_counter[*lit] += 1; + auto idea_now = std::chrono::steady_clock::now(); + for (size_t dit_counter = 0; dit_counter != m_download_chain.size(); ++dit_counter) { + auto & dit = m_download_chain.at(dit_counter); + if (dit.status != DownloadCell::DOWNLOADING || dit.downloading_client) + continue; // downloaded or downloading + if (total_downloading_blocks >= TOTAL_DOWNLOAD_BLOCKS) + break; + P2PProtocolBytecoin *ready_client = nullptr; + size_t ready_counter = std::numeric_limits::max(); + size_t ready_speed = 1; + for (auto &&who : m_good_clients) { + size_t speed = + std::max(1, std::min(TOTAL_DOWNLOAD_BLOCKS / 4, + who_downloaded_counter[who.first])); + // We clamp speed so that if even 1 downloaded all blocks, we will give + // small % of blocks to other peers + if (who.second * ready_speed < ready_counter * speed && + who.first->get_last_received_sync_data().current_height >= dit.expected_height) { + ready_client = who.first; + ready_counter = who.second; + ready_speed = speed; + } + } + if (!ready_client && m_chain_client) + ready_client = m_chain_client; + if (!ready_client){ // Cannot download chain from any client + m_node->m_log(logging::INFO) << "DownloaderV3::advance_download cannot download blocks from any + connected client, cleaning chain" << std::endl; + while (m_download_chain.size() > dit_counter) { + stop_download(m_download_chain.back(), false); + m_download_chain.pop_back(); + } + m_chain.clear(); + advance_chain(); + return; + } + start_download(dit, ready_client); + } + const bool bad_timeout = + !m_download_chain.empty() && m_download_chain.front().status == DownloadCell::DOWNLOADING && + m_download_chain.front().downloading_client && !m_download_chain.front().protect_from_disconnect && + std::chrono::duration_cast(idea_now - m_download_chain.front().request_time).count() > + 2 * SYNC_TIMEOUT; + const bool bad_relatively_slow = + total_downloading_blocks < TOTAL_DOWNLOAD_BLOCKS && m_download_chain.size() >= TOTAL_DOWNLOAD_WINDOW && + m_good_clients.size() > 1 && m_download_chain.front().status == DownloadCell::DOWNLOADING && + m_download_chain.front().downloading_client && !m_download_chain.front().protect_from_disconnect; + if (bad_relatively_slow || bad_timeout) { + auto who = m_download_chain.front().downloading_client; + for (auto &&dc : m_download_chain) + if (dc.downloading_client == who) + dc.protect_from_disconnect = true; + m_node->m_peer_db.delay_connection_attempt(who->get_address(), m_node->m_p2p.get_local_time()); + m_node->m_log(logging::INFO) << "DownloaderV3::advance_download disconnecting slacker " << + who->get_address() + << std::endl; + who->disconnect(std::string()); + }*/ +} diff --git a/src/Core/NodeLegacyAPI.cpp b/src/Core/NodeLegacyAPI.cpp index b6781f88..c66286f2 100644 --- a/src/Core/NodeLegacyAPI.cpp +++ b/src/Core/NodeLegacyAPI.cpp @@ -7,32 +7,26 @@ #include "Node.hpp" #include "TransactionExtra.hpp" #include "common/JsonValue.hpp" +#include "common/exception.hpp" #include "seria/BinaryInputStream.hpp" #include "seria/BinaryOutputStream.hpp" #include "seria/KVBinaryInputStream.hpp" #include "seria/KVBinaryOutputStream.hpp" // TODO - move to appropriate place -#define CORE_RPC_STATUS_OK "OK" -#define CORE_RPC_ERROR_CODE_WRONG_PARAM -1 #define CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT -2 -#define CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE -3 -#define CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS -4 -#define CORE_RPC_ERROR_CODE_INTERNAL_ERROR -5 -#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB -6 -#define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7 using namespace bytecoin; -bool Node::process_json_rpc_request(http::Client *who, http::RequestData &&request, http::ResponseData &response) { +bool Node::on_json_rpc(http::Client *who, http::RequestData &&request, http::ResponseData &response) { response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); - json_rpc::Response json_resp; + common::JsonValue jid(nullptr); try { json_rpc::Request json_req(request.body); - json_resp.set_id(json_req.get_id()); // copy id + jid = json_req.get_id().get(); auto it = m_jsonrpc_handlers.find(json_req.get_method()); if (it == m_jsonrpc_handlers.end()) { @@ -41,23 +35,61 @@ bool Node::process_json_rpc_request(http::Client *who, http::RequestData &&reque } // m_log(logging::INFO) << "jsonrpc request method=" << // json_req.get_method() << std::endl; - - if (!it->second(this, who, std::move(request), std::move(json_req), json_resp)) + std::string response_body; + if (!it->second(this, who, std::move(request), std::move(json_req), response_body)) return false; - } catch (const api::bytecoind::SendTransaction::Error &err) { - json_resp.set_error(err); - } catch (const api::bytecoind::GetArchive::Error &err) { - json_resp.set_error(err); + response.set_body(std::move(response_body)); + // } catch (const api::bytecoind::SendTransaction::Error &err) { + // response.set_body(json_rpc::create_error_response_body(err, jid)); + // } catch (const api::bytecoind::GetArchive::Error &err) { + // response.set_body(json_rpc::create_error_response_body(err, jid)); } catch (const json_rpc::Error &err) { - json_resp.set_error(err); + response.set_body(json_rpc::create_error_response_body(err, jid)); } catch (const std::exception &e) { - json_resp.set_error(json_rpc::Error(json_rpc::INTERNAL_ERROR, e.what())); + json_rpc::Error json_err(json_rpc::INTERNAL_ERROR, common::what(e)); + response.set_body(json_rpc::create_error_response_body(json_err, jid)); } - response.set_body(json_resp.get_body()); response.r.status = 200; return true; } +bool Node::on_binary_rpc(http::Client *who, http::RequestData &&request, http::ResponseData &response) { + response.r.headers.push_back({"Content-Type", "application/octet-stream"}); + + common::JsonValue jid(nullptr); + try { + size_t sep = request.body.find(char(0)); + if (sep == std::string::npos) + throw std::runtime_error("binary request contains no 0-character separator"); + json_rpc::Request binary_req(request.body.substr(0, sep)); + jid = binary_req.get_id().get(); + + common::MemoryInputStream body_stream(request.body.data() + sep + 1, request.body.size() - sep - 1); + + auto it = m_binaryrpc_handlers.find(binary_req.get_method()); + if (it == m_binaryrpc_handlers.end()) { + m_log(logging::INFO) << "binaryrpc request method not found - " << binary_req.get_method() << std::endl; + throw json_rpc::Error(json_rpc::METHOD_NOT_FOUND, "Method not found " + binary_req.get_method()); + } + // m_log(logging::INFO) << "jsonrpc request method=" << + // json_req.get_method() << std::endl; + std::string response_body; + if (!it->second(this, who, body_stream, std::move(binary_req), response_body)) + return false; + response.set_body(std::move(response_body)); + // } catch (const api::bytecoind::SendTransaction::Error &err) { + // response.set_body(json_rpc::create_binary_response_error_body(err, jid)); + // } catch (const api::bytecoind::GetArchive::Error &err) { + // response.set_body(json_rpc::create_binary_response_error_body(err, jid)); + } catch (const json_rpc::Error &err) { + response.set_body(json_rpc::create_binary_response_error_body(err, jid)); + } catch (const std::exception &e) { + json_rpc::Error json_err(json_rpc::INTERNAL_ERROR, common::what(e)); + response.set_body(json_rpc::create_binary_response_error_body(json_err, jid)); + } + response.r.status = 200; + return true; +} namespace { // Seeking blob in blob. TODO - check that it works the same as common::slow_memmem size_t slow_memmem(void *start_buff, size_t buflen, void *pat, size_t patlen) { @@ -80,12 +112,16 @@ bool Node::on_getblocktemplate(http::Client *who, http::RequestData &&raw_reques sta.top_block_hash = req.top_block_hash; sta.transaction_pool_version = req.transaction_pool_version; m_log(logging::INFO) << "Node received getblocktemplate REQ transaction_pool_version=" - << req.transaction_pool_version << " top_block_hash=" << req.top_block_hash << std::endl; + << (req.transaction_pool_version ? common::to_string(req.transaction_pool_version.get()) + : "empty") + << " top_block_hash=" + << (req.top_block_hash ? common::pod_to_hex(req.top_block_hash.get()) : "empty") << std::endl; m_log(logging::INFO) << "Node received getblocktemplate CUR transaction_pool_version=" << m_block_chain.get_tx_pool_version() << " top_block_hash=" << m_block_chain.get_tip_bid() << std::endl; - if (sta.top_block_hash == m_block_chain.get_tip_bid() && - sta.transaction_pool_version == m_block_chain.get_tx_pool_version()) { + if ((!sta.top_block_hash || sta.top_block_hash.get() == m_block_chain.get_tip_bid()) && + (!sta.transaction_pool_version || sta.transaction_pool_version.get() == m_block_chain.get_tx_pool_version()) && + (sta.top_block_hash || sta.transaction_pool_version)) { // m_log(logging::INFO) << "on_getblocktemplate will long poll, // json=" //<< @@ -104,32 +140,28 @@ bool Node::on_getblocktemplate(http::Client *who, http::RequestData &&raw_reques void Node::getblocktemplate(const api::bytecoind::GetBlockTemplate::Request &req, api::bytecoind::GetBlockTemplate::Response &res) { - if (req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) { - throw json_rpc::Error{CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE, "To big reserved size, maximum 255"}; - } - + if (req.reserve_size > TransactionExtraNonce::MAX_COUNT) + throw json_rpc::Error{api::bytecoind::GetBlockTemplate::TOO_BIG_RESERVE_SIZE, + "To big reserved size, maximum " + common::to_string(TransactionExtraNonce::MAX_COUNT)}; AccountPublicAddress acc{}; - - if (req.wallet_address.empty() || - !m_block_chain.get_currency().parse_account_address_string(req.wallet_address, &acc)) { - throw json_rpc::Error{CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS, "Failed to parse wallet address"}; - } + if (!m_block_chain.get_currency().parse_account_address_string(req.wallet_address, &acc)) + throw api::ErrorAddress( + api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Failed to parse wallet address", req.wallet_address); BlockTemplate block_template{}; BinaryArray blob_reserve; blob_reserve.resize(req.reserve_size, 0); - if (!m_block_chain.create_mining_block_template(&block_template, acc, blob_reserve, &res.difficulty, &res.height)) { + if (!m_block_chain.create_mining_block_template(acc, blob_reserve, &block_template, &res.difficulty, &res.height)) { m_log(logging::ERROR) << "Failed to create block template"; - throw json_rpc::Error{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template"}; + throw json_rpc::Error{json_rpc::INTERNAL_ERROR, "Internal error: failed to create block template"}; } BinaryArray block_blob = seria::to_binary(block_template); - PublicKey tx_pub_key = get_transaction_public_key_from_extra(block_template.base_transaction.extra); + PublicKey tx_pub_key = extra_get_transaction_public_key(block_template.base_transaction.extra); if (tx_pub_key == PublicKey{}) { m_log(logging::ERROR) << "Failed to find tx pub key in coinbase extra"; - throw json_rpc::Error{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to find tx pub key in coinbase extra"}; + throw json_rpc::Error{json_rpc::INTERNAL_ERROR, "Internal error: failed to find tx pub key in coinbase extra"}; } if (0 < req.reserve_size) { @@ -137,15 +169,13 @@ void Node::getblocktemplate(const api::bytecoind::GetBlockTemplate::Request &req static_cast(slow_memmem(block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key))); if (!res.reserved_offset) { m_log(logging::ERROR) << "Failed to find tx pub key in blockblob"; - throw json_rpc::Error{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template"}; + throw json_rpc::Error{json_rpc::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()) { m_log(logging::ERROR) << "Failed to calculate offset for reserved bytes"; - throw json_rpc::Error{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template"}; + throw json_rpc::Error{json_rpc::INTERNAL_ERROR, "Internal error: failed to create block template"}; } } else { res.reserved_offset = 0; @@ -155,7 +185,6 @@ void Node::getblocktemplate(const api::bytecoind::GetBlockTemplate::Request &req res.top_block_hash = m_block_chain.get_tip_bid(); res.transaction_pool_version = m_block_chain.get_tx_pool_version(); res.previous_block_hash = m_block_chain.get_tip().previous_block_hash; - res.status = CORE_RPC_STATUS_OK; } bool Node::on_get_currency_id(http::Client *, http::RequestData &&, json_rpc::Request &&, @@ -164,50 +193,27 @@ bool Node::on_get_currency_id(http::Client *, http::RequestData &&, json_rpc::Re return true; } -bool Node::on_submitblock(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::bytecoind::SubmitBlock::Request &&req, api::bytecoind::SubmitBlock::Response &res) { - BinaryArray blockblob = req.blocktemplate_blob; - - BlockTemplate block_template; - seria::from_binary(block_template, blockblob); - RawBlock raw_block; - api::BlockHeader info; - auto broad = m_block_chain.add_mined_block(blockblob, &raw_block, &info); - if (broad == BroadcastAction::BAN) - throw json_rpc::Error{CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED, "Block not accepted"}; - NOTIFY_NEW_BLOCK::request msg; - msg.b = RawBlockLegacy{raw_block.block, raw_block.transactions}; - msg.hop = 1; - msg.current_blockchain_height = m_block_chain.get_tip_height() + 1; // TODO check - BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_BLOCK::ID, LevinProtocol::encode(msg), false); - m_p2p.broadcast(nullptr, raw_msg); - - advance_long_poll(); - res.status = CORE_RPC_STATUS_OK; - return true; -} - bool Node::on_submitblock_legacy(http::Client *who, http::RequestData &&rd, json_rpc::Request &&jr, api::bytecoind::SubmitBlockLegacy::Request &&req, api::bytecoind::SubmitBlockLegacy::Response &res) { - if (req.size() != 1) { - throw json_rpc::Error{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong param"}; - } + if (req.size() != 1) + throw json_rpc::Error{json_rpc::INVALID_PARAMS, "Request params should be an array with exactly 1 element"}; - api::bytecoind::SubmitBlock::Request other_req; - if (!common::from_hex(req[0], other_req.blocktemplate_blob)) { - throw json_rpc::Error{CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob 1"}; + BinaryArray blocktemplate_blob; + if (!common::from_hex(req[0], blocktemplate_blob)) { + throw json_rpc::Error{api::bytecoind::SubmitBlock::WRONG_BLOCKBLOB, "blocktemplate_blob should be in hex"}; } - return on_submitblock(who, std::move(rd), std::move(jr), std::move(other_req), res); + api::BlockHeader info; + submit_block(blocktemplate_blob, &info); + return true; } bool Node::on_get_last_block_header(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::GetLastBlockHeaderLegacy::Request &&, api::bytecoind::GetLastBlockHeaderLegacy::Response &response) { static_cast(response.block_header) = m_block_chain.get_tip(); - response.block_header.orphan_status = false; - response.block_header.depth = - api::HeightOrDepth(m_block_chain.get_tip_height()) - api::HeightOrDepth(response.block_header.height); - response.status = CORE_RPC_STATUS_OK; + m_block_chain.fix_block_sizes(&response.block_header); + response.block_header.orphan_status = false; + response.block_header.depth = api::HeightOrDepth(m_block_chain.get_tip_height() - response.block_header.height); return true; } @@ -215,14 +221,11 @@ bool Node::on_get_block_header_by_hash(http::Client *, http::RequestData &&, jso api::bytecoind::GetBlockHeaderByHashLegacy::Request &&request, api::bytecoind::GetBlockHeaderByHashLegacy::Response &response) { if (!m_block_chain.read_header(request.hash, &response.block_header)) - throw json_rpc::Error{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by hash. Hash = " + common::pod_to_hex(request.hash) + '.'}; - Hash hash_at_height; + throw api::ErrorHashNotFound("Block is neither in main nor in any side chain", request.hash); + m_block_chain.fix_block_sizes(&response.block_header); response.block_header.orphan_status = - !m_block_chain.read_chain(response.block_header.height, &hash_at_height) || hash_at_height != request.hash; - response.block_header.depth = - api::HeightOrDepth(m_block_chain.get_tip_height()) - api::HeightOrDepth(response.block_header.height); - response.status = CORE_RPC_STATUS_OK; + !m_block_chain.in_chain(response.block_header.height, response.block_header.hash); + response.block_header.depth = api::HeightOrDepth(m_block_chain.get_tip_height() - response.block_header.height); return true; } @@ -230,18 +233,15 @@ bool Node::on_get_block_header_by_height(http::Client *, http::RequestData &&, j api::bytecoind::GetBlockHeaderByHeightLegacy::Request &&request, api::bytecoind::GetBlockHeaderByHeightLegacy::Response &response) { Hash block_hash; - if (!m_block_chain.read_chain(request.height - 1, &block_hash)) { // This call counts blocks from 1 - throw json_rpc::Error{CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") + common::to_string(request.height) + - ", current blockchain height = " + common::to_string(m_block_chain.get_tip_height())}; + // Freaking legacy, this call request counts blocks from 1, response counts from 0 + if (request.height == 0 || !m_block_chain.read_chain(request.height - 1, &block_hash)) { + throw api::ErrorWrongHeight( + "Too big height. Note, this method request counts blocks from 1, not 0 as all other methods", + request.height - 1, m_block_chain.get_tip_height()); } - if (!m_block_chain.read_header(block_hash, &response.block_header)) - throw json_rpc::Error{CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") + common::to_string(request.height) + - ", current blockchain height = " + common::to_string(m_block_chain.get_tip_height())}; + invariant(m_block_chain.read_header(block_hash, &response.block_header), ""); + m_block_chain.fix_block_sizes(&response.block_header); response.block_header.orphan_status = false; - response.block_header.depth = - api::HeightOrDepth(m_block_chain.get_tip_height()) - api::HeightOrDepth(response.block_header.height); - response.status = CORE_RPC_STATUS_OK; + response.block_header.depth = api::HeightOrDepth(m_block_chain.get_tip_height() - response.block_header.height); return true; } diff --git a/src/Core/Node_P2PProtocolBytecoin.cpp b/src/Core/Node_P2PProtocolBytecoin.cpp new file mode 100644 index 00000000..e4e696ca --- /dev/null +++ b/src/Core/Node_P2PProtocolBytecoin.cpp @@ -0,0 +1,298 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include +#include "Config.hpp" +#include "CryptoNoteTools.hpp" +#include "Node.hpp" +#include "TransactionExtra.hpp" +#include "common/JsonValue.hpp" +#include "platform/PathTools.hpp" +#include "seria/BinaryInputStream.hpp" +#include "seria/BinaryOutputStream.hpp" +#include "seria/KVBinaryInputStream.hpp" +#include "seria/KVBinaryOutputStream.hpp" +#include "version.hpp" + +using namespace bytecoin; + +Node::P2PProtocolBytecoin::~P2PProtocolBytecoin() { + // std::cout << "~P2PProtocolBytecoin this=" << std::hex << (size_t)this << std::dec << std::endl; +} + +void Node::P2PProtocolBytecoin::on_msg_bytes(size_t, size_t) { // downloaded. uploaded + // node->peers.on_peer_bytes(get_address(), downloaded, uploaded, + // node->p2p.get_local_time()); +} + +CORE_SYNC_DATA +Node::P2PProtocolBytecoin::get_sync_data() const { + CORE_SYNC_DATA sync_data; + sync_data.current_height = m_node->m_block_chain.get_tip_height(); + sync_data.top_id = m_node->m_block_chain.get_tip_bid(); + return sync_data; +} + +std::vector Node::P2PProtocolBytecoin::get_peers_to_share() const { + auto result = m_node->m_peer_db.get_peerlist_to_p2p_legacy( + get_address(), m_node->m_p2p.get_local_time(), config.p2p_default_peers_in_handshake); + return result; +} + +void Node::P2PProtocolBytecoin::on_first_message_after_handshake() { + // if we set just seen on handshake, we will keep connecting to seed nodes + // forever + m_node->m_peer_db.set_peer_just_seen( + get_last_received_unique_number(), get_address(), m_node->m_p2p.get_local_time()); +} + +void Node::P2PProtocolBytecoin::on_immediate_protocol_switch(unsigned char first_byte) { +#if bytecoin_NEWP2P + // We ignore first_byte for now, because we have only 1 new protocol + get_client()->set_protocol(std::make_unique(m_node, get_client())); +#endif +} + +void Node::P2PProtocolBytecoin::after_handshake() { + m_node->m_p2p.peers_updated(); + m_node->broadcast_protocols.insert(this); + m_node->advance_long_poll(); + + auto signed_checkpoints = m_node->m_block_chain.get_latest_checkpoints(); + for (const auto &sck : signed_checkpoints) { + BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_CHECKPOINT::ID, LevinProtocol::encode(sck), false); + send(std::move(raw_msg)); + } + m_node->m_downloader.on_connect(this); // Can destroy this... +} + +void Node::P2PProtocolBytecoin::on_msg_handshake(COMMAND_HANDSHAKE::request &&req) { +#if bytecoin_NEWP2P + if (get_version() == P2PProtocolVersion::V3_NEW && P2PProtocolVersion::V3_NEW == P2PProtocolVersion::CURRENT) { + get_client()->set_protocol(std::make_unique(m_node, get_client())); + return; + } +#endif + m_node->m_peer_db.add_incoming_peer(get_address(), m_node->m_p2p.get_local_time()); + after_handshake(); +} + +void Node::P2PProtocolBytecoin::on_msg_handshake(COMMAND_HANDSHAKE::response &&req) { +#if bytecoin_NEWP2P + if (get_version() == P2PProtocolVersion::V3_NEW && P2PProtocolVersion::V3_NEW == P2PProtocolVersion::CURRENT) { + get_client()->set_protocol(std::make_unique(m_node, get_client())); + return; + } +#endif + m_node->m_peer_db.merge_peerlist_from_p2p(get_address(), req.local_peerlist, m_node->m_p2p.get_local_time()); + after_handshake(); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_request_chain(NOTIFY_REQUEST_CHAIN::request &&req) { + NOTIFY_RESPONSE_CHAIN_ENTRY::request msg; + msg.m_block_ids = m_node->m_block_chain.get_sync_headers_chain( + req.block_ids, &msg.start_height, config.p2p_block_ids_sync_default_count); + msg.total_height = m_node->m_block_chain.get_tip_height() + 1; + + BinaryArray raw_msg = + LevinProtocol::send_message(NOTIFY_RESPONSE_CHAIN_ENTRY::ID, LevinProtocol::encode(msg), false); + send(std::move(raw_msg)); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_request_chain(NOTIFY_RESPONSE_CHAIN_ENTRY::request &&req) { + m_node->m_downloader.on_msg_notify_request_chain(this, req); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_request_objects(NOTIFY_REQUEST_GET_OBJECTS::request &&req) { + NOTIFY_RESPONSE_GET_OBJECTS::request msg; + msg.current_blockchain_height = m_node->m_block_chain.get_tip_height() + 1; + for (auto &&bh : req.blocks) { + RawBlock raw_block; + if (m_node->m_block_chain.read_block(bh, &raw_block)) { + msg.blocks.push_back(RawBlockLegacy{raw_block.block, raw_block.transactions}); + } else + msg.missed_ids.push_back(bh); + } + if (!req.txs.empty()) { + // TODO - remove after we are sure transactions are never asked + throw std::runtime_error( + "Transactions asked in NOTIFY_REQUEST_GET_OBJECTS by " + common::ip_address_to_string(get_address().ip)); + } + BinaryArray raw_msg = + LevinProtocol::send_message(NOTIFY_RESPONSE_GET_OBJECTS::ID, LevinProtocol::encode(msg), false); + send(std::move(raw_msg)); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_request_objects(NOTIFY_RESPONSE_GET_OBJECTS::request &&req) { + m_node->m_downloader.on_msg_notify_request_objects(this, req); +} + +void Node::P2PProtocolBytecoin::on_disconnect(const std::string &ban_reason) { + m_node->broadcast_protocols.erase(this); + m_node->m_downloader.on_disconnect(this); + + P2PProtocolBasic::on_disconnect(ban_reason); + m_node->advance_long_poll(); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_request_tx_pool(NOTIFY_REQUEST_TX_POOL::request &&req) { + NOTIFY_NEW_TRANSACTIONS::request msg; + auto mytxs = m_node->m_block_chain.get_memory_state_transactions(); + msg.txs.reserve(mytxs.size()); + std::sort(req.txs.begin(), req.txs.end()); // Should have been sorted on wire, + // checked here, but alas, legacy + for (auto &&tx : mytxs) { + auto it = std::lower_bound(req.txs.begin(), req.txs.end(), tx.first); + if (it != req.txs.end() && *it == tx.first) + continue; + msg.txs.push_back(tx.second.binary_tx); + } + m_node->m_log(logging::TRACE) << "on_msg_notify_request_tx_pool from " << get_address() + << " peer sent=" << req.txs.size() << " we are relaying=" << msg.txs.size() + << std::endl; + if (msg.txs.empty()) + return; + BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_TRANSACTIONS::ID, LevinProtocol::encode(msg), false); + send(std::move(raw_msg)); +} + +void Node::P2PProtocolBytecoin::on_msg_timed_sync(COMMAND_TIMED_SYNC::request &&req) { + m_node->m_downloader.advance_download(); +} +void Node::P2PProtocolBytecoin::on_msg_timed_sync(COMMAND_TIMED_SYNC::response &&req) { + m_node->m_downloader.advance_download(); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_new_block(NOTIFY_NEW_BLOCK::request &&req) { + RawBlock raw_block{req.b.block, req.b.transactions}; + PreparedBlock pb(std::move(raw_block), m_node->m_block_chain.get_currency(), nullptr); + api::BlockHeader info; + auto action = m_node->m_block_chain.add_block(pb, &info, get_address().to_string()); + switch (action) { + case BroadcastAction::BAN: + disconnect("NOTIFY_NEW_BLOCK add_block BAN"); + return; + case BroadcastAction::BROADCAST_ALL: { + req.hop += 1; + BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_BLOCK::ID, LevinProtocol::encode(req), false); + m_node->broadcast(this, raw_msg); + m_node->broadcast_new(nullptr, pb.raw_block.block); + m_node->advance_long_poll(); + break; + } + case BroadcastAction::NOTHING: + break; + } + set_last_received_sync_data(CORE_SYNC_DATA{req.current_blockchain_height - 1, pb.bid}); + // -1 is in legacy protocol + m_node->m_downloader.advance_download(); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_new_transactions(NOTIFY_NEW_TRANSACTIONS::request &&req) { + if (m_node->m_block_chain_reader1 || m_node->m_block_chain_reader2 || + m_node->m_block_chain.get_tip_height() < m_node->m_block_chain.internal_import_known_height()) + return; // We cannot check tx while downloading anyway + NOTIFY_NEW_TRANSACTIONS::request msg; + Hash any_tid; + for (auto &&raw_tx : req.txs) { + Transaction tx; + try { + seria::from_binary(tx, raw_tx); + } catch (const std::exception &ex) { + disconnect("NOTIFY_NEW_TRANSACTIONS add_transaction BAN from_binary failed " + common::what(ex)); + return; + } + const Hash tid = get_transaction_hash(tx); + any_tid = tid; + Height conflict_height = 0; + auto action = m_node->m_block_chain.add_transaction( + tid, tx, raw_tx, m_node->m_p2p.get_local_time(), &conflict_height, get_address().to_string()); + switch (action) { + case AddTransactionResult::BAN: + disconnect("NOTIFY_NEW_TRANSACTIONS add_transaction BAN"); + return; + case AddTransactionResult::BROADCAST_ALL: + msg.txs.push_back(raw_tx); + break; + case AddTransactionResult::ALREADY_IN_POOL: + case AddTransactionResult::INCREASE_FEE: + case AddTransactionResult::FAILED_TO_REDO: + case AddTransactionResult::OUTPUT_ALREADY_SPENT: + break; + } + } + m_node->m_log(logging::TRACE) << "on_msg_notify_new_transactions from " << get_address() + << " got=" << req.txs.size() << " relaying=" << msg.txs.size() + << (req.txs.size() > 1 ? " notify_tx_reply (?) " : " ") + << (any_tid == Hash{} ? "" : common::pod_to_hex(any_tid)) << std::endl; + if (msg.txs.empty()) + return; + BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_NEW_TRANSACTIONS::ID, LevinProtocol::encode(msg), false); + m_node->broadcast(this, raw_msg); + // m_node->broadcast_new(nullptr, ); // TODO - broadcast + m_node->advance_long_poll(); +} + +void Node::P2PProtocolBytecoin::on_msg_notify_checkpoint(NOTIFY_CHECKPOINT::request &&req) { + if (!m_node->m_block_chain.add_checkpoint(req, get_address().to_string())) + return; + m_node->m_log(logging::INFO) << "NOTIFY_CHECKPOINT::request height=" << req.height << " hash=" << req.hash + << " key_id=" << req.key_id << " counter=" << req.counter << std::endl; + BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_CHECKPOINT::ID, LevinProtocol::encode(req), false); + m_node->broadcast(nullptr, raw_msg); // nullptr, not this - so a sender sees "reflection" of message + // m_node->broadcast_new(nullptr, raw_msg); // nullptr, not this - so a sender sees "reflection" of message + COMMAND_TIMED_SYNC::request ts_req; + ts_req.payload_data = CORE_SYNC_DATA{m_node->m_block_chain.get_tip_height(), m_node->m_block_chain.get_tip_bid()}; + raw_msg = LevinProtocol::send_message(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(ts_req), true); + m_node->broadcast(nullptr, raw_msg); + // m_node->broadcast_new(nullptr, raw_msg); // ? + m_node->advance_long_poll(); +} + +#if bytecoin_ALLOW_DEBUG_COMMANDS +void Node::P2PProtocolBytecoin::on_msg_network_state(COMMAND_REQUEST_NETWORK_STATE::request &&req) { + if (!m_node->check_trust(req.tr)) { + disconnect(std::string()); + return; + } + COMMAND_REQUEST_NETWORK_STATE::response msg; + msg.local_time = m_node->m_p2p.get_local_time(); + msg.my_id = get_unique_number(); + for (auto &&cc : m_node->m_downloader.get_good_clients()) { + connection_entry item; + item.is_income = cc.first->is_incoming(); + item.id = cc.first->get_unique_number(); + item.adr.port = cc.first->get_address().port; + item.adr.ip = ip_address_to_legacy(cc.first->get_address().ip); + msg.connections_list.push_back(item); + } + BinaryArray raw_msg = LevinProtocol::send_reply(COMMAND_REQUEST_NETWORK_STATE::ID, LevinProtocol::encode(msg), 0); + send(std::move(raw_msg)); +} + +void Node::P2PProtocolBytecoin::on_msg_stat_info(COMMAND_REQUEST_STAT_INFO::request &&req) { + if (!m_node->check_trust(req.tr)) { + disconnect(std::string()); + return; + } + COMMAND_REQUEST_STAT_INFO::response msg; + for (auto &&pb : m_node->broadcast_protocols) + if (pb->is_incoming()) + msg.incoming_connections_count += 1; + else + msg.connections_count += 1; + for (auto &&pb : m_node->broadcast_protocols_new) + if (pb->is_incoming()) + msg.incoming_connections_count += 1; + else + msg.connections_count += 1; + msg.connections_count += msg.incoming_connections_count; + msg.version = app_version(); + msg.os_version = platform::get_os_version_string(); + msg.payload_info = CoreStatistics{}; + BinaryArray raw_msg = LevinProtocol::send_reply(COMMAND_REQUEST_STAT_INFO::ID, LevinProtocol::encode(msg), 0); + send(std::move(raw_msg)); +} + +#endif diff --git a/src/Core/Node_P2PProtocolBytecoinNew.cpp b/src/Core/Node_P2PProtocolBytecoinNew.cpp new file mode 100644 index 00000000..1016b55c --- /dev/null +++ b/src/Core/Node_P2PProtocolBytecoinNew.cpp @@ -0,0 +1,154 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include +#include "Config.hpp" +#include "CryptoNoteTools.hpp" +#include "Node.hpp" +#include "TransactionExtra.hpp" +#include "common/JsonValue.hpp" +#include "platform/PathTools.hpp" +#include "seria/BinaryInputStream.hpp" +#include "seria/BinaryOutputStream.hpp" +#include "seria/KVBinaryInputStream.hpp" +#include "seria/KVBinaryOutputStream.hpp" +#include "version.hpp" + +using namespace bytecoin; + +np::TopBlockDesc Node::P2PProtocolBytecoinNew::get_top_block_desc() const { + np::TopBlockDesc result; + result.cd = m_node->m_block_chain.get_tip_cumulative_difficulty(); + result.height = m_node->m_block_chain.get_tip_height(); + result.hash = m_node->m_block_chain.get_tip_bid(); + return result; +} + +std::vector Node::P2PProtocolBytecoinNew::get_peers_to_share() const { + auto result = m_node->m_peer_db.get_peerlist_to_p2p( + get_address(), m_node->m_p2p.get_local_time(), m_node->m_config.p2p_default_peers_in_handshake); + return result; +} + +void Node::P2PProtocolBytecoinNew::on_first_message_after_handshake() { + // if we set just seen on handshake, we will keep connecting to seed nodes forever + m_node->m_peer_db.set_peer_just_seen(get_other_peer_desc().peer_id, get_address(), m_node->m_p2p.get_local_time()); +} + +void Node::P2PProtocolBytecoinNew::on_disconnect(const std::string &ban_reason) { + m_node->broadcast_protocols_new.erase(this); + m_node->m_downloader_v3.on_disconnect(this); + + P2PProtocolNew::on_disconnect(ban_reason); + m_node->advance_long_poll(); +} + +void Node::P2PProtocolBytecoinNew::on_msg_bytes(size_t, size_t) { // downloaded. uploaded + // P2PProtocolNew::on_msg_bytes(, ); +} +void Node::P2PProtocolBytecoinNew::after_handshake() { + m_node->m_p2p.peers_updated(); + m_node->broadcast_protocols_new.insert(this); + m_node->advance_long_poll(); + + auto signed_checkpoints = m_node->m_block_chain.get_latest_checkpoints(); + for (const auto &sck : signed_checkpoints) { + BinaryArray raw_msg = LevinProtocol::send_message(NOTIFY_CHECKPOINT::ID, LevinProtocol::encode(sck), false); + send(std::move(raw_msg)); + } + m_node->m_downloader_v3.on_connect(this); // Can destroy this... +} + +void Node::P2PProtocolBytecoinNew::on_msg_handshake(np::Handshake::Request &&req) { + m_node->m_peer_db.add_incoming_peer(get_address(), m_node->m_p2p.get_local_time()); + after_handshake(); +} +void Node::P2PProtocolBytecoinNew::on_msg_handshake(np::Handshake::Response &&req) { + m_node->m_peer_db.merge_peerlist_from_p2p(get_address(), req.peerlist, m_node->m_p2p.get_local_time()); + after_handshake(); +} +void Node::P2PProtocolBytecoinNew::on_msg_find_diff(np::FindDiff::Request &&req) { + np::FindDiff::Response fd; + if (req.gap_start.size() > np::FindDiff::Request::MAX_GAP_START_LENGTH || req.gap_start.size() == 0) + return disconnect("FindDiff.Request.gap_start.size violation"); + for (Hash gap : req.gap_start) { + fd.sparse_chain = m_node->m_block_chain.get_sparse_chain(gap, req.desired_bid); + if (fd.sparse_chain.empty()) + continue; + BinaryArray msg = seria::to_binary_kv(fd); + send(create_header(np::FindDiff::Response::ID, msg.size())); + send(std::move(msg)); + } + disconnect("FindDiff Request protocol violation - no hash from req.gap_start found in blockchain"); +} +void Node::P2PProtocolBytecoinNew::on_msg_find_diff(np::FindDiff::Response &&resp) { + m_node->m_downloader_v3.on_msg_find_diff(this, std::move(resp)); +} +void Node::P2PProtocolBytecoinNew::on_msg_sync_headers(np::SyncHeaders::Request &&req) { + if (req.max_count > np::SyncHeaders::Request::MAX_COUNT) + return disconnect("SyncHeaders.Request.max_count violation"); + api::BlockHeader previous_header; + if (!m_node->m_block_chain.read_header(req.previous_hash, &previous_header)) + return disconnect("SyncHeaders.Request.previous_hash block not found"); + np::SyncHeaders::Response res; + if (m_node->m_block_chain.in_chain(previous_header.height, previous_header.hash)) { + for (Height ha = previous_header.height + 1; ha < m_node->m_block_chain.get_tip_height(); ++ha) { + if (res.binary_headers.size() >= req.max_count) + break; + Hash bid; + invariant(m_node->m_block_chain.read_chain(ha, &bid), ""); + RawBlock rb; + invariant(m_node->m_block_chain.read_block(bid, &rb), "SyncHeaders.Request block not found"); + res.binary_headers.push_back(rb.block); + } + } + BinaryArray msg = seria::to_binary_kv(res); + send(create_header(np::SyncHeaders::Response::ID, msg.size())); + send(std::move(msg)); +} +void Node::P2PProtocolBytecoinNew::on_msg_sync_headers(np::SyncHeaders::Response &&resp) { + m_node->m_downloader_v3.on_msg_sync_headers(this, std::move(resp)); +} +void Node::P2PProtocolBytecoinNew::on_msg_get_transactions(np::GetTransactions::Request &&req) { + if (req.transaction_hashes.size() > np::GetTransactions::Request::MAX_TRANSACTION_HASHES) + return disconnect("MAX_TRANSACTION_HASHES"); + np::GetTransactions::Response fd; + fd.top_block_desc = get_top_block_desc(); + if (req.transaction_hashes.empty()) { + RawBlock rb; + if (m_node->m_block_chain.read_block(req.block_hash, &rb)) { + fd.transactions = std::move(rb.transactions); + } + } else { + fd.transactions.reserve(req.transaction_hashes.size()); + for (auto &&tid : req.transaction_hashes) { + BinaryArray binary_tx; + Height block_height = 0; + Hash block_hash; + size_t index_in_block = 0; + if (m_node->m_block_chain.read_transaction(tid, &binary_tx, &block_height, &block_hash, &index_in_block)) { + fd.transactions.push_back(std::move(binary_tx)); + } + } + } + BinaryArray msg = seria::to_binary_kv(fd); + send(create_header(np::GetTransactions::Response::ID, msg.size())); + send(std::move(msg)); +} +void Node::P2PProtocolBytecoinNew::on_msg_get_transactions(np::GetTransactions::Response &&resp) { + m_node->m_downloader_v3.on_msg_get_transactions(this, std::move(resp)); +} +void Node::P2PProtocolBytecoinNew::on_msg_get_pool_hashes(np::GetPoolHashes::Request &&req) {} +void Node::P2PProtocolBytecoinNew::on_msg_get_pool_hashes(np::GetPoolHashes::Response &&resp) {} +void Node::P2PProtocolBytecoinNew::on_msg_relay_block_header(np::RelayBlockHeader &&req) {} +void Node::P2PProtocolBytecoinNew::on_msg_relay_transaction_desc(np::RelayTransactionDescs &&req) {} +void Node::P2PProtocolBytecoinNew::on_msg_get_peer_statistics(np::GetPeerStatistics::Request &&req) { + if (!m_node->check_trust(req.tr)) + return disconnect(std::string()); + np::GetPeerStatistics::Response res; + res = m_node->create_statistics_response(); + BinaryArray msg = seria::to_binary_kv(res); + send(create_header(np::GetPeerStatistics::Response::ID, msg.size())); + send(std::move(msg)); +} diff --git a/src/Core/TransactionBuilder.cpp b/src/Core/TransactionBuilder.cpp index cdbf37cb..0eb48a0c 100644 --- a/src/Core/TransactionBuilder.cpp +++ b/src/Core/TransactionBuilder.cpp @@ -25,21 +25,13 @@ bool TransactionBuilder::derive_public_key(const AccountPublicAddress &to, return crypto::derive_public_key(derivation, output_index, to.spend_public_key, ephemeral_key); } -TransactionBuilder::TransactionBuilder(const Currency ¤cy, UnlockMoment unlock_time) { - m_transaction.version = currency.current_transaction_version; - m_transaction.unlock_time = unlock_time; +TransactionBuilder::TransactionBuilder(const Currency ¤cy, BlockOrTimestamp unlock_time) { + m_transaction.version = currency.current_transaction_version; + m_transaction.unlock_block_or_timestamp = unlock_time; } void TransactionBuilder::set_payment_id(const Hash &hash) { - BinaryArray payment_id_blob; - set_payment_id_to_transaction_extra_nonce(payment_id_blob, reinterpret_cast(hash)); - set_extra_nonce(payment_id_blob); -} - -void TransactionBuilder::set_extra_nonce(const BinaryArray &nonce) { - TransactionExtraNonce extra_nonce = {nonce}; - m_extra.set(extra_nonce); - m_transaction.extra = m_extra.serialize(); + extra_add_payment_id(m_transaction.extra, hash); } size_t TransactionBuilder::add_output(uint64_t amount, const AccountPublicAddress &to) { @@ -52,12 +44,8 @@ size_t TransactionBuilder::add_output(uint64_t amount, const AccountPublicAddres return m_output_descs.size() - 1; } -static bool APIOutputLessGlobalIndex(const api::Output &a, const api::Output &b) { - return a.global_index < b.global_index; -} -static bool APIOutputEqualGlobalIndex(const api::Output &a, const api::Output &b) { - return a.global_index == b.global_index; -} +static bool APIOutputLessGlobalIndex(const api::Output &a, const api::Output &b) { return a.index < b.index; } +static bool APIOutputEqualGlobalIndex(const api::Output &a, const api::Output &b) { return a.index == b.index; } bool TransactionBuilder::generate_key_image_helper(const AccountKeys &ack, const PublicKey &tx_public_key, size_t real_output_index, KeyPair &in_ephemeral, KeyImage &ki) { KeyDerivation recv_derivation; @@ -105,7 +93,7 @@ size_t TransactionBuilder::add_input(const AccountKeys &sender_keys, for (const auto &out : desc.outputs) { if (out.amount != real_output.amount) // they are all zero as sent from node throw std::runtime_error("Mixin outputs with different amounts is not allowed"); - desc.input.output_indexes.push_back(out.global_index); + desc.input.output_indexes.push_back(out.index); } desc.input.output_indexes = absolute_output_offsets_to_relative(desc.input.output_indexes); @@ -141,14 +129,12 @@ Transaction TransactionBuilder::sign(const Hash &tx_derivation_seed) { m_transaction.inputs.at(i) = std::move(m_input_descs[i].input); KeyPair tx_keys = deterministic_keys_from_seed(m_transaction, tx_derivation_seed); - TransactionExtraPublicKey pk = {tx_keys.public_key}; - m_extra.set(pk); - m_transaction.extra = m_extra.serialize(); + extra_add_transaction_public_key(m_transaction.extra, tx_keys.public_key); // Now when we set tx keys we can derive output keys m_transaction.outputs.resize(m_output_descs.size()); for (size_t i = 0; i != m_output_descs.size(); ++i) { KeyOutput out_key; - if (!derive_public_key(m_output_descs[i].addr, tx_keys.secret_key, i, out_key.key)) + if (!derive_public_key(m_output_descs[i].addr, tx_keys.secret_key, i, out_key.public_key)) throw std::runtime_error("output keys detected as corrupted during output key derivation"); TransactionOutput out; // TODO - return {} initializer after NDK compiler upgrade out.amount = m_output_descs[i].amount; @@ -188,16 +174,16 @@ void UnspentSelector::reset(Unspents &&unspents) { m_ra_amounts.clear(); } -void UnspentSelector::add_mixed_inputs(const SecretKey &view_secret_key, +void UnspentSelector::add_mixed_inputs(const SecretKey &view_secret_key, const Wallet *wallet, const std::unordered_map &wallet_records, TransactionBuilder *builder, uint32_t anonymity, api::bytecoind::GetRandomOutputs::Response &&ra_response) { - for (const auto & uu : m_used_unspents) { + for (const auto &uu : m_used_unspents) { std::vector mix_outputs; auto &our_ra_outputs = ra_response.outputs[uu.amount]; while (mix_outputs.size() < anonymity + 1) { if (our_ra_outputs.empty()) - throw api::walletd::CreateTransaction::Error(api::walletd::CreateTransaction::NOT_ENOUGH_ANONYMITY, - "Not enough anonymity for amount " + common::to_string(uu.amount)); + throw json_rpc::Error(api::walletd::CreateTransaction::NOT_ENOUGH_ANONYMITY, + "Requested anonymity too high for amount " + common::to_string(uu.amount)); mix_outputs.push_back(std::move(our_ra_outputs.back())); our_ra_outputs.pop_back(); } @@ -207,7 +193,7 @@ void UnspentSelector::add_mixed_inputs(const SecretKey &view_secret_key, int best_distance = 0; size_t best_index = mix_outputs.size(); for (size_t i = 0; i != mix_outputs.size(); ++i) { - int distance = abs(int(uu.global_index) - int(mix_outputs[i].global_index)); + int distance = abs(int(uu.index) - int(mix_outputs[i].index)); if (best_index == mix_outputs.size() || distance < best_distance) { best_index = i; best_distance = distance; @@ -218,11 +204,18 @@ void UnspentSelector::add_mixed_inputs(const SecretKey &view_secret_key, AccountKeys sender_keys; sender_keys.view_secret_key = view_secret_key; if (!m_currency.parse_account_address_string(uu.address, &sender_keys.address)) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Could not parse address " + uu.address); - auto rit = wallet_records.find(sender_keys.address.spend_public_key); - if (rit == wallet_records.end() || rit->second.spend_public_key != sender_keys.address.spend_public_key) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "No keys in wallet for address " + uu.address); - sender_keys.spend_secret_key = rit->second.spend_secret_key; + throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "Could not parse address " + uu.address); + if (wallet) { + WalletRecord record; + if (!wallet->get_record(record, sender_keys.address)) + throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "No keys in wallet for address " + uu.address); + sender_keys.spend_secret_key = record.spend_secret_key; + } else { + auto rit = wallet_records.find(sender_keys.address.spend_public_key); + if (rit == wallet_records.end() || rit->second.spend_public_key != sender_keys.address.spend_public_key) + throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "No keys in wallet for address " + uu.address); + sender_keys.spend_secret_key = rit->second.spend_secret_key; + } builder->add_input(sender_keys, uu, mix_outputs); } } @@ -257,10 +250,11 @@ std::string UnspentSelector::select_optimal_outputs(Height block_height, Timesta size_t optimization_median_percent = (optimization_level == "aggressive") ? MEDIAN_PERCENT_AGGRESSIVE : MEDIAN_PERCENT; const size_t optimization_median = effective_median_size * optimization_median_percent / 100; + const Amount dust_threshold = m_currency.default_dust_threshold; while (true) { if (!select_optimal_outputs(&have_coins, &dust_coins, max_digits, total_amount + fee, anonymity, optimizations)) return "NOT_ENOUGH_FUNDS"; - Amount change_dust_fee = (m_used_total - total_amount - fee) % m_currency.default_dust_threshold; + Amount change_dust_fee = (m_used_total - total_amount - fee) % dust_threshold; size_t tx_size = get_maximum_tx_size(m_inputs_count, total_outputs + 8, anonymity); // TODO - 8 is expected max change outputs if (tx_size > optimization_median && optimizations > 0) { @@ -277,15 +271,13 @@ std::string UnspentSelector::select_optimal_outputs(Height block_height, Timesta *change = m_used_total - total_amount - fee - change_dust_fee; combine_optimized_unspents(); std::string final_coins; - for (const auto & uu : m_used_unspents) + for (const auto &uu : m_used_unspents) final_coins += " " + common::to_string(uu.amount); m_log(logging::INFO) << "Selected used_total=" << m_used_total << " for total_amount=" << total_amount << ", final coins" << final_coins << std::endl; return std::string(); } - fee = - ((size_fee - change_dust_fee + m_currency.default_dust_threshold - 1) / m_currency.default_dust_threshold) * - m_currency.default_dust_threshold; + fee = ((size_fee - change_dust_fee + dust_threshold - 1) / dust_threshold) * dust_threshold; unoptimize_amounts(&have_coins, &dust_coins); } } @@ -297,7 +289,7 @@ void UnspentSelector::create_have_coins(Height block_height, Timestamp block_tim api::Output &un = *uit; if (un.height >= confirmed_height) // unconfirmed continue; - if (!m_currency.is_transaction_spend_time_unlocked(un.unlock_time, block_height, block_time)) + if (!m_currency.is_transaction_spend_time_unlocked(un.unlock_block_or_timestamp, block_height, block_time)) continue; if (!Currency::is_dust(un.amount)) { Amount am = un.amount; @@ -353,8 +345,8 @@ void UnspentSelector::optimize_amounts(HaveCoins *have_coins, size_t max_digit, continue; size_t best_two_counts[2] = {}; size_t best_weight = 0; - for (const auto & ait : dit->second) - for (const auto & bit : dit->second) { + for (const auto &ait : dit->second) + for (const auto &bit : dit->second) { if ((ait.first + bit.first + am) % 10 == 0 && (ait.second.size() >= TWO_THRESHOLD || bit.second.size() >= TWO_THRESHOLD) && (ait.second.size() + bit.second.size()) > best_weight) { @@ -385,7 +377,7 @@ void UnspentSelector::optimize_amounts(HaveCoins *have_coins, size_t max_digit, continue; size_t best_single = 0; best_weight = 0; - for (const auto & ait : dit->second) + for (const auto &ait : dit->second) if ((ait.first + am) % 10 == 0) { best_single = ait.first; break; diff --git a/src/Core/TransactionBuilder.hpp b/src/Core/TransactionBuilder.hpp index 1bfb15f3..5a0f4ea4 100644 --- a/src/Core/TransactionBuilder.hpp +++ b/src/Core/TransactionBuilder.hpp @@ -7,7 +7,7 @@ #include "CryptoNote.hpp" #include "TransactionExtra.hpp" #include "Wallet.hpp" -#include "crypto/chacha8.h" +#include "crypto/chacha8.hpp" #include "rpc_api.hpp" namespace bytecoin { @@ -31,15 +31,15 @@ class TransactionBuilder { static bool less_amount(const OutputDesc &a, const OutputDesc &b) { return a.amount < b.amount; } }; std::vector m_output_descs; - TransactionExtra m_extra; +// TransactionExtra m_extra; Amount m_outputs_amount = 0; Amount m_inputs_amount = 0; public: - explicit TransactionBuilder(const Currency &, UnlockMoment); + explicit TransactionBuilder(const Currency &, BlockOrTimestamp); void set_payment_id(const Hash &); - void set_extra_nonce(const BinaryArray &); +// void set_extra_nonce(const BinaryArray &); // before calling, make sure mix_outputs do not contain real_output... size_t add_input( @@ -53,10 +53,10 @@ class TransactionBuilder { BinaryArray generate_history(const crypto::chacha8_key &history_key) const; - static crypto::KeyPair deterministic_keys_from_seed(const Hash &tx_inputs_hash, const Hash &tx_derivation_seed); - static crypto::KeyPair deterministic_keys_from_seed(const TransactionPrefix &tx, const Hash &tx_derivation_seed); - static bool generate_key_image_helper(const AccountKeys &ack, const crypto::PublicKey &tx_public_key, - size_t real_output_index, KeyPair &in_ephemeral, crypto::KeyImage &ki); + static KeyPair deterministic_keys_from_seed(const Hash &tx_inputs_hash, const Hash &tx_derivation_seed); + static KeyPair deterministic_keys_from_seed(const TransactionPrefix &tx, const Hash &tx_derivation_seed); + static bool generate_key_image_helper(const AccountKeys &ack, const PublicKey &tx_public_key, + size_t real_output_index, KeyPair &in_ephemeral, KeyImage &ki); static bool derive_public_key( const AccountPublicAddress &to, const SecretKey &tx_key, size_t output_index, PublicKey &ephemeral_key); static std::vector absolute_output_offsets_to_relative(const std::vector &off); @@ -86,7 +86,7 @@ class UnspentSelector { public: explicit UnspentSelector(logging::ILogger &logger, const Currency ¤cy, Unspents &&unspents); void reset(Unspents &&unspents); - void add_mixed_inputs(const SecretKey &view_secret_key, + void add_mixed_inputs(const SecretKey &view_secret_key, const Wallet *wallet, const std::unordered_map &wallet_records, TransactionBuilder *builder, uint32_t anonymity, api::bytecoind::GetRandomOutputs::Response &&ra_response); diff --git a/src/Core/TransactionExtra.cpp b/src/Core/TransactionExtra.cpp index 10390ac6..63eb4a36 100644 --- a/src/Core/TransactionExtra.cpp +++ b/src/Core/TransactionExtra.cpp @@ -9,9 +9,73 @@ #include "seria/BinaryInputStream.hpp" #include "seria/BinaryOutputStream.hpp" -namespace bytecoin { +using namespace bytecoin; + +template +bool set_field_good(const T &, U &){ + return false; +} +template +bool set_field_good(const T & a, T & b){ + b = a; + return true; +} template +bool find_field_in_extra(const BinaryArray &extra, T & field) { + try { + common::MemoryInputStream iss(extra.data(), extra.size()); + seria::BinaryInputStream ar(iss); + + while (!iss.empty()) { + int c = common::read(iss); + switch (c) { + case TransactionExtraPadding::tag: { + size_t size = 1; // tag is itself '0', counts towards max count + for (; !iss.empty() && size <= TransactionExtraPadding::MAX_COUNT; ++size) { + if (common::read(iss) != 0) + return false; // all bytes should be zero + } + if (size > TransactionExtraPadding::MAX_COUNT) + return false; + TransactionExtraPadding padding; + padding.size = size; + return set_field_good(padding, field); // last field + } + case TransactionExtraPublicKey::tag: { + TransactionExtraPublicKey extra_pk; + iss.read(extra_pk.public_key.data, sizeof(extra_pk.public_key.data)); + if(set_field_good(extra_pk, field)) + return true; + break; + } + case TransactionExtraNonce::tag: { + TransactionExtraNonce extra_nonce; + uint8_t size = common::read(iss); + extra_nonce.nonce.resize(size); + iss.read(extra_nonce.nonce.data(), extra_nonce.nonce.size()); + // We have some base transactions (like in blocks 558479, 558984) + // which have wrong extra nonce size, so they will not parse and + // throw here from iss.read + if(set_field_good(extra_nonce, field)) + return true; + break; + } + case TransactionExtraMergeMiningTag::tag: { + TransactionExtraMergeMiningTag mm_tag; + ser(mm_tag, ar); + if(set_field_good(mm_tag, field)) + return true; + break; + } + } + } + } catch (std::exception &) { + } + return false; // Not found +} + +/*template bool find_transaction_extra_field_by_type(const std::vector &tx_extra_fields, T &field) { auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), [](const TransactionExtraField &f) { return typeid(T) == f.type(); }); @@ -23,35 +87,30 @@ bool find_transaction_extra_field_by_type(const std::vector &extra_fields) { +bool bytecoin::parse_transaction_extra(const BinaryArray &extra, std::vector &extra_fields) { extra_fields.clear(); - if (extra.empty()) - return true; +// if (extra.empty()) +// return true; try { common::MemoryInputStream iss(extra.data(), extra.size()); seria::BinaryInputStream ar(iss); - int c = 0; - while (!iss.empty()) { - c = common::read(iss); + int c = common::read(iss); switch (c) { case TransactionExtraPadding::tag: { - size_t size = 1; + size_t size = 1; // tag is itself '0', counts towards max count for (; !iss.empty() && size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) { - if (common::read(iss) != 0) { + if (common::read(iss) != 0) return false; // all bytes should be zero - } } - - if (size > TX_EXTRA_PADDING_MAX_COUNT) { + if (size > TX_EXTRA_PADDING_MAX_COUNT) return false; - } TransactionExtraPadding padding; padding.size = size; - extra_fields.push_back(padding); // TODO - return {} initializer when Google updates NDK copmiler + extra_fields.push_back(padding); // TODO - return {} initializer when Google updates NDK compiler break; } @@ -65,22 +124,22 @@ bool parse_transaction_extra(const BinaryArray &extra, std::vector(iss); - if (size > 0) { +// if (size > 0) { extra_nonce.nonce.resize(size); iss.read(extra_nonce.nonce.data(), extra_nonce.nonce.size()); // We have some base transactions (like in blocks 558479, 558984) // which have wrong // extra nonce size, so they will not parse and throw here from // iss.read - } - +// } extra_fields.push_back(extra_nonce); + if (size > 127) + throw std::runtime_error(""); // TODO - remove before release break; } - case TransactionExtraMergeMiningTag::tag: { TransactionExtraMergeMiningTag mm_tag; - ar(mm_tag); + ser(mm_tag, ar); extra_fields.push_back(mm_tag); break; } @@ -107,15 +166,16 @@ struct ExtraSerializerVisitor : public boost::static_visitor { } bool operator()(const TransactionExtraPublicKey &t) { - return add_transaction_public_key_to_extra(extra, t.public_key); + add_transaction_public_key_to_extra(extra, t.public_key); return true; } - bool operator()(const TransactionExtraNonce &t) { return add_extra_nonce_to_transaction_extra(extra, t.nonce); } + bool operator()(const TransactionExtraNonce &t) { add_extra_nonce_to_transaction_extra(extra, t.nonce); return true; } - bool operator()(const TransactionExtraMergeMiningTag &t) { return append_merge_mining_tag_to_extra(extra, t); } + bool operator()(const TransactionExtraMergeMiningTag &t) { append_merge_mining_tag_to_extra(extra, t); return true; } }; -bool write_transaction_extra(BinaryArray &tx_extra, const std::vector &tx_extra_fields) { +bool bytecoin::write_transaction_extra( + BinaryArray &tx_extra, const std::vector &tx_extra_fields) { ExtraSerializerVisitor visitor(tx_extra); for (const auto &tag : tx_extra_fields) { @@ -125,85 +185,69 @@ bool write_transaction_extra(BinaryArray &tx_extra, const std::vector tx_extra_fields; - parse_transaction_extra(tx_extra, tx_extra_fields); +}*/ +PublicKey bytecoin::extra_get_transaction_public_key(const BinaryArray &tx_extra) { TransactionExtraPublicKey pub_key_field; - if (!find_transaction_extra_field_by_type(tx_extra_fields, pub_key_field)) + if( !find_field_in_extra(tx_extra, pub_key_field) ) return PublicKey{}; - return pub_key_field.public_key; } -bool add_transaction_public_key_to_extra(BinaryArray &tx_extra, const PublicKey &tx_pub_key) { +void bytecoin::extra_add_transaction_public_key(BinaryArray &tx_extra, const PublicKey &tx_pub_key) { tx_extra.push_back(TransactionExtraPublicKey::tag); common::append(tx_extra, std::begin(tx_pub_key.data), std::end(tx_pub_key.data)); - return true; } -bool add_extra_nonce_to_transaction_extra(BinaryArray &tx_extra, const BinaryArray &extra_nonce) { - if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { - return false; - } - - size_t start_pos = tx_extra.size(); - tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); +void bytecoin::extra_add_nonce(BinaryArray &tx_extra, const BinaryArray &extra_nonce) { + if (extra_nonce.size() > TransactionExtraNonce::MAX_COUNT) + throw std::runtime_error("Extra nonce cannot be > " + common::to_string(TransactionExtraNonce::MAX_COUNT)); + tx_extra.push_back(TransactionExtraNonce::tag); + tx_extra.push_back(static_cast(extra_nonce.size())); + common::append(tx_extra, extra_nonce.begin(), extra_nonce.end()); +// size_t start_pos = tx_extra.size(); +// tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); // write tag - tx_extra[start_pos] = TransactionExtraNonce::tag; +// tx_extra[start_pos] = TransactionExtraNonce::tag; // write len - ++start_pos; - tx_extra[start_pos] = static_cast(extra_nonce.size()); +// ++start_pos; +// tx_extra[start_pos] = static_cast(extra_nonce.size()); // write data - ++start_pos; - memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); - return true; +// ++start_pos; +// memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); } -bool append_merge_mining_tag_to_extra(BinaryArray &tx_extra, const TransactionExtraMergeMiningTag &mm_tag) { +void bytecoin::extra_add_merge_mining_tag(BinaryArray &tx_extra, const TransactionExtraMergeMiningTag &mm_tag) { BinaryArray blob = seria::to_binary(mm_tag); tx_extra.push_back(TransactionExtraMergeMiningTag::tag); common::append(tx_extra, blob.begin(), blob.end()); - return true; } -bool get_merge_mining_tag_from_extra(const BinaryArray &tx_extra, TransactionExtraMergeMiningTag &mm_tag) { - std::vector tx_extra_fields; - parse_transaction_extra(tx_extra, tx_extra_fields); - - return find_transaction_extra_field_by_type(tx_extra_fields, mm_tag); +bool bytecoin::extra_get_merge_mining_tag(const BinaryArray &tx_extra, TransactionExtraMergeMiningTag &mm_tag) { + return find_field_in_extra(tx_extra, mm_tag); } -void set_payment_id_to_transaction_extra_nonce(BinaryArray &extra_nonce, const Hash &payment_id) { - extra_nonce.clear(); - extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); +void bytecoin::extra_add_payment_id(BinaryArray &tx_extra, const Hash &payment_id) { + BinaryArray extra_nonce; + extra_nonce.push_back(TransactionExtraNonce::PAYMENT_ID); common::append(extra_nonce, std::begin(payment_id.data), std::end(payment_id.data)); + extra_add_nonce(tx_extra, extra_nonce); } -bool get_payment_id_from_transaction_extra_nonce(const BinaryArray &extra_nonce, Hash &payment_id) { - if (sizeof(Hash) + 1 != extra_nonce.size()) - return false; - if (TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) - return false; - payment_id = *reinterpret_cast(extra_nonce.data() + 1); - return true; -} - -bool get_payment_id_from_tx_extra(const BinaryArray &extra, Hash &payment_id) { - std::vector tx_extra_fields; - parse_transaction_extra(extra, tx_extra_fields); +bool bytecoin::extra_get_payment_id(const BinaryArray &tx_extra, Hash &payment_id) { TransactionExtraNonce extra_nonce; - if (!find_transaction_extra_field_by_type(tx_extra_fields, extra_nonce)) + if (!find_field_in_extra(tx_extra, extra_nonce)) + return false; + if (extra_nonce.nonce.size() != sizeof(Hash) + 1) return false; - if (!get_payment_id_from_transaction_extra_nonce(extra_nonce.nonce, payment_id)) + if (extra_nonce.nonce.at(0) != TransactionExtraNonce::PAYMENT_ID) return false; +// memcpy(payment_id.data, extra_nonce.nonce.data() + 1, sizeof(Hash)); + std::copy(extra_nonce.nonce.begin() + 1, extra_nonce.nonce.end(), payment_id.data); return true; } -} -static void do_serialize(bytecoin::TransactionExtraMergeMiningTag &tag, seria::ISeria &s) { +static void do_serialize(TransactionExtraMergeMiningTag &tag, seria::ISeria &s) { s.begin_object(); uint64_t depth = static_cast(tag.depth); seria_kv("depth", depth, s); @@ -212,10 +256,10 @@ static void do_serialize(bytecoin::TransactionExtraMergeMiningTag &tag, seria::I s.end_object(); } -void seria::ser(bytecoin::TransactionExtraMergeMiningTag &v, ISeria &s) { +void seria::ser(TransactionExtraMergeMiningTag &v, ISeria &s) { if (s.is_input()) { std::string field; - s(field); + ser(field, s); common::MemoryInputStream stream(field.data(), field.size()); seria::BinaryInputStream input(stream); do_serialize(v, input); @@ -224,6 +268,6 @@ void seria::ser(bytecoin::TransactionExtraMergeMiningTag &v, ISeria &s) { common::StringOutputStream os(field); seria::BinaryOutputStream output(os); do_serialize(v, output); - s(field); + ser(field, s); } } diff --git a/src/Core/TransactionExtra.hpp b/src/Core/TransactionExtra.hpp index 00a8514b..99a78d05 100644 --- a/src/Core/TransactionExtra.hpp +++ b/src/Core/TransactionExtra.hpp @@ -11,26 +11,24 @@ namespace bytecoin { -enum { TX_EXTRA_PADDING_MAX_COUNT = 255, TX_EXTRA_NONCE_MAX_COUNT = 255, TX_EXTRA_NONCE_PAYMENT_ID = 0x00 }; - struct TransactionExtraPadding { size_t size = 0; - enum { tag = 0x00 }; + enum { tag = 0x00, MAX_COUNT = 255 }; }; struct TransactionExtraPublicKey { - crypto::PublicKey public_key; + PublicKey public_key; enum { tag = 0x01 }; }; struct TransactionExtraNonce { BinaryArray nonce; - enum { tag = 0x02 }; + enum { tag = 0x02, MAX_COUNT = 255, PAYMENT_ID = 0x00 }; }; struct TransactionExtraMergeMiningTag { size_t depth = 0; - crypto::Hash merkle_root; + Hash merkle_root; enum { tag = 0x03 }; }; @@ -38,24 +36,26 @@ struct TransactionExtraMergeMiningTag { // varint tag; // varint size; // varint data[]; -typedef boost::variant - TransactionExtraField; +//typedef boost::variant +// TransactionExtraField; + +//bool parse_transaction_extra(const BinaryArray &tx_extra, std::vector &tx_extra_fields); +//bool write_transaction_extra(BinaryArray &tx_extra, const std::vector &tx_extra_fields); + +PublicKey extra_get_transaction_public_key(const BinaryArray &tx_extra); +void extra_add_transaction_public_key(BinaryArray &tx_extra, const PublicKey &tx_pub_key); -bool parse_transaction_extra(const BinaryArray &tx_extra, std::vector &tx_extra_fields); -bool write_transaction_extra(BinaryArray &tx_extra, const std::vector &tx_extra_fields); +void extra_add_nonce(BinaryArray &tx_extra, const BinaryArray &extra_nonce); -crypto::PublicKey get_transaction_public_key_from_extra(const BinaryArray &tx_extra); -bool add_transaction_public_key_to_extra(BinaryArray &tx_extra, const crypto::PublicKey &tx_pub_key); -bool add_extra_nonce_to_transaction_extra(BinaryArray &tx_extra, const BinaryArray &extra_nonce); -void set_payment_id_to_transaction_extra_nonce(BinaryArray &extra_nonce, const crypto::Hash &payment_id); -bool get_payment_id_from_transaction_extra_nonce(const BinaryArray &extra_nonce, crypto::Hash &payment_id); -bool append_merge_mining_tag_to_extra(BinaryArray &tx_extra, const TransactionExtraMergeMiningTag &mm_tag); -bool get_merge_mining_tag_from_extra(const BinaryArray &tx_extra, TransactionExtraMergeMiningTag &mm_tag); +void extra_add_merge_mining_tag(BinaryArray &tx_extra, const TransactionExtraMergeMiningTag &mm_tag); +bool extra_get_merge_mining_tag(const BinaryArray &tx_extra, TransactionExtraMergeMiningTag &mm_tag); -bool get_payment_id_from_tx_extra(const BinaryArray &extra, crypto::Hash &payment_id); +void extra_add_payment_id(BinaryArray &tx_extra, const Hash &payment_id); +bool extra_get_payment_id(const BinaryArray &tx_extra, Hash &payment_id); -class TransactionExtra { + +/*class TransactionExtra { public: TransactionExtra() {} TransactionExtra(const BinaryArray &extra) { parse(extra); } @@ -87,8 +87,8 @@ class TransactionExtra { m_fields.push_back(value); } - bool get_public_key(crypto::PublicKey &pk) const { - bytecoin::TransactionExtraPublicKey extra_pk; + bool get_public_key(PublicKey &pk) const { + TransactionExtraPublicKey extra_pk; if (!get(extra_pk)) { return false; } @@ -103,17 +103,17 @@ class TransactionExtra { } private: - std::vector::const_iterator find(const std::type_info &t) const { + std::vector::const_iterator find(const std::type_info &t) const { return std::find_if( - m_fields.begin(), m_fields.end(), [&t](const bytecoin::TransactionExtraField &f) { return t == f.type(); }); + m_fields.begin(), m_fields.end(), [&t](const TransactionExtraField &f) { return t == f.type(); }); } - std::vector::iterator find(const std::type_info &t) { + std::vector::iterator find(const std::type_info &t) { return std::find_if( - m_fields.begin(), m_fields.end(), [&t](const bytecoin::TransactionExtraField &f) { return t == f.type(); }); + m_fields.begin(), m_fields.end(), [&t](const TransactionExtraField &f) { return t == f.type(); }); } - std::vector m_fields; -}; + std::vector m_fields; +};*/ } namespace seria { diff --git a/src/Core/Wallet.cpp b/src/Core/Wallet.cpp index 9692bcbe..1c584c9d 100644 --- a/src/Core/Wallet.cpp +++ b/src/Core/Wallet.cpp @@ -4,8 +4,10 @@ #include "CryptoNoteTools.hpp" #include "WalletSerializationV1.hpp" #include "WalletState.hpp" +#include "common/Math.hpp" #include "common/MemoryStreams.hpp" #include "common/StringTools.hpp" +#include "common/Varint.hpp" #include "crypto/crypto.hpp" #include "http/JsonRpc.hpp" #include "platform/Files.hpp" @@ -16,41 +18,43 @@ using namespace bytecoin; static const uint8_t SERIALIZATION_VERSION_V2 = 6; -static const size_t CHECK_KEYS_COUNT = 100; +static const size_t CHECK_KEYS_COUNT = 128; // >8 KB checked at start and end of file #pragma pack(push, 1) struct EncryptedWalletRecord { crypto::chacha8_iv iv; // Secret key, public key and creation timestamp - uint8_t data[sizeof(crypto::PublicKey) + sizeof(crypto::SecretKey) + sizeof(uint64_t)]{}; + uint8_t data[sizeof(PublicKey) + sizeof(SecretKey) + sizeof(uint64_t)]{}; }; struct ContainerStoragePrefix { // We moved uint8_t version out of this struct, because with it other fields become unaligned crypto::chacha8_iv next_iv; EncryptedWalletRecord encrypted_view_keys; }; -struct ContainerStorageWalletRecord { - crypto::PublicKey pk{}; - crypto::SecretKey sk{}; - uint64_t ct = 0; -}; +// struct ContainerStorageWalletRecord { +// PublicKey pk{}; +// SecretKey sk{}; +// uint64_t ct = 0; +//}; #pragma pack(pop) static void decrypt_key_pair( const EncryptedWalletRecord &r, PublicKey &pk, SecretKey &sk, Timestamp &ct, const WalletKey &key) { - ContainerStorageWalletRecord rec; - chacha8(r.data, sizeof(r.data), key, r.iv, &rec); - pk = rec.pk; - sk = rec.sk; - ct = static_cast(rec.ct); + // ContainerStorageWalletRecord rec; + unsigned char rec_data[sizeof(r.data)]{}; + chacha8(r.data, sizeof(r.data), key, r.iv, rec_data); + memcpy(pk.data, rec_data, sizeof(PublicKey)); + memcpy(sk.data, rec_data + sizeof(PublicKey), sizeof(SecretKey)); + ct = static_cast( + common::uint_le_from_bytes(rec_data + sizeof(PublicKey) + sizeof(SecretKey), sizeof(uint64_t))); } static void encrypt_key_pair(EncryptedWalletRecord &r, PublicKey pk, SecretKey sk, Timestamp ct, const WalletKey &key) { - ContainerStorageWalletRecord rec; - rec.pk = pk; - rec.sk = sk; - rec.ct = ct; - r.iv = crypto::rand(); - chacha8(&rec, sizeof(r.data), key, r.iv, r.data); + unsigned char rec_data[sizeof(r.data)]{}; + memcpy(rec_data, pk.data, sizeof(PublicKey)); + memcpy(rec_data + sizeof(PublicKey), sk.data, sizeof(SecretKey)); + common::uint_le_to_bytes(rec_data + sizeof(PublicKey) + sizeof(SecretKey), sizeof(uint64_t), ct); + r.iv = crypto::rand(); + chacha8(&rec_data, sizeof(r.data), key, r.iv, r.data); } size_t Wallet::wallet_file_size(size_t records) { @@ -60,12 +64,13 @@ size_t Wallet::wallet_file_size(size_t records) { void Wallet::load_container_storage() { uint8_t version = 0; ContainerStoragePrefix prefix{}; - uint64_t f_item_capacity = 0; - uint64_t f_item_count = 0; + unsigned char count_capacity_data[2 * sizeof(uint64_t)]{}; file->read(&version, 1); - file->read(reinterpret_cast(&prefix), sizeof(prefix)); - file->read(reinterpret_cast(&f_item_capacity), sizeof(f_item_capacity)); - file->read(reinterpret_cast(&f_item_count), sizeof(f_item_count)); + file->read(&prefix, sizeof(prefix)); + file->read(count_capacity_data, 2 * sizeof(uint64_t)); + uint64_t f_item_capacity = common::uint_le_from_bytes(count_capacity_data, sizeof(uint64_t)); + uint64_t f_item_count = + common::uint_le_from_bytes(count_capacity_data + sizeof(uint64_t), sizeof(uint64_t)); if (version < SERIALIZATION_VERSION_V2) throw Exception(api::WALLET_FILE_DECRYPT_ERROR, "Wallet version too old"); @@ -77,10 +82,14 @@ void Wallet::load_container_storage() { throw Exception(api::WALLET_FILE_DECRYPT_ERROR, "Restored view public key doesn't correspond to secret key"); const size_t item_count = - boost::lexical_cast(std::min(f_item_count, f_item_capacity)); // Protection against write shredding + common::integer_cast(std::min(f_item_count, f_item_capacity)); // Protection against write shredding + if (item_count > std::numeric_limits::max() / sizeof(EncryptedWalletRecord)) + throw Exception( + api::WALLET_FILE_DECRYPT_ERROR, "Restored item count is too big " + common::to_string(item_count)); std::vector all_encrypted(item_count); file->read(reinterpret_cast(all_encrypted.data()), sizeof(EncryptedWalletRecord) * item_count); bool tracking_mode = false; + m_wallet_records.reserve(item_count); for (size_t i = 0; i != item_count; ++i) { WalletRecord wallet_record; decrypt_key_pair(all_encrypted[i], wallet_record.spend_public_key, wallet_record.spend_secret_key, @@ -88,7 +97,6 @@ void Wallet::load_container_storage() { if (i == 0) { tracking_mode = wallet_record.spend_secret_key == SecretKey{}; - first_record = wallet_record; } else if ((tracking_mode && wallet_record.spend_secret_key != SecretKey{}) || (!tracking_mode && wallet_record.spend_secret_key == SecretKey{})) { throw Exception(api::WALLET_FILE_DECRYPT_ERROR, "All addresses must be either tracking or not"); @@ -106,7 +114,8 @@ void Wallet::load_container_storage() { } } m_oldest_timestamp = std::min(m_oldest_timestamp, wallet_record.creation_timestamp); - m_wallet_records.insert(std::make_pair(wallet_record.spend_public_key, wallet_record)); + m_records_map.insert(std::make_pair(wallet_record.spend_public_key, m_wallet_records.size())); + m_wallet_records.push_back(wallet_record); } auto file_size = file->seek(0, SEEK_END); auto should_be_file_size = wallet_file_size(item_count); @@ -121,16 +130,18 @@ void Wallet::load_container_storage() { } void Wallet::load_legacy_wallet_file() { - std::vector wallets_container; + // m_wallet_records.clear(); + // std::vector wallets_container; - WalletSerializerV1 s(m_view_public_key, m_view_secret_key, wallets_container); + WalletSerializerV1 s(m_view_public_key, m_view_secret_key, m_wallet_records); s.load(m_wallet_key, *file.get()); - first_record = wallets_container.at(0); - for (auto &&w : wallets_container) { - m_oldest_timestamp = std::min(m_oldest_timestamp, w.creation_timestamp); - m_wallet_records.insert(std::make_pair(w.spend_public_key, w)); + // m_wallet_records.reserve() + // m_first_record = wallets_container.at(0); + for (size_t i = 0; i != m_wallet_records.size(); ++i) { + m_oldest_timestamp = std::min(m_oldest_timestamp, m_wallet_records[i].creation_timestamp); + m_records_map.insert(std::make_pair(m_wallet_records[i].spend_public_key, i)); } } @@ -152,9 +163,9 @@ Wallet::Wallet(logging::ILogger &log, const std::string &path, const std::string if (import_keys.empty()) { m_oldest_timestamp = platform::now_unix_timestamp(); crypto::random_keypair(m_view_public_key, m_view_secret_key); - first_record.creation_timestamp = m_oldest_timestamp; - crypto::random_keypair(first_record.spend_public_key, first_record.spend_secret_key); - m_wallet_records.insert(std::make_pair(first_record.spend_public_key, first_record)); + m_wallet_records.push_back(WalletRecord{}); + m_wallet_records.at(0).creation_timestamp = m_oldest_timestamp; + crypto::random_keypair(m_wallet_records.at(0).spend_public_key, m_wallet_records.at(0).spend_secret_key); } else { if (import_keys.size() != 256) throw Exception(api::WALLET_FILE_DECRYPT_ERROR, "Imported keys should be exactly 128 hex bytes"); @@ -170,10 +181,10 @@ Wallet::Wallet(logging::ILogger &log, const std::string &path, const std::string if (record.spend_secret_key != SecretKey{} && !keys_match(record.spend_secret_key, record.spend_public_key)) throw Exception(api::WALLET_FILE_DECRYPT_ERROR, "Imported secret spend key does not match corresponding public key"); - m_wallet_records.insert(std::make_pair(record.spend_public_key, record)); - first_record = m_wallet_records.begin()->second; + m_wallet_records.push_back(record); m_oldest_timestamp = 0; // Alas, will scan entire blockchain } + m_records_map.insert(std::make_pair(m_wallet_records.at(0).spend_public_key, 0)); save_and_check(); } try { @@ -190,9 +201,11 @@ Wallet::Wallet(logging::ILogger &log, const std::string &path, const std::string try { load_legacy_wallet_file(); } catch (const common::StreamError &ex) { - throw Exception(api::WALLET_FILE_READ_ERROR, std::string("Error reading wallet file ") + ex.what()); + std::throw_with_nested( + Exception(api::WALLET_FILE_READ_ERROR, std::string("Error reading wallet file ") + common::what(ex))); } catch (const std::exception &ex) { - throw Exception(api::WALLET_FILE_DECRYPT_ERROR, std::string("Error decrypting wallet file ") + ex.what()); + std::throw_with_nested(Exception( + api::WALLET_FILE_DECRYPT_ERROR, std::string("Error decrypting wallet file ") + common::what(ex))); } file.reset(); // Indicates legacy format try { @@ -209,50 +222,57 @@ Wallet::Wallet(logging::ILogger &log, const std::string &path, const std::string if (!is_view_only()) { BinaryArray seed_data; seed_data.assign(std::begin(m_view_secret_key.data), std::end(m_view_secret_key.data)); - common::append( - seed_data, std::begin(first_record.spend_secret_key.data), std::end(first_record.spend_secret_key.data)); + common::append(seed_data, std::begin(m_wallet_records.at(0).spend_secret_key.data), + std::end(m_wallet_records.at(0).spend_secret_key.data)); m_seed = crypto::cn_fast_hash(seed_data.data(), seed_data.size()); - const unsigned char derivation_prefix[] = "tx_derivation"; - seed_data.assign(derivation_prefix, derivation_prefix + sizeof(derivation_prefix) - 1); - common::append(seed_data, std::begin(m_seed.data), std::end(m_seed.data)); - m_tx_derivation_seed = crypto::cn_fast_hash(seed_data.data(), seed_data.size()); - - const unsigned char history_filename_prefix[] = "history_filename"; - seed_data.assign(history_filename_prefix, history_filename_prefix + sizeof(history_filename_prefix) - 1); - common::append(seed_data, std::begin(m_seed.data), std::end(m_seed.data)); - m_history_filename_seed = crypto::cn_fast_hash(seed_data.data(), seed_data.size()); - - const unsigned char history_prefix[] = "history"; - seed_data.assign(history_prefix, history_prefix + sizeof(history_prefix) - 1); - common::append(seed_data, std::begin(m_seed.data), std::end(m_seed.data)); - m_history_key = crypto::chacha8_key{crypto::cn_fast_hash(seed_data.data(), seed_data.size())}; + // const unsigned char derivation_prefix[] = "tx_derivation"; + // seed_data.assign(derivation_prefix, derivation_prefix + sizeof(derivation_prefix) - 1); + // common::append(seed_data, std::begin(m_seed.data), std::end(m_seed.data)); + // m_tx_derivation_seed = crypto::cn_fast_hash(seed_data.data(), seed_data.size()); + m_tx_derivation_seed = derive_from_seed("tx_derivation"); + + m_coinbase_tx_derivation_seed = derive_from_seed("coinbase_tx_derivation"); + + // const unsigned char history_filename_prefix[] = "history_filename"; + // seed_data.assign(history_filename_prefix, history_filename_prefix + sizeof(history_filename_prefix) - + // 1); + // common::append(seed_data, std::begin(m_seed.data), std::end(m_seed.data)); + // m_history_filename_seed = crypto::cn_fast_hash(seed_data.data(), seed_data.size()); + m_history_filename_seed = derive_from_seed("history_filename"); + + // const unsigned char history_prefix[] = "history"; + // seed_data.assign(history_prefix, history_prefix + sizeof(history_prefix) - 1); + // common::append(seed_data, std::begin(m_seed.data), std::end(m_seed.data)); + // m_history_key = crypto::chacha8_key{crypto::cn_fast_hash(seed_data.data(), seed_data.size())}; + m_history_key = crypto::chacha8_key{derive_from_seed("history")}; } } -void Wallet::save(const std::string &export_path, bool view_only) { +Hash Wallet::derive_from_seed(const std::string &append) { + BinaryArray seed_data(append.data(), append.data() + append.size()); + common::append(seed_data, std::begin(m_seed.data), std::end(m_seed.data)); + return crypto::cn_fast_hash(seed_data.data(), seed_data.size()); +} + +void Wallet::save(const std::string &export_path, const WalletKey &wallet_key, bool view_only) { platform::FileStream f(export_path, platform::FileStream::TRUNCATE_READ_WRITE); uint8_t version = SERIALIZATION_VERSION_V2; ContainerStoragePrefix prefix{}; - encrypt_key_pair( - prefix.encrypted_view_keys, m_view_public_key, m_view_secret_key, m_oldest_timestamp, m_wallet_key); - uint64_t item_count = m_wallet_records.size(); + encrypt_key_pair(prefix.encrypted_view_keys, m_view_public_key, m_view_secret_key, m_oldest_timestamp, wallet_key); + unsigned char count_capacity_data[sizeof(uint64_t)]{}; + common::uint_le_to_bytes(count_capacity_data, sizeof(uint64_t), m_wallet_records.size()); f.write(&version, 1); f.write(&prefix, sizeof(prefix)); - f.write(&item_count, sizeof(item_count)); // we set capacity to item_count - f.write(&item_count, sizeof(item_count)); - EncryptedWalletRecord record; - encrypt_key_pair(record, first_record.spend_public_key, view_only ? SecretKey{} : first_record.spend_secret_key, - first_record.creation_timestamp, m_wallet_key); - f.write(&record, sizeof(record)); - for (auto &&r : m_wallet_records) { - if (r.second.spend_public_key == first_record.spend_public_key) - continue; - encrypt_key_pair(record, r.second.spend_public_key, view_only ? SecretKey{} : r.second.spend_secret_key, - r.second.creation_timestamp, m_wallet_key); + f.write(count_capacity_data, sizeof(uint64_t)); // we set capacity to item_count + f.write(count_capacity_data, sizeof(uint64_t)); + EncryptedWalletRecord record; + for (const auto &rec : m_wallet_records) { + encrypt_key_pair(record, rec.spend_public_key, view_only ? SecretKey{} : rec.spend_secret_key, + rec.creation_timestamp, wallet_key); f.write(&record, sizeof(record)); } f.fsync(); @@ -260,14 +280,11 @@ void Wallet::save(const std::string &export_path, bool view_only) { BinaryArray Wallet::export_keys() const { BinaryArray result; - if (m_wallet_records.size() != 1) - throw Exception( - api::WALLETD_EXPORTKEYS_MORETHANONE, "You can only export keys from a wallet containing 1 address"); - common::append( - result, std::begin(first_record.spend_public_key.data), std::end(first_record.spend_public_key.data)); + common::append(result, std::begin(m_wallet_records.at(0).spend_public_key.data), + std::end(m_wallet_records.at(0).spend_public_key.data)); common::append(result, std::begin(m_view_public_key.data), std::end(m_view_public_key.data)); - common::append( - result, std::begin(first_record.spend_secret_key.data), std::end(first_record.spend_secret_key.data)); + common::append(result, std::begin(m_wallet_records.at(0).spend_secret_key.data), + std::end(m_wallet_records.at(0).spend_secret_key.data)); common::append(result, std::begin(m_view_secret_key.data), std::end(m_view_secret_key.data)); return result; } @@ -275,7 +292,7 @@ BinaryArray Wallet::export_keys() const { void Wallet::save_and_check() { const std::string tmp_path = m_path + ".tmp"; - save(tmp_path, false); + save(tmp_path, m_wallet_key, false); Wallet other(m_log.get_logger(), tmp_path, m_password); if (*this != other) @@ -293,7 +310,7 @@ void Wallet::set_password(const std::string &password) { save_and_check(); } -void Wallet::export_wallet(const std::string &export_path, bool view_only) { +void Wallet::export_wallet(const std::string &export_path, const std::string &new_password, bool view_only) { std::unique_ptr export_file; try { export_file.reset(new platform::FileStream(export_path, platform::FileStream::READ_EXISTING)); @@ -303,18 +320,20 @@ void Wallet::export_wallet(const std::string &export_path, bool view_only) { if (export_file.get()) // opened ok throw Exception(api::WALLET_FILE_EXISTS, "Will not overwrite existing wallet - delete it first or specify another file " + export_path); - for (auto &&r : m_wallet_records) { - if (r.second.spend_secret_key != SecretKey{}) { - if (!keys_match(r.second.spend_secret_key, r.second.spend_public_key)) + for (const auto &rec : m_wallet_records) { + if (rec.spend_secret_key != SecretKey{}) { + if (!keys_match(rec.spend_secret_key, rec.spend_public_key)) throw Exception(api::WALLET_FILE_DECRYPT_ERROR, "Spend public key doesn't correspond to secret key (corrupted wallet?)"); } else { - if (!key_isvalid(r.second.spend_public_key)) { + if (!key_isvalid(rec.spend_public_key)) { throw Exception(api::WALLET_FILE_DECRYPT_ERROR, "Public spend key is incorrect (corrupted wallet?)"); } } } - save(export_path, view_only); + crypto::CryptoNightContext cn_ctx; + auto new_wallet_key = generate_chacha8_key(cn_ctx, new_password); + save(export_path, new_wallet_key, view_only); } bool Wallet::operator==(const Wallet &other) const { @@ -323,7 +342,7 @@ bool Wallet::operator==(const Wallet &other) const { } AccountPublicAddress Wallet::get_first_address() const { - return AccountPublicAddress{first_record.spend_public_key, m_view_public_key}; + return AccountPublicAddress{m_wallet_records.at(0).spend_public_key, m_view_public_key}; } std::vector Wallet::generate_new_addresses( @@ -344,7 +363,7 @@ std::vector Wallet::generate_new_addresses( record.creation_timestamp = now; do { crypto::random_keypair(record.spend_public_key, record.spend_secret_key); - } while (m_wallet_records.count(record.spend_public_key) != 0); + } while (m_records_map.count(record.spend_public_key) != 0); m_oldest_timestamp = std::min(m_oldest_timestamp, record.creation_timestamp); } else { record.creation_timestamp = ct; @@ -352,31 +371,33 @@ std::vector Wallet::generate_new_addresses( if (!secret_key_to_public_key(sk, record.spend_public_key)) throw Exception(101, "Imported keypair is invalid - sk=" + common::pod_to_hex(sk)); } - if (first_record.spend_public_key == record.spend_public_key && - first_record.creation_timestamp > record.creation_timestamp) - first_record.creation_timestamp = record.creation_timestamp; - auto rit = m_wallet_records.find(record.spend_public_key); - if (rit != m_wallet_records.end()) { - if (rit->second.creation_timestamp > record.creation_timestamp) { - rit->second.creation_timestamp = record.creation_timestamp; - m_oldest_timestamp = std::min(m_oldest_timestamp, record.creation_timestamp); - *rescan_from_ct = true; + auto rit = m_records_map.find(record.spend_public_key); + if (rit != m_records_map.end()) { + if (m_wallet_records.at(rit->second).creation_timestamp > record.creation_timestamp) { + m_wallet_records.at(rit->second).creation_timestamp = record.creation_timestamp; + m_oldest_timestamp = std::min(m_oldest_timestamp, record.creation_timestamp); + *rescan_from_ct = true; } - result.push_back(rit->second); + result.push_back(m_wallet_records.at(rit->second)); continue; } - rit = m_wallet_records.insert(std::make_pair(record.spend_public_key, record)).first; + m_records_map.insert(std::make_pair(record.spend_public_key, m_wallet_records.size())); + m_wallet_records.push_back(record); EncryptedWalletRecord enc_record; - encrypt_key_pair(enc_record, rit->second.spend_public_key, rit->second.spend_secret_key, - rit->second.creation_timestamp, m_wallet_key); + encrypt_key_pair( + enc_record, record.spend_public_key, record.spend_secret_key, record.creation_timestamp, m_wallet_key); file->write(&enc_record, sizeof(enc_record)); - result.push_back(rit->second); + result.push_back(record); } file->fsync(); file->seek(1 + sizeof(ContainerStoragePrefix), SEEK_SET); - uint64_t item_count = m_wallet_records.size(); - file->write(&item_count, sizeof(item_count)); - file->write(&item_count, sizeof(item_count)); + + unsigned char count_capacity_data[sizeof(uint64_t)]{}; + common::uint_le_to_bytes(count_capacity_data, sizeof(uint64_t), m_wallet_records.size()); + + file->write(count_capacity_data, sizeof(uint64_t)); + file->write(count_capacity_data, sizeof(uint64_t)); + file->fsync(); if (*rescan_from_ct) { // We never write to the middle of the file m_log(logging::WARNING) << "Updating creation timestamp of existing addresses to " << ct @@ -387,14 +408,12 @@ std::vector Wallet::generate_new_addresses( } void Wallet::on_first_output_found(Timestamp ts) { - if (ts == 0 || m_oldest_timestamp >= ts) // TODO - investigate why ts == 0 is possible + if (ts == 0 || m_oldest_timestamp != 0) // TODO - investigate why ts == 0 is possible return; m_oldest_timestamp = ts; - if (first_record.creation_timestamp < ts) - first_record.creation_timestamp = ts; for (auto &&rec : m_wallet_records) - if (rec.second.creation_timestamp < ts) - rec.second.creation_timestamp = ts; + if (rec.creation_timestamp == 0) + rec.creation_timestamp = ts; m_log(logging::WARNING) << "Updating creation timestamp to " << ts << " in a wallet file (might take minutes for large wallets)..." << std::endl; save_and_check(); @@ -405,24 +424,20 @@ std::string Wallet::get_cache_name() const { return common::pod_to_hex(h) + (is_view_only() ? "-view-only" : std::string()); } -bool Wallet::spend_keys_for_address(const AccountPublicAddress &addr, AccountKeys &keys) const { - auto sec = m_wallet_records.find(addr.spend_public_key); - if (m_view_public_key != addr.view_public_key || sec == m_wallet_records.end() || - sec->second.spend_public_key != addr.spend_public_key || sec->second.spend_secret_key == SecretKey{}) +bool Wallet::is_our_address(const AccountPublicAddress &addr) const { + auto rit = m_records_map.find(addr.spend_public_key); + if (m_view_public_key != addr.view_public_key || rit == m_records_map.end()) return false; - keys.address = addr; - keys.spend_secret_key = sec->second.spend_secret_key; - keys.view_secret_key = m_view_secret_key; - return true; + return m_wallet_records.at(rit->second).spend_public_key == addr.spend_public_key; } -bool Wallet::get_only_record( - std::unordered_map &records, const AccountPublicAddress &addr) const { - auto rit = m_wallet_records.find(addr.spend_public_key); - if (rit == m_wallet_records.end() || rit->second.spend_public_key != addr.spend_public_key || - m_view_public_key != addr.view_public_key) +bool Wallet::get_record(WalletRecord &record, const AccountPublicAddress &addr) const { + auto rit = m_records_map.find(addr.spend_public_key); + if (m_view_public_key != addr.view_public_key || rit == m_records_map.end()) return false; - records.insert(*rit); + if (m_wallet_records.at(rit->second).spend_public_key != addr.spend_public_key) + return false; // TODO - invariant + record = m_wallet_records.at(rit->second); return true; } diff --git a/src/Core/Wallet.hpp b/src/Core/Wallet.hpp index f31eca48..d44cde56 100644 --- a/src/Core/Wallet.hpp +++ b/src/Core/Wallet.hpp @@ -7,7 +7,7 @@ #include #include "CryptoNote.hpp" #include "Currency.hpp" -#include "crypto/chacha8.h" +#include "crypto/chacha8.hpp" #include "logging/LoggerMessage.hpp" #include "platform/Files.hpp" @@ -17,8 +17,8 @@ using WalletKey = crypto::chacha8_key; using HistoryKey = crypto::chacha8_key; struct WalletRecord { - crypto::PublicKey spend_public_key{}; - crypto::SecretKey spend_secret_key{}; + PublicKey spend_public_key{}; + SecretKey spend_secret_key{}; Timestamp creation_timestamp = 0; }; @@ -40,24 +40,27 @@ class Wallet { PublicKey m_view_public_key; SecretKey m_view_secret_key; - WalletRecord first_record; - std::unordered_map m_wallet_records; + std::vector m_wallet_records; + std::unordered_map m_records_map; // index into vector // Timestamp m_creation_timestamp = 0; Timestamp m_oldest_timestamp = std::numeric_limits::max(); - Hash m_seed; // Main seed, never used directly - Hash m_tx_derivation_seed; // Hashed from seed - HistoryKey m_history_key; // Hashed from seed - Hash m_history_filename_seed; // Hashed from seed + Hash m_seed; // Main seed, never used directly + Hash m_tx_derivation_seed; // Hashed from seed + Hash m_coinbase_tx_derivation_seed; // Hashed from seed + HistoryKey m_history_key; // Hashed from seed + Hash m_history_filename_seed; // Hashed from seed void load_container_storage(); void load_legacy_wallet_file(); bool operator==(const Wallet &) const; bool operator!=(const Wallet &other) const { return !(*this == other); } - void save(const std::string &export_path, bool view_only); + void save(const std::string &export_path, const WalletKey &wallet_key, bool view_only); void save_and_check(); + Hash derive_from_seed(const std::string &append); + public: class Exception : public std::runtime_error { public: @@ -69,15 +72,15 @@ class Wallet { std::vector generate_new_addresses(const std::vector &sks, Timestamp ct, Timestamp now, bool *rescan_from_ct); // set secret_key to SecretKey{} to generate void set_password(const std::string &password); - void export_wallet(const std::string &export_path, bool view_only); - bool is_view_only() const { return first_record.spend_secret_key == SecretKey{}; } + void export_wallet(const std::string &export_path, const std::string &new_password, bool view_only); + bool is_view_only() const { return m_wallet_records.at(0).spend_secret_key == SecretKey{}; } BinaryArray export_keys() const; const PublicKey &get_view_public_key() const { return m_view_public_key; } const SecretKey &get_view_secret_key() const { return m_view_secret_key; } - const std::unordered_map &get_records() const { return m_wallet_records; } - bool get_only_record(std::unordered_map &records, const AccountPublicAddress &) const; + const std::vector &get_records() const { return m_wallet_records; } + bool get_record(WalletRecord &record, const AccountPublicAddress &) const; - bool spend_keys_for_address(const AccountPublicAddress &, AccountKeys &) const; + bool is_our_address(const AccountPublicAddress &) const; AccountPublicAddress get_first_address() const; static size_t wallet_file_size(size_t records); @@ -85,6 +88,7 @@ class Wallet { std::string get_cache_name() const; const Hash &get_tx_derivation_seed() const { return m_tx_derivation_seed; } + const Hash &get_coinbase_tx_derivation_seed() const { return m_coinbase_tx_derivation_seed; } const HistoryKey &get_history_key() const { return m_history_key; } const Hash &get_history_filename_seed() const { return m_history_filename_seed; } std::string get_history_folder() const { return m_path + ".history"; } diff --git a/src/Core/WalletNode.cpp b/src/Core/WalletNode.cpp index 014afe31..9c64ddd4 100644 --- a/src/Core/WalletNode.cpp +++ b/src/Core/WalletNode.cpp @@ -5,6 +5,7 @@ #include "Config.hpp" #include "CryptoNoteTools.hpp" #include "TransactionBuilder.hpp" +#include "common/Math.hpp" #include "platform/Time.hpp" #include "seria/BinaryInputStream.hpp" #include "seria/BinaryOutputStream.hpp" @@ -13,18 +14,19 @@ using namespace bytecoin; -const WalletNode::HandlersMap WalletNode::m_jsonrpc3_handlers = { - {api::walletd::GetStatus::method(), json_rpc::make_member_method(&WalletNode::handle_get_status3)}, - {api::walletd::GetAddresses::method(), json_rpc::make_member_method(&WalletNode::handle_get_addresses3)}, - {api::walletd::CreateAddresses::method(), json_rpc::make_member_method(&WalletNode::handle_create_address_list3)}, - {api::walletd::GetViewKeyPair::method(), json_rpc::make_member_method(&WalletNode::handle_get_view_key3)}, - {api::walletd::GetBalance::method(), json_rpc::make_member_method(&WalletNode::handle_get_balance3)}, - {api::walletd::GetUnspents::method(), json_rpc::make_member_method(&WalletNode::handle_get_unspent3)}, - {api::walletd::GetTransfers::method(), json_rpc::make_member_method(&WalletNode::handle_get_transfers3)}, - {api::walletd::CreateTransaction::method(), json_rpc::make_member_method(&WalletNode::handle_create_transaction3)}, - {api::walletd::SendTransaction::method(), json_rpc::make_member_method(&WalletNode::handle_send_transaction3)}, - {api::walletd::CreateSendProof::method(), json_rpc::make_member_method(&WalletNode::handle_create_sendproof3)}, - {api::walletd::GetTransaction::method(), json_rpc::make_member_method(&WalletNode::handle_get_transaction3)}}; +const WalletNode::HandlersMap WalletNode::m_jsonrpc_handlers = { + {api::walletd::GetStatus::method(), json_rpc::make_member_method(&WalletNode::handle_get_status)}, + {api::walletd::GetAddresses::method(), json_rpc::make_member_method(&WalletNode::handle_get_addresses)}, + {api::walletd::GetWalletInfo::method(), json_rpc::make_member_method(&WalletNode::handle_get_wallet_info)}, + {api::walletd::CreateAddresses::method(), json_rpc::make_member_method(&WalletNode::handle_create_address_list)}, + {api::walletd::GetViewKeyPair::method(), json_rpc::make_member_method(&WalletNode::handle_get_view_key)}, + {api::walletd::GetBalance::method(), json_rpc::make_member_method(&WalletNode::handle_get_balance)}, + {api::walletd::GetUnspents::method(), json_rpc::make_member_method(&WalletNode::handle_get_unspent)}, + {api::walletd::GetTransfers::method(), json_rpc::make_member_method(&WalletNode::handle_get_transfers)}, + {api::walletd::CreateTransaction::method(), json_rpc::make_member_method(&WalletNode::handle_create_transaction)}, + {api::walletd::SendTransaction::method(), json_rpc::make_member_method(&WalletNode::handle_send_transaction)}, + {api::walletd::CreateSendProof::method(), json_rpc::make_member_method(&WalletNode::handle_create_sendproof)}, + {api::walletd::GetTransaction::method(), json_rpc::make_member_method(&WalletNode::handle_get_transaction)}}; WalletNode::WalletNode(Node *inproc_node, logging::ILogger &log, const Config &config, WalletState &wallet_state) : WalletSync(log, config, wallet_state, std::bind(&WalletNode::advance_long_poll, this)) @@ -39,20 +41,21 @@ bool WalletNode::on_api_http_request(http::Client *who, http::RequestData &&requ response.r.add_headers_nocache(); bool method_found = false; if (request.r.uri == api::walletd::url()) { - bool result = process_json_rpc_request(m_jsonrpc3_handlers, who, std::move(request), response, method_found); + bool result = on_json_rpc(who, std::move(request), response, method_found); if (method_found) return result; } if (m_inproc_node) - return m_inproc_node->process_json_rpc_request(who, std::move(request), response); - m_log(logging::INFO) << "http_request node tunneling url=" << request.r.uri << " start of body=" << request.body.substr(0, 200) << std::endl; + return m_inproc_node->on_json_rpc(who, std::move(request), response); + m_log(logging::INFO) << "http_request node tunneling url=" << request.r.uri + << " start of body=" << request.body.substr(0, 200) << std::endl; http::RequestData original_request; original_request.r = request.r; request.r.http_version_major = 1; request.r.http_version_minor = 1; request.r.keep_alive = true; request.r.basic_authorization = m_config.bytecoind_authorization; - add_waiting_command(who, std::move(original_request), json_rpc::OptionalJsonValue{}, std::move(request), + add_waiting_command(who, std::move(original_request), common::JsonValue(nullptr), std::move(request), [=](const WaitingClient &wc, http::ResponseData &&send_response) mutable { send_response.r.http_version_major = wc.original_request.r.http_version_major; send_response.r.http_version_minor = wc.original_request.r.http_version_minor; @@ -84,22 +87,19 @@ void WalletNode::on_api_http_disconnect(http::Client *who) { ++lit; } -bool WalletNode::process_json_rpc_request(const HandlersMap &handlers, - http::Client *who, - http::RequestData &&request, - http::ResponseData &response, - bool &method_found) { +bool WalletNode::on_json_rpc( + http::Client *who, http::RequestData &&request, http::ResponseData &response, bool &method_found) { method_found = false; response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); - json_rpc::Response json_resp; + common::JsonValue jid(nullptr); try { json_rpc::Request json_req(request.body); - json_resp.set_id(json_req.get_id()); // copy id + jid = json_req.get_id().get(); - auto it = handlers.find(json_req.get_method()); - if (it == handlers.end()) { + auto it = m_jsonrpc_handlers.find(json_req.get_method()); + if (it == m_jsonrpc_handlers.end()) { return false; // m_log(logging::INFO) << "json request method not // found - " << json_req.get_method() << std::endl; @@ -116,24 +116,23 @@ bool WalletNode::process_json_rpc_request(const HandlersMap &handlers, // m_log(logging::INFO) << "json request method=" << // json_req.get_method() //<< std::endl; - bool result = it->second(this, who, std::move(request), std::move(json_req), json_resp); - if (!result) + std::string response_body; + if (!it->second(this, who, std::move(request), std::move(json_req), response_body)) return false; - + response.set_body(std::move(response_body)); } catch (const json_rpc::Error &err) { - json_resp.set_error(err); + response.set_body(json_rpc::create_error_response_body(err, jid)); } catch (const std::exception &e) { - json_resp.set_error(json_rpc::Error(json_rpc::INTERNAL_ERROR, e.what())); + json_rpc::Error json_err(json_rpc::INTERNAL_ERROR, common::what(e)); + response.set_body(json_rpc::create_error_response_body(json_err, jid)); } - - response.set_body(json_resp.get_body()); response.r.status = 200; return true; } // New protocol -api::walletd::GetStatus::Response WalletNode::create_status_response3() const { +api::walletd::GetStatus::Response WalletNode::create_status_response() const { api::walletd::GetStatus::Response response = m_last_node_status; response.top_block_height = m_wallet_state.get_tip_height(); response.top_block_hash = m_wallet_state.get_tip().hash; @@ -148,18 +147,18 @@ api::walletd::GetStatus::Response WalletNode::create_status_response3() const { return response; } -bool WalletNode::handle_get_status3(http::Client *who, http::RequestData &&raw_request, +bool WalletNode::handle_get_status(http::Client *who, http::RequestData &&raw_request, json_rpc::Request &&raw_js_request, api::walletd::GetStatus::Request &&request, api::walletd::GetStatus::Response &response) { - response = create_status_response3(); - if (request == response) { - // m_log(logging::INFO) << "handle_get_status3 will long poll, json=" + response = create_status_response(); + if (!response.ready_for_longpoll(request)) { + // m_log(logging::INFO) << "handle_get_status will long poll, json=" //<< // raw_request.body << std::endl; LongPollClient lpc; lpc.original_who = who; lpc.original_request = raw_request; - lpc.original_jsonrpc_id = raw_js_request.get_id(); + lpc.original_jsonrpc_id = raw_js_request.get_id().get(); lpc.original_get_status = request; m_long_poll_http_clients.push_back(lpc); return false; @@ -167,29 +166,46 @@ bool WalletNode::handle_get_status3(http::Client *who, http::RequestData &&raw_r return true; } -bool WalletNode::handle_get_addresses3(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::walletd::GetAddresses::Request &&, api::walletd::GetAddresses::Response &response) { - response.view_only = m_wallet_state.get_wallet().is_view_only(); - response.addresses.reserve(m_wallet_state.get_wallet().get_records().size()); - // We want "first address" to actually be first in list - AccountPublicAddress fa = m_wallet_state.get_wallet().get_first_address(); - response.addresses.push_back(m_wallet_state.get_currency().account_address_as_string(fa)); - for (auto &&wc : m_wallet_state.get_wallet().get_records()) { - AccountPublicAddress addr{wc.second.spend_public_key, m_wallet_state.get_wallet().get_view_public_key()}; - if (addr != fa) - response.addresses.push_back(m_wallet_state.get_currency().account_address_as_string(addr)); +bool WalletNode::handle_get_addresses(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::walletd::GetAddresses::Request &&request, api::walletd::GetAddresses::Response &response) { + // TODO - from_address, max_count + const Wallet &wa = m_wallet_state.get_wallet(); + response.total_address_count = common::integer_cast(wa.get_records().size()); + response.addresses.reserve(wa.get_records().size()); + if (request.need_secret_spend_keys) + response.secret_spend_keys.reserve(wa.get_records().size()); + for (size_t i = request.from_address; i < wa.get_records().size(); ++i) { + if (response.addresses.size() >= request.max_count) + break; + AccountPublicAddress addr{wa.get_records().at(i).spend_public_key, wa.get_view_public_key()}; + response.addresses.push_back(m_wallet_state.get_currency().account_address_as_string(addr)); + if (request.need_secret_spend_keys) + response.secret_spend_keys.push_back(wa.get_records().at(i).spend_secret_key); } return true; } -bool WalletNode::handle_get_view_key3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool WalletNode::handle_get_wallet_info(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::walletd::GetWalletInfo::Request &&request, api::walletd::GetWalletInfo::Response &response) { + const Wallet &wa = m_wallet_state.get_wallet(); + response.view_only = wa.is_view_only(); + response.mineproof_secret = wa.get_coinbase_tx_derivation_seed(); + response.total_address_count = common::integer_cast(wa.get_records().size()); + response.wallet_creation_timestamp = wa.get_oldest_timestamp(); + response.first_address = m_wallet_state.get_currency().account_address_as_string(wa.get_first_address()); + response.net = m_config.net; + return true; +} + +bool WalletNode::handle_get_view_key(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetViewKeyPair::Request &&, api::walletd::GetViewKeyPair::Response &response) { response.public_view_key = m_wallet_state.get_wallet().get_view_public_key(); response.secret_view_key = m_wallet_state.get_wallet().get_view_secret_key(); + response.import_keys = m_wallet_state.get_wallet().export_keys(); return true; } -bool WalletNode::handle_create_address_list3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool WalletNode::handle_create_address_list(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::CreateAddresses::Request &&request, api::walletd::CreateAddresses::Response &response) { if (request.secret_spend_keys.empty()) return true; @@ -212,50 +228,39 @@ void WalletNode::check_address_in_wallet_or_throw(const std::string &addr) const return; AccountPublicAddress address; if (!m_wallet_state.get_currency().parse_account_address_string(addr, &address)) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Failed to parse address " + addr); - auto sk = m_wallet_state.get_wallet().get_records().find(address.spend_public_key); - if (m_wallet_state.get_wallet().get_view_public_key() != address.view_public_key || - sk == m_wallet_state.get_wallet().get_records().end()) - throw json_rpc::Error( - json_rpc::INVALID_PARAMS, "Cannot get balance for address not in a wallet, address " + addr); + throw api::ErrorAddress(api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Failed to parse address", addr); + if (!m_wallet_state.get_wallet().is_our_address(address)) + throw api::ErrorAddress(api::ErrorAddress::ADDRESS_NOT_IN_WALLET, "Address not in wallet", addr); } -static void fix_height_or_depth(api::HeightOrDepth &ha, Height tip_height, Height max_depth) { - if (ha < 0) - ha = std::max(0, static_cast(tip_height) + 1 + ha); - if(ha + max_depth < tip_height) - throw json_rpc::Error( - json_rpc::INVALID_PARAMS, "height_or_depth cannot be deeper than 128 blocks before top blocks"); - if(ha > static_cast(tip_height)) - ha = static_cast(tip_height); -} - -bool WalletNode::handle_get_balance3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool WalletNode::handle_get_balance(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetBalance::Request &&request, api::walletd::GetBalance::Response &response) { check_address_in_wallet_or_throw(request.address); - fix_height_or_depth(request.height_or_depth, m_wallet_state.get_tip_height(), 128); - response = m_wallet_state.get_balance(request.address, request.height_or_depth); + Height height_or_depth = api::ErrorWrongHeight::fix_height_or_depth( + request.height_or_depth, m_wallet_state.get_tip_height(), false, false, 128); + response = m_wallet_state.get_balance(request.address, height_or_depth); return true; } -bool WalletNode::handle_get_unspent3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool WalletNode::handle_get_unspent(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetUnspents::Request &&request, api::walletd::GetUnspents::Response &response) { check_address_in_wallet_or_throw(request.address); - fix_height_or_depth(request.height_or_depth, m_wallet_state.get_tip_height(), 128); + Height height_or_depth = api::ErrorWrongHeight::fix_height_or_depth( + request.height_or_depth, m_wallet_state.get_tip_height(), false, false, 128); Amount total_amount = 0; - m_wallet_state.api_add_unspent(&response.spendable, &total_amount, request.address, request.height_or_depth); + m_wallet_state.api_add_unspent(&response.spendable, &total_amount, request.address, height_or_depth); response.locked_or_unconfirmed = - m_wallet_state.api_get_locked_or_unconfirmed_unspent(request.address, request.height_or_depth); + m_wallet_state.api_get_locked_or_unconfirmed_unspent(request.address, height_or_depth); return true; } -bool WalletNode::handle_get_transfers3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool WalletNode::handle_get_transfers(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetTransfers::Request &&request, api::walletd::GetTransfers::Response &response) { check_address_in_wallet_or_throw(request.address); response.next_to_height = request.to_height; response.next_from_height = request.from_height; response.blocks = m_wallet_state.api_get_transfers( - request.address, &request.from_height, &request.to_height, request.forward, request.desired_transactions_count); + request.address, &request.from_height, &request.to_height, request.forward, request.desired_transaction_count); if (request.from_height < m_wallet_state.get_tip_height() && request.to_height >= m_wallet_state.get_tip_height()) { api::Block pool_block = m_wallet_state.api_get_pool_as_history(request.address); if (!pool_block.transactions.empty()) { @@ -265,17 +270,18 @@ bool WalletNode::handle_get_transfers3(http::Client *, http::RequestData &&, jso response.blocks.insert(response.blocks.begin(), pool_block); } } - auto unlocked_outputs = - m_wallet_state.api_get_unlocked_outputs(request.address, request.from_height, request.to_height); - response.unlocked_transfers.reserve(unlocked_outputs.size()); - for (auto &&lou : unlocked_outputs) { - api::Transfer tr; - tr.ours = true; - tr.amount = lou.second.amount; - tr.address = lou.second.address; - tr.outputs.push_back(lou.second); - response.unlocked_transfers.push_back(std::move(tr)); - } + response.unlocked_transfers = m_wallet_state.api_get_unlocked_transfers(request.address, request.from_height, request.to_height); +// auto unlocked_outputs = +// m_wallet_state.api_get_unlocked_outputs(request.address, request.from_height, request.to_height); +// response.unlocked_transfers.reserve(unlocked_outputs.size()); +// for (auto &&lou : unlocked_outputs) { +// api::Transfer tr; +// tr.ours = true; +// tr.amount = lou.second.amount; +// tr.address = lou.second.address; +// tr.outputs.push_back(lou.second); +// response.unlocked_transfers.push_back(std::move(tr)); +// } if (request.forward) response.next_from_height = request.to_height; else @@ -283,10 +289,11 @@ bool WalletNode::handle_get_transfers3(http::Client *, http::RequestData &&, jso return true; } -bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData &&raw_request, +bool WalletNode::handle_create_transaction(http::Client *who, http::RequestData &&raw_request, json_rpc::Request &&raw_js_request, api::walletd::CreateTransaction::Request &&request, api::walletd::CreateTransaction::Response &response) { - m_log(logging::TRACE) << "create_transaction request tip_height=" << m_wallet_state.get_tip_height() << " body=" << raw_request.body << std::endl; + m_log(logging::TRACE) << "create_transaction request tip_height=" << m_wallet_state.get_tip_height() + << " body=" << raw_request.body << std::endl; for (auto &&tid : request.prevent_conflict_with_transactions) { if (m_wallet_state.api_has_transaction(tid, true)) continue; @@ -296,27 +303,25 @@ bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData return true; if (m_last_node_status.next_block_effective_median_size == 0) throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Next block median size unknown, need to sync to bytecoind"); - if (request.confirmed_height_or_depth < 0) - request.confirmed_height_or_depth = std::max(0, - static_cast(m_wallet_state.get_tip_height()) + 1 + request.confirmed_height_or_depth); - if (request.confirmed_height_or_depth > static_cast(m_wallet_state.get_tip_height())) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, - "'confirmed_height_or_depth' cannot be larger than top block height"); + if (request.transaction.anonymity > 100) // Arbitrary value + throw json_rpc::Error(api::walletd::CreateTransaction::NOT_ENOUGH_ANONYMITY, + "Wallet will not create transactions with anonymity > 100 because large anonymity values actually reduce anonymity due to tiny number of similar transactions"); + Height confirmed_height_or_depth = api::ErrorWrongHeight::fix_height_or_depth( + request.confirmed_height_or_depth, m_wallet_state.get_tip_height(), true, false); if (request.fee_per_byte == 0) request.fee_per_byte = m_last_node_status.recommended_fee_per_byte; if (request.fee_per_byte == 0) throw json_rpc::Error(json_rpc::INVALID_PARAMS, - "'fee_per_byte' set to 0, and it is impossible to " - "set it to 'status.recommended_fee_per_byte', " - "because walletd never connected to bytecoind after " - "it was restarted"); + "'fee_per_byte' set to 0, and it is impossible to set it to 'status.recommended_fee_per_byte', " + "because walletd never connected to bytecoind after it was restarted"); AccountPublicAddress change_addr; // We require change address, even if you are lucky and would get zero change if (m_wallet_state.get_wallet().is_view_only()) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, - "Unable to create transaction - view-only wallet " - "contains no spend keys"); + throw json_rpc::Error(api::walletd::CreateTransaction::VIEW_ONLY_WALLET, + "Unable to create transaction - view-only wallet contains no spend keys"); if (!m_wallet_state.get_currency().parse_account_address_string(request.change_address, &change_addr)) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Failed to parse change address " + request.change_address); + throw api::ErrorAddress( + api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Failed to parse change address", request.change_address); + // We do not require that change_addr should be in our wallet if (request.spend_addresses.empty() && !request.any_spend_address) throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Empty spend addresses requires setting " @@ -324,17 +329,21 @@ bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData if (!request.spend_addresses.empty() && request.any_spend_address) throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Non-empty spend addresses requires setting " - "'any_spend_address':false for additional " - "protection"); + "'any_spend_address':false for additional protection"); std::unordered_map only_records; + // We protect against our programming errors by fetching spend keys in only_records + // and passing only_records to transaction builder, so that it cannot accidentally + // spend funds from unintended addresses for (auto &&ad : request.spend_addresses) { AccountPublicAddress addr; if (!m_wallet_state.get_currency().parse_account_address_string(ad, &addr)) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Failed to parse change address " + ad); - if (!m_wallet_state.get_wallet().get_only_record(only_records, addr)) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Spend address does not belong to wallet " + ad); + throw api::ErrorAddress(api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Failed to parse spend address", ad); + WalletRecord record; + if (!m_wallet_state.get_wallet().get_record(record, addr)) + throw api::ErrorAddress(api::ErrorAddress::ADDRESS_NOT_IN_WALLET, "Address not in wallet", ad); + only_records[addr.spend_public_key] = record; } - TransactionBuilder builder(m_wallet_state.get_currency(), request.transaction.unlock_time); + TransactionBuilder builder(m_wallet_state.get_currency(), request.transaction.unlock_block_or_timestamp); Wallet::History history; if (request.transaction.payment_id != Hash{}) builder.set_payment_id(request.transaction.payment_id); @@ -348,38 +357,40 @@ bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData "Negative transfer amount " + common::to_string(tr.amount) + " for address " + tr.address); AccountPublicAddress addr; if (!m_wallet_state.get_currency().parse_account_address_string(tr.address, &addr)) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Failed to parse address " + tr.address); + throw api::ErrorAddress( + api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Failed to parse transfer address", tr.address); combined_outputs[addr] += tr.amount; history.insert(addr); sum_positive_transfers += tr.amount; } size_t total_outputs = 0; - for (const auto & aa : combined_outputs) { + for (const auto &aa : combined_outputs) { std::vector decomposed_amounts; decompose_amount(aa.second, m_wallet_state.get_currency().default_dust_threshold, &decomposed_amounts); total_outputs += decomposed_amounts.size(); } if (sum_positive_transfers == 0) throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Sum of amounts of all outgoing transfers cannot be 0"); - const std::string optimization = - request.transaction.unlock_time == 0 ? request.optimization : "minimal"; // Do not lock excess coins :) + const std::string optimization = request.transaction.unlock_block_or_timestamp == 0 + ? request.optimization + : "minimal"; // Do not lock excess coins :) Amount change = 0; std::vector unspents; Amount total_unspents = 0; if (!request.spend_addresses.empty()) for (auto &&ad : request.spend_addresses) { if (!m_wallet_state.api_add_unspent( - &unspents, &total_unspents, ad, request.confirmed_height_or_depth, sum_positive_transfers * 2)) + &unspents, &total_unspents, ad, confirmed_height_or_depth, sum_positive_transfers * 2)) break; // found enough funds } else m_wallet_state.api_add_unspent( - &unspents, &total_unspents, std::string(), request.confirmed_height_or_depth, sum_positive_transfers * 2); + &unspents, &total_unspents, std::string(), confirmed_height_or_depth, sum_positive_transfers * 2); UnspentSelector selector(m_log.get_logger(), m_wallet_state.get_currency(), std::move(unspents)); // First we select just outputs with sum = 2x requires sum if (!selector .select_optimal_outputs(m_wallet_state.get_tip_height(), m_wallet_state.get_tip().timestamp, - request.confirmed_height_or_depth, m_last_node_status.next_block_effective_median_size, + confirmed_height_or_depth, m_last_node_status.next_block_effective_median_size, request.transaction.anonymity, sum_positive_transfers, total_outputs, request.fee_per_byte, optimization, &change) .empty()) { @@ -388,48 +399,46 @@ bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData total_unspents = 0; if (!request.spend_addresses.empty()) for (auto &&ad : request.spend_addresses) { - m_wallet_state.api_add_unspent(&unspents, &total_unspents, ad, request.confirmed_height_or_depth); + m_wallet_state.api_add_unspent(&unspents, &total_unspents, ad, confirmed_height_or_depth); } else - m_wallet_state.api_add_unspent( - &unspents, &total_unspents, std::string(), request.confirmed_height_or_depth); + m_wallet_state.api_add_unspent(&unspents, &total_unspents, std::string(), confirmed_height_or_depth); selector.reset(std::move(unspents)); std::string error = selector.select_optimal_outputs(m_wallet_state.get_tip_height(), - m_wallet_state.get_tip().timestamp, request.confirmed_height_or_depth, + m_wallet_state.get_tip().timestamp, confirmed_height_or_depth, m_last_node_status.next_block_effective_median_size, request.transaction.anonymity, sum_positive_transfers, total_outputs, request.fee_per_byte, optimization, &change); if (error == "NOT_ENOUGH_FUNDS") - throw api::walletd::CreateTransaction::Error(api::walletd::CreateTransaction::NOT_ENOUGH_FUNDS, + throw json_rpc::Error(api::walletd::CreateTransaction::NOT_ENOUGH_FUNDS, "Outputs cannot be selected for transaction " + error); if (error == "TRANSACTION_DOES_NOT_FIT_IN_BLOCK") - throw api::walletd::CreateTransaction::Error( - api::walletd::CreateTransaction::TRANSACTION_DOES_NOT_FIT_IN_BLOCK, + throw json_rpc::Error(api::walletd::CreateTransaction::TRANSACTION_DOES_NOT_FIT_IN_BLOCK, "Outputs cannot be selected for transaction " + error); if (!error.empty()) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Outputs cannot be selected for transaction " + error); + throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "Outputs cannot be selected for transaction " + error); } // Selector ensures the change should be as "round" as possible if (change > 0) { combined_outputs[change_addr] += change; history.insert(change_addr); } - for (const auto & aa : combined_outputs) { + for (const auto &aa : combined_outputs) { std::vector decomposed_amounts; decompose_amount(aa.second, m_wallet_state.get_currency().default_dust_threshold, &decomposed_amounts); for (auto &&da : decomposed_amounts) builder.add_output(da, aa.first); } api::bytecoind::GetRandomOutputs::Request ra_request; - ra_request.confirmed_height_or_depth = request.confirmed_height_or_depth; - ra_request.outs_count = + ra_request.confirmed_height_or_depth = confirmed_height_or_depth; + ra_request.output_count = request.transaction.anonymity + 1; // Ask excess output for the case of collision with our output ra_request.amounts = selector.get_ra_amounts(); api::bytecoind::GetRandomOutputs::Response ra_response; if (m_inproc_node) { - m_inproc_node->on_get_random_outputs3( + m_inproc_node->on_get_random_outputs( nullptr, http::RequestData(raw_request), json_rpc::Request(), std::move(ra_request), ra_response); selector.add_mixed_inputs(m_wallet_state.get_wallet().get_view_secret_key(), - request.any_spend_address ? m_wallet_state.get_wallet().get_records() : only_records, &builder, + request.any_spend_address ? &m_wallet_state.get_wallet() : nullptr, only_records, &builder, request.transaction.anonymity, std::move(ra_response)); Transaction tx = builder.sign(m_wallet_state.get_wallet().get_tx_derivation_seed()); response.binary_transaction = seria::to_binary(tx); @@ -454,10 +463,10 @@ bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData json_rpc::create_request(api::bytecoind::url(), api::bytecoind::GetRandomOutputs::method(), ra_request); new_request.r.basic_authorization = m_config.bytecoind_authorization; m_log(logging::TRACE) << "sending get_random_outputs, body=" << new_request.body << std::endl; - add_waiting_command(who, std::move(raw_request), raw_js_request.get_id(), std::move(new_request), + add_waiting_command(who, std::move(raw_request), raw_js_request.get_id().get(), std::move(new_request), [=](const WaitingClient &wc, http::ResponseData &&random_response) mutable { m_log(logging::TRACE) << "got response to get_random_outputs, status=" << random_response.r.status - << " body " << random_response.body << std::endl; + << " body " << random_response.body << std::endl; if (random_response.r.status != 200) { throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "got error as response on get_random_outputs"); } @@ -468,7 +477,7 @@ bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData api::bytecoind::GetRandomOutputs::Response ra_response; json_resp.get_result(ra_response); selector.add_mixed_inputs(m_wallet_state.get_wallet().get_view_secret_key(), - request.any_spend_address ? m_wallet_state.get_wallet().get_records() : only_records, &builder, + request.any_spend_address ? &m_wallet_state.get_wallet() : nullptr, only_records, &builder, request.transaction.anonymity, std::move(ra_response)); tx = builder.sign(m_wallet_state.get_wallet().get_tx_derivation_seed()); last_response.binary_transaction = seria::to_binary(tx); @@ -481,20 +490,25 @@ bool WalletNode::handle_create_transaction3(http::Client *who, http::RequestData } if (!m_wallet_state.parse_raw_transaction(last_response.transaction, tx, tx_hash)) throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "Created trsnsaction cannot be parsed"); - http::ResponseData last_http_response = - json_rpc::create_response(wc.original_request, last_response, wc.original_jsonrpc_id); + http::ResponseData last_http_response(wc.original_request.r); + last_http_response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); + last_http_response.r.status = 200; + last_http_response.set_body(json_rpc::create_response_body(last_response, wc.original_jsonrpc_id)); wc.original_who->write(std::move(last_http_response)); }, [=](const WaitingClient &wc, std::string err) mutable { m_log(logging::INFO) << "got error to get_random_outputs from bytecoind, " << err << std::endl; - http::ResponseData last_http_response = json_rpc::create_error_response( - wc.original_request, json_rpc::Error(json_rpc::INTERNAL_ERROR, err), wc.original_jsonrpc_id); + http::ResponseData last_http_response(wc.original_request.r); + last_http_response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); + last_http_response.r.status = 200; + last_http_response.set_body(json_rpc::create_error_response_body( + json_rpc::Error(json_rpc::INTERNAL_ERROR, err), wc.original_jsonrpc_id)); wc.original_who->write(std::move(last_http_response)); }); return false; } -bool WalletNode::handle_create_sendproof3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool WalletNode::handle_create_sendproof(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::CreateSendProof::Request &&request, api::walletd::CreateSendProof::Response &response) { std::set addresses; if (request.addresses.empty()) { @@ -508,7 +522,7 @@ bool WalletNode::handle_create_sendproof3(http::Client *, http::RequestData &&, for (auto &&addr : request.addresses) { AccountPublicAddress address; if (!m_wallet_state.get_currency().parse_account_address_string(addr, &address)) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "Address failed to parse " + addr); + throw api::ErrorAddress(api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Failed to parse address", addr); addresses.insert(address); } for (auto &&address : addresses) { @@ -516,19 +530,26 @@ bool WalletNode::handle_create_sendproof3(http::Client *, http::RequestData &&, sp.transaction_hash = request.transaction_hash; sp.message = request.message; sp.address = address; - if (m_wallet_state.api_create_proof(sp)) - response.sendproofs.push_back(seria::to_json_value(sp).to_string()); + if (m_wallet_state.api_create_proof(sp)) { +// seria::JsonOutputStreamValue s; +// s.begin_object(); +// ser_members(sp, s, m_wallet_state.get_currency()); +// s.end_object(); +// response.sendproofs.push_back(s.move_value().to_string()); + // return s.move_value(); + response.sendproofs.push_back(seria::to_json_value(sp, m_wallet_state.get_currency()).to_string()); + } } return true; } -bool WalletNode::handle_send_transaction3(http::Client *who, http::RequestData &&raw_request, +bool WalletNode::handle_send_transaction(http::Client *who, http::RequestData &&raw_request, json_rpc::Request &&raw_js_request, api::bytecoind::SendTransaction::Request &&request, api::bytecoind::SendTransaction::Response &response) { m_wallet_state.add_to_payment_queue(request.binary_transaction, true); advance_long_poll(); if (m_inproc_node) { - m_inproc_node->handle_send_transaction3( + m_inproc_node->handle_send_transaction( nullptr, std::move(raw_request), std::move(raw_js_request), std::move(request), response); return true; } @@ -536,7 +557,7 @@ bool WalletNode::handle_send_transaction3(http::Client *who, http::RequestData & new_request.set_body(std::move(raw_request.body)); // We save on copying body here new_request.r.set_firstline("POST", api::bytecoind::url(), 1, 1); new_request.r.basic_authorization = m_config.bytecoind_authorization; - add_waiting_command(who, std::move(raw_request), raw_js_request.get_id(), std::move(new_request), + add_waiting_command(who, std::move(raw_request), raw_js_request.get_id().get(), std::move(new_request), [=](const WaitingClient &wc2, http::ResponseData &&send_response) mutable { http::ResponseData resp(std::move(send_response)); resp.r.http_version_major = wc2.original_request.r.http_version_major; @@ -545,14 +566,21 @@ bool WalletNode::handle_send_transaction3(http::Client *who, http::RequestData & wc2.original_who->write(std::move(resp)); }, [=](const WaitingClient &wc2, std::string err) { - http::ResponseData resp = json_rpc::create_error_response( - wc2.original_request, json_rpc::Error(json_rpc::INTERNAL_ERROR, err), wc2.original_jsonrpc_id); + // http::ResponseData resp = json_rpc::create_error_response( + // wc2.original_request, json_rpc::Error(json_rpc::INTERNAL_ERROR, err), + // wc2.original_jsonrpc_id); + // wc2.original_who->write(std::move(resp)); + http::ResponseData resp(wc2.original_request.r); + resp.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); + resp.r.status = 200; + resp.set_body(json_rpc::create_error_response_body( + json_rpc::Error(json_rpc::INTERNAL_ERROR, err), wc2.original_jsonrpc_id)); wc2.original_who->write(std::move(resp)); }); return false; } -bool WalletNode::handle_get_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, +bool WalletNode::handle_get_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetTransaction::Request &&req, api::walletd::GetTransaction::Response &res) { TransactionPrefix tx; m_wallet_state.api_get_transaction(req.hash, true, &tx, &res.transaction); @@ -568,14 +596,20 @@ void WalletNode::process_waiting_command_response(http::ResponseData &&resp) { auto err_fun = std::move(cli.err_fun); auto fun = std::move(cli.fun); try { - fun(cli, std::move(resp)); - } catch (std::exception &ex) { - m_log(logging::WARNING) << " Parsing received waiting command leads to throw/catch what=" << ex.what() - << std::endl; - err_fun(cli, ex.what()); + try { + fun(cli, std::move(resp)); + } catch (const std::exception &ex) { + m_log(logging::WARNING) << " Parsing received waiting command leads to throw/catch what=" + << common::what(ex) << std::endl; + err_fun(cli, common::what(ex)); + } catch (...) { + m_log(logging::WARNING) << " Parsing received waiting command leads to throw/catch" << std::endl; + err_fun(cli, "catch ..."); + } + } catch (const std::exception &ex) { + m_log(logging::WARNING) << " Error function leads to throw/catch what=" << common::what(ex) << std::endl; } catch (...) { - m_log(logging::WARNING) << " Parsing received waiting command leads to throw/catch" << std::endl; - err_fun(cli, "catch ..."); + m_log(logging::WARNING) << " Error function leads to throw/catch" << std::endl; } } send_next_waiting_command(); @@ -603,7 +637,7 @@ void WalletNode::send_next_waiting_command() { } void WalletNode::add_waiting_command(http::Client *who, http::RequestData &&original_request, - const json_rpc::OptionalJsonValue &original_rpc_id, http::RequestData &&request, + const common::JsonValue &original_rpc_id, http::RequestData &&request, std::function fun, std::function err_fun) { WaitingClient wc2; @@ -620,20 +654,17 @@ void WalletNode::add_waiting_command(http::Client *who, http::RequestData &&orig void WalletNode::advance_long_poll() { if (m_long_poll_http_clients.empty()) return; - api::walletd::GetStatus::Response resp = create_status_response3(); - json_rpc::Response last_json_resp; - last_json_resp.set_result(resp); + const api::walletd::GetStatus::Response resp = create_status_response(); for (auto lit = m_long_poll_http_clients.begin(); lit != m_long_poll_http_clients.end();) - if (lit->original_get_status != resp) { - last_json_resp.set_id(lit->original_jsonrpc_id); + if (resp.ready_for_longpoll(lit->original_get_status)) { http::ResponseData last_http_response; last_http_response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); last_http_response.r.status = 200; last_http_response.r.http_version_major = lit->original_request.r.http_version_major; last_http_response.r.http_version_minor = lit->original_request.r.http_version_minor; last_http_response.r.keep_alive = lit->original_request.r.keep_alive; - last_http_response.set_body(last_json_resp.get_body()); + last_http_response.set_body(json_rpc::create_response_body(resp, lit->original_jsonrpc_id)); // m_log(logging::INFO) << "advance_long_poll will // reply to long poll json=" << last_http_response.body << std::endl; lit->original_who->write(std::move(last_http_response)); diff --git a/src/Core/WalletNode.hpp b/src/Core/WalletNode.hpp index e7abf6aa..e0d09fc5 100644 --- a/src/Core/WalletNode.hpp +++ b/src/Core/WalletNode.hpp @@ -13,33 +13,33 @@ class WalletNode : public WalletSync { public: explicit WalletNode(Node *inproc_node, logging::ILogger &, const Config &, WalletState &); - typedef std::function + typedef std::function JSONRPCHandlerFunction; - // New protocol (json_rpc3) - bool handle_get_status3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_get_status(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetStatus::Request &&, api::walletd::GetStatus::Response &); - bool handle_get_addresses3(http::Client *, http::RequestData &&, json_rpc::Request &&, - api::walletd::GetAddresses::Request &&, api::walletd::GetAddresses::Response &); - bool handle_create_address_list3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_get_addresses(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::walletd::GetAddresses::Request &&, api::walletd::GetAddresses::Response &); + bool handle_get_wallet_info(http::Client *, http::RequestData &&, json_rpc::Request &&, + api::walletd::GetWalletInfo::Request &&, api::walletd::GetWalletInfo::Response &); + bool handle_create_address_list(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::CreateAddresses::Request &&, api::walletd::CreateAddresses::Response &); - bool handle_get_view_key3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_get_view_key(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetViewKeyPair::Request &&, api::walletd::GetViewKeyPair::Response &); - bool handle_get_unspent3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_get_unspent(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetUnspents::Request &&, api::walletd::GetUnspents::Response &); - bool handle_get_balance3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_get_balance(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetBalance::Request &&, api::walletd::GetBalance::Response &); - bool handle_get_transfers3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_get_transfers(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetTransfers::Request &&, api::walletd::GetTransfers::Response &); - bool handle_create_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_create_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::CreateTransaction::Request &&, api::walletd::CreateTransaction::Response &); - bool handle_create_sendproof3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_create_sendproof(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::CreateSendProof::Request &&, api::walletd::CreateSendProof::Response &); - bool handle_send_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_send_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, api::bytecoind::SendTransaction::Request &&, api::bytecoind::SendTransaction::Response &); // We lock spent outputs until next pool sync - bool handle_get_transaction3(http::Client *, http::RequestData &&, json_rpc::Request &&, + bool handle_get_transaction(http::Client *, http::RequestData &&, json_rpc::Request &&, api::walletd::GetTransaction::Request &&, api::walletd::GetTransaction::Response &); private: @@ -51,13 +51,13 @@ class WalletNode : public WalletSync { http::Client *original_who = nullptr; http::RequestData request; http::RequestData original_request; - json_rpc::OptionalJsonValue original_jsonrpc_id; + common::JsonValue original_jsonrpc_id; std::function fun; std::function err_fun; }; std::deque m_waiting_command_requests; void add_waiting_command(http::Client *who, http::RequestData &&original_request, - const json_rpc::OptionalJsonValue &original_rpc_id, http::RequestData &&request, + const common::JsonValue &original_rpc_id, http::RequestData &&request, std::function fun, std::function err_fun); void send_next_waiting_command(); @@ -67,22 +67,21 @@ class WalletNode : public WalletSync { struct LongPollClient { http::Client *original_who = nullptr; http::RequestData original_request; - json_rpc::OptionalJsonValue original_jsonrpc_id; - bytecoin::api::walletd::GetStatus::Request original_get_status; + common::JsonValue original_jsonrpc_id; + api::walletd::GetStatus::Request original_get_status; }; std::list m_long_poll_http_clients; void advance_long_poll(); typedef std::unordered_map HandlersMap; - static const HandlersMap m_jsonrpc3_handlers; + static const HandlersMap m_jsonrpc_handlers; - api::walletd::GetStatus::Response create_status_response3() const; + api::walletd::GetStatus::Response create_status_response() const; bool on_api_http_request(http::Client *, http::RequestData &&, http::ResponseData &); void on_api_http_disconnect(http::Client *); - bool process_json_rpc_request( - const HandlersMap &, http::Client *, http::RequestData &&, http::ResponseData &, bool &method_found); + bool on_json_rpc(http::Client *, http::RequestData &&, http::ResponseData &, bool &method_found); void check_address_in_wallet_or_throw(const std::string &addr) const; }; diff --git a/src/Core/WalletSerializationV1.cpp b/src/Core/WalletSerializationV1.cpp index 2132d2bb..92f644fd 100644 --- a/src/Core/WalletSerializationV1.cpp +++ b/src/Core/WalletSerializationV1.cpp @@ -29,7 +29,7 @@ struct ObsoleteSpentOutputDto { Hash transaction_hash; uint32_t output_in_transaction; uint64_t wallet_index; - crypto::Hash spending_transaction_hash; + Hash spending_transaction_hash; }; // DO NOT CHANGE IT @@ -57,7 +57,7 @@ struct UnlockTransactionJobDto { total_amount = wallet.total_amount; fee = wallet.fee; creation_time = wallet.creation_time; - unlock_time = wallet.unlock_time; + unlock_block_or_timestamp = wallet.unlock_block_or_timestamp; extra = wallet.extra; } @@ -68,7 +68,7 @@ struct UnlockTransactionJobDto { int64_t total_amount; uint64_t fee; uint64_t creation_time; - uint64_t unlock_time; + uint64_t unlock_block_or_timestamp; std::string extra; }; @@ -101,18 +101,18 @@ version(version) {} struct KeysStorage { uint64_t creation_timestamp; - crypto::PublicKey spend_public_key; - crypto::SecretKey spend_secret_key; + PublicKey spend_public_key; + SecretKey spend_secret_key; - crypto::PublicKey view_public_key; - crypto::SecretKey view_secret_key; + PublicKey view_public_key; + SecretKey view_secret_key; }; std::string read_cipher(common::IInputStream &source, const std::string &name) { std::string cipher; // bytecoin::BinaryInputStreamSerializer s(source); seria::BinaryInputStream s(source); - s(cipher); // , name + ser(cipher, s); // , name return cipher; } @@ -129,7 +129,7 @@ template void deserialize(Object &obj, const std::string &name, const std::string &plain) { MemoryInputStream stream(plain.data(), plain.size()); seria::BinaryInputStream s(stream); - s(obj); + ser(obj, s); } template @@ -208,11 +208,11 @@ void WalletSerializerV1::load_wallet_v1(common::IInputStream &source, const cryp seria::BinaryInputStream encrypted(source); - encrypted(crypto_ctx.iv); + ser(crypto_ctx.iv, encrypted); crypto_ctx.key = key; std::string cipher; - encrypted(cipher); + ser(cipher, encrypted); std::string plain = decrypt(cipher, crypto_ctx); @@ -223,16 +223,16 @@ void WalletSerializerV1::load_wallet_v1(common::IInputStream &source, const cryp check_keys(); bool details_saved; - serializer(details_saved); // , "has_details" + ser(details_saved, serializer); // , "has_details" } void WalletSerializerV1::load_wallet_v1_keys(seria::ISeria &s) { KeysStorage keys; try { - s(keys); - } catch (const std::runtime_error &) { - throw std::runtime_error("WRONG_PASSWORD"); + ser(keys, s); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error("WRONG_PASSWORD")); } m_view_public_key = keys.view_public_key; @@ -250,7 +250,7 @@ uint32_t WalletSerializerV1::load_version(common::IInputStream &source) { seria::BinaryInputStream s(source); uint32_t version = std::numeric_limits::max(); - s(version); + ser(version, s); return version; } @@ -265,8 +265,8 @@ void WalletSerializerV1::load_keys(common::IInputStream &source, CryptoContext & try { load_public_key(source, crypto_ctx); load_secret_key(source, crypto_ctx); - } catch (const std::runtime_error &) { - throw std::runtime_error("WRONG_PASSWORD"); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error("WRONG_PASSWORD")); } } diff --git a/src/Core/WalletSerializationV1.hpp b/src/Core/WalletSerializationV1.hpp index b41fa943..169b59b0 100644 --- a/src/Core/WalletSerializationV1.hpp +++ b/src/Core/WalletSerializationV1.hpp @@ -5,15 +5,15 @@ #include "Core/Wallet.hpp" // for WalletRecord #include "common/Streams.hpp" -#include "crypto/chacha8.h" +#include "crypto/chacha8.hpp" #include "seria/ISeria.hpp" namespace bytecoin { class WalletSerializerV1 { public: - WalletSerializerV1(crypto::PublicKey &view_public_key, crypto::SecretKey &view_secret_key, - std::vector &wallets_container); + WalletSerializerV1( + PublicKey &view_public_key, SecretKey &view_secret_key, std::vector &wallets_container); void load(const crypto::chacha8_key &key, common::IInputStream &source); @@ -41,8 +41,8 @@ class WalletSerializerV1 { void load_wallet_v1_keys(seria::ISeria &s); - crypto::PublicKey &m_view_public_key; - crypto::SecretKey &m_view_secret_key; + PublicKey &m_view_public_key; + SecretKey &m_view_secret_key; std::vector &m_wallets_container; }; diff --git a/src/Core/WalletState.cpp b/src/Core/WalletState.cpp index 54a406d9..bea55e13 100644 --- a/src/Core/WalletState.cpp +++ b/src/Core/WalletState.cpp @@ -17,7 +17,7 @@ static const std::string ADDRESSES_PREFIX = "a"; // this is not undone using namespace bytecoin; using namespace platform; -Amount WalletState::DeltaState::add_incoming_output(const api::Output &output) { +Amount WalletState::DeltaState::add_incoming_output(const api::Output &output, const Hash & tid) { m_unspents[output.public_key].push_back(output); return output.amount; } @@ -47,7 +47,7 @@ void WalletState::DeltaState::undo_transaction(const Hash &tid) { for (const auto &output : tx.outputs) { if (output.target.type() == typeid(KeyOutput)) { const KeyOutput &key_output = boost::get(output.target); - auto uit = m_unspents.find(key_output.key); + auto uit = m_unspents.find(key_output.public_key); if (uit == m_unspents.end()) // Actually should never be empty continue; // Not our output for (auto oit = uit->second.begin(); oit != uit->second.end(); ++oit) @@ -103,11 +103,16 @@ WalletState::WalletState(Wallet &wallet, logging::ILogger &log, const Config &co } } +Height WalletState::get_pq_confirmations() const { + if (m_config.net == "test") + return 20; // std::max(20, 720 / platform::get_time_multiplier_for_tests()); + return m_currency.expected_blocks_per_day(); +} + void WalletState::wallet_addresses_updated() { Timestamp undo_timestamp = std::numeric_limits::max(); try { - for (auto rec : m_wallet.get_records()) { - const WalletRecord &wa = rec.second; + for (const auto &wa : m_wallet.get_records()) { auto keyuns = ADDRESSES_PREFIX + DB::to_binary_key(wa.spend_public_key.data, sizeof(wa.spend_public_key.data)); std::string st; @@ -130,7 +135,7 @@ void WalletState::wallet_addresses_updated() { } catch (const std::exception &ex) { m_log(logging::ERROR) << "Exception in wallet_addresses_updated, probably out of disk space or database corrupted error=" - << ex.what() << " path=" << m_db.get_path() << std::endl; + << common::what(ex) << " path=" << m_db.get_path() << std::endl; std::exit(api::BYTECOIND_DATABASE_ERROR); } fix_payment_queue_after_undo_redo(); @@ -169,7 +174,7 @@ bool WalletState::add_to_payment_queue(const BinaryArray &binary_transaction, bo // std::cout << "by_hash_index.size=" << by_hash_index.size() << std::endl; m_pq_version += 1; if (api_get_transaction(tid, false, &tx_prefix, &ptx)) { - entry.remove_height = ptx.block_height + m_currency.expected_blocks_per_day(); + entry.remove_height = ptx.block_height + get_pq_confirmations(); m_log(logging::INFO) << "Now PQ transaction " << tid << " is in BC, remove_height=" << entry.remove_height << std::endl; entry.fee_per_kb = ptx.fee / binary_transaction.size(); @@ -203,7 +208,7 @@ BinaryArray WalletState::get_next_from_sending_queue(Hash *previous_hash) { void WalletState::process_payment_queue_send_error(Hash hash, const api::bytecoind::SendTransaction::Error &error) { if (error.code == api::bytecoind::SendTransaction::OUTPUT_ALREADY_SPENT || error.code == api::bytecoind::SendTransaction::WRONG_OUTPUT_REFERENCE) { - if (get_tip_height() > error.conflict_height + m_currency.expected_blocks_per_day()) { + if (get_tip_height() > error.conflict_height + get_pq_confirmations()) { auto &by_hash_index = payment_queue.get(); auto git = by_hash_index.find(hash); if (git != by_hash_index.end()) @@ -264,7 +269,7 @@ void WalletState::fix_payment_queue_after_undo_redo() { continue; QueueEntry entry = *git; by_hash_index.erase(git); - entry.remove_height = ptx.block_height + m_currency.expected_blocks_per_day(); + entry.remove_height = ptx.block_height + get_pq_confirmations(); payment_queue.insert(entry); pq_modified = true; remove_transaction_from_mempool(tid, true); @@ -344,7 +349,7 @@ bool WalletState::sync_with_blockchain(api::bytecoind::SyncBlocks::Response &res if (!empty_chain() && header.previous_block_hash != get_tip_bid()) // TODO - investigate first condition return false; if (header.timestamp + m_currency.block_future_time_limit >= m_wallet.get_oldest_timestamp()) { - const auto &block_gi = resp.blocks.at(bin).global_indices; + const auto &block_gi = resp.blocks.at(bin).output_indexes; PreparedWalletBlock pb = preparator.get_ready_work(get_tip_height() + 1); // PreparedWalletBlock pb(std::move(resp.blocks.at(bin).block), m_wallet.get_view_secret_key()); redo_block(header, pb, block_gi, get_tip_height() + 1); @@ -352,12 +357,13 @@ bool WalletState::sync_with_blockchain(api::bytecoind::SyncBlocks::Response &res // pop_chain(); // redo_block(header, pb, block_gi, m_tip_height + 1); auto now = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast(now - log_redo_block).count() > 1000) { + if (std::chrono::duration_cast(now - log_redo_block).count() > 1000 || + get_tip_height() + 1 == resp.status.top_known_block_height) { log_redo_block = now; - m_log(logging::INFO) << "WalletState redo block, height=" << get_tip_height() << "/" + m_log(logging::INFO) << "WalletState redo block, height=" << get_tip_height() + 1 << "/" << resp.status.top_known_block_height << std::endl; } else - m_log(logging::TRACE) << "WalletState redo block, height=" << get_tip_height() << "/" + m_log(logging::TRACE) << "WalletState redo block, height=" << get_tip_height() + 1 << "/" << resp.status.top_known_block_height << std::endl; } push_chain(header); @@ -367,8 +373,8 @@ bool WalletState::sync_with_blockchain(api::bytecoind::SyncBlocks::Response &res fix_empty_chain(); } catch (const std::exception &ex) { m_log(logging::ERROR) - << "Exception in sync_with_blockchain, probably out of disk space or database corrupted error=" << ex.what() - << " path=" << m_db.get_path() << std::endl; + << "Exception in sync_with_blockchain, probably out of disk space or database corrupted error=" + << common::what(ex) << " path=" << m_db.get_path() << std::endl; std::exit(api::BYTECOIND_DATABASE_ERROR); } fix_payment_queue_after_undo_redo(); @@ -380,7 +386,7 @@ std::vector WalletState::get_tx_pool_hashes() const { } bool WalletState::sync_with_blockchain(api::bytecoind::SyncMemPool::Response &resp) { - for (const auto & tid : resp.removed_hashes) { + for (const auto &tid : resp.removed_hashes) { if (m_pool_hashes.erase(tid) != 0) remove_transaction_from_mempool(tid, false); } @@ -425,18 +431,18 @@ bool WalletState::parse_raw_transaction(api::Transaction *ptx, std::vectorhash = tid; - ptx->block_height = block_height; - ptx->anonymity = std::numeric_limits::max(); - ptx->unlock_time = tx.unlock_time; - ptx->public_key = tx_public_key; - ptx->extra = tx.extra; - get_payment_id_from_tx_extra(tx.extra, ptx->payment_id); + ptx->hash = tid; + ptx->block_height = block_height; + ptx->anonymity = std::numeric_limits::max(); + ptx->unlock_block_or_timestamp = tx.unlock_block_or_timestamp; + ptx->public_key = tx_public_key; + ptx->extra = tx.extra; + extra_get_payment_id(tx.extra, ptx->payment_id); uint32_t out_index = 0; Amount output_amount = 0; @@ -446,33 +452,31 @@ bool WalletState::parse_raw_transaction(api::Transaction *ptx, std::vector transfer_map_outputs[2]; // We index by ours for (const auto &output : tx.outputs) { output_amount += output.amount; - ptx->fee -= output.amount; if (output.target.type() == typeid(KeyOutput)) { const KeyOutput &key_output = boost::get(output.target); PublicKey spend_key = pwtx.spend_keys.at(key_index); bool our_key = false; if (spend_key != PublicKey{}) { - auto sk = m_wallet.get_records().find(spend_key); - if (sk != m_wallet.get_records().end()) { + AccountPublicAddress address{spend_key, m_wallet.get_view_public_key()}; + WalletRecord record; + if (m_wallet.get_record(record, address)) { KeyPair in_ephemeral; if (derive_public_key(pwtx.derivation, out_index, spend_key, in_ephemeral.public_key)) { - derive_secret_key( - pwtx.derivation, out_index, sk->second.spend_secret_key, in_ephemeral.secret_key); + derive_secret_key(pwtx.derivation, out_index, record.spend_secret_key, in_ephemeral.secret_key); // std::cout << "My output! // out_index=" << out_index << "amount=" << output.amount << std::endl; - AccountPublicAddress address{spend_key, m_wallet.get_view_public_key()}; api::Output out; out.amount = output.amount; - out.global_index = global_indices.at(out_index); + out.index = global_indices.at(out_index); out.dust = Currency::is_dust(output.amount); out.height = block_height; out.index_in_transaction = out_index; - if (sk->second.spend_secret_key != SecretKey{}) + if (record.spend_secret_key != SecretKey{}) generate_key_image(in_ephemeral.public_key, in_ephemeral.secret_key, out.key_image); - out.public_key = key_output.key; - out.transaction_public_key = tx_public_key; - out.unlock_time = tx.unlock_time; - api::Transfer &transfer = transfer_map_outputs[true][address]; + out.public_key = key_output.public_key; + out.transaction_public_key = tx_public_key; + out.unlock_block_or_timestamp = tx.unlock_block_or_timestamp; + api::Transfer &transfer = transfer_map_outputs[true][address]; if (transfer.address.empty()) transfer.address = m_currency.account_address_as_string(address); out.address = transfer.address; @@ -492,18 +496,18 @@ bool WalletState::parse_raw_transaction(api::Transaction *ptx, std::vectorunlock_time != 0; + tm.second.locked = ptx->unlock_block_or_timestamp != 0; tm.second.ours = ours; + tm.second.transaction_hash = tid; if (tm.second.amount != 0) // We use map as a map of addresses ptx->transfers.push_back(std::move(tm.second)); } @@ -530,7 +535,6 @@ bool WalletState::parse_raw_transaction(api::Transaction *ptx, std::vector(input); input_amount += in.amount; - ptx->fee += in.amount; ptx->anonymity = std::min(ptx->anonymity, static_cast(in.output_indexes.size() - 1)); api::Output existing_output; if (try_adding_incoming_keyimage(in.key_image, &existing_output)) { @@ -545,9 +549,12 @@ bool WalletState::parse_raw_transaction(api::Transaction *ptx, std::vectorpush_back(tm.second); } - ptx->amount = std::max(input_amount, output_amount); + ptx->amount = output_amount; + if (input_amount >= output_amount) + ptx->fee = input_amount - output_amount; if (ptx->anonymity == std::numeric_limits::max()) ptx->anonymity = 0; // No key inputs return our_transaction; @@ -592,7 +599,7 @@ bool WalletState::redo_transaction(const PreparedWalletTransaction &pwtx, const if (!tr.ours) continue; for (auto &&out : tr.outputs) { - Amount adjusted_amount = delta_state->add_incoming_output(out); + Amount adjusted_amount = delta_state->add_incoming_output(out, tid); out.amount = adjusted_amount; } } @@ -650,7 +657,8 @@ bool WalletState::api_create_proof(SendProof &sp) const { api::Transaction ptx; if (!api_get_transaction(sp.transaction_hash, true, &tx, &ptx)) return false; - KeyPair tx_keys = TransactionBuilder::deterministic_keys_from_seed(tx, m_wallet.get_tx_derivation_seed()); + KeyPair tx_keys = TransactionBuilder::deterministic_keys_from_seed( + tx, ptx.coinbase ? m_wallet.get_coinbase_tx_derivation_seed() : m_wallet.get_tx_derivation_seed()); if (!crypto::generate_key_derivation(sp.address.view_public_key, tx_keys.secret_key, sp.derivation)) return false; Hash message_hash = crypto::cn_fast_hash(sp.message.data(), sp.message.size()); @@ -664,7 +672,7 @@ bool WalletState::api_create_proof(SendProof &sp) const { if (output.target.type() == typeid(KeyOutput)) { const KeyOutput &key_output = boost::get(output.target); PublicKey spend_key; - if (underive_public_key(sp.derivation, key_index, key_output.key, spend_key) && + if (underive_public_key(sp.derivation, key_index, key_output.public_key, spend_key) && spend_key == sp.address.spend_public_key) { total_amount += output.amount; } @@ -680,17 +688,21 @@ api::Block WalletState::api_get_pool_as_history(const std::string &address) cons // TODO - faster filter by address api::Block current_block; current_block.header.height = get_tip_height() + 1; - for (auto &&hit : m_memory_state.get_transactions()) { - current_block.transactions.push_back(hit.second.second); - auto &tx = current_block.transactions.back(); + for (const auto &hit : m_memory_state.get_transactions()) { + auto tx = hit.second.second; tx.block_height = get_tip_height() + 1; +// for(auto & tr : tx.transfers) // TODO - remove after DB version switch +// tr.transaction_hash = hit.second.second.hash; if (!address.empty()) { for (auto tit = tx.transfers.begin(); tit != tx.transfers.end();) if (tit->address == address) ++tit; else tit = tx.transfers.erase(tit); + if(tx.transfers.empty()) + continue; } + current_block.transactions.push_back(std::move(tx)); } return current_block; } diff --git a/src/Core/WalletState.hpp b/src/Core/WalletState.hpp index ee3d7261..0bd52ca5 100644 --- a/src/Core/WalletState.hpp +++ b/src/Core/WalletState.hpp @@ -13,7 +13,7 @@ #include "Multicore.hpp" #include "Wallet.hpp" #include "WalletStateBasic.hpp" -#include "crypto/chacha8.h" +#include "crypto/chacha8.hpp" #include "platform/DB.hpp" namespace bytecoin { @@ -41,7 +41,7 @@ class WalletState : public WalletStateBasic { void undo_transaction(const Hash &tid); // For mem pool - virtual Amount add_incoming_output(const api::Output &) override; // added amount may be lower + virtual Amount add_incoming_output(const api::Output &, const Hash & tid) override; // added amount may be lower virtual Amount add_incoming_keyimage(Height, const KeyImage &) override; virtual void add_transaction( Height, const Hash &tid, const TransactionPrefix &tx, const api::Transaction &ptx) override; @@ -100,6 +100,7 @@ class WalletState : public WalletStateBasic { Height remove_height = 0; bool in_blockchain() const { return remove_height != 0; } }; + Height get_pq_confirmations() const; private: uint32_t m_tx_pool_version = 1; diff --git a/src/Core/WalletStateBasic.cpp b/src/Core/WalletStateBasic.cpp index 283273f8..7f6918e7 100644 --- a/src/Core/WalletStateBasic.cpp +++ b/src/Core/WalletStateBasic.cpp @@ -6,6 +6,7 @@ #include "CryptoNoteTools.hpp" #include "TransactionBuilder.hpp" #include "TransactionExtra.hpp" +#include "common/Math.hpp" #include "common/Varint.hpp" #include "common/string.hpp" #include "crypto/crypto.hpp" @@ -16,7 +17,7 @@ static const auto LEVEL = logging::TRACE; -static const std::string version_current = "3"; +static const std::string version_current = "4"; static const std::string INDEX_UID_to_STATE = "X"; // We do not store it for empty blocks @@ -51,19 +52,23 @@ static const std::string LOCKED_INDEX_KI_AM_GI = "li"; // (unl_he, am, gi) -> (output) <- find yet locked by height // (unl_ti, am, gi) -> (output) <- find yet locked by timestamp -static const std::string LOCKED_INDEX_HEIGHT_AM_GI_to_OUTPUT = "lh"; // key contain clamped unlock_time -static const std::string LOCKED_INDEX_TIMESTAMP_AM_GI_to_OUTPUT = "lt"; // key contain clamped unlock_time +static const std::string LOCKED_INDEX_HEIGHT_AM_GI_to_OUTPUT = "lh"; // key contain clamped unlock_block_or_timestamp +static const std::string LOCKED_INDEX_TIMESTAMP_AM_GI_to_OUTPUT = + "lt"; // key contain clamped unlock_block_or_timestamp + +static const std::string LOCKED_INDEX_AM_GI_to_TID = "Y"; // Remove on next DB upgrade +// Index is simply overwritten and not undone. using namespace bytecoin; using namespace platform; -void seria::ser_members(bytecoin::WalletStateBasic::HeightAmounGi &v, ISeria &s) { +void seria::ser_members(WalletStateBasic::HeightAmounGi &v, ISeria &s) { seria_kv("height", v.height, s); seria_kv("amount", v.amount, s); - seria_kv("global_index", v.global_index, s); + seria_kv("index", v.global_index, s); } -void seria::ser_members(bytecoin::WalletStateBasic::UndoValue &v, seria::ISeria &s) { +void seria::ser_members(WalletStateBasic::UndoValue &v, seria::ISeria &s) { seria_kv("exists", v.exists, s); seria_kv("value", v.value, s); } @@ -94,10 +99,15 @@ WalletStateBasic::WalletStateBasic( m_db.get("$version", version); m_db.get("$genesis_bid", other_genesis_bid); m_db.get("$cache_name", other_cache_name); + if( version == "3"){ + version_3_to_4(); + m_db.get("$version", version); + } if (version != version_current || other_genesis_bid != common::pod_to_hex(m_genesis_bid) || other_cache_name != cache_name) { - m_log(logging::INFO) << "Data format, wallet seed or genesis bid different, old version=" << version - << " current version=" << version_current << ", clearing wallet cache..." << std::endl; + if (!version.empty()) + m_log(logging::INFO) << "Data format, wallet seed or genesis bid different, old version=" << version + << " current version=" << version_current << ", clearing wallet cache..." << std::endl; size_t total_items = m_db.get_approximate_items_count(); size_t erased = 0; for (DB::Cursor cur = m_db.rbegin(std::string()); !cur.end(); cur.erase()) { @@ -106,7 +116,8 @@ WalletStateBasic::WalletStateBasic( << " million DB records" << std::endl; erased += 1; } - m_db.put("$version", version_current, true); + version = version_current; + m_db.put("$version", version, true); m_db.put("$cache_name", cache_name, true); m_db.put("$genesis_bid", common::pod_to_hex(m_genesis_bid), true); } @@ -114,15 +125,63 @@ WalletStateBasic::WalletStateBasic( DB::Cursor cur1 = m_db.begin(INDEX_HEIGHT_to_HEADER); DB::Cursor cur2 = m_db.rbegin(INDEX_HEIGHT_to_HEADER); if (!cur1.end() && !cur2.end()) { - m_tip_height = boost::lexical_cast(common::read_varint_sqlite4(cur2.get_suffix())); + m_tip_height = common::integer_cast(common::read_varint_sqlite4(cur2.get_suffix())); ; - m_tail_height = boost::lexical_cast(common::read_varint_sqlite4(cur1.get_suffix())); + m_tail_height = common::integer_cast(common::read_varint_sqlite4(cur1.get_suffix())); m_tip = (m_tip_height + 1 == m_tail_height) ? api::BlockHeader{} : read_chain(m_tip_height); } fix_empty_chain(); } } +void WalletStateBasic::version_3_to_4(){ +// size_t total_items = m_db.get_approximate_items_count(); + m_log(logging::INFO) << "Updating locked transactions index. This will take a minute or two depending on your wallet size and computer speed..." << std::endl; + int transaction_counter = 0; + int upgrade_counter = 0; + int last_hash_byte = -1; + for (DB::Cursor cur = m_db.begin(INDEX_TID_to_TRANSACTIONS); !cur.end(); cur.next()) { + const std::string &suf = cur.get_suffix(); + const char *be = suf.data(); + const char *en = be + suf.size(); + Hash tid; + invariant(en - be == sizeof(tid.data), "INDEX_TID_to_TRANSACTIONS corrupted"); + DB::from_binary_key(cur.get_suffix(), 0, tid.data, sizeof(tid.data)); + if( tid.data[0] != last_hash_byte ){ + last_hash_byte = tid.data[0]; + m_log(logging::INFO) << "Transactions processed " << transaction_counter << " (" << (last_hash_byte * 100 / 256) << "%)" << std::endl; + } + std::pair pa; + seria::from_binary(pa, cur.get_value_array()); + transaction_counter += 1; + if (pa.second.unlock_block_or_timestamp == 0) + continue; + for(const auto & tr : pa.second.transfers) + for(const auto & output : tr.outputs){ + put_am_gi_tid(output.amount, output.index, tid); + upgrade_counter += 1; + } + } + m_db.put("$version", "4", false); + m_log(logging::INFO) << "Updating locked transactions index finished. " << transaction_counter << " transactions processed, " << upgrade_counter << " DB records modified." << std::endl; +} +void WalletStateBasic::put_am_gi_tid(Amount am, uint32_t gi, Hash tid){ + std::string unkey = LOCKED_INDEX_AM_GI_to_TID + common::write_varint_sqlite4(am) + + common::write_varint_sqlite4(gi); + BinaryArray ba = seria::to_binary(tid); + m_db.put(unkey, ba, false); +} +Hash WalletStateBasic::get_am_gi_tid(Amount am, uint32_t gi)const{ + std::string unkey = LOCKED_INDEX_AM_GI_to_TID + common::write_varint_sqlite4(am) + + common::write_varint_sqlite4(gi); + BinaryArray ba; + if(!m_db.get(unkey, ba)) + return Hash{}; + Hash tid; + seria::from_binary(tid, ba); + return tid; +} + void WalletStateBasic::combine_balance( api::Balance &balance, const api::Output &output, int locked_op, int spendable_op) { common::Uint128 &mod = output.dust ? balance.spendable_dust : balance.spendable; @@ -147,9 +206,9 @@ void WalletStateBasic::db_commit() { std::string WalletStateBasic::format_output(const api::Output &v) { std::stringstream str; - str << " he=" << v.height << " am=" << m_currency.format_amount(v.amount) << " gi=" << v.global_index + str << " he=" << v.height << " am=" << m_currency.format_amount(v.amount) << " gi=" << v.index << " ki=" << v.key_image << " addr=" << v.address - << (v.unlock_time == 0 ? "" : " unl=" + common::to_string(v.unlock_time)); + << (v.unlock_block_or_timestamp == 0 ? "" : " unl=" + common::to_string(v.unlock_block_or_timestamp)); return str.str(); } @@ -215,7 +274,7 @@ std::vector WalletStateBasic::get_sparse_chain() const { return tip_path; } -void WalletStateBasic::record_undo(UndoMap &undo_map, const std::string &key) { +WalletStateBasic::UndoMap::iterator WalletStateBasic::record_undo(UndoMap &undo_map, const std::string &key) { UndoMap::iterator kit = undo_map.find(key); if (kit == undo_map.end()) { kit = undo_map.insert(std::make_pair(key, UndoValue{})).first; @@ -225,16 +284,23 @@ void WalletStateBasic::record_undo(UndoMap &undo_map, const std::string &key) { kit->second.value = std::move(was_value); } } + return kit; } void WalletStateBasic::put_with_undo(const std::string &key, const common::BinaryArray &value, bool nooverwrite) { +// UndoMap::iterator kit = record_undo(current_undo_map, key); m_db.put(key, value, nooverwrite); +// if(kit->second.exists && kit->second.value == value) - TODO - test before next release +// current_undo_map.erase(kit); } void WalletStateBasic::del_with_undo(const std::string &key, bool mustexist) { +// UndoMap::iterator kit = record_undo(current_undo_map, key); m_db.del(key, mustexist); +// if(!kit->second.exists) - TODO - test before next release +// current_undo_map.erase(kit); } void WalletStateBasic::save_db_state(uint32_t state, const UndoMap &undo_map) { @@ -268,7 +334,7 @@ bool WalletStateBasic::try_add_incoming_output(const api::Output &output, Amount bool is_existing_unspent = ki_exists && read_from_unspent_index(heamgi, &existing_output); if (ki_exists && !is_existing_unspent) return false; - if (output.unlock_time != 0) { + if (output.unlock_block_or_timestamp != 0) { *confirmed_balance_delta = output.amount; // It will be locked by transfer.locked == true return true; } @@ -282,7 +348,7 @@ bool WalletStateBasic::try_add_incoming_output(const api::Output &output, Amount return true; } -Amount WalletStateBasic::add_incoming_output(const api::Output &output, bool just_unlocked) { +Amount WalletStateBasic::add_incoming_output(const api::Output &output, const Hash & tid, bool just_unlocked) { HeightAmounGi heamgi; bool ki_exists = read_by_keyimage(output.key_image, &heamgi); api::Output existing_output; @@ -291,8 +357,8 @@ Amount WalletStateBasic::add_incoming_output(const api::Output &output, bool jus m_log(logging::WARNING) << " Duplicate key_output attack, ignoring output because already spent" << std::endl; return 0; } - if (output.unlock_time != 0 && !just_unlocked) { // incoming - add_to_lock_index(output); + if (output.unlock_block_or_timestamp != 0 && !just_unlocked) { // incoming + add_to_lock_index(output, tid); return output.amount; } Amount added_amount = output.amount; @@ -312,14 +378,14 @@ Amount WalletStateBasic::add_incoming_output(const api::Output &output, bool jus add_to_unspent_index(output); heamgi.height = output.height; heamgi.amount = output.amount; - heamgi.global_index = output.global_index; + heamgi.global_index = output.index; update_keyimage(output.key_image, heamgi, !ki_exists); return added_amount; } -Amount WalletStateBasic::add_incoming_output(const api::Output &output) { +Amount WalletStateBasic::add_incoming_output(const api::Output &output, const Hash & tid) { m_log(LEVEL) << "Incoming output " << format_output(output) << std::endl; - return add_incoming_output(output, false); + return add_incoming_output(output, tid, false); } Amount WalletStateBasic::add_incoming_keyimage(Height block_height, const KeyImage &key_image) { @@ -332,11 +398,11 @@ Amount WalletStateBasic::add_incoming_keyimage(Height block_height, const KeyIma const char *be = suf.data(); const char *en = be + suf.size(); Amount am = common::read_varint_sqlite4(be, en); - uint32_t gi = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + uint32_t gi = common::integer_cast(common::read_varint_sqlite4(be, en)); invariant(en - be == 0, ""); - UnlockMoment unl = 0; + BlockOrTimestamp unl = 0; seria::from_binary(unl, cur.get_value_array()); - uint32_t clamped_unlock_time = static_cast(std::min(unl, 0xFFFFFFFF)); + uint32_t clamped_unlock_time = static_cast(std::min(unl, 0xFFFFFFFF)); std::string unkey = m_currency.is_transaction_spend_time_block(unl) ? LOCKED_INDEX_HEIGHT_AM_GI_to_OUTPUT : LOCKED_INDEX_TIMESTAMP_AM_GI_to_OUTPUT; unkey += common::write_varint_sqlite4(clamped_unlock_time) + common::write_varint_sqlite4(am) + @@ -374,13 +440,13 @@ bool WalletStateBasic::try_adding_incoming_keyimage(const KeyImage &key_image, a const char *be = suf.data(); const char *en = be + suf.size(); Amount am = common::read_varint_sqlite4(be, en); - uint32_t gi = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + uint32_t gi = common::integer_cast(common::read_varint_sqlite4(be, en)); invariant(en - be == 0, ""); if (candidate_found && am <= spending_output->amount) continue; - UnlockMoment unl = 0; + BlockOrTimestamp unl = 0; seria::from_binary(unl, cur.get_value_array()); - uint32_t clamped_unlock_time = static_cast(std::min(unl, 0xFFFFFFFF)); + uint32_t clamped_unlock_time = static_cast(std::min(unl, 0xFFFFFFFF)); std::string unkey = m_currency.is_transaction_spend_time_block(unl) ? LOCKED_INDEX_HEIGHT_AM_GI_to_OUTPUT : LOCKED_INDEX_TIMESTAMP_AM_GI_to_OUTPUT; unkey += common::write_varint_sqlite4(clamped_unlock_time) + common::write_varint_sqlite4(am) + @@ -389,7 +455,7 @@ bool WalletStateBasic::try_adding_incoming_keyimage(const KeyImage &key_image, a invariant(m_db.get(unkey, output_ba), ""); api::Output output; seria::from_binary(output, output_ba); - invariant(output.amount == am && output.global_index == gi, ""); + invariant(output.amount == am && output.index == gi, ""); if (candidate_found && output.address != spending_output->address) continue; *spending_output = output; @@ -420,11 +486,10 @@ void WalletStateBasic::add_transaction( bool WalletStateBasic::api_add_unspent(std::vector *result, Amount *total_amount, const std::string &address, Height confirmed_height, Amount max_amount) const { - auto recently_unlocked = api_get_unlocked_outputs(address, confirmed_height, std::numeric_limits::max()); + auto recently_unlocked = get_unlocked_outputs(address, confirmed_height, std::numeric_limits::max()); const size_t min_count = 10000; // We return up to 10k outputs after we find requested sum return for_each_in_unspent_index(address, Height(-1), confirmed_height, [&](const api::Output &output) -> bool { - if (!is_memory_spent(output) && - recently_unlocked.count(std::make_pair(output.amount, output.global_index)) == 0) { + if (!is_memory_spent(output) && recently_unlocked.count(std::make_pair(output.amount, output.index)) == 0) { result->push_back(output); if (!output.dust) // We ensure total can be spent with non-zero anonymity *total_amount += output.amount; @@ -448,7 +513,7 @@ std::vector WalletStateBasic::api_get_transfers( const std::string &suf = cur.get_suffix(); const char *be = suf.data(); const char *en = be + suf.size(); - Height height = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + Height height = common::integer_cast(common::read_varint_sqlite4(be, en)); Hash tid; invariant(en - be == sizeof(tid.data), "CD_TIPS_PREFIX corrupted"); DB::from_binary_key(cur.get_suffix(), cur.get_suffix().size() - sizeof(tid.data), tid.data, sizeof(tid.data)); @@ -459,6 +524,15 @@ std::vector WalletStateBasic::api_get_transfers( TransactionPrefix ptx; api::Transaction tx; get_transaction(tid, &ptx, &tx); + if (!address.empty()) { + for (auto tit = tx.transfers.begin(); tit != tx.transfers.end();) + if (tit->address == address) + ++tit; + else + tit = tx.transfers.erase(tit); + if(tx.transfers.empty()) + continue; + } if (current_block.header.height != height && !current_block.transactions.empty()) { result.push_back(std::move(current_block)); current_block = api::Block(); @@ -473,13 +547,6 @@ std::vector WalletStateBasic::api_get_transfers( if (current_block.transactions.empty()) { read_chain(height, current_block.header); } - if (!address.empty()) { - for (auto tit = tx.transfers.begin(); tit != tx.transfers.end();) - if (tit->address == address) - ++tit; - else - tit = tx.transfers.erase(tit); - } current_block.transactions.push_back(std::move(tx)); total_transactions_found += 1; } @@ -498,12 +565,12 @@ std::vector WalletStateBasic::api_get_locked_or_unconfirmed_unspent result.push_back(output); return true; }); - auto recently_unlocked = api_get_unlocked_outputs(address, confirmed_height, std::numeric_limits::max()); + auto recently_unlocked = get_unlocked_outputs(address, confirmed_height, std::numeric_limits::max()); for (auto &&lou : recently_unlocked) { HeightAmounGi heamgi; heamgi.height = lou.second.height; heamgi.amount = lou.second.amount; - heamgi.global_index = lou.second.global_index; + heamgi.global_index = lou.second.index; api::Output existing_output; bool is_existing_unspent = read_from_unspent_index(heamgi, &existing_output); if (!is_existing_unspent || is_memory_spent(lou.second)) @@ -539,7 +606,7 @@ api::Balance WalletStateBasic::get_balance(const std::string &address, Height co return true; }); - auto recently_unlocked = api_get_unlocked_outputs(address, confirmed_height, std::numeric_limits::max()); + auto recently_unlocked = get_unlocked_outputs(address, confirmed_height, std::numeric_limits::max()); for (auto &&lou : recently_unlocked) { HeightAmounGi heamgi; heamgi.height = lou.second.height; @@ -587,6 +654,8 @@ bool WalletStateBasic::get_transaction(Hash tid, TransactionPrefix *tx, api::Tra return false; std::pair pa; seria::from_binary(pa, data); + for(auto & tr : pa.second.transfers) // TODO - remove after DB version switch + tr.transaction_hash = pa.second.hash; *tx = std::move(pa.first); *ptx = std::move(pa.second); return true; @@ -596,14 +665,14 @@ static void parse_lock_key( const std::string &suffix, uint32_t *clamped_unlocktime, Amount *amount, uint32_t *global_index) { const char *be = suffix.data(); const char *en = be + suffix.size(); - *clamped_unlocktime = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + *clamped_unlocktime = common::integer_cast(common::read_varint_sqlite4(be, en)); *amount = common::read_varint_sqlite4(be, en); - *global_index = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + *global_index = common::integer_cast(common::read_varint_sqlite4(be, en)); invariant(en - be == 0, ""); } // Unique in that it reads (begin, end] interval, not [begin, end) as most other // funs. That is because block height 312 -// unlocks output with unlock_time=312 +// unlocks output with unlock_block_or_timestamp=312 void WalletStateBasic::read_unlock_index(std::map, api::Output> *add, const std::string &index_prefix, const std::string &address, uint32_t begin, uint32_t end) const { if (begin != uint32_t(-1) && begin >= end) // optimization @@ -620,22 +689,43 @@ void WalletStateBasic::read_unlock_index(std::map, a seria::from_binary(output, cur.get_value_array()); // amount can be different to output.amount, if added from te same ki group // original amount is in unlocked index key, use it as a key because it is unambigous - invariant(output.global_index == global_index, "Index corrupted"); + invariant(output.index == global_index, "Index corrupted"); if (address.empty() || output.address == address) - invariant(add->insert(std::make_pair(std::make_pair(amount, output.global_index), output)).second, + invariant(add->insert(std::make_pair(std::make_pair(amount, output.index), output)).second, "Invariant dead read_unlock_index adding output twice"); } } -std::map, api::Output> WalletStateBasic::api_get_unlocked_outputs( - const std::string &address, - Height from_height, - Height to_height) const { +std::map, api::Output> WalletStateBasic::get_unlocked_outputs( + const std::string &address, + Height from_height, + Height to_height) const { std::map, api::Output> unlocked; read_unlock_index(&unlocked, UNLOCKED_INDEX_REALHE_AM_GI_to_OUTPUT, address, from_height, to_height); return unlocked; } +std::vector WalletStateBasic::api_get_unlocked_transfers( + const std::string &address, Height from_height, Height to_height) const { + auto unlocked = get_unlocked_outputs(address, from_height, to_height); + std::map, api::Transfer> transfers; + for(auto & unl : unlocked){ + Hash tid = get_am_gi_tid(unl.second.amount, unl.second.index); + api::Transfer & tr = transfers[std::make_pair(tid, unl.second.address)]; + tr.ours = true; + tr.amount += unl.second.amount; + tr.address = unl.second.address; + tr.transaction_hash = tid; + tr.outputs.push_back(std::move(unl.second)); + } + std::vector result; + result.reserve(transfers.size()); + for(auto & tra : transfers){ + result.push_back(std::move(tra.second)); + } + return result; +} + void WalletStateBasic::modify_balance(const api::Output &output, int locked_op, int spendable_op) { auto bakey = INDEX_ADDRESS_to_BALANCE + output.address; auto bakey2 = INDEX_ADDRESS_to_BALANCE; @@ -665,48 +755,52 @@ const std::map &WalletStateBasic::get_used_key_images() const { r void WalletStateBasic::unlock(Height now_height, api::Output &&output) { remove_from_lock_index(output); - Amount adjusted_amount = add_incoming_output(output, true); + Amount adjusted_amount = add_incoming_output(output, Hash{}, true); // if( adjusted_amount == 0) // Unlocked and have coin with the same ki and amount // continue; // We decided to put in DB anyway, so that we know we did not miss unlock // We add into index with original amount as a key because otherwise there could be ambiguity in index auto unkey = UNLOCKED_INDEX_REALHE_AM_GI_to_OUTPUT + common::write_varint_sqlite4(now_height) + - common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.index); output.amount = adjusted_amount; BinaryArray ba = seria::to_binary(output); put_with_undo(unkey, ba, true); } -void WalletStateBasic::add_to_lock_index(const api::Output &output) { +void WalletStateBasic::add_to_lock_index(const api::Output &output, const Hash & tid) { m_log(LEVEL) << " Adding output to lock index, " << format_output(output) << std::endl; + put_am_gi_tid(output.amount, output.index, tid); + modify_balance(output, 1, 0); - uint32_t clamped_unlock_time = static_cast(std::min(output.unlock_time, 0xFFFFFFFF)); - std::string unkey = m_currency.is_transaction_spend_time_block(output.unlock_time) + uint32_t clamped_unlock_time = + static_cast(std::min(output.unlock_block_or_timestamp, 0xFFFFFFFF)); + std::string unkey = m_currency.is_transaction_spend_time_block(output.unlock_block_or_timestamp) ? LOCKED_INDEX_HEIGHT_AM_GI_to_OUTPUT : LOCKED_INDEX_TIMESTAMP_AM_GI_to_OUTPUT; unkey += common::write_varint_sqlite4(clamped_unlock_time) + common::write_varint_sqlite4(output.amount) + - common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.index); put_with_undo(unkey, seria::to_binary(output), true); if (output.key_image != KeyImage{}) { unkey = LOCKED_INDEX_KI_AM_GI + DB::to_binary_key(output.key_image.data, sizeof(output.key_image.data)) + - common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.global_index); - BinaryArray ba = seria::to_binary(output.unlock_time); + common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.index); + BinaryArray ba = seria::to_binary(output.unlock_block_or_timestamp); put_with_undo(unkey, ba, true); } } void WalletStateBasic::remove_from_lock_index(const api::Output &output) { m_log(LEVEL) << " Removing output from lock index, " << format_output(output) << std::endl; - uint32_t clamped_unlock_time = static_cast(std::min(output.unlock_time, 0xFFFFFFFF)); - std::string unkey = m_currency.is_transaction_spend_time_block(output.unlock_time) + uint32_t clamped_unlock_time = + static_cast(std::min(output.unlock_block_or_timestamp, 0xFFFFFFFF)); + std::string unkey = m_currency.is_transaction_spend_time_block(output.unlock_block_or_timestamp) ? LOCKED_INDEX_HEIGHT_AM_GI_to_OUTPUT : LOCKED_INDEX_TIMESTAMP_AM_GI_to_OUTPUT; unkey += common::write_varint_sqlite4(clamped_unlock_time) + common::write_varint_sqlite4(output.amount) + - common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.index); modify_balance(output, -1, 0); del_with_undo(unkey, true); if (output.key_image != KeyImage{}) { unkey = LOCKED_INDEX_KI_AM_GI + DB::to_binary_key(output.key_image.data, sizeof(output.key_image.data)) + - common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.index); del_with_undo(unkey, true); } } @@ -739,9 +833,9 @@ bool WalletStateBasic::for_each_in_unspent_index( const std::string &suf = cur.get_suffix(); const char *be = suf.data(); const char *en = be + suf.size(); - Height he = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + Height he = common::integer_cast(common::read_varint_sqlite4(be, en)); Amount am = common::read_varint_sqlite4(be, en); - uint32_t gi = boost::lexical_cast(common::read_varint_sqlite4(be, en)); + uint32_t gi = common::integer_cast(common::read_varint_sqlite4(be, en)); invariant(en - be == 0, ""); if (he > to) break; @@ -762,11 +856,11 @@ void WalletStateBasic::add_to_unspent_index(const api::Output &output) { m_log(LEVEL) << " Adding to unspent, " << format_output(output) << std::endl; modify_balance(output, 0, 1); auto keyun = INDEX_HE_AM_GI_to_OUTPUT + common::write_varint_sqlite4(output.height) + - common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.index); put_with_undo(keyun, seria::to_binary(output), true); keyun = INDEX_ADDRESS_HE_AM_GI + output.address + "/" + common::write_varint_sqlite4(output.height) + - common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.index); put_with_undo(keyun, BinaryArray{}, true); } @@ -774,11 +868,11 @@ void WalletStateBasic::remove_from_unspent_index(const api::Output &output) { m_log(LEVEL) << " Removing from unspent, " << format_output(output) << std::endl; modify_balance(output, 0, -1); auto keyun = INDEX_HE_AM_GI_to_OUTPUT + common::write_varint_sqlite4(output.height) + - common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.index); del_with_undo(keyun, true); keyun = INDEX_ADDRESS_HE_AM_GI + output.address + "/" + common::write_varint_sqlite4(output.height) + - common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.global_index); + common::write_varint_sqlite4(output.amount) + common::write_varint_sqlite4(output.index); del_with_undo(keyun, true); } diff --git a/src/Core/WalletStateBasic.hpp b/src/Core/WalletStateBasic.hpp index e0daf9bb..b93cd1bd 100644 --- a/src/Core/WalletStateBasic.hpp +++ b/src/Core/WalletStateBasic.hpp @@ -14,7 +14,7 @@ #include "BlockChainState.hpp" #include "CryptoNote.hpp" #include "Wallet.hpp" -#include "crypto/chacha8.h" +#include "crypto/chacha8.hpp" #include "platform/DB.hpp" #include "rpc_api.hpp" @@ -26,7 +26,7 @@ class IWalletState { public: virtual ~IWalletState() {} - virtual Amount add_incoming_output(const api::Output &) = 0; // added amount may be lower + virtual Amount add_incoming_output(const api::Output &, const Hash & tid) = 0; // added amount may be lower virtual Amount add_incoming_keyimage(Height block_height, const KeyImage &) = 0; virtual void add_transaction( Height block_height, const Hash &tid, const TransactionPrefix &tx, const api::Transaction &ptx) = 0; @@ -52,7 +52,7 @@ class WalletStateBasic : protected IWalletState { virtual std::vector api_get_locked_or_unconfirmed_unspent( const std::string &address, Height height) const; virtual api::Balance get_balance(const std::string &address, Height height) const; - std::map, api::Output> api_get_unlocked_outputs( + std::vector api_get_unlocked_transfers( const std::string &address, Height from_height, Height to_height = std::numeric_limits::max()) const; bool get_transaction(Hash tid, TransactionPrefix *tx, api::Transaction *ptx) const; bool has_transaction(Hash tid) const; @@ -102,7 +102,7 @@ class WalletStateBasic : protected IWalletState { // returns true if our keyimage // methods to add incoming tx - Amount add_incoming_output(const api::Output &) override; // added amount may be lower + Amount add_incoming_output(const api::Output &, const Hash & tid) override; // added amount may be lower Amount add_incoming_keyimage(Height block_height, const KeyImage &) override; void add_transaction(Height, const Hash &tid, const TransactionPrefix &tx, const api::Transaction &ptx) override; @@ -113,26 +113,31 @@ class WalletStateBasic : protected IWalletState { Height m_tail_height = 0; api::BlockHeader m_tip; + void version_3_to_4(); + void put_am_gi_tid(Amount am, uint32_t gi, Hash tid); + Hash get_am_gi_tid(Amount am, uint32_t gi)const; // DB generic undo machinery typedef std::map UndoMap; UndoMap current_undo_map; - void record_undo(UndoMap &undo_map, const std::string &key); + UndoMap::iterator record_undo(UndoMap &undo_map, const std::string &key); void put_with_undo(const std::string &key, const common::BinaryArray &value, bool nooverwrite); void del_with_undo(const std::string &key, bool mustexist); void save_db_state(uint32_t state, const UndoMap &undo_map); void undo_db_state(uint32_t state); // indices implemenation - Amount add_incoming_output(const api::Output &, bool just_unlocked); + Amount add_incoming_output(const api::Output &, const Hash & tid, bool just_unlocked); void modify_balance(const api::Output &output, int locked_op, int spendable_op); // lock/unlock - void add_to_lock_index(const api::Output &); + void add_to_lock_index(const api::Output &, const Hash & tid); void remove_from_lock_index(const api::Output &); void unlock(Height now_height, api::Output &&output); void add_to_unlocked_index(const api::Output &, Height); void read_unlock_index(std::map, api::Output> *add, const std::string &index_prefix, const std::string &address, uint32_t begin, uint32_t end) const; + std::map, api::Output> get_unlocked_outputs(const std::string &address, + Height from_height, Height to_height) const; // add coin/spend coin void add_to_unspent_index(const api::Output &); diff --git a/src/Core/WalletSync.cpp b/src/Core/WalletSync.cpp index 0d7656ea..f378a247 100644 --- a/src/Core/WalletSync.cpp +++ b/src/Core/WalletSync.cpp @@ -30,12 +30,12 @@ WalletSync::WalletSync( , m_wallet_state(wallet_state) , m_commit_timer(std::bind(&WalletSync::db_commit, this)) { advance_sync(); - m_commit_timer.once(DB_COMMIT_PERIOD_WALLET_CACHE); + m_commit_timer.once(m_config.db_commit_period_wallet_cache); } void WalletSync::db_commit() { m_wallet_state.db_commit(); - m_commit_timer.once(DB_COMMIT_PERIOD_WALLET_CACHE); + m_commit_timer.once(m_config.db_commit_period_wallet_cache); } void WalletSync::send_get_status() { @@ -45,13 +45,10 @@ void WalletSync::send_get_status() { req.outgoing_peer_count = m_last_node_status.outgoing_peer_count; req.incoming_peer_count = m_last_node_status.incoming_peer_count; req.lower_level_error = m_last_node_status.lower_level_error; - json_rpc::Request json_send_raw_req; - json_send_raw_req.set_method(api::bytecoind::GetStatus::method()); - json_send_raw_req.set_params(req); - http::RequestData req_header; - req_header.r.set_firstline("POST", api::bytecoind::url(), 1, 1); + + http::RequestData req_header = + json_rpc::create_request(api::bytecoind::url(), api::bytecoind::GetStatus::method(), req); req_header.r.basic_authorization = m_config.bytecoind_authorization; - req_header.set_body(json_send_raw_req.get_body()); m_sync_request = std::make_unique(m_sync_agent, std::move(req_header), [&](http::ResponseData &&response) { @@ -64,11 +61,17 @@ void WalletSync::send_get_status() { m_status_timer.once(STATUS_ERROR_PERIOD); } else { api::bytecoind::GetStatus::Response resp; - json_rpc::parse_response(response.body, resp); - m_last_node_status = resp; - m_sync_error = std::string(); - m_state_changed_handler(); - advance_sync(); + json_rpc::Error error; + if (json_rpc::parse_response(response.body, resp, error)) { + m_last_node_status = resp; + m_sync_error = std::string(); + m_state_changed_handler(); + advance_sync(); + } else { + m_log(logging::INFO) << "GetStatus request RPC error code=" << error.code + << " message=" << error.message << std::endl; + m_status_timer.once(STATUS_ERROR_PERIOD); + } } }, [&](std::string err) { @@ -110,9 +113,9 @@ void WalletSync::send_sync_pool() { api::bytecoind::SyncMemPool::Request msg; msg.known_hashes = m_wallet_state.get_tx_pool_hashes(); http::RequestData req_header; - req_header.r.set_firstline("POST", api::bytecoind::SyncMemPool::bin_method(), 1, 1); + req_header.r.set_firstline("POST", api::bytecoind::binary_url(), 1, 1); req_header.r.basic_authorization = m_config.bytecoind_authorization; - req_header.set_body(seria::to_binary_str(msg)); + req_header.set_body(json_rpc::create_binary_request_body(api::bytecoind::SyncMemPool::method(), msg)); m_sync_request = std::make_unique(m_sync_agent, std::move(req_header), [&](http::ResponseData &&response) { m_sync_request.reset(); @@ -121,20 +124,22 @@ void WalletSync::send_sync_pool() { m_sync_error = "AUTHORIZATION_FAILED"; m_log(logging::INFO) << "Wrong daemon password - please check --bytecoind-authorization" << std::endl; m_status_timer.once(STATUS_ERROR_PERIOD); - } else if (response.r.status == 410) { - m_sync_error = "WRONG_DAEMON_VERSION"; - m_log(logging::INFO) << "Wrong daemon version - please upgrade bytecoind" << std::endl; - m_status_timer.once(STATUS_ERROR_PERIOD); } else if (response.r.status == 200) { m_sync_error = "WRONG_BLOCKCHAIN"; api::bytecoind::SyncMemPool::Response resp; - seria::from_binary(resp, response.body); - m_last_node_status = resp.status; - if (m_wallet_state.sync_with_blockchain(resp)) { - m_sync_error = std::string(); - advance_sync(); - } else + json_rpc::Error error; + if (json_rpc::parse_binary_response(response.body, resp, error)) { + m_last_node_status = resp.status; + if (m_wallet_state.sync_with_blockchain(resp)) { + m_sync_error = std::string(); + advance_sync(); + } else + m_status_timer.once(STATUS_ERROR_PERIOD); + } else { + m_log(logging::INFO) << "SyncMemPool request RPC error code=" << error.code + << " message=" << error.message << std::endl; m_status_timer.once(STATUS_ERROR_PERIOD); + } } else { m_sync_error = response.body; m_status_timer.once(STATUS_ERROR_PERIOD); @@ -155,10 +160,11 @@ void WalletSync::send_get_blocks() { api::bytecoind::SyncBlocks::Request msg; msg.sparse_chain = m_wallet_state.get_sparse_chain(); msg.first_block_timestamp = m_wallet_state.get_wallet().get_oldest_timestamp(); + msg.need_redundant_data = false; http::RequestData req_header; - req_header.r.set_firstline("POST", api::bytecoind::SyncBlocks::bin_method(), 1, 1); + req_header.r.set_firstline("POST", api::bytecoind::binary_url(), 1, 1); req_header.r.basic_authorization = m_config.bytecoind_authorization; - req_header.set_body(seria::to_binary_str(msg)); + req_header.set_body(json_rpc::create_binary_request_body(api::bytecoind::SyncBlocks::method(), msg)); m_sync_request = std::make_unique(m_sync_agent, std::move(req_header), [&](http::ResponseData &&response) { m_sync_request.reset(); @@ -167,20 +173,22 @@ void WalletSync::send_get_blocks() { m_sync_error = "AUTHORIZATION_FAILED"; m_log(logging::INFO) << "Wrong daemon password - please check --bytecoind-authorization" << std::endl; m_status_timer.once(STATUS_ERROR_PERIOD); - } else if (response.r.status == 410) { - m_sync_error = "WRONG_DAEMON_VERSION"; - m_log(logging::INFO) << "Wrong daemon version - please upgrade bytecoind" << std::endl; - m_status_timer.once(STATUS_ERROR_PERIOD); } else if (response.r.status == 200) { m_sync_error = "WRONG_BLOCKCHAIN"; api::bytecoind::SyncBlocks::Response resp; - seria::from_binary(resp, response.body); - m_last_node_status = resp.status; - if (m_wallet_state.sync_with_blockchain(resp)) { - m_sync_error = std::string(); - advance_sync(); - } else + json_rpc::Error error; + if (json_rpc::parse_binary_response(response.body, resp, error)) { + m_last_node_status = resp.status; + if (m_wallet_state.sync_with_blockchain(resp)) { + m_sync_error = std::string(); + advance_sync(); + } else + m_status_timer.once(STATUS_ERROR_PERIOD); + } else { + m_log(logging::INFO) << "SyncBlocks request RPC error code=" << error.code + << " message=" << error.message << std::endl; m_status_timer.once(STATUS_ERROR_PERIOD); + } } else { m_sync_error = response.body; m_status_timer.once(STATUS_ERROR_PERIOD); @@ -216,19 +224,17 @@ bool WalletSync::send_send_transaction() { m_status_timer.once(STATUS_ERROR_PERIOD); } else if (response.r.status == 200) { m_sync_error = "SEND_ERROR"; - json_rpc::Response json_resp(response.body); api::bytecoind::SendTransaction::Response resp; - api::bytecoind::SendTransaction::Error err_resp; - if (json_resp.get_error(err_resp)) { - m_log(logging::INFO) << "Json Error sending transaction from payment queue conflict height=" - << err_resp.conflict_height << " code=" << err_resp.code - << " msg=" << err_resp.message << std::endl; - m_wallet_state.process_payment_queue_send_error(sending_transaction_hash, err_resp); - } else { - json_resp.get_result(resp); + api::bytecoind::SendTransaction::Error error; + if (json_rpc::parse_response(response.body, resp, error)) { m_log(logging::INFO) << "Success sending transaction from payment queue with result " << resp.send_result << std::endl; m_sync_error = std::string(); + } else { + m_log(logging::INFO) << "Json Error sending transaction from payment queue conflict height=" + << error.conflict_height << " code=" << error.code << " msg=" << error.message + << std::endl; + m_wallet_state.process_payment_queue_send_error(sending_transaction_hash, error); } advance_sync(); } else { diff --git a/src/CryptoNote.cpp b/src/CryptoNote.cpp new file mode 100644 index 00000000..108d7e03 --- /dev/null +++ b/src/CryptoNote.cpp @@ -0,0 +1,422 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include "CryptoNote.hpp" +#include "Core/CryptoNoteTools.hpp" +#include "Core/TransactionExtra.hpp" +#include "rpc_api.hpp" +#include "seria/JsonOutputStream.hpp" +// includes below are for proof seria +#include "Core/Currency.hpp" +#include "common/Base58.hpp" +#include "common/Varint.hpp" + +using namespace bytecoin; + +namespace seria { +enum class SerializationTag2 : uint8_t { Base = 0xff, Key = 0x2 }; + +void ser(Hash &v, ISeria &s) { s.binary(v.data, sizeof(v.data)); } +void ser(KeyImage &v, ISeria &s) { s.binary(v.data, sizeof(v.data)); } +void ser(PublicKey &v, ISeria &s) { s.binary(v.data, sizeof(v.data)); } +void ser(SecretKey &v, ISeria &s) { s.binary(v.data, sizeof(v.data)); } +void ser(KeyDerivation &v, ISeria &s) { s.binary(v.data, sizeof(v.data)); } +void ser(Signature &v, ISeria &s) { s.binary(reinterpret_cast(&v), sizeof(Signature)); } +void ser_members(AccountPublicAddress &v, ISeria &s) { + seria_kv("spend", v.spend_public_key, s); + seria_kv("view", v.view_public_key, s); +} +void ser_members(SendProof &v, ISeria &s, const Currency ¤cy) { + std::string addr; + if (!s.is_input()) + addr = currency.account_address_as_string(v.address); + seria_kv("address", addr, s); + if (s.is_input() && (!currency.parse_account_address_string(addr, &v.address))) + throw api::ErrorAddress(api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Failed to parse wallet address", addr); + std::string proof; + BinaryArray binary_proof; + if (!s.is_input()) { + common::append(binary_proof, std::begin(v.derivation.data), std::end(v.derivation.data)); + common::append(binary_proof, std::begin(v.signature.c.data), std::end(v.signature.c.data)); + common::append(binary_proof, std::begin(v.signature.r.data), std::end(v.signature.r.data)); + proof = common::base58::encode(binary_proof); + } + seria_kv("proof", proof, s); + if (s.is_input()) { + if (!common::base58::decode(proof, &binary_proof) || + binary_proof.size() != sizeof(v.derivation.data) + sizeof(v.signature.c) + sizeof(v.signature.r)) + throw std::runtime_error("Wrong proof format - " + proof); + memmove(v.derivation.data, binary_proof.data(), sizeof(v.derivation)); + memmove(v.signature.c.data, binary_proof.data() + sizeof(v.derivation), sizeof(v.signature.c)); + memmove(v.signature.r.data, + binary_proof.data() + sizeof(v.derivation) + sizeof(v.signature.c), + sizeof(v.signature.r)); + } + seria_kv("transaction_hash", v.transaction_hash, s); + seria_kv("message", v.message, s); + seria_kv("amount", v.amount, s); +} +void ser_members(TransactionInput &v, ISeria &s) { + if (s.is_input()) { + uint8_t tag = 0; + s.object_key("type"); + if (dynamic_cast(&s)) { + std::string str_tag; + ser(str_tag, s); + if (str_tag == "coinbase") + tag = (uint8_t)SerializationTag2::Base; + else if (str_tag == "key") + tag = (uint8_t)SerializationTag2::Key; + } else + s.binary(&tag, 1); + switch ((SerializationTag2)tag) { + case SerializationTag2::Base: { + CoinbaseInput in{}; + ser_members(in, s); + v = in; + break; + } + case SerializationTag2::Key: { + KeyInput in{}; + ser_members(in, s); + v = in; + break; + } + default: + throw std::runtime_error("Deserialization error - unknown input tag"); + } + return; + } + if (v.type() == typeid(CoinbaseInput)) { + CoinbaseInput &in = boost::get(v); + uint8_t tag = (uint8_t)SerializationTag2::Base; + s.object_key("type"); + if (dynamic_cast(&s)) { + std::string str_tag = "coinbase"; + ser(str_tag, s); + } else + s.binary(&tag, 1); + ser_members(in, s); + } else if (v.type() == typeid(KeyInput)) { + KeyInput &in = boost::get(v); + uint8_t tag = (uint8_t)SerializationTag2::Key; + s.object_key("type"); + if (dynamic_cast(&s)) { + std::string str_tag = "key"; + ser(str_tag, s); + } else + s.binary(&tag, 1); + ser_members(in, s); + } +} +void ser_members(TransactionOutputTarget &v, ISeria &s) { + if (s.is_input()) { + uint8_t tag = 0; + s.object_key("type"); + if (dynamic_cast(&s)) { + std::string str_tag; + ser(str_tag, s); + if (str_tag == "key") + tag = (uint8_t)SerializationTag2::Key; + } else + s.binary(&tag, 1); + switch ((SerializationTag2)tag) { + case SerializationTag2::Key: { + KeyOutput in{}; + ser_members(in, s); + v = in; + break; + } + default: + throw std::runtime_error("Deserialization error - unknown output tag"); + } + return; + } + if (v.type() == typeid(KeyOutput)) { + KeyOutput &in = boost::get(v); + uint8_t tag = (uint8_t)SerializationTag2::Key; + s.object_key("type"); + if (dynamic_cast(&s)) { + std::string str_tag = "key"; + ser(str_tag, s); + } else + s.binary(&tag, 1); + ser_members(in, s); + } +} +void ser_members(TransactionOutput &v, ISeria &s) { + seria_kv("amount", v.amount, s); + ser_members(v.target, s); + // seria_kv("target", v.target, s); +} +void ser_members(CoinbaseInput &v, ISeria &s) { seria_kv("height", v.height, s); } +void ser_members(KeyInput &v, ISeria &s) { + seria_kv("amount", v.amount, s); + seria_kv("output_indexes", v.output_indexes, s); + seria_kv("key_image", v.key_image, s); +} + +void ser_members(KeyOutput &v, ISeria &s) { seria_kv("public_key", v.public_key, s); } + +void ser_members(TransactionPrefix &v, ISeria &s) { + seria_kv("version", v.version, s); + seria_kv("unlock_block_or_timestamp", v.unlock_block_or_timestamp, s); + seria_kv("inputs", v.inputs, s); + seria_kv("outputs", v.outputs, s); + seria_kv("extra", v.extra, s); +} +void ser_members(BaseTransaction &v, ISeria &s) { + ser_members(static_cast(v), s); + if (v.version >= 2) { + size_t ignored = 0; + seria_kv("ignored", ignored, s); + } +} + +static size_t get_signatures_count(const TransactionInput &input) { + struct txin_signature_size_visitor : public boost::static_visitor { + size_t operator()(const CoinbaseInput &) const { return 0; } + size_t operator()(const KeyInput &txin) const { return txin.output_indexes.size(); } + }; + return boost::apply_visitor(txin_signature_size_visitor(), input); +} + +void ser_members(Transaction &v, ISeria &s) { + ser_members(static_cast(v), s); + + bool is_base = (v.inputs.size() == 1) && (v.inputs[0].type() == typeid(CoinbaseInput)); + size_t sig_size = is_base ? 0 : v.inputs.size(); + + if (s.is_input()) + v.signatures.resize(sig_size); + + if (sig_size && v.inputs.size() != v.signatures.size()) + throw std::runtime_error("Serialization error: unexpected signatures size"); + + s.object_key("signatures"); + s.begin_array(sig_size, true); + for (size_t i = 0; i < sig_size; ++i) { + size_t signature_size = get_signatures_count(v.inputs[i]); + if (!s.is_input()) { + if (signature_size != v.signatures[i].size()) + throw std::runtime_error("Serialization error: unexpected signatures size"); + s.begin_array(signature_size, true); + for (crypto::Signature &sig : v.signatures[i]) { + ser(sig, s); + } + s.end_array(); + } else { + std::vector signatures(signature_size); + s.begin_array(signature_size, true); + for (crypto::Signature &sig : signatures) { + ser(sig, s); + } + s.end_array(); + v.signatures[i] = std::move(signatures); + } + } + s.end_array(); +} +void ser_members(ParentBlock &v, ISeria &s, BlockSeriaType seria_type) { + seria_kv("major_version", v.major_version, s); + + seria_kv("minor_version", v.minor_version, s); + seria_kv("timestamp", v.timestamp, s); + seria_kv("previous_block_hash", v.previous_block_hash, s); + unsigned char nonce_data[4]; + common::uint_le_to_bytes(nonce_data, 4, v.nonce); + s.object_key("nonce"); + s.binary(nonce_data, 4); + if (s.is_input()) + v.nonce = common::uint_le_from_bytes(nonce_data, 4); + + if (seria_type == BlockSeriaType::BLOCKHASH || seria_type == BlockSeriaType::LONG_BLOCKHASH) { + Hash miner_tx_hash = get_base_transaction_hash(v.base_transaction); + Hash merkle_root = crypto::tree_hash_from_branch( + v.base_transaction_branch.data(), v.base_transaction_branch.size(), miner_tx_hash, nullptr); + + seria_kv("merkle_root", merkle_root, s); + } + seria_kv("transaction_count", v.transaction_count, s); + if (v.transaction_count < 1) + throw std::runtime_error("Wrong transactions number"); + + if (seria_type == BlockSeriaType::LONG_BLOCKHASH) + return; + + size_t branch_size = crypto::coinbase_tree_depth(v.transaction_count); + if (!s.is_input()) { + if (v.base_transaction_branch.size() != branch_size) + throw std::runtime_error("Wrong miner transaction branch size"); + } else { + v.base_transaction_branch.resize(branch_size); + } + + s.object_key("coinbase_transaction_branch"); + size_t btb_size = v.base_transaction_branch.size(); + s.begin_array(btb_size, true); + for (Hash &hash : v.base_transaction_branch) { + ser(hash, s); + } + s.end_array(); + + seria_kv("coinbase_transaction", v.base_transaction, s); + + TransactionExtraMergeMiningTag mm_tag; + if (!extra_get_merge_mining_tag(v.base_transaction.extra, mm_tag)) + throw std::runtime_error("Can't get extra merge mining tag"); + if (mm_tag.depth > 8 * sizeof(Hash)) + throw std::runtime_error("Wrong merge mining tag depth"); + + if (!s.is_input()) { + if (mm_tag.depth != v.blockchain_branch.size()) + throw std::runtime_error("Blockchain branch size must be equal to merge mining tag depth"); + } else { + v.blockchain_branch.resize(mm_tag.depth); + } + + s.object_key("blockchain_branch"); + btb_size = v.blockchain_branch.size(); + s.begin_array(btb_size, true); + for (Hash &hash : v.blockchain_branch) { + ser(hash, s); + } + s.end_array(); +} +void ser_members(BlockHeader &v, ISeria &s, BlockSeriaType seria_type, BlockBodyProxy body_proxy) { + if (v.major_version == 1 || seria_type != BlockSeriaType::LONG_BLOCKHASH) { + seria_kv("major_version", v.major_version, s); + seria_kv("minor_version", v.minor_version, s); + } +#if bytecoin_ALLOW_CM + if (v.major_version == 104) { // CM, experimental + seria_kv("timestamp", v.timestamp, s); + seria_kv("previous_block_hash", v.previous_block_hash, s); + unsigned char nonce_data[8]; + common::uint_le_to_bytes(nonce_data, 8, v.nonce); + s.object_key("nonce"); + s.binary(nonce_data, 8); + if (s.is_input()) + v.nonce = common::uint_le_from_bytes(nonce_data, 4); + if (seria_type != BlockSeriaType::NORMAL) + seria_kv("body_proxy", body_proxy, s); + if (seria_type != BlockSeriaType::PREHASH) { + size_t length = v.cm_merkle_branch.size(); + seria_kv("cm_merkle_branch_length", length, s); + std::vector mask((length + 7) / 8); + if (s.is_input()) { + v.cm_merkle_branch.resize(length); + s.object_key("cm_merkle_branch_mask"); + s.binary(mask.data(), mask.size()); + size_t non_zero_count = 0; + for (size_t i = 0; i != length; ++i) + if ((mask.at(i / 8) & (1 << (i % 8))) != 0) + non_zero_count += 1; + s.object_key("cm_merkle_branch"); + s.begin_array(non_zero_count, true); + for (size_t i = 0; i != length; ++i) { + if ((mask.at(i / 8) & (1 << (i % 8))) != 0) + ser(v.cm_merkle_branch.at(i), s); + else + v.cm_merkle_branch.at(i) = Hash{}; + } + s.end_array(); + } else { + std::vector non_zero_hashes; + for (size_t i = 0; i != length; ++i) + if (v.cm_merkle_branch.at(i) != Hash{}) { + mask.at(i / 8) |= 1 << (i % 8); + non_zero_hashes.push_back(v.cm_merkle_branch.at(i)); + } + s.object_key("cm_merkle_branch_mask"); + s.binary(mask.data(), mask.size()); + s.object_key("cm_merkle_branch"); + size_t non_zero_count = non_zero_hashes.size(); + s.begin_array(non_zero_count, true); + for (Hash &hash : non_zero_hashes) { + ser(hash, s); + } + s.end_array(); + } + // seria_kv("cm_merkle_branch", v.cm_merkle_branch, s); + } + return; + } +#endif + if (v.major_version == 1) { + seria_kv("timestamp", v.timestamp, s); + seria_kv("previous_block_hash", v.previous_block_hash, s); + unsigned char nonce_data[4]; + common::uint_le_to_bytes(nonce_data, 4, v.nonce); + s.object_key("nonce"); + s.binary(nonce_data, 4); + if (s.is_input()) + v.nonce = common::uint_le_from_bytes(nonce_data, 4); + if (seria_type != BlockSeriaType::NORMAL) + seria_kv("body_proxy", body_proxy, s); + return; + } + if (v.major_version == 2 || v.major_version == 3) { + if (seria_type != BlockSeriaType::LONG_BLOCKHASH) { + seria_kv("previous_block_hash", v.previous_block_hash, s); + if (seria_type != BlockSeriaType::NORMAL) + seria_kv("body_proxy", body_proxy, s); + } + // auto parent_block_serializer = make_parent_block_serializer(v, false, false); + if (seria_type != BlockSeriaType::PREHASH) { + s.object_key("parent_block"); + s.begin_object(); + ser_members(v.parent_block, s, seria_type); + s.end_object(); + if (s.is_input()) { + v.nonce = v.parent_block.nonce; + v.timestamp = v.parent_block.timestamp; + } + } + // seria_kv("parent_block", parent_block_serializer, s); + return; + } + throw std::runtime_error("Unknown block major version " + common::to_string(v.major_version)); +} +void ser_members(BlockBodyProxy &v, ISeria &s) { + seria_kv("transactions_merkle_root", v.transactions_merkle_root, s); + seria_kv("transaction_count", v.transaction_count, s); +} +void ser_members(BlockTemplate &v, ISeria &s) { + // BlockBodyProxy body_proxy; + // if(seria_type != BlockSeriaType::NORMAL) + // body_proxy = get_body_proxy_from_template(v); + ser_members(static_cast(v), s); + // if(v.major_version == 104) { // CM, experimental + // seria_kv("transaction_hashes", v.transaction_hashes, s); + // return; + // } + // if(seria_type != BlockSeriaType::NORMAL) + // return; + seria_kv("coinbase_transaction", v.base_transaction, s); + seria_kv("transaction_hashes", v.transaction_hashes, s); +} +void ser_members(RawBlock &v, ISeria &s) { + seria_kv("block", v.block, s); + seria_kv("transactions", v.transactions, s); +} +void ser_members(Block &v, ISeria &s) { + seria_kv("header", v.header, s); + seria_kv("transactions", v.transactions, s); +} +void ser_members(SWCheckpoint &v, ISeria &s) { + seria_kv("height", v.height, s); + seria_kv("hash", v.hash, s); +} +void ser_members(Checkpoint &v, seria::ISeria &s) { + seria_kv("height", v.height, s); + seria_kv("hash", v.hash, s); + seria_kv("key_id", v.key_id, s); + seria_kv("counter", v.counter, s); +} +void ser_members(SignedCheckpoint &v, seria::ISeria &s) { + ser_members(static_cast(v), s); + seria_kv("signature", v.signature, s); +} + +} // namespace seria diff --git a/src/CryptoNote.hpp b/src/CryptoNote.hpp index 0b6c52ba..198c4569 100644 --- a/src/CryptoNote.hpp +++ b/src/CryptoNote.hpp @@ -13,13 +13,9 @@ // We define here, as CryptoNoteConfig.h is never included anywhere anymore #define bytecoin_ALLOW_DEBUG_COMMANDS 1 -// Remove once we upgrade to version 4 -#define UPGRADE_TO_VERSION_4 1 -// We add height to keyimage and outputs indexes to bytecoind -// We use more efficient tid->transaction index -// We make CD and Balances 128-bit -// We add binary size to api::Transactions -// We remove unlock_timestamp from api::BlockHeader +#define bytecoin_ALLOW_CM 0 + +#define bytecoin_NEWP2P 0 namespace bytecoin { @@ -39,12 +35,12 @@ typedef uint32_t Height; typedef uint64_t Difficulty; typedef uint64_t Amount; typedef uint32_t Timestamp; -typedef uint64_t UnlockMoment; +typedef uint64_t BlockOrTimestamp; // Height or Timestamp, 32-bit is enough, but historically we already have several very large values in blockchain typedef int64_t SignedAmount; struct CoinbaseInput { - Height block_index = 0; + Height height = 0; }; struct KeyInput { @@ -54,7 +50,7 @@ struct KeyInput { }; struct KeyOutput { - PublicKey key; + PublicKey public_key; }; typedef boost::variant TransactionInput; @@ -63,28 +59,34 @@ typedef boost::variant TransactionOutputTarget; struct TransactionOutput { Amount amount = 0; + // Freaking stupidity - why excess indirection with amount left outside variant part? + // We cannot fix it easily. Will have to switch transaction version TransactionOutputTarget target; }; struct TransactionPrefix { - uint8_t version = 0; - UnlockMoment unlock_time = 0; + uint8_t version = 0; + BlockOrTimestamp unlock_block_or_timestamp = 0; std::vector inputs; std::vector outputs; BinaryArray extra; }; +typedef std::vector> TransactionSignatures; + struct Transaction : public TransactionPrefix { - std::vector> signatures; + TransactionSignatures signatures; }; struct BaseTransaction : public TransactionPrefix {}; // has 'ignored' field during seria -struct ParentBlock { +struct ParentBlock { // or Merge Mining Root, not to be confused with previous block! uint8_t major_version = 0; uint8_t minor_version = 0; + Timestamp timestamp = 0; Hash previous_block_hash; - uint16_t transaction_count = 0; // TODO - check overflow everywhere it is assigned + uint32_t nonce = 0; + uint32_t transaction_count = 0; std::vector base_transaction_branch; BaseTransaction base_transaction; std::vector blockchain_branch; @@ -92,18 +94,28 @@ struct ParentBlock { struct BlockHeader { uint8_t major_version = 0; - uint8_t minor_version = 0; - uint32_t nonce = 0; + uint8_t minor_version = 0; // Used for hard fork voting + uint64_t nonce = 0; // only 32-bit is used in blocks without CM Timestamp timestamp = 0; Hash previous_block_hash; + ParentBlock parent_block; // For block with MM (V2, V3) + std::vector cm_merkle_branch; // For blocks with CM (V104) }; +struct BlockBodyProxy { + Hash transactions_merkle_root; + uint32_t transaction_count = 0; +}; +// BlockHeader + (BlockBodyProxy | BlockBody) are enough to calc POW consensus +// BlockBody is std::vector where coinbase is the first one + struct BlockTemplate : public BlockHeader { - ParentBlock parent_block; Transaction base_transaction; std::vector transaction_hashes; }; +enum BlockSeriaType { NORMAL, PREHASH, BLOCKHASH, LONG_BLOCKHASH }; + struct AccountPublicAddress { PublicKey spend_public_key; PublicKey view_public_key; @@ -141,14 +153,19 @@ class Block { bool to_raw_block(RawBlock &) const; }; -struct CheckPoint { +struct SWCheckpoint { + Height height = 0; + Hash hash; +}; +struct Checkpoint { Height height = 0; Hash hash; uint32_t key_id = 0; uint64_t counter = 0; Hash get_message_hash() const; + bool is_enabled() const { return counter != std::numeric_limits::max(); } }; -struct SignedCheckPoint : public CheckPoint { +struct SignedCheckpoint : public Checkpoint { Signature signature; }; @@ -161,8 +178,10 @@ inline bool operator<(const AccountPublicAddress &a, const AccountPublicAddress return std::tie(a.view_public_key, a.spend_public_key) < std::tie(b.view_public_key, b.spend_public_key); } +class Currency; // For ser_members of bytecoin::SendProof } // namespace bytecoin +// Serialization is part of CryptoNote standard, not problem to put it here namespace seria { class ISeria; @@ -174,7 +193,7 @@ void ser(bytecoin::KeyDerivation &v, ISeria &s); void ser(bytecoin::Signature &v, ISeria &s); void ser_members(bytecoin::AccountPublicAddress &v, ISeria &s); -void ser_members(bytecoin::SendProof &v, ISeria &s); +void ser_members(bytecoin::SendProof &v, ISeria &s, const bytecoin::Currency &); void ser_members(bytecoin::TransactionInput &v, ISeria &s); void ser_members(bytecoin::TransactionOutput &v, ISeria &s); void ser_members(bytecoin::TransactionOutputTarget &v, ISeria &s); @@ -188,13 +207,18 @@ void ser_members(bytecoin::TransactionPrefix &v, ISeria &s); void ser_members(bytecoin::BaseTransaction &v, ISeria &s); void ser_members(bytecoin::Transaction &v, ISeria &s); +void ser_members( + bytecoin::ParentBlock &v, ISeria &s, bytecoin::BlockSeriaType seria_type = bytecoin::BlockSeriaType::NORMAL); +void ser_members(bytecoin::BlockHeader &v, ISeria &s, + bytecoin::BlockSeriaType seria_type = bytecoin::BlockSeriaType::NORMAL, + bytecoin::BlockBodyProxy body_proxy = bytecoin::BlockBodyProxy{}); +void ser_members(bytecoin::BlockBodyProxy &v, ISeria &s); void ser_members(bytecoin::BlockTemplate &v, ISeria &s); -void ser_members(bytecoin::BlockHeader &v, ISeria &s); -void ser_members(bytecoin::ParentBlock &v, ISeria &s); void ser_members(bytecoin::RawBlock &v, ISeria &s); void ser_members(bytecoin::Block &v, ISeria &s); -void ser_members(bytecoin::CheckPoint &v, ISeria &s); -void ser_members(bytecoin::SignedCheckPoint &v, ISeria &s); +void ser_members(bytecoin::SWCheckpoint &v, ISeria &s); +void ser_members(bytecoin::Checkpoint &v, ISeria &s); +void ser_members(bytecoin::SignedCheckpoint &v, ISeria &s); } diff --git a/src/CryptoNoteConfig.hpp b/src/CryptoNoteConfig.hpp index 59bf6138..dc6f6170 100644 --- a/src/CryptoNoteConfig.hpp +++ b/src/CryptoNoteConfig.hpp @@ -6,6 +6,8 @@ #include #include #include +#include "CryptoNote.hpp" +#include "common/StringTools.hpp" // All values below should only be used in code through Currency and Config classes, never directly. // This approach allows unlimited customization through config file/command line parameters @@ -13,70 +15,58 @@ namespace bytecoin { namespace parameters { -const uint32_t CRYPTONOTE_MAX_BLOCK_NUMBER = 500000000; -const uint32_t CRYPTONOTE_MAX_BLOCK_BLOB_SIZE = 500000000; -const uint32_t CRYPTONOTE_MAX_TX_SIZE = 1000000000; -const uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 6; // addresses start with "2" -const uint32_t CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW = 10; -const uint32_t CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT = 60 * 60 * 2; +const Height MAX_BLOCK_NUMBER = 500000000; +const uint32_t MAX_BLOCK_BLOB_SIZE = 500000000; +const uint32_t MAX_TX_SIZE = 1000000000; +const uint64_t PUBLIC_ADDRESS_BASE58_PREFIX = 6; // addresses start with "2" +const Height MINED_MONEY_UNLOCK_WINDOW = 10; +const Timestamp BLOCK_FUTURE_TIME_LIMIT = 60 * 60 * 2; -const uint32_t BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW = 60; +const Height BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW = 60; // MONEY_SUPPLY - total number coins to be generated -const uint64_t MONEY_SUPPLY = std::numeric_limits::max(); +const Amount MONEY_SUPPLY = std::numeric_limits::max(); const unsigned EMISSION_SPEED_FACTOR = 18; static_assert(EMISSION_SPEED_FACTOR <= 8 * sizeof(uint64_t), "Bad EMISSION_SPEED_FACTOR"); -const size_t CRYPTONOTE_REWARD_BLOCKS_WINDOW = 100; -const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE = - 100000; // size of block (bytes) after which reward for block calculated using block size -const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 = 20000; -const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 = 10000; -// const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_CURRENT = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; -const size_t CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE = 600; -const size_t CRYPTONOTE_DISPLAY_DECIMAL_POINT = 8; -const uint64_t MINIMUM_FEE = 1000000; // pow(10, 6) -const uint64_t DEFAULT_DUST_THRESHOLD = 1000000; // pow(10, 6) - -const uint32_t DIFFICULTY_TARGET = 120; // seconds -const uint64_t MINIMUM_DIFFICULTY_FROM_V2 = 10000; // TODO - complete fix in the next hardfork -constexpr uint32_t EXPECTED_NUMBER_OF_BLOCKS_PER_DAY(uint32_t difficulty_target) { - return 24 * 60 * 60 / difficulty_target; -} -constexpr uint32_t DIFFICULTY_WINDOW(uint32_t difficulty_target) { - return EXPECTED_NUMBER_OF_BLOCKS_PER_DAY(difficulty_target); -} // blocks -const size_t DIFFICULTY_CUT = 60; // timestamps to cut after sorting -const size_t DIFFICULTY_LAG = 15; // !!! -static_assert( - 2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW(DIFFICULTY_TARGET) - 2, "Bad DIFFICULTY_WINDOW or DIFFICULTY_CUT"); - -const size_t MAX_BLOCK_SIZE_INITIAL = 20 * 1024; -const uint64_t MAX_BLOCK_SIZE_GROWTH_SPEED_NUMERATOR = 100 * 1024; -constexpr uint64_t MAX_BLOCK_SIZE_GROWTH_SPEED_DENOMINATOR(uint32_t difficulty_target) { - return 365 * 24 * 60 * 60 / difficulty_target; -} +const Height REWARD_BLOCKS_WINDOW = 100; + +// size of block (bytes) after which reward for block calculated using block size +const size_t MINIMUM_SIZE_MEDIAN = 100000; +const size_t MINIMUM_SIZE_MEDIAN_V2 = 20000; +const size_t MINIMUM_SIZE_MEDIAN_V1 = 10000; +const size_t COINBASE_BLOB_RESERVED_SIZE = 600; +const size_t DISPLAY_DECIMAL_POINT = 8; +const Amount MINIMUM_FEE = 1000000; // pow(10, 6) +const Amount DEFAULT_DUST_THRESHOLD = 1000000; // pow(10, 6) + +const Timestamp DIFFICULTY_TARGET = 120; + +const Difficulty MINIMUM_DIFFICULTY_V1 = 1; +const Difficulty MINIMUM_DIFFICULTY = 100000; + +const Height DIFFICULTY_CUT = 60; // out-of-family timestamps to cut after sorting +const Height DIFFICULTY_LAG = 15; // skip last blocks for difficulty calcs (against lowering difficulty attack) + +const uint32_t MAX_BLOCK_SIZE_INITIAL = 20 * 1024; +const uint32_t MAX_BLOCK_SIZE_GROWTH_PER_YEAR = 100 * 1024; // After next hardfork remove settings below -const uint32_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS = 1; -constexpr uint32_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS(uint32_t difficulty_target) { - return difficulty_target * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS; +const Height LOCKED_TX_ALLOWED_DELTA_BLOCKS = 1; +constexpr Timestamp LOCKED_TX_ALLOWED_DELTA_SECONDS(Timestamp difficulty_target) { + return difficulty_target * LOCKED_TX_ALLOWED_DELTA_BLOCKS; } -const uint32_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = 60 * 60 * 24; // seconds, one day -// const uint32_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week -// const uint32_t CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL = 7; // -// CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL * CRYPTONOTE_MEMPOOL_TX_LIVETIME = time to forget tx - -// const size_t FUSION_TX_MAX_SIZE = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_CURRENT * 30 / 100; -// const size_t FUSION_TX_MIN_INPUT_COUNT = 12; -// const size_t FUSION_TX_MIN_IN_OUT_COUNT_RATIO = 4; +const Height UPGRADE_HEIGHT_V2 = 546603; +const Height UPGRADE_HEIGHT_V3 = 985549; +const Height KEY_IMAGE_SUBGROUP_CHECKING_HEIGHT = 1267000; // TODO - after fork remove, check subgroup if version >= 4 -const uint32_t UPGRADE_HEIGHT_V2 = 546602; -const uint32_t UPGRADE_HEIGHT_V3 = 985548; +// const uint32_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks +// const uint32_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY*7; // blocks +// static_assert(UPGRADE_VOTING_WINDOW > 1, "Bad UPGRADE_VOTING_WINDOW"); -const char CRYPTONOTE_BLOCKS_FILENAME[] = "blocks.bin"; -const char CRYPTONOTE_BLOCKINDEXES_FILENAME[] = "blockindexes.bin"; +const char BLOCKS_FILENAME[] = "blocks.bin"; +const char BLOCKINDEXES_FILENAME[] = "blockindexes.bin"; } // parameters const char CRYPTONOTE_NAME[] = "bytecoin"; @@ -87,109 +77,117 @@ const size_t BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT = 10000; // by default, blo const size_t BLOCKS_SYNCHRONIZING_DEFAULT_COUNT = 100; // by default, blocks count in blocks downloading const size_t COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT = 1000; -const int P2P_DEFAULT_PORT = 8080; -const int RPC_DEFAULT_PORT = 8081; -const int WALLET_RPC_DEFAULT_PORT = 8070; - -const size_t P2P_LOCAL_WHITE_PEERLIST_LIMIT = 1000; -const size_t P2P_LOCAL_GRAY_PEERLIST_LIMIT = 5000; - -const size_t P2P_CONNECTION_MAX_WRITE_BUFFER_SIZE = 32 * 1024 * 1024; // 32 Mb -const uint32_t P2P_DEFAULT_CONNECTIONS_COUNT = 8; -const uint32_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; -const uint32_t P2P_DEFAULT_HANDSHAKE_INTERVAL = 60; // seconds -const uint32_t P2P_DEFAULT_PACKET_MAX_SIZE = 50000000; // 50000000 bytes maximum packet size -const uint32_t P2P_DEFAULT_PEERS_IN_HANDSHAKE = 250; -const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; // 5 seconds -const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds -const uint32_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes -const uint32_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds -const char P2P_STAT_TRUSTED_PUBLIC_KEY[] = "E29507CA55455F37A3B783EE2C5123B8B6A34A0C5CAAE050922C6254161480C1"; - -const char *const CHECKPOINT_PUBLIC_KEYS[] = {"b397e789ba603046d5750bbf490e1569f55dc9cf1f91edd2605d55d7bc3603fc", - "10fdd8f7331304b2818b86158be07e5e71441a3e96fccc3451f4c12862ce2d75", - "6e03debc66cfeabe0fb8720f4ed3a433a16a40dc9b72e6d14679f0b8a784cd58", - "7afcd21a758f0568d536bec2e613c8470c086c97f14dfec3f2a744492ad02f0f", - "64aadc345b4e12c10ae19e02a1536550abf0cb5118e9ad7d4c7184215a551240", - "247eb4681afe8fbbf09fa7145249be27f8afdaefb023850e1399aaf49747d5e4", - "eb39db3c11b09c637a06122e48d0ee88603e7b216dda01901daa27c485d82eff"}; -const char *const CHECKPOINT_PUBLIC_KEYS_TESTNET[] = { - "577ac6a6cdc5e0114c5a7e6338f1332fd0684e2aaf7aa3efb415e9f623d04bf5", - "49950afc665e2f23354c03559f67e01e4f23fe2f30c6c6037b4de6dbd914ed80", - "07f8bba2577c0bfd9f5dc8af7319b6acbbde22bf95678927c707bb42e22fd157", - "9d385d34b2b4a4eb21cc1eab33ad0763b43423bdf9921db20ca5b13edd595b35", - "7b897d24abb76a31230b1de982be9b32a5f12dae716bbec4804a3866555e5cad", - "89ccf482916c8e381e344542537d908be76a0180e4043bf748407bd7b3b7193c", - "005d18764a7c4514d217d55f39633c8145e25afe91fd84837fc1a9ab5e048e8e"}; +const uint16_t P2P_DEFAULT_PORT = 8080; +const uint16_t RPC_DEFAULT_PORT = 8081; +const uint16_t WALLET_RPC_DEFAULT_PORT = 8070; + +// const size_t P2P_CONNECTION_MAX_WRITE_BUFFER_SIZE = 32 * 1024 * 1024; // 32 Mb +// const uint32_t P2P_DEFAULT_HANDSHAKE_INTERVAL = 60; // seconds +// const uint32_t P2P_DEFAULT_PACKET_MAX_SIZE = 50000000; // 50000000 bytes maximum packet size +const uint32_t P2P_DEFAULT_PEERS_IN_HANDSHAKE = 250; +// const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; // 5 seconds +// const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds +// const uint32_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes +// const uint32_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds +constexpr PublicKey P2P_STAT_TRUSTED_PUBLIC_KEY = + common::pfh("E29507CA55455F37A3B783EE2C5123B8B6A34A0C5CAAE050922C6254161480C1"); + +constexpr PublicKey CHECKPOINT_PUBLIC_KEYS[] = { + common::pfh("b397e789ba603046d5750bbf490e1569f55dc9cf1f91edd2605d55d7bc3603fc"), + common::pfh("10fdd8f7331304b2818b86158be07e5e71441a3e96fccc3451f4c12862ce2d75"), + common::pfh("6e03debc66cfeabe0fb8720f4ed3a433a16a40dc9b72e6d14679f0b8a784cd58"), + common::pfh("7afcd21a758f0568d536bec2e613c8470c086c97f14dfec3f2a744492ad02f0f"), + common::pfh("64aadc345b4e12c10ae19e02a1536550abf0cb5118e9ad7d4c7184215a551240"), + common::pfh("247eb4681afe8fbbf09fa7145249be27f8afdaefb023850e1399aaf49747d5e4"), + common::pfh("eb39db3c11b09c637a06122e48d0ee88603e7b216dda01901daa27c485d82eff")}; +constexpr PublicKey CHECKPOINT_PUBLIC_KEYS_TESTNET[] = { + common::pfh("577ac6a6cdc5e0114c5a7e6338f1332fd0684e2aaf7aa3efb415e9f623d04bf5"), + common::pfh("49950afc665e2f23354c03559f67e01e4f23fe2f30c6c6037b4de6dbd914ed80"), + common::pfh("07f8bba2577c0bfd9f5dc8af7319b6acbbde22bf95678927c707bb42e22fd157"), + common::pfh("9d385d34b2b4a4eb21cc1eab33ad0763b43423bdf9921db20ca5b13edd595b35"), + common::pfh("7b897d24abb76a31230b1de982be9b32a5f12dae716bbec4804a3866555e5cad"), + common::pfh("89ccf482916c8e381e344542537d908be76a0180e4043bf748407bd7b3b7193c"), + common::pfh("005d18764a7c4514d217d55f39633c8145e25afe91fd84837fc1a9ab5e048e8e")}; +constexpr PublicKey CHECKPOINT_PUBLIC_KEYS_STAGENET[] = { + common::pfh("11bcb3340a24e7cc2d3e4faa4c4f66ff7ef2813c1ae49e4f8b545d14b0f79bdc"), + common::pfh("32be85c1afd74f924a7487a76dda12b4a9925adf6212c903d7188ebd16ce8495"), + common::pfh("d1789d5103bc8328285124dfc77d3fd3c5d3d76e70616bb409d84d3f335326cf"), + common::pfh("8ccd5e4828b4b3d785e0f9c910771271ad40e9b1f427db1df9021a7a4083288c"), + common::pfh("6269b60e38cd1879807e3591f1e19b936c4d156a3d15b0693a8700ee7770e431"), + common::pfh("c9b8aa2f09fb81f77c135d1eb23cd7eac5b66c409058d5b53f724a1b887fe70f"), + common::pfh("62020c71bbf2447ee588b28c15430434f2ceac8443c40b6e48b627e437110981")}; const char *const SEED_NODES[] = { "207.246.127.160:8080", "108.61.174.232:8080", "45.32.156.183:8080", "45.76.29.96:8080"}; +const char *const SEED_NODES_STAGENET[] = { + "207.246.127.160:10080", "108.61.174.232:10080", "45.32.156.183:10080", "45.76.29.96:10080"}; +// testnet will have no seed nodes + +constexpr const SWCheckpoint CHECKPOINTS[] = { + {79000, common::pfh("cae33204e624faeb64938d80073bb7bbacc27017dc63f36c5c0f313cad455a02")}, + {140000, common::pfh("993059fb6ab92db7d80d406c67a52d9c02d873ca34b6290a12b744c970208772")}, + {200000, common::pfh("a5f74c7542077df6859f48b5b1f9c3741f29df38f91a47e14c94b5696e6c3073")}, + {230580, common::pfh("32bd7cb6c68a599cf2861941f29002a5e203522b9af54f08dfced316f6459103")}, + {260000, common::pfh("f68e70b360ca194f48084da7a7fd8e0251bbb4b5587f787ca65a6f5baf3f5947")}, + {300000, common::pfh("8e80861713f68354760dc10ea6ea79f5f3ff28f39b3f0835a8637463b09d70ff")}, + {390285, common::pfh("e00bdc9bf407aeace2f3109de11889ed25894bf194231d075eddaec838097eb7")}, + {417000, common::pfh("2dc96f8fc4d4a4d76b3ed06722829a7ab09d310584b8ecedc9b578b2c458a69f")}, + {427193, common::pfh("00feabb08f2d5759ed04fd6b799a7513187478696bba2db2af10d4347134e311")}, + {453537, common::pfh("d17de6916c5aa6ffcae575309c80b0f8fdcd0a84b5fa8e41a841897d4b5a4e97")}, + {462250, common::pfh("13468d210a5ec884cf839f0259f247ccf3efef0414ac45172033d32c739beb3e")}, + {468000, common::pfh("251bcbd398b1f593193a7210934a3d87f692b2cb0c45206150f59683dd7e9ba1")}, + {480200, common::pfh("363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b")}, + {484500, common::pfh("5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb")}, + {506000, common::pfh("3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c")}, + {544000, common::pfh("f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0")}, + {553300, common::pfh("f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f")}, + {580000, common::pfh("93aea06936fa4dc0a84c9109c9d5f0e1b0815f96898171e42fd2973d262ed9ac")}, + {602000, common::pfh("a05fd2fccbb5f567ece940ebb62a82fdb1517ff5696551ae704e5f0ef8edb979")}, + {623000, common::pfh("7c92dd374efd0221065c7d98fce0568a1a1c130b5da28bb3f338cdc367b93d0b")}, + {645000, common::pfh("1eeba944c0dd6b9a1228a425a74076fbdbeaf9b657ba7ef02547d99f971de70d")}, + {667000, common::pfh("a020c8fcaa567845d04b520bb7ebe721e097a9bed2bdb8971081f933b5b42995")}, + {689000, common::pfh("212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008")}, + {713000, common::pfh("a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495")}, + {750300, common::pfh("5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c")}, + {780000, common::pfh("8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070")}, + {785500, common::pfh("de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd")}, + {789000, common::pfh("acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01")}, + {796000, common::pfh("04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44")}, + {800000, common::pfh("d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86")}, + {804000, common::pfh("bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d")}, + {810500, common::pfh("302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381")}, + {816000, common::pfh("32b7fdd4e4d715db81f8f09f4ba5e5c78e8113f2804d61a57378baee479ce745")}, + {822000, common::pfh("a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984")}, + {841000, common::pfh("2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce")}, + {890000, common::pfh("a7132932ea31236ce6b8775cd1380edf90b5e536ee4202c77b69a3d62445fcd2")}, + {894000, common::pfh("ae2624ea1472ecc36de0d812f21a32da2d4afc7d5770830083cbaf652209d316")}, + {979000, common::pfh("d8290eb4eedbe638f5dbadebcaf3ea434857ce96168185dc04f75b6cc1f4fda6")}, + {985548, common::pfh("8d53e0d97594755a621feaee0978c0431fc01f42b85ff76a03af8641e2009d57")}, + {985549, common::pfh("dc6f8d9319282475c981896b98ff9772ae2499533c2302c32faf65115aaf2554")}, + {996000, common::pfh("c9a9243049acc7773a3e58ae354d66f8ea83996ece93ffbaad0b8b42b5fb7223")}, + {1021000, common::pfh("a0c4107d327ffeb31dabe135a7124191b0a5ef7c4fa34f06babc1f0546ab938e")}, + {1039000, common::pfh("8c9208940fc92539fac98cc658b95d240635f8729ee8bd756d6bdbab52de2c04")}, + {1170000, common::pfh("f48441157749e89687dfa6edec2128ff332bdaa9eb139f2330a193e3139d2980")}, + {1268000, common::pfh("d49fcaec1d53095e2c244913f123bfd4b26eabb6d75aca7b77a00de8aa8ac680")}, + {1272000, common::pfh("2fb2c50328c8345d2f0a16b3ec4ea680a8a93730358494265ada9edbb9bfa1a6")}, + {1273000, common::pfh("496a9238c654d79c48d269224aa75d61f51831bae6dc744f5e709bec11c7c9f2")}, + {1278000, common::pfh("de0225cd279ca27cc8d4f8da1b5b92ba0112e48b3777b8c50301846ccfc9146b")}, + {1283000, common::pfh("826043db95e9801f038f254d223ce0d0912da269dcce1461b5f0f05ddfae9e1c")}, + {1324000, common::pfh("981e6f6871a7c295b56c5ce544adb5a7d52540ee23e15474b4357c7728952fef")}, + {1329000, common::pfh("b88ed8dfe95a19bd6377f77c01d87df9cf7bd14cd6de7ec616beca95deb1fc85")}, + {1343000, common::pfh("1696231b026b4e10412b16d65ba036c9750d287ab76da7e25efd4ba3fa9ed999")}, + {1372000, common::pfh("55e02f544df808a12d3c2809b8c7490f8b0729aef196745240e94522c69a7181")}, + {1398000, common::pfh("5e9eaf424ffba3957c569efc119a6e9ba0a636af99c44ea4cb921654ba634146")}, + {1422000, common::pfh("edae8fec5d6572c84b4c6c794922b1e4ce97d82a057c77235407e29568525a46")}, + {1451000, common::pfh("327814e8ee24650ad95d62b61e066d884abbb9d5ac18cd258baf24086c2a0882")}, + {1479000, common::pfh("16c9a464514685d325ac06b82e4476d0d5467c59b733f5fbd950e9931e58d18c")}, + {1510000, common::pfh("fcdc93636c47266f6d71606456d5767b7bc4567adbe8055b6d72b56401b48ece")}, + {1540000, common::pfh("8014ee1613e13aea282f95b343cf46c376cf8050f801a145665ae80e33a867a1")}, + {1560000, common::pfh("1a28c09c74b4b1ad97e4d65b99f97e62aa4f225be5b33017efc07c5c708b83ef")}, + {1579000, common::pfh("debfa79d14ff49dc7e8c24e5e27a22f9a67819124a7dcd187c67493a969044be")}}; +// {1605000, common::pfh("a34a41f2b5091f28f234b55a6255a9727fed355ca41233d59f779b2f87d1a359")}}; + +constexpr const SWCheckpoint CHECKPOINTS_STAGENET[] = { + {450, common::pfh("c69823a6b3e0c1f724411e697219a9d31a2df900cb49bb0488b1a91a9989a805")}}; -struct CheckpointData { - uint32_t height; - const char *hash; -}; - -constexpr const CheckpointData CHECKPOINTS[] = { - {79000, "cae33204e624faeb64938d80073bb7bbacc27017dc63f36c5c0f313cad455a02"}, - {140000, "993059fb6ab92db7d80d406c67a52d9c02d873ca34b6290a12b744c970208772"}, - {200000, "a5f74c7542077df6859f48b5b1f9c3741f29df38f91a47e14c94b5696e6c3073"}, - {230580, "32bd7cb6c68a599cf2861941f29002a5e203522b9af54f08dfced316f6459103"}, - {260000, "f68e70b360ca194f48084da7a7fd8e0251bbb4b5587f787ca65a6f5baf3f5947"}, - {300000, "8e80861713f68354760dc10ea6ea79f5f3ff28f39b3f0835a8637463b09d70ff"}, - {390285, "e00bdc9bf407aeace2f3109de11889ed25894bf194231d075eddaec838097eb7"}, - {417000, "2dc96f8fc4d4a4d76b3ed06722829a7ab09d310584b8ecedc9b578b2c458a69f"}, - {427193, "00feabb08f2d5759ed04fd6b799a7513187478696bba2db2af10d4347134e311"}, - {453537, "d17de6916c5aa6ffcae575309c80b0f8fdcd0a84b5fa8e41a841897d4b5a4e97"}, - {462250, "13468d210a5ec884cf839f0259f247ccf3efef0414ac45172033d32c739beb3e"}, - {468000, "251bcbd398b1f593193a7210934a3d87f692b2cb0c45206150f59683dd7e9ba1"}, - {480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"}, - {484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"}, - {506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"}, - {544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"}, - {553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"}, - {580000, "93aea06936fa4dc0a84c9109c9d5f0e1b0815f96898171e42fd2973d262ed9ac"}, - {602000, "a05fd2fccbb5f567ece940ebb62a82fdb1517ff5696551ae704e5f0ef8edb979"}, - {623000, "7c92dd374efd0221065c7d98fce0568a1a1c130b5da28bb3f338cdc367b93d0b"}, - {645000, "1eeba944c0dd6b9a1228a425a74076fbdbeaf9b657ba7ef02547d99f971de70d"}, - {667000, "a020c8fcaa567845d04b520bb7ebe721e097a9bed2bdb8971081f933b5b42995"}, - {689000, "212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008"}, - {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"}, - {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"}, - {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"}, - {785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"}, - {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"}, - {796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"}, - {800000, "d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86"}, - {804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"}, - {810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"}, - {816000, "32b7fdd4e4d715db81f8f09f4ba5e5c78e8113f2804d61a57378baee479ce745"}, - {822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"}, - {841000, "2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce"}, - {890000, "a7132932ea31236ce6b8775cd1380edf90b5e536ee4202c77b69a3d62445fcd2"}, - {894000, "ae2624ea1472ecc36de0d812f21a32da2d4afc7d5770830083cbaf652209d316"}, - {979000, "d8290eb4eedbe638f5dbadebcaf3ea434857ce96168185dc04f75b6cc1f4fda6"}, - {985548, "8d53e0d97594755a621feaee0978c0431fc01f42b85ff76a03af8641e2009d57"}, - {985549, "dc6f8d9319282475c981896b98ff9772ae2499533c2302c32faf65115aaf2554"}, - {996000, "c9a9243049acc7773a3e58ae354d66f8ea83996ece93ffbaad0b8b42b5fb7223"}, - {1021000, "a0c4107d327ffeb31dabe135a7124191b0a5ef7c4fa34f06babc1f0546ab938e"}, - {1039000, "8c9208940fc92539fac98cc658b95d240635f8729ee8bd756d6bdbab52de2c04"}, - {1170000, "f48441157749e89687dfa6edec2128ff332bdaa9eb139f2330a193e3139d2980"}, - {1268000, "d49fcaec1d53095e2c244913f123bfd4b26eabb6d75aca7b77a00de8aa8ac680"}, - {1272000, "2fb2c50328c8345d2f0a16b3ec4ea680a8a93730358494265ada9edbb9bfa1a6"}, - {1273000, "496a9238c654d79c48d269224aa75d61f51831bae6dc744f5e709bec11c7c9f2"}, - {1278000, "de0225cd279ca27cc8d4f8da1b5b92ba0112e48b3777b8c50301846ccfc9146b"}, - {1283000, "826043db95e9801f038f254d223ce0d0912da269dcce1461b5f0f05ddfae9e1c"}, - {1324000, "981e6f6871a7c295b56c5ce544adb5a7d52540ee23e15474b4357c7728952fef"}, - {1329000, "b88ed8dfe95a19bd6377f77c01d87df9cf7bd14cd6de7ec616beca95deb1fc85"}, - {1343000, "1696231b026b4e10412b16d65ba036c9750d287ab76da7e25efd4ba3fa9ed999"}, - {1372000, "55e02f544df808a12d3c2809b8c7490f8b0729aef196745240e94522c69a7181"}, - {1398000, "5e9eaf424ffba3957c569efc119a6e9ba0a636af99c44ea4cb921654ba634146"}, - {1422000, "edae8fec5d6572c84b4c6c794922b1e4ce97d82a057c77235407e29568525a46"}, - {1451000, "327814e8ee24650ad95d62b61e066d884abbb9d5ac18cd258baf24086c2a0882"}, - {1479000, "16c9a464514685d325ac06b82e4476d0d5467c59b733f5fbd950e9931e58d18c"}, - {1510000, "fcdc93636c47266f6d71606456d5767b7bc4567adbe8055b6d72b56401b48ece"}, - {1540000, "8014ee1613e13aea282f95b343cf46c376cf8050f801a145665ae80e33a867a1"}, - {1560000, "1a28c09c74b4b1ad97e4d65b99f97e62aa4f225be5b33017efc07c5c708b83ef"}, - {1579000, "debfa79d14ff49dc7e8c24e5e27a22f9a67819124a7dcd187c67493a969044be"}}; } // CryptoNote diff --git a/src/common/Base58.cpp b/src/common/Base58.cpp index 4ce89357..ea99f187 100644 --- a/src/common/Base58.cpp +++ b/src/common/Base58.cpp @@ -4,6 +4,7 @@ #include "Base58.hpp" #include +#include #include #include "Varint.hpp" @@ -23,16 +24,18 @@ const size_t addr_checksum_size = 4; struct reverse_alphabet { reverse_alphabet() { - m_data.resize(alphabet[alphabet_size - 1] - alphabet[0] + 1, -1); + base = *std::min_element(alphabet, alphabet + alphabet_size); + auto top = *std::max_element(alphabet, alphabet + alphabet_size); + m_data.resize(top - base + 1, -1); for (size_t i = 0; i < alphabet_size; ++i) { - size_t idx = static_cast(alphabet[i] - alphabet[0]); + size_t idx = static_cast(alphabet[i] - base); m_data[idx] = static_cast(i); } } int operator()(char letter) const { - size_t idx = static_cast(letter - alphabet[0]); + size_t idx = static_cast(letter - base); return idx < m_data.size() ? m_data[idx] : -1; } @@ -40,6 +43,7 @@ struct reverse_alphabet { private: std::vector m_data; + char base = 0; }; reverse_alphabet reverse_alphabet::instance; @@ -60,64 +64,27 @@ struct decoded_block_sizes { static decoded_block_sizes instance; private: - std::vector m_data; + std::vector m_data; // {0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8}; }; decoded_block_sizes decoded_block_sizes::instance; -uint64_t uint_8be_to_64(const uint8_t *data, size_t size) { - assert(1 <= size && size <= sizeof(uint64_t)); - - 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); - } - - return res; -} - -void uint_64_to_8be(uint64_t num, size_t size, uint8_t *data) { - assert(1 <= size && size <= sizeof(uint64_t)); - - uint64_t num_be = SWAP64BE(num); - memcpy(data, reinterpret_cast(&num_be) + sizeof(uint64_t) - size, size); -} - void encode_block(const uint8_t *block, size_t size, char *res) { assert(1 <= size && size <= full_block_size); - uint64_t num = uint_8be_to_64(block, size); - int i = static_cast(encoded_block_sizes[size]) - 1; // TODO - size_t i? - while (0 < num) { + // uint64_t num = uint_be_from_bytes(block, size); + // size_t i = encoded_block_sizes[size] - 1; + // while (num > 0) { + // uint64_t remainder = num % alphabet_size; + // num /= alphabet_size; + // res[i] = alphabet[remainder]; + // --i; + // } + uint64_t num = uint_be_from_bytes(block, size); + for (size_t i = encoded_block_sizes[size]; i-- > 0;) { uint64_t remainder = num % alphabet_size; num /= alphabet_size; res[i] = alphabet[remainder]; - --i; } } @@ -130,7 +97,7 @@ bool decode_block(const char *block, size_t size, uint8_t *res) { uint64_t res_num = 0; uint64_t order = 1; - for (size_t i = size - 1; i < size; --i) { + for (size_t i = size; i-- > 0;) { int digit = reverse_alphabet::instance(block[i]); if (digit < 0) return false; // Invalid symbol @@ -144,42 +111,31 @@ bool decode_block(const char *block, size_t size, uint8_t *res) { order *= alphabet_size; // Never overflows, 58^10 < 2^64 } - if (static_cast(res_size) < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num) + if (static_cast(res_size) < full_block_size && (uint64_t(1) << (8 * res_size)) <= res_num) return false; // Overflow - uint_64_to_8be(res_num, res_size, res); - + uint_be_to_bytes(res, res_size, res_num); return true; } } // namespace std::string encode(const BinaryArray &data) { - if (data.empty()) - return std::string(); - size_t full_block_count = data.size() / full_block_size; size_t last_block_size = data.size() % full_block_size; size_t res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size]; - std::string res(res_size, alphabet[0]); + std::string res(res_size, '*'); for (size_t i = 0; i < full_block_count; ++i) { encode_block(data.data() + i * full_block_size, full_block_size, &res[i * full_encoded_block_size]); } - - if (0 < last_block_size) { + if (last_block_size > 0) { encode_block(data.data() + full_block_count * full_block_size, last_block_size, &res[full_block_count * full_encoded_block_size]); } - return res; } bool decode(const std::string &enc, BinaryArray *data) { - if (enc.empty()) { - data->clear(); - return true; - } - size_t full_block_count = enc.size() / full_encoded_block_size; size_t last_block_size = enc.size() % full_encoded_block_size; int last_block_decoded_size = decoded_block_sizes::instance(last_block_size); @@ -194,7 +150,7 @@ bool decode(const std::string &enc, BinaryArray *data) { return false; } - if (0 < last_block_size) { + if (last_block_size > 0) { if (!decode_block(enc.data() + full_block_count * full_encoded_block_size, last_block_size, &(*data)[full_block_count * full_block_size])) return false; @@ -236,4 +192,177 @@ bool decode_addr(std::string addr, uint64_t *tag, BinaryArray *data) { return true; } } + +static const uint32_t table[] = {0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, + 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, + 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, + 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, + 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, + 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, + 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, + 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, + 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, + 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, + 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, + 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, + 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, + 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, + 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, + 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, + 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, + 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, + 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; + +uint32_t crc32(const uint8_t *data, size_t size, uint32_t crc) { + crc = crc ^ 0xFFFFFFFFU; + for (size_t i = 0; i != size; ++i) { + size_t y = (crc ^ data[i]) & 0xFFU; + crc = (crc >> 8) ^ table[y]; + } + return crc ^ 0xFFFFFFFFU; } + +// Optimized for checking by simple javascript +void encode_crc_block(uint32_t num, size_t encoded_size, char *res) { + for (size_t i = encoded_size; i-- > 0;) { + uint64_t remainder = num % base58::alphabet_size; + num /= base58::alphabet_size; + res[i] = base58::alphabet[remainder]; + } +} +const size_t future_addr_checksum_size = 4; +static_assert(future_addr_checksum_size <= 4, "we user crc32 so cannot be > 4"); +std::string encode_addr_future(std::string prefix, const BinaryArray &addr_data) { + std::string encoded_data = prefix + base58::encode(addr_data); + uint32_t crc = crc32(reinterpret_cast(encoded_data.data()), encoded_data.size()); + size_t res_size = base58::encoded_block_sizes[future_addr_checksum_size]; + std::string crc_str(res_size, '*'); + encode_crc_block(crc, res_size, &crc_str[0]); + return encoded_data + crc_str; +} + +bool decode_addr_future(std::string addr, std::string prefix, BinaryArray *addr_data) { + size_t res_size = base58::encoded_block_sizes[future_addr_checksum_size]; + if (addr.size() < res_size + prefix.size()) + return false; + std::string encoded_data = addr.substr(0, addr.size() - res_size); + uint32_t crc = crc32(reinterpret_cast(encoded_data.data()), encoded_data.size()); + std::string crc_str(res_size, '*'); + encode_crc_block(crc, res_size, &crc_str[0]); + + std::string actual_crc_str = addr.substr(addr.size() - res_size); + + if (crc_str != actual_crc_str) + return false; + if (encoded_data.substr(0, prefix.size()) != prefix) + return false; + return base58::decode(encoded_data.substr(prefix.size()), addr_data); +} + +void test_addr_future() { + // auto a1 = crypto::random_keypair(); + // auto a2 = crypto::random_keypair(); + // AccountPublicAddress ap{a1.public_key, a2.public_key}; + // std::cout << currency.account_address_as_string(ap) << std::endl; + // + // BinaryArray addr_data = seria::to_binary(ap); + // std::string addr = common::encode_addr_future("bcn", addr_data); + // std::cout << addr << std::endl; + // BinaryArray addr_data2; + // bool addr_good = common::decode_addr_future(addr, "bcn", &addr_data2); + // std::cout << addr_good << std::endl; +} +} + +// Example random addresses + +// bcn7rcNCaFR3gSFDgAcgRMn12Fm7BcHot3DBPEXmX3t6x8PdbxwwprTJtrbPN2ismWzYzNpKwmAXT6BqfEbMX5VtW8W5TTRQz +// bcncpS2YQUzZZN52XwyspjWsP6Mcz4Rb36RHEfEduvb1wTdFH9foAsr3xDJJHGzuvX94Qb2oaxsZDAVB5HdMpwtTuS45jxGNu +// bcnVsnFc5eSpMUPCVwtk8vGcf591HcLxjxkTVkPNWSDBvwSGg5p6CCXV6DCrK4Z6mYjrd3DtLFsirNdJEUBCDYPqDAm2U1aqx + +// crc 509845007 +// crc_str "1n46AA" +// bcnTvZ8DRwgnRZLC2Db3FHC375LBE9NfrYpREbXDByVwbpB5htuNXSJ1roUDjtbJ5nxVUPWbKCpiaq3eXPcN5x9iMxJ1n46AA + +// Javascript impl of future address checking + +// function crc32 ( str ) { +// // http://kevin.vanzonneveld.net +// // + original by: Webtoolkit.info (http://www.webtoolkit.info/) +// // + improved by: T0bsn +// // - depends on: utf8_encode +// // * example 1: crc32('Kevin van Zonneveld'); +// // * returns 1: 1249991249 +// +// var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E +// 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 +// 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 +// A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF +// ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 +// B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 +// E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 +// F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 +// FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 +// DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 +// CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C +// 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 +// 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A +// 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD +// 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 +// 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B +// 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE +// 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 +// 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 +// 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F +// 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 +// 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; +// +// var crc = 0; +// var x = 0; +// var y = 0; +// +// crc = crc ^ (-1); +// for( var i = 0, iTop = str.length; i < iTop; i++ ) { +// y = ( crc ^ str.charCodeAt( i ) ) & 0xFF; +// x = "0x" + table.substr( y * 9, 8 ); +// crc = ( crc >>> 8 ) ^ x; +// } +// +// return (crc ^ (-1)) >>> 0; +//} +// var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +// +// function encode_crc_block(num, encoded_size) { +// var result = "" +// for(var i = encoded_size; i-- > 0 ;) { +// remainder = num % alphabet.length; +// num = Math.floor(num/alphabet.length); +// result = alphabet[remainder] + result; +// } +// return result +//} +// +// function check_address(addr, prefix) { +// var res_size = 6; +// if( addr.length < res_size + prefix.length ) +// return false; +// var actual_crc_str = addr.substr(addr.length - res_size); +// var encoded_addr = addr.substr(0, addr.length - res_size); +// var crc = crc32(encoded_addr) +// crc_str = encode_crc_block(crc, res_size); +// if(crc_str != actual_crc_str) +// return false; +// if(encoded_addr.substr(0, prefix.length) != prefix) +// return false; +// return true; +//} diff --git a/src/common/Base58.hpp b/src/common/Base58.hpp index 136d2f07..2a8b8859 100644 --- a/src/common/Base58.hpp +++ b/src/common/Base58.hpp @@ -16,4 +16,9 @@ bool decode(const std::string &enc, BinaryArray *data); std::string encode_addr(uint64_t tag, const BinaryArray &data); bool decode_addr(std::string addr, uint64_t *tag, BinaryArray *data); } + +uint32_t crc32(const uint8_t *data, size_t size, uint32_t crc = 0); +std::string encode_addr_future(std::string prefix, const BinaryArray &addr_data); +bool decode_addr_future(std::string addr, std::string prefix, BinaryArray *addr_data); +void test_addr_future(); } diff --git a/src/common/BinaryArray.hpp b/src/common/BinaryArray.hpp index fce7cd19..6e6c3944 100644 --- a/src/common/BinaryArray.hpp +++ b/src/common/BinaryArray.hpp @@ -7,7 +7,7 @@ #include #include #include - +#include namespace common { class BinaryArrayImpl { @@ -63,6 +63,8 @@ class BinaryArrayImpl { iterator end() { return m_data + m_size; } value_type &operator[](size_t i) { return m_data[i]; } const value_type &operator[](size_t i) const { return m_data[i]; } + value_type &at(size_t i) { if( i >= m_size) throw std::out_of_range("BinaryArray subscript out of range"); return m_data[i]; } + const value_type &at(size_t i) const { if( i >= m_size) throw std::out_of_range("BinaryArray subscript out of range"); return m_data[i]; } void clear() { resize(0); } void resize(size_t si); void resize(size_t si, value_type va); diff --git a/src/common/Ipv4Address.cpp b/src/common/Ipv4Address.cpp index ad09f5b6..baee5a3e 100644 --- a/src/common/Ipv4Address.cpp +++ b/src/common/Ipv4Address.cpp @@ -8,33 +8,36 @@ namespace common { -std::string ip_address_to_string(uint32_t ip) { - uint8_t bytes[4]{}; - bytes[0] = uint8_t(ip & 0xFF); - bytes[1] = uint8_t((ip >> 8) & 0xFF); - bytes[2] = uint8_t((ip >> 16) & 0xFF); - bytes[3] = uint8_t((ip >> 24) & 0xFF); - +std::string ip_address_to_string(BinaryArray ip) { + if (ip.size() != 4) + return "?.?.?.?"; char buf[16]{}; - sprintf(buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]); + sprintf(buf, "%u.%u.%u.%u", ip.data()[0], ip.data()[1], ip.data()[2], ip.data()[3]); return std::string(buf); } -std::string ip_address_and_port_to_string(uint32_t ip, uint32_t port) { - uint8_t bytes[4]{}; - bytes[0] = uint8_t(ip & 0xFF); - bytes[1] = uint8_t((ip >> 8) & 0xFF); - bytes[2] = uint8_t((ip >> 16) & 0xFF); - bytes[3] = uint8_t((ip >> 24) & 0xFF); +uint32_t ip_address_to_legacy(BinaryArray ip) { + if (ip.size() != 4) + return 0; + return (static_cast(ip.data()[0])) | (static_cast(ip.data()[1]) << 8) | + (static_cast(ip.data()[2]) << 16) | (static_cast(ip.data()[3]) << 24); +} +BinaryArray ip_address_from_legacy(uint32_t ip) { + return BinaryArray{static_cast(ip), static_cast(ip >> 8), static_cast(ip >> 16), + static_cast(ip >> 24)}; +} +std::string ip_address_and_port_to_string(BinaryArray ip, uint16_t port) { + if (ip.size() != 4) + return "?.?.?.?"; char buf[24]{}; - sprintf(buf, "%d.%d.%d.%d:%d", bytes[0], bytes[1], bytes[2], bytes[3], port); + sprintf(buf, "%u.%u.%u.%u:%u", ip.data()[0], ip.data()[1], ip.data()[2], ip.data()[3], port); return std::string(buf); } -bool parse_ip_address(const std::string &addr, uint32_t *ip) { +bool parse_ip_address(const std::string &addr, BinaryArray *ip) { uint32_t v[4]{}; if (sscanf(addr.c_str(), "%u.%u.%u.%u", &v[0], &v[1], &v[2], &v[3]) != 4) { @@ -46,12 +49,14 @@ bool parse_ip_address(const std::string &addr, uint32_t *ip) { return false; } } - - *ip = (v[3] << 24u) | (v[2] << 16u) | (v[1] << 8u) | v[0]; + *ip = BinaryArray{ + static_cast(v[0]), static_cast(v[1]), static_cast(v[2]), static_cast(v[3])}; + // *ip = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0]; return true; } -bool parse_ip_address_and_port(const std::string &addr, uint32_t *ip, uint32_t *port) { +// TODO - add IPv6 support +bool parse_ip_address_and_port(const std::string &addr, BinaryArray *ip, uint16_t *port) { uint32_t v[4]{}; uint32_t local_port = 0; @@ -65,7 +70,8 @@ bool parse_ip_address_and_port(const std::string &addr, uint32_t *ip, uint32_t * } } - *ip = (v[3] << 24u) | (v[2] << 16u) | (v[1] << 8u) | v[0]; + *ip = BinaryArray{ + static_cast(v[0]), static_cast(v[1]), static_cast(v[2]), static_cast(v[3])}; if (local_port > 65535) return false; *port = local_port; @@ -73,27 +79,24 @@ bool parse_ip_address_and_port(const std::string &addr, uint32_t *ip, uint32_t * } bool parse_ip_address_and_port(const std::string &addr, std::string *ip, uint16_t *port) { - uint32_t sip = 0, sport = 0; - if (!parse_ip_address_and_port(addr, &sip, &sport)) + BinaryArray sip; + if (!parse_ip_address_and_port(addr, &sip, port)) return false; - *port = static_cast(sport); - *ip = ip_address_to_string(sip); + // *port = static_cast(sport); + *ip = ip_address_to_string(sip); return true; } -int get_private_network_prefix(uint32_t ip) { - uint8_t bytes[4]{}; - bytes[0] = uint8_t(ip & 0xFF); - bytes[1] = uint8_t((ip >> 8) & 0xFF); - bytes[2] = uint8_t((ip >> 16) & 0xFF); - bytes[3] = uint8_t((ip >> 24) & 0xFF); - if (bytes[0] == 127) // 127.x.x.x +int get_private_network_prefix(BinaryArray ip) { + if (ip.size() != 4) + return 6; + if (ip.data()[0] == 127) // 127.x.x.x return 127; - if (bytes[0] == 10) // 10.0.0.0/8 + if (ip.data()[0] == 10) // 10.0.0.0/8 return 10; - if (bytes[0] == 192 && bytes[1] == 168) // 192.168.0.0/16 + if (ip.data()[0] == 192 && ip.data()[1] == 168) // 192.168.0.0/16 return 192; - if (bytes[0] == 172 && (bytes[1] & 0xf0) == 16) // 172.16.0.0/12 + if (ip.data()[0] == 172 && (ip.data()[1] & 0xf0) == 16) // 172.16.0.0/12 return 172; return 0; } diff --git a/src/common/Ipv4Address.hpp b/src/common/Ipv4Address.hpp index 611bbc9f..c5528fcb 100644 --- a/src/common/Ipv4Address.hpp +++ b/src/common/Ipv4Address.hpp @@ -5,15 +5,18 @@ #include #include +#include "BinaryArray.hpp" namespace common { -std::string ip_address_to_string(uint32_t ip); -std::string ip_address_and_port_to_string(uint32_t ip, uint32_t port); -bool parse_ip_address(const std::string &addr, uint32_t *ip); -bool parse_ip_address_and_port(const std::string &addr, uint32_t *ip, uint32_t *port); +std::string ip_address_to_string(BinaryArray ip); +uint32_t ip_address_to_legacy(BinaryArray ip); +BinaryArray ip_address_from_legacy(uint32_t ip); +std::string ip_address_and_port_to_string(BinaryArray ip, uint16_t port); +bool parse_ip_address(const std::string &addr, BinaryArray *ip); +bool parse_ip_address_and_port(const std::string &addr, BinaryArray *ip, uint16_t *port); bool parse_ip_address_and_port(const std::string &addr, std::string *ip, uint16_t *port); -int get_private_network_prefix(uint32_t ip); // 0, 10, 127, 172, 192 for all classes of private addresses -inline bool is_ip_address_loopback(uint32_t ip) { return get_private_network_prefix(ip) == 127; } -inline bool is_ip_address_private(uint32_t ip) { return get_private_network_prefix(ip) == 0; } +int get_private_network_prefix(BinaryArray ip); // 0, 10, 127, 172, 192 for all classes of IPv4 private addresses +inline bool is_ip_address_loopback(BinaryArray ip) { return get_private_network_prefix(ip) == 127; } +inline bool is_ip_address_private(BinaryArray ip) { return get_private_network_prefix(ip) == 0; } } diff --git a/src/common/JsonValue.cpp b/src/common/JsonValue.cpp index dfd1dbeb..e4a9f7c0 100644 --- a/src/common/JsonValue.cpp +++ b/src/common/JsonValue.cpp @@ -3,8 +3,11 @@ #include "JsonValue.hpp" #include +#include +#include #include #include "StringTools.hpp" +#include "string.hpp" namespace common { @@ -13,7 +16,7 @@ JsonValue::JsonValue() : type(NIL) {} JsonValue::JsonValue(const JsonValue &other) { switch (other.type) { case ARRAY: - new (&value_array) Array(*reinterpret_cast(&other.value_array)); + new (&value_array) Array(reinterpret_cast(other.value_array)); break; case BOOL: value_bool = other.value_bool; @@ -27,24 +30,22 @@ JsonValue::JsonValue(const JsonValue &other) { case NIL: break; case OBJECT: - new (&value_object) Object(*reinterpret_cast(&other.value_object)); + new (&value_object) Object(reinterpret_cast(other.value_object)); break; case DOUBLE: value_real = other.value_real; break; case STRING: - new (&value_string) String(*reinterpret_cast(&other.value_string)); + new (&value_string) String(reinterpret_cast(other.value_string)); break; } - type = other.type; } JsonValue::JsonValue(JsonValue &&other) { switch (other.type) { case ARRAY: - new (&value_array) Array(std::move(*reinterpret_cast(&other.value_array))); - reinterpret_cast(&other.value_array)->~Array(); + new (&value_array) Array(std::move(reinterpret_cast(other.value_array))); break; case BOOL: value_bool = other.value_bool; @@ -58,20 +59,17 @@ JsonValue::JsonValue(JsonValue &&other) { case NIL: break; case OBJECT: - new (&value_object) Object(std::move(*reinterpret_cast(&other.value_object))); - reinterpret_cast(&other.value_object)->~Object(); + new (&value_object) Object(std::move(reinterpret_cast(other.value_object))); break; case DOUBLE: value_real = other.value_real; break; case STRING: - new (&value_string) String(std::move(*reinterpret_cast(&other.value_string))); - reinterpret_cast(&other.value_string)->~String(); + new (&value_string) String(std::move(reinterpret_cast(other.value_string))); break; } - - type = other.type; - other.type = NIL; + type = other.type; + other.destruct_value(); } JsonValue::JsonValue(Type value_type) { @@ -143,8 +141,7 @@ JsonValue &JsonValue::operator=(const JsonValue &other) { destruct_value(); switch (other.type) { case ARRAY: - type = NIL; - new (&value_array) Array(*reinterpret_cast(&other.value_array)); + new (&value_array) Array(reinterpret_cast(other.value_array)); break; case BOOL: value_bool = other.value_bool; @@ -158,23 +155,20 @@ JsonValue &JsonValue::operator=(const JsonValue &other) { case NIL: break; case OBJECT: - type = NIL; - new (&value_object) Object(*reinterpret_cast(&other.value_object)); + new (&value_object) Object(reinterpret_cast(other.value_object)); break; case DOUBLE: value_real = other.value_real; break; case STRING: - type = NIL; - new (&value_string) String(*reinterpret_cast(&other.value_string)); + new (&value_string) String(reinterpret_cast(other.value_string)); break; } - type = other.type; } else { switch (type) { case ARRAY: - *reinterpret_cast(&value_array) = *reinterpret_cast(&other.value_array); + reinterpret_cast(value_array) = reinterpret_cast(other.value_array); break; case BOOL: value_bool = other.value_bool; @@ -188,17 +182,16 @@ JsonValue &JsonValue::operator=(const JsonValue &other) { case NIL: break; case OBJECT: - *reinterpret_cast(&value_object) = *reinterpret_cast(&other.value_object); + reinterpret_cast(value_object) = reinterpret_cast(other.value_object); break; case DOUBLE: value_real = other.value_real; break; case STRING: - *reinterpret_cast(&value_string) = *reinterpret_cast(&other.value_string); + reinterpret_cast(value_string) = reinterpret_cast(other.value_string); break; } } - return *this; } @@ -208,9 +201,7 @@ JsonValue &JsonValue::operator=(JsonValue &&other) { destruct_value(); switch (other.type) { case ARRAY: - type = NIL; - new (&value_array) Array(std::move(*reinterpret_cast(&other.value_array))); - reinterpret_cast(&other.value_array)->~Array(); + new (&value_array) Array(std::move(reinterpret_cast(other.value_array))); break; case BOOL: value_bool = other.value_bool; @@ -224,26 +215,20 @@ JsonValue &JsonValue::operator=(JsonValue &&other) { case NIL: break; case OBJECT: - type = NIL; - new (&value_object) Object(std::move(*reinterpret_cast(&other.value_object))); - reinterpret_cast(&other.value_object)->~Object(); + new (&value_object) Object(std::move(reinterpret_cast(other.value_object))); break; case DOUBLE: value_real = other.value_real; break; case STRING: - type = NIL; - new (&value_string) String(std::move(*reinterpret_cast(&other.value_string))); - reinterpret_cast(&other.value_string)->~String(); + new (&value_string) String(std::move(reinterpret_cast(other.value_string))); break; } - type = other.type; } else { switch (type) { case ARRAY: - *reinterpret_cast(&value_array) = std::move(*reinterpret_cast(&other.value_array)); - reinterpret_cast(&other.value_array)->~Array(); + reinterpret_cast(value_array) = std::move(reinterpret_cast(other.value_array)); break; case BOOL: value_bool = other.value_bool; @@ -257,21 +242,17 @@ JsonValue &JsonValue::operator=(JsonValue &&other) { case NIL: break; case OBJECT: - *reinterpret_cast(&value_object) = - std::move(*reinterpret_cast(&other.value_object)); - reinterpret_cast(&other.value_object)->~Object(); + reinterpret_cast(value_object) = std::move(reinterpret_cast(other.value_object)); break; case DOUBLE: value_real = other.value_real; break; case STRING: - *reinterpret_cast(&value_string) = - std::move(*reinterpret_cast(&other.value_string)); - reinterpret_cast(&other.value_string)->~String(); + reinterpret_cast(value_string) = std::move(reinterpret_cast(other.value_string)); break; } } - other.type = NIL; + other.destruct_value(); return *this; } @@ -281,7 +262,7 @@ JsonValue &JsonValue::operator=(const Array &value) { new (&value_array) Array(value); type = ARRAY; } else { - *reinterpret_cast(&value_array) = value; + reinterpret_cast(value_array) = value; } return *this; } @@ -292,7 +273,7 @@ JsonValue &JsonValue::operator=(Array &&value) { new (&value_array) Array(std::move(value)); type = ARRAY; } else { - *reinterpret_cast(&value_array) = std::move(value); + reinterpret_cast(value_array) = std::move(value); } return *this; } @@ -329,7 +310,7 @@ JsonValue &JsonValue::operator=(const Object &value) { new (&value_object) Object(value); type = OBJECT; } else { - *reinterpret_cast(&value_object) = value; + reinterpret_cast(value_object) = value; } return *this; } @@ -340,7 +321,7 @@ JsonValue &JsonValue::operator=(Object &&value) { new (&value_object) Object(std::move(value)); type = OBJECT; } else { - *reinterpret_cast(&value_object) = std::move(value); + reinterpret_cast(value_object) = std::move(value); } return *this; } @@ -358,7 +339,7 @@ JsonValue &JsonValue::operator=(const String &value) { new (&value_string) String(value); type = STRING; } else { - *reinterpret_cast(&value_string) = value; + reinterpret_cast(value_string) = value; } return *this; } @@ -369,89 +350,89 @@ JsonValue &JsonValue::operator=(String &&value) { new (&value_string) String(std::move(value)); type = STRING; } else { - *reinterpret_cast(&value_string) = std::move(value); + reinterpret_cast(value_string) = std::move(value); } return *this; } JsonValue::Array &JsonValue::get_array() { - if (type != ARRAY) { + if (type != ARRAY) throw std::runtime_error("JsonValue type is not ARRAY"); - } - return *reinterpret_cast(&value_array); + return reinterpret_cast(value_array); } const JsonValue::Array &JsonValue::get_array() const { - if (type != ARRAY) { + if (type != ARRAY) throw std::runtime_error("JsonValue type is not ARRAY"); - } - return *reinterpret_cast(&value_array); + return reinterpret_cast(value_array); } JsonValue::Bool JsonValue::get_bool() const { - if (type != BOOL) { + if (type != BOOL) throw std::runtime_error("JsonValue type is not BOOL"); - } return value_bool; } JsonValue::Integer JsonValue::get_integer() const { if (type == SIGNED_INTEGER) return value_integer; - if (type == UNSIGNED_INTEGER) + if (type == UNSIGNED_INTEGER) { + if (value_unsigned > static_cast(std::numeric_limits::max())) + throw std::runtime_error( + "Too big unsigned number does not fit into integer (" + common::to_string(value_unsigned) + ")"); return value_unsigned; + } throw std::runtime_error("JsonValue type is not INTEGER"); } JsonValue::Unsigned JsonValue::get_unsigned() const { - if (type == SIGNED_INTEGER) + if (type == SIGNED_INTEGER) { + if (value_integer < 0) + throw std::runtime_error( + "JsonValue value < 0 cannot be returned as unsigned (" + common::to_string(value_integer) + ")"); return value_integer; + } if (type == UNSIGNED_INTEGER) return value_unsigned; throw std::runtime_error("JsonValue type is not INTEGER"); } JsonValue::Object &JsonValue::get_object() { - if (type != OBJECT) { + if (type != OBJECT) throw std::runtime_error("JsonValue type is not OBJECT"); - } - return *reinterpret_cast(&value_object); + return reinterpret_cast(value_object); } const JsonValue::Object &JsonValue::get_object() const { - if (type != OBJECT) { + if (type != OBJECT) throw std::runtime_error("JsonValue type is not OBJECT"); - } - return *reinterpret_cast(&value_object); + return reinterpret_cast(value_object); } JsonValue::Double JsonValue::get_double() const { - if (type != DOUBLE) { + if (type != DOUBLE) throw std::runtime_error("JsonValue type is not REAL"); - } return value_real; } JsonValue::String &JsonValue::get_string() { - if (type != STRING) { + if (type != STRING) throw std::runtime_error("JsonValue type is not STRING"); - } - return *reinterpret_cast(&value_string); + return reinterpret_cast(value_string); } const JsonValue::String &JsonValue::get_string() const { - if (type != STRING) { + if (type != STRING) throw std::runtime_error("JsonValue type is not STRING"); - } - return *reinterpret_cast(&value_string); + return reinterpret_cast(value_string); } size_t JsonValue::size() const { switch (type) { case ARRAY: - return reinterpret_cast(&value_array)->size(); + return reinterpret_cast(value_array).size(); case OBJECT: - return reinterpret_cast(&value_object)->size(); + return reinterpret_cast(value_object).size(); default: throw std::runtime_error("JsonValue type is not ARRAY or OBJECT"); } @@ -502,15 +483,11 @@ size_t JsonValue::erase(const Key &key) { return get_object().erase(key); } JsonValue JsonValue::from_string(const std::string &source) { JsonValue json_value; std::istringstream stream(source); - stream >> json_value; - if (stream.fail()) { - throw std::runtime_error("Unable to parse JsonValue"); - } - char c = 0; - while (stream >> c) { - if (!isspace(c)) - throw std::runtime_error("Extra characters at end of stream"); - } + StreamContext ctx(stream); + json_value.read_json(ctx); + if (stream.fail()) + ctx.throw_error("Stream error"); + ctx.eat_all_whitespace(); // if( !json_value.is_object() && !json_value.is_array()) // throw std::runtime_error("Json should be object or array at top level"); return json_value; @@ -522,7 +499,7 @@ std::string JsonValue::to_string() const { return stream.str(); } -static std::string escape_string(const std::string &str) { +std::string JsonValue::escape_string(const std::string &str) { std::string result; static const std::string escape_table[32] = {"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\u000B", "\\f", "\\r", "\\u000E", "\\u000F", "\\u0010", "\\u0011", @@ -572,10 +549,10 @@ std::ostream &operator<<(std::ostream &out, const JsonValue &json_value) { out << '{'; auto iter = object.begin(); if (iter != object.end()) { - out << '"' << escape_string(iter->first) << "\":" << iter->second; + out << '"' << JsonValue::escape_string(iter->first) << "\":" << iter->second; ++iter; for (; iter != object.end(); ++iter) { - out << ",\"" << escape_string(iter->first) << "\":" << iter->second; + out << ",\"" << JsonValue::escape_string(iter->first) << "\":" << iter->second; } } @@ -594,55 +571,89 @@ std::ostream &operator<<(std::ostream &out, const JsonValue &json_value) { break; } case JsonValue::STRING: - out << '"' << escape_string(*reinterpret_cast(&json_value.value_string)) << '"'; + out << '"' << JsonValue::escape_string(*reinterpret_cast(&json_value.value_string)) + << '"'; break; } return out; } -namespace { - -char read_char(std::istream &in) { - char c = 0; +JsonValue::StreamContext::StreamContext(std::istream &in) : it(in) {} - if (!(in >> c)) { - throw std::runtime_error("Unable to parse: unexpected end of stream"); +char JsonValue::StreamContext::read_char() { + if (it == end) + throw_error("unexpected end of stream"); + char c = *it++; + bool white_space = isspace(c); + if (!white_space || !prev_white_space) { + if (mini_pos == mini_buf.size()) { + if (mini_buf.size() < 32) // We track up to 32 characters + mini_buf.push_back(' '); // will be overwritten immediately + else + mini_pos = 0; + } + mini_buf[mini_pos++] = c; + prev_white_space = white_space; } return c; } -char read_non_ws_char(std::istream &in) { +char JsonValue::StreamContext::peek_char() { + if (it == end) + throw_error("unexpected end of stream"); + return *it; +} + +char JsonValue::StreamContext::read_non_ws_char() { char c = 0; do { - c = read_char(in); + c = read_char(); } while (isspace(c)); return c; } -char read_char2(std::istreambuf_iterator &it, const std::istreambuf_iterator &end) { - if (it == end) - throw std::runtime_error("Unable to parse: unexpected end of stream"); - char c = *it++; +char JsonValue::StreamContext::peek_non_ws_char() { + char c = peek_char(); + + while (isspace(c)) { + read_char(); + c = peek_char(); + } return c; } -std::string read_string_token(std::istream &in) { - std::string value; +void JsonValue::StreamContext::throw_error(const std::string &text) { + std::string before = mini_buf.substr(mini_pos) + mini_buf.substr(0, mini_pos); + throw std::runtime_error("Failed to parse json, " + text + ", ..." + before + " <-- here"); +} +void JsonValue::StreamContext::expect(char c, char should_be_c) { + if (c == should_be_c) + return; + throw_error("expecting '" + std::string({should_be_c}) + "' but got '" + std::string({c}) + "' (character code " + + common::to_string(static_cast(c)) + ") instead"); +} - std::istreambuf_iterator it(in), end; +void JsonValue::StreamContext::eat_all_whitespace() { + while (it != end) + if (!isspace(read_char())) + throw_error("expecting only whitespace at the end of json"); +} + +std::string JsonValue::StreamContext::read_string_token() { + std::string value; while (it != end) { - char c = read_char2(it, end); + char c = read_char(); if (iscntrl(c)) - throw std::runtime_error("Unable to parse: control character inside string"); - if (c == '"') { + throw_error("control character inside string '" + std::string({c}) + "' (character code " + + common::to_string(static_cast(c)) + ")"); + if (c == '"') return value; - } if (c == '\\') { - c = read_char2(it, end); + c = read_char(); switch (c) { case '\\': value += '\\'; @@ -670,19 +681,19 @@ std::string read_string_token(std::istream &in) { continue; case 'u': { // WTF those retards invented... - char c0 = read_char2(it, end); - char c1 = read_char2(it, end); - char c2 = read_char2(it, end); - char c3 = read_char2(it, end); + char c0 = read_char(); + char c1 = read_char(); + char c2 = read_char(); + char c3 = read_char(); unsigned char c0v = 0, c1v = 0, c2v = 0, c3v = 0; if (!common::from_hex(c0, c0v) || !common::from_hex(c1, c1v) || !common::from_hex(c2, c2v) || !common::from_hex(c3, c3v)) - throw std::runtime_error( - "Unable to parse: \\u wrong control code " + std::string({c0, c1, c2, c3})); + throw_error( + "Unable to parse json: \\u wrong hex characters '" + std::string({c0, c1, c2, c3}) + "'"); unsigned cp = unsigned(c0v) * 4096 + unsigned(c1v) * 256 + unsigned(c2v) * 16 + unsigned(c3v); if ((cp >= 0xD800 && cp <= 0xDFFF) || cp >= 0xFFFE) - throw std::runtime_error( - "Unable to parse: \\u does not support surrogate pairs " + std::string({c0, c1, c2, c3})); + throw_error( + "Unable to parse json: \\u does not support surrogate pairs " + std::string({c0, c1, c2, c3})); if (cp < 0x80) { value += static_cast(cp); continue; @@ -701,37 +712,36 @@ std::string read_string_token(std::istream &in) { continue; } default: - throw std::runtime_error("Unable to parse: unknown escape character " + std::string({c})); + throw_error("unknown escape character '" + std::string({c}) + "' (character code " + + common::to_string(static_cast(c)) + ")"); } } value += c; } - throw std::runtime_error("Unable to parse: end of stream inside string"); -} + throw_error("end of stream inside string"); + return std::string(); } -std::istream &operator>>(std::istream &in, JsonValue &json_value) { - char c = read_non_ws_char(in); +void JsonValue::read_json(StreamContext &ctx) { + char c = ctx.read_non_ws_char(); if (c == '[') { - json_value.read_array(in); + read_array(ctx); } else if (c == 't') { - json_value.read_true(in); + read_true(ctx); } else if (c == 'f') { - json_value.read_false(in); + read_false(ctx); } else if ((c == '-') || (c >= '0' && c <= '9')) { - json_value.read_number(in, c); + read_number(ctx, c); } else if (c == 'n') { - json_value.read_null(in); + read_null(ctx); } else if (c == '{') { - json_value.read_object(in); + read_object(ctx); } else if (c == '"') { - json_value.read_string(in); + read_string(ctx); } else { - throw std::runtime_error("Unable to parse"); + ctx.throw_error("Unexpected character"); } - - return in; } void JsonValue::destruct_value() { @@ -751,75 +761,65 @@ void JsonValue::destruct_value() { type = NIL; } -void JsonValue::read_array(std::istream &in) { +void JsonValue::read_array(StreamContext &ctx) { JsonValue::Array value; - char c = read_non_ws_char(in); + char c = ctx.peek_non_ws_char(); - if (c != ']') { - in.putback(c); + if (c == ']') + ctx.read_non_ws_char(); + else { for (;;) { value.resize(value.size() + 1); - in >> value.back(); - c = read_non_ws_char(in); + value.back().read_json(ctx); + c = ctx.read_non_ws_char(); - if (c == ']') { + if (c == ']') break; - } - - if (c != ',') { - throw std::runtime_error("Unable to parse"); - } + ctx.expect(c, ','); } } *this = std::move(value); } -void JsonValue::read_true(std::istream &in) { - char data[3]; - in.read(data, 3); - if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') { - throw std::runtime_error("Unable to parse"); - } - +void JsonValue::read_true(StreamContext &ctx) { + char data[3]{ctx.read_char(), ctx.read_char(), ctx.read_char()}; + if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') + ctx.throw_error("'true' is expected"); destruct_value(); type = JsonValue::BOOL; value_bool = true; } -void JsonValue::read_false(std::istream &in) { - char data[4]; - in.read(data, 4); - if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') { - throw std::runtime_error("Unable to parse"); - } +void JsonValue::read_false(StreamContext &ctx) { + char data[4]{ctx.read_char(), ctx.read_char(), ctx.read_char(), ctx.read_char()}; + if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') + ctx.throw_error("'false' is expected"); destruct_value(); type = JsonValue::BOOL; value_bool = false; } -void JsonValue::read_null(std::istream &in) { - char data[3]; - in.read(data, 3); - if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') { - throw std::runtime_error("Unable to parse"); - } +void JsonValue::read_null(StreamContext &ctx) { + char data[3]{ctx.read_char(), ctx.read_char(), ctx.read_char()}; + if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') + ctx.throw_error("'null' is expected"); destruct_value(); } -void JsonValue::read_number(std::istream &in, char c) { +void JsonValue::read_number(StreamContext &ctx, char c) { std::string text; text += c; size_t dots = 0; for (;;) { - int i = in.peek(); + int i = ctx.peek_char(); if (i >= '0' && i <= '9') { - in.read(&c, 1); + c = ctx.read_char(); text += c; } else if (i == '.') { - in.read(&c, 1); + c = ctx.read_char(); text += '.'; ++dots; } else { @@ -827,43 +827,40 @@ void JsonValue::read_number(std::istream &in, char c) { } } - char pee = in.peek(); + char pee = ctx.peek_char(); if (dots > 0 || pee == 'e' || pee == 'E') { if (dots > 1) { - throw std::runtime_error("Unable to parse"); + ctx.throw_error("More than one '.' in a number"); } if (pee == 'e' || pee == 'E') { - in.read(&c, 1); + c = ctx.read_char(); text += c; - int i = in.peek(); + int i = ctx.peek_char(); if (i == '+') { - in.read(&c, 1); + c = ctx.read_char(); text += c; - i = in.peek(); + i = ctx.peek_char(); } else if (i == '-') { - in.read(&c, 1); + c = ctx.read_char(); text += c; - i = in.peek(); + i = ctx.peek_char(); } - if (i < '0' || i > '9') { - throw std::runtime_error("Unable to parse"); - } + if (i < '0' || i > '9') + ctx.throw_error("Digit expected"); do { - in.read(&c, 1); + c = ctx.read_char(); text += c; - i = in.peek(); + i = ctx.peek_char(); } while (i >= '0' && i <= '9'); } - destruct_value(); std::istringstream(text) >> value_real; type = DOUBLE; } else { - if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) { - throw std::runtime_error("Unable to parse"); - } + if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) + ctx.throw_error("Number expected"); destruct_value(); if (text.size() > 1 && text[0] == '-') { std::istringstream(text) >> value_integer; @@ -875,44 +872,33 @@ void JsonValue::read_number(std::istream &in, char c) { } } -void JsonValue::read_object(std::istream &in) { - char c = read_non_ws_char(in); +void JsonValue::read_object(StreamContext &ctx) { + char c = ctx.read_non_ws_char(); JsonValue::Object value; if (c != '}') { std::string name; - for (;;) { - if (c != '"') { - throw std::runtime_error("Unable to parse"); - } + ctx.expect(c, '"'); + name = ctx.read_string_token(); + c = ctx.read_non_ws_char(); - name = read_string_token(in); - c = read_non_ws_char(in); - - if (c != ':') { - throw std::runtime_error("Unable to parse"); - } + ctx.expect(c, ':'); - in >> value[name]; - c = read_non_ws_char(in); + value[name].read_json(ctx); + c = ctx.read_non_ws_char(); - if (c == '}') { + if (c == '}') break; - } - - if (c != ',') { - throw std::runtime_error("Unable to parse"); - } - - c = read_non_ws_char(in); + ctx.expect(c, ','); + c = ctx.read_non_ws_char(); } } *this = std::move(value); } -void JsonValue::read_string(std::istream &in) { - String value = read_string_token(in); +void JsonValue::read_string(StreamContext &ctx) { + String value = ctx.read_string_token(); *this = std::move(value); } } diff --git a/src/common/JsonValue.hpp b/src/common/JsonValue.hpp index 299a09e2..7c0cd0f7 100644 --- a/src/common/JsonValue.hpp +++ b/src/common/JsonValue.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -126,10 +127,9 @@ class JsonValue { static JsonValue from_string(const std::string &source); std::string to_string() const; - // those operators should no be used because they do not check for correct end of object (example - extra comma - // after json object) friend std::ostream &operator<<(std::ostream &out, const JsonValue &json_value); - friend std::istream &operator>>(std::istream &in, JsonValue &json_value); + + static std::string escape_string(const std::string &str); private: Type type; @@ -145,12 +145,31 @@ class JsonValue { void destruct_value(); - void read_array(std::istream &in); - void read_true(std::istream &in); - void read_false(std::istream &in); - void read_null(std::istream &in); - void read_number(std::istream &in, char c); - void read_object(std::istream &in); - void read_string(std::istream &in); + struct StreamContext { + std::istreambuf_iterator it; + const std::istreambuf_iterator end; + std::string mini_buf; // relatively efficient char history buffer + size_t mini_pos = 0; + bool prev_white_space = true; // collapse whitespaces in mini_buf + public: + explicit StreamContext(std::istream &in); + char peek_char(); + char read_char(); + char read_non_ws_char(); + char peek_non_ws_char(); + std::string read_string_token(); + void eat_all_whitespace(); + void throw_error(const std::string &text); + void expect(char c, char should_be_c); + }; + void read_json(StreamContext &ctx); + + void read_array(StreamContext &ctx); + void read_true(StreamContext &ctx); + void read_false(StreamContext &ctx); + void read_null(StreamContext &ctx); + void read_number(StreamContext &ctx, char c); + void read_object(StreamContext &ctx); + void read_string(StreamContext &ctx); }; } diff --git a/src/common/Math.hpp b/src/common/Math.hpp index 1beba1d2..872d8ff1 100644 --- a/src/common/Math.hpp +++ b/src/common/Math.hpp @@ -4,7 +4,10 @@ #pragma once #include +#include #include +#include "exception.hpp" +#include "string.hpp" namespace common { @@ -20,4 +23,104 @@ T median_value(std::vector *v) { return (*v)[n]; return ((*v)[n - 1] + (*v)[n]) / 2; // 2, 4, 6... } + +template +void integer_cast_throw(const Source &arg) { + throw std::out_of_range( + "Failed integer cast of " + common::to_string(arg) + " to " + common::demangle(typeid(Target).name())); +} + +template +inline Target integer_cast_impl(const Source &arg, std::true_type, std::true_type) { + // both unsigned + if (arg > std::numeric_limits::max()) + integer_cast_throw(arg); + return static_cast(arg); +} + +template +inline Target integer_cast_impl(const Source &arg, std::false_type, std::false_type) { + // both signed + if (arg > std::numeric_limits::max()) + integer_cast_throw(arg); + if (arg < std::numeric_limits::min()) + integer_cast_throw(arg); + return static_cast(arg); +} + +template +inline Target integer_cast_impl(const Source &arg, std::true_type, std::false_type) { + // signed to unsigned + typedef typename std::make_unsigned::type USource; + if (arg < 0) + integer_cast_throw(arg); + if (static_cast(arg) > std::numeric_limits::max()) + integer_cast_throw(arg); + return static_cast(arg); +} + +template +inline Target integer_cast_impl(const Source &arg, std::false_type, std::true_type) { + // unsigned to signed + typedef typename std::make_signed::type UTarget; + if (arg > static_cast(std::numeric_limits::max())) + integer_cast_throw(arg); + return static_cast(arg); +} + +template +inline Target integer_cast(const Source &arg) { + static_assert(std::is_integral::value && std::is_integral::value, "Needs 2 integral types"); + return integer_cast_impl(arg, std::is_unsigned{}, std::is_unsigned{}); +} + +// template +// void test_convert(Source arg){ +// try{ +// Target t = common::integer_cast(arg); +// std::cout << "Success " << arg << " -> " << common::to_string(t) << std::endl; +// }catch(const std::exception &){ +// std::cout << "Fail " << arg << " -> " << typeid(Target).name() << std::endl; +// } +//} + +// test_convert(1); +// test_convert(0xFF); +// test_convert(1); +// test_convert(0x7F); +// test_convert(-2); +// +// test_convert(2000000000); +// test_convert(-2000000000); +// test_convert(-2); +// +// test_convert(2); +// test_convert(2000000000); +// test_convert(4000000000); +// +// test_convert(2); +// test_convert(20000); +// test_convert(2000000000); +// test_convert(4000000000); +// +// test_convert(-2); +// test_convert(2); +// test_convert(20000); +// test_convert(60000); +// test_convert(2000000000); +// +// test_convert(2); +// test_convert(20000); +// test_convert(60000); +// test_convert(2000000000); +// test_convert(4000000000); +// +// test_convert(-2000000000); +// test_convert(-60000); +// test_convert(-20000); +// test_convert(-2); +// test_convert(2); +// test_convert(20000); +// test_convert(60000); +// test_convert(2000000000); } diff --git a/src/common/MemoryStreams.hpp b/src/common/MemoryStreams.hpp index df326ece..c28e4acd 100644 --- a/src/common/MemoryStreams.hpp +++ b/src/common/MemoryStreams.hpp @@ -68,6 +68,7 @@ class StringStream : public StringInputStream, public StringOutputStream { std::string &buffer() { return m_buffer; } const std::string &buffer() const { return m_buffer; } + std::string move_buffer() { return std::move(m_buffer); } void clear() { in_position = 0; diff --git a/src/common/StringTools.cpp b/src/common/StringTools.cpp index ae2d3496..2b012d1b 100644 --- a/src/common/StringTools.cpp +++ b/src/common/StringTools.cpp @@ -3,17 +3,16 @@ #include "StringTools.hpp" #include +#include "string.hpp" namespace common { -namespace { - -const uint8_t character_values[256] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +static const uint8_t character_values[256] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -21,8 +20,7 @@ const uint8_t character_values[256] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -} + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; std::string as_string(const void *data, size_t size) { return std::string(static_cast(data), size); } @@ -32,98 +30,61 @@ BinaryArray as_binary_array(const std::string &data) { } uint8_t from_hex(char character) { - uint8_t value = character_values[static_cast(character)]; - if (value > 0x0f) { - throw std::runtime_error("from_hex: invalid character"); - } - + const unsigned char uc = static_cast(character); + uint8_t value = character_values[uc]; + if (value > 0x0f) + throw std::runtime_error( + "from_hex: invalid character '" + std::string({character}) + "', character code " + common::to_string(uc)); return value; } bool from_hex(char character, uint8_t &value) { - if (character_values[static_cast(character)] > 0x0f) { + if (character_values[static_cast(character)] > 0x0f) return false; - } - value = character_values[static_cast(character)]; return true; } -size_t from_hex(const std::string &text, void *data, size_t buffer_size) { - if ((text.size() & 1) != 0) { - throw std::runtime_error("from_hex: invalid string size"); - } - - if (text.size() >> 1 > buffer_size) { - throw std::runtime_error("from_hex: invalid buffer size"); - } - - for (size_t i = 0; i> 1; ++i) { - static_cast(data)[i] = from_hex(text[i << 1]) << 4 | from_hex(text[(i << 1) + 1]); - } - - return text.size() >> 1; +void from_hex_or_throw(const std::string &text, void *data, size_t buffer_size) { + if (text.size() != buffer_size * 2) + throw std::runtime_error("from_hex: Wrong string size (" + common::to_string(text.size()) + ") must be " + + common::to_string(buffer_size * 2)); + for (size_t i = 0; i < buffer_size; ++i) + static_cast(data)[i] = from_hex(text[i * 2]) << 4 | from_hex(text[i * 2 + 1]); } -bool from_hex(const std::string &text, void *data, size_t buffer_size, size_t &size) { - if ((text.size() & 1) != 0) { - return false; - } - - if (text.size() >> 1 > buffer_size) { +bool from_hex(const std::string &text, void *data, size_t buffer_size) { + if (text.size() != buffer_size * 2) return false; - } - - for (size_t i = 0; i> 1; ++i) { - uint8_t value1; - if (!from_hex(text[i << 1], value1)) { - return false; - } - - uint8_t value2; - if (!from_hex(text[(i << 1) + 1], value2)) { + for (size_t i = 0; i < buffer_size; ++i) { + uint8_t value1, value2; + if (!from_hex(text[i * 2], value1) || !from_hex(text[i * 2 + 1], value2)) return false; - } - static_cast(data)[i] = value1 << 4 | value2; } - - size = text.size() >> 1; return true; } BinaryArray from_hex(const std::string &text) { - if ((text.size() & 1) != 0) { + if (text.size() % 2 != 0) throw std::runtime_error("from_hex: invalid string size"); - } - - BinaryArray data(text.size() >> 1); - for (size_t i = 0; i < data.size(); ++i) { - data[i] = from_hex(text[i << 1]) << 4 | from_hex(text[(i << 1) + 1]); - } - + BinaryArray data(text.size() / 2); + for (size_t i = 0; i < data.size(); ++i) + data[i] = from_hex(text[i * 2]) << 4 | from_hex(text[i * 2 + 1]); return data; } bool from_hex(const std::string &text, BinaryArray &data) { - if ((text.size() & 1) != 0) { + if (text.size() % 2 != 0) return false; - } - - for (size_t i = 0; i> 1; ++i) { - uint8_t value1; - if (!from_hex(text[i << 1], value1)) { - return false; - } - - uint8_t value2; - if (!from_hex(text[(i << 1) + 1], value2)) { + BinaryArray result(text.size() / 2); + for (size_t i = 0; i < result.size(); ++i) { + uint8_t value1, value2; + if (!from_hex(text[i * 2], value1) || !from_hex(text[i * 2 + 1], value2)) return false; - } - - data.push_back(value1 << 4 | value2); + result[i] = value1 << 4 | value2; } - + data = std::move(result); return true; } @@ -131,36 +92,22 @@ std::string to_hex(const void *data, size_t size) { std::string text; for (size_t i = 0; i < size; ++i) { text += "0123456789abcdef"[static_cast(data)[i] >> 4]; - text += "0123456789abcdef"[static_cast(data)[i] & 15]; + text += "0123456789abcdef"[static_cast(data)[i] & 0x0f]; } return text; } -void append_hex(const void *data, size_t size, std::string &text) { - for (size_t i = 0; i < size; ++i) { - text += "0123456789abcdef"[static_cast(data)[i] >> 4]; - text += "0123456789abcdef"[static_cast(data)[i] & 15]; - } -} - std::string to_hex(const BinaryArray &data) { std::string text; for (size_t i = 0; i < data.size(); ++i) { text += "0123456789abcdef"[data[i] >> 4]; - text += "0123456789abcdef"[data[i] & 15]; + text += "0123456789abcdef"[data[i] & 0x0f]; } return text; } -void append_hex(const BinaryArray &data, std::string &text) { - for (size_t i = 0; i < data.size(); ++i) { - text += "0123456789abcdef"[data[i] >> 4]; - text += "0123456789abcdef"[data[i] & 15]; - } -} - std::string extract(std::string &text, char delimiter) { size_t delimiter_pos = text.find(delimiter); std::string sub_text; diff --git a/src/common/StringTools.hpp b/src/common/StringTools.hpp index 6be03cd6..9eb89b3c 100644 --- a/src/common/StringTools.hpp +++ b/src/common/StringTools.hpp @@ -4,42 +4,32 @@ #pragma once #include +#include #include #include #include "BinaryArray.hpp" namespace common { -std::string as_string(const void *data, size_t size); // Does not throw +std::string as_string(const void *data, size_t size); inline std::string as_string(const BinaryArray &data) { return as_string(data.data(), data.size()); } BinaryArray as_binary_array(const std::string &data); -uint8_t from_hex(char character); // Returns value of hex 'character', throws on error -bool from_hex(char character, - uint8_t &value); // Assigns value of hex 'character' to 'value', returns false on error, does not throw -size_t from_hex(const std::string &text, void *data, size_t buffer_size); // Assigns values of hex 'text' to buffer - // 'data' up to 'buffer_size', returns actual - // data size, throws on error -bool from_hex(const std::string &text, void *data, size_t buffer_size, - size_t &size); // Assigns values of hex 'text' to buffer 'data' up to 'buffer_size', assigns actual data size to - // 'size', returns false on error, does not throw +uint8_t from_hex(char character); +bool from_hex(char character, uint8_t &value); +void from_hex_or_throw(const std::string &text, void *data, size_t buffer_size); +bool from_hex(const std::string &text, void *data, size_t buffer_size); BinaryArray from_hex(const std::string &text); // Returns values of hex 'text', throws on error -bool from_hex(const std::string &text, - BinaryArray &data); // Appends values of hex 'text' to 'data', returns false on error, does not throw +bool from_hex(const std::string &text, BinaryArray &data); template bool pod_from_hex(const std::string &text, T &val) { static_assert(std::is_standard_layout::value, "T must be Standard Layout"); - size_t out_size; - return from_hex(text, &val, sizeof(val), out_size) && out_size == sizeof(val); + return from_hex(text, &val, sizeof(val)); } -std::string to_hex(const void *data, size_t size); // Returns hex representation of ('data', 'size'), does not throw -void append_hex(const void *data, size_t size, - std::string &text); // Appends hex representation of ('data', 'size') to 'text', does not throw -std::string to_hex(const BinaryArray &data); // Returns hex representation of 'data', does not throw -void append_hex( - const BinaryArray &data, std::string &text); // Appends hex representation of 'data' to 'text', does not throw +std::string to_hex(const void *data, size_t size); +std::string to_hex(const BinaryArray &data); template std::string pod_to_hex(const T &s) { @@ -53,8 +43,11 @@ inline bool split_string_helper(const std::string &str, size_t pos, const std::s } template -inline bool split_string_helper( - const std::string &str, size_t pos, const std::string &separator, std::string &head, Parts &... parts) { +inline bool split_string_helper(const std::string &str, + size_t pos, + const std::string &separator, + std::string &head, + Parts &... parts) { size_t pos2 = str.find(separator, pos); if (pos2 == std::string::npos) return false; @@ -66,4 +59,60 @@ template inline bool split_string(const std::string &str, const std::string &separator, Parts &... parts) { return split_string_helper(str, 0, separator, parts...); } + +// Compile time from_hex +constexpr unsigned char compile_time_parse_digit(char c) { + return (c == '0') + ? 0 + : (c == '1') + ? 1 + : (c == '2') + ? 2 + : (c == '3') + ? 3 + : (c == '4') + ? 4 + : (c == '5') + ? 5 + : (c == '6') + ? 6 + : (c == '7') + ? 7 + : (c == '8') + ? 8 + : (c == '9') + ? 9 + : (c == 'a' || c == 'A') + ? 0xa + : (c == 'b' || c == 'B') + ? 0xb + : (c == 'c' || c == 'C') + ? 0xc + : (c == 'd' || c == 'D') + ? 0xd + : (c == 'e' || c == 'E') + ? 0xe + : (c == 'f' || + c == 'F') + ? 0xf + : throw std:: + runtime_error( + "bad digit"); +} + +template +constexpr T compile_time_from_hex_impl(const char *str, size_t s, T t) { + if (s == 0) + return t; + char c0 = str[(s - 1) * 2]; + char c1 = str[(s - 1) * 2 + 1]; + t.data[s - 1] = (compile_time_parse_digit(c0) << 4) + compile_time_parse_digit(c1); + return compile_time_from_hex_impl(str, s - 1, t); +} + +template +constexpr T pfh(const char (&str)[sizeof(T) * 2 + 1]) { + static_assert(std::is_standard_layout::value, "T must be Standard Layout"); + return compile_time_from_hex_impl(str, sizeof(T), T{}); +} } diff --git a/src/common/Varint.cpp b/src/common/Varint.cpp index 99f4ac14..02626d76 100644 --- a/src/common/Varint.cpp +++ b/src/common/Varint.cpp @@ -6,24 +6,6 @@ namespace common { -template -T uint_be_from_bytes(const unsigned char *buf, size_t si) { - T result = 0; - for (size_t i = 0; i != si; ++i) { - result <<= 8; - result |= buf[i]; - } - return result; -} - -template -void uint_be_to_bytes(unsigned char *buf, size_t si, T val) { - for (size_t i = si; i-- > 0;) { - buf[i] = static_cast(val); - val >>= 8; - } -} - size_t get_varint_sqlite4_size(uint64_t val) { if (val <= 240) return 1; diff --git a/src/common/Varint.hpp b/src/common/Varint.hpp index a6dc8b88..5174f6b2 100644 --- a/src/common/Varint.hpp +++ b/src/common/Varint.hpp @@ -70,4 +70,46 @@ inline uint64_t read_varint_sqlite4(const char *&begin, const char *end) { uint64_t read_varint_sqlite4(const std::string &str); std::string write_varint_sqlite4(uint64_t val); + +template +T uint_be_from_bytes(const unsigned char *buf, size_t si) { + static_assert(std::is_unsigned::value, "works only with unsigned types"); + T result = 0; + for (size_t i = 0; i != si; ++i) + result = (result << 8) + buf[i]; + return result; +} + +template +void uint_be_to_bytes(unsigned char *buf, size_t si, T val) { + static_assert(std::is_unsigned::value, "works only with unsigned types"); + for (size_t i = si; i-- > 0;) { + buf[i] = static_cast(val); + val >>= 8; + } +} + +template +T uint_le_from_bytes(const unsigned char *buf, size_t si) { + static_assert(std::is_unsigned::value, "works only with unsigned types"); + T result = 0; + for (size_t i = si; i-- > 0;) + result = (result << 8) + buf[i]; + return result; +} + +template +void uint_le_to_bytes(unsigned char *buf, size_t si, T val) { + static_assert(std::is_unsigned::value, "works only with unsigned types"); + for (size_t i = 0; i != si; ++i) { + buf[i] = static_cast(val); + val >>= 8; + } +} +inline void uint_le_to_bytes(unsigned char *buf, size_t si, unsigned char val) { + for (size_t i = 0; i != si; ++i) { + buf[i] = static_cast(val); + val = 0; + } +} } diff --git a/src/common/exception.cpp b/src/common/exception.cpp new file mode 100644 index 00000000..e6f7a436 --- /dev/null +++ b/src/common/exception.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include "exception.hpp" +#include +#include + +static std::string what_impl(const std::exception &e, int level) { + std::string nested_what; + try { + std::rethrow_if_nested(e); + } catch (const std::exception &n) { + nested_what = "\n" + what_impl(n, level + 1); + } catch (...) { + nested_what = "\n..."; + } + return std::string(level * 2, ' ') + e.what() + nested_what; +} + +std::string common::demangle(const char *name) { + std::string str = boost::core::demangle(name); + boost::replace_all(str, "bytecoin::", ""); + boost::replace_all(str, "api::walletd::", ""); + boost::replace_all(str, "api::bytecoind::", ""); + boost::replace_all(str, "crypto::", ""); + return str; +} + +std::string common::what(const std::exception &e) { return what_impl(e, 0); } diff --git a/src/common/exception.hpp b/src/common/exception.hpp new file mode 100644 index 00000000..b161b0e2 --- /dev/null +++ b/src/common/exception.hpp @@ -0,0 +1,13 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#include +#include +#include // clients of demangle will probably appreciate this include + +namespace common { +std::string demangle(const char *name); +std::string what(const std::exception &e); +} diff --git a/src/crypto/bernstein/c_types.h b/src/crypto/bernstein/c_types.h new file mode 100644 index 00000000..83ec0e47 --- /dev/null +++ b/src/crypto/bernstein/c_types.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#if defined(__cplusplus) +namespace crypto { extern "C" { +#endif + +#pragma pack(push, 1) +struct EllipticCurvePoint { + unsigned char data[32]; +}; +struct EllipticCurveScalar { + unsigned char data[32]; +}; +#pragma pack(pop) + +#if defined(__cplusplus) +}} +#endif diff --git a/src/crypto/bernstein/chacha8.c b/src/crypto/bernstein/chacha8.c new file mode 100644 index 00000000..0e711f31 --- /dev/null +++ b/src/crypto/bernstein/chacha8.c @@ -0,0 +1,166 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include +#include + +#include "chacha8.h" +#include "../int-util.h" // TODO - take original implementation + +/* + * The following macros are used to obtain exact-width results. + */ +#define U8V(v) ((uint8_t)(v) & UINT8_C(0xFF)) +#define U32V(v) ((uint32_t)(v) & UINT32_C(0xFFFFFFFF)) + +/* + * The following macros load words from an array of bytes with + * different types of endianness, and vice versa. + */ +#define U8TO32_LITTLE(p) SWAP32LE(((uint32_t*)(p))[0]) +#define U32TO8_LITTLE(p, v) (((uint32_t*)(p))[0] = SWAP32LE(v)) + +#define ROTATE(v,c) (rol32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +//static const char sigma[] = "expand 32-byte k"; + +void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) { + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + char* ctarget = 0; + char tmp[64]; + int i; + + if (!length) return; + + j0 = 1634760805; // U8TO32_LITTLE(sigma + 0); // + j1 = 857760878; // U8TO32_LITTLE(sigma + 4); // + j2 = 2036477234; // U8TO32_LITTLE(sigma + 8); // + j3 = 1797285236; // U8TO32_LITTLE(sigma + 12); // + j4 = U8TO32_LITTLE(key + 0); + j5 = U8TO32_LITTLE(key + 4); + j6 = U8TO32_LITTLE(key + 8); + j7 = U8TO32_LITTLE(key + 12); + j8 = U8TO32_LITTLE(key + 16); + j9 = U8TO32_LITTLE(key + 20); + j10 = U8TO32_LITTLE(key + 24); + j11 = U8TO32_LITTLE(key + 28); + j12 = 0; + j13 = 0; + j14 = U8TO32_LITTLE(iv + 0); + j15 = U8TO32_LITTLE(iv + 4); + + for (;;) { + if (length < 64) { + memcpy(tmp, data, length); + data = tmp; + ctarget = cipher; + cipher = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 8;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS( x0, j0); + x1 = PLUS( x1, j1); + x2 = PLUS( x2, j2); + x3 = PLUS( x3, j3); + x4 = PLUS( x4, j4); + x5 = PLUS( x5, j5); + x6 = PLUS( x6, j6); + x7 = PLUS( x7, j7); + x8 = PLUS( x8, j8); + x9 = PLUS( x9, j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + x0 = XOR( x0,U8TO32_LITTLE((uint8_t*)data + 0)); + x1 = XOR( x1,U8TO32_LITTLE((uint8_t*)data + 4)); + x2 = XOR( x2,U8TO32_LITTLE((uint8_t*)data + 8)); + x3 = XOR( x3,U8TO32_LITTLE((uint8_t*)data + 12)); + x4 = XOR( x4,U8TO32_LITTLE((uint8_t*)data + 16)); + x5 = XOR( x5,U8TO32_LITTLE((uint8_t*)data + 20)); + x6 = XOR( x6,U8TO32_LITTLE((uint8_t*)data + 24)); + x7 = XOR( x7,U8TO32_LITTLE((uint8_t*)data + 28)); + x8 = XOR( x8,U8TO32_LITTLE((uint8_t*)data + 32)); + x9 = XOR( x9,U8TO32_LITTLE((uint8_t*)data + 36)); + x10 = XOR(x10,U8TO32_LITTLE((uint8_t*)data + 40)); + x11 = XOR(x11,U8TO32_LITTLE((uint8_t*)data + 44)); + x12 = XOR(x12,U8TO32_LITTLE((uint8_t*)data + 48)); + x13 = XOR(x13,U8TO32_LITTLE((uint8_t*)data + 52)); + x14 = XOR(x14,U8TO32_LITTLE((uint8_t*)data + 56)); + x15 = XOR(x15,U8TO32_LITTLE((uint8_t*)data + 60)); + + j12 = PLUSONE(j12); + if (!j12) + { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per iv is user's responsibility */ + } + + U32TO8_LITTLE(cipher + 0,x0); + U32TO8_LITTLE(cipher + 4,x1); + U32TO8_LITTLE(cipher + 8,x2); + U32TO8_LITTLE(cipher + 12,x3); + U32TO8_LITTLE(cipher + 16,x4); + U32TO8_LITTLE(cipher + 20,x5); + U32TO8_LITTLE(cipher + 24,x6); + U32TO8_LITTLE(cipher + 28,x7); + U32TO8_LITTLE(cipher + 32,x8); + U32TO8_LITTLE(cipher + 36,x9); + U32TO8_LITTLE(cipher + 40,x10); + U32TO8_LITTLE(cipher + 44,x11); + U32TO8_LITTLE(cipher + 48,x12); + U32TO8_LITTLE(cipher + 52,x13); + U32TO8_LITTLE(cipher + 56,x14); + U32TO8_LITTLE(cipher + 60,x15); + + if (length <= 64) { + if (length < 64) { + memcpy(ctarget, cipher, length); + } + return; + } + length -= 64; + cipher += 64; + data = (uint8_t*)data + 64; + } +} diff --git a/src/crypto/bernstein/chacha8.h b/src/crypto/bernstein/chacha8.h new file mode 100644 index 00000000..1ae885c8 --- /dev/null +++ b/src/crypto/bernstein/chacha8.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#define CHACHA8_KEY_SIZE 32 +#define CHACHA8_IV_SIZE 8 + +#if defined(__cplusplus) +namespace crypto { +extern "C" { +#endif + +void chacha8(const void *data, size_t length, const uint8_t *key, const uint8_t *iv, char *cipher); + +#if defined(__cplusplus) +}} +#endif diff --git a/src/crypto/bernstein/crypto-ops-data.c b/src/crypto/bernstein/crypto-ops-data.c new file mode 100644 index 00000000..02f162b8 --- /dev/null +++ b/src/crypto/bernstein/crypto-ops-data.c @@ -0,0 +1,845 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include + +#include "crypto-ops-data.h" + +/* sqrt(x) is such an integer y that 0 <= y <= p - 1, y % 2 = 0, and y^2 = x (mod p). */ +/* d = -121665 / 121666 */ +const fe fe_d = {-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116}; /* d */ +const fe fe_sqrtm1 = {-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482}; /* sqrt(-1) */ +const fe fe_d2 = {-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199}; /* 2 * d */ + +/* base[i][j] = (j+1)*256^i*B */ +const ge_precomp ge_base[32][8] = { + { + {{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, + {-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, + {-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}}, + {{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303}, + {-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081}, + {26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697}}, + {{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, + {16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, + {30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}}, + {{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540}, + {23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397}, + {7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325}}, + {{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, + {4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, + {19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}}, + {{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777}, + {-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737}, + {-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652}}, + {{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, + {-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, + {28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}}, + {{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726}, + {-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955}, + {27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425}} + }, { + {{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171}, + {27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510}, + {17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660}}, + {{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639}, + {29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963}, + {5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950}}, + {{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568}, + {12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335}, + {25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628}}, + {{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007}, + {-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772}, + {-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653}}, + {{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567}, + {13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686}, + {21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372}}, + {{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887}, + {-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954}, + {-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953}}, + {{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833}, + {-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532}, + {-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876}}, + {{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268}, + {33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214}, + {1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038}} + }, { + {{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800}, + {4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645}, + {-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664}}, + {{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933}, + {-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182}, + {-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222}}, + {{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991}, + {20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880}, + {9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092}}, + {{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295}, + {19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788}, + {8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553}}, + {{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026}, + {11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347}, + {-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033}}, + {{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395}, + {-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278}, + {1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890}}, + {{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995}, + {-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596}, + {-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891}}, + {{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060}, + {11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608}, + {-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606}} + }, { + {{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389}, + {-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016}, + {-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341}}, + {{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505}, + {14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553}, + {-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655}}, + {{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220}, + {12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631}, + {-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099}}, + {{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556}, + {14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749}, + {236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930}}, + {{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391}, + {5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253}, + {20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066}}, + {{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958}, + {-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082}, + {-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383}}, + {{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521}, + {-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807}, + {23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948}}, + {{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134}, + {-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455}, + {27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629}} + }, { + {{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069}, + {-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746}, + {24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919}}, + {{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837}, + {8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906}, + {-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771}}, + {{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817}, + {10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098}, + {10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409}}, + {{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504}, + {-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727}, + {28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420}}, + {{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003}, + {-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605}, + {-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384}}, + {{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701}, + {-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683}, + {29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708}}, + {{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563}, + {-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260}, + {-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387}}, + {{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672}, + {23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686}, + {-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665}} + }, { + {{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182}, + {-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277}, + {14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628}}, + {{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474}, + {-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539}, + {-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822}}, + {{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970}, + {19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756}, + {-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508}}, + {{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683}, + {-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655}, + {-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158}}, + {{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125}, + {-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839}, + {-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664}}, + {{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294}, + {-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899}, + {-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070}}, + {{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294}, + {-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949}, + {-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083}}, + {{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420}, + {-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940}, + {29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396}} + }, { + {{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567}, + {20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127}, + {-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294}}, + {{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887}, + {22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964}, + {16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195}}, + {{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244}, + {24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999}, + {-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762}}, + {{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274}, + {-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236}, + {-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605}}, + {{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761}, + {-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884}, + {-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482}}, + {{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638}, + {-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490}, + {-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170}}, + {{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736}, + {10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124}, + {-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392}}, + {{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029}, + {6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048}, + {28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958}} + }, { + {{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593}, + {26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071}, + {-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692}}, + {{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687}, + {-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441}, + {-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001}}, + {{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460}, + {-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007}, + {-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762}}, + {{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005}, + {-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674}, + {4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035}}, + {{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590}, + {-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957}, + {-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812}}, + {{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740}, + {-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122}, + {-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158}}, + {{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885}, + {26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140}, + {19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857}}, + {{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155}, + {19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260}, + {19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483}} + }, { + {{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677}, + {32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815}, + {22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751}}, + {{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203}, + {-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208}, + {1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230}}, + {{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850}, + {-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389}, + {-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968}}, + {{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689}, + {14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880}, + {5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304}}, + {{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632}, + {-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412}, + {20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566}}, + {{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038}, + {-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232}, + {-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943}}, + {{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856}, + {23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738}, + {15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971}}, + {{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718}, + {-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697}, + {-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883}} + }, { + {{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912}, + {-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358}, + {3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849}}, + {{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307}, + {-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977}, + {-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335}}, + {{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644}, + {-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616}, + {-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735}}, + {{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099}, + {29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341}, + {-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336}}, + {{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646}, + {31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425}, + {-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388}}, + {{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743}, + {-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822}, + {-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462}}, + {{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985}, + {9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702}, + {-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797}}, + {{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293}, + {27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100}, + {19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688}} + }, { + {{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186}, + {2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610}, + {-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707}}, + {{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220}, + {915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025}, + {32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044}}, + {{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992}, + {-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027}, + {21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197}}, + {{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901}, + {31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952}, + {19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878}}, + {{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390}, + {32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730}, + {2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730}}, + {{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180}, + {-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272}, + {-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715}}, + {{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970}, + {-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772}, + {-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865}}, + {{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750}, + {20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373}, + {32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348}} + }, { + {{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144}, + {-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195}, + {5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086}}, + {{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684}, + {-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518}, + {-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233}}, + {{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793}, + {-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794}, + {580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435}}, + {{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921}, + {13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518}, + {2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563}}, + {{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278}, + {-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024}, + {4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030}}, + {{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783}, + {27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717}, + {6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844}}, + {{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333}, + {16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048}, + {22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760}}, + {{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760}, + {-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757}, + {-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112}} + }, { + {{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468}, + {3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184}, + {10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289}}, + {{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066}, + {24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882}, + {13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226}}, + {{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101}, + {29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279}, + {-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811}}, + {{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709}, + {20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714}, + {-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121}}, + {{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464}, + {12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847}, + {13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400}}, + {{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414}, + {-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158}, + {17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045}}, + {{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415}, + {-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459}, + {-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079}}, + {{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412}, + {-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743}, + {-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836}} + }, { + {{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022}, + {18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429}, + {-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065}}, + {{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861}, + {10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000}, + {-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101}}, + {{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815}, + {29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642}, + {10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966}}, + {{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574}, + {-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742}, + {-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689}}, + {{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020}, + {-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772}, + {3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982}}, + {{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953}, + {-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218}, + {-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265}}, + {{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073}, + {-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325}, + {-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798}}, + {{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870}, + {-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863}, + {-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927}} + }, { + {{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267}, + {-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663}, + {22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862}}, + {{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673}, + {15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943}, + {15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020}}, + {{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238}, + {11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064}, + {14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795}}, + {{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052}, + {-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904}, + {29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531}}, + {{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979}, + {-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841}, + {10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431}}, + {{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324}, + {-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940}, + {10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320}}, + {{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184}, + {14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114}, + {30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878}}, + {{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784}, + {-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091}, + {-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585}} + }, { + {{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208}, + {10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864}, + {17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661}}, + {{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233}, + {26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212}, + {-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525}}, + {{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068}, + {9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397}, + {-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988}}, + {{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889}, + {32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038}, + {14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697}}, + {{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875}, + {-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905}, + {-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656}}, + {{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818}, + {27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714}, + {10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203}}, + {{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931}, + {-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024}, + {-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084}}, + {{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204}, + {20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817}, + {27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667}} + }, { + {{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504}, + {-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768}, + {-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255}}, + {{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790}, + {1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438}, + {-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333}}, + {{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971}, + {31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905}, + {29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409}}, + {{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409}, + {6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499}, + {-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363}}, + {{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664}, + {-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324}, + {-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940}}, + {{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990}, + {-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914}, + {-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290}}, + {{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257}, + {-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433}, + {-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236}}, + {{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045}, + {11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093}, + {-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347}} + }, { + {{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191}, + {-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507}, + {-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906}}, + {{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018}, + {-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109}, + {-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926}}, + {{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528}, + {8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625}, + {-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286}}, + {{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033}, + {27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866}, + {21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896}}, + {{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075}, + {26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347}, + {-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437}}, + {{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165}, + {-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588}, + {-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193}}, + {{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017}, + {-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883}, + {21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961}}, + {{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043}, + {29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663}, + {-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362}} + }, { + {{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860}, + {2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466}, + {-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063}}, + {{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997}, + {-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295}, + {-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369}}, + {{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385}, + {18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109}, + {2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906}}, + {{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424}, + {-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185}, + {7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962}}, + {{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325}, + {10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593}, + {696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404}}, + {{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644}, + {17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801}, + {26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804}}, + {{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884}, + {-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577}, + {-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849}}, + {{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473}, + {-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644}, + {-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319}} + }, { + {{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599}, + {-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768}, + {-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084}}, + {{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328}, + {-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369}, + {20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920}}, + {{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815}, + {-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025}, + {-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397}}, + {{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448}, + {6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981}, + {30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165}}, + {{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501}, + {17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073}, + {-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861}}, + {{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845}, + {-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211}, + {18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870}}, + {{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096}, + {33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803}, + {-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168}}, + {{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965}, + {-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505}, + {18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598}} + }, { + {{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782}, + {5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900}, + {-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479}}, + {{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208}, + {8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232}, + {17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719}}, + {{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271}, + {-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326}, + {-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132}}, + {{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300}, + {8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570}, + {15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670}}, + {{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994}, + {-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913}, + {31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317}}, + {{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730}, + {842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096}, + {-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078}}, + {{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411}, + {-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905}, + {-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654}}, + {{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870}, + {-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498}, + {12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579}} + }, { + {{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677}, + {10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647}, + {-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743}}, + {{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468}, + {21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375}, + {-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155}}, + {{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725}, + {-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612}, + {-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943}}, + {{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944}, + {30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928}, + {9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406}}, + {{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139}, + {-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963}, + {-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693}}, + {{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734}, + {-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680}, + {-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410}}, + {{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931}, + {-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654}, + {22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710}}, + {{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180}, + {-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684}, + {-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895}} + }, { + {{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501}, + {-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413}, + {6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880}}, + {{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874}, + {22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962}, + {-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899}}, + {{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152}, + {9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063}, + {7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080}}, + {{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146}, + {-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183}, + {-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133}}, + {{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421}, + {-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622}, + {-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197}}, + {{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663}, + {31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753}, + {4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755}}, + {{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862}, + {-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118}, + {26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171}}, + {{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380}, + {16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824}, + {28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270}} + }, { + {{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438}, + {-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584}, + {-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562}}, + {{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471}, + {18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610}, + {19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269}}, + {{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650}, + {14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369}, + {19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461}}, + {{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462}, + {-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793}, + {-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218}}, + {{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226}, + {18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019}, + {-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037}}, + {{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171}, + {-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132}, + {-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841}}, + {{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181}, + {-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210}, + {-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040}}, + {{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935}, + {24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105}, + {-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814}} + }, { + {{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852}, + {5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581}, + {-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646}}, + {{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844}, + {10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025}, + {27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453}}, + {{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068}, + {4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192}, + {-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921}}, + {{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259}, + {-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426}, + {-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072}}, + {{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305}, + {13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832}, + {28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943}}, + {{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011}, + {24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447}, + {17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494}}, + {{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245}, + {-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859}, + {28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915}}, + {{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707}, + {10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848}, + {-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224}} + }, { + {{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391}, + {15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215}, + {-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101}}, + {{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713}, + {21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849}, + {-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930}}, + {{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940}, + {-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031}, + {-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404}}, + {{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243}, + {-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116}, + {-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525}}, + {{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509}, + {-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883}, + {15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865}}, + {{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660}, + {4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273}, + {-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138}}, + {{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560}, + {-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135}, + {2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941}}, + {{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739}, + {18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756}, + {-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819}} + }, { + {{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347}, + {-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028}, + {21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075}}, + {{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799}, + {-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609}, + {-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817}}, + {{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989}, + {-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523}, + {4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278}}, + {{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045}, + {19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377}, + {24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480}}, + {{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016}, + {510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426}, + {18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525}}, + {{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396}, + {9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080}, + {12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892}}, + {{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275}, + {11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074}, + {20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140}}, + {{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717}, + {-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101}, + {24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127}} + }, { + {{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632}, + {-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415}, + {-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160}}, + {{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876}, + {22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625}, + {-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478}}, + {{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164}, + {26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595}, + {-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248}}, + {{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858}, + {15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193}, + {8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184}}, + {{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942}, + {-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635}, + {21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948}}, + {{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935}, + {-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415}, + {-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416}}, + {{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018}, + {4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778}, + {366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659}}, + {{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385}, + {18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503}, + {476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329}} + }, { + {{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056}, + {-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838}, + {24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948}}, + {{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691}, + {-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118}, + {-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517}}, + {{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269}, + {-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904}, + {-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589}}, + {{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193}, + {-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910}, + {-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930}}, + {{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667}, + {25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481}, + {-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876}}, + {{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640}, + {-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278}, + {-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112}}, + {{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272}, + {17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012}, + {-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221}}, + {{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046}, + {13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345}, + {-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310}} + }, { + {{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937}, + {31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636}, + {-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008}}, + {{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429}, + {-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576}, + {31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066}}, + {{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490}, + {-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104}, + {33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053}}, + {{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275}, + {-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511}, + {22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095}}, + {{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439}, + {23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939}, + {-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424}}, + {{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310}, + {3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608}, + {-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079}}, + {{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101}, + {21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418}, + {18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576}}, + {{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356}, + {9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996}, + {-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099}} + }, { + {{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728}, + {-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658}, + {-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242}}, + {{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001}, + {-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766}, + {18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373}}, + {{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458}, + {-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628}, + {-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657}}, + {{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062}, + {25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616}, + {31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014}}, + {{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383}, + {-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814}, + {-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718}}, + {{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417}, + {2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222}, + {33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444}}, + {{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597}, + {23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970}, + {1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799}}, + {{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647}, + {13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511}, + {-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032}} + }, { + {{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834}, + {-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461}, + {29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062}}, + {{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516}, + {-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547}, + {-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240}}, + {{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038}, + {-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741}, + {16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103}}, + {{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747}, + {-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323}, + {31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016}}, + {{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373}, + {15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228}, + {-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141}}, + {{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399}, + {11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831}, + {-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376}}, + {{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313}, + {-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958}, + {-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577}}, + {{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743}, + {29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684}, + {-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476}} + } +}; + +const ge_precomp ge_Bi[8] = { + {{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, + {-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, + {-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}}, {{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, + {16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, + {30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}}, {{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, + {4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, + {19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}}, {{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, + {-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, + {28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}}, {{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877}, + {-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951}, + {4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784}}, {{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436}, + {25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918}, + {23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877}}, {{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800}, + {-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305}, + {-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300}}, {{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876}, + {-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619}, + {-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683}} +}; + +/* A = 2 * (1 - d) / (1 + d) = 486662 */ +const fe fe_ma2 = {-12721188, -3529, 0, 0, 0, 0, 0, 0, 0, 0}; /* -A^2 */ +const fe fe_ma = {-486662, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* -A */ +const fe fe_fffb1 = {-31702527, -2466483, -26106795, -12203692, -12169197, -321052, 14850977, -10296299, -16929438, -407568}; /* sqrt(-2 * A * (A + 2)) */ +const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -30876704, -6368709, 10503587, -13363080}; /* sqrt(2 * A * (A + 2)) */ +const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ +const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ diff --git a/src/crypto/bernstein/crypto-ops-data.h b/src/crypto/bernstein/crypto-ops-data.h new file mode 100644 index 00000000..a4b64304 --- /dev/null +++ b/src/crypto/bernstein/crypto-ops-data.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#include "crypto-ops.h" +#if defined(__cplusplus) +namespace crypto { extern "C" { +#endif + + +/* From ge_double_scalarmult.c, modified */ +extern const ge_precomp ge_Bi[8]; + +/* From ge_frombytes.c, modified */ + +extern const fe fe_sqrtm1; +extern const fe fe_d; + +/* From ge_p3_to_cached.c */ + +extern const fe fe_d2; + +/* From ge_scalarmult_base.c */ + +extern const ge_precomp ge_base[32][8]; + +/* New code */ + +extern const fe fe_ma2; +extern const fe fe_ma; +extern const fe fe_fffb1; +extern const fe fe_fffb2; +extern const fe fe_fffb3; +extern const fe fe_fffb4; + +#if defined(__cplusplus) +}} +#endif diff --git a/src/crypto/bernstein/crypto-ops.c b/src/crypto/bernstein/crypto-ops.c new file mode 100644 index 00000000..b9a5ed88 --- /dev/null +++ b/src/crypto/bernstein/crypto-ops.c @@ -0,0 +1,3461 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include + +#include "crypto-ops.h" +#include "crypto-ops-data.h" + +/* Predeclarations */ + +static void fe_mul(fe, const fe, const fe); +static void fe_sq(fe, const fe); +static void fe_tobytes(unsigned char *, const fe); +static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *); +static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *); +static void ge_p2_0(ge_p2 *); +static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *); +static void fe_divpowm1(fe, const fe, const fe); + +/* Common functions */ + +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + return result; +} + +static uint64_t load_4(const unsigned char *in) +{ + uint64_t result; + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + return result; +} + +/* From fe_0.c */ + +/* +h = 0 +*/ + +static void fe_0(fe h) { + h[0] = 0; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + +/* From fe_1.c */ + +/* +h = 1 +*/ + +static void fe_1(fe h) { + h[0] = 1; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + +/* From fe_add.c */ + +/* +h = f + g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +static void fe_add(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 + g0; + int32_t h1 = f1 + g1; + int32_t h2 = f2 + g2; + int32_t h3 = f3 + g3; + int32_t h4 = f4 + g4; + int32_t h5 = f5 + g5; + int32_t h6 = f6 + g6; + int32_t h7 = f7 + g7; + int32_t h8 = f8 + g8; + int32_t h9 = f9 + g9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + +/* From fe_cmov.c */ + +/* +Replace (f,g) with (g,g) if b == 1; +replace (f,g) with (f,g) if b == 0. + +Preconditions: b in {0,1}. +*/ + +static void fe_cmov(fe f, const fe g, unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + assert((((b - 1) & ~b) | ((b - 2) & ~(b - 1))) == (unsigned int) -1); + b = -(int) b; + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} + +/* From fe_copy.c */ + +/* +h = f +*/ + +static void fe_copy(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} + +/* From fe_invert.c */ + +static void fe_invert(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + fe t3; + int i; + + fe_sq(t0, z); + fe_sq(t1, t0); + fe_sq(t1, t1); + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t2, t0); + fe_mul(t1, t1, t2); + fe_sq(t2, t1); + for (i = 0; i < 4; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + for (i = 0; i < 9; ++i) { + fe_sq(t2, t2); + } + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + for (i = 0; i < 19; ++i) { + fe_sq(t3, t3); + } + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + for (i = 0; i < 9; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + for (i = 0; i < 49; ++i) { + fe_sq(t2, t2); + } + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + for (i = 0; i < 99; ++i) { + fe_sq(t3, t3); + } + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + for (i = 0; i < 49; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + for (i = 0; i < 4; ++i) { + fe_sq(t1, t1); + } + fe_mul(out, t1, t0); + + return; +} + +/* From fe_isnegative.c */ + +/* +return 1 if f is in {1,3,5,...,q-2} +return 0 if f is in {0,2,4,...,q-1} + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +static int fe_isnegative(const fe f) { + unsigned char s[32]; + fe_tobytes(s, f); + return s[0] & 1; +} + +/* From fe_isnonzero.c, modified */ + +static int fe_isnonzero(const fe f) { + unsigned char s[32]; + fe_tobytes(s, f); + return (((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | + s[9] | s[10] | s[11] | s[12] | s[13] | s[14] | s[15] | s[16] | s[17] | + s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | + s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; +} + +/* From fe_mul.c */ + +/* +h = f * g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +Notes on implementation strategy: + +Using schoolbook multiplication. +Karatsuba would save a little in some cost models. + +Most multiplications by 2 and 19 are 32-bit precomputations; +cheaper than 64-bit postcomputations. + +There is one remaining multiplication by 19 in the carry chain; +one *19 precomputation can be merged into this, +but the resulting data flow is considerably less clean. + +There are 12 carries below. +10 of them are 2-way parallelizable and vectorizable. +Can get away with 11 carries, but then data flow is much deeper. + +With tighter constraints on inputs can squeeze carries into int32. +*/ + +static void fe_mul(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ + int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + int32_t g3_19 = 19 * g3; + int32_t g4_19 = 19 * g4; + int32_t g5_19 = 19 * g5; + int32_t g6_19 = 19 * g6; + int32_t g7_19 = 19 * g7; + int32_t g8_19 = 19 * g8; + int32_t g9_19 = 19 * g9; + int32_t f1_2 = 2 * f1; + int32_t f3_2 = 2 * f3; + int32_t f5_2 = 2 * f5; + int32_t f7_2 = 2 * f7; + int32_t f9_2 = 2 * f9; + int64_t f0g0 = f0 * (int64_t) g0; + int64_t f0g1 = f0 * (int64_t) g1; + int64_t f0g2 = f0 * (int64_t) g2; + int64_t f0g3 = f0 * (int64_t) g3; + int64_t f0g4 = f0 * (int64_t) g4; + int64_t f0g5 = f0 * (int64_t) g5; + int64_t f0g6 = f0 * (int64_t) g6; + int64_t f0g7 = f0 * (int64_t) g7; + int64_t f0g8 = f0 * (int64_t) g8; + int64_t f0g9 = f0 * (int64_t) g9; + int64_t f1g0 = f1 * (int64_t) g0; + int64_t f1g1_2 = f1_2 * (int64_t) g1; + int64_t f1g2 = f1 * (int64_t) g2; + int64_t f1g3_2 = f1_2 * (int64_t) g3; + int64_t f1g4 = f1 * (int64_t) g4; + int64_t f1g5_2 = f1_2 * (int64_t) g5; + int64_t f1g6 = f1 * (int64_t) g6; + int64_t f1g7_2 = f1_2 * (int64_t) g7; + int64_t f1g8 = f1 * (int64_t) g8; + int64_t f1g9_38 = f1_2 * (int64_t) g9_19; + int64_t f2g0 = f2 * (int64_t) g0; + int64_t f2g1 = f2 * (int64_t) g1; + int64_t f2g2 = f2 * (int64_t) g2; + int64_t f2g3 = f2 * (int64_t) g3; + int64_t f2g4 = f2 * (int64_t) g4; + int64_t f2g5 = f2 * (int64_t) g5; + int64_t f2g6 = f2 * (int64_t) g6; + int64_t f2g7 = f2 * (int64_t) g7; + int64_t f2g8_19 = f2 * (int64_t) g8_19; + int64_t f2g9_19 = f2 * (int64_t) g9_19; + int64_t f3g0 = f3 * (int64_t) g0; + int64_t f3g1_2 = f3_2 * (int64_t) g1; + int64_t f3g2 = f3 * (int64_t) g2; + int64_t f3g3_2 = f3_2 * (int64_t) g3; + int64_t f3g4 = f3 * (int64_t) g4; + int64_t f3g5_2 = f3_2 * (int64_t) g5; + int64_t f3g6 = f3 * (int64_t) g6; + int64_t f3g7_38 = f3_2 * (int64_t) g7_19; + int64_t f3g8_19 = f3 * (int64_t) g8_19; + int64_t f3g9_38 = f3_2 * (int64_t) g9_19; + int64_t f4g0 = f4 * (int64_t) g0; + int64_t f4g1 = f4 * (int64_t) g1; + int64_t f4g2 = f4 * (int64_t) g2; + int64_t f4g3 = f4 * (int64_t) g3; + int64_t f4g4 = f4 * (int64_t) g4; + int64_t f4g5 = f4 * (int64_t) g5; + int64_t f4g6_19 = f4 * (int64_t) g6_19; + int64_t f4g7_19 = f4 * (int64_t) g7_19; + int64_t f4g8_19 = f4 * (int64_t) g8_19; + int64_t f4g9_19 = f4 * (int64_t) g9_19; + int64_t f5g0 = f5 * (int64_t) g0; + int64_t f5g1_2 = f5_2 * (int64_t) g1; + int64_t f5g2 = f5 * (int64_t) g2; + int64_t f5g3_2 = f5_2 * (int64_t) g3; + int64_t f5g4 = f5 * (int64_t) g4; + int64_t f5g5_38 = f5_2 * (int64_t) g5_19; + int64_t f5g6_19 = f5 * (int64_t) g6_19; + int64_t f5g7_38 = f5_2 * (int64_t) g7_19; + int64_t f5g8_19 = f5 * (int64_t) g8_19; + int64_t f5g9_38 = f5_2 * (int64_t) g9_19; + int64_t f6g0 = f6 * (int64_t) g0; + int64_t f6g1 = f6 * (int64_t) g1; + int64_t f6g2 = f6 * (int64_t) g2; + int64_t f6g3 = f6 * (int64_t) g3; + int64_t f6g4_19 = f6 * (int64_t) g4_19; + int64_t f6g5_19 = f6 * (int64_t) g5_19; + int64_t f6g6_19 = f6 * (int64_t) g6_19; + int64_t f6g7_19 = f6 * (int64_t) g7_19; + int64_t f6g8_19 = f6 * (int64_t) g8_19; + int64_t f6g9_19 = f6 * (int64_t) g9_19; + int64_t f7g0 = f7 * (int64_t) g0; + int64_t f7g1_2 = f7_2 * (int64_t) g1; + int64_t f7g2 = f7 * (int64_t) g2; + int64_t f7g3_38 = f7_2 * (int64_t) g3_19; + int64_t f7g4_19 = f7 * (int64_t) g4_19; + int64_t f7g5_38 = f7_2 * (int64_t) g5_19; + int64_t f7g6_19 = f7 * (int64_t) g6_19; + int64_t f7g7_38 = f7_2 * (int64_t) g7_19; + int64_t f7g8_19 = f7 * (int64_t) g8_19; + int64_t f7g9_38 = f7_2 * (int64_t) g9_19; + int64_t f8g0 = f8 * (int64_t) g0; + int64_t f8g1 = f8 * (int64_t) g1; + int64_t f8g2_19 = f8 * (int64_t) g2_19; + int64_t f8g3_19 = f8 * (int64_t) g3_19; + int64_t f8g4_19 = f8 * (int64_t) g4_19; + int64_t f8g5_19 = f8 * (int64_t) g5_19; + int64_t f8g6_19 = f8 * (int64_t) g6_19; + int64_t f8g7_19 = f8 * (int64_t) g7_19; + int64_t f8g8_19 = f8 * (int64_t) g8_19; + int64_t f8g9_19 = f8 * (int64_t) g9_19; + int64_t f9g0 = f9 * (int64_t) g0; + int64_t f9g1_38 = f9_2 * (int64_t) g1_19; + int64_t f9g2_19 = f9 * (int64_t) g2_19; + int64_t f9g3_38 = f9_2 * (int64_t) g3_19; + int64_t f9g4_19 = f9 * (int64_t) g4_19; + int64_t f9g5_38 = f9_2 * (int64_t) g5_19; + int64_t f9g6_19 = f9 * (int64_t) g6_19; + int64_t f9g7_38 = f9_2 * (int64_t) g7_19; + int64_t f9g8_19 = f9 * (int64_t) g8_19; + int64_t f9g9_38 = f9_2 * (int64_t) g9_19; + int64_t h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38; + int64_t h1 = f0g1+f1g0 +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19; + int64_t h2 = f0g2+f1g1_2 +f2g0 +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38; + int64_t h3 = f0g3+f1g2 +f2g1 +f3g0 +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19; + int64_t h4 = f0g4+f1g3_2 +f2g2 +f3g1_2 +f4g0 +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38; + int64_t h5 = f0g5+f1g4 +f2g3 +f3g2 +f4g1 +f5g0 +f6g9_19+f7g8_19+f8g7_19+f9g6_19; + int64_t h6 = f0g6+f1g5_2 +f2g4 +f3g3_2 +f4g2 +f5g1_2 +f6g0 +f7g9_38+f8g8_19+f9g7_38; + int64_t h7 = f0g7+f1g6 +f2g5 +f3g4 +f4g3 +f5g2 +f6g1 +f7g0 +f8g9_19+f9g8_19; + int64_t h8 = f0g8+f1g7_2 +f2g6 +f3g5_2 +f4g4 +f5g3_2 +f6g2 +f7g1_2 +f8g0 +f9g9_38; + int64_t h9 = f0g9+f1g8 +f2g7 +f3g6 +f4g5 +f5g4 +f6g3 +f7g2 +f8g1 +f9g0 ; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + /* + |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38)) + i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8 + |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19)) + i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9 + */ + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + /* |h0| <= 2^25 */ + /* |h4| <= 2^25 */ + /* |h1| <= 1.71*2^59 */ + /* |h5| <= 1.71*2^59 */ + + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + /* |h1| <= 2^24; from now on fits into int32 */ + /* |h5| <= 2^24; from now on fits into int32 */ + /* |h2| <= 1.41*2^60 */ + /* |h6| <= 1.41*2^60 */ + + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + /* |h2| <= 2^25; from now on fits into int32 unchanged */ + /* |h6| <= 2^25; from now on fits into int32 unchanged */ + /* |h3| <= 1.71*2^59 */ + /* |h7| <= 1.71*2^59 */ + + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + /* |h3| <= 2^24; from now on fits into int32 unchanged */ + /* |h7| <= 2^24; from now on fits into int32 unchanged */ + /* |h4| <= 1.72*2^34 */ + /* |h8| <= 1.41*2^60 */ + + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + /* |h4| <= 2^25; from now on fits into int32 unchanged */ + /* |h8| <= 2^25; from now on fits into int32 unchanged */ + /* |h5| <= 1.01*2^24 */ + /* |h9| <= 1.71*2^59 */ + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + /* |h9| <= 2^24; from now on fits into int32 unchanged */ + /* |h0| <= 1.1*2^39 */ + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + /* |h0| <= 2^25; from now on fits into int32 unchanged */ + /* |h1| <= 1.01*2^24 */ + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +/* From fe_neg.c */ + +/* +h = -f + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +static void fe_neg(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t h0 = -f0; + int32_t h1 = -f1; + int32_t h2 = -f2; + int32_t h3 = -f3; + int32_t h4 = -f4; + int32_t h5 = -f5; + int32_t h6 = -f6; + int32_t h7 = -f7; + int32_t h8 = -f8; + int32_t h9 = -f9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + +/* From fe_sq.c */ + +/* +h = f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +static void fe_sq(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; + int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; + int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; + int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; + int64_t h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; + int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; + int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; + int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; + int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; + int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +/* From fe_sq2.c */ + +/* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +static void fe_sq2(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; + int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; + int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; + int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; + int64_t h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; + int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; + int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; + int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; + int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; + int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +/* From fe_sub.c */ + +/* +h = f - g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +static void fe_sub(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 - g0; + int32_t h1 = f1 - g1; + int32_t h2 = f2 - g2; + int32_t h3 = f3 - g3; + int32_t h4 = f4 - g4; + int32_t h5 = f5 - g5; + int32_t h6 = f6 - g6; + int32_t h7 = f7 - g7; + int32_t h8 = f8 - g8; + int32_t h9 = f9 - g9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + +/* From fe_tobytes.c */ + +/* +Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Write p=2^255-19; q=floor(h/p). +Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + +Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + + carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; + carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; + carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; + carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; + carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; + carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; + carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; + carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; + carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; + carry9 = h9 >> 25; h9 -= carry9 << 25; + /* h10 = carry9 */ + + /* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + Goal: Output h0+...+2^230 h9. + */ + + s[0] = h0 >> 0; + s[1] = h0 >> 8; + s[2] = h0 >> 16; + s[3] = (h0 >> 24) | (h1 << 2); + s[4] = h1 >> 6; + s[5] = h1 >> 14; + s[6] = (h1 >> 22) | (h2 << 3); + s[7] = h2 >> 5; + s[8] = h2 >> 13; + s[9] = (h2 >> 21) | (h3 << 5); + s[10] = h3 >> 3; + s[11] = h3 >> 11; + s[12] = (h3 >> 19) | (h4 << 6); + s[13] = h4 >> 2; + s[14] = h4 >> 10; + s[15] = h4 >> 18; + s[16] = h5 >> 0; + s[17] = h5 >> 8; + s[18] = h5 >> 16; + s[19] = (h5 >> 24) | (h6 << 1); + s[20] = h6 >> 7; + s[21] = h6 >> 15; + s[22] = (h6 >> 23) | (h7 << 3); + s[23] = h7 >> 5; + s[24] = h7 >> 13; + s[25] = (h7 >> 21) | (h8 << 4); + s[26] = h8 >> 4; + s[27] = h8 >> 12; + s[28] = (h8 >> 20) | (h9 << 6); + s[29] = h9 >> 2; + s[30] = h9 >> 10; + s[31] = h9 >> 18; +} + +/* From ge_add.c */ + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YplusX); + fe_mul(r->Y, r->Y, q->YminusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + +/* From ge_double_scalarmult.c, modified */ + +static void slide(signed char *r, const unsigned char *a) { + int i; + int b; + int k; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + + for (i = 0; i < 256; ++i) { + if (r[i]) { + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } else + break; + } + } + } + } +} + +void ge_dsm_precomp(ge_dsmp r, const ge_p3 *s) { + ge_p1p1 t; + ge_p3 s2, u; + ge_p3_to_cached(&r[0], s); + ge_p3_dbl(&t, s); ge_p1p1_to_p3(&s2, &t); + ge_add(&t, &s2, &r[0]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&r[1], &u); + ge_add(&t, &s2, &r[1]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&r[2], &u); + ge_add(&t, &s2, &r[2]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&r[3], &u); + ge_add(&t, &s2, &r[3]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&r[4], &u); + ge_add(&t, &s2, &r[4]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&r[5], &u); + ge_add(&t, &s2, &r[5]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&r[6], &u); + ge_add(&t, &s2, &r[6]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&r[7], &u); +} + +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_base_vartime(ge_p2 *r, const struct EllipticCurveScalar *aa, const ge_p3 *A, const struct EllipticCurveScalar *bb) { + const unsigned char * a = aa->data; + const unsigned char * b = bb->data; + signed char aslide[256]; + signed char bslide[256]; + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + ge_p1p1 t; + ge_p3 u; + int i; + + slide(aslide, a); + slide(bslide, b); + ge_dsm_precomp(Ai, A); + + ge_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) break; + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i]/2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i])/2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &ge_Bi[bslide[i]/2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &ge_Bi[(-bslide[i])/2]); + } + + ge_p1p1_to_p2(r, &t); + } +} + +/* From ge_frombytes.c, modified */ + +int ge_frombytes_vartime(ge_p3 *h, const struct EllipticCurvePoint *ss) { + const unsigned char * s = ss->data; + fe u; + fe v; + fe vxx; + fe check; + + /* From fe_frombytes.c */ + + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = (load_3(s + 29) & 8388607) << 2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + /* Validate the number to be canonical */ + if (h9 == 33554428 && h8 == 268435440 && h7 == 536870880 && h6 == 2147483520 && + h5 == 4294967295 && h4 == 67108860 && h3 == 134217720 && h2 == 536870880 && + h1 == 1073741760 && h0 >= 4294967277) { + return -1; + } + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h->Y[0] = (int32_t) h0; + h->Y[1] = (int32_t) h1; + h->Y[2] = (int32_t) h2; + h->Y[3] = (int32_t) h3; + h->Y[4] = (int32_t) h4; + h->Y[5] = (int32_t) h5; + h->Y[6] = (int32_t) h6; + h->Y[7] = (int32_t) h7; + h->Y[8] = (int32_t) h8; + h->Y[9] = (int32_t) h9; + + /* End fe_frombytes.c */ + + fe_1(h->Z); + fe_sq(u, h->Y); + fe_mul(v, u, fe_d); + fe_sub(u, u, h->Z); /* u = y^2-1 */ + fe_add(v, v, h->Z); /* v = dy^2+1 */ + + fe_divpowm1(h->X, u, v); /* x = uv^3(uv^7)^((q-5)/8) */ + + fe_sq(vxx, h->X); + fe_mul(vxx, vxx, v); + fe_sub(check, vxx, u); /* vx^2-u */ + if (fe_isnonzero(check)) { + fe_add(check, vxx, u); /* vx^2+u */ + if (fe_isnonzero(check)) { + return -1; + } + fe_mul(h->X, h->X, fe_sqrtm1); + } + + if (fe_isnegative(h->X) != (s[31] >> 7)) { + /* If x = 0, the sign must be positive */ + if (!fe_isnonzero(h->X)) { + return -1; + } + fe_neg(h->X, h->X); + } + + fe_mul(h->T, h->X, h->Y); + return 0; +} + +/* From ge_madd.c */ + +/* +r = p + q +*/ + +static void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yplusx); + fe_mul(r->Y, r->Y, q->yminusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + +/* From ge_msub.c */ + +/* +r = p - q +*/ + +static void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yminusx); + fe_mul(r->Y, r->Y, q->yplusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + +/* From ge_p1p1_to_p2.c */ + +/* +r = p +*/ + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); +} + +/* From ge_p1p1_to_p3.c */ + +/* +r = p +*/ + +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); + fe_mul(r->T, p->X, p->Y); +} + +/* From ge_p2_0.c */ + +static void ge_p2_0(ge_p2 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); +} + +/* From ge_p2_dbl.c */ + +/* +r = 2 * p +*/ + +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { + fe t0; + fe_sq(r->X, p->X); + fe_sq(r->Z, p->Y); + fe_sq2(r->T, p->Z); + fe_add(r->Y, p->X, p->Y); + fe_sq(t0, r->Y); + fe_add(r->Y, r->Z, r->X); + fe_sub(r->Z, r->Z, r->X); + fe_sub(r->X, t0, r->Y); + fe_sub(r->T, r->T, r->Z); +} + +/* From ge_p3_0.c */ + +static void ge_p3_0(ge_p3 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); + fe_0(h->T); +} + +/* From ge_p3_dbl.c */ + +/* +r = 2 * p +*/ + +static void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { + ge_p2 q; + ge_p3_to_p2(&q, p); + ge_p2_dbl(r, &q); +} + +/* From ge_p3_to_cached.c */ + +/* +r = p +*/ + +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { + fe_add(r->YplusX, p->Y, p->X); + fe_sub(r->YminusX, p->Y, p->X); + fe_copy(r->Z, p->Z); + fe_mul(r->T2d, p->T, fe_d2); +} + +/* From ge_p3_to_p2.c */ + +/* +r = p +*/ + +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { + fe_copy(r->X, p->X); + fe_copy(r->Y, p->Y); + fe_copy(r->Z, p->Z); +} + +/* From ge_p3_tobytes.c */ + +void ge_p3_tobytes(struct EllipticCurvePoint * ss, const ge_p3 *h) { + unsigned char * s = ss->data; + fe recip; + fe x; + fe y; + + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} + +/* From ge_precomp_0.c */ + +static void ge_precomp_0(ge_precomp *h) { + fe_1(h->yplusx); + fe_1(h->yminusx); + fe_0(h->xy2d); +} + +/* From ge_scalarmult_base.c */ + +static unsigned char equal(signed char b, signed char c) { + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + uint32_t y = x; /* 0: yes; 1..255: no */ + y -= 1; /* 4294967295: yes; 0..254: no */ + y >>= 31; /* 1: yes; 0: no */ + return y; +} + +static unsigned char negative(signed char b) { + unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return (unsigned char) x; +} + +static void ge_precomp_cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) { + fe_cmov(t->yplusx, u->yplusx, b); + fe_cmov(t->yminusx, u->yminusx, b); + fe_cmov(t->xy2d, u->xy2d, b); +} + +static void select(ge_precomp *t, int pos, signed char b) { + ge_precomp minust; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + + ge_precomp_0(t); + ge_precomp_cmov(t, &ge_base[pos][0], equal(babs, 1)); + ge_precomp_cmov(t, &ge_base[pos][1], equal(babs, 2)); + ge_precomp_cmov(t, &ge_base[pos][2], equal(babs, 3)); + ge_precomp_cmov(t, &ge_base[pos][3], equal(babs, 4)); + ge_precomp_cmov(t, &ge_base[pos][4], equal(babs, 5)); + ge_precomp_cmov(t, &ge_base[pos][5], equal(babs, 6)); + ge_precomp_cmov(t, &ge_base[pos][6], equal(babs, 7)); + ge_precomp_cmov(t, &ge_base[pos][7], equal(babs, 8)); + fe_copy(minust.yplusx, t->yminusx); + fe_copy(minust.yminusx, t->yplusx); + fe_neg(minust.xy2d, t->xy2d); + ge_precomp_cmov(t, &minust, bnegative); +} + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + +void ge_scalarmult_base(ge_p3 *h, const struct EllipticCurveScalar *aa) { + const unsigned char * a = aa->data; + signed char e[64]; + signed char carry; + ge_p1p1 r; + ge_p2 s; + ge_precomp t; + int i; + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + + carry = 0; + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + e[63] += carry; + /* each e[i] is between -8 and 8 */ + + ge_p3_0(h); + for (i = 1; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); + } + + ge_p3_dbl(&r, h); ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r); + + for (i = 0; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); + } +} + +/* From ge_sub.c */ + +/* +r = p - q +*/ + +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YminusX); + fe_mul(r->Y, r->Y, q->YplusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + +/* From ge_tobytes.c */ + +void ge_tobytes(struct EllipticCurvePoint *ss, const ge_p2 *h) { + unsigned char * s = ss->data; + fe recip; + fe x; + fe y; + + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} + +/* From sc_reduce.c */ + +/* +Input: + s[0]+256*s[1]+...+256^63*s[63] = s + +Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. +*/ +void sc_reduce(struct EllipticCurveScalar * aa, const unsigned char s[64]) { + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = 2097151 & (load_4(s + 28) >> 7); + int64_t s12 = 2097151 & (load_4(s + 31) >> 4); + int64_t s13 = 2097151 & (load_3(s + 34) >> 1); + int64_t s14 = 2097151 & (load_4(s + 36) >> 6); + int64_t s15 = 2097151 & (load_3(s + 39) >> 3); + int64_t s16 = 2097151 & load_3(s + 42); + int64_t s17 = 2097151 & (load_4(s + 44) >> 5); + int64_t s18 = 2097151 & (load_3(s + 47) >> 2); + int64_t s19 = 2097151 & (load_4(s + 49) >> 7); + int64_t s20 = 2097151 & (load_4(s + 52) >> 4); + int64_t s21 = 2097151 & (load_3(s + 55) >> 1); + int64_t s22 = 2097151 & (load_4(s + 57) >> 6); + int64_t s23 = (load_4(s + 60) >> 3); + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + aa->data[0] = (unsigned char) (s0 >> 0); + aa->data[1] = (unsigned char) (s0 >> 8); + aa->data[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + aa->data[3] = (unsigned char) (s1 >> 3); + aa->data[4] = (unsigned char) (s1 >> 11); + aa->data[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + aa->data[6] = (unsigned char) (s2 >> 6); + aa->data[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + aa->data[8] = (unsigned char) (s3 >> 1); + aa->data[9] = (unsigned char) (s3 >> 9); + aa->data[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + aa->data[11] = (unsigned char) (s4 >> 4); + aa->data[12] = (unsigned char) (s4 >> 12); + aa->data[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + aa->data[14] = (unsigned char) (s5 >> 7); + aa->data[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + aa->data[16] = (unsigned char) (s6 >> 2); + aa->data[17] = (unsigned char) (s6 >> 10); + aa->data[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + aa->data[19] = (unsigned char) (s7 >> 5); + aa->data[20] = (unsigned char) (s7 >> 13); + aa->data[21] = (unsigned char) (s8 >> 0); + aa->data[22] = (unsigned char) (s8 >> 8); + aa->data[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + aa->data[24] = (unsigned char) (s9 >> 3); + aa->data[25] = (unsigned char) (s9 >> 11); + aa->data[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + aa->data[27] = (unsigned char) (s10 >> 6); + aa->data[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + aa->data[29] = (unsigned char) (s11 >> 1); + aa->data[30] = (unsigned char) (s11 >> 9); + aa->data[31] = (unsigned char) (s11 >> 17); +} + +/* New code */ + +static void fe_divpowm1(fe r, const fe u, const fe v) { + fe v3, uv7, t0, t1, t2; + int i; + + fe_sq(v3, v); + fe_mul(v3, v3, v); /* v3 = v^3 */ + fe_sq(uv7, v3); + fe_mul(uv7, uv7, v); + fe_mul(uv7, uv7, u); /* uv7 = uv^7 */ + + /*fe_pow22523(uv7, uv7);*/ + + /* From fe_pow22523.c */ + + fe_sq(t0, uv7); + fe_sq(t1, t0); + fe_sq(t1, t1); + fe_mul(t1, uv7, t1); + fe_mul(t0, t0, t1); + fe_sq(t0, t0); + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + for (i = 0; i < 4; ++i) { + fe_sq(t1, t1); + } + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + for (i = 0; i < 9; ++i) { + fe_sq(t1, t1); + } + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + for (i = 0; i < 19; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + for (i = 0; i < 10; ++i) { + fe_sq(t1, t1); + } + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + for (i = 0; i < 49; ++i) { + fe_sq(t1, t1); + } + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + for (i = 0; i < 99; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + for (i = 0; i < 50; ++i) { + fe_sq(t1, t1); + } + fe_mul(t0, t1, t0); + fe_sq(t0, t0); + fe_sq(t0, t0); + fe_mul(t0, t0, uv7); + + /* End fe_pow22523.c */ + /* t0 = (uv^7)^((q-5)/8) */ + fe_mul(t0, t0, v3); + fe_mul(r, t0, u); /* u^(m+1)v^(-(m+1)) */ +} + +static void ge_cached_0(ge_cached *r) { + fe_1(r->YplusX); + fe_1(r->YminusX); + fe_1(r->Z); + fe_0(r->T2d); +} + +static void ge_cached_cmov(ge_cached *t, const ge_cached *u, unsigned char b) { + fe_cmov(t->YplusX, u->YplusX, b); + fe_cmov(t->YminusX, u->YminusX, b); + fe_cmov(t->Z, u->Z, b); + fe_cmov(t->T2d, u->T2d, b); +} + +/* Assumes that a[31] <= 127 */ +void ge_scalarmult(ge_p2 *r, const struct EllipticCurveScalar *a, const ge_p3 *A) { + signed char e[64]; + int carry, carry2, i; + ge_cached Ai[8]; /* 1 * A, 2 * A, ..., 8 * A */ + ge_p1p1 t; + ge_p3 u; + + carry = 0; /* 0..1 */ + for (i = 0; i < 31; i++) { + carry += a->data[i]; /* 0..256 */ + carry2 = (carry + 8) >> 4; /* 0..16 */ + e[2 * i] = carry - (carry2 << 4); /* -8..7 */ + carry = (carry2 + 8) >> 4; /* 0..1 */ + e[2 * i + 1] = carry2 - (carry << 4); /* -8..7 */ + } + carry += a->data[31]; /* 0..128 */ + carry2 = (carry + 8) >> 4; /* 0..8 */ + e[62] = carry - (carry2 << 4); /* -8..7 */ + e[63] = carry2; /* 0..8 */ + + ge_p3_to_cached(&Ai[0], A); + for (i = 0; i < 7; i++) { + ge_add(&t, A, &Ai[i]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[i + 1], &u); + } + + ge_p2_0(r); + for (i = 63; i >= 0; i--) { + signed char b = e[i]; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + ge_cached cur, minuscur; + ge_p2_dbl(&t, r); + ge_p1p1_to_p2(r, &t); + ge_p2_dbl(&t, r); + ge_p1p1_to_p2(r, &t); + ge_p2_dbl(&t, r); + ge_p1p1_to_p2(r, &t); + ge_p2_dbl(&t, r); + ge_p1p1_to_p3(&u, &t); + ge_cached_0(&cur); + ge_cached_cmov(&cur, &Ai[0], equal(babs, 1)); + ge_cached_cmov(&cur, &Ai[1], equal(babs, 2)); + ge_cached_cmov(&cur, &Ai[2], equal(babs, 3)); + ge_cached_cmov(&cur, &Ai[3], equal(babs, 4)); + ge_cached_cmov(&cur, &Ai[4], equal(babs, 5)); + ge_cached_cmov(&cur, &Ai[5], equal(babs, 6)); + ge_cached_cmov(&cur, &Ai[6], equal(babs, 7)); + ge_cached_cmov(&cur, &Ai[7], equal(babs, 8)); + fe_copy(minuscur.YplusX, cur.YminusX); + fe_copy(minuscur.YminusX, cur.YplusX); + fe_copy(minuscur.Z, cur.Z); + fe_neg(minuscur.T2d, cur.T2d); + ge_cached_cmov(&cur, &minuscur, bnegative); + ge_add(&t, &u, &cur); + ge_p1p1_to_p2(r, &t); + } +} + +void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const struct EllipticCurveScalar *aa, const ge_p3 *A, const struct EllipticCurveScalar *bb, const ge_dsmp Bi) { + const unsigned char * a = aa->data; + const unsigned char * b = bb->data; + signed char aslide[256]; + signed char bslide[256]; + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + ge_p1p1 t; + ge_p3 u; + int i; + + slide(aslide, a); + slide(bslide, b); + ge_dsm_precomp(Ai, A); + + ge_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) break; + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i]/2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i])/2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Bi[bslide[i]/2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Bi[(-bslide[i])/2]); + } + + ge_p1p1_to_p2(r, &t); + } +} + +int ge_check_subgroup_precomp_vartime(const ge_dsmp p) { + ge_p3 s; + ge_p1p1 t; + ge_p2 u; + ge_p3_0(&s); + ge_add(&t, &s, p + 7); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 2); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 3); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 1); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 5); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 1); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 1); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 6); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 5); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 5); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 4); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 1); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 1); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 6); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p + 1); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 1); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 2); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 5); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_sub(&t, &s, p + 2); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p2(&u, &t); + ge_p2_dbl(&t, &u); + ge_p1p1_to_p3(&s, &t); + ge_add(&t, &s, p); + fe_sub(t.Y, t.Y, t.T); + return fe_isnonzero(t.Y); +} + +void ge_mul8(ge_p1p1 *r, const ge_p2 *t) { + ge_p2 u; + ge_p2_dbl(r, t); + ge_p1p1_to_p2(&u, r); + ge_p2_dbl(r, &u); + ge_p1p1_to_p2(&u, r); + ge_p2_dbl(r, &u); +} + +void ge_fromfe_frombytes_vartime(ge_p2 *r, const unsigned char s[32]) { + fe u, v, w, x, y, z; + unsigned char sign; + + /* From fe_frombytes.c */ + + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = load_3(s + 29) << 2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + u[0] = (int32_t) h0; + u[1] = (int32_t) h1; + u[2] = (int32_t) h2; + u[3] = (int32_t) h3; + u[4] = (int32_t) h4; + u[5] = (int32_t) h5; + u[6] = (int32_t) h6; + u[7] = (int32_t) h7; + u[8] = (int32_t) h8; + u[9] = (int32_t) h9; + + /* End fe_frombytes.c */ + + fe_sq2(v, u); /* 2 * u^2 */ + fe_1(w); + fe_add(w, v, w); /* w = 2 * u^2 + 1 */ + fe_sq(x, w); /* w^2 */ + fe_mul(y, fe_ma2, v); /* -2 * A^2 * u^2 */ + fe_add(x, x, y); /* x = w^2 - 2 * A^2 * u^2 */ + fe_divpowm1(r->X, w, x); /* (w / x)^(m + 1) */ + fe_sq(y, r->X); + fe_mul(x, y, x); + fe_sub(y, w, x); + fe_copy(z, fe_ma); + if (fe_isnonzero(y)) { + fe_add(y, w, x); + if (fe_isnonzero(y)) { + goto negative; + } else { + fe_mul(r->X, r->X, fe_fffb1); + } + } else { + fe_mul(r->X, r->X, fe_fffb2); + } + fe_mul(r->X, r->X, u); /* u * sqrt(2 * A * (A + 2) * w / x) */ + fe_mul(z, z, v); /* -2 * A * u^2 */ + sign = 0; + goto setsign; +negative: + fe_mul(x, x, fe_sqrtm1); + fe_sub(y, w, x); + if (fe_isnonzero(y)) { + assert((fe_add(y, w, x), !fe_isnonzero(y))); + fe_mul(r->X, r->X, fe_fffb3); + } else { + fe_mul(r->X, r->X, fe_fffb4); + } + /* r->X = sqrt(A * (A + 2) * w / x) */ + /* z = -A */ + sign = 1; +setsign: + if (fe_isnegative(r->X) != sign) { + assert(fe_isnonzero(r->X)); + fe_neg(r->X, r->X); + } + fe_add(r->Z, z, w); + fe_sub(r->Y, z, w); + fe_mul(r->X, r->X, r->Z); +#if !defined(NDEBUG) + { + fe check_x, check_y, check_iz, check_v; + fe_invert(check_iz, r->Z); + fe_mul(check_x, r->X, check_iz); + fe_mul(check_y, r->Y, check_iz); + fe_sq(check_x, check_x); + fe_sq(check_y, check_y); + fe_mul(check_v, check_x, check_y); + fe_mul(check_v, fe_d, check_v); + fe_add(check_v, check_v, check_x); + fe_sub(check_v, check_v, check_y); + fe_1(check_x); + fe_add(check_v, check_v, check_x); + assert(!fe_isnonzero(check_v)); + } +#endif +} + +void sc_0(struct EllipticCurveScalar *s) { + int i; + for (i = 0; i < 32; i++) { + s->data[i] = 0; + } +} + +void sc_reduce32(struct EllipticCurveScalar * aa, const unsigned char s[32]) { + unsigned char * a = aa->data; + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = (load_4(s + 28) >> 7); + int64_t s12 = 0; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + a[0] = (unsigned char) (s0 >> 0); + a[1] = (unsigned char) (s0 >> 8); + a[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + a[3] = (unsigned char) (s1 >> 3); + a[4] = (unsigned char) (s1 >> 11); + a[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + a[6] = (unsigned char) (s2 >> 6); + a[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + a[8] = (unsigned char) (s3 >> 1); + a[9] = (unsigned char) (s3 >> 9); + a[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + a[11] = (unsigned char) (s4 >> 4); + a[12] = (unsigned char) (s4 >> 12); + a[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + a[14] = (unsigned char) (s5 >> 7); + a[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + a[16] = (unsigned char) (s6 >> 2); + a[17] = (unsigned char) (s6 >> 10); + a[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + a[19] = (unsigned char) (s7 >> 5); + a[20] = (unsigned char) (s7 >> 13); + a[21] = (unsigned char) (s8 >> 0); + a[22] = (unsigned char) (s8 >> 8); + a[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + a[24] = (unsigned char) (s9 >> 3); + a[25] = (unsigned char) (s9 >> 11); + a[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + a[27] = (unsigned char) (s10 >> 6); + a[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + a[29] = (unsigned char) (s11 >> 1); + a[30] = (unsigned char) (s11 >> 9); + a[31] = (unsigned char) (s11 >> 17); +} + +void sc_add(struct EllipticCurveScalar *s, const struct EllipticCurveScalar *a, const struct EllipticCurveScalar *b) { + int64_t a0 = 2097151 & load_3(a->data); + int64_t a1 = 2097151 & (load_4(a->data + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a->data + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a->data + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a->data + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a->data + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a->data + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a->data + 18) >> 3); + int64_t a8 = 2097151 & load_3(a->data + 21); + int64_t a9 = 2097151 & (load_4(a->data + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a->data + 26) >> 2); + int64_t a11 = (load_4(a->data + 28) >> 7); + int64_t b0 = 2097151 & load_3(b->data); + int64_t b1 = 2097151 & (load_4(b->data + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b->data + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b->data + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b->data + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b->data + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b->data + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b->data + 18) >> 3); + int64_t b8 = 2097151 & load_3(b->data + 21); + int64_t b9 = 2097151 & (load_4(b->data + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b->data + 26) >> 2); + int64_t b11 = (load_4(b->data + 28) >> 7); + int64_t s0 = a0 + b0; + int64_t s1 = a1 + b1; + int64_t s2 = a2 + b2; + int64_t s3 = a3 + b3; + int64_t s4 = a4 + b4; + int64_t s5 = a5 + b5; + int64_t s6 = a6 + b6; + int64_t s7 = a7 + b7; + int64_t s8 = a8 + b8; + int64_t s9 = a9 + b9; + int64_t s10 = a10 + b10; + int64_t s11 = a11 + b11; + int64_t s12 = 0; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s->data[0] = (unsigned char) (s0 >> 0); + s->data[1] = (unsigned char) (s0 >> 8); + s->data[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s->data[3] = (unsigned char) (s1 >> 3); + s->data[4] = (unsigned char) (s1 >> 11); + s->data[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s->data[6] = (unsigned char) (s2 >> 6); + s->data[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s->data[8] = (unsigned char) (s3 >> 1); + s->data[9] = (unsigned char) (s3 >> 9); + s->data[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s->data[11] = (unsigned char) (s4 >> 4); + s->data[12] = (unsigned char) (s4 >> 12); + s->data[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s->data[14] = (unsigned char) (s5 >> 7); + s->data[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s->data[16] = (unsigned char) (s6 >> 2); + s->data[17] = (unsigned char) (s6 >> 10); + s->data[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s->data[19] = (unsigned char) (s7 >> 5); + s->data[20] = (unsigned char) (s7 >> 13); + s->data[21] = (unsigned char) (s8 >> 0); + s->data[22] = (unsigned char) (s8 >> 8); + s->data[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s->data[24] = (unsigned char) (s9 >> 3); + s->data[25] = (unsigned char) (s9 >> 11); + s->data[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s->data[27] = (unsigned char) (s10 >> 6); + s->data[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s->data[29] = (unsigned char) (s11 >> 1); + s->data[30] = (unsigned char) (s11 >> 9); + s->data[31] = (unsigned char) (s11 >> 17); +} + +void sc_sub(struct EllipticCurveScalar *s, const struct EllipticCurveScalar *a, const struct EllipticCurveScalar *b) { + int64_t a0 = 2097151 & load_3(a->data); + int64_t a1 = 2097151 & (load_4(a->data + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a->data + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a->data + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a->data + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a->data + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a->data + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a->data + 18) >> 3); + int64_t a8 = 2097151 & load_3(a->data + 21); + int64_t a9 = 2097151 & (load_4(a->data + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a->data + 26) >> 2); + int64_t a11 = (load_4(a->data + 28) >> 7); + int64_t b0 = 2097151 & load_3(b->data); + int64_t b1 = 2097151 & (load_4(b->data + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b->data + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b->data + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b->data + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b->data + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b->data + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b->data + 18) >> 3); + int64_t b8 = 2097151 & load_3(b->data + 21); + int64_t b9 = 2097151 & (load_4(b->data + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b->data + 26) >> 2); + int64_t b11 = (load_4(b->data + 28) >> 7); + int64_t s0 = a0 - b0; + int64_t s1 = a1 - b1; + int64_t s2 = a2 - b2; + int64_t s3 = a3 - b3; + int64_t s4 = a4 - b4; + int64_t s5 = a5 - b5; + int64_t s6 = a6 - b6; + int64_t s7 = a7 - b7; + int64_t s8 = a8 - b8; + int64_t s9 = a9 - b9; + int64_t s10 = a10 - b10; + int64_t s11 = a11 - b11; + int64_t s12 = 0; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s->data[0] = (unsigned char) (s0 >> 0); + s->data[1] = (unsigned char) (s0 >> 8); + s->data[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s->data[3] = (unsigned char) (s1 >> 3); + s->data[4] = (unsigned char) (s1 >> 11); + s->data[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s->data[6] = (unsigned char) (s2 >> 6); + s->data[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s->data[8] = (unsigned char) (s3 >> 1); + s->data[9] = (unsigned char) (s3 >> 9); + s->data[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s->data[11] = (unsigned char) (s4 >> 4); + s->data[12] = (unsigned char) (s4 >> 12); + s->data[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s->data[14] = (unsigned char) (s5 >> 7); + s->data[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s->data[16] = (unsigned char) (s6 >> 2); + s->data[17] = (unsigned char) (s6 >> 10); + s->data[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s->data[19] = (unsigned char) (s7 >> 5); + s->data[20] = (unsigned char) (s7 >> 13); + s->data[21] = (unsigned char) (s8 >> 0); + s->data[22] = (unsigned char) (s8 >> 8); + s->data[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s->data[24] = (unsigned char) (s9 >> 3); + s->data[25] = (unsigned char) (s9 >> 11); + s->data[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s->data[27] = (unsigned char) (s10 >> 6); + s->data[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s->data[29] = (unsigned char) (s11 >> 1); + s->data[30] = (unsigned char) (s11 >> 9); + s->data[31] = (unsigned char) (s11 >> 17); +} + +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (c-ab) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_mulsub(struct EllipticCurveScalar *ss, const struct EllipticCurveScalar *aa, const struct EllipticCurveScalar *bb, const struct EllipticCurveScalar *cc) { + unsigned char * s = ss->data; + const unsigned char * a = aa->data; + const unsigned char * b = bb->data; + const unsigned char * c = cc->data; + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 - a0*b0; + s1 = c1 - (a0*b1 + a1*b0); + s2 = c2 - (a0*b2 + a1*b1 + a2*b0); + s3 = c3 - (a0*b3 + a1*b2 + a2*b1 + a3*b0); + s4 = c4 - (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0); + s5 = c5 - (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0); + s6 = c6 - (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0); + s7 = c7 - (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0); + s8 = c8 - (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0); + s9 = c9 - (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0); + s10 = c10 - (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0); + s11 = c11 - (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0); + s12 = -(a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1); + s13 = -(a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2); + s14 = -(a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3); + s15 = -(a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4); + s16 = -(a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5); + s17 = -(a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6); + s18 = -(a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7); + s19 = -(a8*b11 + a9*b10 + a10*b9 + a11*b8); + s20 = -(a9*b11 + a10*b10 + a11*b9); + s21 = -(a10*b11 + a11*b10); + s22 = -a11*b11; + s23 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); +} + +/* Assumes that a != INT64_MIN */ +static int64_t signum(int64_t a) { + return (a >> 63) - ((-a) >> 63); +} + +int sc_isvalid_vartime(const struct EllipticCurveScalar *s) { + int64_t s0 = load_4(s->data); + int64_t s1 = load_4(s->data + 4); + int64_t s2 = load_4(s->data + 8); + int64_t s3 = load_4(s->data + 12); + int64_t s4 = load_4(s->data + 16); + int64_t s5 = load_4(s->data + 20); + int64_t s6 = load_4(s->data + 24); + int64_t s7 = load_4(s->data + 28); + return (int) ((signum(1559614444 - s0) + (signum(1477600026 - s1) << 1) + (signum(2734136534 - s2) << 2) + (signum(350157278 - s3) << 3) + (signum(-s4) << 4) + (signum(-s5) << 5) + (signum(-s6) << 6) + (signum(268435456 - s7) << 7)) >> 8) == 0; + // TODO - remove compariosn +} + +int sc_iszero(const struct EllipticCurveScalar *ss) { + const unsigned char * s = ss->data; + return (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | + s[9] | s[10] | s[11] | s[12] | s[13] | s[14] | s[15] | s[16] | s[17] | + s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | + s[27] | s[28] | s[29] | s[30] | s[31]) == 0; +} diff --git a/src/crypto/bernstein/crypto-ops.h b/src/crypto/bernstein/crypto-ops.h new file mode 100644 index 00000000..fdf105fa --- /dev/null +++ b/src/crypto/bernstein/crypto-ops.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#include "c_types.h" +#include +#if defined(__cplusplus) +namespace crypto { extern "C" { +#endif + +/* From fe.h */ + +typedef int32_t fe[10]; + +/* From ge.h */ + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +/* From ge_add.c */ + +void ge_add(ge_p1p1 *, const ge_p3 *, const ge_cached *); + +/* From ge_double_scalarmult.c, modified */ + +typedef ge_cached ge_dsmp[8]; +void ge_dsm_precomp(ge_dsmp r, const ge_p3 *s); +void ge_double_scalarmult_base_vartime(ge_p2 *, const struct EllipticCurveScalar *, const ge_p3 *, const struct EllipticCurveScalar *); + +/* From ge_frombytes.c, modified */ + +int ge_frombytes_vartime(ge_p3 *, const struct EllipticCurvePoint *); + +/* From ge_p1p1_to_p2.c */ + +void ge_p1p1_to_p2(ge_p2 *, const ge_p1p1 *); + +/* From ge_p1p1_to_p3.c */ + +void ge_p1p1_to_p3(ge_p3 *, const ge_p1p1 *); + +/* From ge_p2_dbl.c */ + +void ge_p2_dbl(ge_p1p1 *, const ge_p2 *); + +/* From ge_p3_to_cached.c */ + +void ge_p3_to_cached(ge_cached *, const ge_p3 *); + +/* From ge_p3_to_p2.c */ + +void ge_p3_to_p2(ge_p2 *, const ge_p3 *); + +/* From ge_p3_tobytes.c */ + +void ge_p3_tobytes(struct EllipticCurvePoint *, const ge_p3 *); + +/* From ge_scalarmult_base.c */ + +void ge_scalarmult_base(ge_p3 *, const struct EllipticCurveScalar *); + +/* From ge_sub.c */ + +void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *); + +/* From ge_tobytes.c */ + +void ge_tobytes(struct EllipticCurvePoint *, const ge_p2 *); + +/* From sc_reduce.c */ + +void sc_reduce(struct EllipticCurveScalar *, const unsigned char[64]); + +/* New code */ + +void ge_scalarmult(ge_p2 *, const struct EllipticCurveScalar *, const ge_p3 *); +void ge_double_scalarmult_precomp_vartime(ge_p2 *, const struct EllipticCurveScalar *, const ge_p3 *, const struct EllipticCurveScalar *, const ge_dsmp); +int ge_check_subgroup_precomp_vartime(const ge_dsmp); +void ge_mul8(ge_p1p1 *, const ge_p2 *); +void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char[32]); +void sc_0(struct EllipticCurveScalar *); +void sc_reduce32(struct EllipticCurveScalar *, const unsigned char[32]); +void sc_add(struct EllipticCurveScalar *, const struct EllipticCurveScalar *, const struct EllipticCurveScalar *); +void sc_sub(struct EllipticCurveScalar *, const struct EllipticCurveScalar *, const struct EllipticCurveScalar *); +void sc_mulsub(struct EllipticCurveScalar *, const struct EllipticCurveScalar *, const struct EllipticCurveScalar *, const struct EllipticCurveScalar *); +int sc_isvalid_vartime(const struct EllipticCurveScalar *); +int sc_iszero(const struct EllipticCurveScalar *); // Doesn't normalize + +#if defined(__cplusplus) +}} +#endif diff --git a/src/crypto/blake/blake256.c b/src/crypto/blake/blake256.c new file mode 100644 index 00000000..c5d36160 --- /dev/null +++ b/src/crypto/blake/blake256.c @@ -0,0 +1,326 @@ +/* + * The blake256_* and blake224_* functions are largely copied from + * blake256_light.c and blake224_light.c from the BLAKE website: + * + * http://131002.net/blake/ + * + * The hmac_* functions implement HMAC-BLAKE-256 and HMAC-BLAKE-224. + * HMAC is specified by RFC 2104. + */ + +#include +#include +#include +#include "blake256.h" + +#define U8TO32(p) \ + (((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | \ + ((uint32_t)((p)[2]) << 8) | ((uint32_t)((p)[3]) )) +#define U32TO8(p, v) \ + (p)[0] = (uint8_t)((v) >> 24); (p)[1] = (uint8_t)((v) >> 16); \ + (p)[2] = (uint8_t)((v) >> 8); (p)[3] = (uint8_t)((v) ); + +const uint8_t sigma[][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}, + {14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3}, + {11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4}, + { 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8}, + { 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13}, + { 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9}, + {12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11}, + {13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10}, + { 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13, 0}, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}, + {14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3}, + {11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4}, + { 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8} +}; + +const uint32_t cst[16] = { + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917 +}; + +static const uint8_t padding[] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +void blake256_compress(state *S, const uint8_t *block) { + uint32_t v[16], m[16], i; + +#define ROT(x,n) (((x)<<(32-n))|((x)>>(n))) +#define G(a,b,c,d,e) \ + v[a] += (m[sigma[i][e]] ^ cst[sigma[i][e+1]]) + v[b]; \ + v[d] = ROT(v[d] ^ v[a],16); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c],12); \ + v[a] += (m[sigma[i][e+1]] ^ cst[sigma[i][e]])+v[b]; \ + v[d] = ROT(v[d] ^ v[a], 8); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c], 7); + + for (i = 0; i < 16; ++i) m[i] = U8TO32(block + i * 4); + for (i = 0; i < 8; ++i) v[i] = S->h[i]; + v[ 8] = S->s[0] ^ 0x243F6A88; + v[ 9] = S->s[1] ^ 0x85A308D3; + v[10] = S->s[2] ^ 0x13198A2E; + v[11] = S->s[3] ^ 0x03707344; + v[12] = 0xA4093822; + v[13] = 0x299F31D0; + v[14] = 0x082EFA98; + v[15] = 0xEC4E6C89; + + if (S->nullt == 0) { + v[12] ^= S->t[0]; + v[13] ^= S->t[0]; + v[14] ^= S->t[1]; + v[15] ^= S->t[1]; + } + + for (i = 0; i < 14; ++i) { + G(0, 4, 8, 12, 0); + G(1, 5, 9, 13, 2); + G(2, 6, 10, 14, 4); + G(3, 7, 11, 15, 6); + G(3, 4, 9, 14, 14); + G(2, 7, 8, 13, 12); + G(0, 5, 10, 15, 8); + G(1, 6, 11, 12, 10); + } + + for (i = 0; i < 16; ++i) S->h[i % 8] ^= v[i]; + for (i = 0; i < 8; ++i) S->h[i] ^= S->s[i % 4]; +} + +void blake256_init(state *S) { + S->h[0] = 0x6A09E667; + S->h[1] = 0xBB67AE85; + S->h[2] = 0x3C6EF372; + S->h[3] = 0xA54FF53A; + S->h[4] = 0x510E527F; + S->h[5] = 0x9B05688C; + S->h[6] = 0x1F83D9AB; + S->h[7] = 0x5BE0CD19; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +void blake224_init(state *S) { + S->h[0] = 0xC1059ED8; + S->h[1] = 0x367CD507; + S->h[2] = 0x3070DD17; + S->h[3] = 0xF70E5939; + S->h[4] = 0xFFC00B31; + S->h[5] = 0x68581511; + S->h[6] = 0x64F98FA7; + S->h[7] = 0xBEFA4FA4; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +// datalen = number of bits +void blake256_update(state *S, const uint8_t *data, uint64_t datalen) { + int left = S->buflen >> 3; + int fill = 64 - left; + + if (left && (((datalen >> 3) & 0x3F) >= (unsigned) fill)) { + memcpy((void *) (S->buf + left), (void *) data, fill); + S->t[0] += 512; + if (S->t[0] == 0) S->t[1]++; + blake256_compress(S, S->buf); + data += fill; + datalen -= (fill << 3); + left = 0; + } + + while (datalen >= 512) { + S->t[0] += 512; + if (S->t[0] == 0) S->t[1]++; + blake256_compress(S, data); + data += 64; + datalen -= 512; + } + + if (datalen > 0) { + memcpy((void *) (S->buf + left), (void *) data, (size_t)(datalen >> 3)); + S->buflen = (left << 3) + (int)datalen; + } else { + S->buflen = 0; + } +} + +// datalen = number of bits +void blake224_update(state *S, const uint8_t *data, uint64_t datalen) { + blake256_update(S, data, datalen); +} + +void blake256_final_h(state *S, uint8_t *digest, uint8_t pa, uint8_t pb) { + uint8_t msglen[8]; + uint32_t lo = S->t[0] + S->buflen, hi = S->t[1]; + if (lo < (unsigned) S->buflen) hi++; + U32TO8(msglen + 0, hi); + U32TO8(msglen + 4, lo); + + if (S->buflen == 440) { /* one padding byte */ + S->t[0] -= 8; + blake256_update(S, &pa, 8); + } else { + if (S->buflen < 440) { /* enough space to fill the block */ + if (S->buflen == 0) S->nullt = 1; + S->t[0] -= 440 - S->buflen; + blake256_update(S, padding, 440 - S->buflen); + } else { /* need 2 compressions */ + S->t[0] -= 512 - S->buflen; + blake256_update(S, padding, 512 - S->buflen); + S->t[0] -= 440; + blake256_update(S, padding + 1, 440); + S->nullt = 1; + } + blake256_update(S, &pb, 8); + S->t[0] -= 8; + } + S->t[0] -= 64; + blake256_update(S, msglen, 64); + + U32TO8(digest + 0, S->h[0]); + U32TO8(digest + 4, S->h[1]); + U32TO8(digest + 8, S->h[2]); + U32TO8(digest + 12, S->h[3]); + U32TO8(digest + 16, S->h[4]); + U32TO8(digest + 20, S->h[5]); + U32TO8(digest + 24, S->h[6]); + U32TO8(digest + 28, S->h[7]); +} + +void blake256_final(state *S, uint8_t *digest) { + blake256_final_h(S, digest, 0x81, 0x01); +} + +void blake224_final(state *S, uint8_t *digest) { + blake256_final_h(S, digest, 0x80, 0x00); +} + +// inlen = number of bytes +void blake256_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) { + state S; + blake256_init(&S); + blake256_update(&S, in, inlen * 8); + blake256_final(&S, out); +} + +// inlen = number of bytes +void blake224_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) { + state S; + blake224_init(&S); + blake224_update(&S, in, inlen * 8); + blake224_final(&S, out); +} + +// keylen = number of bytes +void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { + const uint8_t *key = _key; + uint8_t keyhash[32]; + uint8_t pad[64]; + uint64_t i; + + if (keylen > 64) { + blake256_hash(keyhash, key, keylen); + key = keyhash; + keylen = 32; + } + + blake256_init(&S->inner); + memset(pad, 0x36, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake256_update(&S->inner, pad, 512); + + blake256_init(&S->outer); + memset(pad, 0x5c, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake256_update(&S->outer, pad, 512); + + memset(keyhash, 0, 32); +} + +// keylen = number of bytes +void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { + const uint8_t *key = _key; + uint8_t keyhash[32]; + uint8_t pad[64]; + uint64_t i; + + if (keylen > 64) { + blake256_hash(keyhash, key, keylen); + key = keyhash; + keylen = 28; + } + + blake224_init(&S->inner); + memset(pad, 0x36, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake224_update(&S->inner, pad, 512); + + blake224_init(&S->outer); + memset(pad, 0x5c, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake224_update(&S->outer, pad, 512); + + memset(keyhash, 0, 32); +} + +// datalen = number of bits +void hmac_blake256_update(hmac_state *S, const uint8_t *data, uint64_t datalen) { + // update the inner state + blake256_update(&S->inner, data, datalen); +} + +// datalen = number of bits +void hmac_blake224_update(hmac_state *S, const uint8_t *data, uint64_t datalen) { + // update the inner state + blake224_update(&S->inner, data, datalen); +} + +void hmac_blake256_final(hmac_state *S, uint8_t *digest) { + uint8_t ihash[32]; + blake256_final(&S->inner, ihash); + blake256_update(&S->outer, ihash, 256); + blake256_final(&S->outer, digest); + memset(ihash, 0, 32); +} + +void hmac_blake224_final(hmac_state *S, uint8_t *digest) { + uint8_t ihash[32]; + blake224_final(&S->inner, ihash); + blake224_update(&S->outer, ihash, 224); + blake224_final(&S->outer, digest); + memset(ihash, 0, 32); +} + +// keylen = number of bytes; inlen = number of bytes +void hmac_blake256_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) { + hmac_state S; + hmac_blake256_init(&S, key, keylen); + hmac_blake256_update(&S, in, inlen * 8); + hmac_blake256_final(&S, out); +} + +// keylen = number of bytes; inlen = number of bytes +void hmac_blake224_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) { + hmac_state S; + hmac_blake224_init(&S, key, keylen); + hmac_blake224_update(&S, in, inlen * 8); + hmac_blake224_final(&S, out); +} diff --git a/src/crypto/blake/blake256.h b/src/crypto/blake/blake256.h new file mode 100644 index 00000000..33c76b1f --- /dev/null +++ b/src/crypto/blake/blake256.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct { + uint32_t h[8], s[4], t[2]; + int buflen, nullt; + uint8_t buf[64]; +} state; + +typedef struct { + state inner; + state outer; +} hmac_state; + +void blake256_init(state *); +void blake224_init(state *); + +void blake256_update(state *, const uint8_t *, uint64_t); +void blake224_update(state *, const uint8_t *, uint64_t); + +void blake256_final(state *, uint8_t *); +void blake224_final(state *, uint8_t *); + +void blake256_hash(uint8_t *, const uint8_t *, uint64_t); +void blake224_hash(uint8_t *, const uint8_t *, uint64_t); + +/* HMAC functions: */ + +void hmac_blake256_init(hmac_state *, const uint8_t *, uint64_t); +void hmac_blake224_init(hmac_state *, const uint8_t *, uint64_t); + +void hmac_blake256_update(hmac_state *, const uint8_t *, uint64_t); +void hmac_blake224_update(hmac_state *, const uint8_t *, uint64_t); + +void hmac_blake256_final(hmac_state *, uint8_t *); +void hmac_blake224_final(hmac_state *, uint8_t *); + +void hmac_blake256_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t); +void hmac_blake224_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t); + +#if defined(__cplusplus) +} +#endif + diff --git a/src/crypto/chacha8.hpp b/src/crypto/chacha8.hpp new file mode 100644 index 00000000..19f09037 --- /dev/null +++ b/src/crypto/chacha8.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "bernstein/chacha8.h" + +#include "crypto-util.h" +#include "hash.hpp" + +namespace crypto { + +#pragma pack(push, 1) +struct chacha8_key { + uint8_t data[CHACHA8_KEY_SIZE]; + + chacha8_key() : data{} {} + explicit chacha8_key(const Hash &ha) { + memcpy(data, ha.data, CHACHA8_KEY_SIZE); // safe because of static_assert below + } + ~chacha8_key() { sodium_memzero(data, sizeof(data)); } +}; +struct chacha8_iv { + uint8_t data[CHACHA8_IV_SIZE]{}; +}; +#pragma pack(pop) + +static_assert( + sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size"); +static_assert(sizeof(chacha8_key) <= sizeof(Hash), "Size of hash must be at least that of chacha8_key"); + +inline void chacha8(const void *data, size_t length, const chacha8_key &key, const chacha8_iv &iv, void *cipher) { + chacha8(data, length, key.data, iv.data, (char *)cipher); +} + +inline chacha8_key generate_chacha8_key( + crypto::CryptoNightContext &context, const void *password_data, size_t password_size) { + Hash pwd_hash = context.cn_slow_hash(password_data, password_size); + return chacha8_key{pwd_hash}; +} +inline chacha8_key generate_chacha8_key(crypto::CryptoNightContext &context, const std::string &password) { + return generate_chacha8_key(context, password.data(), password.size()); +} +} diff --git a/src/crypto/crypto-util.c b/src/crypto/crypto-util.c index 1d7efd52..040508b5 100644 --- a/src/crypto/crypto-util.c +++ b/src/crypto/crypto-util.c @@ -11,49 +11,48 @@ #include "crypto-util.h" #ifdef _WIN32 - #include +#include #endif -void sodium_memzero(void* pnt, size_t len){ +void sodium_memzero(void *pnt, size_t len) { #ifdef _WIN32 - SecureZeroMemory(pnt, len); + SecureZeroMemory(pnt, len); #else - volatile unsigned char *volatile pnt_ = (volatile unsigned char *volatile) pnt; - size_t i = (size_t) 0U; + volatile unsigned char *volatile pnt_ = (volatile unsigned char *volatile)pnt; + size_t i = (size_t)0U; - while (i < len) { - pnt_[i++] = 0U; - } + while (i < len) { + pnt_[i++] = 0U; + } #endif } -int sodium_compare(const void* a1, const void* a2, size_t len){ - const volatile unsigned char *volatile b1 = (const volatile unsigned char *) a1; - const volatile unsigned char *volatile b2 = (const volatile unsigned char *) a2; - size_t i; - volatile unsigned char gt = 0U; - volatile unsigned char eq = 1U; - uint16_t x1, x2; - - i = len; - while (i != 0U) { - i--; - x1 = b1[i]; - x2 = b2[i]; - gt |= ((x2 - x1) >> 8) & eq; - eq &= ((x2 ^ x1) - 1) >> 8; - } - return (int) (gt + gt + eq) - 1; +int sodium_compare(const void *a1, const void *a2, size_t len) { + const volatile unsigned char *volatile b1 = (const volatile unsigned char *)a1; + const volatile unsigned char *volatile b2 = (const volatile unsigned char *)a2; + size_t i; + volatile unsigned char gt = 0U; + volatile unsigned char eq = 1U; + uint16_t x1, x2; + + i = len; + while (i != 0U) { + i--; + x1 = b1[i]; + x2 = b2[i]; + gt |= ((x2 - x1) >> 8) & eq; + eq &= ((x2 ^ x1) - 1) >> 8; + } + return (int)(gt + gt + eq) - 1; } -int sodium_is_zero(const void * data, const size_t nlen) -{ - const unsigned char *n = data; - size_t i; - volatile unsigned char d = 0U; +int sodium_is_zero(const void *data, const size_t nlen) { + const unsigned char *n = data; + size_t i; + volatile unsigned char d = 0U; - for (i = 0U; i < nlen; i++) { - d |= n[i]; - } - return 1 & ((d - 1) >> 8); + for (i = 0U; i < nlen; i++) { + d |= n[i]; + } + return 1 & ((d - 1) >> 8); } diff --git a/src/crypto/crypto-util.h b/src/crypto/crypto-util.h index 617483d2..24d0c03f 100644 --- a/src/crypto/crypto-util.h +++ b/src/crypto/crypto-util.h @@ -7,21 +7,19 @@ #pragma once -#include #include - +#include #if defined(__cplusplus) -#include -#include namespace crypto { extern "C" { #endif // We borrow from https://libsodium.org/ -void sodium_memzero(void* pnt, size_t length); -int sodium_compare(const void* a1, const void* a2, size_t length); -int sodium_is_zero(const void * data, const size_t nlen); +void sodium_memzero(void *pnt, size_t length); +int sodium_compare(const void *a1, const void *a2, size_t length); +int sodium_is_zero(const void *data, const size_t nlen); #if defined(__cplusplus) -}} +} +} #endif diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index faa11943..0fac1350 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -10,7 +10,7 @@ #include #include -#include "crypto-ops.h" +#include "bernstein/crypto-ops.h" #include "crypto.hpp" #include "hash.hpp" #include "random.h" @@ -86,10 +86,12 @@ static void write_varint(uint8_t *&dest, size_t i) { *dest++ = static_cast(i); } static void derivation_to_scalar(const KeyDerivation &derivation, size_t output_index, EllipticCurveScalar &res) { +#pragma pack(push, 1) struct { KeyDerivation derivation; uint8_t output_index[(sizeof(size_t) * 8 + 6) / 7]; } buf; +#pragma pack(pop) uint8_t *end = buf.output_index; buf.derivation = derivation; write_varint(end, output_index); @@ -100,10 +102,12 @@ static void derivation_to_scalar(const KeyDerivation &derivation, size_t output_ static void derivation_to_scalar(const KeyDerivation &derivation, size_t output_index, const uint8_t *suffix, size_t suffix_length, EllipticCurveScalar &res) { assert(suffix_length <= 32); +#pragma pack(push, 1) struct { KeyDerivation derivation; uint8_t output_index[(sizeof(size_t) * 8 + 6) / 7 + 32]; } buf; +#pragma pack(pop) uint8_t *end = buf.output_index; buf.derivation = derivation; write_varint(end, output_index); @@ -400,7 +404,7 @@ bool generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, con } bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, const PublicKey *const pubs[], - size_t pubs_count, const Signature sigs[], bool check_key_image, bool *key_corrupted) { + size_t pubs_count, const Signature sigs[], bool key_image_subgroup_check, bool *key_corrupted) { if (key_corrupted) *key_corrupted = false; ge_p3 image_unp; @@ -412,7 +416,7 @@ bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, const return false; } ge_dsm_precomp(image_pre, &image_unp); - if (check_key_image && ge_check_subgroup_precomp_vartime(image_pre) != 0) { + if (key_image_subgroup_check && ge_check_subgroup_precomp_vartime(image_pre) != 0) { // Example of key_images that fail subgroup check // c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa // c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a diff --git a/src/crypto/crypto.hpp b/src/crypto/crypto.hpp index da09711b..22af53e1 100644 --- a/src/crypto/crypto.hpp +++ b/src/crypto/crypto.hpp @@ -102,7 +102,7 @@ void generate_key_image(const PublicKey &pub, const SecretKey &sec, KeyImage &im bool generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, const PublicKey *const pubs[], std::size_t pubs_count, const SecretKey &sec, std::size_t sec_index, Signature sigs[]); bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, const PublicKey *const pubs[], - size_t pubs_count, const Signature sigs[], bool check_key_image, bool *key_corrupted = nullptr); + size_t pubs_count, const Signature sigs[], bool key_image_subgroup_check, bool *key_corrupted = nullptr); // TODO - remove one pair of funs // returns false if keys are corrupted/invalid inline bool generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, @@ -110,9 +110,10 @@ inline bool generate_ring_signature(const Hash &prefix_hash, const KeyImage &ima return generate_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sec, sec_index, sigs); } inline bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, - const std::vector &pubs, const Signature sigs[], bool check_key_image, + const std::vector &pubs, const Signature sigs[], bool key_image_subgroup_check, bool *key_corrupted = nullptr) { - return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sigs, check_key_image, key_corrupted); + return check_ring_signature( + prefix_hash, image, pubs.data(), pubs.size(), sigs, key_image_subgroup_check, key_corrupted); } bool generate_sendproof(const PublicKey &txkey_pub, const SecretKey &txkey_sec, const PublicKey &receiver_view_key_pub, diff --git a/src/crypto/groestl/Groestl-opt.c b/src/crypto/groestl/Groestl-opt.c new file mode 100644 index 00000000..e8ede0a1 --- /dev/null +++ b/src/crypto/groestl/Groestl-opt.c @@ -0,0 +1,553 @@ +/* Groestl-opt.c January 2011 + * ANSI C code optimised for 32-bit machines + * Authors: Soeren S. Thomsen + * Krystian Matusiewicz + * + * This code is placed in the public domain + */ + +#include "Groestl-opt.h" +#include "tables.h" + +/* compute one new state column */ +#define COLUMN(x,y,i,c0,c1,c2,c3,c4,c5,c6,c7) \ + y[i] = \ + T[0*256+EXT_BYTE(x[c0], 0)]^ \ + T[1*256+EXT_BYTE(x[c1], 1)]^ \ + T[2*256+EXT_BYTE(x[c2], 2)]^ \ + T[3*256+EXT_BYTE(x[c3], 3)]^ \ + T[4*256+EXT_BYTE(x[c4], 0)]^ \ + T[5*256+EXT_BYTE(x[c5], 1)]^ \ + T[6*256+EXT_BYTE(x[c6], 2)]^ \ + T[7*256+EXT_BYTE(x[c7], 3)] + +/* compute one round of P (short variants) */ +void RND512P(u32 *x, u32 *y, u32 r) { + x[ 0] ^= U32BIG((u32)0x00000000u)^r; + x[ 2] ^= U32BIG((u32)0x10000000u)^r; + x[ 4] ^= U32BIG((u32)0x20000000u)^r; + x[ 6] ^= U32BIG((u32)0x30000000u)^r; + x[ 8] ^= U32BIG((u32)0x40000000u)^r; + x[10] ^= U32BIG((u32)0x50000000u)^r; + x[12] ^= U32BIG((u32)0x60000000u)^r; + x[14] ^= U32BIG((u32)0x70000000u)^r; + COLUMN(x,y, 0, 0, 2, 4, 6, 9, 11, 13, 15); + COLUMN(x,y, 1, 9, 11, 13, 15, 0, 2, 4, 6); + COLUMN(x,y, 2, 2, 4, 6, 8, 11, 13, 15, 1); + COLUMN(x,y, 3, 11, 13, 15, 1, 2, 4, 6, 8); + COLUMN(x,y, 4, 4, 6, 8, 10, 13, 15, 1, 3); + COLUMN(x,y, 5, 13, 15, 1, 3, 4, 6, 8, 10); + COLUMN(x,y, 6, 6, 8, 10, 12, 15, 1, 3, 5); + COLUMN(x,y, 7, 15, 1, 3, 5, 6, 8, 10, 12); + COLUMN(x,y, 8, 8, 10, 12, 14, 1, 3, 5, 7); + COLUMN(x,y, 9, 1, 3, 5, 7, 8, 10, 12, 14); + COLUMN(x,y,10, 10, 12, 14, 0, 3, 5, 7, 9); + COLUMN(x,y,11, 3, 5, 7, 9, 10, 12, 14, 0); + COLUMN(x,y,12, 12, 14, 0, 2, 5, 7, 9, 11); + COLUMN(x,y,13, 5, 7, 9, 11, 12, 14, 0, 2); + COLUMN(x,y,14, 14, 0, 2, 4, 7, 9, 11, 13); + COLUMN(x,y,15, 7, 9, 11, 13, 14, 0, 2, 4); +} + +/* compute one round of Q (short variants) */ +void RND512Q(u32 *x, u32 *y, u32 r) { + x[ 0] = ~x[ 0]; + x[ 1] ^= U32BIG((u32)0xffffffffu)^r; + x[ 2] = ~x[ 2]; + x[ 3] ^= U32BIG((u32)0xffffffefu)^r; + x[ 4] = ~x[ 4]; + x[ 5] ^= U32BIG((u32)0xffffffdfu)^r; + x[ 6] = ~x[ 6]; + x[ 7] ^= U32BIG((u32)0xffffffcfu)^r; + x[ 8] = ~x[ 8]; + x[ 9] ^= U32BIG((u32)0xffffffbfu)^r; + x[10] = ~x[10]; + x[11] ^= U32BIG((u32)0xffffffafu)^r; + x[12] = ~x[12]; + x[13] ^= U32BIG((u32)0xffffff9fu)^r; + x[14] = ~x[14]; + x[15] ^= U32BIG((u32)0xffffff8fu)^r; + COLUMN(x,y, 0, 2, 6, 10, 14, 1, 5, 9, 13); + COLUMN(x,y, 1, 1, 5, 9, 13, 2, 6, 10, 14); + COLUMN(x,y, 2, 4, 8, 12, 0, 3, 7, 11, 15); + COLUMN(x,y, 3, 3, 7, 11, 15, 4, 8, 12, 0); + COLUMN(x,y, 4, 6, 10, 14, 2, 5, 9, 13, 1); + COLUMN(x,y, 5, 5, 9, 13, 1, 6, 10, 14, 2); + COLUMN(x,y, 6, 8, 12, 0, 4, 7, 11, 15, 3); + COLUMN(x,y, 7, 7, 11, 15, 3, 8, 12, 0, 4); + COLUMN(x,y, 8, 10, 14, 2, 6, 9, 13, 1, 5); + COLUMN(x,y, 9, 9, 13, 1, 5, 10, 14, 2, 6); + COLUMN(x,y,10, 12, 0, 4, 8, 11, 15, 3, 7); + COLUMN(x,y,11, 11, 15, 3, 7, 12, 0, 4, 8); + COLUMN(x,y,12, 14, 2, 6, 10, 13, 1, 5, 9); + COLUMN(x,y,13, 13, 1, 5, 9, 14, 2, 6, 10); + COLUMN(x,y,14, 0, 4, 8, 12, 15, 3, 7, 11); + COLUMN(x,y,15, 15, 3, 7, 11, 0, 4, 8, 12); +} + +/* compute one round of P (short variants) */ +void RND1024P(u32 *x, u32 *y, u32 r) { + x[ 0] ^= U32BIG((u32)0x00000000u)^r; + x[ 2] ^= U32BIG((u32)0x10000000u)^r; + x[ 4] ^= U32BIG((u32)0x20000000u)^r; + x[ 6] ^= U32BIG((u32)0x30000000u)^r; + x[ 8] ^= U32BIG((u32)0x40000000u)^r; + x[10] ^= U32BIG((u32)0x50000000u)^r; + x[12] ^= U32BIG((u32)0x60000000u)^r; + x[14] ^= U32BIG((u32)0x70000000u)^r; + x[16] ^= U32BIG((u32)0x80000000u)^r; + x[18] ^= U32BIG((u32)0x90000000u)^r; + x[20] ^= U32BIG((u32)0xa0000000u)^r; + x[22] ^= U32BIG((u32)0xb0000000u)^r; + x[24] ^= U32BIG((u32)0xc0000000u)^r; + x[26] ^= U32BIG((u32)0xd0000000u)^r; + x[28] ^= U32BIG((u32)0xe0000000u)^r; + x[30] ^= U32BIG((u32)0xf0000000u)^r; + + COLUMN(x,y, 0, 0, 2, 4, 6, 9,11,13,23); + COLUMN(x,y, 2, 2, 4, 6, 8,11,13,15,25); + COLUMN(x,y, 4, 4, 6, 8,10,13,15,17,27); + COLUMN(x,y, 6, 6, 8,10,12,15,17,19,29); + COLUMN(x,y, 8, 8,10,12,14,17,19,21,31); + COLUMN(x,y,10,10,12,14,16,19,21,23, 1); + COLUMN(x,y,12,12,14,16,18,21,23,25, 3); + COLUMN(x,y,14,14,16,18,20,23,25,27, 5); + COLUMN(x,y,16,16,18,20,22,25,27,29, 7); + COLUMN(x,y,18,18,20,22,24,27,29,31, 9); + COLUMN(x,y,20,20,22,24,26,29,31, 1,11); + COLUMN(x,y,22,22,24,26,28,31, 1, 3,13); + COLUMN(x,y,24,24,26,28,30, 1, 3, 5,15); + COLUMN(x,y,26,26,28,30, 0, 3, 5, 7,17); + COLUMN(x,y,28,28,30, 0, 2, 5, 7, 9,19); + COLUMN(x,y,30,30, 0, 2, 4, 7, 9,11,21); + + COLUMN(x,y, 1, 9,11,13,23, 0, 2, 4, 6); + COLUMN(x,y, 3,11,13,15,25, 2, 4, 6, 8); + COLUMN(x,y, 5,13,15,17,27, 4, 6, 8,10); + COLUMN(x,y, 7,15,17,19,29, 6, 8,10,12); + COLUMN(x,y, 9,17,19,21,31, 8,10,12,14); + COLUMN(x,y,11,19,21,23, 1,10,12,14,16); + COLUMN(x,y,13,21,23,25, 3,12,14,16,18); + COLUMN(x,y,15,23,25,27, 5,14,16,18,20); + COLUMN(x,y,17,25,27,29, 7,16,18,20,22); + COLUMN(x,y,19,27,29,31, 9,18,20,22,24); + COLUMN(x,y,21,29,31, 1,11,20,22,24,26); + COLUMN(x,y,23,31, 1, 3,13,22,24,26,28); + COLUMN(x,y,25, 1, 3, 5,15,24,26,28,30); + COLUMN(x,y,27, 3, 5, 7,17,26,28,30, 0); + COLUMN(x,y,29, 5, 7, 9,19,28,30, 0, 2); + COLUMN(x,y,31, 7, 9,11,21,30, 0, 2, 4); +} + +/* compute one round of Q (short variants) */ +void RND1024Q(u32 *x, u32 *y, u32 r) { + x[ 0] = ~x[ 0]; + x[ 1] ^= U32BIG((u32)0xffffffffu)^r; + x[ 2] = ~x[ 2]; + x[ 3] ^= U32BIG((u32)0xffffffefu)^r; + x[ 4] = ~x[ 4]; + x[ 5] ^= U32BIG((u32)0xffffffdfu)^r; + x[ 6] = ~x[ 6]; + x[ 7] ^= U32BIG((u32)0xffffffcfu)^r; + x[ 8] = ~x[ 8]; + x[ 9] ^= U32BIG((u32)0xffffffbfu)^r; + x[10] = ~x[10]; + x[11] ^= U32BIG((u32)0xffffffafu)^r; + x[12] = ~x[12]; + x[13] ^= U32BIG((u32)0xffffff9fu)^r; + x[14] = ~x[14]; + x[15] ^= U32BIG((u32)0xffffff8fu)^r; + x[16] = ~x[16]; + x[17] ^= U32BIG((u32)0xffffff7fu)^r; + x[18] = ~x[18]; + x[19] ^= U32BIG((u32)0xffffff6fu)^r; + x[20] = ~x[20]; + x[21] ^= U32BIG((u32)0xffffff5fu)^r; + x[22] = ~x[22]; + x[23] ^= U32BIG((u32)0xffffff4fu)^r; + x[24] = ~x[24]; + x[25] ^= U32BIG((u32)0xffffff3fu)^r; + x[26] = ~x[26]; + x[27] ^= U32BIG((u32)0xffffff2fu)^r; + x[28] = ~x[28]; + x[29] ^= U32BIG((u32)0xffffff1fu)^r; + x[30] = ~x[30]; + x[31] ^= U32BIG((u32)0xffffff0fu)^r; + + COLUMN(x,y, 0, 2, 6, 10, 22, 1, 5, 9, 13); + COLUMN(x,y, 1, 1, 5, 9, 13, 2, 6, 10, 22); + COLUMN(x,y, 2, 4, 8, 12, 24, 3, 7, 11, 15); + COLUMN(x,y, 3, 3, 7, 11, 15, 4, 8, 12, 24); + COLUMN(x,y, 4, 6, 10, 14, 26, 5, 9, 13, 17); + COLUMN(x,y, 5, 5, 9, 13, 17, 6, 10, 14, 26); + COLUMN(x,y, 6, 8, 12, 16, 28, 7, 11, 15, 19); + COLUMN(x,y, 7, 7, 11, 15, 19, 8, 12, 16, 28); + COLUMN(x,y, 8, 10, 14, 18, 30, 9, 13, 17, 21); + COLUMN(x,y, 9, 9, 13, 17, 21, 10, 14, 18, 30); + COLUMN(x,y,10, 12, 16, 20, 0, 11, 15, 19, 23); + COLUMN(x,y,11, 11, 15, 19, 23, 12, 16, 20, 0); + COLUMN(x,y,12, 14, 18, 22, 2, 13, 17, 21, 25); + COLUMN(x,y,13, 13, 17, 21, 25, 14, 18, 22, 2); + COLUMN(x,y,14, 16, 20, 24, 4, 15, 19, 23, 27); + COLUMN(x,y,15, 15, 19, 23, 27, 16, 20, 24, 4); + + COLUMN(x,y,16, 18, 22, 26, 6, 17, 21, 25, 29); + COLUMN(x,y,17, 17, 21, 25, 29, 18, 22, 26, 6); + COLUMN(x,y,18, 20, 24, 28, 8, 19, 23, 27, 31); + COLUMN(x,y,19, 19, 23, 27, 31, 20, 24, 28, 8); + COLUMN(x,y,20, 22, 26, 30, 10, 21, 25, 29, 1); + COLUMN(x,y,21, 21, 25, 29, 1, 22, 26, 30, 10); + COLUMN(x,y,22, 24, 28, 0, 12, 23, 27, 31, 3); + COLUMN(x,y,23, 23, 27, 31, 3, 24, 28, 0, 12); + COLUMN(x,y,24, 26, 30, 2, 14, 25, 29, 1, 5); + COLUMN(x,y,25, 25, 29, 1, 5, 26, 30, 2, 14); + COLUMN(x,y,26, 28, 0, 4, 16, 27, 31, 3, 7); + COLUMN(x,y,27, 27, 31, 3, 7, 28, 0, 4, 16); + COLUMN(x,y,28, 30, 2, 6, 18, 29, 1, 5, 9); + COLUMN(x,y,29, 29, 1, 5, 9, 30, 2, 6, 18); + COLUMN(x,y,30, 0, 4, 8, 20, 31, 3, 7, 11); + COLUMN(x,y,31, 31, 3, 7, 11, 0, 4, 8, 20); +} + + +/* compute compression function (short variants) */ +void F512(u32 *h, const u32 *m) { + int i; + u32 Ptmp[2*COLS512]; + u32 Qtmp[2*COLS512]; + u32 y[2*COLS512]; + u32 z[2*COLS512]; + + for (i = 0; i < 2*COLS512; i++) { + z[i] = m[i]; + Ptmp[i] = h[i]^m[i]; + } + + /* compute Q(m) */ + RND512Q(z, y, U32BIG((u32)0x00000000u)); + RND512Q(y, z, U32BIG((u32)0x00000001u)); + RND512Q(z, y, U32BIG((u32)0x00000002u)); + RND512Q(y, z, U32BIG((u32)0x00000003u)); + RND512Q(z, y, U32BIG((u32)0x00000004u)); + RND512Q(y, z, U32BIG((u32)0x00000005u)); + RND512Q(z, y, U32BIG((u32)0x00000006u)); + RND512Q(y, z, U32BIG((u32)0x00000007u)); + RND512Q(z, y, U32BIG((u32)0x00000008u)); + RND512Q(y, Qtmp, U32BIG((u32)0x00000009u)); + + /* compute P(h+m) */ + RND512P(Ptmp, y, U32BIG((u32)0x00000000u)); + RND512P(y, z, U32BIG((u32)0x01000000u)); + RND512P(z, y, U32BIG((u32)0x02000000u)); + RND512P(y, z, U32BIG((u32)0x03000000u)); + RND512P(z, y, U32BIG((u32)0x04000000u)); + RND512P(y, z, U32BIG((u32)0x05000000u)); + RND512P(z, y, U32BIG((u32)0x06000000u)); + RND512P(y, z, U32BIG((u32)0x07000000u)); + RND512P(z, y, U32BIG((u32)0x08000000u)); + RND512P(y, Ptmp, U32BIG((u32)0x09000000u)); + + /* compute P(h+m) + Q(m) + h */ + for (i = 0; i < 2*COLS512; i++) { + h[i] ^= Ptmp[i]^Qtmp[i]; + } +} + +/* compute compression function (long variants) */ +void F1024(u32 *h, const u32 *m) { + int i; + u32 Ptmp[2*COLS1024]; + u32 Qtmp[2*COLS1024]; + u32 y[2*COLS1024]; + u32 z[2*COLS1024]; + + for (i = 0; i < 2*COLS1024; i++) { + z[i] = m[i]; + Ptmp[i] = h[i]^m[i]; + } + + /* compute Q(m) */ + RND1024Q(z, y, U32BIG((u32)0x00000000u)); + for (i = 1; i < ROUNDS1024-1; i += 2) { + RND1024Q(y, z, U32BIG((u32)i)); + RND1024Q(z, y, U32BIG((u32)i+1)); + } + RND1024Q(y, Qtmp, U32BIG((u32)0x0000000du)); + + /* compute P(h+m) */ + RND1024P(Ptmp, y, U32BIG((u32)0x00000000u)); + for (i = 1; i < ROUNDS1024-1; i += 2) { + RND1024P(y, z, U32BIG((u32)i<<24)); + RND1024P(z, y, U32BIG(((u32)i+1)<<24)); + } + RND1024P(y, Ptmp, U32BIG(((u32)ROUNDS1024-1)<<24)); + + /* compute P(h+m) + Q(m) + h */ + for (i = 0; i < 2*COLS1024; i++) { + h[i] ^= Ptmp[i]^Qtmp[i]; + } +} + + +/* digest up to msglen bytes of input (full blocks only) */ +void Transform(hashState *ctx, + const u8 *input, + int msglen) { + /* determine variant, SHORT or LONG, and select underlying + compression function based on the variant */ + void (*F)(u32*,const u32*); + switch ( ctx->v ) { + case SHORT : F = &F512; break; + case LONG : + default : F = &F1024; break; + } + + /* digest message, one block at a time */ + for (; msglen >= ctx->statesize; + msglen -= ctx->statesize, input += ctx->statesize) { + F(ctx->chaining,(u32*)input); + + /* increment block counter */ + ctx->block_counter1++; + if (ctx->block_counter1 == 0) ctx->block_counter2++; + } +} + +/* given state h, do h <- P(h)+h */ +void OutputTransformation(hashState *ctx) { + int j; + u32 *temp, *y, *z; + temp = malloc(2*ctx->columns*sizeof(u32)); + y = malloc(2*ctx->columns*sizeof(u32)); + z = malloc(2*ctx->columns*sizeof(u32)); + + /* determine variant */ + switch (ctx->v) { + case SHORT : + for (j = 0; j < 2*COLS512; j++) { + temp[j] = ctx->chaining[j]; + } + RND512P(temp, y, U32BIG((u32)0x00000000u)); + RND512P(y, z, U32BIG((u32)0x01000000u)); + RND512P(z, y, U32BIG((u32)0x02000000u)); + RND512P(y, z, U32BIG((u32)0x03000000u)); + RND512P(z, y, U32BIG((u32)0x04000000u)); + RND512P(y, z, U32BIG((u32)0x05000000u)); + RND512P(z, y, U32BIG((u32)0x06000000u)); + RND512P(y, z, U32BIG((u32)0x07000000u)); + RND512P(z, y, U32BIG((u32)0x08000000u)); + RND512P(y, temp, U32BIG((u32)0x09000000u)); + for (j = 0; j < 2*COLS512; j++) { + ctx->chaining[j] ^= temp[j]; + } + break; + case LONG : + for (j = 0; j < 2*COLS1024; j++) { + temp[j] = ctx->chaining[j]; + } + RND1024P(temp,y,U32BIG((u32)0x00000000u)); + for (j = 1; j < ROUNDS1024-1; j += 2) { + RND1024P(y,z,U32BIG((u32)j<<24)); + RND1024P(z,y,U32BIG(((u32)j+1)<<24)); + } + RND1024P(y,temp,U32BIG(((u32)ROUNDS1024-1)<<24)); + for (j = 0; j < 2*COLS1024; j++) { + ctx->chaining[j] ^= temp[j]; + } + break; + } + + free(temp); + free(y); + free(z); +} + +/* initialise context */ +HashReturn Init(hashState* ctx, + int hashbitlen) { + /* output size (in bits) must be a positive integer less than or + equal to 512, and divisible by 8 */ + if (hashbitlen <= 0 || (hashbitlen%8) || hashbitlen > 512) + return BAD_HASHLEN; + + /* set number of state columns and state size depending on + variant */ + if (hashbitlen <= 256) { + ctx->columns = COLS512; + ctx->statesize = SIZE512; + ctx->v = SHORT; + } + else { + ctx->columns = COLS1024; + ctx->statesize = SIZE1024; + ctx->v = LONG; + } + + /* allocate memory for state and data buffer */ + ctx->chaining = calloc(ctx->statesize,1); + ctx->buffer = malloc(ctx->statesize); + if (ctx->chaining == NULL || ctx->buffer == NULL) + return FAIL; + + /* set initial value */ + ctx->chaining[2*ctx->columns-1] = U32BIG((u32)hashbitlen); + + /* set other variables */ + ctx->hashbitlen = hashbitlen; + ctx->buf_ptr = 0; + ctx->block_counter1 = 0; + ctx->block_counter2 = 0; + ctx->bits_in_last_byte = 0; + + return SUCCESS; +} + +/* update state with databitlen bits of input */ +HashReturn Update(hashState* ctx, + const BitSequence* input, + DataLength databitlen) { + int index = 0; + int msglen = (int)(databitlen/8); + int rem = (int)(databitlen%8); + + /* non-integral number of message bytes can only be supplied in the + last call to this function */ + if (ctx->bits_in_last_byte) return FAIL; + + /* if the buffer contains data that has not yet been digested, first + add data to buffer until full */ + if (ctx->buf_ptr) { + while (ctx->buf_ptr < ctx->statesize && index < msglen) { + ctx->buffer[(int)ctx->buf_ptr++] = input[index++]; + } + if (ctx->buf_ptr < ctx->statesize) { + /* buffer still not full, return */ + if (rem) { + ctx->bits_in_last_byte = rem; + ctx->buffer[(int)ctx->buf_ptr++] = input[index]; + } + return SUCCESS; + } + + /* digest buffer */ + ctx->buf_ptr = 0; + Transform(ctx, ctx->buffer, ctx->statesize); + } + + /* digest bulk of message */ + Transform(ctx, input+index, msglen-index); + index += ((msglen-index)/ctx->statesize)*ctx->statesize; + + /* store remaining data in buffer */ + while (index < msglen) { + ctx->buffer[(int)ctx->buf_ptr++] = input[index++]; + } + + /* if non-integral number of bytes have been supplied, store + remaining bits in last byte, together with information about + number of bits */ + if (rem) { + ctx->bits_in_last_byte = rem; + ctx->buffer[(int)ctx->buf_ptr++] = input[index]; + } + return SUCCESS; +} + +#define BILB ctx->bits_in_last_byte + +/* finalise: process remaining data (including padding), perform + output transformation, and write hash result to 'output' */ +HashReturn Final(hashState* ctx, + BitSequence* output) { + int i, j = 0, hashbytelen = ctx->hashbitlen/8; + u8 *s = (BitSequence*)ctx->chaining; + + /* pad with '1'-bit and first few '0'-bits */ + if (BILB) { + ctx->buffer[(int)ctx->buf_ptr-1] &= ((1<buffer[(int)ctx->buf_ptr-1] ^= 0x1<<(7-BILB); + BILB = 0; + } + else ctx->buffer[(int)ctx->buf_ptr++] = 0x80; + + /* pad with '0'-bits */ + if (ctx->buf_ptr > ctx->statesize-LENGTHFIELDLEN) { + /* padding requires two blocks */ + while (ctx->buf_ptr < ctx->statesize) { + ctx->buffer[(int)ctx->buf_ptr++] = 0; + } + /* digest first padding block */ + Transform(ctx, ctx->buffer, ctx->statesize); + ctx->buf_ptr = 0; + } + while (ctx->buf_ptr < ctx->statesize-LENGTHFIELDLEN) { + ctx->buffer[(int)ctx->buf_ptr++] = 0; + } + + /* length padding */ + ctx->block_counter1++; + if (ctx->block_counter1 == 0) ctx->block_counter2++; + ctx->buf_ptr = ctx->statesize; + + while (ctx->buf_ptr > ctx->statesize-(int)sizeof(u32)) { + ctx->buffer[(int)--ctx->buf_ptr] = (u8)ctx->block_counter1; + ctx->block_counter1 >>= 8; + } + while (ctx->buf_ptr > ctx->statesize-LENGTHFIELDLEN) { + ctx->buffer[(int)--ctx->buf_ptr] = (u8)ctx->block_counter2; + ctx->block_counter2 >>= 8; + } + + /* digest final padding block */ + Transform(ctx, ctx->buffer, ctx->statesize); + /* perform output transformation */ + OutputTransformation(ctx); + + /* store hash result in output */ + for (i = ctx->statesize-hashbytelen; i < ctx->statesize; i++,j++) { + output[j] = s[i]; + } + + /* zeroise relevant variables and deallocate memory */ + for (i = 0; i < ctx->columns; i++) { + ctx->chaining[i] = 0; + } + for (i = 0; i < ctx->statesize; i++) { + ctx->buffer[i] = 0; + } + free(ctx->chaining); + free(ctx->buffer); + + return SUCCESS; +} + +/* hash bit sequence */ +HashReturn Hash(int hashbitlen, + const BitSequence* data, + DataLength databitlen, + BitSequence* hashval) { + HashReturn ret; + hashState context; + + /* initialise */ + if ((ret = Init(&context, hashbitlen)) != SUCCESS) + return ret; + + /* process message */ + if ((ret = Update(&context, data, databitlen)) != SUCCESS) + return ret; + + /* finalise */ + ret = Final(&context, hashval); + + return ret; +} + +void PrintHash(const BitSequence* hash, + int hashbitlen) { + int i; + for (i = 0; i < hashbitlen/8; i++) { + printf("%02x", hash[i]); + } + printf("\n"); +} diff --git a/src/crypto/groestl/Groestl-opt.h b/src/crypto/groestl/Groestl-opt.h new file mode 100644 index 00000000..2bc299ef --- /dev/null +++ b/src/crypto/groestl/Groestl-opt.h @@ -0,0 +1,65 @@ +#ifndef __groestl_opt_h +#define __groestl_opt_h + +#include +#include +#include "brg_endian.h" +#include "brg_types.h" + +/* some sizes (number of bytes) */ +#define ROWS 8 +#define LENGTHFIELDLEN ROWS +#define COLS512 8 +#define COLS1024 16 + +#define SIZE512 (ROWS*COLS512) +#define SIZE1024 (ROWS*COLS1024) + +#define ROUNDS512 10 +#define ROUNDS1024 14 + +#define ROTL32(v, n) ((((v)<<(n))|((v)>>(32-(n))))&li_32(ffffffff)) + +#if (PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN) +#define EXT_BYTE(var,n) ((u8)((u32)(var) >> (8*(3-(n))))) +#define U32BIG(a) (a) +#endif /* IS_BIG_ENDIAN */ + +#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) +#define EXT_BYTE(var,n) ((u8)((u32)(var) >> (8*n))) +#define U32BIG(a) \ + ((ROTL32(a,8) & li_32(00FF00FF)) | \ + (ROTL32(a,24) & li_32(FF00FF00))) +#endif /* IS_LITTLE_ENDIAN */ + +typedef enum { LONG, SHORT } Var; + + +/* NIST API begin */ +typedef unsigned char BitSequence; +typedef unsigned long long DataLength; +typedef enum { SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2 } HashReturn; +typedef struct { + u32 *chaining; /* actual state */ + u32 block_counter1, + block_counter2; /* message block counter(s) */ + int hashbitlen; /* output length in bits */ + BitSequence *buffer; /* data buffer */ + int buf_ptr; /* data buffer pointer */ + int bits_in_last_byte; /* no. of message bits in last byte of + data buffer */ + int columns; /* no. of columns in state */ + int statesize; /* total no. of bytes in state */ + Var v; /* LONG or SHORT */ +} hashState; + +HashReturn Init(hashState*, int); +HashReturn Update(hashState*, const BitSequence*, DataLength); +HashReturn Final(hashState*, BitSequence*); +HashReturn Hash(int, const BitSequence*, DataLength, BitSequence*); +/* NIST API end */ + +/* helper functions */ +void PrintHash(const BitSequence*, int); + +#endif /* __groestl_opt_h */ diff --git a/src/crypto/groestl/README.md b/src/crypto/groestl/README.md new file mode 100644 index 00000000..056a817d --- /dev/null +++ b/src/crypto/groestl/README.md @@ -0,0 +1,5 @@ +Implementation included from NIST submission package + +http://www.groestl.info/implementations.html + +We've included Optimized_32bit folder without any modifications diff --git a/src/crypto/groestl/brg_endian.h b/src/crypto/groestl/brg_endian.h new file mode 100644 index 00000000..1c1d0913 --- /dev/null +++ b/src/crypto/groestl/brg_endian.h @@ -0,0 +1,133 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +#ifndef _BRG_ENDIAN_H +#define _BRG_ENDIAN_H + +#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ +#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ + +/* Include files where endian defines and byteswap functions may reside */ +#if defined( __sun ) +# include +#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ ) +# include +#elif defined( BSD ) && ( BSD >= 199103 ) || defined( __APPLE__ ) || \ + defined( __CYGWIN32__ ) || defined( __DJGPP__ ) || defined( __osf__ ) +# include +#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ ) +# if !defined( __MINGW32__ ) && !defined( _AIX ) +# include +# if !defined( __BEOS__ ) +# include +# endif +# endif +#endif + +/* Now attempt to set the define for platform byte order using any */ +/* of the four forms SYMBOL, _SYMBOL, __SYMBOL & __SYMBOL__, which */ +/* seem to encompass most endian symbol definitions */ + +#if defined( BIG_ENDIAN ) && defined( LITTLE_ENDIAN ) +# if defined( BYTE_ORDER ) && BYTE_ORDER == BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( BYTE_ORDER ) && BYTE_ORDER == LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( _BIG_ENDIAN ) && defined( _LITTLE_ENDIAN ) +# if defined( _BYTE_ORDER ) && _BYTE_ORDER == _BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( _BYTE_ORDER ) && _BYTE_ORDER == _LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( _BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( _LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN ) && defined( __LITTLE_ENDIAN ) +# if defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN__ ) && defined( __LITTLE_ENDIAN__ ) +# if defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __BIG_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __LITTLE_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +/* if the platform byte order could not be determined, then try to */ +/* set this define using common machine defines */ +#if !defined(PLATFORM_BYTE_ORDER) + +#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \ + defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \ + defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \ + defined( vax ) || defined( vms ) || defined( VMS ) || \ + defined( __VMS ) || defined( _M_X64 ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN + +#elif defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \ + defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \ + defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \ + defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \ + defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \ + defined( __VOS__ ) || defined( __TIGCC__ ) || defined( __TANDEM ) || \ + defined( THINK_C ) || defined( __VMCMS__ ) || defined( _AIX ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN + +#elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#else +# error Please edit lines 126 or 128 in brg_endian.h to set the platform byte order +#endif + +#endif + +#endif diff --git a/src/crypto/groestl/brg_types.h b/src/crypto/groestl/brg_types.h new file mode 100644 index 00000000..13b289b8 --- /dev/null +++ b/src/crypto/groestl/brg_types.h @@ -0,0 +1,231 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + (a few lines added by Soeren S. Thomsen, October 2008) + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + + The unsigned integer types defined here are of the form uint_t where + is the length of the type; for example, the unsigned 32-bit type is + 'uint_32t'. These are NOT the same as the 'C99 integer types' that are + defined in the inttypes.h and stdint.h headers since attempts to use these + types have shown that support for them is still highly variable. However, + since the latter are of the form uint_t, a regular expression search + and replace (in VC++ search on 'uint_{:z}t' and replace with 'uint\1_t') + can be used to convert the types used here to the C99 standard types. +*/ + +#ifndef _BRG_TYPES_H +#define _BRG_TYPES_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include + +#if defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) +# include +# define ptrint_t intptr_t +#elif defined( __GNUC__ ) && ( __GNUC__ >= 3 ) +# include +# define ptrint_t intptr_t +#else +# define ptrint_t int +#endif + +#ifndef BRG_UI8 +# define BRG_UI8 +# if UCHAR_MAX == 255u + typedef unsigned char uint_8t; +# else +# error Please define uint_8t as an 8-bit unsigned integer type in brg_types.h +# endif +#endif + +#ifndef BRG_UI16 +# define BRG_UI16 +# if USHRT_MAX == 65535u + typedef unsigned short uint_16t; +# else +# error Please define uint_16t as a 16-bit unsigned short type in brg_types.h +# endif +#endif + +#ifndef BRG_UI32 +# define BRG_UI32 +# if UINT_MAX == 4294967295u +# define li_32(h) 0x##h##u + typedef unsigned int uint_32t; +# elif ULONG_MAX == 4294967295u +# define li_32(h) 0x##h##ul + typedef unsigned long uint_32t; +# elif defined( _CRAY ) +# error This code needs 32-bit data types, which Cray machines do not provide +# else +# error Please define uint_32t as a 32-bit unsigned integer type in brg_types.h +# endif +#endif + +#ifndef BRG_UI64 +# if defined( __BORLANDC__ ) && !defined( __MSDOS__ ) +# define BRG_UI64 +# define li_64(h) 0x##h##ui64 + typedef unsigned __int64 uint_64t; +# elif defined( _MSC_VER ) && ( _MSC_VER < 1300 ) /* 1300 == VC++ 7.0 */ +# define BRG_UI64 +# define li_64(h) 0x##h##ui64 + typedef unsigned __int64 uint_64t; +# elif defined( __sun ) && defined( ULONG_MAX ) && ULONG_MAX == 0xfffffffful +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned long long uint_64t; +# elif defined( __MVS__ ) +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned int long long uint_64t; +# elif defined( UINT_MAX ) && UINT_MAX > 4294967295u +# if UINT_MAX == 18446744073709551615u +# define BRG_UI64 +# define li_64(h) 0x##h##u + typedef unsigned int uint_64t; +# endif +# elif defined( ULONG_MAX ) && ULONG_MAX > 4294967295u +# if ULONG_MAX == 18446744073709551615ul +# define BRG_UI64 +# define li_64(h) 0x##h##ul + typedef unsigned long uint_64t; +# endif +# elif defined( ULLONG_MAX ) && ULLONG_MAX > 4294967295u +# if ULLONG_MAX == 18446744073709551615ull +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned long long uint_64t; +# endif +# elif defined( ULONG_LONG_MAX ) && ULONG_LONG_MAX > 4294967295u +# if ULONG_LONG_MAX == 18446744073709551615ull +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned long long uint_64t; +# endif +# endif +#endif + +#if !defined( BRG_UI64 ) +# if defined( NEED_UINT_64T ) +# error Please define uint_64t as an unsigned 64 bit type in brg_types.h +# endif +#endif + +#ifndef RETURN_VALUES +# define RETURN_VALUES +# if defined( DLL_EXPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllexport ) void __stdcall +# define INT_RETURN __declspec( dllexport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllexport__ ) void +# define INT_RETURN __declspec( __dllexport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( DLL_IMPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllimport ) void __stdcall +# define INT_RETURN __declspec( dllimport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllimport__ ) void +# define INT_RETURN __declspec( __dllimport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( __WATCOMC__ ) +# define VOID_RETURN void __cdecl +# define INT_RETURN int __cdecl +# else +# define VOID_RETURN void +# define INT_RETURN int +# endif +#endif + +/* These defines are used to detect and set the memory alignment of pointers. + Note that offsets are in bytes. + + ALIGN_OFFSET(x,n) return the positive or zero offset of + the memory addressed by the pointer 'x' + from an address that is aligned on an + 'n' byte boundary ('n' is a power of 2) + + ALIGN_FLOOR(x,n) return a pointer that points to memory + that is aligned on an 'n' byte boundary + and is not higher than the memory address + pointed to by 'x' ('n' is a power of 2) + + ALIGN_CEIL(x,n) return a pointer that points to memory + that is aligned on an 'n' byte boundary + and is not lower than the memory address + pointed to by 'x' ('n' is a power of 2) +*/ + +#define ALIGN_OFFSET(x,n) (((ptrint_t)(x)) & ((n) - 1)) +#define ALIGN_FLOOR(x,n) ((uint_8t*)(x) - ( ((ptrint_t)(x)) & ((n) - 1))) +#define ALIGN_CEIL(x,n) ((uint_8t*)(x) + (-((ptrint_t)(x)) & ((n) - 1))) + +/* These defines are used to declare buffers in a way that allows + faster operations on longer variables to be used. In all these + defines 'size' must be a power of 2 and >= 8. NOTE that the + buffer size is in bytes but the type length is in bits + + UNIT_TYPEDEF(x,size) declares a variable 'x' of length + 'size' bits + + BUFR_TYPEDEF(x,size,bsize) declares a buffer 'x' of length 'bsize' + bytes defined as an array of variables + each of 'size' bits (bsize must be a + multiple of size / 8) + + UNIT_CAST(x,size) casts a variable to a type of + length 'size' bits + + UPTR_CAST(x,size) casts a pointer to a pointer to a + varaiable of length 'size' bits +*/ + +#define UI_TYPE(size) uint_##size##t +#define UNIT_TYPEDEF(x,size) typedef UI_TYPE(size) x +#define BUFR_TYPEDEF(x,size,bsize) typedef UI_TYPE(size) x[bsize / (size >> 3)] +#define UNIT_CAST(x,size) ((UI_TYPE(size) )(x)) +#define UPTR_CAST(x,size) ((UI_TYPE(size)*)(x)) + + /* Added by Soeren S. Thomsen (begin) */ +#define u8 uint_8t +#define u32 uint_32t +#define u64 uint_64t + /* (end) */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/crypto/groestl/tables.h b/src/crypto/groestl/tables.h new file mode 100644 index 00000000..923f9845 --- /dev/null +++ b/src/crypto/groestl/tables.h @@ -0,0 +1,39 @@ +#ifndef __tables_h +#define __tables_h + +#include "brg_endian.h" +#include "brg_types.h" + +#if defined(_MSC_VER) +#define groestl_ALIGN __declspec(align(64)) +#elif defined(__GNUC__) +#define groestl_ALIGN __attribute__ ((aligned(64))) +#else +#define groestl_ALIGN +#endif + +#if (PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN) +groestl_ALIGN const u32 T[8*256] = { + 0xc632f4a5UL,0xf86f9784UL,0xee5eb099UL,0xf67a8c8dUL,0xffe8170dUL,0xd60adcbdUL,0xde16c8b1UL,0x916dfc54UL,0x6090f050UL,0x02070503UL,0xce2ee0a9UL,0x56d1877dUL,0xe7cc2b19UL,0xb513a662UL,0x4d7c31e6UL,0xec59b59aUL,0x8f40cf45UL,0x1fa3bc9dUL,0x8949c040UL,0xfa689287UL,0xefd03f15UL,0xb29426ebUL,0x8ece40c9UL,0xfbe61d0bUL,0x416e2fecUL,0xb31aa967UL,0x5f431cfdUL,0x456025eaUL,0x23f9dabfUL,0x535102f7UL,0xe445a196UL,0x9b76ed5bUL,0x75285dc2UL,0xe1c5241cUL,0x3dd4e9aeUL,0x4cf2be6aUL,0x6c82ee5aUL,0x7ebdc341UL,0xf5f30602UL,0x8352d14fUL,0x688ce45cUL,0x515607f4UL,0xd18d5c34UL,0xf9e11808UL,0xe24cae93UL,0xab3e9573UL,0x6297f553UL,0x2a6b413fUL,0x081c140cUL,0x9563f652UL,0x46e9af65UL,0x9d7fe25eUL,0x30487828UL,0x37cff8a1UL,0x0a1b110fUL,0x2febc4b5UL,0x0e151b09UL,0x247e5a36UL,0x1badb69bUL,0xdf98473dUL,0xcda76a26UL,0x4ef5bb69UL,0x7f334ccdUL,0xea50ba9fUL,0x123f2d1bUL,0x1da4b99eUL,0x58c49c74UL,0x3446722eUL,0x3641772dUL,0xdc11cdb2UL,0xb49d29eeUL,0x5b4d16fbUL,0xa4a501f6UL,0x76a1d74dUL,0xb714a361UL,0x7d3449ceUL,0x52df8d7bUL,0xdd9f423eUL,0x5ecd9371UL,0x13b1a297UL,0xa6a204f5UL,0xb901b868UL,0x00000000UL,0xc1b5742cUL,0x40e0a060UL,0xe3c2211fUL,0x793a43c8UL,0xb69a2cedUL,0xd40dd9beUL,0x8d47ca46UL,0x671770d9UL,0x72afdd4bUL,0x94ed79deUL,0x98ff67d4UL,0xb09323e8UL,0x855bde4aUL,0xbb06bd6bUL,0xc5bb7e2aUL,0x4f7b34e5UL,0xedd73a16UL,0x86d254c5UL,0x9af862d7UL,0x6699ff55UL,0x11b6a794UL,0x8ac04acfUL,0xe9d93010UL,0x040e0a06UL,0xfe669881UL,0xa0ab0bf0UL,0x78b4cc44UL,0x25f0d5baUL,0x4b753ee3UL,0xa2ac0ef3UL,0x5d4419feUL,0x80db5bc0UL,0x0580858aUL,0x3fd3ecadUL,0x21fedfbcUL,0x70a8d848UL,0xf1fd0c04UL,0x63197adfUL,0x772f58c1UL,0xaf309f75UL,0x42e7a563UL,0x20705030UL,0xe5cb2e1aUL,0xfdef120eUL,0xbf08b76dUL,0x8155d44cUL,0x18243c14UL,0x26795f35UL,0xc3b2712fUL,0xbe8638e1UL,0x35c8fda2UL,0x88c74fccUL,0x2e654b39UL,0x936af957UL,0x55580df2UL,0xfc619d82UL,0x7ab3c947UL,0xc827efacUL,0xba8832e7UL,0x324f7d2bUL,0xe642a495UL,0xc03bfba0UL,0x19aab398UL,0x9ef668d1UL,0xa322817fUL,0x44eeaa66UL,0x54d6827eUL,0x3bdde6abUL,0x0b959e83UL,0x8cc945caUL,0xc7bc7b29UL,0x6b056ed3UL,0x286c443cUL,0xa72c8b79UL,0xbc813de2UL,0x1631271dUL,0xad379a76UL,0xdb964d3bUL,0x649efa56UL,0x74a6d24eUL,0x1436221eUL,0x92e476dbUL,0x0c121e0aUL,0x48fcb46cUL,0xb88f37e4UL,0x9f78e75dUL,0xbd0fb26eUL,0x43692aefUL,0xc435f1a6UL,0x39dae3a8UL,0x31c6f7a4UL,0xd38a5937UL,0xf274868bUL,0xd5835632UL,0x8b4ec543UL,0x6e85eb59UL,0xda18c2b7UL,0x018e8f8cUL,0xb11dac64UL,0x9cf16dd2UL,0x49723be0UL,0xd81fc7b4UL,0xacb915faUL,0xf3fa0907UL,0xcfa06f25UL,0xca20eaafUL,0xf47d898eUL,0x476720e9UL,0x10382818UL,0x6f0b64d5UL,0xf0738388UL,0x4afbb16fUL,0x5cca9672UL,0x38546c24UL,0x575f08f1UL,0x732152c7UL,0x9764f351UL,0xcbae6523UL,0xa125847cUL,0xe857bf9cUL,0x3e5d6321UL,0x96ea7cddUL,0x611e7fdcUL,0x0d9c9186UL,0x0f9b9485UL,0xe04bab90UL,0x7cbac642UL,0x712657c4UL,0xcc29e5aaUL,0x90e373d8UL,0x06090f05UL,0xf7f40301UL,0x1c2a3612UL,0xc23cfea3UL,0x6a8be15fUL,0xaebe10f9UL,0x69026bd0UL,0x17bfa891UL,0x9971e858UL,0x3a536927UL,0x27f7d0b9UL,0xd9914838UL,0xebde3513UL,0x2be5ceb3UL,0x22775533UL,0xd204d6bbUL,0xa9399070UL,0x07878089UL,0x33c1f2a7UL,0x2decc1b6UL,0x3c5a6622UL,0x15b8ad92UL,0xc9a96020UL,0x875cdb49UL,0xaab01affUL,0x50d88878UL,0xa52b8e7aUL,0x03898a8fUL,0x594a13f8UL,0x09929b80UL,0x1a233917UL,0x651075daUL,0xd7845331UL,0x84d551c6UL,0xd003d3b8UL,0x82dc5ec3UL,0x29e2cbb0UL,0x5ac39977UL,0x1e2d3311UL,0x7b3d46cbUL,0xa8b71ffcUL,0x6d0c61d6UL,0x2c624e3aUL, + 0xc6c632f4UL,0xf8f86f97UL,0xeeee5eb0UL,0xf6f67a8cUL,0xffffe817UL,0xd6d60adcUL,0xdede16c8UL,0x91916dfcUL,0x606090f0UL,0x02020705UL,0xcece2ee0UL,0x5656d187UL,0xe7e7cc2bUL,0xb5b513a6UL,0x4d4d7c31UL,0xecec59b5UL,0x8f8f40cfUL,0x1f1fa3bcUL,0x898949c0UL,0xfafa6892UL,0xefefd03fUL,0xb2b29426UL,0x8e8ece40UL,0xfbfbe61dUL,0x41416e2fUL,0xb3b31aa9UL,0x5f5f431cUL,0x45456025UL,0x2323f9daUL,0x53535102UL,0xe4e445a1UL,0x9b9b76edUL,0x7575285dUL,0xe1e1c524UL,0x3d3dd4e9UL,0x4c4cf2beUL,0x6c6c82eeUL,0x7e7ebdc3UL,0xf5f5f306UL,0x838352d1UL,0x68688ce4UL,0x51515607UL,0xd1d18d5cUL,0xf9f9e118UL,0xe2e24caeUL,0xabab3e95UL,0x626297f5UL,0x2a2a6b41UL,0x08081c14UL,0x959563f6UL,0x4646e9afUL,0x9d9d7fe2UL,0x30304878UL,0x3737cff8UL,0x0a0a1b11UL,0x2f2febc4UL,0x0e0e151bUL,0x24247e5aUL,0x1b1badb6UL,0xdfdf9847UL,0xcdcda76aUL,0x4e4ef5bbUL,0x7f7f334cUL,0xeaea50baUL,0x12123f2dUL,0x1d1da4b9UL,0x5858c49cUL,0x34344672UL,0x36364177UL,0xdcdc11cdUL,0xb4b49d29UL,0x5b5b4d16UL,0xa4a4a501UL,0x7676a1d7UL,0xb7b714a3UL,0x7d7d3449UL,0x5252df8dUL,0xdddd9f42UL,0x5e5ecd93UL,0x1313b1a2UL,0xa6a6a204UL,0xb9b901b8UL,0x00000000UL,0xc1c1b574UL,0x4040e0a0UL,0xe3e3c221UL,0x79793a43UL,0xb6b69a2cUL,0xd4d40dd9UL,0x8d8d47caUL,0x67671770UL,0x7272afddUL,0x9494ed79UL,0x9898ff67UL,0xb0b09323UL,0x85855bdeUL,0xbbbb06bdUL,0xc5c5bb7eUL,0x4f4f7b34UL,0xededd73aUL,0x8686d254UL,0x9a9af862UL,0x666699ffUL,0x1111b6a7UL,0x8a8ac04aUL,0xe9e9d930UL,0x04040e0aUL,0xfefe6698UL,0xa0a0ab0bUL,0x7878b4ccUL,0x2525f0d5UL,0x4b4b753eUL,0xa2a2ac0eUL,0x5d5d4419UL,0x8080db5bUL,0x05058085UL,0x3f3fd3ecUL,0x2121fedfUL,0x7070a8d8UL,0xf1f1fd0cUL,0x6363197aUL,0x77772f58UL,0xafaf309fUL,0x4242e7a5UL,0x20207050UL,0xe5e5cb2eUL,0xfdfdef12UL,0xbfbf08b7UL,0x818155d4UL,0x1818243cUL,0x2626795fUL,0xc3c3b271UL,0xbebe8638UL,0x3535c8fdUL,0x8888c74fUL,0x2e2e654bUL,0x93936af9UL,0x5555580dUL,0xfcfc619dUL,0x7a7ab3c9UL,0xc8c827efUL,0xbaba8832UL,0x32324f7dUL,0xe6e642a4UL,0xc0c03bfbUL,0x1919aab3UL,0x9e9ef668UL,0xa3a32281UL,0x4444eeaaUL,0x5454d682UL,0x3b3bdde6UL,0x0b0b959eUL,0x8c8cc945UL,0xc7c7bc7bUL,0x6b6b056eUL,0x28286c44UL,0xa7a72c8bUL,0xbcbc813dUL,0x16163127UL,0xadad379aUL,0xdbdb964dUL,0x64649efaUL,0x7474a6d2UL,0x14143622UL,0x9292e476UL,0x0c0c121eUL,0x4848fcb4UL,0xb8b88f37UL,0x9f9f78e7UL,0xbdbd0fb2UL,0x4343692aUL,0xc4c435f1UL,0x3939dae3UL,0x3131c6f7UL,0xd3d38a59UL,0xf2f27486UL,0xd5d58356UL,0x8b8b4ec5UL,0x6e6e85ebUL,0xdada18c2UL,0x01018e8fUL,0xb1b11dacUL,0x9c9cf16dUL,0x4949723bUL,0xd8d81fc7UL,0xacacb915UL,0xf3f3fa09UL,0xcfcfa06fUL,0xcaca20eaUL,0xf4f47d89UL,0x47476720UL,0x10103828UL,0x6f6f0b64UL,0xf0f07383UL,0x4a4afbb1UL,0x5c5cca96UL,0x3838546cUL,0x57575f08UL,0x73732152UL,0x979764f3UL,0xcbcbae65UL,0xa1a12584UL,0xe8e857bfUL,0x3e3e5d63UL,0x9696ea7cUL,0x61611e7fUL,0x0d0d9c91UL,0x0f0f9b94UL,0xe0e04babUL,0x7c7cbac6UL,0x71712657UL,0xcccc29e5UL,0x9090e373UL,0x0606090fUL,0xf7f7f403UL,0x1c1c2a36UL,0xc2c23cfeUL,0x6a6a8be1UL,0xaeaebe10UL,0x6969026bUL,0x1717bfa8UL,0x999971e8UL,0x3a3a5369UL,0x2727f7d0UL,0xd9d99148UL,0xebebde35UL,0x2b2be5ceUL,0x22227755UL,0xd2d204d6UL,0xa9a93990UL,0x07078780UL,0x3333c1f2UL,0x2d2decc1UL,0x3c3c5a66UL,0x1515b8adUL,0xc9c9a960UL,0x87875cdbUL,0xaaaab01aUL,0x5050d888UL,0xa5a52b8eUL,0x0303898aUL,0x59594a13UL,0x0909929bUL,0x1a1a2339UL,0x65651075UL,0xd7d78453UL,0x8484d551UL,0xd0d003d3UL,0x8282dc5eUL,0x2929e2cbUL,0x5a5ac399UL,0x1e1e2d33UL,0x7b7b3d46UL,0xa8a8b71fUL,0x6d6d0c61UL,0x2c2c624eUL, + 0xa5c6c632UL,0x84f8f86fUL,0x99eeee5eUL,0x8df6f67aUL,0x0dffffe8UL,0xbdd6d60aUL,0xb1dede16UL,0x5491916dUL,0x50606090UL,0x03020207UL,0xa9cece2eUL,0x7d5656d1UL,0x19e7e7ccUL,0x62b5b513UL,0xe64d4d7cUL,0x9aecec59UL,0x458f8f40UL,0x9d1f1fa3UL,0x40898949UL,0x87fafa68UL,0x15efefd0UL,0xebb2b294UL,0xc98e8eceUL,0x0bfbfbe6UL,0xec41416eUL,0x67b3b31aUL,0xfd5f5f43UL,0xea454560UL,0xbf2323f9UL,0xf7535351UL,0x96e4e445UL,0x5b9b9b76UL,0xc2757528UL,0x1ce1e1c5UL,0xae3d3dd4UL,0x6a4c4cf2UL,0x5a6c6c82UL,0x417e7ebdUL,0x02f5f5f3UL,0x4f838352UL,0x5c68688cUL,0xf4515156UL,0x34d1d18dUL,0x08f9f9e1UL,0x93e2e24cUL,0x73abab3eUL,0x53626297UL,0x3f2a2a6bUL,0x0c08081cUL,0x52959563UL,0x654646e9UL,0x5e9d9d7fUL,0x28303048UL,0xa13737cfUL,0x0f0a0a1bUL,0xb52f2febUL,0x090e0e15UL,0x3624247eUL,0x9b1b1badUL,0x3ddfdf98UL,0x26cdcda7UL,0x694e4ef5UL,0xcd7f7f33UL,0x9feaea50UL,0x1b12123fUL,0x9e1d1da4UL,0x745858c4UL,0x2e343446UL,0x2d363641UL,0xb2dcdc11UL,0xeeb4b49dUL,0xfb5b5b4dUL,0xf6a4a4a5UL,0x4d7676a1UL,0x61b7b714UL,0xce7d7d34UL,0x7b5252dfUL,0x3edddd9fUL,0x715e5ecdUL,0x971313b1UL,0xf5a6a6a2UL,0x68b9b901UL,0x00000000UL,0x2cc1c1b5UL,0x604040e0UL,0x1fe3e3c2UL,0xc879793aUL,0xedb6b69aUL,0xbed4d40dUL,0x468d8d47UL,0xd9676717UL,0x4b7272afUL,0xde9494edUL,0xd49898ffUL,0xe8b0b093UL,0x4a85855bUL,0x6bbbbb06UL,0x2ac5c5bbUL,0xe54f4f7bUL,0x16ededd7UL,0xc58686d2UL,0xd79a9af8UL,0x55666699UL,0x941111b6UL,0xcf8a8ac0UL,0x10e9e9d9UL,0x0604040eUL,0x81fefe66UL,0xf0a0a0abUL,0x447878b4UL,0xba2525f0UL,0xe34b4b75UL,0xf3a2a2acUL,0xfe5d5d44UL,0xc08080dbUL,0x8a050580UL,0xad3f3fd3UL,0xbc2121feUL,0x487070a8UL,0x04f1f1fdUL,0xdf636319UL,0xc177772fUL,0x75afaf30UL,0x634242e7UL,0x30202070UL,0x1ae5e5cbUL,0x0efdfdefUL,0x6dbfbf08UL,0x4c818155UL,0x14181824UL,0x35262679UL,0x2fc3c3b2UL,0xe1bebe86UL,0xa23535c8UL,0xcc8888c7UL,0x392e2e65UL,0x5793936aUL,0xf2555558UL,0x82fcfc61UL,0x477a7ab3UL,0xacc8c827UL,0xe7baba88UL,0x2b32324fUL,0x95e6e642UL,0xa0c0c03bUL,0x981919aaUL,0xd19e9ef6UL,0x7fa3a322UL,0x664444eeUL,0x7e5454d6UL,0xab3b3bddUL,0x830b0b95UL,0xca8c8cc9UL,0x29c7c7bcUL,0xd36b6b05UL,0x3c28286cUL,0x79a7a72cUL,0xe2bcbc81UL,0x1d161631UL,0x76adad37UL,0x3bdbdb96UL,0x5664649eUL,0x4e7474a6UL,0x1e141436UL,0xdb9292e4UL,0x0a0c0c12UL,0x6c4848fcUL,0xe4b8b88fUL,0x5d9f9f78UL,0x6ebdbd0fUL,0xef434369UL,0xa6c4c435UL,0xa83939daUL,0xa43131c6UL,0x37d3d38aUL,0x8bf2f274UL,0x32d5d583UL,0x438b8b4eUL,0x596e6e85UL,0xb7dada18UL,0x8c01018eUL,0x64b1b11dUL,0xd29c9cf1UL,0xe0494972UL,0xb4d8d81fUL,0xfaacacb9UL,0x07f3f3faUL,0x25cfcfa0UL,0xafcaca20UL,0x8ef4f47dUL,0xe9474767UL,0x18101038UL,0xd56f6f0bUL,0x88f0f073UL,0x6f4a4afbUL,0x725c5ccaUL,0x24383854UL,0xf157575fUL,0xc7737321UL,0x51979764UL,0x23cbcbaeUL,0x7ca1a125UL,0x9ce8e857UL,0x213e3e5dUL,0xdd9696eaUL,0xdc61611eUL,0x860d0d9cUL,0x850f0f9bUL,0x90e0e04bUL,0x427c7cbaUL,0xc4717126UL,0xaacccc29UL,0xd89090e3UL,0x05060609UL,0x01f7f7f4UL,0x121c1c2aUL,0xa3c2c23cUL,0x5f6a6a8bUL,0xf9aeaebeUL,0xd0696902UL,0x911717bfUL,0x58999971UL,0x273a3a53UL,0xb92727f7UL,0x38d9d991UL,0x13ebebdeUL,0xb32b2be5UL,0x33222277UL,0xbbd2d204UL,0x70a9a939UL,0x89070787UL,0xa73333c1UL,0xb62d2decUL,0x223c3c5aUL,0x921515b8UL,0x20c9c9a9UL,0x4987875cUL,0xffaaaab0UL,0x785050d8UL,0x7aa5a52bUL,0x8f030389UL,0xf859594aUL,0x80090992UL,0x171a1a23UL,0xda656510UL,0x31d7d784UL,0xc68484d5UL,0xb8d0d003UL,0xc38282dcUL,0xb02929e2UL,0x775a5ac3UL,0x111e1e2dUL,0xcb7b7b3dUL,0xfca8a8b7UL,0xd66d6d0cUL,0x3a2c2c62UL, + 0x97a5c6c6UL,0xeb84f8f8UL,0xc799eeeeUL,0xf78df6f6UL,0xe50dffffUL,0xb7bdd6d6UL,0xa7b1dedeUL,0x39549191UL,0xc0506060UL,0x04030202UL,0x87a9ceceUL,0xac7d5656UL,0xd519e7e7UL,0x7162b5b5UL,0x9ae64d4dUL,0xc39aececUL,0x05458f8fUL,0x3e9d1f1fUL,0x09408989UL,0xef87fafaUL,0xc515efefUL,0x7febb2b2UL,0x07c98e8eUL,0xed0bfbfbUL,0x82ec4141UL,0x7d67b3b3UL,0xbefd5f5fUL,0x8aea4545UL,0x46bf2323UL,0xa6f75353UL,0xd396e4e4UL,0x2d5b9b9bUL,0xeac27575UL,0xd91ce1e1UL,0x7aae3d3dUL,0x986a4c4cUL,0xd85a6c6cUL,0xfc417e7eUL,0xf102f5f5UL,0x1d4f8383UL,0xd05c6868UL,0xa2f45151UL,0xb934d1d1UL,0xe908f9f9UL,0xdf93e2e2UL,0x4d73ababUL,0xc4536262UL,0x543f2a2aUL,0x100c0808UL,0x31529595UL,0x8c654646UL,0x215e9d9dUL,0x60283030UL,0x6ea13737UL,0x140f0a0aUL,0x5eb52f2fUL,0x1c090e0eUL,0x48362424UL,0x369b1b1bUL,0xa53ddfdfUL,0x8126cdcdUL,0x9c694e4eUL,0xfecd7f7fUL,0xcf9feaeaUL,0x241b1212UL,0x3a9e1d1dUL,0xb0745858UL,0x682e3434UL,0x6c2d3636UL,0xa3b2dcdcUL,0x73eeb4b4UL,0xb6fb5b5bUL,0x53f6a4a4UL,0xec4d7676UL,0x7561b7b7UL,0xface7d7dUL,0xa47b5252UL,0xa13eddddUL,0xbc715e5eUL,0x26971313UL,0x57f5a6a6UL,0x6968b9b9UL,0x00000000UL,0x992cc1c1UL,0x80604040UL,0xdd1fe3e3UL,0xf2c87979UL,0x77edb6b6UL,0xb3bed4d4UL,0x01468d8dUL,0xced96767UL,0xe44b7272UL,0x33de9494UL,0x2bd49898UL,0x7be8b0b0UL,0x114a8585UL,0x6d6bbbbbUL,0x912ac5c5UL,0x9ee54f4fUL,0xc116ededUL,0x17c58686UL,0x2fd79a9aUL,0xcc556666UL,0x22941111UL,0x0fcf8a8aUL,0xc910e9e9UL,0x08060404UL,0xe781fefeUL,0x5bf0a0a0UL,0xf0447878UL,0x4aba2525UL,0x96e34b4bUL,0x5ff3a2a2UL,0xbafe5d5dUL,0x1bc08080UL,0x0a8a0505UL,0x7ead3f3fUL,0x42bc2121UL,0xe0487070UL,0xf904f1f1UL,0xc6df6363UL,0xeec17777UL,0x4575afafUL,0x84634242UL,0x40302020UL,0xd11ae5e5UL,0xe10efdfdUL,0x656dbfbfUL,0x194c8181UL,0x30141818UL,0x4c352626UL,0x9d2fc3c3UL,0x67e1bebeUL,0x6aa23535UL,0x0bcc8888UL,0x5c392e2eUL,0x3d579393UL,0xaaf25555UL,0xe382fcfcUL,0xf4477a7aUL,0x8bacc8c8UL,0x6fe7babaUL,0x642b3232UL,0xd795e6e6UL,0x9ba0c0c0UL,0x32981919UL,0x27d19e9eUL,0x5d7fa3a3UL,0x88664444UL,0xa87e5454UL,0x76ab3b3bUL,0x16830b0bUL,0x03ca8c8cUL,0x9529c7c7UL,0xd6d36b6bUL,0x503c2828UL,0x5579a7a7UL,0x63e2bcbcUL,0x2c1d1616UL,0x4176adadUL,0xad3bdbdbUL,0xc8566464UL,0xe84e7474UL,0x281e1414UL,0x3fdb9292UL,0x180a0c0cUL,0x906c4848UL,0x6be4b8b8UL,0x255d9f9fUL,0x616ebdbdUL,0x86ef4343UL,0x93a6c4c4UL,0x72a83939UL,0x62a43131UL,0xbd37d3d3UL,0xff8bf2f2UL,0xb132d5d5UL,0x0d438b8bUL,0xdc596e6eUL,0xafb7dadaUL,0x028c0101UL,0x7964b1b1UL,0x23d29c9cUL,0x92e04949UL,0xabb4d8d8UL,0x43faacacUL,0xfd07f3f3UL,0x8525cfcfUL,0x8fafcacaUL,0xf38ef4f4UL,0x8ee94747UL,0x20181010UL,0xded56f6fUL,0xfb88f0f0UL,0x946f4a4aUL,0xb8725c5cUL,0x70243838UL,0xaef15757UL,0xe6c77373UL,0x35519797UL,0x8d23cbcbUL,0x597ca1a1UL,0xcb9ce8e8UL,0x7c213e3eUL,0x37dd9696UL,0xc2dc6161UL,0x1a860d0dUL,0x1e850f0fUL,0xdb90e0e0UL,0xf8427c7cUL,0xe2c47171UL,0x83aaccccUL,0x3bd89090UL,0x0c050606UL,0xf501f7f7UL,0x38121c1cUL,0x9fa3c2c2UL,0xd45f6a6aUL,0x47f9aeaeUL,0xd2d06969UL,0x2e911717UL,0x29589999UL,0x74273a3aUL,0x4eb92727UL,0xa938d9d9UL,0xcd13ebebUL,0x56b32b2bUL,0x44332222UL,0xbfbbd2d2UL,0x4970a9a9UL,0x0e890707UL,0x66a73333UL,0x5ab62d2dUL,0x78223c3cUL,0x2a921515UL,0x8920c9c9UL,0x15498787UL,0x4fffaaaaUL,0xa0785050UL,0x517aa5a5UL,0x068f0303UL,0xb2f85959UL,0x12800909UL,0x34171a1aUL,0xcada6565UL,0xb531d7d7UL,0x13c68484UL,0xbbb8d0d0UL,0x1fc38282UL,0x52b02929UL,0xb4775a5aUL,0x3c111e1eUL,0xf6cb7b7bUL,0x4bfca8a8UL,0xdad66d6dUL,0x583a2c2cUL, + 0xf497a5c6UL,0x97eb84f8UL,0xb0c799eeUL,0x8cf78df6UL,0x17e50dffUL,0xdcb7bdd6UL,0xc8a7b1deUL,0xfc395491UL,0xf0c05060UL,0x05040302UL,0xe087a9ceUL,0x87ac7d56UL,0x2bd519e7UL,0xa67162b5UL,0x319ae64dUL,0xb5c39aecUL,0xcf05458fUL,0xbc3e9d1fUL,0xc0094089UL,0x92ef87faUL,0x3fc515efUL,0x267febb2UL,0x4007c98eUL,0x1ded0bfbUL,0x2f82ec41UL,0xa97d67b3UL,0x1cbefd5fUL,0x258aea45UL,0xda46bf23UL,0x02a6f753UL,0xa1d396e4UL,0xed2d5b9bUL,0x5deac275UL,0x24d91ce1UL,0xe97aae3dUL,0xbe986a4cUL,0xeed85a6cUL,0xc3fc417eUL,0x06f102f5UL,0xd11d4f83UL,0xe4d05c68UL,0x07a2f451UL,0x5cb934d1UL,0x18e908f9UL,0xaedf93e2UL,0x954d73abUL,0xf5c45362UL,0x41543f2aUL,0x14100c08UL,0xf6315295UL,0xaf8c6546UL,0xe2215e9dUL,0x78602830UL,0xf86ea137UL,0x11140f0aUL,0xc45eb52fUL,0x1b1c090eUL,0x5a483624UL,0xb6369b1bUL,0x47a53ddfUL,0x6a8126cdUL,0xbb9c694eUL,0x4cfecd7fUL,0xbacf9feaUL,0x2d241b12UL,0xb93a9e1dUL,0x9cb07458UL,0x72682e34UL,0x776c2d36UL,0xcda3b2dcUL,0x2973eeb4UL,0x16b6fb5bUL,0x0153f6a4UL,0xd7ec4d76UL,0xa37561b7UL,0x49face7dUL,0x8da47b52UL,0x42a13eddUL,0x93bc715eUL,0xa2269713UL,0x0457f5a6UL,0xb86968b9UL,0x00000000UL,0x74992cc1UL,0xa0806040UL,0x21dd1fe3UL,0x43f2c879UL,0x2c77edb6UL,0xd9b3bed4UL,0xca01468dUL,0x70ced967UL,0xdde44b72UL,0x7933de94UL,0x672bd498UL,0x237be8b0UL,0xde114a85UL,0xbd6d6bbbUL,0x7e912ac5UL,0x349ee54fUL,0x3ac116edUL,0x5417c586UL,0x622fd79aUL,0xffcc5566UL,0xa7229411UL,0x4a0fcf8aUL,0x30c910e9UL,0x0a080604UL,0x98e781feUL,0x0b5bf0a0UL,0xccf04478UL,0xd54aba25UL,0x3e96e34bUL,0x0e5ff3a2UL,0x19bafe5dUL,0x5b1bc080UL,0x850a8a05UL,0xec7ead3fUL,0xdf42bc21UL,0xd8e04870UL,0x0cf904f1UL,0x7ac6df63UL,0x58eec177UL,0x9f4575afUL,0xa5846342UL,0x50403020UL,0x2ed11ae5UL,0x12e10efdUL,0xb7656dbfUL,0xd4194c81UL,0x3c301418UL,0x5f4c3526UL,0x719d2fc3UL,0x3867e1beUL,0xfd6aa235UL,0x4f0bcc88UL,0x4b5c392eUL,0xf93d5793UL,0x0daaf255UL,0x9de382fcUL,0xc9f4477aUL,0xef8bacc8UL,0x326fe7baUL,0x7d642b32UL,0xa4d795e6UL,0xfb9ba0c0UL,0xb3329819UL,0x6827d19eUL,0x815d7fa3UL,0xaa886644UL,0x82a87e54UL,0xe676ab3bUL,0x9e16830bUL,0x4503ca8cUL,0x7b9529c7UL,0x6ed6d36bUL,0x44503c28UL,0x8b5579a7UL,0x3d63e2bcUL,0x272c1d16UL,0x9a4176adUL,0x4dad3bdbUL,0xfac85664UL,0xd2e84e74UL,0x22281e14UL,0x763fdb92UL,0x1e180a0cUL,0xb4906c48UL,0x376be4b8UL,0xe7255d9fUL,0xb2616ebdUL,0x2a86ef43UL,0xf193a6c4UL,0xe372a839UL,0xf762a431UL,0x59bd37d3UL,0x86ff8bf2UL,0x56b132d5UL,0xc50d438bUL,0xebdc596eUL,0xc2afb7daUL,0x8f028c01UL,0xac7964b1UL,0x6d23d29cUL,0x3b92e049UL,0xc7abb4d8UL,0x1543faacUL,0x09fd07f3UL,0x6f8525cfUL,0xea8fafcaUL,0x89f38ef4UL,0x208ee947UL,0x28201810UL,0x64ded56fUL,0x83fb88f0UL,0xb1946f4aUL,0x96b8725cUL,0x6c702438UL,0x08aef157UL,0x52e6c773UL,0xf3355197UL,0x658d23cbUL,0x84597ca1UL,0xbfcb9ce8UL,0x637c213eUL,0x7c37dd96UL,0x7fc2dc61UL,0x911a860dUL,0x941e850fUL,0xabdb90e0UL,0xc6f8427cUL,0x57e2c471UL,0xe583aaccUL,0x733bd890UL,0x0f0c0506UL,0x03f501f7UL,0x3638121cUL,0xfe9fa3c2UL,0xe1d45f6aUL,0x1047f9aeUL,0x6bd2d069UL,0xa82e9117UL,0xe8295899UL,0x6974273aUL,0xd04eb927UL,0x48a938d9UL,0x35cd13ebUL,0xce56b32bUL,0x55443322UL,0xd6bfbbd2UL,0x904970a9UL,0x800e8907UL,0xf266a733UL,0xc15ab62dUL,0x6678223cUL,0xad2a9215UL,0x608920c9UL,0xdb154987UL,0x1a4fffaaUL,0x88a07850UL,0x8e517aa5UL,0x8a068f03UL,0x13b2f859UL,0x9b128009UL,0x3934171aUL,0x75cada65UL,0x53b531d7UL,0x5113c684UL,0xd3bbb8d0UL,0x5e1fc382UL,0xcb52b029UL,0x99b4775aUL,0x333c111eUL,0x46f6cb7bUL,0x1f4bfca8UL,0x61dad66dUL,0x4e583a2cUL, + 0xa5f497a5UL,0x8497eb84UL,0x99b0c799UL,0x8d8cf78dUL,0x0d17e50dUL,0xbddcb7bdUL,0xb1c8a7b1UL,0x54fc3954UL,0x50f0c050UL,0x03050403UL,0xa9e087a9UL,0x7d87ac7dUL,0x192bd519UL,0x62a67162UL,0xe6319ae6UL,0x9ab5c39aUL,0x45cf0545UL,0x9dbc3e9dUL,0x40c00940UL,0x8792ef87UL,0x153fc515UL,0xeb267febUL,0xc94007c9UL,0x0b1ded0bUL,0xec2f82ecUL,0x67a97d67UL,0xfd1cbefdUL,0xea258aeaUL,0xbfda46bfUL,0xf702a6f7UL,0x96a1d396UL,0x5bed2d5bUL,0xc25deac2UL,0x1c24d91cUL,0xaee97aaeUL,0x6abe986aUL,0x5aeed85aUL,0x41c3fc41UL,0x0206f102UL,0x4fd11d4fUL,0x5ce4d05cUL,0xf407a2f4UL,0x345cb934UL,0x0818e908UL,0x93aedf93UL,0x73954d73UL,0x53f5c453UL,0x3f41543fUL,0x0c14100cUL,0x52f63152UL,0x65af8c65UL,0x5ee2215eUL,0x28786028UL,0xa1f86ea1UL,0x0f11140fUL,0xb5c45eb5UL,0x091b1c09UL,0x365a4836UL,0x9bb6369bUL,0x3d47a53dUL,0x266a8126UL,0x69bb9c69UL,0xcd4cfecdUL,0x9fbacf9fUL,0x1b2d241bUL,0x9eb93a9eUL,0x749cb074UL,0x2e72682eUL,0x2d776c2dUL,0xb2cda3b2UL,0xee2973eeUL,0xfb16b6fbUL,0xf60153f6UL,0x4dd7ec4dUL,0x61a37561UL,0xce49faceUL,0x7b8da47bUL,0x3e42a13eUL,0x7193bc71UL,0x97a22697UL,0xf50457f5UL,0x68b86968UL,0x00000000UL,0x2c74992cUL,0x60a08060UL,0x1f21dd1fUL,0xc843f2c8UL,0xed2c77edUL,0xbed9b3beUL,0x46ca0146UL,0xd970ced9UL,0x4bdde44bUL,0xde7933deUL,0xd4672bd4UL,0xe8237be8UL,0x4ade114aUL,0x6bbd6d6bUL,0x2a7e912aUL,0xe5349ee5UL,0x163ac116UL,0xc55417c5UL,0xd7622fd7UL,0x55ffcc55UL,0x94a72294UL,0xcf4a0fcfUL,0x1030c910UL,0x060a0806UL,0x8198e781UL,0xf00b5bf0UL,0x44ccf044UL,0xbad54abaUL,0xe33e96e3UL,0xf30e5ff3UL,0xfe19bafeUL,0xc05b1bc0UL,0x8a850a8aUL,0xadec7eadUL,0xbcdf42bcUL,0x48d8e048UL,0x040cf904UL,0xdf7ac6dfUL,0xc158eec1UL,0x759f4575UL,0x63a58463UL,0x30504030UL,0x1a2ed11aUL,0x0e12e10eUL,0x6db7656dUL,0x4cd4194cUL,0x143c3014UL,0x355f4c35UL,0x2f719d2fUL,0xe13867e1UL,0xa2fd6aa2UL,0xcc4f0bccUL,0x394b5c39UL,0x57f93d57UL,0xf20daaf2UL,0x829de382UL,0x47c9f447UL,0xacef8bacUL,0xe7326fe7UL,0x2b7d642bUL,0x95a4d795UL,0xa0fb9ba0UL,0x98b33298UL,0xd16827d1UL,0x7f815d7fUL,0x66aa8866UL,0x7e82a87eUL,0xabe676abUL,0x839e1683UL,0xca4503caUL,0x297b9529UL,0xd36ed6d3UL,0x3c44503cUL,0x798b5579UL,0xe23d63e2UL,0x1d272c1dUL,0x769a4176UL,0x3b4dad3bUL,0x56fac856UL,0x4ed2e84eUL,0x1e22281eUL,0xdb763fdbUL,0x0a1e180aUL,0x6cb4906cUL,0xe4376be4UL,0x5de7255dUL,0x6eb2616eUL,0xef2a86efUL,0xa6f193a6UL,0xa8e372a8UL,0xa4f762a4UL,0x3759bd37UL,0x8b86ff8bUL,0x3256b132UL,0x43c50d43UL,0x59ebdc59UL,0xb7c2afb7UL,0x8c8f028cUL,0x64ac7964UL,0xd26d23d2UL,0xe03b92e0UL,0xb4c7abb4UL,0xfa1543faUL,0x0709fd07UL,0x256f8525UL,0xafea8fafUL,0x8e89f38eUL,0xe9208ee9UL,0x18282018UL,0xd564ded5UL,0x8883fb88UL,0x6fb1946fUL,0x7296b872UL,0x246c7024UL,0xf108aef1UL,0xc752e6c7UL,0x51f33551UL,0x23658d23UL,0x7c84597cUL,0x9cbfcb9cUL,0x21637c21UL,0xdd7c37ddUL,0xdc7fc2dcUL,0x86911a86UL,0x85941e85UL,0x90abdb90UL,0x42c6f842UL,0xc457e2c4UL,0xaae583aaUL,0xd8733bd8UL,0x050f0c05UL,0x0103f501UL,0x12363812UL,0xa3fe9fa3UL,0x5fe1d45fUL,0xf91047f9UL,0xd06bd2d0UL,0x91a82e91UL,0x58e82958UL,0x27697427UL,0xb9d04eb9UL,0x3848a938UL,0x1335cd13UL,0xb3ce56b3UL,0x33554433UL,0xbbd6bfbbUL,0x70904970UL,0x89800e89UL,0xa7f266a7UL,0xb6c15ab6UL,0x22667822UL,0x92ad2a92UL,0x20608920UL,0x49db1549UL,0xff1a4fffUL,0x7888a078UL,0x7a8e517aUL,0x8f8a068fUL,0xf813b2f8UL,0x809b1280UL,0x17393417UL,0xda75cadaUL,0x3153b531UL,0xc65113c6UL,0xb8d3bbb8UL,0xc35e1fc3UL,0xb0cb52b0UL,0x7799b477UL,0x11333c11UL,0xcb46f6cbUL,0xfc1f4bfcUL,0xd661dad6UL,0x3a4e583aUL, + 0xf4a5f497UL,0x978497ebUL,0xb099b0c7UL,0x8c8d8cf7UL,0x170d17e5UL,0xdcbddcb7UL,0xc8b1c8a7UL,0xfc54fc39UL,0xf050f0c0UL,0x05030504UL,0xe0a9e087UL,0x877d87acUL,0x2b192bd5UL,0xa662a671UL,0x31e6319aUL,0xb59ab5c3UL,0xcf45cf05UL,0xbc9dbc3eUL,0xc040c009UL,0x928792efUL,0x3f153fc5UL,0x26eb267fUL,0x40c94007UL,0x1d0b1dedUL,0x2fec2f82UL,0xa967a97dUL,0x1cfd1cbeUL,0x25ea258aUL,0xdabfda46UL,0x02f702a6UL,0xa196a1d3UL,0xed5bed2dUL,0x5dc25deaUL,0x241c24d9UL,0xe9aee97aUL,0xbe6abe98UL,0xee5aeed8UL,0xc341c3fcUL,0x060206f1UL,0xd14fd11dUL,0xe45ce4d0UL,0x07f407a2UL,0x5c345cb9UL,0x180818e9UL,0xae93aedfUL,0x9573954dUL,0xf553f5c4UL,0x413f4154UL,0x140c1410UL,0xf652f631UL,0xaf65af8cUL,0xe25ee221UL,0x78287860UL,0xf8a1f86eUL,0x110f1114UL,0xc4b5c45eUL,0x1b091b1cUL,0x5a365a48UL,0xb69bb636UL,0x473d47a5UL,0x6a266a81UL,0xbb69bb9cUL,0x4ccd4cfeUL,0xba9fbacfUL,0x2d1b2d24UL,0xb99eb93aUL,0x9c749cb0UL,0x722e7268UL,0x772d776cUL,0xcdb2cda3UL,0x29ee2973UL,0x16fb16b6UL,0x01f60153UL,0xd74dd7ecUL,0xa361a375UL,0x49ce49faUL,0x8d7b8da4UL,0x423e42a1UL,0x937193bcUL,0xa297a226UL,0x04f50457UL,0xb868b869UL,0x00000000UL,0x742c7499UL,0xa060a080UL,0x211f21ddUL,0x43c843f2UL,0x2ced2c77UL,0xd9bed9b3UL,0xca46ca01UL,0x70d970ceUL,0xdd4bdde4UL,0x79de7933UL,0x67d4672bUL,0x23e8237bUL,0xde4ade11UL,0xbd6bbd6dUL,0x7e2a7e91UL,0x34e5349eUL,0x3a163ac1UL,0x54c55417UL,0x62d7622fUL,0xff55ffccUL,0xa794a722UL,0x4acf4a0fUL,0x301030c9UL,0x0a060a08UL,0x988198e7UL,0x0bf00b5bUL,0xcc44ccf0UL,0xd5bad54aUL,0x3ee33e96UL,0x0ef30e5fUL,0x19fe19baUL,0x5bc05b1bUL,0x858a850aUL,0xecadec7eUL,0xdfbcdf42UL,0xd848d8e0UL,0x0c040cf9UL,0x7adf7ac6UL,0x58c158eeUL,0x9f759f45UL,0xa563a584UL,0x50305040UL,0x2e1a2ed1UL,0x120e12e1UL,0xb76db765UL,0xd44cd419UL,0x3c143c30UL,0x5f355f4cUL,0x712f719dUL,0x38e13867UL,0xfda2fd6aUL,0x4fcc4f0bUL,0x4b394b5cUL,0xf957f93dUL,0x0df20daaUL,0x9d829de3UL,0xc947c9f4UL,0xefacef8bUL,0x32e7326fUL,0x7d2b7d64UL,0xa495a4d7UL,0xfba0fb9bUL,0xb398b332UL,0x68d16827UL,0x817f815dUL,0xaa66aa88UL,0x827e82a8UL,0xe6abe676UL,0x9e839e16UL,0x45ca4503UL,0x7b297b95UL,0x6ed36ed6UL,0x443c4450UL,0x8b798b55UL,0x3de23d63UL,0x271d272cUL,0x9a769a41UL,0x4d3b4dadUL,0xfa56fac8UL,0xd24ed2e8UL,0x221e2228UL,0x76db763fUL,0x1e0a1e18UL,0xb46cb490UL,0x37e4376bUL,0xe75de725UL,0xb26eb261UL,0x2aef2a86UL,0xf1a6f193UL,0xe3a8e372UL,0xf7a4f762UL,0x593759bdUL,0x868b86ffUL,0x563256b1UL,0xc543c50dUL,0xeb59ebdcUL,0xc2b7c2afUL,0x8f8c8f02UL,0xac64ac79UL,0x6dd26d23UL,0x3be03b92UL,0xc7b4c7abUL,0x15fa1543UL,0x090709fdUL,0x6f256f85UL,0xeaafea8fUL,0x898e89f3UL,0x20e9208eUL,0x28182820UL,0x64d564deUL,0x838883fbUL,0xb16fb194UL,0x967296b8UL,0x6c246c70UL,0x08f108aeUL,0x52c752e6UL,0xf351f335UL,0x6523658dUL,0x847c8459UL,0xbf9cbfcbUL,0x6321637cUL,0x7cdd7c37UL,0x7fdc7fc2UL,0x9186911aUL,0x9485941eUL,0xab90abdbUL,0xc642c6f8UL,0x57c457e2UL,0xe5aae583UL,0x73d8733bUL,0x0f050f0cUL,0x030103f5UL,0x36123638UL,0xfea3fe9fUL,0xe15fe1d4UL,0x10f91047UL,0x6bd06bd2UL,0xa891a82eUL,0xe858e829UL,0x69276974UL,0xd0b9d04eUL,0x483848a9UL,0x351335cdUL,0xceb3ce56UL,0x55335544UL,0xd6bbd6bfUL,0x90709049UL,0x8089800eUL,0xf2a7f266UL,0xc1b6c15aUL,0x66226678UL,0xad92ad2aUL,0x60206089UL,0xdb49db15UL,0x1aff1a4fUL,0x887888a0UL,0x8e7a8e51UL,0x8a8f8a06UL,0x13f813b2UL,0x9b809b12UL,0x39173934UL,0x75da75caUL,0x533153b5UL,0x51c65113UL,0xd3b8d3bbUL,0x5ec35e1fUL,0xcbb0cb52UL,0x997799b4UL,0x3311333cUL,0x46cb46f6UL,0x1ffc1f4bUL,0x61d661daUL,0x4e3a4e58UL, + 0x32f4a5f4UL,0x6f978497UL,0x5eb099b0UL,0x7a8c8d8cUL,0xe8170d17UL,0x0adcbddcUL,0x16c8b1c8UL,0x6dfc54fcUL,0x90f050f0UL,0x07050305UL,0x2ee0a9e0UL,0xd1877d87UL,0xcc2b192bUL,0x13a662a6UL,0x7c31e631UL,0x59b59ab5UL,0x40cf45cfUL,0xa3bc9dbcUL,0x49c040c0UL,0x68928792UL,0xd03f153fUL,0x9426eb26UL,0xce40c940UL,0xe61d0b1dUL,0x6e2fec2fUL,0x1aa967a9UL,0x431cfd1cUL,0x6025ea25UL,0xf9dabfdaUL,0x5102f702UL,0x45a196a1UL,0x76ed5bedUL,0x285dc25dUL,0xc5241c24UL,0xd4e9aee9UL,0xf2be6abeUL,0x82ee5aeeUL,0xbdc341c3UL,0xf3060206UL,0x52d14fd1UL,0x8ce45ce4UL,0x5607f407UL,0x8d5c345cUL,0xe1180818UL,0x4cae93aeUL,0x3e957395UL,0x97f553f5UL,0x6b413f41UL,0x1c140c14UL,0x63f652f6UL,0xe9af65afUL,0x7fe25ee2UL,0x48782878UL,0xcff8a1f8UL,0x1b110f11UL,0xebc4b5c4UL,0x151b091bUL,0x7e5a365aUL,0xadb69bb6UL,0x98473d47UL,0xa76a266aUL,0xf5bb69bbUL,0x334ccd4cUL,0x50ba9fbaUL,0x3f2d1b2dUL,0xa4b99eb9UL,0xc49c749cUL,0x46722e72UL,0x41772d77UL,0x11cdb2cdUL,0x9d29ee29UL,0x4d16fb16UL,0xa501f601UL,0xa1d74dd7UL,0x14a361a3UL,0x3449ce49UL,0xdf8d7b8dUL,0x9f423e42UL,0xcd937193UL,0xb1a297a2UL,0xa204f504UL,0x01b868b8UL,0x00000000UL,0xb5742c74UL,0xe0a060a0UL,0xc2211f21UL,0x3a43c843UL,0x9a2ced2cUL,0x0dd9bed9UL,0x47ca46caUL,0x1770d970UL,0xafdd4bddUL,0xed79de79UL,0xff67d467UL,0x9323e823UL,0x5bde4adeUL,0x06bd6bbdUL,0xbb7e2a7eUL,0x7b34e534UL,0xd73a163aUL,0xd254c554UL,0xf862d762UL,0x99ff55ffUL,0xb6a794a7UL,0xc04acf4aUL,0xd9301030UL,0x0e0a060aUL,0x66988198UL,0xab0bf00bUL,0xb4cc44ccUL,0xf0d5bad5UL,0x753ee33eUL,0xac0ef30eUL,0x4419fe19UL,0xdb5bc05bUL,0x80858a85UL,0xd3ecadecUL,0xfedfbcdfUL,0xa8d848d8UL,0xfd0c040cUL,0x197adf7aUL,0x2f58c158UL,0x309f759fUL,0xe7a563a5UL,0x70503050UL,0xcb2e1a2eUL,0xef120e12UL,0x08b76db7UL,0x55d44cd4UL,0x243c143cUL,0x795f355fUL,0xb2712f71UL,0x8638e138UL,0xc8fda2fdUL,0xc74fcc4fUL,0x654b394bUL,0x6af957f9UL,0x580df20dUL,0x619d829dUL,0xb3c947c9UL,0x27efacefUL,0x8832e732UL,0x4f7d2b7dUL,0x42a495a4UL,0x3bfba0fbUL,0xaab398b3UL,0xf668d168UL,0x22817f81UL,0xeeaa66aaUL,0xd6827e82UL,0xdde6abe6UL,0x959e839eUL,0xc945ca45UL,0xbc7b297bUL,0x056ed36eUL,0x6c443c44UL,0x2c8b798bUL,0x813de23dUL,0x31271d27UL,0x379a769aUL,0x964d3b4dUL,0x9efa56faUL,0xa6d24ed2UL,0x36221e22UL,0xe476db76UL,0x121e0a1eUL,0xfcb46cb4UL,0x8f37e437UL,0x78e75de7UL,0x0fb26eb2UL,0x692aef2aUL,0x35f1a6f1UL,0xdae3a8e3UL,0xc6f7a4f7UL,0x8a593759UL,0x74868b86UL,0x83563256UL,0x4ec543c5UL,0x85eb59ebUL,0x18c2b7c2UL,0x8e8f8c8fUL,0x1dac64acUL,0xf16dd26dUL,0x723be03bUL,0x1fc7b4c7UL,0xb915fa15UL,0xfa090709UL,0xa06f256fUL,0x20eaafeaUL,0x7d898e89UL,0x6720e920UL,0x38281828UL,0x0b64d564UL,0x73838883UL,0xfbb16fb1UL,0xca967296UL,0x546c246cUL,0x5f08f108UL,0x2152c752UL,0x64f351f3UL,0xae652365UL,0x25847c84UL,0x57bf9cbfUL,0x5d632163UL,0xea7cdd7cUL,0x1e7fdc7fUL,0x9c918691UL,0x9b948594UL,0x4bab90abUL,0xbac642c6UL,0x2657c457UL,0x29e5aae5UL,0xe373d873UL,0x090f050fUL,0xf4030103UL,0x2a361236UL,0x3cfea3feUL,0x8be15fe1UL,0xbe10f910UL,0x026bd06bUL,0xbfa891a8UL,0x71e858e8UL,0x53692769UL,0xf7d0b9d0UL,0x91483848UL,0xde351335UL,0xe5ceb3ceUL,0x77553355UL,0x04d6bbd6UL,0x39907090UL,0x87808980UL,0xc1f2a7f2UL,0xecc1b6c1UL,0x5a662266UL,0xb8ad92adUL,0xa9602060UL,0x5cdb49dbUL,0xb01aff1aUL,0xd8887888UL,0x2b8e7a8eUL,0x898a8f8aUL,0x4a13f813UL,0x929b809bUL,0x23391739UL,0x1075da75UL,0x84533153UL,0xd551c651UL,0x03d3b8d3UL,0xdc5ec35eUL,0xe2cbb0cbUL,0xc3997799UL,0x2d331133UL,0x3d46cb46UL,0xb71ffc1fUL,0x0c61d661UL,0x624e3a4eUL}; +#endif /* IS_BIG_ENDIAN */ + +#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) +groestl_ALIGN const u32 T[8*256] = { + 0xa5f432c6UL,0x84976ff8UL,0x99b05eeeUL,0x8d8c7af6UL,0x0d17e8ffUL,0xbddc0ad6UL,0xb1c816deUL,0x54fc6d91UL,0x50f09060UL,0x03050702UL,0xa9e02eceUL,0x7d87d156UL,0x192bcce7UL,0x62a613b5UL,0xe6317c4dUL,0x9ab559ecUL,0x45cf408fUL,0x9dbca31fUL,0x40c04989UL,0x879268faUL,0x153fd0efUL,0xeb2694b2UL,0xc940ce8eUL,0x0b1de6fbUL,0xec2f6e41UL,0x67a91ab3UL,0xfd1c435fUL,0xea256045UL,0xbfdaf923UL,0xf7025153UL,0x96a145e4UL,0x5bed769bUL,0xc25d2875UL,0x1c24c5e1UL,0xaee9d43dUL,0x6abef24cUL,0x5aee826cUL,0x41c3bd7eUL,0x0206f3f5UL,0x4fd15283UL,0x5ce48c68UL,0xf4075651UL,0x345c8dd1UL,0x0818e1f9UL,0x93ae4ce2UL,0x73953eabUL,0x53f59762UL,0x3f416b2aUL,0x0c141c08UL,0x52f66395UL,0x65afe946UL,0x5ee27f9dUL,0x28784830UL,0xa1f8cf37UL,0x0f111b0aUL,0xb5c4eb2fUL,0x091b150eUL,0x365a7e24UL,0x9bb6ad1bUL,0x3d4798dfUL,0x266aa7cdUL,0x69bbf54eUL,0xcd4c337fUL,0x9fba50eaUL,0x1b2d3f12UL,0x9eb9a41dUL,0x749cc458UL,0x2e724634UL,0x2d774136UL,0xb2cd11dcUL,0xee299db4UL,0xfb164d5bUL,0xf601a5a4UL,0x4dd7a176UL,0x61a314b7UL,0xce49347dUL,0x7b8ddf52UL,0x3e429fddUL,0x7193cd5eUL,0x97a2b113UL,0xf504a2a6UL,0x68b801b9UL,0x00000000UL,0x2c74b5c1UL,0x60a0e040UL,0x1f21c2e3UL,0xc8433a79UL,0xed2c9ab6UL,0xbed90dd4UL,0x46ca478dUL,0xd9701767UL,0x4bddaf72UL,0xde79ed94UL,0xd467ff98UL,0xe82393b0UL,0x4ade5b85UL,0x6bbd06bbUL,0x2a7ebbc5UL,0xe5347b4fUL,0x163ad7edUL,0xc554d286UL,0xd762f89aUL,0x55ff9966UL,0x94a7b611UL,0xcf4ac08aUL,0x1030d9e9UL,0x060a0e04UL,0x819866feUL,0xf00baba0UL,0x44ccb478UL,0xbad5f025UL,0xe33e754bUL,0xf30eaca2UL,0xfe19445dUL,0xc05bdb80UL,0x8a858005UL,0xadecd33fUL,0xbcdffe21UL,0x48d8a870UL,0x040cfdf1UL,0xdf7a1963UL,0xc1582f77UL,0x759f30afUL,0x63a5e742UL,0x30507020UL,0x1a2ecbe5UL,0x0e12effdUL,0x6db708bfUL,0x4cd45581UL,0x143c2418UL,0x355f7926UL,0x2f71b2c3UL,0xe13886beUL,0xa2fdc835UL,0xcc4fc788UL,0x394b652eUL,0x57f96a93UL,0xf20d5855UL,0x829d61fcUL,0x47c9b37aUL,0xacef27c8UL,0xe73288baUL,0x2b7d4f32UL,0x95a442e6UL,0xa0fb3bc0UL,0x98b3aa19UL,0xd168f69eUL,0x7f8122a3UL,0x66aaee44UL,0x7e82d654UL,0xabe6dd3bUL,0x839e950bUL,0xca45c98cUL,0x297bbcc7UL,0xd36e056bUL,0x3c446c28UL,0x798b2ca7UL,0xe23d81bcUL,0x1d273116UL,0x769a37adUL,0x3b4d96dbUL,0x56fa9e64UL,0x4ed2a674UL,0x1e223614UL,0xdb76e492UL,0x0a1e120cUL,0x6cb4fc48UL,0xe4378fb8UL,0x5de7789fUL,0x6eb20fbdUL,0xef2a6943UL,0xa6f135c4UL,0xa8e3da39UL,0xa4f7c631UL,0x37598ad3UL,0x8b8674f2UL,0x325683d5UL,0x43c54e8bUL,0x59eb856eUL,0xb7c218daUL,0x8c8f8e01UL,0x64ac1db1UL,0xd26df19cUL,0xe03b7249UL,0xb4c71fd8UL,0xfa15b9acUL,0x0709faf3UL,0x256fa0cfUL,0xafea20caUL,0x8e897df4UL,0xe9206747UL,0x18283810UL,0xd5640b6fUL,0x888373f0UL,0x6fb1fb4aUL,0x7296ca5cUL,0x246c5438UL,0xf1085f57UL,0xc7522173UL,0x51f36497UL,0x2365aecbUL,0x7c8425a1UL,0x9cbf57e8UL,0x21635d3eUL,0xdd7cea96UL,0xdc7f1e61UL,0x86919c0dUL,0x85949b0fUL,0x90ab4be0UL,0x42c6ba7cUL,0xc4572671UL,0xaae529ccUL,0xd873e390UL,0x050f0906UL,0x0103f4f7UL,0x12362a1cUL,0xa3fe3cc2UL,0x5fe18b6aUL,0xf910beaeUL,0xd06b0269UL,0x91a8bf17UL,0x58e87199UL,0x2769533aUL,0xb9d0f727UL,0x384891d9UL,0x1335deebUL,0xb3cee52bUL,0x33557722UL,0xbbd604d2UL,0x709039a9UL,0x89808707UL,0xa7f2c133UL,0xb6c1ec2dUL,0x22665a3cUL,0x92adb815UL,0x2060a9c9UL,0x49db5c87UL,0xff1ab0aaUL,0x7888d850UL,0x7a8e2ba5UL,0x8f8a8903UL,0xf8134a59UL,0x809b9209UL,0x1739231aUL,0xda751065UL,0x315384d7UL,0xc651d584UL,0xb8d303d0UL,0xc35edc82UL,0xb0cbe229UL,0x7799c35aUL,0x11332d1eUL,0xcb463d7bUL,0xfc1fb7a8UL,0xd6610c6dUL,0x3a4e622cUL, + 0xf432c6c6UL,0x976ff8f8UL,0xb05eeeeeUL,0x8c7af6f6UL,0x17e8ffffUL,0xdc0ad6d6UL,0xc816dedeUL,0xfc6d9191UL,0xf0906060UL,0x05070202UL,0xe02ececeUL,0x87d15656UL,0x2bcce7e7UL,0xa613b5b5UL,0x317c4d4dUL,0xb559ececUL,0xcf408f8fUL,0xbca31f1fUL,0xc0498989UL,0x9268fafaUL,0x3fd0efefUL,0x2694b2b2UL,0x40ce8e8eUL,0x1de6fbfbUL,0x2f6e4141UL,0xa91ab3b3UL,0x1c435f5fUL,0x25604545UL,0xdaf92323UL,0x02515353UL,0xa145e4e4UL,0xed769b9bUL,0x5d287575UL,0x24c5e1e1UL,0xe9d43d3dUL,0xbef24c4cUL,0xee826c6cUL,0xc3bd7e7eUL,0x06f3f5f5UL,0xd1528383UL,0xe48c6868UL,0x07565151UL,0x5c8dd1d1UL,0x18e1f9f9UL,0xae4ce2e2UL,0x953eababUL,0xf5976262UL,0x416b2a2aUL,0x141c0808UL,0xf6639595UL,0xafe94646UL,0xe27f9d9dUL,0x78483030UL,0xf8cf3737UL,0x111b0a0aUL,0xc4eb2f2fUL,0x1b150e0eUL,0x5a7e2424UL,0xb6ad1b1bUL,0x4798dfdfUL,0x6aa7cdcdUL,0xbbf54e4eUL,0x4c337f7fUL,0xba50eaeaUL,0x2d3f1212UL,0xb9a41d1dUL,0x9cc45858UL,0x72463434UL,0x77413636UL,0xcd11dcdcUL,0x299db4b4UL,0x164d5b5bUL,0x01a5a4a4UL,0xd7a17676UL,0xa314b7b7UL,0x49347d7dUL,0x8ddf5252UL,0x429fddddUL,0x93cd5e5eUL,0xa2b11313UL,0x04a2a6a6UL,0xb801b9b9UL,0x00000000UL,0x74b5c1c1UL,0xa0e04040UL,0x21c2e3e3UL,0x433a7979UL,0x2c9ab6b6UL,0xd90dd4d4UL,0xca478d8dUL,0x70176767UL,0xddaf7272UL,0x79ed9494UL,0x67ff9898UL,0x2393b0b0UL,0xde5b8585UL,0xbd06bbbbUL,0x7ebbc5c5UL,0x347b4f4fUL,0x3ad7ededUL,0x54d28686UL,0x62f89a9aUL,0xff996666UL,0xa7b61111UL,0x4ac08a8aUL,0x30d9e9e9UL,0x0a0e0404UL,0x9866fefeUL,0x0baba0a0UL,0xccb47878UL,0xd5f02525UL,0x3e754b4bUL,0x0eaca2a2UL,0x19445d5dUL,0x5bdb8080UL,0x85800505UL,0xecd33f3fUL,0xdffe2121UL,0xd8a87070UL,0x0cfdf1f1UL,0x7a196363UL,0x582f7777UL,0x9f30afafUL,0xa5e74242UL,0x50702020UL,0x2ecbe5e5UL,0x12effdfdUL,0xb708bfbfUL,0xd4558181UL,0x3c241818UL,0x5f792626UL,0x71b2c3c3UL,0x3886bebeUL,0xfdc83535UL,0x4fc78888UL,0x4b652e2eUL,0xf96a9393UL,0x0d585555UL,0x9d61fcfcUL,0xc9b37a7aUL,0xef27c8c8UL,0x3288babaUL,0x7d4f3232UL,0xa442e6e6UL,0xfb3bc0c0UL,0xb3aa1919UL,0x68f69e9eUL,0x8122a3a3UL,0xaaee4444UL,0x82d65454UL,0xe6dd3b3bUL,0x9e950b0bUL,0x45c98c8cUL,0x7bbcc7c7UL,0x6e056b6bUL,0x446c2828UL,0x8b2ca7a7UL,0x3d81bcbcUL,0x27311616UL,0x9a37adadUL,0x4d96dbdbUL,0xfa9e6464UL,0xd2a67474UL,0x22361414UL,0x76e49292UL,0x1e120c0cUL,0xb4fc4848UL,0x378fb8b8UL,0xe7789f9fUL,0xb20fbdbdUL,0x2a694343UL,0xf135c4c4UL,0xe3da3939UL,0xf7c63131UL,0x598ad3d3UL,0x8674f2f2UL,0x5683d5d5UL,0xc54e8b8bUL,0xeb856e6eUL,0xc218dadaUL,0x8f8e0101UL,0xac1db1b1UL,0x6df19c9cUL,0x3b724949UL,0xc71fd8d8UL,0x15b9acacUL,0x09faf3f3UL,0x6fa0cfcfUL,0xea20cacaUL,0x897df4f4UL,0x20674747UL,0x28381010UL,0x640b6f6fUL,0x8373f0f0UL,0xb1fb4a4aUL,0x96ca5c5cUL,0x6c543838UL,0x085f5757UL,0x52217373UL,0xf3649797UL,0x65aecbcbUL,0x8425a1a1UL,0xbf57e8e8UL,0x635d3e3eUL,0x7cea9696UL,0x7f1e6161UL,0x919c0d0dUL,0x949b0f0fUL,0xab4be0e0UL,0xc6ba7c7cUL,0x57267171UL,0xe529ccccUL,0x73e39090UL,0x0f090606UL,0x03f4f7f7UL,0x362a1c1cUL,0xfe3cc2c2UL,0xe18b6a6aUL,0x10beaeaeUL,0x6b026969UL,0xa8bf1717UL,0xe8719999UL,0x69533a3aUL,0xd0f72727UL,0x4891d9d9UL,0x35deebebUL,0xcee52b2bUL,0x55772222UL,0xd604d2d2UL,0x9039a9a9UL,0x80870707UL,0xf2c13333UL,0xc1ec2d2dUL,0x665a3c3cUL,0xadb81515UL,0x60a9c9c9UL,0xdb5c8787UL,0x1ab0aaaaUL,0x88d85050UL,0x8e2ba5a5UL,0x8a890303UL,0x134a5959UL,0x9b920909UL,0x39231a1aUL,0x75106565UL,0x5384d7d7UL,0x51d58484UL,0xd303d0d0UL,0x5edc8282UL,0xcbe22929UL,0x99c35a5aUL,0x332d1e1eUL,0x463d7b7bUL,0x1fb7a8a8UL,0x610c6d6dUL,0x4e622c2cUL, + 0x32c6c6a5UL,0x6ff8f884UL,0x5eeeee99UL,0x7af6f68dUL,0xe8ffff0dUL,0x0ad6d6bdUL,0x16dedeb1UL,0x6d919154UL,0x90606050UL,0x07020203UL,0x2ececea9UL,0xd156567dUL,0xcce7e719UL,0x13b5b562UL,0x7c4d4de6UL,0x59ecec9aUL,0x408f8f45UL,0xa31f1f9dUL,0x49898940UL,0x68fafa87UL,0xd0efef15UL,0x94b2b2ebUL,0xce8e8ec9UL,0xe6fbfb0bUL,0x6e4141ecUL,0x1ab3b367UL,0x435f5ffdUL,0x604545eaUL,0xf92323bfUL,0x515353f7UL,0x45e4e496UL,0x769b9b5bUL,0x287575c2UL,0xc5e1e11cUL,0xd43d3daeUL,0xf24c4c6aUL,0x826c6c5aUL,0xbd7e7e41UL,0xf3f5f502UL,0x5283834fUL,0x8c68685cUL,0x565151f4UL,0x8dd1d134UL,0xe1f9f908UL,0x4ce2e293UL,0x3eabab73UL,0x97626253UL,0x6b2a2a3fUL,0x1c08080cUL,0x63959552UL,0xe9464665UL,0x7f9d9d5eUL,0x48303028UL,0xcf3737a1UL,0x1b0a0a0fUL,0xeb2f2fb5UL,0x150e0e09UL,0x7e242436UL,0xad1b1b9bUL,0x98dfdf3dUL,0xa7cdcd26UL,0xf54e4e69UL,0x337f7fcdUL,0x50eaea9fUL,0x3f12121bUL,0xa41d1d9eUL,0xc4585874UL,0x4634342eUL,0x4136362dUL,0x11dcdcb2UL,0x9db4b4eeUL,0x4d5b5bfbUL,0xa5a4a4f6UL,0xa176764dUL,0x14b7b761UL,0x347d7dceUL,0xdf52527bUL,0x9fdddd3eUL,0xcd5e5e71UL,0xb1131397UL,0xa2a6a6f5UL,0x01b9b968UL,0x00000000UL,0xb5c1c12cUL,0xe0404060UL,0xc2e3e31fUL,0x3a7979c8UL,0x9ab6b6edUL,0x0dd4d4beUL,0x478d8d46UL,0x176767d9UL,0xaf72724bUL,0xed9494deUL,0xff9898d4UL,0x93b0b0e8UL,0x5b85854aUL,0x06bbbb6bUL,0xbbc5c52aUL,0x7b4f4fe5UL,0xd7eded16UL,0xd28686c5UL,0xf89a9ad7UL,0x99666655UL,0xb6111194UL,0xc08a8acfUL,0xd9e9e910UL,0x0e040406UL,0x66fefe81UL,0xaba0a0f0UL,0xb4787844UL,0xf02525baUL,0x754b4be3UL,0xaca2a2f3UL,0x445d5dfeUL,0xdb8080c0UL,0x8005058aUL,0xd33f3fadUL,0xfe2121bcUL,0xa8707048UL,0xfdf1f104UL,0x196363dfUL,0x2f7777c1UL,0x30afaf75UL,0xe7424263UL,0x70202030UL,0xcbe5e51aUL,0xeffdfd0eUL,0x08bfbf6dUL,0x5581814cUL,0x24181814UL,0x79262635UL,0xb2c3c32fUL,0x86bebee1UL,0xc83535a2UL,0xc78888ccUL,0x652e2e39UL,0x6a939357UL,0x585555f2UL,0x61fcfc82UL,0xb37a7a47UL,0x27c8c8acUL,0x88babae7UL,0x4f32322bUL,0x42e6e695UL,0x3bc0c0a0UL,0xaa191998UL,0xf69e9ed1UL,0x22a3a37fUL,0xee444466UL,0xd654547eUL,0xdd3b3babUL,0x950b0b83UL,0xc98c8ccaUL,0xbcc7c729UL,0x056b6bd3UL,0x6c28283cUL,0x2ca7a779UL,0x81bcbce2UL,0x3116161dUL,0x37adad76UL,0x96dbdb3bUL,0x9e646456UL,0xa674744eUL,0x3614141eUL,0xe49292dbUL,0x120c0c0aUL,0xfc48486cUL,0x8fb8b8e4UL,0x789f9f5dUL,0x0fbdbd6eUL,0x694343efUL,0x35c4c4a6UL,0xda3939a8UL,0xc63131a4UL,0x8ad3d337UL,0x74f2f28bUL,0x83d5d532UL,0x4e8b8b43UL,0x856e6e59UL,0x18dadab7UL,0x8e01018cUL,0x1db1b164UL,0xf19c9cd2UL,0x724949e0UL,0x1fd8d8b4UL,0xb9acacfaUL,0xfaf3f307UL,0xa0cfcf25UL,0x20cacaafUL,0x7df4f48eUL,0x674747e9UL,0x38101018UL,0x0b6f6fd5UL,0x73f0f088UL,0xfb4a4a6fUL,0xca5c5c72UL,0x54383824UL,0x5f5757f1UL,0x217373c7UL,0x64979751UL,0xaecbcb23UL,0x25a1a17cUL,0x57e8e89cUL,0x5d3e3e21UL,0xea9696ddUL,0x1e6161dcUL,0x9c0d0d86UL,0x9b0f0f85UL,0x4be0e090UL,0xba7c7c42UL,0x267171c4UL,0x29ccccaaUL,0xe39090d8UL,0x09060605UL,0xf4f7f701UL,0x2a1c1c12UL,0x3cc2c2a3UL,0x8b6a6a5fUL,0xbeaeaef9UL,0x026969d0UL,0xbf171791UL,0x71999958UL,0x533a3a27UL,0xf72727b9UL,0x91d9d938UL,0xdeebeb13UL,0xe52b2bb3UL,0x77222233UL,0x04d2d2bbUL,0x39a9a970UL,0x87070789UL,0xc13333a7UL,0xec2d2db6UL,0x5a3c3c22UL,0xb8151592UL,0xa9c9c920UL,0x5c878749UL,0xb0aaaaffUL,0xd8505078UL,0x2ba5a57aUL,0x8903038fUL,0x4a5959f8UL,0x92090980UL,0x231a1a17UL,0x106565daUL,0x84d7d731UL,0xd58484c6UL,0x03d0d0b8UL,0xdc8282c3UL,0xe22929b0UL,0xc35a5a77UL,0x2d1e1e11UL,0x3d7b7bcbUL,0xb7a8a8fcUL,0x0c6d6dd6UL,0x622c2c3aUL, + 0xc6c6a597UL,0xf8f884ebUL,0xeeee99c7UL,0xf6f68df7UL,0xffff0de5UL,0xd6d6bdb7UL,0xdedeb1a7UL,0x91915439UL,0x606050c0UL,0x02020304UL,0xcecea987UL,0x56567dacUL,0xe7e719d5UL,0xb5b56271UL,0x4d4de69aUL,0xecec9ac3UL,0x8f8f4505UL,0x1f1f9d3eUL,0x89894009UL,0xfafa87efUL,0xefef15c5UL,0xb2b2eb7fUL,0x8e8ec907UL,0xfbfb0bedUL,0x4141ec82UL,0xb3b3677dUL,0x5f5ffdbeUL,0x4545ea8aUL,0x2323bf46UL,0x5353f7a6UL,0xe4e496d3UL,0x9b9b5b2dUL,0x7575c2eaUL,0xe1e11cd9UL,0x3d3dae7aUL,0x4c4c6a98UL,0x6c6c5ad8UL,0x7e7e41fcUL,0xf5f502f1UL,0x83834f1dUL,0x68685cd0UL,0x5151f4a2UL,0xd1d134b9UL,0xf9f908e9UL,0xe2e293dfUL,0xabab734dUL,0x626253c4UL,0x2a2a3f54UL,0x08080c10UL,0x95955231UL,0x4646658cUL,0x9d9d5e21UL,0x30302860UL,0x3737a16eUL,0x0a0a0f14UL,0x2f2fb55eUL,0x0e0e091cUL,0x24243648UL,0x1b1b9b36UL,0xdfdf3da5UL,0xcdcd2681UL,0x4e4e699cUL,0x7f7fcdfeUL,0xeaea9fcfUL,0x12121b24UL,0x1d1d9e3aUL,0x585874b0UL,0x34342e68UL,0x36362d6cUL,0xdcdcb2a3UL,0xb4b4ee73UL,0x5b5bfbb6UL,0xa4a4f653UL,0x76764decUL,0xb7b76175UL,0x7d7dcefaUL,0x52527ba4UL,0xdddd3ea1UL,0x5e5e71bcUL,0x13139726UL,0xa6a6f557UL,0xb9b96869UL,0x00000000UL,0xc1c12c99UL,0x40406080UL,0xe3e31fddUL,0x7979c8f2UL,0xb6b6ed77UL,0xd4d4beb3UL,0x8d8d4601UL,0x6767d9ceUL,0x72724be4UL,0x9494de33UL,0x9898d42bUL,0xb0b0e87bUL,0x85854a11UL,0xbbbb6b6dUL,0xc5c52a91UL,0x4f4fe59eUL,0xeded16c1UL,0x8686c517UL,0x9a9ad72fUL,0x666655ccUL,0x11119422UL,0x8a8acf0fUL,0xe9e910c9UL,0x04040608UL,0xfefe81e7UL,0xa0a0f05bUL,0x787844f0UL,0x2525ba4aUL,0x4b4be396UL,0xa2a2f35fUL,0x5d5dfebaUL,0x8080c01bUL,0x05058a0aUL,0x3f3fad7eUL,0x2121bc42UL,0x707048e0UL,0xf1f104f9UL,0x6363dfc6UL,0x7777c1eeUL,0xafaf7545UL,0x42426384UL,0x20203040UL,0xe5e51ad1UL,0xfdfd0ee1UL,0xbfbf6d65UL,0x81814c19UL,0x18181430UL,0x2626354cUL,0xc3c32f9dUL,0xbebee167UL,0x3535a26aUL,0x8888cc0bUL,0x2e2e395cUL,0x9393573dUL,0x5555f2aaUL,0xfcfc82e3UL,0x7a7a47f4UL,0xc8c8ac8bUL,0xbabae76fUL,0x32322b64UL,0xe6e695d7UL,0xc0c0a09bUL,0x19199832UL,0x9e9ed127UL,0xa3a37f5dUL,0x44446688UL,0x54547ea8UL,0x3b3bab76UL,0x0b0b8316UL,0x8c8cca03UL,0xc7c72995UL,0x6b6bd3d6UL,0x28283c50UL,0xa7a77955UL,0xbcbce263UL,0x16161d2cUL,0xadad7641UL,0xdbdb3badUL,0x646456c8UL,0x74744ee8UL,0x14141e28UL,0x9292db3fUL,0x0c0c0a18UL,0x48486c90UL,0xb8b8e46bUL,0x9f9f5d25UL,0xbdbd6e61UL,0x4343ef86UL,0xc4c4a693UL,0x3939a872UL,0x3131a462UL,0xd3d337bdUL,0xf2f28bffUL,0xd5d532b1UL,0x8b8b430dUL,0x6e6e59dcUL,0xdadab7afUL,0x01018c02UL,0xb1b16479UL,0x9c9cd223UL,0x4949e092UL,0xd8d8b4abUL,0xacacfa43UL,0xf3f307fdUL,0xcfcf2585UL,0xcacaaf8fUL,0xf4f48ef3UL,0x4747e98eUL,0x10101820UL,0x6f6fd5deUL,0xf0f088fbUL,0x4a4a6f94UL,0x5c5c72b8UL,0x38382470UL,0x5757f1aeUL,0x7373c7e6UL,0x97975135UL,0xcbcb238dUL,0xa1a17c59UL,0xe8e89ccbUL,0x3e3e217cUL,0x9696dd37UL,0x6161dcc2UL,0x0d0d861aUL,0x0f0f851eUL,0xe0e090dbUL,0x7c7c42f8UL,0x7171c4e2UL,0xccccaa83UL,0x9090d83bUL,0x0606050cUL,0xf7f701f5UL,0x1c1c1238UL,0xc2c2a39fUL,0x6a6a5fd4UL,0xaeaef947UL,0x6969d0d2UL,0x1717912eUL,0x99995829UL,0x3a3a2774UL,0x2727b94eUL,0xd9d938a9UL,0xebeb13cdUL,0x2b2bb356UL,0x22223344UL,0xd2d2bbbfUL,0xa9a97049UL,0x0707890eUL,0x3333a766UL,0x2d2db65aUL,0x3c3c2278UL,0x1515922aUL,0xc9c92089UL,0x87874915UL,0xaaaaff4fUL,0x505078a0UL,0xa5a57a51UL,0x03038f06UL,0x5959f8b2UL,0x09098012UL,0x1a1a1734UL,0x6565dacaUL,0xd7d731b5UL,0x8484c613UL,0xd0d0b8bbUL,0x8282c31fUL,0x2929b052UL,0x5a5a77b4UL,0x1e1e113cUL,0x7b7bcbf6UL,0xa8a8fc4bUL,0x6d6dd6daUL,0x2c2c3a58UL, + 0xc6a597f4UL,0xf884eb97UL,0xee99c7b0UL,0xf68df78cUL,0xff0de517UL,0xd6bdb7dcUL,0xdeb1a7c8UL,0x915439fcUL,0x6050c0f0UL,0x02030405UL,0xcea987e0UL,0x567dac87UL,0xe719d52bUL,0xb56271a6UL,0x4de69a31UL,0xec9ac3b5UL,0x8f4505cfUL,0x1f9d3ebcUL,0x894009c0UL,0xfa87ef92UL,0xef15c53fUL,0xb2eb7f26UL,0x8ec90740UL,0xfb0bed1dUL,0x41ec822fUL,0xb3677da9UL,0x5ffdbe1cUL,0x45ea8a25UL,0x23bf46daUL,0x53f7a602UL,0xe496d3a1UL,0x9b5b2dedUL,0x75c2ea5dUL,0xe11cd924UL,0x3dae7ae9UL,0x4c6a98beUL,0x6c5ad8eeUL,0x7e41fcc3UL,0xf502f106UL,0x834f1dd1UL,0x685cd0e4UL,0x51f4a207UL,0xd134b95cUL,0xf908e918UL,0xe293dfaeUL,0xab734d95UL,0x6253c4f5UL,0x2a3f5441UL,0x080c1014UL,0x955231f6UL,0x46658cafUL,0x9d5e21e2UL,0x30286078UL,0x37a16ef8UL,0x0a0f1411UL,0x2fb55ec4UL,0x0e091c1bUL,0x2436485aUL,0x1b9b36b6UL,0xdf3da547UL,0xcd26816aUL,0x4e699cbbUL,0x7fcdfe4cUL,0xea9fcfbaUL,0x121b242dUL,0x1d9e3ab9UL,0x5874b09cUL,0x342e6872UL,0x362d6c77UL,0xdcb2a3cdUL,0xb4ee7329UL,0x5bfbb616UL,0xa4f65301UL,0x764decd7UL,0xb76175a3UL,0x7dcefa49UL,0x527ba48dUL,0xdd3ea142UL,0x5e71bc93UL,0x139726a2UL,0xa6f55704UL,0xb96869b8UL,0x00000000UL,0xc12c9974UL,0x406080a0UL,0xe31fdd21UL,0x79c8f243UL,0xb6ed772cUL,0xd4beb3d9UL,0x8d4601caUL,0x67d9ce70UL,0x724be4ddUL,0x94de3379UL,0x98d42b67UL,0xb0e87b23UL,0x854a11deUL,0xbb6b6dbdUL,0xc52a917eUL,0x4fe59e34UL,0xed16c13aUL,0x86c51754UL,0x9ad72f62UL,0x6655ccffUL,0x119422a7UL,0x8acf0f4aUL,0xe910c930UL,0x0406080aUL,0xfe81e798UL,0xa0f05b0bUL,0x7844f0ccUL,0x25ba4ad5UL,0x4be3963eUL,0xa2f35f0eUL,0x5dfeba19UL,0x80c01b5bUL,0x058a0a85UL,0x3fad7eecUL,0x21bc42dfUL,0x7048e0d8UL,0xf104f90cUL,0x63dfc67aUL,0x77c1ee58UL,0xaf75459fUL,0x426384a5UL,0x20304050UL,0xe51ad12eUL,0xfd0ee112UL,0xbf6d65b7UL,0x814c19d4UL,0x1814303cUL,0x26354c5fUL,0xc32f9d71UL,0xbee16738UL,0x35a26afdUL,0x88cc0b4fUL,0x2e395c4bUL,0x93573df9UL,0x55f2aa0dUL,0xfc82e39dUL,0x7a47f4c9UL,0xc8ac8befUL,0xbae76f32UL,0x322b647dUL,0xe695d7a4UL,0xc0a09bfbUL,0x199832b3UL,0x9ed12768UL,0xa37f5d81UL,0x446688aaUL,0x547ea882UL,0x3bab76e6UL,0x0b83169eUL,0x8cca0345UL,0xc729957bUL,0x6bd3d66eUL,0x283c5044UL,0xa779558bUL,0xbce2633dUL,0x161d2c27UL,0xad76419aUL,0xdb3bad4dUL,0x6456c8faUL,0x744ee8d2UL,0x141e2822UL,0x92db3f76UL,0x0c0a181eUL,0x486c90b4UL,0xb8e46b37UL,0x9f5d25e7UL,0xbd6e61b2UL,0x43ef862aUL,0xc4a693f1UL,0x39a872e3UL,0x31a462f7UL,0xd337bd59UL,0xf28bff86UL,0xd532b156UL,0x8b430dc5UL,0x6e59dcebUL,0xdab7afc2UL,0x018c028fUL,0xb16479acUL,0x9cd2236dUL,0x49e0923bUL,0xd8b4abc7UL,0xacfa4315UL,0xf307fd09UL,0xcf25856fUL,0xcaaf8feaUL,0xf48ef389UL,0x47e98e20UL,0x10182028UL,0x6fd5de64UL,0xf088fb83UL,0x4a6f94b1UL,0x5c72b896UL,0x3824706cUL,0x57f1ae08UL,0x73c7e652UL,0x975135f3UL,0xcb238d65UL,0xa17c5984UL,0xe89ccbbfUL,0x3e217c63UL,0x96dd377cUL,0x61dcc27fUL,0x0d861a91UL,0x0f851e94UL,0xe090dbabUL,0x7c42f8c6UL,0x71c4e257UL,0xccaa83e5UL,0x90d83b73UL,0x06050c0fUL,0xf701f503UL,0x1c123836UL,0xc2a39ffeUL,0x6a5fd4e1UL,0xaef94710UL,0x69d0d26bUL,0x17912ea8UL,0x995829e8UL,0x3a277469UL,0x27b94ed0UL,0xd938a948UL,0xeb13cd35UL,0x2bb356ceUL,0x22334455UL,0xd2bbbfd6UL,0xa9704990UL,0x07890e80UL,0x33a766f2UL,0x2db65ac1UL,0x3c227866UL,0x15922aadUL,0xc9208960UL,0x874915dbUL,0xaaff4f1aUL,0x5078a088UL,0xa57a518eUL,0x038f068aUL,0x59f8b213UL,0x0980129bUL,0x1a173439UL,0x65daca75UL,0xd731b553UL,0x84c61351UL,0xd0b8bbd3UL,0x82c31f5eUL,0x29b052cbUL,0x5a77b499UL,0x1e113c33UL,0x7bcbf646UL,0xa8fc4b1fUL,0x6dd6da61UL,0x2c3a584eUL, + 0xa597f4a5UL,0x84eb9784UL,0x99c7b099UL,0x8df78c8dUL,0x0de5170dUL,0xbdb7dcbdUL,0xb1a7c8b1UL,0x5439fc54UL,0x50c0f050UL,0x03040503UL,0xa987e0a9UL,0x7dac877dUL,0x19d52b19UL,0x6271a662UL,0xe69a31e6UL,0x9ac3b59aUL,0x4505cf45UL,0x9d3ebc9dUL,0x4009c040UL,0x87ef9287UL,0x15c53f15UL,0xeb7f26ebUL,0xc90740c9UL,0x0bed1d0bUL,0xec822fecUL,0x677da967UL,0xfdbe1cfdUL,0xea8a25eaUL,0xbf46dabfUL,0xf7a602f7UL,0x96d3a196UL,0x5b2ded5bUL,0xc2ea5dc2UL,0x1cd9241cUL,0xae7ae9aeUL,0x6a98be6aUL,0x5ad8ee5aUL,0x41fcc341UL,0x02f10602UL,0x4f1dd14fUL,0x5cd0e45cUL,0xf4a207f4UL,0x34b95c34UL,0x08e91808UL,0x93dfae93UL,0x734d9573UL,0x53c4f553UL,0x3f54413fUL,0x0c10140cUL,0x5231f652UL,0x658caf65UL,0x5e21e25eUL,0x28607828UL,0xa16ef8a1UL,0x0f14110fUL,0xb55ec4b5UL,0x091c1b09UL,0x36485a36UL,0x9b36b69bUL,0x3da5473dUL,0x26816a26UL,0x699cbb69UL,0xcdfe4ccdUL,0x9fcfba9fUL,0x1b242d1bUL,0x9e3ab99eUL,0x74b09c74UL,0x2e68722eUL,0x2d6c772dUL,0xb2a3cdb2UL,0xee7329eeUL,0xfbb616fbUL,0xf65301f6UL,0x4decd74dUL,0x6175a361UL,0xcefa49ceUL,0x7ba48d7bUL,0x3ea1423eUL,0x71bc9371UL,0x9726a297UL,0xf55704f5UL,0x6869b868UL,0x00000000UL,0x2c99742cUL,0x6080a060UL,0x1fdd211fUL,0xc8f243c8UL,0xed772cedUL,0xbeb3d9beUL,0x4601ca46UL,0xd9ce70d9UL,0x4be4dd4bUL,0xde3379deUL,0xd42b67d4UL,0xe87b23e8UL,0x4a11de4aUL,0x6b6dbd6bUL,0x2a917e2aUL,0xe59e34e5UL,0x16c13a16UL,0xc51754c5UL,0xd72f62d7UL,0x55ccff55UL,0x9422a794UL,0xcf0f4acfUL,0x10c93010UL,0x06080a06UL,0x81e79881UL,0xf05b0bf0UL,0x44f0cc44UL,0xba4ad5baUL,0xe3963ee3UL,0xf35f0ef3UL,0xfeba19feUL,0xc01b5bc0UL,0x8a0a858aUL,0xad7eecadUL,0xbc42dfbcUL,0x48e0d848UL,0x04f90c04UL,0xdfc67adfUL,0xc1ee58c1UL,0x75459f75UL,0x6384a563UL,0x30405030UL,0x1ad12e1aUL,0x0ee1120eUL,0x6d65b76dUL,0x4c19d44cUL,0x14303c14UL,0x354c5f35UL,0x2f9d712fUL,0xe16738e1UL,0xa26afda2UL,0xcc0b4fccUL,0x395c4b39UL,0x573df957UL,0xf2aa0df2UL,0x82e39d82UL,0x47f4c947UL,0xac8befacUL,0xe76f32e7UL,0x2b647d2bUL,0x95d7a495UL,0xa09bfba0UL,0x9832b398UL,0xd12768d1UL,0x7f5d817fUL,0x6688aa66UL,0x7ea8827eUL,0xab76e6abUL,0x83169e83UL,0xca0345caUL,0x29957b29UL,0xd3d66ed3UL,0x3c50443cUL,0x79558b79UL,0xe2633de2UL,0x1d2c271dUL,0x76419a76UL,0x3bad4d3bUL,0x56c8fa56UL,0x4ee8d24eUL,0x1e28221eUL,0xdb3f76dbUL,0x0a181e0aUL,0x6c90b46cUL,0xe46b37e4UL,0x5d25e75dUL,0x6e61b26eUL,0xef862aefUL,0xa693f1a6UL,0xa872e3a8UL,0xa462f7a4UL,0x37bd5937UL,0x8bff868bUL,0x32b15632UL,0x430dc543UL,0x59dceb59UL,0xb7afc2b7UL,0x8c028f8cUL,0x6479ac64UL,0xd2236dd2UL,0xe0923be0UL,0xb4abc7b4UL,0xfa4315faUL,0x07fd0907UL,0x25856f25UL,0xaf8feaafUL,0x8ef3898eUL,0xe98e20e9UL,0x18202818UL,0xd5de64d5UL,0x88fb8388UL,0x6f94b16fUL,0x72b89672UL,0x24706c24UL,0xf1ae08f1UL,0xc7e652c7UL,0x5135f351UL,0x238d6523UL,0x7c59847cUL,0x9ccbbf9cUL,0x217c6321UL,0xdd377cddUL,0xdcc27fdcUL,0x861a9186UL,0x851e9485UL,0x90dbab90UL,0x42f8c642UL,0xc4e257c4UL,0xaa83e5aaUL,0xd83b73d8UL,0x050c0f05UL,0x01f50301UL,0x12383612UL,0xa39ffea3UL,0x5fd4e15fUL,0xf94710f9UL,0xd0d26bd0UL,0x912ea891UL,0x5829e858UL,0x27746927UL,0xb94ed0b9UL,0x38a94838UL,0x13cd3513UL,0xb356ceb3UL,0x33445533UL,0xbbbfd6bbUL,0x70499070UL,0x890e8089UL,0xa766f2a7UL,0xb65ac1b6UL,0x22786622UL,0x922aad92UL,0x20896020UL,0x4915db49UL,0xff4f1affUL,0x78a08878UL,0x7a518e7aUL,0x8f068a8fUL,0xf8b213f8UL,0x80129b80UL,0x17343917UL,0xdaca75daUL,0x31b55331UL,0xc61351c6UL,0xb8bbd3b8UL,0xc31f5ec3UL,0xb052cbb0UL,0x77b49977UL,0x113c3311UL,0xcbf646cbUL,0xfc4b1ffcUL,0xd6da61d6UL,0x3a584e3aUL, + 0x97f4a5f4UL,0xeb978497UL,0xc7b099b0UL,0xf78c8d8cUL,0xe5170d17UL,0xb7dcbddcUL,0xa7c8b1c8UL,0x39fc54fcUL,0xc0f050f0UL,0x04050305UL,0x87e0a9e0UL,0xac877d87UL,0xd52b192bUL,0x71a662a6UL,0x9a31e631UL,0xc3b59ab5UL,0x05cf45cfUL,0x3ebc9dbcUL,0x09c040c0UL,0xef928792UL,0xc53f153fUL,0x7f26eb26UL,0x0740c940UL,0xed1d0b1dUL,0x822fec2fUL,0x7da967a9UL,0xbe1cfd1cUL,0x8a25ea25UL,0x46dabfdaUL,0xa602f702UL,0xd3a196a1UL,0x2ded5bedUL,0xea5dc25dUL,0xd9241c24UL,0x7ae9aee9UL,0x98be6abeUL,0xd8ee5aeeUL,0xfcc341c3UL,0xf1060206UL,0x1dd14fd1UL,0xd0e45ce4UL,0xa207f407UL,0xb95c345cUL,0xe9180818UL,0xdfae93aeUL,0x4d957395UL,0xc4f553f5UL,0x54413f41UL,0x10140c14UL,0x31f652f6UL,0x8caf65afUL,0x21e25ee2UL,0x60782878UL,0x6ef8a1f8UL,0x14110f11UL,0x5ec4b5c4UL,0x1c1b091bUL,0x485a365aUL,0x36b69bb6UL,0xa5473d47UL,0x816a266aUL,0x9cbb69bbUL,0xfe4ccd4cUL,0xcfba9fbaUL,0x242d1b2dUL,0x3ab99eb9UL,0xb09c749cUL,0x68722e72UL,0x6c772d77UL,0xa3cdb2cdUL,0x7329ee29UL,0xb616fb16UL,0x5301f601UL,0xecd74dd7UL,0x75a361a3UL,0xfa49ce49UL,0xa48d7b8dUL,0xa1423e42UL,0xbc937193UL,0x26a297a2UL,0x5704f504UL,0x69b868b8UL,0x00000000UL,0x99742c74UL,0x80a060a0UL,0xdd211f21UL,0xf243c843UL,0x772ced2cUL,0xb3d9bed9UL,0x01ca46caUL,0xce70d970UL,0xe4dd4bddUL,0x3379de79UL,0x2b67d467UL,0x7b23e823UL,0x11de4adeUL,0x6dbd6bbdUL,0x917e2a7eUL,0x9e34e534UL,0xc13a163aUL,0x1754c554UL,0x2f62d762UL,0xccff55ffUL,0x22a794a7UL,0x0f4acf4aUL,0xc9301030UL,0x080a060aUL,0xe7988198UL,0x5b0bf00bUL,0xf0cc44ccUL,0x4ad5bad5UL,0x963ee33eUL,0x5f0ef30eUL,0xba19fe19UL,0x1b5bc05bUL,0x0a858a85UL,0x7eecadecUL,0x42dfbcdfUL,0xe0d848d8UL,0xf90c040cUL,0xc67adf7aUL,0xee58c158UL,0x459f759fUL,0x84a563a5UL,0x40503050UL,0xd12e1a2eUL,0xe1120e12UL,0x65b76db7UL,0x19d44cd4UL,0x303c143cUL,0x4c5f355fUL,0x9d712f71UL,0x6738e138UL,0x6afda2fdUL,0x0b4fcc4fUL,0x5c4b394bUL,0x3df957f9UL,0xaa0df20dUL,0xe39d829dUL,0xf4c947c9UL,0x8befacefUL,0x6f32e732UL,0x647d2b7dUL,0xd7a495a4UL,0x9bfba0fbUL,0x32b398b3UL,0x2768d168UL,0x5d817f81UL,0x88aa66aaUL,0xa8827e82UL,0x76e6abe6UL,0x169e839eUL,0x0345ca45UL,0x957b297bUL,0xd66ed36eUL,0x50443c44UL,0x558b798bUL,0x633de23dUL,0x2c271d27UL,0x419a769aUL,0xad4d3b4dUL,0xc8fa56faUL,0xe8d24ed2UL,0x28221e22UL,0x3f76db76UL,0x181e0a1eUL,0x90b46cb4UL,0x6b37e437UL,0x25e75de7UL,0x61b26eb2UL,0x862aef2aUL,0x93f1a6f1UL,0x72e3a8e3UL,0x62f7a4f7UL,0xbd593759UL,0xff868b86UL,0xb1563256UL,0x0dc543c5UL,0xdceb59ebUL,0xafc2b7c2UL,0x028f8c8fUL,0x79ac64acUL,0x236dd26dUL,0x923be03bUL,0xabc7b4c7UL,0x4315fa15UL,0xfd090709UL,0x856f256fUL,0x8feaafeaUL,0xf3898e89UL,0x8e20e920UL,0x20281828UL,0xde64d564UL,0xfb838883UL,0x94b16fb1UL,0xb8967296UL,0x706c246cUL,0xae08f108UL,0xe652c752UL,0x35f351f3UL,0x8d652365UL,0x59847c84UL,0xcbbf9cbfUL,0x7c632163UL,0x377cdd7cUL,0xc27fdc7fUL,0x1a918691UL,0x1e948594UL,0xdbab90abUL,0xf8c642c6UL,0xe257c457UL,0x83e5aae5UL,0x3b73d873UL,0x0c0f050fUL,0xf5030103UL,0x38361236UL,0x9ffea3feUL,0xd4e15fe1UL,0x4710f910UL,0xd26bd06bUL,0x2ea891a8UL,0x29e858e8UL,0x74692769UL,0x4ed0b9d0UL,0xa9483848UL,0xcd351335UL,0x56ceb3ceUL,0x44553355UL,0xbfd6bbd6UL,0x49907090UL,0x0e808980UL,0x66f2a7f2UL,0x5ac1b6c1UL,0x78662266UL,0x2aad92adUL,0x89602060UL,0x15db49dbUL,0x4f1aff1aUL,0xa0887888UL,0x518e7a8eUL,0x068a8f8aUL,0xb213f813UL,0x129b809bUL,0x34391739UL,0xca75da75UL,0xb5533153UL,0x1351c651UL,0xbbd3b8d3UL,0x1f5ec35eUL,0x52cbb0cbUL,0xb4997799UL,0x3c331133UL,0xf646cb46UL,0x4b1ffc1fUL,0xda61d661UL,0x584e3a4eUL, + 0xf4a5f432UL,0x9784976fUL,0xb099b05eUL,0x8c8d8c7aUL,0x170d17e8UL,0xdcbddc0aUL,0xc8b1c816UL,0xfc54fc6dUL,0xf050f090UL,0x05030507UL,0xe0a9e02eUL,0x877d87d1UL,0x2b192bccUL,0xa662a613UL,0x31e6317cUL,0xb59ab559UL,0xcf45cf40UL,0xbc9dbca3UL,0xc040c049UL,0x92879268UL,0x3f153fd0UL,0x26eb2694UL,0x40c940ceUL,0x1d0b1de6UL,0x2fec2f6eUL,0xa967a91aUL,0x1cfd1c43UL,0x25ea2560UL,0xdabfdaf9UL,0x02f70251UL,0xa196a145UL,0xed5bed76UL,0x5dc25d28UL,0x241c24c5UL,0xe9aee9d4UL,0xbe6abef2UL,0xee5aee82UL,0xc341c3bdUL,0x060206f3UL,0xd14fd152UL,0xe45ce48cUL,0x07f40756UL,0x5c345c8dUL,0x180818e1UL,0xae93ae4cUL,0x9573953eUL,0xf553f597UL,0x413f416bUL,0x140c141cUL,0xf652f663UL,0xaf65afe9UL,0xe25ee27fUL,0x78287848UL,0xf8a1f8cfUL,0x110f111bUL,0xc4b5c4ebUL,0x1b091b15UL,0x5a365a7eUL,0xb69bb6adUL,0x473d4798UL,0x6a266aa7UL,0xbb69bbf5UL,0x4ccd4c33UL,0xba9fba50UL,0x2d1b2d3fUL,0xb99eb9a4UL,0x9c749cc4UL,0x722e7246UL,0x772d7741UL,0xcdb2cd11UL,0x29ee299dUL,0x16fb164dUL,0x01f601a5UL,0xd74dd7a1UL,0xa361a314UL,0x49ce4934UL,0x8d7b8ddfUL,0x423e429fUL,0x937193cdUL,0xa297a2b1UL,0x04f504a2UL,0xb868b801UL,0x00000000UL,0x742c74b5UL,0xa060a0e0UL,0x211f21c2UL,0x43c8433aUL,0x2ced2c9aUL,0xd9bed90dUL,0xca46ca47UL,0x70d97017UL,0xdd4bddafUL,0x79de79edUL,0x67d467ffUL,0x23e82393UL,0xde4ade5bUL,0xbd6bbd06UL,0x7e2a7ebbUL,0x34e5347bUL,0x3a163ad7UL,0x54c554d2UL,0x62d762f8UL,0xff55ff99UL,0xa794a7b6UL,0x4acf4ac0UL,0x301030d9UL,0x0a060a0eUL,0x98819866UL,0x0bf00babUL,0xcc44ccb4UL,0xd5bad5f0UL,0x3ee33e75UL,0x0ef30eacUL,0x19fe1944UL,0x5bc05bdbUL,0x858a8580UL,0xecadecd3UL,0xdfbcdffeUL,0xd848d8a8UL,0x0c040cfdUL,0x7adf7a19UL,0x58c1582fUL,0x9f759f30UL,0xa563a5e7UL,0x50305070UL,0x2e1a2ecbUL,0x120e12efUL,0xb76db708UL,0xd44cd455UL,0x3c143c24UL,0x5f355f79UL,0x712f71b2UL,0x38e13886UL,0xfda2fdc8UL,0x4fcc4fc7UL,0x4b394b65UL,0xf957f96aUL,0x0df20d58UL,0x9d829d61UL,0xc947c9b3UL,0xefacef27UL,0x32e73288UL,0x7d2b7d4fUL,0xa495a442UL,0xfba0fb3bUL,0xb398b3aaUL,0x68d168f6UL,0x817f8122UL,0xaa66aaeeUL,0x827e82d6UL,0xe6abe6ddUL,0x9e839e95UL,0x45ca45c9UL,0x7b297bbcUL,0x6ed36e05UL,0x443c446cUL,0x8b798b2cUL,0x3de23d81UL,0x271d2731UL,0x9a769a37UL,0x4d3b4d96UL,0xfa56fa9eUL,0xd24ed2a6UL,0x221e2236UL,0x76db76e4UL,0x1e0a1e12UL,0xb46cb4fcUL,0x37e4378fUL,0xe75de778UL,0xb26eb20fUL,0x2aef2a69UL,0xf1a6f135UL,0xe3a8e3daUL,0xf7a4f7c6UL,0x5937598aUL,0x868b8674UL,0x56325683UL,0xc543c54eUL,0xeb59eb85UL,0xc2b7c218UL,0x8f8c8f8eUL,0xac64ac1dUL,0x6dd26df1UL,0x3be03b72UL,0xc7b4c71fUL,0x15fa15b9UL,0x090709faUL,0x6f256fa0UL,0xeaafea20UL,0x898e897dUL,0x20e92067UL,0x28182838UL,0x64d5640bUL,0x83888373UL,0xb16fb1fbUL,0x967296caUL,0x6c246c54UL,0x08f1085fUL,0x52c75221UL,0xf351f364UL,0x652365aeUL,0x847c8425UL,0xbf9cbf57UL,0x6321635dUL,0x7cdd7ceaUL,0x7fdc7f1eUL,0x9186919cUL,0x9485949bUL,0xab90ab4bUL,0xc642c6baUL,0x57c45726UL,0xe5aae529UL,0x73d873e3UL,0x0f050f09UL,0x030103f4UL,0x3612362aUL,0xfea3fe3cUL,0xe15fe18bUL,0x10f910beUL,0x6bd06b02UL,0xa891a8bfUL,0xe858e871UL,0x69276953UL,0xd0b9d0f7UL,0x48384891UL,0x351335deUL,0xceb3cee5UL,0x55335577UL,0xd6bbd604UL,0x90709039UL,0x80898087UL,0xf2a7f2c1UL,0xc1b6c1ecUL,0x6622665aUL,0xad92adb8UL,0x602060a9UL,0xdb49db5cUL,0x1aff1ab0UL,0x887888d8UL,0x8e7a8e2bUL,0x8a8f8a89UL,0x13f8134aUL,0x9b809b92UL,0x39173923UL,0x75da7510UL,0x53315384UL,0x51c651d5UL,0xd3b8d303UL,0x5ec35edcUL,0xcbb0cbe2UL,0x997799c3UL,0x3311332dUL,0x46cb463dUL,0x1ffc1fb7UL,0x61d6610cUL,0x4e3a4e62UL}; +#endif /* IS_LITTLE_ENDIAN */ + +#endif /* __tables_h */ diff --git a/src/crypto/hash-blake.c b/src/crypto/hash-blake.c new file mode 100644 index 00000000..65a88cb9 --- /dev/null +++ b/src/crypto/hash-blake.c @@ -0,0 +1,9 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include + +#include "blake/blake256.h" + +void hash_extra_blake(const void *data, size_t length, unsigned char *hash) { blake256_hash(hash, data, length); } diff --git a/src/crypto/hash-groestl.c b/src/crypto/hash-groestl.c new file mode 100644 index 00000000..9f7c7d14 --- /dev/null +++ b/src/crypto/hash-groestl.c @@ -0,0 +1,15 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include + +#include "groestl/Groestl-opt.h" + +void hash_extra_groestl(const void *data, size_t length, unsigned char *hash) { + hashState context; + + Init(&context, 256); + Update(&context, data, length * 8); + Final(&context, hash); +} diff --git a/src/crypto/hash-jh.c b/src/crypto/hash-jh.c new file mode 100644 index 00000000..4376df88 --- /dev/null +++ b/src/crypto/hash-jh.c @@ -0,0 +1,15 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include +#include +#include + +#include "hash.h" +#include "jh/jh.h" + +void hash_extra_jh(const void *data, size_t length, struct CHash *hash) { + int r = jh_hash(sizeof(struct CHash) * 8, data, 8 * length, hash->data); + assert(SUCCESS == r); +} diff --git a/src/crypto/hash-keccak.c b/src/crypto/hash-keccak.c new file mode 100644 index 00000000..0d3431ca --- /dev/null +++ b/src/crypto/hash-keccak.c @@ -0,0 +1,52 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include +#include + +#include "hash.h" + +void KeccakF1600_StatePermute(void *state); // Forward declarations from keccak/ folder + +// Our Non Standard variant +static int keccak(const uint8_t *in, size_t inlen, uint8_t *md, size_t mdlen) { + struct keccak_state st; + uint8_t temp[144]; + size_t rsiz = sizeof(struct keccak_state) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; + + memset(st.b, 0, sizeof(st)); + + for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) { + for (size_t i = 0; i < rsiz; i++) + st.b[i] ^= in[i]; + KeccakF1600_StatePermute(st.b); + } + + // last block and padding + memcpy(temp, in, inlen); + temp[inlen++] = 1; + memset(temp + inlen, 0, rsiz - inlen); + temp[rsiz - 1] |= 0x80; + + for (size_t i = 0; i < rsiz; i++) + st.b[i] ^= temp[i]; + + KeccakF1600_StatePermute(st.b); + + memcpy(md, st.b, mdlen); + + return 0; +} + +void keccak_permutation(struct keccak_state *state) { KeccakF1600_StatePermute(state); } + +void keccak_into_state(const uint8_t *buf, size_t count, struct keccak_state *state) { + keccak(buf, count, state->b, sizeof(struct keccak_state)); +} + +void cn_fast_hash(const void *data, size_t length, struct CHash *hash) { + struct keccak_state state; + keccak_into_state(data, length, &state); + memcpy(hash->data, &state, sizeof(struct CHash)); +} diff --git a/src/crypto/hash-skein.c b/src/crypto/hash-skein.c new file mode 100644 index 00000000..ce73a7b6 --- /dev/null +++ b/src/crypto/hash-skein.c @@ -0,0 +1,13 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include + +#include "hash.h" +#include "skein/skein.h" + +void hash_extra_skein(const void *data, size_t length, struct CHash *hash) { + int r = skein_hash(8 * sizeof(struct CHash), data, 8 * length, hash->data); + assert(SKEIN_SUCCESS == r); +} diff --git a/src/crypto/hash.cpp b/src/crypto/hash.cpp new file mode 100644 index 00000000..6c488b23 --- /dev/null +++ b/src/crypto/hash.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include +#include + +#include "hash.hpp" + +#if defined(_WIN32) +#include +#else +#include +#endif + +namespace crypto { + +enum { MAP_SIZE = SLOW_HASH_CONTEXT_SIZE + ((-SLOW_HASH_CONTEXT_SIZE) & 0xfff) }; + +#if defined(_WIN32) + +CryptoNightContext::CryptoNightContext() { + data = VirtualAlloc(nullptr, MAP_SIZE, MEM_COMMIT, PAGE_READWRITE); + if (data == nullptr) + throw std::bad_alloc(); +} + +CryptoNightContext::~CryptoNightContext() { + if (!VirtualFree(data, 0, MEM_RELEASE)) + assert(false); +} + +#else + +CryptoNightContext::CryptoNightContext() { +#if !defined(__APPLE__) + data = mmap(nullptr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0); +#else + data = mmap(nullptr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +#endif + if (data == MAP_FAILED) + throw std::bad_alloc(); + mlock(data, MAP_SIZE); +} + +CryptoNightContext::~CryptoNightContext() { + if (munmap(data, MAP_SIZE) != 0) + assert(false); +} + +#endif + +static Hash fill_merge_mining_branches(const std::vector &pitems, size_t depth) { + if (pitems.size() == 1) + return pitems.at(0)->leaf; + std::vector halves[2]; + for (auto pitem : pitems) { + bool dir = (pitem->path.data[depth >> 3] & (1 << (depth & 7))) != 0; + halves[dir].push_back(pitem); + pitem->branch.push_back(Hash{}); + } + Hash hashes[2] = {halves[0].empty() ? Hash{} : fill_merge_mining_branches(halves[0], depth + 1), + halves[1].empty() ? Hash{} : fill_merge_mining_branches(halves[1], depth + 1)}; + for (size_t ha = 0; ha != 2; ++ha) + for (auto pitem : halves[ha]) + pitem->branch.at(depth) = hashes[1 - ha]; + return cn_fast_hash(hashes, 2 * sizeof(Hash)); +} + +Hash fill_merge_mining_branches(MergeMiningItem items[], size_t count) { + assert(count > 0); + if (count == 0) + return Hash{}; + std::vector pitems(count); + for (size_t i = 0; i != count; ++i) + pitems[i] = items + i; + return fill_merge_mining_branches(pitems, 0); +} +} diff --git a/src/crypto/hash.h b/src/crypto/hash.h new file mode 100644 index 00000000..443eca2a --- /dev/null +++ b/src/crypto/hash.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#include +#include +#include + +#if defined(__cplusplus) +#include "generic-ops.hpp" +namespace crypto { +extern "C" { +#endif + +#pragma pack(push, 1) +struct CHash { + unsigned char data[32]; +}; +#pragma pack(pop) + +enum { HASH_DATA_AREA = 136, SLOW_HASH_CONTEXT_SIZE = 2097552 }; + +void cn_fast_hash(const void *data, size_t length, struct CHash *hash); + +void cn_slow_hash(void *scratchpad, const void *data, size_t length, struct CHash *hash); +void cn_slow_hash_platform_independent(void *scratchpad, const void *data, size_t length, struct CHash *hash); + +struct keccak_state { + uint8_t b[200]; +}; + +static_assert(sizeof(struct keccak_state) == 200, "Invalid structure size"); + +void keccak_permutation(struct keccak_state *state); +void keccak_into_state(const uint8_t *buf, size_t count, struct keccak_state *state); + +void hash_extra_blake(const void *data, size_t length, struct CHash *hash); +void hash_extra_groestl(const void *data, size_t length, struct CHash *hash); +void hash_extra_jh(const void *data, size_t length, struct CHash *hash); +void hash_extra_skein(const void *data, size_t length, struct CHash *hash); + +#if defined(__cplusplus) +} +} + +CRYPTO_MAKE_COMPARABLE(crypto, CHash, std::memcmp) + +#endif diff --git a/src/crypto/hash.hpp b/src/crypto/hash.hpp index 2a40af84..0c2468ce 100644 --- a/src/crypto/hash.hpp +++ b/src/crypto/hash.hpp @@ -5,7 +5,8 @@ #include -#include "hash-ops.h" +#include "hash.h" +#include "tree-hash.h" #include "types.hpp" namespace crypto { @@ -13,7 +14,7 @@ namespace crypto { inline Hash cn_fast_hash(const void *data, size_t length) { Hash h; - cn_fast_hash(data, length, h.data); + cn_fast_hash(data, length, &h); return h; } // inline Hash cn_fast_hash(const std::vector & data) { @@ -28,34 +29,37 @@ class CryptoNightContext { CryptoNightContext(const CryptoNightContext &) = delete; void operator=(const CryptoNightContext &) = delete; - inline void cn_slow_hash(const void *src_data, size_t length, unsigned char *hash) { + inline void cn_slow_hash(const void *src_data, size_t length, CHash *hash) { crypto::cn_slow_hash(data, src_data, length, hash); } inline Hash cn_slow_hash(const void *src_data, size_t length) { Hash hash; - crypto::cn_slow_hash(data, src_data, length, hash.data); + crypto::cn_slow_hash(data, src_data, length, &hash); return hash; } + void *get_data() const { return data; } private: void *data; }; -inline Hash tree_hash(const Hash *hashes, size_t count) { +inline Hash tree_hash(const Hash hashes[], size_t count) { Hash root_hash; - tree_hash(reinterpret_cast(hashes), count, root_hash.data); + tree_hash(hashes, count, &root_hash); return root_hash; } -inline void tree_branch(const Hash *hashes, size_t count, Hash *branch) { - tree_branch(reinterpret_cast(hashes), count, - reinterpret_cast(branch)); -} - -inline Hash tree_hash_from_branch(const Hash *branch, size_t depth, const Hash &leaf, const void *path) { +inline Hash tree_hash_from_branch(const Hash branch[], size_t depth, const Hash &leaf, const Hash *path) { Hash root_hash; - tree_hash_from_branch( - reinterpret_cast(branch), depth, leaf.data, path, root_hash.data); + tree_hash_from_branch(branch, depth, &leaf, path, &root_hash); return root_hash; } + +struct MergeMiningItem { + Hash leaf; + Hash path; + std::vector branch; +}; + +Hash fill_merge_mining_branches(MergeMiningItem items[], size_t count); } diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index a1b4d55c..e00c5067 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -6,7 +6,7 @@ #if defined(__GNUC__) #define INITIALIZER(name) __attribute__((constructor(101))) static void name(void) #define FINALIZER(name) __attribute__((destructor(101))) static void name(void) -#define REGISTER_FINALIZER(name) ((void) 0) +#define REGISTER_FINALIZER(name) ((void)0) #elif defined(_MSC_VER) #include @@ -14,17 +14,16 @@ // http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc // http://msdn.microsoft.com/en-us/library/bb918180.aspx #pragma section(".CRT$XCT", read) -#define INITIALIZER(name) \ - static void __cdecl name(void); \ - __declspec(allocate(".CRT$XCT")) void (__cdecl *const _##name)(void) = &name; \ - static void __cdecl name(void) -#define FINALIZER(name) \ - static void __cdecl name(void) +#define INITIALIZER(name) \ + static void __cdecl name(void); \ + __declspec(allocate(".CRT$XCT")) void(__cdecl * const _##name)(void) = &name; \ + static void __cdecl name(void) +#define FINALIZER(name) static void __cdecl name(void) #define REGISTER_FINALIZER(name) \ - do { \ - int _res = atexit(name); \ - assert(_res == 0); \ - } while (0); + do { \ + int _res = atexit(name); \ + assert(_res == 0); \ + } while (0); #else #error Unsupported compiler diff --git a/src/crypto/int-util.h b/src/crypto/int-util.h index 18f38be3..ab63d7d7 100644 --- a/src/crypto/int-util.h +++ b/src/crypto/int-util.h @@ -8,6 +8,8 @@ #include #include +// TODO - if possible, get rid of this crap + #if defined(_MSC_VER) #include #define LITTLE_ENDIAN 1234 @@ -19,13 +21,11 @@ extern "C" { #endif inline uint32_t rol32(uint32_t x, int r) { - static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers"); - return _rotl(x, r); + static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers"); + return _rotl(x, r); } -inline uint64_t rol64(uint64_t x, int r) { - return _rotl64(x, r); -} +inline uint64_t rol64(uint64_t x, int r) { return _rotl64(x, r); } #if defined(__cplusplus) } #endif @@ -36,13 +36,9 @@ inline uint64_t rol64(uint64_t x, int r) { #if defined(__cplusplus) extern "C" { #endif -static inline uint32_t rol32(uint32_t x, int r) { - return (x << (r & 31)) | (x >> (-r & 31)); -} +static inline uint32_t rol32(uint32_t x, int r) { return (x << (r & 31)) | (x >> (-r & 31)); } -static inline uint64_t rol64(uint64_t x, int r) { - return (x << (r & 63)) | (x >> (-r & 63)); -} +static inline uint64_t rol64(uint64_t x, int r) { return (x << (r & 63)) | (x >> (-r & 63)); } #if defined(__cplusplus) } #endif @@ -53,153 +49,84 @@ static inline uint64_t rol64(uint64_t x, int r) { extern "C" { #endif -static inline uint64_t hi_dword(uint64_t val) { - return val >> 32; -} +static inline uint64_t hi_dword(uint64_t val) { return val >> 32; } -static inline uint64_t lo_dword(uint64_t val) { - return val & 0xFFFFFFFF; -} +static inline uint64_t lo_dword(uint64_t val) { return val & 0xFFFFFFFF; } -static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t * product_hi) { +static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { #if defined(__GNUC__) && defined(__x86_64__) uint64_t hi, lo; - __asm__("mulq %3\n\t" - : "=d" (hi), - "=a" (lo) - : "%a" (multiplier), - "rm" (multiplicand) - : "cc" ); + __asm__("mulq %3\n\t" : "=d"(hi), "=a"(lo) : "%a"(multiplier), "rm"(multiplicand) : "cc"); *product_hi = hi; return lo; #elif defined(__SIZEOF_INT128__) typedef unsigned __int128 uint128_t; uint128_t res = (uint128_t)multiplier * (uint128_t)multiplicand; - *product_hi = (uint64_t) (res >> 64); - return (uint64_t) res; + *product_hi = (uint64_t)(res >> 64); + return (uint64_t)res; #else - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = hi_dword(multiplier); - uint64_t b = lo_dword(multiplier); - uint64_t c = hi_dword(multiplicand); - uint64_t d = lo_dword(multiplicand); - - uint64_t ac = a * c; - uint64_t ad = a * d; - uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + bc; - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - assert(ac <= *product_hi); - - return product_lo; + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = hi_dword(multiplier); + uint64_t b = lo_dword(multiplier); + uint64_t c = hi_dword(multiplicand); + uint64_t d = lo_dword(multiplicand); + + uint64_t ac = a * c; + uint64_t ad = a * d; + uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + bc; + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + assert(ac <= *product_hi); + + return product_lo; #endif } -static inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) { - dividend |= ((uint64_t)*remainder) << 32; - *remainder = dividend % divisor; - return dividend / divisor; +static inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t *remainder) { + dividend |= ((uint64_t)*remainder) << 32; + *remainder = dividend % divisor; + return dividend / divisor; } // Long division with 2^32 base -static inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) { - uint64_t dividend_dwords[4]; - uint32_t remainder = 0; - - dividend_dwords[3] = hi_dword(dividend_hi); - dividend_dwords[2] = lo_dword(dividend_hi); - dividend_dwords[1] = hi_dword(dividend_lo); - dividend_dwords[0] = lo_dword(dividend_lo); - - *quotient_hi = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32; - *quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder); - *quotient_lo = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32; - *quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder); - - return remainder; +static inline uint32_t div128_32(uint64_t dividend_hi, + uint64_t dividend_lo, + uint32_t divisor, + uint64_t *quotient_hi, + uint64_t *quotient_lo) { + uint64_t dividend_dwords[4]; + uint32_t remainder = 0; + + dividend_dwords[3] = hi_dword(dividend_hi); + dividend_dwords[2] = lo_dword(dividend_hi); + dividend_dwords[1] = hi_dword(dividend_lo); + dividend_dwords[0] = lo_dword(dividend_lo); + + *quotient_hi = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32; + *quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder); + *quotient_lo = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32; + *quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder); + + return remainder; } -#define IDENT32(x) ((uint32_t) (x)) -#define IDENT64(x) ((uint64_t) (x)) - -#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \ - (((uint32_t) (x) & 0x0000ff00) << 8) | \ - (((uint32_t) (x) & 0x00ff0000) >> 8) | \ - (((uint32_t) (x) & 0xff000000) >> 24)) -#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \ - (((uint64_t) (x) & 0x000000000000ff00) << 40) | \ - (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \ - (((uint64_t) (x) & 0x00000000ff000000) << 8) | \ - (((uint64_t) (x) & 0x000000ff00000000) >> 8) | \ - (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \ - (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \ - (((uint64_t) (x) & 0xff00000000000000) >> 56)) - -static inline uint32_t ident32(uint32_t x) { return x; } -static inline uint64_t ident64(uint64_t x) { return x; } - -static inline uint32_t swap32(uint32_t x) { - x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8); - return (x << 16) | (x >> 16); -} -static inline uint64_t swap64(uint64_t x) { - x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8); - x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16); - return (x << 32) | (x >> 32); -} +#define IDENT32(x) ((uint32_t)(x)) -#if defined(__GNUC__) -#define UNUSED __attribute__((unused)) -#else -#define UNUSED -#endif -//inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { } -#undef UNUSED - -//inline void mem_inplace_swap32(void *mem, size_t n) { -// size_t i; -// for (i = 0; i < n; i++) { -// ((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]); -// } -//} -//inline void mem_inplace_swap64(void *mem, size_t n) { -// size_t i; -// for (i = 0; i < n; i++) { -// ((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]); -// } -//} - -//inline void memcpy_ident32(void *dst, const void *src, size_t n) { -// memcpy(dst, src, 4 * n); -//} -//inline void memcpy_ident64(void *dst, const void *src, size_t n) { -// memcpy(dst, src, 8 * n); -//} -// -//inline void memcpy_swap32(void *dst, const void *src, size_t n) { -// size_t i; -// for (i = 0; i < n; i++) { -// ((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]); -// } -//} -//inline void memcpy_swap64(void *dst, const void *src, size_t n) { -// size_t i; -// for (i = 0; i < n; i++) { -// ((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]); -// } -//} +#define SWAP32(x) \ + ((((uint32_t)(x)&0x000000ff) << 24) | (((uint32_t)(x)&0x0000ff00) << 8) | (((uint32_t)(x)&0x00ff0000) >> 8) | \ + (((uint32_t)(x)&0xff000000) >> 24)) #if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) -//static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled"); +// static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled"); #define LITTLE_ENDIAN 1234 #define BIG_ENDIAN 4321 #define BYTE_ORDER LITTLE_ENDIAN @@ -207,40 +134,10 @@ static inline uint64_t swap64(uint64_t x) { #if BYTE_ORDER == LITTLE_ENDIAN #define SWAP32LE IDENT32 -#define SWAP32BE SWAP32 -#define swap32le ident32 -#define swap32be swap32 -#define mem_inplace_swap32le mem_inplace_ident -#define mem_inplace_swap32be mem_inplace_swap32 -#define memcpy_swap32le memcpy_ident32 -#define memcpy_swap32be memcpy_swap32 -#define SWAP64LE IDENT64 -#define SWAP64BE SWAP64 -#define swap64le ident64 -#define swap64be swap64 -#define mem_inplace_swap64le mem_inplace_ident -#define mem_inplace_swap64be mem_inplace_swap64 -#define memcpy_swap64le memcpy_ident64 -#define memcpy_swap64be memcpy_swap64 #endif #if BYTE_ORDER == BIG_ENDIAN -#define SWAP32BE IDENT32 #define SWAP32LE SWAP32 -#define swap32be ident32 -#define swap32le swap32 -#define mem_inplace_swap32be mem_inplace_ident -#define mem_inplace_swap32le mem_inplace_swap32 -#define memcpy_swap32be memcpy_ident32 -#define memcpy_swap32le memcpy_swap32 -#define SWAP64BE IDENT64 -#define SWAP64LE SWAP64 -#define swap64be ident64 -#define swap64le swap64 -#define mem_inplace_swap64be mem_inplace_ident -#define mem_inplace_swap64le mem_inplace_swap64 -#define memcpy_swap64be memcpy_ident64 -#define memcpy_swap64le memcpy_swap64 #endif #if defined(__cplusplus) diff --git a/src/crypto/jh/jh.c b/src/crypto/jh/jh.c new file mode 100644 index 00000000..a0bd253d --- /dev/null +++ b/src/crypto/jh/jh.c @@ -0,0 +1,367 @@ +/*This program gives the 64-bit optimized bitslice implementation of JH using ANSI C + + -------------------------------- + Performance + + Microprocessor: Intel CORE 2 processor (Core 2 Duo Mobile T6600 2.2GHz) + Operating System: 64-bit Ubuntu 10.04 (Linux kernel 2.6.32-22-generic) + Speed for long message: + 1) 45.8 cycles/byte compiler: Intel C++ Compiler 11.1 compilation option: icc -O2 + 2) 56.8 cycles/byte compiler: gcc 4.4.3 compilation option: gcc -O3 + + -------------------------------- + Last Modified: January 16, 2011 +*/ + +#include "jh.h" + +#include +#include + +/*typedef unsigned long long uint64;*/ +typedef uint64_t uint64; + +/*define data alignment for different C compilers*/ +#if defined(__GNUC__) + #define DATA_ALIGN16(x) x __attribute__ ((aligned(16))) +#else + #define DATA_ALIGN16(x) __declspec(align(16)) x +#endif + + +typedef struct { + int hashbitlen; /*the message digest size*/ + unsigned long long databitlen; /*the message size in bits*/ + unsigned long long datasize_in_buffer; /*the size of the message remained in buffer; assumed to be multiple of 8bits except for the last partial block at the end of the message*/ + DATA_ALIGN16(uint64 x[8][2]); /*the 1024-bit state, ( x[i][0] || x[i][1] ) is the ith row of the state in the pseudocode*/ + unsigned char buffer[64]; /*the 512-bit message block to be hashed;*/ +} hashState; + + +/*The initial hash value H(0)*/ +const unsigned char JH224_H0[128]={0x2d,0xfe,0xdd,0x62,0xf9,0x9a,0x98,0xac,0xae,0x7c,0xac,0xd6,0x19,0xd6,0x34,0xe7,0xa4,0x83,0x10,0x5,0xbc,0x30,0x12,0x16,0xb8,0x60,0x38,0xc6,0xc9,0x66,0x14,0x94,0x66,0xd9,0x89,0x9f,0x25,0x80,0x70,0x6f,0xce,0x9e,0xa3,0x1b,0x1d,0x9b,0x1a,0xdc,0x11,0xe8,0x32,0x5f,0x7b,0x36,0x6e,0x10,0xf9,0x94,0x85,0x7f,0x2,0xfa,0x6,0xc1,0x1b,0x4f,0x1b,0x5c,0xd8,0xc8,0x40,0xb3,0x97,0xf6,0xa1,0x7f,0x6e,0x73,0x80,0x99,0xdc,0xdf,0x93,0xa5,0xad,0xea,0xa3,0xd3,0xa4,0x31,0xe8,0xde,0xc9,0x53,0x9a,0x68,0x22,0xb4,0xa9,0x8a,0xec,0x86,0xa1,0xe4,0xd5,0x74,0xac,0x95,0x9c,0xe5,0x6c,0xf0,0x15,0x96,0xd,0xea,0xb5,0xab,0x2b,0xbf,0x96,0x11,0xdc,0xf0,0xdd,0x64,0xea,0x6e}; +const unsigned char JH256_H0[128]={0xeb,0x98,0xa3,0x41,0x2c,0x20,0xd3,0xeb,0x92,0xcd,0xbe,0x7b,0x9c,0xb2,0x45,0xc1,0x1c,0x93,0x51,0x91,0x60,0xd4,0xc7,0xfa,0x26,0x0,0x82,0xd6,0x7e,0x50,0x8a,0x3,0xa4,0x23,0x9e,0x26,0x77,0x26,0xb9,0x45,0xe0,0xfb,0x1a,0x48,0xd4,0x1a,0x94,0x77,0xcd,0xb5,0xab,0x26,0x2,0x6b,0x17,0x7a,0x56,0xf0,0x24,0x42,0xf,0xff,0x2f,0xa8,0x71,0xa3,0x96,0x89,0x7f,0x2e,0x4d,0x75,0x1d,0x14,0x49,0x8,0xf7,0x7d,0xe2,0x62,0x27,0x76,0x95,0xf7,0x76,0x24,0x8f,0x94,0x87,0xd5,0xb6,0x57,0x47,0x80,0x29,0x6c,0x5c,0x5e,0x27,0x2d,0xac,0x8e,0xd,0x6c,0x51,0x84,0x50,0xc6,0x57,0x5,0x7a,0xf,0x7b,0xe4,0xd3,0x67,0x70,0x24,0x12,0xea,0x89,0xe3,0xab,0x13,0xd3,0x1c,0xd7,0x69}; +const unsigned char JH384_H0[128]={0x48,0x1e,0x3b,0xc6,0xd8,0x13,0x39,0x8a,0x6d,0x3b,0x5e,0x89,0x4a,0xde,0x87,0x9b,0x63,0xfa,0xea,0x68,0xd4,0x80,0xad,0x2e,0x33,0x2c,0xcb,0x21,0x48,0xf,0x82,0x67,0x98,0xae,0xc8,0x4d,0x90,0x82,0xb9,0x28,0xd4,0x55,0xea,0x30,0x41,0x11,0x42,0x49,0x36,0xf5,0x55,0xb2,0x92,0x48,0x47,0xec,0xc7,0x25,0xa,0x93,0xba,0xf4,0x3c,0xe1,0x56,0x9b,0x7f,0x8a,0x27,0xdb,0x45,0x4c,0x9e,0xfc,0xbd,0x49,0x63,0x97,0xaf,0xe,0x58,0x9f,0xc2,0x7d,0x26,0xaa,0x80,0xcd,0x80,0xc0,0x8b,0x8c,0x9d,0xeb,0x2e,0xda,0x8a,0x79,0x81,0xe8,0xf8,0xd5,0x37,0x3a,0xf4,0x39,0x67,0xad,0xdd,0xd1,0x7a,0x71,0xa9,0xb4,0xd3,0xbd,0xa4,0x75,0xd3,0x94,0x97,0x6c,0x3f,0xba,0x98,0x42,0x73,0x7f}; +const unsigned char JH512_H0[128]={0x6f,0xd1,0x4b,0x96,0x3e,0x0,0xaa,0x17,0x63,0x6a,0x2e,0x5,0x7a,0x15,0xd5,0x43,0x8a,0x22,0x5e,0x8d,0xc,0x97,0xef,0xb,0xe9,0x34,0x12,0x59,0xf2,0xb3,0xc3,0x61,0x89,0x1d,0xa0,0xc1,0x53,0x6f,0x80,0x1e,0x2a,0xa9,0x5,0x6b,0xea,0x2b,0x6d,0x80,0x58,0x8e,0xcc,0xdb,0x20,0x75,0xba,0xa6,0xa9,0xf,0x3a,0x76,0xba,0xf8,0x3b,0xf7,0x1,0x69,0xe6,0x5,0x41,0xe3,0x4a,0x69,0x46,0xb5,0x8a,0x8e,0x2e,0x6f,0xe6,0x5a,0x10,0x47,0xa7,0xd0,0xc1,0x84,0x3c,0x24,0x3b,0x6e,0x71,0xb1,0x2d,0x5a,0xc1,0x99,0xcf,0x57,0xf6,0xec,0x9d,0xb1,0xf8,0x56,0xa7,0x6,0x88,0x7c,0x57,0x16,0xb1,0x56,0xe3,0xc2,0xfc,0xdf,0xe6,0x85,0x17,0xfb,0x54,0x5a,0x46,0x78,0xcc,0x8c,0xdd,0x4b}; + +/*42 round constants, each round constant is 32-byte (256-bit)*/ +const unsigned char E8_bitslice_roundconstant[42][32]={ +{0x72,0xd5,0xde,0xa2,0xdf,0x15,0xf8,0x67,0x7b,0x84,0x15,0xa,0xb7,0x23,0x15,0x57,0x81,0xab,0xd6,0x90,0x4d,0x5a,0x87,0xf6,0x4e,0x9f,0x4f,0xc5,0xc3,0xd1,0x2b,0x40}, +{0xea,0x98,0x3a,0xe0,0x5c,0x45,0xfa,0x9c,0x3,0xc5,0xd2,0x99,0x66,0xb2,0x99,0x9a,0x66,0x2,0x96,0xb4,0xf2,0xbb,0x53,0x8a,0xb5,0x56,0x14,0x1a,0x88,0xdb,0xa2,0x31}, +{0x3,0xa3,0x5a,0x5c,0x9a,0x19,0xe,0xdb,0x40,0x3f,0xb2,0xa,0x87,0xc1,0x44,0x10,0x1c,0x5,0x19,0x80,0x84,0x9e,0x95,0x1d,0x6f,0x33,0xeb,0xad,0x5e,0xe7,0xcd,0xdc}, +{0x10,0xba,0x13,0x92,0x2,0xbf,0x6b,0x41,0xdc,0x78,0x65,0x15,0xf7,0xbb,0x27,0xd0,0xa,0x2c,0x81,0x39,0x37,0xaa,0x78,0x50,0x3f,0x1a,0xbf,0xd2,0x41,0x0,0x91,0xd3}, +{0x42,0x2d,0x5a,0xd,0xf6,0xcc,0x7e,0x90,0xdd,0x62,0x9f,0x9c,0x92,0xc0,0x97,0xce,0x18,0x5c,0xa7,0xb,0xc7,0x2b,0x44,0xac,0xd1,0xdf,0x65,0xd6,0x63,0xc6,0xfc,0x23}, +{0x97,0x6e,0x6c,0x3,0x9e,0xe0,0xb8,0x1a,0x21,0x5,0x45,0x7e,0x44,0x6c,0xec,0xa8,0xee,0xf1,0x3,0xbb,0x5d,0x8e,0x61,0xfa,0xfd,0x96,0x97,0xb2,0x94,0x83,0x81,0x97}, +{0x4a,0x8e,0x85,0x37,0xdb,0x3,0x30,0x2f,0x2a,0x67,0x8d,0x2d,0xfb,0x9f,0x6a,0x95,0x8a,0xfe,0x73,0x81,0xf8,0xb8,0x69,0x6c,0x8a,0xc7,0x72,0x46,0xc0,0x7f,0x42,0x14}, +{0xc5,0xf4,0x15,0x8f,0xbd,0xc7,0x5e,0xc4,0x75,0x44,0x6f,0xa7,0x8f,0x11,0xbb,0x80,0x52,0xde,0x75,0xb7,0xae,0xe4,0x88,0xbc,0x82,0xb8,0x0,0x1e,0x98,0xa6,0xa3,0xf4}, +{0x8e,0xf4,0x8f,0x33,0xa9,0xa3,0x63,0x15,0xaa,0x5f,0x56,0x24,0xd5,0xb7,0xf9,0x89,0xb6,0xf1,0xed,0x20,0x7c,0x5a,0xe0,0xfd,0x36,0xca,0xe9,0x5a,0x6,0x42,0x2c,0x36}, +{0xce,0x29,0x35,0x43,0x4e,0xfe,0x98,0x3d,0x53,0x3a,0xf9,0x74,0x73,0x9a,0x4b,0xa7,0xd0,0xf5,0x1f,0x59,0x6f,0x4e,0x81,0x86,0xe,0x9d,0xad,0x81,0xaf,0xd8,0x5a,0x9f}, +{0xa7,0x5,0x6,0x67,0xee,0x34,0x62,0x6a,0x8b,0xb,0x28,0xbe,0x6e,0xb9,0x17,0x27,0x47,0x74,0x7,0x26,0xc6,0x80,0x10,0x3f,0xe0,0xa0,0x7e,0x6f,0xc6,0x7e,0x48,0x7b}, +{0xd,0x55,0xa,0xa5,0x4a,0xf8,0xa4,0xc0,0x91,0xe3,0xe7,0x9f,0x97,0x8e,0xf1,0x9e,0x86,0x76,0x72,0x81,0x50,0x60,0x8d,0xd4,0x7e,0x9e,0x5a,0x41,0xf3,0xe5,0xb0,0x62}, +{0xfc,0x9f,0x1f,0xec,0x40,0x54,0x20,0x7a,0xe3,0xe4,0x1a,0x0,0xce,0xf4,0xc9,0x84,0x4f,0xd7,0x94,0xf5,0x9d,0xfa,0x95,0xd8,0x55,0x2e,0x7e,0x11,0x24,0xc3,0x54,0xa5}, +{0x5b,0xdf,0x72,0x28,0xbd,0xfe,0x6e,0x28,0x78,0xf5,0x7f,0xe2,0xf,0xa5,0xc4,0xb2,0x5,0x89,0x7c,0xef,0xee,0x49,0xd3,0x2e,0x44,0x7e,0x93,0x85,0xeb,0x28,0x59,0x7f}, +{0x70,0x5f,0x69,0x37,0xb3,0x24,0x31,0x4a,0x5e,0x86,0x28,0xf1,0x1d,0xd6,0xe4,0x65,0xc7,0x1b,0x77,0x4,0x51,0xb9,0x20,0xe7,0x74,0xfe,0x43,0xe8,0x23,0xd4,0x87,0x8a}, +{0x7d,0x29,0xe8,0xa3,0x92,0x76,0x94,0xf2,0xdd,0xcb,0x7a,0x9,0x9b,0x30,0xd9,0xc1,0x1d,0x1b,0x30,0xfb,0x5b,0xdc,0x1b,0xe0,0xda,0x24,0x49,0x4f,0xf2,0x9c,0x82,0xbf}, +{0xa4,0xe7,0xba,0x31,0xb4,0x70,0xbf,0xff,0xd,0x32,0x44,0x5,0xde,0xf8,0xbc,0x48,0x3b,0xae,0xfc,0x32,0x53,0xbb,0xd3,0x39,0x45,0x9f,0xc3,0xc1,0xe0,0x29,0x8b,0xa0}, +{0xe5,0xc9,0x5,0xfd,0xf7,0xae,0x9,0xf,0x94,0x70,0x34,0x12,0x42,0x90,0xf1,0x34,0xa2,0x71,0xb7,0x1,0xe3,0x44,0xed,0x95,0xe9,0x3b,0x8e,0x36,0x4f,0x2f,0x98,0x4a}, +{0x88,0x40,0x1d,0x63,0xa0,0x6c,0xf6,0x15,0x47,0xc1,0x44,0x4b,0x87,0x52,0xaf,0xff,0x7e,0xbb,0x4a,0xf1,0xe2,0xa,0xc6,0x30,0x46,0x70,0xb6,0xc5,0xcc,0x6e,0x8c,0xe6}, +{0xa4,0xd5,0xa4,0x56,0xbd,0x4f,0xca,0x0,0xda,0x9d,0x84,0x4b,0xc8,0x3e,0x18,0xae,0x73,0x57,0xce,0x45,0x30,0x64,0xd1,0xad,0xe8,0xa6,0xce,0x68,0x14,0x5c,0x25,0x67}, +{0xa3,0xda,0x8c,0xf2,0xcb,0xe,0xe1,0x16,0x33,0xe9,0x6,0x58,0x9a,0x94,0x99,0x9a,0x1f,0x60,0xb2,0x20,0xc2,0x6f,0x84,0x7b,0xd1,0xce,0xac,0x7f,0xa0,0xd1,0x85,0x18}, +{0x32,0x59,0x5b,0xa1,0x8d,0xdd,0x19,0xd3,0x50,0x9a,0x1c,0xc0,0xaa,0xa5,0xb4,0x46,0x9f,0x3d,0x63,0x67,0xe4,0x4,0x6b,0xba,0xf6,0xca,0x19,0xab,0xb,0x56,0xee,0x7e}, +{0x1f,0xb1,0x79,0xea,0xa9,0x28,0x21,0x74,0xe9,0xbd,0xf7,0x35,0x3b,0x36,0x51,0xee,0x1d,0x57,0xac,0x5a,0x75,0x50,0xd3,0x76,0x3a,0x46,0xc2,0xfe,0xa3,0x7d,0x70,0x1}, +{0xf7,0x35,0xc1,0xaf,0x98,0xa4,0xd8,0x42,0x78,0xed,0xec,0x20,0x9e,0x6b,0x67,0x79,0x41,0x83,0x63,0x15,0xea,0x3a,0xdb,0xa8,0xfa,0xc3,0x3b,0x4d,0x32,0x83,0x2c,0x83}, +{0xa7,0x40,0x3b,0x1f,0x1c,0x27,0x47,0xf3,0x59,0x40,0xf0,0x34,0xb7,0x2d,0x76,0x9a,0xe7,0x3e,0x4e,0x6c,0xd2,0x21,0x4f,0xfd,0xb8,0xfd,0x8d,0x39,0xdc,0x57,0x59,0xef}, +{0x8d,0x9b,0xc,0x49,0x2b,0x49,0xeb,0xda,0x5b,0xa2,0xd7,0x49,0x68,0xf3,0x70,0xd,0x7d,0x3b,0xae,0xd0,0x7a,0x8d,0x55,0x84,0xf5,0xa5,0xe9,0xf0,0xe4,0xf8,0x8e,0x65}, +{0xa0,0xb8,0xa2,0xf4,0x36,0x10,0x3b,0x53,0xc,0xa8,0x7,0x9e,0x75,0x3e,0xec,0x5a,0x91,0x68,0x94,0x92,0x56,0xe8,0x88,0x4f,0x5b,0xb0,0x5c,0x55,0xf8,0xba,0xbc,0x4c}, +{0xe3,0xbb,0x3b,0x99,0xf3,0x87,0x94,0x7b,0x75,0xda,0xf4,0xd6,0x72,0x6b,0x1c,0x5d,0x64,0xae,0xac,0x28,0xdc,0x34,0xb3,0x6d,0x6c,0x34,0xa5,0x50,0xb8,0x28,0xdb,0x71}, +{0xf8,0x61,0xe2,0xf2,0x10,0x8d,0x51,0x2a,0xe3,0xdb,0x64,0x33,0x59,0xdd,0x75,0xfc,0x1c,0xac,0xbc,0xf1,0x43,0xce,0x3f,0xa2,0x67,0xbb,0xd1,0x3c,0x2,0xe8,0x43,0xb0}, +{0x33,0xa,0x5b,0xca,0x88,0x29,0xa1,0x75,0x7f,0x34,0x19,0x4d,0xb4,0x16,0x53,0x5c,0x92,0x3b,0x94,0xc3,0xe,0x79,0x4d,0x1e,0x79,0x74,0x75,0xd7,0xb6,0xee,0xaf,0x3f}, +{0xea,0xa8,0xd4,0xf7,0xbe,0x1a,0x39,0x21,0x5c,0xf4,0x7e,0x9,0x4c,0x23,0x27,0x51,0x26,0xa3,0x24,0x53,0xba,0x32,0x3c,0xd2,0x44,0xa3,0x17,0x4a,0x6d,0xa6,0xd5,0xad}, +{0xb5,0x1d,0x3e,0xa6,0xaf,0xf2,0xc9,0x8,0x83,0x59,0x3d,0x98,0x91,0x6b,0x3c,0x56,0x4c,0xf8,0x7c,0xa1,0x72,0x86,0x60,0x4d,0x46,0xe2,0x3e,0xcc,0x8,0x6e,0xc7,0xf6}, +{0x2f,0x98,0x33,0xb3,0xb1,0xbc,0x76,0x5e,0x2b,0xd6,0x66,0xa5,0xef,0xc4,0xe6,0x2a,0x6,0xf4,0xb6,0xe8,0xbe,0xc1,0xd4,0x36,0x74,0xee,0x82,0x15,0xbc,0xef,0x21,0x63}, +{0xfd,0xc1,0x4e,0xd,0xf4,0x53,0xc9,0x69,0xa7,0x7d,0x5a,0xc4,0x6,0x58,0x58,0x26,0x7e,0xc1,0x14,0x16,0x6,0xe0,0xfa,0x16,0x7e,0x90,0xaf,0x3d,0x28,0x63,0x9d,0x3f}, +{0xd2,0xc9,0xf2,0xe3,0x0,0x9b,0xd2,0xc,0x5f,0xaa,0xce,0x30,0xb7,0xd4,0xc,0x30,0x74,0x2a,0x51,0x16,0xf2,0xe0,0x32,0x98,0xd,0xeb,0x30,0xd8,0xe3,0xce,0xf8,0x9a}, +{0x4b,0xc5,0x9e,0x7b,0xb5,0xf1,0x79,0x92,0xff,0x51,0xe6,0x6e,0x4,0x86,0x68,0xd3,0x9b,0x23,0x4d,0x57,0xe6,0x96,0x67,0x31,0xcc,0xe6,0xa6,0xf3,0x17,0xa,0x75,0x5}, +{0xb1,0x76,0x81,0xd9,0x13,0x32,0x6c,0xce,0x3c,0x17,0x52,0x84,0xf8,0x5,0xa2,0x62,0xf4,0x2b,0xcb,0xb3,0x78,0x47,0x15,0x47,0xff,0x46,0x54,0x82,0x23,0x93,0x6a,0x48}, +{0x38,0xdf,0x58,0x7,0x4e,0x5e,0x65,0x65,0xf2,0xfc,0x7c,0x89,0xfc,0x86,0x50,0x8e,0x31,0x70,0x2e,0x44,0xd0,0xb,0xca,0x86,0xf0,0x40,0x9,0xa2,0x30,0x78,0x47,0x4e}, +{0x65,0xa0,0xee,0x39,0xd1,0xf7,0x38,0x83,0xf7,0x5e,0xe9,0x37,0xe4,0x2c,0x3a,0xbd,0x21,0x97,0xb2,0x26,0x1,0x13,0xf8,0x6f,0xa3,0x44,0xed,0xd1,0xef,0x9f,0xde,0xe7}, +{0x8b,0xa0,0xdf,0x15,0x76,0x25,0x92,0xd9,0x3c,0x85,0xf7,0xf6,0x12,0xdc,0x42,0xbe,0xd8,0xa7,0xec,0x7c,0xab,0x27,0xb0,0x7e,0x53,0x8d,0x7d,0xda,0xaa,0x3e,0xa8,0xde}, +{0xaa,0x25,0xce,0x93,0xbd,0x2,0x69,0xd8,0x5a,0xf6,0x43,0xfd,0x1a,0x73,0x8,0xf9,0xc0,0x5f,0xef,0xda,0x17,0x4a,0x19,0xa5,0x97,0x4d,0x66,0x33,0x4c,0xfd,0x21,0x6a}, +{0x35,0xb4,0x98,0x31,0xdb,0x41,0x15,0x70,0xea,0x1e,0xf,0xbb,0xed,0xcd,0x54,0x9b,0x9a,0xd0,0x63,0xa1,0x51,0x97,0x40,0x72,0xf6,0x75,0x9d,0xbf,0x91,0x47,0x6f,0xe2}}; + + +static void E8(hashState *state); /*The bijective function E8, in bitslice form*/ +static void F8(hashState *state); /*The compression function F8 */ + +/*The API functions*/ +static HashReturn Init(hashState *state, int hashbitlen); +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen); +static HashReturn Final(hashState *state, BitSequence *hashval); +HashReturn jh_hash(int hashbitlen, const BitSequence *data,DataLength databitlen, BitSequence *hashval); + +/*swapping bit 2i with bit 2i+1 of 64-bit x*/ +#define SWAP1(x) (x) = ((((x) & 0x5555555555555555ULL) << 1) | (((x) & 0xaaaaaaaaaaaaaaaaULL) >> 1)); +/*swapping bits 4i||4i+1 with bits 4i+2||4i+3 of 64-bit x*/ +#define SWAP2(x) (x) = ((((x) & 0x3333333333333333ULL) << 2) | (((x) & 0xccccccccccccccccULL) >> 2)); +/*swapping bits 8i||8i+1||8i+2||8i+3 with bits 8i+4||8i+5||8i+6||8i+7 of 64-bit x*/ +#define SWAP4(x) (x) = ((((x) & 0x0f0f0f0f0f0f0f0fULL) << 4) | (((x) & 0xf0f0f0f0f0f0f0f0ULL) >> 4)); +/*swapping bits 16i||16i+1||......||16i+7 with bits 16i+8||16i+9||......||16i+15 of 64-bit x*/ +#define SWAP8(x) (x) = ((((x) & 0x00ff00ff00ff00ffULL) << 8) | (((x) & 0xff00ff00ff00ff00ULL) >> 8)); +/*swapping bits 32i||32i+1||......||32i+15 with bits 32i+16||32i+17||......||32i+31 of 64-bit x*/ +#define SWAP16(x) (x) = ((((x) & 0x0000ffff0000ffffULL) << 16) | (((x) & 0xffff0000ffff0000ULL) >> 16)); +/*swapping bits 64i||64i+1||......||64i+31 with bits 64i+32||64i+33||......||64i+63 of 64-bit x*/ +#define SWAP32(x) (x) = (((x) << 32) | ((x) >> 32)); + +/*The MDS transform*/ +#define L(m0,m1,m2,m3,m4,m5,m6,m7) \ + (m4) ^= (m1); \ + (m5) ^= (m2); \ + (m6) ^= (m0) ^ (m3); \ + (m7) ^= (m0); \ + (m0) ^= (m5); \ + (m1) ^= (m6); \ + (m2) ^= (m4) ^ (m7); \ + (m3) ^= (m4); + +/*Two Sboxes are computed in parallel, each Sbox implements S0 and S1, selected by a constant bit*/ +/*The reason to compute two Sboxes in parallel is to try to fully utilize the parallel processing power*/ +#define SS(m0,m1,m2,m3,m4,m5,m6,m7,cc0,cc1) \ + m3 = ~(m3); \ + m7 = ~(m7); \ + m0 ^= ((~(m2)) & (cc0)); \ + m4 ^= ((~(m6)) & (cc1)); \ + temp0 = (cc0) ^ ((m0) & (m1));\ + temp1 = (cc1) ^ ((m4) & (m5));\ + m0 ^= ((m2) & (m3)); \ + m4 ^= ((m6) & (m7)); \ + m3 ^= ((~(m1)) & (m2)); \ + m7 ^= ((~(m5)) & (m6)); \ + m1 ^= ((m0) & (m2)); \ + m5 ^= ((m4) & (m6)); \ + m2 ^= ((m0) & (~(m3))); \ + m6 ^= ((m4) & (~(m7))); \ + m0 ^= ((m1) | (m3)); \ + m4 ^= ((m5) | (m7)); \ + m3 ^= ((m1) & (m2)); \ + m7 ^= ((m5) & (m6)); \ + m1 ^= (temp0 & (m0)); \ + m5 ^= (temp1 & (m4)); \ + m2 ^= temp0; \ + m6 ^= temp1; + +/*The bijective function E8, in bitslice form*/ +static void E8(hashState *state) +{ + uint64 i,roundnumber,temp0,temp1; + + for (roundnumber = 0; roundnumber < 42; roundnumber = roundnumber+7) { + /*round 7*roundnumber+0: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+0])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+0])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP1(state->x[1][i]); SWAP1(state->x[3][i]); SWAP1(state->x[5][i]); SWAP1(state->x[7][i]); + } + + /*round 7*roundnumber+1: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+1])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+1])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP2(state->x[1][i]); SWAP2(state->x[3][i]); SWAP2(state->x[5][i]); SWAP2(state->x[7][i]); + } + + /*round 7*roundnumber+2: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+2])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+2])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP4(state->x[1][i]); SWAP4(state->x[3][i]); SWAP4(state->x[5][i]); SWAP4(state->x[7][i]); + } + + /*round 7*roundnumber+3: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+3])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+3])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP8(state->x[1][i]); SWAP8(state->x[3][i]); SWAP8(state->x[5][i]); SWAP8(state->x[7][i]); + } + + /*round 7*roundnumber+4: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+4])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+4])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP16(state->x[1][i]); SWAP16(state->x[3][i]); SWAP16(state->x[5][i]); SWAP16(state->x[7][i]); + } + + /*round 7*roundnumber+5: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+5])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+5])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP32(state->x[1][i]); SWAP32(state->x[3][i]); SWAP32(state->x[5][i]); SWAP32(state->x[7][i]); + } + + /*round 7*roundnumber+6: Sbox and MDS layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+6])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+6])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + } + /*round 7*roundnumber+6: swapping layer*/ + for (i = 1; i < 8; i = i+2) { + temp0 = state->x[i][0]; state->x[i][0] = state->x[i][1]; state->x[i][1] = temp0; + } + } + +} + +/*The compression function F8 */ +static void F8(hashState *state) +{ + uint64 i; + + /*xor the 512-bit message with the fist half of the 1024-bit hash state*/ + for (i = 0; i < 8; i++) state->x[i >> 1][i & 1] ^= ((uint64*)state->buffer)[i]; + + /*the bijective function E8 */ + E8(state); + + /*xor the 512-bit message with the second half of the 1024-bit hash state*/ + for (i = 0; i < 8; i++) state->x[(8+i) >> 1][(8+i) & 1] ^= ((uint64*)state->buffer)[i]; +} + +/*before hashing a message, initialize the hash state as H0 */ +static HashReturn Init(hashState *state, int hashbitlen) +{ + state->databitlen = 0; + state->datasize_in_buffer = 0; + + /*initialize the initial hash value of JH*/ + state->hashbitlen = hashbitlen; + + /*load the intital hash value into state*/ + switch (hashbitlen) + { + case 224: memcpy(state->x,JH224_H0,128); break; + case 256: memcpy(state->x,JH256_H0,128); break; + case 384: memcpy(state->x,JH384_H0,128); break; + case 512: memcpy(state->x,JH512_H0,128); break; + } + + return(SUCCESS); +} + + +/*hash each 512-bit message block, except the last partial block*/ +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen) +{ + DataLength index; /*the starting address of the data to be compressed*/ + + state->databitlen += databitlen; + index = 0; + + /*if there is remaining data in the buffer, fill it to a full message block first*/ + /*we assume that the size of the data in the buffer is the multiple of 8 bits if it is not at the end of a message*/ + + /*There is data in the buffer, but the incoming data is insufficient for a full block*/ + if ( (state->datasize_in_buffer > 0 ) && (( state->datasize_in_buffer + databitlen) < 512) ) { + if ( (databitlen & 7) == 0 ) { + memcpy(state->buffer + (state->datasize_in_buffer >> 3), data, (size_t)(64-(state->datasize_in_buffer >> 3))) ; + } + else memcpy(state->buffer + (state->datasize_in_buffer >> 3), data, (size_t)(64-(state->datasize_in_buffer >> 3)+1)) ; + state->datasize_in_buffer += databitlen; + databitlen = 0; + } + + /*There is data in the buffer, and the incoming data is sufficient for a full block*/ + if ( (state->datasize_in_buffer > 0 ) && (( state->datasize_in_buffer + databitlen) >= 512) ) { + memcpy( state->buffer + (state->datasize_in_buffer >> 3), data, (size_t)(64-(state->datasize_in_buffer >> 3)) ) ; + index = 64-(state->datasize_in_buffer >> 3); + databitlen = databitlen - (512 - state->datasize_in_buffer); + F8(state); + state->datasize_in_buffer = 0; + } + + /*hash the remaining full message blocks*/ + for ( ; databitlen >= 512; index = index+64, databitlen = databitlen - 512) { + memcpy(state->buffer, data+index, 64); + F8(state); + } + + /*store the partial block into buffer, assume that -- if part of the last byte is not part of the message, then that part consists of 0 bits*/ + if ( databitlen > 0) { + if ((databitlen & 7) == 0) + memcpy(state->buffer, data+index, (databitlen & 0x1ff) >> 3); + else + memcpy(state->buffer, data+index, ((databitlen & 0x1ff) >> 3)+1); + state->datasize_in_buffer = databitlen; + } + + return(SUCCESS); +} + +/*pad the message, process the padded block(s), truncate the hash value H to obtain the message digest*/ +static HashReturn Final(hashState *state, BitSequence *hashval) +{ + unsigned int i; + + if ( (state->databitlen & 0x1ff) == 0 ) { + /*pad the message when databitlen is multiple of 512 bits, then process the padded block*/ + memset(state->buffer, 0, 64); + state->buffer[0] = 0x80; + state->buffer[63] = state->databitlen & 0xff; + state->buffer[62] = (state->databitlen >> 8) & 0xff; + state->buffer[61] = (state->databitlen >> 16) & 0xff; + state->buffer[60] = (state->databitlen >> 24) & 0xff; + state->buffer[59] = (state->databitlen >> 32) & 0xff; + state->buffer[58] = (state->databitlen >> 40) & 0xff; + state->buffer[57] = (state->databitlen >> 48) & 0xff; + state->buffer[56] = (state->databitlen >> 56) & 0xff; + F8(state); + } + else { + /*set the rest of the bytes in the buffer to 0*/ + if ( (state->datasize_in_buffer & 7) == 0) + for (i = (state->databitlen & 0x1ff) >> 3; i < 64; i++) state->buffer[i] = 0; + else + for (i = ((state->databitlen & 0x1ff) >> 3)+1; i < 64; i++) state->buffer[i] = 0; + + /*pad and process the partial block when databitlen is not multiple of 512 bits, then hash the padded blocks*/ + state->buffer[((state->databitlen & 0x1ff) >> 3)] |= 1 << (7- (state->databitlen & 7)); + + F8(state); + memset(state->buffer, 0, 64); + state->buffer[63] = state->databitlen & 0xff; + state->buffer[62] = (state->databitlen >> 8) & 0xff; + state->buffer[61] = (state->databitlen >> 16) & 0xff; + state->buffer[60] = (state->databitlen >> 24) & 0xff; + state->buffer[59] = (state->databitlen >> 32) & 0xff; + state->buffer[58] = (state->databitlen >> 40) & 0xff; + state->buffer[57] = (state->databitlen >> 48) & 0xff; + state->buffer[56] = (state->databitlen >> 56) & 0xff; + F8(state); + } + + /*truncating the final hash value to generate the message digest*/ + switch(state->hashbitlen) { + case 224: memcpy(hashval,(unsigned char*)state->x+64+36,28); break; + case 256: memcpy(hashval,(unsigned char*)state->x+64+32,32); break; + case 384: memcpy(hashval,(unsigned char*)state->x+64+16,48); break; + case 512: memcpy(hashval,(unsigned char*)state->x+64,64); break; + } + + return(SUCCESS); +} + +/* hash a message, + three inputs: message digest size in bits (hashbitlen); message (data); message length in bits (databitlen) + one output: message digest (hashval) +*/ +HashReturn jh_hash(int hashbitlen, const BitSequence *data,DataLength databitlen, BitSequence *hashval) +{ + hashState state; + + if ( hashbitlen == 224 || hashbitlen == 256 || hashbitlen == 384 || hashbitlen == 512 ) { + Init(&state, hashbitlen); + Update(&state, data, databitlen); + Final(&state, hashval); + return SUCCESS; + } + else + return(BAD_HASHLEN); +} diff --git a/src/crypto/jh/jh.h b/src/crypto/jh/jh.h new file mode 100644 index 00000000..6bda483e --- /dev/null +++ b/src/crypto/jh/jh.h @@ -0,0 +1,29 @@ +/*This program gives the 64-bit optimized bitslice implementation of JH using ANSI C + + -------------------------------- + Performance + + Microprocessor: Intel CORE 2 processor (Core 2 Duo Mobile T6600 2.2GHz) + Operating System: 64-bit Ubuntu 10.04 (Linux kernel 2.6.32-22-generic) + Speed for long message: + 1) 45.8 cycles/byte compiler: Intel C++ Compiler 11.1 compilation option: icc -O2 + 2) 56.8 cycles/byte compiler: gcc 4.4.3 compilation option: gcc -O3 + + -------------------------------- + Last Modified: January 16, 2011 +*/ +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef unsigned char BitSequence; +typedef unsigned long long DataLength; +typedef enum {SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2} HashReturn; + +HashReturn jh_hash(int hashbitlen, const BitSequence *data, DataLength databitlen, BitSequence *hashval); + +#if defined(__cplusplus) +} +#endif diff --git a/src/crypto/keccak/Keccak-readable-and-compact.c b/src/crypto/keccak/Keccak-readable-and-compact.c new file mode 100644 index 00000000..36c38f9f --- /dev/null +++ b/src/crypto/keccak/Keccak-readable-and-compact.c @@ -0,0 +1,336 @@ +/* +Implementation by the Keccak Team, namely, Guido Bertoni, Joan Daemen, +Michael Peeters, Gilles Van Assche and Ronny Van Keer, +hereby denoted as "the implementer". + +For more information, feedback or questions, please refer to our website: +https://keccak.team/ + +To the extent possible under law, the implementer has waived all copyright +and related or neighboring rights to the source code in this file. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +/* +================================================================ +The purpose of this source file is to demonstrate a readable and compact +implementation of all the Keccak instances approved in the FIPS 202 standard, +including the hash functions and the extendable-output functions (XOFs). + +We focused on clarity and on source-code compactness, +rather than on the performance. + +The advantages of this implementation are: + + The source code is compact, after removing the comments, that is. :-) + + There are no tables with arbitrary constants. + + For clarity, the comments link the operations to the specifications using + the same notation as much as possible. + + There is no restriction in cryptographic features. In particular, + the SHAKE128 and SHAKE256 XOFs can produce any output length. + + The code does not use much RAM, as all operations are done in place. + +The drawbacks of this implementation are: + - There is no message queue. The whole message must be ready in a buffer. + - It is not optimized for performance. + +The implementation is even simpler on a little endian platform. Just define the +LITTLE_ENDIAN symbol in that case. + +For a more complete set of implementations, please refer to +the Keccak Code Package at https://github.com/gvanas/KeccakCodePackage + +For more information, please refer to: + * [Keccak Reference] https://keccak.team/files/Keccak-reference-3.0.pdf + * [Keccak Specifications Summary] https://keccak.team/keccak_specs_summary.html + +This file uses UTF-8 encoding, as some comments use Greek letters. +================================================================ +*/ + +// TODO - fix problems with endiannes +#ifdef _WIN32 +#define LITTLE_ENDIAN +#endif + +/** + * Function to compute the Keccak[r, c] sponge function over a given input. + * @param rate The value of the rate r. + * @param capacity The value of the capacity c. + * @param input Pointer to the input message. + * @param inputByteLen The number of input bytes provided in the input message. + * @param delimitedSuffix Bits that will be automatically appended to the end + * of the input message, as in domain separation. + * This is a byte containing from 0 to 7 bits + * These n bits must be in the least significant bit positions + * and must be delimited with a bit 1 at position n + * (counting from 0=LSB to 7=MSB) and followed by bits 0 + * from position n+1 to position 7. + * Some examples: + * - If no bits are to be appended, then @a delimitedSuffix must be 0x01. + * - If the 2-bit sequence 0,1 is to be appended (as for SHA3-*), @a delimitedSuffix must be 0x06. + * - If the 4-bit sequence 1,1,1,1 is to be appended (as for SHAKE*), @a delimitedSuffix must be 0x1F. + * - If the 7-bit sequence 1,1,0,1,0,0,0 is to be absorbed, @a delimitedSuffix must be 0x8B. + * @param output Pointer to the buffer where to store the output. + * @param outputByteLen The number of output bytes desired. + * @pre One must have r+c=1600 and the rate a multiple of 8 bits in this implementation. + */ +void Keccak(unsigned int rate, unsigned int capacity, const unsigned char *input, unsigned long long int inputByteLen, unsigned char delimitedSuffix, unsigned char *output, unsigned long long int outputByteLen); + +/** + * Function to compute SHAKE128 on the input message with any output length. + */ +void FIPS202_SHAKE128(const unsigned char *input, unsigned int inputByteLen, unsigned char *output, int outputByteLen) +{ + Keccak(1344, 256, input, inputByteLen, 0x1F, output, outputByteLen); +} + +/** + * Function to compute SHAKE256 on the input message with any output length. + */ +void FIPS202_SHAKE256(const unsigned char *input, unsigned int inputByteLen, unsigned char *output, int outputByteLen) +{ + Keccak(1088, 512, input, inputByteLen, 0x1F, output, outputByteLen); +} + +/** + * Function to compute SHA3-224 on the input message. The output length is fixed to 28 bytes. + */ +void FIPS202_SHA3_224(const unsigned char *input, unsigned int inputByteLen, unsigned char *output) +{ + Keccak(1152, 448, input, inputByteLen, 0x06, output, 28); +} + +/** + * Function to compute SHA3-256 on the input message. The output length is fixed to 32 bytes. + */ +void FIPS202_SHA3_256(const unsigned char *input, unsigned int inputByteLen, unsigned char *output) +{ + Keccak(1088, 512, input, inputByteLen, 0x06, output, 32); +} + +/** + * Function to compute SHA3-384 on the input message. The output length is fixed to 48 bytes. + */ +void FIPS202_SHA3_384(const unsigned char *input, unsigned int inputByteLen, unsigned char *output) +{ + Keccak(832, 768, input, inputByteLen, 0x06, output, 48); +} + +/** + * Function to compute SHA3-512 on the input message. The output length is fixed to 64 bytes. + */ +void FIPS202_SHA3_512(const unsigned char *input, unsigned int inputByteLen, unsigned char *output) +{ + Keccak(576, 1024, input, inputByteLen, 0x06, output, 64); +} + +/* +================================================================ +Technicalities +================================================================ +*/ + +typedef unsigned char UINT8; +typedef unsigned long long int UINT64; +typedef UINT64 tKeccakLane; + +#ifndef LITTLE_ENDIAN +/** Function to load a 64-bit value using the little-endian (LE) convention. + * On a LE platform, this could be greatly simplified using a cast. + */ +static UINT64 load64(const UINT8 *x) +{ + int i; + UINT64 u=0; + + for(i=7; i>=0; --i) { + u <<= 8; + u |= x[i]; + } + return u; +} + +/** Function to store a 64-bit value using the little-endian (LE) convention. + * On a LE platform, this could be greatly simplified using a cast. + */ +static void store64(UINT8 *x, UINT64 u) +{ + unsigned int i; + + for(i=0; i<8; ++i) { + x[i] = u; + u >>= 8; + } +} + +/** Function to XOR into a 64-bit value using the little-endian (LE) convention. + * On a LE platform, this could be greatly simplified using a cast. + */ +static void xor64(UINT8 *x, UINT64 u) +{ + unsigned int i; + + for(i=0; i<8; ++i) { + x[i] ^= u; + u >>= 8; + } +} +#endif + +/* +================================================================ +A readable and compact implementation of the Keccak-f[1600] permutation. +================================================================ +*/ + +#define ROL64(a, offset) ((((UINT64)a) << offset) ^ (((UINT64)a) >> (64-offset))) +#define i(x, y) ((x)+5*(y)) + +#ifdef LITTLE_ENDIAN + #define readLane(x, y) (((tKeccakLane*)state)[i(x, y)]) + #define writeLane(x, y, lane) (((tKeccakLane*)state)[i(x, y)]) = (lane) + #define XORLane(x, y, lane) (((tKeccakLane*)state)[i(x, y)]) ^= (lane) +#else + #define readLane(x, y) load64((UINT8*)state+sizeof(tKeccakLane)*i(x, y)) + #define writeLane(x, y, lane) store64((UINT8*)state+sizeof(tKeccakLane)*i(x, y), lane) + #define XORLane(x, y, lane) xor64((UINT8*)state+sizeof(tKeccakLane)*i(x, y), lane) +#endif + +/** + * Function that computes the linear feedback shift register (LFSR) used to + * define the round constants (see [Keccak Reference, Section 1.2]). + */ +int LFSR86540(UINT8 *LFSR) +{ + int result = ((*LFSR) & 0x01) != 0; + if (((*LFSR) & 0x80) != 0) + /* Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 */ + (*LFSR) = ((*LFSR) << 1) ^ 0x71; + else + (*LFSR) <<= 1; + return result; +} + +/** + * Function that computes the Keccak-f[1600] permutation on the given state. + */ +void KeccakF1600_StatePermute(void *state) +{ + unsigned int round, x, y, j, t; + UINT8 LFSRstate = 0x01; + + for(round=0; round<24; round++) { + { /* === θ step (see [Keccak Reference, Section 2.3.2]) === */ + tKeccakLane C[5], D; + + /* Compute the parity of the columns */ + for(x=0; x<5; x++) + C[x] = readLane(x, 0) ^ readLane(x, 1) ^ readLane(x, 2) ^ readLane(x, 3) ^ readLane(x, 4); + for(x=0; x<5; x++) { + /* Compute the θ effect for a given column */ + D = C[(x+4)%5] ^ ROL64(C[(x+1)%5], 1); + /* Add the θ effect to the whole column */ + for (y=0; y<5; y++) + XORLane(x, y, D); + } + } + + { /* === ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) === */ + tKeccakLane current, temp; + /* Start at coordinates (1 0) */ + x = 1; y = 0; + current = readLane(x, y); + /* Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 */ + for(t=0; t<24; t++) { + /* Compute the rotation constant r = (t+1)(t+2)/2 */ + unsigned int r = ((t+1)*(t+2)/2)%64; + /* Compute ((0 1)(2 3)) * (x y) */ + unsigned int Y = (2*x+3*y)%5; x = y; y = Y; + /* Swap current and state(x,y), and rotate */ + temp = readLane(x, y); + writeLane(x, y, ROL64(current, r)); + current = temp; + } + } + + { /* === χ step (see [Keccak Reference, Section 2.3.1]) === */ + tKeccakLane temp[5]; + for(y=0; y<5; y++) { + /* Take a copy of the plane */ + for(x=0; x<5; x++) + temp[x] = readLane(x, y); + /* Compute χ on the plane */ + for(x=0; x<5; x++) + writeLane(x, y, temp[x] ^((~temp[(x+1)%5]) & temp[(x+2)%5])); + } + } + + { /* === ι step (see [Keccak Reference, Section 2.3.5]) === */ + for(j=0; j<7; j++) { + unsigned int bitPosition = (1< +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +void Keccak(unsigned int rate, unsigned int capacity, const unsigned char *input, unsigned long long int inputByteLen, unsigned char delimitedSuffix, unsigned char *output, unsigned long long int outputByteLen) +{ + UINT8 state[200]; + unsigned int rateInBytes = rate/8; + unsigned int blockSize = 0; + unsigned int i; + + if (((rate + capacity) != 1600) || ((rate % 8) != 0)) + return; + + /* === Initialize the state === */ + memset(state, 0, sizeof(state)); + + /* === Absorb all the input blocks === */ + while(inputByteLen > 0) { + blockSize = (unsigned int)MIN(inputByteLen, rateInBytes); + for(i=0; i 0) { + blockSize = (unsigned int)MIN(outputByteLen, rateInBytes); + memcpy(output, state, blockSize); + output += blockSize; + outputByteLen -= blockSize; + + if (outputByteLen > 0) + KeccakF1600_StatePermute(state); + } +} diff --git a/src/crypto/keccak/README.md b/src/crypto/keccak/README.md new file mode 100644 index 00000000..da0b7273 --- /dev/null +++ b/src/crypto/keccak/README.md @@ -0,0 +1,13 @@ +Implementation included from https://github.com/XKCP/XKCP + +We've included file Keccak-readable-and-compact.c from Standalone/CompactFIPS202/C folder with minor modifications +We added explicit cast to unsigned to result of MIN function with arguments (unsigned, unsigned long long) hopefully changing no semantic +Because we aim to address all warnings printed by compiler + +We use pre-final variant of Keccak(1088, 512, in, inlen, 1, out, outlen); + +Difference is in the way data is returned from keccak function. + +With our parameters standard Keccak returns up to 136 bytes as is, then performs additional permutation per 136 bytes + +Our Keccak returns up to 200 bytes as is, and cannot return more diff --git a/src/crypto/oaes/aesb.h b/src/crypto/oaes/aesb.h new file mode 100644 index 00000000..35cadc7c --- /dev/null +++ b/src/crypto/oaes/aesb.h @@ -0,0 +1,176 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ +#pragma once +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define TABLE_ALIGN 32 +#define WPOLY 0x011b +#define N_COLS 4 +#define AES_BLOCK_SIZE 16 +#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) + +#if defined(_MSC_VER) +#define aesb_ALIGN __declspec(align(TABLE_ALIGN)) +#elif defined(__GNUC__) +#define aesb_ALIGN __attribute__ ((aligned(16))) +#else +#define aesb_ALIGN +#endif + +#define rf1(r,c) (r) +#define word_in(x,c) (*((uint32_t*)(x)+(c))) +#define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = (v)) + +#define s(x,c) x[c] +#define si(y,x,c) (s(y,c) = word_in(x, c)) +#define so(y,x,c) word_out(y, c, s(x,c)) +#define state_in(y,x) si(y,x,0); si(y,x,1); si(y,x,2); si(y,x,3) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) +#define to_byte(x) ((x) & 0xff) +#define bval(x,n) to_byte((x) >> (8 * (n))) + +#define fwd_var(x,r,c)\ + ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ + : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\ + : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ + : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))) + +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c)) + +#define sb_data(w) {\ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define rc_data(w) {\ + w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\ + w(0x1b), w(0x36) } + +#define bytes2word(b0, b1, b2, b3) (((uint32_t)(b3) << 24) | \ + ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0)) + +#define h0(x) (x) +#define w0(p) bytes2word(p, 0, 0, 0) +#define w1(p) bytes2word(0, p, 0, 0) +#define w2(p) bytes2word(0, 0, p, 0) +#define w3(p) bytes2word(0, 0, 0, p) + +#define u0(p) bytes2word(f2(p), p, p, f3(p)) +#define u1(p) bytes2word(f3(p), f2(p), p, p) +#define u2(p) bytes2word(p, f3(p), f2(p), p) +#define u3(p) bytes2word(p, p, f3(p), f2(p)) + +#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) +#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) +#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) +#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) + +#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY)) +#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY)) +#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) ^ (((x>>5) & 4) * WPOLY)) +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#define t_dec(m,n) t_##m##n +#define t_set(m,n) t_##m##n +#define t_use(m,n) t_##m##n + +#define d_4(t,n,b,e,f,g,h) aesb_ALIGN const t n[4][256] = { b(e), b(f), b(g), b(h) } + +#define four_tables(x,tab,vf,rf,c) \ + (tab[0][bval(vf(x,0,c),rf(0,c))] \ + ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ + ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ + ^ tab[3][bval(vf(x,3,c),rf(3,c))]) + +d_4(uint32_t, t_dec(f,n), sb_data, u0, u1, u2, u3); + +void aesb_single_round(const uint8_t *in, uint8_t *out, uint8_t *expandedKey) +{ + uint32_t b0[4], b1[4]; + const uint32_t *kp = (uint32_t *) expandedKey; + state_in(b0, in); + + round(fwd_rnd, b1, b0, kp); + + state_out(out, b1); +} + +void aesb_pseudo_round(const uint8_t *in, uint8_t *out, uint8_t *expandedKey) +{ + uint32_t b0[4], b1[4]; + const uint32_t *kp = (uint32_t *) expandedKey; + state_in(b0, in); + + round(fwd_rnd, b1, b0, kp); + round(fwd_rnd, b0, b1, kp + 1 * N_COLS); + round(fwd_rnd, b1, b0, kp + 2 * N_COLS); + round(fwd_rnd, b0, b1, kp + 3 * N_COLS); + round(fwd_rnd, b1, b0, kp + 4 * N_COLS); + round(fwd_rnd, b0, b1, kp + 5 * N_COLS); + round(fwd_rnd, b1, b0, kp + 6 * N_COLS); + round(fwd_rnd, b0, b1, kp + 7 * N_COLS); + round(fwd_rnd, b1, b0, kp + 8 * N_COLS); + round(fwd_rnd, b0, b1, kp + 9 * N_COLS); + + state_out(out, b0); +} + + +#if defined(__cplusplus) +} +#endif diff --git a/src/crypto/oaes/oaes_config.h b/src/crypto/oaes/oaes_config.h new file mode 100644 index 00000000..ce3bb2e7 --- /dev/null +++ b/src/crypto/oaes/oaes_config.h @@ -0,0 +1,48 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * --------------------------------------------------------------------------- + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +//#ifndef OAES_HAVE_ISAAC +//#define OAES_HAVE_ISAAC 1 +//#endif // OAES_HAVE_ISAAC + +//#ifndef OAES_DEBUG +//#define OAES_DEBUG 0 +//#endif // OAES_DEBUG + +#ifdef __cplusplus +} +#endif + diff --git a/src/crypto/oaes/oaes_lib.c b/src/crypto/oaes/oaes_lib.c new file mode 100644 index 00000000..1a2c7b4a --- /dev/null +++ b/src/crypto/oaes/oaes_lib.c @@ -0,0 +1,1467 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * --------------------------------------------------------------------------- + */ +//static const char _NR[] = { +// 0x4e,0x61,0x62,0x69,0x6c,0x20,0x53,0x2e,0x20, +// 0x41,0x6c,0x20,0x52,0x61,0x6d,0x6c,0x69,0x00 }; +// Hello to "Nabil S. Al Ramli", nice trick! + +#include +#include +//#include +#ifdef __APPLE__ +#include +#else +#include +#endif +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include "oaes_config.h" +#include "oaes_lib.h" + +#ifdef OAES_HAVE_ISAAC +#include "rand.h" +#endif // OAES_HAVE_ISAAC + +#define OAES_RKEY_LEN 4 +#define OAES_COL_LEN 4 +#define OAES_ROUND_BASE 7 + +// the block is padded +#define OAES_FLAG_PAD 0x01 + +#ifndef min +# define min(a,b) (((a)<(b)) ? (a) : (b)) +#endif /* min */ + +// "OAES<8-bit header version><8-bit type><16-bit options><8-bit flags><56-bit reserved>" +static uint8_t oaes_header[OAES_BLOCK_SIZE] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x4f, 0x41, 0x45, 0x53, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static uint8_t oaes_gf_8[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +static uint8_t oaes_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76 }, + /*1*/ { 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0 }, + /*2*/ { 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15 }, + /*3*/ { 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75 }, + /*4*/ { 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84 }, + /*5*/ { 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf }, + /*6*/ { 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8 }, + /*7*/ { 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2 }, + /*8*/ { 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73 }, + /*9*/ { 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb }, + /*a*/ { 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79 }, + /*b*/ { 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08 }, + /*c*/ { 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a }, + /*d*/ { 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e }, + /*e*/ { 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf }, + /*f*/ { 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }, +}; + +static uint8_t oaes_inv_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb }, + /*1*/ { 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb }, + /*2*/ { 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e }, + /*3*/ { 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 }, + /*4*/ { 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 }, + /*5*/ { 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 }, + /*6*/ { 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 }, + /*7*/ { 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b }, + /*8*/ { 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 }, + /*9*/ { 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e }, + /*a*/ { 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b }, + /*b*/ { 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 }, + /*c*/ { 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f }, + /*d*/ { 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef }, + /*e*/ { 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 }, + /*f*/ { 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }, +}; + +static uint8_t oaes_gf_mul_2[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e }, + /*1*/ { 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e }, + /*2*/ { 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e }, + /*3*/ { 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e }, + /*4*/ { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e }, + /*5*/ { 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe }, + /*6*/ { 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde }, + /*7*/ { 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe }, + /*8*/ { 0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05 }, + /*9*/ { 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25 }, + /*a*/ { 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45 }, + /*b*/ { 0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65 }, + /*c*/ { 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85 }, + /*d*/ { 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5 }, + /*e*/ { 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5 }, + /*f*/ { 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 }, +}; + +static uint8_t oaes_gf_mul_3[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11 }, + /*1*/ { 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21 }, + /*2*/ { 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71 }, + /*3*/ { 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41 }, + /*4*/ { 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1 }, + /*5*/ { 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1 }, + /*6*/ { 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1 }, + /*7*/ { 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81 }, + /*8*/ { 0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a }, + /*9*/ { 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba }, + /*a*/ { 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea }, + /*b*/ { 0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda }, + /*c*/ { 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a }, + /*d*/ { 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a }, + /*e*/ { 0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a }, + /*f*/ { 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a }, +}; + +static uint8_t oaes_gf_mul_9[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77 }, + /*1*/ { 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7 }, + /*2*/ { 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c }, + /*3*/ { 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc }, + /*4*/ { 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01 }, + /*5*/ { 0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91 }, + /*6*/ { 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a }, + /*7*/ { 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa }, + /*8*/ { 0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b }, + /*9*/ { 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b }, + /*a*/ { 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0 }, + /*b*/ { 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30 }, + /*c*/ { 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed }, + /*d*/ { 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d }, + /*e*/ { 0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6 }, + /*f*/ { 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 }, +}; + +static uint8_t oaes_gf_mul_b[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69 }, + /*1*/ { 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9 }, + /*2*/ { 0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12 }, + /*3*/ { 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2 }, + /*4*/ { 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f }, + /*5*/ { 0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f }, + /*6*/ { 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4 }, + /*7*/ { 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54 }, + /*8*/ { 0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e }, + /*9*/ { 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e }, + /*a*/ { 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5 }, + /*b*/ { 0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55 }, + /*c*/ { 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68 }, + /*d*/ { 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8 }, + /*e*/ { 0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13 }, + /*f*/ { 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 }, +}; + +static uint8_t oaes_gf_mul_d[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b }, + /*1*/ { 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b }, + /*2*/ { 0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0 }, + /*3*/ { 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20 }, + /*4*/ { 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26 }, + /*5*/ { 0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6 }, + /*6*/ { 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d }, + /*7*/ { 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d }, + /*8*/ { 0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91 }, + /*9*/ { 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41 }, + /*a*/ { 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a }, + /*b*/ { 0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa }, + /*c*/ { 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc }, + /*d*/ { 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c }, + /*e*/ { 0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47 }, + /*f*/ { 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 }, +}; + +static uint8_t oaes_gf_mul_e[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a }, + /*1*/ { 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba }, + /*2*/ { 0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81 }, + /*3*/ { 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61 }, + /*4*/ { 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7 }, + /*5*/ { 0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17 }, + /*6*/ { 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c }, + /*7*/ { 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc }, + /*8*/ { 0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b }, + /*9*/ { 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb }, + /*a*/ { 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0 }, + /*b*/ { 0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20 }, + /*c*/ { 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6 }, + /*d*/ { 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56 }, + /*e*/ { 0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d }, + /*f*/ { 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d }, +}; + +static OAES_RET oaes_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_inv_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} +/* +static OAES_RET oaes_word_rot_right( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + memcpy( _temp + 1, word, OAES_COL_LEN - 1 ); + _temp[0] = word[OAES_COL_LEN - 1]; + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} +*/ +static OAES_RET oaes_word_rot_left( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + memcpy( _temp, word + 1, OAES_COL_LEN - 1 ); + _temp[OAES_COL_LEN - 1] = word[0]; + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x05]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x0f]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x09]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x03]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x0d]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x07]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x01]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x0b]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x0d]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x07]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x01]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x0b]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x05]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x0f]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x09]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x03]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static uint8_t oaes_gf_mul(uint8_t left, uint8_t right) +{ + size_t _x, _y; + + _x = _y = left; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + + switch( right ) + { + case 0x02: + return oaes_gf_mul_2[_y][_x]; + break; + case 0x03: + return oaes_gf_mul_3[_y][_x]; + break; + case 0x09: + return oaes_gf_mul_9[_y][_x]; + break; + case 0x0b: + return oaes_gf_mul_b[_y][_x]; + break; + case 0x0d: + return oaes_gf_mul_d[_y][_x]; + break; + case 0x0e: + return oaes_gf_mul_e[_y][_x]; + break; + default: + return left; + break; + } +} + +static OAES_RET oaes_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul(word[0], 0x02) ^ oaes_gf_mul( word[1], 0x03 ) ^ + word[2] ^ word[3]; + _temp[1] = word[0] ^ oaes_gf_mul( word[1], 0x02 ) ^ + oaes_gf_mul( word[2], 0x03 ) ^ word[3]; + _temp[2] = word[0] ^ word[1] ^ + oaes_gf_mul( word[2], 0x02 ) ^ oaes_gf_mul( word[3], 0x03 ); + _temp[3] = oaes_gf_mul( word[0], 0x03 ) ^ word[1] ^ + word[2] ^ oaes_gf_mul( word[3], 0x02 ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul( word[0], 0x0e ) ^ oaes_gf_mul( word[1], 0x0b ) ^ + oaes_gf_mul( word[2], 0x0d ) ^ oaes_gf_mul( word[3], 0x09 ); + _temp[1] = oaes_gf_mul( word[0], 0x09 ) ^ oaes_gf_mul( word[1], 0x0e ) ^ + oaes_gf_mul( word[2], 0x0b ) ^ oaes_gf_mul( word[3], 0x0d ); + _temp[2] = oaes_gf_mul( word[0], 0x0d ) ^ oaes_gf_mul( word[1], 0x09 ) ^ + oaes_gf_mul( word[2], 0x0e ) ^ oaes_gf_mul( word[3], 0x0b ); + _temp[3] = oaes_gf_mul( word[0], 0x0b ) ^ oaes_gf_mul( word[1], 0x0d ) ^ + oaes_gf_mul( word[2], 0x09 ) ^ oaes_gf_mul( word[3], 0x0e ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_sprintf( + char * buf, size_t * buf_len, const uint8_t * data, size_t data_len ) +{ + size_t _i, _buf_len_in; + char _temp[4]; + + if( NULL == buf_len ) + return OAES_RET_ARG2; + + _buf_len_in = *buf_len; + *buf_len = data_len * 3 + data_len / OAES_BLOCK_SIZE + 1; + + if( NULL == buf ) + return OAES_RET_SUCCESS; + + if( *buf_len > _buf_len_in ) + return OAES_RET_BUF; + + if( NULL == data ) + return OAES_RET_ARG3; + + strcpy( buf, "" ); + + for( _i = 0; _i < data_len; _i++ ) + { + sprintf( _temp, "%02x ", data[_i] ); + strcat( buf, _temp ); + if( _i && 0 == ( _i + 1 ) % OAES_BLOCK_SIZE ) + strcat( buf, "\n" ); + } + + return OAES_RET_SUCCESS; +} + +#ifdef OAES_HAVE_ISAAC +static void oaes_get_seed( char buf[RANDSIZ + 1] ) +{ + struct timeb timer; + struct tm *gmTimer; + char * _test = NULL; + + ftime (&timer); + gmTimer = gmtime( &timer.time ); + _test = (char *) calloc( sizeof( char ), timer.millitm ); + sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", + gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, + gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.millitm, + _test + timer.millitm, getpid() ); + + if( _test ) + free( _test ); +} +#else +static uint32_t oaes_get_seed(void) +{ +// struct timeb timer; +// struct tm *gmTimer; +// char * _test = NULL; + uint32_t _ret = 0; + +// ftime (&timer); +// gmTimer = gmtime( &timer.time ); +// _test = (char *) calloc( sizeof( char ), timer.millitm ); +// _ret = (uint32_t)(gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + +// gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm + +// (uintptr_t) ( _test + timer.millitm )); + +// if( _test ) +// free( _test ); + + return _ret; +} +#endif // OAES_HAVE_ISAAC + +static OAES_RET oaes_key_destroy( oaes_key ** key ) +{ + if( NULL == *key ) + return OAES_RET_SUCCESS; + + if( (*key)->data ) + { + free( (*key)->data ); + (*key)->data = NULL; + } + + if( (*key)->exp_data ) + { + free( (*key)->exp_data ); + (*key)->exp_data = NULL; + } + + (*key)->data_len = 0; + (*key)->exp_data_len = 0; + (*key)->num_keys = 0; + (*key)->key_base = 0; + free( *key ); + *key = NULL; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_key_expand( OAES_CTX * ctx ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + _ctx->key->key_base = _ctx->key->data_len / OAES_RKEY_LEN; + _ctx->key->num_keys = _ctx->key->key_base + OAES_ROUND_BASE; + + _ctx->key->exp_data_len = _ctx->key->num_keys * OAES_RKEY_LEN * OAES_COL_LEN; + _ctx->key->exp_data = (uint8_t *) + calloc( _ctx->key->exp_data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->exp_data ) + return OAES_RET_MEM; + + // the first _ctx->key->data_len are a direct copy + memcpy( _ctx->key->exp_data, _ctx->key->data, _ctx->key->data_len ); + + // apply ExpandKey algorithm for remainder + for( _i = _ctx->key->key_base; _i < _ctx->key->num_keys * OAES_RKEY_LEN; _i++ ) + { + uint8_t _temp[OAES_COL_LEN]; + + memcpy( _temp, + _ctx->key->exp_data + ( _i - 1 ) * OAES_RKEY_LEN, OAES_COL_LEN ); + + // transform key column + if( 0 == _i % _ctx->key->key_base ) + { + oaes_word_rot_left( _temp ); + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + + _temp[0] = _temp[0] ^ oaes_gf_8[ _i / _ctx->key->key_base - 1 ]; + } + else if( _ctx->key->key_base > 6 && 4 == _i % _ctx->key->key_base ) + { + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + } + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + { + _ctx->key->exp_data[ _i * OAES_RKEY_LEN + _j ] = + _ctx->key->exp_data[ ( _i - _ctx->key->key_base ) * + OAES_RKEY_LEN + _j ] ^ _temp[_j]; + } + } + + return OAES_RET_SUCCESS; +} + +/*static OAES_RET oaes_key_gen( OAES_CTX * ctx, size_t key_size ) +{ + size_t _i; + oaes_key * _key = NULL; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + _key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _key ) + return OAES_RET_MEM; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _key->data_len = key_size; + _key->data = (uint8_t *) calloc( key_size, sizeof( uint8_t )); + + if( NULL == _key->data ) + return OAES_RET_MEM; + + for( _i = 0; _i < key_size; _i++ ) +#ifdef OAES_HAVE_ISAAC + _key->data[_i] = (uint8_t) rand( _ctx->rctx ); +#else + _key->data[_i] = (uint8_t) rand(); +#endif // OAES_HAVE_ISAAC + + _ctx->key = _key; + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_gen_128( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 16 ); +} + +OAES_RET oaes_key_gen_192( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 24 ); +} + +OAES_RET oaes_key_gen_256( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 32 ); +} + +OAES_RET oaes_key_export( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + // data + header + *data_len = _ctx->key->data_len + OAES_BLOCK_SIZE; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + // header + memcpy( data, oaes_header, OAES_BLOCK_SIZE ); + data[5] = 0x01; + data[7] = (uint8_t)_ctx->key->data_len; + memcpy( data + OAES_BLOCK_SIZE, _ctx->key->data, _ctx->key->data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_export_data( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + *data_len = _ctx->key->data_len; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + memcpy( data, _ctx->key->data, *data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_import( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + int _key_length; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16 + OAES_BLOCK_SIZE: + case 24 + OAES_BLOCK_SIZE: + case 32 + OAES_BLOCK_SIZE: + break; + default: + return OAES_RET_ARG3; + } + + // header + if( 0 != memcmp( data, oaes_header, 4 ) ) + return OAES_RET_HEADER; + + // header version + switch( data[4] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // header type + switch( data[5] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // options + _key_length = data[7]; + switch( _key_length ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_HEADER; + } + + if( (int)data_len != _key_length + OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = _key_length; + _ctx->key->data = (uint8_t *) + calloc( _key_length, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data + OAES_BLOCK_SIZE, _key_length ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +}*/ + +OAES_RET oaes_key_import_data( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_ARG3; + } + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = data_len; + _ctx->key->data = (uint8_t *) + calloc( data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data, data_len ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_CTX * oaes_alloc(void) +{ + oaes_ctx * _ctx = (oaes_ctx *) calloc( sizeof( oaes_ctx ), 1 ); + + if( NULL == _ctx ) + return NULL; + +#ifdef OAES_HAVE_ISAAC + { + ub4 _i = 0; + char _seed[RANDSIZ + 1]; + + _ctx->rctx = (randctx *) calloc( sizeof( randctx ), 1 ); + + if( NULL == _ctx->rctx ) + { + free( _ctx ); + return NULL; + } + + oaes_get_seed( _seed ); + memset( _ctx->rctx->randrsl, 0, RANDSIZ ); + memcpy( _ctx->rctx->randrsl, _seed, RANDSIZ ); + randinit( _ctx->rctx, TRUE); + } +#else + srand( oaes_get_seed() ); +#endif // OAES_HAVE_ISAAC + + _ctx->key = NULL; + oaes_set_option( _ctx, OAES_OPTION_CBC, NULL ); + +#ifdef OAES_DEBUG + _ctx->step_cb = NULL; + oaes_set_option( _ctx, OAES_OPTION_STEP_OFF, NULL ); +#endif // OAES_DEBUG + + return (OAES_CTX *) _ctx; +} + +OAES_RET oaes_free( OAES_CTX ** ctx ) +{ + oaes_ctx ** _ctx = (oaes_ctx **) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == *_ctx ) + return OAES_RET_SUCCESS; + + if( (*_ctx)->key ) + oaes_key_destroy( &((*_ctx)->key) ); + +#ifdef OAES_HAVE_ISAAC + if( (*_ctx)->rctx ) + { + free( (*_ctx)->rctx ); + (*_ctx)->rctx = NULL; + } +#endif // OAES_HAVE_ISAAC + + free( *_ctx ); + *_ctx = NULL; + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_set_option( OAES_CTX * ctx, + OAES_OPTION option, const void * value ) +{ + size_t _i; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + switch( option ) + { + case OAES_OPTION_ECB: + _ctx->options &= ~OAES_OPTION_CBC; + memset( _ctx->iv, 0, OAES_BLOCK_SIZE ); + break; + + case OAES_OPTION_CBC: + _ctx->options &= ~OAES_OPTION_ECB; + if( value ) + memcpy( _ctx->iv, value, OAES_BLOCK_SIZE ); + else + { + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) +#ifdef OAES_HAVE_ISAAC + _ctx->iv[_i] = (uint8_t) rand( _ctx->rctx ); +#else + _ctx->iv[_i] = (uint8_t) rand(); +#endif // OAES_HAVE_ISAAC + } + break; + +#ifdef OAES_DEBUG + + case OAES_OPTION_STEP_ON: + if( value ) + { + _ctx->options &= ~OAES_OPTION_STEP_OFF; + _ctx->step_cb = value; + } + else + { + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->options |= OAES_OPTION_STEP_OFF; + _ctx->step_cb = NULL; + return OAES_RET_ARG3; + } + break; + + case OAES_OPTION_STEP_OFF: + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->step_cb = NULL; + break; + +#endif // OAES_DEBUG + + default: + return OAES_RET_ARG2; + } + + _ctx->options |= option; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_encrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "input", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(State, K0) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "k_sch", 1, NULL ); + _ctx->step_cb( c, "k_add", 1, NULL ); + } +#endif // OAES_DEBUG + + // for round = 1 step 1 to Nr-1 + for( _i = 1; _i < _ctx->key->num_keys - 1; _i++ ) + { + // SubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _i, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _i, NULL ); +#endif // OAES_DEBUG + + // MixColumns(state) + oaes_mix_cols( c ); + oaes_mix_cols( c + 4 ); + oaes_mix_cols( c + 8 ); + oaes_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "m_col", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _i, NULL ); + _ctx->step_cb( c, "k_add", _i, NULL ); + } +#endif // OAES_DEBUG + + } + + // SubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "output", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_decrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "iinput", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "ik_add", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + for( _i = _ctx->key->num_keys - 2; _i > 0; _i-- ) + { + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", _i, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_inv_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _i, NULL ); + _ctx->step_cb( c, "ik_add", _i, NULL ); + } +#endif // OAES_DEBUG + + // InvMixColums(state) + oaes_inv_mix_cols( c ); + oaes_inv_mix_cols( c + 4 ); + oaes_inv_mix_cols( c + 8 ); + oaes_inv_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "im_col", _i, NULL ); +#endif // OAES_DEBUG + + } + + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", 1, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_inv_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[0, Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "ik_sch", 1, NULL ); + _ctx->step_cb( c, "ioutput", 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_encrypt( OAES_CTX * ctx, + const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len ) +{ + size_t _i, _j, _c_len_in, _c_data_len; + size_t _pad_len = m_len % OAES_BLOCK_SIZE == 0 ? + 0 : OAES_BLOCK_SIZE - m_len % OAES_BLOCK_SIZE; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _flags = _pad_len ? OAES_FLAG_PAD : 0; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == m ) + return OAES_RET_ARG2; + + if( NULL == c_len ) + return OAES_RET_ARG5; + + _c_len_in = *c_len; + // data + pad + _c_data_len = m_len + _pad_len; + // header + iv + data + pad + *c_len = 2 * OAES_BLOCK_SIZE + m_len + _pad_len; + + if( NULL == c ) + return OAES_RET_SUCCESS; + + if( _c_len_in < *c_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // header + memcpy(c, oaes_header, OAES_BLOCK_SIZE ); + memcpy(c + 6, &_ctx->options, sizeof(_ctx->options)); + memcpy(c + 8, &_flags, sizeof(_flags)); + // iv + memcpy(c + OAES_BLOCK_SIZE, _ctx->iv, OAES_BLOCK_SIZE ); + // data + memcpy(c + 2 * OAES_BLOCK_SIZE, m, m_len ); + + for( _i = 0; _i < _c_data_len; _i += OAES_BLOCK_SIZE ) + { + uint8_t _block[OAES_BLOCK_SIZE]; + size_t _block_size = min( m_len - _i, OAES_BLOCK_SIZE ); + + memcpy( _block, c + 2 * OAES_BLOCK_SIZE + _i, _block_size ); + + // insert pad + for( _j = 0; _j < OAES_BLOCK_SIZE - _block_size; _j++ ) + _block[ _block_size + _j ] = (uint8_t)(_j + 1); + + // CBC + if( _ctx->options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + _block[_j] = _block[_j] ^ _ctx->iv[_j]; + } + + _rc = _rc || + oaes_encrypt_block( ctx, _block, OAES_BLOCK_SIZE ); + memcpy( c + 2 * OAES_BLOCK_SIZE + _i, _block, OAES_BLOCK_SIZE ); + + if( _ctx->options & OAES_OPTION_CBC ) + memcpy( _ctx->iv, _block, OAES_BLOCK_SIZE ); + } + + return _rc; +} + +OAES_RET oaes_decrypt( OAES_CTX * ctx, + const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len ) +{ + size_t _i, _j, _m_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _iv[OAES_BLOCK_SIZE]; + uint8_t _flags; + OAES_OPTION _options; + + if( NULL == ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len % OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == m_len ) + return OAES_RET_ARG5; + + _m_len_in = *m_len; + *m_len = c_len - 2 * OAES_BLOCK_SIZE; + + if( NULL == m ) + return OAES_RET_SUCCESS; + + if( _m_len_in < *m_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // header + if( 0 != memcmp( c, oaes_header, 4 ) ) + return OAES_RET_HEADER; + + // header version + switch( c[4] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // header type + switch( c[5] ) + { + case 0x02: + break; + default: + return OAES_RET_HEADER; + } + + // options + memcpy(&_options, c + 6, sizeof(_options)); + // validate that all options are valid + if( _options & ~( + OAES_OPTION_ECB + | OAES_OPTION_CBC +#ifdef OAES_DEBUG + | OAES_OPTION_STEP_ON + | OAES_OPTION_STEP_OFF +#endif // OAES_DEBUG + ) ) + return OAES_RET_HEADER; + if( ( _options & OAES_OPTION_ECB ) && + ( _options & OAES_OPTION_CBC ) ) + return OAES_RET_HEADER; + if( _options == OAES_OPTION_NONE ) + return OAES_RET_HEADER; + + // flags + memcpy(&_flags, c + 8, sizeof(_flags)); + // validate that all flags are valid + if( _flags & ~( + OAES_FLAG_PAD + ) ) + return OAES_RET_HEADER; + + // iv + memcpy( _iv, c + OAES_BLOCK_SIZE, OAES_BLOCK_SIZE); + // data + pad + memcpy( m, c + 2 * OAES_BLOCK_SIZE, *m_len ); + + for( _i = 0; _i < *m_len; _i += OAES_BLOCK_SIZE ) + { + if( ( _options & OAES_OPTION_CBC ) && _i > 0 ) + memcpy( _iv, c + OAES_BLOCK_SIZE + _i, OAES_BLOCK_SIZE ); + + _rc = _rc || + oaes_decrypt_block( ctx, m + _i, min( *m_len - _i, OAES_BLOCK_SIZE ) ); + + // CBC + if( _options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + m[ _i + _j ] = m[ _i + _j ] ^ _iv[_j]; + } + } + + // remove pad + if( _flags & OAES_FLAG_PAD ) + { + int _is_pad = 1; + size_t _temp = (size_t) m[*m_len - 1]; + + if( _temp <= 0x00 || _temp > 0x0f ) + return OAES_RET_HEADER; + for( _i = 0; _i < _temp; _i++ ) + if( m[*m_len - 1 - _i] != _temp - _i ) + _is_pad = 0; + if( _is_pad ) + { + memset( m + *m_len - _temp, 0, _temp ); + *m_len -= _temp; + } + else + return OAES_RET_HEADER; + } + + return OAES_RET_SUCCESS; +} + + +OAES_API OAES_RET oaes_encryption_round( const uint8_t * key, uint8_t * c ) +{ + size_t _i; + + if( NULL == key ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + // SubBytes(state) + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + oaes_sub_byte( c + _i ); + + // ShiftRows(state) + oaes_shift_rows( c ); + + // MixColumns(state) + oaes_mix_cols( c ); + oaes_mix_cols( c + 4 ); + oaes_mix_cols( c + 8 ); + oaes_mix_cols( c + 12 ); + + // AddRoundKey(State, key) + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + c[_i] ^= key[_i]; + + return OAES_RET_SUCCESS; +} + +OAES_API OAES_RET oaes_pseudo_encrypt_ecb( OAES_CTX * ctx, uint8_t * c ) +{ + size_t _i; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + for ( _i = 0; _i < 10; ++_i ) + { + oaes_encryption_round( &_ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN], c ); + } + + return OAES_RET_SUCCESS; +} diff --git a/src/crypto/oaes/oaes_lib.h b/src/crypto/oaes/oaes_lib.h new file mode 100644 index 00000000..e10910f7 --- /dev/null +++ b/src/crypto/oaes/oaes_lib.h @@ -0,0 +1,213 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * --------------------------------------------------------------------------- + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +# ifdef OAES_SHARED +# ifdef oaes_lib_EXPORTS +# define OAES_API __declspec(dllexport) +# else +# define OAES_API __declspec(dllimport) +# endif +# else +# define OAES_API +# endif +#else +# define OAES_API +#endif // _WIN32 + +#define OAES_VERSION "0.8.1" +#define OAES_BLOCK_SIZE 16 + +typedef void OAES_CTX; + +typedef enum +{ + OAES_RET_FIRST = 0, + OAES_RET_SUCCESS = 0, + OAES_RET_UNKNOWN, + OAES_RET_ARG1, + OAES_RET_ARG2, + OAES_RET_ARG3, + OAES_RET_ARG4, + OAES_RET_ARG5, + OAES_RET_NOKEY, + OAES_RET_MEM, + OAES_RET_BUF, + OAES_RET_HEADER, + OAES_RET_COUNT +} OAES_RET; + +/* + * oaes_set_option() takes one of these values for its [option] parameter + * some options accept either an optional or a required [value] parameter + */ +// no option +#define OAES_OPTION_NONE 0 +// enable ECB mode, disable CBC mode +#define OAES_OPTION_ECB 1 +// enable CBC mode, disable ECB mode +// value is optional, may pass uint8_t iv[OAES_BLOCK_SIZE] to specify +// the value of the initialization vector, iv +#define OAES_OPTION_CBC 2 + +#ifdef OAES_DEBUG +typedef int ( * oaes_step_cb ) ( + const uint8_t state[OAES_BLOCK_SIZE], + const char * step_name, + int step_count, + void * user_data ); +// enable state stepping mode +// value is required, must pass oaes_step_cb to receive the state at each step +#define OAES_OPTION_STEP_ON 4 +// disable state stepping mode +#define OAES_OPTION_STEP_OFF 8 +#endif // OAES_DEBUG + +typedef uint16_t OAES_OPTION; + +typedef struct _oaes_key +{ + size_t data_len; + uint8_t *data; + size_t exp_data_len; + uint8_t *exp_data; + size_t num_keys; + size_t key_base; +} oaes_key; + +typedef struct _oaes_ctx +{ +#ifdef OAES_HAVE_ISAAC + randctx * rctx; +#endif // OAES_HAVE_ISAAC + +#ifdef OAES_DEBUG + oaes_step_cb step_cb; +#endif // OAES_DEBUG + + oaes_key * key; + OAES_OPTION options; + uint8_t iv[OAES_BLOCK_SIZE]; +} oaes_ctx; +/* + * // usage: + * + * OAES_CTX * ctx = oaes_alloc(); + * . + * . + * . + * { + * oaes_gen_key_xxx( ctx ); + * { + * oaes_key_export( ctx, _buf, &_buf_len ); + * // or + * oaes_key_export_data( ctx, _buf, &_buf_len );\ + * } + * } + * // or + * { + * oaes_key_import( ctx, _buf, _buf_len ); + * // or + * oaes_key_import_data( ctx, _buf, _buf_len ); + * } + * . + * . + * . + * oaes_encrypt( ctx, m, m_len, c, &c_len ); + * . + * . + * . + * oaes_decrypt( ctx, c, c_len, m, &m_len ); + * . + * . + * . + * oaes_free( &ctx ); + */ + +OAES_API OAES_CTX * oaes_alloc(void); + +OAES_API OAES_RET oaes_free( OAES_CTX ** ctx ); + +OAES_API OAES_RET oaes_set_option( OAES_CTX * ctx, + OAES_OPTION option, const void * value ); + +OAES_API OAES_RET oaes_key_gen_128( OAES_CTX * ctx ); + +OAES_API OAES_RET oaes_key_gen_192( OAES_CTX * ctx ); + +OAES_API OAES_RET oaes_key_gen_256( OAES_CTX * ctx ); + +// export key with header information +// set data == NULL to get the required data_len +OAES_API OAES_RET oaes_key_export( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ); + +// directly export the data from key +// set data == NULL to get the required data_len +OAES_API OAES_RET oaes_key_export_data( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ); + +// import key with header information +OAES_API OAES_RET oaes_key_import( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ); + +// directly import data into key +OAES_API OAES_RET oaes_key_import_data( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ); + +// set c == NULL to get the required c_len +OAES_API OAES_RET oaes_encrypt( OAES_CTX * ctx, + const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len ); + +// set m == NULL to get the required m_len +OAES_API OAES_RET oaes_decrypt( OAES_CTX * ctx, + const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len ); + +// set buf == NULL to get the required buf_len +OAES_API OAES_RET oaes_sprintf( + char * buf, size_t * buf_len, const uint8_t * data, size_t data_len ); + +OAES_API OAES_RET oaes_encryption_round( const uint8_t * key, uint8_t * c ); + +OAES_API OAES_RET oaes_pseudo_encrypt_ecb( OAES_CTX * ctx, uint8_t * c ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/crypto/random.c b/src/crypto/random.c index e638584c..475e8c68 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -19,21 +19,22 @@ #include #include -#include "hash-impl.h" +#include "hash.h" #include "initializer.h" #include "random.h" +static inline void *padd(void *p, size_t i) { return (char *)p + i; } + #if defined(_WIN32) - #include - #include +#include +#include static void generate_system_random_bytes(size_t n, void *result) { HCRYPTPROV prov = 0; - if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT) || - !CryptGenRandom(prov, (DWORD)n, result) || - !CryptReleaseContext(prov, 0)){ + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT) || + !CryptGenRandom(prov, (DWORD)n, result) || !CryptReleaseContext(prov, 0)) { assert(0); -// std::terminate(); // TODO - exit with message on Windows + // std::terminate(); // TODO - exit with message on Windows } } @@ -52,7 +53,7 @@ static void generate_system_random_bytes(size_t n, void *result) { } for (;;) { ssize_t res = read(fd, result, n); - if ((size_t) res == n) { + if ((size_t)res == n) { break; } if (res < 0) { @@ -62,8 +63,8 @@ static void generate_system_random_bytes(size_t n, void *result) { } else if (res == 0) { err(EXIT_FAILURE, "read /dev/urandom: end of file"); } else { - result = padd(result, (size_t) res); - n -= (size_t) res; + result = padd(result, (size_t)res); + n -= (size_t)res; } } if (close(fd) < 0) { @@ -73,19 +74,19 @@ static void generate_system_random_bytes(size_t n, void *result) { #endif -static union hash_state state; -static bool initialized = false; +static struct keccak_state state; +static int initialized = 0; -void initialize_random(void){ +void initialize_random(void) { generate_system_random_bytes(32, &state); - initialized = true; + initialized = 1; } void unsafe_generate_random_bytes(size_t n, void *result) { - if( !initialized) + if (!initialized) initialize_random(); for (;;) { - hash_permutation(&state); + keccak_permutation(&state); if (n <= HASH_DATA_AREA) { memcpy(result, &state, n); return; @@ -97,12 +98,10 @@ void unsafe_generate_random_bytes(size_t n, void *result) { } void initialize_random_for_tests(void) { - memset(&state, 42, sizeof(union hash_state)); - initialized = true; + memset(&state, 42, sizeof(struct keccak_state)); + initialized = 1; } // We keep initialize@start, because generate_system_random_bytes will exit on errror -// If INITIALIZER fails to compile on your platform, just comment out 3 lines below -INITIALIZER(init_random) { - initialize_random(); -} +// If INITIALIZER fails to compile on your platform, just comment out INITIALIZER below +INITIALIZER(init_random) { initialize_random(); } diff --git a/src/crypto/random.h b/src/crypto/random.h index e072eab9..de8cdc81 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -3,18 +3,18 @@ #pragma once -#if !defined(__cplusplus) #include -#endif #if defined(__cplusplus) -namespace crypto { extern "C" { +namespace crypto { +extern "C" { #endif -void unsafe_generate_random_bytes(size_t n, void *result); // Not thread-safe +void unsafe_generate_random_bytes(size_t n, void *result); // Not thread-safe void initialize_random(void); void initialize_random_for_tests(void); #if defined(__cplusplus) -}} +} +} #endif diff --git a/src/crypto/skein/skein.c b/src/crypto/skein/skein.c new file mode 100644 index 00000000..cd11b102 --- /dev/null +++ b/src/crypto/skein/skein.c @@ -0,0 +1,2045 @@ +/*********************************************************************** +** +** Implementation of the Skein hash function. +** +** Source code author: Doug Whiting, 2008. +** +** This algorithm and source code is released to the public domain. +** +************************************************************************/ + +#define SKEIN_PORT_CODE /* instantiate any code in skein_port.h */ + +#include /* get size_t definition */ +#include /* get the memcpy/memset functions */ +#include "skein.h" /* get the Skein API definitions */ + +#define DISABLE_UNUSED 0 + +#ifndef SKEIN_256_NIST_MAX_HASHBITS +#define SKEIN_256_NIST_MAX_HASHBITS (0) +#endif + +#ifndef SKEIN_512_NIST_MAX_HASHBITS +#define SKEIN_512_NIST_MAX_HASHBITS (512) +#endif + +#define SKEIN_MODIFIER_WORDS ( 2) /* number of modifier (tweak) words */ + +#define SKEIN_256_STATE_WORDS ( 4) +#define SKEIN_512_STATE_WORDS ( 8) +#define SKEIN1024_STATE_WORDS (16) +#define SKEIN_MAX_STATE_WORDS (16) + +#define SKEIN_256_STATE_BYTES ( 8*SKEIN_256_STATE_WORDS) +#define SKEIN_512_STATE_BYTES ( 8*SKEIN_512_STATE_WORDS) +#define SKEIN1024_STATE_BYTES ( 8*SKEIN1024_STATE_WORDS) + +#define SKEIN_256_STATE_BITS (64*SKEIN_256_STATE_WORDS) +#define SKEIN_512_STATE_BITS (64*SKEIN_512_STATE_WORDS) +#define SKEIN1024_STATE_BITS (64*SKEIN1024_STATE_WORDS) + +#define SKEIN_256_BLOCK_BYTES ( 8*SKEIN_256_STATE_WORDS) +#define SKEIN_512_BLOCK_BYTES ( 8*SKEIN_512_STATE_WORDS) +#define SKEIN1024_BLOCK_BYTES ( 8*SKEIN1024_STATE_WORDS) + +#define SKEIN_RND_SPECIAL (1000u) +#define SKEIN_RND_KEY_INITIAL (SKEIN_RND_SPECIAL+0u) +#define SKEIN_RND_KEY_INJECT (SKEIN_RND_SPECIAL+1u) +#define SKEIN_RND_FEED_FWD (SKEIN_RND_SPECIAL+2u) + +typedef struct +{ + size_t hashBitLen; /* size of hash result, in bits */ + size_t bCnt; /* current byte count in buffer b[] */ + u64b_t T[SKEIN_MODIFIER_WORDS]; /* tweak words: T[0]=byte cnt, T[1]=flags */ +} Skein_Ctxt_Hdr_t; + +typedef struct /* 256-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN_256_STATE_WORDS]; /* chaining variables */ + union { + u08b_t b[SKEIN_256_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ + u64b_t balias[SKEIN_256_STATE_WORDS]; /* partial block buffer (8-byte aligned) */ + }; +} Skein_256_Ctxt_t; + +typedef struct /* 512-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN_512_STATE_WORDS]; /* chaining variables */ + union { + u08b_t b[SKEIN_512_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ + u64b_t balias[SKEIN_512_STATE_WORDS]; + }; +} Skein_512_Ctxt_t; + +typedef struct /* 1024-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN1024_STATE_WORDS]; /* chaining variables */ + union { + u08b_t b[SKEIN1024_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ + u64b_t balias[SKEIN1024_STATE_WORDS]; /* partial block buffer (8-byte aligned) */ + }; +} Skein1024_Ctxt_t; + +/* Skein APIs for (incremental) "straight hashing" */ +#if SKEIN_256_NIST_MAX_HASHBITS +static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen); +#endif +static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); +static int Skein1024_Init (Skein1024_Ctxt_t *ctx, size_t hashBitLen); + +static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); +static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); + +static int Skein_256_Final (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Final (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Final (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); + +/* +** Skein APIs for "extended" initialization: MAC keys, tree hashing. +** After an InitExt() call, just use Update/Final calls as with Init(). +** +** Notes: Same parameters as _Init() calls, plus treeInfo/key/keyBytes. +** When keyBytes == 0 and treeInfo == SKEIN_SEQUENTIAL, +** the results of InitExt() are identical to calling Init(). +** The function Init() may be called once to "precompute" the IV for +** a given hashBitLen value, then by saving a copy of the context +** the IV computation may be avoided in later calls. +** Similarly, the function InitExt() may be called once per MAC key +** to precompute the MAC IV, then a copy of the context saved and +** reused for each new MAC computation. +**/ +#if 0 +static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +#endif + +/* +** Skein APIs for MAC and tree hash: +** Final_Pad: pad, do final block, but no OUTPUT type +** Output: do just the output stage +*/ +#if 0 +static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t * hashVal); +#endif + +#ifndef SKEIN_TREE_HASH +#define SKEIN_TREE_HASH (1) +#endif +#if 0 +#if SKEIN_TREE_HASH +static int Skein_256_Output (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Output (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Output (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); +#endif +#endif + +/***************************************************************** +** "Internal" Skein definitions +** -- not needed for sequential hashing API, but will be +** helpful for other uses of Skein (e.g., tree hash mode). +** -- included here so that they can be shared between +** reference and optimized code. +******************************************************************/ + +/* tweak word T[1]: bit field starting positions */ +#define SKEIN_T1_BIT(BIT) ((BIT) - 64) /* offset 64 because it's the second word */ + +#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112) /* bits 112..118: level in hash tree */ +#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119) /* bit 119 : partial final input byte */ +#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120) /* bits 120..125: type field */ +#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126) /* bits 126 : first block flag */ +#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127) /* bit 127 : final block flag */ + +/* tweak word T[1]: flag bit definition(s) */ +#define SKEIN_T1_FLAG_FIRST (((u64b_t) 1 ) << SKEIN_T1_POS_FIRST) +#define SKEIN_T1_FLAG_FINAL (((u64b_t) 1 ) << SKEIN_T1_POS_FINAL) +#define SKEIN_T1_FLAG_BIT_PAD (((u64b_t) 1 ) << SKEIN_T1_POS_BIT_PAD) + +/* tweak word T[1]: tree level bit field mask */ +#define SKEIN_T1_TREE_LVL_MASK (((u64b_t)0x7F) << SKEIN_T1_POS_TREE_LVL) +#define SKEIN_T1_TREE_LEVEL(n) (((u64b_t) (n)) << SKEIN_T1_POS_TREE_LVL) + +/* tweak word T[1]: block type field */ +#define SKEIN_BLK_TYPE_KEY ( 0) /* key, for MAC and KDF */ +#define SKEIN_BLK_TYPE_CFG ( 4) /* configuration block */ +#define SKEIN_BLK_TYPE_PERS ( 8) /* personalization string */ +#define SKEIN_BLK_TYPE_PK (12) /* public key (for digital signature hashing) */ +#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */ +#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */ +#define SKEIN_BLK_TYPE_MSG (48) /* message processing */ +#define SKEIN_BLK_TYPE_OUT (63) /* output stage */ +#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */ + +#define SKEIN_T1_BLK_TYPE(T) (((u64b_t) (SKEIN_BLK_TYPE_##T)) << SKEIN_T1_POS_BLK_TYPE) +#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY) /* key, for MAC and KDF */ +#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG) /* configuration block */ +#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS) /* personalization string */ +#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK) /* public key (for digital signature hashing) */ +#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF) /* key identifier for KDF */ +#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)/* nonce for PRNG */ +#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG) /* message processing */ +#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT) /* output stage */ +#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK) /* field bit mask */ + +#define SKEIN_T1_BLK_TYPE_CFG_FINAL (SKEIN_T1_BLK_TYPE_CFG | SKEIN_T1_FLAG_FINAL) +#define SKEIN_T1_BLK_TYPE_OUT_FINAL (SKEIN_T1_BLK_TYPE_OUT | SKEIN_T1_FLAG_FINAL) + +#define SKEIN_VERSION (1) + +#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */ +#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian)*/ +#endif + +#define SKEIN_MK_64(hi32,lo32) ((lo32) + (((u64b_t) (hi32)) << 32)) +#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION,SKEIN_ID_STRING_LE) +#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA,0xA9FC1A22) + +#define SKEIN_CFG_STR_LEN (4*8) + +/* bit field definitions in config block treeInfo word */ +#define SKEIN_CFG_TREE_LEAF_SIZE_POS ( 0) +#define SKEIN_CFG_TREE_NODE_SIZE_POS ( 8) +#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16) + +#define SKEIN_CFG_TREE_LEAF_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_LEAF_SIZE_POS) +#define SKEIN_CFG_TREE_NODE_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_NODE_SIZE_POS) +#define SKEIN_CFG_TREE_MAX_LEVEL_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_MAX_LEVEL_POS) + +#define SKEIN_CFG_TREE_INFO(leaf,node,maxLvl) \ + ( (((u64b_t)(leaf )) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \ + (((u64b_t)(node )) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \ + (((u64b_t)(maxLvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS) ) + +#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0,0,0) /* use as treeInfo in InitExt() call for sequential processing */ + +/* +** Skein macros for getting/setting tweak words, etc. +** These are useful for partial input bytes, hash tree init/update, etc. +**/ +#define Skein_Get_Tweak(ctxPtr,TWK_NUM) ((ctxPtr)->h.T[TWK_NUM]) +#define Skein_Set_Tweak(ctxPtr,TWK_NUM,tVal) {(ctxPtr)->h.T[TWK_NUM] = (tVal);} + +#define Skein_Get_T0(ctxPtr) Skein_Get_Tweak(ctxPtr,0) +#define Skein_Get_T1(ctxPtr) Skein_Get_Tweak(ctxPtr,1) +#define Skein_Set_T0(ctxPtr,T0) Skein_Set_Tweak(ctxPtr,0,T0) +#define Skein_Set_T1(ctxPtr,T1) Skein_Set_Tweak(ctxPtr,1,T1) + +/* set both tweak words at once */ +#define Skein_Set_T0_T1(ctxPtr,T0,T1) \ +{ \ + Skein_Set_T0(ctxPtr,(T0)); \ + Skein_Set_T1(ctxPtr,(T1)); \ +} + +#define Skein_Set_Type(ctxPtr,BLK_TYPE) \ + Skein_Set_T1(ctxPtr,SKEIN_T1_BLK_TYPE_##BLK_TYPE) + +/* set up for starting with a new type: h.T[0]=0; h.T[1] = NEW_TYPE; h.bCnt=0; */ +#define Skein_Start_New_Type(ctxPtr,BLK_TYPE) \ +{ Skein_Set_T0_T1(ctxPtr,0,SKEIN_T1_FLAG_FIRST | SKEIN_T1_BLK_TYPE_##BLK_TYPE); (ctxPtr)->h.bCnt=0; } + +#define Skein_Clear_First_Flag(hdr) { (hdr).T[1] &= ~SKEIN_T1_FLAG_FIRST; } +#define Skein_Set_Bit_Pad_Flag(hdr) { (hdr).T[1] |= SKEIN_T1_FLAG_BIT_PAD; } + +#define Skein_Set_Tree_Level(hdr,height) { (hdr).T[1] |= SKEIN_T1_TREE_LEVEL(height);} + +/***************************************************************** +** "Internal" Skein definitions for debugging and error checking +******************************************************************/ +#define Skein_Show_Block(bits,ctx,X,blkPtr,wPtr,ksEvenPtr,ksOddPtr) +#define Skein_Show_Round(bits,ctx,r,X) +#define Skein_Show_R_Ptr(bits,ctx,r,X_ptr) +#define Skein_Show_Final(bits,ctx,cnt,outPtr) +#define Skein_Show_Key(bits,ctx,key,keyBytes) + + +#ifndef SKEIN_ERR_CHECK /* run-time checks (e.g., bad params, uninitialized context)? */ +#define Skein_Assert(x,retCode)/* default: ignore all Asserts, for performance */ +#define Skein_assert(x) +#elif defined(SKEIN_ASSERT) +#include +#define Skein_Assert(x,retCode) assert(x) +#define Skein_assert(x) assert(x) +#else +#include +#define Skein_Assert(x,retCode) { if (!(x)) return retCode; } /* caller error */ +#define Skein_assert(x) assert(x) /* internal error */ +#endif + +/***************************************************************** +** Skein block function constants (shared across Ref and Opt code) +******************************************************************/ +enum +{ + /* Skein_256 round rotation constants */ + R_256_0_0=14, R_256_0_1=16, + R_256_1_0=52, R_256_1_1=57, + R_256_2_0=23, R_256_2_1=40, + R_256_3_0= 5, R_256_3_1=37, + R_256_4_0=25, R_256_4_1=33, + R_256_5_0=46, R_256_5_1=12, + R_256_6_0=58, R_256_6_1=22, + R_256_7_0=32, R_256_7_1=32, + + /* Skein_512 round rotation constants */ + R_512_0_0=46, R_512_0_1=36, R_512_0_2=19, R_512_0_3=37, + R_512_1_0=33, R_512_1_1=27, R_512_1_2=14, R_512_1_3=42, + R_512_2_0=17, R_512_2_1=49, R_512_2_2=36, R_512_2_3=39, + R_512_3_0=44, R_512_3_1= 9, R_512_3_2=54, R_512_3_3=56, + R_512_4_0=39, R_512_4_1=30, R_512_4_2=34, R_512_4_3=24, + R_512_5_0=13, R_512_5_1=50, R_512_5_2=10, R_512_5_3=17, + R_512_6_0=25, R_512_6_1=29, R_512_6_2=39, R_512_6_3=43, + R_512_7_0= 8, R_512_7_1=35, R_512_7_2=56, R_512_7_3=22, + + /* Skein1024 round rotation constants */ + R1024_0_0=24, R1024_0_1=13, R1024_0_2= 8, R1024_0_3=47, R1024_0_4= 8, R1024_0_5=17, R1024_0_6=22, R1024_0_7=37, + R1024_1_0=38, R1024_1_1=19, R1024_1_2=10, R1024_1_3=55, R1024_1_4=49, R1024_1_5=18, R1024_1_6=23, R1024_1_7=52, + R1024_2_0=33, R1024_2_1= 4, R1024_2_2=51, R1024_2_3=13, R1024_2_4=34, R1024_2_5=41, R1024_2_6=59, R1024_2_7=17, + R1024_3_0= 5, R1024_3_1=20, R1024_3_2=48, R1024_3_3=41, R1024_3_4=47, R1024_3_5=28, R1024_3_6=16, R1024_3_7=25, + R1024_4_0=41, R1024_4_1= 9, R1024_4_2=37, R1024_4_3=31, R1024_4_4=12, R1024_4_5=47, R1024_4_6=44, R1024_4_7=30, + R1024_5_0=16, R1024_5_1=34, R1024_5_2=56, R1024_5_3=51, R1024_5_4= 4, R1024_5_5=53, R1024_5_6=42, R1024_5_7=41, + R1024_6_0=31, R1024_6_1=44, R1024_6_2=47, R1024_6_3=46, R1024_6_4=19, R1024_6_5=42, R1024_6_6=44, R1024_6_7=25, + R1024_7_0= 9, R1024_7_1=48, R1024_7_2=35, R1024_7_3=52, R1024_7_4=23, R1024_7_5=31, R1024_7_6=37, R1024_7_7=20 +}; + +#ifndef SKEIN_ROUNDS +#define SKEIN_256_ROUNDS_TOTAL (72) /* number of rounds for the different block sizes */ +#define SKEIN_512_ROUNDS_TOTAL (72) +#define SKEIN1024_ROUNDS_TOTAL (80) +#else /* allow command-line define in range 8*(5..14) */ +#define SKEIN_256_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/100) + 5) % 10) + 5)) +#define SKEIN_512_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/ 10) + 5) % 10) + 5)) +#define SKEIN1024_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS ) + 5) % 10) + 5)) +#endif + + +/* +***************** Pre-computed Skein IVs ******************* +** +** NOTE: these values are not "magic" constants, but +** are generated using the Threefish block function. +** They are pre-computed here only for speed; i.e., to +** avoid the need for a Threefish call during Init(). +** +** The IV for any fixed hash length may be pre-computed. +** Only the most common values are included here. +** +************************************************************ +**/ + +#define MK_64 SKEIN_MK_64 + +/* blkSize = 256 bits. hashSize = 128 bits */ +const u64b_t SKEIN_256_IV_128[] = + { + MK_64(0xE1111906,0x964D7260), + MK_64(0x883DAAA7,0x7C8D811C), + MK_64(0x10080DF4,0x91960F7A), + MK_64(0xCCF7DDE5,0xB45BC1C2) + }; + +/* blkSize = 256 bits. hashSize = 160 bits */ +const u64b_t SKEIN_256_IV_160[] = + { + MK_64(0x14202314,0x72825E98), + MK_64(0x2AC4E9A2,0x5A77E590), + MK_64(0xD47A5856,0x8838D63E), + MK_64(0x2DD2E496,0x8586AB7D) + }; + +/* blkSize = 256 bits. hashSize = 224 bits */ +const u64b_t SKEIN_256_IV_224[] = + { + MK_64(0xC6098A8C,0x9AE5EA0B), + MK_64(0x876D5686,0x08C5191C), + MK_64(0x99CB88D7,0xD7F53884), + MK_64(0x384BDDB1,0xAEDDB5DE) + }; + +/* blkSize = 256 bits. hashSize = 256 bits */ +const u64b_t SKEIN_256_IV_256[] = + { + MK_64(0xFC9DA860,0xD048B449), + MK_64(0x2FCA6647,0x9FA7D833), + MK_64(0xB33BC389,0x6656840F), + MK_64(0x6A54E920,0xFDE8DA69) + }; + +/* blkSize = 512 bits. hashSize = 128 bits */ +const u64b_t SKEIN_512_IV_128[] = + { + MK_64(0xA8BC7BF3,0x6FBF9F52), + MK_64(0x1E9872CE,0xBD1AF0AA), + MK_64(0x309B1790,0xB32190D3), + MK_64(0xBCFBB854,0x3F94805C), + MK_64(0x0DA61BCD,0x6E31B11B), + MK_64(0x1A18EBEA,0xD46A32E3), + MK_64(0xA2CC5B18,0xCE84AA82), + MK_64(0x6982AB28,0x9D46982D) + }; + +/* blkSize = 512 bits. hashSize = 160 bits */ +const u64b_t SKEIN_512_IV_160[] = + { + MK_64(0x28B81A2A,0xE013BD91), + MK_64(0xC2F11668,0xB5BDF78F), + MK_64(0x1760D8F3,0xF6A56F12), + MK_64(0x4FB74758,0x8239904F), + MK_64(0x21EDE07F,0x7EAF5056), + MK_64(0xD908922E,0x63ED70B8), + MK_64(0xB8EC76FF,0xECCB52FA), + MK_64(0x01A47BB8,0xA3F27A6E) + }; + +/* blkSize = 512 bits. hashSize = 224 bits */ +const u64b_t SKEIN_512_IV_224[] = + { + MK_64(0xCCD06162,0x48677224), + MK_64(0xCBA65CF3,0xA92339EF), + MK_64(0x8CCD69D6,0x52FF4B64), + MK_64(0x398AED7B,0x3AB890B4), + MK_64(0x0F59D1B1,0x457D2BD0), + MK_64(0x6776FE65,0x75D4EB3D), + MK_64(0x99FBC70E,0x997413E9), + MK_64(0x9E2CFCCF,0xE1C41EF7) + }; + +/* blkSize = 512 bits. hashSize = 256 bits */ +const u64b_t SKEIN_512_IV_256[] = + { + MK_64(0xCCD044A1,0x2FDB3E13), + MK_64(0xE8359030,0x1A79A9EB), + MK_64(0x55AEA061,0x4F816E6F), + MK_64(0x2A2767A4,0xAE9B94DB), + MK_64(0xEC06025E,0x74DD7683), + MK_64(0xE7A436CD,0xC4746251), + MK_64(0xC36FBAF9,0x393AD185), + MK_64(0x3EEDBA18,0x33EDFC13) + }; + +/* blkSize = 512 bits. hashSize = 384 bits */ +const u64b_t SKEIN_512_IV_384[] = + { + MK_64(0xA3F6C6BF,0x3A75EF5F), + MK_64(0xB0FEF9CC,0xFD84FAA4), + MK_64(0x9D77DD66,0x3D770CFE), + MK_64(0xD798CBF3,0xB468FDDA), + MK_64(0x1BC4A666,0x8A0E4465), + MK_64(0x7ED7D434,0xE5807407), + MK_64(0x548FC1AC,0xD4EC44D6), + MK_64(0x266E1754,0x6AA18FF8) + }; + +/* blkSize = 512 bits. hashSize = 512 bits */ +const u64b_t SKEIN_512_IV_512[] = + { + MK_64(0x4903ADFF,0x749C51CE), + MK_64(0x0D95DE39,0x9746DF03), + MK_64(0x8FD19341,0x27C79BCE), + MK_64(0x9A255629,0xFF352CB1), + MK_64(0x5DB62599,0xDF6CA7B0), + MK_64(0xEABE394C,0xA9D5C3F4), + MK_64(0x991112C7,0x1A75B523), + MK_64(0xAE18A40B,0x660FCC33) + }; + +/* blkSize = 1024 bits. hashSize = 384 bits */ +const u64b_t SKEIN1024_IV_384[] = + { + MK_64(0x5102B6B8,0xC1894A35), + MK_64(0xFEEBC9E3,0xFE8AF11A), + MK_64(0x0C807F06,0xE32BED71), + MK_64(0x60C13A52,0xB41A91F6), + MK_64(0x9716D35D,0xD4917C38), + MK_64(0xE780DF12,0x6FD31D3A), + MK_64(0x797846B6,0xC898303A), + MK_64(0xB172C2A8,0xB3572A3B), + MK_64(0xC9BC8203,0xA6104A6C), + MK_64(0x65909338,0xD75624F4), + MK_64(0x94BCC568,0x4B3F81A0), + MK_64(0x3EBBF51E,0x10ECFD46), + MK_64(0x2DF50F0B,0xEEB08542), + MK_64(0x3B5A6530,0x0DBC6516), + MK_64(0x484B9CD2,0x167BBCE1), + MK_64(0x2D136947,0xD4CBAFEA) + }; + +/* blkSize = 1024 bits. hashSize = 512 bits */ +const u64b_t SKEIN1024_IV_512[] = + { + MK_64(0xCAEC0E5D,0x7C1B1B18), + MK_64(0xA01B0E04,0x5F03E802), + MK_64(0x33840451,0xED912885), + MK_64(0x374AFB04,0xEAEC2E1C), + MK_64(0xDF25A0E2,0x813581F7), + MK_64(0xE4004093,0x8B12F9D2), + MK_64(0xA662D539,0xC2ED39B6), + MK_64(0xFA8B85CF,0x45D8C75A), + MK_64(0x8316ED8E,0x29EDE796), + MK_64(0x053289C0,0x2E9F91B8), + MK_64(0xC3F8EF1D,0x6D518B73), + MK_64(0xBDCEC3C4,0xD5EF332E), + MK_64(0x549A7E52,0x22974487), + MK_64(0x67070872,0x5B749816), + MK_64(0xB9CD28FB,0xF0581BD1), + MK_64(0x0E2940B8,0x15804974) + }; + +/* blkSize = 1024 bits. hashSize = 1024 bits */ +const u64b_t SKEIN1024_IV_1024[] = + { + MK_64(0xD593DA07,0x41E72355), + MK_64(0x15B5E511,0xAC73E00C), + MK_64(0x5180E5AE,0xBAF2C4F0), + MK_64(0x03BD41D3,0xFCBCAFAF), + MK_64(0x1CAEC6FD,0x1983A898), + MK_64(0x6E510B8B,0xCDD0589F), + MK_64(0x77E2BDFD,0xC6394ADA), + MK_64(0xC11E1DB5,0x24DCB0A3), + MK_64(0xD6D14AF9,0xC6329AB5), + MK_64(0x6A9B0BFC,0x6EB67E0D), + MK_64(0x9243C60D,0xCCFF1332), + MK_64(0x1A1F1DDE,0x743F02D4), + MK_64(0x0996753C,0x10ED0BB8), + MK_64(0x6572DD22,0xF2B4969A), + MK_64(0x61FD3062,0xD00A579A), + MK_64(0x1DE0536E,0x8682E539) + }; + + +#ifndef SKEIN_USE_ASM +#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */ +#endif + +#ifndef SKEIN_LOOP +#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */ +#endif + +#define BLK_BITS (WCNT*64) /* some useful definitions for code here */ +#define KW_TWK_BASE (0) +#define KW_KEY_BASE (3) +#define ks (kw + KW_KEY_BASE) +#define ts (kw + KW_TWK_BASE) + +#ifdef SKEIN_DEBUG +#define DebugSaveTweak(ctx) { ctx->h.T[0] = ts[0]; ctx->h.T[1] = ts[1]; } +#else +#define DebugSaveTweak(ctx) +#endif + +/***************************** Skein_256 ******************************/ +#if !(SKEIN_USE_ASM & 256) +static void Skein_256_Process_Block(Skein_256_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C */ + enum + { + WCNT = SKEIN_256_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN_256_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_256 (((SKEIN_LOOP)/100)%10) +#else +#define SKEIN_UNROLL_256 (0) +#endif + +#if SKEIN_UNROLL_256 +#if (RCNT % SKEIN_UNROLL_256) +#error "Invalid SKEIN_UNROLL_256" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + u64b_t X0,X1,X2,X3; /* local copy of context vars, for speed */ + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[4]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; +#endif + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[0] = ctx->X[0]; + ks[1] = ctx->X[1]; + ks[2] = ctx->X[2]; + ks[3] = ctx->X[3]; + ks[4] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X0 = w[0] + ks[0]; /* do the first full key injection */ + X1 = w[1] + ks[1] + ts[0]; + X2 = w[2] + ks[2] + ts[1]; + X3 = w[3] + ks[3]; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); /* show starting state values */ + + blkPtr += SKEIN_256_BLOCK_BYTES; + + /* run the rounds */ + +#define Round256(p0,p1,p2,p3,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + +#if SKEIN_UNROLL_256 == 0 +#define R256(p0,p1,p2,p3,ROT,rNum) /* fully unrolled */ \ + Round256(p0,p1,p2,p3,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); + +#define I256(R) \ + X0 += ks[((R)+1) % 5]; /* inject the key schedule value */ \ + X1 += ks[((R)+2) % 5] + ts[((R)+1) % 3]; \ + X2 += ks[((R)+3) % 5] + ts[((R)+2) % 3]; \ + X3 += ks[((R)+4) % 5] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R256(p0,p1,p2,p3,ROT,rNum) \ + Round256(p0,p1,p2,p3,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); + +#define I256(R) \ + X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ + X1 += ks[r+(R)+1] + ts[r+(R)+0]; \ + X2 += ks[r+(R)+2] + ts[r+(R)+1]; \ + X3 += ks[r+(R)+3] + r+(R) ; \ + ks[r + (R)+4 ] = ks[r+(R)-1]; /* rotate key schedule */\ + ts[r + (R)+2 ] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_256) /* loop thru it */ +#endif + { +#define R256_8_rounds(R) \ + R256(0,1,2,3,R_256_0,8*(R) + 1); \ + R256(0,3,2,1,R_256_1,8*(R) + 2); \ + R256(0,1,2,3,R_256_2,8*(R) + 3); \ + R256(0,3,2,1,R_256_3,8*(R) + 4); \ + I256(2*(R)); \ + R256(0,1,2,3,R_256_4,8*(R) + 5); \ + R256(0,3,2,1,R_256_5,8*(R) + 6); \ + R256(0,1,2,3,R_256_6,8*(R) + 7); \ + R256(0,3,2,1,R_256_7,8*(R) + 8); \ + I256(2*(R)+1); + + R256_8_rounds( 0); + +#define R256_Unroll_R(NN) ((SKEIN_UNROLL_256 == 0 && SKEIN_256_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_256 > (NN))) + + #if R256_Unroll_R( 1) + R256_8_rounds( 1); + #endif + #if R256_Unroll_R( 2) + R256_8_rounds( 2); + #endif + #if R256_Unroll_R( 3) + R256_8_rounds( 3); + #endif + #if R256_Unroll_R( 4) + R256_8_rounds( 4); + #endif + #if R256_Unroll_R( 5) + R256_8_rounds( 5); + #endif + #if R256_Unroll_R( 6) + R256_8_rounds( 6); + #endif + #if R256_Unroll_R( 7) + R256_8_rounds( 7); + #endif + #if R256_Unroll_R( 8) + R256_8_rounds( 8); + #endif + #if R256_Unroll_R( 9) + R256_8_rounds( 9); + #endif + #if R256_Unroll_R(10) + R256_8_rounds(10); + #endif + #if R256_Unroll_R(11) + R256_8_rounds(11); + #endif + #if R256_Unroll_R(12) + R256_8_rounds(12); + #endif + #if R256_Unroll_R(13) + R256_8_rounds(13); + #endif + #if R256_Unroll_R(14) + R256_8_rounds(14); + #endif + #if (SKEIN_UNROLL_256 > 14) +#error "need more unrolling in Skein_256_Process_Block" + #endif + } + /* do the final "feedforward" xor, update context chaining vars */ + ctx->X[0] = X0 ^ w[0]; + ctx->X[1] = X1 ^ w[1]; + ctx->X[2] = X2 ^ w[2]; + ctx->X[3] = X3 ^ w[3]; + + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_256_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein_256_Process_Block_CodeSize) - + ((u08b_t *) Skein_256_Process_Block); + } +static uint_t Skein_256_Unroll_Cnt(void) + { + return SKEIN_UNROLL_256; + } +#endif +#endif + +/***************************** Skein_512 ******************************/ +#if !(SKEIN_USE_ASM & 512) +static void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C */ + enum + { + WCNT = SKEIN_512_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN_512_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_512 (((SKEIN_LOOP)/10)%10) +#else +#define SKEIN_UNROLL_512 (0) +#endif + +#if SKEIN_UNROLL_512 +#if (RCNT % SKEIN_UNROLL_512) +#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + u64b_t X0,X1,X2,X3,X4,X5,X6,X7; /* local copy of vars, for speed */ + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[8]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; + Xptr[4] = &X4; Xptr[5] = &X5; Xptr[6] = &X6; Xptr[7] = &X7; +#endif + + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[0] = ctx->X[0]; + ks[1] = ctx->X[1]; + ks[2] = ctx->X[2]; + ks[3] = ctx->X[3]; + ks[4] = ctx->X[4]; + ks[5] = ctx->X[5]; + ks[6] = ctx->X[6]; + ks[7] = ctx->X[7]; + ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ + ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X0 = w[0] + ks[0]; /* do the first full key injection */ + X1 = w[1] + ks[1]; + X2 = w[2] + ks[2]; + X3 = w[3] + ks[3]; + X4 = w[4] + ks[4]; + X5 = w[5] + ks[5] + ts[0]; + X6 = w[6] + ks[6] + ts[1]; + X7 = w[7] + ks[7]; + + blkPtr += SKEIN_512_BLOCK_BYTES; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); + /* run the rounds */ +#define Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ + X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ + +#if SKEIN_UNROLL_512 == 0 +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) /* unrolled */ \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); + +#define I512(R) \ + X0 += ks[((R)+1) % 9]; /* inject the key schedule value */ \ + X1 += ks[((R)+2) % 9]; \ + X2 += ks[((R)+3) % 9]; \ + X3 += ks[((R)+4) % 9]; \ + X4 += ks[((R)+5) % 9]; \ + X5 += ks[((R)+6) % 9] + ts[((R)+1) % 3]; \ + X6 += ks[((R)+7) % 9] + ts[((R)+2) % 3]; \ + X7 += ks[((R)+8) % 9] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); + +#define I512(R) \ + X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ + X1 += ks[r+(R)+1]; \ + X2 += ks[r+(R)+2]; \ + X3 += ks[r+(R)+3]; \ + X4 += ks[r+(R)+4]; \ + X5 += ks[r+(R)+5] + ts[r+(R)+0]; \ + X6 += ks[r+(R)+6] + ts[r+(R)+1]; \ + X7 += ks[r+(R)+7] + r+(R) ; \ + ks[r + (R)+8] = ks[r+(R)-1]; /* rotate key schedule */ \ + ts[r + (R)+2] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_512) /* loop thru it */ +#endif /* end of looped code definitions */ + { +#define R512_8_rounds(R) /* do 8 full rounds */ \ + R512(0,1,2,3,4,5,6,7,R_512_0,8*(R)+ 1); \ + R512(2,1,4,7,6,5,0,3,R_512_1,8*(R)+ 2); \ + R512(4,1,6,3,0,5,2,7,R_512_2,8*(R)+ 3); \ + R512(6,1,0,7,2,5,4,3,R_512_3,8*(R)+ 4); \ + I512(2*(R)); \ + R512(0,1,2,3,4,5,6,7,R_512_4,8*(R)+ 5); \ + R512(2,1,4,7,6,5,0,3,R_512_5,8*(R)+ 6); \ + R512(4,1,6,3,0,5,2,7,R_512_6,8*(R)+ 7); \ + R512(6,1,0,7,2,5,4,3,R_512_7,8*(R)+ 8); \ + I512(2*(R)+1); /* and key injection */ + + R512_8_rounds( 0); + +#define R512_Unroll_R(NN) ((SKEIN_UNROLL_512 == 0 && SKEIN_512_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_512 > (NN))) + + #if R512_Unroll_R( 1) + R512_8_rounds( 1); + #endif + #if R512_Unroll_R( 2) + R512_8_rounds( 2); + #endif + #if R512_Unroll_R( 3) + R512_8_rounds( 3); + #endif + #if R512_Unroll_R( 4) + R512_8_rounds( 4); + #endif + #if R512_Unroll_R( 5) + R512_8_rounds( 5); + #endif + #if R512_Unroll_R( 6) + R512_8_rounds( 6); + #endif + #if R512_Unroll_R( 7) + R512_8_rounds( 7); + #endif + #if R512_Unroll_R( 8) + R512_8_rounds( 8); + #endif + #if R512_Unroll_R( 9) + R512_8_rounds( 9); + #endif + #if R512_Unroll_R(10) + R512_8_rounds(10); + #endif + #if R512_Unroll_R(11) + R512_8_rounds(11); + #endif + #if R512_Unroll_R(12) + R512_8_rounds(12); + #endif + #if R512_Unroll_R(13) + R512_8_rounds(13); + #endif + #if R512_Unroll_R(14) + R512_8_rounds(14); + #endif + #if (SKEIN_UNROLL_512 > 14) +#error "need more unrolling in Skein_512_Process_Block" + #endif + } + + /* do the final "feedforward" xor, update context chaining vars */ + ctx->X[0] = X0 ^ w[0]; + ctx->X[1] = X1 ^ w[1]; + ctx->X[2] = X2 ^ w[2]; + ctx->X[3] = X3 ^ w[3]; + ctx->X[4] = X4 ^ w[4]; + ctx->X[5] = X5 ^ w[5]; + ctx->X[6] = X6 ^ w[6]; + ctx->X[7] = X7 ^ w[7]; + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_512_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein_512_Process_Block_CodeSize) - + ((u08b_t *) Skein_512_Process_Block); + } +static uint_t Skein_512_Unroll_Cnt(void) + { + return SKEIN_UNROLL_512; + } +#endif +#endif + +/***************************** Skein1024 ******************************/ +#if !(SKEIN_USE_ASM & 1024) +static void Skein1024_Process_Block(Skein1024_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C, always looping (unrolled is bigger AND slower!) */ + enum + { + WCNT = SKEIN1024_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN1024_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_1024 ((SKEIN_LOOP)%10) +#else +#define SKEIN_UNROLL_1024 (0) +#endif + +#if (SKEIN_UNROLL_1024 != 0) +#if (RCNT % SKEIN_UNROLL_1024) +#error "Invalid SKEIN_UNROLL_1024" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + + u64b_t X00,X01,X02,X03,X04,X05,X06,X07, /* local copy of vars, for speed */ + X08,X09,X10,X11,X12,X13,X14,X15; + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[16]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[ 0] = &X00; Xptr[ 1] = &X01; Xptr[ 2] = &X02; Xptr[ 3] = &X03; + Xptr[ 4] = &X04; Xptr[ 5] = &X05; Xptr[ 6] = &X06; Xptr[ 7] = &X07; + Xptr[ 8] = &X08; Xptr[ 9] = &X09; Xptr[10] = &X10; Xptr[11] = &X11; + Xptr[12] = &X12; Xptr[13] = &X13; Xptr[14] = &X14; Xptr[15] = &X15; +#endif + + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[ 0] = ctx->X[ 0]; + ks[ 1] = ctx->X[ 1]; + ks[ 2] = ctx->X[ 2]; + ks[ 3] = ctx->X[ 3]; + ks[ 4] = ctx->X[ 4]; + ks[ 5] = ctx->X[ 5]; + ks[ 6] = ctx->X[ 6]; + ks[ 7] = ctx->X[ 7]; + ks[ 8] = ctx->X[ 8]; + ks[ 9] = ctx->X[ 9]; + ks[10] = ctx->X[10]; + ks[11] = ctx->X[11]; + ks[12] = ctx->X[12]; + ks[13] = ctx->X[13]; + ks[14] = ctx->X[14]; + ks[15] = ctx->X[15]; + ks[16] = ks[ 0] ^ ks[ 1] ^ ks[ 2] ^ ks[ 3] ^ + ks[ 4] ^ ks[ 5] ^ ks[ 6] ^ ks[ 7] ^ + ks[ 8] ^ ks[ 9] ^ ks[10] ^ ks[11] ^ + ks[12] ^ ks[13] ^ ks[14] ^ ks[15] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X00 = w[ 0] + ks[ 0]; /* do the first full key injection */ + X01 = w[ 1] + ks[ 1]; + X02 = w[ 2] + ks[ 2]; + X03 = w[ 3] + ks[ 3]; + X04 = w[ 4] + ks[ 4]; + X05 = w[ 5] + ks[ 5]; + X06 = w[ 6] + ks[ 6]; + X07 = w[ 7] + ks[ 7]; + X08 = w[ 8] + ks[ 8]; + X09 = w[ 9] + ks[ 9]; + X10 = w[10] + ks[10]; + X11 = w[11] + ks[11]; + X12 = w[12] + ks[12]; + X13 = w[13] + ks[13] + ts[0]; + X14 = w[14] + ks[14] + ts[1]; + X15 = w[15] + ks[15]; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); + +#define Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ + X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ + X##p8 += X##p9; X##p9 = RotL_64(X##p9,ROT##_4); X##p9 ^= X##p8; \ + X##pA += X##pB; X##pB = RotL_64(X##pB,ROT##_5); X##pB ^= X##pA; \ + X##pC += X##pD; X##pD = RotL_64(X##pD,ROT##_6); X##pD ^= X##pC; \ + X##pE += X##pF; X##pF = RotL_64(X##pF,ROT##_7); X##pF ^= X##pE; \ + +#if SKEIN_UNROLL_1024 == 0 +#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rn,Xptr); + +#define I1024(R) \ + X00 += ks[((R)+ 1) % 17]; /* inject the key schedule value */ \ + X01 += ks[((R)+ 2) % 17]; \ + X02 += ks[((R)+ 3) % 17]; \ + X03 += ks[((R)+ 4) % 17]; \ + X04 += ks[((R)+ 5) % 17]; \ + X05 += ks[((R)+ 6) % 17]; \ + X06 += ks[((R)+ 7) % 17]; \ + X07 += ks[((R)+ 8) % 17]; \ + X08 += ks[((R)+ 9) % 17]; \ + X09 += ks[((R)+10) % 17]; \ + X10 += ks[((R)+11) % 17]; \ + X11 += ks[((R)+12) % 17]; \ + X12 += ks[((R)+13) % 17]; \ + X13 += ks[((R)+14) % 17] + ts[((R)+1) % 3]; \ + X14 += ks[((R)+15) % 17] + ts[((R)+2) % 3]; \ + X15 += ks[((R)+16) % 17] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rn,Xptr); + +#define I1024(R) \ + X00 += ks[r+(R)+ 0]; /* inject the key schedule value */ \ + X01 += ks[r+(R)+ 1]; \ + X02 += ks[r+(R)+ 2]; \ + X03 += ks[r+(R)+ 3]; \ + X04 += ks[r+(R)+ 4]; \ + X05 += ks[r+(R)+ 5]; \ + X06 += ks[r+(R)+ 6]; \ + X07 += ks[r+(R)+ 7]; \ + X08 += ks[r+(R)+ 8]; \ + X09 += ks[r+(R)+ 9]; \ + X10 += ks[r+(R)+10]; \ + X11 += ks[r+(R)+11]; \ + X12 += ks[r+(R)+12]; \ + X13 += ks[r+(R)+13] + ts[r+(R)+0]; \ + X14 += ks[r+(R)+14] + ts[r+(R)+1]; \ + X15 += ks[r+(R)+15] + r+(R) ; \ + ks[r + (R)+16] = ks[r+(R)-1]; /* rotate key schedule */ \ + ts[r + (R)+ 2] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r <= 2*RCNT;r+=2*SKEIN_UNROLL_1024) /* loop thru it */ +#endif + { +#define R1024_8_rounds(R) /* do 8 full rounds */ \ + R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_0,8*(R) + 1); \ + R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_1,8*(R) + 2); \ + R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_2,8*(R) + 3); \ + R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_3,8*(R) + 4); \ + I1024(2*(R)); \ + R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_4,8*(R) + 5); \ + R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_5,8*(R) + 6); \ + R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_6,8*(R) + 7); \ + R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_7,8*(R) + 8); \ + I1024(2*(R)+1); + + R1024_8_rounds( 0); + +#define R1024_Unroll_R(NN) ((SKEIN_UNROLL_1024 == 0 && SKEIN1024_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_1024 > (NN))) + + #if R1024_Unroll_R( 1) + R1024_8_rounds( 1); + #endif + #if R1024_Unroll_R( 2) + R1024_8_rounds( 2); + #endif + #if R1024_Unroll_R( 3) + R1024_8_rounds( 3); + #endif + #if R1024_Unroll_R( 4) + R1024_8_rounds( 4); + #endif + #if R1024_Unroll_R( 5) + R1024_8_rounds( 5); + #endif + #if R1024_Unroll_R( 6) + R1024_8_rounds( 6); + #endif + #if R1024_Unroll_R( 7) + R1024_8_rounds( 7); + #endif + #if R1024_Unroll_R( 8) + R1024_8_rounds( 8); + #endif + #if R1024_Unroll_R( 9) + R1024_8_rounds( 9); + #endif + #if R1024_Unroll_R(10) + R1024_8_rounds(10); + #endif + #if R1024_Unroll_R(11) + R1024_8_rounds(11); + #endif + #if R1024_Unroll_R(12) + R1024_8_rounds(12); + #endif + #if R1024_Unroll_R(13) + R1024_8_rounds(13); + #endif + #if R1024_Unroll_R(14) + R1024_8_rounds(14); + #endif + #if (SKEIN_UNROLL_1024 > 14) +#error "need more unrolling in Skein_1024_Process_Block" + #endif + } + /* do the final "feedforward" xor, update context chaining vars */ + + ctx->X[ 0] = X00 ^ w[ 0]; + ctx->X[ 1] = X01 ^ w[ 1]; + ctx->X[ 2] = X02 ^ w[ 2]; + ctx->X[ 3] = X03 ^ w[ 3]; + ctx->X[ 4] = X04 ^ w[ 4]; + ctx->X[ 5] = X05 ^ w[ 5]; + ctx->X[ 6] = X06 ^ w[ 6]; + ctx->X[ 7] = X07 ^ w[ 7]; + ctx->X[ 8] = X08 ^ w[ 8]; + ctx->X[ 9] = X09 ^ w[ 9]; + ctx->X[10] = X10 ^ w[10]; + ctx->X[11] = X11 ^ w[11]; + ctx->X[12] = X12 ^ w[12]; + ctx->X[13] = X13 ^ w[13]; + ctx->X[14] = X14 ^ w[14]; + ctx->X[15] = X15 ^ w[15]; + + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + blkPtr += SKEIN1024_BLOCK_BYTES; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein1024_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein1024_Process_Block_CodeSize) - + ((u08b_t *) Skein1024_Process_Block); + } +static uint_t Skein1024_Unroll_Cnt(void) + { + return SKEIN_UNROLL_1024; + } +#endif +#endif + + +#if 0 +/*****************************************************************/ +/* 256-bit Skein */ +/*****************************************************************/ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein_256_Init(Skein_256_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN_256_STATE_BYTES]; + u64b_t w[SKEIN_256_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 256: memcpy(ctx->X,SKEIN_256_IV_256,sizeof(ctx->X)); break; + case 224: memcpy(ctx->X,SKEIN_256_IV_224,sizeof(ctx->X)); break; + case 160: memcpy(ctx->X,SKEIN_256_IV_160,sizeof(ctx->X)); break; + case 128: memcpy(ctx->X,SKEIN_256_IV_128,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein_256_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN_256_STATE_BYTES]; + u64b_t w[SKEIN_256_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein_256_Update(ctx,key,keyBytes); /* hash the key */ + Skein_256_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(256,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN_256_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN_256_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN_256_BLOCK_BYTES); + Skein_256_Process_Block(ctx,ctx->b,1,SKEIN_256_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN_256_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN_256_BLOCK_BYTES; /* number of full blocks to process */ + Skein_256_Process_Block(ctx,msg,n,SKEIN_256_BLOCK_BYTES); + msgByteCnt -= n * SKEIN_256_BLOCK_BYTES; + msg += n * SKEIN_256_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein_256_Final(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_256_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); + + Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++) + { + ctx->balias[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_256_BLOCK_BYTES) + n = SKEIN_256_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_256_API_CodeSize(void) + { + return ((u08b_t *) Skein_256_API_CodeSize) - + ((u08b_t *) Skein_256_Init); + } +#endif + +/*****************************************************************/ +/* 512-bit Skein */ +/*****************************************************************/ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN_512_STATE_BYTES]; + u64b_t w[SKEIN_512_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 512: memcpy(ctx->X,SKEIN_512_IV_512,sizeof(ctx->X)); break; + case 384: memcpy(ctx->X,SKEIN_512_IV_384,sizeof(ctx->X)); break; + case 256: memcpy(ctx->X,SKEIN_512_IV_256,sizeof(ctx->X)); break; + case 224: memcpy(ctx->X,SKEIN_512_IV_224,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein_512_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN_512_STATE_BYTES]; + u64b_t w[SKEIN_512_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein_512_Update(ctx,key,keyBytes); /* hash the key */ + Skein_512_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(512,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN_512_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN_512_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN_512_BLOCK_BYTES); + Skein_512_Process_Block(ctx,ctx->b,1,SKEIN_512_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN_512_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN_512_BLOCK_BYTES; /* number of full blocks to process */ + Skein_512_Process_Block(ctx,msg,n,SKEIN_512_BLOCK_BYTES); + msgByteCnt -= n * SKEIN_512_BLOCK_BYTES; + msg += n * SKEIN_512_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein_512_Final(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_512_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); + + Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) + { + ctx->balias[0] = Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_512_BLOCK_BYTES) + n = SKEIN_512_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(512,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_512_API_CodeSize(void) + { + return ((u08b_t *) Skein_512_API_CodeSize) - + ((u08b_t *) Skein_512_Init); + } +#endif + +/*****************************************************************/ +/* 1024-bit Skein */ +/*****************************************************************/ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein1024_Init(Skein1024_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN1024_STATE_BYTES]; + u64b_t w[SKEIN1024_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 512: memcpy(ctx->X,SKEIN1024_IV_512 ,sizeof(ctx->X)); break; + case 384: memcpy(ctx->X,SKEIN1024_IV_384 ,sizeof(ctx->X)); break; + case 1024: memcpy(ctx->X,SKEIN1024_IV_1024,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein1024_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN1024_STATE_BYTES]; + u64b_t w[SKEIN1024_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein1024_Update(ctx,key,keyBytes); /* hash the key */ + Skein1024_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(1024,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN1024_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN1024_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN1024_BLOCK_BYTES); + Skein1024_Process_Block(ctx,ctx->b,1,SKEIN1024_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN1024_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN1024_BLOCK_BYTES; /* number of full blocks to process */ + Skein1024_Process_Block(ctx,msg,n,SKEIN1024_BLOCK_BYTES); + msgByteCnt -= n * SKEIN1024_BLOCK_BYTES; + msg += n * SKEIN1024_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein1024_Final(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN1024_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); + + Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) + { + ctx->balias[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN1024_BLOCK_BYTES) + n = SKEIN1024_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(1024,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein1024_API_CodeSize(void) + { + return ((u08b_t *) Skein1024_API_CodeSize) - + ((u08b_t *) Skein1024_Init); + } +#endif + +/**************** Functions to support MAC/tree hashing ***************/ +/* (this code is identical for Optimized and Reference versions) */ + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); + Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_256_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); + Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_512_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); + Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN1024_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + + +#if SKEIN_TREE_HASH +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein_256_Output(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_256_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_256_BLOCK_BYTES) + n = SKEIN_256_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein_512_Output(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_512_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_512_BLOCK_BYTES) + n = SKEIN_512_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein1024_Output(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN1024_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN1024_BLOCK_BYTES) + n = SKEIN1024_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } +#endif +#endif + +typedef struct +{ + uint_t statebits; /* 256, 512, or 1024 */ + union + { + Skein_Ctxt_Hdr_t h; /* common header "overlay" */ + Skein_256_Ctxt_t ctx_256; + Skein_512_Ctxt_t ctx_512; + Skein1024_Ctxt_t ctx1024; + } u; +} +hashState; + +/* "incremental" hashing API */ +static HashReturn Init (hashState *state, int hashbitlen); +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen); +static HashReturn Final (hashState *state, BitSequence *hashval); + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* select the context size and init the context */ +static HashReturn Init(hashState *state, int hashbitlen) +{ +#if SKEIN_256_NIST_MAX_HASHBITS + if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS) + { + Skein_Assert(hashbitlen > 0,BAD_HASHLEN); + state->statebits = 64*SKEIN_256_STATE_WORDS; + return Skein_256_Init(&state->u.ctx_256,(size_t) hashbitlen); + } +#endif + if (hashbitlen <= SKEIN_512_NIST_MAX_HASHBITS) + { + state->statebits = 64*SKEIN_512_STATE_WORDS; + return Skein_512_Init(&state->u.ctx_512,(size_t) hashbitlen); + } + else + { + state->statebits = 64*SKEIN1024_STATE_WORDS; + return Skein1024_Init(&state->u.ctx1024,(size_t) hashbitlen); + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process data to be hashed */ +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen) +{ + /* only the final Update() call is allowed do partial bytes, else assert an error */ + Skein_Assert((state->u.h.T[1] & SKEIN_T1_FLAG_BIT_PAD) == 0 || databitlen == 0, SKEIN_FAIL); + + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,SKEIN_FAIL); + if ((databitlen & 7) == 0) /* partial bytes? */ + { + switch ((state->statebits >> 8) & 3) + { + case 2: return Skein_512_Update(&state->u.ctx_512,data,databitlen >> 3); + case 1: return Skein_256_Update(&state->u.ctx_256,data,databitlen >> 3); + case 0: return Skein1024_Update(&state->u.ctx1024,data,databitlen >> 3); + default: return SKEIN_FAIL; + } + } + else + { /* handle partial final byte */ + size_t bCnt = (databitlen >> 3) + 1; /* number of bytes to handle (nonzero here!) */ + u08b_t b,mask; + + mask = (u08b_t) (1u << (7 - (databitlen & 7))); /* partial byte bit mask */ + b = (u08b_t) ((data[bCnt-1] & (0-mask)) | mask); /* apply bit padding on final byte */ + + switch ((state->statebits >> 8) & 3) + { + case 2: Skein_512_Update(&state->u.ctx_512,data,bCnt-1); /* process all but the final byte */ + Skein_512_Update(&state->u.ctx_512,&b , 1 ); /* process the (masked) partial byte */ + break; + case 1: Skein_256_Update(&state->u.ctx_256,data,bCnt-1); /* process all but the final byte */ + Skein_256_Update(&state->u.ctx_256,&b , 1 ); /* process the (masked) partial byte */ + break; + case 0: Skein1024_Update(&state->u.ctx1024,data,bCnt-1); /* process all but the final byte */ + Skein1024_Update(&state->u.ctx1024,&b , 1 ); /* process the (masked) partial byte */ + break; + default: return SKEIN_FAIL; + } + Skein_Set_Bit_Pad_Flag(state->u.h); /* set tweak flag for the final call */ + + return SKEIN_SUCCESS; + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize hash computation and output the result (hashbitlen bits) */ +static HashReturn Final(hashState *state, BitSequence *hashval) +{ + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,FAIL); + switch ((state->statebits >> 8) & 3) + { + case 2: return Skein_512_Final(&state->u.ctx_512,hashval); + case 1: return Skein_256_Final(&state->u.ctx_256,hashval); + case 0: return Skein1024_Final(&state->u.ctx1024,hashval); + default: return SKEIN_FAIL; + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* all-in-one hash function */ +HashReturn skein_hash(int hashbitlen, const BitSequence *data, /* all-in-one call */ + DataLength databitlen,BitSequence *hashval) +{ + hashState state; + HashReturn r = Init(&state,hashbitlen); + if (r == SKEIN_SUCCESS) + { /* these calls do not fail when called properly */ + r = Update(&state,data,databitlen); + Final(&state,hashval); + } + return r; +} diff --git a/src/crypto/skein/skein.h b/src/crypto/skein/skein.h new file mode 100644 index 00000000..f86e3bb0 --- /dev/null +++ b/src/crypto/skein/skein.h @@ -0,0 +1,52 @@ +#pragma once +/************************************************************************** +** +** Interface declarations and internal definitions for Skein hashing. +** +** Source code author: Doug Whiting, 2008. +** +** This algorithm and source code is released to the public domain. +** +*************************************************************************** +** +** The following compile-time switches may be defined to control some +** tradeoffs between speed, code size, error checking, and security. +** +** The "default" note explains what happens when the switch is not defined. +** +** SKEIN_DEBUG -- make callouts from inside Skein code +** to examine/display intermediate values. +** [default: no callouts (no overhead)] +** +** SKEIN_ERR_CHECK -- how error checking is handled inside Skein +** code. If not defined, most error checking +** is disabled (for performance). Otherwise, +** the switch value is interpreted as: +** 0: use assert() to flag errors +** 1: return SKEIN_FAIL to flag errors +** +***************************************************************************/ +#include "skein_port.h" /* get platform-specific definitions */ + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum +{ + SKEIN_SUCCESS = 0, /* return codes from Skein calls */ + SKEIN_FAIL = 1, + SKEIN_BAD_HASHLEN = 2 +} +HashReturn; + +typedef size_t DataLength; /* bit count type */ +typedef u08b_t BitSequence; /* bit stream type */ + +/* "all-in-one" call */ +HashReturn skein_hash(int hashbitlen, const BitSequence *data, + DataLength databitlen, BitSequence *hashval); + +#if defined(__cplusplus) +} +#endif diff --git a/src/crypto/skein/skein_port.h b/src/crypto/skein/skein_port.h new file mode 100644 index 00000000..ec90d18b --- /dev/null +++ b/src/crypto/skein/skein_port.h @@ -0,0 +1,189 @@ +#pragma once + +#include +#include + +#ifndef RETURN_VALUES +# define RETURN_VALUES +# if defined( DLL_EXPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllexport ) void __stdcall +# define INT_RETURN __declspec( dllexport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllexport__ ) void +# define INT_RETURN __declspec( __dllexport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( DLL_IMPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllimport ) void __stdcall +# define INT_RETURN __declspec( dllimport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllimport__ ) void +# define INT_RETURN __declspec( __dllimport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( __WATCOMC__ ) +# define VOID_RETURN void __cdecl +# define INT_RETURN int __cdecl +# else +# define VOID_RETURN void +# define INT_RETURN int +# endif +#endif + +/* These defines are used to declare buffers in a way that allows + faster operations on longer variables to be used. In all these + defines 'size' must be a power of 2 and >= 8 + + dec_unit_type(size,x) declares a variable 'x' of length + 'size' bits + + dec_bufr_type(size,bsize,x) declares a buffer 'x' of length 'bsize' + bytes defined as an array of variables + each of 'size' bits (bsize must be a + multiple of size / 8) + + ptr_cast(x,size) casts a pointer to a pointer to a + varaiable of length 'size' bits +*/ + +#define ui_type(size) uint##size##_t +#define dec_unit_type(size,x) typedef ui_type(size) x +#define dec_bufr_type(size,bsize,x) typedef ui_type(size) x[bsize / (size >> 3)] +#define ptr_cast(x,size) ((ui_type(size)*)(x)) + +typedef unsigned int uint_t; /* native unsigned integer */ +typedef uint8_t u08b_t; /* 8-bit unsigned integer */ +typedef uint64_t u64b_t; /* 64-bit unsigned integer */ + +#ifndef RotL_64 +#define RotL_64(x,N) (((x) << (N)) | ((x) >> (64-(N)))) +#endif + +/* + * Skein is "natively" little-endian (unlike SHA-xxx), for optimal + * performance on x86 CPUs. The Skein code requires the following + * definitions for dealing with endianness: + * + * SKEIN_NEED_SWAP: 0 for little-endian, 1 for big-endian + * Skein_Put64_LSB_First + * Skein_Get64_LSB_First + * Skein_Swap64 + * + * If SKEIN_NEED_SWAP is defined at compile time, it is used here + * along with the portable versions of Put64/Get64/Swap64, which + * are slow in general. + * + * Otherwise, an "auto-detect" of endianness is attempted below. + * If the default handling doesn't work well, the user may insert + * platform-specific code instead (e.g., for big-endian CPUs). + * + */ +#ifndef SKEIN_NEED_SWAP /* compile-time "override" for endianness? */ + + +#include "../int-util.h" // TODO - take original implementation + +#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ +#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ + +#if BYTE_ORDER == LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if BYTE_ORDER == BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#endif + +/* special handler for IA64, which may be either endianness (?) */ +/* here we assume little-endian, but this may need to be changed */ +#if defined(__ia64) || defined(__ia64__) || defined(_M_IA64) +# define PLATFORM_MUST_ALIGN (1) +#ifndef PLATFORM_BYTE_ORDER +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif +#endif + +#ifndef PLATFORM_MUST_ALIGN +# define PLATFORM_MUST_ALIGN (0) +#endif + + +#if PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN + /* here for big-endian CPUs */ +#define SKEIN_NEED_SWAP (1) +#elif PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN + /* here for x86 and x86-64 CPUs (and other detected little-endian CPUs) */ +#define SKEIN_NEED_SWAP (0) +#if PLATFORM_MUST_ALIGN == 0 /* ok to use "fast" versions? */ +#define Skein_Put64_LSB_First(dst08,src64,bCnt) memcpy(dst08,src64,bCnt) +#define Skein_Get64_LSB_First(dst64,src08,wCnt) memcpy(dst64,src08,8*(wCnt)) +#endif +#else +#error "Skein needs endianness setting!" +#endif + +#endif /* ifndef SKEIN_NEED_SWAP */ + +/* + ****************************************************************** + * Provide any definitions still needed. + ****************************************************************** + */ +#ifndef Skein_Swap64 /* swap for big-endian, nop for little-endian */ +#if SKEIN_NEED_SWAP +#define Skein_Swap64(w64) \ + ( (( ((u64b_t)(w64)) & 0xFF) << 56) | \ + (((((u64b_t)(w64)) >> 8) & 0xFF) << 48) | \ + (((((u64b_t)(w64)) >>16) & 0xFF) << 40) | \ + (((((u64b_t)(w64)) >>24) & 0xFF) << 32) | \ + (((((u64b_t)(w64)) >>32) & 0xFF) << 24) | \ + (((((u64b_t)(w64)) >>40) & 0xFF) << 16) | \ + (((((u64b_t)(w64)) >>48) & 0xFF) << 8) | \ + (((((u64b_t)(w64)) >>56) & 0xFF) ) ) +#else +#define Skein_Swap64(w64) (w64) +#endif +#endif /* ifndef Skein_Swap64 */ + + +#ifndef Skein_Put64_LSB_First +void Skein_Put64_LSB_First(u08b_t *dst,const u64b_t *src,size_t bCnt) +#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */ + { /* this version is fully portable (big-endian or little-endian), but slow */ + size_t n; + + for (n=0;n>3] >> (8*(n&7))); + } +#else + ; /* output only the function prototype */ +#endif +#endif /* ifndef Skein_Put64_LSB_First */ + + +#ifndef Skein_Get64_LSB_First +void Skein_Get64_LSB_First(u64b_t *dst,const u08b_t *src,size_t wCnt) +#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */ + { /* this version is fully portable (big-endian or little-endian), but slow */ + size_t n; + + for (n=0;n<8*wCnt;n+=8) + dst[n/8] = (((u64b_t) src[n ]) ) + + (((u64b_t) src[n+1]) << 8) + + (((u64b_t) src[n+2]) << 16) + + (((u64b_t) src[n+3]) << 24) + + (((u64b_t) src[n+4]) << 32) + + (((u64b_t) src[n+5]) << 40) + + (((u64b_t) src[n+6]) << 48) + + (((u64b_t) src[n+7]) << 56) ; + } +#else + ; /* output only the function prototype */ +#endif +#endif /* ifndef Skein_Get64_LSB_First */ + + diff --git a/src/crypto/slow-hash_stdc.c b/src/crypto/slow-hash_stdc.c index e4d4d711..9e2727c8 100644 --- a/src/crypto/slow-hash_stdc.c +++ b/src/crypto/slow-hash_stdc.c @@ -6,65 +6,75 @@ #include #include +#include "hash.h" #include "int-util.h" -#include "hash-impl.h" -#include "oaes_lib.h" +#include "oaes/oaes_lib.h" #ifdef __APPLE__ #include "TargetConditionals.h" #endif -static void (*const extra_hashes[4])(const void *, size_t, unsigned char *) = { - hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein -}; +static void (*const extra_hashes[4])(const void *, size_t, struct CHash *) = { + hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein}; -#define MEMORY (1 << 21) /* 2 MiB */ -#define ITER (1 << 20) -#define AES_BLOCK_SIZE 16 -#define AES_KEY_SIZE 32 /*16*/ -#define INIT_SIZE_BLK 8 +#define MEMORY (1 << 21) /* 2 MiB */ +#define ITER (1 << 20) +#define AES_BLOCK_SIZE 16 +#define AES_KEY_SIZE 32 /*16*/ +#define INIT_SIZE_BLK 8 #define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) -static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); } +static void uint_le_to_bytes(unsigned char *buf, size_t si, uint64_t val) { + for (size_t i = 0; i != si; ++i) { + buf[i] = (unsigned char)val; + val >>= 8; + } +} +static uint64_t uint_le_from_bytes(const unsigned char *buf, size_t si) { + uint64_t result = 0; + for (size_t i = si; i-- > 0;) + result = (result << 8) + buf[i]; + return result; +} + +static size_t e2i(const uint8_t *a, size_t count) { return (uint_le_from_bytes(a, 8) / AES_BLOCK_SIZE) & (count - 1); } -static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { +static void mul(const uint8_t *a, const uint8_t *b, uint8_t *res) { uint64_t a0, b0; uint64_t hi, lo; - - a0 = SWAP64LE(((uint64_t*)a)[0]); - b0 = SWAP64LE(((uint64_t*)b)[0]); + + a0 = uint_le_from_bytes(a, 8); + b0 = uint_le_from_bytes(b, 8); lo = mul128(a0, b0, &hi); - ((uint64_t*)res)[0] = SWAP64LE(hi); - ((uint64_t*)res)[1] = SWAP64LE(lo); + uint_le_to_bytes(res, 8, hi); + uint_le_to_bytes(res + 8, 8, lo); } -static void sum_half_blocks(uint8_t* a, const uint8_t* b) { +static void sum_half_blocks(uint8_t *a, const uint8_t *b) { uint64_t a0, a1, b0, b1; - - a0 = SWAP64LE(((uint64_t*)a)[0]); - a1 = SWAP64LE(((uint64_t*)a)[1]); - b0 = SWAP64LE(((uint64_t*)b)[0]); - b1 = SWAP64LE(((uint64_t*)b)[1]); + + a0 = uint_le_from_bytes(a, 8); + a1 = uint_le_from_bytes(a + 8, 8); + b0 = uint_le_from_bytes(b, 8); + b1 = uint_le_from_bytes(b + 8, 8); a0 += b0; a1 += b1; - ((uint64_t*)a)[0] = SWAP64LE(a0); - ((uint64_t*)a)[1] = SWAP64LE(a1); + uint_le_to_bytes(a, 8, a0); + uint_le_to_bytes(a + 8, 8, a1); } -static void copy_block(uint8_t* dst, const uint8_t* src) { - memcpy(dst, src, AES_BLOCK_SIZE); -} +static void copy_block(uint8_t *dst, const uint8_t *src) { memcpy(dst, src, AES_BLOCK_SIZE); } -static void swap_blocks(uint8_t* a, uint8_t* b) { +static void swap_blocks(uint8_t *a, uint8_t *b) { size_t i; uint8_t t; for (i = 0; i < AES_BLOCK_SIZE; i++) { - t = a[i]; + t = a[i]; a[i] = b[i]; b[i] = t; } } -static void xor_blocks(uint8_t* a, const uint8_t* b) { +static void xor_blocks(uint8_t *a, const uint8_t *b) { size_t i; for (i = 0; i < AES_BLOCK_SIZE; i++) { a[i] ^= b[i]; @@ -73,7 +83,7 @@ static void xor_blocks(uint8_t* a, const uint8_t* b) { #pragma pack(push, 1) union cn_slow_hash_state { - union hash_state hs; + struct keccak_state hs; struct { uint8_t k[64]; uint8_t init[INIT_SIZE_BYTE]; @@ -81,8 +91,8 @@ union cn_slow_hash_state { }; #pragma pack(pop) -static void cn_slow_hash_platform_independent(void * scratchpad, const void *data, size_t length, void *hash) { - uint8_t * long_state = (uint8_t*)scratchpad; +void cn_slow_hash_platform_independent(void *scratchpad, const void *data, size_t length, struct CHash *hash) { + uint8_t *long_state = (uint8_t *)scratchpad; union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; @@ -91,9 +101,9 @@ static void cn_slow_hash_platform_independent(void * scratchpad, const void *dat uint8_t d[AES_BLOCK_SIZE]; size_t i, j; uint8_t aes_key[AES_KEY_SIZE]; - OAES_CTX* aes_ctx; + OAES_CTX *aes_ctx; - hash_process(&state.hs, data, length); + keccak_into_state(data, length, &state.hs); memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(aes_key, state.hs.b, AES_KEY_SIZE); aes_ctx = oaes_alloc(); @@ -107,7 +117,7 @@ static void cn_slow_hash_platform_independent(void * scratchpad, const void *dat } for (i = 0; i < 16; i++) { - a[i] = state.k[ i] ^ state.k[32 + i]; + a[i] = state.k[i] ^ state.k[32 + i]; b[i] = state.k[16 + i] ^ state.k[48 + i]; } @@ -146,13 +156,15 @@ static void cn_slow_hash_platform_independent(void * scratchpad, const void *dat } } memcpy(state.init, text, INIT_SIZE_BYTE); - hash_permutation(&state.hs); + keccak_permutation(&state.hs); extra_hashes[state.hs.b[0] & 3](&state, 200, hash); oaes_free(&aes_ctx); } -#if TARGET_OS_IPHONE || defined(__ANDROID__) // We need if !x86, but no portable way to express that -void cn_slow_hash(void * scratchpad, const void *data, size_t length, void *hash) { +#if defined(__PPC__) || TARGET_OS_IPHONE || \ + defined(__ANDROID__) // We need if !x86, but no portable way to express that +void cn_slow_hash(void *scratchpad, const void *data, size_t length, void *hash) { cn_slow_hash_platform_independent(scratchpad, data, length, hash); } -#endif // TARGET_OS_IPHONE + +#endif // TARGET_OS_IPHONE diff --git a/src/crypto/slow-hash_x86.c b/src/crypto/slow-hash_x86.c index ff707089..c6ba04a4 100644 --- a/src/crypto/slow-hash_x86.c +++ b/src/crypto/slow-hash_x86.c @@ -9,7 +9,8 @@ #include "TargetConditionals.h" #endif -#if !TARGET_OS_IPHONE // We need "if x86", but no portable way to express that +#if !defined(__PPC__) && !TARGET_OS_IPHONE && \ + !defined(__ANDROID__) // We need "if x86", but no portable way to express that #include #include @@ -20,11 +21,11 @@ #include #endif -#include "aesb.h" +#include "hash.h" #include "initializer.h" #include "int-util.h" -#include "hash-impl.h" -#include "oaes_lib.h" +#include "oaes/aesb.h" +#include "oaes/oaes_lib.h" #if defined(__GNUC__) #define likely(x) (__builtin_expect(!!(x), 1)) @@ -39,20 +40,20 @@ #define restrict #endif -#define MEMORY (1 << 21) /* 2 MiB */ -#define ITER (1 << 20) -#define AES_BLOCK_SIZE 16 -#define AES_KEY_SIZE 32 /*16*/ -#define INIT_SIZE_BLK 8 -#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) // 128 +#define MEMORY (1 << 21) /* 2 MiB */ +#define ITER (1 << 20) +#define AES_BLOCK_SIZE 16 +#define AES_KEY_SIZE 32 /*16*/ +#define INIT_SIZE_BLK 8 +#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) // 128 #pragma pack(push, 1) union cn_slow_hash_state { - union hash_state hs; - struct { - uint8_t k[64]; - uint8_t init[INIT_SIZE_BYTE]; - }; + struct keccak_state hs; + struct { + uint8_t k[64]; + uint8_t init[INIT_SIZE_BYTE]; + }; }; #pragma pack(pop) @@ -65,135 +66,132 @@ union cn_slow_hash_state { #endif struct cn_ctx { - ALIGNED_DECL(uint8_t long_state[MEMORY], 16); - ALIGNED_DECL(union cn_slow_hash_state state, 16); - ALIGNED_DECL(uint8_t text[INIT_SIZE_BYTE], 16); - ALIGNED_DECL(uint64_t a[AES_BLOCK_SIZE >> 3], 16); - ALIGNED_DECL(uint64_t b[AES_BLOCK_SIZE >> 3], 16); - ALIGNED_DECL(uint8_t c[AES_BLOCK_SIZE], 16); - oaes_ctx* aes_ctx; + ALIGNED_DECL(uint8_t long_state[MEMORY], 16); + ALIGNED_DECL(union cn_slow_hash_state state, 16); + ALIGNED_DECL(uint8_t text[INIT_SIZE_BYTE], 16); + ALIGNED_DECL(uint64_t a[AES_BLOCK_SIZE >> 3], 16); + ALIGNED_DECL(uint64_t b[AES_BLOCK_SIZE >> 3], 16); + ALIGNED_DECL(uint8_t c[AES_BLOCK_SIZE], 16); + oaes_ctx *aes_ctx; }; static_assert(sizeof(struct cn_ctx) == SLOW_HASH_CONTEXT_SIZE, "Invalid structure size"); -static void ExpandAESKey256_sub1(__m128i *tmp1, __m128i *tmp2) -{ - __m128i tmp4; - *tmp2 = _mm_shuffle_epi32(*tmp2, 0xFF); - tmp4 = _mm_slli_si128(*tmp1, 0x04); - *tmp1 = _mm_xor_si128(*tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - *tmp1 = _mm_xor_si128(*tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - *tmp1 = _mm_xor_si128(*tmp1, tmp4); - *tmp1 = _mm_xor_si128(*tmp1, *tmp2); +static void ExpandAESKey256_sub1(__m128i *tmp1, __m128i *tmp2) { + __m128i tmp4; + *tmp2 = _mm_shuffle_epi32(*tmp2, 0xFF); + tmp4 = _mm_slli_si128(*tmp1, 0x04); + *tmp1 = _mm_xor_si128(*tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp1 = _mm_xor_si128(*tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp1 = _mm_xor_si128(*tmp1, tmp4); + *tmp1 = _mm_xor_si128(*tmp1, *tmp2); } -static void ExpandAESKey256_sub2(__m128i *tmp1, __m128i *tmp3) -{ - __m128i tmp2, tmp4; - - tmp4 = _mm_aeskeygenassist_si128(*tmp1, 0x00); - tmp2 = _mm_shuffle_epi32(tmp4, 0xAA); - tmp4 = _mm_slli_si128(*tmp3, 0x04); - *tmp3 = _mm_xor_si128(*tmp3, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - *tmp3 = _mm_xor_si128(*tmp3, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - *tmp3 = _mm_xor_si128(*tmp3, tmp4); - *tmp3 = _mm_xor_si128(*tmp3, tmp2); +static void ExpandAESKey256_sub2(__m128i *tmp1, __m128i *tmp3) { + __m128i tmp2, tmp4; + + tmp4 = _mm_aeskeygenassist_si128(*tmp1, 0x00); + tmp2 = _mm_shuffle_epi32(tmp4, 0xAA); + tmp4 = _mm_slli_si128(*tmp3, 0x04); + *tmp3 = _mm_xor_si128(*tmp3, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp3 = _mm_xor_si128(*tmp3, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp3 = _mm_xor_si128(*tmp3, tmp4); + *tmp3 = _mm_xor_si128(*tmp3, tmp2); } // Special thanks to Intel for helping me // with ExpandAESKey256() and its subroutines -static void ExpandAESKey256(uint8_t *keybuf) -{ - __m128i tmp1, tmp2, tmp3, *keys; - - keys = (__m128i *)keybuf; - - tmp1 = _mm_load_si128((__m128i *)keybuf); - tmp3 = _mm_load_si128((__m128i *)(keybuf+0x10)); - - tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x01); - ExpandAESKey256_sub1(&tmp1, &tmp2); - keys[2] = tmp1; - ExpandAESKey256_sub2(&tmp1, &tmp3); - keys[3] = tmp3; - - tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x02); - ExpandAESKey256_sub1(&tmp1, &tmp2); - keys[4] = tmp1; - ExpandAESKey256_sub2(&tmp1, &tmp3); - keys[5] = tmp3; - - tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x04); - ExpandAESKey256_sub1(&tmp1, &tmp2); - keys[6] = tmp1; - ExpandAESKey256_sub2(&tmp1, &tmp3); - keys[7] = tmp3; - - tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x08); - ExpandAESKey256_sub1(&tmp1, &tmp2); - keys[8] = tmp1; - ExpandAESKey256_sub2(&tmp1, &tmp3); - keys[9] = tmp3; - - tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x10); - ExpandAESKey256_sub1(&tmp1, &tmp2); - keys[10] = tmp1; - ExpandAESKey256_sub2(&tmp1, &tmp3); - keys[11] = tmp3; - - tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x20); - ExpandAESKey256_sub1(&tmp1, &tmp2); - keys[12] = tmp1; - ExpandAESKey256_sub2(&tmp1, &tmp3); - keys[13] = tmp3; - - tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x40); - ExpandAESKey256_sub1(&tmp1, &tmp2); - keys[14] = tmp1; +static void ExpandAESKey256(uint8_t *keybuf) { + __m128i tmp1, tmp2, tmp3, *keys; + + keys = (__m128i *)keybuf; + + tmp1 = _mm_load_si128((__m128i *)keybuf); + tmp3 = _mm_load_si128((__m128i *)(keybuf + 0x10)); + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x01); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[2] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[3] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x02); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[4] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[5] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x04); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[6] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[7] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x08); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[8] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[9] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x10); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[10] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[11] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x20); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[12] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[13] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x40); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[14] = tmp1; } -static void (*const extra_hashes[4])(const void *, size_t, unsigned char *) = -{ - hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein -}; +static void (*const extra_hashes[4])(const void *, size_t, struct CHash *) = { + hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein}; #include "slow-hash_x86.inl" #define AESNI #include "slow-hash_x86.inl" -static int cpu_has_aesni(void){ - int ecx; +static int cpu_has_aesni(void) { + int ecx; #if defined(_MSC_VER) - int cpuinfo[4]; - __cpuid(cpuinfo, 1); - ecx = cpuinfo[2]; + int cpuinfo[4]; + __cpuid(cpuinfo, 1); + ecx = cpuinfo[2]; #else - int a, b, d; - __cpuid(1, a, b, ecx, d); + int a, b, d; + __cpuid(1, a, b, ecx, d); #endif - return (ecx & (1 << 25)) ? 1 : 0; + return (ecx & (1 << 25)) ? 1 : 0; } -static void cn_slow_hash_runtime_aes_check(void * a, const void * b, size_t c, void * d){ - if( cpu_has_aesni() ) - cn_slow_hash_aesni(a, b, c, d); - else - cn_slow_hash_noaesni(a, b, c, d); +static void cn_slow_hash_runtime_aes_check(void *a, const void *b, size_t c, void *d) { + if (cpu_has_aesni()) + cn_slow_hash_aesni(a, b, c, d); + else + cn_slow_hash_noaesni(a, b, c, d); } static void (*cn_slow_hash_fp)(void *, const void *, size_t, void *) = cn_slow_hash_runtime_aes_check; -void cn_slow_hash(void * a, const void * b, size_t c, void * d){ - (*cn_slow_hash_fp)(a, b, c, d); +void cn_slow_hash(void *a, const void *b, size_t c, struct CHash *d) { + (*cn_slow_hash_fp)(a, b, c, d); + // unsigned char da[HASH_SIZE]; + // cn_slow_hash_platform_independent(a, b, c, da); + // if( memcmp(d, da, HASH_SIZE) != 0 ) + // cn_slow_hash_platform_independent(a, b, c, da); } -// If INITIALIZER fails to compile on your platform, just comment out 3 lines below -INITIALIZER(detect_aes) { - cn_slow_hash_fp = cpu_has_aesni() ? &cn_slow_hash_aesni : &cn_slow_hash_noaesni; -} +// If INITIALIZER fails to compile on your platform, just comment out INITIALIZER below +INITIALIZER(detect_aes) { cn_slow_hash_fp = cpu_has_aesni() ? &cn_slow_hash_aesni : &cn_slow_hash_noaesni; } -#endif // !TARGET_OS_IPHONE +#endif // !TARGET_OS_IPHONE diff --git a/src/crypto/slow-hash_x86.inl b/src/crypto/slow-hash_x86.inl index d5aee11a..75d6fe00 100644 --- a/src/crypto/slow-hash_x86.inl +++ b/src/crypto/slow-hash_x86.inl @@ -9,180 +9,172 @@ cn_slow_hash_aesni #else cn_slow_hash_noaesni #endif -(void *restrict context, const void *restrict data, size_t length, void *restrict hash) -{ -#define ctx ((struct cn_ctx *) context) - ALIGNED_DECL(uint8_t ExpandedKey[256], 16); - size_t i; - __m128i *longoutput, *expkey, *xmminput, b_x; - ALIGNED_DECL(uint64_t a[2], 16); - hash_process(&ctx->state.hs, (const uint8_t*) data, length); - - memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE); + (void *restrict context, const void *restrict data, size_t length, void *restrict hash) { +#define ctx ((struct cn_ctx *)context) + ALIGNED_DECL(uint8_t ExpandedKey[256], 16); + size_t i; + __m128i *longoutput, *expkey, *xmminput, b_x; + ALIGNED_DECL(uint64_t a[2], 16); + keccak_into_state((const uint8_t *)data, length, &ctx->state.hs); + + memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE); #if defined(AESNI) - memcpy(ExpandedKey, ctx->state.hs.b, AES_KEY_SIZE); - ExpandAESKey256(ExpandedKey); + memcpy(ExpandedKey, ctx->state.hs.b, AES_KEY_SIZE); + ExpandAESKey256(ExpandedKey); #else - ctx->aes_ctx = oaes_alloc(); - oaes_key_import_data(ctx->aes_ctx, ctx->state.hs.b, AES_KEY_SIZE); - memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len); + ctx->aes_ctx = oaes_alloc(); + oaes_key_import_data(ctx->aes_ctx, ctx->state.hs.b, AES_KEY_SIZE); + memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len); #endif - longoutput = (__m128i *) ctx->long_state; - expkey = (__m128i *) ExpandedKey; - xmminput = (__m128i *) ctx->text; + longoutput = (__m128i *)ctx->long_state; + expkey = (__m128i *)ExpandedKey; + xmminput = (__m128i *)ctx->text; - //for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) - // aesni_parallel_noxor(&ctx->long_state[i], ctx->text, ExpandedKey); + // for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) + // aesni_parallel_noxor(&ctx->long_state[i], ctx->text, ExpandedKey); - for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) - { + for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) { #if defined(AESNI) - for(size_t j = 0; j < 10; j++) - { - xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); - xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); - xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]); - xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]); - xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]); - xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]); - xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]); - xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]); - } + for (size_t j = 0; j < 10; j++) { + xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); + xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); + xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]); + xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]); + xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]); + xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]); + xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]); + xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]); + } #else - aesb_pseudo_round((uint8_t *) &xmminput[0], (uint8_t *) &xmminput[0], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[1], (uint8_t *) &xmminput[1], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[2], (uint8_t *) &xmminput[2], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[3], (uint8_t *) &xmminput[3], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[4], (uint8_t *) &xmminput[4], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[5], (uint8_t *) &xmminput[5], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[6], (uint8_t *) &xmminput[6], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[7], (uint8_t *) &xmminput[7], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *)&xmminput[0], (uint8_t *)&xmminput[0], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[1], (uint8_t *)&xmminput[1], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[2], (uint8_t *)&xmminput[2], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[3], (uint8_t *)&xmminput[3], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[4], (uint8_t *)&xmminput[4], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[5], (uint8_t *)&xmminput[5], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[6], (uint8_t *)&xmminput[6], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[7], (uint8_t *)&xmminput[7], (uint8_t *)expkey); #endif - _mm_store_si128(&(longoutput[(i >> 4)]), xmminput[0]); - _mm_store_si128(&(longoutput[(i >> 4) + 1]), xmminput[1]); - _mm_store_si128(&(longoutput[(i >> 4) + 2]), xmminput[2]); - _mm_store_si128(&(longoutput[(i >> 4) + 3]), xmminput[3]); - _mm_store_si128(&(longoutput[(i >> 4) + 4]), xmminput[4]); - _mm_store_si128(&(longoutput[(i >> 4) + 5]), xmminput[5]); - _mm_store_si128(&(longoutput[(i >> 4) + 6]), xmminput[6]); - _mm_store_si128(&(longoutput[(i >> 4) + 7]), xmminput[7]); - } - - for (i = 0; i < 2; i++) - { - ctx->a[i] = ((uint64_t *)ctx->state.k)[i] ^ ((uint64_t *)ctx->state.k)[i+4]; - ctx->b[i] = ((uint64_t *)ctx->state.k)[i+2] ^ ((uint64_t *)ctx->state.k)[i+6]; - } - - b_x = _mm_load_si128((__m128i *)ctx->b); - a[0] = ctx->a[0]; - a[1] = ctx->a[1]; - - for(i = 0; likely(i < 0x80000); i++) - { - __m128i c_x = _mm_load_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0]); - __m128i a_x = _mm_load_si128((__m128i *)a); - ALIGNED_DECL(uint64_t c[2], 16); - ALIGNED_DECL(uint64_t b[2], 16); - uint64_t *nextblock, *dst; + _mm_store_si128(&(longoutput[(i >> 4)]), xmminput[0]); + _mm_store_si128(&(longoutput[(i >> 4) + 1]), xmminput[1]); + _mm_store_si128(&(longoutput[(i >> 4) + 2]), xmminput[2]); + _mm_store_si128(&(longoutput[(i >> 4) + 3]), xmminput[3]); + _mm_store_si128(&(longoutput[(i >> 4) + 4]), xmminput[4]); + _mm_store_si128(&(longoutput[(i >> 4) + 5]), xmminput[5]); + _mm_store_si128(&(longoutput[(i >> 4) + 6]), xmminput[6]); + _mm_store_si128(&(longoutput[(i >> 4) + 7]), xmminput[7]); + } + + for (i = 0; i < 2; i++) { + ctx->a[i] = ((uint64_t *)ctx->state.k)[i] ^ ((uint64_t *)ctx->state.k)[i + 4]; + ctx->b[i] = ((uint64_t *)ctx->state.k)[i + 2] ^ ((uint64_t *)ctx->state.k)[i + 6]; + } + + b_x = _mm_load_si128((__m128i *)ctx->b); + a[0] = ctx->a[0]; + a[1] = ctx->a[1]; + + for (i = 0; likely(i < 0x80000); i++) { + __m128i c_x = _mm_load_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0]); + __m128i a_x = _mm_load_si128((__m128i *)a); + ALIGNED_DECL(uint64_t c[2], 16); + ALIGNED_DECL(uint64_t b[2], 16); + uint64_t *nextblock, *dst; #if defined(AESNI) - c_x = _mm_aesenc_si128(c_x, a_x); + c_x = _mm_aesenc_si128(c_x, a_x); #else - aesb_single_round((uint8_t *) &c_x, (uint8_t *) &c_x, (uint8_t *) &a_x); + aesb_single_round((uint8_t *)&c_x, (uint8_t *)&c_x, (uint8_t *)&a_x); #endif - _mm_store_si128((__m128i *)c, c_x); - //__builtin_prefetch(&ctx->long_state[c[0] & 0x1FFFF0], 0, 1); - - b_x = _mm_xor_si128(b_x, c_x); - _mm_store_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0], b_x); - - nextblock = (uint64_t *)&ctx->long_state[c[0] & 0x1FFFF0]; - b[0] = nextblock[0]; - b[1] = nextblock[1]; - - { - uint64_t hi, lo; - // hi,lo = 64bit x 64bit multiply of c[0] and b[0] - -//#if defined(__GNUC__) && defined(__x86_64__) -// __asm__("mulq %3\n\t" -// : "=d" (hi), -// "=a" (lo) -// : "%a" (c[0]), -// "rm" (b[0]) -// : "cc" ); -//#else - lo = mul128(c[0], b[0], &hi); -//#endif - a[0] += hi; - a[1] += lo; - } - dst = (uint64_t *) &ctx->long_state[c[0] & 0x1FFFF0]; - dst[0] = a[0]; - dst[1] = a[1]; - - a[0] ^= b[0]; - a[1] ^= b[1]; - b_x = c_x; - //__builtin_prefetch(&ctx->long_state[a[0] & 0x1FFFF0], 0, 3); - } - - memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE); + _mm_store_si128((__m128i *)c, c_x); + //__builtin_prefetch(&ctx->long_state[c[0] & 0x1FFFF0], 0, 1); + + b_x = _mm_xor_si128(b_x, c_x); + _mm_store_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0], b_x); + + nextblock = (uint64_t *)&ctx->long_state[c[0] & 0x1FFFF0]; + b[0] = nextblock[0]; + b[1] = nextblock[1]; + + { + uint64_t hi, lo; + // hi,lo = 64bit x 64bit multiply of c[0] and b[0] + + //#if defined(__GNUC__) && defined(__x86_64__) + // __asm__("mulq %3\n\t" + // : "=d" (hi), + // "=a" (lo) + // : "%a" (c[0]), + // "rm" (b[0]) + // : "cc" ); + //#else + lo = mul128(c[0], b[0], &hi); + //#endif + a[0] += hi; + a[1] += lo; + } + dst = (uint64_t *)&ctx->long_state[c[0] & 0x1FFFF0]; + dst[0] = a[0]; + dst[1] = a[1]; + + a[0] ^= b[0]; + a[1] ^= b[1]; + b_x = c_x; + //__builtin_prefetch(&ctx->long_state[a[0] & 0x1FFFF0], 0, 3); + } + + memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE); #if defined(AESNI) - memcpy(ExpandedKey, &ctx->state.hs.b[32], AES_KEY_SIZE); - ExpandAESKey256(ExpandedKey); + memcpy(ExpandedKey, &ctx->state.hs.b[32], AES_KEY_SIZE); + ExpandAESKey256(ExpandedKey); #else - oaes_key_import_data(ctx->aes_ctx, &ctx->state.hs.b[32], AES_KEY_SIZE); - memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len); + oaes_key_import_data(ctx->aes_ctx, &ctx->state.hs.b[32], AES_KEY_SIZE); + memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len); #endif - //for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) - // aesni_parallel_xor(&ctx->text, ExpandedKey, &ctx->long_state[i]); + // for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) + // aesni_parallel_xor(&ctx->text, ExpandedKey, &ctx->long_state[i]); - for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) - { - xmminput[0] = _mm_xor_si128(longoutput[(i >> 4)], xmminput[0]); - xmminput[1] = _mm_xor_si128(longoutput[(i >> 4) + 1], xmminput[1]); - xmminput[2] = _mm_xor_si128(longoutput[(i >> 4) + 2], xmminput[2]); - xmminput[3] = _mm_xor_si128(longoutput[(i >> 4) + 3], xmminput[3]); - xmminput[4] = _mm_xor_si128(longoutput[(i >> 4) + 4], xmminput[4]); - xmminput[5] = _mm_xor_si128(longoutput[(i >> 4) + 5], xmminput[5]); - xmminput[6] = _mm_xor_si128(longoutput[(i >> 4) + 6], xmminput[6]); - xmminput[7] = _mm_xor_si128(longoutput[(i >> 4) + 7], xmminput[7]); + for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) { + xmminput[0] = _mm_xor_si128(longoutput[(i >> 4)], xmminput[0]); + xmminput[1] = _mm_xor_si128(longoutput[(i >> 4) + 1], xmminput[1]); + xmminput[2] = _mm_xor_si128(longoutput[(i >> 4) + 2], xmminput[2]); + xmminput[3] = _mm_xor_si128(longoutput[(i >> 4) + 3], xmminput[3]); + xmminput[4] = _mm_xor_si128(longoutput[(i >> 4) + 4], xmminput[4]); + xmminput[5] = _mm_xor_si128(longoutput[(i >> 4) + 5], xmminput[5]); + xmminput[6] = _mm_xor_si128(longoutput[(i >> 4) + 6], xmminput[6]); + xmminput[7] = _mm_xor_si128(longoutput[(i >> 4) + 7], xmminput[7]); #if defined(AESNI) - for(size_t j = 0; j < 10; j++) - { - xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); - xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); - xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]); - xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]); - xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]); - xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]); - xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]); - xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]); - } + for (size_t j = 0; j < 10; j++) { + xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); + xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); + xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]); + xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]); + xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]); + xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]); + xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]); + xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]); + } #else - aesb_pseudo_round((uint8_t *) &xmminput[0], (uint8_t *) &xmminput[0], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[1], (uint8_t *) &xmminput[1], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[2], (uint8_t *) &xmminput[2], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[3], (uint8_t *) &xmminput[3], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[4], (uint8_t *) &xmminput[4], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[5], (uint8_t *) &xmminput[5], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[6], (uint8_t *) &xmminput[6], (uint8_t *) expkey); - aesb_pseudo_round((uint8_t *) &xmminput[7], (uint8_t *) &xmminput[7], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *)&xmminput[0], (uint8_t *)&xmminput[0], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[1], (uint8_t *)&xmminput[1], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[2], (uint8_t *)&xmminput[2], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[3], (uint8_t *)&xmminput[3], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[4], (uint8_t *)&xmminput[4], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[5], (uint8_t *)&xmminput[5], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[6], (uint8_t *)&xmminput[6], (uint8_t *)expkey); + aesb_pseudo_round((uint8_t *)&xmminput[7], (uint8_t *)&xmminput[7], (uint8_t *)expkey); #endif - - } + } #if !defined(AESNI) - oaes_free((OAES_CTX **) &ctx->aes_ctx); + oaes_free((OAES_CTX **)&ctx->aes_ctx); #endif - memcpy(ctx->state.init, ctx->text, INIT_SIZE_BYTE); - hash_permutation(&ctx->state.hs); - extra_hashes[ctx->state.hs.b[0] & 3](&ctx->state, 200, hash); + memcpy(ctx->state.init, ctx->text, INIT_SIZE_BYTE); + keccak_permutation(&ctx->state.hs); + extra_hashes[ctx->state.hs.b[0] & 3](&ctx->state, 200, hash); } diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 5a26d640..d544f5db 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,6 +1,7 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. // Licensed under the GNU Lesser General Public License. See LICENSE for details. +#include "tree-hash.h" #include #include #include @@ -11,106 +12,109 @@ #include #endif -#include "hash-ops.h" - -void tree_hash(const unsigned char (*hashes)[HASH_SIZE], size_t count, unsigned char *root_hash) { - assert(count > 0); - if (count == 1) { - memcpy(root_hash, hashes, HASH_SIZE); - } else if (count == 2) { - cn_fast_hash(hashes, 2 * HASH_SIZE, root_hash); - } else { - size_t i, j; - size_t cnt = count - 1; - unsigned char (*ints)[HASH_SIZE]; - for (i = 1; i < 8 * sizeof(size_t); i <<= 1) { - cnt |= cnt >> i; - } - cnt &= ~(cnt >> 1); - ints = alloca(cnt * HASH_SIZE); - memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); - for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { - cn_fast_hash(hashes[i], 2 * HASH_SIZE, ints[j]); - } - assert(i == count); - while (cnt > 2) { - cnt >>= 1; - for (i = 0, j = 0; j < cnt; i += 2, ++j) { - cn_fast_hash(ints[i], 2 * HASH_SIZE, ints[j]); - } - } - cn_fast_hash(ints[0], 2 * HASH_SIZE, root_hash); - } +void tree_hash(const struct CHash hashes[], size_t count, struct CHash *root_hash) { + assert(count > 0); + if (count == 1) { + *root_hash = *hashes; + return; + } + if (count == 2) { + cn_fast_hash(hashes, 2 * sizeof(struct CHash), root_hash); + return; + } + size_t i, j; + size_t cnt = 1; + while (cnt * 2 < count) + cnt *= 2; + // size_t cnt = count - 1; + // for (i = 1; i < 8 * sizeof(size_t); i <<= 1) { + // cnt |= cnt >> i; + // } + // cnt &= ~(cnt >> 1); + struct CHash *ints = (struct CHash *)alloca(cnt * sizeof(struct CHash)); + memcpy(ints, hashes, (2 * cnt - count) * sizeof(struct CHash)); + for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { + cn_fast_hash(hashes + i, 2 * sizeof(struct CHash), ints + j); + } + assert(i == count); + while (cnt > 2) { + cnt /= 2; + for (j = 0; j < cnt; ++j) { + cn_fast_hash(ints + 2 * j, 2 * sizeof(struct CHash), ints + j); + } + } + cn_fast_hash(ints, 2 * sizeof(struct CHash), root_hash); } -size_t tree_depth(size_t count) { - size_t i; - size_t depth = 0; - assert(count > 0); - for (i = sizeof(size_t) << 2; i > 0; i >>= 1) { - if (count >> i > 0) { - count >>= i; - depth += i; - } - } - return depth; +size_t coinbase_tree_depth(size_t count) { + assert(count > 0); + size_t depth = 0; + while ((1ULL << (depth + 1)) <= count) + depth += 1; + // for (i = sizeof(size_t) << 2; i > 0; i >>= 1) { + // if (count >> i > 0) { + // count >>= i; + // depth += i; + // } + // } + return depth; } -void tree_branch(const unsigned char (*hashes)[HASH_SIZE], size_t count, unsigned char (*branch)[HASH_SIZE]) { - size_t i, j; - size_t cnt = 1; - size_t depth = 0; - unsigned char (*ints)[HASH_SIZE]; - assert(count > 0); - for (i = sizeof(size_t) << 2; i > 0; i >>= 1) { - if (cnt << i <= count) { - cnt <<= i; - depth += i; - } - } - assert(cnt == 1ULL << depth); - assert(depth == tree_depth(count)); - ints = alloca((cnt - 1) * HASH_SIZE); - memcpy(ints, hashes + 1, (2 * cnt - count - 1) * HASH_SIZE); - for (i = 2 * cnt - count, j = 2 * cnt - count - 1; j < cnt - 1; i += 2, ++j) { - cn_fast_hash(hashes[i], 2 * HASH_SIZE, ints[j]); - } - assert(i == count); - while (depth > 0) { - assert(cnt == 1ULL << depth); - cnt >>= 1; - --depth; - memcpy(branch[depth], ints[0], HASH_SIZE); - for (i = 1, j = 0; j < cnt - 1; i += 2, ++j) { - cn_fast_hash(ints[i], 2 * HASH_SIZE, ints[j]); - } - } +void coinbase_tree_branch(const struct CHash hashes[], size_t count, struct CHash branch[]) { + assert(count > 0); + size_t i, j; + size_t depth = coinbase_tree_depth(count); + size_t cnt = 1ULL << depth; + // for (i = sizeof(size_t) << 2; i > 0; i >>= 1) { + // if (cnt << i <= count) { + // cnt <<= i; + // depth += i; + // } + // } + // assert(cnt == 1ULL << depth); + // assert(depth == coinbase_tree_depth(count)); + struct CHash *ints = (struct CHash *)alloca((cnt - 1) * sizeof(struct CHash)); + memcpy(ints, hashes + 1, (2 * cnt - count - 1) * sizeof(struct CHash)); + for (i = 2 * cnt - count, j = 2 * cnt - count - 1; j < cnt - 1; i += 2, ++j) { + cn_fast_hash(hashes + i, 2 * sizeof(struct CHash), ints + j); + } + assert(i == count); + while (depth > 0) { + assert(cnt == 1U << depth); + cnt >>= 1; + --depth; + branch[depth] = ints[0]; + for (i = 1, j = 0; j < cnt - 1; i += 2, ++j) { + cn_fast_hash(ints + i, 2 * sizeof(struct CHash), ints + j); + } + } } -void tree_hash_from_branch(const unsigned char (*branch)[HASH_SIZE], size_t depth, const unsigned char *leaf, const void *path, unsigned char *root_hash) { - if (depth == 0) { - memcpy(root_hash, leaf, HASH_SIZE); - } else { - unsigned char buffer[2][HASH_SIZE]; - int from_leaf = 1; - unsigned char *leaf_path, *branch_path; - while (depth > 0) { - --depth; - if (path && (((const char *) path)[depth >> 3] & (1 << (depth & 7))) != 0) { - leaf_path = buffer[1]; - branch_path = buffer[0]; - } else { - leaf_path = buffer[0]; - branch_path = buffer[1]; - } - if (from_leaf) { - memcpy(leaf_path, leaf, HASH_SIZE); - from_leaf = 0; - } else { - cn_fast_hash(buffer, 2 * HASH_SIZE, leaf_path); - } - memcpy(branch_path, branch[depth], HASH_SIZE); - } - cn_fast_hash(buffer, 2 * HASH_SIZE, root_hash); - } +void tree_hash_from_branch(const struct CHash branch[], size_t depth, const struct CHash *leaf, + const struct CHash *path, struct CHash *root_hash) { + if (depth == 0) { + *root_hash = *leaf; + return; + } + struct CHash buffer[2]; + int from_leaf = 1; + struct CHash *leaf_path, *branch_path; + while (depth > 0) { + --depth; + if (path && (path->data[depth >> 3] & (1 << (depth & 7))) != 0) { + leaf_path = buffer + 1; + branch_path = buffer + 0; + } else { + leaf_path = buffer + 0; + branch_path = buffer + 1; + } + if (from_leaf) { + *leaf_path = *leaf; + from_leaf = 0; + } else { + cn_fast_hash(buffer, 2 * sizeof(struct CHash), leaf_path); + } + *branch_path = branch[depth]; + } + cn_fast_hash(buffer, 2 * sizeof(struct CHash), root_hash); } diff --git a/src/crypto/tree-hash.h b/src/crypto/tree-hash.h new file mode 100644 index 00000000..e68ede62 --- /dev/null +++ b/src/crypto/tree-hash.h @@ -0,0 +1,23 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#include "hash.h" + +#if defined(__cplusplus) +namespace crypto { +extern "C" { +#endif + +void tree_hash(const struct CHash hashes[], size_t count, struct CHash *root_hash); +size_t coinbase_tree_depth(size_t count); +void coinbase_tree_branch(const struct CHash hashes[], size_t count, struct CHash branch[]); +void tree_hash_from_branch(const struct CHash branch[], size_t depth, const struct CHash *leaf, + const struct CHash *path, struct CHash *root_hash); + +#if defined(__cplusplus) +} +} + +#endif diff --git a/src/crypto/types.hpp b/src/crypto/types.hpp index 7235609c..9f5b7221 100644 --- a/src/crypto/types.hpp +++ b/src/crypto/types.hpp @@ -7,36 +7,37 @@ #include #include #include -#include "c_types.h" +#include "bernstein/c_types.h" #include "crypto-util.h" +#include "hash.h" namespace crypto { #pragma pack(push, 1) -struct Hash { - uint8_t data[32]{}; +struct Hash : public CHash { + constexpr Hash() : CHash{} {} }; struct PublicKey : public EllipticCurvePoint { - PublicKey() : EllipticCurvePoint{} {} + constexpr PublicKey() : EllipticCurvePoint{} {} }; struct SecretKey : public EllipticCurveScalar { - SecretKey() : EllipticCurveScalar{} {} + constexpr SecretKey() : EllipticCurveScalar{} {} ~SecretKey() { sodium_memzero(data, sizeof(data)); } }; struct KeyDerivation : public EllipticCurvePoint { - KeyDerivation() : EllipticCurvePoint{} {} + constexpr KeyDerivation() : EllipticCurvePoint{} {} }; struct KeyImage : public EllipticCurvePoint { - KeyImage() : EllipticCurvePoint{} {} + constexpr KeyImage() : EllipticCurvePoint{} {} }; struct Signature { EllipticCurveScalar c, r; - Signature() : c{}, r{} {} + constexpr Signature() : c{}, r{} {} }; #pragma pack(pop) @@ -63,10 +64,13 @@ CRYPTO_MAKE_COMPARABLE(crypto, Hash, std::memcmp) CRYPTO_MAKE_HASHABLE(crypto, PublicKey) CRYPTO_MAKE_COMPARABLE(crypto, PublicKey, std::memcmp) -CRYPTO_MAKE_HASHABLE(crypto, KeyImage) -CRYPTO_MAKE_COMPARABLE(crypto, KeyImage, std::memcmp) - CRYPTO_MAKE_HASHABLE(crypto, SecretKey) CRYPTO_MAKE_COMPARABLE(crypto, SecretKey, crypto::sodium_compare) +CRYPTO_MAKE_HASHABLE(crypto, KeyDerivation) +CRYPTO_MAKE_COMPARABLE(crypto, KeyDerivation, std::memcmp) + +CRYPTO_MAKE_HASHABLE(crypto, KeyImage) +CRYPTO_MAKE_COMPARABLE(crypto, KeyImage, std::memcmp) + CRYPTO_MAKE_COMPARABLE(crypto, Signature, std::memcmp) diff --git a/src/http/Agent.cpp b/src/http/Agent.cpp index 3c01bebe..363c3859 100644 --- a/src/http/Agent.cpp +++ b/src/http/Agent.cpp @@ -7,6 +7,7 @@ #include #include #include "common/Invariant.hpp" +#include "common/exception.hpp" using namespace http; @@ -159,13 +160,20 @@ void Agent::on_client_response() { Request::R_handler r_handler = std::move(was_sent_request->r_handler); Request::E_handler e_handler = std::move(was_sent_request->e_handler); try { - r_handler(std::move(response)); + try { + r_handler(std::move(response)); + } catch (const std::exception &ex) { + std::cout << " Parsing received submit leads to throw/catch what=" << common::what(ex) + << std::endl; + e_handler(common::what(ex)); + } catch (...) { + std::cout << " Parsing received submit leads to throw/catch" << std::endl; + e_handler("catch ..."); + } } catch (const std::exception &ex) { - std::cout << " Parsing received submit leads to throw/catch what=" << ex.what() << std::endl; - e_handler(ex.what()); + std::cout << " Error handler leads to throw/catch what=" << common::what(ex) << std::endl; } catch (...) { - std::cout << " Parsing received submit leads to throw/catch" << std::endl; - e_handler("catch ..."); + std::cout << " Error handler leads to throw/catch" << std::endl; } } } diff --git a/src/http/BinaryRpc.hpp b/src/http/BinaryRpc.hpp new file mode 100644 index 00000000..a5ee0095 --- /dev/null +++ b/src/http/BinaryRpc.hpp @@ -0,0 +1,98 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#include "JsonRpc.hpp" +#include "seria/BinaryInputStream.hpp" +#include "seria/BinaryOutputStream.hpp" + +namespace bytecoin { +namespace json_rpc { + +// Binary Json Rpc: +// Request: normal json request with params set to {}, followed by 0 char, followed by binary params +// No Error Response: normal json response with result set to {}, followed by 0 char, followed by binary result +// Error Response: normal error json response, followed by 0 char + +template // , typename ErrorType +bool parse_binary_response( + const std::string &body, ResultType &result, Error &error, OptionalJsonValue *jid = nullptr) { + size_t sep = body.find(char(0)); + if (sep == std::string::npos) + throw std::runtime_error("binary response contains no 0-character separator"); + Response json_resp(body.substr(0, sep)); + if (jid) + *jid = json_resp.get_id(); + if (json_resp.get_error(error)) + return false; + common::MemoryInputStream body_stream(body.data() + sep + 1, body.size() - sep - 1); + seria::BinaryInputStream ba(body_stream); + ser(result, ba); + return true; +} + +template +std::string create_binary_request_body( + const std::string &method, const ParamsType ¶ms, const OptionalJsonValue &jid = common::JsonValue(nullptr)) { + common::JsonValue ps_req(common::JsonValue::OBJECT); + ps_req.set("jsonrpc", std::string("2.0")); + ps_req.set("method", method); + ps_req.set("params", common::JsonValue(common::JsonValue::OBJECT)); + if (jid) + ps_req.set("id", std::move(jid.get())); + std::string json_body = ps_req.to_string(); + json_body += char(0); + common::StringOutputStream str(json_body); // continue writing + seria::BinaryOutputStream ba(str); + ser(const_cast(params), ba); + return json_body; +} + +template +std::string create_binary_response_body(const ResultType &result, const common::JsonValue &jid) { + common::JsonValue ps_req(common::JsonValue::OBJECT); + ps_req.set("jsonrpc", std::string("2.0")); + ps_req.set("id", jid); + ps_req.set("result", common::JsonValue(common::JsonValue::OBJECT)); + std::string json_body = ps_req.to_string(); + json_body += char(0); + common::StringOutputStream str(json_body); // continue writing + seria::BinaryOutputStream ba(str); + ser(const_cast(result), ba); + return json_body; +} + +// template +std::string create_binary_response_error_body(const Error &error, const common::JsonValue &jid); + +template +bool invoke_binary_method( + Agent *agent, common::IInputStream &body_stream, Request &&binary_req, std::string &raw_response, Handler handler) { + ParamsType params{}; + ResultType result{}; + + seria::BinaryInputStream ba(body_stream); + ser(params, ba); + + common::JsonValue jid = binary_req.get_id().get(); + http::RequestData empty_http_req; // Do not move into handler call, will not compile on MSVC 2017 + bool success = handler(agent, std::move(empty_http_req), std::move(binary_req), std::move(params), result); + + if (success) + raw_response = create_binary_response_body(result, jid); + return success; +} + +template +std::function make_binary_member_method( + bool (Owner::*handler)(Agent *, http::RequestData &&, Request &&, ParamsType &&, ResultType &)) { + return [handler]( + Owner *obj, Agent *agent, common::IInputStream &body_stream, Request &&req, std::string &res) -> bool { + return invoke_binary_method(agent, body_stream, std::move(req), res, + std::bind(handler, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5)); + }; +} +} +} diff --git a/src/http/JsonRpc.cpp b/src/http/JsonRpc.cpp index 71d1c4fb..e184c830 100644 --- a/src/http/JsonRpc.cpp +++ b/src/http/JsonRpc.cpp @@ -29,6 +29,13 @@ std::string Error::get_message(int code) { Error::Error(int c, const std::string &msg) : code(c), message(msg) {} +void Error::seria_data(seria::ISeria &s) { + s.begin_object(); + seria_data_members(s); + s.end_object(); +} +void Error::seria_data_members(seria::ISeria &s) {} + void make_generic_error_reponse(common::JsonValue &resp, const std::string &what, int error_code) { common::JsonValue error(common::JsonValue::OBJECT); @@ -39,5 +46,126 @@ void make_generic_error_reponse(common::JsonValue &resp, const std::string &what resp.insert("error", error); } + +void Request::parse(const std::string &request_body, bool allow_empty_id) { + common::JsonValue ps_req; + try { + ps_req = common::JsonValue::from_string(request_body); + } catch (const std::exception &ex) { + throw Error(PARSE_ERROR, common::what(ex)); + } + if (!ps_req.is_object()) + throw Error(INVALID_REQUEST, "Request is not a json object"); + if (!ps_req.contains("jsonrpc")) + throw Error(INVALID_REQUEST, "Request must include jsonrpc key"); + auto &j = ps_req("jsonrpc"); + if (!j.is_string() || j.get_string() != "2.0") + throw Error(INVALID_REQUEST, "jsonrpc value must be exactly \"2.0\""); + if (!ps_req.contains("method")) + throw Error(INVALID_REQUEST, "Request must include method key"); + auto &m = ps_req("method"); + if (!m.is_string()) + throw Error(INVALID_REQUEST, "method value must be string"); + method = m.get_string(); + if (ps_req.contains("id")) { + auto &p = ps_req("id"); + if (!p.is_string() && !p.is_integer() && !p.is_nil()) // Json RPC spec 4.2 + throw Error(INVALID_REQUEST, "id value must be an integer number, string or null"); + jid = std::move(p); + } else { + if (!allow_empty_id) + throw Error(INVALID_REQUEST, "id value is REQUIRED"); + } + if (ps_req.contains("params")) { + auto &p = ps_req("params"); + if (!p.is_object() && !p.is_array()) // Json RPC spec 4.2 + throw Error(INVALID_REQUEST, "params value must be an object or array"); + params = std::move(p); + } +} + +void Response::parse(const std::string &response_body) { + common::JsonValue ps_req; + try { + ps_req = common::JsonValue::from_string(response_body); + } catch (const std::exception &ex) { + throw Error(PARSE_ERROR, common::what(ex)); + } + if (!ps_req.is_object()) + throw Error(INVALID_REQUEST, "Response is not a json object"); + if (!ps_req.contains("jsonrpc")) + throw Error(INVALID_REQUEST, "Response must include jsonrpc key"); + auto &j = ps_req("jsonrpc"); + if (!j.is_string() || j.get_string() != "2.0") + throw Error(INVALID_REQUEST, "jsonrpc value must be exactly \"2.0\""); + if (!ps_req.contains("id")) + throw Error(INVALID_REQUEST, "id value is REQUIRED"); + auto &p = ps_req("id"); + if (!p.is_string() && !p.is_integer() && !p.is_nil()) // Json RPC spec 4.2 + throw Error(INVALID_REQUEST, "id value must be an integer number, string or null"); + jid = std::move(p); + if (ps_req.contains("result")) + result = std::move(ps_req("result")); + if (ps_req.contains("error")) { + auto &e = ps_req("error"); + if (!e.is_object()) + throw Error(INVALID_REQUEST, "error value must be an object"); + error = std::move(e); + } + if (result && error) + throw Error(INVALID_REQUEST, "Response cannot contain both error and result"); + if (!result && !error) + throw Error(INVALID_REQUEST, "Response must contain either error or result"); +} + +std::string create_error_response_body(const Error &error, const common::JsonValue &jid) { + common::JsonValue ps_req(common::JsonValue::OBJECT); + ps_req.set("jsonrpc", std::string("2.0")); + ps_req.set("id", jid); + ps_req.set("error", seria::to_json_value(error)); + return ps_req.to_string(); +} +std::string create_binary_response_error_body(const Error &error, const common::JsonValue &jid) { + // static_assert(std::is_base_of::value, "ErrorType must be an json_rpc::Error + // descendant"); + common::JsonValue ps_req(common::JsonValue::OBJECT); + ps_req.set("jsonrpc", std::string("2.0")); + ps_req.set("id", jid); + ps_req.set("error", seria::to_json_value(error)); + std::string json_body = ps_req.to_string(); + json_body += char(0); + return json_body; +} + +/*std::string Response::get_body() { + common::JsonValue ps_req(common::JsonValue::OBJECT); + ps_req.set("jsonrpc", std::string("2.0")); + invariant(bool(result) ^ bool(error), ""); + if (error) + ps_req.set("error", std::move(error.get())); + else + ps_req.set("result", std::move(result.get())); + if (jid) + ps_req.set("id", std::move(jid.get())); + common::JsonValue test_value = common::JsonValue::from_string(result_body); + auto str1 = test_value.to_string(); + auto str2 = ps_req.to_string(); + if(str1 != str2){ + std::cout << str1 << std::endl; + std::cout << str2 << std::endl; + } + return str2; +} +void Response::prepare_result_body(){ + result_body = "{"; + result_body += "\"id\":" + jid.to_string() + ","; + result_body += "\"jsonrpc\":\"2.0\",\"result\":"; +}*/ +std::string prepare_result_prefix(const common::JsonValue &jid) { + std::string result = "{"; + result += "\"id\":" + jid.to_string() + ","; + result += "\"jsonrpc\":\"2.0\",\"result\":"; + return result; +} } } diff --git a/src/http/JsonRpc.hpp b/src/http/JsonRpc.hpp index d28177f4..8a6e1b58 100644 --- a/src/http/JsonRpc.hpp +++ b/src/http/JsonRpc.hpp @@ -8,8 +8,9 @@ #include #include +#include "common/Invariant.hpp" #include "common/JsonValue.hpp" -#include "seria/JsonInputValue.hpp" +#include "seria/JsonInputStream.hpp" #include "seria/JsonOutputStream.hpp" #include "types.hpp" @@ -33,215 +34,142 @@ class Error : public std::exception { int code; std::string message; -}; -} -} -namespace seria { -inline void ser_members(bytecoin::json_rpc::Error &v, ISeria &s) { - seria_kv("code", v.code, s); - seria_kv("message", v.message, s); -} -} - -namespace bytecoin { -namespace json_rpc { + virtual void seria_data(seria::ISeria &s); + virtual void seria_data_members(seria::ISeria &s); +}; typedef boost::optional OptionalJsonValue; class Request { - bool parse_request(const std::string &request_body) { - common::JsonValue ps_req; - try { - ps_req = common::JsonValue::from_string(request_body); - } catch (std::exception &) { - throw Error(PARSE_ERROR); - } - if (!ps_req.is_object() || !ps_req.contains("method")) - throw Error(INVALID_REQUEST); - method = ps_req("method").get_string(); - if (ps_req.contains("id")) - id = ps_req("id"); - if (ps_req.contains("params")) { - auto p = ps_req("params"); - if (!p.is_object() && !p.is_array()) // Json RPC spec 4.2 - return false; - params = p; - } - return true; - } - public: Request() {} - explicit Request(const std::string &request_body) { parse_request(request_body); } - template - void set_params(const T &v) { - params = seria::to_json_value(v); + explicit Request(const std::string &request_body, bool allow_empty_id = false) { + parse(request_body, allow_empty_id); } template void load_params(T &v) const { if (params) seria::from_json_value(v, params.get()); } - - void set_method(const std::string &m) { method = m; } const std::string &get_method() const { return method; } - - void set_id(const OptionalJsonValue &sid) { id = sid; } - const OptionalJsonValue &get_id() const { return id; } - - std::string get_body() { - common::JsonValue ps_req(common::JsonValue::OBJECT); - ps_req.set("jsonrpc", std::string("2.0")); - ps_req.set("method", method); - if (params) - ps_req.set("params", params.get()); - if (id) - ps_req.set("id", id.get()); - return ps_req.to_string(); - } + const OptionalJsonValue &get_id() const { return jid; } private: + void parse(const std::string &request_body, bool allow_empty_id); + OptionalJsonValue params; - OptionalJsonValue id; + OptionalJsonValue jid; std::string method; }; class Response { - void parse(const std::string &response_body) { - common::JsonValue ps_req; - try { - ps_req = common::JsonValue::from_string(response_body); - } catch (std::exception &) { - throw Error(PARSE_ERROR); - } - if (!ps_req.is_object()) - throw Error(INVALID_REQUEST); - if (ps_req.contains("id")) - id = ps_req("id"); - if (ps_req.contains("result")) - result = ps_req("result"); - if (ps_req.contains("error")) - error = ps_req("error"); - } - public: - Response() {} explicit Response(const std::string &response_body) { parse(response_body); } - void set_id(const OptionalJsonValue &sid) { id = sid; } - const OptionalJsonValue &get_id() const { return id; } - - template - void set_error(const T &err) { - static_assert(std::is_base_of::value, "T must be an Error descendant"); - error = seria::to_json_value(err); - } + const common::JsonValue &get_id() const { return jid; } template bool get_error(T &err) const { - static_assert(std::is_base_of::value, "T must be an Error descendant"); + static_assert(std::is_base_of::value, "T must be an json_rpc::Error descendant"); if (!error) return false; seria::from_json_value(err, error.get()); return true; } - - std::string get_body() { - common::JsonValue ps_req(common::JsonValue::OBJECT); - ps_req.set("jsonrpc", std::string("2.0")); - if (error) - ps_req.set("error", error.get()); - else - ps_req.set("result", result); - if (id) - ps_req.set("id", id.get()); - return ps_req.to_string(); - } - - template - void set_result(const T &v) { - result = seria::to_json_value(v); - } - template void get_result(T &v) const { - seria::from_json_value(v, result); + invariant(result, ""); + seria::from_json_value(v, result.get()); } private: - common::JsonValue result; - OptionalJsonValue id; + void parse(const std::string &response_body); + + OptionalJsonValue result; + std::string result_body; + common::JsonValue jid; OptionalJsonValue error; }; -template -bool invoke_method(Agent *agent, RawRequest &&raw_request, Request &&js_req, Response &js_res, Handler handler) { - RequestType req{}; - ResponseType res{}; - js_req.load_params(req); - - bool result = handler(agent, std::move(raw_request), std::move(js_req), std::move(req), res); - - if (result) - js_res.set_result(res); - return result; -} - -template -std::function make_member_method( - bool (Owner::*handler)(Agent *, RawRequest &&, json_rpc::Request &&, Params &&, Result &)) { - return [handler](Owner *obj, Agent *agent, RawRequest &&raw_request, Request &&req, Response &res) -> bool { - return json_rpc::invoke_method(agent, std::move(raw_request), std::move(req), - res, std::bind(handler, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, - std::placeholders::_4, std::placeholders::_5)); - }; -} +std::string prepare_result_prefix(const common::JsonValue &jid); // Always POST HTTP/1.1 template http::RequestData create_request(const std::string &uri, const std::string &method, const ParamsType ¶ms, - const OptionalJsonValue &id = OptionalJsonValue{}) { - Request json_send_raw_req; - json_send_raw_req.set_method(method); - json_send_raw_req.set_params(params); - json_send_raw_req.set_id(id); - http::RequestData req_header; - req_header.r.set_firstline("POST", uri, 1, 1); - req_header.set_body(json_send_raw_req.get_body()); - return req_header; + const OptionalJsonValue &jid = common::JsonValue(nullptr)) { + common::JsonValue ps_req(common::JsonValue::OBJECT); + ps_req.set("jsonrpc", std::string("2.0")); + ps_req.set("method", method); + ps_req.set("params", seria::to_json_value(params)); + if (jid) + ps_req.set("id", std::move(jid.get())); + http::RequestData http_request; + http_request.r.set_firstline("POST", uri, 1, 1); + http_request.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); + http_request.set_body(ps_req.to_string()); + return http_request; } -template -http::ResponseData create_response( - const http::RequestData &request, const ResponseType &response, const OptionalJsonValue &id = OptionalJsonValue{}) { - json_rpc::Response last_json_resp; - last_json_resp.set_id(id); - last_json_resp.set_result(response); - http::ResponseData last_http_response(request.r); - last_http_response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); - last_http_response.set_body(last_json_resp.get_body()); - last_http_response.r.status = 200; - return last_http_response; -} -inline http::ResponseData create_error_response( - const http::RequestData &request, const Error &response, const OptionalJsonValue &id = OptionalJsonValue{}) { - json_rpc::Response last_json_resp; - last_json_resp.set_id(id); - last_json_resp.set_error(response); - http::ResponseData last_http_response(request.r); - last_http_response.r.headers.push_back({"Content-Type", "application/json; charset=utf-8"}); - last_http_response.set_body(last_json_resp.get_body()); - last_http_response.r.status = 200; - return last_http_response; +template +std::string create_response_body(const ResultType &result, const common::JsonValue &jid) { + std::string result_body = prepare_result_prefix(jid); + seria::JsonOutputStreamText s(result_body); + ser(const_cast(result), s); + result_body += "}"; + return result_body; + // common::JsonValue ps_req(common::JsonValue::OBJECT); + // ps_req.set("jsonrpc", std::string("2.0")); + // ps_req.set("id", jid); + // ps_req.set("result", seria::to_json_value(result)); + // return ps_req.to_string(); } +std::string create_error_response_body(const Error &error, const common::JsonValue &jid); -template -void parse_response(const std::string &body, ResponseType &response, OptionalJsonValue *jid = nullptr) { +template //, typename ErrorType +bool parse_response(const std::string &body, ResultType &result, Error &error, OptionalJsonValue *jid = nullptr) { json_rpc::Response json_resp(body); if (jid) *jid = json_resp.get_id(); - response = ResponseType(); - json_resp.get_result(response); + if (json_resp.get_error(error)) + return false; + result = ResultType{}; + json_resp.get_result(result); + return true; } + +template +bool invoke_method( + Agent *agent, http::RequestData &&http_request, Request &&json_req, std::string &raw_response, Handler handler) { + ParamsType params{}; + ResultType result{}; + json_req.load_params(params); + + common::JsonValue jid = json_req.get_id().get(); + bool success = handler(agent, std::move(http_request), std::move(json_req), std::move(params), result); + + if (success) + raw_response = create_response_body(result, jid); + return success; +} + +template +std::function make_member_method( + bool (Owner::*handler)(Agent *, http::RequestData &&, json_rpc::Request &&, ParamsType &&, ResultType &)) { + return [handler](Owner *obj, Agent *agent, http::RequestData &&raw_request, Request &&req, + std::string &raw_response) -> bool { + return json_rpc::invoke_method(agent, std::move(raw_request), std::move(req), + raw_response, std::bind(handler, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5)); + }; +} +} +} + +namespace seria { +inline void ser_members(bytecoin::json_rpc::Error &v, ISeria &s) { + seria_kv("code", v.code, s); + seria_kv("message", v.message, s); + s.object_key("data"); + v.seria_data(s); } } diff --git a/src/http/RequestParser.cpp b/src/http/RequestParser.cpp index bacc57af..b10813d9 100644 --- a/src/http/RequestParser.cpp +++ b/src/http/RequestParser.cpp @@ -164,7 +164,7 @@ bool RequestParser::process_ready_header(request &req) { req.content_length = boost::lexical_cast(lowcase.value); // std::stoull req.headers.pop_back(); return true; - } catch (...) { + } catch (const std::exception &) { } return false; } diff --git a/src/http/ResponseParser.cpp b/src/http/ResponseParser.cpp index 7384e98d..7a867bb8 100644 --- a/src/http/ResponseParser.cpp +++ b/src/http/ResponseParser.cpp @@ -172,7 +172,7 @@ bool ResponseParser::process_ready_header(response &req) { req.content_length = boost::lexical_cast(lowcase.value); // std::stoull req.headers.pop_back(); return true; - } catch (...) { + } catch (const std::exception &) { } return false; } diff --git a/src/http/Server.cpp b/src/http/Server.cpp index 84cc5fcb..95ac9f8c 100644 --- a/src/http/Server.cpp +++ b/src/http/Server.cpp @@ -6,11 +6,12 @@ #include #include #include "common/Invariant.hpp" +#include "common/exception.hpp" // to test // httperf --port 8090 --num-calls 100000 --uri /index.html -// curl -s "http://localhost:8888/?[1-10000]" -// USELESS siege -b -r 1000 -c 100 http://localhost:8888/index.html +// curl -s "http://127.0.0.1:8888/?[1-10000]" +// USELESS siege -b -r 1000 -c 100 http://127.0.0.1:8888/index.html // USELESS ab -n 100000 -c 50 -k localhost:8090/index.html // wrk -t2 -c20 -d5s http://127.0.0.1:8888/index.html @@ -134,10 +135,14 @@ void Server::on_client_handler(Client *who) { bool result = true; try { result = r_handler(who, std::move(request), response); + } catch (const ErrorAuthorization &e) { + std::cout << "HTTP unauthorized request" << std::endl; + response.r.headers.push_back({"WWW-Authenticate", "Basic realm=\"" + e.realm + "\", charset=\"UTF-8\""}); + response.r.status = 401; } catch (const std::exception &e) { - std::cout << "HTTP request leads to throw/catch, what=" << e.what() << std::endl; + std::cout << "HTTP request leads to throw/catch, what=" << common::what(e) << std::endl; response.r.status = 422; - response.set_body(e.what()); + response.set_body(common::what(e)); } catch (...) { std::cout << "HTTP request leads to throw/catch" << std::endl; response.r.status = 422; diff --git a/src/http/Server.hpp b/src/http/Server.hpp index c5610d26..c4067777 100644 --- a/src/http/Server.hpp +++ b/src/http/Server.hpp @@ -13,6 +13,13 @@ namespace http { +class ErrorAuthorization : public std::runtime_error { +public: + explicit ErrorAuthorization(const std::string &realm) + : std::runtime_error("Error Authorization Required"), realm(realm) {} + std::string realm; +}; + class Client { public: typedef std::function handler; diff --git a/src/logging/FileLogger.cpp b/src/logging/FileLogger.cpp index 65461d57..06d2ff76 100644 --- a/src/logging/FileLogger.cpp +++ b/src/logging/FileLogger.cpp @@ -3,6 +3,7 @@ #include "FileLogger.hpp" #include "common/ConsoleTools.hpp" +#include "common/exception.hpp" #include "platform/PathTools.hpp" namespace logging { @@ -41,7 +42,7 @@ void FileLogger::do_log_string(const std::string &message) { file_stream->write(real_message.data(), real_message.size()); } catch (const std::exception &ex) { // Will continue trying to write when space becomes available common::console::set_text_color(Color::Yellow); - std::cout << "Log File Write Failed, error=" << ex.what() << std::endl; + std::cout << "Log File Write Failed, error=" << common::what(ex) << std::endl; common::console::set_text_color(Color::Default); } @@ -65,7 +66,7 @@ void FileLogger::do_log_string(const std::string &message) { max_size *= 2; // doubling size of next rotation... common::console::set_text_color(Color::Yellow); std::cout << "Failed to fully rotate log, writing to prev, next rotate attempt on size " << max_size - << " error=" << ex.what() << std::endl; + << " error=" << common::what(ex) << std::endl; common::console::set_text_color(Color::Default); } } diff --git a/src/logging/LoggerManager.cpp b/src/logging/LoggerManager.cpp index ece6efad..060395c7 100644 --- a/src/logging/LoggerManager.cpp +++ b/src/logging/LoggerManager.cpp @@ -19,7 +19,8 @@ void LoggerManager::write( LoggerGroup::write(category, level, time, body); } -void LoggerManager::configure_default(const std::string &log_folder, const std::string &log_prefix) { +void LoggerManager::configure_default( + const std::string &log_folder, const std::string &log_prefix, const std::string &version) { { std::unique_lock lock(reconfigure_lock); // TODO - investigate possible deadlocks loggers.clear(); @@ -39,8 +40,8 @@ void LoggerManager::configure_default(const std::string &log_folder, const std:: loggers.emplace_back(std::move(logger)); add_logger(*loggers.back()); } - write( - "START", TRACE, boost::posix_time::microsec_clock::local_time(), "----------------------------------------\n"); + write("START", TRACE, boost::posix_time::microsec_clock::local_time(), + version + " ----------------------------------------\n"); } void LoggerManager::configure(const JsonValue &val) { diff --git a/src/logging/LoggerManager.hpp b/src/logging/LoggerManager.hpp index c67a45bc..986c8a5e 100644 --- a/src/logging/LoggerManager.hpp +++ b/src/logging/LoggerManager.hpp @@ -15,8 +15,9 @@ namespace logging { class LoggerManager : public LoggerGroup { public: LoggerManager(); - void configure_default(const std::string &log_folder, const std::string &log_prefix); // log_folder must exist - void configure(const common::JsonValue &val); // from json config + void configure_default(const std::string &log_folder, const std::string &log_prefix, const std::string &version); + // log_folder must exist + void configure(const common::JsonValue &val); // from json config virtual void write( const std::string &category, Level level, boost::posix_time::ptime time, const std::string &body) override; diff --git a/src/main_bytecoind.cpp b/src/main_bytecoind.cpp index c76b31a4..8c7a30a3 100644 --- a/src/main_bytecoind.cpp +++ b/src/main_bytecoind.cpp @@ -32,11 +32,12 @@ static const char USAGE[] = --exclusive-node-address= Specify list (one or more) of nodes to connect to only. All other nodes including seed nodes will be ignored. --export-blocks= Perform hot export of blockchain into specified folder as blocks.bin and blockindexes.bin, then exit. This overwrites existing files. --backup-blockchain= Perform hot backup of blockchain into specified backup data folder, then exit. - --net= Configure for mainnet or testnet [default: main]. + --net= Configure for mainnet or testnet [default: main]. --archive Work as an archive node [default: off]. --data-folder= Folder for blockchain, logs and peer DB [default: )" platform_DEFAULT_DATA_FOLDER_PATH_PREFIX R"(bytecoin]. - --bytecoind-authorization= HTTP basic authentication credentials for RPC API.)" + --bytecoind-authorization= HTTP basic authentication credentials for RPC API. + --bytecoind-authorization-private= HTTP basic authentication credentials for get_statistics and get_archive methods.)" #if platform_USE_SSL R"( --ssl-certificate-pem-file= Full path to file containing both server SSL certificate and private key in PEM format. @@ -57,8 +58,8 @@ int main(int argc, const char *argv[]) try { std::string backup_blockchain; if (const char *pa = cmd.get("--backup-blockchain")) backup_blockchain = pa; - bytecoin::Config config(cmd); - bytecoin::Currency currency(config.is_testnet); + Config config(cmd); + Currency currency(config.net); Height print_structure = Height(-1); if (const char *pa = cmd.get("--print-structure")) @@ -116,7 +117,7 @@ int main(int argc, const char *argv[]) try { platform::ExclusiveLock coin_lock(coin_folder, "bytecoind.lock"); logging::LoggerManager log_manager; - log_manager.configure_default(config.get_data_folder("logs"), "bytecoind-"); + log_manager.configure_default(config.get_data_folder("logs"), "bytecoind-", bytecoin::app_version()); BlockChainState block_chain(log_manager, config, currency, false); // block_chain.test_undo_everything(0); @@ -126,7 +127,11 @@ int main(int argc, const char *argv[]) try { LegacyBlockChainReader::import_blockchain2(coin_folder, &block_chain, 300000); return 0; } - // block_chain.test_undo_everything(0); + // block_chain.test_undo_everything(0); + // block_chain.test_print_tips(); + // while(block_chain.test_prune_oldest()){ + // block_chain.test_print_tips(); + // } boost::asio::io_service io; platform::EventLoop run_loop(io); @@ -144,9 +149,12 @@ int main(int argc, const char *argv[]) try { } return 0; } catch (const platform::ExclusiveLock::FailedToLock &ex) { - std::cout << "Bytecoind already running - " << ex.what() << std::endl; + std::cout << "Bytecoind already running - " << common::what(ex) << std::endl; return api::BYTECOIND_ALREADY_RUNNING; +} catch (const platform::TCPAcceptor::AddressInUse &ex) { + std::cout << common::what(ex) << std::endl; + return api::BYTECOIND_BIND_PORT_IN_USE; } catch (const std::exception &ex) { // On Windows what() is not printed if thrown from main - std::cout << "Exception in main() - " << ex.what() << std::endl; + std::cout << "Uncaught Exception in main() - " << common::what(ex) << std::endl; throw; } diff --git a/src/main_tests.cpp b/src/main_tests.cpp index 5ff5f209..37c5fe72 100644 --- a/src/main_tests.cpp +++ b/src/main_tests.cpp @@ -5,6 +5,7 @@ #include #include "common/CommandLine.hpp" +#include "platform/DB.hpp" #include "version.hpp" #include "../tests/blockchain/test_blockchain.hpp" @@ -30,24 +31,26 @@ uses relative paths and should be run from bin folder int main(int argc, const char *argv[]) { common::CommandLine cmd(argc, argv); - std::cout << "Testing Block Chain" << std::endl; - test_blockchain(cmd); + std::cout << "Testing Hashes" << std::endl; + test_hashes("../tests/hash"); + + std::cout << "Testing Crypto" << std::endl; + test_crypto("../tests/crypto/tests.txt"); + std::cout << "Testing Wallet Files" << std::endl; + test_wallet_file("../tests/wallet_file"); std::cout << "Testing Wallet State" << std::endl; test_wallet_state(cmd); - std::cout << "Testing Wallet Files" << std::endl; - test_wallet_file("../tests/wallet_file"); + std::cout << "Testing Block Chain" << std::endl; + test_blockchain(cmd); - std::cout << "Testing Hashes" << std::endl; - test_hashes("../tests/hash"); + std::cout << "Testing DB" << std::endl; + platform::DB::run_tests(); std::cout << "Testing Json" << std::endl; test_json("../tests/json"); - std::cout << "Testing Crypto" << std::endl; - test_crypto("../tests/crypto/tests.txt"); - if (cmd.should_quit(USAGE, bytecoin::app_version())) return 0; return 0; diff --git a/src/main_walletd.cpp b/src/main_walletd.cpp index 1dc5b1db..7b9cb38e 100644 --- a/src/main_walletd.cpp +++ b/src/main_walletd.cpp @@ -16,6 +16,8 @@ #include "platform/PathTools.hpp" #include "version.hpp" +#include "common/Base58.hpp" + using namespace bytecoin; static const char USAGE[] = @@ -28,21 +30,25 @@ static const char USAGE[] = Options: --wallet-file= Path to wallet file to open. - --wallet-password= DEPRECATED AND NOT RECOMMENDED (as entailing security risk). Use given string as password and not read it from stdin. - --create-wallet Create wallet file with new random keys. Must be used with --wallet-file option. + --wallet-password= DEPRECATED AND NOT RECOMMENDED as entailing security risk. Use given string as password and not read it from stdin. + --create-wallet Create wallet file with new random keys, then exit. Must be used with --wallet-file option. --import-keys Create wallet file with imported keys read as a line from stdin. Must be used with --create-wallet. - --set-password Read new password as a line from stdin (twice) and re-encrypt wallet file. - --export-view-only= Export view-only version of wallet file with the same password, then exit. - --export-keys Export wallet keys to stdout, then exit. + --set-password Read new password as a line from stdin (twice) and re-encrypt wallet file, then exit. + --launch-after-command Instead of exiting, continue launching after --create-wallet and --set-password commands + --export-view-only= Export view-only version of wallet file, then exit. Add --set-password to export with different password. + --export-keys Export unencrypted wallet keys to stdout, then exit. --walletd-bind-address= IP and port for walletd RPC API [default: 127.0.0.1:8070]. --data-folder= Folder for wallet cache, blockchain, logs and peer DB [default: )" platform_DEFAULT_DATA_FOLDER_PATH_PREFIX R"(bytecoin]. --bytecoind-remote-address= Connect to remote bytecoind and suppress running built-in bytecoind. + Set this option to https:// instead, to connect to remote bytecoind via https --bytecoind-authorization= HTTP basic authentication credentials for RPC API. --backup-wallet-data= Perform hot backup of wallet file, history, payment queue and wallet cache into specified backup data folder, then exit. - --net= Configure for mainnet or testnet [default: main]. + Add --set-password to set different password for backed-up wallet file. + --net= Configure for mainnet or testnet [default: main]. Options for built-in bytecoind (run when no --bytecoind-remote-address specified): +DEPRECATED AND NOT RECOMMENDED as entailing security risk. Please always run bytecoind as a separate process. --p2p-bind-address= IP and port for P2P network protocol [default: 0.0.0.0:8080]. --p2p-external-port= External port for P2P network protocol, if port forwarding used with NAT [default: 8080]. --bytecoind-bind-address= IP and port for bytecoind RPC [default: 127.0.0.1:8081]. @@ -57,13 +63,14 @@ int main(int argc, const char *argv[]) try { auto idea_start = std::chrono::high_resolution_clock::now(); common::CommandLine cmd(argc, argv); std::string wallet_file, password, new_password, export_view_only, import_keys_value, backup_wallet; - // const bool set_password_and_continue = cmd.get_bool("--set-password-and-continue"); // Run normally after set - // password, used by GUI wallet - const bool set_password = cmd.get_bool("--set-password"); // || set_password_and_continue; - bool ask_password = true; - const bool export_keys = cmd.get_bool("--export-keys"); - const bool create_wallet = cmd.get_bool("--create-wallet"); - const bool import_keys = cmd.get_bool("--import-keys"); + const bool launch_after_command = cmd.get_bool("--launch-after-command"); + // used by GUI wallet, launch normally after create-wallet and set_password + const bool set_password = cmd.get_bool("--set-password"); + bool ask_password = true; + const bool export_keys = cmd.get_bool("--export-keys"); + const bool create_wallet = cmd.get_bool("--create-wallet"); + const bool import_keys = cmd.get_bool("--import-keys"); + const bool print_mineproof_secret = cmd.get_bool("--print-mineproof-secret"); if (import_keys && !create_wallet) { std::cout << "When importing keys, you should use --create-wallet. You cannot import into existing wallet." << std::endl; @@ -93,9 +100,8 @@ int main(int argc, const char *argv[]) try { << std::endl; return api::WALLETD_WRONG_ARGS; } - - bytecoin::Config config(cmd); - bytecoin::Currency currency(config.is_testnet); + Config config(cmd); + Currency currency(config.net); if (cmd.should_quit(USAGE, bytecoin::app_version())) return api::WALLETD_WRONG_ARGS; @@ -110,18 +116,32 @@ int main(int argc, const char *argv[]) try { std::cout << "Unexpected end of stdin" << std::endl; return api::WALLETD_WRONG_ARGS; } + std::cout << std::endl; boost::algorithm::trim(import_keys_value); if (import_keys_value.empty()) { std::cout << "Imported keys should not be empty" << std::endl; return api::WALLETD_WRONG_ARGS; } } + if (!config.bytecoind_remote_port && !create_wallet && !set_password && !export_keys && export_view_only.empty() && + backup_wallet.empty()) { + common::console::set_text_color(common::console::BrightRed); + std::cout << "Warning: inproc bytecoind is deprecated and will be removed soon." << std::endl; + std::cout + << " Please run bytecoind separately, then specify --remote-bytecoind-address=: argument to walletd" + << std::endl; + std::cout + << " This is important to prevent bytecoind P2P attack vectors from reaching walletd address space where wallet keys reside" + << std::endl; + common::console::set_text_color(common::console::Default); + } if (!create_wallet && ask_password) { std::cout << "Enter current wallet password: " << std::flush; if (!console_setup.getline(password, true)) { std::cout << "Unexpected end of stdin" << std::endl; return api::WALLETD_WRONG_ARGS; } + std::cout << std::endl; boost::algorithm::trim(password); } if (create_wallet || set_password) { @@ -130,6 +150,7 @@ int main(int argc, const char *argv[]) try { std::cout << "Unexpected end of stdin" << std::endl; return api::WALLETD_WRONG_ARGS; } + std::cout << std::endl; boost::algorithm::trim(new_password); std::cout << "Repeat new wallet password:" << std::flush; std::string new_password2; @@ -137,6 +158,7 @@ int main(int argc, const char *argv[]) try { std::cout << "Unexpected end of stdin" << std::endl; return api::WALLETD_WRONG_ARGS; } + std::cout << std::endl; boost::algorithm::trim(new_password2); if (new_password != new_password2) { std::cout << "New passwords do not match" << std::endl; @@ -148,18 +170,29 @@ int main(int argc, const char *argv[]) try { // wallet_file = "C:\\Users\\user\\test.wallet"; logging::LoggerManager logManagerWalletNode; - logManagerWalletNode.configure_default(config.get_data_folder("logs"), "walletd-"); + logManagerWalletNode.configure_default(config.get_data_folder("logs"), "walletd-", bytecoin::app_version()); std::unique_ptr wallet; try { wallet = std::make_unique(logManagerWalletNode, wallet_file, create_wallet ? new_password : password, create_wallet, import_keys_value); } catch (const common::StreamError &ex) { - std::cout << ex.what() << std::endl; + std::cout << common::what(ex) << std::endl; return api::WALLET_FILE_READ_ERROR; } catch (const Wallet::Exception &ex) { - std::cout << ex.what() << std::endl; + std::cout << common::what(ex) << std::endl; return ex.return_code; } + if (print_mineproof_secret) { + std::cout << "To mine with mineproofs to this wallet address " + << currency.account_address_as_string(wallet->get_first_address()) << std::endl; + std::cout << "Run bytecoind with --mineproof-secret=" << wallet->get_coinbase_tx_derivation_seed() << std::endl; + } + if (create_wallet) { + std::cout << "Successfully created wallet with address " + << currency.account_address_as_string(wallet->get_first_address()) << std::endl; + if (!launch_after_command) + return 0; + } try { if (!backup_wallet.empty()) { if (import_keys || create_wallet || export_keys) { @@ -172,6 +205,14 @@ int main(int argc, const char *argv[]) try { const std::string dst_history_name = dst_name + ".history"; const std::string dst_payments_name = dst_name + ".payments"; const std::string dst_cache = backup_wallet + "/wallet_cache/" + wallet->get_cache_name(); + if (!platform::create_folder_if_necessary(dst_payments_name)) { + std::cout << "Could not create folder for backup " << dst_payments_name << std::endl; + return 1; + } + if (!platform::create_folder_if_necessary(dst_history_name)) { + std::cout << "Could not create folder for backup " << dst_history_name << std::endl; + return 1; + } if (!platform::create_folder_if_necessary(backup_wallet + "/wallet_cache")) { std::cout << "Could not create folder for backup " << (backup_wallet + "/wallet_cache") << std::endl; return 1; @@ -180,13 +221,13 @@ int main(int argc, const char *argv[]) try { std::cout << "Could not create folder for backup " << dst_cache << std::endl; return 1; } - if (!platform::create_folder_if_necessary(dst_payments_name)) { - std::cout << "Could not create folder for backup " << dst_payments_name << std::endl; - return 1; + std::cout << "Backing up wallet file to " << dst_name << std::endl; + wallet->export_wallet(dst_name, set_password ? new_password : password, false); + for (const auto &file : platform::get_filenames_in_folder(wallet->get_payment_queue_folder())) { + platform::copy_file(wallet->get_payment_queue_folder() + "/" + file, dst_payments_name + "/" + file); } - if (!platform::create_folder_if_necessary(dst_history_name)) { - std::cout << "Could not create folder for backup " << dst_history_name << std::endl; - return 1; + for (const auto &file : platform::get_filenames_in_folder(wallet->get_history_folder())) { + platform::copy_file(wallet->get_history_folder() + "/" + file, dst_history_name + "/" + file); } common::console::set_text_color(common::console::BrightRed); std::cout @@ -194,19 +235,9 @@ int main(int argc, const char *argv[]) try { << std::endl; common::console::set_text_color(common::console::Default); - std::cout << "Starting wallet cache backup..." << std::endl; + std::cout << "Starting wallet cache backup to " << dst_cache << std::endl; platform::DB::backup_db(coin_folder + "/wallet_cache/" + wallet->get_cache_name(), dst_cache); - std::cout << "Finished wallet cache backup." << std::endl; - - for (const auto &file : platform::get_filenames_in_folder(wallet->get_payment_queue_folder())) { - platform::copy_file(wallet->get_payment_queue_folder() + "/" + file, dst_payments_name + "/" + file); - } - for (const auto &file : platform::get_filenames_in_folder(wallet->get_history_folder())) { - platform::copy_file(wallet->get_history_folder() + "/" + file, dst_history_name + "/" + file); - } - std::cout << "Backing up wallet file to " << (backup_wallet + "/" + name) << std::endl; - wallet->export_wallet(backup_wallet + "/" + name, false); // TODO - use set_password - std::cout << "Backing up wallet cache to " << dst_cache << std::endl; + std::cout << "Backing of wallet data finished successfully" << std::endl; return 0; } if (!export_view_only.empty()) { @@ -214,25 +245,28 @@ int main(int argc, const char *argv[]) try { std::cout << "Cannot export as view-only, wallet file is already view-only" << std::endl; return api::WALLETD_WRONG_ARGS; } - wallet->export_wallet(export_view_only, true); // TODO - use set_password + wallet->export_wallet(export_view_only, set_password ? new_password : password, true); + std::cout << "Successfully exported view-only copy of the wallet" << std::endl; return 0; } if (export_keys) { + if (wallet->get_records().size() != 1) + throw Wallet::Exception( + api::WALLETD_EXPORTKEYS_MORETHANONE, "You can only export keys from a wallet containing 1 address"); std::cout << common::to_hex(wallet->export_keys()) << std::endl; return 0; } if (set_password) { wallet->set_password(new_password); - // if( !set_password_and_continue ){ - // std::cout << "New password set" << std::endl; - // return 0; - // } + std::cout << "Successfully set new password" << std::endl; + if (!launch_after_command) + return 0; } } catch (const common::StreamError &ex) { - std::cout << ex.what() << std::endl; + std::cout << common::what(ex) << std::endl; return api::WALLET_FILE_WRITE_ERROR; } catch (const Wallet::Exception &ex) { - std::cout << ex.what() << std::endl; + std::cout << common::what(ex) << std::endl; return ex.return_code; } std::unique_ptr blockchain_lock; @@ -240,7 +274,7 @@ int main(int argc, const char *argv[]) try { if (!config.bytecoind_remote_port) blockchain_lock = std::make_unique(coin_folder, "bytecoind.lock"); } catch (const platform::ExclusiveLock::FailedToLock &ex) { - std::cout << "Bytecoind already running - " << ex.what() << std::endl; + std::cout << "Bytecoind already running - " << common::what(ex) << std::endl; return api::BYTECOIND_ALREADY_RUNNING; } std::unique_ptr walletcache_lock; @@ -250,7 +284,7 @@ int main(int argc, const char *argv[]) try { walletcache_lock = std::make_unique( config.get_data_folder("wallet_cache"), wallet->get_cache_name() + ".lock"); } catch (const platform::ExclusiveLock::FailedToLock &ex) { - std::cout << "Wallet with the same viewkey is in use - " << ex.what() << std::endl; + std::cout << "Wallet with the same first address is in use - " << common::what(ex) << std::endl; return api::WALLET_WITH_SAME_KEYS_IN_USE; } if (!ask_password) { @@ -266,6 +300,7 @@ int main(int argc, const char *argv[]) try { std::cout << "Unexpected end of stdin" << std::endl; return api::WALLETD_WRONG_ARGS; } + std::cout << std::endl; boost::algorithm::trim(auth); config.walletd_authorization = common::base64::encode(BinaryArray(auth.data(), auth.data() + auth.size())); if (config.walletd_authorization.empty()) { @@ -287,8 +322,16 @@ int main(int argc, const char *argv[]) try { std::unique_ptr node; logging::LoggerManager logManagerNode; - logManagerNode.configure_default(config.get_data_folder("logs"), "bytecoind-"); + logManagerNode.configure_default(config.get_data_folder("logs"), "bytecoind-", bytecoin::app_version()); + std::unique_ptr wallet_node; + try { + wallet_node = std::make_unique(nullptr, logManagerWalletNode, config, wallet_state); + } catch (const platform::TCPAcceptor::AddressInUse &ex) { + std::cout << common::what(ex) << std::endl; + return api::WALLETD_BIND_PORT_IN_USE; // We should return before we create bytecoind thread, otherwise + // terminate + } std::promise prm; std::thread bytecoind_thread; if (!config.bytecoind_remote_port) { @@ -322,24 +365,17 @@ int main(int argc, const char *argv[]) try { block_chain = std::make_unique(logManagerNode, config, currency, false); node = std::make_unique(logManagerNode, config, *block_chain); } - } catch (const boost::system::system_error &ex) { - std::cout << ex.what() << std::endl; + } catch (const platform::TCPAcceptor::AddressInUse &ex) { + std::cout << common::what(ex) << std::endl; if (bytecoind_thread.joinable()) bytecoind_thread.join(); // otherwise terminate will be called in ~thread return api::BYTECOIND_BIND_PORT_IN_USE; } catch (const std::exception &ex) { // On Windows what() is not printed if thrown from main - std::cout << "Exception in main() - " << ex.what() << std::endl; + std::cout << "Uncaught Exception in main() - " << common::what(ex) << std::endl; + // TODO - check that ..joinable()..join().. code from above does not apply also throw; } } - std::unique_ptr wallet_node; - try { - wallet_node = std::make_unique(nullptr, logManagerWalletNode, config, wallet_state); - } catch (const boost::system::system_error &ex) { - std::cout << ex.what() << std::endl; - return api::WALLETD_BIND_PORT_IN_USE; - } - auto idea_ms = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - idea_start); std::cout << "walletd started seconds=" << double(idea_ms.count()) / 1000 << std::endl; @@ -352,6 +388,6 @@ int main(int argc, const char *argv[]) try { } return 0; } catch (const std::exception &ex) { // On Windows what() is not printed if thrown from main - std::cout << "Exception in main() - " << ex.what() << std::endl; + std::cout << "Uncaught Exception in main() - " << common::what(ex) << std::endl; throw; } diff --git a/src/p2p/CryptoNoteProtocolDefinitions.hpp b/src/p2p/CryptoNoteProtocolDefinitions.hpp index 67d046ad..4a274f14 100644 --- a/src/p2p/CryptoNoteProtocolDefinitions.hpp +++ b/src/p2p/CryptoNoteProtocolDefinitions.hpp @@ -35,8 +35,8 @@ struct NOTIFY_NEW_TRANSACTIONS { struct NOTIFY_REQUEST_GET_OBJECTS { enum { ID = BC_COMMANDS_POOL_BASE + 3 }; struct request { - std::vector txs; - std::vector blocks; + std::vector txs; + std::vector blocks; }; }; @@ -45,7 +45,7 @@ struct NOTIFY_RESPONSE_GET_OBJECTS { struct request { std::vector txs; std::vector blocks; - std::vector missed_ids; + std::vector missed_ids; uint32_t current_blockchain_height = 0; // top block height + 1 }; }; @@ -54,9 +54,9 @@ struct NOTIFY_REQUEST_CHAIN { enum { ID = BC_COMMANDS_POOL_BASE + 6 }; struct request { - std::vector block_ids; // IDs of the first 10 blocks are sequential, next goes with pow(2,n) - // offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always - // genesis block + std::vector block_ids; // IDs of the first 10 blocks are sequential, next goes with pow(2,n) + // offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always + // genesis block }; }; @@ -65,19 +65,19 @@ struct NOTIFY_RESPONSE_CHAIN_ENTRY { struct request { uint32_t start_height = 0; // height of first block_id uint32_t total_height = 0; // top block height + 1 - std::vector m_block_ids; + std::vector m_block_ids; }; }; struct NOTIFY_REQUEST_TX_POOL { enum { ID = BC_COMMANDS_POOL_BASE + 8 }; struct request { - std::vector txs; + std::vector txs; }; }; struct NOTIFY_CHECKPOINT { enum { ID = BC_COMMANDS_POOL_BASE + 10 }; - typedef SignedCheckPoint request; + typedef SignedCheckpoint request; }; } diff --git a/src/p2p/LevinProtocol.cpp b/src/p2p/LevinProtocol.cpp index cf9b5a9a..5d43dcdb 100644 --- a/src/p2p/LevinProtocol.cpp +++ b/src/p2p/LevinProtocol.cpp @@ -49,6 +49,8 @@ BinaryArray LevinProtocol::send_message(uint32_t command, const BinaryArray &out size_t LevinProtocol::HEADER_SIZE() { return sizeof(bucket_head2); } +unsigned char LevinProtocol::FIRST_BYTE() { return static_cast(LEVIN_SIGNATURE & 0xff); } + size_t LevinProtocol::read_command_header(const BinaryArray &raw_header, Command &cmd, std::string &ban_reason) { bucket_head2 head = {}; if (raw_header.size() != sizeof(head)) { diff --git a/src/p2p/LevinProtocol.hpp b/src/p2p/LevinProtocol.hpp index 7e0632cc..740b9106 100644 --- a/src/p2p/LevinProtocol.hpp +++ b/src/p2p/LevinProtocol.hpp @@ -41,6 +41,7 @@ class LevinProtocol { }; static size_t HEADER_SIZE(); + static unsigned char FIRST_BYTE(); static size_t read_command_header(const BinaryArray &raw_header, Command &cmd, std::string &ban_reason); static BinaryArray send_message(uint32_t command, const BinaryArray &out, bool need_response); @@ -49,8 +50,8 @@ class LevinProtocol { template static bool decode(const BinaryArray &buf, T &value) { try { - seria::from_binary_key_value(value, buf); - } catch (std::exception &) { + seria::from_binary_kv(value, buf); + } catch (const std::exception &) { return false; } return true; @@ -58,7 +59,7 @@ class LevinProtocol { template static BinaryArray encode(const T &value) { - return seria::to_binary_key_value(value); + return seria::to_binary_kv(value); } }; } diff --git a/src/p2p/P2P.cpp b/src/p2p/P2P.cpp index ef02a0ff..e969f1fe 100644 --- a/src/p2p/P2P.cpp +++ b/src/p2p/P2P.cpp @@ -16,14 +16,40 @@ const float NO_INTERNET_RECONNECT_DELAY = 0.5f; // when network is unreachable, using namespace bytecoin; -P2PClient::P2PClient(size_t header_size, bool incoming, D_handler d_handler) +void P2PProtocol::on_disconnect(const std::string &ban_reason) { + // std::cout << "P2PProtocol::on_disconnect this=" << std::hex << (size_t)this << " m_client=" << (size_t)m_client + //<< std::dec << std::endl; + m_client = nullptr; +} + +const NetworkAddress &P2PProtocol::get_address() const { return m_client->get_address(); } +bool P2PProtocol::is_incoming() const { return m_client->is_incoming(); } +void P2PProtocol::send(BinaryArray &&body) { return m_client->send(std::move(body)); } +void P2PProtocol::send_shutdown() { return m_client->send_shutdown(); } +void P2PProtocol::disconnect(const std::string &ban_reason) { return m_client->disconnect(ban_reason); } +void P2PProtocol::update_my_port(uint16_t port) { return m_client->update_my_port(port); } + +P2PClient::P2PClient(bool incoming, D_handler d_handler) : sock([this](bool canread, bool canwrite) { advance_state(true); }, std::bind(&P2PClient::on_socket_disconnect, this)) , incoming(incoming) - , header_size(header_size) , d_handler(d_handler) , buffer(RECOMMENDED_BUFFER_SIZE) {} +void P2PClient::set_protocol(std::unique_ptr &&protocol) { + bool protocol_switch = protocol && m_protocol; + // std::cout << "P2PClient::set_protocol this=" << std::hex << (size_t)this << std::dec << " m_protocol=" << + //(size_t)m_protocol.get() << " protocol=" << (size_t)protocol.get() << std::endl; + if (m_protocol) + m_protocol->on_disconnect(std::string()); + m_protocol.reset(); + if (protocol_switch) + std::cout << "P2PClient::set_protocol protocol switch" << std::endl; + m_protocol = std::move(protocol); + if (m_protocol) + m_protocol->on_connect(); +} + void P2PClient::write() { while (!responses.empty()) { responses.front().copy_to(sock); @@ -38,17 +64,17 @@ void P2PClient::write() { void P2PClient::read(bool called_from_runloop) { if (!receiving_body) { buffer.copy_from(sock); - if (buffer.size() < header_size) - return; - request.resize(header_size); - buffer.read(request.data(), request.size()); - std::string ban_reason; - request_body_length = on_request_header(request, ban_reason); - if (request_body_length == std::string::npos) { - // log(Logging::WARNING) << "P2PClient::read got bad message from " << address.get_address() << ":" << - // address.get_port() << std::endl; - disconnect(ban_reason); - return; + while (true) { // Support for N immediate protocol switches + std::string ban_reason; + P2PProtocol *was_protocol = m_protocol.get(); + request_body_length = m_protocol->on_parse_header(buffer, request, ban_reason); + if (!ban_reason.empty()) + return disconnect(ban_reason); + if (request_body_length != std::string::npos) + break; + if (was_protocol == m_protocol.get()) + return; + // Immediate protocol switch, repeat parse header for new protocol } receiving_body = true; receiving_body_stream = common::VectorStream(); @@ -59,7 +85,7 @@ void P2PClient::read(bool called_from_runloop) { buffer.copy_to(receiving_body_stream, max_count); if (receiving_body_stream.size() == request_body_length) { if (called_from_runloop) - on_request_ready(); + process_requests(); return; } buffer.copy_from(sock); @@ -68,6 +94,12 @@ void P2PClient::read(bool called_from_runloop) { } } +void P2PClient::process_requests() { + BinaryArray header, body; + while (m_protocol && read_next_request(header, body)) + m_protocol->on_request_ready(std::move(header), std::move(body)); +} + bool P2PClient::read_next_request(BinaryArray &header, BinaryArray &body) { advance_state(false); if (!receiving_body) @@ -102,7 +134,10 @@ void P2PClient::disconnect(const std::string &ban_reason) { responses.clear(); sock.close(); - on_disconnect(ban_reason); + // std::cout << "P2PClient::disconnect this=" << std::hex << (size_t)this << std::dec << std::endl; + if (m_protocol) + m_protocol->on_disconnect(ban_reason); + m_protocol.reset(); d_handler(ban_reason); } @@ -112,7 +147,7 @@ bool P2PClient::test_connect(const NetworkAddress &addr) { if (!sock.connect(common::ip_address_to_string(addr.ip), addr.port)) return false; address = addr; - on_connect(); + m_protocol->on_connect(); return true; } @@ -129,52 +164,57 @@ void P2PClient::advance_state(bool called_from_runloop) { void P2PClient::on_socket_disconnect() { disconnect(std::string()); } void P2P::on_client_disconnected(P2PClient *who, std::string ban_reason) { - auto cit = clients[who->is_incoming()].find(who); - if (cit == clients[who->is_incoming()].end()) + const bool incoming = who->is_incoming(); + auto cit = clients[incoming].find(who); + if (cit == clients[incoming].end()) return; disconnected_clients.push_back(std::move(cit->second)); free_diconnected_timer.once(1); - cit = clients[who->is_incoming()].erase(cit); - connect_all(); + cit = clients[incoming].erase(cit); + if (incoming) + accept_all(); + else + connect_all(); } -void P2P::broadcast(P2PClient *exclude_who, const BinaryArray &data, bool incoming, bool outgoing) { - for (int inc = 0; inc != 2; ++inc) { - if (!incoming && inc == 0) - continue; - if (!outgoing && inc == 1) - continue; - for (auto &&cli : clients[inc]) { - if (cli.first->handshake_ok() && cli.first != exclude_who) { - cli.first->send(BinaryArray(data)); - } - } - } -} +// void P2P::broadcast(P2PProtocol *exclude_who, const BinaryArray &data, bool incoming, bool outgoing) { +// for (int inc = 0; inc != 2; ++inc) { +// if (!incoming && inc == 0) +// continue; +// if (!outgoing && inc == 1) +// continue; +// for (auto &&cli : clients[inc]) { +// if (cli.first->get_protocol()->handshake_ok() && cli.first->get_protocol() != exclude_who) { +// cli.first->send(BinaryArray(data)); +// } +// } +// } +//} -P2PClient *P2P::find_connecting_client(const NetworkAddress &address) { - const bool incoming = false; - for (auto &&cli : clients[incoming]) - if (cli.first->get_address() == address) - return cli.first; - return nullptr; -} +// P2PClient *P2P::find_connecting_client(const NetworkAddress &address) { +// const bool incoming = false; +// for (auto &&cli : clients[incoming]) +// if (cli.first->get_address() == address) +// return cli.first; +// return nullptr; +//} -P2PClient *P2P::find_client(const NetworkAddress &address, bool incoming) { - for (auto &&cli : clients[incoming]) - if (cli.first->handshake_ok() && cli.first->get_address() == address) - return cli.first; - return nullptr; -} +// P2PClient *P2P::find_client(const NetworkAddress &address, bool incoming) { +// for (auto &&cli : clients[incoming]) +// if (cli.first->handshake_ok() && cli.first->get_address() == address) +// return cli.first; +// return nullptr; +//} void P2P::accept_all() { if (!la_socket) return; // std::cout << "Server::accept=" << std::endl; const bool incoming = true; - while (true) { + while (clients[incoming].size() < config.p2p_max_incoming_connections) { if (!next_client[incoming]) { - next_client[incoming] = c_factory(incoming, [](std::string ban_reason) {}); // We do not know Client * yet + next_client[incoming] = + std::make_unique(incoming, [](std::string ban_reason) {}); // We do not know Client * yet next_client[incoming]->d_handler = std::bind(&P2P::on_client_disconnected, this, next_client[incoming].get(), _1); } @@ -186,6 +226,7 @@ void P2P::accept_all() { address.port = 0; // Will set to self-reported port on handshake, was config.p2p_bind_port; next_client[incoming]->address = address; if (peers.is_peer_banned(address, get_local_time())) { + // TODO - do not write to log more often than 1/sec or this can be attack vector log(logging::INFO) << "Accepted from banned address " << addr << " disconnecting immediately" << std::endl; next_client[incoming]->sock.close(); continue; @@ -193,31 +234,32 @@ void P2P::accept_all() { P2PClient *who = next_client[incoming].get(); clients[incoming][next_client[incoming].get()] = std::move(next_client[incoming]); log(logging::INFO) << "Accepted from addr=" << addr << std::endl; - who->on_connect(); + who->set_protocol(c_factory(who)); } } bool P2P::connect_one(const NetworkAddress &address) { const bool incoming = false; if (!next_client[incoming]) { - next_client[incoming] = c_factory(incoming, [](std::string ban_reason) {}); // We do not know Client * yet + next_client[incoming] = + std::make_unique(incoming, [](std::string ban_reason) {}); // We do not know Client * yet next_client[incoming]->d_handler = std::bind(&P2P::on_client_disconnected, this, next_client[incoming].get(), _1); } if (!next_client[incoming]->sock.connect(common::ip_address_to_string(address.ip), address.port)) { return false; } - next_client[incoming]->address = address; - P2PClient *who = next_client[incoming].get(); - clients[incoming][next_client[incoming].get()] = std::move(next_client[incoming]); + next_client[incoming]->address = address; + P2PClient *who = next_client[incoming].get(); + clients[incoming][who] = std::move(next_client[incoming]); log(logging::INFO) << "Connecting to=" << common::ip_address_and_port_to_string(address.ip, address.port) << std::endl; - who->on_connect(); + who->set_protocol(c_factory(who)); return true; } void P2P::connect_all() { - if (failed_connection_attempts_counter > config.p2p_default_connections_count * 5) // CONSTANT in code + if (failed_connection_attempts_counter > config.p2p_max_outgoing_connections * 5) // CONSTANT in code reconnect_timer.once(NO_INTERNET_RECONNECT_DELAY); else connect_all_nodelay(); @@ -226,15 +268,15 @@ void P2P::connect_all() { void P2P::connect_all_nodelay() { const bool incoming = false; unsigned immediate_attempts = 0; - while (clients[incoming].size() < config.p2p_default_connections_count) { // Some clients are not connected + while (clients[incoming].size() < config.p2p_max_outgoing_connections) { std::set connected; for (auto &&cit : clients[incoming]) { connected.insert(cit.first->address); } - NetworkAddress self_connected; - common::parse_ip_address("127.0.0.1", &self_connected.ip); - self_connected.port = config.p2p_bind_port; - connected.insert(self_connected); + // NetworkAddress self_connected; + // common::parse_ip_address("127.0.0.1", &self_connected.ip); + // self_connected.port = config.p2p_bind_port; + // connected.insert(self_connected); NetworkAddress best_address; if (!peers.get_peer_to_connect(best_address, connected, get_local_time())) { @@ -247,7 +289,7 @@ void P2P::connect_all_nodelay() { if (!connect_one(best_address)) immediate_attempts += 1; if (immediate_attempts >= - config.p2p_default_connections_count) { // When network is not reachable, we try just a handfull times + config.p2p_max_outgoing_connections) { // When network is not reachable, we try just a handfull times log(logging::INFO) << "Connect repeatedly fails, will try again after " << RECONNECT_TIMEOUT << " seconds" << std::endl; reconnect_timer.once(RECONNECT_TIMEOUT); @@ -272,8 +314,8 @@ P2P::P2P(logging::ILogger &log, const Config &config, PeerDB &peers, client_fact try { la_socket.reset( new platform::TCPAcceptor{config.p2p_bind_ip, config.p2p_bind_port, std::bind(&P2P::accept_all, this)}); - } catch (const std::runtime_error &r) { - this->log(logging::WARNING) << " failed to create listening socket, what=" << r.what() + } catch (const std::runtime_error &ex) { + this->log(logging::WARNING) << " failed to create listening socket, what=" << common::what(ex) << ", working with outbound connections only" << std::endl; } connect_all(); @@ -285,12 +327,12 @@ uint32_t P2P::get_p2p_time() const { return get_local_time(); } uint32_t P2P::get_local_time() const { return platform::now_unix_timestamp(); } -std::vector P2P::good_clients(bool incoming) const { - std::vector result; - for (auto &&cit : clients[incoming]) { - if (cit.first->handshake_ok()) { - result.push_back(cit.first->address); - } - } - return result; -} +// std::vector P2P::good_clients(bool incoming) const { +// std::vector result; +// for (auto &&cit : clients[incoming]) { +// if (cit.first->get_protocol()->handshake_ok()) { +// result.push_back(cit.first->address); +// } +// } +// return result; +//} diff --git a/src/p2p/P2P.hpp b/src/p2p/P2P.hpp index b921dda5..fc267d5a 100644 --- a/src/p2p/P2P.hpp +++ b/src/p2p/P2P.hpp @@ -17,6 +17,30 @@ namespace bytecoin { class Config; class P2P; +class P2PClient; + +class P2PProtocol { +public: + explicit P2PProtocol(P2PClient *client) : m_client(client) {} + virtual ~P2PProtocol() {} + virtual void on_connect() = 0; + virtual size_t on_parse_header(common::CircularBuffer &buffer, BinaryArray &request, std::string &ban_reason) = 0; + virtual void on_request_ready(BinaryArray &&header, BinaryArray &&body) = 0; + virtual void on_disconnect(const std::string &ban_reason); + virtual bool handshake_ok() const = 0; // if true, will be used for broadcast and find_client + const NetworkAddress &get_address() const; + bool is_incoming() const; + virtual void send(BinaryArray &&body); + void send_shutdown(); + void disconnect(const std::string &ban_reason); + P2PClient *get_client() const { return m_client; } + +protected: + void update_my_port(uint16_t port); + +private: + P2PClient *m_client; +}; class P2PClient { public: @@ -24,9 +48,9 @@ class P2PClient { typedef std::function D_handler; - explicit P2PClient(size_t header_size, bool incoming, D_handler d_handler); + explicit P2PClient(bool incoming, D_handler d_handler); + void set_protocol(std::unique_ptr &&protocol); - bool read_next_request(BinaryArray &header, BinaryArray &body); const NetworkAddress &get_address() const { return address; } bool is_incoming() const { return incoming; } virtual void send(BinaryArray &&body); // We want to make sure to update stats when calling with a base class @@ -35,26 +59,24 @@ class P2PClient { bool test_connect(const NetworkAddress &addr); // for single connects without p2p bool is_connected() const; virtual ~P2PClient() {} - -protected: + P2PProtocol *get_protocol() const { return m_protocol.get(); } void update_my_port(uint16_t port) { address.port = port; } - virtual void on_connect() = 0; - virtual size_t on_request_header(const BinaryArray &header, std::string &ban_reason) const = 0; - virtual void on_request_ready() = 0; - virtual void on_disconnect(const std::string &ban_reason) = 0; - virtual bool handshake_ok() const = 0; // if true, will be used for broadcast and find_client + private: void advance_state(bool called_from_runloop); void on_socket_disconnect(); void write(); void read(bool called_from_runloop); + bool read_next_request(BinaryArray &header, BinaryArray &body); + void process_requests(); + friend class P2P; + std::unique_ptr m_protocol; NetworkAddress address; platform::TCPSocket sock; const bool incoming; - const size_t header_size; D_handler d_handler; BinaryArray request; @@ -70,16 +92,16 @@ class P2PClient { class P2P { public: - typedef std::function(bool incoming, P2PClient::D_handler d_handler)> client_factory; + typedef std::function(P2PClient *client)> client_factory; explicit P2P(logging::ILogger &log, const Config &config, PeerDB &peers, client_factory c_factory); - void broadcast( - P2PClient *exclude_who, const BinaryArray &data, bool incoming, bool outgoing); // to all, except who - void broadcast(P2PClient *exclude_who, const BinaryArray &data) { broadcast(exclude_who, data, true, true); } - P2PClient *find_client(const NetworkAddress &address, bool incoming); - P2PClient *find_connecting_client(const NetworkAddress &address); - std::vector good_clients(bool incoming) const; + // void broadcast( + // P2PProtocol *exclude_who, const BinaryArray &data, bool incoming, bool outgoing); // to all, except who + // void broadcast(P2PProtocol *exclude_who, const BinaryArray &data) { broadcast(exclude_who, data, true, true); } + // P2PClient *find_client(const NetworkAddress &address, bool incoming); + // P2PClient *find_connecting_client(const NetworkAddress &address); + // std::vector good_clients(bool incoming) const; uint32_t get_p2p_time() const; uint32_t get_local_time() const; uint64_t get_unique_number() const { return unique_number; } diff --git a/src/p2p/P2PClientBasic.cpp b/src/p2p/P2PClientBasic.cpp index bf2d0532..a1879b4a 100644 --- a/src/p2p/P2PClientBasic.cpp +++ b/src/p2p/P2PClientBasic.cpp @@ -13,8 +13,8 @@ const float TIMED_SYNC_TIMEOUT = 60 * 4; using namespace bytecoin; template -bytecoin::P2PClientBasic::LevinHandlerFunction levin_method(void (bytecoin::P2PClientBasic::*handler)(Cmd &&)) { - return [handler](P2PClientBasic *who, BinaryArray &&body) { +P2PProtocolBasic::LevinHandlerFunction levin_method(void (P2PProtocolBasic::*handler)(Cmd &&)) { + return [handler](P2PProtocolBasic *who, BinaryArray &&body) { Cmd req{}; if (!LevinProtocol::decode(body, req)) { @@ -26,49 +26,51 @@ bytecoin::P2PClientBasic::LevinHandlerFunction levin_method(void (bytecoin::P2PC }; } -std::map, P2PClientBasic::LevinHandlerFunction> P2PClientBasic::before_handshake_handlers = { - {{COMMAND_PING::ID, false}, levin_method(&P2PClientBasic::msg_ping)}, - {{COMMAND_PING::ID, true}, levin_method(&P2PClientBasic::msg_ping)}, - {{COMMAND_HANDSHAKE::ID, false}, levin_method(&P2PClientBasic::msg_handshake)}, - {{COMMAND_HANDSHAKE::ID, true}, levin_method(&P2PClientBasic::msg_handshake)}}; +std::map, P2PProtocolBasic::LevinHandlerFunction> + P2PProtocolBasic::before_handshake_handlers = { + {{COMMAND_PING::ID, false}, levin_method(&P2PProtocolBasic::msg_ping)}, + {{COMMAND_PING::ID, true}, levin_method(&P2PProtocolBasic::msg_ping)}, + {{COMMAND_HANDSHAKE::ID, false}, levin_method(&P2PProtocolBasic::msg_handshake)}, + {{COMMAND_HANDSHAKE::ID, true}, levin_method(&P2PProtocolBasic::msg_handshake)}}; -std::map, P2PClientBasic::LevinHandlerFunction> P2PClientBasic::after_handshake_handlers = { - {{COMMAND_TIMED_SYNC::ID, false}, levin_method(&P2PClientBasic::msg_timed_sync)}, - {{COMMAND_TIMED_SYNC::ID, true}, levin_method(&P2PClientBasic::msg_timed_sync)}, +std::map, P2PProtocolBasic::LevinHandlerFunction> P2PProtocolBasic::after_handshake_handlers = + {{{COMMAND_TIMED_SYNC::ID, false}, levin_method(&P2PProtocolBasic::msg_timed_sync)}, + {{COMMAND_TIMED_SYNC::ID, true}, levin_method(&P2PProtocolBasic::msg_timed_sync)}, #if bytecoin_ALLOW_DEBUG_COMMANDS - {{COMMAND_REQUEST_NETWORK_STATE::ID, false}, - levin_method(&P2PClientBasic::on_msg_network_state)}, - {{COMMAND_REQUEST_NETWORK_STATE::ID, true}, - levin_method(&P2PClientBasic::on_msg_network_state)}, - {{COMMAND_REQUEST_STAT_INFO::ID, false}, - levin_method(&P2PClientBasic::on_msg_stat_info)}, - {{COMMAND_REQUEST_STAT_INFO::ID, true}, - levin_method(&P2PClientBasic::on_msg_stat_info)}, + {{COMMAND_REQUEST_NETWORK_STATE::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_network_state)}, + {{COMMAND_REQUEST_NETWORK_STATE::ID, true}, + levin_method(&P2PProtocolBasic::on_msg_network_state)}, + {{COMMAND_REQUEST_STAT_INFO::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_stat_info)}, + {{COMMAND_REQUEST_STAT_INFO::ID, true}, + levin_method(&P2PProtocolBasic::on_msg_stat_info)}, #endif - {{NOTIFY_NEW_BLOCK::ID, false}, levin_method(&P2PClientBasic::on_msg_notify_new_block)}, - {{NOTIFY_NEW_TRANSACTIONS::ID, false}, - levin_method(&P2PClientBasic::on_msg_notify_new_transactions)}, - {{NOTIFY_REQUEST_TX_POOL::ID, false}, - levin_method(&P2PClientBasic::on_msg_notify_request_tx_pool)}, - {{NOTIFY_REQUEST_CHAIN::ID, false}, - levin_method(&P2PClientBasic::on_msg_notify_request_chain)}, - {{NOTIFY_RESPONSE_CHAIN_ENTRY::ID, false}, - levin_method(&P2PClientBasic::on_msg_notify_request_chain)}, - {{NOTIFY_CHECKPOINT::ID, false}, - levin_method(&P2PClientBasic::on_msg_notify_checkpoint)}, - {{NOTIFY_REQUEST_GET_OBJECTS::ID, false}, - levin_method(&P2PClientBasic::on_msg_notify_request_objects)}, - {{NOTIFY_RESPONSE_GET_OBJECTS::ID, false}, - levin_method(&P2PClientBasic::on_msg_notify_request_objects)}}; + {{NOTIFY_NEW_BLOCK::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_new_block)}, + {{NOTIFY_NEW_TRANSACTIONS::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_new_transactions)}, + {{NOTIFY_REQUEST_TX_POOL::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_request_tx_pool)}, + {{NOTIFY_REQUEST_CHAIN::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_request_chain)}, + {{NOTIFY_RESPONSE_CHAIN_ENTRY::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_request_chain)}, + {{NOTIFY_CHECKPOINT::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_checkpoint)}, + {{NOTIFY_REQUEST_GET_OBJECTS::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_request_objects)}, + {{NOTIFY_RESPONSE_GET_OBJECTS::ID, false}, + levin_method(&P2PProtocolBasic::on_msg_notify_request_objects)}}; -P2PClientBasic::P2PClientBasic(const Config &config, uint64_t unique_number, bool incoming, D_handler d_handler) - : P2PClient(LevinProtocol::HEADER_SIZE(), incoming, d_handler) +P2PProtocolBasic::P2PProtocolBasic(const Config &config, uint64_t unique_number, P2PClient *client) + : P2PProtocol(client) , no_activity_timer([this]() { disconnect(std::string()); }) - , timed_sync_timer(std::bind(&P2PClientBasic::send_timed_sync, this)) + , timed_sync_timer(std::bind(&P2PProtocolBasic::send_timed_sync, this)) , unique_number(unique_number) , config(config) {} -void P2PClientBasic::send_timed_sync() { +void P2PProtocolBasic::send_timed_sync() { COMMAND_TIMED_SYNC::request req; req.payload_data = get_sync_data(); @@ -79,15 +81,15 @@ void P2PClientBasic::send_timed_sync() { timed_sync_timer.once(TIMED_SYNC_TIMEOUT); } -void P2PClientBasic::send(BinaryArray &&body) { +void P2PProtocolBasic::send(BinaryArray &&body) { timed_sync_timer.once(TIMED_SYNC_TIMEOUT); on_msg_bytes(0, body.size()); - P2PClient::send(std::move(body)); + P2PProtocol::send(std::move(body)); } -Timestamp P2PClientBasic::get_local_time() const { return platform::now_unix_timestamp(); } +Timestamp P2PProtocolBasic::get_local_time() const { return platform::now_unix_timestamp(); } -basic_node_data P2PClientBasic::get_node_data() const { +basic_node_data P2PProtocolBasic::get_node_data() const { basic_node_data node_data; node_data.version = P2PProtocolVersion::CURRENT; node_data.local_time = get_local_time(); @@ -97,7 +99,7 @@ basic_node_data P2PClientBasic::get_node_data() const { return node_data; } -void P2PClientBasic::on_connect() { +void P2PProtocolBasic::on_connect() { no_activity_timer.once(HANDSHAKE_TIMEOUT); if (is_incoming()) return; @@ -110,19 +112,34 @@ void P2PClientBasic::on_connect() { send(std::move(msg)); } -void P2PClientBasic::on_disconnect(const std::string &ban_reason) { +void P2PProtocolBasic::on_disconnect(const std::string &ban_reason) { + P2PProtocol::on_disconnect(ban_reason); + timed_sync_timer.cancel(); + no_activity_timer.cancel(); version = 0; // We reuse client instances between connects, so we reinit vars here first_message_after_handshake_processed = false; last_received_sync_data = CORE_SYNC_DATA{}; last_received_unique_number = 0; } -size_t P2PClientBasic::on_request_header(const BinaryArray &header, std::string &ban_reason) const { +size_t P2PProtocolBasic::on_parse_header( + common::CircularBuffer &buffer, BinaryArray &request, std::string &ban_reason) { + if (!handshake_ok() && buffer.size() > 0 && *buffer.read_ptr() != LevinProtocol::FIRST_BYTE()) { + on_immediate_protocol_switch(*buffer.read_ptr()); + return std::string::npos; + } LevinProtocol::Command cmd; - return LevinProtocol::read_command_header(header, cmd, ban_reason); + if (buffer.size() < LevinProtocol::HEADER_SIZE()) + return std::string::npos; + request.resize(LevinProtocol::HEADER_SIZE()); + buffer.read(request.data(), request.size()); + auto body_size = LevinProtocol::read_command_header(request, cmd, ban_reason); + if (body_size == std::string::npos) + return std::string::npos; // ban_reason set by LevinProtocol + return static_cast(body_size); } -void P2PClientBasic::msg_handshake(COMMAND_HANDSHAKE::request &&req) { +void P2PProtocolBasic::msg_handshake(COMMAND_HANDSHAKE::request &&req) { if (!is_incoming()) { disconnect("COMMAND_HANDSHAKE from outgoing node"); return; @@ -147,10 +164,10 @@ void P2PClientBasic::msg_handshake(COMMAND_HANDSHAKE::request &&req) { std::cout << "P2p COMMAND_HANDSHAKE request version=" << int(req.node_data.version) << " unique_number=" << req.node_data.peer_id << " current_height=" << req.payload_data.current_height << " from " << get_address() << std::endl; + timed_sync_timer.once(TIMED_SYNC_TIMEOUT); // Order important, can switch to different protocol on_msg_handshake(std::move(req)); - timed_sync_timer.once(TIMED_SYNC_TIMEOUT); } -void P2PClientBasic::msg_handshake(COMMAND_HANDSHAKE::response &&req) { +void P2PProtocolBasic::msg_handshake(COMMAND_HANDSHAKE::response &&req) { if (is_incoming()) { disconnect("COMMAND_HANDSHAKE response from incoming node"); return; @@ -170,10 +187,10 @@ void P2PClientBasic::msg_handshake(COMMAND_HANDSHAKE::response &&req) { std::cout << "P2p COMMAND_HANDSHAKE response version=" << int(req.node_data.version) << " unique_number=" << req.node_data.peer_id << " current_height=" << req.payload_data.current_height << " local_peerlist.size=" << req.local_peerlist.size() << " from " << get_address() << std::endl; + timed_sync_timer.once(TIMED_SYNC_TIMEOUT); // Order important, can switch to different protocol on_msg_handshake(std::move(req)); - timed_sync_timer.once(TIMED_SYNC_TIMEOUT); } -void P2PClientBasic::msg_ping(COMMAND_PING::request &&req) { +void P2PProtocolBasic::msg_ping(COMMAND_PING::request &&req) { if (!is_incoming()) { disconnect("COMMAND_PING from outgoing node"); return; @@ -188,7 +205,7 @@ void P2PClientBasic::msg_ping(COMMAND_PING::request &&req) { std::cout << "P2p PING" << std::endl; on_msg_ping(std::move(req)); } -void P2PClientBasic::msg_ping(COMMAND_PING::response &&req) { +void P2PProtocolBasic::msg_ping(COMMAND_PING::response &&req) { if (is_incoming()) { disconnect("COMMAND_PING response from incoming node"); return; @@ -196,7 +213,7 @@ void P2PClientBasic::msg_ping(COMMAND_PING::response &&req) { std::cout << "P2p PONG" << std::endl; on_msg_ping(std::move(req)); } -void P2PClientBasic::msg_timed_sync(COMMAND_TIMED_SYNC::request &&req) { +void P2PProtocolBasic::msg_timed_sync(COMMAND_TIMED_SYNC::request &&req) { // std::cout << "P2p COMMAND_TIMED_SYNC request height=" << req.payload_data.current_height << std::endl; last_received_sync_data = req.payload_data; @@ -209,51 +226,47 @@ void P2PClientBasic::msg_timed_sync(COMMAND_TIMED_SYNC::request &&req) { send(std::move(raw_msg)); on_msg_timed_sync(std::move(req)); } -void P2PClientBasic::msg_timed_sync(COMMAND_TIMED_SYNC::response &&req) { +void P2PProtocolBasic::msg_timed_sync(COMMAND_TIMED_SYNC::response &&req) { // std::cout << "P2p COMMAND_TIMED_SYNC response height=" << req.payload_data.current_height << std::endl; last_received_sync_data = req.payload_data; on_msg_timed_sync(std::move(req)); } -void P2PClientBasic::on_request_ready() { - BinaryArray header; - BinaryArray body; - while (read_next_request(header, body)) { - try { - no_activity_timer.once(MESSAGE_TIMEOUT); - on_msg_bytes(header.size() + body.size(), 0); - LevinProtocol::Command cmd; - std::string ban_reason; - if (LevinProtocol::read_command_header(header, cmd, ban_reason) == std::string::npos) { - disconnect(ban_reason); - return; - } - if (!handshake_ok()) { - auto ha = before_handshake_handlers.find({cmd.command, cmd.is_response}); - if (ha != before_handshake_handlers.end()) { - (ha->second)(this, std::move(body)); - continue; - } - disconnect("202 Expecting handshake or ping"); - return; - } - auto ha = after_handshake_handlers.find({cmd.command, cmd.is_response}); - if (ha != after_handshake_handlers.end()) { +void P2PProtocolBasic::on_request_ready(BinaryArray &&header, BinaryArray &&body) { + try { + no_activity_timer.once(MESSAGE_TIMEOUT); + on_msg_bytes(header.size() + body.size(), 0); + LevinProtocol::Command cmd; + std::string ban_reason; + if (LevinProtocol::read_command_header(header, cmd, ban_reason) == std::string::npos) { + disconnect(ban_reason); + return; + } + if (!handshake_ok()) { + auto ha = before_handshake_handlers.find({cmd.command, cmd.is_response}); + if (ha != before_handshake_handlers.end()) { (ha->second)(this, std::move(body)); - if (!first_message_after_handshake_processed) { - first_message_after_handshake_processed = true; - on_first_message_after_handshake(); - } - continue; + return; } - std::cout << "generic bytecoin::P2P cmd=" << cmd.command << " " << cmd.is_response << " " << cmd.is_notify - << std::endl; - } catch (const std::exception &ex) { - disconnect(std::string("299 Exception processing p2p message what=") + ex.what()); + disconnect("202 Expecting handshake or ping"); return; - } catch (...) { - disconnect("299 Exception processing p2p message"); + } + auto ha = after_handshake_handlers.find({cmd.command, cmd.is_response}); + if (ha != after_handshake_handlers.end()) { + if (!first_message_after_handshake_processed) { + first_message_after_handshake_processed = true; + on_first_message_after_handshake(); + } + (ha->second)(this, std::move(body)); return; } + std::cout << "generic bytecoin::P2P cmd={" << cmd.command << "} " << cmd.is_response << " " << cmd.is_notify + << std::endl; + } catch (const std::exception &ex) { + disconnect(std::string("299 Exception processing p2p message what=") + common::what(ex)); + return; + } catch (...) { + disconnect("299 Exception processing p2p message"); + return; } } diff --git a/src/p2p/P2PClientBasic.hpp b/src/p2p/P2PClientBasic.hpp index 58d40e53..df05013c 100644 --- a/src/p2p/P2PClientBasic.hpp +++ b/src/p2p/P2PClientBasic.hpp @@ -12,9 +12,9 @@ namespace bytecoin { class Config; -class P2PClientBasic : public P2PClient { +class P2PProtocolBasic : public P2PProtocol { public: - typedef std::function LevinHandlerFunction; + typedef std::function LevinHandlerFunction; private: platform::Timer no_activity_timer; @@ -42,12 +42,15 @@ class P2PClientBasic : public P2PClient { const Config &config; virtual void on_connect() override; virtual void on_disconnect(const std::string &ban_reason) override; - virtual size_t on_request_header(const BinaryArray &header, std::string &ban_reason) const override; - virtual void on_request_ready() override; + virtual size_t on_parse_header( + common::CircularBuffer &buffer, BinaryArray &request, std::string &ban_reason) override; + virtual void on_request_ready(BinaryArray &&header, BinaryArray &&body) override; virtual bool handshake_ok() const override { return version != 0; } - virtual void on_msg_bytes(size_t, size_t) {} // downloaded, uploaded - virtual void on_first_message_after_handshake() {} + virtual void on_immediate_protocol_switch(unsigned char first_byte) {} + + virtual void on_msg_bytes(size_t, size_t) {} // downloaded, uploaded + virtual void on_first_message_after_handshake() {} // calling disconnect from here will crash virtual void on_msg_handshake(COMMAND_HANDSHAKE::request &&) {} // called after some internal processing virtual void on_msg_handshake(COMMAND_HANDSHAKE::response &&) {} // called after some internal processing virtual void on_msg_ping(COMMAND_PING::request &&) {} // called after some internal processing @@ -69,16 +72,16 @@ class P2PClientBasic : public P2PClient { virtual void on_msg_notify_request_objects(NOTIFY_RESPONSE_GET_OBJECTS::request &&) {} virtual void on_msg_notify_checkpoint(NOTIFY_CHECKPOINT::request &&) {} virtual CORE_SYNC_DATA get_sync_data() const = 0; - virtual std::vector get_peers_to_share() const { return std::vector(); } + virtual std::vector get_peers_to_share() const { return std::vector(); } void set_last_received_sync_data(CORE_SYNC_DATA cd) { last_received_sync_data = cd; } public: - explicit P2PClientBasic(const Config &config, uint64_t unique_number, bool incoming, D_handler d_handler); + explicit P2PProtocolBasic(const Config &config, uint64_t unique_number, P2PClient *client); int get_version() const { return version; } uint64_t get_unique_number() const { return unique_number; } virtual void send(BinaryArray &&body) override; - basic_node_data get_node_data() const; + virtual basic_node_data get_node_data() const; CORE_SYNC_DATA get_last_received_sync_data() const { return last_received_sync_data; } uint64_t get_last_received_unique_number() const { return last_received_unique_number; } }; diff --git a/src/p2p/P2PClientNew.cpp b/src/p2p/P2PClientNew.cpp index b95b3f04..1a32985f 100644 --- a/src/p2p/P2PClientNew.cpp +++ b/src/p2p/P2PClientNew.cpp @@ -5,34 +5,68 @@ #include #include "Core/Config.hpp" #include "platform/Time.hpp" -#include "seria/BinaryInputStream.hpp" -#include "seria/BinaryOutputStream.hpp" +#include "seria/KVBinaryInputStream.hpp" +#include "seria/KVBinaryOutputStream.hpp" const float NO_INCOMING_HANDSHAKE_DISCONNECT_TIMEOUT = 30; -const float NO_INCOMING_MESSAGE_DISCONNECT_TIMEOUT = 60 * 6; -const float NO_OUTGOING_MESSAGE_PING_TIMEOUT = 60 * 4; +const float NO_INCOMING_MESSAGE_DISCONNECT_TIMEOUT = 60 * 3; +const float NO_OUTGOING_MESSAGE_PING_TIMEOUT = 60 * 2; using namespace bytecoin; -static size_t parse_header(const BinaryArray &header_data, np::Header &header, std::string &ban_reason) { +bool P2PProtocolNew::parse_header(const BinaryArray &header_data, np::Header &header, std::string &ban_reason) { if (header_data.size() != sizeof(np::Header)) { - ban_reason = "Levin wrong header size"; - return std::string::npos; + ban_reason = "P2P wrong header size"; + return false; } memmove(&header, header_data.data(), sizeof(np::Header)); if (header.magic != np::Header::MAGIC) { - ban_reason = "Magic mismatch"; - return std::string::npos; + ban_reason = "P2P magic mismatch"; + return false; } if (header.body_size > np::Header::MAX_PACKET_SIZE) { - ban_reason = "Packet size is too big"; - return std::string::npos; + ban_reason = "P2P packet size is too big"; + return false; } - return static_cast(header.body_size); + return true; +} + +BinaryArray P2PProtocolNew::create_multicast_announce(Hash genesis_bid, uint16_t p2p_external_port) { + np::Handshake::Request resp; + resp.peer_desc.p2p_version = P2PProtocolVersion::V3_NEW; + resp.peer_desc.p2p_external_port = p2p_external_port; + resp.peer_desc.genesis_block_hash = genesis_bid; + resp.top_block_desc.cd = 1; + resp.top_block_desc.hash = genesis_bid; + + BinaryArray msg = seria::to_binary_kv(resp); + BinaryArray ha = P2PProtocolNew::create_header(np::Handshake::Request::ID, msg.size()); + common::append(ha, msg.begin(), msg.end()); + return ha; } -static BinaryArray create_header(uint32_t cmd, size_t size) { +uint16_t P2PProtocolNew::parse_multicast_announce(const unsigned char *data, size_t size, Hash genesis_bid) { + try { + if (size < sizeof(np::Header)) + return 0; + np::Header header; + std::string ban_reason; + if (!parse_header(BinaryArray(data, data + sizeof(np::Header)), header, ban_reason)) + return 0; + if (header.command != np::Handshake::Request::ID || header.body_size != size - sizeof(np::Header)) + return 0; + np::Handshake::Request req; + seria::from_binary_kv(req, BinaryArray(data + sizeof(np::Header), data + size)); + if (req.peer_desc.p2p_version != P2PProtocolVersion::V3_NEW || req.peer_desc.genesis_block_hash != genesis_bid) + return 0; + return req.peer_desc.p2p_external_port; + } catch (const std::exception &) { + } + return false; +} + +BinaryArray P2PProtocolNew::create_header(uint32_t cmd, size_t size) { if (size > np::Header::MAX_PACKET_SIZE) throw std::runtime_error("Attempt to send packet with too big size"); np::Header header{}; @@ -45,19 +79,19 @@ static BinaryArray create_header(uint32_t cmd, size_t size) { } template -bytecoin::P2PClientNew::HandlerFunction handler_method(void (bytecoin::P2PClientNew::*handler)(Cmd &&)) { - return [handler](P2PClientNew *who, BinaryArray &&body) { +P2PProtocolNew::HandlerFunction handler_method(void (P2PProtocolNew::*handler)(Cmd &&)) { + return [handler](P2PProtocolNew *who, BinaryArray &&body) { Cmd req{}; - seria::from_binary(req, body); + seria::from_binary_kv(req, body); (who->*handler)(std::move(req)); }; } -std::map P2PClientNew::handler_functions = { - {np::Handshake::Request::ID, handler_method(&P2PClientNew::msg_handshake)}, - {np::Handshake::Response::ID, handler_method(&P2PClientNew::msg_handshake)}, - {np::FindDiff::Request::ID, handler_method(&P2PClientNew::on_msg_find_diff)}, - {np::FindDiff::Response::ID, handler_method(&P2PClientNew::on_msg_find_diff)}}; +std::map P2PProtocolNew::handler_functions = { + {np::Handshake::Request::ID, handler_method(&P2PProtocolNew::msg_handshake)}, + {np::Handshake::Response::ID, handler_method(&P2PProtocolNew::msg_handshake)}, + {np::FindDiff::Request::ID, handler_method(&P2PProtocolNew::on_msg_find_diff)}, + {np::FindDiff::Response::ID, handler_method(&P2PProtocolNew::on_msg_find_diff)}}; /*std::map, P2PClientBasic::LevinHandlerFunction> P2PClientBasic::before_handshake_handlers = { @@ -94,45 +128,45 @@ std::map, P2PClientBasic::LevinHandlerFunction> P2PCli levin_method(&P2PClientBasic::on_msg_notify_request_objects)}}; */ -P2PClientNew::P2PClientNew( - const Config &config, const Currency ¤cy, uint64_t unique_number, bool incoming, D_handler d_handler) - : P2PClient(sizeof(np::Header), incoming, d_handler) +P2PProtocolNew::P2PProtocolNew(const Config &config, + const Currency ¤cy, + uint64_t unique_number, + P2PClient *client) + : P2PProtocol(client) , no_incoming_timer([this]() { disconnect(std::string()); }) - , no_outgoing_timer(std::bind(&P2PClientNew::send_timed_sync, this)) + , no_outgoing_timer(std::bind(&P2PProtocolNew::send_timed_sync, this)) , unique_number(unique_number) , m_config(config) , m_currency(currency) {} -void P2PClientNew::send_timed_sync() { +void P2PProtocolNew::send_timed_sync() { np::RelayTransactionDescs resp; resp.top_block_desc = get_top_block_desc(); - BinaryArray msg = seria::to_binary(resp); + BinaryArray msg = seria::to_binary_kv(resp); send(create_header(np::RelayTransactionDescs::ID, msg.size())); send(std::move(msg)); - - // no_outgoing_timer.once(NO_OUTGOING_MESSAGE_PING_TIMEOUT); } -void P2PClientNew::send(BinaryArray &&body) { +void P2PProtocolNew::send(BinaryArray &&body) { no_outgoing_timer.once(NO_OUTGOING_MESSAGE_PING_TIMEOUT); on_msg_bytes(0, body.size()); - P2PClient::send(std::move(body)); + P2PProtocol::send(std::move(body)); } -Timestamp P2PClientNew::get_local_time() const { return platform::now_unix_timestamp(); } +Timestamp P2PProtocolNew::get_local_time() const { return platform::now_unix_timestamp(); } -np::PeerDesc P2PClientNew::get_peer_desc() const { +np::PeerDesc P2PProtocolNew::get_peer_desc() const { np::PeerDesc result; - result.p2p_version = np::P2PProtocolVersion::EXPERIMENTAL; + result.p2p_version = P2PProtocolVersion::V3_NEW; result.local_time = get_local_time(); result.peer_id = unique_number; - result.my_external_port = m_config.p2p_external_port; + result.p2p_external_port = m_config.p2p_external_port; result.genesis_block_hash = m_currency.genesis_block_hash; // TODO return result; } -void P2PClientNew::on_connect() { +void P2PProtocolNew::on_connect() { no_incoming_timer.once(NO_INCOMING_HANDSHAKE_DISCONNECT_TIMEOUT); if (is_incoming()) return; @@ -140,23 +174,31 @@ void P2PClientNew::on_connect() { resp.peer_desc = get_peer_desc(); resp.top_block_desc = get_top_block_desc(); - BinaryArray msg = seria::to_binary(resp); + BinaryArray msg = seria::to_binary_kv(resp); send(create_header(np::Handshake::Request::ID, msg.size())); send(std::move(msg)); } -void P2PClientNew::on_disconnect(const std::string &ban_reason) { - peer_desc = np::PeerDesc{}; // We reuse client instances between connects, so we reinit vars here - last_received_top_block_desc = np::TopBlockDesc{}; +void P2PProtocolNew::on_disconnect(const std::string &ban_reason) { + P2PProtocol::on_disconnect(ban_reason); + no_incoming_timer.cancel(); + no_outgoing_timer.cancel(); + other_peer_desc = np::PeerDesc{}; // We reuse client instances between connects, so we reinit vars here + other_top_block_desc = np::TopBlockDesc{}; first_message_after_handshake_processed = false; } -size_t P2PClientNew::on_request_header(const BinaryArray &header_data, std::string &ban_reason) const { +size_t P2PProtocolNew::on_parse_header(common::CircularBuffer &buffer, BinaryArray &request, std::string &ban_reason) { np::Header header{}; - return parse_header(header_data, header, ban_reason); + if (buffer.size() < sizeof(np::Header)) + return std::string::npos; + request.resize(sizeof(np::Header)); + buffer.read(request.data(), request.size()); + if (!parse_header(request, header, ban_reason)) + return std::string::npos; // ban_reason set by parse_header + return static_cast(header.body_size); } - -void P2PClientNew::msg_handshake(np::Handshake::Request &&req) { +void P2PProtocolNew::msg_handshake(np::Handshake::Request &&req) { if (!is_incoming()) { disconnect("Handshake::Request from outgoing node"); return; @@ -171,20 +213,19 @@ void P2PClientNew::msg_handshake(np::Handshake::Request &&req) { resp.top_block_desc = get_top_block_desc(); resp.peerlist = get_peers_to_share(); - BinaryArray msg = seria::to_binary(resp); + BinaryArray msg = seria::to_binary_kv(resp); send(create_header(np::Handshake::Response::ID, msg.size())); send(std::move(msg)); - peer_desc = req.peer_desc; - last_received_top_block_desc = req.top_block_desc; - std::cout << "P2p COMMAND_HANDSHAKE request version=" << int(peer_desc.p2p_version) - << " unique_number=" << peer_desc.peer_id << " current_cd=" << last_received_top_block_desc.cd - << std::endl; + other_peer_desc = req.peer_desc; + other_top_block_desc = req.top_block_desc; + std::cout << "NewP2p COMMAND_HANDSHAKE request version=" << int(other_peer_desc.p2p_version) + << " unique_number=" << other_peer_desc.peer_id << " current_cd=" << other_top_block_desc.cd << std::endl; on_msg_handshake(std::move(req)); // no_outgoing_timer.once(NO_OUTGOING_MESSAGE_PING_TIMEOUT); } -void P2PClientNew::msg_handshake(np::Handshake::Response &&req) { +void P2PProtocolNew::msg_handshake(np::Handshake::Response &&req) { if (is_incoming()) { disconnect("Handshake::Response response from incoming node"); return; @@ -198,102 +239,57 @@ void P2PClientNew::msg_handshake(np::Handshake::Response &&req) { disconnect("203 self-connect"); return; } - peer_desc = req.peer_desc; - last_received_top_block_desc = req.top_block_desc; - std::cout << "P2p COMMAND_HANDSHAKE response version=" << int(peer_desc.p2p_version) - << " unique_number=" << peer_desc.peer_id << " current_cd=" << last_received_top_block_desc.cd + other_peer_desc = req.peer_desc; + other_top_block_desc = req.top_block_desc; + std::cout << "NewP2p COMMAND_HANDSHAKE response version=" << int(other_peer_desc.p2p_version) + << " unique_number=" << other_peer_desc.peer_id << " current_cd=" << other_top_block_desc.cd << " peerlist.size=" << req.peerlist.size() << std::endl; on_msg_handshake(std::move(req)); // no_outgoing_timer.once(NO_OUTGOING_MESSAGE_PING_TIMEOUT); } -/*void P2PClientNew::msg_ping(COMMAND_PING::request &&req) { - if (!is_incoming()) { - disconnect("COMMAND_PING from outgoing node"); - return; - } - COMMAND_PING::response msg; - msg.status = COMMAND_PING::status_ok(); - msg.peer_id = unique_number; - - BinaryArray raw_msg = LevinProtocol::send_reply(COMMAND_PING::ID, LevinProtocol::encode(msg), 0); - send(std::move(raw_msg)); - send_shutdown(); - std::cout << "P2p PING" << std::endl; - on_msg_ping(std::move(req)); -} -void P2PClientNew::msg_ping(COMMAND_PING::response &&req) { - if (is_incoming()) { - disconnect("COMMAND_PING response from incoming node"); - return; - } - std::cout << "P2p PONG" << std::endl; - on_msg_ping(std::move(req)); -} -void P2PClientNew::msg_timed_sync(COMMAND_TIMED_SYNC::request &&req) { - // std::cout << "P2p COMMAND_TIMED_SYNC request height=" << req.payload_data.current_height << std::endl; - last_received_sync_data = req.payload_data; - - COMMAND_TIMED_SYNC::response msg; - msg.payload_data = get_sync_data(); - msg.local_time = get_local_time(); - msg.local_peerlist = get_peers_to_share(); - - BinaryArray raw_msg = LevinProtocol::send_reply(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(msg), 0); - send(std::move(raw_msg)); - on_msg_timed_sync(std::move(req)); -} -void P2PClientNew::msg_timed_sync(COMMAND_TIMED_SYNC::response &&req) { - // std::cout << "P2p COMMAND_TIMED_SYNC response height=" << req.payload_data.current_height << std::endl; - last_received_sync_data = req.payload_data; - on_msg_timed_sync(std::move(req)); -}*/ -void P2PClientNew::on_request_ready() { - BinaryArray binary_header; - BinaryArray body; - while (read_next_request(binary_header, body)) { - try { - no_incoming_timer.once(NO_INCOMING_MESSAGE_DISCONNECT_TIMEOUT); - on_msg_bytes(binary_header.size() + body.size(), 0); - np::Header header; - std::string ban_reason; - if (parse_header(binary_header, header, ban_reason) == std::string::npos) { - disconnect(ban_reason); +void P2PProtocolNew::on_request_ready(BinaryArray &&binary_header, BinaryArray &&body) { + try { + no_incoming_timer.once(NO_INCOMING_MESSAGE_DISCONNECT_TIMEOUT); + on_msg_bytes(binary_header.size() + body.size(), 0); + np::Header header; + std::string ban_reason; + if (!parse_header(binary_header, header, ban_reason)) { + disconnect(ban_reason); + return; + } + if (!handshake_ok()) { + if (header.command == np::Handshake::Request::ID) { + np::Handshake::Request req; + seria::from_binary_kv(req, body); + msg_handshake(std::move(req)); return; } - if (!handshake_ok()) { - if (header.command == np::Handshake::Request::ID) { - np::Handshake::Request req; - seria::from_binary(req, body); - msg_handshake(std::move(req)); - continue; - } - if (header.command == np::Handshake::Response::ID) { - np::Handshake::Response req; - seria::from_binary(req, body); - msg_handshake(std::move(req)); - continue; - } - disconnect("202 Expecting handshake"); + if (header.command == np::Handshake::Response::ID) { + np::Handshake::Response req; + seria::from_binary_kv(req, body); + msg_handshake(std::move(req)); return; } - auto ha = handler_functions.find(header.command); - if (ha != handler_functions.end()) { - (ha->second)(this, std::move(body)); - if (!first_message_after_handshake_processed) { - first_message_after_handshake_processed = true; - on_first_message_after_handshake(); - } - continue; - } - std::cout << "Skipping unknown bytecoin::P2P cmd=" << header.command << " size=" << header.body_size - << std::endl; - } catch (const std::exception &ex) { - disconnect(std::string("299 Exception processing p2p message what=") + ex.what()); + disconnect("202 Expecting handshake"); return; - } catch (...) { - disconnect("299 Exception processing p2p message"); + } + auto ha = handler_functions.find(header.command); + if (ha != handler_functions.end()) { + (ha->second)(this, std::move(body)); + if (!first_message_after_handshake_processed) { + first_message_after_handshake_processed = true; + on_first_message_after_handshake(); + } return; } + std::cout << "Skipping unknown bytecoin::P2P cmd=" << header.command << " size=" << header.body_size + << std::endl; + } catch (const std::exception &ex) { + disconnect(std::string("299 Exception processing p2p message what=") + common::what(ex)); + return; + } catch (...) { + disconnect("299 Exception processing p2p message"); + return; } } diff --git a/src/p2p/P2PClientNew.hpp b/src/p2p/P2PClientNew.hpp index 2ec079d4..745d159c 100644 --- a/src/p2p/P2PClientNew.hpp +++ b/src/p2p/P2PClientNew.hpp @@ -11,9 +11,11 @@ namespace bytecoin { class Config; class Currency; -class P2PClientNew : public P2PClient { +class P2PProtocolNew : public P2PProtocol { public: - typedef std::function HandlerFunction; + typedef std::function HandlerFunction; + + static BinaryArray create_header(uint32_t cmd, size_t size); private: platform::Timer no_incoming_timer; @@ -21,8 +23,8 @@ class P2PClientNew : public P2PClient { bool first_message_after_handshake_processed = false; // we add node to peerdb after first non-handshake message received to avoid adding seed nodes const uint64_t unique_number; - np::PeerDesc peer_desc; // version 0 if not received yet - np::TopBlockDesc last_received_top_block_desc; + np::PeerDesc other_peer_desc; // version 0 if not received yet + np::TopBlockDesc other_top_block_desc; Timestamp get_local_time() const; static std::map handler_functions; @@ -36,30 +38,45 @@ class P2PClientNew : public P2PClient { const Currency &m_currency; virtual void on_connect() override; virtual void on_disconnect(const std::string &ban_reason) override; - virtual size_t on_request_header(const BinaryArray &header, std::string &ban_reason) const override; - virtual void on_request_ready() override; - virtual bool handshake_ok() const override { return peer_desc.p2p_version != 0; } + virtual size_t on_parse_header( + common::CircularBuffer &buffer, BinaryArray &request, std::string &ban_reason) override; + virtual void on_request_ready(BinaryArray &&header, BinaryArray &&body) override; + virtual bool handshake_ok() const override { return other_peer_desc.p2p_version != 0; } - virtual void on_msg_bytes(size_t, size_t) {} // downloaded, uploaded - virtual void on_first_message_after_handshake() {} + virtual void on_msg_bytes(size_t, size_t) {} // downloaded, uploaded + virtual void on_first_message_after_handshake() {} // calling disconnect from here will crash - virtual void on_msg_handshake(np::Handshake::Request &&req) {} - virtual void on_msg_handshake(np::Handshake::Response &&req) {} - virtual void on_msg_find_diff(np::FindDiff::Request &&) {} // called after some internal processing - virtual void on_msg_find_diff(np::FindDiff::Response &&) {} // called after some internal processing + virtual void on_msg_handshake(np::Handshake::Request &&req) {} // called after some internal processing + virtual void on_msg_handshake(np::Handshake::Response &&req) {} // called after some internal processing + virtual void on_msg_find_diff(np::FindDiff::Request &&) {} + virtual void on_msg_find_diff(np::FindDiff::Response &&) {} + virtual void on_msg_sync_headers(np::SyncHeaders::Request &&) {} + virtual void on_msg_sync_headers(np::SyncHeaders::Response &&) {} + virtual void on_msg_get_transactions(np::GetTransactions::Request &&) {} + virtual void on_msg_get_transactions(np::GetTransactions::Response &&) {} + virtual void on_msg_get_pool_hashes(np::GetPoolHashes::Request &&) {} + virtual void on_msg_get_pool_hashes(np::GetPoolHashes::Response &&) {} + virtual void on_msg_relay_block_header(np::RelayBlockHeader &&) {} + virtual void on_msg_relay_transaction_desc(np::RelayTransactionDescs &&) {} #if bytecoin_ALLOW_DEBUG_COMMANDS + virtual void on_msg_get_peer_statistics(np::GetPeerStatistics::Request &&) {} + virtual void on_msg_get_peer_statistics(np::GetPeerStatistics::Response &&) {} #endif virtual np::TopBlockDesc get_top_block_desc() const = 0; - virtual std::vector get_peers_to_share() const { return std::vector(); } + virtual std::vector get_peers_to_share() const { return std::vector(); } public: - explicit P2PClientNew( - const Config &config, const Currency ¤cy, uint64_t unique_number, bool incoming, D_handler d_handler); - int get_version() const { return peer_desc.p2p_version; } + explicit P2PProtocolNew(const Config &config, const Currency ¤cy, uint64_t unique_number, P2PClient *client); + // int get_version() const { return peer_desc.p2p_version; } uint64_t get_unique_number() const { return unique_number; } virtual void send(BinaryArray &&body) override; np::PeerDesc get_peer_desc() const; - np::TopBlockDesc get_last_received_top_block_desc() const { return last_received_top_block_desc; } - uint64_t get_last_received_unique_number() const { return peer_desc.peer_id; } + np::PeerDesc get_other_peer_desc() const { return other_peer_desc; } + np::TopBlockDesc get_other_top_block_desc() const { return other_top_block_desc; } + + static bool parse_header(const BinaryArray &header_data, np::Header &header, std::string &ban_reason); + static BinaryArray create_multicast_announce(Hash genesis_bid, uint16_t p2p_external_port); + static uint16_t parse_multicast_announce( + const unsigned char *data, size_t size, Hash genesis_bid); // returns port or 0 if failed to parse }; } diff --git a/src/p2p/P2pProtocolDefinitions.hpp b/src/p2p/P2pProtocolDefinitions.hpp index 1b28cf21..f1cb9b6e 100644 --- a/src/p2p/P2pProtocolDefinitions.hpp +++ b/src/p2p/P2pProtocolDefinitions.hpp @@ -20,8 +20,6 @@ struct network_config { uint32_t send_peerlist_sz = 0; }; -enum P2PProtocolVersion : uint8_t { V0 = 0, V1 = 1, CURRENT = V1, EXPERIMENTAL = 3 }; - struct basic_node_data { UUID network_id; uint8_t version = 0; @@ -33,7 +31,7 @@ struct basic_node_data { struct CORE_SYNC_DATA { uint32_t current_height = 0; // crazy, but this one is top block + 1 instead of top block // We conform to legacy by sending incremented field on wire - crypto::Hash top_id; + Hash top_id; }; enum { P2P_COMMANDS_POOL_BASE = 1000 }; @@ -49,7 +47,7 @@ struct COMMAND_HANDSHAKE { struct response { basic_node_data node_data; CORE_SYNC_DATA payload_data; - std::vector local_peerlist; + std::vector local_peerlist; }; }; @@ -63,7 +61,7 @@ struct COMMAND_TIMED_SYNC { struct response { uint64_t local_time = 0; CORE_SYNC_DATA payload_data; - std::vector local_peerlist; + std::vector local_peerlist; }; }; @@ -87,12 +85,12 @@ struct COMMAND_PING { // These commands are considered as insecure, and made in debug purposes for a limited lifetime. // Anyone who feel unsafe with this commands can disable the bytecoin_ALLOW_DEBUG_COMMANDS macro in CryptoNote.hpp -struct proof_of_trust { +struct ProofOfTrustLegacy { PeerIdType peer_id = 0; uint64_t time = 0; crypto::Signature sign; - crypto::Hash get_hash() const; + Hash get_hash() const; }; struct CoreStatistics { @@ -107,7 +105,7 @@ struct COMMAND_REQUEST_STAT_INFO { enum { ID = P2P_COMMANDS_POOL_BASE + 4 }; struct request { - proof_of_trust tr; + ProofOfTrustLegacy tr; }; struct response { @@ -123,12 +121,12 @@ struct COMMAND_REQUEST_NETWORK_STATE { enum { ID = P2P_COMMANDS_POOL_BASE + 5 }; struct request { - proof_of_trust tr; + ProofOfTrustLegacy tr; }; struct response { - std::vector local_peerlist_white; - std::vector local_peerlist_gray; + std::vector local_peerlist_white; + std::vector local_peerlist_gray; std::vector connections_list; PeerIdType my_id = 0; uint64_t local_time = 0; @@ -158,7 +156,7 @@ void ser_members(bytecoin::COMMAND_TIMED_SYNC::response &v, seria::ISeria &s); void ser_members(bytecoin::COMMAND_PING::request &v, seria::ISeria &s); void ser_members(bytecoin::COMMAND_PING::response &v, seria::ISeria &s); #if bytecoin_ALLOW_DEBUG_COMMANDS -void ser_members(bytecoin::proof_of_trust &v, seria::ISeria &s); +void ser_members(bytecoin::ProofOfTrustLegacy &v, seria::ISeria &s); void ser_members(bytecoin::COMMAND_REQUEST_STAT_INFO::request &v, seria::ISeria &s); void ser_members(bytecoin::COMMAND_REQUEST_STAT_INFO::response &v, seria::ISeria &s); void ser_members(bytecoin::COMMAND_REQUEST_NETWORK_STATE::request &v, seria::ISeria &s); diff --git a/src/p2p/P2pProtocolNew.cpp b/src/p2p/P2pProtocolNew.cpp index 8ea34a61..6357faa4 100644 --- a/src/p2p/P2pProtocolNew.cpp +++ b/src/p2p/P2pProtocolNew.cpp @@ -2,62 +2,143 @@ // Licensed under the GNU Lesser General Public License. See LICENSE for details. #include "P2pProtocolNew.hpp" +#include "crypto/hash.hpp" +#include "seria/BinaryOutputStream.hpp" +#include "seria/JsonInputStream.hpp" +#include "seria/JsonOutputStream.hpp" + +using namespace bytecoin; + +#if bytecoin_ALLOW_DEBUG_COMMANDS +Hash np::ProofOfTrust::get_hash() const { + BinaryArray ba = seria::to_binary(*this); + return crypto::cn_fast_hash(ba.data(), ba.size()); +} +#endif + +std::string NetworkAddress::to_string() const { return common::ip_address_and_port_to_string(ip, port); } namespace seria { -void ser_members(bytecoin::np::NetworkAddress &v, seria::ISeria &s) { - seria_kv("version", v.version, s); - seria_kv("port", v.port, s); - s.object_key("ip"); - s.binary(v.ip.data(), v.ip.size()); // if not correct for version during save, will throw on load +void ser(NetworkAddress &v, seria::ISeria &s) { + if (dynamic_cast(&s)) { + std::string str = common::ip_address_and_port_to_string(v.ip, v.port); + ser(str, s); + } else if (dynamic_cast(&s)) { + std::string str; + ser(str, s); + if (!common::parse_ip_address_and_port(str, &v.ip, &v.port)) + throw std::runtime_error("Failed to parse ip address and port from " + str); + } else { + s.begin_object(); + seria_kv("ip", v.ip, s); + seria_kv("port", v.port, s); + s.end_object(); + } } -void ser_members(bytecoin::np::PeerlistEntry &v, seria::ISeria &s) { +void ser_members(PeerlistEntry &v, seria::ISeria &s) { seria_kv("peer_id", v.peer_id, s); seria_kv("last_seen", v.last_seen, s); seria_kv("address", v.address, s); + seria_kv("ban_until", v.ban_until, s); + seria_kv("ban_reason", v.ban_reason, s); } -void ser_members(bytecoin::np::ConnectionDesc &v, seria::ISeria &s) { +void ser_members(np::ConnectionDesc &v, seria::ISeria &s) { seria_kv("peer_id", v.peer_id, s); seria_kv("address", v.address, s); seria_kv("is_incoming", v.is_incoming, s); + seria_kv("p2p_version", v.p2p_version, s); + seria_kv("top_block_desc", v.top_block_desc, s); } -void ser_members(bytecoin::np::PeerDesc &v, seria::ISeria &s) { +void ser_members(np::PeerDesc &v, seria::ISeria &s) { seria_kv("p2p_version", v.p2p_version, s); seria_kv("genesis_block_hash", v.genesis_block_hash, s); seria_kv("peer_id", v.peer_id, s); seria_kv("local_time", v.local_time, s); - seria_kv("my_external_port", v.my_external_port, s); + seria_kv("p2p_external_port", v.p2p_external_port, s); } -void ser_members(bytecoin::np::TopBlockDesc &v, seria::ISeria &s) { - seria_kv("top_bid", v.top_bid, s); +void ser_members(np::TopBlockDesc &v, seria::ISeria &s) { + seria_kv("hash", v.hash, s); + seria_kv("height", v.height, s); seria_kv("cumulative_difficulty", v.cd.lo, s); - seria_kv("cumulative_difficulty_hi", v.cd.hi, s); + seria_kv_optional("cumulative_difficulty_hi", v.cd.hi, s); } -void ser_members(bytecoin::np::TransactionDesc &v, seria::ISeria &s) { +void ser_members(np::TransactionDesc &v, seria::ISeria &s) { seria_kv("hash", v.hash, s); seria_kv("fee", v.fee, s); seria_kv("size", v.size, s); - seria_kv("newest_referenced_block", v.newest_referenced_block, s); + seria_kv("nrb", v.newest_referenced_block, s); } -void ser_members(bytecoin::np::Handshake::Request &v, seria::ISeria &s) { +void ser_members(np::Handshake::Request &v, seria::ISeria &s) { seria_kv("peer_desc", v.peer_desc, s); seria_kv("top_block_desc", v.top_block_desc, s); } -void ser_members(bytecoin::np::Handshake::Response &v, seria::ISeria &s) { +void ser_members(np::Handshake::Response &v, seria::ISeria &s) { seria_kv("peer_desc", v.peer_desc, s); seria_kv("top_block_desc", v.top_block_desc, s); seria_kv("peerlist", v.peerlist, s); } -void ser_members(bytecoin::np::FindDiff::Request &v, seria::ISeria &s) { +void ser_members(np::FindDiff::Request &v, seria::ISeria &s) { seria_kv("gap_start", v.gap_start, s); - seria_kv("gap_end", v.gap_end, s); + seria_kv("desired_bid", v.desired_bid, s); +} +void ser_members(np::FindDiff::Response &v, seria::ISeria &s) { seria_kv("sparse_chain", v.sparse_chain, s); } +void ser_members(np::SyncHeaders::Request &v, seria::ISeria &s) { + seria_kv("previous_hash", v.previous_hash, s); + seria_kv("max_count", v.max_count, s); } -void ser_members(bytecoin::np::FindDiff::Response &v, seria::ISeria &s) { +void ser_members(np::SyncHeaders::Response &v, seria::ISeria &s) { seria_kv("binary_headers", v.binary_headers, s); } +void ser_members(np::GetTransactions::Request &v, seria::ISeria &s) { + seria_kv("transaction_hashes", v.transaction_hashes, s); + seria_kv("block_hash", v.block_hash, s); +} +void ser_members(np::GetTransactions::Response &v, seria::ISeria &s) { seria_kv("top_block_desc", v.top_block_desc, s); - seria_kv("sparse_chain", v.sparse_chain, s); + seria_kv("transactions", v.transactions, s); +} +void ser_members(np::GetPoolHashes::Request &v, seria::ISeria &s) { + seria_kv("min_fee_per_byte", v.min_fee_per_byte, s); + seria_kv("start_fee_per_byte", v.start_fee_per_byte, s); + seria_kv("start_hash", v.start_hash, s); + seria_kv("max_total_size", v.max_total_size, s); + seria_kv("max_total_count", v.max_total_count, s); } -void ser_members(bytecoin::np::RelayTransactionDescs &v, seria::ISeria &s) { +void ser_members(np::GetPoolHashes::Response &v, seria::ISeria &s) { seria_kv("top_block_desc", v.top_block_desc, s); seria_kv("transaction_descs", v.transaction_descs, s); } +void ser_members(np::RelayBlockHeader &v, seria::ISeria &s) { + seria_kv("top_block_desc", v.top_block_desc, s); + seria_kv("binary_header", v.binary_header, s); +} + +void ser_members(np::RelayTransactionDescs &v, seria::ISeria &s) { + seria_kv("top_block_desc", v.top_block_desc, s); + seria_kv("transaction_descs", v.transaction_descs, s); +} +#if bytecoin_ALLOW_DEBUG_COMMANDS +void ser_members(np::ProofOfTrust &v, seria::ISeria &s) { + seria_kv("peer_id", v.peer_id, s); + seria_kv("time", v.time, s); + seria_kv("sign", v.sign, s); +} +void ser_members(np::GetPeerStatistics::Request &v, seria::ISeria &s) { seria_kv("tr", v.tr, s); } +void ser_members(np::GetPeerStatistics::Response &v, seria::ISeria &s) { + seria_kv("version", v.version, s); + seria_kv("platform", v.platform, s); + seria_kv("net", v.net, s); + seria_kv("genesis_block_hash", v.genesis_block_hash, s); + seria_kv("peer_id", v.peer_id, s); + seria_kv("start_time", v.start_time, s); + seria_kv("checkpoints", v.checkpoints, s); + seria_kv("transaction_pool_size", v.transaction_pool_size, s); + seria_kv("transaction_pool_max_size", v.transaction_pool_max_size, s); + seria_kv("transaction_pool_lowest_fee_per_byte", v.transaction_pool_lowest_fee_per_byte, s); + seria_kv("upgrade_decided_height", v.upgrade_decided_height, s); + seria_kv("upgrade_votes_in_top_block", v.upgrade_votes_in_top_block, s); + seria_kv("peer_list_white", v.peer_list_white, s); + seria_kv("peer_list_gray", v.peer_list_gray, s); + seria_kv("connections", v.connections, s); +} +#endif } diff --git a/src/p2p/P2pProtocolNew.hpp b/src/p2p/P2pProtocolNew.hpp index 44a8d0e3..6367d7a2 100644 --- a/src/p2p/P2pProtocolNew.hpp +++ b/src/p2p/P2pProtocolNew.hpp @@ -14,17 +14,41 @@ namespace bytecoin { -namespace np { // new protocol - typedef uint64_t PeerIdType; -enum P2PProtocolVersion : uint8_t { V0 = 0, V1 = 1, EXPERIMENTAL = 2 }; +enum P2PProtocolVersion : uint8_t { V0 = 0, V1 = 1, V3_NEW = 3, CURRENT = V1 }; + +struct NetworkAddress { + BinaryArray ip; // 4 or 16 bytes, depending on version + uint16_t port = 0; + + std::string to_string() const; +}; + +inline bool operator<(const NetworkAddress &a, const NetworkAddress &b) { + return std::tie(a.ip, a.port) < std::tie(b.ip, b.port); +} + +inline bool operator==(const NetworkAddress &a, const NetworkAddress &b) { return a.ip == b.ip && a.port == b.port; } + +inline std::ostream &operator<<(std::ostream &s, const NetworkAddress &na) { return s << na.to_string(); } + +struct PeerlistEntry { + PeerIdType peer_id = 0; + Timestamp last_seen = 0; + NetworkAddress address; + Timestamp ban_until = 0; + std::string ban_reason; +}; + +namespace np { // new protocol #pragma pack(push, 1) struct Header { enum : uint64_t { MAGIC = 0x02E85C0A89412FC1 }; // New bender's nightmare enum { - MAX_PACKET_SIZE = 100000000 // Remove after per-command limits implemented + MAGIC_FIRST_BYTE = MAGIC & 0xFF, + MAX_PACKET_SIZE = 100000000 // Remove after per-command limits implemented }; uint64_t magic = 0; uint32_t body_size = 0; @@ -32,42 +56,33 @@ struct Header { }; #pragma pack(pop) -struct NetworkAddress { - uint8_t version = 0; // 4, 6 - uint16_t port = 0; - BinaryArray ip; // 4 or 16 bytes, depending on version -}; - -struct PeerlistEntry { - PeerIdType peer_id = 0; - Timestamp last_seen = 0; // coincides with Timestamp - NetworkAddress address; +struct TopBlockDesc { + Hash hash; + Height height = 0; + CumulativeDifficulty cd = 0; }; struct ConnectionDesc { PeerIdType peer_id = 0; NetworkAddress address; - bool is_incoming = false; + bool is_incoming = false; + uint8_t p2p_version = 0; + TopBlockDesc top_block_desc; }; struct PeerDesc { uint8_t p2p_version = 0; Hash genesis_block_hash; - PeerIdType peer_id = 0; - Timestamp local_time = 0; - uint16_t my_external_port = 0; -}; - -struct TopBlockDesc { - crypto::Hash top_bid; - CumulativeDifficulty cd = 0; + PeerIdType peer_id = 0; + Timestamp local_time = 0; + uint16_t p2p_external_port = 0; }; struct TransactionDesc { Hash hash; Amount fee = 0; uint32_t size = 0; - Hash newest_referenced_block; + Hash newest_referenced_block; // serialized as "nrb" for space reasons in packets }; struct Handshake { @@ -79,7 +94,7 @@ struct Handshake { }; struct Response { - enum { ID = 102, MAX_PEER_LIST_LENGTH = 5000 }; + enum { ID = 102, GOOD_PEER_LIST_LENGTH = 100, MAX_PEER_LIST_LENGTH = 5000 }; PeerDesc peer_desc; TopBlockDesc top_block_desc; @@ -89,17 +104,16 @@ struct Handshake { struct FindDiff { struct Request { - enum { ID = 201 }; + enum { ID = 201, MAX_GAP_START_LENGTH = 100 }; - Hash gap_start; - Hash gap_end; + std::vector gap_start; + Hash desired_bid; }; struct Response { enum { ID = 202, MAX_SPARSE_CHAIN_LENGTH = 100 }; - TopBlockDesc top_block_desc; - std::vector sparse_chain; + std::vector sparse_chain; }; }; @@ -107,23 +121,22 @@ struct SyncHeaders { struct Request { enum { ID = 301, GOOD_COUNT = 500, MAX_COUNT = 4000 }; - Hash start_hash; + Hash previous_hash; uint32_t max_count = 0; }; struct Response { enum { ID = 302 }; - TopBlockDesc top_block_desc; - std::vector headers; + std::vector binary_headers; }; }; struct GetTransactions { struct Request { - enum { ID = 401 }; + enum { ID = 401, MAX_TRANSACTION_HASHES = 10000 }; - std::vector hashes; + std::vector transaction_hashes; Hash block_hash; // If hashes empty, will send all transactions from block }; @@ -137,7 +150,7 @@ struct GetTransactions { struct GetPoolHashes { struct Request { - enum { ID = 501 }; + enum { ID = 501, MAX_TOTAL_SIZE = 10000000, MAX_TOTAL_COUNT = 20000 }; Amount min_fee_per_byte; Amount start_fee_per_byte; @@ -154,11 +167,11 @@ struct GetPoolHashes { }; }; -struct RelayHeader { +struct RelayBlockHeader { enum { ID = 600 }; TopBlockDesc top_block_desc; - BlockTemplate header; + BinaryArray binary_header; }; struct RelayTransactionDescs { @@ -170,53 +183,51 @@ struct RelayTransactionDescs { #if bytecoin_ALLOW_DEBUG_COMMANDS -struct proof_of_trust { +struct ProofOfTrust { PeerIdType peer_id = 0; Timestamp time = 0; crypto::Signature sign; - // crypto::Hash get_hash() const; + Hash get_hash() const; }; struct GetPeerStatistics { struct Request { enum { ID = 801 }; - proof_of_trust tr; + ProofOfTrust tr; }; struct Response { enum { ID = 802 }; - std::string version; // bytecoind version - std::string platform; // Windows, Linux, Darwin, etc. + std::string version; + std::string platform; + Timestamp start_time = 0; // Unix timestamp UTC + std::string net; + Hash genesis_block_hash; // To be sure about which coin :) + + uint64_t peer_id = 0; // For p2p std::vector peer_list_white; - std::vector peer_list_ban; std::vector peer_list_gray; - std::vector connections; - std::string stats; // json + std::vector connections; + + std::vector checkpoints; + uint64_t transaction_pool_size = 0; + uint64_t transaction_pool_max_size = 0; + Amount transaction_pool_lowest_fee_per_byte = 0; + Height upgrade_decided_height = 0; + Height upgrade_votes_in_top_block = 0; }; }; #endif - -inline bool operator<(const NetworkAddress &a, const NetworkAddress &b) { - return std::tie(a.version, a.ip, a.port) < std::tie(b.version, b.ip, b.port); -} - -inline bool operator==(const NetworkAddress &a, const NetworkAddress &b) { - return a.version == b.version && a.ip == b.ip && a.port == b.port; -} - -// inline std::ostream &operator<<(std::ostream &s, const NetworkAddress &na) { -// return s << common::ip_address_and_port_to_string(na.ip, na.port); -//} TODO - implement for both ipv4 and ipv6 } } namespace seria { -void ser_members(bytecoin::np::NetworkAddress &v, seria::ISeria &s); -void ser_members(bytecoin::np::PeerlistEntry &v, seria::ISeria &s); +void ser(bytecoin::NetworkAddress &v, seria::ISeria &s); +void ser_members(bytecoin::PeerlistEntry &v, seria::ISeria &s); void ser_members(bytecoin::np::ConnectionDesc &v, seria::ISeria &s); void ser_members(bytecoin::np::PeerDesc &v, seria::ISeria &s); void ser_members(bytecoin::np::TopBlockDesc &v, seria::ISeria &s); @@ -225,5 +236,17 @@ void ser_members(bytecoin::np::Handshake::Request &v, seria::ISeria &s); void ser_members(bytecoin::np::Handshake::Response &v, seria::ISeria &s); void ser_members(bytecoin::np::FindDiff::Request &v, seria::ISeria &s); void ser_members(bytecoin::np::FindDiff::Response &v, seria::ISeria &s); +void ser_members(bytecoin::np::SyncHeaders::Request &v, seria::ISeria &s); +void ser_members(bytecoin::np::SyncHeaders::Response &v, seria::ISeria &s); +void ser_members(bytecoin::np::GetTransactions::Request &v, seria::ISeria &s); +void ser_members(bytecoin::np::GetTransactions::Response &v, seria::ISeria &s); +void ser_members(bytecoin::np::GetPoolHashes::Request &v, seria::ISeria &s); +void ser_members(bytecoin::np::GetPoolHashes::Response &v, seria::ISeria &s); +void ser_members(bytecoin::np::RelayBlockHeader &v, seria::ISeria &s); void ser_members(bytecoin::np::RelayTransactionDescs &v, seria::ISeria &s); +#if bytecoin_ALLOW_DEBUG_COMMANDS +void ser_members(bytecoin::np::ProofOfTrust &v, seria::ISeria &s); +void ser_members(bytecoin::np::GetPeerStatistics::Request &v, seria::ISeria &s); +void ser_members(bytecoin::np::GetPeerStatistics::Response &v, seria::ISeria &s); +#endif } diff --git a/src/p2p/P2pProtocolTypes.hpp b/src/p2p/P2pProtocolTypes.hpp index fffdbc13..3e3d4694 100644 --- a/src/p2p/P2pProtocolTypes.hpp +++ b/src/p2p/P2pProtocolTypes.hpp @@ -5,6 +5,7 @@ #include #include +#include "P2pProtocolNew.hpp" #include "common/Ipv4Address.hpp" #include "common/StringTools.hpp" #include "crypto/generic-ops.hpp" @@ -12,47 +13,49 @@ namespace bytecoin { -typedef uint64_t PeerIdType; +// typedef uint64_t PeerIdType; #pragma pack(push, 1) struct UUID { uint8_t data[16]; // TODO - return {} initializer when Google updates NDK compiler }; -struct NetworkAddress { +struct NetworkAddressLegacy { uint32_t ip = 0; uint32_t port = 0; }; -struct PeerlistEntry { - NetworkAddress adr; +struct PeerlistEntryLegacy { + NetworkAddressLegacy adr; PeerIdType id = 0; uint32_t last_seen = 0; // coincides with Timestamp uint32_t reserved = 0; // High part of former 64-bit last_seen }; struct connection_entry { - NetworkAddress adr; + NetworkAddressLegacy adr; PeerIdType id = 0; bool is_income = false; }; #pragma pack(pop) -inline bool operator<(const NetworkAddress &a, const NetworkAddress &b) { +inline bool operator<(const NetworkAddressLegacy &a, const NetworkAddressLegacy &b) { return std::tie(a.ip, a.port) < std::tie(b.ip, b.port); } -inline bool operator==(const NetworkAddress &a, const NetworkAddress &b) { return a.ip == b.ip && a.port == b.port; } - -inline std::ostream &operator<<(std::ostream &s, const NetworkAddress &na) { - return s << common::ip_address_and_port_to_string(na.ip, na.port); +inline bool operator==(const NetworkAddressLegacy &a, const NetworkAddressLegacy &b) { + return a.ip == b.ip && a.port == b.port; } + +// inline std::ostream &operator<<(std::ostream &s, const NetworkAddressLegacy &na) { +// return s << common::ip_address_and_port_to_string(na.ip, na.port); +//} } CRYPTO_MAKE_COMPARABLE(bytecoin, UUID, std::memcmp) namespace seria { void ser(bytecoin::UUID &v, seria::ISeria &s); -void ser_members(bytecoin::PeerlistEntry &v, seria::ISeria &s); -void ser_members(bytecoin::NetworkAddress &v, seria::ISeria &s); +void ser_members(bytecoin::PeerlistEntryLegacy &v, seria::ISeria &s); +void ser_members(bytecoin::NetworkAddressLegacy &v, seria::ISeria &s); void ser_members(bytecoin::connection_entry &v, seria::ISeria &s); } diff --git a/src/p2p/P2pSeria.cpp b/src/p2p/P2pSeria.cpp index 465e77b6..1f4a76d5 100644 --- a/src/p2p/P2pSeria.cpp +++ b/src/p2p/P2pSeria.cpp @@ -7,12 +7,16 @@ #include "crypto/hash.hpp" #include "seria/BinaryOutputStream.hpp" -crypto::Hash bytecoin::proof_of_trust::get_hash() const { +using namespace bytecoin; + +#if bytecoin_ALLOW_DEBUG_COMMANDS +Hash ProofOfTrustLegacy::get_hash() const { std::string s; s.append(reinterpret_cast(&peer_id), sizeof(peer_id)); s.append(reinterpret_cast(&time), sizeof(time)); return crypto::cn_fast_hash(s.data(), s.size()); } +#endif namespace seria { template @@ -21,7 +25,7 @@ typename std::enable_if::value>::type serialize_as_bi serializer.object_key(name); std::string blob; if (serializer.is_input()) { - serializer(blob); + ser(blob, serializer); value.resize(blob.size() / sizeof(T)); if (blob.size()) { memcpy(&value[0], blob.data(), blob.size()); @@ -30,14 +34,14 @@ typename std::enable_if::value>::type serialize_as_bi if (!value.empty()) { blob.assign(reinterpret_cast(&value[0]), value.size() * sizeof(T)); } - serializer(blob); + ser(blob, serializer); } } // P2pProtocolTypes -void ser(bytecoin::UUID &v, seria::ISeria &s) { s.binary(&v, sizeof(v)); } +void ser(UUID &v, seria::ISeria &s) { s.binary(&v, sizeof(v)); } -void ser_members(bytecoin::PeerlistEntry &v, seria::ISeria &s) { +void ser_members(PeerlistEntryLegacy &v, seria::ISeria &s) { seria_kv("adr", v.adr, s); seria_kv("id", v.id, s); seria_kv("last_seen", v.last_seen, s); @@ -48,18 +52,18 @@ void ser_members(bytecoin::PeerlistEntry &v, seria::ISeria &s) { // seria_kv("reserved", v.reserved, s); } -void ser_members(bytecoin::NetworkAddress &v, seria::ISeria &s) { +void ser_members(NetworkAddressLegacy &v, seria::ISeria &s) { seria_kv("ip", v.ip, s); seria_kv("port", v.port, s); } -void ser_members(bytecoin::connection_entry &v, seria::ISeria &s) { +void ser_members(connection_entry &v, seria::ISeria &s) { seria_kv("adr", v.adr, s); seria_kv("id", v.id, s); seria_kv("is_income", v.is_income, s); } -void ser_members(bytecoin::CoreStatistics &v, seria::ISeria &s) { +void ser_members(CoreStatistics &v, seria::ISeria &s) { seria::seria_kv("tx_pool_size", v.tx_pool_size, s); seria::seria_kv("blockchain_height", v.blockchain_height, s); seria::seria_kv("mining_speed", v.mining_speed, s); @@ -68,14 +72,14 @@ void ser_members(bytecoin::CoreStatistics &v, seria::ISeria &s) { } // P2pProtocolDefinitions -void ser_members(bytecoin::network_config &v, seria::ISeria &s) { - seria_kv("connections_count", v.connections_count, s); +void ser_members(network_config &v, seria::ISeria &s) { + seria_kv("connections_count", v.connections_count, s); // "*s_count" will be removed together with legacy P2P seria_kv("handshake_interval", v.handshake_interval, s); seria_kv("packet_max_size", v.packet_max_size, s); seria_kv("config_id", v.config_id, s); } -void ser_members(bytecoin::basic_node_data &v, seria::ISeria &s) { +void ser_members(basic_node_data &v, seria::ISeria &s) { seria_kv("network_id", v.network_id, s); if (s.is_input()) { v.version = 0; @@ -86,68 +90,67 @@ void ser_members(bytecoin::basic_node_data &v, seria::ISeria &s) { seria_kv("my_port", v.my_port, s); } -void ser_members(bytecoin::CORE_SYNC_DATA &v, seria::ISeria &s) { +void ser_members(CORE_SYNC_DATA &v, seria::ISeria &s) { if (s.is_input()) { uint32_t on_wire = 0; s.object_key("current_height"); - s(on_wire); + ser(on_wire, s); v.current_height = on_wire - 1; } else { uint32_t on_wire = v.current_height + 1; s.object_key("current_height"); - s(on_wire); + ser(on_wire, s); } seria_kv("top_id", v.top_id, s); } -void ser_members(bytecoin::COMMAND_HANDSHAKE::request &v, seria::ISeria &s) { +void ser_members(COMMAND_HANDSHAKE::request &v, seria::ISeria &s) { seria_kv("node_data", v.node_data, s); seria_kv("payload_data", v.payload_data, s); } -void ser_members(bytecoin::COMMAND_HANDSHAKE::response &v, seria::ISeria &s) { +void ser_members(COMMAND_HANDSHAKE::response &v, seria::ISeria &s) { seria_kv("node_data", v.node_data, s); seria_kv("payload_data", v.payload_data, s); serialize_as_binary(v.local_peerlist, "local_peerlist", s); } -void ser_members(bytecoin::COMMAND_TIMED_SYNC::request &v, seria::ISeria &s) { - seria_kv("payload_data", v.payload_data, s); -} +void ser_members(COMMAND_TIMED_SYNC::request &v, seria::ISeria &s) { seria_kv("payload_data", v.payload_data, s); } -void ser_members(bytecoin::COMMAND_TIMED_SYNC::response &v, seria::ISeria &s) { +void ser_members(COMMAND_TIMED_SYNC::response &v, seria::ISeria &s) { seria_kv("local_time", v.local_time, s); seria_kv("payload_data", v.payload_data, s); serialize_as_binary(v.local_peerlist, "local_peerlist", s); } -void ser_members(bytecoin::COMMAND_PING::request &v, seria::ISeria &s) {} +void ser_members(COMMAND_PING::request &v, seria::ISeria &s) {} -void ser_members(bytecoin::COMMAND_PING::response &v, seria::ISeria &s) { +void ser_members(COMMAND_PING::response &v, seria::ISeria &s) { seria_kv("status", v.status, s); seria_kv("peer_id", v.peer_id, s); } #if bytecoin_ALLOW_DEBUG_COMMANDS -void ser_members(bytecoin::proof_of_trust &v, seria::ISeria &s) { +void ser_members(ProofOfTrustLegacy &v, seria::ISeria &s) { seria_kv("peer_id", v.peer_id, s); seria_kv("time", v.time, s); seria_kv("sign", v.sign, s); } -void ser_members(bytecoin::COMMAND_REQUEST_STAT_INFO::request &v, seria::ISeria &s) { seria_kv("tr", v.tr, s); } +void ser_members(COMMAND_REQUEST_STAT_INFO::request &v, seria::ISeria &s) { seria_kv("tr", v.tr, s); } -void ser_members(bytecoin::COMMAND_REQUEST_STAT_INFO::response &v, seria::ISeria &s) { +void ser_members(COMMAND_REQUEST_STAT_INFO::response &v, seria::ISeria &s) { seria_kv("version", v.version, s); seria_kv("os_version", v.os_version, s); - seria_kv("connections_count", v.connections_count, s); - seria_kv("incoming_connections_count", v.incoming_connections_count, s); + seria_kv("connections_count", v.connections_count, s); // "*s_count" will be removed together with legacy P2P + seria_kv("incoming_connections_count", v.incoming_connections_count, + s); // "*s_count" will be removed together with legacy P2P seria_kv("payload_info", v.payload_info, s); } -void ser_members(bytecoin::COMMAND_REQUEST_NETWORK_STATE::request &v, seria::ISeria &s) { seria_kv("tr", v.tr, s); } +void ser_members(COMMAND_REQUEST_NETWORK_STATE::request &v, seria::ISeria &s) { seria_kv("tr", v.tr, s); } -void ser_members(bytecoin::COMMAND_REQUEST_NETWORK_STATE::response &v, seria::ISeria &s) { +void ser_members(COMMAND_REQUEST_NETWORK_STATE::response &v, seria::ISeria &s) { serialize_as_binary(v.local_peerlist_white, "local_peerlist_white", s); serialize_as_binary(v.local_peerlist_gray, "local_peerlist_gray", s); serialize_as_binary(v.connections_list, "connections_list", s); @@ -157,7 +160,7 @@ void ser_members(bytecoin::COMMAND_REQUEST_NETWORK_STATE::response &v, seria::IS #endif // CryptoNoteProtocolDefinitions -void ser_members(bytecoin::RawBlockLegacy &v, seria::ISeria &s) { +void ser_members(RawBlockLegacy &v, seria::ISeria &s) { std::string other_block; std::vector other_transactions; if (s.is_input()) { @@ -167,62 +170,60 @@ void ser_members(bytecoin::RawBlockLegacy &v, seria::ISeria &s) { v.transactions.reserve(other_transactions.size()); std::copy(other_block.begin(), other_block.end(), std::back_inserter(v.block)); std::transform(other_transactions.begin(), other_transactions.end(), std::back_inserter(v.transactions), - [](const std::string &s) { return bytecoin::BinaryArray(s.data(), s.data() + s.size()); }); + [](const std::string &s) { return BinaryArray(s.data(), s.data() + s.size()); }); } else { other_block.reserve(v.block.size()); other_transactions.reserve(v.transactions.size()); std::copy(v.block.begin(), v.block.end(), std::back_inserter(other_block)); std::transform(v.transactions.begin(), v.transactions.end(), std::back_inserter(other_transactions), - [](bytecoin::BinaryArray &s) { return std::string(s.begin(), s.end()); }); + [](BinaryArray &s) { return std::string(s.begin(), s.end()); }); seria::seria_kv("block", other_block, s); seria::seria_kv("txs", other_transactions, s); } } -void ser_members(bytecoin::NOTIFY_NEW_BLOCK::request &v, seria::ISeria &s) { +void ser_members(NOTIFY_NEW_BLOCK::request &v, seria::ISeria &s) { seria_kv("b", v.b, s); seria_kv("current_blockchain_height", v.current_blockchain_height, s); seria_kv("hop", v.hop, s); } -void ser_members(bytecoin::NOTIFY_NEW_TRANSACTIONS::request &v, seria::ISeria &s) { +void ser_members(NOTIFY_NEW_TRANSACTIONS::request &v, seria::ISeria &s) { std::vector transactions; if (s.is_input()) { seria::seria_kv("txs", transactions, s); v.txs.reserve(transactions.size()); std::transform(transactions.begin(), transactions.end(), std::back_inserter(v.txs), - [](const std::string &s) { return bytecoin::BinaryArray(s.data(), s.data() + s.size()); }); + [](const std::string &s) { return BinaryArray(s.data(), s.data() + s.size()); }); } else { transactions.reserve(v.txs.size()); std::transform(v.txs.begin(), v.txs.end(), std::back_inserter(transactions), - [](const bytecoin::BinaryArray &s) { return std::string(s.begin(), s.end()); }); + [](const BinaryArray &s) { return std::string(s.begin(), s.end()); }); seria::seria_kv("txs", transactions, s); } } -void ser_members(bytecoin::NOTIFY_REQUEST_GET_OBJECTS::request &v, seria::ISeria &s) { +void ser_members(NOTIFY_REQUEST_GET_OBJECTS::request &v, seria::ISeria &s) { serialize_as_binary(v.txs, "txs", s); serialize_as_binary(v.blocks, "blocks", s); } -void ser_members(bytecoin::NOTIFY_RESPONSE_GET_OBJECTS::request &v, seria::ISeria &s) { +void ser_members(NOTIFY_RESPONSE_GET_OBJECTS::request &v, seria::ISeria &s) { seria_kv("txs", v.txs, s); seria_kv("blocks", v.blocks, s); serialize_as_binary(v.missed_ids, "missed_ids", s); seria_kv("current_blockchain_height", v.current_blockchain_height, s); } -void ser_members(bytecoin::NOTIFY_REQUEST_CHAIN::request &v, seria::ISeria &s) { +void ser_members(NOTIFY_REQUEST_CHAIN::request &v, seria::ISeria &s) { serialize_as_binary(v.block_ids, "block_ids", s); } -void ser_members(bytecoin::NOTIFY_RESPONSE_CHAIN_ENTRY::request &v, seria::ISeria &s) { +void ser_members(NOTIFY_RESPONSE_CHAIN_ENTRY::request &v, seria::ISeria &s) { seria_kv("start_height", v.start_height, s); seria_kv("total_height", v.total_height, s); serialize_as_binary(v.m_block_ids, "m_block_ids", s); } -void ser_members(bytecoin::NOTIFY_REQUEST_TX_POOL::request &v, seria::ISeria &s) { - serialize_as_binary(v.txs, "txs", s); -} +void ser_members(NOTIFY_REQUEST_TX_POOL::request &v, seria::ISeria &s) { serialize_as_binary(v.txs, "txs", s); } } diff --git a/src/p2p/PeerDB.cpp b/src/p2p/PeerDB.cpp index 3fd957ba..7c0f54ec 100644 --- a/src/p2p/PeerDB.cpp +++ b/src/p2p/PeerDB.cpp @@ -10,8 +10,10 @@ #include "seria/BinaryOutputStream.hpp" #include "common/Ipv4Address.hpp" +#include "common/Math.hpp" #include "common/string.hpp" #include "crypto/crypto.hpp" +#include "platform/Time.hpp" #include "seria/ISeria.hpp" using namespace bytecoin; @@ -20,10 +22,8 @@ using namespace platform; namespace seria { void ser_members(PeerDB::Entry &v, ISeria &s) { ser_members(static_cast(v), s); - seria_kv("ban_until", v.ban_until, s); seria_kv("shuffle_random", v.shuffle_random, s); seria_kv("next_connection_attempt", v.next_connection_attempt, s); - seria_kv("error", v.error, s); } } @@ -32,31 +32,32 @@ static const std::string WHITE_LIST("whitelist/"); const Timestamp BAN_PERIOD = 600; const Timestamp RECONNECT_PERIOD = 300; const Timestamp PRIORITY_RECONNECT_PERIOD = 30; +const Timestamp SEED_RECONNECT_PERIOD = 86400; static const float DB_COMMIT_PERIOD = 60; // 1 minute sounds good compromise +static const std::string version_current = "2"; -PeerDB::PeerDB(const Config &config) - : config(config) - , db(false, config.get_data_folder() + "/peer_db", 1024 * 1024 * 128) - , // make sure this is enough for seed node +static Timestamp fix_time_delta(Timestamp delta) { + return std::max(1, delta / platform::get_time_multiplier_for_tests()); +} + +PeerDB::PeerDB(logging::ILogger &log, const Config &config, const std::string &db_suffix) + : m_log(log, "PeerDB") + , config(config) + , db(false, config.get_data_folder() + "/" + db_suffix, 1024 * 1024 * 128) + , // make sure this DB size is enough for seed node commit_timer(std::bind(&PeerDB::db_commit, this)) { + std::string version; + db.get("$version", version); + if (version != version_current) { + if (!version.empty()) + m_log(logging::INFO) << "PeerDB format different, old version=" << version + << " current version=" << version_current << ", clearing Peer DB..." << std::endl; + for (DB::Cursor cur = db.rbegin(std::string()); !cur.end(); cur.erase()) { + } + db.put("$version", version_current, true); + } read_db(WHITE_LIST, whitelist); read_db(GRAY_LIST, graylist); - for (auto &&addr : config.exclusive_nodes) { - Entry new_entry{}; - new_entry.adr = addr; - new_entry.shuffle_random = crypto::rand(); - exclusivelist.insert(new_entry); - } - for (auto &&addr : config.seed_nodes) { - auto &by_addr_index = whitelist.get(); - auto git = by_addr_index.find(addr); - if (git != by_addr_index.end()) // Already in whitelist - continue; - Entry new_entry{}; - new_entry.adr = addr; - new_entry.shuffle_random = crypto::rand(); - whitelist.insert(new_entry); - } commit_timer.once(DB_COMMIT_PERIOD); } @@ -75,7 +76,7 @@ void PeerDB::read_db(const std::string &prefix, peers_indexed &list) { seria::from_binary(peer, db_cur.get_value_array()); // std::cout << common::ip_address_and_port_to_string(peer.adr.ip, peer.adr.port) << std::endl; list.insert(peer); - } catch (...) { + } catch (const std::exception &) { // No problem, will get everything from seed nodes // TODO - log } @@ -83,19 +84,19 @@ void PeerDB::read_db(const std::string &prefix, peers_indexed &list) { } void PeerDB::update_db(const std::string &prefix, const Entry &entry) { - auto key = prefix + common::to_string(entry.adr.ip) + ":" + common::to_string(entry.adr.port); + auto key = prefix + common::ip_address_and_port_to_string(entry.address.ip, entry.address.port); db.put(key, seria::to_binary(entry), false); } void PeerDB::del_db(const std::string &prefix, const NetworkAddress &addr) { - auto key = prefix + common::to_string(addr.ip) + ":" + common::to_string(addr.port); + auto key = prefix + common::ip_address_and_port_to_string(addr.ip, addr.port); db.del(key, false); } void PeerDB::print() { auto &by_time_index = whitelist.get(); for (auto it = by_time_index.begin(); it != by_time_index.end(); ++it) { - std::string a = common::ip_address_and_port_to_string(it->adr.ip, it->adr.port); + std::string a = common::ip_address_and_port_to_string(it->address.ip, it->address.port); std::cout << a << " b=" << it->ban_until << " na=" << it->next_connection_attempt << " ls=" << it->last_seen << std::endl; } @@ -119,7 +120,7 @@ void PeerDB::trim(const std::string &prefix, Timestamp now, peers_indexed &list, auto &by_ban_index = list.get(); while (by_ban_index.size() > count) { auto lit = --by_ban_index.end(); - del_db(prefix, lit->adr); + del_db(prefix, lit->address); by_ban_index.erase(lit); } } @@ -145,19 +146,49 @@ void PeerDB::unban(const std::string &prefix, Timestamp now, peers_indexed &list list.insert(unb); } -std::vector PeerDB::get_peerlist_to_p2p(const NetworkAddress &for_addr, Timestamp now, size_t depth) { - std::vector bs_head; +std::vector PeerDB::get_peerlist_to_p2p(const NetworkAddress &for_addr, Timestamp now, size_t depth) { + std::vector bs_head; + unban(now); + auto &by_time_index = whitelist.get(); + auto fin = by_time_index.lower_bound(boost::make_tuple(Timestamp(1), Timestamp(0), 0)); + int for_addr_network_id = common::get_private_network_prefix(for_addr.ip); + for (auto it = by_time_index.begin(); it != fin; ++it) { + if (is_seed(it->address)) + continue; + int network_id = common::get_private_network_prefix(it->address.ip); + if (for_addr_network_id != network_id && network_id != 0) + continue; + if (it->address.ip.size() != 4) // For now + continue; + bs_head.push_back(it->address); + if (bs_head.size() >= depth) + break; + } + std::shuffle(bs_head.begin(), bs_head.end(), crypto::random_engine{}); + return bs_head; +} + +std::vector PeerDB::get_peerlist_to_p2p_legacy(const NetworkAddress &for_addr, + Timestamp now, + size_t depth) { + std::vector bs_head; unban(now); auto &by_time_index = whitelist.get(); auto fin = by_time_index.lower_bound(boost::make_tuple(Timestamp(1), Timestamp(0), 0)); int for_addr_network_id = common::get_private_network_prefix(for_addr.ip); // std::cout << "for_addr_network_id" << common::ip_address_to_string(for_addr.ip) << std::endl; for (auto it = by_time_index.begin(); it != fin; ++it) { - // std::cout << "network_id" << common::ip_address_to_string(it->adr.ip) << std::endl; - int network_id = common::get_private_network_prefix(it->adr.ip); + if (is_seed(it->address)) + continue; + int network_id = common::get_private_network_prefix(it->address.ip); if (for_addr_network_id != network_id && network_id != 0) continue; - bs_head.push_back(static_cast(*it)); + if (it->address.ip.size() != 4) // For now + continue; + bs_head.push_back(PeerlistEntryLegacy{}); + bs_head.back().id = it->peer_id; + bs_head.back().adr.port = it->address.port; + bs_head.back().adr.ip = ip_address_to_legacy(it->address.ip); bs_head.back().last_seen = 0; if (bs_head.size() >= depth) break; @@ -166,50 +197,114 @@ std::vector PeerDB::get_peerlist_to_p2p(const NetworkAddress &for return bs_head; } -void PeerDB::merge_peerlist_from_p2p(const std::vector &outer_bs, Timestamp now) { +void PeerDB::merge_peerlist_from_p2p(const NetworkAddress &addr, + const std::vector &outer_bs, + Timestamp now) { + unban(now); + for (auto &&pp : outer_bs) { + add_incoming_peer_impl(pp, now); + } + if (is_seed(addr)) { + m_log(logging::INFO) << "Delaying connect to seed " << addr << " because got peer list size=" << outer_bs.size() + << std::endl; + delay_connection_attempt(addr, now); + } + trim(now); +} + +void PeerDB::merge_peerlist_from_p2p(const NetworkAddress &addr, + const std::vector &outer_bs, + Timestamp now) { unban(now); for (auto &&pp : outer_bs) { - add_incoming_peer_impl(pp.adr, pp.id, now); + NetworkAddress na; + na.ip = common::ip_address_from_legacy(pp.adr.ip); + na.port = pp.adr.port; + add_incoming_peer_impl(na, now); + } + if (is_seed(addr)) { + m_log(logging::INFO) << "Delaying connect to seed " << addr << " because got peer list size=" << outer_bs.size() + << std::endl; + delay_connection_attempt(addr, now); } trim(now); } -void PeerDB::add_incoming_peer(const NetworkAddress &addr, PeerIdType peer_id, Timestamp now) { + +bool PeerDB::add_incoming_peer(const NetworkAddress &addr, Timestamp now) { unban(now); - add_incoming_peer_impl(addr, peer_id, now); + if (!add_incoming_peer_impl(addr, now)) + return false; trim(now); + return true; } -void PeerDB::add_incoming_peer_impl(const NetworkAddress &addr, PeerIdType peer_id, Timestamp now) { +bool PeerDB::add_incoming_peer_impl(const NetworkAddress &addr, Timestamp now) { if (addr.port == 0) // client does not want to be in peer lists - return; - // if (!is_ip_allowed(addr.ip)) - // return; + return false; auto &by_addr_index = whitelist.get(); auto git = by_addr_index.find(addr); if (git != by_addr_index.end()) // Already in whitelist - return; + return false; auto &gray_by_addr_index = graylist.get(); git = gray_by_addr_index.find(addr); if (git != gray_by_addr_index.end()) // Already in gray list - return; + return false; Entry new_entry{}; - new_entry.adr = addr; - new_entry.id = peer_id; + new_entry.address = addr; + // We ignore peer_id here // We ignore last_seen here new_entry.shuffle_random = crypto::rand(); graylist.insert(new_entry); update_db(GRAY_LIST, new_entry); + return true; +} + +PeerDB::Entry PeerDB::get_entry_from_lists(const NetworkAddress &addr) const { + auto &gray_by_addr_index = graylist.get(); + auto git = gray_by_addr_index.find(addr); + if (git != gray_by_addr_index.end()) + return *git; + auto &white_by_addr_index = whitelist.get(); + git = white_by_addr_index.find(addr); + if (git != white_by_addr_index.end()) + return *git; + Entry new_entry{}; + new_entry.address = addr; + new_entry.shuffle_random = crypto::rand(); + return new_entry; +} + +void PeerDB::update_lists(const NetworkAddress &addr, std::function fun) { + auto &gray_by_addr_index = graylist.get(); + auto git = gray_by_addr_index.find(addr); + if (git != gray_by_addr_index.end()) { + Entry entry = *git; + fun(entry); + gray_by_addr_index.replace(git, entry); + update_db(GRAY_LIST, entry); + return; + } + auto &white_by_addr_index = whitelist.get(); + git = white_by_addr_index.find(addr); + if (git != white_by_addr_index.end()) { + Entry entry = *git; + fun(entry); + white_by_addr_index.replace(git, entry); + update_db(WHITE_LIST, entry); + return; + } + Entry entry{}; + entry.address = addr; + entry.shuffle_random = crypto::rand(); + fun(entry); + graylist.insert(entry); + update_db(GRAY_LIST, entry); } void PeerDB::set_peer_just_seen(PeerIdType peer_id, const NetworkAddress &addr, Timestamp now, bool reset_next_connection_attempt) { - // auto &exclusive_by_addr_index = exclusivelist.get(); - // auto git = exclusive_by_addr_index.find(addr); - // if (git != exclusive_by_addr_index.end()) { - // return; - // } auto &gray_by_addr_index = graylist.get(); auto git = gray_by_addr_index.find(addr); if (git != gray_by_addr_index.end()) { @@ -217,7 +312,7 @@ void PeerDB::set_peer_just_seen(PeerIdType peer_id, del_db(GRAY_LIST, addr); } Entry new_entry{}; - new_entry.adr = addr; + new_entry.address = addr; new_entry.shuffle_random = crypto::rand(); auto &by_addr_index = whitelist.get(); git = by_addr_index.find(addr); @@ -225,7 +320,7 @@ void PeerDB::set_peer_just_seen(PeerIdType peer_id, new_entry = *git; by_addr_index.erase(git); } - new_entry.id = peer_id; + new_entry.peer_id = peer_id; new_entry.ban_until = 0; // do not reconnect immediately if called inside seed node or if connecting to seed node if (reset_next_connection_attempt && !is_seed(addr)) @@ -236,143 +331,111 @@ void PeerDB::set_peer_just_seen(PeerIdType peer_id, } void PeerDB::delay_connection_attempt(const NetworkAddress &addr, Timestamp now) { - auto &white_by_addr_index = whitelist.get(); - auto git = white_by_addr_index.find(addr); - if (git != white_by_addr_index.end()) { - Entry entry = *git; - white_by_addr_index.erase(git); - entry.next_connection_attempt = now + (!is_priority(addr) ? BAN_PERIOD : PRIORITY_RECONNECT_PERIOD); - whitelist.insert(entry); - update_db(WHITE_LIST, entry); - } + // Used by downloader for slackers and to advance connect attempt for seeds + // We delay slackers always by PRIORITY_RECONNECT_PERIOD (even if they are not priority) + update_lists(addr, [&](Entry &entry) { + entry.next_connection_attempt = + now + fix_time_delta(is_seed(entry.address) ? SEED_RECONNECT_PERIOD : PRIORITY_RECONNECT_PERIOD); + }); } -void PeerDB::set_peer_banned(const NetworkAddress &addr, const std::string &error, Timestamp now) { - auto &exclusive_by_addr_index = exclusivelist.get(); - auto git = exclusive_by_addr_index.find(addr); - if (git != exclusive_by_addr_index.end()) { - Entry entry = *git; - exclusive_by_addr_index.erase(git); - entry.error = error; - entry.ban_until = now + PRIORITY_RECONNECT_PERIOD; - entry.next_connection_attempt = entry.ban_until; - exclusive_by_addr_index.insert(entry); - return; - } - auto &gray_by_addr_index = graylist.get(); - git = gray_by_addr_index.find(addr); - if (git != gray_by_addr_index.end()) { - Entry entry = *git; - gray_by_addr_index.erase(git); - entry.error = error; - entry.ban_until = now + (is_priority(addr) ? PRIORITY_RECONNECT_PERIOD : BAN_PERIOD); - entry.next_connection_attempt = entry.ban_until; - graylist.insert(entry); - update_db(GRAY_LIST, entry); - } - auto &white_by_addr_index = whitelist.get(); - git = white_by_addr_index.find(addr); - if (git != white_by_addr_index.end()) { - Entry entry = *git; - white_by_addr_index.erase(git); - entry.error = error; - entry.ban_until = now + (is_seed(addr) ? PRIORITY_RECONNECT_PERIOD - : is_priority(addr) ? PRIORITY_RECONNECT_PERIOD : BAN_PERIOD); +void PeerDB::set_peer_banned(const NetworkAddress &addr, const std::string &ban_reason, Timestamp now) { + update_lists(addr, [&](Entry &entry) { + entry.ban_reason = ban_reason; + entry.ban_until = + now + fix_time_delta(is_priority_or_seed(entry.address) ? PRIORITY_RECONNECT_PERIOD : BAN_PERIOD); entry.next_connection_attempt = entry.ban_until; - whitelist.insert(entry); - update_db(WHITE_LIST, entry); - } + }); } bool PeerDB::is_peer_banned(NetworkAddress address, Timestamp now) const { - auto &gray_by_addr_index = graylist.get(); - auto git = gray_by_addr_index.find(address); - if (git != gray_by_addr_index.end()) { - if (now < git->ban_until) - return true; - } - auto &white_by_addr_index = whitelist.get(); - git = white_by_addr_index.find(address); - if (git != white_by_addr_index.end()) { - if (now < git->ban_until) - return true; - } - return false; + Entry entry = get_entry_from_lists(address); + return now < entry.ban_until; } bool PeerDB::get_peer_to_connect(NetworkAddress &best_address, const std::set &connected, Timestamp now) { - auto &exclusive_by_time_index = exclusivelist.get(); - if (!exclusive_by_time_index.empty()) { - auto exclusive_sta = exclusive_by_time_index.begin(); - auto exclusive_fin = exclusive_by_time_index.lower_bound(boost::make_tuple(now, Timestamp(0), 0)); - while (exclusive_sta != exclusive_fin && connected.count(exclusive_sta->adr) != 0) - ++exclusive_sta; - if (exclusive_sta == exclusive_fin) - return false; - Entry entry = *exclusive_sta; - exclusive_by_time_index.erase(exclusive_sta); - entry.next_connection_attempt = now + PRIORITY_RECONNECT_PERIOD; - exclusivelist.insert(entry); - best_address = entry.adr; - return true; - } unban(now); - std::vector connected_seeds; - std::vector not_connected_seeds; - for (auto &&cc : config.seed_nodes) - if (connected.count(cc) == 0) - not_connected_seeds.push_back(cc); - else - connected_seeds.push_back(cc); - std::vector connected_priorities; - std::vector not_connected_priorities; + peers_indexed not_connected_priorities; + size_t connected_priorities = 0; for (auto &&cc : config.priority_nodes) - if (connected.count(cc) == 0) - not_connected_priorities.push_back(cc); + if (connected.count(cc)) + connected_priorities += 1; + else + not_connected_priorities.insert(get_entry_from_lists(cc)); + if (connected_priorities < config.priority_nodes.size()) { + auto &ncp_by_time_index = not_connected_priorities.get(); + auto ncp_sta = ncp_by_time_index.begin(); + auto ncp_fin = ncp_by_time_index.lower_bound(boost::make_tuple(now, Timestamp(0), 0)); + if (ncp_sta != ncp_fin && now >= ncp_sta->next_connection_attempt) { + update_lists(ncp_sta->address, + [&](Entry &entry) { entry.next_connection_attempt = now + fix_time_delta(PRIORITY_RECONNECT_PERIOD); }); + best_address = ncp_sta->address; + return true; + } + } + if (config.priority_nodes.size() >= config.p2p_max_outgoing_connections || config.exclusive_nodes) + return false; // Leave slots for all priorities even if some are banned/delayed + const size_t remaining_slots = config.p2p_max_outgoing_connections - config.priority_nodes.size(); + if (connected.size() - connected_priorities >= remaining_slots) + return false; // Leave slots for all priorities even if some are banned/delayed + peers_indexed not_connected_seeds; + size_t connected_seeds = 0; + std::vector seed_next_connection_attempts; + for (auto &&cc : config.seed_nodes) { + Entry entry = get_entry_from_lists(cc); + seed_next_connection_attempts.push_back(entry.next_connection_attempt); + if (connected.count(cc)) + connected_seeds += 1; else - connected_priorities.push_back(cc); - const bool enough_connected_seeds = connected_seeds.size() >= 2; - auto &white_by_time_index = whitelist.get(); - auto white_sta = white_by_time_index.begin(); - auto white_fin = white_by_time_index.lower_bound(boost::make_tuple(now, Timestamp(0), 0)); - while (white_sta != white_fin && - (connected.count(white_sta->adr) != 0 || - (enough_connected_seeds && is_seed(white_sta->adr)))) // Skip connected and seeds if enough connected + not_connected_seeds.insert(entry); + } + if (now >= common::median_value(&seed_next_connection_attempts) && + connected_seeds < 2) { // TODO - constant in code + auto &ncp_by_time_index = not_connected_seeds.get(); + auto ncp_sta = ncp_by_time_index.begin(); + auto ncp_fin = ncp_by_time_index.lower_bound(boost::make_tuple(now, Timestamp(0), 0)); + if (ncp_sta != ncp_fin && now >= ncp_sta->next_connection_attempt) { + update_lists(ncp_sta->address, + [&](Entry &entry) { entry.next_connection_attempt = now + fix_time_delta(PRIORITY_RECONNECT_PERIOD); }); + best_address = ncp_sta->address; + return true; + } + } + auto &white_by_time_index = whitelist.get(); + auto white_sta = white_by_time_index.begin(); + auto white_fin = white_by_time_index.lower_bound(boost::make_tuple(now, Timestamp(0), 0)); + while ( + white_sta != white_fin && (connected.count(white_sta->address) != 0 || is_priority_or_seed(white_sta->address))) ++white_sta; auto &gray_by_time_index = graylist.get(); auto gray_sta = gray_by_time_index.begin(); auto gray_fin = gray_by_time_index.lower_bound(boost::make_tuple(now, Timestamp(0), 0)); - while (gray_sta != gray_fin && (connected.count(gray_sta->adr) != 0 || - (enough_connected_seeds && is_seed(gray_sta->adr)))) // Skip connected and seeds + while (gray_sta != gray_fin && (connected.count(gray_sta->address) != 0 || is_priority_or_seed(gray_sta->address))) ++gray_sta; bool use_white = (crypto::rand() % 100 < config.p2p_whitelist_connections_percent) && white_sta != white_fin && now >= white_sta->next_connection_attempt; if (use_white) { Entry entry = *white_sta; white_by_time_index.erase(white_sta); - entry.next_connection_attempt = now + (is_priority(entry.adr) ? PRIORITY_RECONNECT_PERIOD : RECONNECT_PERIOD); + entry.next_connection_attempt = + now + fix_time_delta(is_priority_or_seed(entry.address) ? PRIORITY_RECONNECT_PERIOD : RECONNECT_PERIOD); whitelist.insert(entry); update_db(WHITE_LIST, entry); - best_address = entry.adr; + best_address = entry.address; return true; } if (gray_sta != gray_fin && now >= gray_sta->next_connection_attempt) { Entry entry = *gray_sta; gray_by_time_index.erase(gray_sta); - entry.next_connection_attempt = now + (is_priority(entry.adr) ? PRIORITY_RECONNECT_PERIOD : RECONNECT_PERIOD); + entry.next_connection_attempt = + now + fix_time_delta(is_priority_or_seed(entry.address) ? PRIORITY_RECONNECT_PERIOD : RECONNECT_PERIOD); graylist.insert(entry); update_db(GRAY_LIST, entry); - best_address = entry.adr; + best_address = entry.address; return true; } - /* if (connected_seeds.size() >= 2) // Already connected/connecting to 2 seed node - return false; - if (not_connected_seeds.empty()) - return false; - size_t pos = crypto::rand() % not_connected_seeds.size(); - best_address = not_connected_seeds[pos];*/ return false; } @@ -383,17 +446,6 @@ bool PeerDB::is_seed(const NetworkAddress &addr) const { return std::binary_search(config.seed_nodes.begin(), config.seed_nodes.end(), addr); } -/*bool PeerDB::is_ip_allowed(uint32_t ip) const { - // TODO - allow exclusive ips - - // never allow loopback ip - if (common::is_ip_address_loopback(ip)) - return false; - if (!config.p2p_allow_local_ip && common::is_ip_address_private(ip)) - return false; - return true; -}*/ - void PeerDB::test() { /* std::vector list; for (int i = 11; i != 22; ++i) { diff --git a/src/p2p/PeerDB.hpp b/src/p2p/PeerDB.hpp index fd9dfcc5..2f6cc676 100644 --- a/src/p2p/PeerDB.hpp +++ b/src/p2p/PeerDB.hpp @@ -9,6 +9,7 @@ #include "CryptoNote.hpp" #include "Core/Currency.hpp" +#include "logging/LoggerMessage.hpp" #include "p2p/P2pProtocolTypes.hpp" #include "platform/DB.hpp" #include "platform/Network.hpp" @@ -26,13 +27,8 @@ class PeerDB { typedef platform::DB DB; struct Entry : public PeerlistEntry { - Entry() - : PeerlistEntry{} // Initialize all fields - {} - Timestamp ban_until = 0; Timestamp next_connection_attempt = 0; uint64_t shuffle_random = 0; // We assign random number to each record, for deterministic order of equal items - std::string error; // last ban reason }; struct by_addr {}; @@ -42,10 +38,10 @@ class PeerDB { typedef boost::multi_index_container, - boost::multi_index::member>, + boost::multi_index::member>, boost::multi_index::ordered_non_unique, boost::multi_index::composite_key, + boost::multi_index::member, boost::multi_index::member, boost::multi_index::member>, boost::multi_index::composite_key_compare, std::greater, @@ -59,34 +55,40 @@ class PeerDB { std::less>>>> peers_indexed; - explicit PeerDB(const Config &config); - - void merge_peerlist_from_p2p(const std::vector &outer_bs, Timestamp now); - void add_incoming_peer(const NetworkAddress &addr, PeerIdType peer_id, Timestamp now); - std::vector get_peerlist_to_p2p(const NetworkAddress &for_addr, Timestamp now, size_t depth); + explicit PeerDB(logging::ILogger &log, const Config &config, const std::string &db_suffix); + void merge_peerlist_from_p2p( + const NetworkAddress &addr, const std::vector &outer_bs, Timestamp now); + void merge_peerlist_from_p2p( + const NetworkAddress &addr, const std::vector &outer_bs, Timestamp now); + bool add_incoming_peer(const NetworkAddress &addr, Timestamp now); + std::vector get_peerlist_to_p2p(const NetworkAddress &for_addr, Timestamp now, size_t depth); + std::vector get_peerlist_to_p2p_legacy( + const NetworkAddress &for_addr, Timestamp now, size_t depth); void set_peer_just_seen( PeerIdType peer_id, const NetworkAddress &addr, Timestamp now, bool reset_next_connection_attempt = true); // seed nodes do not update next connection attempt, in effect rolling lists around - void set_peer_banned(const NetworkAddress &addr, const std::string &error, Timestamp now); + void set_peer_banned(const NetworkAddress &addr, const std::string &ban_reason, Timestamp now); void delay_connection_attempt(const NetworkAddress &addr, Timestamp now); bool is_peer_banned(NetworkAddress address, Timestamp now) const; bool get_peer_to_connect(NetworkAddress &best_address, const std::set &connected, Timestamp now); - // bool is_ip_allowed(uint32_t ip) const; bool is_priority(const NetworkAddress &addr) const; bool is_seed(const NetworkAddress &addr) const; + bool is_priority_or_seed(const NetworkAddress &addr) const { return is_priority(addr) || is_seed(addr); } size_t get_gray_size() const; size_t get_white_size() const; void test(); private: - void add_incoming_peer_impl(const NetworkAddress &addr, PeerIdType peer_id, Timestamp now); + bool add_incoming_peer_impl(const NetworkAddress &addr, Timestamp now); + Entry get_entry_from_lists(const NetworkAddress &addr) const; + void update_lists(const NetworkAddress &addr, std::function fun); + logging::LoggerRef m_log; const Config &config; - peers_indexed exclusivelist; peers_indexed whitelist; peers_indexed graylist; DB db; diff --git a/src/platform/DBlmdb.cpp b/src/platform/DBlmdb.cpp index c5be415d..4e08403c 100644 --- a/src/platform/DBlmdb.cpp +++ b/src/platform/DBlmdb.cpp @@ -5,6 +5,7 @@ #include #include #include "PathTools.hpp" +#include "common/Math.hpp" #include "common/string.hpp" using namespace platform; @@ -226,7 +227,7 @@ std::string DBlmdb::to_ascending_key(uint32_t key) { } uint32_t DBlmdb::from_ascending_key(const std::string &key) { - return boost::lexical_cast(std::stoull(key, nullptr, 16)); + return common::integer_cast(std::stoull(key, nullptr, 16)); } std::string DBlmdb::clean_key(const std::string &key) { diff --git a/src/platform/DBsqlite3.cpp b/src/platform/DBsqlite3.cpp index dcbd7bfb..436acc25 100644 --- a/src/platform/DBsqlite3.cpp +++ b/src/platform/DBsqlite3.cpp @@ -2,10 +2,10 @@ // Licensed under the GNU Lesser General Public License. See LICENSE for details. #include "DBsqlite3.hpp" -#include -#include #include +#include #include "PathTools.hpp" +#include "common/Math.hpp" #include "common/string.hpp" using namespace platform; @@ -67,7 +67,7 @@ size_t DBsqlite::get_approximate_items_count() const { // if (rc != SQLITE_ROW) // throw platform::sqlite::Error("DB::get_approximate_items_count failed sqlite3_step in get " + // common::to_string(rc)); - // return boost::lexical_cast(sqlite3_column_int64(stmt_select_star.handle, 0)); + // return common::integer_cast(sqlite3_column_int64(stmt_select_star.handle, 0)); } static const size_t max_key_size = 128; @@ -161,7 +161,8 @@ static void put(sqlite::Stmt &stmt, const std::string &key, const void *data, si sqlite3_reset(stmt.handle); sqlite_check( sqlite3_bind_blob(stmt.handle, 1, key.data(), static_cast(key.size()), 0), "DB::put sqlite3_bind_blob 1 "); - sqlite_check(sqlite3_bind_blob(stmt.handle, 2, data ? data : "", static_cast(size), 0), "DB::put sqlite3_bind_blob 2 "); + sqlite_check( + sqlite3_bind_blob(stmt.handle, 2, data ? data : "", static_cast(size), 0), "DB::put sqlite3_bind_blob 2 "); // sqlite3_bind_blob uses nullptr as a NULL indicator. Empty arrays can have nullptr as a data(). auto rc = sqlite3_step(stmt.handle); if (rc != SQLITE_DONE) @@ -232,7 +233,8 @@ uint32_t DBsqlite::from_ascending_key(const std::string &key) { long long unsigned val = 0; if (sscanf(key.c_str(), "%llx", &val) != 1) throw std::runtime_error("from_ascending_key failed to convert key=" + key); - return boost::lexical_cast(val); // TODO - std::stoull(key, nullptr, 16) when Google updates NDK compiler + return common::integer_cast( + val); // TODO - std::stoull(key, nullptr, 16) when Google updates NDK compiler } std::string DBsqlite::clean_key(const std::string &key) { diff --git a/src/platform/Files.cpp b/src/platform/Files.cpp index 9ec421dd..ab3b66ad 100644 --- a/src/platform/Files.cpp +++ b/src/platform/Files.cpp @@ -3,9 +3,9 @@ #include "Files.hpp" #include -#include #include #include +#include "common/Math.hpp" #include "common/string.hpp" #ifdef _WIN32 #include "platform/Windows.hpp" @@ -28,7 +28,12 @@ FileStream::FileStream(const std::string &filename, OpenMode mode) { handle = INVALID_HANDLE_VALUE; #endif if (!try_open(filename, mode)) - throw common::StreamError("File failed to open " + filename); + throw common::StreamError( + std::string("Failed to ") + + (mode == READ_EXISTING + ? "open file for reading " + : mode == READ_WRITE_EXISTING ? "open file for reading/writing " : "truncate file for writing ") + + filename); } FileStream::~FileStream() { @@ -134,8 +139,8 @@ void FileStream::truncate(uint64_t size) { std::wstring FileStream::utf8_to_utf16(const std::string &str) { std::wstring result; result.resize(str.size() * 2); // str.size should be enough, but who knows - auto si = MultiByteToWideChar(CP_UTF8, 0, str.data(), boost::lexical_cast(str.size()), &result[0], - boost::lexical_cast(result.size())); + auto si = MultiByteToWideChar(CP_UTF8, 0, str.data(), common::integer_cast(str.size()), &result[0], + common::integer_cast(result.size())); result.resize(si); return result; } @@ -143,8 +148,8 @@ std::wstring FileStream::utf8_to_utf16(const std::string &str) { std::string FileStream::utf16_to_utf8(const std::wstring &str) { std::string result; result.resize(str.size() * 5); // str.size*4 should be enough, but who knows - auto si = WideCharToMultiByte(CP_UTF8, 0, str.data(), boost::lexical_cast(str.size()), &result[0], - boost::lexical_cast(result.size()), nullptr, nullptr); + auto si = WideCharToMultiByte(CP_UTF8, 0, str.data(), common::integer_cast(str.size()), &result[0], + common::integer_cast(result.size()), nullptr, nullptr); result.resize(si); return result; } diff --git a/src/platform/Network.cpp b/src/platform/Network.cpp index 6d36e0ef..483f3fc5 100644 --- a/src/platform/Network.cpp +++ b/src/platform/Network.cpp @@ -4,8 +4,13 @@ #include "Network.hpp" #include "Time.hpp" #include "common/MemoryStreams.hpp" +#include "common/exception.hpp" #include "common/string.hpp" +#ifndef _WIN32 +#include +#endif + #ifdef _WIN32 #pragma comment(lib, "ws2_32.lib") // Windows SDK sockets #pragma comment(lib, "wsock32.lib") // Windows SDK sockets @@ -505,7 +510,15 @@ class TCPSocket::Impl { , pending_connect(false) , socket(EventLoop::current()->io()) , incoming_buffer(8192) - , outgoing_buffer(8192) {} + , outgoing_buffer(8192) { + // std::cout << std::hex << "TCPSocket::Impl this=" << (size_t)this << " owner=" << (size_t)owner << + // std::dec + //<< std::endl; + } + ~Impl() { + // std::cout << std::hex << "TCPSocket::~Impl this=" << (size_t)this << " owner=" << (size_t)owner << + // std::dec << std::endl; + } TCPSocket *owner; bool connected; bool asked_shutdown; @@ -529,7 +542,11 @@ class TCPSocket::Impl { socket.close(); TCPSocket *was_owner = owner; if (pending_write || pending_read || pending_connect) { + // if(socket.lowest_layer().is_open()) owner = nullptr; + // std::cout << std::hex << "Socket close this=" << (size_t)this << " owner=" << (size_t)owner << " + // was_owner=" << (size_t)was_owner << std::dec << " flags" << pending_write << pending_read << + // pending_connect << std::endl; if (was_owner) // error can happen on detached impl was_owner->impl = std::make_shared(was_owner); } else { @@ -562,6 +579,8 @@ class TCPSocket::Impl { void handle_connect(const boost::system::error_code &e) { if (!e) { +// std::cout << std::hex << "Socket handle_connect this=" << (size_t)this << " owner=" << (size_t)owner << " +// was_owner=" << std::dec << " flags" << pending_write << pending_read << pending_connect << std::endl; #if platform_USE_SSL if (ssl_socket) { start_handshake(ssl::stream_base::client); @@ -597,6 +616,9 @@ class TCPSocket::Impl { } void handle_read(const boost::system::error_code &e, std::size_t bytes_transferred) { + // std::cout << std::hex << "Socket handle_read this=" << (size_t)this << " owner=" << (size_t)owner << " + // e=" + //<< !!e << std::dec << " flags" << pending_write << pending_read << pending_connect << std::endl; pending_read = false; if (!e) { if (!asked_shutdown) @@ -751,7 +773,6 @@ class TCPAcceptor::Impl { explicit Impl(TCPAcceptor *owner, bool ssl) : owner(owner) , ssl(ssl) - , pending_accept(false) , acceptor(EventLoop::current()->io()) , socket_being_accepted(EventLoop::current()->io()) #if platform_USE_SSL @@ -759,18 +780,18 @@ class TCPAcceptor::Impl { , ssl_socket_being_accepted( ssl ? std::make_unique(EventLoop::current()->io(), *ssl_context) : nullptr) #endif - , socket_ready(false) { + { } TCPAcceptor *owner; const bool ssl; - bool pending_accept; + bool pending_accept = false; boost::asio::ip::tcp::acceptor acceptor; boost::asio::ip::tcp::socket socket_being_accepted; #if platform_USE_SSL std::shared_ptr ssl_context; std::unique_ptr ssl_socket_being_accepted; #endif - bool socket_ready; + bool socket_ready = false; void close() { acceptor.close(); @@ -807,8 +828,8 @@ class TCPAcceptor::Impl { }; TCPAcceptor::TCPAcceptor(const std::string &addr, uint16_t port, A_handler a_handler, const std::string &ssl_pem_file, - const std::string &ssl_certificate_password) - : impl(std::make_shared(this, !ssl_pem_file.empty())), a_handler(a_handler) { + const std::string &ssl_certificate_password) try : impl(std::make_shared(this, !ssl_pem_file.empty())), + a_handler(a_handler) { #if platform_USE_SSL if (impl->ssl) { @@ -832,6 +853,9 @@ TCPAcceptor::TCPAcceptor(const std::string &addr, uint16_t port, A_handler a_han impl->acceptor.listen(); impl->start_accept(); +} catch (const boost::system::system_error &) { + std::throw_with_nested(AddressInUse("Failed to create TCP listening socket, probably address in use addr=" + addr + + " port=" + common::to_string(port))); } TCPAcceptor::~TCPAcceptor() { impl->close(); } @@ -876,6 +900,135 @@ bool TCPAcceptor::accept(TCPSocket &socket, std::string &accepted_addr) { return true; } +std::vector TCPAcceptor::local_addresses(bool ipv4, bool ipv6){ + std::vector result; +#ifndef _WIN32 // TODO - get adapters info on Win32 + struct ifaddrs *ifaddr = nullptr; + if (getifaddrs(&ifaddr) == -1) + return result; + + for (struct ifaddrs * ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + int family = ifa->ifa_addr->sa_family;; + if (family != AF_INET && family != AF_INET6) + continue; + char host[NI_MAXHOST]{}; + int s = getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if( s == 0 && ipv4 && family == AF_INET) + result.push_back(host); + if( s == 0 && ipv6 && family == AF_INET6) + result.push_back(host); + } + freeifaddrs(ifaddr); +#endif + return result; +} + + +class UDPMulticast::Impl { +public: + explicit Impl(UDPMulticast *owner) : owner(owner), socket(EventLoop::current()->io()) {} + UDPMulticast *owner; + boost::asio::ip::udp::socket socket; + boost::asio::ip::udp::endpoint sender_endpoint; + enum { max_length = 1024 }; + unsigned char data[max_length]; + bool pending_read = false; + + void close() { + socket.close(); + UDPMulticast *was_owner = owner; + owner = nullptr; + if (pending_read) { + if (was_owner) // error can happen on detached impl + was_owner->impl.reset(); // We do not reuse UDP Multicasts + } + } + void start_read() { + if (!owner) + return; + pending_read = true; + socket.async_receive_from( + boost::asio::buffer(data, max_length), sender_endpoint, std::bind(&Impl::handle_read, owner->impl, _1, _2)); + } + void handle_read(const boost::system::error_code &e, size_t bytes_recvd) { + pending_read = false; + if (!e) { + std::string sender_addr = sender_endpoint.address().to_string(); + std::vector data_copy(data, data + bytes_recvd); + if (owner) { + start_read(); // Can modify sender_endpoint and data here + owner->p_handler(sender_addr, data_copy.data(), data_copy.size()); + } + } + if (e != boost::asio::error::operation_aborted) { + // some nasty problem with socket, say so to the client + } + } +}; + +UDPMulticast::UDPMulticast(const std::string &addr, uint16_t port, P_handler p_handler) + : impl(std::make_shared(this)), p_handler(p_handler) { + try { + // Multiple processes can only bind to multicast socket if listen_ad is multicast addr + boost::asio::ip::address listen_ad = boost::asio::ip::address::from_string(addr); + boost::asio::ip::address group_ad = boost::asio::ip::address::from_string(addr); + boost::asio::ip::udp::endpoint listen_endpoint(listen_ad, port); + impl->socket.open(listen_endpoint.protocol()); + impl->socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); + + // boost::asio::ip::multicast::enable_loopback option; + // impl->socket.get_option(option); + // bool is_set = option.value(); + + // impl->socket.set_option(boost::asio::ip::multicast::enable_loopback(false)); + + impl->socket.bind(listen_endpoint); + + impl->socket.set_option(boost::asio::ip::multicast::join_group(group_ad)); + impl->start_read(); + + auto local_addresses = TCPAcceptor::local_addresses(true, false); + for(const auto & la : local_addresses) + std::cout << "UDPMulticast::UDPMulticast listening on local address " << la << std::endl; + } catch (const std::exception & ex) { + std::cout << "UDPMulticast::UDPMulticast exception " << common::what(ex) << std::endl; + } +} +UDPMulticast::~UDPMulticast() { impl->close(); } +void UDPMulticast::send(const std::string &addr, uint16_t port, const void *data, size_t size) { + try { + // Multicast will not work on loopback + { + // boost::asio::ip::address local_ad = boost::asio::ip::address::from_string("127.0.0.1"); + // boost::asio::ip::udp::endpoint local_ep(local_ad, port); + // boost::asio::ip::udp::socket local_socket(EventLoop::current()->io(), local_ep.protocol()); + // local_socket.send_to(boost::asio::buffer(data, size), local_ep); + } + { + boost::asio::ip::address ad = boost::asio::ip::address::from_string(addr); + boost::asio::ip::udp::endpoint ep(ad, port); + boost::asio::ip::udp::socket socket(EventLoop::current()->io(), ep.protocol()); + +// socket.set_option(boost::asio::ip::multicast::enable_loopback(true)); +// socket.set_option(boost::asio::ip::multicast::hops(2)); + auto local_addresses = TCPAcceptor::local_addresses(true, false); + for(const auto & la : local_addresses){ + boost::asio::ip::address_v4 local_interface = + boost::asio::ip::address_v4::from_string(la); + socket.set_option(boost::asio::ip::multicast::outbound_interface(local_interface)); + socket.send_to(boost::asio::buffer(data, size), ep); + } + if(local_addresses.empty()) // Send on default gateway + socket.send_to(boost::asio::buffer(data, size), ep); + } + } catch (const std::exception & ex) { + std::cout << "UDPMulticast::send exception to addr=" << addr << " port=" << port << " error=" << common::what(ex) << std::endl; + } +} + #endif // #if TARGET_OS_IPHONE // Code to stress-test timers diff --git a/src/platform/Network.hpp b/src/platform/Network.hpp index 7f1ae006..2cbde9c3 100644 --- a/src/platform/Network.hpp +++ b/src/platform/Network.hpp @@ -228,6 +228,12 @@ class TCPSocket : public common::IInputStream, public common::IOutputStream, pri class TCPAcceptor : private common::Nocopy { public: typedef std::function A_handler; + class AddressInUse : public std::runtime_error { + public: + explicit AddressInUse(const std::string &msg) : std::runtime_error(msg) {} + }; + + static std::vector local_addresses(bool ipv4, bool ipv6); explicit TCPAcceptor(const std::string &addr, uint16_t port, A_handler a_handler, const std::string &ssl_pem_file = std::string(), const std::string &ssl_certificate_password = std::string()); @@ -245,5 +251,18 @@ class TCPAcceptor : private common::Nocopy { std::shared_ptr impl; // Owned by boost async machinery A_handler a_handler; }; + +// Experimental zero-config for finding local peers (good for testnets) +class UDPMulticast : private common::Nocopy { +public: + typedef std::function P_handler; + UDPMulticast(const std::string &addr, uint16_t port, P_handler p_handler); + ~UDPMulticast(); + static void send(const std::string &addr, uint16_t port, const void *data, size_t size); // simple synchronous send +private: + class Impl; + std::shared_ptr impl; // Owned by boost async machinery + P_handler p_handler; +}; } #endif diff --git a/src/platform/PathTools.cpp b/src/platform/PathTools.cpp index 1e67b228..4bebb612 100644 --- a/src/platform/PathTools.cpp +++ b/src/platform/PathTools.cpp @@ -3,9 +3,9 @@ #include "PathTools.hpp" #include -#include #include #include "Files.hpp" +#include "common/Math.hpp" #ifdef __APPLE__ #include "TargetConditionals.h" @@ -440,7 +440,7 @@ bool load_file(const std::string &filepath, std::string &buf) { FileStream fs; // Allowed because we are friends if (!fs.try_open(filepath, FileStream::READ_EXISTING)) return false; - size_t file_size = boost::lexical_cast(fs.seek(0, SEEK_END)); + size_t file_size = common::integer_cast(fs.seek(0, SEEK_END)); fs.seek(0, SEEK_SET); buf.resize(file_size); fs.read(&buf[0], buf.size()); @@ -455,7 +455,7 @@ bool load_file(const std::string &filepath, common::BinaryArray &buf) { FileStream fs; // Allowed because we are friends if (!fs.try_open(filepath, FileStream::READ_EXISTING)) return false; - size_t file_size = boost::lexical_cast(fs.seek(0, SEEK_END)); + size_t file_size = common::integer_cast(fs.seek(0, SEEK_END)); fs.seek(0, SEEK_SET); buf.resize(file_size); fs.read(buf.data(), buf.size()); diff --git a/src/rpc_api.cpp b/src/rpc_api.cpp new file mode 100644 index 00000000..a424dfac --- /dev/null +++ b/src/rpc_api.cpp @@ -0,0 +1,430 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include "rpc_api.hpp" +#include "Core/CryptoNoteTools.hpp" +#include "Core/TransactionExtra.hpp" +#include "seria/JsonOutputStream.hpp" + +using namespace bytecoin; + +api::ErrorAddress::ErrorAddress(int c, const std::string &msg, const std::string &address) + : json_rpc::Error(c, msg + " address=" + address), address(address) {} +api::ErrorWrongHeight::ErrorWrongHeight(const std::string &msg, HeightOrDepth request_height, Height top_block_height) + : json_rpc::Error(INVALID_HEIGHT_OR_DEPTH, + msg + " request_height=" + common::to_string(request_height) + " top_block_height=" + common::to_string( + top_block_height)) + , request_height(request_height) + , top_block_height(top_block_height) {} +Height api::ErrorWrongHeight::fix_height_or_depth( + HeightOrDepth ha, Height tip_height, bool throw_on_too_big_height, bool throw_on_too_big_depth, Height max_depth) { + if (ha < 0) { + ha = static_cast(tip_height) + 1 + ha; + if (ha < 0) { + if(throw_on_too_big_depth) + throw ErrorWrongHeight("height_or_depth cannot be deeper than genesis block", ha, tip_height); + ha = 0; + } + } + if (max_depth != std::numeric_limits::max() && ha + max_depth < tip_height) + throw ErrorWrongHeight( + "height_or_depth cannot be deeper than " + common::to_string(max_depth) + " blocks from top block", ha, + tip_height); + if (ha > static_cast(tip_height)) { + if (throw_on_too_big_height) + throw ErrorWrongHeight("height_or_depth cannot exceed top block height", ha, tip_height); + return tip_height; + } + return static_cast(ha); +} +void api::ErrorWrongHeight::seria_data_members(seria::ISeria &s) { + seria_kv("request_height", request_height, s); + seria_kv("top_block_height", top_block_height, s); +} +api::ErrorHashNotFound::ErrorHashNotFound(const std::string &msg, const Hash &hash) + : json_rpc::Error(HASH_NOT_FOUND, msg + " request_hasht=" + common::pod_to_hex(hash)), hash(hash) {} +void api::ErrorHashNotFound::seria_data_members(seria::ISeria &s) { seria_kv("hash", hash, s); } + +void api::ErrorAddress::seria_data_members(seria::ISeria &s) { seria_kv("address", address, s); } +void api::walletd::SendTransaction::Error::seria_data_members(seria::ISeria &s) { + seria_kv("conflict_height", conflict_height, s); +} +void api::bytecoind::GetArchive::Error::seria_data_members(seria::ISeria &s) { seria_kv("archive_id", archive_id, s); } + +Hash Checkpoint::get_message_hash() const { return get_object_hash(*this); } + +bool api::walletd::GetStatus::Response::ready_for_longpoll(const Request &other) const { + if (other.top_block_hash && top_block_hash != other.top_block_hash.get()) + return true; + if (other.transaction_pool_version && transaction_pool_version != other.transaction_pool_version.get()) + return true; + if (other.outgoing_peer_count && outgoing_peer_count != other.outgoing_peer_count.get()) + return true; + if (other.incoming_peer_count && incoming_peer_count != other.incoming_peer_count.get()) + return true; + if (other.lower_level_error && lower_level_error != other.lower_level_error.get()) + return true; + return !other.top_block_hash && !other.transaction_pool_version && !other.outgoing_peer_count && + !other.incoming_peer_count && !other.lower_level_error; +} + +namespace seria { + +void ser_members(api::Output &v, ISeria &s) { + seria_kv("public_key", v.public_key, s); + seria_kv("index", v.index, s); + if (dynamic_cast(&s)) + seria_kv("global_index", v.index, s); // deprecated + if (dynamic_cast(&s)) + seria_kv("global_index", v.index, s); // deprecated + seria_kv("amount", v.amount, s); + seria_kv("unlock_block_or_timestamp", v.unlock_block_or_timestamp, s); + if (dynamic_cast(&s)) + seria_kv("unlock_time", v.unlock_block_or_timestamp, s); // deprecated + if (dynamic_cast(&s)) + seria_kv("unlock_time", v.unlock_block_or_timestamp, s); // deprecated + seria_kv("index_in_transaction", v.index_in_transaction, s); + seria_kv("height", v.height, s); + seria_kv("key_image", v.key_image, s); + seria_kv("transaction_public_key", v.transaction_public_key, s); + seria_kv("address", v.address, s); + seria_kv("dust", v.dust, s); +} +void ser_members(api::BlockHeader &v, ISeria &s) { + seria_kv("major_version", v.major_version, s); + seria_kv("minor_version", v.minor_version, s); + seria_kv("timestamp", v.timestamp, s); + seria_kv("previous_block_hash", v.previous_block_hash, s); + seria_kv("nonce", v.nonce, s); + + seria_kv("height", v.height, s); + seria_kv("hash", v.hash, s); + seria_kv("reward", v.reward, s); + seria_kv("cumulative_difficulty", v.cumulative_difficulty.lo, s); + seria_kv_optional("cumulative_difficulty_hi", v.cumulative_difficulty.hi, s); + seria_kv("difficulty", v.difficulty, s); + seria_kv("base_reward", v.base_reward, s); + seria_kv("block_size", v.block_size, s); + seria_kv("transactions_size", v.transactions_size, s); + seria_kv("already_generated_coins", v.already_generated_coins, s); + seria_kv("already_generated_transactions", v.already_generated_transactions, s); + seria_kv("size_median", v.size_median, s); + seria_kv("effective_size_median", v.effective_size_median, s); + seria_kv("timestamp_median", v.timestamp_median, s); + seria_kv("transactions_fee", v.transactions_fee, s); +} +void ser_members(api::bytecoind::BlockHeaderLegacy &v, ISeria &s) { + ser_members(static_cast(v), s); + seria_kv("prev_hash", v.previous_block_hash, s); + seria_kv("depth", v.depth, s); + seria_kv("orphan_status", v.orphan_status, s); + seria_kv("transactions_cumulative_size", v.transactions_size, s); + seria_kv("total_fee_amount", v.transactions_fee, s); +} +void ser_members(api::Transfer &v, ISeria &s) { + seria_kv("address", v.address, s); + seria_kv("amount", v.amount, s); + seria_kv("ours", v.ours, s); + seria_kv("locked", v.locked, s); + seria_kv("outputs", v.outputs, s); + if (dynamic_cast(&s)) + seria_kv("transaction_hash", v.transaction_hash, s); +} +void ser_members(api::Transaction &v, ISeria &s) { + seria_kv("unlock_block_or_timestamp", v.unlock_block_or_timestamp, s); + if (dynamic_cast(&s)) + seria_kv("unlock_time", v.unlock_block_or_timestamp, s); // deprecated + if (dynamic_cast(&s)) + seria_kv("unlock_time", v.unlock_block_or_timestamp, s); // deprecated + seria_kv("amount", v.amount, s); + seria_kv("fee", v.fee, s); + seria_kv("public_key", v.public_key, s); + seria_kv_optional("transfers", v.transfers, s); + seria_kv("payment_id", v.payment_id, s); + seria_kv("anonymity", v.anonymity, s); + seria_kv("extra", v.extra, s); + seria_kv("hash", v.hash, s); + seria_kv("coinbase", v.coinbase, s); + seria_kv("block_height", v.block_height, s); + seria_kv("block_hash", v.block_hash, s); + seria_kv("timestamp", v.timestamp, s); + seria_kv("size", v.size, s); + if (dynamic_cast(&s)) + seria_kv("binary_size", v.size, s); // deprecated + if (dynamic_cast(&s)) + seria_kv("binary_size", v.size, s); // deprecated +} +void ser_members(api::Block &v, ISeria &s) { + seria_kv("header", v.header, s); + seria_kv("transactions", v.transactions, s); +} +void ser_members(api::RawBlock &v, ISeria &s) { + seria_kv("header", v.header, s); + seria_kv("raw_header", v.raw_header, s); + seria_kv("raw_transactions", v.raw_transactions, s); + seria_kv("signatures", v.signatures, s); + seria_kv("transactions", v.transactions, s); + seria_kv("output_indexes", v.output_indexes, s); +} +void ser_members(api::Balance &v, ISeria &s) { + seria_kv("spendable", v.spendable.lo, s); + seria_kv_optional("spendable_hi", v.spendable.hi, s); + + seria_kv("spendable_dust", v.spendable_dust.lo, s); + seria_kv_optional("spendable_dust_hi", v.spendable_dust.hi, s); + + seria_kv("locked_or_unconfirmed", v.locked_or_unconfirmed.lo, s); + seria_kv_optional("locked_or_unconfirmed_hi", v.locked_or_unconfirmed.hi, s); + + seria_kv("spendable_outputs", v.spendable_outputs, s); + seria_kv("spendable_dust_outputs", v.spendable_dust_outputs, s); + seria_kv("locked_or_unconfirmed_outputs", v.locked_or_unconfirmed_outputs, s); +} +void ser_members(api::EmptyStruct &, ISeria &) {} + +void ser_members(api::walletd::GetAddresses::Request &v, ISeria &s) { + seria_kv("need_secret_spend_keys", v.need_secret_spend_keys, s); + seria_kv("from_address", v.from_address, s); + seria_kv("max_count", v.max_count, s); +} +void ser_members(api::walletd::GetAddresses::Response &v, ISeria &s) { + seria_kv("total_address_count", v.total_address_count, s); + seria_kv("addresses", v.addresses, s); + seria_kv_optional("secret_spend_keys", v.secret_spend_keys, s); +} +void ser_members(bytecoin::api::walletd::GetWalletInfo::Response &v, ISeria &s) { + seria_kv("view_only", v.view_only, s); + seria_kv("wallet_creation_timestamp", v.wallet_creation_timestamp, s); + seria_kv("total_address_count", v.total_address_count, s); + seria_kv("mineproof_secret", v.mineproof_secret, s); + seria_kv("first_address", v.first_address, s); + seria_kv("net", v.net, s); +} +void ser_members(api::walletd::GetViewKeyPair::Response &v, ISeria &s) { + seria_kv("secret_view_key", v.secret_view_key, s); + seria_kv("public_view_key", v.public_view_key, s); + seria_kv("import_keys", v.import_keys, s); +} +void ser_members(api::walletd::CreateAddresses::Request &v, ISeria &s) { + seria_kv("secret_spend_keys", v.secret_spend_keys, s); + seria_kv("creation_timestamp", v.creation_timestamp, s); +} +void ser_members(api::walletd::CreateAddresses::Response &v, ISeria &s) { + seria_kv("addresses", v.addresses, s); + seria_kv("secret_spend_keys", v.secret_spend_keys, s); +} +void ser_members(api::walletd::GetBalance::Request &v, ISeria &s) { + seria_kv("address", v.address, s); + seria_kv("height_or_depth", v.height_or_depth, s); +} +void ser_members(api::walletd::GetUnspents::Request &v, ISeria &s) { + seria_kv("address", v.address, s); + seria_kv("height_or_depth", v.height_or_depth, s); +} +void ser_members(api::walletd::GetUnspents::Response &v, ISeria &s) { + seria_kv("spendable", v.spendable, s); + seria_kv("locked_or_unconfirmed", v.locked_or_unconfirmed, s); +} +void ser_members(api::walletd::GetTransfers::Request &v, ISeria &s) { + seria_kv("address", v.address, s); + seria_kv("from_height", v.from_height, s); + seria_kv("to_height", v.to_height, s); + seria_kv("desired_transaction_count", v.desired_transaction_count, s); + if (s.is_input()) + seria_kv("desired_transactions_count", v.desired_transaction_count, s); // deprecated + seria_kv("forward", v.forward, s); +} +void ser_members(api::walletd::GetTransfers::Response &v, ISeria &s) { + seria_kv("blocks", v.blocks, s); + seria_kv("unlocked_transfers", v.unlocked_transfers, s); + seria_kv("next_from_height", v.next_from_height, s); + seria_kv("next_to_height", v.next_to_height, s); +} +void ser_members(api::walletd::CreateTransaction::Request &v, ISeria &s) { + seria_kv("transaction", v.transaction, s); + seria_kv("spend_addresses", v.spend_addresses, s); + seria_kv("any_spend_address", v.any_spend_address, s); + seria_kv("change_address", v.change_address, s); + seria_kv("confirmed_height_or_depth", v.confirmed_height_or_depth, s); + seria_kv("fee_per_byte", v.fee_per_byte, s); + seria_kv("optimization", v.optimization, s); + seria_kv("save_history", v.save_history, s); + seria_kv("prevent_conflict_with_transactions", v.prevent_conflict_with_transactions, s); +} +void ser_members(api::walletd::CreateTransaction::Response &v, ISeria &s) { + seria_kv("transaction", v.transaction, s); + seria_kv("binary_transaction", v.binary_transaction, s); + seria_kv("save_history_error", v.save_history_error, s); + seria_kv("transactions_required", v.transactions_required, s); +} +void ser_members(api::walletd::CreateSendProof::Request &v, ISeria &s) { + seria_kv("transaction_hash", v.transaction_hash, s); + seria_kv("message", v.message, s); + seria_kv("addresses", v.addresses, s); +} +void ser_members(api::walletd::CreateSendProof::Response &v, ISeria &s) { seria_kv("sendproofs", v.sendproofs, s); } + +void ser_members(api::bytecoind::GetStatus::Request &v, ISeria &s) { + seria_kv("top_block_hash", v.top_block_hash, s); + seria_kv("transaction_pool_version", v.transaction_pool_version, s); + seria_kv("outgoing_peer_count", v.outgoing_peer_count, s); + seria_kv("incoming_peer_count", v.incoming_peer_count, s); + seria_kv("lower_level_error", v.lower_level_error, s); +} +void ser_members(api::bytecoind::GetStatus::Response &v, ISeria &s) { + seria_kv("top_block_hash", v.top_block_hash, s); + seria_kv("transaction_pool_version", v.transaction_pool_version, s); + seria_kv("outgoing_peer_count", v.outgoing_peer_count, s); + seria_kv("incoming_peer_count", v.incoming_peer_count, s); + seria_kv("lower_level_error", v.lower_level_error, s); + + seria_kv("top_block_height", v.top_block_height, s); + seria_kv("top_block_difficulty", v.top_block_difficulty, s); + seria_kv("top_block_cumulative_difficulty", v.top_block_cumulative_difficulty.lo, s); + seria_kv_optional("top_block_cumulative_difficulty_hi", v.top_block_cumulative_difficulty.hi, s); + seria_kv("top_block_timestamp", v.top_block_timestamp, s); + seria_kv("top_block_timestamp_median", v.top_block_timestamp_median, s); + seria_kv("recommended_fee_per_byte", v.recommended_fee_per_byte, s); + seria_kv("next_block_effective_median_size", v.next_block_effective_median_size, s); + seria_kv("top_known_block_height", v.top_known_block_height, s); +} +void ser_members(api::bytecoind::GetBlockHeader::Request &v, ISeria &s) { + seria_kv("hash", v.hash, s); + seria_kv("height_or_depth", v.height_or_depth, s); +} +void ser_members(api::bytecoind::GetBlockHeader::Response &v, ISeria &s) { + seria_kv("block_header", v.block_header, s); + seria_kv("orphan_status", v.orphan_status, s); + seria_kv("depth", v.depth, s); +} +void ser_members(api::bytecoind::GetRawBlock::Request &v, ISeria &s) { + seria_kv("hash", v.hash, s); + seria_kv("height_or_depth", v.height_or_depth, s); + seria_kv("need_signatures", v.need_signatures, s); +} +void ser_members(api::bytecoind::GetRawBlock::Response &v, ISeria &s) { + seria_kv("block", v.block, s); + seria_kv("orphan_status", v.orphan_status, s); + seria_kv("depth", v.depth, s); +} +void ser_members(api::bytecoind::SyncBlocks::Request &v, ISeria &s) { + seria_kv("sparse_chain", v.sparse_chain, s); + seria_kv("first_block_timestamp", v.first_block_timestamp, s); + seria_kv("max_count", v.max_count, s); + seria_kv("need_signatures", v.need_signatures, s); + seria_kv("need_redundant_data", v.need_redundant_data, s); +} +void ser_members(api::bytecoind::SyncBlocks::Response &v, ISeria &s) { + seria_kv("blocks", v.blocks, s); + seria_kv("start_height", v.start_height, s); + seria_kv("status", v.status, s); +} +void ser_members(api::bytecoind::GetRawTransaction::Request &v, ISeria &s) { + seria_kv("hash", v.hash, s); + seria_kv("need_signatures", v.need_signatures, s); +} +void ser_members(api::bytecoind::GetRawTransaction::Response &v, ISeria &s) { + seria_kv("transaction", v.transaction, s); + seria_kv("raw_transaction", v.raw_transaction, s); + seria_kv("signatures", v.signatures, s); +} + +void ser_members(api::bytecoind::SyncMemPool::Request &v, ISeria &s) { + if (!s.is_input()) + std::sort(v.known_hashes.begin(), v.known_hashes.end()); + seria_kv("known_hashes", v.known_hashes, s); + if (s.is_input() && !std::is_sorted(v.known_hashes.begin(), v.known_hashes.end())) + throw std::runtime_error("SyncMemPool::Request known_hashes must be sorted in increasing order (from [0000..] to [ffff..])"); + seria_kv("need_signatures", v.need_signatures, s); + seria_kv("need_redundant_data", v.need_redundant_data, s); +} +void ser_members(api::bytecoind::SyncMemPool::Response &v, ISeria &s) { + seria_kv("removed_hashes", v.removed_hashes, s); + seria_kv("added_raw_transactions", v.added_raw_transactions, s); + seria_kv("added_transactions", v.added_transactions, s); + seria_kv("status", v.status, s); +} +void ser_members(api::bytecoind::GetRandomOutputs::Request &v, ISeria &s) { + seria_kv("amounts", v.amounts, s); + seria_kv("output_count", v.output_count, s); + if (s.is_input()) + seria_kv("outs_count", v.output_count, s); // deprecated + seria_kv("confirmed_height_or_depth", v.confirmed_height_or_depth, s); +} +void ser_members(api::bytecoind::GetRandomOutputs::Response &v, ISeria &s) { seria_kv("outputs", v.outputs, s); } +void ser_members(api::bytecoind::SendTransaction::Request &v, ISeria &s) { + seria_kv("binary_transaction", v.binary_transaction, s); +} +void ser_members(api::bytecoind::SendTransaction::Response &v, ISeria &s) { seria_kv("send_result", v.send_result, s); } +void ser_members(api::bytecoind::CheckSendproof::Request &v, ISeria &s) { seria_kv("sendproof", v.sendproof, s); } +void ser_members(api::bytecoind::CheckSendproof::Response &v, ISeria &s) { + seria_kv("transaction_hash", v.transaction_hash, s); + seria_kv("address", v.address, s); + seria_kv("amount", v.amount, s); + seria_kv("message", v.message, s); +} + +void ser_members(api::bytecoind::GetArchive::ArchiveRecord &v, ISeria &s) { + seria_kv("timestamp", v.timestamp, s); + seria_kv("timestamp_usec", v.timestamp_usec, s); + seria_kv("type", v.type, s); + seria_kv("hash", v.hash, s); + seria_kv("source_address", v.source_address, s); +} +void ser_members(api::bytecoind::GetArchive::ArchiveBlock &v, ISeria &s) { + seria_kv("raw_header", v.raw_header, s); + seria_kv("raw_transactions", v.raw_transactions, s); + seria_kv("base_transaction_hash", v.base_transaction_hash, s); + seria_kv("transaction_binary_sizes", v.transaction_binary_sizes, s); +} +void ser_members(api::bytecoind::GetArchive::Request &v, ISeria &s) { + seria_kv("archive_id", v.archive_id, s); + seria_kv("from_record", v.from_record, s); + seria_kv("max_count", v.max_count, s); + seria_kv("records_only", v.records_only, s); +} +void ser_members(api::bytecoind::GetArchive::Response &v, ISeria &s) { + seria_kv("records", v.records, s); + seria_kv("from_record", v.from_record, s); + seria_kv("blocks", v.blocks, s); + seria_kv("transactions", v.transactions, s); + seria_kv("checkpoints", v.checkpoints, s); +} + +void ser_members(api::walletd::GetTransaction::Request &v, ISeria &s) { seria_kv("hash", v.hash, s); } +void ser_members(api::walletd::GetTransaction::Response &v, ISeria &s) { seria_kv("transaction", v.transaction, s); } + +void ser_members(api::bytecoind::GetBlockTemplate::Request &v, ISeria &s) { + seria_kv("reserve_size", v.reserve_size, s); + seria_kv("wallet_address", v.wallet_address, s); + seria_kv("top_block_hash", v.top_block_hash, s); + seria_kv("transaction_pool_version", v.transaction_pool_version, s); +} +void ser_members(api::bytecoind::GetBlockTemplate::Response &v, ISeria &s) { + seria_kv("difficulty", v.difficulty, s); + seria_kv("height", v.height, s); + seria_kv("reserved_offset", v.reserved_offset, s); + seria_kv("blocktemplate_blob", v.blocktemplate_blob, s); + seria_kv("status", v.status, s); + seria_kv("top_block_hash", v.top_block_hash, s); + seria_kv("transaction_pool_version", v.transaction_pool_version, s); + seria_kv("previous_block_hash", v.previous_block_hash, s); +} +void ser_members(api::bytecoind::GetCurrencyId::Response &v, ISeria &s) { + seria_kv("currency_id_blob", v.currency_id_blob, s); +} +void ser_members(api::bytecoind::SubmitBlock::Request &v, ISeria &s) { + seria_kv("blocktemplate_blob", v.blocktemplate_blob, s); +} +void ser_members(api::bytecoind::SubmitBlock::Response &v, ISeria &s) { seria_kv("block_header", v.block_header, s); } +void ser_members(api::bytecoind::SubmitBlockLegacy::Response &v, ISeria &s) { seria_kv("status", v.status, s); } +void ser_members(api::bytecoind::GetLastBlockHeaderLegacy::Response &v, ISeria &s) { + seria_kv("status", v.status, s); + seria_kv("block_header", v.block_header, s); +} +void ser_members(api::bytecoind::GetBlockHeaderByHashLegacy::Request &v, ISeria &s) { seria_kv("hash", v.hash, s); } +void ser_members(api::bytecoind::GetBlockHeaderByHeightLegacy::Request &v, ISeria &s) { + seria_kv("height", v.height, s); +} + +} // namespace seria diff --git a/src/rpc_api.hpp b/src/rpc_api.hpp index 1f520e1c..1708ce80 100644 --- a/src/rpc_api.hpp +++ b/src/rpc_api.hpp @@ -13,11 +13,13 @@ #include "common/Int128.hpp" #include "crypto/types.hpp" #include "http/JsonRpc.hpp" +#include "p2p/P2pProtocolNew.hpp" // Common data structures used in all api calls. // Basic data types are serialized to Json as follows // bool - Bool -// Amount, SignedAmount, Height, Timestamp, UnlockMoment, Difficulty, (u)int - Number. bytecoin does not use fractional +// Amount, SignedAmount, Height, Timestamp, BlockOrTimestamp, Difficulty, (u)int - Number. bytecoin does not use +// fractional // numbers, but uses numbers as large as 2^64-1 for amounts, which is larger than 2^53 exactly representable in double // or JavaScript Number // amounts large than ~91 million BCN cannot be represented exactly in JavaScript and other platforms using IEEE @@ -38,9 +40,10 @@ constexpr HeightOrDepth DEFAULT_CONFIRMATIONS = 6; struct Output { Amount amount = 0; PublicKey public_key; - uint32_t global_index = 0; + uint32_t index = 0; // Added from transaction - UnlockMoment unlock_time = 0; // timestamp | block_index, see function isTransactionSpendTimeUnlocked below + BlockOrTimestamp unlock_block_or_timestamp = + 0; // timestamp | height, see function isTransactionSpendTimeUnlocked below uint32_t index_in_transaction = 0; // # of output, output keys depend on transaction_public_key and this index, so // they are different for the same address // Added from block @@ -60,11 +63,13 @@ struct Transfer { bool locked = false; // locked transfers should not be added to balance immediately. They will be unlocked later // and sent via GetTransfers.Response.unlocked_transfers std::vector outputs; // Outputs corresponding to this transfer + Hash transaction_hash; // TODO - requires changing DB format... For now will be available only on Json seria }; struct Transaction { // Fields for new transactions. - UnlockMoment unlock_time = 0; // timestamp | block_index, see function isTransactionSpendTimeUnlocked below + BlockOrTimestamp unlock_block_or_timestamp = + 0; // timestamp | height, see function isTransactionSpendTimeUnlocked below std::vector transfers; // includes only transfers we can view Hash payment_id; // omit or set to all zero-hash to indicate no payment id. Will be DEPRECATED in future uint32_t anonymity = 0; // recommended to set to DEFAULT_ANONYMITY_LEVEL for new transactions, for existing @@ -83,7 +88,7 @@ struct Transaction { Timestamp timestamp = 0; // Timestamp of block if transaction is in block. For mempool transactions this is the // time node first seen this transaction. // Fields below are serialized only after V4 upgrade - uint32_t binary_size = 0; + uint32_t size = 0; }; struct BlockHeader { @@ -91,26 +96,26 @@ struct BlockHeader { uint8_t minor_version = 0; Timestamp timestamp = 0; Hash previous_block_hash; - uint32_t nonce = 0; + uint64_t nonce = 0; Height height = 0; Hash hash; Amount reward = 0; // Low part is serialized as "cumulative_difficulty", Hi part will be serialized as cumulative_difficulty_hi CumulativeDifficulty cumulative_difficulty{}; - Difficulty difficulty = 0; - Amount base_reward = 0; - uint32_t block_size = 0; // Only sum of all transactions including coinbase. - uint32_t transactions_cumulative_size = 0; // Sum of all transactions without coinbase. - Amount already_generated_coins = 0; + Difficulty difficulty = 0; + Amount base_reward = 0; + uint32_t block_size = 0; // in API True block size, in DB different value + uint32_t transactions_size = 0; // in API Sum of all transactions including coinbase., in DB different value + Amount already_generated_coins = 0; uint64_t already_generated_transactions = 0; - uint32_t size_median = 0; + uint32_t size_median = 0; // median of transactions_size uint32_t effective_size_median = 0; // max(100000, size_median) for block version 3, allows sudden peaks in network load. Timestamp timestamp_median = 0; - Amount total_fee_amount = 0; + Amount transactions_fee = 0; - double penalty() const { - return base_reward == 0 ? 0 : double(base_reward - reward) / base_reward; + Amount penalty() const { + return base_reward + transactions_fee - reward; } // We do not need trivial fields in API. Instead we provide an algorithm to calc. }; @@ -120,6 +125,17 @@ struct Block { // If got from walletd, will contain only transactions with transfers we can view. }; +struct RawBlock { + api::BlockHeader header; + BlockTemplate raw_header; + std::vector raw_transactions; + std::vector signatures; // empty unless request.signatures set + std::vector + transactions; // for each transaction + coinbase, contain only info known to bytecoind + std::vector> + output_indexes; // for each transaction + coinbase, not empty only if block in main chain +}; + // In view-only wallets sum of incoming outputs can be arbitrary large // Low part is serialized as "spendable", Hi part (if not 0) will be serialized as "spendable_hi" struct Balance { @@ -165,6 +181,36 @@ enum return_code { WALLETD_EXPORTKEYS_MORETHANONE = 212 // We can export keys only if wallet file contains exactly 1 spend keypair }; +// Returned from many methods +struct ErrorAddress : public json_rpc::Error { + std::string address; + ErrorAddress() {} + ErrorAddress(int c, const std::string &msg, const std::string &address); + void seria_data_members(seria::ISeria &s) override; + enum { ADDRESS_FAILED_TO_PARSE = -4, ADDRESS_NOT_IN_WALLET = -1002 }; +}; + +struct ErrorHashNotFound : public json_rpc::Error { + Hash hash; + ErrorHashNotFound() {} + ErrorHashNotFound(const std::string &msg, const Hash &hash); + void seria_data_members(seria::ISeria &s) override; + enum { + HASH_NOT_FOUND = -5 // Neither in main nor in side chain + }; +}; + +struct ErrorWrongHeight : public json_rpc::Error { + HeightOrDepth request_height = 0; + Height top_block_height = 0; + ErrorWrongHeight() {} + ErrorWrongHeight(const std::string &msg, HeightOrDepth request_height, Height top_block_height); + void seria_data_members(seria::ISeria &s) override; + enum { INVALID_HEIGHT_OR_DEPTH = -2 }; + static Height fix_height_or_depth(api::HeightOrDepth ha, Height tip_height, bool throw_on_too_big_height, + bool throw_on_too_big_depth, Height max_depth = std::numeric_limits::max()); +}; + namespace walletd { inline std::string url() { return "/json_rpc"; } @@ -175,24 +221,27 @@ struct GetStatus { static std::string method() { return "get_status"; } // get_status of node is shadowed by walletd. struct Request { + boost::optional top_block_hash; + boost::optional transaction_pool_version; + // Pool version is reset to 1 on every new block. Pool version is + // incremented on every modification to pool. + boost::optional outgoing_peer_count; + boost::optional incoming_peer_count; + // You get longpoll (no immediate reply) until any parameter changes. + // You can just send previous response as a next request if you are interested in all changes visible to API. + boost::optional lower_level_error; + // Problems on lower levels (like bytecoind errors in walletd status). Empty - no errors + }; + struct Response { // Response and Request have fields in common. Hash top_block_hash; - uint32_t transaction_pool_version = 0; // Pool version is reset to 1 on every new block. Pool version is - // incremented on every modification to pool. + uint32_t transaction_pool_version = 0; + // Pool version is reset to 1 on every new block. Pool version is + // incremented on every modification to pool. uint32_t outgoing_peer_count = 0; uint32_t incoming_peer_count = 0; // You get longpoll (no immediate reply) until any parameter changes. // You can just send previous response as a next request if you are interested in all changes visible to API. - std::string - lower_level_error; // Problems on lower levels (like bytecoind errors in walletd status). Empty - no errors - - bool operator==(const Request &other) const { - return lower_level_error == other.lower_level_error && top_block_hash == other.top_block_hash && - transaction_pool_version == other.transaction_pool_version && - outgoing_peer_count == other.outgoing_peer_count && incoming_peer_count == other.incoming_peer_count; - } - bool operator!=(const Request &other) const { return !operator==(other); } - }; - struct Response : public Request { // Response and Request have fields in common. + std::string lower_level_error; // Last block analyzed and synched by wallet or node. Height top_block_height = 0; Height top_known_block_height = 0; // Max of heights reported by p2p peers. @@ -201,23 +250,43 @@ struct GetStatus { Amount recommended_fee_per_byte = 0; Timestamp top_block_timestamp = 0; Timestamp top_block_timestamp_median = 0; // This timestamp will be used in unlock calulations after hardfork. - uint32_t next_block_effective_median_size = - 0; // If your tx size is larger, chance it will not be included in block for a long time (or never). + uint32_t next_block_effective_median_size = 0; + // If your tx size is larger, chance it will not be included in block for a long time (or never). + bool ready_for_longpoll(const Request &other) const; }; }; struct GetAddresses { // For simple GUI client, display addresses[0] as main one, show rest of the addresses in popup. static std::string method() { return "get_addresses"; } + struct Request { + bool need_secret_spend_keys = false; + uint32_t from_address = 0; // We can now iterate through addresses + uint32_t max_count = std::numeric_limits::max(); + }; + struct Response { + std::vector addresses; // starting from from_address up to max_count + std::vector secret_spend_keys; // not empty only if need_secret_spend_keys specified + uint32_t total_address_count = 0; // Usefull when when iterating + }; +}; + +struct GetWalletInfo { + static std::string method() { return "get_wallet_info"; } + typedef EmptyStruct Request; struct Response { - std::vector addresses; - bool view_only = false; + bool view_only = false; + Timestamp wallet_creation_timestamp = 0; // O if not known (restored form keys and did not sync yet) + std::string first_address; + uint32_t total_address_count = 0; // Usefull when when iterating + std::string net; // Now - network walletd is launch on. Future - network for which the wallet is generated + Hash mineproof_secret; // Highly experimental }; }; -struct GetViewKeyPair { +struct GetViewKeyPair { // Will be deprecated or heavily changed in Future static std::string method() { return "get_view_key_pair"; } typedef EmptyStruct Request; @@ -225,6 +294,9 @@ struct GetViewKeyPair { struct Response { SecretKey secret_view_key; PublicKey public_view_key; + BinaryArray import_keys; + // passing this value as --import-keys parameter to walletd would recreate wallet with + // first address only, then you can call create_addresses passing other secret keys from your DB }; }; @@ -254,6 +326,11 @@ struct GetBalance { // height_or_depth is limited to arbitrary selected depth of 128 blocks before top block }; typedef api::Balance Response; + enum { + ADDRESS_FAILED_TO_PARSE = -4, // returns ErrorAddress + INVALID_HEIGHT_OR_DEPTH = -2, // height_or_depth too low or too high + ADDRESS_NOT_IN_WALLET = -1002 // returns ErrorAddress + }; }; struct GetUnspents { @@ -269,6 +346,11 @@ struct GetUnspents { std::vector spendable; // Confirmed, unlocked. Dust is also returned here std::vector locked_or_unconfirmed; // Unconfirmed or locked }; + enum { + ADDRESS_FAILED_TO_PARSE = -4, // returns ErrorAddress + INVALID_HEIGHT_OR_DEPTH = -2, // height_or_depth too low or too high + ADDRESS_NOT_IN_WALLET = -1002 // returns ErrorAddress + }; }; struct GetTransfers { // Can be used incrementally by high-performace clients to monitor incoming transfers @@ -280,29 +362,34 @@ struct GetTransfers { // Can be used incrementally by high-performace clients t Height to_height = std::numeric_limits::max(); // Up to, and including to_height. Will return // transfers in mempool if to_height > // top_block_height - bool forward = true; // determines order of blocks returned, additionally if desired_transactions_count set, + bool forward = true; // determines order of blocks returned, additionally if desired_transaction_count set, // then this defines if call starts from from_height forward, or from to_height backwards - uint32_t desired_transactions_count = + uint32_t desired_transaction_count = std::numeric_limits::max(); // Will return this number of transactions or a bit more, It can // return more, because this call always returns full blocks }; struct Response { std::vector blocks; // includes only blocks with transactions with transfers we can view std::vector unlocked_transfers; // Previous transfers unlocked between from_height and to_height - Height next_from_height = 0; // When desired_transactions_count != max you can pass next* to corresponding + Height next_from_height = 0; // When desired_transaction_count != max you can pass next* to corresponding // Request fields to continue iteration Height next_to_height = 0; }; + enum { + ADDRESS_FAILED_TO_PARSE = -4, // returns ErrorAddress + ADDRESS_NOT_IN_WALLET = -1002 // returns ErrorAddress + }; }; struct CreateTransaction { static std::string method() { return "create_transaction"; } struct Request { - api::Transaction transaction; // You fill only basic info (anonymity, optional unlock_time, optional - // payment_id) and transfers. All positive transfers (amount > 0) will be added - // as outputs. For all negative transfers (amount < 0), spendable for requested - // sum and address will be selected and added as inputs + api::Transaction + transaction; // You fill only basic info (anonymity, optional unlock_block_or_timestamp, optional + // payment_id) and transfers. All positive transfers (amount > 0) will be added + // as outputs. For all negative transfers (amount < 0), spendable for requested + // sum and address will be selected and added as inputs std::vector spend_addresses; // If this is not empty, will spend (and optimize) outputs for this addresses to get // neccessary funds. Otherwise will spend any output in the wallet @@ -341,9 +428,12 @@ struct CreateTransaction { enum { NOT_ENOUGH_FUNDS = -301, TRANSACTION_DOES_NOT_FIT_IN_BLOCK = -302, // Sender will have to split funds into several transactions - NOT_ENOUGH_ANONYMITY = -303 + NOT_ENOUGH_ANONYMITY = -303, + VIEW_ONLY_WALLET = -304, + ADDRESS_FAILED_TO_PARSE = -4, // returns ErrorAddress + INVALID_HEIGHT_OR_DEPTH = -2, // height_or_depth too low or too high + ADDRESS_NOT_IN_WALLET = -1002 // returns ErrorAddress }; - typedef json_rpc::Error Error; }; struct SendTransaction { @@ -358,14 +448,18 @@ struct SendTransaction { // when this method returns, transactions is already added to payment queue and queue fsynced to disk. }; enum { - INVALID_TRANSACTION_BINARY_FORMAT = -101, // transaction failed to parse + INVALID_TRANSACTION_BINARY_FORMAT = -101, WRONG_OUTPUT_REFERENCE = -102, // wrong signature or referenced outputs changed during reorg. Bad output - // height is reported in conflict_height. If output index > max current index, conflict_height will// be set to + // height is reported in conflict_height. If output index > max current index, conflict_height will be set to // currency.max_block_number - OUTPUT_ALREADY_SPENT = -103 - }; // conflight height reported in error + OUTPUT_ALREADY_SPENT = -103 // conflight height reported in error + }; struct Error : public json_rpc::Error { Height conflict_height = 0; + Error() {} + Error(int c, const std::string &msg, Height conflict_height) + : json_rpc::Error(c, msg), conflict_height(conflict_height) {} + void seria_data_members(seria::ISeria &s) override; }; }; @@ -382,6 +476,9 @@ struct CreateSendProof { struct Response { std::vector sendproofs; }; + enum { + ADDRESS_FAILED_TO_PARSE = -4, // returns ErrorAddress + }; }; struct GetTransaction { @@ -405,8 +502,7 @@ namespace api { namespace bytecoind { inline std::string url() { return "/json_rpc"; } -inline std::vector legacy_bin_methods() { return {"/sync_mem_pool.bin", "/sync_blocks.bin"}; } -// When we advance method versions, we add legacy version here to get "upgrade bytecoind" message in walletd" +inline std::string binary_url() { return "/binary_rpc"; } struct GetStatus { static std::string method() { return "get_node_status"; } // getNodeStatus works directly or through wallet tunnel @@ -421,30 +517,52 @@ struct GetRawBlock { static std::string method() { return "get_raw_block"; } struct Request { Hash hash; + HeightOrDepth height_or_depth = std::numeric_limits::max(); + bool need_signatures = false; // signatures are usually of no interest, they all are checked and valid }; struct Response { - api::BlockHeader header; - BlockTemplate raw_header; - std::vector raw_transactions; - Hash base_transaction_hash; // BlockTemplate does not contain it - std::vector> global_indices; // for each transaction, not empty only if block in main chain - std::vector transaction_binary_sizes; // for each transaction + api::RawBlock block; + bool orphan_status = false; + HeightOrDepth depth = 0; // new style, -1 is top block, -2 previous block, etc + }; + enum { + HASH_NOT_FOUND = -5, // Neither in main nor in side chain + INVALID_HEIGHT_OR_DEPTH = -2 // height_or_depth too low or too high + }; +}; + +struct GetBlockHeader { + static std::string method() { return "get_block_header"; } + struct Request { + Hash hash; + HeightOrDepth height_or_depth = std::numeric_limits::max(); + }; + struct Response { + BlockHeader block_header; + bool orphan_status = false; + HeightOrDepth depth = 0; // new style, -1 is top block, -2 previous block, etc + }; + enum { + HASH_NOT_FOUND = -5, // Neither in main nor in side chain + INVALID_HEIGHT_OR_DEPTH = -2 // height_or_depth too low or too high }; }; struct SyncBlocks { // Used by walletd, block explorer, etc to sync to bytecoind static std::string method() { return "sync_blocks"; } - static std::string bin_method() { return "/sync_blocks_v1.bin"; } - // we increment method version when binary format changes + static std::string bin_method() { return "sync_blocks_v1"; } + // we increment bin method version when binary format changes struct Request { static constexpr uint32_t MAX_COUNT = 1000; std::vector sparse_chain; Timestamp first_block_timestamp = 0; uint32_t max_count = MAX_COUNT / 10; + bool need_signatures = false; // signatures are usually of no interest, they all are checked and valid + bool need_redundant_data = true; // walletd and smart clients can save traffic }; struct Response { - std::vector blocks; + std::vector blocks; Height start_height = 0; GetStatus::Response status; // We save roundtrip during sync by also sending status here }; @@ -455,28 +573,33 @@ struct GetRawTransaction { static std::string method() { return "get_raw_transaction"; } struct Request { Hash hash; + bool need_signatures = false; // signatures are usually of no interest, they all are checked and valid }; struct Response { - api::Transaction transaction; - // only hash, block_height, block_hash, binary_size, fee returned in transaction - // empty transaction with no hash returned if not in blockchain/mempool + api::Transaction transaction; // contain only info known to bytecoind TransactionPrefix raw_transaction; + TransactionSignatures signatures; // empty unless request.signatures set + }; + enum { + HASH_NOT_FOUND = -5 // Neither in main nor in side chain }; }; // Signature of this method will stabilize to the end of beta struct SyncMemPool { // Used by walletd sync process static std::string method() { return "sync_mem_pool"; } - static std::string bin_method() { return "/sync_mem_pool_v1.bin"; } - // we increment method version when binary format changes + static std::string bin_method() { return "sync_mem_pool_v1"; } + // we increment bin method version when binary format changes struct Request { - std::vector known_hashes; // Should be sent sorted + std::vector known_hashes; // Should be sent sorted + bool need_signatures = false; // signatures are usually of no interest, they all are checked and valid + bool need_redundant_data = true; // walletd and smart clients can save traffic }; struct Response { std::vector removed_hashes; // Hashes no more in pool std::vector added_raw_transactions; // New raw transactions in pool - std::vector added_transactions; - // binary version of this method returns only hash, timestamp, binary_size, and fee here + std::vector added_signatures; // empty unless request.signatures set + std::vector added_transactions; // contain only info known to bytecoind GetStatus::Response status; // We save roundtrip during sync by also sending status here }; }; @@ -484,8 +607,8 @@ struct SyncMemPool { // Used by walletd sync process struct GetRandomOutputs { static std::string method() { return "get_random_outputs"; } struct Request { - std::vector amounts; // Repeating the same amount will give you multiples of outs_count in result - uint32_t outs_count = 0; + std::vector amounts; // Repeating the same amount will give you multiples of output_count in result + uint32_t output_count = 0; HeightOrDepth confirmed_height_or_depth = -DEFAULT_CONFIRMATIONS - 1; // Mix-ins will be selected from the [0..confirmed_height] window. // Reorganizations larger than confirmations may change mix-in global indices, @@ -495,17 +618,25 @@ struct GetRandomOutputs { std::map> outputs; // can have less outputs than asked for some amounts, if blockchain lacks enough }; + enum { + INVALID_HEIGHT_OR_DEPTH = -2 // height_or_depth too low or too high + }; }; typedef walletd::SendTransaction SendTransaction; -struct CheckSendProof { +struct CheckSendproof { static std::string method() { return "check_sendproof"; } struct Request { std::string sendproof; }; - typedef EmptyStruct Response; // All errors are reported as json rpc errors + struct Response { + Hash transaction_hash; + std::string address; + Amount amount = 0; + std::string message; + }; // All errors are reported as json rpc errors enum { FAILED_TO_PARSE = -201, NOT_IN_MAIN_CHAIN = -202, @@ -520,13 +651,7 @@ struct GetStatistics { static std::string method() { return "get_statistics"; } typedef EmptyStruct Request; - struct Response { - std::string version; - std::string platform; - uint64_t peer_id = 0; // For p2p - Timestamp start_time = 0; // Unix timestamp UTC - std::vector checkpoints; - }; + typedef np::GetPeerStatistics::Response Response; }; // This method is highly experimental @@ -537,7 +662,7 @@ struct GetArchive { uint64_t from_record = 0; uint64_t max_count = 100; static constexpr uint64_t MAX_COUNT = 10000; - bool records_only = false; // no objects + bool records_only = false; // no objects }; struct ArchiveRecord { Timestamp timestamp = 0; @@ -546,7 +671,7 @@ struct GetArchive { Hash hash; std::string source_address; }; - struct ArchiveBlock { // Signatures are checked by bytecoind so usually they are of no interest + struct ArchiveBlock { // TODO - use api::RawBlock BlockTemplate raw_header; // the only method returning actual BlockHeader from blockchain, not api::BlockHeader std::vector raw_transactions; @@ -560,16 +685,23 @@ struct GetArchive { std::map blocks; std::map transactions; - std::map checkpoints; + std::map checkpoints; }; enum { - WRONG_ARCHIVE_ID = -501 // If archive id changed, new id is returned in Error + WRONG_ARCHIVE_ID = -501, // If archive id changed, it is returned in Error + ARCHIVE_NOT_ENABLED = -502 // No archive on this node }; struct Error : public json_rpc::Error { std::string archive_id; + Error(int c, const std::string &msg, const std::string &archive_id) + : json_rpc::Error(c, msg), archive_id(archive_id) {} + void seria_data_members(seria::ISeria &s) override; }; }; +inline std::string legacy_status_ok() { return "OK"; } +// There is no point in always returning status="OK" from all methods + // Methods below are used by miners struct GetBlockTemplate { static std::string method_legacy() { return "getblocktemplate"; } // This name is used by old miners @@ -577,19 +709,23 @@ struct GetBlockTemplate { struct Request { uint32_t reserve_size = 0; // max 255 bytes std::string wallet_address; - Hash top_block_hash; // for longpoll in v3 - behaves like GetStatus - uint32_t transaction_pool_version = 0; // for longpoll in v3 - behaves like GetStatus + boost::optional top_block_hash; // for longpoll in v3 - behaves like GetStatus + boost::optional transaction_pool_version = 0; // for longpoll in v3 - behaves like GetStatus }; struct Response { Difficulty difficulty = 0; Height height = 0; uint32_t reserved_offset = 0; BinaryArray blocktemplate_blob; - std::string status; + std::string status = legacy_status_ok(); Hash top_block_hash; // for longpoll in v3 - behaves like GetStatus uint32_t transaction_pool_version = 0; // for longpoll in v3 - behaves like GetStatus Hash previous_block_hash; // Deprecated, used by some legacy miners. }; + enum { + ADDRESS_FAILED_TO_PARSE = -4, // returns ErrorAddress + TOO_BIG_RESERVE_SIZE = -3 + }; }; struct GetCurrencyId { @@ -607,26 +743,34 @@ struct SubmitBlock { BinaryArray blocktemplate_blob; }; struct Response { - std::string status; + BlockHeader block_header; // contains detailed info about accepted block + bool orphan_status = false; + HeightOrDepth depth = 0; // new style, -1 is top block, -2 previous block, etc }; + enum { WRONG_BLOCKBLOB = -6, BLOCK_NOT_ACCEPTED = -7 }; }; // Legacy methods +struct BlockHeaderLegacy : public BlockHeader { + bool orphan_status = false; + HeightOrDepth depth = 0; + // Legacy methods return depth as number (usually >= 0), where 0 is top block +}; + struct SubmitBlockLegacy { static std::string method() { return "submitblock"; } // This name is used by old miners typedef std::vector Request; - typedef SubmitBlock::Response Response; + struct Response { + std::string status = legacy_status_ok(); + }; + // Same errors as SubmitBlock }; -struct BlockHeaderLegacy : public api::BlockHeader { - bool orphan_status = false; - HeightOrDepth depth = 0; -}; struct GetLastBlockHeaderLegacy { // Use GetStatus instead static std::string method() { return "getlastblockheader"; } typedef EmptyStruct Request; struct Response { - std::string status; + std::string status = legacy_status_ok(); BlockHeaderLegacy block_header; }; }; @@ -637,6 +781,9 @@ struct GetBlockHeaderByHashLegacy { Hash hash; }; typedef GetLastBlockHeaderLegacy::Response Response; + enum { + HASH_NOT_FOUND = -5 // Neither in main nor in side chain + }; }; struct GetBlockHeaderByHeightLegacy { @@ -645,6 +792,9 @@ struct GetBlockHeaderByHeightLegacy { Height height = 0; // Beware, in this call height starts from 1, not 0, so height=1 returns genesis }; typedef GetLastBlockHeaderLegacy::Response Response; + enum { + INVALID_HEIGHT_OR_DEPTH = -2 // height_or_depth too low or too high + }; }; } } @@ -657,12 +807,16 @@ class ISeria; void ser_members(bytecoin::api::EmptyStruct &v, ISeria &s); void ser_members(bytecoin::api::Output &v, ISeria &s); void ser_members(bytecoin::api::BlockHeader &v, ISeria &s); +void ser_members(bytecoin::api::bytecoind::BlockHeaderLegacy &v, ISeria &s); void ser_members(bytecoin::api::Transfer &v, ISeria &s); void ser_members(bytecoin::api::Transaction &v, ISeria &s); void ser_members(bytecoin::api::Block &v, ISeria &s); +void ser_members(bytecoin::api::RawBlock &v, ISeria &s); void ser_members(bytecoin::api::Balance &v, ISeria &s); +void ser_members(bytecoin::api::walletd::GetAddresses::Request &v, ISeria &s); void ser_members(bytecoin::api::walletd::GetAddresses::Response &v, ISeria &s); +void ser_members(bytecoin::api::walletd::GetWalletInfo::Response &v, ISeria &s); void ser_members(bytecoin::api::walletd::GetViewKeyPair::Response &v, ISeria &s); void ser_members(bytecoin::api::walletd::CreateAddresses::Request &v, ISeria &s); void ser_members(bytecoin::api::walletd::CreateAddresses::Response &v, ISeria &s); @@ -681,6 +835,8 @@ void ser_members(bytecoin::api::walletd::GetTransaction::Response &v, ISeria &s) void ser_members(bytecoin::api::bytecoind::GetStatus::Request &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetStatus::Response &v, ISeria &s); +void ser_members(bytecoin::api::bytecoind::GetBlockHeader::Request &v, ISeria &s); +void ser_members(bytecoin::api::bytecoind::GetBlockHeader::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetRawBlock::Request &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetRawBlock::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::SyncBlocks::Request &v, ISeria &s); @@ -693,12 +849,10 @@ void ser_members(bytecoin::api::bytecoind::GetRandomOutputs::Request &v, ISeria void ser_members(bytecoin::api::bytecoind::GetRandomOutputs::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::SendTransaction::Request &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::SendTransaction::Response &v, ISeria &s); -void ser_members(bytecoin::api::bytecoind::SendTransaction::Error &v, ISeria &s); -void ser_members(bytecoin::api::bytecoind::CheckSendProof::Request &v, ISeria &s); -void ser_members(bytecoin::api::bytecoind::GetStatistics::Response &v, ISeria &s); +void ser_members(bytecoin::api::bytecoind::CheckSendproof::Request &v, ISeria &s); +void ser_members(bytecoin::api::bytecoind::CheckSendproof::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetArchive::ArchiveRecord &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetArchive::ArchiveBlock &v, ISeria &s); -void ser_members(bytecoin::api::bytecoind::GetArchive::Error &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetArchive::Request &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetArchive::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetBlockTemplate::Request &v, ISeria &s); @@ -706,7 +860,7 @@ void ser_members(bytecoin::api::bytecoind::GetBlockTemplate::Response &v, ISeria void ser_members(bytecoin::api::bytecoind::GetCurrencyId::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::SubmitBlock::Request &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::SubmitBlock::Response &v, ISeria &s); -void ser_members(bytecoin::api::bytecoind::BlockHeaderLegacy &v, ISeria &s); +void ser_members(bytecoin::api::bytecoind::SubmitBlockLegacy::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetLastBlockHeaderLegacy::Response &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetBlockHeaderByHashLegacy::Request &v, ISeria &s); void ser_members(bytecoin::api::bytecoind::GetBlockHeaderByHeightLegacy::Request &v, ISeria &s); diff --git a/src/seria/BinaryInputStream.cpp b/src/seria/BinaryInputStream.cpp index c31770f2..a99ac714 100644 --- a/src/seria/BinaryInputStream.cpp +++ b/src/seria/BinaryInputStream.cpp @@ -8,6 +8,7 @@ #include #include #include "common/Invariant.hpp" +#include "common/Math.hpp" #include "common/Streams.hpp" using namespace common; @@ -29,7 +30,7 @@ void BinaryInputStream::begin_array(size_t &size, bool fixed_size) { void BinaryInputStream::begin_map(size_t &size) { read_varint_as(stream, size); } -void BinaryInputStream::next_map_key(std::string &name) { (*this)(name); } +void BinaryInputStream::next_map_key(std::string &name) { ser(name, *this); } void BinaryInputStream::seria_v(uint8_t &value) { read_varint(stream, value); } @@ -50,19 +51,19 @@ void BinaryInputStream::seria_v(bool &value) { value = read(stream) != void BinaryInputStream::seria_v(BinaryArray &value) { uint64_t size; read_varint(stream, size); - common::read(stream, value, boost::lexical_cast(size)); + common::read(stream, value, common::integer_cast(size)); } void BinaryInputStream::seria_v(std::string &value) { uint64_t size; read_varint(stream, size); - common::read(stream, value, boost::lexical_cast(size)); + common::read(stream, value, common::integer_cast(size)); } void BinaryInputStream::binary(void *value, size_t size) { stream.read(value, size); } void BinaryInputStream::seria_v(double &value) { assert(false); // the method is not supported for this type of serialization - invariant(false, "double serialization is not supported in BinaryInputStreamSeria"); + throw std::logic_error("double serialization is not supported in BinaryInputStreamSeria"); } diff --git a/src/seria/BinaryInputStream.hpp b/src/seria/BinaryInputStream.hpp index f780c914..eb559441 100644 --- a/src/seria/BinaryInputStream.hpp +++ b/src/seria/BinaryInputStream.hpp @@ -3,9 +3,9 @@ #pragma once -#include #include "ISeria.hpp" #include "common/MemoryStreams.hpp" +#include "common/exception.hpp" namespace seria { @@ -17,7 +17,7 @@ class BinaryInputStream : public ISeria { virtual bool is_input() const override { return true; } virtual void begin_object() override {} - virtual void object_key(common::StringView, bool optional = false) override {} + virtual bool object_key(common::StringView, bool optional = false) override { return true; } virtual void end_object() override {} virtual void begin_map(size_t &size) override; @@ -44,22 +44,28 @@ class BinaryInputStream : public ISeria { common::IInputStream &stream; }; -template -void from_binary(T &obj, const common::BinaryArray &blob) { +template +void from_binary(T &obj, common::MemoryInputStream &stream, Context... context) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); - common::MemoryInputStream stream(blob.data(), blob.size()); BinaryInputStream ba(stream); - ba(obj); + try { + ser(obj, ba, context...); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error( + "Error while serializing binary object of type '" + common::demangle(typeid(T).name()) + "'")); + } if (!stream.empty()) - throw std::runtime_error("Excess data in from_binary " + std::string(typeid(T).name())); + throw std::runtime_error( + "Excess data after serializing binary object of type '" + common::demangle(typeid(T).name()) + "'"); } -template -void from_binary(T &obj, const std::string &blob) { - static_assert(!std::is_pointer::value, "Cannot be called with pointer"); +template +void from_binary(T &obj, const common::BinaryArray &blob, Context... context) { common::MemoryInputStream stream(blob.data(), blob.size()); - BinaryInputStream ba(stream); - ba(obj); - if (!stream.empty()) - throw std::runtime_error("Excess data in from_binary " + std::string(typeid(T).name())); + from_binary(obj, stream, context...); +} +template +void from_binary(T &obj, const std::string &blob, Context... context) { + common::MemoryInputStream stream(blob.data(), blob.size()); + from_binary(obj, stream, context...); } } diff --git a/src/seria/BinaryOutputStream.cpp b/src/seria/BinaryOutputStream.cpp index 596eb3ff..abee37a8 100644 --- a/src/seria/BinaryOutputStream.cpp +++ b/src/seria/BinaryOutputStream.cpp @@ -14,7 +14,7 @@ using namespace seria; void BinaryOutputStream::begin_map(size_t &size) { write_varint(stream, size); } -void BinaryOutputStream::next_map_key(std::string &name) { (*this)(name); } +void BinaryOutputStream::next_map_key(std::string &name) { ser(name, *this); } void BinaryOutputStream::begin_array(size_t &size, bool fixed_size) { if (!fixed_size) @@ -53,5 +53,5 @@ void BinaryOutputStream::binary(void *value, size_t size) { stream.write(static_ void BinaryOutputStream::seria_v(double &value) { assert(false); // the method is not supported for this type of serialization - invariant(false, "double serialization is not supported in BinaryOutputStreamSeria"); + throw std::logic_error("double serialization is not supported in BinaryOutputStreamSeria"); } diff --git a/src/seria/BinaryOutputStream.hpp b/src/seria/BinaryOutputStream.hpp index 7e154044..83da9d5c 100644 --- a/src/seria/BinaryOutputStream.hpp +++ b/src/seria/BinaryOutputStream.hpp @@ -16,7 +16,7 @@ class BinaryOutputStream : public ISeria { virtual bool is_input() const override { return false; } virtual void begin_object() override {} - virtual void object_key(common::StringView, bool optional = false) override {} + virtual bool object_key(common::StringView, bool optional = false) override { return true; } virtual void end_object() override {} virtual void begin_array(size_t &size, bool fixed_size = false) override; @@ -43,31 +43,31 @@ class BinaryOutputStream : public ISeria { common::IOutputStream &stream; }; -template -common::BinaryArray to_binary(const T &obj) { +template +common::BinaryArray to_binary(const T &obj, Context... context) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); common::BinaryArray result; common::VectorOutputStream stream(result); BinaryOutputStream ba(stream); - ba(const_cast(obj)); + ser(const_cast(obj), ba, context...); return result; } -template -std::string to_binary_str(const T &obj) { +template +std::string to_binary_str(const T &obj, Context... context) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); std::string result; common::StringOutputStream stream(result); BinaryOutputStream ba(stream); - ba(const_cast(obj)); + ser(const_cast(obj), ba, context...); return result; } -template -size_t binary_size(const T &obj) { +template +size_t binary_size(const T &obj, Context... context) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); common::BinaryArray result; common::VectorOutputStream stream(result); BinaryOutputStream ba(stream); - ba(const_cast(obj)); + ser(const_cast(obj), ba, context...); return result.size(); } } diff --git a/src/seria/ISeria.hpp b/src/seria/ISeria.hpp index 2f00253a..c9d0d1be 100644 --- a/src/seria/ISeria.hpp +++ b/src/seria/ISeria.hpp @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -15,13 +16,14 @@ #include "common/BinaryArray.hpp" #include "common/Int128.hpp" #include "common/StringView.hpp" +#include "common/exception.hpp" #include "common/string.hpp" namespace seria { class ISeria; -template -void ser(T &value, ISeria &s); +// template +// void ser(T &value, ISeria &s); class ISeria { public: @@ -30,7 +32,8 @@ class ISeria { virtual bool is_input() const = 0; virtual void begin_object() = 0; - virtual void object_key(common::StringView name, bool optional = false) = 0; // throw if key not found + virtual bool object_key(common::StringView name, bool optional = false) = 0; + // return true if key is there virtual void end_object() = 0; virtual void begin_map(size_t &size) = 0; @@ -55,11 +58,6 @@ class ISeria { // read/write binary block virtual void binary(void *value, size_t size) = 0; // fixed width, no size written - - template - void operator()(T &value) { - ser(value, *this); - } }; inline void ser(uint8_t &value, ISeria &s) { return s.seria_v(value); } @@ -104,9 +102,42 @@ inline void ser(std::string &value, ISeria &s) { return s.seria_v(value); } inline void ser(common::BinaryArray &value, ISeria &s) { return s.seria_v(value); } template -void seria_kv(common::StringView name, T &value, ISeria &s, bool optional = false) { - s.object_key(name, optional); - s(value); +void seria_kv(common::StringView name, T &value, ISeria &s) { + try { + s.object_key(name); + ser(value, s); + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing object value for key '" + std::string(name) + "'")); + } +} +template +void seria_kv(common::StringView name, boost::optional &value, ISeria &s) { + try { + if (s.is_input()) { + if (s.object_key(name, true)) { + value = T{}; + ser(value.get(), s); + } + } else { + s.object_key(name, !value); + T temp{}; + ser(value ? value.get() : temp, s); + } + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing object value for key '" + std::string(name) + "'")); + } +} +template +void seria_kv_optional(common::StringView name, T &value, ISeria &s) { + try { + s.object_key(name, true); + ser(value, s); + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing object value for key '" + std::string(name) + "'")); + } } template @@ -114,10 +145,10 @@ void ser_members(T &value, ISeria &s); //{ // static_assert(false); // Good idea, but clang complains // } -template -void ser(T &value, ISeria &s) { +template +void ser(T &value, ISeria &s, Context... context) { s.begin_object(); - ser_members(value, s); + ser_members(value, s, context...); s.end_object(); } template @@ -126,8 +157,15 @@ void seria_container(Cont &value, ISeria &s) { s.begin_array(size); if (s.is_input()) value.resize(size); + size_t counter = 0; for (auto &item : value) { - s(const_cast(item)); + try { + ser(const_cast(item), s); + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing array element #" + common::to_string(counter))); + } + counter += 1; } s.end_array(); } @@ -149,15 +187,30 @@ void seria_map_string(MapT &value, ISeria &s) { if (s.is_input()) { for (size_t i = 0; i != size; ++i) { std::string k; - typename MapT::mapped_type v; - s.next_map_key(k); - s(v); - value.insert(std::make_pair(std::move(k), std::move(v))); + try { + s.next_map_key(k); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error("Error while serializing map key #" + common::to_string(i))); + } + try { + typename MapT::mapped_type v; + ser(v, s); + value.insert(std::make_pair(std::move(k), std::move(v))); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error("Error while serializing map value for key '" + k + "'")); + } } } else { + size_t counter = 0; for (auto &kv : value) { - s.next_map_key(const_cast(kv.first)); - s(const_cast(kv.second)); + try { + s.next_map_key(const_cast(kv.first)); + ser(const_cast(kv.second), s); + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing map value for key '" + kv.first + "'")); + } + counter += 1; } } s.end_map(); @@ -170,18 +223,34 @@ void seria_map_integral(MapT &value, ISeria &s, std::true_type) { if (s.is_input()) { for (size_t i = 0; i != size; ++i) { std::string key; - s.next_map_key(key); - typename MapT::key_type k = static_cast(common::stoll(key)); + typename MapT::key_type k; + try { + s.next_map_key(key); + k = static_cast(common::stoll(key)); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error("Error while serializing map key #" + common::to_string(i))); + } // We use widest possible conversion because no generic function provided in C++ - typename MapT::mapped_type v; - s(v); - value.insert(std::make_pair(k, std::move(v))); + try { + typename MapT::mapped_type v; + ser(v, s); + value.insert(std::make_pair(k, std::move(v))); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error("Error while serializing map value for key '" + key + "'")); + } } } else { + size_t counter = 0; for (auto &kv : value) { auto str_key = common::to_string(kv.first); - s.next_map_key(const_cast(str_key)); - s(const_cast(kv.second)); + try { + s.next_map_key(const_cast(str_key)); + ser(const_cast(kv.second), s); + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing map value for key '" + str_key + "'")); + } + counter += 1; } } s.end_map(); @@ -195,12 +264,24 @@ void seria_set(SetT &value, ISeria &s) { if (s.is_input()) { for (size_t i = 0; i < size; ++i) { typename SetT::value_type key; - s(key); - value.insert(std::move(key)); + try { + ser(key, s); + value.insert(std::move(key)); + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing set element #" + common::to_string(i))); + } } } else { + size_t counter = 0; for (auto &key : value) { - s(const_cast(key)); + try { + ser(const_cast(key), s); + } catch (const std::exception &) { + std::throw_with_nested( + std::runtime_error("Error while serializing set element #" + common::to_string(counter))); + } + counter += 1; } } s.end_array(); @@ -245,9 +326,7 @@ void ser(std::array &value, ISeria &s) { } template void ser_members(std::pair &value, ISeria &s) { - s.object_key("first"); - s(value.first); - s.object_key("second"); - s(value.second); + seria_kv("first", value.first, s); + seria_kv("second", value.second, s); } } diff --git a/src/seria/JsonInputStream.cpp b/src/seria/JsonInputStream.cpp new file mode 100644 index 00000000..d269b940 --- /dev/null +++ b/src/seria/JsonInputStream.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#include "JsonInputStream.hpp" + +#include +#include +#include + +#include "common/Invariant.hpp" +#include "common/Math.hpp" +#include "common/StringTools.hpp" + +using common::JsonValue; +using namespace seria; + +namespace { +template +void get_integer(const common::JsonValue *val, T &v, const char *t_name) { + if (val) { + try { + v = common::integer_cast(val->get_integer()); + } catch (const std::exception &) { + throw std::out_of_range( + "value " + common::to_string(val->get_integer()) + " does not fit into " + std::string(t_name)); + } + } +} + +template +void get_unsigned(const common::JsonValue *val, T &v, const char *t_name) { + if (val) { + try { + v = common::integer_cast(val->get_unsigned()); + } catch (const std::exception &) { + throw std::out_of_range( + "value " + common::to_string(val->get_unsigned()) + " does not fit into " + std::string(t_name)); + } + } +} +} + +JsonInputStreamValue::JsonInputStreamValue(const common::JsonValue &value) : value(value) {} + +bool JsonInputStreamValue::object_key(common::StringView name, bool optional) { + const JsonValue *parent = chain.back(); + if (!parent) { + object_key_value = nullptr; // All fields are optional + return false; + } + if (!parent->is_object()) + throw std::runtime_error("JsonInputStreamValue::object_key this is not an object"); + std::string str_name(name); + remaining_object_keys.back().erase(str_name); + if (!parent->contains(str_name)) { + object_key_value = nullptr; // All fields are optional + // throw std::runtime_error("JsonInputStreamValue::object_key not in object key=" + str_name); + return false; + } + object_key_value = &((*parent)(str_name)); + return true; +} + +void JsonInputStreamValue::begin_map(size_t &size) { + begin_object(); + size = chain.back() ? chain.back()->get_object().size() : 0; + if (chain.back()) // TODO - better map logic handling + remaining_object_keys.back().clear(); +} + +void JsonInputStreamValue::next_map_key(std::string &name) { + const JsonValue *parent = chain.back(); + if (!parent) + throw std::runtime_error("JsonInputStreamValue::object_key object key of optional empty map is requested"); + if (!parent->is_object()) + throw std::runtime_error("JsonInputStreamValue::object_key this is not an map"); + if (itrs.back() == parent->get_object().end()) + throw std::runtime_error("JsonInputStreamValue::object_key too many map keys requested"); + name = itrs.back()->first; + object_key_value = &(itrs.back()->second); + ++itrs.back(); +} + +void JsonInputStreamValue::begin_object() { + const JsonValue *val = get_value(); + if (val && !val->is_object()) + throw std::runtime_error("JsonInputStreamValue doesn't support this type of serialization: Object expected."); + if (val) { + itrs.push_back(val->get_object().begin()); + std::set all_keys; + for (const auto &kv : val->get_object()) + all_keys.insert(kv.first); + remaining_object_keys.push_back(std::move(all_keys)); + } + chain.push_back(val); +} + +void JsonInputStreamValue::end_object() { + invariant(!chain.empty() && !itrs.empty(), "JsonInputStreamValue unexpected end_object."); + if (chain.back()) { + if (!remaining_object_keys.back().empty()) { + std::string all_keys; + for (const auto &k : remaining_object_keys.back()) + all_keys += (all_keys.empty() ? "'" : ", '") + k + "'"; + throw std::runtime_error("key(s) " + all_keys + " have no meaning. Typo?"); + } + remaining_object_keys.pop_back(); + itrs.pop_back(); + } + chain.pop_back(); +} + +void JsonInputStreamValue::begin_array(size_t &size, bool fixed_size) { + const JsonValue *val = get_value(); + if (val && !val->is_array()) + throw std::runtime_error("JsonInputStreamValue doesn't support this type of serialization: Array expected."); + size = val ? val->size() : 0; + chain.push_back(val); + idxs.push_back(0); +} + +void JsonInputStreamValue::end_array() { + invariant(!chain.empty() && !idxs.empty(), "JsonInputStreamValue unexpected end_array."); + chain.pop_back(); + idxs.pop_back(); +} + +void JsonInputStreamValue::seria_v(uint16_t &value) { get_unsigned(get_value(), value, "uint16_t"); } + +void JsonInputStreamValue::seria_v(int16_t &value) { get_integer(get_value(), value, "int16_t"); } + +void JsonInputStreamValue::seria_v(uint32_t &value) { get_unsigned(get_value(), value, "uint32_t"); } + +void JsonInputStreamValue::seria_v(int32_t &value) { get_integer(get_value(), value, "int32_t"); } + +void JsonInputStreamValue::seria_v(int64_t &value) { get_integer(get_value(), value, "int64_t"); } + +void JsonInputStreamValue::seria_v(uint64_t &value) { get_unsigned(get_value(), value, "uint64_t"); } + +void JsonInputStreamValue::seria_v(double &value) { + const common::JsonValue *val = get_value(); + if (val) + value = val->get_double(); +} + +void JsonInputStreamValue::seria_v(uint8_t &value) { get_unsigned(get_value(), value, "uint8_t"); } + +void JsonInputStreamValue::seria_v(std::string &value) { + const JsonValue *val = get_value(); + if (val) + value = val->get_string(); +} + +void JsonInputStreamValue::seria_v(bool &value) { + const JsonValue *val = get_value(); + if (val) + value = val->get_bool(); +} + +void JsonInputStreamValue::binary(void *value, size_t size) { + const JsonValue *val = get_value(); + if (val) { + const std::string &str = val->get_string(); + if (str.empty()) + memset(value, 0, size); + else + common::from_hex_or_throw(str, value, size); + } +} + +void JsonInputStreamValue::seria_v(common::BinaryArray &value) { + const JsonValue *val = get_value(); + if (!val) + return; + value = common::from_hex(val->get_string()); +} + +const JsonValue *JsonInputStreamValue::get_value() { + if (chain.empty()) + return &value; + if (!chain.back()) // Optional object + return nullptr; + const JsonValue &val = *chain.back(); + if (val.is_array()) + return &val[idxs.back()++]; + auto ret = object_key_value; + object_key_value = nullptr; + return ret; +} diff --git a/src/seria/JsonInputStream.hpp b/src/seria/JsonInputStream.hpp new file mode 100644 index 00000000..df53a48e --- /dev/null +++ b/src/seria/JsonInputStream.hpp @@ -0,0 +1,67 @@ +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. +// Licensed under the GNU Lesser General Public License. See LICENSE for details. + +#pragma once + +#include "ISeria.hpp" +#include "common/JsonValue.hpp" +#include "common/Nocopy.hpp" +#include "common/exception.hpp" + +namespace seria { + +class JsonInputStream : public ISeria {}; // Common base für use with dynamic_cast in ser() methods + +class JsonInputStreamValue : public JsonInputStream, private common::Nocopy { +public: + JsonInputStreamValue(const common::JsonValue &value); + + virtual bool is_input() const override { return true; } + + virtual void begin_object() override; + virtual bool object_key(common::StringView name, bool optional = false) override; + virtual void end_object() override; + + virtual void begin_map(size_t &size) override; + virtual void next_map_key(std::string &name) override; + virtual void end_map() override { end_object(); } + + virtual void begin_array(size_t &size, bool fixed_size = false) override; + virtual void end_array() override; + + virtual void seria_v(uint8_t &value) override; + virtual void seria_v(int16_t &value) override; + virtual void seria_v(uint16_t &value) override; + virtual void seria_v(int32_t &value) override; + virtual void seria_v(uint32_t &value) override; + virtual void seria_v(int64_t &value) override; + virtual void seria_v(uint64_t &value) override; + virtual void seria_v(double &value) override; + virtual void seria_v(bool &value) override; + virtual void seria_v(std::string &value) override; + virtual void seria_v(common::BinaryArray &value) override; + virtual void binary(void *value, size_t size) override; + +private: + const common::JsonValue &value; + const common::JsonValue *object_key_value = nullptr; + std::vector chain; + std::vector idxs; + std::vector itrs; + std::vector> remaining_object_keys; + + const common::JsonValue *get_value(); +}; + +template +void from_json_value(T &v, const common::JsonValue &js, Context... context) { + static_assert(!std::is_pointer::value, "Cannot be called with pointer"); + JsonInputStreamValue s(js); + try { + ser(v, s, context...); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error( + "Error while deserializing json value of type '" + common::demangle(typeid(T).name()) + "'")); + } +} +} diff --git a/src/seria/JsonOutputStream.cpp b/src/seria/JsonOutputStream.cpp index 7e0ab7d3..3e92e885 100644 --- a/src/seria/JsonOutputStream.cpp +++ b/src/seria/JsonOutputStream.cpp @@ -12,67 +12,78 @@ using namespace seria; // We do not use optional values yet, all empty strings arrays objects are saved as is -JsonOutputStream::JsonOutputStream() : root(JsonValue::NIL) {} +JsonOutputStreamValue::JsonOutputStreamValue() : root(JsonValue::NIL) {} -void JsonOutputStream::object_key(common::StringView name, bool optional) { +bool JsonOutputStreamValue::object_key(common::StringView name, bool optional) { // TODO - check if m_next_key already exists next_key = name; next_optional = optional; + return true; } -void JsonOutputStream::next_map_key(std::string &name) { +void JsonOutputStreamValue::next_map_key(std::string &name) { // TODO - check if m_next_key already exists next_key = name; } -void JsonOutputStream::begin_object() { chain.push_back(insert_or_push(JsonValue(JsonValue::OBJECT), false)); } +void JsonOutputStreamValue::begin_object() { chain.push_back(insert_or_push(JsonValue(JsonValue::OBJECT), false)); } -void JsonOutputStream::end_object() { - assert(!chain.empty()); +void JsonOutputStreamValue::end_object() { + invariant(!chain.empty(), ""); chain.pop_back(); } -void JsonOutputStream::begin_array(size_t &size, bool fixed_size) { +void JsonOutputStreamValue::begin_array(size_t &size, bool fixed_size) { chain.push_back(insert_or_push(JsonValue(JsonValue::ARRAY), size == 0)); } -void JsonOutputStream::end_array() { - assert(!chain.empty()); +void JsonOutputStreamValue::end_array() { + invariant(!chain.empty(), ""); chain.pop_back(); } -void JsonOutputStream::seria_v(uint64_t &value) { insert_or_push(JsonValue(value), value == 0); } +void JsonOutputStreamValue::seria_v(uint64_t &value) { insert_or_push(JsonValue(value), value == 0); } -void JsonOutputStream::seria_v(uint16_t &value) { insert_or_push(JsonValue(JsonValue::Unsigned(value)), value == 0); } +void JsonOutputStreamValue::seria_v(uint16_t &value) { + insert_or_push(JsonValue(JsonValue::Unsigned(value)), value == 0); +} -void JsonOutputStream::seria_v(int16_t &value) { insert_or_push(JsonValue(JsonValue::Integer(value)), value == 0); } +void JsonOutputStreamValue::seria_v(int16_t &value) { + insert_or_push(JsonValue(JsonValue::Integer(value)), value == 0); +} -void JsonOutputStream::seria_v(uint32_t &value) { insert_or_push(JsonValue(JsonValue::Unsigned(value)), value == 0); } +void JsonOutputStreamValue::seria_v(uint32_t &value) { + insert_or_push(JsonValue(JsonValue::Unsigned(value)), value == 0); +} -void JsonOutputStream::seria_v(int32_t &value) { insert_or_push(JsonValue(JsonValue::Integer(value)), value == 0); } +void JsonOutputStreamValue::seria_v(int32_t &value) { + insert_or_push(JsonValue(JsonValue::Integer(value)), value == 0); +} -void JsonOutputStream::seria_v(int64_t &value) { insert_or_push(JsonValue(value), value == 0); } +void JsonOutputStreamValue::seria_v(int64_t &value) { insert_or_push(JsonValue(value), value == 0); } -void JsonOutputStream::seria_v(double &value) { insert_or_push(JsonValue(value), value == 0); } +void JsonOutputStreamValue::seria_v(double &value) { insert_or_push(JsonValue(value), value == 0); } -void JsonOutputStream::seria_v(std::string &value) { insert_or_push(JsonValue(value), value.empty()); } +void JsonOutputStreamValue::seria_v(std::string &value) { insert_or_push(JsonValue(value), value.empty()); } -void JsonOutputStream::seria_v(common::BinaryArray &value) { +void JsonOutputStreamValue::seria_v(common::BinaryArray &value) { std::string hex = common::to_hex(value); seria_v(hex); } -void JsonOutputStream::seria_v(uint8_t &value) { insert_or_push(JsonValue(JsonValue::Integer(value)), value == 0); } +void JsonOutputStreamValue::seria_v(uint8_t &value) { + insert_or_push(JsonValue(JsonValue::Integer(value)), value == 0); +} -void JsonOutputStream::seria_v(bool &value) { insert_or_push(JsonValue(value), !value); } +void JsonOutputStreamValue::seria_v(bool &value) { insert_or_push(JsonValue(value), !value); } -void JsonOutputStream::binary(void *value, size_t size) { +void JsonOutputStreamValue::binary(void *value, size_t size) { std::string hex = common::to_hex(value, size); bool all_zeroes = hex.find_first_not_of('0') == std::string::npos; insert_or_push(all_zeroes ? std::string() : hex, all_zeroes); } -common::JsonValue *JsonOutputStream::insert_or_push(const common::JsonValue &value, bool skip_if_optional) { +common::JsonValue *JsonOutputStreamValue::insert_or_push(const common::JsonValue &value, bool skip_if_optional) { if (chain.empty()) { invariant(expecting_root, "unexpected root"); root = common::JsonValue(value); @@ -90,6 +101,112 @@ common::JsonValue *JsonOutputStream::insert_or_push(const common::JsonValue &val return nullptr; return &js->insert((std::string)key, value); } - invariant(false, "can only insert into object array or root"); - return nullptr; // invariant Will always throw, but compiler does not know it + throw std::logic_error("can only insert into object array or root"); +} + +bool JsonOutputStreamText::append_prefix(const std::string &value, bool skip_if_optional) { + if (chain.empty()) { + invariant(expecting_root, "unexpected root"); + expecting_root = false; + text += value; + return true; + } + if (chain.back().first == JsonValue::ARRAY) { + if (chain.back().second != 0) + text += ","; + chain.back().second += 1; + text += value; + return true; + } + common::StringView key = next_key; + next_key = common::StringView(""); + if (skip_if_optional && next_optional) + return false; + if (chain.back().second != 0) + text += ","; + chain.back().second += 1; + text += "\""; + text += JsonValue::escape_string(std::string(key.begin(), key.end())); + text += "\":"; + text += value; + return true; +} + +bool JsonOutputStreamText::object_key(common::StringView name, bool optional) { + // TODO - check if m_next_key already exists + next_key = name; + next_optional = optional; + return true; +} + +void JsonOutputStreamText::next_map_key(std::string &name) { + // TODO - check if m_next_key already exists + next_key = name; +} + +void JsonOutputStreamText::begin_object() { + if (!append_prefix("{", false)) + return; + chain.push_back(std::make_pair(JsonValue::OBJECT, 0)); +} + +void JsonOutputStreamText::end_object() { + invariant(!chain.empty() && chain.back().first == JsonValue::OBJECT, ""); + text += "}"; + chain.pop_back(); +} + +void JsonOutputStreamText::begin_array(size_t &size, bool fixed_size) { + if (!append_prefix(std::string(), size == 0)) { + chain.push_back(std::make_pair(JsonValue::NIL, 0)); // NIL to mark empty optional array + return; + } + text += "["; + chain.push_back(std::make_pair(JsonValue::ARRAY, 0)); +} + +void JsonOutputStreamText::end_array() { + invariant(!chain.empty() && (chain.back().first == JsonValue::ARRAY || chain.back().first == JsonValue::NIL), ""); + if (chain.back().first == JsonValue::ARRAY) + text += "]"; + chain.pop_back(); +} + +void JsonOutputStreamText::seria_v(uint64_t &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(uint16_t &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(int16_t &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(uint32_t &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(int32_t &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(int64_t &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(double &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(std::string &value) { + if (!append_prefix("\"", value.empty())) + return; + text += JsonValue::escape_string(value); + text += "\""; +} + +void JsonOutputStreamText::seria_v(common::BinaryArray &value) { + std::string hex = common::to_hex(value); + seria_v(hex); +} + +void JsonOutputStreamText::seria_v(uint8_t &value) { append_prefix(common::to_string(value), value == 0); } + +void JsonOutputStreamText::seria_v(bool &value) { append_prefix(value ? "true" : "false", !value); } + +void JsonOutputStreamText::binary(void *value, size_t size) { + std::string hex = common::to_hex(value, size); + bool all_zeroes = hex.find_first_not_of('0') == std::string::npos; + if (!append_prefix("\"", all_zeroes)) + return; + text += all_zeroes ? std::string() : hex; + text += "\""; } diff --git a/src/seria/JsonOutputStream.hpp b/src/seria/JsonOutputStream.hpp index 3b8fb51e..dba9993d 100644 --- a/src/seria/JsonOutputStream.hpp +++ b/src/seria/JsonOutputStream.hpp @@ -9,14 +9,16 @@ namespace seria { -class JsonOutputStream : public ISeria { +class JsonOutputStream : public ISeria {}; // Common base für use with dynamic_cast in ser() methods + +class JsonOutputStreamValue : public JsonOutputStream { public: - JsonOutputStream(); + JsonOutputStreamValue(); virtual bool is_input() const override { return false; } virtual void begin_object() override; - virtual void object_key(common::StringView name, bool optional = false) override; + virtual bool object_key(common::StringView name, bool optional = false) override; virtual void end_object() override; virtual void begin_map(size_t &) override { begin_object(); } @@ -38,7 +40,8 @@ class JsonOutputStream : public ISeria { virtual void seria_v(std::string &value) override; virtual void seria_v(common::BinaryArray &value) override; virtual void binary(void *value, size_t size) override; - const common::JsonValue &get_value() const { return root; } + + common::JsonValue move_value() const { return std::move(root); } private: bool expecting_root = true; @@ -50,11 +53,51 @@ class JsonOutputStream : public ISeria { common::JsonValue *insert_or_push(const common::JsonValue &value, bool skip_if_optional); }; -template -common::JsonValue to_json_value(const T &v) { +class JsonOutputStreamText : public JsonOutputStream { +public: + explicit JsonOutputStreamText(std::string &text) : text(text) {} + + virtual bool is_input() const override { return false; } + + virtual void begin_object() override; + virtual bool object_key(common::StringView name, bool optional = false) override; + virtual void end_object() override; + + virtual void begin_map(size_t &) override { begin_object(); } + virtual void next_map_key(std::string &name) override; + virtual void end_map() override { end_object(); } + + virtual void begin_array(size_t &size, bool fixed_size = false) override; + virtual void end_array() override; + + virtual void seria_v(uint8_t &value) override; + virtual void seria_v(int16_t &value) override; + virtual void seria_v(uint16_t &value) override; + virtual void seria_v(int32_t &value) override; + virtual void seria_v(uint32_t &value) override; + virtual void seria_v(int64_t &value) override; + virtual void seria_v(uint64_t &value) override; + virtual void seria_v(double &value) override; + virtual void seria_v(bool &value) override; + virtual void seria_v(std::string &value) override; + virtual void seria_v(common::BinaryArray &value) override; + virtual void binary(void *value, size_t size) override; + +private: + bool expecting_root = true; + common::StringView next_key; + bool next_optional = false; + std::string &text; + std::vector> + chain; // object, array or null (for empty array) only + count of elements + bool append_prefix(const std::string &value, bool skip_if_optional); +}; + +template +common::JsonValue to_json_value(const T &v, Context... context) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); - JsonOutputStream s; - s(const_cast(v)); - return s.get_value(); + JsonOutputStreamValue s; + ser(const_cast(v), s, context...); + return s.move_value(); } } diff --git a/src/seria/KVBinaryCommon.hpp b/src/seria/KVBinaryCommon.hpp index 989cbf7a..ca9dd2dc 100644 --- a/src/seria/KVBinaryCommon.hpp +++ b/src/seria/KVBinaryCommon.hpp @@ -39,9 +39,9 @@ const uint8_t BIN_KV_SERIALIZE_FLAG_ARRAY = 0x80; #pragma pack(push) #pragma pack(1) struct KVBinaryStorageBlockHeader { - uint32_t m_signature_a; - uint32_t m_signature_b; - uint8_t m_ver; + uint32_t m_signature_a = 0; + uint32_t m_signature_b = 0; + uint8_t m_ver = 0; }; #pragma pack(pop) } diff --git a/src/seria/KVBinaryInputStream.cpp b/src/seria/KVBinaryInputStream.cpp index 82d91f8e..e9a94029 100644 --- a/src/seria/KVBinaryInputStream.cpp +++ b/src/seria/KVBinaryInputStream.cpp @@ -8,7 +8,9 @@ #include #include #include "KVBinaryCommon.hpp" +#include "common/Invariant.hpp" #include "common/Streams.hpp" +#include "common/Varint.hpp" using namespace common; using namespace bytecoin; @@ -18,9 +20,9 @@ namespace { template T read_pod(common::IInputStream &s) { - T v; - s.read(&v, sizeof(T)); - return v; + unsigned char buf[sizeof(T)]; + s.read(buf, sizeof(T)); + return common::uint_le_from_bytes::type>(buf, sizeof(T)); } template @@ -30,9 +32,9 @@ JsonValue read_pod_json(common::IInputStream &s) { return jv; } -template +template JsonValue read_integer_json(common::IInputStream &s) { - return read_pod_json(s); + return read_pod_json(s); } size_t read_varint(common::IInputStream &s) { @@ -76,7 +78,8 @@ std::string read_string(common::IInputStream &s) { JsonValue read_string_json(common::IInputStream &s) { return JsonValue(read_string(s)); } void read_name(common::IInputStream &s, std::string &name) { - uint8_t len = read_pod(s); + uint8_t len; + s.read(&len, 1); common::read(s, name, len); } @@ -101,23 +104,28 @@ JsonValue load_object(common::IInputStream &stream) { JsonValue load_value(common::IInputStream &stream, uint8_t type) { switch (type) { case BIN_KV_SERIALIZE_TYPE_INT64: - return read_integer_json(stream); + return read_integer_json(stream); case BIN_KV_SERIALIZE_TYPE_INT32: - return read_integer_json(stream); + return read_integer_json(stream); case BIN_KV_SERIALIZE_TYPE_INT16: - return read_integer_json(stream); + return read_integer_json(stream); case BIN_KV_SERIALIZE_TYPE_INT8: - return read_integer_json(stream); + return read_integer_json(stream); case BIN_KV_SERIALIZE_TYPE_UINT64: - return read_integer_json(stream); + return read_integer_json(stream); case BIN_KV_SERIALIZE_TYPE_UINT32: - return read_integer_json(stream); + return read_integer_json(stream); case BIN_KV_SERIALIZE_TYPE_UINT16: - return read_integer_json(stream); + return read_integer_json(stream); case BIN_KV_SERIALIZE_TYPE_UINT8: - return read_integer_json(stream); - case BIN_KV_SERIALIZE_TYPE_DOUBLE: - return read_pod_json(stream); // TODO - double as binary is a BUG + return read_integer_json(stream); + case BIN_KV_SERIALIZE_TYPE_DOUBLE: { + assert(false); // the method is not supported for this type of serialization + throw std::logic_error("double serialization is not supported in KVBinaryInputStream"); + // double dv = 0; // TODO - double as binary is a BUG + // stream.read(&dv, sizeof(dv)); + // return dv; + } case BIN_KV_SERIALIZE_TYPE_BOOL: return JsonValue(read(stream) != 0); case BIN_KV_SERIALIZE_TYPE_STRING: @@ -133,7 +141,8 @@ JsonValue load_value(common::IInputStream &stream, uint8_t type) { } JsonValue load_entry(common::IInputStream &stream) { - uint8_t type = read_pod(stream); + uint8_t type; + stream.read(&type, 1); if (type & BIN_KV_SERIALIZE_FLAG_ARRAY) { type &= ~BIN_KV_SERIALIZE_FLAG_ARRAY; @@ -149,7 +158,8 @@ JsonValue load_array(common::IInputStream &stream, uint8_t item_type) { while (count--) { if (item_type == BIN_KV_SERIALIZE_TYPE_ARRAY) { - uint8_t type = read_pod(stream); + uint8_t type; + stream.read(&type, 1); if ((type & BIN_KV_SERIALIZE_FLAG_ARRAY) == 0) throw std::runtime_error("Incorrect array of array encoding"); type &= ~BIN_KV_SERIALIZE_FLAG_ARRAY; @@ -163,7 +173,10 @@ JsonValue load_array(common::IInputStream &stream, uint8_t item_type) { } JsonValue parse_binary(common::IInputStream &stream) { - auto hdr = read_pod(stream); + KVBinaryStorageBlockHeader hdr; + hdr.m_signature_a = read_pod(stream); + hdr.m_signature_b = read_pod(stream); + stream.read(&hdr.m_ver, 1); if (hdr.m_signature_a != PORTABLE_STORAGE_SIGNATUREA || hdr.m_signature_b != PORTABLE_STORAGE_SIGNATUREB) { throw std::runtime_error("Invalid binary storage signature"); @@ -177,7 +190,10 @@ JsonValue parse_binary(common::IInputStream &stream) { } } -KVBinaryInputStream::KVBinaryInputStream(common::IInputStream &strm) : JsonInputValue(parse_binary(strm)) {} +KVBinaryInputStream::KVBinaryInputStream(common::IInputStream &strm) : JsonInputStreamValue(value_storage) { + // We init parent with & of value_storage, then set storage + value_storage = parse_binary(strm); +} void KVBinaryInputStream::seria_v(common::BinaryArray &value) { std::string str; @@ -190,7 +206,7 @@ void KVBinaryInputStream::binary(void *value, size_t size) { return; std::string str; - (*this)(str); + ser(str, *this); if (str.size() != size) { throw std::runtime_error("Binary block size mismatch"); diff --git a/src/seria/KVBinaryInputStream.hpp b/src/seria/KVBinaryInputStream.hpp index b825f469..4b300ee7 100644 --- a/src/seria/KVBinaryInputStream.hpp +++ b/src/seria/KVBinaryInputStream.hpp @@ -3,37 +3,45 @@ #pragma once -#include #include "ISeria.hpp" -#include "JsonInputValue.hpp" +#include "JsonInputStream.hpp" #include "common/MemoryStreams.hpp" +#include "common/exception.hpp" namespace seria { -class KVBinaryInputStream : public JsonInputValue { +class KVBinaryInputStream : public JsonInputStreamValue { + common::JsonValue value_storage; + public: KVBinaryInputStream(common::IInputStream &strm); - using JsonInputValue::seria_v; + using JsonInputStreamValue::seria_v; virtual void seria_v(common::BinaryArray &value) override; virtual void binary(void *value, size_t size) override; }; template -void from_binary_key_value(T &v, const common::BinaryArray &buf) { +void from_binary_kv(T &v, common::MemoryInputStream &stream) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); - common::MemoryInputStream stream(buf.data(), buf.size()); KVBinaryInputStream s(stream); - s(v); + try { + ser(v, s); + } catch (const std::exception &) { + std::throw_with_nested(std::runtime_error( + "Error while serializing KV binary object of type '" + common::demangle(typeid(T).name()) + "'")); + } if (!stream.empty()) - throw std::runtime_error("Excess data in from_binary_key_value " + std::string(typeid(T).name())); + throw std::runtime_error( + "Excess data after serializing KV binary object of type '" + common::demangle(typeid(T).name()) + "'"); } template -void from_binary_key_value(T &v, const std::string &buf) { - static_assert(!std::is_pointer::value, "Cannot be called with pointer"); +void from_binary_kv(T &v, const common::BinaryArray &buf) { common::MemoryInputStream stream(buf.data(), buf.size()); - KVBinaryInputStream s(stream); - s(v); - if (!stream.empty()) - throw std::runtime_error("Excess data in from_binary_key_value " + std::string(typeid(T).name())); + from_binary_kv(v, stream); +} +template +void from_binary_kv(T &v, const std::string &buf) { + common::MemoryInputStream stream(buf.data(), buf.size()); + from_binary_kv(v, stream); } } diff --git a/src/seria/KVBinaryOutputStream.cpp b/src/seria/KVBinaryOutputStream.cpp index 1b782f07..5dd8615f 100644 --- a/src/seria/KVBinaryOutputStream.cpp +++ b/src/seria/KVBinaryOutputStream.cpp @@ -9,6 +9,7 @@ #include #include "common/Invariant.hpp" #include "common/Streams.hpp" +#include "common/Varint.hpp" using namespace common; using namespace bytecoin; @@ -21,14 +22,17 @@ namespace { template void write_pod(IOutputStream &s, const T &value) { - s.write(&value, sizeof(T)); + unsigned char buf[sizeof(T)]; + common::uint_le_to_bytes(buf, sizeof(T), static_cast::type>(value)); + s.write(buf, sizeof(T)); } template size_t pack_varint(IOutputStream &s, uint8_t type_or, size_t pv) { T v = static_cast(pv << 2); v |= type_or; - s.write(&v, sizeof(T)); + write_pod(s, v); + // s.write(&v, sizeof(T)); return sizeof(T); } @@ -54,7 +58,7 @@ size_t write_array_size(IOutputStream &s, size_t val) { } else if (val <= 1073741823) { return pack_varint(s, PORTABLE_RAW_SIZE_MARK_DWORD, val); } else { - if (val > 4611686018427387903) { + if (val > 4611686018427387903) { // Warning here on 32-bit platforms throw std::runtime_error("failed to pack varint - too big amount"); } return pack_varint(s, PORTABLE_RAW_SIZE_MARK_INT64, val); @@ -63,15 +67,17 @@ size_t write_array_size(IOutputStream &s, size_t val) { } KVBinaryOutputStream::KVBinaryOutputStream(common::IOutputStream &target) : m_target(target) { - KVBinaryStorageBlockHeader hdr; - hdr.m_signature_a = PORTABLE_STORAGE_SIGNATUREA; - hdr.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; - hdr.m_ver = PORTABLE_STORAGE_FORMAT_VER; - - m_target.write(&hdr, sizeof(hdr)); + KVBinaryStorageBlockHeader hdr{ + PORTABLE_STORAGE_SIGNATUREA, PORTABLE_STORAGE_SIGNATUREB, PORTABLE_STORAGE_FORMAT_VER}; + write_pod(m_target, hdr.m_signature_a); + write_pod(m_target, hdr.m_signature_b); + m_target.write(&hdr.m_ver, 1); } -void KVBinaryOutputStream::object_key(common::StringView name, bool optional) { m_next_key = name; } +bool KVBinaryOutputStream::object_key(common::StringView name, bool optional) { + m_next_key = name; + return true; +} void KVBinaryOutputStream::next_map_key(std::string &name) { m_next_key = name; } void KVBinaryOutputStream::begin_object() { @@ -208,12 +214,14 @@ void KVBinaryOutputStream::seria_v(uint64_t &value) { void KVBinaryOutputStream::seria_v(bool &value) { write_element_prefix(BIN_KV_SERIALIZE_TYPE_BOOL); - write_pod(stream(), value); + write_pod(stream(), uint8_t(value)); } void KVBinaryOutputStream::seria_v(double &value) { - write_element_prefix(BIN_KV_SERIALIZE_TYPE_DOUBLE); - write_pod(stream(), value); + assert(false); // the method is not supported for this type of serialization + throw std::logic_error("double serialization is not supported in KVBinaryOutputStream"); + // write_element_prefix(BIN_KV_SERIALIZE_TYPE_DOUBLE); + // stream().write(&value, sizeof(double)); // TODO - double in binary is bug } void KVBinaryOutputStream::seria_v(std::string &value) { diff --git a/src/seria/KVBinaryOutputStream.hpp b/src/seria/KVBinaryOutputStream.hpp index aa699a75..eb2f2020 100644 --- a/src/seria/KVBinaryOutputStream.hpp +++ b/src/seria/KVBinaryOutputStream.hpp @@ -17,7 +17,7 @@ class KVBinaryOutputStream : public ISeria { virtual bool is_input() const override { return false; } virtual void begin_object() override; - virtual void object_key(common::StringView name, bool optional = false) override; + virtual bool object_key(common::StringView name, bool optional = false) override; virtual void end_object() override; virtual void begin_array(size_t &size, bool fixed_size = false) override; @@ -69,21 +69,21 @@ class KVBinaryOutputStream : public ISeria { }; template -common::BinaryArray to_binary_key_value(const T &v) { +common::BinaryArray to_binary_kv(const T &v) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); common::BinaryArray ba; common::VectorOutputStream stream(ba); KVBinaryOutputStream s(stream); - s(const_cast(v)); + ser(const_cast(v), s); return ba; } template -std::string to_binary_key_value_str(const T &v) { +std::string to_binary_kv_str(const T &v) { static_assert(!std::is_pointer::value, "Cannot be called with pointer"); std::string ba; common::StringOutputStream stream(ba); KVBinaryOutputStream s(stream); - s(const_cast(v)); + ser(const_cast(v), s); return ba; } } diff --git a/src/version.hpp b/src/version.hpp index ca6152f1..ed3b8173 100644 --- a/src/version.hpp +++ b/src/version.hpp @@ -4,8 +4,9 @@ #pragma once // defines are for Windows resource compiler -#define bytecoin_VERSION_WINDOWS_COMMA 3, 18, 8, 17 -#define bytecoin_VERSION_STRING "3.2.4" +#define bytecoin_VERSION_WINDOWS_COMMA 3, 18, 8, 31 +#define bytecoin_VERSION_STRING "3.3.0" + #ifndef RC_INVOKED // Windows resource compiler namespace bytecoin { diff --git a/tests/blockchain/test_blockchain.cpp b/tests/blockchain/test_blockchain.cpp index 4a1d3a0e..a9f28c76 100644 --- a/tests/blockchain/test_blockchain.cpp +++ b/tests/blockchain/test_blockchain.cpp @@ -58,19 +58,24 @@ class TestMiner { BlockTemplate block; Difficulty difficulty = 0; - invariant( - block_chain.create_mining_block_template2(&block, address, bytecoin::BinaryArray{}, &difficulty, bid), ""); - fix_merge_mining_tag(block); - block.timestamp = parent.timestamp + currency.difficulty_target; - block.nonce = crypto::rand(); + Height height = 0; + invariant(block_chain.create_mining_block_template(address, BinaryArray{}, &block, &difficulty, &height, bid), ""); + set_solo_mining_tag(block); + block.parent_block.timestamp = parent.timestamp + currency.difficulty_target; + block.timestamp = block.parent_block.timestamp; + block.parent_block.nonce = crypto::rand(); + block.nonce = block.parent_block.nonce; + auto body_proxy = get_body_proxy_from_template(block); while (true) { - crypto::Hash hash = get_block_long_hash(block, cryptoContext); + BinaryArray ba = currency.get_block_long_hashing_data(block, body_proxy); + Hash hash = cryptoContext.cn_slow_hash(ba.data(), ba.size()); if (check_hash(hash, difficulty)) break; - block.nonce += 1; + block.parent_block.nonce += 1; + block.nonce = block.parent_block.nonce; } RawBlock rb; - MinedBlockDesc desc{block, seria::to_binary(block), get_block_hash(block), parent.height + 1}; + MinedBlockDesc desc{block, seria::to_binary(block), get_block_hash(block, body_proxy), parent.height + 1}; return desc; } void add_mined_block(const MinedBlockDesc &desc, bool log = true) { @@ -93,7 +98,7 @@ class TestMiner { return desc; } void add_checkpoint(uint32_t key_id, uint64_t counter, Hash hash, Height height) { - SignedCheckPoint small_checkpoint; + SignedCheckpoint small_checkpoint; small_checkpoint.height = height; small_checkpoint.hash = hash; small_checkpoint.key_id = key_id; @@ -110,17 +115,22 @@ void test_blockchain(common::CommandLine &cmd) { logging::ConsoleLogger logger; Config config(cmd); config.data_folder = "../tests/scratchpad"; - config.is_testnet = true; - bytecoin::BlockChain::DB::delete_db(config.data_folder + "/blockchain"); + config.net = "test"; + BlockChain::DB::delete_db(config.data_folder + "/blockchain"); - Currency currency(config.is_testnet); + std::cout << "Point 1" << std::endl; + Currency currency(config.net); + std::cout << "Point 2" << std::endl; BlockChainState block_chain(logger, config, currency, /*read only*/ false); + std::cout << "Point 3" << std::endl; TestMiner test_miner(block_chain, currency); + std::cout << "Point 4" << std::endl; auto middle_desc = test_miner.test_grow_chain(block_chain.get_tip().hash, 25); + std::cout << "Point 5" << std::endl; auto small_desc = test_miner.test_grow_chain(middle_desc.hash, 25); invariant(block_chain.get_tip_bid() == small_desc.hash, ""); @@ -170,14 +180,14 @@ class TestBlockChain { api::BlockHeader header; std::bitset<64> checkpoint_key_ids; - BlockChain::CheckPointDifficulty checkpoint_difficulty; // (key_count-1)->max_height + BlockChain::CheckpointDifficulty checkpoint_difficulty; // (key_count-1)->max_height TestBlock *parent = nullptr; std::vector children; }; std::map blocks; - std::map checkpoints; - std::map stable_checkpoints; + std::map checkpoints; + std::map stable_checkpoints; bool add_block(const api::BlockHeader &info) { auto bit = blocks.find(info.hash); @@ -203,7 +213,7 @@ class TestBlockChain { } Hash get_tip_bid() const { return m_tip_bid; } Height get_tip_height() const { return m_tip_height; } - bool add_checkpoint(const SignedCheckPoint &checkpoint) { return false; } + bool add_checkpoint(const SignedCheckpoint &checkpoint) { return false; } BroadcastAction add_block(const PreparedBlock &pb, api::BlockHeader *info) { return BroadcastAction::NOTHING; } BroadcastAction add_mined_block(const BinaryArray &raw_block_template, RawBlock *raw_block, diff --git a/tests/crypto/test_crypto.cpp b/tests/crypto/test_crypto.cpp index 02449c40..b4ae5223 100644 --- a/tests/crypto/test_crypto.cpp +++ b/tests/crypto/test_crypto.cpp @@ -9,11 +9,17 @@ #include "test_crypto.hpp" #include "../io.hpp" -#include "crypto/crypto-ops.h" +#include "crypto/bernstein/crypto-ops.h" #include "crypto/crypto.hpp" #include "crypto/hash.hpp" #include "crypto/random.h" +CRYPTO_MAKE_HASHABLE(crypto, EllipticCurveScalar) +CRYPTO_MAKE_COMPARABLE(crypto, EllipticCurveScalar, crypto::sodium_compare) + +CRYPTO_MAKE_HASHABLE(crypto, EllipticCurvePoint) +CRYPTO_MAKE_COMPARABLE(crypto, EllipticCurvePoint, std::memcmp) + static void check(bool expr, size_t test) { if (expr) return; diff --git a/tests/hash/test_hash.cpp b/tests/hash/test_hash.cpp index b158f701..5dc7f683 100644 --- a/tests/hash/test_hash.cpp +++ b/tests/hash/test_hash.cpp @@ -10,35 +10,43 @@ //#include #include "../io.hpp" +#include "common/Invariant.hpp" #include "common/StringTools.hpp" -#include "crypto/hash-impl.h" +#include "crypto/crypto.hpp" +#include "crypto/hash.h" #include "crypto/hash.hpp" -using namespace std; - static crypto::CryptoNightContext context; -extern "C" { -#ifdef _MSC_VER -#pragma warning(disable : 4297) -#endif +//#ifdef _MSC_VER +//#pragma warning(disable : 4297) +//#endif -static void hash_tree(const void *data, size_t length, unsigned char *hash) { - if ((length & 31) != 0) { - throw ios_base::failure("Invalid input length for tree_hash"); - } - crypto::tree_hash((const unsigned char(*)[32])data, length >> 5, hash); +static void hash_tree(const void *vdata, size_t vlength, crypto::CHash *hash) { + if (vlength % 32 != 0) + throw std::ios_base::failure("Invalid input length for tree_hash"); + const struct crypto::CHash *data = (const struct crypto::CHash *)vdata; + size_t length = vlength / 32; + crypto::tree_hash(data, length, hash); + std::vector branch(crypto::coinbase_tree_depth(length) + 1); + crypto::coinbase_tree_branch(data, length, branch.data()); + invariant(branch.back() == crypto::CHash{}, ""); // No output array overwrite + crypto::CHash hash2; + crypto::tree_hash_from_branch(branch.data(), branch.size() - 1, data, nullptr, &hash2); + invariant(*hash == hash2, ""); } -static void slow_hash(const void *data, size_t length, unsigned char *hash) { +static void slow_hash(const void *data, size_t length, crypto::CHash *hash) { context.cn_slow_hash(data, length, hash); -} + crypto::CHash hash2; + crypto::cn_slow_hash_platform_independent(context.get_data(), data, length, &hash2); + invariant(*hash == hash2, ""); } -extern "C" typedef void hash_f(const void *, size_t, unsigned char *); +extern "C" typedef void hash_f(const void *, size_t, crypto::CHash *); struct hash_func { - const string name; + const std::string name; hash_f &f; } hashes[] = {{"fast", crypto::cn_fast_hash}, {"slow", slow_hash}, {"tree", hash_tree}, {"extra-blake", crypto::hash_extra_blake}, {"extra-groestl", crypto::hash_extra_groestl}, @@ -46,13 +54,13 @@ struct hash_func { void test_hash(const char *test_fun_name, const std::string &test_vectors_filename) { hash_f *f = nullptr; - fstream input; - vector data; + std::fstream input; + std::vector data; crypto::Hash expected, actual; size_t test = 0; for (hash_func *hf = hashes;; hf++) { if (hf >= &hashes[sizeof(hashes) / sizeof(hash_func)]) { - cerr << "Unknown function" << endl; + std::cerr << "Unknown function" << std::endl; throw std::runtime_error("test_hash failed"); } if (test_fun_name == hf->name) { @@ -63,23 +71,23 @@ void test_hash(const char *test_fun_name, const std::string &test_vectors_filena // if (f == slow_hash) { // context = new Crypto::cn_context(); // } - input.open(test_vectors_filename, ios_base::in); + input.open(test_vectors_filename, std::ios_base::in); for (;;) { ++test; - input.exceptions(ios_base::badbit); + input.exceptions(std::ios_base::badbit); get(input, expected); - if (input.rdstate() & ios_base::eofbit) { + if (input.rdstate() & std::ios_base::eofbit) { break; } - input.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit); + input.exceptions(std::ios_base::badbit | std::ios_base::failbit | std::ios_base::eofbit); input.clear(input.rdstate()); get(input, data); - f(data.data(), data.size(), actual.data); + f(data.data(), data.size(), &actual); if (expected != actual) { - cerr << "Hash mismatch on test " << test << endl; + std::cerr << "Hash mismatch on test " << test << std::endl; // cerr << "Input: " << common::pod_to_hex(data) << endl; - cerr << "Expected hash: " << expected << endl; - cerr << "Actual hash: " << actual << endl; + std::cerr << "Expected hash: " << expected << std::endl; + std::cerr << "Actual hash: " << actual << std::endl; ; throw std::runtime_error("test_hash failed"); } @@ -87,6 +95,9 @@ void test_hash(const char *test_fun_name, const std::string &test_vectors_filena } void test_hashes(const std::string &test_vectors_folder) { + size_t depths[17] = {0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4}; + for (size_t i = 1; i < sizeof(depths) / sizeof(*depths); ++i) + invariant(crypto::coinbase_tree_depth(i) == depths[i], ""); test_hash("extra-blake", test_vectors_folder + "/tests-extra-blake.txt"); test_hash("extra-groestl", test_vectors_folder + "/tests-extra-groestl.txt"); test_hash("extra-jh", test_vectors_folder + "/tests-extra-jh.txt"); @@ -94,4 +105,22 @@ void test_hashes(const std::string &test_vectors_folder) { test_hash("fast", test_vectors_folder + "/tests-fast.txt"); test_hash("slow", test_vectors_folder + "/tests-slow.txt"); test_hash("tree", test_vectors_folder + "/tests-tree.txt"); + + for (size_t si = 1; si != 34; ++si) { + std::vector mm_items(si); + for (auto &item : mm_items) { + item.leaf = crypto::rand(); + item.path = crypto::rand(); + if (si > 20) { + item.path.data[0] = (si % 2) ? 0 : 0xff; + item.path.data[1] = (si % 2) ? 0 : 0xff; + } + } + crypto::Hash root = crypto::fill_merge_mining_branches(mm_items.data(), mm_items.size()); + for (auto &item : mm_items) { + crypto::Hash root2 = + crypto::tree_hash_from_branch(item.branch.data(), item.branch.size(), item.leaf, &item.path); + invariant(root == root2, ""); + } + } } diff --git a/tests/json/test_json.cpp b/tests/json/test_json.cpp index 2174fbb6..a2cc8981 100644 --- a/tests/json/test_json.cpp +++ b/tests/json/test_json.cpp @@ -20,7 +20,7 @@ void test_json(const std::string &filename, bool should_be) { common::JsonValue val = common::JsonValue::from_string(content); success = true; } catch (const std::exception &) { - // std::cout << filename << " fail reason: " << ex.what() << std::endl; + // std::cout << filename << " fail reason: " << common::what(ex) << std::endl; } if (success != should_be) throw std::runtime_error("test case failed " + filename); diff --git a/tests/wallet_file/test_wallet_file.cpp b/tests/wallet_file/test_wallet_file.cpp index ade2120b..c5e70d8d 100644 --- a/tests/wallet_file/test_wallet_file.cpp +++ b/tests/wallet_file/test_wallet_file.cpp @@ -8,7 +8,7 @@ #include "test_wallet_file.hpp" -using namespace std; +using namespace bytecoin; // test01.simplewallet.wallet // format - simplewallet with cache @@ -56,34 +56,35 @@ using namespace std; const std::string tmp_name("../tests/scratchpad/test_wallet_file.tmp"); -static void test_body(const bytecoin::Currency ¤cy, const std::string &path, const std::string &password, +static void test_body(const Currency ¤cy, const std::string &path, const std::string &password, const std::vector &addresses, bool view_only, bool test_create_addresses) { logging::ConsoleLogger logger; - bytecoin::Wallet wallet(logger, tmp_name, password); + Wallet wallet(logger, tmp_name, password); if (wallet.is_view_only() != view_only) throw std::runtime_error("view_only test failed for " + path); auto records = wallet.get_records(); if (!crypto::keys_match(wallet.get_view_secret_key(), wallet.get_view_public_key())) throw std::runtime_error("view keys do not match for " + path); - bytecoin::WalletRecord first_record; + WalletRecord first_record = records.at(0); for (auto &&a : addresses) { - bytecoin::AccountPublicAddress address; + AccountPublicAddress address; if (!currency.parse_account_address_string(a, &address)) throw std::runtime_error("failed to parse address " + a); if (address.view_public_key != wallet.get_view_public_key()) throw std::runtime_error("view_public_key test failed for " + path); - auto rit = records.find(address.spend_public_key); - if (rit == records.end()) + size_t pos = 0; + for (; pos != records.size(); ++pos) + if (records.at(pos).spend_public_key == address.spend_public_key) + break; + if (pos == records.size()) throw std::runtime_error("spend_public_key not found for " + path); - if (first_record.spend_public_key == crypto::PublicKey{}) - first_record = rit->second; - if (view_only && rit->second.spend_secret_key != crypto::SecretKey{}) + if (view_only && records.at(pos).spend_secret_key != crypto::SecretKey{}) throw std::runtime_error("non empty secret spend key for " + path); - if (!view_only && !crypto::keys_match(rit->second.spend_secret_key, rit->second.spend_public_key)) + if (!view_only && !crypto::keys_match(records.at(pos).spend_secret_key, records.at(pos).spend_public_key)) throw std::runtime_error("spend keys do not match for " + path); - if (address.spend_public_key != rit->second.spend_public_key) + if (address.spend_public_key != records.at(pos).spend_public_key) throw std::runtime_error("spend_public_key test failed for " + path); - records.erase(rit); + records.erase(records.begin() + pos); } if (!records.empty()) throw std::runtime_error("excess wallet records for " + path); @@ -94,7 +95,7 @@ static void test_body(const bytecoin::Currency ¤cy, const std::string &pat try { wallet.generate_new_addresses({first_record.spend_secret_key}, first_record.creation_timestamp + 1, first_record.creation_timestamp + 1, &rescan_from_ct); - } catch (const bytecoin::Wallet::Exception &) { + } catch (const Wallet::Exception &) { if (!view_only) throw; return; @@ -115,14 +116,14 @@ static void test_body(const bytecoin::Currency ¤cy, const std::string &pat throw std::runtime_error("Reducing timestamp of exising address should lead to rescan " + path); } -static void test_single_file(const bytecoin::Currency ¤cy, const std::string &path, const std::string &password, +static void test_single_file(const Currency ¤cy, const std::string &path, const std::string &password, const std::vector &addresses, bool view_only) { platform::copy_file(path, tmp_name); test_body(currency, tmp_name, password, addresses, view_only, false); { platform::FileStream fs(tmp_name, platform::FileStream::READ_EXISTING); auto si = fs.seek(0, SEEK_END); - if (si != bytecoin::Wallet::wallet_file_size(addresses.size())) + if (si != Wallet::wallet_file_size(addresses.size())) throw std::runtime_error("truncated/overwritten wallet size wrong " + path); } test_body(currency, tmp_name, password, addresses, view_only, true); @@ -130,7 +131,7 @@ static void test_single_file(const bytecoin::Currency ¤cy, const std::stri } void test_wallet_file(const std::string &path_prefix) { - bytecoin::Currency currency(false); + Currency currency("main"); test_single_file(currency, path_prefix + "/test01.simplewallet.wallet", "", {"24xTx43fFtNBUn5f6Fj1wC7y8JsbD4N1XS2s3Q8HzWxtfvERccTPX6e5ua1mf55Wm7Z4MiaWT7LPeiBxPtD8kU9V7z3kuex"}, false); diff --git a/tests/wallet_state/test_wallet_state.cpp b/tests/wallet_state/test_wallet_state.cpp index 4165fd9f..964b0367 100644 --- a/tests/wallet_state/test_wallet_state.cpp +++ b/tests/wallet_state/test_wallet_state.cpp @@ -16,8 +16,8 @@ class WalletStateTest : public WalletStateBasic { std::map memory_spent; explicit WalletStateTest(logging::ILogger &log, const Config &config, const Currency ¤cy) : WalletStateBasic(log, config, currency, "test_wallet_state") {} - Amount add_incoming_output(const api::Output &output) override { - return WalletStateBasic::add_incoming_output(output); + Amount add_incoming_output(const api::Output &output, const Hash & tid) override { + return WalletStateBasic::add_incoming_output(output, tid); } Amount add_incoming_keyimage(Height block_height, const KeyImage &ki) override { return WalletStateBasic::add_incoming_keyimage(block_height, ki); @@ -37,10 +37,10 @@ class WalletStateTest : public WalletStateBasic { }; static bool less_output(const api::Output &a, const api::Output &b) { - return std::tie(a.height, a.amount, a.global_index) < std::tie(b.height, b.amount, b.global_index); + return std::tie(a.height, a.amount, a.index) < std::tie(b.height, b.amount, b.index); } static bool eq_output(const api::Output &a, const api::Output &b) { - return std::tie(a.height, a.amount, a.global_index) == std::tie(b.height, b.amount, b.global_index); + return std::tie(a.height, a.amount, a.index) == std::tie(b.height, b.amount, b.index); } // We will check that WalletStateBasic and model have the same behaviour @@ -67,8 +67,8 @@ class WalletStateModel : public IWalletState { bool ki_exists = all_keyimages.count(output.key_image) != 0; bool unspent_exists = ki_exists && outputs.count(all_keyimages.at(output.key_image)) != 0; if (ki_exists && !unspent_exists) - return 0; // second unspent after first spent - if (output.unlock_time != 0 && !just_unlocked) { // incoming + return 0; // second unspent after first spent + if (output.unlock_block_or_timestamp != 0 && !just_unlocked) { // incoming locked_outputs.push_back(output); return output.amount; } @@ -80,7 +80,7 @@ class WalletStateModel : public IWalletState { added_amount = output.amount - existing_output.amount; invariant(outputs.erase(all_keyimages.at(output.key_image)) == 1, ""); } - auto pa = std::make_pair(output.amount, output.global_index); + auto pa = std::make_pair(output.amount, output.index); invariant(outputs.insert(std::make_pair(pa, output)).second, ""); if (output.key_image != KeyImage{}) all_keyimages[output.key_image] = pa; @@ -100,20 +100,19 @@ class WalletStateModel : public IWalletState { } void unlock(Height block_height, const api::Output &output) { for (size_t i = 0; i != locked_outputs.size(); ++i) { - if (locked_outputs.at(i).amount == output.amount && - locked_outputs.at(i).global_index == output.global_index) { + if (locked_outputs.at(i).amount == output.amount && locked_outputs.at(i).index == output.index) { locked_outputs.erase(locked_outputs.begin() + i); --i; } } Amount adjusted_amount = add_incoming_output(block_height, output, true); - auto pa = std::make_pair(output.amount, output.global_index); + auto pa = std::make_pair(output.amount, output.index); invariant( unlocked_outputs.insert(std::make_pair(pa, std::make_pair(block_height, adjusted_amount))).second, ""); } public: - virtual Amount add_incoming_output(const api::Output &output) override { + virtual Amount add_incoming_output(const api::Output &output, const Hash & tid) override { return add_incoming_output(output.height, output, false); } std::map memory_spent; @@ -121,11 +120,11 @@ class WalletStateModel : public IWalletState { void unlock(Height height, Timestamp timestamp) { std::vector to_unlock; for (size_t i = 0; i != locked_outputs.size(); ++i) - if (m_currency.is_transaction_spend_time_block(locked_outputs.at(i).unlock_time)) { - if (locked_outputs.at(i).unlock_time <= height) + if (m_currency.is_transaction_spend_time_block(locked_outputs.at(i).unlock_block_or_timestamp)) { + if (locked_outputs.at(i).unlock_block_or_timestamp <= height) to_unlock.push_back(locked_outputs.at(i)); } else { - if (locked_outputs.at(i).unlock_time <= timestamp) + if (locked_outputs.at(i).unlock_block_or_timestamp <= timestamp) to_unlock.push_back(locked_outputs.at(i)); } for (auto &&unl : to_unlock) @@ -184,7 +183,7 @@ class WalletStateModel : public IWalletState { (address.empty() || la.second.address == address)) result.push_back(la.second); } - for (const auto & la : locked_outputs) + for (const auto &la : locked_outputs) if (!is_memory_spent(la) && (address.empty() || la.address == address)) result.push_back(la); return result; @@ -234,11 +233,11 @@ class WalletStateModel : public IWalletState { void test_wallet_state(common::CommandLine &cmd) { common::Random random{}; logging::ConsoleLogger logger; - bytecoin::Currency currency(false); - bytecoin::Config config(cmd); + Currency currency("main"); + Config config(cmd); config.data_folder = "../tests/scratchpad"; - bytecoin::BlockChain::DB::delete_db(config.data_folder + "/wallet_cache/test_wallet_state"); + BlockChain::DB::delete_db(config.data_folder + "/wallet_cache/test_wallet_state"); WalletStateTest ws(logger, config, currency); WalletStateModel wm(currency); @@ -305,17 +304,17 @@ void test_wallet_state(common::CommandLine &cmd) { if (add_outputs < 3 && ha != TEST_HEIGHT - 1) { for (uint32_t j = 0; j != add_outputs; ++j) { api::Output output; - size_t dc = 5 + (random() % 5); - size_t am = 1 + (random() % 9); - output.height = ha; - output.amount = am * Currency::DECIMAL_PLACES.at(dc); - output.dust = currency.is_dust(output.amount); - output.global_index = next_gi[output.amount]; - output.address = addresses.at(random() % (addresses.size() - 1)); // last one is empty + size_t dc = 5 + (random() % 5); + size_t am = 1 + (random() % 9); + output.height = ha; + output.amount = am * Currency::DECIMAL_PLACES.at(dc); + output.dust = currency.is_dust(output.amount); + output.index = next_gi[output.amount]; + output.address = addresses.at(random() % (addresses.size() - 1)); // last one is empty if (random() % 20 == 0) - output.unlock_time = (3 * ha / 4) + random() % (TEST_HEIGHT / 2); + output.unlock_block_or_timestamp = (3 * ha / 4) + random() % (TEST_HEIGHT / 2); else if (random() % 20 == 1) - output.unlock_time = + output.unlock_block_or_timestamp = TEST_TIMESTAMP + currency.difficulty_target * ((3 * ha / 4) + random() % (TEST_HEIGHT / 2)); next_gi[output.amount] += 1; if (!VIEW_ONLY) { @@ -330,12 +329,12 @@ void test_wallet_state(common::CommandLine &cmd) { transfer.amount += confirmed_balance_delta; transfer.address = output.address; transfer.ours = true; - transfer.locked = output.unlock_time != 0; + transfer.locked = output.unlock_block_or_timestamp != 0; transfer.outputs.push_back(output); ptx.transfers.push_back(transfer); } - auto inc1 = wm.add_incoming_output(output); - auto inc2 = ws.add_incoming_output(output); + auto inc1 = wm.add_incoming_output(output, Hash{}); + auto inc2 = ws.add_incoming_output(output, Hash{}); invariant(inc1 == inc2, ""); } } @@ -344,7 +343,7 @@ void test_wallet_state(common::CommandLine &cmd) { std::cout << "Total coins remains before final spend" << (ba.locked_or_unconfirmed_outputs + ba.spendable_outputs + ba.spendable_dust_outputs) << " spent " << wm.all_keyimages.size() << std::endl; - for (const auto & ki : output_keyimages) { + for (const auto &ki : output_keyimages) { // if (used_keyimages.insert(ki).second) { // We never get same ki from blockchain api::Output spending_output; if (ws.try_adding_incoming_keyimage(ki, &spending_output)) { @@ -369,7 +368,7 @@ void test_wallet_state(common::CommandLine &cmd) { continue; // if(ha == 19 && wi == 1) // std::cout << "Aha"; - for (const auto & addr : addresses) { + for (const auto &addr : addresses) { auto ba1 = wm.get_balance(addr, ha + wi - 20); auto ba2 = ws.get_balance(addr, ha + wi - 20); if (ba1 != ba2 || ha == TEST_HEIGHT - 1) { @@ -393,7 +392,7 @@ void test_wallet_state(common::CommandLine &cmd) { invariant(ba1 == ba2, ""); } } - for (const auto & addr : addresses) { + for (const auto &addr : addresses) { std::map transfer_balances1; std::map transfer_balances2; Height from_height = ha == 0 ? ha : ha - 1; @@ -411,11 +410,11 @@ void test_wallet_state(common::CommandLine &cmd) { } from_height = ha == 0 ? ha : ha - 1; to_height = ha; - auto unl2 = ws.api_get_unlocked_outputs(addr, from_height, to_height); + auto unl2 = ws.api_get_unlocked_transfers(addr, from_height, to_height); for (auto &&u : unl2) - if (addr.empty() || u.second.address == addr) { - invariant(!u.second.address.empty(), ""); - transfer_balances2[addr] += u.second.amount; + if (addr.empty() || u.address == addr) { + invariant(!u.address.empty(), ""); + transfer_balances2[addr] += u.amount; // invariant(transfer_balances2[addr] >= 0, ""); if (transfer_balances2[addr] == 0) transfer_balances2.erase(addr);