Skip to content

Commit

Permalink
Merge pull request #4568 from oscarguindzberg/segwitWallet
Browse files Browse the repository at this point in the history
Add segwit support to the BTC wallet
  • Loading branch information
sqrrm authored Oct 8, 2020
2 parents 2d06946 + 261e0ec commit 35e0c34
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 110 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ configure(subprojects) {

ext { // in alphabetical order
bcVersion = '1.63'
bitcoinjVersion = '44ddbdc'
bitcoinjVersion = 'a733034'
btcdCli4jVersion = '27b94333'
codecVersion = '1.13'
easybindVersion = '1.0.3'
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/bisq/core/app/BisqSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ private void initWallet() {
if (requestWalletPasswordHandler != null) {
requestWalletPasswordHandler.accept(aesKey -> {
walletsManager.setAesKey(aesKey);
walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(),
aesKey);
if (preferences.isResyncSpvRequested()) {
if (showFirstPopupIfResyncSPVRequestedHandler != null)
showFirstPopupIfResyncSPVRequestedHandler.run();
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/bisq/core/app/WalletAppSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void init(@Nullable Consumer<String> chainFileLockedExceptionHandler,
Runnable downloadCompleteHandler,
Runnable walletInitializedHandler) {
log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}",
VersionMessage.BITCOINJ_VERSION, "44ddbdc");
VersionMessage.BITCOINJ_VERSION, "a733034");

ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
btcInfoBinding = EasyBind.combine(walletsSetup.downloadPercentageProperty(),
Expand Down
30 changes: 22 additions & 8 deletions core/src/main/java/bisq/core/btc/model/AddressEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@

import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.script.Script;

import java.util.Optional;

Expand Down Expand Up @@ -74,6 +74,9 @@ public enum Context {

private long coinLockedInMultiSig;

@Getter
private boolean segwit;

@Nullable
transient private DeterministicKey keyPair;
@Nullable
Expand All @@ -86,18 +89,24 @@ public enum Context {
// Constructor, initialization
///////////////////////////////////////////////////////////////////////////////////////////

public AddressEntry(DeterministicKey keyPair, Context context) {
this(keyPair, context, null);
public AddressEntry(DeterministicKey keyPair, Context context, boolean segwit) {
this(keyPair, context, null, segwit);
}

public AddressEntry(@NotNull DeterministicKey keyPair,
Context context,
@Nullable String offerId) {
@Nullable String offerId,
boolean segwit) {
if (segwit && (!Context.AVAILABLE.equals(context) || offerId != null)) {
throw new IllegalArgumentException("Segwit addresses are only allowed for " +
"AVAILABLE entries without an offerId");
}
this.keyPair = keyPair;
this.context = context;
this.offerId = offerId;
pubKey = keyPair.getPubKey();
pubKeyHash = keyPair.getPubKeyHash();
this.segwit = segwit;
}


Expand All @@ -109,20 +118,23 @@ private AddressEntry(byte[] pubKey,
byte[] pubKeyHash,
Context context,
@Nullable String offerId,
Coin coinLockedInMultiSig) {
Coin coinLockedInMultiSig,
boolean segwit) {
this.pubKey = pubKey;
this.pubKeyHash = pubKeyHash;
this.context = context;
this.offerId = offerId;
this.coinLockedInMultiSig = coinLockedInMultiSig.value;
this.segwit = segwit;
}

public static AddressEntry fromProto(protobuf.AddressEntry proto) {
return new AddressEntry(proto.getPubKey().toByteArray(),
proto.getPubKeyHash().toByteArray(),
ProtoUtil.enumFromProto(AddressEntry.Context.class, proto.getContext().name()),
ProtoUtil.stringOrNullFromProto(proto.getOfferId()),
Coin.valueOf(proto.getCoinLockedInMultiSig()));
Coin.valueOf(proto.getCoinLockedInMultiSig()),
proto.getSegwit());
}

@Override
Expand All @@ -131,7 +143,8 @@ public protobuf.AddressEntry toProtoMessage() {
.setPubKey(ByteString.copyFrom(pubKey))
.setPubKeyHash(ByteString.copyFrom(pubKeyHash))
.setContext(protobuf.AddressEntry.Context.valueOf(context.name()))
.setCoinLockedInMultiSig(coinLockedInMultiSig);
.setCoinLockedInMultiSig(coinLockedInMultiSig)
.setSegwit(segwit);
Optional.ofNullable(offerId).ifPresent(builder::setOfferId);
return builder.build();
}
Expand Down Expand Up @@ -175,7 +188,7 @@ public String getAddressString() {
@Nullable
public Address getAddress() {
if (address == null && keyPair != null)
address = LegacyAddress.fromKey(Config.baseCurrencyNetworkParameters(), keyPair);
address = Address.fromKey(Config.baseCurrencyNetworkParameters(), keyPair, segwit ? Script.ScriptType.P2WPKH : Script.ScriptType.P2PKH);
return address;
}

Expand All @@ -198,6 +211,7 @@ public String toString() {
", context=" + context +
", offerId='" + offerId + '\'' +
", coinLockedInMultiSig=" + coinLockedInMultiSig +
", segwit=" + segwit +
"}";
}
}
30 changes: 18 additions & 12 deletions core/src/main/java/bisq/core/btc/model/AddressEntryList.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import com.google.protobuf.Message;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.SegwitAddress;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.script.Script;
Expand All @@ -35,6 +35,8 @@

import com.google.common.collect.ImmutableList;

import org.apache.commons.lang3.tuple.Pair;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -107,11 +109,13 @@ public void onWalletReady(Wallet wallet) {
if (!entrySet.isEmpty()) {
Set<AddressEntry> toBeRemoved = new HashSet<>();
entrySet.forEach(addressEntry -> {
Script.ScriptType scriptType = addressEntry.isSegwit() ? Script.ScriptType.P2WPKH
: Script.ScriptType.P2PKH;
DeterministicKey keyFromPubHash = (DeterministicKey) wallet.findKeyFromPubKeyHash(
addressEntry.getPubKeyHash(),
Script.ScriptType.P2PKH);
addressEntry.getPubKeyHash(), scriptType);
if (keyFromPubHash != null) {
Address addressFromKey = LegacyAddress.fromKey(Config.baseCurrencyNetworkParameters(), keyFromPubHash);
Address addressFromKey = Address.fromKey(Config.baseCurrencyNetworkParameters(), keyFromPubHash,
scriptType);
// We want to ensure key and address matches in case we have address in entry available already
if (addressEntry.getAddress() == null || addressFromKey.equals(addressEntry.getAddress())) {
addressEntry.setDeterministicKey(keyFromPubHash);
Expand All @@ -133,7 +137,8 @@ public void onWalletReady(Wallet wallet) {
toBeRemoved.forEach(entrySet::remove);
} else {
// As long the old arbitration domain is not removed from the code base we still support it here.
entrySet.add(new AddressEntry(wallet.freshReceiveKey(), AddressEntry.Context.ARBITRATOR));
DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2PKH));
entrySet.add(new AddressEntry(key, AddressEntry.Context.ARBITRATOR, false));
}

// In case we restore from seed words and have balance we need to add the relevant addresses to our list.
Expand All @@ -147,7 +152,7 @@ public void onWalletReady(Wallet wallet) {
DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(address);
if (key != null) {
// Address will be derived from key in getAddress method
entrySet.add(new AddressEntry(key, AddressEntry.Context.AVAILABLE));
entrySet.add(new AddressEntry(key, AddressEntry.Context.AVAILABLE, address instanceof SegwitAddress));
}
});
}
Expand Down Expand Up @@ -192,7 +197,8 @@ public void addAddressEntry(AddressEntry addressEntry) {
public void swapToAvailable(AddressEntry addressEntry) {
boolean setChangedByRemove = entrySet.remove(addressEntry);
boolean setChangedByAdd = entrySet.add(new AddressEntry(addressEntry.getKeyPair(),
AddressEntry.Context.AVAILABLE));
AddressEntry.Context.AVAILABLE,
addressEntry.isSegwit()));
if (setChangedByRemove || setChangedByAdd) {
requestPersistence();
}
Expand All @@ -202,7 +208,7 @@ public AddressEntry swapAvailableToAddressEntryWithOfferId(AddressEntry addressE
AddressEntry.Context context,
String offerId) {
boolean setChangedByRemove = entrySet.remove(addressEntry);
final AddressEntry newAddressEntry = new AddressEntry(addressEntry.getKeyPair(), context, offerId);
final AddressEntry newAddressEntry = new AddressEntry(addressEntry.getKeyPair(), context, offerId, addressEntry.isSegwit());
boolean setChangedByAdd = entrySet.add(newAddressEntry);
if (setChangedByRemove || setChangedByAdd)
requestPersistence();
Expand All @@ -225,10 +231,10 @@ private void maybeAddNewAddressEntry(Transaction tx) {
.map(output -> output.getScriptPubKey().getToAddress(wallet.getNetworkParameters()))
.filter(Objects::nonNull)
.filter(this::isAddressNotInEntries)
.map(address -> (DeterministicKey) wallet.findKeyFromPubKeyHash(address.getHash(),
Script.ScriptType.P2PKH))
.filter(Objects::nonNull)
.map(deterministicKey -> new AddressEntry(deterministicKey, AddressEntry.Context.AVAILABLE))
.map(address -> Pair.of(address, (DeterministicKey) wallet.findKeyFromAddress(address)))
.filter(pair -> pair.getRight() != null)
.map(pair -> new AddressEntry(pair.getRight(), AddressEntry.Context.AVAILABLE,
pair.getLeft() instanceof SegwitAddress))
.forEach(this::addAddressEntry);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ public class BisqKeyChainGroupStructure implements KeyChainGroupStructure {
new ChildNumber(142, true),
ChildNumber.ZERO_HARDENED);

public static final ImmutableList<ChildNumber> BIP44_BSQ_SEGWIT_ACCOUNT_PATH = ImmutableList.of(
new ChildNumber(44, true),
new ChildNumber(142, true),
ChildNumber.ONE_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);

private boolean isBsqWallet;

Expand All @@ -71,7 +72,8 @@ 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;
//return BIP44_BSQ_SEGWIT_ACCOUNT_PATH;
throw new IllegalArgumentException(outputScriptType.toString());
else
throw new IllegalArgumentException(outputScriptType.toString());
}
Expand Down
Loading

0 comments on commit 35e0c34

Please sign in to comment.