mirror of
https://github.com/boldsuck/haveno.git
synced 2024-12-23 04:29:22 +00:00
support rolling backup of multisig wallets
This commit is contained in:
parent
f17c5803b5
commit
c28108a4b6
4 changed files with 45 additions and 21 deletions
|
@ -18,7 +18,7 @@
|
||||||
package bisq.common.crypto;
|
package bisq.common.crypto;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
import bisq.common.file.FileUtil;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
@ -139,6 +139,7 @@ public class KeyStorage {
|
||||||
* @param secretKey The symmetric key that protects the key entry file
|
* @param secretKey The symmetric key that protects the key entry file
|
||||||
*/
|
*/
|
||||||
public KeyPair loadKeyPair(KeyEntry keyEntry, SecretKey secretKey) {
|
public KeyPair loadKeyPair(KeyEntry keyEntry, SecretKey secretKey) {
|
||||||
|
FileUtil.rollingBackup(storageDir, keyEntry.getFileName() + ".key", 20);
|
||||||
try {
|
try {
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance(keyEntry.getAlgorithm());
|
KeyFactory keyFactory = KeyFactory.getInstance(keyEntry.getAlgorithm());
|
||||||
byte[] encodedPrivateKey = loadKeyBytes(keyEntry, secretKey);
|
byte[] encodedPrivateKey = loadKeyBytes(keyEntry, secretKey);
|
||||||
|
|
|
@ -74,6 +74,19 @@ public class FileUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteRollingBackup(File dir, String fileName) {
|
||||||
|
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||||
|
if (!backupDir.exists()) throw new RuntimeException("backup directory does not exist: " + backupDir);
|
||||||
|
String dirName = "backups_" + fileName;
|
||||||
|
if (dirName.contains(".")) dirName = dirName.replace(".", "_");
|
||||||
|
File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());
|
||||||
|
try {
|
||||||
|
FileUtils.deleteDirectory(backupFileDir);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void pruneBackup(File backupDir, int numMaxBackupFiles) {
|
private static void pruneBackup(File backupDir, int numMaxBackupFiles) {
|
||||||
if (backupDir.isDirectory()) {
|
if (backupDir.isDirectory()) {
|
||||||
File[] files = backupDir.listFiles();
|
File[] files = backupDir.listFiles();
|
||||||
|
|
|
@ -76,6 +76,7 @@ public class XmrWalletService {
|
||||||
private static final String MONERO_WALLET_RPC_USERNAME = "haveno_user";
|
private static final String MONERO_WALLET_RPC_USERNAME = "haveno_user";
|
||||||
private static final String MONERO_WALLET_RPC_DEFAULT_PASSWORD = "password"; // only used if account password is null
|
private static final String MONERO_WALLET_RPC_DEFAULT_PASSWORD = "password"; // only used if account password is null
|
||||||
private static final String MONERO_WALLET_NAME = "haveno_XMR";
|
private static final String MONERO_WALLET_NAME = "haveno_XMR";
|
||||||
|
private static final String MONERO_MULTISIG_WALLET_PREFIX = "xmr_multisig_trade_";
|
||||||
private static final long MONERO_WALLET_SYNC_PERIOD = 5000l;
|
private static final long MONERO_WALLET_SYNC_PERIOD = 5000l;
|
||||||
|
|
||||||
private final CoreAccountService accountService;
|
private final CoreAccountService accountService;
|
||||||
|
@ -168,7 +169,7 @@ public class XmrWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean multisigWalletExists(String tradeId) {
|
public boolean multisigWalletExists(String tradeId) {
|
||||||
return walletExists("xmr_multisig_trade_" + tradeId);
|
return walletExists(MONERO_MULTISIG_WALLET_PREFIX + tradeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (woodser): test retaking failed trade. create new multisig wallet or replace? cannot reuse
|
// TODO (woodser): test retaking failed trade. create new multisig wallet or replace? cannot reuse
|
||||||
|
@ -177,7 +178,7 @@ public class XmrWalletService {
|
||||||
Trade trade = tradeManager.getOpenTrade(tradeId).get();
|
Trade trade = tradeManager.getOpenTrade(tradeId).get();
|
||||||
synchronized (trade) {
|
synchronized (trade) {
|
||||||
if (multisigWallets.containsKey(trade.getId())) return multisigWallets.get(trade.getId());
|
if (multisigWallets.containsKey(trade.getId())) return multisigWallets.get(trade.getId());
|
||||||
String path = "xmr_multisig_trade_" + trade.getId();
|
String path = MONERO_MULTISIG_WALLET_PREFIX + trade.getId();
|
||||||
MoneroWallet multisigWallet = createWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null); // auto-assign port
|
MoneroWallet multisigWallet = createWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null); // auto-assign port
|
||||||
multisigWallets.put(trade.getId(), multisigWallet);
|
multisigWallets.put(trade.getId(), multisigWallet);
|
||||||
return multisigWallet;
|
return multisigWallet;
|
||||||
|
@ -190,7 +191,7 @@ public class XmrWalletService {
|
||||||
Trade trade = tradeManager.getTrade(tradeId);
|
Trade trade = tradeManager.getTrade(tradeId);
|
||||||
synchronized (trade) {
|
synchronized (trade) {
|
||||||
if (multisigWallets.containsKey(trade.getId())) return multisigWallets.get(trade.getId());
|
if (multisigWallets.containsKey(trade.getId())) return multisigWallets.get(trade.getId());
|
||||||
String path = "xmr_multisig_trade_" + trade.getId();
|
String path = MONERO_MULTISIG_WALLET_PREFIX + trade.getId();
|
||||||
if (!walletExists(path)) throw new RuntimeException("Multisig wallet does not exist for trade " + trade.getId());
|
if (!walletExists(path)) throw new RuntimeException("Multisig wallet does not exist for trade " + trade.getId());
|
||||||
MoneroWallet multisigWallet = openWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null);
|
MoneroWallet multisigWallet = openWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null);
|
||||||
multisigWallets.put(trade.getId(), multisigWallet);
|
multisigWallets.put(trade.getId(), multisigWallet);
|
||||||
|
@ -198,6 +199,11 @@ public class XmrWalletService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveWallet(MoneroWallet wallet) {
|
||||||
|
wallet.save();
|
||||||
|
backupWallet(wallet.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
public void closeMultisigWallet(String tradeId) {
|
public void closeMultisigWallet(String tradeId) {
|
||||||
log.info("{}.closeMultisigWallet({})", getClass().getSimpleName(), tradeId);
|
log.info("{}.closeMultisigWallet({})", getClass().getSimpleName(), tradeId);
|
||||||
Trade trade = tradeManager.getTrade(tradeId);
|
Trade trade = tradeManager.getTrade(tradeId);
|
||||||
|
@ -212,7 +218,7 @@ public class XmrWalletService {
|
||||||
log.info("{}.deleteMultisigWallet({})", getClass().getSimpleName(), tradeId);
|
log.info("{}.deleteMultisigWallet({})", getClass().getSimpleName(), tradeId);
|
||||||
Trade trade = tradeManager.getTrade(tradeId);
|
Trade trade = tradeManager.getTrade(tradeId);
|
||||||
synchronized (trade) {
|
synchronized (trade) {
|
||||||
String walletName = "xmr_multisig_trade_" + tradeId;
|
String walletName = MONERO_MULTISIG_WALLET_PREFIX + tradeId;
|
||||||
if (!walletExists(walletName)) return false;
|
if (!walletExists(walletName)) return false;
|
||||||
if (multisigWallets.containsKey(trade.getId())) closeMultisigWallet(tradeId);
|
if (multisigWallets.containsKey(trade.getId())) closeMultisigWallet(tradeId);
|
||||||
deleteWallet(walletName);
|
deleteWallet(walletName);
|
||||||
|
@ -372,9 +378,6 @@ public class XmrWalletService {
|
||||||
|
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
|
|
||||||
// backup wallet files
|
|
||||||
backupWallets();
|
|
||||||
|
|
||||||
// initialize main wallet if connected or previously created
|
// initialize main wallet if connected or previously created
|
||||||
tryInitMainWallet();
|
tryInitMainWallet();
|
||||||
|
|
||||||
|
@ -402,7 +405,7 @@ public class XmrWalletService {
|
||||||
try {
|
try {
|
||||||
wallet.sync(); // blocking
|
wallet.sync(); // blocking
|
||||||
connectionsService.doneDownload(); // TODO: using this to signify both daemon and wallet synced, refactor sync handling of both
|
connectionsService.doneDownload(); // TODO: using this to signify both daemon and wallet synced, refactor sync handling of both
|
||||||
wallet.save();
|
saveWallet(wallet);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -485,12 +488,6 @@ public class XmrWalletService {
|
||||||
return MONERO_WALLET_RPC_MANAGER.startInstance(cmd);
|
return MONERO_WALLET_RPC_MANAGER.startInstance(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void backupWallets() {
|
|
||||||
FileUtil.rollingBackup(walletDir, xmrWalletFile.getName(), 20);
|
|
||||||
FileUtil.rollingBackup(walletDir, xmrWalletFile.getName() + ".keys", 20);
|
|
||||||
FileUtil.rollingBackup(walletDir, xmrWalletFile.getName() + ".address.txt", 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setWalletDaemonConnections(MoneroRpcConnection connection) {
|
private void setWalletDaemonConnections(MoneroRpcConnection connection) {
|
||||||
log.info("Setting wallet daemon connections: " + (connection == null ? null : connection.getUri()));
|
log.info("Setting wallet daemon connections: " + (connection == null ? null : connection.getUri()));
|
||||||
if (wallet == null) tryInitMainWallet();
|
if (wallet == null) tryInitMainWallet();
|
||||||
|
@ -520,7 +517,7 @@ public class XmrWalletService {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
wallet.changePassword(oldPassword, newPassword);
|
wallet.changePassword(oldPassword, newPassword);
|
||||||
wallet.save();
|
saveWallet(wallet);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -534,7 +531,7 @@ public class XmrWalletService {
|
||||||
MoneroWallet multisigWallet = getMultisigWallet(tradeId); // TODO (woodser): this unnecessarily connects and syncs unopen wallets and leaves open
|
MoneroWallet multisigWallet = getMultisigWallet(tradeId); // TODO (woodser): this unnecessarily connects and syncs unopen wallets and leaves open
|
||||||
if (multisigWallet == null) return;
|
if (multisigWallet == null) return;
|
||||||
multisigWallet.changePassword(oldPassword, newPassword);
|
multisigWallet.changePassword(oldPassword, newPassword);
|
||||||
multisigWallet.save();
|
saveWallet(multisigWallet);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -552,7 +549,8 @@ public class XmrWalletService {
|
||||||
log.info("{}.closeWallet({}, {})", getClass().getSimpleName(), walletRpc.getPath(), save);
|
log.info("{}.closeWallet({}, {})", getClass().getSimpleName(), walletRpc.getPath(), save);
|
||||||
MoneroError err = null;
|
MoneroError err = null;
|
||||||
try {
|
try {
|
||||||
walletRpc.close(save);
|
if (save) saveWallet(walletRpc);
|
||||||
|
walletRpc.close();
|
||||||
} catch (MoneroError e) {
|
} catch (MoneroError e) {
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
|
@ -567,7 +565,7 @@ public class XmrWalletService {
|
||||||
if (!new File(path).delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
if (!new File(path).delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
||||||
if (!new File(path + ".keys").delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
if (!new File(path + ".keys").delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
||||||
if (!new File(path + ".address.txt").delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
if (!new File(path + ".address.txt").delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
||||||
// WalletsSetup.deleteRollingBackup(walletName); // TODO (woodser): necessary to delete rolling backup?
|
deleteBackupWallets(walletName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeAllWallets() {
|
private void closeAllWallets() {
|
||||||
|
@ -609,6 +607,18 @@ public class XmrWalletService {
|
||||||
multisigWallets.clear();
|
multisigWallets.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void backupWallet(String walletName) {
|
||||||
|
FileUtil.rollingBackup(walletDir, walletName, 20);
|
||||||
|
FileUtil.rollingBackup(walletDir, walletName + ".keys", 20);
|
||||||
|
FileUtil.rollingBackup(walletDir, walletName + ".address.txt", 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteBackupWallets(String walletName) {
|
||||||
|
FileUtil.deleteRollingBackup(walletDir, walletName);
|
||||||
|
FileUtil.deleteRollingBackup(walletDir, walletName + ".keys");
|
||||||
|
FileUtil.deleteRollingBackup(walletDir, walletName + ".address.txt");
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------- LEGACY APP -------------------------------
|
// ----------------------------- LEGACY APP -------------------------------
|
||||||
|
|
||||||
public XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
|
public XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
|
||||||
|
|
|
@ -124,7 +124,7 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void completeAux() {
|
private void completeAux() {
|
||||||
processModel.getXmrWalletService().getWallet().save();
|
processModel.getXmrWalletService().saveWallet(processModel.getXmrWalletService().getWallet());
|
||||||
complete();
|
complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue