aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp244
1 files changed, 192 insertions, 52 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 405afb6d8d..79e29d050f 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -27,7 +27,6 @@
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/translation.h>
-#include <util/validation.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
@@ -714,7 +713,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
return success;
}
-void CWallet::SetUsedDestinationState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
+void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
{
AssertLockHeld(cs_wallet);
const CWalletTx* srctx = GetWalletTx(hash);
@@ -734,7 +733,7 @@ void CWallet::SetUsedDestinationState(WalletBatch& batch, const uint256& hash, u
}
}
-bool CWallet::IsUsedDestination(const uint256& hash, unsigned int n) const
+bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
{
AssertLockHeld(cs_wallet);
CTxDestination dst;
@@ -777,7 +776,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
for (const CTxIn& txin : wtxIn.tx->vin) {
const COutPoint& op = txin.prevout;
- SetUsedDestinationState(batch, op.hash, op.n, true, tx_destinations);
+ SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations);
}
MarkDestinationsDirty(tx_destinations);
@@ -847,6 +846,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
if (!strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
+#ifndef WIN32
+ // Substituting the wallet name isn't currently supported on windows
+ // because windows shell escaping has not been implemented yet:
+ // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875
+ // A few ways it could be implemented in the future are described in:
+ // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094
+ boost::replace_all(strCmd, "%w", ShellEscape(GetName()));
+#endif
std::thread t(runCommand, strCmd);
t.detach(); // thread runs free
}
@@ -1143,7 +1150,7 @@ void CWallet::UpdatedBlockTip()
}
-void CWallet::BlockUntilSyncedToCurrentChain() {
+void CWallet::BlockUntilSyncedToCurrentChain() const {
AssertLockNotHeld(cs_wallet);
// Skip the queue-draining stuff if we know we're caught up with
// ::ChainActive().Tip(), otherwise put a callback in the validation interface queue and wait
@@ -1326,7 +1333,7 @@ bool CWallet::IsHDEnabled() const
return result;
}
-bool CWallet::CanGetAddresses(bool internal)
+bool CWallet::CanGetAddresses(bool internal) const
{
LOCK(cs_wallet);
if (m_spk_managers.empty()) return false;
@@ -1400,7 +1407,7 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig
const CScript& scriptPubKey = txout.scriptPubKey;
SignatureData sigdata;
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = GetSolvingProvider(scriptPubKey);
if (!provider) {
// We don't know about this scriptpbuKey;
return false;
@@ -1870,7 +1877,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++)
{
- if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) {
+ if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
@@ -2160,11 +2167,11 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
- if (!allow_used_addresses && IsUsedDestination(wtxid, i)) {
+ if (!allow_used_addresses && IsSpentKey(wtxid, i)) {
continue;
}
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(wtx.tx->vout[i].scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey);
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
@@ -2403,34 +2410,172 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return res;
}
-bool CWallet::SignTransaction(CMutableTransaction& tx)
+bool CWallet::SignTransaction(CMutableTransaction& tx) const
{
AssertLockHeld(cs_wallet);
- // sign the new tx
- int nIn = 0;
+ // Build coins map
+ std::map<COutPoint, Coin> coins;
for (auto& input : tx.vin) {
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash);
if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
return false;
}
- const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
- const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
+ const CWalletTx& wtx = mi->second;
+ coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase());
+ }
+ std::map<int, std::string> input_errors;
+ return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
+}
+
+bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
+{
+ // Sign the tx with ScriptPubKeyMans
+ // Because each ScriptPubKeyMan can sign more than one input, we need to keep track of each ScriptPubKeyMan that has signed this transaction.
+ // Each iteration, we may sign more txins than the txin that is specified in that iteration.
+ // We assume that each input is signed by only one ScriptPubKeyMan.
+ std::set<uint256> visited_spk_mans;
+ for (unsigned int i = 0; i < tx.vin.size(); i++) {
+ // Get the prevout
+ CTxIn& txin = tx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ input_errors[i] = "Input not found or already spent";
+ continue;
+ }
+
+ // Check if this input is complete
+ SignatureData sigdata = DataFromTransaction(tx, i, coin->second.out);
+ if (sigdata.complete) {
+ continue;
+ }
+
+ // Input needs to be signed, find the right ScriptPubKeyMan
+ std::set<ScriptPubKeyMan*> spk_mans = GetScriptPubKeyMans(coin->second.out.scriptPubKey, sigdata);
+ if (spk_mans.size() == 0) {
+ input_errors[i] = "Unable to sign input, missing keys";
+ continue;
+ }
+
+ for (auto& spk_man : spk_mans) {
+ // If we've already been signed by this spk_man, skip it
+ if (visited_spk_mans.count(spk_man->GetID()) > 0) {
+ continue;
+ }
+
+ // Sign the tx.
+ // spk_man->SignTransaction will return true if the transaction is complete,
+ // so we can exit early and return true if that happens.
+ if (spk_man->SignTransaction(tx, coins, sighash, input_errors)) {
+ return true;
+ }
+
+ // Add this spk_man to visited_spk_mans so we can skip it later
+ visited_spk_mans.insert(spk_man->GetID());
+ }
+ }
+ return false;
+}
+
+TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs) const
+{
+ LOCK(cs_wallet);
+ // Get all of the previous transactions
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
+ if (!input.IsSane()) {
+ return TransactionError::INVALID_PSBT;
+ }
+
+ // If we have no utxo, grab it from the wallet.
+ if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
+ const uint256& txhash = txin.prevout.hash;
+ const auto it = mapWallet.find(txhash);
+ if (it != mapWallet.end()) {
+ const CWalletTx& wtx = it->second;
+ // We only need the non_witness_utxo, which is a superset of the witness_utxo.
+ // The signing code will switch to the smaller witness_utxo if this is ok.
+ input.non_witness_utxo = wtx.tx;
+ }
+ }
+ }
+
+ // Fill in information from ScriptPubKeyMans
+ // Because each ScriptPubKeyMan may be able to fill more than one input, we need to keep track of each ScriptPubKeyMan that has filled this psbt.
+ // Each iteration, we may fill more inputs than the input that is specified in that iteration.
+ // We assume that each input is filled by only one ScriptPubKeyMan
+ std::set<uint256> visited_spk_mans;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Get the scriptPubKey to know which ScriptPubKeyMan to use
+ CScript script;
+ if (!input.witness_utxo.IsNull()) {
+ script = input.witness_utxo.scriptPubKey;
+ } else if (input.non_witness_utxo) {
+ if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
+ return TransactionError::MISSING_INPUTS;
+ }
+ script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
+ } else {
+ // There's no UTXO so we can just skip this now
+ continue;
+ }
SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+ std::set<ScriptPubKeyMan*> spk_mans = GetScriptPubKeyMans(script, sigdata);
+ if (spk_mans.size() == 0) {
+ continue;
+ }
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(scriptPubKey);
- if (!provider) {
- // We don't know about this scriptpbuKey;
- return false;
+ for (auto& spk_man : spk_mans) {
+ // If we've already been signed by this spk_man, skip it
+ if (visited_spk_mans.count(spk_man->GetID()) > 0) {
+ continue;
+ }
+
+ // Fill in the information from the spk_man
+ TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
+ if (res != TransactionError::OK) {
+ return res;
+ }
+
+ // Add this spk_man to visited_spk_mans so we can skip it later
+ visited_spk_mans.insert(spk_man->GetID());
}
+ }
- if (!ProduceSignature(*provider, MutableTransactionSignatureCreator(&tx, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
- return false;
+ // Complete if every input is now signed
+ complete = true;
+ for (const auto& input : psbtx.inputs) {
+ complete &= PSBTInputSigned(input);
+ }
+
+ return TransactionError::OK;
+}
+
+SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
+{
+ SignatureData sigdata;
+ CScript script_pub_key = GetScriptForDestination(pkhash);
+ for (const auto& spk_man_pair : m_spk_managers) {
+ if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {
+ return spk_man_pair.second->SignMessage(message, pkhash, str_sig);
}
- UpdateInput(input, sigdata);
- nIn++;
}
- return true;
+ return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
@@ -2879,25 +3024,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
- if (sign)
- {
- int nIn = 0;
- for (const auto& coin : selected_coins)
- {
- const CScript& scriptPubKey = coin.txout.scriptPubKey;
- SignatureData sigdata;
-
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(scriptPubKey);
- if (!provider || !ProduceSignature(*provider, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
- {
- strFailReason = _("Signing transaction failed").translated;
- return false;
- } else {
- UpdateInput(txNew.vin.at(nIn), sigdata);
- }
-
- nIn++;
- }
+ if (sign && !SignTransaction(txNew)) {
+ strFailReason = _("Signing transaction failed").translated;
+ return false;
}
// Return the constructed transaction data.
@@ -3105,7 +3234,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
return WalletBatch(*database).EraseName(EncodeDestination(address));
}
-size_t CWallet::KeypoolCountExternalKeys()
+size_t CWallet::KeypoolCountExternalKeys() const
{
AssertLockHeld(cs_wallet);
@@ -3170,7 +3299,7 @@ bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& des
return true;
}
-int64_t CWallet::GetOldestKeyPoolTime()
+int64_t CWallet::GetOldestKeyPoolTime() const
{
LOCK(cs_wallet);
int64_t oldestKey = std::numeric_limits<int64_t>::max();
@@ -3194,7 +3323,7 @@ void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations
}
}
-std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain)
+std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain) const
{
std::map<CTxDestination, CAmount> balances;
@@ -3235,7 +3364,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
return balances;
}
-std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
+std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
{
AssertLockHeld(cs_wallet);
std::set< std::set<CTxDestination> > groupings;
@@ -3997,7 +4126,7 @@ void CWallet::postInitProcess()
chain().requestMempoolTransactions(*this);
}
-bool CWallet::BackupWallet(const std::string& strDest)
+bool CWallet::BackupWallet(const std::string& strDest) const
{
return database->Backup(strDest);
}
@@ -4148,6 +4277,17 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern
return it->second;
}
+std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const
+{
+ std::set<ScriptPubKeyMan*> spk_mans;
+ for (const auto& spk_man_pair : m_spk_managers) {
+ if (spk_man_pair.second->CanProvide(script, sigdata)) {
+ spk_mans.insert(spk_man_pair.second.get());
+ }
+ }
+ return spk_mans;
+}
+
ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const
{
SignatureData sigdata;
@@ -4167,17 +4307,17 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const
return nullptr;
}
-std::unique_ptr<SigningProvider> CWallet::GetSigningProvider(const CScript& script) const
+std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script) const
{
SignatureData sigdata;
- return GetSigningProvider(script, sigdata);
+ return GetSolvingProvider(script, sigdata);
}
-std::unique_ptr<SigningProvider> CWallet::GetSigningProvider(const CScript& script, SignatureData& sigdata) const
+std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script, SignatureData& sigdata) const
{
for (const auto& spk_man_pair : m_spk_managers) {
if (spk_man_pair.second->CanProvide(script, sigdata)) {
- return spk_man_pair.second->GetSigningProvider(script);
+ return spk_man_pair.second->GetSolvingProvider(script);
}
}
return nullptr;