From 9df3a81801af2a8d02add6ef27b5af73df71ef16 Mon Sep 17 00:00:00 2001 From: jezal Date: Mon, 25 Aug 2014 15:35:07 +0100 Subject: [PATCH] Transaction history and 'reset' command for simplewallet --- ReleaseNotes.txt | 6 + src/cryptonote_config.h | 6 +- src/cryptonote_core/Currency.cpp | 3 +- src/cryptonote_core/blockchain_storage.cpp | 25 +++- src/cryptonote_core/cryptonote_core.cpp | 2 +- .../cryptonote_protocol_handler.inl | 10 +- src/simplewallet/simplewallet.cpp | 59 ++++++++-- src/simplewallet/simplewallet.h | 4 +- src/version.h.in | 4 +- src/wallet/WalletTransactionSender.cpp | 2 +- src/wallet/wallet2.cpp | 107 +++++++++++++----- src/wallet/wallet2.h | 86 ++++++++++++-- src/wallet/wallet_rpc_server.cpp | 42 ++++++- src/wallet/wallet_rpc_server.h | 14 ++- src/wallet/wallet_rpc_server_commans_defs.h | 66 +++++++++++ tests/core_tests/block_validation.cpp | 31 +---- tests/core_tests/block_validation.h | 7 +- 17 files changed, 376 insertions(+), 98 deletions(-) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 7d3de6eb..cc0f8a41 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,9 @@ +Release notes 1.0.2 + +- Transaction history for simplewallet +- Reset command for simplewallet +- Various simplewallet improvements + Release notes 1.0.1 - Fix transfers in simplewallet diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 55d9103e..fc52139f 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -63,6 +63,7 @@ const uint64_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS = DIFFICULTY_TARGET const uint64_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = 60 * 60 * 24; //seconds, one day const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week +const uint64_t UPGRADE_HEIGHT = 546602; const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks @@ -104,7 +105,7 @@ const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; // const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds const uint64_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes const size_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds -const char P2P_STAT_TRUSTED_PUB_KEY[] = "8f80f9a5a434a9f1510d13336228debfee9c918ce505efe225d8c94d045fa115"; +const char P2P_STAT_TRUSTED_PUB_KEY[] = "93467628927eaa0b13a4e52e61864a75aa475e67f6b5748eb3fc1d2fe468aed4"; const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; const unsigned THREAD_STACK_SIZE = 5 * 1024 * 1024; @@ -143,7 +144,8 @@ const CheckpointData CHECKPOINTS[] = { {480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"}, {484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"}, {506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"}, - {544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"} + {544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"}, + {553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"} }; } // cryptonote diff --git a/src/cryptonote_core/Currency.cpp b/src/cryptonote_core/Currency.cpp index 1c14c243..fd7a95ae 100644 --- a/src/cryptonote_core/Currency.cpp +++ b/src/cryptonote_core/Currency.cpp @@ -40,6 +40,7 @@ namespace cryptonote { CHECK_AND_ASSERT_MES(r, false, "Failed to get genesis block hash"); if (isTestnet()) { + m_upgradeHeight = 0; m_blocksFileName = "testnet_" + m_blocksFileName; m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; @@ -380,7 +381,7 @@ namespace cryptonote { mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME); mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); - upgradeHeight(UpgradeDetectorBase::UNDEF_HEIGHT); + upgradeHeight(parameters::UPGRADE_HEIGHT); upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW); upgradeWindow(parameters::UPGRADE_WINDOW); diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 18c74f29..4dfd8733 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -231,8 +231,9 @@ namespace cryptonote if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) return; + std::string operation; if (Archive::is_loading::value) { - + operation = "- loading "; crypto::hash blockHash; ar & blockHash; @@ -241,13 +242,23 @@ namespace cryptonote } } else { + operation = "- saving "; ar & m_lastBlockHash; } + LOG_PRINT_L0(operation << "block index..."); ar & m_bs.m_blockIndex; + + LOG_PRINT_L0(operation << "transaction map..."); ar & m_bs.m_transactionMap; + + LOG_PRINT_L0(operation << "spend keys..."); ar & m_bs.m_spent_keys; + + LOG_PRINT_L0(operation << "outputs..."); ar & m_bs.m_outputs; + + LOG_PRINT_L0(operation << "multi-signature outputs..."); ar & m_bs.m_multisignatureOutputs; m_loaded = true; @@ -377,6 +388,9 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi m_outputs.clear(); m_multisignatureOutputs.clear(); for (uint32_t b = 0; b < m_blocks.size(); ++b) { + if (b % 1000 == 0) { + std::cout << "Height " << b << " of " << m_blocks.size() << '\r'; + } const BlockEntry& block = m_blocks[b]; crypto::hash blockHash = get_block_hash(block.bl); m_blockIndex.push(blockHash); @@ -435,6 +449,7 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi bool blockchain_storage::storeCache() { CRITICAL_REGION_LOCAL(m_blockchain_lock); + LOG_PRINT_L0("Saving blockchain..."); BlockCacheSerializer ser(*this, get_tail_id()); if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { LOG_ERROR("Failed to save blockchain cache"); @@ -900,9 +915,9 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: } if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) { - LOG_PRINT_RED_L0("Block with id: " << id - << ENDL << " can't be accepted for alternative chain, block height: " << block_height - << ENDL << " blockchain height: " << get_current_blockchain_height()); + LOG_PRINT_L2("Block with id: " << id << std::endl << + " can't be accepted for alternative chain, block height: " << block_height << std::endl << + " blockchain height: " << get_current_blockchain_height()); bvc.m_verifivation_failed = true; return false; } @@ -1525,7 +1540,7 @@ bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& b uint64_t height = get_block_height(b); const uint8_t expectedBlockVersion = get_block_major_version_for_height(height); if (b.majorVersion != expectedBlockVersion) { - LOG_PRINT_L0("Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << + LOG_PRINT_L2("Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << ", at height " << height << " expected version is " << static_cast(expectedBlockVersion)); return false; } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index ca4f1f96..ce12c819 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -257,7 +257,7 @@ namespace cryptonote if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) { - LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << + LOG_PRINT_RED_L0("transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " << (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize())); return false; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index bcbcf968..47427ac9 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -123,9 +123,9 @@ namespace cryptonote int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height - << " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " - << (0 <= diff ? std::string("behind") : std::string("ahead")) - << "] " << ENDL << "SYNCHRONIZATION started", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1)); + << " [" << std::abs(diff) << " blocks (" << std::abs(diff) / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " + << (diff >= 0 ? std::string("behind") : std::string("ahead")) << "] " << std::endl << + "SYNCHRONIZATION started", (diff >= 0 ? (is_inital ? LOG_LEVEL_0 : LOG_LEVEL_1) : LOG_LEVEL_2)); LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id); context.m_state = cryptonote_connection_context::state_synchronizing; context.m_remote_blockchain_height = hshd.current_height; @@ -173,7 +173,7 @@ namespace cryptonote block_verification_context bvc = boost::value_initialized(); m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false); if (bvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; } @@ -332,7 +332,7 @@ namespace cryptonote m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false); if (bvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; } else if (bvc.m_marked_as_orphaned) { diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8f512385..80e71d30 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -230,7 +230,7 @@ namespace } if (!r) { - fail_msg_writer() << "payment id has invalid format: \"" << value << "\", expected 64-character string"; + fail_msg_writer() << "payment ID has invalid format: \"" << value << "\", expected 64-character string"; return false; } } else if (arg == "-f") { @@ -249,7 +249,13 @@ namespace cryptonote::tx_destination_entry de; if (!m_currency.parseAccountAddressString(arg, de.addr)) { - fail_msg_writer() << "wrong address: " << arg; + crypto::hash paymentId; + if (tools::wallet2::parse_payment_id(arg, paymentId)) { + fail_msg_writer() << "Invalid payment ID usage. Please, use -p . See help for details."; + } else { + fail_msg_writer() << "Wrong address: " << arg; + } + return false; } @@ -307,6 +313,7 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency) m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); + m_cmd_binder.set_handler("list_transfers", boost::bind(&simple_wallet::listTransfers, this, _1), "Show all known transfers"); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments [ ... ] - Show payments , ... "); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), @@ -316,6 +323,7 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency) m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); + m_cmd_binder.set_handler("reset", boost::bind(&simple_wallet::reset, this, _1), "Discard cache data and start synchronizing from the start"); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help"); } //---------------------------------------------------------------------------------------------------- @@ -560,7 +568,14 @@ bool simple_wallet::save(const std::vector &args) return true; } -//---------------------------------------------------------------------------------------------------- + +bool simple_wallet::reset(const std::vector &args) { + m_wallet->reset(); + success_msg_writer(true) << "Reset is complete successfully"; + refresh(); + return true; +} + bool simple_wallet::start_mining(const std::vector& args) { if (!try_connect_to_daemon()) @@ -652,7 +667,7 @@ void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::Trans m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::refresh(const std::vector& args) +bool simple_wallet::refresh(const std::vector& args/* = std::vector()*/) { if (!try_connect_to_daemon()) return true; @@ -776,12 +791,35 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args return true; } -//---------------------------------------------------------------------------------------------------- + +bool simple_wallet::listTransfers(const std::vector& args) { + const std::vector& transfers = m_wallet->getTransfers(); + for (const tools::wallet2::Transfer& transfer : transfers) { + std::string address = "UNKNOWN"; + if (transfer.hasAddress) { + address = getAccountAddressAsStr(m_currency.publicAddressBase58Prefix(), transfer.address); + } + + message_writer(transfer.output ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) + << transfer.time + << ", " << (transfer.output ? "OUTPUT" : "INPUT") + << ", " << transfer.transactionHash + << ", " << m_currency.formatAmount(transfer.amount) + << ", " << m_currency.formatAmount(transfer.fee) + << ", " << transfer.paymentId + << ", " << address + << ", " << transfer.blockIndex + << ", " << transfer.unlockTime; + } + + return true; +} + bool simple_wallet::show_payments(const std::vector &args) { if(args.empty()) { - fail_msg_writer() << "expected at least one payment_id"; + fail_msg_writer() << "expected at least one payment ID"; return true; } @@ -819,7 +857,7 @@ bool simple_wallet::show_payments(const std::vector &args) } else { - fail_msg_writer() << "payment id has invalid format: \"" << arg << "\", expected 64-character string"; + fail_msg_writer() << "payment ID has invalid format: \"" << arg << "\", expected 64-character string"; } } @@ -864,6 +902,13 @@ bool simple_wallet::transfer(const std::vector &args) cryptonote::Transaction tx; m_wallet->transfer(cmd.dsts, cmd.fake_outs_count, 0, cmd.fee, cmd.extra, tx); success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); + + try { + m_wallet->store(); + } catch (const std::exception& e) { + fail_msg_writer() << e.what(); + return false; + } } catch (const tools::error::daemon_busy&) { diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 529b9d0a..5256ce7e 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -62,14 +62,16 @@ namespace cryptonote bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); - bool refresh(const std::vector &args); + bool refresh(const std::vector &args = std::vector()); bool show_balance(const std::vector &args = std::vector()); bool show_incoming_transfers(const std::vector &args); bool show_payments(const std::vector &args); bool show_blockchain_height(const std::vector &args); + bool listTransfers(const std::vector &args); bool transfer(const std::vector &args); bool print_address(const std::vector &args = std::vector()); bool save(const std::vector &args); + bool reset(const std::vector &args); bool set_log(const std::vector &args); uint64_t get_daemon_blockchain_height(std::string& err); diff --git a/src/version.h.in b/src/version.h.in index d04bb627..497e2ae5 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.1" -#define PROJECT_VERSION_BUILD_NO "316" +#define PROJECT_VERSION "1.0.2" +#define PROJECT_VERSION_BUILD_NO "336" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 54b55e67..e831b7fe 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -74,7 +74,7 @@ WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& cur m_unconfirmedTransactions(unconfirmedTransactions), m_isInitialized(false), m_isStoping(false) { - m_upperTransactionSizeLimit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize(); + m_upperTransactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); } void WalletTransactionSender::init(cryptonote::account_keys keys) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6792bf23..7e9e00d6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -65,7 +65,7 @@ void wallet2::init(const std::string& daemon_address) m_daemon_address = daemon_address; } //---------------------------------------------------------------------------------------------------- -bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id) { +bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id) { process_unconfirmed(tx); std::vector tx_extra_fields; @@ -84,7 +84,7 @@ bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transactio return false; } - TxItem txItem = { tx, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) }; + TxItem txItem = { tx, time, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) }; queue.push(std::unique_ptr(new TxItem(std::move(txItem)))); return true; } @@ -159,26 +159,59 @@ void wallet2::processCheckedTransaction(const TxItem& item) { } } - tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) - { - crypto::hash payment_id; - if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - { - uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0; - if (0 < received && null_hash != payment_id) - { - payment_details payment; - payment.m_tx_hash = cryptonote::get_transaction_hash(tx); - payment.m_amount = received; - payment.m_block_height = height; - payment.m_unlock_time = tx.unlockTime; - m_payments.emplace(payment_id, payment); - LOG_PRINT_L2("Payment found: " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount); - } + crypto::hash transactionHash = get_transaction_hash(tx); + + bool ownTransfer = false; + for (Transfer& transfer : transfers) { + if (transfer.transactionHash == transactionHash) { + transfer.blockIndex = height; + ownTransfer = true; } } + if (!ownTransfer) { + crypto::hash paymentId = null_hash; + tx_extra_nonce extraNonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extraNonce)) { + get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); + } + + if (tx_money_spent_in_ins < tx_money_got_in_outs) { + if (paymentId != null_hash) { + payment_details payment; + payment.m_tx_hash = transactionHash; + payment.m_amount = tx_money_got_in_outs - tx_money_spent_in_ins; + payment.m_block_height = height; + payment.m_unlock_time = tx.unlockTime; + m_payments.emplace(paymentId, payment); + LOG_PRINT_L2("Payment found: " << paymentId << " / " << payment.m_tx_hash << " / " << payment.m_amount); + } + + Transfer transfer; + transfer.time = item.time; + transfer.output = false; + transfer.transactionHash = transactionHash; + transfer.amount = tx_money_got_in_outs - tx_money_spent_in_ins; + transfer.fee = 0; + transfer.paymentId = paymentId; + transfer.hasAddress = false; + transfer.blockIndex = height; + transfer.unlockTime = tx.unlockTime; + transfers.push_back(transfer); + } else if (tx_money_got_in_outs < tx_money_spent_in_ins) { + Transfer transfer; + transfer.time = item.time; + transfer.output = true; + transfer.transactionHash = transactionHash; + transfer.amount = tx_money_spent_in_ins - tx_money_got_in_outs; + transfer.fee = 0; + transfer.paymentId = paymentId; + transfer.hasAddress = false; + transfer.blockIndex = height; + transfer.unlockTime = tx.unlockTime; + transfers.push_back(transfer); + } + } } //---------------------------------------------------------------------------------------------------- @@ -211,7 +244,6 @@ bool wallet2::addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_he "current_index=" + std::to_string(current_index) + ", m_blockchain.size()=" + std::to_string(m_blockchain.size())); m_blockchain.push_back(bl_id); - ++m_local_bc_height; if (0 != m_callback) { m_callback->on_new_block(current_index); @@ -234,7 +266,7 @@ size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::bloc if (b.timestamp + 60 * 60 * 24 > m_account.get_createtime()) { TIME_MEASURE_START(miner_tx_handle_time); - if(processNewTransaction(queue, b.minerTx, height, bl_id)) + if(processNewTransaction(queue, b.minerTx, height, b.timestamp, bl_id)) ++processedTransactions; TIME_MEASURE_FINISH(miner_tx_handle_time); @@ -244,7 +276,7 @@ size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::bloc cryptonote::Transaction tx; bool r = parse_and_validate_tx_from_blob(txblob, tx); THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - if(processNewTransaction(queue, tx, height, bl_id)) + if(processNewTransaction(queue, tx, height, b.timestamp, bl_id)) ++processedTransactions; } TIME_MEASURE_FINISH(txs_handle_time); @@ -512,7 +544,6 @@ size_t wallet2::detach_blockchain(uint64_t height) size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height); m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); - m_local_bc_height -= blocks_detached; for (auto it = m_payments.begin(); it != m_payments.end(); ) { @@ -522,6 +553,14 @@ size_t wallet2::detach_blockchain(uint64_t height) ++it; } + for (std::size_t transferIndex = 0; transferIndex < transfers.size();) { + if (transfers[transferIndex].blockIndex != 0 && transfers[transferIndex].blockIndex >= height) { + transfers.erase(transfers.begin() + transferIndex); + } else { + ++transferIndex; + } + } + LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); return blocks_detached; } @@ -536,7 +575,6 @@ bool wallet2::clear() m_blockchain.clear(); m_transfers.clear(); m_blockchain.push_back(m_currency.genesisBlockHash()); - m_local_bc_height = 1; return true; } //---------------------------------------------------------------------------------------------------- @@ -702,8 +740,6 @@ void wallet2::load(const std::string& wallet_, const std::string& password) THROW_WALLET_EXCEPTION_IF(m_blockchain[0] != m_currency.genesisBlockHash(), error::wallet_internal_error, "Genesis block missmatch. You probably use wallet without testnet flag with blockchain from test network or vice versa"); } - - m_local_bc_height = m_blockchain.size(); } //---------------------------------------------------------------------------------------------------- void wallet2::store() @@ -852,4 +888,23 @@ void wallet2::transfer(const std::vector& dsts transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx); } //---------------------------------------------------------------------------------------------------- +const std::vector& wallet2::getTransfers() { + return transfers; +} + +void wallet2::reset() { + clear(); + m_unconfirmed_txs.clear(); + m_payments.clear(); + m_key_images.clear(); + for (std::size_t transferIndex = 0; transferIndex < transfers.size();) { + if (transfers[transferIndex].hasAddress) { + transfers[transferIndex].blockIndex = 0; + ++transferIndex; + } else { + transfers.erase(transfers.begin() + transferIndex); + } + } +} + } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b7d452ba..f2ff6ed9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -70,14 +70,12 @@ namespace tools wallet2(const wallet2& rhs) : m_currency(rhs.m_currency), m_run(true), - m_callback(0), - m_upper_transaction_size_limit(rhs.m_upper_transaction_size_limit) { + m_callback(0) { }; public: wallet2(const cryptonote::Currency& currency) : m_currency(currency), m_run(true), m_callback(0) { - m_upper_transaction_size_limit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize(); } struct transfer_details @@ -121,6 +119,37 @@ namespace tools END_SERIALIZE() }; + struct Transfer { + uint64_t time; + bool output; + crypto::hash transactionHash; + uint64_t amount; + uint64_t fee; + crypto::hash paymentId; + bool hasAddress; + cryptonote::AccountPublicAddress address; + uint64_t blockIndex; + uint64_t unlockTime; + + template void serialize(Archive& archive, unsigned int version) { + archive & time; + archive & output; + archive & transactionHash; + archive & amount; + archive & fee; + archive & paymentId; + archive & hasAddress; + if (hasAddress) { + archive & address; + } + + archive & blockIndex; + archive & unlockTime; + } + }; + + typedef std::vector Transfers; + void generate(const std::string& wallet, const std::string& password); void load(const std::string& wallet, const std::string& password); void store(); @@ -153,7 +182,10 @@ namespace tools bool connectClient(epee::net_utils::http::http_simple_client& client); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments) const; - uint64_t get_blockchain_current_height() const { return m_local_bc_height; } + uint64_t get_blockchain_current_height() const { return m_blockchain.size(); } + const std::vector& getTransfers(); + void reset(); + template inline void serialize(t_archive &a, const unsigned int ver) { @@ -169,6 +201,9 @@ namespace tools if(ver < 7) return; a & m_payments; + if (ver >= 8) { + a & transfers; + } } static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists); @@ -180,6 +215,7 @@ namespace tools struct TxItem { cryptonote::Transaction tx; + uint64_t time; uint64_t height; crypto::hash blockId; crypto::public_key txPubKey; @@ -196,7 +232,7 @@ namespace tools bool addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_height, uint64_t height); size_t processNewBlockchainEntry(TxQueue& queue, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height); - bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id); + bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id); void processCheckedTransaction(const TxItem& item); // returns number of blocks added @@ -234,21 +270,21 @@ namespace tools std::string m_keys_file; epee::net_utils::http::http_simple_client m_http_client; std::vector m_blockchain; - std::atomic m_local_bc_height; //temporary workaround std::unordered_map m_unconfirmed_txs; transfer_container m_transfers; payment_container m_payments; std::unordered_map m_key_images; cryptonote::AccountPublicAddress m_account_public_address; - uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value std::atomic m_run; i_wallet2_callback* m_callback; + + Transfers transfers; }; } -BOOST_CLASS_VERSION(tools::wallet2, 7) +BOOST_CLASS_VERSION(tools::wallet2, 8) namespace boost { @@ -466,7 +502,8 @@ namespace tools bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, m_currency.publicAddressBase58Prefix(), sources, splitted_dsts, unlock_time); - THROW_WALLET_EXCEPTION_IF(m_upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, m_upper_transaction_size_limit); + uint64_t transactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); + THROW_WALLET_EXCEPTION_IF(get_object_blobsize(tx) > transactionSizeLimit, error::tx_too_big, tx, transactionSizeLimit); std::string key_images; bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const TransactionInput& s_e) -> bool @@ -487,13 +524,38 @@ namespace tools add_unconfirmed_tx(tx, change_dts.amount); - LOG_PRINT_L2("transaction " << get_transaction_hash(tx) << " generated ok and sent to daemon, key_images: [" << key_images << "]"); + crypto::hash transactionHash = get_transaction_hash(tx); + LOG_PRINT_L2("transaction " << transactionHash << " generated ok and sent to daemon, key_images: [" << key_images << "]"); BOOST_FOREACH(transfer_container::iterator it, selected_transfers) it->m_spent = true; - LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL - << "Commission: " << m_currency.formatAmount(fee + dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL + crypto::hash paymentId = null_hash; + std::vector transactionExtras; + if (parse_tx_extra(tx.extra, transactionExtras)) { + tx_extra_nonce extraNonce; + if (find_tx_extra_field_by_type(transactionExtras, extraNonce)) { + get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); + } + } + + for (auto& destination : dsts) { + Transfer transfer; + transfer.time = static_cast(time(NULL)); + transfer.output = true; + transfer.transactionHash = transactionHash; + transfer.amount = destination.amount; + transfer.fee = fee; + transfer.paymentId = paymentId; + transfer.hasAddress = true; + transfer.address = destination.addr; + transfer.blockIndex = 0; + transfer.unlockTime = unlock_time; + transfers.push_back(transfer); + } + + LOG_PRINT_L0("Transaction successfully sent. <" << transactionHash << ">" << ENDL + << "Commission: " << m_currency.formatAmount(fee+dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL << "Balance: " << m_currency.formatAmount(balance()) << ENDL << "Unlocked: " << m_currency.formatAmount(unlocked_balance()) << ENDL << "Please, wait for confirmation for your balance to be unlocked."); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de783cbb..ccd2ad4b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -46,9 +46,16 @@ namespace tools m_net_server.add_idle_handler([this](){ try { m_wallet.refresh(); - } catch (const std::exception& ex) { - LOG_ERROR("Exception at while refreshing, what=" << ex.what()); + } catch (const std::exception& e) { + LOG_ERROR("Exception while refreshing, what=" << e.what()); } + + try { + m_wallet.store(); + } catch (const std::exception& e) { + LOG_ERROR("Exception while storing, what=" << e.what()); + } + return true; }, 20000); @@ -192,5 +199,34 @@ namespace tools return true; } - //------------------------------------------------------------------------------------------------------------------------------ + + bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx) { + res.transfers.clear(); + const std::vector& transfers = m_wallet.getTransfers(); + for (const tools::wallet2::Transfer& transfer : transfers) { + wallet_rpc::Transfer transfer2; + transfer2.time = transfer.time; + transfer2.output = transfer.output; + transfer2.transactionHash = epee::string_tools::pod_to_hex(transfer.transactionHash); + transfer2.amount = transfer.amount; + transfer2.fee = transfer.fee; + transfer2.paymentId = transfer.paymentId == cryptonote::null_hash ? "" : epee::string_tools::pod_to_hex(transfer.paymentId); + transfer2.address = transfer.hasAddress ? getAccountAddressAsStr(m_wallet.currency().publicAddressBase58Prefix(), transfer.address) : ""; + transfer2.blockIndex = transfer.blockIndex; + transfer2.unlockTime = transfer.unlockTime; + res.transfers.push_back(transfer2); + } + + return true; + } + + bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx) { + res.height = m_wallet.get_blockchain_current_height(); + return true; + } + + bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx) { + m_wallet.reset(); + return true; + } } diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 09f09889..680324f2 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -48,10 +48,13 @@ namespace tools BEGIN_URI_MAP2() BEGIN_JSON_RPC_MAP("/json_rpc") - MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) - MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) - MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) - MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) + MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) + MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) + MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) + MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) + MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS) + MAP_JON_RPC_WE("get_height", on_get_height, wallet_rpc::COMMAND_RPC_GET_HEIGHT) + MAP_JON_RPC_WE("reset", on_reset, wallet_rpc::COMMAND_RPC_RESET) END_JSON_RPC_MAP() END_URI_MAP2() @@ -60,6 +63,9 @@ namespace tools bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx); bool handle_command_line(const boost::program_options::variables_map& vm); diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index 7649332f..8ef30557 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -136,5 +136,71 @@ namespace wallet_rpc END_KV_SERIALIZE_MAP() }; }; + + struct Transfer { + uint64_t time; + bool output; + std::string transactionHash; + uint64_t amount; + uint64_t fee; + std::string paymentId; + std::string address; + uint64_t blockIndex; + uint64_t unlockTime; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(time) + KV_SERIALIZE(output) + KV_SERIALIZE(transactionHash) + KV_SERIALIZE(amount) + KV_SERIALIZE(fee) + KV_SERIALIZE(paymentId) + KV_SERIALIZE(address) + KV_SERIALIZE(blockIndex) + KV_SERIALIZE(unlockTime) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_TRANSFERS { + struct request { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response { + std::list transfers; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(transfers) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_HEIGHT { + struct request { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response { + uint64_t height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_RESET { + struct request { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; } } diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index c01551ff..d87dffe3 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -591,37 +591,14 @@ bool gen_block_has_invalid_tx::generate(std::vector& events) c bool gen_block_is_too_big::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); + generator.defaultMajorVersion = m_blockMajorVersion; - // Creating a huge miner_tx, it will have a lot of outs - MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - static const size_t tx_out_count = m_currency.blockGrantedFullRewardZone() / 2; - uint64_t amount = get_outs_money_amount(miner_tx); - uint64_t portion = amount / tx_out_count; - uint64_t remainder = amount % tx_out_count; - TransactionOutputTarget target = miner_tx.vout[0].target; - miner_tx.vout.clear(); - for (size_t i = 0; i < tx_out_count; ++i) - { - TransactionOutput o; - o.amount = portion; - o.target = target; - miner_tx.vout.push_back(o); - } - if (0 < remainder) - { - TransactionOutput o; - o.amount = remainder; - o.target = target; - miner_tx.vout.push_back(o); - } - - // Block reward will be incorrect, as it must be reduced if cumulative block size is very big, - // but in this test it doesn't matter Block blk_1; - if (!generator.constructBlockManually(blk_1, blk_0, miner_account, - test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx)) + if (!generator.constructMaxSizeBlock(blk_1, blk_0, miner_account)) { return false; + } + blk_1.minerTx.extra.resize(blk_1.minerTx.extra.size() + 1); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 351b30b0..eed583b5 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -297,7 +297,12 @@ struct gen_block_has_invalid_tx : public CheckBlockPurged struct gen_block_is_too_big : public CheckBlockPurged { gen_block_is_too_big(uint8_t blockMajorVersion) - : CheckBlockPurged(1, blockMajorVersion) {} + : CheckBlockPurged(1, blockMajorVersion) { + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); + m_currency = currencyBuilder.currency(); + } bool generate(std::vector& events) const; };