support rolling backup of multisig wallets
This commit is contained in:
parent
f17c5803b5
commit
c28108a4b6
@ -18,7 +18,7 @@
|
||||
package bisq.common.crypto;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import bisq.common.file.FileUtil;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javax.inject.Named;
|
||||
@ -139,6 +139,7 @@ public class KeyStorage {
|
||||
* @param secretKey The symmetric key that protects the key entry file
|
||||
*/
|
||||
public KeyPair loadKeyPair(KeyEntry keyEntry, SecretKey secretKey) {
|
||||
FileUtil.rollingBackup(storageDir, keyEntry.getFileName() + ".key", 20);
|
||||
try {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(keyEntry.getAlgorithm());
|
||||
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) {
|
||||
if (backupDir.isDirectory()) {
|
||||
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_DEFAULT_PASSWORD = "password"; // only used if account password is null
|
||||
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 final CoreAccountService accountService;
|
||||
@ -168,7 +169,7 @@ public class XmrWalletService {
|
||||
}
|
||||
|
||||
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
|
||||
@ -177,7 +178,7 @@ public class XmrWalletService {
|
||||
Trade trade = tradeManager.getOpenTrade(tradeId).get();
|
||||
synchronized (trade) {
|
||||
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
|
||||
multisigWallets.put(trade.getId(), multisigWallet);
|
||||
return multisigWallet;
|
||||
@ -190,7 +191,7 @@ public class XmrWalletService {
|
||||
Trade trade = tradeManager.getTrade(tradeId);
|
||||
synchronized (trade) {
|
||||
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());
|
||||
MoneroWallet multisigWallet = openWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null);
|
||||
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) {
|
||||
log.info("{}.closeMultisigWallet({})", getClass().getSimpleName(), tradeId);
|
||||
Trade trade = tradeManager.getTrade(tradeId);
|
||||
@ -212,7 +218,7 @@ public class XmrWalletService {
|
||||
log.info("{}.deleteMultisigWallet({})", getClass().getSimpleName(), tradeId);
|
||||
Trade trade = tradeManager.getTrade(tradeId);
|
||||
synchronized (trade) {
|
||||
String walletName = "xmr_multisig_trade_" + tradeId;
|
||||
String walletName = MONERO_MULTISIG_WALLET_PREFIX + tradeId;
|
||||
if (!walletExists(walletName)) return false;
|
||||
if (multisigWallets.containsKey(trade.getId())) closeMultisigWallet(tradeId);
|
||||
deleteWallet(walletName);
|
||||
@ -358,7 +364,7 @@ public class XmrWalletService {
|
||||
if (miningFeePadding) depositThreshold = depositThreshold.add(feeThreshold.multiply(BigInteger.valueOf(3l))); // prove reserve of at least deposit amount + (3 * min mining fee)
|
||||
if (check.getReceivedAmount().compareTo(depositThreshold) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + depositThreshold + " but was " + check.getReceivedAmount());
|
||||
} finally {
|
||||
|
||||
|
||||
// flush tx from pool if we added it
|
||||
if (submittedToPool) daemon.flushTxPool(txHash);
|
||||
}
|
||||
@ -372,9 +378,6 @@ public class XmrWalletService {
|
||||
|
||||
private void initialize() {
|
||||
|
||||
// backup wallet files
|
||||
backupWallets();
|
||||
|
||||
// initialize main wallet if connected or previously created
|
||||
tryInitMainWallet();
|
||||
|
||||
@ -402,7 +405,7 @@ public class XmrWalletService {
|
||||
try {
|
||||
wallet.sync(); // blocking
|
||||
connectionsService.doneDownload(); // TODO: using this to signify both daemon and wallet synced, refactor sync handling of both
|
||||
wallet.save();
|
||||
saveWallet(wallet);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -485,12 +488,6 @@ public class XmrWalletService {
|
||||
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) {
|
||||
log.info("Setting wallet daemon connections: " + (connection == null ? null : connection.getUri()));
|
||||
if (wallet == null) tryInitMainWallet();
|
||||
@ -520,7 +517,7 @@ public class XmrWalletService {
|
||||
public void run() {
|
||||
try {
|
||||
wallet.changePassword(oldPassword, newPassword);
|
||||
wallet.save();
|
||||
saveWallet(wallet);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
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
|
||||
if (multisigWallet == null) return;
|
||||
multisigWallet.changePassword(oldPassword, newPassword);
|
||||
multisigWallet.save();
|
||||
saveWallet(multisigWallet);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -552,7 +549,8 @@ public class XmrWalletService {
|
||||
log.info("{}.closeWallet({}, {})", getClass().getSimpleName(), walletRpc.getPath(), save);
|
||||
MoneroError err = null;
|
||||
try {
|
||||
walletRpc.close(save);
|
||||
if (save) saveWallet(walletRpc);
|
||||
walletRpc.close();
|
||||
} catch (MoneroError 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 + ".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);
|
||||
// WalletsSetup.deleteRollingBackup(walletName); // TODO (woodser): necessary to delete rolling backup?
|
||||
deleteBackupWallets(walletName);
|
||||
}
|
||||
|
||||
private void closeAllWallets() {
|
||||
@ -609,6 +607,18 @@ public class XmrWalletService {
|
||||
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 -------------------------------
|
||||
|
||||
public XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
|
||||
|
@ -124,7 +124,7 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
|
||||
}
|
||||
|
||||
private void completeAux() {
|
||||
processModel.getXmrWalletService().getWallet().save();
|
||||
processModel.getXmrWalletService().saveWallet(processModel.getXmrWalletService().getWallet());
|
||||
complete();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user