Offline transaction signing
This commit is contained in:
parent
b6a21de70b
commit
cebe0ef944
|
@ -35,6 +35,8 @@ set(wallet_api_sources
|
|||
wallet_manager.cpp
|
||||
transaction_info.cpp
|
||||
transaction_history.cpp
|
||||
transaction_construction_info.cpp
|
||||
pending_transaction_info.cpp
|
||||
pending_transaction.cpp
|
||||
utils.cpp
|
||||
address_book.cpp
|
||||
|
@ -50,6 +52,8 @@ set(wallet_api_private_headers
|
|||
wallet_manager.h
|
||||
transaction_info.h
|
||||
transaction_history.h
|
||||
transaction_construction_info.h
|
||||
pending_transaction_info.h
|
||||
pending_transaction.h
|
||||
common_defines.h
|
||||
address_book.h
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "common/base58.h"
|
||||
#include "string_coding.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
@ -263,4 +264,50 @@ std::vector<std::string> PendingTransactionImpl::signersKeys() const {
|
|||
return keys;
|
||||
}
|
||||
|
||||
std::string PendingTransactionImpl::unsignedTxToBin() const {
|
||||
return m_wallet.m_wallet->dump_tx_to_str(m_pending_tx);
|
||||
}
|
||||
|
||||
std::string PendingTransactionImpl::unsignedTxToBase64() const {
|
||||
return epee::string_encoding::base64_encode(m_wallet.m_wallet->dump_tx_to_str(m_pending_tx));
|
||||
}
|
||||
|
||||
std::string PendingTransactionImpl::signedTxToHex(int index) const {
|
||||
auto index_ = static_cast<unsigned>(index);
|
||||
if (index < 0 || index_ >= m_pending_tx.size()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(m_pending_tx[index_].tx));
|
||||
}
|
||||
|
||||
size_t PendingTransactionImpl::signedTxSize(int index) const {
|
||||
auto index_ = static_cast<unsigned>(index);
|
||||
if (index < 0 || index_ >= m_pending_tx.size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cryptonote::tx_to_blob(m_pending_tx[index_].tx).size();
|
||||
}
|
||||
|
||||
PendingTransactionInfo * PendingTransactionImpl::transaction(int index) const {
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
auto index_ = static_cast<unsigned>(index);
|
||||
return index_ < m_pending_tx_info.size() ? m_pending_tx_info[index_] : nullptr;
|
||||
}
|
||||
|
||||
void PendingTransactionImpl::refresh() {
|
||||
for (auto t : m_pending_tx_info)
|
||||
delete t;
|
||||
m_pending_tx_info.clear();
|
||||
|
||||
for (const auto& p : m_pending_tx)
|
||||
m_pending_tx_info.push_back(new PendingTransactionInfoImpl(m_wallet, p));
|
||||
}
|
||||
|
||||
std::vector<PendingTransactionInfo*> PendingTransactionImpl::getAll() const {
|
||||
return m_pending_tx_info;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include "wallet/wallet2.h"
|
||||
#include "pending_transaction_info.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -53,6 +54,13 @@ public:
|
|||
uint64_t txCount() const override;
|
||||
std::vector<uint32_t> subaddrAccount() const override;
|
||||
std::vector<std::set<uint32_t>> subaddrIndices() const override;
|
||||
std::string unsignedTxToBin() const override;
|
||||
std::string unsignedTxToBase64() const override;
|
||||
std::string signedTxToHex(int index) const override;
|
||||
size_t signedTxSize(int index) const override;
|
||||
void refresh() override;
|
||||
std::vector<PendingTransactionInfo*> getAll() const override;
|
||||
PendingTransactionInfo * transaction(int index) const override;
|
||||
// TODO: continue with interface;
|
||||
|
||||
std::string multisigSignData() override;
|
||||
|
@ -66,6 +74,7 @@ private:
|
|||
int m_status;
|
||||
std::string m_errorString;
|
||||
std::vector<tools::wallet2::pending_tx> m_pending_tx;
|
||||
std::vector<PendingTransactionInfo*> m_pending_tx_info;
|
||||
std::unordered_set<crypto::public_key> m_signers;
|
||||
std::vector<std::string> m_tx_device_aux;
|
||||
std::vector<crypto::key_image> m_key_images;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#include "pending_transaction_info.h"
|
||||
#include "transaction_construction_info.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
PendingTransactionInfo::~PendingTransactionInfo() = default;
|
||||
|
||||
PendingTransactionInfoImpl::PendingTransactionInfoImpl(WalletImpl &wallet, const tools::wallet2::pending_tx & ptx)
|
||||
: m_wallet(wallet)
|
||||
, m_ptx(ptx)
|
||||
, m_constructionData(new TransactionConstructionInfoImpl(wallet, ptx.construction_data))
|
||||
{
|
||||
}
|
||||
|
||||
PendingTransactionInfoImpl::~PendingTransactionInfoImpl() = default;
|
||||
|
||||
uint64_t PendingTransactionInfoImpl::fee() const
|
||||
{
|
||||
return m_ptx.fee;
|
||||
}
|
||||
|
||||
uint64_t PendingTransactionInfoImpl::dust() const
|
||||
{
|
||||
return m_ptx.dust;
|
||||
}
|
||||
|
||||
bool PendingTransactionInfoImpl::dustAddedToFee() const
|
||||
{
|
||||
return m_ptx.dust_added_to_fee;
|
||||
}
|
||||
|
||||
std::string PendingTransactionInfoImpl::txKey() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(m_ptx.tx_key);
|
||||
}
|
||||
|
||||
TransactionConstructionInfo * PendingTransactionInfoImpl::constructionData() const {
|
||||
return m_constructionData;
|
||||
}
|
||||
|
||||
// TransactionConstructionInfo::Output TransactionConstructionInfoImpl::change() const {
|
||||
// return Output(
|
||||
// {m_ptx.change_dts.amount, m_ptx.change_dts.address(m_wallet.m_wallet->nettype(), crypto::hash())});
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef WOWLET_PENDING_TX_H
|
||||
#define WOWLET_PENDING_TX_H
|
||||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include "wallet/wallet2.h"
|
||||
#include "wallet.h"
|
||||
#include <string>
|
||||
|
||||
namespace Monero {
|
||||
|
||||
class PendingTransactionImpl;
|
||||
|
||||
class PendingTransactionInfoImpl : public PendingTransactionInfo
|
||||
{
|
||||
public:
|
||||
PendingTransactionInfoImpl(WalletImpl &wallet, const tools::wallet2::pending_tx & ptx);
|
||||
~PendingTransactionInfoImpl() override;
|
||||
|
||||
uint64_t fee() const override;
|
||||
uint64_t dust() const override;
|
||||
bool dustAddedToFee() const override;
|
||||
std::string txKey() const override;
|
||||
TransactionConstructionInfo *constructionData() const override;
|
||||
// Output change() const override;
|
||||
|
||||
private:
|
||||
friend class WalletImpl;
|
||||
WalletImpl &m_wallet;
|
||||
tools::wallet2::pending_tx m_ptx;
|
||||
TransactionConstructionInfo *m_constructionData;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif //FEATHER_PENDING_TX_H
|
|
@ -0,0 +1,63 @@
|
|||
#include "transaction_construction_info.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
TransactionConstructionInfo::~TransactionConstructionInfo() = default;
|
||||
|
||||
TransactionConstructionInfo::Input::Input(uint64_t _amount, const std::string &_pubkey)
|
||||
: amount(_amount), pubkey(_pubkey) {}
|
||||
|
||||
TransactionConstructionInfo::Output::Output(uint64_t _amount, const std::string &_address)
|
||||
: amount(_amount), address(_address) {}
|
||||
|
||||
TransactionConstructionInfoImpl::TransactionConstructionInfoImpl(WalletImpl &wallet, const tools::wallet2::tx_construction_data & txcd)
|
||||
: m_wallet(wallet)
|
||||
, m_txcd(txcd) {}
|
||||
|
||||
TransactionConstructionInfoImpl::~TransactionConstructionInfoImpl() = default;
|
||||
|
||||
uint64_t TransactionConstructionInfoImpl::unlockTime() const {
|
||||
return m_txcd.unlock_time;
|
||||
}
|
||||
|
||||
std::set<std::uint32_t> TransactionConstructionInfoImpl::subaddressIndices() const {
|
||||
return m_txcd.subaddr_indices;
|
||||
}
|
||||
|
||||
std::vector<std::string> TransactionConstructionInfoImpl::subaddresses() const {
|
||||
std::vector<std::string> s;
|
||||
auto major = m_txcd.subaddr_account;
|
||||
for (const auto &minor : m_txcd.subaddr_indices) {
|
||||
s.push_back(m_wallet.m_wallet->get_subaddress_as_str({major, minor}));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
uint64_t TransactionConstructionInfoImpl::minMixinCount() const {
|
||||
uint64_t min_mixin = -1;
|
||||
for (const auto &source : m_txcd.sources) {
|
||||
size_t mixin = source.outputs.size() - 1;
|
||||
if (mixin < min_mixin)
|
||||
min_mixin = mixin;
|
||||
}
|
||||
|
||||
return min_mixin;
|
||||
}
|
||||
|
||||
std::vector<TransactionConstructionInfo::Input> TransactionConstructionInfoImpl::inputs() const {
|
||||
std::vector<Input> inputs;
|
||||
for (const auto &i : m_txcd.sources) {
|
||||
inputs.emplace_back(i.amount, epee::string_tools::pod_to_hex(i.real_out_tx_key));
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
std::vector<TransactionConstructionInfo::Output> TransactionConstructionInfoImpl::outputs() const {
|
||||
std::vector<Output> outputs;
|
||||
for (const auto &o : m_txcd.splitted_dsts) {
|
||||
outputs.emplace_back(o.amount, o.address(m_wallet.m_wallet->nettype(), crypto::hash()));
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef WOWLET_TRANSACTION_CONSTRUCTION_INFO_H
|
||||
#define WOWLET_TRANSACTION_CONSTRUCTION_INFO_H
|
||||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include "wallet/wallet2.h"
|
||||
#include "wallet.h"
|
||||
#include <string>
|
||||
|
||||
namespace Monero {
|
||||
|
||||
class TransactionConstructionInfoImpl : public TransactionConstructionInfo
|
||||
{
|
||||
public:
|
||||
TransactionConstructionInfoImpl(WalletImpl &wallet, const tools::wallet2::tx_construction_data & ptx);
|
||||
~TransactionConstructionInfoImpl() override;
|
||||
|
||||
uint64_t unlockTime() const override;
|
||||
std::set<std::uint32_t> subaddressIndices() const override;
|
||||
std::vector<std::string> subaddresses() const override;
|
||||
uint64_t minMixinCount() const override;
|
||||
std::vector<Input> inputs() const override;
|
||||
std::vector<Output> outputs() const override;
|
||||
|
||||
private:
|
||||
friend class WalletImpl;
|
||||
WalletImpl &m_wallet;
|
||||
tools::wallet2::tx_construction_data m_txcd;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //WOWLET_TRANSACTION_CONSTRUCTION_INFO_H
|
|
@ -31,6 +31,7 @@
|
|||
#include "unsigned_transaction.h"
|
||||
#include "wallet.h"
|
||||
#include "common_defines.h"
|
||||
#include "transaction_construction_info.h"
|
||||
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
|
@ -315,4 +316,24 @@ uint64_t UnsignedTransactionImpl::minMixinCount() const
|
|||
return min_mixin;
|
||||
}
|
||||
|
||||
TransactionConstructionInfo * UnsignedTransactionImpl::transaction(int index) const {
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
auto index_ = static_cast<unsigned>(index);
|
||||
return index_ < m_constructionInfo.size() ? m_constructionInfo[index_] : nullptr;
|
||||
}
|
||||
|
||||
void UnsignedTransactionImpl::refresh() {
|
||||
for (auto t : m_constructionInfo)
|
||||
delete t;
|
||||
m_constructionInfo.clear();
|
||||
|
||||
for (const auto& p : m_unsigned_tx_set.txes)
|
||||
m_constructionInfo.push_back(new TransactionConstructionInfoImpl(m_wallet, p));
|
||||
}
|
||||
|
||||
std::vector<TransactionConstructionInfo*> UnsignedTransactionImpl::getAll() const {
|
||||
return m_constructionInfo;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -55,6 +55,9 @@ public:
|
|||
bool sign(const std::string &signedFileName) override;
|
||||
std::string confirmationMessage() const override {return m_confirmationMessage;}
|
||||
uint64_t minMixinCount() const override;
|
||||
void refresh() override;
|
||||
std::vector<TransactionConstructionInfo*> getAll() const override;
|
||||
TransactionConstructionInfo * transaction(int index) const override;
|
||||
|
||||
private:
|
||||
// Callback function to check all loaded tx's and generate confirmationMessage
|
||||
|
@ -67,7 +70,7 @@ private:
|
|||
std::string m_errorString;
|
||||
tools::wallet2::unsigned_tx_set m_unsigned_tx_set;
|
||||
std::string m_confirmationMessage;
|
||||
std::vector<TransactionConstructionInfo*> m_constructionInfo;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "subaddress_account.h"
|
||||
#include "common_defines.h"
|
||||
#include "common/util.h"
|
||||
#include "string_coding.h"
|
||||
|
||||
#include "mnemonics/electrum-words.h"
|
||||
#include "mnemonics/english.h"
|
||||
|
@ -1147,6 +1148,48 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
|
|||
return transaction;
|
||||
}
|
||||
|
||||
UnsignedTransaction *WalletImpl::loadUnsignedTxFromStr(const std::string &unsigned_tx) {
|
||||
clearStatus();
|
||||
|
||||
UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this);
|
||||
if (!m_wallet->parse_unsigned_tx_from_str(unsigned_tx, transaction->m_unsigned_tx_set)) {
|
||||
setStatusError(tr("Failed to load unsigned transactions"));
|
||||
transaction->m_status = UnsignedTransaction::Status::Status_Error;
|
||||
transaction->m_errorString = errorString();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
// Check tx data and construct confirmation message
|
||||
std::string extra_message;
|
||||
if (!transaction->m_unsigned_tx_set.transfers.second.empty())
|
||||
extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.second.size()).str();
|
||||
transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message);
|
||||
setStatus(transaction->status(), transaction->errorString());
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
UnsignedTransaction *WalletImpl::loadUnsignedTxFromBase64Str(const std::string &unsigned_tx_base64) {
|
||||
clearStatus();
|
||||
|
||||
std::string decoded_tx = epee::string_encoding::base64_decode(unsigned_tx_base64);
|
||||
|
||||
return this->loadUnsignedTxFromStr(decoded_tx);
|
||||
}
|
||||
|
||||
PendingTransaction *WalletImpl::loadSignedTx(const std::string &signed_filename) {
|
||||
clearStatus();
|
||||
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
|
||||
|
||||
if (!m_wallet->load_tx(signed_filename, transaction->m_pending_tx)) {
|
||||
setStatusError(tr("Failed to load unsigned transactions"));
|
||||
return transaction;
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
bool WalletImpl::submitTransaction(const string &fileName) {
|
||||
clearStatus();
|
||||
std::unique_ptr<PendingTransactionImpl> transaction(new PendingTransactionImpl(*this));
|
||||
|
|
|
@ -48,6 +48,8 @@ class AddressBookImpl;
|
|||
class SubaddressImpl;
|
||||
class SubaddressAccountImpl;
|
||||
struct Wallet2CallbackImpl;
|
||||
class PendingTransactionInfoImpl;
|
||||
class TransactionConstructionInfoImpl;
|
||||
|
||||
class WalletImpl : public Wallet
|
||||
{
|
||||
|
@ -165,6 +167,9 @@ public:
|
|||
virtual PendingTransaction * createSweepUnmixableTransaction() override;
|
||||
bool submitTransaction(const std::string &fileName) override;
|
||||
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override;
|
||||
virtual UnsignedTransaction * loadUnsignedTxFromStr(const std::string &unsigned_tx) override;
|
||||
virtual UnsignedTransaction * loadUnsignedTxFromBase64Str(const std::string &unsigned_tx) override;
|
||||
virtual PendingTransaction * loadSignedTx(const std::string &signed_filename) override;
|
||||
bool exportKeyImages(const std::string &filename, bool all = false) override;
|
||||
bool importKeyImages(const std::string &filename) override;
|
||||
bool exportOutputs(const std::string &filename, bool all = false) override;
|
||||
|
@ -245,6 +250,8 @@ private:
|
|||
friend class AddressBookImpl;
|
||||
friend class SubaddressImpl;
|
||||
friend class SubaddressAccountImpl;
|
||||
friend class PendingTransactionInfoImpl;
|
||||
friend class TransactionConstructionInfoImpl;
|
||||
|
||||
std::unique_ptr<tools::wallet2> m_wallet;
|
||||
mutable boost::mutex m_statusMutex;
|
||||
|
|
|
@ -66,6 +66,47 @@ enum NetworkType : uint8_t {
|
|||
bool set;
|
||||
};
|
||||
|
||||
/*
|
||||
* @brief Transaction construction data
|
||||
*/
|
||||
struct TransactionConstructionInfo
|
||||
{
|
||||
struct Input {
|
||||
Input(uint64_t _amount, const std::string &_pubkey);
|
||||
const uint64_t amount;
|
||||
const std::string pubkey;
|
||||
};
|
||||
|
||||
struct Output {
|
||||
Output(uint64_t _amount, const std::string &_address);
|
||||
const uint64_t amount;
|
||||
const std::string address;
|
||||
};
|
||||
|
||||
virtual ~TransactionConstructionInfo() = 0;
|
||||
virtual uint64_t unlockTime() const = 0;
|
||||
virtual std::set<std::uint32_t> subaddressIndices() const = 0;
|
||||
virtual std::vector<std::string> subaddresses() const = 0;
|
||||
virtual uint64_t minMixinCount() const = 0;
|
||||
virtual std::vector<Input> inputs() const = 0;
|
||||
virtual std::vector<Output> outputs() const = 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* @brief Detailed pending transaction information
|
||||
*/
|
||||
struct PendingTransactionInfo
|
||||
{
|
||||
virtual ~PendingTransactionInfo() = 0;
|
||||
virtual uint64_t fee() const = 0;
|
||||
virtual uint64_t dust() const = 0;
|
||||
virtual bool dustAddedToFee() const = 0;
|
||||
virtual std::string txKey() const = 0;
|
||||
virtual TransactionConstructionInfo * constructionData() const = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Transaction-like interface for sending money
|
||||
*/
|
||||
|
@ -101,6 +142,13 @@ 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;
|
||||
virtual std::string unsignedTxToBin() const = 0;
|
||||
virtual std::string unsignedTxToBase64() const = 0;
|
||||
virtual std::string signedTxToHex(int index) const = 0;
|
||||
virtual size_t signedTxSize(int index) const = 0;
|
||||
virtual PendingTransactionInfo * transaction(int index) const = 0;
|
||||
virtual void refresh() = 0;
|
||||
virtual std::vector<PendingTransactionInfo*> getAll() const = 0;
|
||||
|
||||
/**
|
||||
* @brief multisigSignData
|
||||
|
@ -160,6 +208,9 @@ struct UnsignedTransaction
|
|||
* return - true on success
|
||||
*/
|
||||
virtual bool sign(const std::string &signedFileName) = 0;
|
||||
virtual void refresh() = 0;
|
||||
virtual std::vector<TransactionConstructionInfo*> getAll() const = 0;
|
||||
virtual TransactionConstructionInfo * transaction(int index) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -882,7 +933,27 @@ struct Wallet
|
|||
* after object returned
|
||||
*/
|
||||
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0;
|
||||
|
||||
|
||||
/*!
|
||||
* \brief loadUnsignedTx - creates transaction from unsigned tx string
|
||||
* \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
virtual UnsignedTransaction * loadUnsignedTxFromStr(const std::string &unsigned_tx) = 0;
|
||||
|
||||
/*!
|
||||
* \brief loadUnsignedTx - creates transaction from unsigned base64 encoded tx string
|
||||
* \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
virtual UnsignedTransaction * loadUnsignedTxFromBase64Str(const std::string &unsigned_tx_base64) = 0;
|
||||
|
||||
/*!
|
||||
* \brief loadSignedTx - creates transaction from signed tx file
|
||||
* \return - PendingTransaction object.
|
||||
*/
|
||||
virtual PendingTransaction * loadSignedTx(const std::string &signed_filename) = 0;
|
||||
|
||||
/*!
|
||||
* \brief submitTransaction - submits transaction in signed tx file
|
||||
* \return - true on success
|
||||
|
|
Loading…
Reference in New Issue