This implements a proposal in #12805.
I follow the suggestion by @ryanofsky and compare a genesis block hash, extracted from wallet’s best block locator, to an chainActive
’s genesis block hash. If they don’t match, the error is reported.
This implements a proposal in #12805.
I follow the suggestion by @ryanofsky and compare a genesis block hash, extracted from wallet’s best block locator, to an chainActive
’s genesis block hash. If they don’t match, the error is reported.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
-allowreusewallets
(maybe should be renamed to something clearer). Still working on sufficiency issue. First of all, since CChain::GetLocator
builds a vHave
sequence deterministically, maybe the whole vectors can be compared, but a lot of forked altcoins share those blocks as well, so I’m looking for an alternative.
4094@@ -4095,13 +4095,28 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
4095
4096 LOCK(cs_main);
4097
4098- CBlockIndex *pindexRescan = chainActive.Genesis();
4099- if (!gArgs.GetBoolArg("-rescan", false))
4100+ CBlockIndex* activeChainGenesis = chainActive.Genesis();
4101+ WalletBatch batch(*walletInstance->database);
batch
is already used in this context :-)
bestBlockRead
) both here and in -rescan
check.
Implemented the new check via FindForkInGlobalIndex
. This solution looks at all blocks known to the wallet and not only at a genesis. It may misbehave in case of temporary forks (reorgs), but there seems to be no simple way around that.
This can be tested with the following function test (here two disconnected nodes in regtest mode generate two different chains with the same genesis, so can be used to model fork coins with shared genesis):
0 # ...
1 self.log.info("Generating initial distinct blockchains")
2 self.nodes[0].generate(5)
3 self.nodes[1].generate(5)
4
5 self.log.info("Creating wallets")
6 w0_path = os.path.join(self.nodes[0].datadir, 'w0')
7 w1_path = os.path.join(self.nodes[1].datadir, 'w1')
8 self.nodes[0].createwallet(w0_path)
9 self.nodes[1].createwallet(w1_path)
10
11 self.log.info("Restarting nodes") # to close wallets
12 self.stop_node(0)
13 self.stop_node(1)
14 self.start_node(0)
15 self.start_node(1)
16
17 self.log.info("Loading wallet")
18 print(self.nodes[0].loadwallet(w1_path)) # this should fail with wallet reuse error
Not sure if this needs a separate test though, so any suggestions on which one should have it appended?
It doesn’t seem like a good idea to prevent a wallet from opening in the case of even a 1 block reorg IMO. Maybe checking 6 blocks back in the chain or something would be better?
Concept ACK though, I think this is still a nice check to have
A new test file here would be fine too I think, but you also need to fix up the existing tests to work with this change too, travis is failing on some of them
4094@@ -4095,6 +4095,24 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
4095
4096 LOCK(cs_main);
4097
4098+ // Unless allowed, ensure wallet files are not reused across chains:
4099+ if (!gArgs.GetBoolArg("-allowreusewallets", false))
DEFAULT_ALLOWREUSEWALLETS
4105+ // Wallet is assumed to be from another chain, if its best known
4106+ // block is not in active chain:
4107+ CBlockIndex *forkIndex = FindForkInGlobalIndex(chainActive, locator);
4108+ if (locator.vHave.size() > 0 && forkIndex && forkIndex->GetBlockHash() != locator.vHave.front())
4109+ {
4110+ InitError(_("Wallet files should not be reused across chains"));
walletInstance->WalletLogPrintf
the hashes or something too
37@@ -38,6 +38,7 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
38 void WalletInit::AddWalletOptions() const
39 {
40 gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
41+ gArgs.AddArg("-allowreusewallets", strprintf("Allow reusing wallet files across chains (default: %u)", DEFAULT_ALLOWREUSEWALLETS), false, OptionsCategory::WALLET);
-walletcrosschain
might be better.
4132+ WalletBatch batch(*walletInstance->database);
4133+ CBlockLocator locator;
4134+ if (batch.ReadBestBlock(locator))
4135+ {
4136+ // Wallet is assumed to be from another chain, if its best known
4137+ // block is not in active chain: