Commit fda2da27 authored by zeroleak's avatar zeroleak
Browse files

add --reconstruct

parent 29c88d53
......@@ -8,6 +8,7 @@
| --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|
| --reconstruct | Reconstruct mix counters and exit |
| --dump-payload | Dump pairing-payload of current wallet and exit |
......
......@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.samourai.code.whirlpool</groupId>
<artifactId>whirlpool-client-cli</artifactId>
<version>0.10.7</version>
<version>0.10.8-QA</version>
<name>whirlpool-client-cli</name>
<properties>
<spring-boot.version>2.1.6.RELEASE</spring-boot.version>
......@@ -17,7 +17,7 @@
<dependency>
<groupId>io.samourai.code.whirlpool</groupId>
<artifactId>whirlpool-client</artifactId>
<version>0.23.21</version>
<version>0.23.22-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
......
......@@ -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_RECONSTRUCT = "reconstruct";
private ApplicationArguments args;
......@@ -109,6 +110,10 @@ public class ApplicationArgs {
return args.containsOption(ARG_DUMP_PAYLOAD);
}
public boolean isReconstruct() {
return args.containsOption(ARG_RECONSTRUCT);
}
public String getAggregatePostmix() {
return optionalOption(ARG_AGGREGATE_POSTMIX);
}
......
......@@ -45,6 +45,9 @@ public class RunCliCommand {
} else if (appArgs.isListPools()) {
CliWallet cliWallet = cliWalletService.getSessionWallet();
new RunListPools(cliWallet).run();
} else if (appArgs.isReconstruct()) {
CliWallet cliWallet = cliWalletService.getSessionWallet();
new RunReconstruct(cliWallet).run();
} else {
throw new Exception("Unknown command.");
}
......@@ -55,6 +58,9 @@ public class RunCliCommand {
}
public static boolean hasCommandToRun(ApplicationArgs appArgs, CliConfig cliConfig) {
return appArgs.isDumpPayload() || appArgs.isAggregatePostmix() || appArgs.isListPools();
return appArgs.isDumpPayload()
|| appArgs.isAggregatePostmix()
|| appArgs.isListPools()
|| appArgs.isReconstruct();
}
}
package com.samourai.whirlpool.cli.run;
import com.samourai.wallet.api.backend.beans.TxsResponse;
import com.samourai.whirlpool.cli.utils.CliUtils;
import com.samourai.whirlpool.cli.wallet.CliWallet;
import com.samourai.whirlpool.client.wallet.beans.WhirlpoolAccount;
import com.samourai.whirlpool.client.wallet.beans.WhirlpoolUtxo;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RunReconstruct {
private static final int TXS_PER_PAGE = 1000;
private Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private CliWallet cliWallet;
public RunReconstruct(CliWallet cliWallet) {
this.cliWallet = cliWallet;
}
public void run() throws Exception {
log.info(CliUtils.LOG_SEPARATOR);
log.info("⣿ RECONSTRUCT");
log.info("⣿ This will reconstruct mix counters.");
log.info("⣿ • Continue?");
String userInput =
CliUtils.readUserInputRequired("Continue? (y/n)", false, new String[] {"y", "n", "Y", "N"});
if (!userInput.toLowerCase().equals("y")) {
return;
}
Collection<WhirlpoolUtxo> whirlpoolUtxos =
cliWallet.getUtxoSupplier().findUtxos(WhirlpoolAccount.POSTMIX);
Map<String, TxsResponse.Tx> txs = fetchTxs();
int fixedUtxos = 0;
for (WhirlpoolUtxo whirlpoolUtxo : whirlpoolUtxos) {
int mixsDone = recount(whirlpoolUtxo, txs);
if (mixsDone != whirlpoolUtxo.getMixsDone()) {
log.info(
"Fixed "
+ whirlpoolUtxo.getUtxo().tx_hash
+ ":"
+ whirlpoolUtxo.getUtxo().tx_output_n
+ ": "
+ whirlpoolUtxo.getMixsDone()
+ " => "
+ mixsDone);
whirlpoolUtxo.setMixsDone(mixsDone);
fixedUtxos++;
}
}
log.info(CliUtils.LOG_SEPARATOR);
log.info("⣿ RECONSTRUCT SUCCESS");
log.info("⣿ " + fixedUtxos + "/" + whirlpoolUtxos.size() + " utxos updated.");
log.info(CliUtils.LOG_SEPARATOR);
}
private Map<String, TxsResponse.Tx> fetchTxs() throws Exception {
Map<String, TxsResponse.Tx> txs = new LinkedHashMap<>();
int page = -1;
String[] zpubs = new String[] {cliWallet.getWalletPostmix().getZpub()};
TxsResponse txsResponse;
do {
page++;
txsResponse = cliWallet.getConfig().getBackendApi().fetchTxs(zpubs, page, TXS_PER_PAGE);
if (txsResponse.txs != null) {
for (TxsResponse.Tx tx : txsResponse.txs) {
txs.put(tx.hash, tx);
}
}
log.info("Fetching txs... " + txs.size() + "/" + txsResponse.n_tx);
} while ((page * TXS_PER_PAGE) < txsResponse.n_tx);
return txs;
}
private int recount(WhirlpoolUtxo whirlpoolUtxo, Map<String, TxsResponse.Tx> txs) {
int mixsDone = 0;
String txid = whirlpoolUtxo.getUtxo().tx_hash;
while (true) {
TxsResponse.Tx tx = txs.get(txid);
mixsDone++;
if (tx == null || tx.inputs == null || tx.inputs.length == 0) {
return mixsDone;
}
txid = tx.inputs[0].prev_out.txid;
}
}
}
......@@ -5,7 +5,6 @@ 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.NoSessionWalletException;
import com.samourai.whirlpool.cli.run.CliStatusOrchestrator;
import com.samourai.whirlpool.cli.run.RunCliCommand;
import com.samourai.whirlpool.cli.run.RunCliInit;
......@@ -228,17 +227,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) {
......
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