diff --git a/CMakeLists.txt b/CMakeLists.txt index ef9728cd..d7069803 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ if(WIN32) else() 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") + # We can use -Wshadow with clang occasionally if(BETTER_DEBUG) message(STATUS "Using better debug: " ${BETTER_DEBUG}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0") @@ -90,6 +91,7 @@ file(GLOB SRC_HTTP src/http/*.cpp src/http/*.hpp) file(GLOB SRC_PLATFORM src/platform/ExclusiveLock.cpp src/platform/ExclusiveLock.hpp src/platform/Files.cpp src/platform/Files.hpp + src/platform/Ledger.cpp src/platform/Ledger.hpp src/platform/Time.cpp src/platform/Time.hpp src/platform/Network.cpp src/platform/Network.hpp src/platform/PathTools.cpp src/platform/PathTools.hpp diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 7a2c40e8..e60a3b88 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,5 +1,13 @@ ## Release Notes +### v3.4.0-beta-20181218 + +- Signatures are now fully prunable (but not yet pruned) via modification to transaction hash calculations. +- A view-only wallet now shows address balance even for non-auditable unlinkable addresses, but only if sender follows the protocol. If in doubt about sender's fair play, auditable addresses should be used instead. +- A view-only wallet can now be exported with or without ability to view transaction destination addresses. +- `bytecoind` will not automatically look for `blocks.bin` anywhere. If you need to import blocks from file, use `--import-blocks=` command line parameter. +- `bytecoind` command line parameters `--ssl-certificate-pem-file`, `--ssl-certificate-password` removed, use ngnx in https proxy mode! + ### v3.4.0-beta-20181212 *Consensus update (hard fork)* diff --git a/src/Core/BlockChainState.cpp b/src/Core/BlockChainState.cpp index 6ec22c6e..c61e4376 100644 --- a/src/Core/BlockChainState.cpp +++ b/src/Core/BlockChainState.cpp @@ -165,6 +165,10 @@ static Amount validate_semantic(const Currency ¤cy, uint8_t block_major_ve amount = key_output.amount; if (check_output_key && !key_isvalid(key_output.public_key)) throw ConsensusError(common::to_string("Output key not valid elliptic point", key_output.public_key)); + if (check_output_key && tx.version >= currency.amethyst_transaction_version && + !key_isvalid(key_output.encrypted_secret)) + throw ConsensusError( + common::to_string("Output encrypted secret not valid elliptic point", key_output.encrypted_secret)); if (tx.version < currency.amethyst_transaction_version && key_output.is_auditable) throw ConsensusError( common::to_string("Transaction version", tx.version, "insufficient for output with audit")); diff --git a/src/Core/Config.cpp b/src/Core/Config.cpp index d5e25c54..e497daab 100644 --- a/src/Core/Config.cpp +++ b/src/Core/Config.cpp @@ -90,20 +90,6 @@ Config::Config(common::CommandLine &cmd) if (!common::parse_ip_address_and_port(pa, &walletd_bind_ip, &walletd_bind_port)) throw std::runtime_error("Wrong address format " + std::string(pa) + ", should be ip:port"); } - if (const char *pa = cmd.get("--ssl-certificate-pem-file")) { - ssl_certificate_pem_file = pa; -#if !platform_USE_SSL - throw std::runtime_error( - "Setting --ssl-certificate-pem-file impossible - this binary is built without OpenSSL"); -#endif - } - if (const char *pa = cmd.get("--ssl-certificate-password")) { - ssl_certificate_password = pa; -#if !platform_USE_SSL - throw std::runtime_error( - "Setting --ssl_certificate_password impossible - this binary is built without OpenSSL"); -#endif - } if (const char *pa = cmd.get("--" CRYPTONOTE_NAME "d-authorization")) { bytecoind_authorization = common::base64::encode(BinaryArray(pa, pa + strlen(pa))); bytecoind_authorization_private = bytecoind_authorization; diff --git a/src/Core/Config.hpp b/src/Core/Config.hpp index 01bfd3f2..fbd118b4 100644 --- a/src/Core/Config.hpp +++ b/src/Core/Config.hpp @@ -39,8 +39,6 @@ class Config { // Consensus does not depend on those parameters float multicast_period; bool secrets_via_api; - 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; diff --git a/src/Core/Currency.cpp b/src/Core/Currency.cpp index fc0b9c47..952ac811 100644 --- a/src/Core/Currency.cpp +++ b/src/Core/Currency.cpp @@ -87,7 +87,6 @@ Currency::Currency(const std::string &net) if (net == "stage") { upgrade_heights = {1, 1}; // block 1 is already V3 upgrade_desired_major_version = 4; - upgrade_window = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; } { BinaryArray miner_tx_blob; @@ -291,7 +290,8 @@ Height Currency::largest_window() const { Transaction Currency::construct_miner_tx( uint8_t block_major_version, Height height, Amount block_reward, const AccountAddress &miner_address) const { Transaction tx; - const size_t max_outs = get_max_coinbase_outputs(); + const bool is_tx_amethyst = miner_address.type() != typeid(AccountAddressSimple); + const size_t max_outs = get_max_coinbase_outputs(); // If we wish to limit number of outputs, it makes sense to round miner reward to some arbitrary number // Though this solution will reduce number of coins to mix @@ -300,7 +300,8 @@ Transaction Currency::construct_miner_tx( Hash tx_inputs_hash = get_transaction_inputs_hash(tx); KeyPair txkey = crypto::random_keypair(); - extra_add_transaction_public_key(tx.extra, txkey.public_key); + if (!is_tx_amethyst) + extra_add_transaction_public_key(tx.extra, txkey.public_key); std::vector out_amounts; decompose_amount(block_reward, min_dust_threshold, &out_amounts); @@ -312,9 +313,9 @@ Transaction Currency::construct_miner_tx( Amount summary_amounts = 0; for (size_t out_index = 0; out_index < out_amounts.size(); out_index++) { - Hash output_secret = crypto::rand(); - OutputKey tk = TransactionBuilder::create_output( - miner_address, txkey.secret_key, tx_inputs_hash, out_index, output_secret); + const KeyPair output_det_keys = crypto::random_keypair(); + OutputKey tk = TransactionBuilder::create_output( + is_tx_amethyst, miner_address, txkey.secret_key, tx_inputs_hash, out_index, output_det_keys); tk.amount = out_amounts.at(out_index); summary_amounts += tk.amount; tx.outputs.push_back(tk); @@ -322,7 +323,7 @@ Transaction Currency::construct_miner_tx( invariant(summary_amounts == block_reward, ""); - tx.version = miner_address.type() == typeid(AccountAddressSimple) ? 1 : amethyst_transaction_version; + tx.version = is_tx_amethyst ? amethyst_transaction_version : 1; // if mining on old address, we maintain binary compatibility if (block_major_version < amethyst_block_version) tx.unlock_block_or_timestamp = height + mined_money_unlock_window; @@ -557,7 +558,8 @@ bool Currency::amount_allowed_in_output(uint8_t block_major_version, Amount amou }*/ Hash cn::get_transaction_inputs_hash(const TransactionPrefix &tx) { - BinaryArray ba = seria::to_binary(tx.inputs); + const bool is_tx_amethyst = (tx.version >= parameters::TRANSACTION_VERSION_AMETHYST); + BinaryArray ba = seria::to_binary(tx.inputs, is_tx_amethyst); return crypto::cn_fast_hash(ba.data(), ba.size()); } diff --git a/src/Core/Currency.hpp b/src/Core/Currency.hpp index e687b17b..d086bda7 100644 --- a/src/Core/Currency.hpp +++ b/src/Core/Currency.hpp @@ -113,7 +113,6 @@ class Currency { // Consensus calculations depend on those parameters bool is_transaction_unlocked(uint8_t block_major_version, BlockOrTimestamp unlock_time, Height block_height, Timestamp block_time, Timestamp block_median_time) const; - // bool is_dust(Amount amount) const; bool amount_allowed_in_output(uint8_t block_major_version, Amount amount) const; static uint64_t get_penalized_amount(uint64_t amount, size_t median_size, size_t current_transactions_size); @@ -128,13 +127,13 @@ class Currency { // Consensus calculations depend on those parameters const HardCheckpoint *checkpoints_end = nullptr; }; -// we should probaly find better place for these global funs +// we should probably find better place for these global funs 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 BlockHeader &, const BlockBodyProxy &); Hash get_auxiliary_block_header_hash(const BlockHeader &, const BlockBodyProxy &); -// Auxilary hash, or prehash - inserted into MM or CM tree +// Auxiliary or Pre- hash - inserted into MM or CM tree } // namespace cn diff --git a/src/Core/Multicore.cpp b/src/Core/Multicore.cpp index 35ffdcb3..9520a277 100644 --- a/src/Core/Multicore.cpp +++ b/src/Core/Multicore.cpp @@ -252,9 +252,8 @@ WalletPreparatorMulticore::~WalletPreparatorMulticore() { th.join(); } -PreparedWalletTransaction::PreparedWalletTransaction( - TransactionPrefix &&ttx, TransactionSignatures &&sigs, const Wallet::OutputHandler &o_handler) - : tx(std::move(ttx)), sigs(std::move(sigs)) { +PreparedWalletTransaction::PreparedWalletTransaction(TransactionPrefix &&ttx, const Wallet::OutputHandler &o_handler) + : tx(std::move(ttx)) { // We ignore results of most crypto calls here and absence of tx_public_key // All errors will lead to spend_key not found in our wallet PublicKey tx_public_key = extra_get_transaction_public_key(tx.extra); @@ -275,19 +274,16 @@ PreparedWalletTransaction::PreparedWalletTransaction( } PreparedWalletTransaction::PreparedWalletTransaction(Transaction &&tx, const Wallet::OutputHandler &o_handler) - : PreparedWalletTransaction(std::move(static_cast(tx)), std::move(tx.signatures), o_handler) { -} + : PreparedWalletTransaction(std::move(static_cast(tx)), o_handler) {} PreparedWalletBlock::PreparedWalletBlock(BlockTemplate &&bc_header, std::vector &&raw_transactions, - std::vector &&signatures, Hash base_transaction_hash, const Wallet::OutputHandler &o_handler) + Hash base_transaction_hash, const Wallet::OutputHandler &o_handler) : base_transaction_hash(base_transaction_hash) { - header = bc_header; - base_transaction = - PreparedWalletTransaction(std::move(bc_header.base_transaction), TransactionSignatures{}, o_handler); + header = bc_header; + base_transaction = PreparedWalletTransaction(std::move(bc_header.base_transaction), o_handler); transactions.reserve(raw_transactions.size()); for (size_t tx_index = 0; tx_index != raw_transactions.size(); ++tx_index) { - transactions.emplace_back(std::move(raw_transactions.at(tx_index)), - tx_index < signatures.size() ? std::move(signatures.at(tx_index)) : TransactionSignatures{}, o_handler); + transactions.emplace_back(std::move(raw_transactions.at(tx_index)), o_handler); } } @@ -314,7 +310,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), - std::move(sync_block.signatures), sync_block.transactions.at(0).hash, o_handler); + sync_block.transactions.at(0).hash, o_handler); { std::unique_lock lock(mu); if (local_work_counter == work_counter) { diff --git a/src/Core/Multicore.hpp b/src/Core/Multicore.hpp index 4917d91d..b1c22262 100644 --- a/src/Core/Multicore.hpp +++ b/src/Core/Multicore.hpp @@ -92,7 +92,6 @@ class RingCheckerMulticore { struct PreparedWalletTransaction { TransactionPrefix tx; - TransactionSignatures sigs; Hash prefix_hash; Hash inputs_hash; boost::optional derivation; // Will be assigned on first actual use @@ -100,8 +99,7 @@ struct PreparedWalletTransaction { std::vector output_secret_scalars; PreparedWalletTransaction() = default; - PreparedWalletTransaction( - TransactionPrefix &&tx, TransactionSignatures &&sigs, const Wallet::OutputHandler &o_handler); + PreparedWalletTransaction(TransactionPrefix &&tx, const Wallet::OutputHandler &o_handler); PreparedWalletTransaction(Transaction &&tx, const Wallet::OutputHandler &o_handler); }; @@ -112,8 +110,7 @@ struct PreparedWalletBlock { std::vector transactions; PreparedWalletBlock() = default; PreparedWalletBlock(BlockTemplate &&bc_header, std::vector &&raw_transactions, - std::vector &&signatures, Hash base_transaction_hash, - const Wallet::OutputHandler &o_handler); + Hash base_transaction_hash, const Wallet::OutputHandler &o_handler); }; class WalletPreparatorMulticore { diff --git a/src/Core/Node.cpp b/src/Core/Node.cpp index 309a16e9..031ac2fe 100644 --- a/src/Core/Node.cpp +++ b/src/Core/Node.cpp @@ -5,7 +5,6 @@ #include #include #include -#include "BlockChainFileFormat.hpp" #include "Config.hpp" #include "CryptoNoteTools.hpp" #include "TransactionExtra.hpp" @@ -40,15 +39,10 @@ Node::Node(logging::ILogger &log, const Config &config, BlockChainState &block_c const std::string old_path = platform::get_default_data_directory(CRYPTONOTE_NAME); const std::string new_path = config.get_data_folder(); - m_block_chain_reader1 = std::make_unique( - block_chain.get_currency(), new_path + config.block_indexes_file_name, new_path + config.blocks_file_name); - 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 = std::make_unique(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()); + std::bind(&Node::on_api_http_request, this, _1, _2, _3), + std::bind(&Node::on_api_http_disconnect, this, _1)); m_commit_timer.once(float(m_config.db_commit_period_blockchain)); advance_long_poll(); @@ -106,8 +100,7 @@ bool Node::on_idle() { auto idle_start = std::chrono::steady_clock::now(); Hash was_top_bid = m_block_chain.get_tip_bid(); bool on_idle_result = false; - if (!m_block_chain_reader1 && !m_block_chain_reader2 && - m_block_chain.get_tip_height() >= m_block_chain.internal_import_known_height()) { + if (m_block_chain.get_tip_height() >= m_block_chain.internal_import_known_height()) { for (size_t s = 0; s != 10; ++s) { bool on_idle_result_s = false; std::vector bp_copy{m_broadcast_protocols.begin(), m_broadcast_protocols.end()}; @@ -121,14 +114,6 @@ bool Node::on_idle() { } if (m_block_chain.get_tip_height() < m_block_chain.internal_import_known_height()) m_block_chain.internal_import(); - else { - if (m_block_chain_reader1 && !m_block_chain_reader1->import_blocks(&m_block_chain)) { - m_block_chain_reader1.reset(); - } - if (m_block_chain_reader2 && !m_block_chain_reader2->import_blocks(&m_block_chain)) { - m_block_chain_reader2.reset(); - } - } if (m_block_chain.get_tip_bid() != was_top_bid) { advance_long_poll(); } @@ -319,12 +304,6 @@ api::cnd::GetStatus::Response Node::create_status_response() const { res.top_known_block_height = std::max(res.top_known_block_height, gc->get_peer_sync_data().current_height); res.top_known_block_height = std::max(res.top_known_block_height, m_block_chain.internal_import_known_height()); - if (m_block_chain_reader1) - res.top_known_block_height = - std::max(res.top_known_block_height, m_block_chain_reader1->get_block_count()); - if (m_block_chain_reader2) - res.top_known_block_height = - std::max(res.top_known_block_height, m_block_chain_reader2->get_block_count()); for (auto &&pb : m_broadcast_protocols) if (pb->is_incoming()) res.incoming_peer_count += 1; @@ -722,8 +701,12 @@ bool Node::on_check_sendproof(http::Client *, http::RequestBody &&, json_rpc::Re } Transaction tx; seria::from_binary(tx, binary_tx); - Hash message_hash = crypto::cn_fast_hash(sp.message.data(), sp.message.size()); - if (sp.address.type() == typeid(AccountAddressSimple)) { + const Hash tx_inputs_hash = get_transaction_inputs_hash(tx); + const Hash message_hash = crypto::cn_fast_hash(sp.message.data(), sp.message.size()); + if (tx.version < m_block_chain.get_currency().amethyst_transaction_version) { + if (sp.address.type() != typeid(AccountAddressSimple)) + throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::ADDRESS_NOT_IN_TRANSACTION, + "Transaction version too low to contain address of type other than simple"); auto &addr = boost::get(sp.address); auto &var = boost::get(sp.proof); PublicKey tx_public_key = extra_get_transaction_public_key(tx.extra); @@ -753,58 +736,84 @@ bool Node::on_check_sendproof(http::Client *, http::RequestBody &&, json_rpc::Re "Wrong amount in outputs, actual amount is " + common::to_string(total_amount)); return true; } - if (sp.address.type() == typeid(AccountAddressUnlinkable)) { - auto &addr = boost::get(sp.address); - auto &var = boost::get(sp.proof); - Hash tx_inputs_hash = get_transaction_inputs_hash(tx); - for (size_t oi = 1; oi < var.elements.size(); ++oi) { - if (var.elements.at(oi).out_index <= var.elements.at(oi - 1).out_index) + auto &var = boost::get(sp.proof); + for (size_t oi = 1; oi < var.elements.size(); ++oi) { + if (var.elements.at(oi).out_index <= var.elements.at(oi - 1).out_index) + throw api::cnd::CheckSendproof::Error( + api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof object elements are not in ascending order"); + } + std::reverse(var.elements.begin(), var.elements.end()); // pop_back instead of erase(begin) + Amount total_amount = 0; + for (size_t out_index = 0; out_index != tx.outputs.size() && !var.elements.empty(); ++out_index) { + const auto &output = tx.outputs.at(out_index); + if (output.type() != typeid(OutputKey)) + continue; + const auto &key_output = boost::get(output); + if (var.elements.back().out_index != out_index) + continue; + const auto &el = var.elements.back(); + BinaryArray ba(std::begin(el.deterministic_public_key.data), std::end(el.deterministic_public_key.data)); + const SecretKey output_secret_scalar = crypto::hash_to_scalar(ba.data(), ba.size()); + const PublicKey output_secret_point = crypto::hash_to_point(ba.data(), ba.size()); + ba.push_back(0); // Or use cn_fast_hash64 + const Hash output_secret2 = crypto::cn_fast_hash(ba.data(), ba.size()); + + if (sp.address.type() == typeid(AccountAddressSimple)) { + const auto &addr = boost::get(sp.address); + AccountAddressSimple address; + crypto::linkable_underive_address(output_secret_scalar, tx_inputs_hash, out_index, key_output.public_key, + key_output.encrypted_secret, &address.spend_public_key, &address.view_public_key); + if (address != addr) throw api::cnd::CheckSendproof::Error( - api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof object elements are not in ascending order"); - } - Amount total_amount = 0; - size_t out_index = 0; - for (const auto &output : tx.outputs) { - if (output.type() == typeid(OutputKey)) { - const auto &key_output = boost::get(output); - for (size_t oi = 0; oi != var.elements.size(); ++oi) { - const auto &el = var.elements.at(oi); - if (el.out_index == out_index) { - if (addr.is_auditable != key_output.is_auditable) - throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::WRONG_SIGNATURE, - "Auditability of coin does not match auditability of address"); - Hash output_secret = crypto::cn_fast_hash(el.q.data, sizeof(el.q.data)); - if (!crypto::check_signature(output_secret, el.q, el.signature)) - throw api::cnd::CheckSendproof::Error( - api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof object element signature wrong"); - AccountAddressUnlinkable address; - if (!crypto::unlinkable_underive_address(output_secret, tx_inputs_hash, out_index, - key_output.public_key, key_output.encrypted_secret, &address.s, &address.sv) || - address != addr) { - throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::WRONG_SIGNATURE, - "Proof unlinkable address derivation failed"); - } - total_amount += key_output.amount; - response.output_indexes.push_back(out_index); - var.elements.erase(var.elements.begin() + oi); - break; - } - } - } - ++out_index; - } - if (!var.elements.empty()) + api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof linkable address derivation failed"); + const uint8_t should_be_encrypted_address_type = AccountAddressSimple::type_tag ^ output_secret2.data[0]; + if (key_output.encrypted_address_type != should_be_encrypted_address_type) + throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::WRONG_SIGNATURE, + "Proof invalid because encrypted address type wrong (protocol violated when sending)"); + if (!crypto::amethyst_check_sendproof(el.deterministic_public_key, sp.transaction_hash, message_hash, + address.spend_public_key, address.view_public_key, el.signature)) + throw api::cnd::CheckSendproof::Error( + api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof object element signature wrong"); + } else if (sp.address.type() == typeid(AccountAddressUnlinkable)) { + const auto &addr = boost::get(sp.address); + AccountAddressUnlinkable address; + crypto::unlinkable_underive_address(output_secret_point, tx_inputs_hash, out_index, key_output.public_key, + key_output.encrypted_secret, &address.s, &address.sv); + address.is_auditable = key_output.is_auditable; + if (addr.is_auditable != address.is_auditable) + throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::WRONG_SIGNATURE, + "Auditability of coin does not match auditability of address"); + if (address != addr) + throw api::cnd::CheckSendproof::Error( + api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof unlinkable address derivation failed"); + const uint8_t should_be_encrypted_address_type = + (addr.is_auditable ? AccountAddressUnlinkable::type_tag_auditable + : AccountAddressUnlinkable::type_tag) ^ + output_secret2.data[0]; + if (key_output.encrypted_address_type != should_be_encrypted_address_type) + throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::WRONG_SIGNATURE, + "Proof invalid because encrypted address type wrong (protocol violated when sending)"); + if (!crypto::amethyst_check_sendproof(el.deterministic_public_key, sp.transaction_hash, message_hash, + address.s, address.sv, el.signature)) + throw api::cnd::CheckSendproof::Error( + api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof object element signature wrong"); + } else throw api::cnd::CheckSendproof::Error( - api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof object contains excess elements"); - if (total_amount == 0) - throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::ADDRESS_NOT_IN_TRANSACTION, - "No outputs found in transaction for the address being proofed"); - if (total_amount != sp.amount) - throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::WRONG_AMOUNT, - "Wrong amount in outputs, actual amount is " + common::to_string(total_amount)); - return true; + api::cnd::CheckSendproof::WRONG_SIGNATURE, "Unknown address type in proof"); + total_amount += key_output.amount; + response.output_indexes.push_back(out_index); + var.elements.pop_back(); } - return false; + if (!var.elements.empty()) + throw api::cnd::CheckSendproof::Error( + api::cnd::CheckSendproof::WRONG_SIGNATURE, "Proof object contains excess elements"); + if (total_amount == 0) + throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::ADDRESS_NOT_IN_TRANSACTION, + "No outputs found in transaction for the address being proofed"); + if (total_amount != sp.amount) + throw api::cnd::CheckSendproof::Error(api::cnd::CheckSendproof::WRONG_AMOUNT, + "Wrong amount in outputs, actual amount is " + common::to_string(total_amount)); + return true; } void Node::submit_block(const BinaryArray &blockblob, api::BlockHeader *info) { diff --git a/src/Core/Node.hpp b/src/Core/Node.hpp index 449361f8..6071e4a3 100644 --- a/src/Core/Node.hpp +++ b/src/Core/Node.hpp @@ -23,7 +23,6 @@ namespace platform { class PreventSleep; } namespace cn { -class LegacyBlockChainReader; class Node { public: @@ -88,9 +87,6 @@ class Node { const Config &m_config; protected: - // We read from both because any could be truncated/corrupted - std::unique_ptr m_block_chain_reader1; - std::unique_ptr m_block_chain_reader2; std::unique_ptr m_api; std::unique_ptr m_prevent_sleep; struct LongPollClient { diff --git a/src/Core/TransactionBuilder.cpp b/src/Core/TransactionBuilder.cpp index 294e5914..e6adfd2c 100644 --- a/src/Core/TransactionBuilder.cpp +++ b/src/Core/TransactionBuilder.cpp @@ -17,27 +17,114 @@ using namespace cn; -OutputKey TransactionBuilder::create_output(const AccountAddress &to, const SecretKey &tx_secret_key, - const Hash &tx_inputs_hash, size_t output_index, const Hash &output_secret) { +std::array TransactionBuilder::encrypt_real_index( + const PublicKey &output_key, const SecretKey &view_secret_key) { + std::array result{}; + BinaryArray ba; + common::append(ba, std::begin(view_secret_key.data), std::end(view_secret_key.data)); + common::append(ba, std::begin(output_key.data), std::end(output_key.data)); + Hash ha = crypto::cn_fast_hash(ba.data(), ba.size()); + static_assert(result.size() < sizeof(ha), "Modify line below"); + std::copy(ha.data, ha.data + result.size(), result.begin()); + return result; +} + +OutputKey TransactionBuilder::create_output(bool tx_amethyst, const AccountAddress &to, const SecretKey &tx_secret_key, + const Hash &tx_inputs_hash, size_t output_index, const KeyPair &output_det_keys) { OutputKey out_key; + + BinaryArray ba(std::begin(output_det_keys.public_key.data), std::end(output_det_keys.public_key.data)); + SecretKey output_secret_scalar = crypto::hash_to_scalar(ba.data(), ba.size()); + PublicKey output_secret_point = crypto::hash_to_point(ba.data(), ba.size()); + ba.push_back(0); // Or use cn_fast_hash64 + Hash output_secret2 = crypto::cn_fast_hash(ba.data(), ba.size()); + + if (!tx_amethyst) { + if (to.type() == typeid(AccountAddressSimple)) { + auto &addr = boost::get(to); + const KeyDerivation derivation = crypto::generate_key_derivation(addr.view_public_key, tx_secret_key); + out_key.public_key = crypto::derive_public_key(derivation, output_index, addr.spend_public_key); + out_key.encrypted_address_type = AccountAddressSimple::type_tag ^ output_secret2.data[0]; + return out_key; + } + throw std::runtime_error( + "TransactionBuilder::create_output output type forbidden for transaction version, wait for amethyst upgrade"); + } if (to.type() == typeid(AccountAddressSimple)) { - auto &addr = boost::get(to); - const KeyDerivation derivation = crypto::generate_key_derivation(addr.view_public_key, tx_secret_key); - out_key.public_key = crypto::derive_public_key(derivation, output_index, addr.spend_public_key); - for (size_t i = 0; i != sizeof(output_secret); ++i) - out_key.encrypted_secret.data[i] = output_secret.data[i] ^ addr.view_public_key.data[i]; + auto &addr = boost::get(to); + out_key.public_key = crypto::linkable_derive_public_key(output_secret_scalar, tx_inputs_hash, output_index, + addr.spend_public_key, addr.view_public_key, &out_key.encrypted_secret); + out_key.encrypted_address_type = AccountAddressSimple::type_tag ^ output_secret2.data[0]; return out_key; } if (to.type() == typeid(AccountAddressUnlinkable)) { auto &addr = boost::get(to); out_key.public_key = crypto::unlinkable_derive_public_key( - output_secret, tx_inputs_hash, output_index, addr.s, addr.sv, &out_key.encrypted_secret); + output_secret_point, tx_inputs_hash, output_index, addr.s, addr.sv, &out_key.encrypted_secret); out_key.is_auditable = addr.is_auditable; + out_key.encrypted_address_type = + (addr.is_auditable ? AccountAddressUnlinkable::type_tag_auditable : AccountAddressUnlinkable::type_tag) ^ + output_secret2.data[0]; return out_key; } throw std::runtime_error("TransactionBuilder::create_output unknown address type"); } +bool TransactionBuilder::detect_not_our_output(const Wallet *wallet, bool tx_amethyst, const Hash &tid, + const Hash &tx_inputs_hash, boost::optional *history, KeyPair *tx_keys, size_t out_index, + const OutputKey &key_output, Amount *amount, AccountAddress *address) { + if (!tx_amethyst) { + if (!*history) + *history = wallet->load_history(tid); + if (!history->get().empty()) { + if (tx_keys->secret_key == SecretKey{}) + *tx_keys = transaction_keys_from_seed(tx_inputs_hash, wallet->get_tx_derivation_seed()); + for (const auto &addr : history->get()) { + const KeyDerivation derivation = generate_key_derivation(addr.view_public_key, tx_keys->secret_key); + const PublicKey guess_key = crypto::derive_public_key(derivation, out_index, addr.spend_public_key); + if (guess_key == key_output.public_key) { + *address = addr; + *amount = key_output.amount; + return true; + } + } + } + return false; + } + // In amethyst, we sholud always detect out outputs + const KeyPair output_det_keys = TransactionBuilder::deterministic_keys_from_seed( + tx_inputs_hash, wallet->get_tx_derivation_seed(), common::get_varint_data(out_index)); + BinaryArray ba(std::begin(output_det_keys.public_key.data), std::end(output_det_keys.public_key.data)); + const SecretKey output_secret_scalar = crypto::hash_to_scalar(ba.data(), ba.size()); + const PublicKey output_secret_point = crypto::hash_to_point(ba.data(), ba.size()); + ba.push_back(0); // Or use cn_fast_hash64 + const Hash output_secret2 = crypto::cn_fast_hash(ba.data(), ba.size()); + + const uint8_t address_type = key_output.encrypted_address_type ^ output_secret2.data[0]; + if (address_type == AccountAddressSimple::type_tag) { + AccountAddressSimple addr; + crypto::linkable_underive_address(output_secret_scalar, tx_inputs_hash, out_index, key_output.public_key, + key_output.encrypted_secret, &addr.spend_public_key, &addr.view_public_key); + *address = addr; + *amount = key_output.amount; + return true; + } + if (address_type == AccountAddressUnlinkable::type_tag || + address_type == AccountAddressUnlinkable::type_tag_auditable) { + const bool address_auditable = (address_type == AccountAddressUnlinkable::type_tag_auditable); + if (address_auditable != key_output.is_auditable) + return false; + AccountAddressUnlinkable u_address; + crypto::unlinkable_underive_address(output_secret_point, tx_inputs_hash, out_index, key_output.public_key, + key_output.encrypted_secret, &u_address.s, &u_address.sv); + u_address.is_auditable = key_output.is_auditable; + *amount = key_output.amount; + *address = u_address; + return true; + } + return false; +} + void TransactionBuilder::add_output(uint64_t amount, const AccountAddress &to) { m_output_descs.push_back(OutputDesc{amount, to}); } @@ -97,24 +184,24 @@ Transaction TransactionBuilder::sign( input_key.amount = our_output.amount; for (const auto &o : desc.outputs) input_key.output_indexes.push_back(o.index); - input_key.output_indexes = absolute_output_offsets_to_relative(input_key.output_indexes); + input_key.output_indexes = absolute_output_offsets_to_relative(input_key.output_indexes); + input_key.encrypted_real_index = encrypt_real_index(our_output.public_key, wallet->get_view_secret_key()); m_transaction.inputs.push_back(input_key); } // Deterministic generation of tx private key. const Hash tx_inputs_hash = get_transaction_inputs_hash(m_transaction); const KeyPair tx_keys = transaction_keys_from_seed(tx_inputs_hash, wallet->get_tx_derivation_seed()); - extra_add_transaction_public_key(m_transaction.extra, tx_keys.public_key); + if (!is_tx_amethyst) + 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 out_index = 0; out_index != m_output_descs.size(); ++out_index) { - KeyPair output_secret_keys = deterministic_keys_from_seed( + KeyPair output_det_keys = deterministic_keys_from_seed( tx_inputs_hash, wallet->get_tx_derivation_seed(), common::get_varint_data(out_index)); - Hash output_secret = - crypto::cn_fast_hash(output_secret_keys.public_key.data, sizeof(output_secret_keys.public_key.data)); - OutputKey out_key = TransactionBuilder::create_output( - m_output_descs.at(out_index).addr, tx_keys.secret_key, tx_inputs_hash, out_index, output_secret); - out_key.amount = m_output_descs.at(out_index).amount; + OutputKey out_key = TransactionBuilder::create_output(is_tx_amethyst, m_output_descs.at(out_index).addr, + tx_keys.secret_key, tx_inputs_hash, out_index, output_det_keys); + out_key.amount = m_output_descs.at(out_index).amount; m_transaction.outputs.at(out_index) = out_key; } // Now we can sign @@ -141,7 +228,7 @@ Transaction TransactionBuilder::sign( throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "Could not parse address " + our_output.address); invariant(!only_records || only_records->count(address) != 0, "Output with wrong address selected by selector"); WalletRecord record; - if (!wallet->get_record(record, address)) + if (!wallet->get_record(&record, address)) throw json_rpc::Error(json_rpc::INTERNAL_ERROR, "No keys in wallet for address " + our_output.address); KeyPair output_keypair; boost::optional kd; diff --git a/src/Core/TransactionBuilder.hpp b/src/Core/TransactionBuilder.hpp index 403659cb..a35c5109 100644 --- a/src/Core/TransactionBuilder.hpp +++ b/src/Core/TransactionBuilder.hpp @@ -4,12 +4,12 @@ #pragma once #include "CryptoNote.hpp" +#include "Wallet.hpp" #include "logging/LoggerMessage.hpp" #include "rpc_api.hpp" namespace cn { -class Wallet; class Currency; class WalletStateBasic; @@ -45,9 +45,12 @@ class TransactionBuilder { const Hash &tx_inputs_hash, const Hash &tx_derivation_seed, const BinaryArray &add); static KeyPair deterministic_keys_from_seed( const TransactionPrefix &tx, const Hash &tx_derivation_seed, const BinaryArray &add); - - static OutputKey create_output(const AccountAddress &to, const SecretKey &tx_secret_key, const Hash &tx_inputs_hash, - size_t output_index, const Hash &output_secret); + static std::array encrypt_real_index(const PublicKey &output_key, const SecretKey &view_secret_key); + static OutputKey create_output(bool tx_amethyst, const AccountAddress &to, const SecretKey &tx_secret_key, + const Hash &tx_inputs_hash, size_t output_index, const KeyPair &output_det_keys); + static bool detect_not_our_output(const Wallet *wallet, bool tx_amethyst, const Hash &tid, + const Hash &tx_inputs_hash, boost::optional *, KeyPair *tx_keys, size_t out_index, + const OutputKey &, Amount *, AccountAddress *); }; class UnspentSelector { diff --git a/src/Core/Wallet.cpp b/src/Core/Wallet.cpp index 147d8841..bc041338 100644 --- a/src/Core/Wallet.cpp +++ b/src/Core/Wallet.cpp @@ -93,18 +93,15 @@ static Hash derive_from_key(const crypto::chacha_key &key, const std::string &ap AccountAddress Wallet::get_first_address() const { return record_to_address(m_wallet_records.at(0)); } std::string Wallet::get_cache_name() const { - Hash h = crypto::cn_fast_hash(m_view_public_key.data, sizeof(m_view_public_key.data)); - return common::pod_to_hex(h) + (is_view_only() ? "-view-only" : std::string()); -} - -bool Wallet::is_our_address(const AccountAddress &v_addr) const { - if (v_addr.type() != typeid(AccountAddressSimple)) - return false; - auto &addr = boost::get(v_addr); - 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; - return m_wallet_records.at(rit->second).spend_public_key == addr.spend_public_key; + Hash h = crypto::cn_fast_hash(m_view_public_key.data, sizeof(m_view_public_key.data)); + std::string name = common::pod_to_hex(h); + if (is_view_only()) { + if (can_view_outgoing_addresses()) + name += "-view-only-voa"; + else + name += "-view-only"; + } + return name; } bool Wallet::get_look_ahead_record(WalletRecord &record, const PublicKey &spend_public_key) { @@ -159,6 +156,11 @@ static void encrypt_key_pair( chacha8(&rec_data, sizeof(r.data), key, r.iv, r.data); } +bool Wallet::is_our_address(const AccountAddress &v_addr) const { + WalletRecord wr; + return get_record(&wr, v_addr); +} + size_t WalletContainerStorage::wallet_file_size(size_t records) { return 1 + sizeof(ContainerStoragePrefix) + sizeof(uint64_t) * 2 + sizeof(EncryptedWalletRecord) * records; } @@ -496,7 +498,7 @@ AccountAddress WalletContainerStorage::record_to_address(const WalletRecord &rec return AccountAddressSimple{record.spend_public_key, m_view_public_key}; } -bool WalletContainerStorage::get_record(WalletRecord &record, const AccountAddress &v_addr) const { +bool WalletContainerStorage::get_record(WalletRecord *record, const AccountAddress &v_addr) const { if (v_addr.type() != typeid(AccountAddressSimple)) return false; auto &addr = boost::get(v_addr); @@ -506,7 +508,7 @@ bool WalletContainerStorage::get_record(WalletRecord &record, const AccountAddre if (rit->second >= get_actual_records_count()) return false; invariant(m_wallet_records.at(rit->second).spend_public_key == addr.spend_public_key, ""); - record = m_wallet_records.at(rit->second); + *record = m_wallet_records.at(rit->second); return true; } @@ -674,47 +676,6 @@ bool WalletContainerStorage::detect_our_output(const Hash &tid, const Hash &tx_i return true; } -bool WalletContainerStorage::detect_not_our_output(bool tx_amethyst, const Hash &tid, const Hash &tx_inputs_hash, - boost::optional *history, size_t out_index, const OutputKey &key_output, Amount *amount, - AccountAddress *address) { - KeyPair tx_keys; // We do some expensive calcs once and only if needed - if (tx_amethyst) { - KeyPair output_secret_keys = TransactionBuilder::deterministic_keys_from_seed( - tx_inputs_hash, m_tx_derivation_seed, common::get_varint_data(out_index)); - Hash output_secret = - crypto::cn_fast_hash(output_secret_keys.public_key.data, sizeof(output_secret_keys.public_key.data)); - AccountAddressSimple addr; - for (size_t i = 0; i != sizeof(output_secret); ++i) - addr.view_public_key.data[i] = output_secret.data[i] ^ key_output.encrypted_secret.data[i]; - if (crypto::key_isvalid(addr.view_public_key)) { - if (tx_keys.secret_key == SecretKey{}) - tx_keys = TransactionBuilder::transaction_keys_from_seed(tx_inputs_hash, m_tx_derivation_seed); - KeyDerivation to_derivation = generate_key_derivation(addr.view_public_key, tx_keys.secret_key); - addr.spend_public_key = underive_public_key(to_derivation, out_index, key_output.public_key); - *address = addr; - *amount = key_output.amount; - return true; - } - return false; - } - if (!*history) - *history = load_history(tid); - if (!history->get().empty()) { - if (tx_keys.secret_key == SecretKey{}) - tx_keys = TransactionBuilder::transaction_keys_from_seed(tx_inputs_hash, m_tx_derivation_seed); - for (const auto &addr : history->get()) { - const KeyDerivation derivation = generate_key_derivation(addr.view_public_key, tx_keys.secret_key); - const PublicKey guess_key = crypto::derive_public_key(derivation, out_index, addr.spend_public_key); - if (guess_key == key_output.public_key) { - *address = addr; - *amount = key_output.amount; - return true; - } - } - } - return false; -} - static const std::string current_version = "CryptoNoteWallet1"; static const size_t GENERATE_AHEAD = 20000; // TODO - move to better place @@ -818,9 +779,9 @@ WalletHD::WalletHD(const Currency ¤cy, logging::ILogger &log, const std::s m_wallet_key = generate_chacha8_key(cn_ctx, salt.data(), salt.size()); try { load(); - } catch (const Bip32Key::Exception &ex) { + } catch (const Bip32Key::Exception &) { std::throw_with_nested(Exception{api::WALLETD_MNEMONIC_CRC, "Wrong mnemonic"}); - } catch (const std::exception &ex) { + } catch (const std::exception &) { std::throw_with_nested(Exception{api::WALLET_FILE_DECRYPT_ERROR, "Wallet file invalid or wrong password"}); } } @@ -859,9 +820,9 @@ WalletHD::WalletHD(const Currency ¤cy, logging::ILogger &log, const std::s try { load(); - } catch (const Bip32Key::Exception &ex) { + } catch (const Bip32Key::Exception &) { std::throw_with_nested(Exception{api::WALLETD_MNEMONIC_CRC, "Wrong mnemonic"}); - } catch (const std::exception &ex) { + } catch (const std::exception &) { std::throw_with_nested(Exception{api::WALLET_FILE_DECRYPT_ERROR, "Wallet file invalid or wrong password"}); } commit(); @@ -1096,11 +1057,13 @@ AccountAddress WalletHD::record_to_address(const WalletRecord &record) const { record.spend_public_key, sv, m_address_type == AccountAddressUnlinkable::type_tag_auditable}; } -bool WalletHD::get_record(WalletRecord &record, const AccountAddress &v_addr) const { +bool WalletHD::get_record(WalletRecord *record, const AccountAddress &v_addr) const { if (v_addr.type() != typeid(AccountAddressUnlinkable)) return false; auto &addr = boost::get(v_addr); - auto rit = m_records_map.find(addr.s); + if (addr.is_auditable != is_auditable()) + return false; + auto rit = m_records_map.find(addr.s); if (rit == m_records_map.end() || rit->second >= get_actual_records_count()) return false; // TODO - do not call record_to_address @@ -1108,7 +1071,7 @@ bool WalletHD::get_record(WalletRecord &record, const AccountAddress &v_addr) co if (v_addr != addr2) return false; // invariant (m_wallet_records.at(rit->second).spend_public_key == addr.spend_public_key, ""); - record = m_wallet_records.at(rit->second); + *record = m_wallet_records.at(rit->second); return true; } @@ -1350,21 +1313,3 @@ bool WalletHD::detect_our_output(const Hash &tid, const Hash &tx_inputs_hash, co *amount = key_output.amount; return true; } - -bool WalletHD::detect_not_our_output(bool tx_amethyst, const Hash &tid, const Hash &tx_inputs_hash, - boost::optional *history, size_t out_index, const OutputKey &key_output, Amount *amount, - AccountAddress *address) { - KeyPair output_secret_keys = TransactionBuilder::deterministic_keys_from_seed( - tx_inputs_hash, m_tx_derivation_seed, common::get_varint_data(out_index)); - Hash output_secret = - crypto::cn_fast_hash(output_secret_keys.public_key.data, sizeof(output_secret_keys.public_key.data)); - AccountAddressUnlinkable u_address; - if (!crypto::unlinkable_underive_address(output_secret, tx_inputs_hash, out_index, key_output.public_key, - key_output.encrypted_secret, &u_address.s, &u_address.sv)) - return false; - u_address.is_auditable = key_output.is_auditable; - *amount = key_output.amount; - // We cannot generate key_image for others addresses - *address = u_address; - return true; -} diff --git a/src/Core/Wallet.hpp b/src/Core/Wallet.hpp index 83c97325..dfbc06b0 100644 --- a/src/Core/Wallet.hpp +++ b/src/Core/Wallet.hpp @@ -59,6 +59,7 @@ class Wallet { virtual void export_wallet(const std::string &export_path, const std::string &new_password, bool view_only, bool view_outgoing_addresses) const = 0; virtual bool is_view_only() const { return m_wallet_records.at(0).spend_secret_key == SecretKey{}; } + bool can_view_outgoing_addresses() const { return m_tx_derivation_seed != Hash{}; } virtual bool is_deterministic() const { return false; } virtual bool is_unlinkable() const { return false; } virtual bool is_auditable() const { return false; } @@ -68,7 +69,7 @@ class Wallet { const SecretKey &get_view_secret_key() const { return m_view_secret_key; } const std::vector &get_records() const { return m_wallet_records; } virtual size_t get_actual_records_count() const { return m_wallet_records.size(); } - virtual bool get_record(WalletRecord &record, const AccountAddress &) const = 0; + virtual bool get_record(WalletRecord *record, const AccountAddress &) const = 0; virtual void create_look_ahead_records(size_t count) {} bool get_look_ahead_record(WalletRecord &record, const PublicKey &); @@ -109,8 +110,6 @@ class Wallet { virtual bool detect_our_output(const Hash &tid, const Hash &tx_inputs_hash, const boost::optional &kd, size_t out_index, const PublicKey &spend_public_key, const SecretKey &secret_scalar, const OutputKey &, Amount *, KeyPair *output_keypair, AccountAddress *) = 0; - virtual bool detect_not_our_output(bool tx_amethyst, const Hash &tid, const Hash &tx_inputs_hash, - boost::optional *, size_t out_index, const OutputKey &, Amount *, AccountAddress *) = 0; }; // stores at most 1 view secret key. 1 or more spend secret keys @@ -146,7 +145,7 @@ class WalletContainerStorage : public Wallet { std::vector generate_new_addresses( const std::vector &sks, Timestamp ct, Timestamp now, bool *rescan_from_ct) override; AccountAddress record_to_address(const WalletRecord &record) const override; - bool get_record(WalletRecord &record, const AccountAddress &) const override; + bool get_record(WalletRecord *record, const AccountAddress &) const override; void set_password(const std::string &password) override; void export_wallet(const std::string &export_path, const std::string &new_password, bool view_only, bool view_outgoing_addresses) const override; @@ -172,8 +171,6 @@ class WalletContainerStorage : public Wallet { bool detect_our_output(const Hash &tid, const Hash &tx_inputs_hash, const boost::optional &kd, size_t out_index, const PublicKey &spend_public_key, const SecretKey &secret_scalar, const OutputKey &, Amount *, KeyPair *output_keypair, AccountAddress *) override; - bool detect_not_our_output(bool tx_amethyst, const Hash &tid, const Hash &tx_inputs_hash, - boost::optional *, size_t out_index, const OutputKey &, Amount *, AccountAddress *) override; }; // stores either mnemonic or some seeds if view-only @@ -222,7 +219,7 @@ class WalletHD : public Wallet { std::vector generate_new_addresses( const std::vector &sks, Timestamp ct, Timestamp now, bool *rescan_from_ct) override; AccountAddress record_to_address(const WalletRecord &record) const override; - bool get_record(WalletRecord &record, const AccountAddress &) const override; + bool get_record(WalletRecord *record, const AccountAddress &) const override; void set_password(const std::string &password) override; void export_wallet(const std::string &export_path, const std::string &new_password, bool view_only, bool view_outgoing_addresses) const override; @@ -248,8 +245,6 @@ class WalletHD : public Wallet { bool detect_our_output(const Hash &tid, const Hash &tx_inputs_hash, const boost::optional &kd, size_t out_index, const PublicKey &spend_public_key, const SecretKey &secret_scalar, const OutputKey &, Amount *, KeyPair *output_keypair, AccountAddress *) override; - bool detect_not_our_output(bool tx_amethyst, const Hash &tid, const Hash &tx_inputs_hash, - boost::optional *, size_t out_index, const OutputKey &, Amount *, AccountAddress *) override; }; } // namespace cn diff --git a/src/Core/WalletNode.cpp b/src/Core/WalletNode.cpp index 81b7fad3..cee27609 100644 --- a/src/Core/WalletNode.cpp +++ b/src/Core/WalletNode.cpp @@ -187,12 +187,14 @@ bool WalletNode::on_get_addresses(http::Client *, http::RequestBody &&, json_rpc bool WalletNode::on_get_wallet_info(http::Client *, http::RequestBody &&, 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.deterministic = wa.is_deterministic(); - response.auditable = wa.is_auditable(); - response.total_address_count = wa.get_actual_records_count(); - response.wallet_creation_timestamp = wa.get_oldest_timestamp(); + const Wallet &wa = m_wallet_state.get_wallet(); + response.view_only = wa.is_view_only(); + response.deterministic = wa.is_deterministic(); + response.auditable = wa.is_auditable(); + response.unlinkable = wa.is_unlinkable(); + response.can_view_outgoing_addresses = wa.can_view_outgoing_addresses(); + response.total_address_count = wa.get_actual_records_count(); + 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; if (request.need_secrets) { @@ -243,6 +245,7 @@ bool WalletNode::on_get_wallet_records(http::Client *, http::RequestBody &&, jso bool WalletNode::on_set_label(http::Client *, http::RequestBody &&, json_rpc::Request &&, api::walletd::SetAddressLabel::Request &&request, api::walletd::SetAddressLabel::Response &) { + check_address_in_wallet_or_throw(request.address); m_wallet_state.get_wallet().set_label(request.address, request.label); return true; } @@ -260,8 +263,8 @@ bool WalletNode::on_get_view_key(http::Client *, http::RequestBody &&, json_rpc: bool WalletNode::on_create_addresses(http::Client *, http::RequestBody &&, json_rpc::Request &&, api::walletd::CreateAddresses::Request &&request, api::walletd::CreateAddresses::Response &response) { - if (m_wallet_state.get_wallet().is_view_only()) - throw json_rpc::Error(json_rpc::INVALID_PARAMS, "wallet is view-only, impossible to create addresses"); + // if (m_wallet_state.get_wallet().is_view_only()) + // throw json_rpc::Error(json_rpc::INVALID_PARAMS, "wallet is view-only, impossible to create addresses"); if (request.secret_spend_keys.empty()) return true; auto records = m_wallet_state.generate_new_addresses( @@ -407,8 +410,7 @@ bool WalletNode::on_create_transaction(http::Client *who, http::RequestBody &&ra AccountAddress addr; if (!m_wallet_state.get_currency().parse_account_address_string(ad, &addr)) 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)) + if (!m_wallet_state.get_wallet().is_our_address(addr)) throw api::ErrorAddress(api::ErrorAddress::ADDRESS_NOT_IN_WALLET, "Address not in wallet", ad); only_records.insert(addr); } diff --git a/src/Core/WalletState.cpp b/src/Core/WalletState.cpp index b9387fcc..ffc43872 100644 --- a/src/Core/WalletState.cpp +++ b/src/Core/WalletState.cpp @@ -449,34 +449,36 @@ bool WalletState::parse_raw_transaction(bool is_base, api::Transaction *ptx, return false; ptx->anonymity = std::min(ptx->anonymity, in.output_indexes.size() - 1); api::Output existing_output; - if (m_wallet.is_det_viewonly() && pwtx.sigs.type() == typeid(RingSignature3)) { - auto &signatures = boost::get(pwtx.sigs); - auto my_mixin_index = in.output_indexes.size(); - if (in_index < signatures.r.size() && in.output_indexes.size() == signatures.r[in_index].size()) - my_mixin_index = crypto::find_deterministic_input3( - pwtx.prefix_hash, in_index, signatures.r[in_index], m_wallet.get_view_secret_key()); - if (my_mixin_index < in.output_indexes.size()) { - size_t my_index = 0; - for (size_t i = 0; i < my_mixin_index + 1; ++i) { - my_index += in.output_indexes.at(i); - } - if (try_adding_deterministic_input(in.amount, my_index, &existing_output)) { - api::Transfer &transfer = transfer_map_inputs[existing_output.address]; - transfer.amount -= static_cast(existing_output.amount); - transfer.ours = true; - transfer.outputs.push_back(existing_output); - our_inputs = true; - continue; + if (m_wallet.is_det_viewonly()) { + std::vector global_indexes; + if (!relative_output_offsets_to_absolute(&global_indexes, in.output_indexes)) + return false; + size_t my_index = std::numeric_limits::max(); + for (auto index : global_indexes) + if (try_adding_deterministic_input(in.amount, index, &existing_output) && + (in.output_indexes.size() == 1 || + TransactionBuilder::encrypt_real_index(existing_output.public_key, + m_wallet.get_view_secret_key()) == in.encrypted_real_index)) { + my_index = index; + break; } + if (my_index != std::numeric_limits::max()) { + api::Transfer &transfer = transfer_map_inputs[existing_output.address]; + transfer.amount -= static_cast(existing_output.amount); + transfer.ours = true; + transfer.outputs.push_back(existing_output); + our_inputs = true; + continue; + } + } else { + if (try_adding_incoming_keyimage(in.key_image, &existing_output)) { + api::Transfer &transfer = transfer_map_inputs[existing_output.address]; + transfer.amount -= static_cast(existing_output.amount); + transfer.ours = true; + transfer.outputs.push_back(existing_output); + our_inputs = true; + continue; } - } - if (try_adding_incoming_keyimage(in.key_image, &existing_output)) { - api::Transfer &transfer = transfer_map_inputs[existing_output.address]; - transfer.amount -= static_cast(existing_output.amount); - transfer.ours = true; - transfer.outputs.push_back(existing_output); - our_inputs = true; - continue; } *unrecognized_inputs_amount += in.amount; } @@ -527,8 +529,8 @@ bool WalletState::parse_raw_transaction(bool is_base, api::Transaction *ptx, our_key = true; } if (!our_key && our_inputs && - m_wallet.detect_not_our_output( - is_tx_amethyst, tid, pwtx.inputs_hash, &history, out_index, key_output, &out.amount, &address)) { + TransactionBuilder::detect_not_our_output(&m_wallet, is_tx_amethyst, tid, pwtx.inputs_hash, &history, + &tx_keys, out_index, key_output, &out.amount, &address)) { // out.dust = m_currency.is_dust(key_output.amount); api::Transfer &transfer = transfer_map_outputs[false][address]; if (transfer.address.empty()) @@ -657,13 +659,16 @@ bool WalletState::api_get_transaction(Hash tid, bool check_pool, TransactionPref bool WalletState::api_create_proof(const TransactionPrefix &tx, Sendproof &sp) const { const Hash tx_inputs_hash = get_transaction_inputs_hash(tx); - KeyPair tx_keys = TransactionBuilder::transaction_keys_from_seed(tx_inputs_hash, m_wallet.get_tx_derivation_seed()); - if (sp.address.type() == typeid(AccountAddressSimple)) { - auto &addr = boost::get(sp.address); + const Hash message_hash = crypto::cn_fast_hash(sp.message.data(), sp.message.size()); + if (tx.version < m_currency.amethyst_transaction_version) { + if (sp.address.type() != typeid(AccountAddressSimple)) + return false; + KeyPair tx_keys = + TransactionBuilder::transaction_keys_from_seed(tx_inputs_hash, m_wallet.get_tx_derivation_seed()); + const auto &addr = boost::get(sp.address); SendproofKey var; - var.derivation = crypto::generate_key_derivation(addr.view_public_key, tx_keys.secret_key); - Hash message_hash = crypto::cn_fast_hash(sp.message.data(), sp.message.size()); - var.signature = crypto::generate_sendproof( + var.derivation = crypto::generate_key_derivation(addr.view_public_key, tx_keys.secret_key); + var.signature = crypto::generate_sendproof( tx_keys.public_key, tx_keys.secret_key, addr.view_public_key, var.derivation, message_hash); Amount total_amount = 0; for (size_t out_index = 0; out_index != tx.outputs.size(); ++out_index) { @@ -680,36 +685,62 @@ bool WalletState::api_create_proof(const TransactionPrefix &tx, Sendproof &sp) c sp.proof = var; return total_amount != 0; } - if (sp.address.type() == typeid(AccountAddressUnlinkable)) { - auto &addr = boost::get(sp.address); - Hash tx_inputs_hash = get_transaction_inputs_hash(tx); - SendproofUnlinkable var; - Amount total_amount = 0; - for (size_t out_index = 0; out_index != tx.outputs.size(); ++out_index) { - const auto &output = tx.outputs.at(out_index); - if (output.type() != typeid(OutputKey)) + SendproofAmethyst var; + Amount total_amount = 0; + for (size_t out_index = 0; out_index != tx.outputs.size(); ++out_index) { + const auto &output = tx.outputs.at(out_index); + if (output.type() != typeid(OutputKey)) + continue; + const auto &key_output = boost::get(output); + const KeyPair output_det_keys = TransactionBuilder::deterministic_keys_from_seed( + tx_inputs_hash, m_wallet.get_tx_derivation_seed(), common::get_varint_data(out_index)); + BinaryArray ba(std::begin(output_det_keys.public_key.data), std::end(output_det_keys.public_key.data)); + const SecretKey output_secret_scalar = crypto::hash_to_scalar(ba.data(), ba.size()); + const PublicKey output_secret_point = crypto::hash_to_point(ba.data(), ba.size()); + ba.push_back(0); // Or use cn_fast_hash64 + const Hash output_secret2 = crypto::cn_fast_hash(ba.data(), ba.size()); + if (sp.address.type() == typeid(AccountAddressSimple)) { + const uint8_t should_be_encrypted_address_type = AccountAddressSimple::type_tag ^ output_secret2.data[0]; + if (key_output.encrypted_address_type != should_be_encrypted_address_type) + continue; // Protocol violation to fool auditor + const auto &addr = boost::get(sp.address); + AccountAddressSimple address; + crypto::linkable_underive_address(output_secret_scalar, tx_inputs_hash, out_index, key_output.public_key, + key_output.encrypted_secret, &address.spend_public_key, &address.view_public_key); + if (address != addr) continue; - const auto &key_output = boost::get(output); - KeyPair output_secret_keys = TransactionBuilder::deterministic_keys_from_seed( - tx_inputs_hash, m_wallet.get_tx_derivation_seed(), common::get_varint_data(out_index)); - Hash output_secret = - crypto::cn_fast_hash(output_secret_keys.public_key.data, sizeof(output_secret_keys.public_key.data)); + SendproofAmethyst::Element el{out_index, output_det_keys.public_key, Signature{}}; + el.signature = crypto::amethyst_generate_sendproof( + output_det_keys, sp.transaction_hash, message_hash, address.spend_public_key, address.view_public_key); + var.elements.push_back(el); + total_amount += key_output.amount; + continue; + } + if (sp.address.type() == typeid(AccountAddressUnlinkable)) { + const auto &addr = boost::get(sp.address); + const uint8_t should_be_encrypted_address_type = + (addr.is_auditable ? AccountAddressUnlinkable::type_tag_auditable + : AccountAddressUnlinkable::type_tag) ^ + output_secret2.data[0]; + if (key_output.encrypted_address_type != should_be_encrypted_address_type) + continue; // Protocol violation to fool auditor AccountAddressUnlinkable address; - if (!crypto::unlinkable_underive_address(output_secret, tx_inputs_hash, out_index, key_output.public_key, - key_output.encrypted_secret, &address.s, &address.sv) || - address != addr) + crypto::unlinkable_underive_address(output_secret_point, tx_inputs_hash, out_index, key_output.public_key, + key_output.encrypted_secret, &address.s, &address.sv); + address.is_auditable = key_output.is_auditable; + if (address != addr) continue; - SendproofUnlinkable::Element el{out_index, output_secret_keys.public_key, Signature{}}; - el.signature = - crypto::generate_signature(output_secret, output_secret_keys.public_key, output_secret_keys.secret_key); + SendproofAmethyst::Element el{out_index, output_det_keys.public_key, Signature{}}; + el.signature = crypto::amethyst_generate_sendproof( + output_det_keys, sp.transaction_hash, message_hash, address.s, address.sv); var.elements.push_back(el); total_amount += key_output.amount; + continue; } - sp.amount = total_amount; - sp.proof = var; - return total_amount != 0; } - return false; + sp.amount = total_amount; + sp.proof = var; + return total_amount != 0; } api::Block WalletState::api_get_pool_as_history(const std::string &address) const { diff --git a/src/Core/WalletSync.cpp b/src/Core/WalletSync.cpp index 4c92131b..873e0f21 100644 --- a/src/Core/WalletSync.cpp +++ b/src/Core/WalletSync.cpp @@ -34,7 +34,7 @@ WalletSync::WalletSync( m_commit_timer.once(float(m_config.db_commit_period_wallet_cache)); } -WalletSync::~WalletSync() {} // we have unique_ptr to inoplete type +WalletSync::~WalletSync() {} // we have unique_ptr to incomplete type void WalletSync::db_commit() { m_wallet_state.db_commit(); @@ -117,8 +117,7 @@ void WalletSync::advance_sync() { void WalletSync::send_sync_pool() { m_log(logging::TRACE) << "Sending SyncMemPool request" << std::endl; api::cnd::SyncMemPool::Request msg; - msg.known_hashes = m_wallet_state.get_tx_pool_hashes(); - msg.need_signatures = m_wallet_state.get_wallet().is_det_viewonly(); + msg.known_hashes = m_wallet_state.get_tx_pool_hashes(); http::RequestBody req_header; req_header.r.set_firstline("POST", api::cnd::binary_url(), 1, 1); req_header.r.basic_authorization = m_config.bytecoind_authorization; @@ -175,7 +174,6 @@ void WalletSync::send_get_blocks() { (m_wallet_state.get_wallet().get_oldest_timestamp() / m_config.wallet_sync_timestamp_granularity) * m_config.wallet_sync_timestamp_granularity; msg.need_redundant_data = false; - msg.need_signatures = m_wallet_state.get_wallet().is_det_viewonly(); http::RequestBody req_header; req_header.r.set_firstline("POST", api::cnd::binary_url(), 1, 1); req_header.r.basic_authorization = m_config.bytecoind_authorization; diff --git a/src/CryptoNote.cpp b/src/CryptoNote.cpp index edb58d8b..a3848407 100644 --- a/src/CryptoNote.cpp +++ b/src/CryptoNote.cpp @@ -101,12 +101,12 @@ void ser_members(cn::SendproofKey &v, ISeria &s) { seria_kv("derivation", v.derivation, s); seria_kv("signature", v.signature, s); } -void ser_members(cn::SendproofUnlinkable::Element &v, ISeria &s) { +void ser_members(cn::SendproofAmethyst::Element &v, ISeria &s) { seria_kv("out_index", v.out_index, s); - seria_kv("q", v.q, s); + seria_kv("deterministic_public_key", v.deterministic_public_key, s); seria_kv("signature", v.signature, s); } -void ser_members(cn::SendproofUnlinkable &v, ISeria &s) { seria_kv("elements", v.elements, s); } +void ser_members(cn::SendproofAmethyst &v, ISeria &s) { seria_kv("elements", v.elements, s); } void ser_members(Sendproof &v, ISeria &s, const Currency ¤cy) { std::string addr; if (!s.is_input()) @@ -120,11 +120,15 @@ void ser_members(Sendproof &v, ISeria &s, const Currency ¤cy) { std::string proof; BinaryArray binary_proof; if (!s.is_input()) { - if (v.address.type() == typeid(AccountAddressSimple)) { + if (v.proof.type() == typeid(SendproofKey)) { + auto type = SendproofKey::str_type_tag(); + seria_kv("type", type, s); auto &var = boost::get(v.proof); binary_proof = seria::to_binary(var); - } else if (v.address.type() == typeid(AccountAddressUnlinkable)) { - auto &var = boost::get(v.proof); + } else if (v.proof.type() == typeid(SendproofAmethyst)) { + auto type = SendproofAmethyst::str_type_tag(); + seria_kv("type", type, s); + auto &var = boost::get(v.proof); binary_proof = seria::to_binary(var); } else throw api::ErrorAddress(api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Unknown address type", addr); @@ -134,19 +138,21 @@ void ser_members(Sendproof &v, ISeria &s, const Currency ¤cy) { if (s.is_input()) { if (!common::base58::decode(proof, &binary_proof)) throw std::runtime_error("Wrong proof format - " + proof); - if (v.address.type() == typeid(AccountAddressSimple)) { + std::string type = SendproofKey::str_type_tag(); + seria_kv("type", type, s); + if (type == SendproofKey::str_type_tag()) { SendproofKey sp; seria::from_binary(sp, binary_proof); v.proof = sp; - } else if (v.address.type() == typeid(AccountAddressUnlinkable)) { - SendproofUnlinkable sp; + } else if (type == SendproofAmethyst::str_type_tag()) { + SendproofAmethyst sp; seria::from_binary(sp, binary_proof); v.proof = sp; } else throw api::ErrorAddress(api::ErrorAddress::ADDRESS_FAILED_TO_PARSE, "Unknown address type", addr); } } -void ser_members(TransactionInput &v, ISeria &s) { +void ser_members(TransactionInput &v, ISeria &s, bool is_tx_amethyst) { if (s.is_input()) { uint8_t type = 0; s.object_key("type"); @@ -170,7 +176,7 @@ void ser_members(TransactionInput &v, ISeria &s) { } case InputKey::type_tag: { InputKey in{}; - ser_members(in, s); + ser_members(in, s, is_tx_amethyst); v = in; break; } @@ -198,7 +204,7 @@ void ser_members(TransactionInput &v, ISeria &s) { ser(str_type_tag, s); } else s.binary(&type, 1); - ser_members(in, s); + ser_members(in, s, is_tx_amethyst); } } void ser_members(TransactionOutput &v, ISeria &s, bool is_tx_amethyst) { @@ -251,10 +257,12 @@ void ser_members(TransactionOutput &v, ISeria &s, bool is_tx_amethyst) { } } void ser_members(InputCoinbase &v, ISeria &s) { seria_kv("height", v.height, s); } -void ser_members(InputKey &v, ISeria &s) { +void ser_members(InputKey &v, ISeria &s, bool is_tx_amethyst) { seria_kv("amount", v.amount, s); seria_kv("output_indexes", v.output_indexes, s); seria_kv("key_image", v.key_image, s); + if (is_tx_amethyst && v.output_indexes.size() >= 2) + seria_kv("encrypted_real_index", v.encrypted_real_index, s); } void ser_members(cn::RingSignatures &v, ISeria &s) { seria_kv("signatures", v.signatures, s); } void ser_members(cn::RingSignature3 &v, ISeria &s) { @@ -422,15 +430,17 @@ void ser_members(OutputKey &v, ISeria &s, bool is_tx_amethyst) { if (is_tx_amethyst) // We moved amount inside variant part in amethyst seria_kv("amount", v.amount, s); seria_kv("public_key", v.public_key, s); - if (is_tx_amethyst) + if (is_tx_amethyst) { seria_kv("encrypted_secret", v.encrypted_secret, s); + seria_kv_binary("encrypted_address_type", &v.encrypted_address_type, 1, s); + } } void ser_members(TransactionPrefix &v, ISeria &s) { seria_kv("version", v.version, s); const bool is_tx_amethyst = (v.version >= parameters::TRANSACTION_VERSION_AMETHYST); seria_kv("unlock_block_or_timestamp", v.unlock_block_or_timestamp, s); - seria_kv("inputs", v.inputs, s); + seria_kv("inputs", v.inputs, s, is_tx_amethyst); seria_kv("outputs", v.outputs, s, is_tx_amethyst); seria_kv("extra", v.extra, s); } @@ -461,8 +471,7 @@ void ser_members(RootBlock &v, ISeria &s, BlockSeriaType seria_type) { seria_kv("minor_version", v.minor_version, s); seria_kv("timestamp", v.timestamp, s); seria_kv("previous_block_hash", v.previous_block_hash, s); - s.object_key("nonce"); - s.binary(v.nonce, 4); + seria_kv_binary("nonce", v.nonce, 4, s); if (seria_type == BlockSeriaType::BLOCKHASH || seria_type == BlockSeriaType::LONG_BLOCKHASH) { Hash miner_tx_hash = get_root_block_base_transaction_hash(v.base_transaction); @@ -560,8 +569,7 @@ void ser_members(RootBlock &v, ISeria &s, BlockSeriaType seria_type) { } */ void ser_members(crypto::CMBranchElement &v, ISeria &s) { - s.object_key("depth"); - s.binary(&v.depth, 1); + seria_kv_binary("depth", &v.depth, 1, s); seria_kv("hash", v.hash, s); } @@ -577,8 +585,7 @@ void ser_members(BlockHeader &v, seria_kv("timestamp", v.timestamp, s); seria_kv("previous_block_hash", v.previous_block_hash, s); v.nonce.resize(4); - s.object_key("nonce"); - s.binary(v.nonce.data(), 4); + seria_kv_binary("nonce", v.nonce.data(), 4, s); return; } if (v.is_merge_mined()) { @@ -607,8 +614,7 @@ void ser_members(BlockHeader &v, seria_kv("timestamp", v.timestamp, s); seria_kv("previous_block_hash", v.previous_block_hash, s); invariant(v.nonce.size() == 4, ""); - s.object_key("nonce"); - s.binary(v.nonce.data(), 4); + seria_kv_binary("nonce", v.nonce.data(), 4, s); seria_kv("body_proxy", body_proxy, s); return; } @@ -636,8 +642,7 @@ void ser_members(BlockHeader &v, // We select "nonce" to be first, because this would allow compatibility with some weird // ASICs which select algorithm based on block major version. Nonce could be made to contain // all required bytes and be of appropriate length (39+4+1 bytes for full binary compatibilty) - s.object_key("nonce"); - s.binary(v.nonce.data(), v.nonce.size()); + seria_kv_binary("nonce", v.nonce.data(), v.nonce.size(), s); seria_kv("cm_merkle_root", cm_merkle_root, s); return; } diff --git a/src/CryptoNote.hpp b/src/CryptoNote.hpp index 85d47782..5324bb41 100644 --- a/src/CryptoNote.hpp +++ b/src/CryptoNote.hpp @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -49,6 +50,7 @@ struct InputKey { Amount amount = 0; std::vector output_indexes; KeyImage key_image; + std::array encrypted_real_index{}; // serialized only in amethyst if output_indexes.size >= 2 enum { type_tag = 2 }; static std::string str_type_tag() { return "key"; } }; @@ -56,7 +58,9 @@ struct InputKey { struct OutputKey { Amount amount = 0; PublicKey public_key; - Hash encrypted_secret; // serialized only in amethyst + PublicKey encrypted_secret; // serialized only in amethyst + uint8_t encrypted_address_type = 0; // serialized only in amethyst + bool is_auditable = false; enum { type_tag = 2, type_tag_auditable = 32 + 2 }; // we treat it similar to a flag // type_tag_auditable is only allowed in amethyst @@ -156,14 +160,17 @@ struct SendproofKey { // he or she wished to include public view key of address into proof. To further check, look up tx_hash in // main chain and sum amounts of outputs which have spend keys corresponding to address public spend key // For unlinkable addresses + static std::string str_type_tag() { return ""; } // legacy sendproofs lack explicit type }; -struct SendproofUnlinkable { +struct SendproofAmethyst { struct Element { size_t out_index = 0; - PublicKey q; + PublicKey deterministic_public_key; Signature signature; }; std::vector elements; + // signature per output, proving that creator knows deterministic output private key + static std::string str_type_tag() { return "amethyst"; } }; struct Sendproof { // proofing that some tx actually sent amount to particular address @@ -171,7 +178,7 @@ struct Sendproof { // proofing that some tx actually sent amount to particular AccountAddress address; Amount amount = 0; std::string message; - boost::variant proof; + boost::variant proof; }; struct RawBlock { @@ -244,14 +251,14 @@ void ser_members(cn::AccountAddressSimple &v, ISeria &s); void ser_members(cn::AccountAddressUnlinkable &v, ISeria &s); void ser_members(cn::AccountAddress &v, ISeria &s); void ser_members(cn::SendproofKey &v, ISeria &s); -void ser_members(cn::SendproofUnlinkable::Element &v, ISeria &s); -void ser_members(cn::SendproofUnlinkable &v, ISeria &s); +void ser_members(cn::SendproofAmethyst::Element &v, ISeria &s); +void ser_members(cn::SendproofAmethyst &v, ISeria &s); void ser_members(cn::Sendproof &v, ISeria &s, const cn::Currency &); -void ser_members(cn::TransactionInput &v, ISeria &s); +void ser_members(cn::TransactionInput &v, ISeria &s, bool is_tx_amethyst); void ser_members(cn::TransactionOutput &v, ISeria &s, bool is_tx_amethyst); void ser_members(cn::InputCoinbase &v, ISeria &s); -void ser_members(cn::InputKey &v, ISeria &s); +void ser_members(cn::InputKey &v, ISeria &s, bool is_tx_amethyst); void ser_members(cn::RingSignatures &v, ISeria &s); void ser_members(cn::RingSignature3 &v, ISeria &s); void ser_members(cn::TransactionSignatures &v, ISeria &s); diff --git a/src/common/BIPs.cpp b/src/common/BIPs.cpp index fd659885..50955389 100644 --- a/src/common/BIPs.cpp +++ b/src/common/BIPs.cpp @@ -256,7 +256,7 @@ Bip32Key Bip32Key::derive_key(uint32_t child_num) const { unsigned char numbuf[4]{}; common::uint_be_to_bytes(numbuf, 4, child_num); Bip32Key result; - result.child_num = child_num; + result.key_num = child_num; common::BinaryArray buf; if (child_num >= 0x80000000U) { buf.push_back(0); diff --git a/src/common/BIPs.hpp b/src/common/BIPs.hpp index 5a512965..96fa7b7d 100644 --- a/src/common/BIPs.hpp +++ b/src/common/BIPs.hpp @@ -13,7 +13,7 @@ class Bip32Key { common::BinaryArray priv_key; common::BinaryArray pub_key; common::BinaryArray chain_code; - uint32_t child_num = 0; + uint32_t key_num = 0; void make_pub(); Bip32Key() = default; @@ -27,7 +27,7 @@ class Bip32Key { static std::string check_bip39_mnemonic(const std::string &bip39_mnemonic); // normalizes mnemonic static Bip32Key create_master_key(const std::string &bip39_mnemonic, const std::string &passphrase); Bip32Key derive_key(uint32_t child_num) const; - uint32_t get_child_num() const { return child_num; } + uint32_t get_key_num() const { return key_num; } const common::BinaryArray get_chain_code() const { return chain_code; } const common::BinaryArray get_priv_key() const { return priv_key; } const common::BinaryArray get_pub_key() const { return pub_key; } diff --git a/src/common/Base58.cpp b/src/common/Base58.cpp index 762b83cc..1410f823 100644 --- a/src/common/Base58.cpp +++ b/src/common/Base58.cpp @@ -152,7 +152,7 @@ bool decode_block_good(const char *block, size_t size, uint8_t *res) { } java_hi_part += java_lo_part / 0x100000000; java_lo_part %= 0x100000000; // Not strictly necessary - if (java_hi_part > 0x100000000) + if (java_hi_part >= 0x100000000) return false; if (res_size > 4) { uint_be_to_bytes(res, res_size - 4, java_hi_part); diff --git a/src/crypto/bernstein/crypto-ops.c b/src/crypto/bernstein/crypto-ops.c index cae7e6c9..6deb0fb9 100644 --- a/src/crypto/bernstein/crypto-ops.c +++ b/src/crypto/bernstein/crypto-ops.c @@ -1977,6 +1977,71 @@ void ge_scalarmult(ge_p2 *r, const struct cryptoEllipticCurveScalar *a, const ge } } +/* Assumes that a[31] <= 127 */ +void ge_scalarmult3(ge_p3 *rr, const struct cryptoEllipticCurveScalar *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; + ge_p2 r; + + 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); + if(i == 0) + ge_p1p1_to_p3(rr, &t); + else + ge_p1p1_to_p2(&r, &t); + } +} + void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const struct cryptoEllipticCurveScalar *aa, const ge_p3 *A, const struct cryptoEllipticCurveScalar *bb, const ge_dsmp Bi) { const unsigned char * a = aa->data; const unsigned char * b = bb->data; diff --git a/src/crypto/bernstein/crypto-ops.h b/src/crypto/bernstein/crypto-ops.h index cf9aa695..a11c96d9 100644 --- a/src/crypto/bernstein/crypto-ops.h +++ b/src/crypto/bernstein/crypto-ops.h @@ -105,6 +105,9 @@ void sc_reduce(struct cryptoEllipticCurveScalar *, const unsigned char[64]); /* New code */ void ge_scalarmult(ge_p2 *, const struct cryptoEllipticCurveScalar *, const ge_p3 *); +void ge_scalarmult3(ge_p3 *, const struct cryptoEllipticCurveScalar *, const ge_p3 *); +// TODO - ge_scalarmult3 is quick fix. conversion of p2 -> p1p1 would also work + void ge_double_scalarmult_precomp_vartime(ge_p2 *, const struct cryptoEllipticCurveScalar *, const ge_p3 *, const struct cryptoEllipticCurveScalar *, const ge_dsmp); int ge_check_subgroup_precomp_vartime(const ge_dsmp); void ge_mul8(ge_p1p1 *, const ge_p2 *); diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 09a90dd7..c624b3e6 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -62,6 +62,12 @@ static ge_p2 ge_scalarmult(const EllipticCurveScalar &sec, const ge_p3 &point_ba return point2; } +static ge_p3 ge_scalarmult3(const EllipticCurveScalar &sec, const ge_p3 &point_base) { + ge_p3 point3; + ge_scalarmult3(&point3, &sec, &point_base); + return point3; +} + static ge_p2 ge_double_scalarmult_base_vartime( const EllipticCurveScalar &a, const ge_p3 &A, const EllipticCurveScalar &b) { ge_p2 tmp2; @@ -111,7 +117,14 @@ ge_cached ge_p3_to_cached(const ge_p3 &p3) { ge_p3_to_cached(&ca, &p3); return ca; } - +/*ge_cached ge_p2_to_cached(const ge_p2 &p2) { + // TODO - faster? + auto by = ge_tobytes(p2); + auto p3 = ge_frombytes_vartime(by); + ge_cached ca; + ge_p3_to_cached(&ca, &p3); + return ca; +}*/ // Integer parameters of all funs in crypto.cpp are size_t enum { max_varint_size = (std::numeric_limits::digits + 6) / 7 }; @@ -143,6 +156,7 @@ struct MiniBuffer { buf[pos++] = static_cast(i); check_overflow(0); // cheap paranoid check } + Hash cn_fast_hash() const { return crypto::cn_fast_hash(buf, pos); } SecretKey hash_to_scalar() const { return crypto::hash_to_scalar(buf, pos); } SecretKey hash_to_scalar64() const { return crypto::hash_to_scalar64(buf, pos); } }; @@ -184,6 +198,19 @@ SecretKey hash_to_scalar64(const void *data, size_t length) { return result; } +PublicKey hash_to_point(const void *data, size_t length) { + Hash h = cn_fast_hash(data, length); + ge_p2 point; + ge_fromfe_frombytes_vartime(&point, h.data); + return ge_tobytes(point); +} + +EllipticCurvePoint hash_to_point_for_tests(const Hash &h) { + ge_p2 point; + ge_fromfe_frombytes_vartime(&point, h.data); + return ge_tobytes(point); +} + void random_keypair(PublicKey &pub, SecretKey &sec) { sec = random_scalar(); pub = ge_tobytes(ge_scalarmult_base(sec)); @@ -207,49 +234,6 @@ bool secret_key_to_public_key(const SecretKey &sec, PublicKey *pub) { return true; } -KeyDerivation generate_key_derivation(const PublicKey &tx_public_key, const SecretKey &view_secret_key) { - check_scalar(view_secret_key); - const ge_p3 tx_public_key_p3 = ge_frombytes_vartime(tx_public_key); - const ge_p2 point2 = ge_scalarmult(view_secret_key, tx_public_key_p3); - KeyDerivation derivation; - static_cast(derivation) = ge_tobytes(ge_p1p1_to_p2(ge_mul8(point2))); - return derivation; -} - -static SecretKey derivation_to_scalar(const KeyDerivation &derivation, size_t output_index) { - FixedBuffer buf; - buf.append(derivation); - buf.append(output_index); - return buf.hash_to_scalar(); -} - -PublicKey derive_public_key(const KeyDerivation &derivation, size_t output_index, const PublicKey &spend_public_key) { - const ge_p3 spend_public_key_g3 = ge_frombytes_vartime(spend_public_key); - const EllipticCurveScalar scalar = derivation_to_scalar(derivation, output_index); - const ge_cached point3 = ge_p3_to_cached(ge_scalarmult_base(scalar)); - ge_p1p1 point_sum; - ge_add(&point_sum, &spend_public_key_g3, &point3); - return ge_tobytes(ge_p1p1_to_p2(point_sum)); -} - -SecretKey derive_secret_key(const KeyDerivation &derivation, size_t output_index, const SecretKey &spend_secret_key) { - check_scalar(spend_secret_key); - const EllipticCurveScalar scalar = derivation_to_scalar(derivation, output_index); - SecretKey output_secret_key; - sc_add(&output_secret_key, &spend_secret_key, &scalar); - return output_secret_key; -} - -PublicKey underive_public_key( - const KeyDerivation &derivation, size_t output_index, const PublicKey &output_public_key) { - const ge_p3 output_public_key_p3 = ge_frombytes_vartime(output_public_key); - const EllipticCurveScalar scalar = derivation_to_scalar(derivation, output_index); - const ge_cached point3 = ge_p3_to_cached(ge_scalarmult_base(scalar)); - ge_p1p1 point_diff; - ge_sub(&point_diff, &output_public_key_p3, &point3); - return ge_tobytes(ge_p1p1_to_p2(point_diff)); -} - Signature generate_signature(const Hash &prefix_hash, const PublicKey &pub, const SecretKey &sec) { if (paranoid_checks && !keys_match(sec, pub)) throw Error("Keys do not match in generate_signature"); @@ -288,12 +272,6 @@ static ge_p3 hash_to_ec_p3(const PublicKey &key) { return ge_p1p1_to_p3(ge_mul8(point_p2)); } -EllipticCurvePoint hash_to_point_for_tests(const Hash &h) { - ge_p2 point; - ge_fromfe_frombytes_vartime(&point, h.data); - return ge_tobytes(point); -} - PublicKey hash_to_ec(const PublicKey &key) { return ge_tobytes(hash_to_ec_p3(key)); } KeyImage generate_key_image(const PublicKey &pub, const SecretKey &sec) { @@ -462,7 +440,7 @@ RingSignature3 generate_ring_signature3(const Hash &prefix_hash, const std::vect FixedBuffer k_buf; k_buf.append(prefix_hash); k_buf.append(secs.at(i)); - const EllipticCurveScalar k = k_buf.hash_to_scalar64(); + const EllipticCurveScalar k = k_buf.hash_to_scalar64(); // TODO - k must be the same in both fors // std::cout << "k[" << i << "]=" << k << std::endl; EllipticCurveScalar next_c = sig.c0; for (size_t j = 0; j != sec_index; ++j) { @@ -542,23 +520,47 @@ bool check_ring_signature3(const Hash &prefix_hash, const std::vector return sc_iszero(&c) != 0; } -size_t find_deterministic_input3(const Hash &prefix_hash, size_t input_index, const std::vector &r, - const SecretKey &view_secret_key) { - size_t bad_index = r.size(); - for (size_t i = 0; i < r.size(); i++) { - FixedBuffer r_buf; - r_buf.append(view_secret_key); - r_buf.append(prefix_hash); - r_buf.append(input_index); - r_buf.append(i); - const SecretKey must_be_r = r_buf.hash_to_scalar64(); - if (must_be_r != r[i]) { - if (bad_index != r.size()) // second bad index - return r.size(); - bad_index = i; - } - } - return bad_index; +KeyDerivation generate_key_derivation(const PublicKey &tx_public_key, const SecretKey &view_secret_key) { + check_scalar(view_secret_key); + const ge_p3 tx_public_key_p3 = ge_frombytes_vartime(tx_public_key); + const ge_p2 point2 = ge_scalarmult(view_secret_key, tx_public_key_p3); + KeyDerivation derivation; + static_cast(derivation) = ge_tobytes(ge_p1p1_to_p2(ge_mul8(point2))); + return derivation; +} + +static SecretKey derivation_to_scalar(const KeyDerivation &derivation, size_t output_index) { + FixedBuffer buf; + buf.append(derivation); + buf.append(output_index); + return buf.hash_to_scalar(); +} + +PublicKey derive_public_key(const KeyDerivation &derivation, size_t output_index, const PublicKey &spend_public_key) { + const ge_p3 spend_public_key_g3 = ge_frombytes_vartime(spend_public_key); + const EllipticCurveScalar scalar = derivation_to_scalar(derivation, output_index); + const ge_cached point3 = ge_p3_to_cached(ge_scalarmult_base(scalar)); + ge_p1p1 point_sum; + ge_add(&point_sum, &spend_public_key_g3, &point3); + return ge_tobytes(ge_p1p1_to_p2(point_sum)); +} + +SecretKey derive_secret_key(const KeyDerivation &derivation, size_t output_index, const SecretKey &spend_secret_key) { + check_scalar(spend_secret_key); + const EllipticCurveScalar scalar = derivation_to_scalar(derivation, output_index); + SecretKey output_secret_key; + sc_add(&output_secret_key, &spend_secret_key, &scalar); + return output_secret_key; +} + +PublicKey underive_public_key( + const KeyDerivation &derivation, size_t output_index, const PublicKey &output_public_key) { + const ge_p3 output_public_key_p3 = ge_frombytes_vartime(output_public_key); + const EllipticCurveScalar scalar = derivation_to_scalar(derivation, output_index); + const ge_cached point3 = ge_p3_to_cached(ge_scalarmult_base(scalar)); + ge_p1p1 point_diff; + ge_sub(&point_diff, &output_public_key_p3, &point3); + return ge_tobytes(ge_p1p1_to_p2(point_diff)); } Signature generate_sendproof(const PublicKey &txkey_pub, const SecretKey &txkey_sec, @@ -642,95 +644,273 @@ PublicKey generate_address_s_v(const PublicKey &spend_public_key, const SecretKe return ge_tobytes(ge_scalarmult(view_secret_key, spend_public_key_p3)); } -static_assert(sizeof(PublicKey) == sizeof(Hash), "We are going to XOR them"); - // create map s*G -> WalletRecord +// In tx we generate per-output deterministic secret +// q = deterministic(wallet_seed_special_for_k | inputs | #o) +// Q = q*G +// K = Hp(Q) *** k = Hs(Q) + +// P.S. If we need more secret bytes, we do k(suffix) = H(Q | suffix) + // In tx, there is 2 values per output T (encrypted output secret) and P (output public key) -// T = k xor inv(H(k|inputs|#o))*v*s*G -// P = inv(H(k|inputs|#o))*s*G) +// T = K + inv(H(K|inputs|#o))*(v*s*G) *** T = k * (v*G) +// P = inv(H(K|inputs|#o))*s*G *** P = S + H(k*G|inputs|#o)*G // look for our output -// k' = P*v xor T -// lookup H(k'|inputs|#o)*P in map +// K' = T - P*v *** D = inv(v) * T +// S' = H(K'|inputs|#o)*P in map *** S' = P - H(D|inputs|#o)*G -// if found, then secret output key -// p = inv(H(k|inputs|#o))*s +// if found in map, then secret output key +// p = inv(H(K'|inputs|#o))*s *** p = s + H(D|inputs|#o) // send_proof -// q = deterministic(wallet_seed_special_for_k | inputs | #o) -// Q = q*G -// k = Q or H(Q) +// proof of send to address s*G is +// (Q, txid, message, s*G, v*s*G) *** (Q, txid, message, s*G, v*G) +// signed by q -// proof of send to address s*G is (Q, txid, message, s*G, v*s*G), signed by q +PublicKey linkable_derive_public_key(const SecretKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &spend_public_key, const PublicKey &view_public_key, PublicKey *encrypted_output_secret) { + check_scalar(output_secret); + const ge_p3 view_public_key_p3 = ge_frombytes_vartime(view_public_key); + *encrypted_output_secret = ge_tobytes(ge_scalarmult(output_secret, view_public_key_p3)); -PublicKey unlinkable_underive_public_key(const SecretKey &view_secret_key, const Hash &tx_inputs_hash, - size_t output_index, const PublicKey &output_public_key, const Hash &encrypted_output_secret, + const EllipticCurvePoint derivation = ge_tobytes(ge_scalarmult_base(output_secret)); + // std::cout << "derivation=" << derivation << std::endl; + FixedBuffer cr_comm; + cr_comm.append(derivation); + cr_comm.append(tx_inputs_hash); + cr_comm.append(output_index); + const EllipticCurveScalar derivation_hash = cr_comm.hash_to_scalar(); + // std::cout << "derivation_hash=" << derivation_hash << std::endl; + const ge_cached point3 = ge_p3_to_cached(ge_scalarmult_base(derivation_hash)); + + const ge_p3 spend_public_key_g3 = ge_frombytes_vartime(spend_public_key); + ge_p1p1 point_sum; + ge_add(&point_sum, &spend_public_key_g3, &point3); + return ge_tobytes(ge_p1p1_to_p2(point_sum)); +} + +PublicKey linkable_underive_public_key(const SecretKey &inv_view_secret_key, const Hash &tx_inputs_hash, + size_t output_index, const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, SecretKey *spend_scalar) { - check_scalar(view_secret_key); - // TODO - When passing to ledger, check that no crypto attacks possible - const ge_p3 output_public_key_p3 = ge_frombytes_vartime(output_public_key); - const PublicKey p_v = ge_tobytes(ge_scalarmult(view_secret_key, output_public_key_p3)); - Hash output_secret; - for (size_t i = 0; i != sizeof(output_secret.data); ++i) - output_secret.data[i] = p_v.data[i] ^ encrypted_output_secret.data[i]; - FixedBuffer<2 * sizeof(Hash) + max_varint_size> cr_comm; - cr_comm.append(output_secret); + check_scalar(inv_view_secret_key); + const ge_p3 encrypted_output_secret_p3 = ge_frombytes_vartime(encrypted_output_secret); + const EllipticCurvePoint derivation = ge_tobytes(ge_scalarmult(inv_view_secret_key, encrypted_output_secret_p3)); + // std::cout << "derivation=" << derivation << std::endl; + FixedBuffer cr_comm; + cr_comm.append(derivation); cr_comm.append(tx_inputs_hash); cr_comm.append(output_index); *spend_scalar = cr_comm.hash_to_scalar(); - return ge_tobytes(ge_scalarmult(*spend_scalar, output_public_key_p3)); + // std::cout << "derivation_hash=" << *spend_scalar << std::endl; + const ge_p3 output_public_key_g3 = ge_frombytes_vartime(output_public_key); + const ge_cached point3 = ge_p3_to_cached(ge_scalarmult_base(*spend_scalar)); + ge_p1p1 point_diff; + ge_sub(&point_diff, &output_public_key_g3, &point3); + return ge_tobytes(ge_p1p1_to_p2(point_diff)); } -SecretKey unlinkable_derive_secret_key(const SecretKey &spend_secret_key, const SecretKey &spend_scalar) { +SecretKey linkable_derive_secret_key(const SecretKey &spend_secret_key, const SecretKey &spend_scalar) { check_scalar(spend_secret_key); check_scalar(spend_scalar); - const SecretKey inv_spend_scalar = sc_invert(spend_scalar); SecretKey output_secret_key; - sc_mul(&output_secret_key, &inv_spend_scalar, &spend_secret_key); + sc_add(&output_secret_key, &spend_secret_key, &spend_scalar); return output_secret_key; } -PublicKey unlinkable_derive_public_key(const Hash &output_secret, const Hash &tx_inputs_hash, size_t output_index, - const PublicKey &spend_public_key, const PublicKey &vs_public_key, Hash *encrypted_output_secret) { +void linkable_underive_address(const SecretKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, PublicKey *spend_public_key, + PublicKey *view_public_key) { + check_scalar(output_secret); + const EllipticCurvePoint derivation = ge_tobytes(ge_scalarmult_base(output_secret)); + FixedBuffer cr_comm; + cr_comm.append(derivation); + cr_comm.append(tx_inputs_hash); + cr_comm.append(output_index); + const EllipticCurveScalar derivation_hash = cr_comm.hash_to_scalar(); + const ge_cached point3 = ge_p3_to_cached(ge_scalarmult_base(derivation_hash)); + + const ge_p3 output_public_key_g3 = ge_frombytes_vartime(output_public_key); + ge_p1p1 point_diff; + ge_sub(&point_diff, &output_public_key_g3, &point3); + *spend_public_key = ge_tobytes(ge_p1p1_to_p2(point_diff)); + + const SecretKey inv_output_secret = sc_invert(output_secret); + + const ge_p3 encrypted_output_secret_g3 = ge_frombytes_vartime(encrypted_output_secret); + *view_public_key = ge_tobytes(ge_scalarmult(inv_output_secret, encrypted_output_secret_g3)); +} + +void test_linkable() { + const SecretKey output_secret = random_scalar(); + const Hash tx_inputs_hash = rand(); + const size_t output_index = rand(); + const KeyPair spend_keypair = random_keypair(); + const KeyPair view_keypair = random_keypair(); + const SecretKey inv_view_secret_key = sc_invert(view_keypair.secret_key); + + PublicKey encrypted_output_secret; + PublicKey output_public_key = linkable_derive_public_key(output_secret, tx_inputs_hash, output_index, + spend_keypair.public_key, view_keypair.public_key, &encrypted_output_secret); + + SecretKey spend_scalar; + PublicKey spend_public_key2 = linkable_underive_public_key( + inv_view_secret_key, tx_inputs_hash, output_index, output_public_key, encrypted_output_secret, &spend_scalar); + if (spend_public_key2 != spend_keypair.public_key) + throw Error("Aha"); + SecretKey output_secret_key2 = linkable_derive_secret_key(spend_keypair.secret_key, spend_scalar); + PublicKey output_public_key2; + if (!secret_key_to_public_key(output_secret_key2, &output_public_key2) || output_public_key2 != output_public_key) + throw Error("Oho"); + PublicKey spend_public_key3; + PublicKey view_public_key3; + linkable_underive_address(output_secret, tx_inputs_hash, output_index, output_public_key, encrypted_output_secret, + &spend_public_key3, &view_public_key3); + if (spend_public_key3 != spend_keypair.public_key || view_public_key3 != view_keypair.public_key) + throw Error("Uhu"); +} + +PublicKey unlinkable_derive_public_key(const PublicKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &spend_public_key, const PublicKey &vs_public_key, PublicKey *encrypted_output_secret) { + // std::cout << "output_secret=" << output_secret << std::endl; const ge_p3 spend_public_key_p3 = ge_frombytes_vartime(spend_public_key); const ge_p3 vs_public_key_p3 = ge_frombytes_vartime(vs_public_key); + const ge_p3 output_secret_p3 = ge_frombytes_vartime(output_secret); - FixedBuffer<2 * sizeof(Hash) + max_varint_size> cr_comm; + FixedBuffer cr_comm; cr_comm.append(output_secret); cr_comm.append(tx_inputs_hash); cr_comm.append(output_index); - const SecretKey spend_scalar = cr_comm.hash_to_scalar(); + const SecretKey spend_scalar = cr_comm.hash_to_scalar(); + // std::cout << "spend_scalar=" << spend_scalar << std::endl; const SecretKey inv_spend_scalar = sc_invert(spend_scalar); - PublicKey output_public_key = ge_tobytes(ge_scalarmult(inv_spend_scalar, spend_public_key_p3)); - const PublicKey p_v = ge_tobytes(ge_scalarmult(inv_spend_scalar, vs_public_key_p3)); - for (size_t i = 0; i != sizeof(output_secret.data); ++i) - encrypted_output_secret->data[i] = p_v.data[i] ^ output_secret.data[i]; + // std::cout << "inv_spend_scalar=" << inv_spend_scalar << std::endl; + PublicKey output_public_key = ge_tobytes(ge_scalarmult(inv_spend_scalar, spend_public_key_p3)); + + const ge_cached p_v = ge_p3_to_cached(ge_scalarmult3(inv_spend_scalar, vs_public_key_p3)); + + ge_p1p1 point_sum; + ge_add(&point_sum, &output_secret_p3, &p_v); + + *encrypted_output_secret = ge_tobytes(ge_p1p1_to_p2(point_sum)); return output_public_key; } -bool unlinkable_underive_address(const Hash &output_secret, const Hash &tx_inputs_hash, size_t output_index, - const PublicKey &output_public_key, const Hash &encrypted_output_secret, PublicKey *spend_public_key, +// T = K + inv(H(K|inputs|#o))*(v*s*G) *** T = k * (v*G) +// P = inv(H(K|inputs|#o))*s*G *** P = S + H(k*G|inputs|#o)*G + +// look for our output +// K' = T - P*v *** D = inv(v) * T +// S' = H(K'|inputs|#o)*P in map *** S' = P - H(D|inputs|#o)*G + +PublicKey unlinkable_underive_public_key(const SecretKey &view_secret_key, const Hash &tx_inputs_hash, + size_t output_index, const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, + SecretKey *spend_scalar) { + check_scalar(view_secret_key); + const ge_p3 output_public_key_p3 = ge_frombytes_vartime(output_public_key); + const ge_p3 encrypted_output_secret_p3 = ge_frombytes_vartime(encrypted_output_secret); + const ge_cached p_v = ge_p3_to_cached(ge_scalarmult3(view_secret_key, output_public_key_p3)); + ge_p1p1 point_diff; + ge_sub(&point_diff, &encrypted_output_secret_p3, &p_v); + + const PublicKey output_secret = ge_tobytes(ge_p1p1_to_p2(point_diff)); + // std::cout << "output_secret=" << output_secret << std::endl; + + FixedBuffer cr_comm; + cr_comm.append(output_secret); + cr_comm.append(tx_inputs_hash); + cr_comm.append(output_index); + *spend_scalar = cr_comm.hash_to_scalar(); + // std::cout << "spend_scalar=" << *spend_scalar << std::endl; + + return ge_tobytes(ge_scalarmult(*spend_scalar, output_public_key_p3)); +} + +SecretKey unlinkable_derive_secret_key(const SecretKey &spend_secret_key, const SecretKey &spend_scalar) { + check_scalar(spend_secret_key); + check_scalar(spend_scalar); + const SecretKey inv_spend_scalar = sc_invert(spend_scalar); + SecretKey output_secret_key; + sc_mul(&output_secret_key, &inv_spend_scalar, &spend_secret_key); + return output_secret_key; +} + +void unlinkable_underive_address(const PublicKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, PublicKey *spend_public_key, PublicKey *vs_public_key) { - ge_p3 output_public_key_p3; - if (ge_frombytes_vartime(&output_public_key_p3, &output_public_key) != 0) - return false; - FixedBuffer<2 * sizeof(Hash) + max_varint_size> cr_comm; + const ge_p3 output_public_key_p3 = ge_frombytes_vartime(output_public_key); + const ge_p3 output_secret_p3 = ge_frombytes_vartime(output_secret); + const ge_p3 encrypted_output_secret_p3 = ge_frombytes_vartime(encrypted_output_secret); + FixedBuffer cr_comm; cr_comm.append(output_secret); cr_comm.append(tx_inputs_hash); cr_comm.append(output_index); const SecretKey spend_scalar = cr_comm.hash_to_scalar(); *spend_public_key = ge_tobytes(ge_scalarmult(spend_scalar, output_public_key_p3)); - PublicKey t; - for (size_t i = 0; i != sizeof(output_secret.data); ++i) - t.data[i] = encrypted_output_secret.data[i] ^ output_secret.data[i]; - ge_p3 t_p3; - if (ge_frombytes_vartime(&t_p3, &t) != 0) - return false; - *vs_public_key = ge_tobytes(ge_scalarmult(spend_scalar, t_p3)); - return true; + + const ge_cached p_v = ge_p3_to_cached(output_secret_p3); + ge_p1p1 point_diff; + ge_sub(&point_diff, &encrypted_output_secret_p3, &p_v); + + const ge_p3 t_minus_k = ge_p1p1_to_p3(point_diff); + + *vs_public_key = ge_tobytes(ge_scalarmult(spend_scalar, t_minus_k)); +} + +void test_unlinkable() { + const PublicKey output_secret = random_keypair().public_key; + const Hash tx_inputs_hash = rand(); + const size_t output_index = rand(); + const KeyPair spend_keypair = random_keypair(); + const KeyPair view_keypair = random_keypair(); + const PublicKey address_s_v = generate_address_s_v(spend_keypair.public_key, view_keypair.secret_key); + + PublicKey encrypted_output_secret; + PublicKey output_public_key = unlinkable_derive_public_key( + output_secret, tx_inputs_hash, output_index, spend_keypair.public_key, address_s_v, &encrypted_output_secret); + + SecretKey spend_scalar; + PublicKey spend_public_key2 = unlinkable_underive_public_key(view_keypair.secret_key, tx_inputs_hash, output_index, + output_public_key, encrypted_output_secret, &spend_scalar); + if (spend_public_key2 != spend_keypair.public_key) + throw Error("Aha"); + SecretKey output_secret_key2 = unlinkable_derive_secret_key(spend_keypair.secret_key, spend_scalar); + PublicKey output_public_key2; + if (!secret_key_to_public_key(output_secret_key2, &output_public_key2) || output_public_key2 != output_public_key) + throw Error("Oho"); + PublicKey spend_public_key3; + PublicKey address_s_v3; + unlinkable_underive_address(output_secret, tx_inputs_hash, output_index, output_public_key, encrypted_output_secret, + &spend_public_key3, &address_s_v3); + if (spend_public_key3 != spend_keypair.public_key || address_s_v3 != address_s_v) + throw Error("Uhu"); +} + +Signature amethyst_generate_sendproof(const KeyPair &output_keys, const Hash &tid, const Hash &message_hash, + const PublicKey &address_spend_key, const PublicKey &address_other_key) { + FixedBuffer<3 * sizeof(PublicKey) + 2 * sizeof(Hash)> cr_comm; + cr_comm.append(output_keys.public_key); + cr_comm.append(tid); + cr_comm.append(message_hash); + cr_comm.append(address_spend_key); + cr_comm.append(address_other_key); + const Hash hash = cr_comm.cn_fast_hash(); + return generate_signature(hash, output_keys.public_key, output_keys.secret_key); +} + +bool amethyst_check_sendproof(const PublicKey &output_public_key, const Hash &tid, const Hash &message_hash, + const PublicKey &address_spend_key, const PublicKey &address_other_key, const Signature &sig) { + FixedBuffer<3 * sizeof(PublicKey) + 2 * sizeof(Hash)> cr_comm; + cr_comm.append(output_public_key); + cr_comm.append(tid); + cr_comm.append(message_hash); + cr_comm.append(address_spend_key); + cr_comm.append(address_other_key); + const Hash hash = cr_comm.cn_fast_hash(); + return check_signature(hash, output_public_key, sig); } } // namespace crypto diff --git a/src/crypto/crypto.hpp b/src/crypto/crypto.hpp index dde600df..ecfd33ae 100644 --- a/src/crypto/crypto.hpp +++ b/src/crypto/crypto.hpp @@ -60,25 +60,6 @@ bool key_isvalid(const PublicKey &key); bool secret_key_to_public_key(const SecretKey &sec, PublicKey *pub); bool keys_match(const SecretKey &secret_key, const PublicKey &expected_public_key); -// To generate an ephemeral key used to send money to: -// The sender generates a new key pair, which becomes the transaction key. The public transaction key is included in -// "extra" field. -// Both the sender and the receiver generate key derivation from the transaction key and the receivers' "view" key. -// The sender uses key derivation, the output index, and the receivers' "spend" key to derive an ephemeral public key. -// The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key -// (to spend the money). -KeyDerivation generate_key_derivation(const PublicKey &tx_public_key, const SecretKey &view_secret_key); - -PublicKey derive_public_key(const KeyDerivation &derivation, size_t output_index, const PublicKey &spend_public_key); - -SecretKey derive_secret_key( - const KeyDerivation &derivation, std::size_t output_index, const SecretKey &spend_secret_key); - -// Inverse function of derive_public_key. It can be used by the receiver to find which "spend" key was used to generate -// a transaction. This may be useful if the receiver used multiple addresses which only differ in "spend" key. - -PublicKey underive_public_key(const KeyDerivation &derivation, size_t output_index, const PublicKey &output_public_key); - // returns false if keys are corrupted/invalid Signature generate_signature(const Hash &prefix_hash, const PublicKey &pub, const SecretKey &sec); @@ -108,18 +89,9 @@ RingSignature3 generate_ring_signature3(const Hash &prefix_hash, const std::vect bool check_ring_signature3(const Hash &prefix_hash, const std::vector &image, const std::vector> &pubs, const RingSignature3 &sig); -size_t find_deterministic_input3(const Hash &prefix_hash, size_t input_index, const std::vector &r, - const SecretKey &view_secret_key); -// returns r.size() if not our input - -Signature generate_sendproof(const PublicKey &txkey_pub, const SecretKey &txkey_sec, - const PublicKey &receiver_view_key_pub, const KeyDerivation &derivation, const Hash &message_hash); -// Transaction key and the derivation supplied with the proof can be invalid, this just means that the proof is invalid. -bool check_sendproof(const PublicKey &txkey_pub, const PublicKey &receiver_view_key_pub, - const KeyDerivation &derivation, const Hash &message_hash, const Signature &proof); - SecretKey hash_to_scalar(const void *data, size_t length); SecretKey hash_to_scalar64(const void *data, size_t length); +PublicKey hash_to_point(const void *data, size_t length); EllipticCurvePoint hash_to_point_for_tests(const Hash &h); // Used only in tests PublicKey hash_to_ec(const PublicKey &key); @@ -128,18 +100,68 @@ void generate_hd_spendkeys( const KeyPair &base, const Hash &keys_generation_seed, size_t index, std::vector *result); PublicKey generate_address_s_v(const PublicKey &spend_public_key, const SecretKey &view_secret_key); -// Unlinkable crypto, spend_scalar is temporary value that is expensive to calc, we pass it arounf +// Legacy crypto +// To generate an ephemeral key used to send money to: +// The sender generates a new key pair, which becomes the transaction key. The public transaction key is included in +// "extra" field. +// Both the sender and the receiver generate key derivation from the transaction key and the receivers' "view" key. +// The sender uses key derivation, the output index, and the receivers' "spend" key to derive an ephemeral public key. +// The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key +// (to spend the money). + +KeyDerivation generate_key_derivation(const PublicKey &tx_public_key, const SecretKey &view_secret_key); + +PublicKey derive_public_key(const KeyDerivation &derivation, size_t output_index, const PublicKey &spend_public_key); + +PublicKey underive_public_key(const KeyDerivation &derivation, size_t output_index, const PublicKey &output_public_key); + +SecretKey derive_secret_key( + const KeyDerivation &derivation, std::size_t output_index, const SecretKey &spend_secret_key); + +Signature generate_sendproof(const PublicKey &txkey_pub, const SecretKey &txkey_sec, + const PublicKey &receiver_view_key_pub, const KeyDerivation &derivation, const Hash &message_hash); + +// Transaction key and the derivation supplied with the proof can be invalid, this just means that the proof is invalid. +bool check_sendproof(const PublicKey &txkey_pub, const PublicKey &receiver_view_key_pub, + const KeyDerivation &derivation, const Hash &message_hash, const Signature &proof); + +// Linkable crypto, spend_scalar is temporary value that is expensive to calc, we pass it around +// Old addresses use improved crypto in amethyst, because we need to enforce unique output public keys +// on crypto level. Enforcing on daemon DB index level does not work (each of 2 solutions is vulnerable attack). + +PublicKey linkable_derive_public_key(const SecretKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &spend_public_key, const PublicKey &view_public_key, PublicKey *encrypted_output_secret); + +PublicKey linkable_underive_public_key(const SecretKey &inv_view_secret_key, const Hash &tx_inputs_hash, + size_t output_index, const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, + Hash *spend_scalar); + +SecretKey linkable_derive_secret_key(const SecretKey &spend_secret_key, const SecretKey &spend_scalar); + +void linkable_underive_address(const SecretKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, PublicKey *spend_public_key, + PublicKey *view_public_key); +void test_linkable(); + +// Unlinkable crypto, spend_scalar is temporary value that is expensive to calc, we pass it around +PublicKey unlinkable_derive_public_key(const PublicKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &spend_public_key, const PublicKey &vs_public_key, PublicKey *encrypted_output_secret); + PublicKey unlinkable_underive_public_key(const SecretKey &view_secret_key, const Hash &tx_inputs_hash, - size_t output_index, const PublicKey &output_public_key, const Hash &encrypted_output_secret, + size_t output_index, const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, SecretKey *spend_scalar); SecretKey unlinkable_derive_secret_key(const SecretKey &spend_secret_key, const SecretKey &spend_scalar); -PublicKey unlinkable_derive_public_key(const Hash &output_secret, const Hash &tx_inputs_hash, size_t output_index, - const PublicKey &spend_public_key, const PublicKey &vs_public_key, Hash *encrypted_output_secret); - -bool unlinkable_underive_address(const Hash &output_secret, const Hash &tx_inputs_hash, size_t output_index, - const PublicKey &output_public_key, const Hash &encrypted_output_secret, PublicKey *spend_public_key, +void unlinkable_underive_address(const PublicKey &output_secret, const Hash &tx_inputs_hash, size_t output_index, + const PublicKey &output_public_key, const PublicKey &encrypted_output_secret, PublicKey *spend_public_key, PublicKey *vs_public_key); +void test_unlinkable(); + +Signature amethyst_generate_sendproof(const KeyPair &output_keys, const Hash &tid, const Hash &message_hash, + const PublicKey &address_spend_key, const PublicKey &address_other_key); + +bool amethyst_check_sendproof(const PublicKey &output_public_key, const Hash &tid, const Hash &message_hash, + const PublicKey &address_spend_key, const PublicKey &address_other_key, const Signature &sig); } // namespace crypto diff --git a/src/crypto/keccak/Keccak-readable-and-compact.c b/src/crypto/keccak/Keccak-readable-and-compact.c index c843056a..40790100 100644 --- a/src/crypto/keccak/Keccak-readable-and-compact.c +++ b/src/crypto/keccak/Keccak-readable-and-compact.c @@ -218,22 +218,22 @@ void KeccakF1600_StatePermute(void *state) UINT8 LFSRstate = 0x01; for(round=0; round<24; round++) { - { /* === θ step (see [Keccak Reference, Section 2.3.2]) === */ + { /* === theta 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 */ + /* Compute the theta effect for a given column */ D = C[(x+4)%5] ^ ROL64(C[(x+1)%5], 1); - /* Add the θ effect to the whole column */ + /* Add the theta 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]) === */ + { /* === ro and pi steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) === */ tKeccakLane current, temp; /* Start at coordinates (1 0) */ x = 1; y = 0; @@ -251,19 +251,19 @@ void KeccakF1600_StatePermute(void *state) } } - { /* === χ step (see [Keccak Reference, Section 2.3.1]) === */ + { /* === xi 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 */ + /* Compute xi 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]) === */ + { /* === iota step (see [Keccak Reference, Section 2.3.5]) === */ for(j=0; j<7; j++) { unsigned int bitPosition = (1< RingSignature; -struct RingSignature2 { // New half-size signatures - EllipticCurveScalar c0; // c[0] - std::vector r; // r[0]..r[n] -}; - struct RingSignature3 { // New half-size signatures EllipticCurveScalar c0; // c0 std::vector> r; // r[i, j] diff --git a/src/http/Agent.cpp b/src/http/Agent.cpp index c451c8c6..a81832e4 100644 --- a/src/http/Agent.cpp +++ b/src/http/Agent.cpp @@ -21,10 +21,10 @@ Agent::Connection::Connection(handler &&r_handler, handler &&d_handler) , sock([this](bool, bool) { advance_state(true); }, std::bind(&Connection::on_disconnect, this)) , keep_alive(true) {} -bool Agent::Connection::connect(const std::string &address, uint16_t port) { +bool Agent::Connection::connect(const std::string &a, uint16_t p) { clear(); waiting_write_response = true; - return sock.connect(address, port); + return sock.connect(a, p); } void Agent::Connection::disconnect() { diff --git a/src/http/Server.cpp b/src/http/Server.cpp index 9de77022..68b2893e 100644 --- a/src/http/Server.cpp +++ b/src/http/Server.cpp @@ -19,10 +19,8 @@ using namespace http; -Server::Server(const std::string &address, uint16_t port, request_handler &&r_handler, disconnect_handler &&d_handler, - const std::string &ssl_pem_file, const std::string &ssl_certificate_password) - : la_socket{new platform::TCPAcceptor{ - address, port, std::bind(&Server::accept_all, this), ssl_pem_file, ssl_certificate_password}} +Server::Server(const std::string &address, uint16_t port, request_handler &&r_handler, disconnect_handler &&d_handler) + : la_socket{new platform::TCPAcceptor{address, port, std::bind(&Server::accept_all, this)}} , r_handler(std::move(r_handler)) , d_handler(std::move(d_handler)) { accept_all(); diff --git a/src/http/Server.hpp b/src/http/Server.hpp index 6980142b..9a601d2c 100644 --- a/src/http/Server.hpp +++ b/src/http/Server.hpp @@ -25,9 +25,8 @@ class Server { typedef std::function request_handler; typedef std::function disconnect_handler; - explicit Server(const std::string &address, uint16_t port, request_handler &&r_handler, - disconnect_handler &&d_handler, const std::string &ssl_pem_file = std::string(), - const std::string &ssl_certificate_password = std::string()); + explicit Server( + const std::string &address, uint16_t port, request_handler &&r_handler, disconnect_handler &&d_handler); ~Server(); private: diff --git a/src/http/types.hpp b/src/http/types.hpp index 04768b5c..0ea5c908 100644 --- a/src/http/types.hpp +++ b/src/http/types.hpp @@ -32,12 +32,11 @@ struct RequestHeader { bool keep_alive = true; size_t content_length = std::numeric_limits::max(); - void set_firstline( - const std::string &method, const std::string &uri, int http_version_major, int http_version_minor) { - this->method = method; - this->uri = uri; - this->http_version_major = http_version_major; - this->http_version_minor = http_version_minor; + void set_firstline(const std::string &m, const std::string &u, int ma, int mi) { + method = m; + uri = u; + http_version_major = ma; + http_version_minor = mi; } bool has_content_length() const { return content_length != size_t(-1); } std::string to_string() const; @@ -73,9 +72,9 @@ struct RequestBody { http::RequestHeader r; std::string body; - void set_body(std::string &&body) { - this->body = std::move(body); - r.content_length = this->body.size(); + void set_body(std::string &&b) { + body = std::move(b); + r.content_length = body.size(); } }; @@ -85,9 +84,9 @@ struct ResponseBody { ResponseBody() = default; explicit ResponseBody(const http::RequestHeader &r) : r(r) {} - void set_body(std::string &&body) { - this->body = std::move(body); - r.content_length = this->body.size(); + void set_body(std::string &&b) { + body = std::move(b); + r.content_length = body.size(); } }; diff --git a/src/main_bytecoind.cpp b/src/main_bytecoind.cpp index ec612dcc..1b4b24c0 100644 --- a/src/main_bytecoind.cpp +++ b/src/main_bytecoind.cpp @@ -38,15 +38,12 @@ static const char USAGE[] = R"(bytecoind )" bytecoin_VERSION_STRING R"(. --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-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. - --ssl-certificate-password= DEPRECATED. Will read password from stdin if not specified.)" -#endif - ; + --bytecoind-authorization-private= HTTP basic authentication credentials for get_statistics and get_archive methods.)"; int main(int argc, const char *argv[]) try { + crypto::test_unlinkable(); + crypto::test_linkable(); + common::console::UnicodeConsoleSetup console_setup; auto idea_start = std::chrono::high_resolution_clock::now(); common::CommandLine cmd(argc, argv); @@ -108,15 +105,6 @@ int main(int argc, const char *argv[]) try { return 0; } - if (!config.ssl_certificate_pem_file.empty() && !config.ssl_certificate_password) { - std::string ssl_certificate_password; - std::cout << "Enter ssl certificate password: " << std::flush; - std::getline(std::cin, ssl_certificate_password); - boost::algorithm::trim(ssl_certificate_password); - - config.ssl_certificate_password = ssl_certificate_password; - } - platform::ExclusiveLock coin_lock(coin_folder, CRYPTONOTE_NAME "d.lock"); logging::LoggerManager log_manager; diff --git a/src/main_walletd.cpp b/src/main_walletd.cpp index c6bccf5b..eda74908 100644 --- a/src/main_walletd.cpp +++ b/src/main_walletd.cpp @@ -77,6 +77,7 @@ int main(int argc, const char *argv[]) try { bool ask_walletd_http_auth = true; const bool export_keys = cmd.get_bool("--export-keys"); const bool create_wallet = cmd.get_bool("--create-wallet"); + const bool check_mnemonic = cmd.get_bool("--check-mnemonic"); // Undocumented, used by GUI for now const bool create_legacy_wallet = cmd.get_bool("--create-legacy-wallet"); const bool import_keys = cmd.get_bool("--import-keys"); bool view_outgoing_addresses = false; @@ -117,13 +118,15 @@ int main(int argc, const char *argv[]) try { std::cout << "You cannot ask to create both legacy and new wallet" << std::endl; return 1; } - if (create_wallet && mnemonic.empty()) { - std::cout << "Enter BIP39 mnemonic: " << std::flush; + if ((create_wallet && mnemonic.empty()) || check_mnemonic) { + if (!check_mnemonic) + std::cout << "Enter BIP39 mnemonic: " << std::flush; if (!console_setup.getline(mnemonic)) { std::cout << "Unexpected end of stdin" << std::endl; return api::WALLETD_WRONG_ARGS; } - std::cout << std::endl; + if (!check_mnemonic) + std::cout << std::endl; if (mnemonic.empty()) { std::cout << "Mnemonic should not be empty" << std::endl; return api::WALLETD_WRONG_ARGS; @@ -134,6 +137,8 @@ int main(int argc, const char *argv[]) try { std::cout << "Mnemonic invalid - " << common::what(ex) << std::endl; return api::WALLETD_MNEMONIC_CRC; } + if (check_mnemonic) + return 0; std::cout << "Enter BIP39 mnemonic password (empty recommended): " << std::flush; if (!console_setup.getline(mnemonic_password)) { std::cout << "Unexpected end of stdin" << std::endl; diff --git a/src/platform/Ledger.cpp b/src/platform/Ledger.cpp new file mode 100644 index 00000000..e81046c9 --- /dev/null +++ b/src/platform/Ledger.cpp @@ -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. + +#include "Ledger.hpp" +#include +#include +#include "common/Invariant.hpp" + +using namespace platform; +using namespace crypto; + +Ledger::Ledger() { + // read m_wallet_key, m_spend_key_base_public_key from device + + // Code to be run on device, m_address_type == 1 for now + + // cn::Bip32Key k0 = master_key.derive_key(0x8000002c); + // cn::Bip32Key k1 = k0.derive_key(0x80000300); + // cn::Bip32Key k2 = k1.derive_key(0x80000000 + m_address_type); + // cn::Bip32Key k3 = k2.derive_key(0); + // cn::Bip32Key k4 = k3.derive_key(0); + // m_seed = crypto::cn_fast_hash(k4.get_priv_key().data(), k4.get_priv_key().size()); + // m_tx_derivation_seed = derive_from_seed(m_seed, "tx_derivation"); + // BinaryArray sk_data = m_seed | "spend_key_base"; + // m_spend_key_base.secret_key = crypto::hash_to_scalar(sk_data.data(), sk_data.size()); + // invariant(crypto::secret_key_to_public_key(m_spend_key_base.secret_key, &m_spend_key_base.public_key), ""); + // BinaryArray vk_data = + // BinaryArray{std::begin(m_spend_key_base.public_key.data), std::end(m_spend_key_base.public_key.data)} | + // "view_key"; + // m_view_secret_key = crypto::hash_to_scalar(vk_data.data(), vk_data.size()); + // invariant(crypto::secret_key_to_public_key(m_view_secret_key, &m_view_public_key), ""); + + // m_wallet_key = chacha_key{derive_from_seed(m_seed, "wallet_key")}; +} + +Ledger::~Ledger() {} + +std::vector Ledger::mul_by_view_secret_key(const std::vector &output_public_keys) const { + // multiply by m_view_secret_key on device, throw if PublicKey detected to be invalid by device + + // const ge_p3 output_public_key_p3 = ge_frombytes_vartime(output_public_key); + // const ge_p3 p_v = ge_scalarmult3(view_secret_key, output_public_key_p3); + + // then either convert ge_p3 to PublicKey on device or computer + + // const PublicKey p_v_packed = ge_tobytes(p_v); + + return std::vector(); +} diff --git a/src/platform/Ledger.hpp b/src/platform/Ledger.hpp new file mode 100644 index 00000000..1fc5328b --- /dev/null +++ b/src/platform/Ledger.hpp @@ -0,0 +1,32 @@ +// 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 "crypto/chacha.hpp" +#include "crypto/crypto.hpp" + +namespace platform { + +// Prototype - max simplified synchronous calls + +// All funs including constructor throw std::runtime_error when connection to ledger lost before end of fun. +// All funs must quickly try reestablishing connection at the start if it was lost during previous call +// Calls might be from different threads, but will be externally synchronized + +class Ledger { + crypto::chacha_key m_wallet_key; // wallet encryption key, derived from secret + crypto::PublicKey m_spend_key_base_public_key; // derived from secret +public: + Ledger(); + ~Ledger(); + crypto::chacha_key get_wallet_key() const { return m_wallet_key; } + crypto::PublicKey get_spend_key_base_public_key() const { return m_spend_key_base_public_key; } + std::vector mul_by_view_secret_key( + const std::vector &output_public_keys) const; + bool sign_transaction() const { return false; } // TODO - params + bool create_sendproof() const { return false; } // TODO - params +}; + +} // namespace platform diff --git a/src/platform/Network.cpp b/src/platform/Network.cpp index 83115da1..a6ea42b0 100644 --- a/src/platform/Network.cpp +++ b/src/platform/Network.cpp @@ -414,48 +414,28 @@ static void add_system_root_certs(ssl::context &ctx) { } #endif -// static thread_local std::shared_ptr shared_client_context; #endif thread_local EventLoop *EventLoop::current_loop = nullptr; EventLoop::EventLoop(boost::asio::io_service &io_service) : io_service(io_service) { - if (current_loop != 0) + if (current_loop) throw std::logic_error("RunLoop::RunLoop Only single RunLoop per thread is allowed"); current_loop = this; } -EventLoop::~EventLoop() { - current_loop = nullptr; - //#if platform_USE_SSL - // shared_client_context.reset(); - //#endif -} +EventLoop::~EventLoop() { current_loop = nullptr; } void EventLoop::cancel() { io_service.stop(); } void EventLoop::run() { io_service.run(); } void EventLoop::wake() { - io_service.post([](void) {}); + io_service.post([]() {}); } -// static bool ispowerof2(unsigned int x) { -// return x && !(x & (x - 1)); -//} -// static unsigned global_timer_impl_counter = 0; - class Timer::Impl { public: - explicit Impl(Timer *owner) : owner(owner), pending_wait(false), timer(EventLoop::current()->io()) { - // global_timer_impl_counter += 1; - // if( ispowerof2(global_timer_impl_counter) ) - // std::cout << "++Timer::Impl::counter=" << global_timer_impl_counter << std::endl; - } - ~Impl() { - // if( ispowerof2(global_timer_impl_counter) ) - // std::cout << "--Timer::Impl::counter=" << global_timer_impl_counter << std::endl; - // global_timer_impl_counter -= 1; - } + explicit Impl(Timer *owner) : owner(owner), pending_wait(false), timer(EventLoop::current()->io()) {} Timer *owner; bool pending_wait; boost::asio::deadline_timer timer; @@ -771,27 +751,12 @@ void TCPSocket::shutdown_both() { class TCPAcceptor::Impl { public: - explicit Impl(TCPAcceptor *owner, bool ssl) - : owner(owner) - , ssl(ssl) - , acceptor(EventLoop::current()->io()) - , socket_being_accepted(EventLoop::current()->io()) -#if platform_USE_SSL - , ssl_context(std::make_shared(ssl::context::sslv23)) - , ssl_socket_being_accepted( - ssl ? std::make_unique(EventLoop::current()->io(), *ssl_context) : nullptr) -#endif - { - } + explicit Impl(TCPAcceptor *owner) + : owner(owner), acceptor(EventLoop::current()->io()), socket_being_accepted(EventLoop::current()->io()) {} TCPAcceptor *owner; - const bool ssl; 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 = false; void close() { @@ -807,13 +772,7 @@ class TCPAcceptor::Impl { if (!owner) return; pending_accept = true; -#if platform_USE_SSL - if (ssl) - acceptor.async_accept( - ssl_socket_being_accepted->next_layer(), std::bind(&Impl::handle_accept, owner->impl, _1)); - else -#endif - acceptor.async_accept(socket_being_accepted, std::bind(&Impl::handle_accept, owner->impl, _1)); + acceptor.async_accept(socket_being_accepted, std::bind(&Impl::handle_accept, owner->impl, _1)); } void handle_accept(const boost::system::error_code &e) { pending_accept = false; @@ -828,23 +787,9 @@ 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) try : impl(std::make_shared(this, !ssl_pem_file.empty())), - a_handler(std::move(a_handler)) { - -#if platform_USE_SSL - if (impl->ssl) { - impl->ssl_context->set_options( - ssl::context::default_workarounds | ssl::context::no_sslv2); // | ssl::context::single_dh_use - impl->ssl_context->set_password_callback( - [ssl_certificate_password](std::size_t max_length, ssl::context::password_purpose purpose) -> std::string { - return ssl_certificate_password; - }); - impl->ssl_context->use_certificate_chain_file(ssl_pem_file); - impl->ssl_context->use_private_key_file(ssl_pem_file, ssl::context::pem); - // impl->ssl_context.use_tmp_dh_file("dh512.pem"); - } -#endif +TCPAcceptor::TCPAcceptor(const std::string &addr, uint16_t port, A_handler &&a_handler) try + : impl(std::make_shared(this)), + a_handler(std::move(a_handler)) { boost::asio::ip::tcp::resolver resolver(EventLoop::current()->io()); boost::asio::ip::tcp::resolver::query query(addr, common::to_string(port)); boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); @@ -871,32 +816,15 @@ bool TCPAcceptor::accept(TCPSocket &socket, std::string &accepted_addr) { socket.close(); std::swap(socket.impl->socket, impl->socket_being_accepted); boost::system::error_code ec; -#if platform_USE_SSL - std::swap(socket.impl->ssl_socket, impl->ssl_socket_being_accepted); - auto endpoint = - impl->ssl ? socket.impl->ssl_socket->next_layer().remote_endpoint(ec) : socket.impl->socket.remote_endpoint(ec); -#else auto endpoint = socket.impl->socket.remote_endpoint(ec); -#endif if (ec) { impl->start_accept(); return false; } - accepted_addr = endpoint.address().to_string(); -#if platform_USE_SSL - if (impl->ssl) { - if (!impl->ssl_socket_being_accepted) - impl->ssl_socket_being_accepted = - std::make_unique(EventLoop::current()->io(), *impl->ssl_context); - socket.impl->ssl_context = impl->ssl_context; - socket.impl->start_handshake(ssl::stream_base::server); - } else -#endif - { - socket.impl->connected = true; - socket.impl->start_read(); - } + accepted_addr = endpoint.address().to_string(); + socket.impl->connected = true; + socket.impl->start_read(); impl->start_accept(); return true; } diff --git a/src/platform/Network.hpp b/src/platform/Network.hpp index e342391d..c44708de 100644 --- a/src/platform/Network.hpp +++ b/src/platform/Network.hpp @@ -66,12 +66,7 @@ class TCPAcceptor : private common::Nocopy { public: typedef std::function A_handler; - 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()) - : a_handler(a_handler) {} + explicit TCPAcceptor(const std::string &addr, uint16_t port, A_handler a_handler) : a_handler(a_handler) {} ~TCPAcceptor() {} bool accept(TCPSocket &socket, std::string &accepted_addr) { return false; } @@ -141,11 +136,7 @@ class TCPAcceptor : private common::Nocopy { public: typedef std::function A_handler; - 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()) + explicit TCPAcceptor(const std::string &addr, uint16_t port, A_handler &&a_handler) : a_handler(std::move(a_handler)) {} ~TCPAcceptor() {} @@ -248,8 +239,7 @@ class TCPAcceptor : private common::Nocopy { 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()); + explicit TCPAcceptor(const std::string &addr, uint16_t port, A_handler &&a_handler); ~TCPAcceptor(); // if accept returns false, will fire accept_handler in future diff --git a/src/rpc_api.cpp b/src/rpc_api.cpp index 7cf261a4..2d2d3e05 100644 --- a/src/rpc_api.cpp +++ b/src/rpc_api.cpp @@ -229,6 +229,8 @@ void ser_members(cn::api::walletd::GetWalletInfo::Response &v, ISeria &s) { seria_kv("view_only", v.view_only, s); seria_kv("deterministic", v.deterministic, s); seria_kv("auditable", v.auditable, s); + seria_kv("unlinkable", v.unlinkable, s); + seria_kv("can_view_outgoing_addresses", v.can_view_outgoing_addresses, s); seria_kv("wallet_creation_timestamp", v.wallet_creation_timestamp, s); seria_kv("total_address_count", v.total_address_count, s); seria_kv("first_address", v.first_address, s); diff --git a/src/rpc_api.hpp b/src/rpc_api.hpp index 6f469966..a6061f29 100644 --- a/src/rpc_api.hpp +++ b/src/rpc_api.hpp @@ -276,6 +276,8 @@ struct GetWalletInfo { bool view_only = false; bool deterministic = false; bool auditable = false; + bool unlinkable = false; + bool can_view_outgoing_addresses = false; Timestamp wallet_creation_timestamp = 0; // O if not known (restored form keys and did not sync yet) std::string first_address; size_t total_address_count = 0; // Useful when iterating diff --git a/src/seria/ISeria.hpp b/src/seria/ISeria.hpp index ad310af6..33cd44c3 100644 --- a/src/seria/ISeria.hpp +++ b/src/seria/ISeria.hpp @@ -141,6 +141,11 @@ void seria_kv_optional(common::StringView name, T &value, ISeria &s) { } } +inline bool seria_kv_binary(common::StringView name, void *value, size_t size, ISeria &s) { + s.object_key(name, true); + return s.binary(value, size); +} + template void ser_members(T &value, ISeria &s, Context... context); //{ // static_assert(false); // Good idea, but clang complains diff --git a/src/seria/JsonInputStream.hpp b/src/seria/JsonInputStream.hpp index bfdf4ab2..ea4cbd0e 100644 --- a/src/seria/JsonInputStream.hpp +++ b/src/seria/JsonInputStream.hpp @@ -10,7 +10,7 @@ namespace seria { -class JsonInputStream : public ISeria {}; // Common base für use with dynamic_cast in ser() methods +class JsonInputStream : public ISeria {}; // Common base for use with dynamic_cast in ser() methods class JsonInputStreamValue : public JsonInputStream, private common::Nocopy { public: diff --git a/src/seria/JsonOutputStream.hpp b/src/seria/JsonOutputStream.hpp index 48f85acc..0eef3564 100644 --- a/src/seria/JsonOutputStream.hpp +++ b/src/seria/JsonOutputStream.hpp @@ -9,7 +9,7 @@ namespace seria { -class JsonOutputStream : public ISeria {}; // Common base für use with dynamic_cast in ser() methods +class JsonOutputStream : public ISeria {}; // Common base for use with dynamic_cast in ser() methods class JsonOutputStreamValue : public JsonOutputStream { public: diff --git a/src/version.hpp b/src/version.hpp index 5d1582af..c9750d41 100644 --- a/src/version.hpp +++ b/src/version.hpp @@ -4,8 +4,8 @@ #pragma once // defines are for Windows resource compiler -#define bytecoin_VERSION_WINDOWS_COMMA 3, 18, 12, 12 -#define bytecoin_VERSION_STRING "3.4.0-beta-20181212 (amethyst)" +#define bytecoin_VERSION_WINDOWS_COMMA 3, 18, 12, 18 +#define bytecoin_VERSION_STRING "3.4.0-beta-20181218 (amethyst)" #ifndef RC_INVOKED // Windows resource compiler diff --git a/tests/Random.hpp b/tests/Random.hpp index fb4af391..a16816ab 100644 --- a/tests/Random.hpp +++ b/tests/Random.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once @@ -10,4 +11,4 @@ namespace common { // Tests require deterministic random with the same sequence on all platforms // We declare it here typedef std::mt19937_64 Random; -} +} // namespace common diff --git a/tests/blockchain/test_blockchain.cpp b/tests/blockchain/test_blockchain.cpp index 6156a303..521a5c35 100644 --- a/tests/blockchain/test_blockchain.cpp +++ b/tests/blockchain/test_blockchain.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #include "test_blockchain.hpp" @@ -11,6 +12,7 @@ #include "Core/Currency.hpp" #include "Core/Difficulty.hpp" #include "Core/TransactionExtra.hpp" +#include "common/Varint.hpp" #include "crypto/crypto.hpp" #include "logging/ConsoleLogger.hpp" #include "seria/BinaryInputStream.hpp" @@ -19,7 +21,7 @@ #include "seria/KVBinaryOutputStream.hpp" #include "version.hpp" -using namespace bytecoin; +using namespace cn; struct MinedBlockDesc { BlockTemplate block_template; @@ -32,13 +34,13 @@ class TestMiner { public: BlockChainState &block_chain; const Currency ¤cy; - AccountPublicAddress address; + AccountAddress address; crypto::CryptoNightContext cryptoContext; std::vector checkpoint_keypairs; TestMiner(BlockChainState &block_chain, const Currency ¤cy) : block_chain(block_chain), currency(currency) { - invariant(currency.parse_account_address_string( - "21mQ7KPdmLbjfpg3Coayi4hZzAEgjeL87QXGeDTHahKeJsvKHc6DoprAJmqUcLhWTUXtxCL6rQFSwEUe6NZdEoqZNpSq1iC", + invariant(currency.parse_account_address_string("21mQ7KPdmLbjfpg3Coayi4hZzAEgjeL87QXGeDTHahKeJsvKHc6DoprAJmqU" + "cLhWTUXtxCL6rQFSwEUe6NZdEoqZNpSq1iC", &address), ""); std::vector skeys{"dacb828348483011f63ebb538401b3f3d52e8ce1916278f9b189f820d1ec730e", @@ -46,33 +48,37 @@ class TestMiner { "16d4d146d8ba2bbff13a4bb174b4c5d73d3ca22817a6585956a697337be26a09"}; for (auto &&sk : skeys) { checkpoint_keypairs.push_back(KeyPair{}); - invariant(common::pod_from_hex(sk, checkpoint_keypairs.back().secret_key), ""); + invariant(common::pod_from_hex(sk, &checkpoint_keypairs.back().secret_key), ""); invariant(crypto::secret_key_to_public_key( - checkpoint_keypairs.back().secret_key, checkpoint_keypairs.back().public_key), + checkpoint_keypairs.back().secret_key, &checkpoint_keypairs.back().public_key), ""); } } MinedBlockDesc mine_block(Hash bid) { api::BlockHeader parent; - invariant(block_chain.read_header(bid, &parent), ""); + invariant(block_chain.get_header(bid, &parent), ""); BlockTemplate block; - Difficulty difficulty = 0; - Height height = 0; - 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); + Difficulty difficulty = 0; + Height height = 0; + size_t reserve_back_offset = 0; + block_chain.create_mining_block_template( + bid, address, BinaryArray{}, &block, &difficulty, &height, &reserve_back_offset); + set_root_extra_to_solo_mining_tag(block); + block.root_block.timestamp = parent.timestamp + currency.difficulty_target; + block.timestamp = block.root_block.timestamp; + // block.root_block.nonce.resize(4); + uint32_t nonce = crypto::rand(); + // block.nonce.resize(4); + auto body_proxy = get_body_proxy_from_template(block); while (true) { + common::uint_le_to_bytes(block.root_block.nonce, 4, nonce); + // block.nonce = block.root_block.nonce; 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.parent_block.nonce += 1; - block.nonce = block.parent_block.nonce; + nonce += 1; } RawBlock rb; MinedBlockDesc desc{block, seria::to_binary(block), get_block_hash(block, body_proxy), parent.height + 1}; @@ -81,7 +87,7 @@ class TestMiner { void add_mined_block(const MinedBlockDesc &desc, bool log = true) { RawBlock rb; api::BlockHeader info; - invariant(block_chain.add_mined_block(desc.binary_block_template, &rb, &info) != BroadcastAction::BAN, ""); + block_chain.add_mined_block(desc.binary_block_template, &rb, &info); if (log) std::cout << "---- After add_mined_block tip=" << block_chain.get_tip_height() << " : " << block_chain.get_tip_bid() << std::endl; @@ -97,14 +103,15 @@ class TestMiner { << block_chain.get_tip_bid() << std::endl; return desc; } - void add_checkpoint(uint32_t key_id, uint64_t counter, Hash hash, Height height) { + void add_checkpoint(size_t key_id, uint64_t counter, Hash hash, Height height) { SignedCheckpoint small_checkpoint; - small_checkpoint.height = height; - small_checkpoint.hash = hash; - small_checkpoint.key_id = key_id; - small_checkpoint.counter = counter; - crypto::generate_signature(small_checkpoint.get_message_hash(), checkpoint_keypairs.at(key_id).public_key, - checkpoint_keypairs.at(key_id).secret_key, small_checkpoint.signature); + small_checkpoint.height = height; + small_checkpoint.hash = hash; + small_checkpoint.key_id = key_id; + small_checkpoint.counter = counter; + small_checkpoint.signature = crypto::generate_signature(small_checkpoint.get_message_hash(), + checkpoint_keypairs.at(key_id).public_key, + checkpoint_keypairs.at(key_id).secret_key); invariant(block_chain.add_checkpoint(small_checkpoint, ""), ""); std::cout << "---- After add_checkpoint tip=" << block_chain.get_tip_height() << " : " << block_chain.get_tip_bid() << std::endl; @@ -122,7 +129,7 @@ void test_blockchain(common::CommandLine &cmd) { Currency currency(config.net); std::cout << "Point 2" << std::endl; - BlockChainState block_chain(logger, config, currency, /*read only*/ false); + BlockChainState block_chain(logger, config, currency, false); std::cout << "Point 3" << std::endl; TestMiner test_miner(block_chain, currency); @@ -186,8 +193,8 @@ class TestBlockChain { 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); @@ -214,10 +221,8 @@ 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; } - BroadcastAction add_block(const PreparedBlock &pb, api::BlockHeader *info) { return BroadcastAction::NOTHING; } - BroadcastAction add_mined_block(const BinaryArray &raw_block_template, - RawBlock *raw_block, - api::BlockHeader *info) { - return BroadcastAction::NOTHING; + bool add_block(const PreparedBlock &pb, api::BlockHeader *info) { return false; } + bool add_mined_block(const BinaryArray &raw_block_template, RawBlock *raw_block, api::BlockHeader *info) { + return false; } }; diff --git a/tests/blockchain/test_blockchain.hpp b/tests/blockchain/test_blockchain.hpp index 642394cb..d5e28910 100644 --- a/tests/blockchain/test_blockchain.hpp +++ b/tests/blockchain/test_blockchain.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once diff --git a/tests/crypto/test_crypto.cpp b/tests/crypto/test_crypto.cpp index b1f286b1..67d85657 100644 --- a/tests/crypto/test_crypto.cpp +++ b/tests/crypto/test_crypto.cpp @@ -1,25 +1,23 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. //#include //#include #include #include +#include "common/Invariant.hpp" +#include "crypto/bernstein/crypto-ops.h" #include "test_crypto.hpp" #include "../io.hpp" +#include "common/StringTools.hpp" #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; @@ -27,15 +25,41 @@ static void check(bool expr, size_t test) { throw std::runtime_error("test_crypto failed"); } +using namespace crypto; + +class FastHashStream { +public: + void append(const void *data, size_t size) {} + Hash cn_fast_hash() const { return Hash{}; } +}; + +class LedgerProxy { + PublicKey m_view_public_key; + SecretKey m_view_secret_key; + Hash m_seed; + Hash m_tx_derivation_seed; + +public: + ge_p3 unlinkable_underive_public_key_step1(const PublicKey &output_public_key) { + // const ge_p3 output_public_key_p3 = ge_frombytes_vartime(output_public_key); + // const ge_cached p_v = ge_p3_to_cached(ge_scalarmult3(view_secret_key, output_public_key_p3)); + // ge_p1p1 point_diff; + // ge_sub(&point_diff, &encrypted_output_secret_p3, &p_v); + return ge_p3{}; + } +}; + +void generate_ring_signature3(const Hash &prefix_hash, const std::vector &images, + const std::vector> &pubs, const std::vector &secs, + const std::vector &sec_indexes, const SecretKey &view_secret_key); + +RingSignature3 create_signature(); + void test_crypto(const std::string &test_vectors_filename) { std::fstream input; std::string cmd; size_t test = 0; - crypto::initialize_random_for_tests(); - // if (argc != 2) { - // cerr << "invalid arguments" << endl; - // return 1; - // } + crypto_initialize_random_for_tests(); input.open(test_vectors_filename, std::ios_base::in); for (;;) { ++test; @@ -46,20 +70,46 @@ void test_crypto(const std::string &test_vectors_filename) { input.exceptions(std::ios_base::badbit | std::ios_base::failbit | std::ios_base::eofbit); if (cmd == "check_scalar") { crypto::EllipticCurveScalar scalar; - bool expected = false, actual; + bool expected = false; get(input, scalar, expected); - actual = sc_isvalid_vartime(&scalar); + const bool actual = sc_isvalid_vartime(&scalar) != 0; check(expected == actual, test); } else if (cmd == "random_scalar") { crypto::EllipticCurveScalar expected, actual; get(input, expected); - crypto::random_scalar(actual); + actual = crypto::random_scalar(); check(expected == actual, test); + + // We do not have separate tests for inversions, so perform it on + // random_scalars + crypto::EllipticCurveScalar inv_actual, inv_inv_actual; + crypto::EllipticCurveScalar b, inv_b, inv_inv_b, a_b, inv_a_inv_b, inv_a_b; + b = crypto::hash_to_scalar(actual.data, sizeof(actual.data)); + + sc_invert(&inv_actual, &actual); + sc_invert(&inv_inv_actual, &inv_actual); + sc_invert(&inv_b, &b); + sc_invert(&inv_inv_b, &inv_b); + invariant(actual == inv_inv_actual, ""); + invariant(b == inv_inv_b, ""); + sc_mul(&a_b, &actual, &b); + sc_mul(&inv_a_inv_b, &inv_actual, &inv_b); + sc_invert(&inv_a_b, &a_b); + invariant(inv_a_inv_b == inv_a_b, ""); + + crypto::EllipticCurveScalar one, inv_one, real_one; + sc_1(&real_one); + sc_mul(&one, &actual, &inv_actual); + invariant(one == real_one, ""); + sc_mul(&one, &b, &inv_b); + invariant(one == real_one, ""); + sc_invert(&inv_one, &one); + invariant(one == inv_one, ""); } else if (cmd == "hash_to_scalar") { std::vector data; - crypto::EllipticCurveScalar expected, actual; + crypto::EllipticCurveScalar expected; get(input, data, expected); - crypto::hash_to_scalar(data.data(), data.size(), actual); + const auto actual = crypto::hash_to_scalar(data.data(), data.size()); check(expected == actual, test); } else if (cmd == "generate_keys") { crypto::PublicKey expected1, actual1; @@ -70,142 +120,182 @@ void test_crypto(const std::string &test_vectors_filename) { check(expected2 == actual2, test); } else if (cmd == "check_key") { crypto::PublicKey key; - bool expected = false, actual; + bool expected = false; get(input, key, expected); - actual = key_isvalid(key); + const bool actual = key_isvalid(key); check(expected == actual, test); } else if (cmd == "secret_key_to_public_key") { crypto::SecretKey sec; - bool expected1 = false, actual1; + bool expected1 = false; crypto::PublicKey expected2, actual2; get(input, sec, expected1); if (expected1) { get(input, expected2); } - actual1 = secret_key_to_public_key(sec, actual2); + const bool actual1 = secret_key_to_public_key(sec, &actual2); check(!(expected1 != actual1 || (expected1 && expected2 != actual2)), test); } else if (cmd == "generate_key_derivation") { crypto::PublicKey key1; crypto::SecretKey key2; - bool expected1 = false, actual1; - crypto::KeyDerivation expected2, actual2; + bool expected1 = false; + crypto::KeyDerivation expected2; get(input, key1, key2, expected1); if (expected1) { get(input, expected2); } - actual1 = generate_key_derivation(key1, key2, actual2); - check(!(expected1 != actual1 || (expected1 && expected2 != actual2)), test); + try { + const auto actual2 = generate_key_derivation(key1, key2); + check(actual2 == expected2, test); + } catch (const std::exception &) { + check(!expected1, test); + } } else if (cmd == "derive_public_key") { crypto::KeyDerivation derivation; size_t output_index; crypto::PublicKey base; - bool expected1 = false, actual1; - crypto::PublicKey expected2, actual2; + bool expected1 = false; + crypto::PublicKey expected2; get(input, derivation, output_index, base, expected1); if (expected1) { get(input, expected2); } - actual1 = derive_public_key(derivation, output_index, base, actual2); - check(!(expected1 != actual1 || (expected1 && expected2 != actual2)), test); + try { + const auto actual2 = derive_public_key(derivation, output_index, base); + check(actual2 == expected2, test); + } catch (const std::exception &) { + check(!expected1, test); + } } else if (cmd == "derive_secret_key") { crypto::KeyDerivation derivation; size_t output_index; crypto::SecretKey base; crypto::SecretKey expected, actual; get(input, derivation, output_index, base, expected); - derive_secret_key(derivation, output_index, base, actual); + // try { + actual = derive_secret_key(derivation, output_index, base); + // }catch(const std::exception &){ + // } check(expected == actual, test); } else if (cmd == "underive_public_key") { crypto::KeyDerivation derivation; size_t output_index; crypto::PublicKey derived_key; - bool expected1 = false, actual1; - crypto::PublicKey expected2, actual2; + bool expected1 = false; + crypto::PublicKey expected2; get(input, derivation, output_index, derived_key, expected1); if (expected1) { get(input, expected2); } - actual1 = underive_public_key(derivation, output_index, derived_key, actual2); - check(!(expected1 != actual1 || (expected1 && expected2 != actual2)), test); + try { + const auto actual2 = underive_public_key(derivation, output_index, derived_key); + check(actual2 == expected2, test); + } catch (const std::exception &) { + check(!expected1, test); + } } else if (cmd == "generate_signature") { crypto::Hash prefix_hash; crypto::PublicKey pub; crypto::SecretKey sec; - crypto::Signature expected, actual; + crypto::Signature expected; get(input, prefix_hash, pub, sec, expected); - generate_signature(prefix_hash, pub, sec, actual); + const auto actual = generate_signature(prefix_hash, pub, sec); check(expected == actual, test); } else if (cmd == "check_signature") { crypto::Hash prefix_hash; crypto::PublicKey pub; crypto::Signature sig; - bool expected = false, actual; + bool expected = false; get(input, prefix_hash, pub, sig, expected); - actual = check_signature(prefix_hash, pub, sig); + const bool actual = check_signature(prefix_hash, pub, sig); check(expected == actual, test); } else if (cmd == "hash_to_point") { crypto::Hash h; - crypto::EllipticCurvePoint expected, actual; + crypto::EllipticCurvePoint expected; get(input, h, expected); - hash_to_point_for_tests(h, actual); + const auto actual = hash_to_point_for_tests(h); check(expected == actual, test); } else if (cmd == "hash_to_ec") { crypto::PublicKey key; - crypto::EllipticCurvePoint expected, actual; + crypto::EllipticCurvePoint expected; get(input, key, expected); - hash_to_ec(key, actual); + const auto actual = hash_to_ec(key); check(expected == actual, test); } else if (cmd == "generate_key_image") { crypto::PublicKey pub; crypto::SecretKey sec; - crypto::KeyImage expected, actual; + crypto::KeyImage expected; get(input, pub, sec, expected); - generate_key_image(pub, sec, actual); + const auto actual = generate_key_image(pub, sec); check(expected == actual, test); } else if (cmd == "generate_ring_signature") { crypto::Hash prefix_hash; crypto::KeyImage image; std::vector vpubs; - std::vector pubs; + // std::vector pubs; size_t pubs_count; crypto::SecretKey sec; size_t sec_index; - std::vector expected, actual; - size_t i; + crypto::RingSignature expected; get(input, prefix_hash, image, pubs_count); vpubs.resize(pubs_count); - pubs.resize(pubs_count); - for (i = 0; i < pubs_count; i++) { + // pubs.resize(pubs_count); + for (size_t i = 0; i < pubs_count; i++) { get(input, vpubs[i]); - pubs[i] = &vpubs[i]; + // pubs[i] = &vpubs[i]; } get(input, sec, sec_index); expected.resize(pubs_count); getvar(input, pubs_count * sizeof(crypto::Signature), expected.data()); - actual.resize(pubs_count); - generate_ring_signature(prefix_hash, image, pubs.data(), pubs_count, sec, sec_index, actual.data()); + crypto::SecretKey view_secret_key; + const auto actual = generate_ring_signature(prefix_hash, image, vpubs.data(), vpubs.size(), sec, sec_index); check(expected == actual, test); + // TODO - better tests for half-size ring signatures + + static std::vector images; + static std::vector secs; + static std::vector> pubss; + static std::vector sec_indexes; + images.push_back(image); + secs.push_back(sec); + pubss.push_back(vpubs); + sec_indexes.push_back(sec_index); + if (images.size() > 32) { + crypto::RingSignature3 sig3 = + crypto::generate_ring_signature3(prefix_hash, images, pubss, secs, sec_indexes, view_secret_key); + bool checked = crypto::check_ring_signature3(prefix_hash, images, pubss, sig3); + // for (size_t i = 0; i != images.size(); ++i) { + // size_t found_sec_index = + // crypto::find_deterministic_input3(prefix_hash, i, sig3.r.at(i), + // view_secret_key); invariant(sec_indexes[i] == found_sec_index, ""); + // } + sig3.r.at(0).at(0).data[13] += 1; + bool checked2 = crypto::check_ring_signature3(prefix_hash, images, pubss, sig3); + invariant(checked && !checked2, ""); + + images.clear(); + secs.clear(); + pubss.clear(); + sec_indexes.clear(); + } } else if (cmd == "check_ring_signature") { crypto::Hash prefix_hash; crypto::KeyImage image; std::vector vpubs; - std::vector pubs; + // std::vector pubs; size_t pubs_count; - std::vector sigs; - bool expected = false, actual; + crypto::RingSignature sigs; + bool expected = false; size_t i; get(input, prefix_hash, image, pubs_count); vpubs.resize(pubs_count); - pubs.resize(pubs_count); for (i = 0; i < pubs_count; i++) { get(input, vpubs[i]); - pubs[i] = &vpubs[i]; } sigs.resize(pubs_count); getvar(input, pubs_count * sizeof(crypto::Signature), sigs.data()); get(input, expected); - actual = check_ring_signature(prefix_hash, image, pubs.data(), pubs_count, sigs.data(), true); - check(expected == actual, test); // TODO - check 2.* + const bool actual = check_ring_signature(prefix_hash, image, vpubs.data(), vpubs.size(), sigs, true); + check(expected == actual, test); } else { throw std::ios_base::failure("Unknown function: " + cmd); } @@ -216,31 +306,30 @@ void test_crypto(const std::string &test_vectors_filename) { int COUNT = 1000; auto idea_start = std::chrono::high_resolution_clock::now(); for (int count = 0; count != COUNT; ++count) { - crypto::KeyDerivation der; - crypto::generate_key_derivation(test_keypair1.public_key, test_keypair2.secret_key, der); - derive_secret_key(der, 0, test_keypair1.secret_key, test_keypair2.secret_key); + crypto::KeyDerivation der = crypto::generate_key_derivation(test_keypair1.public_key, test_keypair2.secret_key); + test_keypair2.secret_key = derive_secret_key(der, 0, test_keypair1.secret_key); } auto idea_ms = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - idea_start); if (idea_ms.count() != 0) - std::cout << "Benchmart generate_derivation+derive_secret_key result=" << test_keypair2.secret_key + std::cout << "Benchmark generate_derivation+derive_secret_key result=" << test_keypair2.secret_key << " ops/sec=" << COUNT * 1000 / idea_ms.count() << std::endl; else - std::cout << "Benchmart generate_derivation+derive_secret_key result=" << test_keypair2.secret_key + std::cout << "Benchmark generate_derivation+derive_secret_key result=" << test_keypair2.secret_key << " ops/sec=inf" << std::endl; crypto::Signature test_sig; test_keypair1 = crypto::random_keypair(); COUNT = 1000; idea_start = std::chrono::high_resolution_clock::now(); for (int count = 0; count != COUNT; ++count) { - crypto::generate_signature( - *(const crypto::Hash *)(&test_sig.c), test_keypair1.public_key, test_keypair1.secret_key, test_sig); + test_sig = crypto::generate_signature( + *(const crypto::Hash *)(&test_sig.c), test_keypair1.public_key, test_keypair1.secret_key); } idea_ms = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - idea_start); if (idea_ms.count() != 0) - std::cout << "Benchmart generate_signature result=" << test_sig.c + std::cout << "Benchmark generate_signature result=" << test_sig.c << " ops/sec=" << COUNT * 1000 / idea_ms.count() << std::endl; else - std::cout << "Benchmart generate_signature result=" << test_sig.r << " ops/sec=inf" << std::endl; + std::cout << "Benchmark generate_signature result=" << test_sig.r << " ops/sec=inf" << std::endl; } diff --git a/tests/crypto/test_crypto.hpp b/tests/crypto/test_crypto.hpp index 0f901069..33b20b28 100644 --- a/tests/crypto/test_crypto.hpp +++ b/tests/crypto/test_crypto.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once diff --git a/tests/hash/test_hash.cpp b/tests/hash/test_hash.cpp index 388f10e0..e08d3c28 100644 --- a/tests/hash/test_hash.cpp +++ b/tests/hash/test_hash.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #include "test_hash.hpp" @@ -22,35 +23,39 @@ static crypto::CryptoNightContext context; //#pragma warning(disable : 4297) //#endif -static void hash_tree(const void *vdata, size_t vlength, crypto::CHash *hash) { +static void hash_tree(const void *vdata, size_t vlength, cryptoHash *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, ""); + const struct cryptoHash *data = (const struct cryptoHash *)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::Hash{}, ""); // No output array overwrite + crypto::Hash hash2; + crypto_tree_hash_from_branch(branch.data(), branch.size() - 1, data, nullptr, &hash2); + crypto::Hash chash; + static_cast(chash) = *hash; + invariant(chash == hash2, ""); } -static void slow_hash(const void *data, size_t length, crypto::CHash *hash) { +static void slow_hash(const void *data, size_t length, cryptoHash *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, ""); + crypto::Hash hash2; + crypto_cn_slow_hash_platform_independent(context.get_data(), data, length, &hash2); + crypto::Hash chash; + static_cast(chash) = *hash; + invariant(chash == hash2, ""); } -extern "C" typedef void hash_f(const void *, size_t, crypto::CHash *); +extern "C" typedef void hash_f(const void *, size_t, cryptoHash *); struct hash_func { 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}, - {"extra-jh", crypto::hash_extra_jh}, {"extra-skein", crypto::hash_extra_skein}}; +} hashes[] = {{"fast", crypto_cn_fast_hash}, {"slow", slow_hash}, {"tree", hash_tree}, + {"extra-blake", crypto_hash_extra_blake}, {"extra-groestl", crypto_hash_extra_groestl}, + {"extra-jh", crypto_hash_extra_jh}, {"extra-skein", crypto_hash_extra_skein}}; void test_hash(const char *test_fun_name, const std::string &test_vectors_filename) { hash_f *f = nullptr; @@ -97,7 +102,7 @@ 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], ""); + 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"); @@ -132,10 +137,10 @@ void test_hashes(const std::string &test_vectors_folder) { auto idea_ms = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - idea_start); if (idea_ms.count() != 0) - std::cout << "Benchmart cn_fast_hash result=" << test_hash << " hashes/sec=" << COUNT * 1000 / idea_ms.count() + std::cout << "Benchmark cn_fast_hash result=" << test_hash << " hashes/sec=" << COUNT * 1000 / idea_ms.count() << std::endl; else - std::cout << "Benchmart cn_fast_hash result=" << test_hash << " hashes/sec=inf" << std::endl; + std::cout << "Benchmark cn_fast_hash result=" << test_hash << " hashes/sec=inf" << std::endl; test_hash = crypto::Hash{}; COUNT = 100; idea_start = std::chrono::high_resolution_clock::now(); @@ -145,8 +150,8 @@ void test_hashes(const std::string &test_vectors_folder) { idea_ms = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - idea_start); if (idea_ms.count() != 0) - std::cout << "Benchmart cn_slow_hash result=" << test_hash << " hashes/sec=" << COUNT * 1000 / idea_ms.count() + std::cout << "Benchmark cn_slow_hash result=" << test_hash << " hashes/sec=" << COUNT * 1000 / idea_ms.count() << std::endl; else - std::cout << "Benchmart cn_slow_hash result=" << test_hash << " hashes/sec=inf" << std::endl; + std::cout << "Benchmark cn_slow_hash result=" << test_hash << " hashes/sec=inf" << std::endl; } diff --git a/tests/hash/test_hash.hpp b/tests/hash/test_hash.hpp index 0edd51ae..a3e00cf4 100644 --- a/tests/hash/test_hash.hpp +++ b/tests/hash/test_hash.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once diff --git a/tests/io.hpp b/tests/io.hpp index 7edea334..b4814a56 100644 --- a/tests/io.hpp +++ b/tests/io.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once @@ -8,30 +9,7 @@ #include #include #include - -inline bool hexdecode(const char *from, std::size_t length, void *to) { - std::size_t i; - for (i = 0; i < length; i++) { - int v = 0; - if (from[2 * i] >= '0' && from[2 * i] <= '9') { - v = from[2 * i] - '0'; - } else if (from[2 * i] >= 'a' && from[2 * i] <= 'f') { - v = from[2 * i] - 'a' + 10; - } else { - return false; - } - v <<= 4; - if (from[2 * i + 1] >= '0' && from[2 * i + 1] <= '9') { - v |= from[2 * i + 1] - '0'; - } else if (from[2 * i + 1] >= 'a' && from[2 * i + 1] <= 'f') { - v |= from[2 * i + 1] - 'a' + 10; - } else { - return false; - } - *(reinterpret_cast(to) + i) = v; - } - return true; -} +#include "common/StringTools.hpp" inline void get(std::istream &input, bool &res) { std::string sres; @@ -53,9 +31,10 @@ typename std::enable_if::value, void>::type get(std::istream inline void getvar(std::istream &input, std::size_t length, void *res) { std::string sres; input >> sres; - if (sres.length() != 2 * length || !hexdecode(sres.data(), length, res)) { + // if (!sres.length() != 2 * length || !hexdecode(sres.data(), length, res)) + // input.setstate(std::ios_base::failbit); + if (!common::from_hex(sres, res, length)) input.setstate(std::ios_base::failbit); - } } template @@ -74,9 +53,9 @@ inline void get(std::istream &input, std::vector &res) { } else { std::size_t length = sres.length() / 2; res.resize(length); - if (!hexdecode(sres.data(), length, res.data())) { + if (!common::from_hex(sres, res.data(), length)) + // if (!hexdecode(sres.data(), length, res.data())) input.setstate(std::ios_base::failbit); - } } } diff --git a/tests/json/test_json.cpp b/tests/json/test_json.cpp index a2cc8981..a570b642 100644 --- a/tests/json/test_json.cpp +++ b/tests/json/test_json.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #include "test_json.hpp" @@ -20,7 +21,8 @@ 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: " << common::what(ex) << 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/json/test_json.hpp b/tests/json/test_json.hpp index 387442da..a85944c6 100644 --- a/tests/json/test_json.hpp +++ b/tests/json/test_json.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once diff --git a/tests/wallet_file/test_wallet_file.cpp b/tests/wallet_file/test_wallet_file.cpp index c5e70d8d..d2408b04 100644 --- a/tests/wallet_file/test_wallet_file.cpp +++ b/tests/wallet_file/test_wallet_file.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #include "Core/Wallet.hpp" #include "crypto/crypto.hpp" @@ -8,7 +9,7 @@ #include "test_wallet_file.hpp" -using namespace bytecoin; +using namespace cn; // test01.simplewallet.wallet // format - simplewallet with cache @@ -17,7 +18,8 @@ using namespace bytecoin; // 24xTx43fFtNBUn5f6Fj1wC7y8JsbD4N1XS2s3Q8HzWxtfvERccTPX6e5ua1mf55Wm7Z4MiaWT7LPeiBxPtD8kU9V7z3kuex // test02.wallet -// format - legacy walletd with cache (file truncated to 1000000 bytes due to git limitations) +// format - legacy walletd with cache (file truncated to 1000000 bytes due to +// git limitations) // no password // created by rpc-wallet-2.1.2, several tx received and sent // 25HYsSBvERcePEb7LWrECYiPw4vpeG3MWCBxB3LbNC43issv7LEDp8gat9CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDb8W8dk @@ -25,13 +27,15 @@ using namespace bytecoin; // 23acTzmNBWFSDK12QNVucEdjRjTwPmXHsPwyXaYtc1yP9FviK5e5bMiat9CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDaQapEx // test03.wallet -// format - legacy walletd with cache (file truncated to 1000000 bytes due to git limitations) +// format - legacy walletd with cache (file truncated to 1000000 bytes due to +// git limitations) // password - test03 // created by rpc-wallet-2.1.2, several tx received and sent // 21vbqHu4mDLGyo7Q959fbggLU4Z6cXMfaDpUJuHQSY9b4bRfYGbv9hF4zcz3DBpH1y4kUop2HPKPsNb9WLBYE6U16w1V12t // test04.wallet -// format - legacy walletd with cache and contacts (file truncated to 1000000 bytes due to git limitations) +// format - legacy walletd with cache and contacts (file truncated to 1000000 +// bytes due to git limitations) // password - test04 // created by GUI wallet 1.1.9.3, several tx received and sent // 27TfAw84qKYEktQW9QvV94VkjNnRJuQ9JTrirjbQC5ASdRS233RAtENBNEZrGCSjPAFBNBReUsaQ8Jo82GTHLU4xQ28tTWU @@ -59,7 +63,7 @@ const std::string tmp_name("../tests/scratchpad/test_wallet_file.tmp"); 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; - Wallet wallet(logger, tmp_name, password); + WalletContainerStorage wallet(currency, 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(); @@ -67,9 +71,11 @@ static void test_body(const Currency ¤cy, const std::string &path, const s throw std::runtime_error("view keys do not match for " + path); WalletRecord first_record = records.at(0); for (auto &&a : addresses) { - AccountPublicAddress address; - if (!currency.parse_account_address_string(a, &address)) + AccountAddress v_address; + if (!currency.parse_account_address_string(a, &v_address)) throw std::runtime_error("failed to parse address " + a); + invariant(v_address.type() == typeid(AccountAddressSimple), ""); + auto &address = boost::get(v_address); if (address.view_public_key != wallet.get_view_public_key()) throw std::runtime_error("view_public_key test failed for " + path); size_t pos = 0; @@ -121,9 +127,9 @@ static void test_single_file(const Currency ¤cy, const std::string &path, 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); + platform::FileStream fs(tmp_name, platform::O_READ_EXISTING); auto si = fs.seek(0, SEEK_END); - if (si != Wallet::wallet_file_size(addresses.size())) + if (si != WalletContainerStorage::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); @@ -134,28 +140,47 @@ void test_wallet_file(const std::string &path_prefix) { Currency currency("main"); test_single_file(currency, path_prefix + "/test01.simplewallet.wallet", "", - {"24xTx43fFtNBUn5f6Fj1wC7y8JsbD4N1XS2s3Q8HzWxtfvERccTPX6e5ua1mf55Wm7Z4MiaWT7LPeiBxPtD8kU9V7z3kuex"}, false); + {"24xTx43fFtNBUn5f6Fj1wC7y8JsbD4N1XS2s3Q8HzWxtfvERccTPX6e5ua" + "1mf55Wm7Z4MiaWT7LPeiBxPtD8kU9V7z3kuex"}, + false); test_single_file(currency, path_prefix + "/test02.wallet", "", - {"25HYsSBvERcePEb7LWrECYiPw4vpeG3MWCBxB3LbNC43issv7LEDp8gat9CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDb8W8dk", - "29gEx3NRgooBHLdzQUGicXBtMdAtHaaDtf6aGZjbsgJrCSFVaRCT9SYat9CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDZriJ8Y", - "23acTzmNBWFSDK12QNVucEdjRjTwPmXHsPwyXaYtc1yP9FviK5e5bMiat9CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDaQapEx"}, + {"25HYsSBvERcePEb7LWrECYiPw4vpeG3MWCBxB3LbNC43issv7LEDp8gat9" + "CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDb8W8dk", + "29gEx3NRgooBHLdzQUGicXBtMdAtHaaDtf6aGZjbsgJrCSFVaRCT9SYat9" + "CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDZriJ8Y", + "23acTzmNBWFSDK12QNVucEdjRjTwPmXHsPwyXaYtc1yP9FviK5e5bMiat9" + "CRkq1ZusM85yZA6y2gTBfdUpJwyJKdDaQapEx"}, false); test_single_file(currency, path_prefix + "/test03.wallet", "test03", - {"21vbqHu4mDLGyo7Q959fbggLU4Z6cXMfaDpUJuHQSY9b4bRfYGbv9hF4zcz3DBpH1y4kUop2HPKPsNb9WLBYE6U16w1V12t"}, false); + {"21vbqHu4mDLGyo7Q959fbggLU4Z6cXMfaDpUJuHQSY9b4bRfYGbv9hF4zc" + "z3DBpH1y4kUop2HPKPsNb9WLBYE6U16w1V12t"}, + false); test_single_file(currency, path_prefix + "/test04.wallet", "test04", - {"27TfAw84qKYEktQW9QvV94VkjNnRJuQ9JTrirjbQC5ASdRS233RAtENBNEZrGCSjPAFBNBReUsaQ8Jo82GTHLU4xQ28tTWU"}, false); + {"27TfAw84qKYEktQW9QvV94VkjNnRJuQ9JTrirjbQC5ASdRS233RAtENBNE" + "ZrGCSjPAFBNBReUsaQ8Jo82GTHLU4xQ28tTWU"}, + false); test_single_file(currency, path_prefix + "/test05.wallet", "test05", - {"23ryfTTpryt8q3h5NFoMGs4BjM6jGmdQqUrtSuU4nM3oP8CKmXXwu6CcEYrwtUm2rx43LvihFEhKEfDagjQxWoLwDTX6XpC"}, false); + {"23ryfTTpryt8q3h5NFoMGs4BjM6jGmdQqUrtSuU4nM3oP8CKmXXwu6CcEY" + "rwtUm2rx43LvihFEhKEfDagjQxWoLwDTX6XpC"}, + false); test_single_file(currency, path_prefix + "/test05v.wallet", "test05", - {"23ryfTTpryt8q3h5NFoMGs4BjM6jGmdQqUrtSuU4nM3oP8CKmXXwu6CcEYrwtUm2rx43LvihFEhKEfDagjQxWoLwDTX6XpC"}, true); + {"23ryfTTpryt8q3h5NFoMGs4BjM6jGmdQqUrtSuU4nM3oP8CKmXXwu6CcEY" + "rwtUm2rx43LvihFEhKEfDagjQxWoLwDTX6XpC"}, + true); test_single_file(currency, path_prefix + "/test06.wallet", "", - {"27j6TP7du1SWKk4fVQa138VNhy339xa76jEBRajhHYnVeLaLs5HmSkEZ4FiLuLy87hgWYkSinGntREBMq3dvui11NkhsuUJ", - "23kJeyCgzH6JkTJqq1NNjrhXFYrfsUYvw2a9HhiEqVtRXK86HAu3uWWZ4FiLuLy87hgWYkSinGntREBMq3dvui11NiUKftd", - "28acio4hR2cjQPSBj8oYbjSYkFvVaPdQTH44HsigJQiSgr6S5BaAZkzZ4FiLuLy87hgWYkSinGntREBMq3dvui11Ng9YMtq"}, + {"27j6TP7du1SWKk4fVQa138VNhy339xa76jEBRajhHYnVeLaLs5HmSkEZ4F" + "iLuLy87hgWYkSinGntREBMq3dvui11NkhsuUJ", + "23kJeyCgzH6JkTJqq1NNjrhXFYrfsUYvw2a9HhiEqVtRXK86HAu3uWWZ4F" + "iLuLy87hgWYkSinGntREBMq3dvui11NiUKftd", + "28acio4hR2cjQPSBj8oYbjSYkFvVaPdQTH44HsigJQiSgr6S5BaAZkzZ4F" + "iLuLy87hgWYkSinGntREBMq3dvui11Ng9YMtq"}, false); test_single_file(currency, path_prefix + "/test06v.wallet", "", - {"27j6TP7du1SWKk4fVQa138VNhy339xa76jEBRajhHYnVeLaLs5HmSkEZ4FiLuLy87hgWYkSinGntREBMq3dvui11NkhsuUJ", - "23kJeyCgzH6JkTJqq1NNjrhXFYrfsUYvw2a9HhiEqVtRXK86HAu3uWWZ4FiLuLy87hgWYkSinGntREBMq3dvui11NiUKftd", - "28acio4hR2cjQPSBj8oYbjSYkFvVaPdQTH44HsigJQiSgr6S5BaAZkzZ4FiLuLy87hgWYkSinGntREBMq3dvui11Ng9YMtq"}, + {"27j6TP7du1SWKk4fVQa138VNhy339xa76jEBRajhHYnVeLaLs5HmSkEZ4F" + "iLuLy87hgWYkSinGntREBMq3dvui11NkhsuUJ", + "23kJeyCgzH6JkTJqq1NNjrhXFYrfsUYvw2a9HhiEqVtRXK86HAu3uWWZ4F" + "iLuLy87hgWYkSinGntREBMq3dvui11NiUKftd", + "28acio4hR2cjQPSBj8oYbjSYkFvVaPdQTH44HsigJQiSgr6S5BaAZkzZ4F" + "iLuLy87hgWYkSinGntREBMq3dvui11Ng9YMtq"}, true); } diff --git a/tests/wallet_file/test_wallet_file.hpp b/tests/wallet_file/test_wallet_file.hpp index e0b18ccb..20cdc326 100644 --- a/tests/wallet_file/test_wallet_file.hpp +++ b/tests/wallet_file/test_wallet_file.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once diff --git a/tests/wallet_state/test_wallet_state.cpp b/tests/wallet_state/test_wallet_state.cpp index 933aa9eb..646befd1 100644 --- a/tests/wallet_state/test_wallet_state.cpp +++ b/tests/wallet_state/test_wallet_state.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #include "../Random.hpp" #include "Core/Config.hpp" @@ -9,19 +10,22 @@ #include "test_wallet_state.hpp" -using namespace bytecoin; +using namespace cn; class WalletStateTest : public WalletStateBasic { public: - std::map memory_spent; + std::map memory_spent; explicit WalletStateTest(logging::ILogger &log, const Config &config, const Currency ¤cy) - : WalletStateBasic(log, config, currency, "test_wallet_state") {} + : WalletStateBasic(log, config, currency, "test_wallet_state", false) {} 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); } + Amount add_incoming_deterministic_input(Height block_height, Amount am, size_t gi, const PublicKey &pk) override { + return 0; // TODO + } bool try_add_incoming_output(const api::Output &output, Amount *confirmed_balance_delta) const { return WalletStateBasic::try_add_incoming_output(output, confirmed_balance_delta); } @@ -33,7 +37,7 @@ class WalletStateTest : public WalletStateBasic { WalletStateBasic::add_transaction(height, tid, tx, ptx); } void unlock(Height height, Timestamp ts) { WalletStateBasic::unlock(height, ts); } - const std::map &get_used_key_images() const override { return memory_spent; } + const std::map &get_mempool_kis_or_pks() const override { return memory_spent; } }; static bool less_output(const api::Output &a, const api::Output &b) { @@ -45,22 +49,25 @@ static bool eq_output(const api::Output &a, const api::Output &b) { // We will check that WalletStateBasic and model have the same behaviour -// Wallet State cannot remember all key images, so sending key image before corresponding output is also NOP in model -// They will both incorrectly show corresponding unspent as available, creating invalid transactions -// We might add "conflicting keyimage" to bytecoind CreateTransaction reply, so that they can somehow update their +// Wallet State cannot remember all key images, so sending key image before +// corresponding output is also NOP in model +// They will both incorrectly show corresponding unspent as available, creating +// invalid transactions +// We might add "conflicting keyimage" to bytecoind CreateTransaction reply, so +// that they can somehow update their // balances class WalletStateModel : public IWalletState { public: const Currency &m_currency; explicit WalletStateModel(const Currency ¤cy) : m_currency(currency) {} - std::map> all_keyimages; - std::map, api::Output> outputs; + std::map> all_keyimages; + std::map, api::Output> outputs; // std::map> transactions; std::map transfers; std::vector locked_outputs; - std::map, std::pair> + std::map, std::pair> unlocked_outputs; // height of unlock and adjusted amount Amount add_incoming_output(Height block_height, const api::Output &output, bool just_unlocked) { @@ -115,12 +122,12 @@ class WalletStateModel : public IWalletState { 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; + std::map memory_spent; bool is_memory_spent(const api::Output &output) const { return memory_spent.count(output.key_image) != 0; } 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_block_or_timestamp)) { + if (m_currency.is_block_or_timestamp_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 { @@ -158,7 +165,10 @@ class WalletStateModel : public IWalletState { transfers[block_height].transactions.back().transfers.push_back(transfer); return existing_output.amount; } - virtual void add_transaction( + Amount add_incoming_deterministic_input(Height block_height, Amount am, size_t gi, const PublicKey &pk) override { + return 0; // TODO + } + void add_transaction( Height height, const Hash &tid, const TransactionPrefix &tx, const api::Transaction &ptx) override { // transactions[height].push_back(ptx); } @@ -190,22 +200,25 @@ class WalletStateModel : public IWalletState { } std::vector api_get_transfers(const std::string &address, Height *from_height, Height *to_height, - bool forward, uint32_t desired_tx_count = std::numeric_limits::max()) const { + bool forward, size_t desired_tx_count = std::numeric_limits::max()) const { std::vector result; - for (auto mit = transfers.upper_bound(*from_height); mit != transfers.upper_bound(*to_height); ++mit) { + for (auto mit = transfers.lower_bound(*from_height); mit != transfers.lower_bound(*to_height); ++mit) { // api::Block block; // block.header.height = mit->second.header.height; // block.transactions.push_back(api::Transaction{}); // for(auto && tr : mit->second.transactions) // for(auto && t : tr.transfers) - // if(address.empty() || t.address == address) + // if(address.empty() || t.address + //== address) // block.transactions.back().transfers.push_back(t); // result.push_back(block); result.push_back(mit->second); } return result; } - // virtual std::vector api_get_locked_or_unconfirmed_unspent(const std::string &address, Height height) + // virtual std::vector + // api_get_locked_or_unconfirmed_unspent(const std::string &address, Height + // height) // const; api::Balance get_balance(const std::string &address, Height height) const { api::Balance balance; @@ -222,7 +235,8 @@ class WalletStateModel : public IWalletState { WalletStateBasic::combine_balance(balance, la.second, 1, 0); } for (auto &&la : locked_outputs) - // if(!is_memory_spent(la)) // WalletStateBasic does not perform this check, because it requires + // if(!is_memory_spent(la)) // WalletStateBasic + // does not perform this check, because it requires // iterating either all used keyimages or all lock index if (address.empty() || la.address == address) WalletStateBasic::combine_balance(balance, la, 1, 0); @@ -241,7 +255,7 @@ void test_wallet_state(common::CommandLine &cmd) { WalletStateTest ws(logger, config, currency); WalletStateModel wm(currency); - std::map next_gi; + std::map next_gi; std::set used_keyimages; std::set output_keyimages; @@ -251,7 +265,7 @@ void test_wallet_state(common::CommandLine &cmd) { const Height TEST_HEIGHT = 2000; if (!VIEW_ONLY) - for (uint32_t i = 0; i != 1024; ++i) { + for (size_t i = 0; i != 1024; ++i) { KeyImage ki; ki.data[0] = random() % 256; ki.data[1] = random() % 16; @@ -267,13 +281,13 @@ void test_wallet_state(common::CommandLine &cmd) { const Timestamp TEST_TIMESTAMP = currency.max_block_height * 2; - for (uint32_t ha = 1; ha != TEST_HEIGHT; ++ha) { + for (Height ha = 1; ha != TEST_HEIGHT; ++ha) { api::Transaction ptx; size_t spend_outputs = (random() % 16); if (ha == 36) std::cout << "Aha"; if (spend_outputs < 10 && ha != TEST_HEIGHT - 1) { - for (uint32_t j = 0; j != spend_outputs; ++j) { + for (size_t j = 0; j != spend_outputs; ++j) { KeyImage ki; if (random() % 4 == 0 || output_keyimages.empty()) { ki.data[0] = random() % 256; @@ -302,13 +316,13 @@ void test_wallet_state(common::CommandLine &cmd) { } size_t add_outputs = (random() % 8); if (add_outputs < 3 && ha != TEST_HEIGHT - 1) { - for (uint32_t j = 0; j != add_outputs; ++j) { + for (size_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); + 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) @@ -344,7 +358,8 @@ void test_wallet_state(common::CommandLine &cmd) { << (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) { - // if (used_keyimages.insert(ki).second) { // We never get same ki from blockchain + // 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)) { api::Transfer transfer; @@ -363,7 +378,7 @@ void test_wallet_state(common::CommandLine &cmd) { TEST_TIMESTAMP + ha * currency.difficulty_target + random() % currency.block_future_time_limit; wm.unlock(ha, uti); ws.unlock(ha, uti); - for (uint32_t wi = 0; wi != 25; ++wi) { // [-20..5) range around tip + for (Height wi = 0; wi != 25; ++wi) { // [-20..5) range around tip if (ha + wi < 20) continue; // if(ha == 19 && wi == 1) @@ -395,8 +410,8 @@ void test_wallet_state(common::CommandLine &cmd) { for (const auto &addr : addresses) { std::map transfer_balances1; std::map transfer_balances2; - Height from_height = ha == 0 ? ha : ha - 1; - Height to_height = ha; + Height from_height = ha; + Height to_height = ha + 1; auto tra1 = wm.api_get_transfers(addr, &from_height, &to_height, true); for (auto &&b1 : tra1) for (auto &&tr1 : b1.transactions) @@ -404,18 +419,20 @@ void test_wallet_state(common::CommandLine &cmd) { if (t1.ours && !t1.locked && (addr.empty() || t1.address == addr)) { invariant(!t1.address.empty(), ""); transfer_balances1[addr] += t1.amount; - // invariant(transfer_balances1[addr] >= 0, ""); + // invariant(transfer_balances1[addr] + //>= 0, ""); if (transfer_balances1[addr] == 0) transfer_balances1.erase(addr); } - from_height = ha == 0 ? ha : ha - 1; - to_height = ha; + from_height = ha; + to_height = ha + 1; auto unl2 = ws.api_get_unlocked_transfers(addr, from_height, to_height); for (auto &&u : unl2) if (addr.empty() || u.address == addr) { invariant(!u.address.empty(), ""); transfer_balances2[addr] += u.amount; - // invariant(transfer_balances2[addr] >= 0, ""); + // invariant(transfer_balances2[addr] >= 0, + //""); if (transfer_balances2[addr] == 0) transfer_balances2.erase(addr); } @@ -426,7 +443,8 @@ void test_wallet_state(common::CommandLine &cmd) { if (t1.ours && !t1.locked && (addr.empty() || t1.address == addr)) { invariant(!t1.address.empty(), ""); transfer_balances2[addr] += t1.amount; - // invariant(transfer_balances2[addr] >= 0, ""); + // invariant(transfer_balances2[addr] + //>= 0, ""); if (transfer_balances2[addr] == 0) transfer_balances2.erase(addr); } @@ -448,7 +466,8 @@ void test_wallet_state(common::CommandLine &cmd) { invariant(sum1 == sum2, ""); } // for (auto uk : output_keyimages) { - // invariant(ws.add_incoming_keyimage(TEST_HEIGHT, uk) == wm.add_incoming_keyimage(TEST_HEIGHT, uk), ""); + // invariant(ws.add_incoming_keyimage(TEST_HEIGHT, uk) == + // wm.add_incoming_keyimage(TEST_HEIGHT, uk), ""); // } for (auto addr : addresses) { auto ba1 = wm.get_balance(addr, TEST_HEIGHT + 10); diff --git a/tests/wallet_state/test_wallet_state.hpp b/tests/wallet_state/test_wallet_state.hpp index d16de86b..ea050ad2 100644 --- a/tests/wallet_state/test_wallet_state.hpp +++ b/tests/wallet_state/test_wallet_state.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. -// Licensed under the GNU Lesser General Public License. See LICENSE for details. +// Licensed under the GNU Lesser General Public License. See LICENSE for +// details. #pragma once