Commit e8892044 authored by zeroleak's avatar zeroleak
Browse files

run --reconstruct on CLI upgrade

parent fda2da27
......@@ -10,7 +10,9 @@ import com.samourai.whirlpool.cli.api.protocol.rest.ApiCliStateResponse;
import com.samourai.whirlpool.cli.beans.CliStatus;
import com.samourai.whirlpool.cli.beans.WhirlpoolPairingPayload;
import com.samourai.whirlpool.cli.config.CliConfig;
import com.samourai.whirlpool.cli.exception.CliRestartException;
import com.samourai.whirlpool.cli.services.CliConfigService;
import com.samourai.whirlpool.cli.services.CliUpgradeService;
import com.samourai.whirlpool.cli.services.CliWalletService;
import com.samourai.whirlpool.client.exception.NotifiableException;
import java.lang.invoke.MethodHandles;
......@@ -32,6 +34,7 @@ public class CliController extends AbstractRestController {
@Autowired private CliConfigService cliConfigService;
@Autowired private CliWalletService cliWalletService;
@Autowired private CliUpgradeService cliUpgradeService;
@Autowired private CliConfig cliConfig;
@Autowired private TaskExecutor taskExecutor;
......@@ -91,7 +94,12 @@ public class CliController extends AbstractRestController {
throws Exception {
checkHeaders(headers);
cliWalletService.openWallet(payload.seedPassphrase).start();
try {
cliWalletService.openWallet(payload.seedPassphrase).start();
} catch (CliRestartException e) {
// CLI upgrade success => restart
Application.restart();
}
// success
return state(headers);
......
package com.samourai.whirlpool.cli.beans;
public abstract class CliUpgrade {
public CliUpgrade() {}
}
package com.samourai.whirlpool.cli.beans;
import com.samourai.whirlpool.cli.wallet.CliWallet;
public abstract class CliUpgradeAuth extends CliUpgrade {
public CliUpgradeAuth() {
super();
}
public abstract void run(CliWallet cliWallet) throws Exception;
}
package com.samourai.whirlpool.cli.beans;
public abstract class CliUpgradeUnauth extends CliUpgrade {
public CliUpgradeUnauth() {}
public abstract void run() throws Exception;
}
package com.samourai.whirlpool.cli.beans;
public enum CliVersion {
VERSION_4(4),
VERSION_5(5);
private int version;
private CliVersion(int version) {
this.version = version;
}
public int getVersion() {
return version;
}
}
package com.samourai.whirlpool.cli.exception;
import com.samourai.whirlpool.client.exception.NotifiableException;
public class CliRestartException extends NotifiableException {
public CliRestartException(String error) {
super(error);
}
}
......@@ -32,10 +32,15 @@ public class RunReconstruct {
if (!userInput.toLowerCase().equals("y")) {
return;
}
reconstruct();
}
public void reconstruct() throws Exception {
Collection<WhirlpoolUtxo> whirlpoolUtxos =
cliWallet.getUtxoSupplier().findUtxos(WhirlpoolAccount.POSTMIX);
log.info("Reconstructing mix counters...");
Map<String, TxsResponse.Tx> txs = fetchTxs();
int fixedUtxos = 0;
......@@ -74,7 +79,7 @@ public class RunReconstruct {
txs.put(tx.hash, tx);
}
}
log.info("Fetching txs... " + txs.size() + "/" + txsResponse.n_tx);
log.info("Fetching postmix history... " + txs.size() + "/" + txsResponse.n_tx);
} while ((page * TXS_PER_PAGE) < txsResponse.n_tx);
return txs;
}
......
package com.samourai.whirlpool.cli.run;
import com.samourai.whirlpool.cli.config.CliConfig;
import com.samourai.whirlpool.cli.services.CliConfigService;
import java.lang.invoke.MethodHandles;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RunUpgradeCli {
private Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private CliConfig cliConfig;
private CliConfigService cliConfigService;
public RunUpgradeCli(CliConfig cliConfig, CliConfigService cliConfigService) {
this.cliConfig = cliConfig;
this.cliConfigService = cliConfigService;
}
public void run(int localVersion) throws Exception {
// run upgrades
if (localVersion < CliConfigService.CLI_VERSION_4) {
upgradeV4();
}
}
public void upgradeV4() throws Exception {
log.info(" - Upgrading to: V4");
// set cli.mix.clients=5 when missing
if (cliConfig.getMix().getClients() == 0) {
Properties props = cliConfigService.loadProperties();
props.put(CliConfigService.KEY_MIX_CLIENTS, "5");
cliConfigService.saveProperties(props);
}
}
}
......@@ -8,7 +8,6 @@ import com.samourai.whirlpool.cli.api.protocol.beans.ApiCliConfig;
import com.samourai.whirlpool.cli.beans.CliStatus;
import com.samourai.whirlpool.cli.beans.WhirlpoolPairingPayload;
import com.samourai.whirlpool.cli.config.CliConfig;
import com.samourai.whirlpool.cli.run.RunUpgradeCli;
import com.samourai.whirlpool.cli.utils.CliUtils;
import com.samourai.whirlpool.client.exception.NotifiableException;
import com.samourai.whirlpool.client.utils.ClientUtils;
......@@ -33,9 +32,6 @@ import org.springframework.util.DefaultPropertiesPersister;
public class CliConfigService {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final int CLI_VERSION_4 = 4;
public static final int CLI_VERSION = CLI_VERSION_4;
public static final String CLI_CONFIG_FILENAME = "whirlpool-cli-config.properties";
private static final String KEY_APIKEY = "cli.apiKey";
private static final String KEY_SEED = "cli.seed";
......@@ -158,7 +154,7 @@ public class CliConfigService {
// save configuration file
Properties props = new Properties();
props.put(KEY_VERSION, Integer.toString(CLI_VERSION));
props.put(KEY_VERSION, Integer.toString(CliUpgradeService.CURRENT_VERSION.getVersion()));
props.put(KEY_APIKEY, apiKey);
props.put(KEY_SEED, encryptedMnemonic);
props.put(KEY_SEED_APPEND_PASSPHRASE, Boolean.toString(appendPassphrase));
......@@ -213,9 +209,9 @@ public class CliConfigService {
Application.restart();
}
public synchronized void setVersionCurrent() throws Exception {
public synchronized void setVersion(int version) throws Exception {
Properties props = loadProperties();
props.put(KEY_VERSION, Integer.toString(CLI_VERSION));
props.put(KEY_VERSION, Integer.toString(version));
// save
save(props);
......@@ -273,30 +269,4 @@ public class CliConfigService {
private File getConfigurationFile() {
return new File(CLI_CONFIG_FILENAME);
}
public boolean checkUpgrade() throws Exception {
boolean shouldRestart = false;
int localVersion = cliConfig.getVersion();
if (localVersion < CLI_VERSION) {
// older version => run upgrade
if (log.isDebugEnabled()) {
log.debug(" • Upgrading cli wallet: " + localVersion + " -> " + CLI_VERSION);
}
new RunUpgradeCli(cliConfig, this).run(localVersion);
// set version
setVersionCurrent();
// stop wallet & restart needed
this.setCliStatusNotReady("Upgrade success. Restarting CLI...");
shouldRestart = true;
} else {
// up to date
if (log.isDebugEnabled()) {
log.debug("cli wallet is up to date: " + CLI_VERSION);
}
}
return shouldRestart;
}
}
......@@ -5,6 +5,7 @@ import com.samourai.whirlpool.cli.beans.CliProxy;
import com.samourai.whirlpool.cli.beans.CliResult;
import com.samourai.whirlpool.cli.config.CliConfig;
import com.samourai.whirlpool.cli.exception.AuthenticationException;
import com.samourai.whirlpool.cli.exception.CliRestartException;
import com.samourai.whirlpool.cli.run.CliStatusOrchestrator;
import com.samourai.whirlpool.cli.run.RunCliCommand;
import com.samourai.whirlpool.cli.run.RunCliInit;
......@@ -34,6 +35,7 @@ public class CliService {
private CliConfigService cliConfigService;
private CliWalletService cliWalletService;
private WalletAggregateService walletAggregateService;
private CliUpgradeService cliUpgradeService;
private CliTorClientService cliTorClientService;
private CliStatusOrchestrator cliStatusOrchestrator;
......@@ -43,12 +45,14 @@ public class CliService {
CliConfigService cliConfigService,
CliWalletService cliWalletService,
WalletAggregateService walletAggregateService,
CliUpgradeService cliUpgradeService,
CliTorClientService cliTorClientService) {
this.appArgs = appArgs;
this.cliConfig = cliConfig;
this.cliConfigService = cliConfigService;
this.cliWalletService = cliWalletService;
this.walletAggregateService = walletAggregateService;
this.cliUpgradeService = cliUpgradeService;
this.cliTorClientService = cliTorClientService;
this.cliStatusOrchestrator = null;
init();
......@@ -135,8 +139,8 @@ public class CliService {
return CliResult.KEEP_RUNNING;
}
// check upgrade
boolean shouldRestart = cliConfigService.checkUpgrade();
// check upgrade (before authentication)
boolean shouldRestart = cliUpgradeService.upgradeUnauthenticated();
if (shouldRestart) {
log.warn(CliUtils.LOG_SEPARATOR);
log.warn("⣿ UPGRADE SUCCESS");
......@@ -167,6 +171,7 @@ public class CliService {
return CliResult.KEEP_RUNNING;
}
// authenticate
CliWallet cliWallet = null;
while (cliWallet == null) {
// authenticate to open wallet when passphrase providen through arguments
......@@ -177,15 +182,18 @@ public class CliService {
cliWalletService.hasSessionWallet()
? cliWalletService.getSessionWallet()
: cliWalletService.openWallet(seedPassphrase);
log.info(CliUtils.LOG_SEPARATOR);
log.info("⣿ AUTHENTICATION SUCCESS");
log.info("⣿ Whirlpool is starting...");
log.info(CliUtils.LOG_SEPARATOR);
} catch (AuthenticationException e) {
log.error(e.getMessage());
// will retry
} catch (CliRestartException e) {
log.error(e.getMessage());
return CliResult.RESTART;
}
}
log.info(CliUtils.LOG_SEPARATOR);
log.info("⣿ AUTHENTICATION SUCCESS");
log.info("⣿ Whirlpool is starting...");
log.info(CliUtils.LOG_SEPARATOR);
if (RunCliCommand.hasCommandToRun(appArgs, cliConfig)) {
// execute specific command
......
package com.samourai.whirlpool.cli.services;
import com.samourai.whirlpool.cli.beans.CliUpgrade;
import com.samourai.whirlpool.cli.beans.CliUpgradeAuth;
import com.samourai.whirlpool.cli.beans.CliUpgradeUnauth;
import com.samourai.whirlpool.cli.beans.CliVersion;
import com.samourai.whirlpool.cli.config.CliConfig;
import com.samourai.whirlpool.cli.run.RunReconstruct;
import com.samourai.whirlpool.cli.wallet.CliWallet;
import java.lang.invoke.MethodHandles;
import java.util.LinkedHashMap;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class CliUpgradeService {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final CliVersion CURRENT_VERSION = CliVersion.VERSION_5;
private CliConfig cliConfig;
private CliConfigService cliConfigService;
private LinkedHashMap<Integer, CliUpgrade> upgrades;
public CliUpgradeService(CliConfig cliConfig, CliConfigService cliConfigService) {
this.cliConfig = cliConfig;
this.cliConfigService = cliConfigService;
this.upgrades = new LinkedHashMap<>();
// V4
this.upgrades.put(
CliVersion.VERSION_4.getVersion(),
new CliUpgradeUnauth() {
@Override
public void run() throws Exception {
// set cli.mix.clients=5 when missing
if (cliConfig.getMix().getClients() == 0) {
Properties props = cliConfigService.loadProperties();
props.put(CliConfigService.KEY_MIX_CLIENTS, "5");
cliConfigService.saveProperties(props);
}
}
});
// V5
this.upgrades.put(
CliVersion.VERSION_5.getVersion(),
new CliUpgradeAuth() {
@Override
public void run(CliWallet cliWallet) throws Exception {
// reconstruct postmix counters
new RunReconstruct(cliWallet).reconstruct();
}
});
}
public boolean upgradeUnauthenticated() throws Exception {
// find next upgrade
int localVersion = cliConfig.getVersion();
int nextVersion = localVersion + 1;
CliUpgradeUnauth cliUpgrade = (CliUpgradeUnauth) getNextUpgrade(nextVersion, false);
if (cliUpgrade == null) {
// up-to-date
return false;
}
// run upgrade
if (log.isDebugEnabled()) {
log.debug(" • Upgrading CLI (unauth): " + localVersion + " -> " + nextVersion);
}
cliUpgrade.run();
afterUpgrade(nextVersion);
return true;
}
public boolean upgradeAuthenticated(CliWallet cliWallet) throws Exception {
// find next upgrade
int localVersion = cliConfig.getVersion();
int nextVersion = localVersion + 1;
CliUpgradeAuth cliUpgrade = (CliUpgradeAuth) getNextUpgrade(nextVersion, true);
if (cliUpgrade == null) {
// up-to-date
return false;
}
// run upgrade
if (log.isDebugEnabled()) {
log.debug(" • Upgrading CLI (auth): " + localVersion + " -> " + nextVersion);
}
cliUpgrade.run(cliWallet);
afterUpgrade(nextVersion);
return true;
}
private CliUpgrade getNextUpgrade(int nextVersion, boolean authenticated) {
if (nextVersion > CURRENT_VERSION.getVersion()) {
// up-to-date
return null;
}
// find next upgrade
CliUpgrade cliUpgrade = upgrades.get(nextVersion);
if (cliUpgrade == null) {
// up-to-date
return null;
}
if (authenticated) {
if (!(cliUpgrade instanceof CliUpgradeAuth)) {
// upgrade will run after authentication
return null;
}
} else {
if (!(cliUpgrade instanceof CliUpgradeUnauth)) {
// should never happen
return null;
}
}
return cliUpgrade;
}
private void afterUpgrade(int nextVersion) throws Exception {
cliConfigService.setVersion(nextVersion);
cliConfigService.setCliStatusNotReady("Upgrade success. Restarting CLI...");
}
}
......@@ -20,6 +20,7 @@ import com.samourai.whirlpool.cli.beans.WhirlpoolPairingPayload;
import com.samourai.whirlpool.cli.config.CliConfig;
import com.samourai.whirlpool.cli.config.CliConfigFile;
import com.samourai.whirlpool.cli.exception.AuthenticationException;
import com.samourai.whirlpool.cli.exception.CliRestartException;
import com.samourai.whirlpool.cli.exception.NoSessionWalletException;
import com.samourai.whirlpool.cli.utils.CliUtils;
import com.samourai.whirlpool.cli.wallet.CliWallet;
......@@ -54,6 +55,7 @@ public class CliWalletService extends WhirlpoolWalletService {
private JavaHttpClientService httpClientService;
private JavaStompClientService stompClientService;
private CliTorClientService cliTorClientService;
private CliUpgradeService cliUpgradeService;
public CliWalletService(
CliConfig cliConfig,
......@@ -62,7 +64,8 @@ public class CliWalletService extends WhirlpoolWalletService {
WalletAggregateService walletAggregateService,
JavaHttpClientService httpClientService,
JavaStompClientService stompClientService,
CliTorClientService cliTorClientService) {
CliTorClientService cliTorClientService,
CliUpgradeService cliUpgradeService) {
super();
this.cliConfig = cliConfig;
this.cliConfigService = cliConfigService;
......@@ -71,6 +74,7 @@ public class CliWalletService extends WhirlpoolWalletService {
this.httpClientService = httpClientService;
this.stompClientService = stompClientService;
this.cliTorClientService = cliTorClientService;
this.cliUpgradeService = cliUpgradeService;
}
public CliWallet openWallet(String passphrase) throws Exception {
......@@ -143,7 +147,19 @@ public class CliWalletService extends WhirlpoolWalletService {
walletAggregateService,
cliTorClientService,
httpClientService);
return (CliWallet) openWallet(cliWallet);
CliWallet result = (CliWallet) openWallet(cliWallet);
// check upgrade
boolean shouldRestart = cliUpgradeService.upgradeAuthenticated(cliWallet);
if (shouldRestart) {
// upgrade success => restart CLI
log.warn(CliUtils.LOG_SEPARATOR);
log.warn("⣿ UPGRADE SUCCESS");
log.warn("⣿ Restarting CLI...");
log.warn(CliUtils.LOG_SEPARATOR);
throw new CliRestartException("Upgrade success, restarting CLI...");
}
return result;
}
private BackendApi computeBackendApiService(String passphrase) throws Exception {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment