diff options
Diffstat (limited to 'src/wallet/wallet2.h')
-rw-r--r-- | src/wallet/wallet2.h | 225 |
1 files changed, 201 insertions, 24 deletions
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 866b95853..399287c3e 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -59,8 +59,6 @@ #include "common/password.h" #include "node_rpc_proxy.h" -#include <iostream> - #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" @@ -146,10 +144,6 @@ namespace tools RefreshDefault = RefreshOptimizeCoinbase, }; - private: - wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshDefault), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false),m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} - - public: static const char* tr(const char* str); static bool has_testnet_option(const boost::program_options::variables_map& vm); @@ -168,9 +162,33 @@ namespace tools //! Just parses variables. static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter); - static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only); + static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key); + + wallet2(bool testnet = false, bool restricted = false); + + struct multisig_info + { + struct LR + { + rct::key m_L; + rct::key m_R; + + BEGIN_SERIALIZE_OBJECT() + FIELD(m_L) + FIELD(m_R) + END_SERIALIZE() + }; - wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshDefault), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), m_light_wallet(false), m_light_wallet_scanned_block_height(0), m_light_wallet_blockchain_height(0), m_light_wallet_connected(false), m_light_wallet_balance(0), m_light_wallet_unlocked_balance(0) {} + crypto::public_key m_signer; + std::vector<LR> m_LR; + std::vector<crypto::key_image> m_partial_key_images; // one per key the participant has + + BEGIN_SERIALIZE_OBJECT() + FIELD(m_signer) + FIELD(m_LR) + FIELD(m_partial_key_images) + END_SERIALIZE() + }; struct tx_scan_info_t { @@ -201,6 +219,9 @@ namespace tools bool m_key_image_known; size_t m_pk_index; cryptonote::subaddress_index m_subaddr_index; + bool m_key_image_partial; + std::vector<rct::key> m_multisig_k; + std::vector<multisig_info> m_multisig_info; // one per other participant bool is_rct() const { return m_rct; } uint64_t amount() const { return m_amount; } @@ -221,6 +242,9 @@ namespace tools FIELD(m_key_image_known) FIELD(m_pk_index) FIELD(m_subaddr_index) + FIELD(m_key_image_partial) + FIELD(m_multisig_k) + FIELD(m_multisig_info) END_SERIALIZE() }; @@ -310,6 +334,15 @@ namespace tools typedef std::vector<transfer_details> transfer_container; typedef std::unordered_multimap<crypto::hash, payment_details> payment_container; + struct multisig_sig + { + rct::rctSig sigs; + crypto::public_key ignore; + std::unordered_set<rct::key> used_L; + std::unordered_set<crypto::public_key> signing_keys; + rct::multisig_out msout; + }; + // The convention for destinations is: // dests does not include change // splitted_dsts (in construction_data) does @@ -324,6 +357,7 @@ namespace tools crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; std::vector<cryptonote::tx_destination_entry> dests; + std::vector<multisig_sig> multisig_sigs; tx_construction_data construction_data; @@ -339,6 +373,7 @@ namespace tools FIELD(additional_tx_keys) FIELD(dests) FIELD(construction_data) + FIELD(multisig_sigs) END_SERIALIZE() }; @@ -356,6 +391,17 @@ namespace tools std::vector<crypto::key_image> key_images; }; + struct multisig_tx_set + { + std::vector<pending_tx> m_ptx; + std::unordered_set<crypto::public_key> m_signers; + + BEGIN_SERIALIZE_OBJECT() + FIELD(m_ptx) + FIELD(m_signers) + END_SERIALIZE() + }; + struct keys_file_data { crypto::chacha8_iv iv; @@ -398,7 +444,7 @@ namespace tools * \param two_random Whether it is a non-deterministic wallet * \return The secret key of the generated wallet */ - crypto::secret_key generate(const std::string& wallet, const std::string& password, + crypto::secret_key generate(const std::string& wallet, const epee::wipeable_string& password, const crypto::secret_key& recovery_param = crypto::secret_key(), bool recover = false, bool two_random = false); /*! @@ -408,7 +454,7 @@ namespace tools * \param viewkey view secret key * \param spendkey spend secret key */ - void generate(const std::string& wallet, const std::string& password, + void generate(const std::string& wallet, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); /*! @@ -417,31 +463,78 @@ namespace tools * \param password Password of wallet file * \param viewkey view secret key */ - void generate(const std::string& wallet, const std::string& password, + void generate(const std::string& wallet, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address, const crypto::secret_key& viewkey = crypto::secret_key()); /*! + * \brief Creates a multisig wallet + * \return empty if done, non empty if we need to send another string + * to other participants + */ + std::string make_multisig(const epee::wipeable_string &password, + const std::vector<std::string> &info, + uint32_t threshold); + /*! + * \brief Creates a multisig wallet + * \return empty if done, non empty if we need to send another string + * to other participants + */ + std::string make_multisig(const epee::wipeable_string &password, + const std::vector<crypto::secret_key> &view_keys, + const std::vector<crypto::public_key> &spend_keys, + uint32_t threshold); + /*! + * \brief Finalizes creation of a multisig wallet + */ + bool finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info); + /*! + * \brief Finalizes creation of a multisig wallet + */ + bool finalize_multisig(const epee::wipeable_string &password, std::unordered_set<crypto::public_key> pkeys, std::vector<crypto::public_key> signers); + /*! + * Get a packaged multisig information string + */ + std::string get_multisig_info() const; + /*! + * Verifies and extracts keys from a packaged multisig information string + */ + static bool verify_multisig_info(const std::string &data, crypto::secret_key &skey, crypto::public_key &pkey); + /*! + * Verifies and extracts keys from a packaged multisig information string + */ + static bool verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer); + /*! + * Export multisig info + * This will generate and remember new k values + */ + cryptonote::blobdata export_multisig(); + /*! + * Import a set of multisig info from multisig partners + * \return the number of inputs which were imported + */ + size_t import_multisig(std::vector<cryptonote::blobdata> info); + /*! * \brief Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there) * \param wallet_name Name of wallet file (should exist) * \param password Password for wallet file */ - void rewrite(const std::string& wallet_name, const std::string& password); - void write_watch_only_wallet(const std::string& wallet_name, const std::string& password); - void load(const std::string& wallet, const std::string& password); + void rewrite(const std::string& wallet_name, const epee::wipeable_string& password); + void write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password); + void load(const std::string& wallet, const epee::wipeable_string& password); void store(); /*! * \brief store_to - stores wallet to another file(s), deleting old ones * \param path - path to the wallet file (keys and address filenames will be generated based on this filename) * \param password - password to protect new wallet (TODO: probably better save the password in the wallet object?) */ - void store_to(const std::string &path, const std::string &password); + void store_to(const std::string &path, const epee::wipeable_string &password); std::string path() const; /*! * \brief verifies given password is correct for default wallet keys file */ - bool verify_password(const std::string& password) const; + bool verify_password(const epee::wipeable_string& password) const; cryptonote::account_base& get_account(){return m_account;} const cryptonote::account_base& get_account()const{return m_account;} @@ -466,7 +559,7 @@ namespace tools * \brief Checks if deterministic wallet */ bool is_deterministic() const; - bool get_seed(std::string& electrum_words, const std::string &passphrase = std::string()) const; + bool get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; /*! * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. @@ -499,6 +592,7 @@ namespace tools void expand_subaddresses(const cryptonote::subaddress_index& index); std::string get_subaddress_label(const cryptonote::subaddress_index& index) const; void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label); + void set_subaddress_lookahead(size_t major, size_t minor); /*! * \brief Tells if the wallet file is deprecated. */ @@ -514,6 +608,8 @@ namespace tools bool testnet() const { return m_testnet; } bool restricted() const { return m_restricted; } bool watch_only() const { return m_watch_only; } + bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; + bool has_multisig_partial_key_images() const; // locked & unlocked balance of given or current subaddress account uint64_t balance(uint32_t subaddr_index_major) const; @@ -541,6 +637,10 @@ namespace tools void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector<pending_tx>& ptx_vector); bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename); + std::string save_multisig_tx(multisig_tx_set txs); + bool save_multisig_tx(const multisig_tx_set &txs, const std::string &filename); + std::string save_multisig_tx(const std::vector<pending_tx>& ptx_vector); + bool save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename); // load unsigned tx from file and sign it. Takes confirmation callback as argument. Used by the cli wallet bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector<wallet2::pending_tx> &ptx, std::function<bool(const unsigned_tx_set&)> accept_func = NULL, bool export_raw = false); // sign unsigned tx. Takes unsigned_tx_set as argument. Used by GUI @@ -553,6 +653,11 @@ namespace tools std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon); + bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); + bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); + bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func); + bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids); + bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids); std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon); bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); void get_transfers(wallet2::transfer_container& incoming_transfers) const; @@ -726,6 +831,7 @@ namespace tools bool delete_address_book_row(std::size_t row_id); uint64_t get_num_rct_outputs(); + size_t get_num_transfer_details() const { return m_transfers.size(); } const transfer_details &get_transfer_details(size_t idx) const; void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); @@ -831,6 +937,11 @@ namespace tools void set_attribute(const std::string &key, const std::string &value); std::string get_attribute(const std::string &key) const; + crypto::public_key get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const; + crypto::public_key get_multisig_signer_public_key() const; + crypto::public_key get_multisig_signing_public_key(size_t idx) const; + crypto::public_key get_multisig_signing_public_key(const crypto::secret_key &skey) const; + private: /*! * \brief Stores wallet information to wallet file. @@ -839,13 +950,13 @@ namespace tools * \param watch_only true to save only view key, false to save both spend and view keys * \return Whether it was successful. */ - bool store_keys(const std::string& keys_file_name, const std::string& password, bool watch_only = false); + bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); /*! * \brief Load wallet information from wallet file. * \param keys_file_name Name of wallet file * \param password Password of wallet file */ - bool load_keys(const std::string& keys_file_name, const std::string& password); + bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); @@ -866,7 +977,6 @@ namespace tools void check_genesis(const crypto::hash& genesis_hash) const; //throws bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const; crypto::hash get_payment_id(const pending_tx &ptx) const; - crypto::hash8 get_short_payment_id(const pending_tx &ptx) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; uint64_t get_upper_transaction_size_limit(); @@ -878,12 +988,16 @@ namespace tools void set_unspent(size_t idx); void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; - bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; void scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs); void trim_hashchain(); + crypto::key_image get_multisig_composite_key_image(size_t n) const; + rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const; + rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const; + rct::key get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const; + void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n); cryptonote::account_base m_account; boost::optional<epee::net_utils::http::login> m_daemon_login; @@ -912,6 +1026,8 @@ namespace tools std::unordered_map<std::string, std::string> m_attributes; std::vector<tools::wallet2::address_book_row> m_address_book; uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value + const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info; + const std::vector<std::vector<rct::key>> *m_multisig_rescan_k; std::atomic<bool> m_run; @@ -923,6 +1039,9 @@ namespace tools std::string seed_language; /*!< Language of the mnemonics (seed). */ bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ bool m_watch_only; /*!< no spend key */ + bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */ + uint32_t m_multisig_threshold; + std::vector<crypto::public_key> m_multisig_signers; bool m_always_confirm_transfers; bool m_print_ring_members; bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */ @@ -941,6 +1060,7 @@ namespace tools bool m_is_initialized; NodeRPCProxy m_node_rpc_proxy; std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; + size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; // Light wallet bool m_light_wallet; /* sends view key to daemon for scanning */ @@ -958,7 +1078,10 @@ namespace tools }; } BOOST_CLASS_VERSION(tools::wallet2, 22) -BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 8) +BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9) +BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) +BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) +BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2) BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7) @@ -967,7 +1090,8 @@ BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17) BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 2) -BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 2) +BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) +BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 0) namespace boost { @@ -1005,6 +1129,12 @@ namespace boost { x.m_subaddr_index = {}; } + if (ver < 9) + { + x.m_key_image_partial = false; + x.m_multisig_k.clear(); + x.m_multisig_info.clear(); + } } template <class Archive> @@ -1078,6 +1208,36 @@ namespace boost return; } a & x.m_subaddr_index; + if (ver < 9) + { + initialize_transfer_details(a, x, ver); + return; + } + a & x.m_multisig_info; + a & x.m_multisig_k; + a & x.m_key_image_partial; + } + + template <class Archive> + inline void serialize(Archive &a, tools::wallet2::multisig_info::LR &x, const boost::serialization::version_type ver) + { + a & x.m_L; + a & x.m_R; + } + + template <class Archive> + inline void serialize(Archive &a, tools::wallet2::multisig_info &x, const boost::serialization::version_type ver) + { + a & x.m_signer; + a & x.m_LR; + a & x.m_partial_key_images; + } + + template <class Archive> + inline void serialize(Archive &a, tools::wallet2::multisig_tx_set &x, const boost::serialization::version_type ver) + { + a & x.m_ptx; + a & x.m_signers; } template <class Archive> @@ -1256,6 +1416,16 @@ namespace boost } template <class Archive> + inline void serialize(Archive &a, tools::wallet2::multisig_sig &x, const boost::serialization::version_type ver) + { + a & x.sigs; + a & x.ignore; + a & x.used_L; + a & x.signing_keys; + a & x.msout; + } + + template <class Archive> inline void serialize(Archive &a, tools::wallet2::pending_tx &x, const boost::serialization::version_type ver) { a & x.tx; @@ -1283,6 +1453,9 @@ namespace boost if (ver < 2) return; a & x.selected_transfers; + if (ver < 3) + return; + a & x.multisig_sigs; } } } @@ -1358,6 +1531,8 @@ namespace tools // throw if attempting a transaction with no destinations THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); + THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs"); + uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t needed_money = fee; @@ -1460,6 +1635,7 @@ namespace tools src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx); src.real_output = interted_it - src.outputs.begin(); src.real_output_in_tx_index = td.m_internal_output_index; + src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); detail::print_source_entry(src); ++i; } @@ -1486,7 +1662,8 @@ namespace tools crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys); + rct::multisig_out msout; + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); |