mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-16 13:01:19 +02:00
Merge 006740b6f6 into merged_master (Bitcoin PR bitcoin/bitcoin#25721)
This commit is contained in:
commit
49d368ea6b
25 changed files with 282 additions and 160 deletions
|
|
@ -121,6 +121,7 @@ BITCOIN_TESTS =\
|
|||
test/random_tests.cpp \
|
||||
test/rbf_tests.cpp \
|
||||
test/rest_tests.cpp \
|
||||
test/result_tests.cpp \
|
||||
test/reverselock_tests.cpp \
|
||||
test/rpc_tests.cpp \
|
||||
test/sanity_tests.cpp \
|
||||
|
|
|
|||
|
|
@ -45,11 +45,8 @@ static void BenchUnloadWallet(std::shared_ptr<CWallet>&& wallet)
|
|||
|
||||
static void AddTx(CWallet& wallet)
|
||||
{
|
||||
const auto& dest = wallet.GetNewDestination(OutputType::BECH32, "");
|
||||
assert(dest.HasRes());
|
||||
|
||||
CMutableTransaction mtx;
|
||||
mtx.vout.push_back({::policyAsset, COIN, GetScriptForDestination(dest.GetObj())});
|
||||
mtx.vout.push_back({::policyAsset, COIN, GetScriptForDestination(*Assert(wallet.GetNewDestination(OutputType::BECH32, "")))});
|
||||
mtx.vin.push_back(CTxIn());
|
||||
|
||||
wallet.AddToWallet(MakeTransactionRef(mtx), TxStateInactive{});
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public:
|
|||
virtual std::string getWalletName() = 0;
|
||||
|
||||
// Get a new address.
|
||||
virtual BResult<CTxDestination> getNewDestination(const OutputType type, const std::string label, bool add_blinding_key = false) = 0;
|
||||
virtual util::Result<CTxDestination> getNewDestination(const OutputType type, const std::string label, bool add_blinding_key = false) = 0;
|
||||
|
||||
//! Get public key.
|
||||
virtual bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) = 0;
|
||||
|
|
@ -141,7 +141,7 @@ public:
|
|||
virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0;
|
||||
|
||||
//! Create transaction.
|
||||
virtual BResult<CTransactionRef> createTransaction(const std::vector<wallet::CRecipient>& recipients,
|
||||
virtual util::Result<CTransactionRef> createTransaction(const std::vector<wallet::CRecipient>& recipients,
|
||||
const wallet::CCoinControl& coin_control,
|
||||
bool sign,
|
||||
int& change_pos,
|
||||
|
|
@ -333,7 +333,7 @@ public:
|
|||
virtual std::string getWalletDir() = 0;
|
||||
|
||||
//! Restore backup wallet
|
||||
virtual BResult<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) = 0;
|
||||
virtual util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) = 0;
|
||||
|
||||
//! Return available wallets in wallet directory.
|
||||
virtual std::vector<std::string> listWalletDir() = 0;
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
|
|||
return QString();
|
||||
}
|
||||
}
|
||||
strAddress = EncodeDestination(op_dest.GetObj());
|
||||
strAddress = EncodeDestination(*op_dest);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -394,8 +394,8 @@ void RestoreWalletActivity::restore(const fs::path& backup_file, const std::stri
|
|||
QTimer::singleShot(0, worker(), [this, backup_file, wallet_name] {
|
||||
auto wallet{node().walletLoader().restoreWallet(backup_file, wallet_name, m_warning_message)};
|
||||
|
||||
m_error_message = wallet ? bilingual_str{} : wallet.GetError();
|
||||
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj());
|
||||
m_error_message = util::ErrorString(wallet);
|
||||
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(*wallet));
|
||||
|
||||
QTimer::singleShot(0, this, &RestoreWalletActivity::finish);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
auto& newTx = transaction.getWtx();
|
||||
std::vector<CAmount> out_amounts;
|
||||
const auto& res = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, blind_details);
|
||||
newTx = res ? res.GetObj() : nullptr;
|
||||
newTx = res ? *res : nullptr;
|
||||
transaction.setTransactionFee(nFeeRequired);
|
||||
if (fSubtractFeeFromAmount && newTx) {
|
||||
if(blind_details) {
|
||||
|
|
@ -257,7 +257,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
{
|
||||
return SendCoinsReturn(AmountWithFeeExceedsBalance);
|
||||
}
|
||||
Q_EMIT message(tr("Send Coins"), QString::fromStdString(res.GetError().translated),
|
||||
Q_EMIT message(tr("Send Coins"), QString::fromStdString(util::ErrorString(res).translated),
|
||||
CClientUIInterface::MSG_ERROR);
|
||||
return TransactionCreationFailed;
|
||||
}
|
||||
|
|
|
|||
96
src/test/result_tests.cpp
Normal file
96
src/test/result_tests.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <util/result.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
inline bool operator==(const bilingual_str& a, const bilingual_str& b)
|
||||
{
|
||||
return a.original == b.original && a.translated == b.translated;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const bilingual_str& s)
|
||||
{
|
||||
return os << "bilingual_str('" << s.original << "' , '" << s.translated << "')";
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(result_tests)
|
||||
|
||||
struct NoCopy {
|
||||
NoCopy(int n) : m_n{std::make_unique<int>(n)} {}
|
||||
std::unique_ptr<int> m_n;
|
||||
};
|
||||
|
||||
bool operator==(const NoCopy& a, const NoCopy& b)
|
||||
{
|
||||
return *a.m_n == *b.m_n;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const NoCopy& o)
|
||||
{
|
||||
return os << "NoCopy(" << *o.m_n << ")";
|
||||
}
|
||||
|
||||
util::Result<int> IntFn(int i, bool success)
|
||||
{
|
||||
if (success) return i;
|
||||
return util::Error{Untranslated(strprintf("int %i error.", i))};
|
||||
}
|
||||
|
||||
util::Result<bilingual_str> StrFn(bilingual_str s, bool success)
|
||||
{
|
||||
if (success) return s;
|
||||
return util::Error{strprintf(Untranslated("str %s error."), s.original)};
|
||||
}
|
||||
|
||||
util::Result<NoCopy> NoCopyFn(int i, bool success)
|
||||
{
|
||||
if (success) return {i};
|
||||
return util::Error{Untranslated(strprintf("nocopy %i error.", i))};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ExpectResult(const util::Result<T>& result, bool success, const bilingual_str& str)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(bool(result), success);
|
||||
BOOST_CHECK_EQUAL(util::ErrorString(result), str);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void ExpectSuccess(const util::Result<T>& result, const bilingual_str& str, Args&&... args)
|
||||
{
|
||||
ExpectResult(result, true, str);
|
||||
BOOST_CHECK_EQUAL(result.has_value(), true);
|
||||
BOOST_CHECK_EQUAL(result.value(), T{std::forward<Args>(args)...});
|
||||
BOOST_CHECK_EQUAL(&result.value(), &*result);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void ExpectFail(const util::Result<T>& result, const bilingual_str& str)
|
||||
{
|
||||
ExpectResult(result, false, str);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(check_returned)
|
||||
{
|
||||
ExpectSuccess(IntFn(5, true), {}, 5);
|
||||
ExpectFail(IntFn(5, false), Untranslated("int 5 error."));
|
||||
ExpectSuccess(NoCopyFn(5, true), {}, 5);
|
||||
ExpectFail(NoCopyFn(5, false), Untranslated("nocopy 5 error."));
|
||||
ExpectSuccess(StrFn(Untranslated("S"), true), {}, Untranslated("S"));
|
||||
ExpectFail(StrFn(Untranslated("S"), false), Untranslated("str S error."));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(check_value_or)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(IntFn(10, true).value_or(20), 10);
|
||||
BOOST_CHECK_EQUAL(IntFn(10, false).value_or(20), 20);
|
||||
BOOST_CHECK_EQUAL(NoCopyFn(10, true).value_or(20), 10);
|
||||
BOOST_CHECK_EQUAL(NoCopyFn(10, false).value_or(20), 20);
|
||||
BOOST_CHECK_EQUAL(StrFn(Untranslated("A"), true).value_or(Untranslated("B")), Untranslated("A"));
|
||||
BOOST_CHECK_EQUAL(StrFn(Untranslated("A"), false).value_or(Untranslated("B")), Untranslated("B"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <outputtype.h>
|
||||
#include <script/standard.h>
|
||||
#ifdef ENABLE_WALLET
|
||||
#include <util/check.h>
|
||||
#include <util/translation.h>
|
||||
#include <wallet/wallet.h>
|
||||
#endif
|
||||
|
|
@ -20,10 +21,7 @@ const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqq
|
|||
std::string getnewaddress(CWallet& w)
|
||||
{
|
||||
constexpr auto output_type = OutputType::BECH32;
|
||||
auto op_dest = w.GetNewDestination(output_type, "");
|
||||
assert(op_dest.HasRes());
|
||||
|
||||
return EncodeDestination(op_dest.GetObj());
|
||||
return EncodeDestination(*Assert(w.GetNewDestination(output_type, "")));
|
||||
}
|
||||
|
||||
#endif // ENABLE_WALLET
|
||||
|
|
|
|||
|
|
@ -5,45 +5,80 @@
|
|||
#ifndef BITCOIN_UTIL_RESULT_H
|
||||
#define BITCOIN_UTIL_RESULT_H
|
||||
|
||||
#include <attributes.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <variant>
|
||||
|
||||
/*
|
||||
* 'BResult' is a generic class useful for wrapping a return object
|
||||
* (in case of success) or propagating the error cause.
|
||||
*/
|
||||
template<class T>
|
||||
class BResult {
|
||||
namespace util {
|
||||
|
||||
struct Error {
|
||||
bilingual_str message;
|
||||
};
|
||||
|
||||
//! The util::Result class provides a standard way for functions to return
|
||||
//! either error messages or result values.
|
||||
//!
|
||||
//! It is intended for high-level functions that need to report error strings to
|
||||
//! end users. Lower-level functions that don't need this error-reporting and
|
||||
//! only need error-handling should avoid util::Result and instead use standard
|
||||
//! classes like std::optional, std::variant, and std::tuple, or custom structs
|
||||
//! and enum types to return function results.
|
||||
//!
|
||||
//! Usage examples can be found in \example ../test/result_tests.cpp, but in
|
||||
//! general code returning `util::Result<T>` values is very similar to code
|
||||
//! returning `std::optional<T>` values. Existing functions returning
|
||||
//! `std::optional<T>` can be updated to return `util::Result<T>` and return
|
||||
//! error strings usually just replacing `return std::nullopt;` with `return
|
||||
//! util::Error{error_string};`.
|
||||
template <class T>
|
||||
class Result
|
||||
{
|
||||
private:
|
||||
std::variant<bilingual_str, T> m_variant;
|
||||
|
||||
template <typename FT>
|
||||
friend bilingual_str ErrorString(const Result<FT>& result);
|
||||
|
||||
public:
|
||||
BResult() : m_variant{Untranslated("")} {}
|
||||
BResult(T obj) : m_variant{std::move(obj)} {}
|
||||
BResult(bilingual_str error) : m_variant{std::move(error)} {}
|
||||
Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {}
|
||||
Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {}
|
||||
|
||||
/* Whether the function succeeded or not */
|
||||
bool HasRes() const { return std::holds_alternative<T>(m_variant); }
|
||||
|
||||
/* In case of success, the result object */
|
||||
const T& GetObj() const {
|
||||
assert(HasRes());
|
||||
return std::get<T>(m_variant);
|
||||
}
|
||||
T ReleaseObj()
|
||||
//! std::optional methods, so functions returning optional<T> can change to
|
||||
//! return Result<T> with minimal changes to existing code, and vice versa.
|
||||
bool has_value() const noexcept { return m_variant.index() == 1; }
|
||||
const T& value() const LIFETIMEBOUND
|
||||
{
|
||||
assert(HasRes());
|
||||
return std::move(std::get<T>(m_variant));
|
||||
assert(has_value());
|
||||
return std::get<1>(m_variant);
|
||||
}
|
||||
|
||||
/* In case of failure, the error cause */
|
||||
const bilingual_str& GetError() const {
|
||||
assert(!HasRes());
|
||||
return std::get<bilingual_str>(m_variant);
|
||||
T& value() LIFETIMEBOUND
|
||||
{
|
||||
assert(has_value());
|
||||
return std::get<1>(m_variant);
|
||||
}
|
||||
|
||||
explicit operator bool() const { return HasRes(); }
|
||||
template <class U>
|
||||
T value_or(U&& default_value) const&
|
||||
{
|
||||
return has_value() ? value() : std::forward<U>(default_value);
|
||||
}
|
||||
template <class U>
|
||||
T value_or(U&& default_value) &&
|
||||
{
|
||||
return has_value() ? std::move(value()) : std::forward<U>(default_value);
|
||||
}
|
||||
explicit operator bool() const noexcept { return has_value(); }
|
||||
const T* operator->() const LIFETIMEBOUND { return &value(); }
|
||||
const T& operator*() const LIFETIMEBOUND { return value(); }
|
||||
T* operator->() LIFETIMEBOUND { return &value(); }
|
||||
T& operator*() LIFETIMEBOUND { return value(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bilingual_str ErrorString(const Result<T>& result)
|
||||
{
|
||||
return result ? bilingual_str{} : std::get<0>(result.m_variant);
|
||||
}
|
||||
} // namespace util
|
||||
|
||||
#endif // BITCOIN_UTIL_RESULT_H
|
||||
|
|
|
|||
|
|
@ -238,11 +238,11 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
|
|||
constexpr int RANDOM_CHANGE_POSITION = -1;
|
||||
auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, new_coin_control, false);
|
||||
if (!res) {
|
||||
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + res.GetError());
|
||||
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + util::ErrorString(res));
|
||||
return Result::WALLET_ERROR;
|
||||
}
|
||||
|
||||
const auto& txr = res.GetObj();
|
||||
const auto& txr = *res;
|
||||
// Write back new fee if successful
|
||||
new_fee = txr.fee;
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ public:
|
|||
void abortRescan() override { m_wallet->AbortRescan(); }
|
||||
bool backupWallet(const std::string& filename) override { return m_wallet->BackupWallet(filename); }
|
||||
std::string getWalletName() override { return m_wallet->GetName(); }
|
||||
BResult<CTxDestination> getNewDestination(const OutputType type, const std::string label, bool add_blinding_key = false) override
|
||||
util::Result<CTxDestination> getNewDestination(const OutputType type, const std::string label, bool add_blinding_key = false) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
return m_wallet->GetNewDestination(type, label, add_blinding_key);
|
||||
|
|
@ -263,7 +263,7 @@ public:
|
|||
LOCK(m_wallet->cs_wallet);
|
||||
return m_wallet->ListLockedCoins(outputs);
|
||||
}
|
||||
BResult<CTransactionRef> createTransaction(const std::vector<CRecipient>& recipients,
|
||||
util::Result<CTransactionRef> createTransaction(const std::vector<CRecipient>& recipients,
|
||||
const CCoinControl& coin_control,
|
||||
bool sign,
|
||||
int& change_pos,
|
||||
|
|
@ -271,10 +271,10 @@ public:
|
|||
BlindDetails* blind_details) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
const auto& res = CreateTransaction(*m_wallet, recipients, change_pos,
|
||||
auto res = CreateTransaction(*m_wallet, recipients, change_pos,
|
||||
coin_control, sign, blind_details);
|
||||
if (!res) return res.GetError();
|
||||
const auto& txr = res.GetObj();
|
||||
if (!res) return util::Error{util::ErrorString(res)};
|
||||
const auto& txr = *res;
|
||||
fee = txr.fee;
|
||||
change_pos = txr.change_pos;
|
||||
|
||||
|
|
@ -585,12 +585,12 @@ public:
|
|||
options.require_existing = true;
|
||||
return MakeWallet(m_context, LoadWallet(m_context, name, true /* load_on_start */, options, status, error, warnings));
|
||||
}
|
||||
BResult<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) override
|
||||
util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) override
|
||||
{
|
||||
DatabaseStatus status;
|
||||
bilingual_str error;
|
||||
BResult<std::unique_ptr<Wallet>> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings))};
|
||||
if (!wallet) return error;
|
||||
util::Result<std::unique_ptr<Wallet>> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings))};
|
||||
if (!wallet) return util::Error{error};
|
||||
return wallet;
|
||||
}
|
||||
std::string getWalletDir() override
|
||||
|
|
|
|||
|
|
@ -72,10 +72,10 @@ RPCHelpMan getnewaddress()
|
|||
|
||||
auto op_dest = pwallet->GetNewDestination(output_type, label, add_blinding_key);
|
||||
if (!op_dest) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, op_dest.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
|
||||
}
|
||||
|
||||
return EncodeDestination(op_dest.GetObj());
|
||||
return EncodeDestination(*op_dest);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -125,9 +125,9 @@ RPCHelpMan getrawchangeaddress()
|
|||
bool add_blinding_key = force_blind || gArgs.GetBoolArg("-blindedaddresses", g_con_elementsmode);
|
||||
auto op_dest = pwallet->GetNewChangeDestination(output_type, add_blinding_key);
|
||||
if (!op_dest) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, op_dest.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
|
||||
}
|
||||
return EncodeDestination(op_dest.GetObj());
|
||||
return EncodeDestination(*op_dest);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,10 +191,10 @@ RPCHelpMan getpeginaddress()
|
|||
// Use native witness destination
|
||||
auto dest = pwallet->GetNewDestination(OutputType::BECH32, "");
|
||||
if (!dest) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, dest.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(dest).original);
|
||||
}
|
||||
|
||||
CScript dest_script = GetScriptForDestination(dest.GetObj());
|
||||
CScript dest_script = GetScriptForDestination(*dest);
|
||||
|
||||
// Also add raw scripts to index to recognize later.
|
||||
spk_man->AddCScript(dest_script);
|
||||
|
|
@ -834,7 +834,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
|||
// Generate a new key that is added to wallet
|
||||
auto wpkhash = pwallet->GetNewDestination(OutputType::BECH32, "");
|
||||
if (!wpkhash) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, wpkhash.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(wpkhash).original);
|
||||
}
|
||||
|
||||
// Get value for output
|
||||
|
|
@ -844,7 +844,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
|||
}
|
||||
|
||||
// one wallet output and one fee output
|
||||
mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, value, GetScriptForDestination(wpkhash.GetObj())));
|
||||
mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, value, GetScriptForDestination(*wpkhash)));
|
||||
mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, 0, CScript()));
|
||||
|
||||
// Estimate fee for transaction, decrement fee output(including witness data)
|
||||
|
|
@ -1377,17 +1377,17 @@ static CTransactionRef SendGenerationTransaction(const CScript& asset_script, co
|
|||
FeeCalculation fee_calc_out;
|
||||
CCoinControl dummy_control;
|
||||
BlindDetails blind_details;
|
||||
BResult<CreatedTransactionResult> txr = CreateTransaction(*pwallet, vecSend, RANDOM_CHANGE_POSITION,
|
||||
util::Result<CreatedTransactionResult> txr = CreateTransaction(*pwallet, vecSend, RANDOM_CHANGE_POSITION,
|
||||
dummy_control, true, &blind_details, issuance_details);
|
||||
if (!txr) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
|
||||
}
|
||||
nFeeRequired = txr.GetObj().fee;
|
||||
nFeeRequired = (*txr).fee;
|
||||
|
||||
mapValue_t map_value;
|
||||
pwallet->CommitTransaction(txr.GetObj().tx, std::move(map_value), {} /* orderForm */, &blind_details);
|
||||
pwallet->CommitTransaction((*txr).tx, std::move(map_value), {} /* orderForm */, &blind_details);
|
||||
|
||||
return txr.GetObj().tx;
|
||||
return (*txr).tx;
|
||||
}
|
||||
|
||||
RPCHelpMan issueasset()
|
||||
|
|
@ -1447,8 +1447,8 @@ RPCHelpMan issueasset()
|
|||
// Generate a new key that is added to wallet
|
||||
bilingual_str error;
|
||||
CPubKey newKey;
|
||||
BResult<CTxDestination> asset_dest;
|
||||
BResult<CTxDestination> token_dest;
|
||||
util::Result<CTxDestination> asset_dest{util::Error{}};
|
||||
util::Result<CTxDestination> token_dest{util::Error{}};
|
||||
CScript asset_script;
|
||||
CScript token_script;
|
||||
CPubKey asset_dest_blindpub;
|
||||
|
|
@ -1457,18 +1457,18 @@ RPCHelpMan issueasset()
|
|||
if (nAmount > 0) {
|
||||
asset_dest = pwallet->GetNewDestination(OutputType::BECH32, "");
|
||||
if (!asset_dest) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, asset_dest.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(asset_dest).original);
|
||||
}
|
||||
asset_script = GetScriptForDestination(asset_dest.GetObj());
|
||||
asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(asset_dest.GetObj()));
|
||||
asset_script = GetScriptForDestination(*asset_dest);
|
||||
asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(*asset_dest));
|
||||
}
|
||||
if (nTokens > 0) {
|
||||
token_dest = pwallet->GetNewDestination(OutputType::BECH32, "");
|
||||
if (!token_dest) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, token_dest.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(token_dest).original);
|
||||
}
|
||||
token_script = GetScriptForDestination(token_dest.GetObj());
|
||||
token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(token_dest.GetObj()));
|
||||
token_script = GetScriptForDestination(*token_dest);
|
||||
token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(*token_dest));
|
||||
}
|
||||
|
||||
CAsset dummyasset;
|
||||
|
|
@ -1562,19 +1562,19 @@ RPCHelpMan reissueasset()
|
|||
bilingual_str error;
|
||||
auto asset_dest = pwallet->GetNewDestination(OutputType::BECH32, "");
|
||||
if (!asset_dest) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, asset_dest.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(asset_dest).original);
|
||||
}
|
||||
CPubKey asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(asset_dest.GetObj()));
|
||||
CPubKey asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(*asset_dest));
|
||||
|
||||
// Add destination for tokens we are moving
|
||||
auto token_dest = pwallet->GetNewDestination(OutputType::BECH32, "");
|
||||
if (!token_dest) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, token_dest.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(token_dest).original);
|
||||
}
|
||||
CPubKey token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(token_dest.GetObj()));
|
||||
CPubKey token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(*token_dest));
|
||||
|
||||
// Attempt a send.
|
||||
CTransactionRef tx_ref = SendGenerationTransaction(GetScriptForDestination(asset_dest.GetObj()), asset_dest_blindpub, GetScriptForDestination(token_dest.GetObj()), token_dest_blindpub, nAmount, -1, &issuance_details, pwallet);
|
||||
CTransactionRef tx_ref = SendGenerationTransaction(GetScriptForDestination(*asset_dest), asset_dest_blindpub, GetScriptForDestination(*token_dest), token_dest_blindpub, nAmount, -1, &issuance_details, pwallet);
|
||||
CHECK_NONFATAL(!tx_ref->vin.empty());
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
|
|
|
|||
|
|
@ -174,14 +174,14 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
|
|||
if (blind_details) blind_details->ignore_blind_failure = ignore_blind_fail;
|
||||
auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, coin_control, true, blind_details.get());
|
||||
if (!res) {
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, res.GetError().original);
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, util::ErrorString(res).original);
|
||||
}
|
||||
const CTransactionRef& tx = res.GetObj().tx;
|
||||
const CTransactionRef& tx = res->tx;
|
||||
wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */, blind_details.get());
|
||||
if (verbose) {
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("txid", tx->GetHash().GetHex());
|
||||
entry.pushKV("fee_reason", StringForFeeReason(res.GetObj().fee_calc.reason));
|
||||
entry.pushKV("fee_reason", StringForFeeReason(res->fee_calc.reason));
|
||||
return entry;
|
||||
}
|
||||
return tx->GetHash().GetHex();
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ namespace wallet {
|
|||
//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details.
|
||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
|
||||
|
||||
BResult<CTxDestination> LegacyScriptPubKeyMan::GetNewDestination(const OutputType type)
|
||||
util::Result<CTxDestination> LegacyScriptPubKeyMan::GetNewDestination(const OutputType type)
|
||||
{
|
||||
if (LEGACY_OUTPUT_TYPES.count(type) == 0) {
|
||||
return _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types");;
|
||||
return util::Error{_("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types")};
|
||||
}
|
||||
assert(type != OutputType::BECH32M);
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ BResult<CTxDestination> LegacyScriptPubKeyMan::GetNewDestination(const OutputTyp
|
|||
// Generate a new key that is added to wallet
|
||||
CPubKey new_key;
|
||||
if (!GetKeyFromPool(new_key, type)) {
|
||||
return _("Error: Keypool ran out, please call keypoolrefill first");
|
||||
return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")};
|
||||
}
|
||||
LearnRelatedScripts(new_key, type);
|
||||
return GetDestinationForKey(new_key, type);
|
||||
|
|
@ -1659,11 +1659,11 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
|
|||
return set_address;
|
||||
}
|
||||
|
||||
BResult<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type)
|
||||
util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type)
|
||||
{
|
||||
// Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later
|
||||
if (!CanGetAddresses()) {
|
||||
return _("No addresses available");
|
||||
return util::Error{_("No addresses available")};
|
||||
}
|
||||
{
|
||||
LOCK(cs_desc_man);
|
||||
|
|
@ -1681,11 +1681,11 @@ BResult<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const Outpu
|
|||
std::vector<CScript> scripts_temp;
|
||||
if (m_wallet_descriptor.range_end <= m_max_cached_index && !TopUp(1)) {
|
||||
// We can't generate anymore keys
|
||||
return _("Error: Keypool ran out, please call keypoolrefill first");
|
||||
return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")};
|
||||
}
|
||||
if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.next_index, m_wallet_descriptor.cache, scripts_temp, out_keys)) {
|
||||
// We can't generate anymore keys
|
||||
return _("Error: Keypool ran out, please call keypoolrefill first");
|
||||
return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")};
|
||||
}
|
||||
|
||||
CTxDestination dest;
|
||||
|
|
@ -1771,11 +1771,11 @@ bool DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bo
|
|||
auto op_dest = GetNewDestination(type);
|
||||
index = m_wallet_descriptor.next_index - 1;
|
||||
if (op_dest) {
|
||||
address = op_dest.GetObj();
|
||||
address = *op_dest;
|
||||
} else {
|
||||
error = op_dest.GetError();
|
||||
error = util::ErrorString(op_dest);
|
||||
}
|
||||
return op_dest.HasRes();
|
||||
return bool(op_dest);
|
||||
}
|
||||
|
||||
void DescriptorScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, const CTxDestination& addr)
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ protected:
|
|||
public:
|
||||
explicit ScriptPubKeyMan(WalletStorage& storage) : m_storage(storage) {}
|
||||
virtual ~ScriptPubKeyMan() {};
|
||||
virtual BResult<CTxDestination> GetNewDestination(const OutputType type) { return Untranslated("Not supported"); }
|
||||
virtual util::Result<CTxDestination> GetNewDestination(const OutputType type) { return util::Error{Untranslated("Not supported")}; }
|
||||
virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; }
|
||||
|
||||
//! Check that the given decryption key is valid for this ScriptPubKeyMan, i.e. it decrypts all of the keys handled by it.
|
||||
|
|
@ -360,7 +360,7 @@ private:
|
|||
public:
|
||||
using ScriptPubKeyMan::ScriptPubKeyMan;
|
||||
|
||||
BResult<CTxDestination> GetNewDestination(const OutputType type) override;
|
||||
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
|
||||
isminetype IsMine(const CScript& script) const override;
|
||||
|
||||
bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
|
||||
|
|
@ -574,7 +574,7 @@ public:
|
|||
|
||||
mutable RecursiveMutex cs_desc_man;
|
||||
|
||||
BResult<CTxDestination> GetNewDestination(const OutputType type) override;
|
||||
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
|
||||
isminetype IsMine(const CScript& script) const override;
|
||||
|
||||
bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
|
||||
|
|
|
|||
|
|
@ -1052,7 +1052,7 @@ static bool fillBlindDetails(BlindDetails* det, CWallet* wallet, CMutableTransac
|
|||
return true;
|
||||
}
|
||||
|
||||
static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
||||
static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
||||
CWallet& wallet,
|
||||
const std::vector<CRecipient>& vecSend,
|
||||
int change_pos,
|
||||
|
|
@ -1198,7 +1198,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
if (dest_err.empty()) {
|
||||
dest_err = _("Keypool ran out, please call keypoolrefill first");
|
||||
}
|
||||
return _("Transaction needs a change address, but we can't generate it.") + Untranslated(" ") + dest_err;
|
||||
return util::Error{_("Transaction needs a change address, but we can't generate it.") + Untranslated(" ") + dest_err};
|
||||
}
|
||||
|
||||
CScript scriptChange = GetScriptForDestination(dest);
|
||||
|
|
@ -1248,11 +1248,11 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
|
||||
// provided one
|
||||
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
|
||||
return strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
|
||||
return util::Error{strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB))};
|
||||
}
|
||||
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
|
||||
// eventually allow a fallback fee
|
||||
return _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
|
||||
return util::Error{_("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.")};
|
||||
}
|
||||
|
||||
// Calculate the cost of change
|
||||
|
|
@ -1287,7 +1287,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
|
||||
if (recipient.asset == policyAsset && IsDust(txout, wallet.chain().relayDustFee()))
|
||||
{
|
||||
return _("Transaction amount too small");
|
||||
return util::Error{_("Transaction amount too small")};
|
||||
}
|
||||
txNew.vout.push_back(txout);
|
||||
|
||||
|
|
@ -1353,7 +1353,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
// Choose coins to use
|
||||
std::optional<SelectionResult> result = SelectCoins(wallet, available_coins, /*mapTargetValue=*/map_selection_target, coin_control, coin_selection_params);
|
||||
if (!result) {
|
||||
return _("Insufficient funds");
|
||||
return util::Error{_("Insufficient funds")};
|
||||
}
|
||||
TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->m_algo).c_str(), result->m_target, result->GetWaste(), result->GetSelectedValue());
|
||||
|
||||
|
|
@ -1387,7 +1387,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
if (nChangePosInOut == -1) {
|
||||
// randomly set policyasset change position
|
||||
} else if ((unsigned int)nChangePosInOut >= fixed_change_pos.size()) {
|
||||
return _("Transaction change output index out of range");
|
||||
return util::Error{_("Transaction change output index out of range")};
|
||||
} else {
|
||||
fixed_change_pos[nChangePosInOut] = policyAsset;
|
||||
}
|
||||
|
|
@ -1423,7 +1423,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
|
||||
const std::map<CAsset, std::pair<int, CScript>>::const_iterator itScript = mapScriptChange.find(asset);
|
||||
if (itScript == mapScriptChange.end()) {
|
||||
return Untranslated(strprintf("No change destination provided for asset %s", asset.GetHex()));
|
||||
return util::Error{Untranslated(strprintf("No change destination provided for asset %s", asset.GetHex()))};
|
||||
}
|
||||
CTxOut newTxOut(asset, change_and_fee, itScript->second.second);
|
||||
|
||||
|
|
@ -1587,14 +1587,14 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
CMutableTransaction tx_blinded = txNew;
|
||||
if (blind_details) {
|
||||
if (!fillBlindDetails(blind_details, &wallet, tx_blinded, selected_coins, error)) {
|
||||
return error;
|
||||
return util::Error{error};
|
||||
}
|
||||
txNew = tx_blinded; // sigh, `fillBlindDetails` may have modified txNew
|
||||
|
||||
int ret = BlindTransaction(blind_details->i_amount_blinds, blind_details->i_asset_blinds, blind_details->i_assets, blind_details->i_amounts, blind_details->o_amount_blinds, blind_details->o_asset_blinds, blind_details->o_pubkeys, issuance_asset_keys, issuance_token_keys, tx_blinded);
|
||||
assert(ret != -1);
|
||||
if (ret != blind_details->num_to_blind) {
|
||||
return _("Unable to blind the transaction properly. This should not happen.");
|
||||
return util::Error{_("Unable to blind the transaction properly. This should not happen.")};
|
||||
}
|
||||
|
||||
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(tx_blinded), &wallet, &coin_control);
|
||||
|
|
@ -1606,7 +1606,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
// Calculate the transaction fee
|
||||
int nBytes = tx_sizes.vsize;
|
||||
if (nBytes == -1) {
|
||||
return _("Missing solving data for estimating transaction size");
|
||||
return util::Error{_("Missing solving data for estimating transaction size")};
|
||||
}
|
||||
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
||||
|
||||
|
|
@ -1660,7 +1660,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
if (blind_details->num_to_blind < 2) {
|
||||
resetBlindDetails(blind_details, true /* don't wipe output data */);
|
||||
if (!fillBlindDetails(blind_details, &wallet, txNew, selected_coins, error)) {
|
||||
return error;
|
||||
return util::Error{error};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1680,7 +1680,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
if (!coin_selection_params.m_subtract_fee_outputs && fee_needed > map_change_and_fee.at(policyAsset) - change_amount) {
|
||||
wallet.WalletLogPrintf("ERROR: not enough coins to cover for fee (needed: %d, total: %d, change: %d)\n",
|
||||
fee_needed, map_change_and_fee.at(policyAsset), change_amount);
|
||||
return _("Could not cover fee");
|
||||
return util::Error{_("Could not cover fee")};
|
||||
}
|
||||
|
||||
// Update nFeeRet in case fee_needed changed due to dropping the change output
|
||||
|
|
@ -1704,7 +1704,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
{
|
||||
CAmount value = txout.nValue.GetAmount();
|
||||
if (recipient.asset != policyAsset) {
|
||||
return Untranslated(strprintf("Wallet does not support more than one type of fee at a time, therefore can not subtract fee from address amount, which is of a different asset id. fee asset: %s recipient asset: %s", policyAsset.GetHex(), recipient.asset.GetHex()));
|
||||
return util::Error{Untranslated(strprintf("Wallet does not support more than one type of fee at a time, therefore can not subtract fee from address amount, which is of a different asset id. fee asset: %s recipient asset: %s", policyAsset.GetHex(), recipient.asset.GetHex()))};
|
||||
}
|
||||
|
||||
value -= to_reduce / outputs_to_subtract_fee_from; // Subtract fee equally from each selected recipient
|
||||
|
|
@ -1718,9 +1718,9 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
// Error if this output is reduced to be below dust
|
||||
if (IsDust(txout, wallet.chain().relayDustFee())) {
|
||||
if (value < 0) {
|
||||
return _("The transaction amount is too small to pay the fee");
|
||||
return util::Error{_("The transaction amount is too small to pay the fee")};
|
||||
} else {
|
||||
return _("The transaction amount is too small to send after the fee has been deducted");
|
||||
return util::Error{_("The transaction amount is too small to send after the fee has been deducted")};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1736,7 +1736,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
if (maybe_change_asset) {
|
||||
auto used = mapScriptChange.extract(*maybe_change_asset);
|
||||
if (used.mapped().second == dummy_script) {
|
||||
return error;
|
||||
return util::Error{error};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1795,7 +1795,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
assert(ret != -1);
|
||||
if (ret != blind_details->num_to_blind) {
|
||||
wallet.WalletLogPrintf("ERROR: tried to blind %d outputs but only blinded %d\n", (int) blind_details->num_to_blind, (int) ret);
|
||||
return _("Unable to blind the transaction properly. This should not happen.");
|
||||
return util::Error{_("Unable to blind the transaction properly. This should not happen.")};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1813,7 +1813,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
|
||||
if (sign) {
|
||||
if (!wallet.SignTransaction(txNew)) {
|
||||
return _("Signing transaction failed");
|
||||
return util::Error{_("Signing transaction failed")};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1829,17 +1829,17 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
|
||||
(!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
|
||||
{
|
||||
return _("Transaction too large");
|
||||
return util::Error{_("Transaction too large")};
|
||||
}
|
||||
|
||||
if (nFeeRet > wallet.m_default_max_tx_fee) {
|
||||
return TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
|
||||
return util::Error{TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED)};
|
||||
}
|
||||
|
||||
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
|
||||
// Lastly, ensure this tx will pass the mempool's chain limits
|
||||
if (!wallet.chain().checkChainLimits(tx)) {
|
||||
return _("Transaction has too long of a mempool chain");
|
||||
return util::Error{_("Transaction has too long of a mempool chain")};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1860,7 +1860,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
|
|||
return CreatedTransactionResult(tx, nFeeRet, nChangePosInOut, feeCalc);
|
||||
}
|
||||
|
||||
BResult<CreatedTransactionResult> CreateTransaction(
|
||||
util::Result<CreatedTransactionResult> CreateTransaction(
|
||||
CWallet& wallet,
|
||||
const std::vector<CRecipient>& vecSend,
|
||||
int change_pos,
|
||||
|
|
@ -1870,27 +1870,27 @@ BResult<CreatedTransactionResult> CreateTransaction(
|
|||
const IssuanceDetails* issuance_details)
|
||||
{
|
||||
if (vecSend.empty()) {
|
||||
return _("Transaction must have at least one recipient");
|
||||
return util::Error{_("Transaction must have at least one recipient")};
|
||||
}
|
||||
|
||||
if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.nAmount < 0; })) {
|
||||
return _("Transaction amounts must not be negative");
|
||||
return util::Error{_("Transaction amounts must not be negative")};
|
||||
}
|
||||
|
||||
// ELEMENTS
|
||||
if (g_con_elementsmode) {
|
||||
if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.asset.IsNull(); })) {
|
||||
return _("No asset provided for recipient");
|
||||
return util::Error{_("No asset provided for recipient")};
|
||||
}
|
||||
}
|
||||
|
||||
LOCK(wallet.cs_wallet);
|
||||
|
||||
auto res = CreateTransactionInternal(wallet, vecSend, change_pos, coin_control, sign, blind_details, issuance_details);
|
||||
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), res.HasRes(),
|
||||
res ? res.GetObj().fee : 0, res ? res.GetObj().change_pos : 0);
|
||||
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), bool(res),
|
||||
res ? res->fee : 0, res ? res->change_pos : 0);
|
||||
if (!res) return res;
|
||||
const auto& txr_ungrouped = res.GetObj();
|
||||
const auto& txr_ungrouped = *res;
|
||||
// try with avoidpartialspends unless it's enabled already
|
||||
if (txr_ungrouped.fee > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
|
||||
TRACE1(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());
|
||||
|
|
@ -1898,9 +1898,7 @@ BResult<CreatedTransactionResult> CreateTransaction(
|
|||
tmp_cc.m_avoid_partial_spends = true;
|
||||
BlindDetails blind_details2;
|
||||
BlindDetails *blind_details2_ptr = blind_details ? &blind_details2 : nullptr;
|
||||
auto res_tx_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, tmp_cc, sign, blind_details2_ptr, issuance_details);
|
||||
// Helper optional class for now
|
||||
std::optional<CreatedTransactionResult> txr_grouped{res_tx_grouped.HasRes() ? std::make_optional(res_tx_grouped.GetObj()) : std::nullopt};
|
||||
auto txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, tmp_cc, sign, blind_details2_ptr, issuance_details);
|
||||
// if fee of this alternative one is within the range of the max fee, we use this one
|
||||
const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee) : false};
|
||||
TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, txr_grouped.has_value(),
|
||||
|
|
@ -1912,7 +1910,7 @@ BResult<CreatedTransactionResult> CreateTransaction(
|
|||
if (blind_details) { // ELEMENTS FIXME: is this if statement + body still needed?
|
||||
*blind_details = blind_details2;
|
||||
}
|
||||
return res_tx_grouped;
|
||||
return txr_grouped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1979,10 +1977,10 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
|
|||
auto blind_details = g_con_elementsmode ? std::make_unique<BlindDetails>() : nullptr;
|
||||
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut, coinControl, false, blind_details.get());
|
||||
if (!res) {
|
||||
error = res.GetError();
|
||||
error = util::ErrorString(res);
|
||||
return false;
|
||||
}
|
||||
const auto& txr = res.GetObj();
|
||||
const auto& txr = *res;
|
||||
CTransactionRef tx_new = txr.tx;
|
||||
nFeeRet = txr.fee;
|
||||
nChangePosInOut = txr.change_pos;
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ struct CreatedTransactionResult
|
|||
* selected by SelectCoins(); Also create the change output, when needed
|
||||
* @note passing change_pos as -1 will result in setting a random position
|
||||
*/
|
||||
BResult<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, int change_pos, const CCoinControl& coin_control, bool sign = true, BlindDetails* blind_details = nullptr, const IssuanceDetails* issuance_details = nullptr);
|
||||
util::Result<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, int change_pos, const CCoinControl& coin_control, bool sign = true, BlindDetails* blind_details = nullptr, const IssuanceDetails* issuance_details = nullptr);
|
||||
|
||||
/**
|
||||
* Insert additional inputs into the transaction by
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public:
|
|||
constexpr int RANDOM_CHANGE_POSITION = -1;
|
||||
auto res = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, dummy);
|
||||
BOOST_CHECK(res);
|
||||
tx = res.GetObj().tx;
|
||||
tx = res->tx;
|
||||
}
|
||||
wallet->CommitTransaction(tx, {}, {});
|
||||
CMutableTransaction blocktx;
|
||||
|
|
@ -62,7 +62,7 @@ public:
|
|||
BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestBech32m, AvailableCoinsTestingSetup)
|
||||
{
|
||||
CoinsResult available_coins;
|
||||
BResult<CTxDestination> dest;
|
||||
util::Result<CTxDestination> dest{util::Error{}};
|
||||
LOCK(wallet->cs_wallet);
|
||||
|
||||
// Verify our wallet has one usable coinbase UTXO before starting
|
||||
|
|
@ -81,8 +81,8 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestBech32m, AvailableCoinsTestingSetup)
|
|||
|
||||
// Bech32m
|
||||
dest = wallet->GetNewDestination(OutputType::BECH32M, "");
|
||||
BOOST_ASSERT(dest.HasRes());
|
||||
AddTx(CRecipient{{GetScriptForDestination(dest.GetObj())}, 1 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
BOOST_ASSERT(dest);
|
||||
AddTx(CRecipient{{GetScriptForDestination(*dest)}, 1 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
available_coins = AvailableCoins(*wallet);
|
||||
BOOST_CHECK_EQUAL(available_coins.bech32m.size(), 2U);
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestBech32m, AvailableCoinsTestingSetup)
|
|||
BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestBech32, AvailableCoinsTestingSetup)
|
||||
{
|
||||
CoinsResult available_coins;
|
||||
BResult<CTxDestination> dest;
|
||||
util::Result<CTxDestination> dest{util::Error{}};
|
||||
LOCK(wallet->cs_wallet);
|
||||
|
||||
available_coins = AvailableCoins(*wallet);
|
||||
|
|
@ -99,8 +99,8 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestBech32, AvailableCoinsTestingSetup)
|
|||
|
||||
// Bech32
|
||||
dest = wallet->GetNewDestination(OutputType::BECH32, "");
|
||||
BOOST_ASSERT(dest.HasRes());
|
||||
AddTx(CRecipient{{GetScriptForDestination(dest.GetObj())}, 2 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
BOOST_ASSERT(dest);
|
||||
AddTx(CRecipient{{GetScriptForDestination(*dest)}, 2 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
available_coins = AvailableCoins(*wallet);
|
||||
BOOST_CHECK_EQUAL(available_coins.bech32.size(), 2U);
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestBech32, AvailableCoinsTestingSetup)
|
|||
BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestP2SHSegWit, AvailableCoinsTestingSetup)
|
||||
{
|
||||
CoinsResult available_coins;
|
||||
BResult<CTxDestination> dest;
|
||||
util::Result<CTxDestination> dest{util::Error{}};
|
||||
LOCK(wallet->cs_wallet);
|
||||
|
||||
available_coins = AvailableCoins(*wallet);
|
||||
|
|
@ -117,7 +117,7 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestP2SHSegWit, AvailableCoinsTestingSet
|
|||
|
||||
// P2SH-SEGWIT
|
||||
dest = wallet->GetNewDestination(OutputType::P2SH_SEGWIT, "");
|
||||
AddTx(CRecipient{{GetScriptForDestination(dest.GetObj())}, 3 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
AddTx(CRecipient{{GetScriptForDestination(*dest)}, 3 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
available_coins = AvailableCoins(*wallet);
|
||||
BOOST_CHECK_EQUAL(available_coins.P2SH_segwit.size(), 2U);
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestP2SHSegWit, AvailableCoinsTestingSet
|
|||
BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestLegacy, AvailableCoinsTestingSetup)
|
||||
{
|
||||
CoinsResult available_coins;
|
||||
BResult<CTxDestination> dest;
|
||||
util::Result<CTxDestination> dest{util::Error{}};
|
||||
LOCK(wallet->cs_wallet);
|
||||
|
||||
available_coins = AvailableCoins(*wallet);
|
||||
|
|
@ -135,8 +135,8 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTestLegacy, AvailableCoinsTestingSetup)
|
|||
|
||||
// Legacy (P2PKH)
|
||||
dest = wallet->GetNewDestination(OutputType::LEGACY, "");
|
||||
BOOST_ASSERT(dest.HasRes());
|
||||
AddTx(CRecipient{{GetScriptForDestination(dest.GetObj())}, 4 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
BOOST_ASSERT(dest);
|
||||
AddTx(CRecipient{{GetScriptForDestination(*dest)}, 4 * COIN, CAsset(), CPubKey(), /*fSubtractFeeFromAmount=*/true});
|
||||
available_coins = AvailableCoins(*wallet);
|
||||
BOOST_CHECK_EQUAL(available_coins.legacy.size(), 2U);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,9 +82,7 @@ static void add_coin(CoinsResult& available_coins, CWallet& wallet, const CAmoun
|
|||
tx.vout.resize(nInput + 1);
|
||||
tx.vout[nInput].nValue = nValue;
|
||||
if (spendable) {
|
||||
auto op_dest = wallet.GetNewDestination(OutputType::BECH32, "");
|
||||
assert(op_dest.HasRes());
|
||||
tx.vout[nInput].scriptPubKey = GetScriptForDestination(op_dest.GetObj());
|
||||
tx.vout[nInput].scriptPubKey = GetScriptForDestination(*Assert(wallet.GetNewDestination(OutputType::BECH32, "")));
|
||||
}
|
||||
uint256 txid = tx.GetHash();
|
||||
|
||||
|
|
|
|||
|
|
@ -69,14 +69,13 @@ struct FuzzedWallet {
|
|||
CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider)
|
||||
{
|
||||
auto type{fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)};
|
||||
BResult<CTxDestination> op_dest;
|
||||
util::Result<CTxDestination> op_dest{util::Error{}};
|
||||
if (fuzzed_data_provider.ConsumeBool()) {
|
||||
op_dest = wallet->GetNewDestination(type, "");
|
||||
} else {
|
||||
op_dest = wallet->GetNewChangeDestination(type);
|
||||
}
|
||||
assert(op_dest.HasRes());
|
||||
return GetScriptForDestination(op_dest.GetObj());
|
||||
return GetScriptForDestination(*Assert(op_dest));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
|
|||
coin_control.m_change_type = OutputType::LEGACY;
|
||||
auto res = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, coin_control);
|
||||
BOOST_CHECK(res);
|
||||
const auto& txr = res.GetObj();
|
||||
const auto& txr = *res;
|
||||
BOOST_CHECK_EQUAL(txr.tx->vout.size(), 1);
|
||||
BOOST_CHECK_EQUAL(txr.tx->vout[0].nValue.GetAmount(), recipient.nAmount + leftover_input_amount - txr.fee);
|
||||
BOOST_CHECK_GT(txr.fee, 0);
|
||||
|
|
|
|||
|
|
@ -540,7 +540,7 @@ public:
|
|||
constexpr int RANDOM_CHANGE_POSITION = -1;
|
||||
auto res = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, dummy);
|
||||
BOOST_CHECK(res);
|
||||
tx = res.GetObj().tx;
|
||||
tx = res->tx;
|
||||
}
|
||||
wallet->CommitTransaction(tx, {}, {});
|
||||
CMutableTransaction blocktx;
|
||||
|
|
@ -924,8 +924,8 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
|
|||
|
||||
// Add tx to wallet
|
||||
const auto& op_dest = wallet.GetNewDestination(OutputType::BECH32M, "");
|
||||
BOOST_ASSERT(op_dest.HasRes());
|
||||
const CTxDestination& dest = op_dest.GetObj();
|
||||
BOOST_ASSERT(op_dest);
|
||||
const CTxDestination& dest = *op_dest;
|
||||
|
||||
CMutableTransaction mtx;
|
||||
mtx.vout.push_back({CAsset(), COIN, GetScriptForDestination(dest)});
|
||||
|
|
|
|||
|
|
@ -2548,30 +2548,30 @@ bool CWallet::GetOnlinePakKey(CPubKey& online_pubkey, std::string& error)
|
|||
}
|
||||
/// end ELEMENTS
|
||||
|
||||
BResult<CTxDestination> CWallet::GetNewDestination(const OutputType type, const std::string label, bool add_blinding_key)
|
||||
util::Result<CTxDestination> CWallet::GetNewDestination(const OutputType type, const std::string label, bool add_blinding_key)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
auto spk_man = GetScriptPubKeyMan(type, false /* internal */);
|
||||
if (!spk_man) {
|
||||
return strprintf(_("Error: No %s addresses available."), FormatOutputType(type));
|
||||
return util::Error{strprintf(_("Error: No %s addresses available."), FormatOutputType(type))};
|
||||
}
|
||||
|
||||
spk_man->TopUp();
|
||||
auto op_dest = spk_man->GetNewDestination(type);
|
||||
if (op_dest) {
|
||||
if (add_blinding_key) {
|
||||
auto dest = op_dest.GetObj();
|
||||
auto dest = *op_dest;
|
||||
CPubKey blinding_pubkey = GetBlindingPubKey(GetScriptForDestination(dest));
|
||||
std::visit(SetBlindingPubKeyVisitor(blinding_pubkey), dest);
|
||||
op_dest = dest;
|
||||
}
|
||||
SetAddressBook(op_dest.GetObj(), label, "receive");
|
||||
SetAddressBook(*op_dest, label, "receive");
|
||||
}
|
||||
|
||||
return op_dest;
|
||||
}
|
||||
|
||||
BResult<CTxDestination> CWallet::GetNewChangeDestination(const OutputType type, bool add_blinding_key)
|
||||
util::Result<CTxDestination> CWallet::GetNewChangeDestination(const OutputType type, bool add_blinding_key)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
|
||||
|
|
@ -2579,7 +2579,7 @@ BResult<CTxDestination> CWallet::GetNewChangeDestination(const OutputType type,
|
|||
bilingual_str error;
|
||||
ReserveDestination reservedest(this, type);
|
||||
if (!reservedest.GetReservedDestination(dest, true, error)) {
|
||||
return error;
|
||||
return util::Error{error};
|
||||
}
|
||||
if (add_blinding_key) {
|
||||
CPubKey blinding_pubkey = GetBlindingPubKey(GetScriptForDestination(dest));
|
||||
|
|
|
|||
|
|
@ -741,8 +741,8 @@ public:
|
|||
void MarkDestinationsDirty(const std::set<CTxDestination>& destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
bool GetOnlinePakKey(CPubKey& online_pubkey, std::string& error);
|
||||
BResult<CTxDestination> GetNewDestination(const OutputType type, const std::string label, bool add_blinding_key = false);
|
||||
BResult<CTxDestination> GetNewChangeDestination(const OutputType type, bool add_blinding_key = false);
|
||||
util::Result<CTxDestination> GetNewDestination(const OutputType type, const std::string label, bool add_blinding_key = false);
|
||||
util::Result<CTxDestination> GetNewChangeDestination(const OutputType type, bool add_blinding_key = false);
|
||||
|
||||
isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue