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.
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
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.
Genesis block isn't sufficient, a lot of altcoins share genesis blocks. :( Also there should be a way to override, since moving wallet files between forks is a common way of collecting fork coins-- obviously not something you'd want to happen accidentally but fine as an unsupported "you get to keep both pieces" option.
@gmaxwell, thanks for the suggestion. Just added an override option -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);
Please use another variable name: batch is already used in this context :-)
It's done on purpose in order to reuse the value, computed by it (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):
# ...
self.log.info("Generating initial distinct blockchains")
self.nodes[0].generate(5)
self.nodes[1].generate(5)
self.log.info("Creating wallets")
w0_path = os.path.join(self.nodes[0].datadir, 'w0')
w1_path = os.path.join(self.nodes[1].datadir, 'w1')
self.nodes[0].createwallet(w0_path)
self.nodes[1].createwallet(w1_path)
self.log.info("Restarting nodes") # to close wallets
self.stop_node(0)
self.stop_node(1)
self.start_node(0)
self.start_node(1)
self.log.info("Loading wallet")
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"));
Maybe 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);
I'm not sure this should really be an option?
If there is an option, the name -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:
This comment's described behaviour is definitely wrong: the best known block could be a stale block!
Indeed, but this is going away after the @MeshCollider's comments are addressed. I didn't have much time lately, will do it sometime the next two weeks.
<!--cf906140f33d8803c4a75a2196329ecb-->Needs rebase
<!--5fd3d806e98f4a0ca80977bb178665a0-->There hasn't been much activity lately and the patch still needs rebase, so I am closing this for now. Please let me know when you want to continue working on this, so the pull request can be re-opened.
Looks like I completely forgot about this one. Updated and tested the old branch yesterday - https://github.com/mrwhythat/bitcoin/tree/wallet-file-reuse-prevention. Can it be reopened or should I just open another PR?
I'm unable to re-open this PR, so creating a new one should be ok.