Merge #16237: Have the wallet give out destinations instead of keys
8e7f930828
Add GetNewChangeDestination for getting new change Destinations (Andrew Chow)33d13edd2b
Replace CReserveKey with ReserveDestinatoin (Andrew Chow)172213be5b
Add GetNewDestination to CWallet to fetch new destinations (Andrew Chow) Pull request description: The wallet should give out destinations instead of keys. It should be the one that handles the conversion from key to destination and the setting of the label, not the caller. In order to do this, two new member functions are introduced `GetNewDestination()` and `GetNewChangeDestination()`. Additionally, `CReserveKey` is changed to be `ReserveDestination` and represents destinations whose keys can be returned to the keypool. ACKs for top commit: instagibbs: re-utACK8e7f930828
sipa: ACK8e7f930828
. Concept ACK as this gives a much cleaner abstraction to work with, and light code review ACK. laanwj: ACK8e7f930828
Tree-SHA512: 5be7051409232b71e0ef2c1fd1a3e76964ed2f5b14d47d06edc2ad3b3687abd0be2803a1adc45c0433aa2c3bed172e14f8a7e9f4a23bff70f86260b5a0497500
This commit is contained in:
commit
8d1286014c
10 changed files with 144 additions and 121 deletions
|
@ -36,7 +36,7 @@ namespace {
|
||||||
class PendingWalletTxImpl : public PendingWalletTx
|
class PendingWalletTxImpl : public PendingWalletTx
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {}
|
explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_dest(&wallet) {}
|
||||||
|
|
||||||
const CTransaction& get() override { return *m_tx; }
|
const CTransaction& get() override { return *m_tx; }
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public:
|
||||||
auto locked_chain = m_wallet.chain().lock();
|
auto locked_chain = m_wallet.chain().lock();
|
||||||
LOCK(m_wallet.cs_wallet);
|
LOCK(m_wallet.cs_wallet);
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, state)) {
|
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_dest, state)) {
|
||||||
reject_reason = state.GetRejectReason();
|
reject_reason = state.GetRejectReason();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public:
|
||||||
|
|
||||||
CTransactionRef m_tx;
|
CTransactionRef m_tx;
|
||||||
CWallet& m_wallet;
|
CWallet& m_wallet;
|
||||||
CReserveKey m_key;
|
ReserveDestination m_dest;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Construct wallet tx struct.
|
//! Construct wallet tx struct.
|
||||||
|
@ -140,9 +140,11 @@ public:
|
||||||
void abortRescan() override { m_wallet->AbortRescan(); }
|
void abortRescan() override { m_wallet->AbortRescan(); }
|
||||||
bool backupWallet(const std::string& filename) override { return m_wallet->BackupWallet(filename); }
|
bool backupWallet(const std::string& filename) override { return m_wallet->BackupWallet(filename); }
|
||||||
std::string getWalletName() override { return m_wallet->GetName(); }
|
std::string getWalletName() override { return m_wallet->GetName(); }
|
||||||
bool getKeyFromPool(bool internal, CPubKey& pub_key) override
|
bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) override
|
||||||
{
|
{
|
||||||
return m_wallet->GetKeyFromPool(pub_key, internal);
|
LOCK(m_wallet->cs_wallet);
|
||||||
|
std::string error;
|
||||||
|
return m_wallet->GetNewDestination(type, label, dest, error);
|
||||||
}
|
}
|
||||||
bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetPubKey(address, pub_key); }
|
bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetPubKey(address, pub_key); }
|
||||||
bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetKey(address, key); }
|
bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetKey(address, key); }
|
||||||
|
@ -236,7 +238,7 @@ public:
|
||||||
auto locked_chain = m_wallet->chain().lock();
|
auto locked_chain = m_wallet->chain().lock();
|
||||||
LOCK(m_wallet->cs_wallet);
|
LOCK(m_wallet->cs_wallet);
|
||||||
auto pending = MakeUnique<PendingWalletTxImpl>(*m_wallet);
|
auto pending = MakeUnique<PendingWalletTxImpl>(*m_wallet);
|
||||||
if (!m_wallet->CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
|
if (!m_wallet->CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_dest, fee, change_pos,
|
||||||
fail_reason, coin_control, sign)) {
|
fail_reason, coin_control, sign)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,8 @@ public:
|
||||||
//! Get wallet name.
|
//! Get wallet name.
|
||||||
virtual std::string getWalletName() = 0;
|
virtual std::string getWalletName() = 0;
|
||||||
|
|
||||||
// Get key from pool.
|
// Get a new address.
|
||||||
virtual bool getKeyFromPool(bool internal, CPubKey& pub_key) = 0;
|
virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) = 0;
|
||||||
|
|
||||||
//! Get public key.
|
//! Get public key.
|
||||||
virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0;
|
virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0;
|
||||||
|
|
|
@ -358,12 +358,15 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add entry
|
||||||
|
walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel, "send");
|
||||||
}
|
}
|
||||||
else if(type == Receive)
|
else if(type == Receive)
|
||||||
{
|
{
|
||||||
// Generate a new address to associate with given label
|
// Generate a new address to associate with given label
|
||||||
CPubKey newKey;
|
CTxDestination dest;
|
||||||
if(!walletModel->wallet().getKeyFromPool(false /* internal */, newKey))
|
if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
|
||||||
{
|
{
|
||||||
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
|
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
|
||||||
if(!ctx.isValid())
|
if(!ctx.isValid())
|
||||||
|
@ -372,23 +375,18 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
|
||||||
editStatus = WALLET_UNLOCK_FAILURE;
|
editStatus = WALLET_UNLOCK_FAILURE;
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
if(!walletModel->wallet().getKeyFromPool(false /* internal */, newKey))
|
if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
|
||||||
{
|
{
|
||||||
editStatus = KEY_GENERATION_FAILURE;
|
editStatus = KEY_GENERATION_FAILURE;
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
walletModel->wallet().learnRelatedScripts(newKey, address_type);
|
strAddress = EncodeDestination(dest);
|
||||||
strAddress = EncodeDestination(GetDestinationForKey(newKey, address_type));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add entry
|
|
||||||
walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel,
|
|
||||||
(type == Send ? "send" : "receive"));
|
|
||||||
return QString::fromStdString(strAddress);
|
return QString::fromStdString(strAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -666,16 +666,14 @@ void PaymentServer::fetchPaymentACK(WalletModel* walletModel, const SendCoinsRec
|
||||||
payment.add_transactions(transaction.data(), transaction.size());
|
payment.add_transactions(transaction.data(), transaction.size());
|
||||||
|
|
||||||
// Create a new refund address, or re-use:
|
// Create a new refund address, or re-use:
|
||||||
CPubKey newKey;
|
CTxDestination dest;
|
||||||
if (walletModel->wallet().getKeyFromPool(false /* internal */, newKey)) {
|
const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::CHANGE_AUTO ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
|
||||||
|
if (walletModel->wallet().getNewDestination(change_type, "", dest)) {
|
||||||
// BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
|
// BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
|
||||||
// types supported by the receiver. As a result, we choose the address format we also
|
// types supported by the receiver. As a result, we choose the address format we also
|
||||||
// use for change. Despite an actual payment and not change, this is a close match:
|
// use for change. Despite an actual payment and not change, this is a close match:
|
||||||
// it's the output type we use subject to privacy issues, but not restricted by what
|
// it's the output type we use subject to privacy issues, but not restricted by what
|
||||||
// other software supports.
|
// other software supports.
|
||||||
const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::CHANGE_AUTO ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
|
|
||||||
walletModel->wallet().learnRelatedScripts(newKey, change_type);
|
|
||||||
CTxDestination dest = GetDestinationForKey(newKey, change_type);
|
|
||||||
std::string label = tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString();
|
std::string label = tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString();
|
||||||
walletModel->wallet().setAddressBook(dest, label, "refund");
|
walletModel->wallet().setAddressBook(dest, label, "refund");
|
||||||
|
|
||||||
|
|
|
@ -23,14 +23,9 @@ const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqq
|
||||||
std::string getnewaddress(CWallet& w)
|
std::string getnewaddress(CWallet& w)
|
||||||
{
|
{
|
||||||
constexpr auto output_type = OutputType::BECH32;
|
constexpr auto output_type = OutputType::BECH32;
|
||||||
|
CTxDestination dest;
|
||||||
CPubKey new_key;
|
std::string error;
|
||||||
if (!w.GetKeyFromPool(new_key)) assert(false);
|
if (!w.GetNewDestination(output_type, "", dest, error)) assert(false);
|
||||||
|
|
||||||
w.LearnRelatedScripts(new_key, output_type);
|
|
||||||
const auto dest = GetDestinationForKey(new_key, output_type);
|
|
||||||
|
|
||||||
w.SetAddressBook(dest, /* label */ "", "receive");
|
|
||||||
|
|
||||||
return EncodeDestination(dest);
|
return EncodeDestination(dest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,17 +272,17 @@ Result CreateRateBumpTransaction(CWallet* wallet, const uint256& txid, const CCo
|
||||||
new_coin_control.m_min_depth = 1;
|
new_coin_control.m_min_depth = 1;
|
||||||
|
|
||||||
CTransactionRef tx_new = MakeTransactionRef();
|
CTransactionRef tx_new = MakeTransactionRef();
|
||||||
CReserveKey reservekey(wallet);
|
ReserveDestination reservedest(wallet);
|
||||||
CAmount fee_ret;
|
CAmount fee_ret;
|
||||||
int change_pos_in_out = -1; // No requested location for change
|
int change_pos_in_out = -1; // No requested location for change
|
||||||
std::string fail_reason;
|
std::string fail_reason;
|
||||||
if (!wallet->CreateTransaction(*locked_chain, recipients, tx_new, reservekey, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
|
if (!wallet->CreateTransaction(*locked_chain, recipients, tx_new, reservedest, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
|
||||||
errors.push_back("Unable to create transaction: " + fail_reason);
|
errors.push_back("Unable to create transaction: " + fail_reason);
|
||||||
return Result::WALLET_ERROR;
|
return Result::WALLET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If change key hasn't been ReturnKey'ed by this point, we take it out of keypool
|
// If change key hasn't been ReturnKey'ed by this point, we take it out of keypool
|
||||||
reservekey.KeepKey();
|
reservedest.KeepDestination();
|
||||||
|
|
||||||
// Write back new fee if successful
|
// Write back new fee if successful
|
||||||
new_fee = fee_ret;
|
new_fee = fee_ret;
|
||||||
|
@ -330,9 +330,9 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
|
||||||
mapValue_t mapValue = oldWtx.mapValue;
|
mapValue_t mapValue = oldWtx.mapValue;
|
||||||
mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
|
mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
|
||||||
|
|
||||||
CReserveKey reservekey(wallet);
|
ReserveDestination reservedest(wallet);
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, state)) {
|
if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservedest, state)) {
|
||||||
// NOTE: CommitTransaction never returns false, so this should never happen.
|
// NOTE: CommitTransaction never returns false, so this should never happen.
|
||||||
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
|
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
|
||||||
return Result::WALLET_ERROR;
|
return Result::WALLET_ERROR;
|
||||||
|
|
|
@ -194,20 +194,12 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pwallet->IsLocked()) {
|
CTxDestination dest;
|
||||||
pwallet->TopUpKeyPool();
|
std::string error;
|
||||||
|
if (!pwallet->GetNewDestination(output_type, label, dest, error)) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new key that is added to wallet
|
|
||||||
CPubKey newKey;
|
|
||||||
if (!pwallet->GetKeyFromPool(newKey)) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
|
||||||
}
|
|
||||||
pwallet->LearnRelatedScripts(newKey, output_type);
|
|
||||||
CTxDestination dest = GetDestinationForKey(newKey, output_type);
|
|
||||||
|
|
||||||
pwallet->SetAddressBook(dest, label, "receive");
|
|
||||||
|
|
||||||
return EncodeDestination(dest);
|
return EncodeDestination(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,10 +233,6 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pwallet->IsLocked()) {
|
|
||||||
pwallet->TopUpKeyPool();
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type;
|
OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type;
|
||||||
if (!request.params[0].isNull()) {
|
if (!request.params[0].isNull()) {
|
||||||
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
|
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
|
||||||
|
@ -252,16 +240,11 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CReserveKey reservekey(pwallet);
|
CTxDestination dest;
|
||||||
CPubKey vchPubKey;
|
std::string error;
|
||||||
if (!reservekey.GetReservedKey(vchPubKey, true))
|
if (!pwallet->GetNewChangeDestination(output_type, dest, error)) {
|
||||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
|
||||||
|
}
|
||||||
reservekey.KeepKey();
|
|
||||||
|
|
||||||
pwallet->LearnRelatedScripts(vchPubKey, output_type);
|
|
||||||
CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
|
|
||||||
|
|
||||||
return EncodeDestination(dest);
|
return EncodeDestination(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +309,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
|
||||||
CScript scriptPubKey = GetScriptForDestination(address);
|
CScript scriptPubKey = GetScriptForDestination(address);
|
||||||
|
|
||||||
// Create and send the transaction
|
// Create and send the transaction
|
||||||
CReserveKey reservekey(pwallet);
|
ReserveDestination reservedest(pwallet);
|
||||||
CAmount nFeeRequired;
|
CAmount nFeeRequired;
|
||||||
std::string strError;
|
std::string strError;
|
||||||
std::vector<CRecipient> vecSend;
|
std::vector<CRecipient> vecSend;
|
||||||
|
@ -334,13 +317,13 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
|
||||||
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
|
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
|
||||||
vecSend.push_back(recipient);
|
vecSend.push_back(recipient);
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
|
if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservedest, nFeeRequired, nChangePosRet, strError, coin_control)) {
|
||||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
|
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
|
||||||
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
|
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
}
|
}
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, state)) {
|
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservedest, state)) {
|
||||||
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
|
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
}
|
}
|
||||||
|
@ -924,16 +907,16 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
|
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
|
||||||
|
|
||||||
// Send
|
// Send
|
||||||
CReserveKey keyChange(pwallet);
|
ReserveDestination changedest(pwallet);
|
||||||
CAmount nFeeRequired = 0;
|
CAmount nFeeRequired = 0;
|
||||||
int nChangePosRet = -1;
|
int nChangePosRet = -1;
|
||||||
std::string strFailReason;
|
std::string strFailReason;
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
|
bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, changedest, nFeeRequired, nChangePosRet, strFailReason, coin_control);
|
||||||
if (!fCreated)
|
if (!fCreated)
|
||||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, state)) {
|
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, changedest, state)) {
|
||||||
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
|
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
|
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,17 +361,17 @@ public:
|
||||||
CWalletTx& AddTx(CRecipient recipient)
|
CWalletTx& AddTx(CRecipient recipient)
|
||||||
{
|
{
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
CReserveKey reservekey(wallet.get());
|
ReserveDestination reservedest(wallet.get());
|
||||||
CAmount fee;
|
CAmount fee;
|
||||||
int changePos = -1;
|
int changePos = -1;
|
||||||
std::string error;
|
std::string error;
|
||||||
CCoinControl dummy;
|
CCoinControl dummy;
|
||||||
{
|
{
|
||||||
auto locked_chain = m_chain->lock();
|
auto locked_chain = m_chain->lock();
|
||||||
BOOST_CHECK(wallet->CreateTransaction(*locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
|
BOOST_CHECK(wallet->CreateTransaction(*locked_chain, {recipient}, tx, reservedest, fee, changePos, error, dummy));
|
||||||
}
|
}
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, state));
|
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservedest, state));
|
||||||
CMutableTransaction blocktx;
|
CMutableTransaction blocktx;
|
||||||
{
|
{
|
||||||
LOCK(wallet->cs_wallet);
|
LOCK(wallet->cs_wallet);
|
||||||
|
@ -464,8 +464,9 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
|
||||||
wallet->SetMinVersion(FEATURE_LATEST);
|
wallet->SetMinVersion(FEATURE_LATEST);
|
||||||
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||||
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
|
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
|
||||||
CPubKey pubkey;
|
CTxDestination dest;
|
||||||
BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
|
std::string error;
|
||||||
|
BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit calculation which is used to test the wallet constant
|
// Explicit calculation which is used to test the wallet constant
|
||||||
|
|
|
@ -2666,9 +2666,9 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
|
||||||
auto locked_chain = chain().lock();
|
auto locked_chain = chain().lock();
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
|
|
||||||
CReserveKey reservekey(this);
|
ReserveDestination reservedest(this);
|
||||||
CTransactionRef tx_new;
|
CTransactionRef tx_new;
|
||||||
if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
|
if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservedest, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2676,7 +2676,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
|
||||||
tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
|
tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
|
||||||
// We don't have the normal Create/Commit cycle, and don't want to risk
|
// We don't have the normal Create/Commit cycle, and don't want to risk
|
||||||
// reusing change, so just remove the key from the keypool here.
|
// reusing change, so just remove the key from the keypool here.
|
||||||
reservekey.KeepKey();
|
reservedest.KeepDestination();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy output sizes from new transaction; they may have had the fee
|
// Copy output sizes from new transaction; they may have had the fee
|
||||||
|
@ -2792,7 +2792,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
|
||||||
return m_default_address_type;
|
return m_default_address_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
|
bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, ReserveDestination& reservedest, CAmount& nFeeRet,
|
||||||
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
|
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
|
||||||
{
|
{
|
||||||
CAmount nValue = 0;
|
CAmount nValue = 0;
|
||||||
|
@ -2833,7 +2833,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
|
||||||
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
|
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
|
||||||
|
|
||||||
// Create change script that will be used if we need change
|
// Create change script that will be used if we need change
|
||||||
// TODO: pass in scriptChange instead of reservekey so
|
// TODO: pass in scriptChange instead of reservedest so
|
||||||
// change transaction isn't always pay-to-bitcoin-address
|
// change transaction isn't always pay-to-bitcoin-address
|
||||||
CScript scriptChange;
|
CScript scriptChange;
|
||||||
|
|
||||||
|
@ -2853,19 +2853,16 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
|
||||||
strFailReason = _("Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.");
|
strFailReason = _("Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CPubKey vchPubKey;
|
CTxDestination dest;
|
||||||
bool ret;
|
const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
|
||||||
ret = reservekey.GetReservedKey(vchPubKey, true);
|
bool ret = reservedest.GetReservedDestination(change_type, dest, true);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
strFailReason = _("Keypool ran out, please call keypoolrefill first");
|
strFailReason = "Keypool ran out, please call keypoolrefill first";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
|
scriptChange = GetScriptForDestination(dest);
|
||||||
|
|
||||||
LearnRelatedScripts(vchPubKey, change_type);
|
|
||||||
scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type));
|
|
||||||
}
|
}
|
||||||
CTxOut change_prototype_txout(0, scriptChange);
|
CTxOut change_prototype_txout(0, scriptChange);
|
||||||
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
|
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
|
||||||
|
@ -3086,7 +3083,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nChangePosInOut == -1) reservekey.ReturnKey(); // Return any reserved key if we don't have change
|
if (nChangePosInOut == -1) reservedest.ReturnDestination(); // Return any reserved address if we don't have change
|
||||||
|
|
||||||
// Shuffle selected coins and fill in final vin
|
// Shuffle selected coins and fill in final vin
|
||||||
txNew.vin.clear();
|
txNew.vin.clear();
|
||||||
|
@ -3159,7 +3156,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
|
||||||
/**
|
/**
|
||||||
* Call after CreateTransaction unless you want to abort
|
* Call after CreateTransaction unless you want to abort
|
||||||
*/
|
*/
|
||||||
bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state)
|
bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, ReserveDestination& reservedest, CValidationState& state)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto locked_chain = chain().lock();
|
auto locked_chain = chain().lock();
|
||||||
|
@ -3174,7 +3171,7 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
|
||||||
WalletLogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); /* Continued */
|
WalletLogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); /* Continued */
|
||||||
{
|
{
|
||||||
// Take key pair from key pool so it won't be used again
|
// Take key pair from key pool so it won't be used again
|
||||||
reservekey.KeepKey();
|
reservedest.KeepDestination();
|
||||||
|
|
||||||
// Add tx to wallet, because if it has change it's also ours,
|
// Add tx to wallet, because if it has change it's also ours,
|
||||||
// otherwise just for transaction history.
|
// otherwise just for transaction history.
|
||||||
|
@ -3575,6 +3572,44 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CWallet::GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error)
|
||||||
|
{
|
||||||
|
LOCK(cs_wallet);
|
||||||
|
error.clear();
|
||||||
|
if (!IsLocked()) {
|
||||||
|
TopUpKeyPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a new key that is added to wallet
|
||||||
|
CPubKey new_key;
|
||||||
|
if (!GetKeyFromPool(new_key)) {
|
||||||
|
error = "Error: Keypool ran out, please call keypoolrefill first";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LearnRelatedScripts(new_key, type);
|
||||||
|
dest = GetDestinationForKey(new_key, type);
|
||||||
|
|
||||||
|
SetAddressBook(dest, label, "receive");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error)
|
||||||
|
{
|
||||||
|
error.clear();
|
||||||
|
if (!IsLocked()) {
|
||||||
|
TopUpKeyPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReserveDestination reservedest(this);
|
||||||
|
if (!reservedest.GetReservedDestination(type, dest, true)) {
|
||||||
|
error = "Error: Keypool ran out, please call keypoolrefill first";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reservedest.KeepDestination();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
|
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
|
||||||
if (setKeyPool.empty()) {
|
if (setKeyPool.empty()) {
|
||||||
return GetTime();
|
return GetTime();
|
||||||
|
@ -3754,7 +3789,7 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
|
bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestination& dest, bool internal)
|
||||||
{
|
{
|
||||||
if (!pwallet->CanGetAddresses(internal)) {
|
if (!pwallet->CanGetAddresses(internal)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -3770,25 +3805,29 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
|
||||||
fInternal = keypool.fInternal;
|
fInternal = keypool.fInternal;
|
||||||
}
|
}
|
||||||
assert(vchPubKey.IsValid());
|
assert(vchPubKey.IsValid());
|
||||||
pubkey = vchPubKey;
|
pwallet->LearnRelatedScripts(vchPubKey, type);
|
||||||
|
address = GetDestinationForKey(vchPubKey, type);
|
||||||
|
dest = address;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CReserveKey::KeepKey()
|
void ReserveDestination::KeepDestination()
|
||||||
{
|
{
|
||||||
if (nIndex != -1)
|
if (nIndex != -1)
|
||||||
pwallet->KeepKey(nIndex);
|
pwallet->KeepKey(nIndex);
|
||||||
nIndex = -1;
|
nIndex = -1;
|
||||||
vchPubKey = CPubKey();
|
vchPubKey = CPubKey();
|
||||||
|
address = CNoDestination();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CReserveKey::ReturnKey()
|
void ReserveDestination::ReturnDestination()
|
||||||
{
|
{
|
||||||
if (nIndex != -1) {
|
if (nIndex != -1) {
|
||||||
pwallet->ReturnKey(nIndex, fInternal, vchPubKey);
|
pwallet->ReturnKey(nIndex, fInternal, vchPubKey);
|
||||||
}
|
}
|
||||||
nIndex = -1;
|
nIndex = -1;
|
||||||
vchPubKey = CPubKey();
|
vchPubKey = CPubKey();
|
||||||
|
address = CNoDestination();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
|
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
|
||||||
|
|
|
@ -85,11 +85,11 @@ static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
|
||||||
|
|
||||||
class CCoinControl;
|
class CCoinControl;
|
||||||
class COutput;
|
class COutput;
|
||||||
class CReserveKey;
|
|
||||||
class CScript;
|
class CScript;
|
||||||
class CWalletTx;
|
class CWalletTx;
|
||||||
struct FeeCalculation;
|
struct FeeCalculation;
|
||||||
enum class FeeEstimateMode;
|
enum class FeeEstimateMode;
|
||||||
|
class ReserveDestination;
|
||||||
|
|
||||||
/** (client) version numbers for particular wallet features */
|
/** (client) version numbers for particular wallet features */
|
||||||
enum WalletFeature
|
enum WalletFeature
|
||||||
|
@ -254,55 +254,57 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A wrapper to reserve a key from a wallet keypool
|
/** A wrapper to reserve an address from a wallet
|
||||||
*
|
*
|
||||||
* CReserveKey is used to reserve a key from the keypool. It is passed around
|
* ReserveDestination is used to reserve an address. It is passed around
|
||||||
* during the CreateTransaction/CommitTransaction procedure.
|
* during the CreateTransaction/CommitTransaction procedure.
|
||||||
*
|
*
|
||||||
* Instantiating a CReserveKey does not reserve a keypool key. To do so,
|
* Instantiating a ReserveDestination does not reserve an address. To do so,
|
||||||
* GetReservedKey() needs to be called on the object. Once a key has been
|
* GetReservedDestination() needs to be called on the object. Once an address has been
|
||||||
* reserved, call KeepKey() on the CReserveKey object to make sure it is not
|
* reserved, call KeepDestination() on the ReserveDestination object to make sure it is not
|
||||||
* returned to the keypool. Call ReturnKey() to return the key to the keypool
|
* returned. Call ReturnDestination() to return the address so it can be re-used (for
|
||||||
* so it can be re-used (for example, if the key was used in a new transaction
|
* example, if the address was used in a new transaction
|
||||||
* and that transaction was not completed and needed to be aborted).
|
* and that transaction was not completed and needed to be aborted).
|
||||||
*
|
*
|
||||||
* If a key is reserved and KeepKey() is not called, then the key will be
|
* If an address is reserved and KeepDestination() is not called, then the address will be
|
||||||
* returned to the keypool when the CReserveObject goes out of scope.
|
* returned when the ReserveDestination goes out of scope.
|
||||||
*/
|
*/
|
||||||
class CReserveKey
|
class ReserveDestination
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
//! The wallet to reserve the keypool key from
|
//! The wallet to reserve from
|
||||||
CWallet* pwallet;
|
CWallet* pwallet;
|
||||||
//! The index of the key in the keypool
|
//! The index of the address's key in the keypool
|
||||||
int64_t nIndex{-1};
|
int64_t nIndex{-1};
|
||||||
//! The public key
|
//! The public key for the address
|
||||||
CPubKey vchPubKey;
|
CPubKey vchPubKey;
|
||||||
|
//! The destination
|
||||||
|
CTxDestination address;
|
||||||
//! Whether this is from the internal (change output) keypool
|
//! Whether this is from the internal (change output) keypool
|
||||||
bool fInternal{false};
|
bool fInternal{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! Construct a CReserveKey object. This does NOT reserve a key from the keypool yet
|
//! Construct a ReserveDestination object. This does NOT reserve an address yet
|
||||||
explicit CReserveKey(CWallet* pwalletIn)
|
explicit ReserveDestination(CWallet* pwalletIn)
|
||||||
{
|
{
|
||||||
pwallet = pwalletIn;
|
pwallet = pwalletIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
CReserveKey(const CReserveKey&) = delete;
|
ReserveDestination(const ReserveDestination&) = delete;
|
||||||
CReserveKey& operator=(const CReserveKey&) = delete;
|
ReserveDestination& operator=(const ReserveDestination&) = delete;
|
||||||
|
|
||||||
//! Destructor. If a key has been reserved and not KeepKey'ed, it will be returned to the keypool
|
//! Destructor. If a key has been reserved and not KeepKey'ed, it will be returned to the keypool
|
||||||
~CReserveKey()
|
~ReserveDestination()
|
||||||
{
|
{
|
||||||
ReturnKey();
|
ReturnDestination();
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Reserve a key from the keypool
|
//! Reserve an address
|
||||||
bool GetReservedKey(CPubKey &pubkey, bool internal = false);
|
bool GetReservedDestination(const OutputType type, CTxDestination& pubkey, bool internal);
|
||||||
//! Return a key to the keypool
|
//! Return reserved address
|
||||||
void ReturnKey();
|
void ReturnDestination();
|
||||||
//! Keep the key. Do not return it to the keypool when this object goes out of scope
|
//! Keep the address. Do not return it's key to the keypool when this object goes out of scope
|
||||||
void KeepKey();
|
void KeepDestination();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Address book data */
|
/** Address book data */
|
||||||
|
@ -835,6 +837,9 @@ private:
|
||||||
*/
|
*/
|
||||||
uint256 m_last_block_processed GUARDED_BY(cs_wallet);
|
uint256 m_last_block_processed GUARDED_BY(cs_wallet);
|
||||||
|
|
||||||
|
//! Fetches a key from the keypool
|
||||||
|
bool GetKeyFromPool(CPubKey &key, bool internal = false);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
* Main wallet lock.
|
* Main wallet lock.
|
||||||
|
@ -1079,9 +1084,9 @@ public:
|
||||||
* selected by SelectCoins(); Also create the change output, when needed
|
* selected by SelectCoins(); Also create the change output, when needed
|
||||||
* @note passing nChangePosInOut as -1 will result in setting a random position
|
* @note passing nChangePosInOut as -1 will result in setting a random position
|
||||||
*/
|
*/
|
||||||
bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
|
bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, ReserveDestination& reservedest, CAmount& nFeeRet, int& nChangePosInOut,
|
||||||
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
|
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
|
||||||
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state);
|
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, ReserveDestination& reservedest, CValidationState& state);
|
||||||
|
|
||||||
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
|
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
|
||||||
{
|
{
|
||||||
|
@ -1136,7 +1141,6 @@ public:
|
||||||
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
|
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
|
||||||
void KeepKey(int64_t nIndex);
|
void KeepKey(int64_t nIndex);
|
||||||
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey);
|
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey);
|
||||||
bool GetKeyFromPool(CPubKey &key, bool internal = false);
|
|
||||||
int64_t GetOldestKeyPoolTime();
|
int64_t GetOldestKeyPoolTime();
|
||||||
/**
|
/**
|
||||||
* Marks all keys in the keypool up to and including reserve_key as used.
|
* Marks all keys in the keypool up to and including reserve_key as used.
|
||||||
|
@ -1149,6 +1153,9 @@ public:
|
||||||
|
|
||||||
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
|
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
|
||||||
|
|
||||||
|
bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error);
|
||||||
|
bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error);
|
||||||
|
|
||||||
isminetype IsMine(const CTxIn& txin) const;
|
isminetype IsMine(const CTxIn& txin) const;
|
||||||
/**
|
/**
|
||||||
* Returns amount of debit if the input matches the
|
* Returns amount of debit if the input matches the
|
||||||
|
|
Loading…
Reference in a new issue