aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet2.h')
-rw-r--r--src/wallet/wallet2.h201
1 files changed, 190 insertions, 11 deletions
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index be8315468..399287c3e 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -144,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);
@@ -166,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 epee::wipeable_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) : 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) {}
+ 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()
+ };
+
+ 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
{
@@ -199,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; }
@@ -219,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()
};
@@ -308,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
@@ -322,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;
@@ -337,6 +373,7 @@ namespace tools
FIELD(additional_tx_keys)
FIELD(dests)
FIELD(construction_data)
+ FIELD(multisig_sigs)
END_SERIALIZE()
};
@@ -354,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;
@@ -419,6 +467,53 @@ namespace tools
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
@@ -497,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.
*/
@@ -512,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;
@@ -539,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
@@ -551,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;
@@ -724,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);
@@ -829,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.
@@ -864,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();
@@ -876,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;
@@ -910,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;
@@ -921,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 */
@@ -939,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 */
@@ -956,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)
@@ -965,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
{
@@ -1003,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>
@@ -1076,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>
@@ -1254,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;
@@ -1281,6 +1453,9 @@ namespace boost
if (ver < 2)
return;
a & x.selected_transfers;
+ if (ver < 3)
+ return;
+ a & x.multisig_sigs;
}
}
}
@@ -1356,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;
@@ -1458,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;
}
@@ -1484,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);