Skip to content

Commit 2d6e76a

Browse files
committed
Merge #17261: Make ScriptPubKeyMan an actual interface and the wallet to have multiple
3f37365 Refactor: Replace SigningProvider pointers with unique_ptrs (Andrew Chow) 3afe53c Cleanup: Drop unused GUI learnRelatedScripts method (Andrew Chow) e2f02aa Refactor: Copy CWallet signals and print function to LegacyScriptPubKeyMan (Andrew Chow) c729afd Box the wallet: Add multiple keyman maps and loops (Andrew Chow) 4977c30 refactor: define a UINT256_ONE global constant (Andrew Chow) 415afcc HD Split: Avoid redundant upgrades (Andrew Chow) 01b4511 Make UpgradeKeyMetadata work only on LegacyScriptPubKeyMan (Andrew Chow) 4a7e43e Store p2sh scripts in AddAndGetDestinationForScript (Andrew Chow) 501acb5 Always try to sign for all pubkeys in multisig (Andrew Chow) 81610ed List output types in an array in order to be iterated over (Andrew Chow) eb81fc3 Refactor: Allow LegacyScriptPubKeyMan to be null (Andrew Chow) fadc08a Locking: Lock cs_KeyStore instead of cs_wallet in legacy keyman (Andrew Chow) f5be479 wallet: Improve CWallet:MarkDestinationsDirty (João Barbosa) Pull request description: Continuation of wallet boxes project. Actually makes ScriptPubKeyMan an interface which LegacyScriptPubkeyMan. Moves around functions and things from CWallet into LegacyScriptPubKeyMan so that they are actually separate things without circular dependencies. *** Introducing the `ScriptPubKeyMan` (short for ScriptPubKeyManager) for managing scriptPubKeys and their associated scripts and keys. This functionality is moved over from `CWallet`. Instead, `CWallet` will have a pointer to a `ScriptPubKeyMan` for every possible address type, internal and external. It will fetch the correct `ScriptPubKeyMan` as necessary. When fetching new addresses, it chooses the `ScriptPubKeyMan` based on address type and whether it is change. For signing, it takes the script and asks each `ScriptPubKeyMan` for whether that `ScriptPubKeyMan` considers that script `IsMine`, whether it has that script, or whether it is able to produce a signature for it. If so, the `ScriptPubKeyMan` will provide a `SigningProvider` to the caller which will use that in order to sign. There is currently one `ScriptPubKeyMan` - the `LegacyScriptPubKeyMan`. Each `CWallet` will have only one `LegacyScriptPubKeyMan` with the pointers for all of the address types and change pointing to this `LegacyScriptPubKeyMan`. It is created when the wallet is loaded and all keys and metadata are loaded into it instead of `CWallet`. The `LegacyScriptPubKeyMan` is primarily made up of all of the key and script management that used to be in `CWallet`. For convenience, `CWallet` has a `GetLegacyScriptPubKeyMan` which will return the `LegacyScriptPubKeyMan` or a `nullptr` if it does not have one (not yet implemented, but callers will check for the `nullptr`). For purposes of signing, `LegacyScriptPubKeyMan`'s `GetSigningProvider` will return itself rather than a separate `SigningProvider`. This will be different for future `ScriptPubKeyMan`s. The `LegacyScriptPubKeyMan` will also handle the importing and exporting of keys and scripts instead of `CWallet`. As such, a number of RPCs have been limited to work only if a `LegacyScriptPubKeyMan` can be retrieved from the wallet. These RPCs are `sethdseed`, `addmultisigaddress`, `importaddress`, `importprivkey`, `importpubkey`, `importmulti`, `dumpprivkey`, and `dumpwallet`. Other RPCs which relied on the wallet for scripts and keys have been modified in order to take the `SigningProvider` retrieved from the `ScriptPubKeyMan` for a given script. Overall, these changes should not effect how everything actually works and the user should experience no difference between having this change and not having it. As such, no functional tests were changed, and the only unit tests changed were those that were directly accessing `CWallet` functions that have been removed. This PR is the last step in the [Wallet Structure Changes](https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Class-Structure-Changes). ACKs for top commit: instagibbs: re-utACK bitcoin/bitcoin@3f37365 Sjors: re-utACK 3f37365 (it still compiles on macOS after bitcoin/bitcoin#17261 (comment)) meshcollider: Tested re-ACK 3f37365 Tree-SHA512: f8e2b8d9efa750b617691e8702d217ec4c33569ec2554a060141d9eb9b9a3a5323e4216938e2485c44625d7a6e0925d40dea1362b3af9857cf08860c2f344716
2 parents 638239d + 3f37365 commit 2d6e76a

30 files changed

+481
-261
lines changed

src/bench/coin_selection.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ static void CoinSelection(benchmark::State& state)
3131
{
3232
NodeContext node;
3333
auto chain = interfaces::MakeChain(node);
34-
const CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
34+
CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
35+
wallet.SetupLegacyScriptPubKeyMan();
3536
std::vector<std::unique_ptr<CWalletTx>> wtxs;
3637
LOCK(wallet.cs_wallet);
3738

@@ -64,7 +65,7 @@ static void CoinSelection(benchmark::State& state)
6465
typedef std::set<CInputCoin> CoinSet;
6566
static NodeContext testNode;
6667
static auto testChain = interfaces::MakeChain(testNode);
67-
static const CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
68+
static CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
6869
std::vector<std::unique_ptr<CWalletTx>> wtxn;
6970

7071
// Copied from src/wallet/test/coinselector_tests.cpp
@@ -93,6 +94,7 @@ static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
9394
static void BnBExhaustion(benchmark::State& state)
9495
{
9596
// Setup
97+
testWallet.SetupLegacyScriptPubKeyMan();
9698
std::vector<OutputGroup> utxo_pool;
9799
CoinSet selection;
98100
CAmount value_ret = 0;

src/bench/wallet_balance.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ static void WalletBalance(benchmark::State& state, const bool set_dirty, const b
2020
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
2121
CWallet wallet{chain.get(), WalletLocation(), WalletDatabase::CreateMock()};
2222
{
23+
wallet.SetupLegacyScriptPubKeyMan();
2324
bool first_run;
2425
if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false);
2526
wallet.handleNotifications();

src/interfaces/wallet.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ class WalletImpl : public Wallet
119119
}
120120
bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override
121121
{
122-
const SigningProvider* provider = m_wallet->GetSigningProvider(script);
122+
std::unique_ptr<SigningProvider> provider = m_wallet->GetSigningProvider(script);
123123
if (provider) {
124124
return provider->GetPubKey(address, pub_key);
125125
}
126126
return false;
127127
}
128128
bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) override
129129
{
130-
const SigningProvider* provider = m_wallet->GetSigningProvider(script);
130+
std::unique_ptr<SigningProvider> provider = m_wallet->GetSigningProvider(script);
131131
if (provider) {
132132
return provider->GetKey(address, key);
133133
}
@@ -180,7 +180,6 @@ class WalletImpl : public Wallet
180180
}
181181
return result;
182182
}
183-
void learnRelatedScripts(const CPubKey& key, OutputType type) override { m_wallet->GetLegacyScriptPubKeyMan()->LearnRelatedScripts(key, type); }
184183
bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override
185184
{
186185
LOCK(m_wallet->cs_wallet);

src/interfaces/wallet.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ class Wallet
108108
//! Get wallet address list.
109109
virtual std::vector<WalletAddress> getAddresses() = 0;
110110

111-
//! Add scripts to key store so old so software versions opening the wallet
112-
//! database can detect payments to newer address types.
113-
virtual void learnRelatedScripts(const CPubKey& key, OutputType type) = 0;
114-
115111
//! Add dest data.
116112
virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0;
117113

src/outputtype.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
1919
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
2020
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
2121

22+
const std::array<OutputType, 3> OUTPUT_TYPES = {OutputType::LEGACY, OutputType::P2SH_SEGWIT, OutputType::BECH32};
23+
2224
bool ParseOutputType(const std::string& type, OutputType& output_type)
2325
{
2426
if (type == OUTPUT_TYPE_STRING_LEGACY) {
@@ -80,22 +82,30 @@ CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore,
8082
{
8183
// Add script to keystore
8284
keystore.AddCScript(script);
85+
ScriptHash sh(script);
8386
// Note that scripts over 520 bytes are not yet supported.
8487
switch (type) {
8588
case OutputType::LEGACY:
86-
return ScriptHash(script);
89+
keystore.AddCScript(GetScriptForDestination(sh));
90+
return sh;
8791
case OutputType::P2SH_SEGWIT:
8892
case OutputType::BECH32: {
8993
CTxDestination witdest = WitnessV0ScriptHash(script);
9094
CScript witprog = GetScriptForDestination(witdest);
9195
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
92-
if (!IsSolvable(keystore, witprog)) return ScriptHash(script);
96+
if (!IsSolvable(keystore, witprog)) {
97+
// Since the wsh is invalid, add and return the sh instead.
98+
keystore.AddCScript(GetScriptForDestination(sh));
99+
return sh;
100+
}
93101
// Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
94102
keystore.AddCScript(witprog);
95103
if (type == OutputType::BECH32) {
96104
return witdest;
97105
} else {
98-
return ScriptHash(witprog);
106+
ScriptHash sh_w = ScriptHash(witprog);
107+
keystore.AddCScript(GetScriptForDestination(sh_w));
108+
return sh_w;
99109
}
100110
}
101111
default: assert(false);

src/outputtype.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <script/signingprovider.h>
1111
#include <script/standard.h>
1212

13+
#include <array>
1314
#include <string>
1415
#include <vector>
1516

@@ -27,6 +28,8 @@ enum class OutputType {
2728
CHANGE_AUTO,
2829
};
2930

31+
extern const std::array<OutputType, 3> OUTPUT_TYPES;
32+
3033
NODISCARD bool ParseOutputType(const std::string& str, OutputType& output_type);
3134
const std::string& FormatOutputType(OutputType type);
3235

@@ -47,4 +50,3 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
4750
CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType);
4851

4952
#endif // BITCOIN_OUTPUTTYPE_H
50-

src/qt/test/addressbooktests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
5959
{
6060
TestChain100Setup test;
6161
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock());
62+
wallet->SetupLegacyScriptPubKeyMan();
6263
bool firstRun;
6364
wallet->LoadWallet(firstRun);
6465

src/qt/test/wallettests.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,9 @@ void TestGUI(interfaces::Node& node)
143143
bool firstRun;
144144
wallet->LoadWallet(firstRun);
145145
{
146-
auto spk_man = wallet->GetLegacyScriptPubKeyMan();
146+
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
147147
auto locked_chain = wallet->chain().lock();
148-
LOCK(wallet->cs_wallet);
149-
AssertLockHeld(spk_man->cs_wallet);
148+
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
150149
wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
151150
spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
152151
wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());

src/script/interpreter.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,13 +1281,11 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
12811281
return ss.GetHash();
12821282
}
12831283

1284-
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
1285-
12861284
// Check for invalid use of SIGHASH_SINGLE
12871285
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
12881286
if (nIn >= txTo.vout.size()) {
12891287
// nOut out of range
1290-
return one;
1288+
return UINT256_ONE();
12911289
}
12921290
}
12931291

src/script/sign.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,13 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
144144
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
145145
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
146146
CPubKey pubkey = CPubKey(vSolutions[i]);
147-
if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) {
148-
ret.push_back(std::move(sig));
147+
// We need to always call CreateSig in order to fill sigdata with all
148+
// possible signatures that we can create. This will allow further PSBT
149+
// processing to work as it needs all possible signature and pubkey pairs
150+
if (CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) {
151+
if (ret.size() < required + 1) {
152+
ret.push_back(std::move(sig));
153+
}
149154
}
150155
}
151156
bool ok = ret.size() == required + 1;

0 commit comments

Comments
 (0)