diff --git a/src/bitcoin/d/mod.rs b/src/bitcoin/d/mod.rs index 395dfdee..3efbaa25 100644 --- a/src/bitcoin/d/mod.rs +++ b/src/bitcoin/d/mod.rs @@ -607,7 +607,7 @@ impl BitcoinD { .collect() } - pub fn maybe_unload_watchonly_wallet(&self, watchonly_wallet_path: String) { + fn maybe_unload_watchonly_wallet(&self, watchonly_wallet_path: String) { while self.list_wallets().contains(&watchonly_wallet_path) { log::info!("Found a leftover watchonly wallet loaded on bitcoind. Removing it."); if let Some(e) = self.unload_wallet(watchonly_wallet_path.clone()) { diff --git a/src/lib.rs b/src/lib.rs index 7ed4f670..dd8ee8a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,8 +89,6 @@ pub enum StartupError { DefaultDataDirNotFound, DatadirCreation(path::PathBuf, io::Error), MissingBitcoindConfig, - WindowsCantGuessBitcoindDatadir(path::PathBuf), - WindowsBitcoindWatchonlyDeletion(path::PathBuf, io::Error), Database(SqliteDbError), Bitcoind(BitcoindError), #[cfg(unix)] @@ -113,15 +111,6 @@ impl fmt::Display for StartupError { f, "Our Bitcoin interface is bitcoind but we have no 'bitcoind_config' entry in the configuration." ), - Self::WindowsCantGuessBitcoindDatadir(cookie_path) => write!( - f, - "Cannot guess the path to the bitcoind data directory from the cookie file whose path is '{}'.", - cookie_path.as_path().to_string_lossy() - ), - Self::WindowsBitcoindWatchonlyDeletion(path, e) => write!( - f, - "Error deleting bitcoind watchonly wallet at '{}': {}", path.as_path().to_string_lossy(), e - ), Self::Database(e) => write!(f, "Error initializing database: '{}'.", e), Self::Bitcoind(e) => write!(f, "Error setting up bitcoind interface: '{}'.", e), #[cfg(unix)] @@ -198,54 +187,6 @@ fn setup_sqlite( Ok(sqlite) } -// Windows-specific utility to remove a leftover watchonly wallet within bitcoind's datadir. -#[cfg(windows)] -fn maybe_delete_watchonly_wallet( - bitcoind: &BitcoinD, - bitcoind_cookie_path: &path::Path, - bitcoin_net: miniscript::bitcoin::Network, - wallet_name: &str, -) -> Result<(), StartupError> { - log::info!( - "Trying to guess where the watchonly wallet would be stored in bitcoind's data directory from the cookie path. \ - This might not work if you are using a custom path for the cookie file (very unlikely). In this case please delete the \ - leftover watchonly wallet in bitcoind's datadir by hand if there is any." - ); - // For the main network both the wallet and the cookie file are stored at the root of the - // datadir. For test networks the wallet is in "//wallets//" and - // the cookie file in "//". - let parent_dir = bitcoind_cookie_path.parent().ok_or_else(|| { - StartupError::WindowsCantGuessBitcoindDatadir(bitcoind_cookie_path.to_path_buf()) - })?; - let wallet_path = match bitcoin_net { - miniscript::bitcoin::Network::Bitcoin => parent_dir.join(wallet_name), - miniscript::bitcoin::Network::Testnet - | miniscript::bitcoin::Network::Signet - | miniscript::bitcoin::Network::Regtest => parent_dir.join("wallets").join(wallet_name), - net => panic!( - "Unsupported network '{}', unknown at the time of writing.", - net - ), - }; - - if wallet_path.exists() { - bitcoind.maybe_unload_watchonly_wallet(wallet_path.to_string_lossy().to_string()); - log::info!( - "Deleting the leftover watchonly wallet at '{}'.", - wallet_path.as_path().to_string_lossy() - ); - fs::remove_dir_all(&wallet_path) - .map_err(|e| StartupError::WindowsBitcoindWatchonlyDeletion(wallet_path, e))?; - } else { - log::info!( - "No leftover watchonly wallet found at '{}'.", - wallet_path.as_path().to_string_lossy() - ); - } - - Ok(()) -} - // Connect to bitcoind. Setup the watchonly wallet, and do some sanity checks. // If all went well, returns the interface to bitcoind. fn setup_bitcoind( @@ -253,40 +194,29 @@ fn setup_bitcoind( data_dir: &path::Path, fresh_data_dir: bool, ) -> Result { - // NOTE: this is a hack! We normally store the watchonly wallet within our data directory. - // But on windows bitcoind would prefix the wallet path with "C:\\\\?" when calling - // 'loadwallet'. Therefore instead on Windows store the wallet.dat in bitcoind's data directory - // instead by not providing an absolute path but the name of a wallet. - #[cfg(not(windows))] let wo_path: path::PathBuf = [data_dir, path::Path::new("lianad_watchonly_wallet")] .iter() .collect(); - #[cfg(windows)] - let wo_name = "lianad_watchonly_wallet"; - #[cfg(windows)] - let wo_path = path::Path::new(wo_name); + let wo_path_str = wo_path.to_str().expect("Must be valid unicode").to_string(); + // NOTE: On Windows, paths are canonicalized with a "\\?\" prefix to tell Windows to interpret + // the string "as is" and to ignore the maximum size of a path. HOWEVER this is not properly + // handled by most implementations of the C++ STL's std::filesystem. Therefore bitcoind would + // fail to find the wallet if we didn't strip this prefix. It's not ideal, but a lesser evil + // than other workarounds i could think about. + // See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#win32-file-namespaces + // about the prefix. + // See https://stackoverflow.com/questions/71590689/how-to-properly-handle-windows-paths-with-the-long-path-prefix-with-stdfilesys + // for a discussion of how one C++ STL implementation handles this. + #[cfg(target_os = "windows")] + let wo_path_str = wo_path_str.replace("\\\\?\\", "").replace("\\\\?", ""); let bitcoind_config = config .bitcoind_config .as_ref() .ok_or(StartupError::MissingBitcoindConfig)?; - let bitcoind = BitcoinD::new( - bitcoind_config, - wo_path.to_str().expect("Must be valid unicode").to_string(), - )?; + let bitcoind = BitcoinD::new(bitcoind_config, wo_path_str)?; bitcoind.node_sanity_checks(config.bitcoin_config.network)?; if fresh_data_dir { - // Because of the hack above, the assumption that whenever the data directory is fresh a - // watchonly wallet doesn't exist doesn't hold for Windows. Make sure it does by removing - // any leftover Liana watchonly wallet from bitcoind's data dir. - #[cfg(windows)] - maybe_delete_watchonly_wallet( - &bitcoind, - &bitcoind_config.cookie_path, - config.bitcoin_config.network, - wo_name, - )?; - log::info!("Creating a new watchonly wallet on bitcoind."); bitcoind.create_watchonly_wallet(&config.main_descriptor)?; log::info!("Watchonly wallet created.");