Skip to content

Commit

Permalink
Perform segwit BSQ wallet migration upon startup
Browse files Browse the repository at this point in the history
Uncomment & enable the m/44'/142'/1' native segwit BSQ account path and
add code to migrate the user's BSQ wallet to segwit upon startup, along
the same lines as the existing BTC wallet segwit migration logic. That
is, set P2WPKH as the default output type, add a native segwit key chain
(set to active) to the BSQ wallet and back up the old 'bisq_BSQ.wallet'
file to 'pre_segwit_bisq_BSQ.wallet.backup'.

Also filter out legacy addresses coming from the original keychain from
BsqWalletService.get(Unused|Change)Address.
  • Loading branch information
stejbac committed Apr 27, 2021
1 parent 3470429 commit 5f1e32a
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 34 deletions.
3 changes: 2 additions & 1 deletion core/src/main/java/bisq/core/api/CoreWalletsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,8 @@ private void maybeSetWalletsManagerKey() {
if (btcWalletService.getAesKey() == null || bsqWalletService.getAesKey() == null) {
KeyParameter aesKey = new KeyParameter(tempAesKey.getKey());
walletsManager.setAesKey(aesKey);
walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(), aesKey);
walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(), aesKey, false);
walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().bsqWallet(), aesKey, true);
}
}

Expand Down
4 changes: 3 additions & 1 deletion core/src/main/java/bisq/core/app/BisqSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,9 @@ private void initWallet() {
requestWalletPasswordHandler.accept(aesKey -> {
walletsManager.setAesKey(aesKey);
walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(),
aesKey);
aesKey, false);
walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().bsqWallet(),
aesKey, true);
if (getResyncSpvSemaphore()) {
if (showFirstPopupIfResyncSPVRequestedHandler != null)
showFirstPopupIfResyncSPVRequestedHandler.run();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,14 @@ public class BisqKeyChainGroupStructure implements KeyChainGroupStructure {
new ChildNumber(142, true),
ChildNumber.ZERO_HARDENED);

// We don't use segwit for BSQ
// public static final ImmutableList<ChildNumber> BIP44_BSQ_SEGWIT_ACCOUNT_PATH = ImmutableList.of(
// new ChildNumber(44, true),
// new ChildNumber(142, true),
// ChildNumber.ONE_HARDENED);
public static final ImmutableList<ChildNumber> BIP44_BSQ_SEGWIT_ACCOUNT_PATH = ImmutableList.of(
new ChildNumber(44, true),
new ChildNumber(142, true),
ChildNumber.ONE_HARDENED);

private boolean isBsqWallet;
private final boolean isBsqWallet;

public BisqKeyChainGroupStructure (boolean isBsqWallet) {
public BisqKeyChainGroupStructure(boolean isBsqWallet) {
this.isBsqWallet = isBsqWallet;
}

Expand All @@ -72,8 +71,7 @@ else if (outputScriptType == Script.ScriptType.P2WPKH)
if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH)
return BIP44_BSQ_NON_SEGWIT_ACCOUNT_PATH;
else if (outputScriptType == Script.ScriptType.P2WPKH)
//return BIP44_BSQ_SEGWIT_ACCOUNT_PATH;
throw new IllegalArgumentException(outputScriptType.toString());
return BIP44_BSQ_SEGWIT_ACCOUNT_PATH;
else
throw new IllegalArgumentException(outputScriptType.toString());
}
Expand Down
39 changes: 27 additions & 12 deletions core/src/main/java/bisq/core/btc/setup/WalletConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;

import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;

Expand Down Expand Up @@ -143,7 +144,11 @@ public class WalletConfig extends AbstractIdleService {
@Setter
private int minBroadcastConnections;
@Getter
private BooleanProperty migratedWalletToSegwit = new SimpleBooleanProperty(false);
private BooleanProperty migratedWalletToBtcSegwit = new SimpleBooleanProperty(false);
@Getter
private BooleanProperty migratedWalletToBsqSegwit = new SimpleBooleanProperty(false);
@Getter
private BooleanExpression migratedWalletToSegwit = migratedWalletToBtcSegwit.and(migratedWalletToBsqSegwit);

/**
* Creates a new WalletConfig, with a newly created {@link Context}. Files will be stored in the given directory.
Expand Down Expand Up @@ -406,15 +411,13 @@ private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean i
wallet = serializer.readWallet(params, extArray, proto);
if (shouldReplayWallet)
wallet.reset();
if (!isBsqWallet) {
maybeAddSegwitKeychain(wallet, null);
}
maybeAddSegwitKeychain(wallet, null, isBsqWallet);
}
return wallet;
}

protected Wallet createWallet(boolean isBsqWallet) {
Script.ScriptType preferredOutputScriptType = isBsqWallet ? Script.ScriptType.P2PKH : Script.ScriptType.P2WPKH;
Script.ScriptType preferredOutputScriptType = Script.ScriptType.P2WPKH;
KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(isBsqWallet);
KeyChainGroup.Builder kcgBuilder = KeyChainGroup.builder(params, structure);
if (restoreFromSeed != null) {
Expand Down Expand Up @@ -545,21 +548,29 @@ public File directory() {
return directory;
}

public void maybeAddSegwitKeychain(Wallet wallet, KeyParameter aesKey) {
if (BisqKeyChainGroupStructure.BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH.equals(wallet.getActiveKeyChain().getAccountPath())) {
public void maybeAddSegwitKeychain(Wallet wallet, KeyParameter aesKey, boolean isBsqWallet) {
var nonSegwitAccountPath = isBsqWallet
? BisqKeyChainGroupStructure.BIP44_BSQ_NON_SEGWIT_ACCOUNT_PATH
: BisqKeyChainGroupStructure.BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH;
var preSegwitBackupFilename = isBsqWallet
? WalletsSetup.PRE_SEGWIT_BSQ_WALLET_BACKUP
: WalletsSetup.PRE_SEGWIT_BTC_WALLET_BACKUP;
var walletFilename = isBsqWallet ? "bisq_BSQ.wallet" : "bisq_BTC.wallet";

if (nonSegwitAccountPath.equals(wallet.getActiveKeyChain().getAccountPath())) {
if (wallet.isEncrypted() && aesKey == null) {
// wait for the aesKey to be set and this method to be invoked again.
return;
}
// Do a backup of the wallet
File backup = new File(directory, WalletsSetup.PRE_SEGWIT_WALLET_BACKUP);
File backup = new File(directory, preSegwitBackupFilename);
try {
FileUtil.copyFile(new File(directory, "bisq_BTC.wallet"), backup);
FileUtil.copyFile(new File(directory, walletFilename), backup);
} catch (IOException e) {
log.error(e.toString(), e);
}

// Btc wallet does not have a native segwit keychain, we should add one.
// Wallet does not have a native segwit keychain, we should add one.
DeterministicSeed seed = wallet.getKeyChainSeed();
if (aesKey != null) {
// If wallet is encrypted, decrypt the seed.
Expand All @@ -568,15 +579,19 @@ public void maybeAddSegwitKeychain(Wallet wallet, KeyParameter aesKey) {
}
DeterministicKeyChain nativeSegwitKeyChain = DeterministicKeyChain.builder().seed(seed)
.outputScriptType(Script.ScriptType.P2WPKH)
.accountPath(new BisqKeyChainGroupStructure(false).accountPathFor(Script.ScriptType.P2WPKH)).build();
.accountPath(new BisqKeyChainGroupStructure(isBsqWallet).accountPathFor(Script.ScriptType.P2WPKH)).build();
if (aesKey != null) {
// If wallet is encrypted, encrypt the new keychain.
KeyCrypter keyCrypter = wallet.getKeyCrypter();
nativeSegwitKeyChain = nativeSegwitKeyChain.toEncrypted(keyCrypter, aesKey);
}
wallet.addAndActivateHDChain(nativeSegwitKeyChain);
}
migratedWalletToSegwit.set(true);
if (isBsqWallet) {
migratedWalletToBsqSegwit.set(true);
} else {
migratedWalletToBtcSegwit.set(true);
}
}

public boolean stateStartingOrRunning() {
Expand Down
17 changes: 10 additions & 7 deletions core/src/main/java/bisq/core/btc/setup/WalletsSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@
@Slf4j
public class WalletsSetup {

public static final String PRE_SEGWIT_WALLET_BACKUP = "pre_segwit_bisq_BTC.wallet.backup";
public static final String PRE_SEGWIT_BTC_WALLET_BACKUP = "pre_segwit_bisq_BTC.wallet.backup";
public static final String PRE_SEGWIT_BSQ_WALLET_BACKUP = "pre_segwit_bisq_BSQ.wallet.backup";

@Getter
public final BooleanProperty walletsSetupFailed = new SimpleBooleanProperty();
Expand Down Expand Up @@ -427,12 +428,14 @@ public void clearBackups() {
e.printStackTrace();
}

File segwitBackup = new File(walletDir, PRE_SEGWIT_WALLET_BACKUP);
try {
FileUtil.deleteFileIfExists(segwitBackup);
} catch (IOException e) {
log.error(e.toString(), e);
}
List.of(PRE_SEGWIT_BTC_WALLET_BACKUP, PRE_SEGWIT_BSQ_WALLET_BACKUP).forEach(filename -> {
File segwitBackup = new File(walletDir, filename);
try {
FileUtil.deleteFileIfExists(segwitBackup);
} catch (IOException e) {
log.error(e.toString(), e);
}
});
}


Expand Down
9 changes: 5 additions & 4 deletions core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@
import org.bitcoinj.core.BlockChain;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptException;
import org.bitcoinj.wallet.CoinSelection;
import org.bitcoinj.wallet.CoinSelector;
Expand Down Expand Up @@ -807,12 +807,13 @@ public Transaction getPreparedUnlockTx(TxOutput lockupTxOutput) throws AddressFo
// Addresses
///////////////////////////////////////////////////////////////////////////////////////////

private LegacyAddress getChangeAddress() {
private Address getChangeAddress() {
return getUnusedAddress();
}

public LegacyAddress getUnusedAddress() {
return (LegacyAddress) wallet.getIssuedReceiveAddresses().stream()
public Address getUnusedAddress() {
return wallet.getIssuedReceiveAddresses().stream()
.filter(address -> Script.ScriptType.P2WPKH.equals(address.getOutputScriptType()))
.filter(this::isAddressUnused)
.findAny()
.orElse(wallet.freshReceiveAddress());
Expand Down

0 comments on commit 5f1e32a

Please sign in to comment.