aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/coinselection.cpp2
-rw-r--r--src/wallet/rpc/spend.cpp6
-rw-r--r--src/wallet/rpc/transactions.cpp2
-rw-r--r--src/wallet/rpc/wallet.cpp4
-rw-r--r--src/wallet/scriptpubkeyman.cpp131
-rw-r--r--src/wallet/scriptpubkeyman.h1
-rw-r--r--src/wallet/spend.cpp38
-rw-r--r--src/wallet/sqlite.cpp67
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp1
-rw-r--r--src/wallet/test/wallet_tests.cpp4
-rw-r--r--src/wallet/transaction.h1
-rw-r--r--src/wallet/wallet.cpp5
-rw-r--r--src/wallet/walletdb.cpp4
-rw-r--r--src/wallet/walletdb.h2
14 files changed, 152 insertions, 116 deletions
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 23faad027f..513572da45 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -163,6 +163,8 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
result.AddInput(utxo_pool.at(i));
}
}
+ result.ComputeAndSetWaste(CAmount{0});
+ assert(best_waste == result.GetWaste());
return result;
}
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index 433b5a1815..072879a42a 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -659,7 +659,7 @@ RPCHelpMan fundrawtransaction()
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
- {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
@@ -1056,7 +1056,7 @@ RPCHelpMan send()
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
{"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
- {"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"change_address", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
{"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
@@ -1351,7 +1351,7 @@ RPCHelpMan walletcreatefundedpsbt()
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
- {"changeAddress", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index eef2c13ee1..ad94ce4b32 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -34,6 +34,7 @@ static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue
}
uint256 hash = wtx.GetHash();
entry.pushKV("txid", hash.GetHex());
+ entry.pushKV("wtxid", wtx.GetWitnessHash().GetHex());
UniValue conflicts(UniValue::VARR);
for (const uint256& conflict : wallet.GetTxConflicts(wtx))
conflicts.push_back(conflict.GetHex());
@@ -431,6 +432,7 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{RPCResult::Type::NUM, "blockindex", /*optional=*/true, "The index of the transaction in the block that includes it."},
{RPCResult::Type::NUM_TIME, "blocktime", /*optional=*/true, "The block time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ {RPCResult::Type::STR_HEX, "wtxid", "The hash of serialized transaction, including witness data."},
{RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index 883a3c102b..1380f1a77a 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -315,7 +315,9 @@ static RPCHelpMan createwallet()
{"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
{"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Encrypt the wallet with this passphrase."},
{"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
- {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
+ {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation."
+ " Setting to \"false\" will create a legacy wallet; however, the legacy wallet type is being deprecated and"
+ " support for creating and opening legacy wallets will be removed in the future."},
{"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
{"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
},
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 53f7b773b4..8633e7c62c 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -322,8 +322,6 @@ bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t i
{
LOCK(cs_KeyStore);
- if (m_storage.IsLocked()) return false;
-
auto it = m_inactive_hd_chains.find(seed_id);
if (it == m_inactive_hd_chains.end()) {
return false;
@@ -331,27 +329,14 @@ bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t i
CHDChain& chain = it->second;
- // Top up key pool
- int64_t target_size = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
-
- // "size" of the keypools. Not really the size, actually the difference between index and the chain counter
- // Since chain counter is 1 based and index is 0 based, one of them needs to be offset by 1.
- int64_t kp_size = (internal ? chain.nInternalChainCounter : chain.nExternalChainCounter) - (index + 1);
+ if (internal) {
+ chain.m_next_internal_index = std::max(chain.m_next_internal_index, index + 1);
+ } else {
+ chain.m_next_external_index = std::max(chain.m_next_external_index, index + 1);
+ }
- // make sure the keypool fits the user-selected target (-keypool)
- int64_t missing = std::max(target_size - kp_size, (int64_t) 0);
+ TopUpChain(chain, 0);
- if (missing > 0) {
- WalletBatch batch(m_storage.GetDatabase());
- for (int64_t i = missing; i > 0; --i) {
- GenerateNewKey(batch, chain, internal);
- }
- if (internal) {
- WalletLogPrintf("inactive seed with id %s added %d internal keys\n", HexStr(seed_id), missing);
- } else {
- WalletLogPrintf("inactive seed with id %s added %d keys\n", HexStr(seed_id), missing);
- }
- }
return true;
}
@@ -383,11 +368,26 @@ std::vector<WalletDestination> LegacyScriptPubKeyMan::MarkUnusedAddresses(const
if (it != mapKeyMetadata.end()){
CKeyMetadata meta = it->second;
if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) {
- bool internal = (meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0;
- int64_t index = meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT;
-
- if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) {
- WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__);
+ std::vector<uint32_t> path;
+ if (meta.has_key_origin) {
+ path = meta.key_origin.path;
+ } else if (!ParseHDKeypath(meta.hdKeypath, path)) {
+ WalletLogPrintf("%s: Adding inactive seed keys failed, invalid hdKeypath: %s\n",
+ __func__,
+ meta.hdKeypath);
+ }
+ if (path.size() != 3) {
+ WalletLogPrintf("%s: Adding inactive seed keys failed, invalid path size: %d, has_key_origin: %s\n",
+ __func__,
+ path.size(),
+ meta.has_key_origin);
+ } else {
+ bool internal = (path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0;
+ int64_t index = path[2] & ~BIP32_HARDENED_KEY_LIMIT;
+
+ if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) {
+ WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__);
+ }
}
}
}
@@ -1265,44 +1265,69 @@ bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize)
if (!CanGenerateKeys()) {
return false;
}
- {
- LOCK(cs_KeyStore);
- if (m_storage.IsLocked()) return false;
+ if (!TopUpChain(m_hd_chain, kpSize)) {
+ return false;
+ }
+ for (auto& [chain_id, chain] : m_inactive_hd_chains) {
+ if (!TopUpChain(chain, kpSize)) {
+ return false;
+ }
+ }
+ NotifyCanGetAddressesChanged();
+ return true;
+}
- // Top up key pool
- unsigned int nTargetSize;
- if (kpSize > 0)
- nTargetSize = kpSize;
- else
- nTargetSize = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
+bool LegacyScriptPubKeyMan::TopUpChain(CHDChain& chain, unsigned int kpSize)
+{
+ LOCK(cs_KeyStore);
- // count amount of available keys (internal, external)
- // make sure the keypool of external and internal keys fits the user selected target (-keypool)
- int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0);
- int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0);
+ if (m_storage.IsLocked()) return false;
- if (!IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD_SPLIT))
- {
- // don't create extra internal keys
- missingInternal = 0;
+ // Top up key pool
+ unsigned int nTargetSize;
+ if (kpSize > 0) {
+ nTargetSize = kpSize;
+ } else {
+ nTargetSize = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{0});
+ }
+ int64_t target = std::max((int64_t) nTargetSize, int64_t{1});
+
+ // count amount of available keys (internal, external)
+ // make sure the keypool of external and internal keys fits the user selected target (-keypool)
+ int64_t missingExternal;
+ int64_t missingInternal;
+ if (chain == m_hd_chain) {
+ missingExternal = std::max(target - (int64_t)setExternalKeyPool.size(), int64_t{0});
+ missingInternal = std::max(target - (int64_t)setInternalKeyPool.size(), int64_t{0});
+ } else {
+ missingExternal = std::max(target - (chain.nExternalChainCounter - chain.m_next_external_index), int64_t{0});
+ missingInternal = std::max(target - (chain.nInternalChainCounter - chain.m_next_internal_index), int64_t{0});
+ }
+
+ if (!IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) {
+ // don't create extra internal keys
+ missingInternal = 0;
+ }
+ bool internal = false;
+ WalletBatch batch(m_storage.GetDatabase());
+ for (int64_t i = missingInternal + missingExternal; i--;) {
+ if (i < missingInternal) {
+ internal = true;
}
- bool internal = false;
- WalletBatch batch(m_storage.GetDatabase());
- for (int64_t i = missingInternal + missingExternal; i--;)
- {
- if (i < missingInternal) {
- internal = true;
- }
- CPubKey pubkey(GenerateNewKey(batch, m_hd_chain, internal));
+ CPubKey pubkey(GenerateNewKey(batch, chain, internal));
+ if (chain == m_hd_chain) {
AddKeypoolPubkeyWithDB(pubkey, internal, batch);
}
- if (missingInternal + missingExternal > 0) {
+ }
+ if (missingInternal + missingExternal > 0) {
+ if (chain == m_hd_chain) {
WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size());
+ } else {
+ WalletLogPrintf("inactive seed with id %s added %d external keys, %d internal keys\n", HexStr(chain.seed_id), missingExternal, missingInternal);
}
}
- NotifyCanGetAddressesChanged();
return true;
}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 6eda133771..ad924791af 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -355,6 +355,7 @@ private:
*/
bool TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal);
+ bool TopUpChain(CHDChain& chain, unsigned int size);
public:
using ScriptPubKeyMan::ScriptPubKeyMan;
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 83eaececc1..a707ef89d2 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -379,7 +379,6 @@ std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAm
// Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, true /* positive_only */);
if (auto bnb_result{SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change)}) {
- bnb_result->ComputeAndSetWaste(CAmount(0));
results.push_back(*bnb_result);
}
@@ -583,12 +582,13 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256&
}
/**
- * Return a height-based locktime for new transactions (uses the height of the
+ * Set a height-based locktime for new transactions (uses the height of the
* current chain tip unless we are not synced with the current chain
*/
-static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height)
+static void DiscourageFeeSniping(CMutableTransaction& tx, interfaces::Chain& chain, const uint256& block_hash, int block_height)
{
- uint32_t locktime;
+ // All inputs must be added by now
+ assert(!tx.vin.empty());
// Discourage fee sniping.
//
// For a large miner the value of the transactions in the best block and
@@ -610,22 +610,34 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin
// now we ensure code won't be written that makes assumptions about
// nLockTime that preclude a fix later.
if (IsCurrentForAntiFeeSniping(chain, block_hash)) {
- locktime = block_height;
+ tx.nLockTime = block_height;
// Secondly occasionally randomly pick a nLockTime even further back, so
// that transactions that are delayed after signing for whatever reason,
// e.g. high-latency mix networks and some CoinJoin implementations, have
// better privacy.
- if (GetRandInt(10) == 0)
- locktime = std::max(0, (int)locktime - GetRandInt(100));
+ if (GetRandInt(10) == 0) {
+ tx.nLockTime = std::max(0, int(tx.nLockTime) - GetRandInt(100));
+ }
} else {
// If our chain is lagging behind, we can't discourage fee sniping nor help
// the privacy of high-latency transactions. To avoid leaking a potentially
// unique "nLockTime fingerprint", set nLockTime to a constant.
- locktime = 0;
+ tx.nLockTime = 0;
+ }
+ // Sanity check all values
+ assert(tx.nLockTime < LOCKTIME_THRESHOLD); // Type must be block height
+ assert(tx.nLockTime <= uint64_t(block_height));
+ for (const auto& in : tx.vin) {
+ // Can not be FINAL for locktime to work
+ assert(in.nSequence != CTxIn::SEQUENCE_FINAL);
+ // May be MAX NONFINAL to disable both BIP68 and BIP125
+ if (in.nSequence == CTxIn::MAX_SEQUENCE_NONFINAL) continue;
+ // May be MAX BIP125 to disable BIP68 and enable BIP125
+ if (in.nSequence == MAX_BIP125_RBF_SEQUENCE) continue;
+ // The wallet does not support any other sequence-use right now.
+ assert(false);
}
- assert(locktime < LOCKTIME_THRESHOLD);
- return locktime;
}
static bool CreateTransactionInternal(
@@ -642,7 +654,6 @@ static bool CreateTransactionInternal(
AssertLockHeld(wallet.cs_wallet);
CMutableTransaction txNew; // The resulting transaction that we make
- txNew.nLockTime = GetLocktimeForNewTransaction(wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
@@ -788,8 +799,8 @@ static bool CreateTransactionInternal(
// Shuffle selected coins and fill in final vin
std::vector<CInputCoin> selected_coins = result->GetShuffledInputVector();
- // Note how the sequence number is set to non-maxint so that
- // the nLockTime set above actually works.
+ // The sequence number is set to non-maxint so that DiscourageFeeSniping
+ // works.
//
// BIP125 defines opt-in RBF as any nSequence < maxint-1, so
// we use the highest possible value in that range (maxint-2)
@@ -800,6 +811,7 @@ static bool CreateTransactionInternal(
for (const auto& coin : selected_coins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
+ DiscourageFeeSniping(txNew, wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());
// Calculate the transaction fee
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 2b2181e70b..55949e6e7a 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -37,6 +37,22 @@ static void ErrorLogCallback(void* arg, int code, const char* msg)
LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg);
}
+static bool BindBlobToStatement(sqlite3_stmt* stmt,
+ int index,
+ Span<const std::byte> blob,
+ const std::string& description)
+{
+ int res = sqlite3_bind_blob(stmt, index, blob.data(), blob.size(), SQLITE_STATIC);
+ if (res != SQLITE_OK) {
+ LogPrintf("Unable to bind %s to statement: %s\n", description, sqlite3_errstr(res));
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
+ return false;
+ }
+
+ return true;
+}
+
static std::optional<int> ReadPragmaInteger(sqlite3* db, const std::string& key, const std::string& description, bilingual_str& error)
{
std::string stmt_text = strprintf("PRAGMA %s", key);
@@ -377,14 +393,8 @@ bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value)
assert(m_read_stmt);
// Bind: leftmost parameter in statement is index 1
- int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(m_read_stmt);
- sqlite3_reset(m_read_stmt);
- return false;
- }
- res = sqlite3_step(m_read_stmt);
+ if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;
+ int res = sqlite3_step(m_read_stmt);
if (res != SQLITE_ROW) {
if (res != SQLITE_DONE) {
// SQLITE_DONE means "not found", don't log an error in that case.
@@ -418,23 +428,11 @@ bool SQLiteBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrit
// Bind: leftmost parameter in statement is index 1
// Insert index 1 is key, 2 is value
- int res = sqlite3_bind_blob(stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind key to statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(stmt);
- sqlite3_reset(stmt);
- return false;
- }
- res = sqlite3_bind_blob(stmt, 2, value.data(), value.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind value to statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(stmt);
- sqlite3_reset(stmt);
- return false;
- }
+ if (!BindBlobToStatement(stmt, 1, key, "key")) return false;
+ if (!BindBlobToStatement(stmt, 2, value, "value")) return false;
// Execute
- res = sqlite3_step(stmt);
+ int res = sqlite3_step(stmt);
sqlite3_clear_bindings(stmt);
sqlite3_reset(stmt);
if (res != SQLITE_DONE) {
@@ -449,16 +447,10 @@ bool SQLiteBatch::EraseKey(CDataStream&& key)
assert(m_delete_stmt);
// Bind: leftmost parameter in statement is index 1
- int res = sqlite3_bind_blob(m_delete_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(m_delete_stmt);
- sqlite3_reset(m_delete_stmt);
- return false;
- }
+ if (!BindBlobToStatement(m_delete_stmt, 1, key, "key")) return false;
// Execute
- res = sqlite3_step(m_delete_stmt);
+ int res = sqlite3_step(m_delete_stmt);
sqlite3_clear_bindings(m_delete_stmt);
sqlite3_reset(m_delete_stmt);
if (res != SQLITE_DONE) {
@@ -473,18 +465,11 @@ bool SQLiteBatch::HasKey(CDataStream&& key)
assert(m_read_stmt);
// Bind: leftmost parameter in statement is index 1
- bool ret = false;
- int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res == SQLITE_OK) {
- res = sqlite3_step(m_read_stmt);
- if (res == SQLITE_ROW) {
- ret = true;
- }
- }
-
+ if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;
+ int res = sqlite3_step(m_read_stmt);
sqlite3_clear_bindings(m_read_stmt);
sqlite3_reset(m_read_stmt);
- return ret;
+ return res == SQLITE_ROW;
}
bool SQLiteBatch::StartCursor()
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index b953f402a2..62053ae8d2 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -68,7 +68,6 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Try to sign the mutated input
SignatureData sigdata;
BOOST_CHECK(m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true, true) != TransactionError::OK);
- //BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
}
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 7693c9c0e8..c59f7e6f05 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -367,10 +367,10 @@ static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lock
CBlockIndex* block = nullptr;
if (blockTime > 0) {
LOCK(cs_main);
- auto inserted = chainman.BlockIndex().emplace(GetRandHash(), new CBlockIndex);
+ auto inserted = chainman.BlockIndex().emplace(std::piecewise_construct, std::make_tuple(GetRandHash()), std::make_tuple());
assert(inserted.second);
const uint256& hash = inserted.first->first;
- block = inserted.first->second;
+ block = &inserted.first->second;
block->nTime = blockTime;
block->phashBlock = &hash;
state = TxStateConfirmed{hash, block->nHeight, /*position_in_block=*/0};
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 00f9c9f154..271d698e56 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -296,6 +296,7 @@ public:
bool isUnconfirmed() const { return !isAbandoned() && !isConflicted() && !isConfirmed(); }
bool isConfirmed() const { return state<TxStateConfirmed>(); }
const uint256& GetHash() const { return tx->GetHash(); }
+ const uint256& GetWitnessHash() const { return tx->GetWitnessHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
// Disable copying of CWalletTx objects to prevent bugs where instances get
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6cf9f9ce74..261d042529 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -354,6 +354,11 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
// Write the wallet settings
UpdateWalletSetting(*context.chain, name, load_on_start, warnings);
+ // Legacy wallets are being deprecated, warn if a newly created wallet is legacy
+ if (!(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS)) {
+ warnings.push_back(_("Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future."));
+ }
+
status = DatabaseStatus::SUCCESS;
return wallet;
}
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 2d88b07146..6e100524a4 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -557,9 +557,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
if (internal) {
chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT;
- chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index);
+ chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1);
} else {
- chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index);
+ chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1);
}
}
} else if (strType == DBKeys::WATCHMETA) {
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 760019e76b..3dfe781d56 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -93,6 +93,8 @@ public:
uint32_t nExternalChainCounter;
uint32_t nInternalChainCounter;
CKeyID seed_id; //!< seed hash160
+ int64_t m_next_external_index{0}; // Next index in the keypool to be used. Memory only.
+ int64_t m_next_internal_index{0}; // Next index in the keypool to be used. Memory only.
static const int VERSION_HD_BASE = 1;
static const int VERSION_HD_CHAIN_SPLIT = 2;