Merge pull request #3489

47fdb74 WalletApi: getMultisigInfo entry for gui wallets... (naughtyfox)
47fdb74 Refactored: work with wallet api statuses to make setting and getting operations atomic along with error strings (naughtyfox)
This commit is contained in:
luigi1111 2018-05-31 14:34:34 -05:00
commit 3a373c3d3a
No known key found for this signature in database
GPG key ID: F4ACA0183641E010
7 changed files with 482 additions and 256 deletions

View file

@ -34,6 +34,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "common/base58.h"
#include <memory>
#include <vector>
@ -102,6 +103,11 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
}
// Commit tx
else {
auto multisigState = m_wallet.multisig();
if (multisigState.isMultisig && m_signers.size() < multisigState.threshold) {
throw runtime_error("Not enough signers to send multisig transaction");
}
m_wallet.pauseRefresh();
while (!m_pending_tx.empty()) {
auto & ptx = m_pending_tx.back();
@ -188,6 +194,53 @@ std::vector<std::set<uint32_t>> PendingTransactionImpl::subaddrIndices() const
return result;
}
std::string PendingTransactionImpl::multisigSignData() {
try {
if (!m_wallet.multisig().isMultisig) {
throw std::runtime_error("wallet is not multisig");
}
auto cipher = m_wallet.m_wallet->save_multisig_tx(m_pending_tx);
return epee::string_tools::buff_to_hex_nodelimer(cipher);
} catch (const std::exception& e) {
m_status = Status_Error;
m_errorString = std::string(tr("Couldn't multisig sign data: ")) + e.what();
}
return std::string();
}
void PendingTransactionImpl::signMultisigTx() {
try {
std::vector<crypto::hash> ignore;
tools::wallet2::multisig_tx_set txSet;
txSet.m_ptx = m_pending_tx;
txSet.m_signers = m_signers;
if (!m_wallet.m_wallet->sign_multisig_tx(txSet, ignore)) {
throw std::runtime_error("couldn't sign multisig transaction");
}
std::swap(m_pending_tx, txSet.m_ptx);
std::swap(m_signers, txSet.m_signers);
} catch (const std::exception& e) {
m_status = Status_Error;
m_errorString = std::string(tr("Couldn't sign multisig transaction: ")) + e.what();
}
}
std::vector<std::string> PendingTransactionImpl::signersKeys() const {
std::vector<std::string> keys;
keys.reserve(m_signers.size());
for (const auto& signer: m_signers) {
keys.emplace_back(tools::base58::encode(cryptonote::t_serializable_object_to_blob(signer)));
}
return keys;
}
}
namespace Bitmonero = Monero;

View file

@ -55,6 +55,10 @@ public:
std::vector<std::set<uint32_t>> subaddrIndices() const;
// TODO: continue with interface;
std::string multisigSignData();
void signMultisigTx();
std::vector<std::string> signersKeys() const;
private:
friend class WalletImpl;
WalletImpl &m_wallet;
@ -62,6 +66,7 @@ private:
int m_status;
std::string m_errorString;
std::vector<tools::wallet2::pending_tx> m_pending_tx;
std::unordered_set<crypto::public_key> m_signers;
};

File diff suppressed because it is too large Load diff

View file

@ -83,6 +83,7 @@ public:
// void setListener(Listener *) {}
int status() const;
std::string errorString() const;
void statusWithErrorString(int& status, std::string& errorString) const override;
bool setPassword(const std::string &password);
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const;
std::string integratedAddress(const std::string &payment_id) const;
@ -126,6 +127,14 @@ public:
std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const;
void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label);
MultisigState multisig() const override;
std::string getMultisigInfo() const override;
std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override;
bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
bool exportMultisigImages(std::string& images) override;
size_t importMultisigImages(const std::vector<std::string>& images) override;
PendingTransaction* restoreMultisigTransaction(const std::string& signData) override;
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
@ -174,6 +183,9 @@ public:
private:
void clearStatus() const;
void setStatusError(const std::string& message) const;
void setStatusCritical(const std::string& message) const;
void setStatus(int status, const std::string& message) const;
void refreshThreadFunc();
void doRefresh();
bool daemonSynced() const;
@ -191,7 +203,8 @@ private:
friend class SubaddressAccountImpl;
tools::wallet2 * m_wallet;
mutable std::atomic<int> m_status;
mutable boost::mutex m_statusMutex;
mutable int m_status;
mutable std::string m_errorString;
std::string m_password;
TransactionHistoryImpl * m_history;

View file

@ -100,6 +100,30 @@ struct PendingTransaction
virtual uint64_t txCount() const = 0;
virtual std::vector<uint32_t> subaddrAccount() const = 0;
virtual std::vector<std::set<uint32_t>> subaddrIndices() const = 0;
/**
* @brief multisigSignData
* @return encoded multisig transaction with signers' keys.
* Transfer this data to another wallet participant to sign it.
* Assumed use case is:
* 1. Initiator:
* auto data = pendingTransaction->multisigSignData();
* 2. Signer1:
* pendingTransaction = wallet->restoreMultisigTransaction(data);
* pendingTransaction->signMultisigTx();
* auto signed = pendingTransaction->multisigSignData();
* 3. Signer2:
* pendingTransaction = wallet->restoreMultisigTransaction(signed);
* pendingTransaction->signMultisigTx();
* pendingTransaction->commit();
*/
virtual std::string multisigSignData() = 0;
virtual void signMultisigTx() = 0;
/**
* @brief signersKeys
* @return vector of base58-encoded signers' public keys
*/
virtual std::vector<std::string> signersKeys() const = 0;
};
/**
@ -291,6 +315,15 @@ struct SubaddressAccount
virtual void refresh() = 0;
};
struct MultisigState {
MultisigState() : isMultisig(false), isReady(false), threshold(0), total(0) {}
bool isMultisig;
bool isReady;
uint32_t threshold;
uint32_t total;
};
struct WalletListener
{
virtual ~WalletListener() = 0;
@ -358,9 +391,11 @@ struct Wallet
virtual std::string getSeedLanguage() const = 0;
virtual void setSeedLanguage(const std::string &arg) = 0;
//! returns wallet status (Status_Ok | Status_Error)
virtual int status() const = 0;
virtual int status() const = 0; //deprecated: use safe alternative statusWithErrorString
//! in case error status, returns error string
virtual std::string errorString() const = 0;
virtual std::string errorString() const = 0; //deprecated: use safe alternative statusWithErrorString
//! returns both error and error string atomically. suggested to use in instead of status() and errorString()
virtual void statusWithErrorString(int& status, std::string& errorString) const = 0;
virtual bool setPassword(const std::string &password) = 0;
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
std::string mainAddress() const { return address(0, 0); }
@ -629,6 +664,48 @@ struct Wallet
*/
virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
/**
* @brief multisig - returns current state of multisig wallet creation process
* @return MultisigState struct
*/
virtual MultisigState multisig() const = 0;
/**
* @brief getMultisigInfo
* @return serialized and signed multisig info string
*/
virtual std::string getMultisigInfo() const = 0;
/**
* @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets
* @param info - vector of multisig infos from other participants obtained with getMulitisInfo call
* @param threshold - number of required signers to make valid transaction. Must be equal to number of participants (N) or N - 1
* @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info
*/
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
/**
* @brief finalizeMultisig - finalizes N - 1 / N multisig wallets creation
* @param extraMultisigInfo - wallet participants' extra multisig info obtained with makeMultisig call
* @return true if success
*/
virtual bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) = 0;
/**
* @brief exportMultisigImages - exports transfers' key images
* @param images - output paramter for hex encoded array of images
* @return true if success
*/
virtual bool exportMultisigImages(std::string& images) = 0;
/**
* @brief importMultisigImages - imports other participants' multisig images
* @param images - array of hex encoded arrays of images obtained with exportMultisigImages
* @return number of imported images
*/
virtual size_t importMultisigImages(const std::vector<std::string>& images) = 0;
/**
* @brief restoreMultisigTransaction creates PendingTransaction from signData
* @param signData encrypted unsigned transaction. Obtained with PendingTransaction::multisigSignData
* @return PendingTransaction
*/
virtual PendingTransaction* restoreMultisigTransaction(const std::string& signData) = 0;
/*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
* \param dst_addr destination address as string

View file

@ -5023,7 +5023,7 @@ bool wallet2::save_multisig_tx(const multisig_tx_set &txs, const std::string &fi
return epee::file_io_utils::save_string_to_file(filename, ciphertext);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
wallet2::multisig_tx_set wallet2::make_multisig_tx_set(const std::vector<pending_tx>& ptx_vector) const
{
multisig_tx_set txs;
txs.m_ptx = ptx_vector;
@ -5035,8 +5035,12 @@ std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
}
txs.m_signers.insert(get_multisig_signer_public_key());
return txs;
}
return save_multisig_tx(txs);
std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
{
return save_multisig_tx(make_multisig_tx_set(ptx_vector));
}
//----------------------------------------------------------------------------------------------------
bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)

View file

@ -688,6 +688,7 @@ namespace tools
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);
multisig_tx_set make_multisig_tx_set(const std::vector<pending_tx>& ptx_vector) const;
// 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