Commit 88f381d6 authored by zeroleak's avatar zeroleak
Browse files

Merge branch 'from-0.10.6' into develop

parents fea3d5db b1c4db4f
......@@ -8,7 +8,13 @@
| --authenticate | Enable interactive authentication on startup |
| --listen | Enable CLI API for remote commands & GUI (see [API.md](API.md))|
| --list-pools | List pools and exit|
#### Advanced
| Argument | Description |
| ---------|------------ |
| --dump-payload | Dump pairing-payload of current wallet and exit |
| --resync | Resynchronize mix counters on startup (startup will be slower) |
#### Debugging
......
......@@ -170,6 +170,6 @@
</repositories>
<scm>
<connection>scm:git:git@code.samourai.io:whirlpool/whirlpool-client-cli.git</connection>
<tag>0.10.6-QA</tag>
<tag>0.10.8</tag>
</scm>
</project>
......@@ -34,6 +34,7 @@ public class ApplicationArgs {
private static final String ARG_AUTHENTICATE = "authenticate";
private static final String ARG_MIXS_TARGET = "mixs-target";
private static final String ARG_DUMP_PAYLOAD = "dump-payload";
private static final String ARG_RESYNC = "resync";
private ApplicationArguments args;
......@@ -95,6 +96,11 @@ public class ApplicationArgs {
if (valueInt != null) {
cliConfig.getMix().setMixsTarget(valueInt);
}
valueBool = optionalBoolean(ARG_RESYNC);
if (valueBool != null) {
cliConfig.setResync(valueBool);
}
}
public boolean isListPools() {
......@@ -109,6 +115,10 @@ public class ApplicationArgs {
return args.containsOption(ARG_DUMP_PAYLOAD);
}
public boolean isResync() {
return args.containsOption(ARG_RESYNC);
}
public String getAggregatePostmix() {
return optionalOption(ARG_AGGREGATE_POSTMIX);
}
......
......@@ -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;
......@@ -85,7 +88,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 boolean run(CliWallet cliWallet) throws Exception;
}
package com.samourai.whirlpool.cli.beans;
public abstract class CliUpgradeUnauth extends CliUpgrade {
public CliUpgradeUnauth() {}
public abstract boolean 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;
}
}
......@@ -19,6 +19,7 @@ import org.springframework.util.StringUtils;
public class CliConfig extends CliConfigFile {
private boolean autoAggregatePostmix;
private String autoTx0PoolId;
private boolean resync;
public CliConfig() {
super();
......@@ -57,6 +58,14 @@ public class CliConfig extends CliConfigFile {
this.autoTx0PoolId = autoTx0PoolId;
}
public boolean isResync() {
return resync;
}
public void setResync(boolean resync) {
this.resync = resync;
}
@Override
public Map<String, String> getConfigInfo() {
Map<String, String> configInfo = super.getConfigInfo();
......@@ -74,6 +83,7 @@ public class CliConfig extends CliConfigFile {
"cli/proxy", getCliProxy().isPresent() ? getCliProxy().get().toString() : "null");
configInfo.put("cli/autoAggregatePostmix", Boolean.toString(autoAggregatePostmix));
configInfo.put("cli/autoTx0PoolId", autoTx0PoolId != null ? autoTx0PoolId : "null");
configInfo.put("cli/resync", Boolean.toString(resync));
return configInfo;
}
......
package com.samourai.whirlpool.cli.exception;
import com.samourai.whirlpool.client.exception.NotifiableException;
public class CliRestartException extends NotifiableException {
public CliRestartException(String error) {
super(error);
}
}
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,12 +209,18 @@ public class CliConfigService {
Application.restart();
}
public synchronized void setVersionCurrent() throws Exception {
public synchronized void setVersion(int version) throws Exception {
if (log.isDebugEnabled()) {
log.debug("setVersion: " + version);
}
Properties props = loadProperties();
props.put(KEY_VERSION, Integer.toString(CLI_VERSION));
props.put(KEY_VERSION, Integer.toString(version));
// save
save(props);
// update runtime
cliConfig.setVersion(version);
}
public synchronized void resetConfiguration() throws Exception {
......@@ -237,6 +239,7 @@ public class CliConfigService {
public void setCliStatusNotReady(String error) {
this.setCliStatus(CliStatus.NOT_READY, error);
log.warn("status -> " + error);
}
protected synchronized void save(Properties props) throws Exception {
......@@ -273,30 +276,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;
}
}
......@@ -6,7 +6,7 @@ import com.samourai.whirlpool.cli.beans.CliResult;
import com.samourai.whirlpool.cli.beans.CliStatus;
import com.samourai.whirlpool.cli.config.CliConfig;
import com.samourai.whirlpool.cli.exception.AuthenticationException;
import com.samourai.whirlpool.cli.exception.NoSessionWalletException;
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;
......@@ -36,6 +36,7 @@ public class CliService {
private CliConfigService cliConfigService;
private CliWalletService cliWalletService;
private WalletAggregateService walletAggregateService;
private CliUpgradeService cliUpgradeService;
private CliTorClientService cliTorClientService;
private CliStatusOrchestrator cliStatusOrchestrator;
......@@ -45,12 +46,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();
......@@ -144,8 +147,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");
......@@ -168,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
......@@ -178,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
......@@ -228,17 +235,8 @@ public class CliService {
log.debug("shutdown");
}
// stop cliWallet
try {
CliWallet cliWallet =
cliWalletService != null && cliWalletService.hasSessionWallet()
? cliWalletService.getSessionWallet()
: null;
if (cliWallet != null && cliWallet.getMixingState().isStarted()) {
cliWallet.stop();
}
} catch (NoSessionWalletException e) {
}
// close cliWallet
cliWalletService.closeWallet();
// disconnect Tor
if (cliTorClientService != null) {
......
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.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 boolean 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);
return true; // restart
}
return false;
}
});
// V5
this.upgrades.put(
CliVersion.VERSION_5.getVersion(),
new CliUpgradeAuth() {
@Override
public boolean run(CliWallet cliWallet) throws Exception {
// resync postmix counters
try {
cliWallet.resync();
} catch (Exception e) {
log.error("", e);
}
return false;
}
});
}
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);
}
boolean shouldRestart = cliUpgrade.run();
afterUpgrade(nextVersion, shouldRestart);
return shouldRestart;
}
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);
}
boolean shouldRestart = cliUpgrade.run(cliWallet);
afterUpgrade(nextVersion, shouldRestart);
return shouldRestart;
}
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, boolean shouldRestart) throws Exception {
cliConfigService.setVersion(nextVersion);
if (shouldRestart) {
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,28 @@ public class CliWalletService extends WhirlpoolWalletService {
walletAggregateService,
cliTorClientService,
httpClientService);
return (CliWallet) openWallet(cliWallet);
cliWallet = (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...");
}
// resync?
if (cliConfig.isResync()) {
try {
cliWallet.resync();
} catch (Exception e) {
log.error("", e);
}
}