mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 11:32:46 +01:00
merge bitcoin#23721: Move restorewallet() logic to the wallet section
This commit is contained in:
parent
e47d5ac81e
commit
04a3f65032
@ -346,6 +346,9 @@ public:
|
|||||||
//! Return default wallet directory.
|
//! Return default wallet directory.
|
||||||
virtual std::string getWalletDir() = 0;
|
virtual std::string getWalletDir() = 0;
|
||||||
|
|
||||||
|
//! Restore backup wallet
|
||||||
|
virtual std::unique_ptr<Wallet> restoreWallet(const std::string& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
|
||||||
|
|
||||||
//! Return available wallets in wallet directory.
|
//! Return available wallets in wallet directory.
|
||||||
virtual std::vector<std::string> listWalletDir() = 0;
|
virtual std::vector<std::string> listWalletDir() = 0;
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ enum RPCErrorCode
|
|||||||
RPC_WALLET_NOT_FOUND = -18, //!< Invalid wallet specified
|
RPC_WALLET_NOT_FOUND = -18, //!< Invalid wallet specified
|
||||||
RPC_WALLET_NOT_SPECIFIED = -19, //!< No wallet specified (error when there are multiple wallets loaded)
|
RPC_WALLET_NOT_SPECIFIED = -19, //!< No wallet specified (error when there are multiple wallets loaded)
|
||||||
RPC_WALLET_ALREADY_LOADED = -35, //!< This same wallet is already loaded
|
RPC_WALLET_ALREADY_LOADED = -35, //!< This same wallet is already loaded
|
||||||
|
RPC_WALLET_ALREADY_EXISTS = -36, //!< There is already a wallet with the same name
|
||||||
|
|
||||||
|
|
||||||
//! Backwards compatible aliases
|
//! Backwards compatible aliases
|
||||||
|
@ -222,6 +222,7 @@ enum class DatabaseStatus {
|
|||||||
FAILED_LOAD,
|
FAILED_LOAD,
|
||||||
FAILED_VERIFY,
|
FAILED_VERIFY,
|
||||||
FAILED_ENCRYPT,
|
FAILED_ENCRYPT,
|
||||||
|
FAILED_INVALID_BACKUP_FILE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Recursively list database paths in directory. */
|
/** Recursively list database paths in directory. */
|
||||||
|
@ -607,6 +607,12 @@ public:
|
|||||||
assert(m_context.m_coinjoin_loader);
|
assert(m_context.m_coinjoin_loader);
|
||||||
return MakeWallet(LoadWallet(*m_context.chain, *m_context.m_coinjoin_loader, name, true /* load_on_start */, options, status, error, warnings));
|
return MakeWallet(LoadWallet(*m_context.chain, *m_context.m_coinjoin_loader, name, true /* load_on_start */, options, status, error, warnings));
|
||||||
}
|
}
|
||||||
|
std::unique_ptr<Wallet> restoreWallet(const std::string& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
|
||||||
|
{
|
||||||
|
DatabaseStatus status;
|
||||||
|
assert(m_context.m_coinjoin_loader);
|
||||||
|
return MakeWallet(RestoreWallet(*m_context.chain, *m_context.m_coinjoin_loader, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings));
|
||||||
|
}
|
||||||
std::string getWalletDir() override
|
std::string getWalletDir() override
|
||||||
{
|
{
|
||||||
return GetWalletDir().string();
|
return GetWalletDir().string();
|
||||||
|
@ -2716,16 +2716,8 @@ static RPCHelpMan listwallets()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWalletHelper(WalletContext& context, UniValue load_on_start_param, const std::string wallet_name)
|
void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error)
|
||||||
{
|
{
|
||||||
DatabaseOptions options;
|
|
||||||
DatabaseStatus status;
|
|
||||||
options.require_existing = true;
|
|
||||||
bilingual_str error;
|
|
||||||
std::vector<bilingual_str> warnings;
|
|
||||||
std::optional<bool> load_on_start = load_on_start_param.isNull() ? std::nullopt : std::optional<bool>(load_on_start_param.get_bool());
|
|
||||||
std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, *context.m_coinjoin_loader, wallet_name, load_on_start, options, status, error, warnings);
|
|
||||||
|
|
||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
// Map bad format to not found, since bad format is returned when the
|
// Map bad format to not found, since bad format is returned when the
|
||||||
// wallet directory exists, but doesn't contain a data file.
|
// wallet directory exists, but doesn't contain a data file.
|
||||||
@ -2738,13 +2730,17 @@ static std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWall
|
|||||||
case DatabaseStatus::FAILED_ALREADY_LOADED:
|
case DatabaseStatus::FAILED_ALREADY_LOADED:
|
||||||
code = RPC_WALLET_ALREADY_LOADED;
|
code = RPC_WALLET_ALREADY_LOADED;
|
||||||
break;
|
break;
|
||||||
|
case DatabaseStatus::FAILED_ALREADY_EXISTS:
|
||||||
|
code = RPC_WALLET_ALREADY_EXISTS;
|
||||||
|
break;
|
||||||
|
case DatabaseStatus::FAILED_INVALID_BACKUP_FILE:
|
||||||
|
code = RPC_INVALID_PARAMETER;
|
||||||
|
break;
|
||||||
default: // RPC_WALLET_ERROR is returned for all other cases.
|
default: // RPC_WALLET_ERROR is returned for all other cases.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
throw JSONRPCError(code, error.original);
|
throw JSONRPCError(code, error.original);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { wallet, warnings };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static RPCHelpMan upgradetohd()
|
static RPCHelpMan upgradetohd()
|
||||||
@ -2872,7 +2868,15 @@ static RPCHelpMan loadwallet()
|
|||||||
WalletContext& context = EnsureWalletContext(request.context);
|
WalletContext& context = EnsureWalletContext(request.context);
|
||||||
const std::string name(request.params[0].get_str());
|
const std::string name(request.params[0].get_str());
|
||||||
|
|
||||||
auto [wallet, warnings] = LoadWalletHelper(context, request.params[1], name);
|
DatabaseOptions options;
|
||||||
|
DatabaseStatus status;
|
||||||
|
options.require_existing = true;
|
||||||
|
bilingual_str error;
|
||||||
|
std::vector<bilingual_str> warnings;
|
||||||
|
std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
|
||||||
|
std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, *context.m_coinjoin_loader, name, load_on_start, options, status, error, warnings);
|
||||||
|
|
||||||
|
HandleWalletError(wallet, status, error);
|
||||||
|
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
obj.pushKV("name", wallet->GetName());
|
obj.pushKV("name", wallet->GetName());
|
||||||
@ -3072,27 +3076,17 @@ static RPCHelpMan restorewallet()
|
|||||||
|
|
||||||
std::string backup_file = request.params[1].get_str();
|
std::string backup_file = request.params[1].get_str();
|
||||||
|
|
||||||
if (!fs::exists(backup_file)) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Backup file does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string wallet_name = request.params[0].get_str();
|
std::string wallet_name = request.params[0].get_str();
|
||||||
|
|
||||||
const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_name);
|
std::optional<bool> load_on_start = request.params[2].isNull() ? std::nullopt : std::optional<bool>(request.params[2].get_bool());
|
||||||
|
|
||||||
if (fs::exists(wallet_path)) {
|
DatabaseStatus status;
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Wallet name already exists.");
|
bilingual_str error;
|
||||||
}
|
std::vector<bilingual_str> warnings;
|
||||||
|
|
||||||
if (!TryCreateDirectories(wallet_path)) {
|
const std::shared_ptr<CWallet> wallet = RestoreWallet(*context.chain, *context.m_coinjoin_loader, backup_file, wallet_name, load_on_start, status, error, warnings);
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto wallet_file = wallet_path / "wallet.dat";
|
HandleWalletError(wallet, status, error);
|
||||||
|
|
||||||
fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists);
|
|
||||||
|
|
||||||
auto [wallet, warnings] = LoadWalletHelper(context, request.params[2], wallet_name);
|
|
||||||
|
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
obj.pushKV("name", wallet->GetName());
|
obj.pushKV("name", wallet->GetName());
|
||||||
|
@ -365,6 +365,38 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, interfaces::Coin
|
|||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CWallet> RestoreWallet(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoin_loader, const std::string& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
|
||||||
|
{
|
||||||
|
DatabaseOptions options;
|
||||||
|
options.require_existing = true;
|
||||||
|
|
||||||
|
if (!fs::exists(backup_file)) {
|
||||||
|
error = Untranslated("Backup file does not exist");
|
||||||
|
status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_name);
|
||||||
|
|
||||||
|
if (fs::exists(wallet_path) || !TryCreateDirectories(wallet_path)) {
|
||||||
|
error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.string()));
|
||||||
|
status = DatabaseStatus::FAILED_ALREADY_EXISTS;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wallet_file = wallet_path / "wallet.dat";
|
||||||
|
fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists);
|
||||||
|
|
||||||
|
auto wallet = LoadWallet(chain, coinjoin_loader, wallet_name, load_on_start, options, status, error, warnings);
|
||||||
|
|
||||||
|
if (!wallet) {
|
||||||
|
fs::remove(wallet_file);
|
||||||
|
fs::remove(wallet_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
/** @defgroup mapWallet
|
/** @defgroup mapWallet
|
||||||
*
|
*
|
||||||
* @{
|
* @{
|
||||||
|
@ -62,6 +62,7 @@ std::vector<std::shared_ptr<CWallet>> GetWallets();
|
|||||||
std::shared_ptr<CWallet> GetWallet(const std::string& name);
|
std::shared_ptr<CWallet> GetWallet(const std::string& name);
|
||||||
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoin_loader, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
|
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoin_loader, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
|
||||||
std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoin_loader, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
|
std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoin_loader, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
|
||||||
|
std::shared_ptr<CWallet> RestoreWallet(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoin_loader, const std::string& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
|
||||||
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
|
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
|
||||||
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
|
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
|
||||||
|
|
||||||
|
@ -107,17 +107,32 @@ class WalletBackupTest(BitcoinTestFramework):
|
|||||||
os.remove(os.path.join(self.nodes[1].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
|
os.remove(os.path.join(self.nodes[1].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
|
||||||
os.remove(os.path.join(self.nodes[2].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
|
os.remove(os.path.join(self.nodes[2].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
|
||||||
|
|
||||||
|
def restore_invalid_wallet(self):
|
||||||
|
node = self.nodes[3]
|
||||||
|
invalid_wallet_file = os.path.join(self.nodes[0].datadir, 'invalid_wallet_file.bak')
|
||||||
|
open(invalid_wallet_file, 'a', encoding="utf8").write('invald wallet')
|
||||||
|
wallet_name = "res0"
|
||||||
|
not_created_wallet_file = os.path.join(node.datadir, self.chain, 'wallets', wallet_name)
|
||||||
|
error_message = "Wallet file verification failed. Failed to load database path '{}'. Data is not in recognized format.".format(not_created_wallet_file)
|
||||||
|
assert_raises_rpc_error(-18, error_message, node.restorewallet, wallet_name, invalid_wallet_file)
|
||||||
|
assert not os.path.exists(not_created_wallet_file)
|
||||||
|
|
||||||
def restore_nonexistent_wallet(self):
|
def restore_nonexistent_wallet(self):
|
||||||
node = self.nodes[3]
|
node = self.nodes[3]
|
||||||
nonexistent_wallet_file = os.path.join(self.nodes[0].datadir, 'nonexistent_wallet.bak')
|
nonexistent_wallet_file = os.path.join(self.nodes[0].datadir, 'nonexistent_wallet.bak')
|
||||||
wallet_name = "res0"
|
wallet_name = "res0"
|
||||||
assert_raises_rpc_error(-8, "Backup file does not exist", node.restorewallet, wallet_name, nonexistent_wallet_file)
|
assert_raises_rpc_error(-8, "Backup file does not exist", node.restorewallet, wallet_name, nonexistent_wallet_file)
|
||||||
|
not_created_wallet_file = os.path.join(node.datadir, self.chain, 'wallets', wallet_name)
|
||||||
|
assert not os.path.exists(not_created_wallet_file)
|
||||||
|
|
||||||
def restore_wallet_existent_name(self):
|
def restore_wallet_existent_name(self):
|
||||||
node = self.nodes[3]
|
node = self.nodes[3]
|
||||||
wallet_file = os.path.join(self.nodes[0].datadir, 'wallet.bak')
|
backup_file = os.path.join(self.nodes[0].datadir, 'wallet.bak')
|
||||||
wallet_name = "res0"
|
wallet_name = "res0"
|
||||||
assert_raises_rpc_error(-8, "Wallet name already exists.", node.restorewallet, wallet_name, wallet_file)
|
wallet_file = os.path.join(node.datadir, self.chain, 'wallets', wallet_name)
|
||||||
|
error_message = "Failed to create database path '{}'. Database already exists.".format(wallet_file)
|
||||||
|
assert_raises_rpc_error(-36, error_message, node.restorewallet, wallet_name, backup_file)
|
||||||
|
assert os.path.exists(wallet_file)
|
||||||
|
|
||||||
def init_three(self):
|
def init_three(self):
|
||||||
self.init_wallet(0)
|
self.init_wallet(0)
|
||||||
@ -179,6 +194,7 @@ class WalletBackupTest(BitcoinTestFramework):
|
|||||||
##
|
##
|
||||||
self.log.info("Restoring wallets on node 3 using backup files")
|
self.log.info("Restoring wallets on node 3 using backup files")
|
||||||
|
|
||||||
|
self.restore_invalid_wallet()
|
||||||
self.restore_nonexistent_wallet()
|
self.restore_nonexistent_wallet()
|
||||||
|
|
||||||
backup_file_0 = os.path.join(self.nodes[0].datadir, 'wallet.bak')
|
backup_file_0 = os.path.join(self.nodes[0].datadir, 'wallet.bak')
|
||||||
@ -189,6 +205,10 @@ class WalletBackupTest(BitcoinTestFramework):
|
|||||||
self.nodes[3].restorewallet("res1", backup_file_1)
|
self.nodes[3].restorewallet("res1", backup_file_1)
|
||||||
self.nodes[3].restorewallet("res2", backup_file_2)
|
self.nodes[3].restorewallet("res2", backup_file_2)
|
||||||
|
|
||||||
|
assert os.path.exists(os.path.join(self.nodes[3].datadir, self.chain, 'wallets', "res0"))
|
||||||
|
assert os.path.exists(os.path.join(self.nodes[3].datadir, self.chain, 'wallets', "res1"))
|
||||||
|
assert os.path.exists(os.path.join(self.nodes[3].datadir, self.chain, 'wallets', "res2"))
|
||||||
|
|
||||||
res0_rpc = self.nodes[3].get_wallet_rpc("res0")
|
res0_rpc = self.nodes[3].get_wallet_rpc("res0")
|
||||||
res1_rpc = self.nodes[3].get_wallet_rpc("res1")
|
res1_rpc = self.nodes[3].get_wallet_rpc("res1")
|
||||||
res2_rpc = self.nodes[3].get_wallet_rpc("res2")
|
res2_rpc = self.nodes[3].get_wallet_rpc("res2")
|
||||||
|
Loading…
Reference in New Issue
Block a user