Skip to content

Commit

Permalink
Allow use of bech32 BSQ addresses
Browse files Browse the repository at this point in the history
Remove the restriction to base58 (P2SH & P2PKH) addresses when parsing,
formatting & validating BSQ addresses, by replacing o.b.c.LegacyAddress
with its superclass o.b.c.Address throughout. Also remove restriction to
LegacyAddress in BsqTxListItem and BsqTransfer(Service|Model).

The bech32 BSQ addresses follow the same format as the old base58 BSQ
addresses, namely 'B' + <btc-address>.
  • Loading branch information
stejbac committed Apr 27, 2021
1 parent ce73fb8 commit 3470429
Show file tree
Hide file tree
Showing 13 changed files with 48 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import bisq.proto.grpc.BsqBalanceInfo;

import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.NetworkParameters;

import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -64,7 +64,7 @@ public void testGetUnusedBsqAddress() {
assertFalse(address.isEmpty());
assertTrue(address.startsWith("B"));

NetworkParameters networkParameters = LegacyAddress.getParametersFromAddress(address.substring(1));
NetworkParameters networkParameters = Address.fromString(null, address.substring(1)).getParameters();
String addressNetwork = networkParameters.getPaymentProtocolId();
assertNotEquals(PAYMENT_PROTOCOL_ID_MAINNET, addressNetwork);
// TODO Fix bug causing the regtest bsq address network to be evaluated as 'testnet' here.
Expand Down
6 changes: 3 additions & 3 deletions assets/src/main/java/bisq/asset/coins/BSQ.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
package bisq.asset.coins;

import bisq.asset.AddressValidationResult;
import bisq.asset.Base58AddressValidator;
import bisq.asset.BitcoinAddressValidator;
import bisq.asset.Coin;

import org.bitcoinj.core.NetworkParameters;
Expand Down Expand Up @@ -57,7 +57,7 @@ public Regtest() {
}


public static class BSQAddressValidator extends Base58AddressValidator {
public static class BSQAddressValidator extends BitcoinAddressValidator {

public BSQAddressValidator(NetworkParameters networkParameters) {
super(networkParameters);
Expand All @@ -68,7 +68,7 @@ public AddressValidationResult validate(String address) {
if (!address.startsWith("B"))
return AddressValidationResult.invalidAddress("BSQ address must start with 'B'");

String addressAsBtc = address.substring(1, address.length());
String addressAsBtc = address.substring(1);

return super.validate(addressAsBtc);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
throw new IllegalArgumentException("api does not currently support " + currencyCode + " accounts");

// Validate the BSQ address string but ignore the return value.
coreWalletsService.getValidBsqLegacyAddress(address);
coreWalletsService.getValidBsqAddress(address);

var cryptoCurrencyAccount = tradeInstant
? (InstantCryptoCurrencyAccount) PaymentAccountFactory.getPaymentAccount(PaymentMethod.BLOCK_CHAINS_INSTANT)
Expand Down
17 changes: 8 additions & 9 deletions core/src/main/java/bisq/core/api/CoreWalletsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
Expand Down Expand Up @@ -226,18 +225,18 @@ String getUnusedBsqAddress() {
return bsqWalletService.getUnusedBsqAddressAsString();
}

void sendBsq(String address,
void sendBsq(String addressStr,
String amount,
String txFeeRate,
TxBroadcaster.Callback callback) {
verifyWalletsAreAvailable();
verifyEncryptedWalletIsUnlocked();

try {
LegacyAddress legacyAddress = getValidBsqLegacyAddress(address);
Address address = getValidBsqAddress(addressStr);
Coin receiverAmount = getValidTransferAmount(amount, bsqFormatter);
Coin txFeePerVbyte = getTxFeeRateFromParamOrPreferenceOrFeeService(txFeeRate);
BsqTransferModel model = bsqTransferService.getBsqTransferModel(legacyAddress,
BsqTransferModel model = bsqTransferService.getBsqTransferModel(address,
receiverAmount,
txFeePerVbyte);
log.info("Sending {} BSQ to {} with tx fee rate {} sats/byte.",
Expand Down Expand Up @@ -313,7 +312,7 @@ void sendBtc(String address,
}

boolean verifyBsqSentToAddress(String address, String amount) {
Address receiverAddress = getValidBsqLegacyAddress(address);
Address receiverAddress = getValidBsqAddress(address);
NetworkParameters networkParameters = getNetworkParameters();
Predicate<TransactionOutput> isTxOutputAddressMatch = (txOut) ->
txOut.getScriptPubKey().getToAddress(networkParameters).equals(receiverAddress);
Expand Down Expand Up @@ -553,12 +552,12 @@ void verifyApplicationIsFullyInitialized() {
throw new IllegalStateException("server is not fully initialized");
}

// Returns a LegacyAddress for the string, or a RuntimeException if invalid.
LegacyAddress getValidBsqLegacyAddress(String address) {
// Returns an Address for the string, or a RuntimeException if invalid.
Address getValidBsqAddress(String address) {
try {
return bsqFormatter.getAddressFromBsqAddress(address);
} catch (Throwable t) {
log.error("", t);
} catch (RuntimeException e) {
log.error("", e);
throw new IllegalStateException(format("%s is not a valid bsq address", address));
}
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/bisq/core/btc/model/BsqTransferModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

import bisq.core.dao.state.model.blockchain.TxType;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Transaction;

import lombok.Getter;

@Getter
public final class BsqTransferModel {

private final LegacyAddress receiverAddress;
private final Address receiverAddress;
private final Coin receiverAmount;
private final Transaction preparedSendTx;
private final Transaction txWithBtcFee;
Expand All @@ -20,7 +20,7 @@ public final class BsqTransferModel {
private final int txSize;
private final TxType txType;

public BsqTransferModel(LegacyAddress receiverAddress,
public BsqTransferModel(Address receiverAddress,
Coin receiverAmount,
Transaction preparedSendTx,
Transaction txWithBtcFee,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.model.BsqTransferModel;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Transaction;

import javax.inject.Inject;
Expand All @@ -32,7 +32,7 @@ public BsqTransferService(WalletsManager walletsManager,
this.btcWalletService = btcWalletService;
}

public BsqTransferModel getBsqTransferModel(LegacyAddress address,
public BsqTransferModel getBsqTransferModel(Address address,
Coin receiverAmount,
Coin txFeePerVbyte)
throws TransactionVerificationException,
Expand Down
17 changes: 6 additions & 11 deletions core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ public void commitTx(Transaction tx, TxType txType) {
public Transaction getPreparedSendBsqTx(String receiverAddress, Coin receiverAmount)
throws AddressFormatException, InsufficientBsqException, WalletException,
TransactionVerificationException, BsqChangeBelowDustException {
return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector, false);
return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector);
}

public Transaction getPreparedSendBsqTx(String receiverAddress,
Expand All @@ -538,7 +538,7 @@ public Transaction getPreparedSendBsqTx(String receiverAddress,
if (utxoCandidates != null) {
bsqCoinSelector.setUtxoCandidates(utxoCandidates);
}
return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector, false);
return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector);
}

///////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -548,7 +548,7 @@ public Transaction getPreparedSendBsqTx(String receiverAddress,
public Transaction getPreparedSendBtcTx(String receiverAddress, Coin receiverAmount)
throws AddressFormatException, InsufficientBsqException, WalletException,
TransactionVerificationException, BsqChangeBelowDustException {
return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector, true);
return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector);
}

public Transaction getPreparedSendBtcTx(String receiverAddress,
Expand All @@ -559,21 +559,16 @@ public Transaction getPreparedSendBtcTx(String receiverAddress,
if (utxoCandidates != null) {
nonBsqCoinSelector.setUtxoCandidates(utxoCandidates);
}
return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector, true);
return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector);
}

private Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount, CoinSelector coinSelector,
boolean allowSegwitOuput)
private Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount, CoinSelector coinSelector)
throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException {
daoKillSwitch.assertDaoIsNotDisabled();
Transaction tx = new Transaction(params);
checkArgument(Restrictions.isAboveDust(receiverAmount),
"The amount is too low (dust limit).");
if (allowSegwitOuput) {
tx.addOutput(receiverAmount, Address.fromString(params, receiverAddress));
} else {
tx.addOutput(receiverAmount, LegacyAddress.fromBase58(params, receiverAddress));
}
tx.addOutput(receiverAmount, Address.fromString(params, receiverAddress));
SendRequest sendRequest = SendRequest.forTx(tx);
sendRequest.fee = Coin.ZERO;
sendRequest.feePerKb = Coin.ZERO;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,25 @@

package bisq.core.dao.governance.proposal;

import bisq.common.config.Config;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;

/**
* Marker interface for proposals which can lead to new BSQ issuance
* Interface for proposals which can lead to new BSQ issuance
*/
public interface IssuanceProposal {
Coin getRequestedBsq();

String getBsqAddress();

String getTxId();

default Address getAddress() throws AddressFormatException {
// Remove leading 'B'
String underlyingBtcAddress = getBsqAddress().substring(1);
return Address.fromString(Config.baseCurrencyNetworkParameters(), underlyingBtcAddress);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@
import bisq.core.dao.state.model.blockchain.TxType;

import bisq.common.app.Version;
import bisq.common.config.Config;
import bisq.common.util.CollectionUtils;

import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.LegacyAddress;

import java.util.Date;
import java.util.Map;
Expand Down Expand Up @@ -113,17 +110,11 @@ public static CompensationProposal fromProto(protobuf.Proposal proto) {
// Getters
///////////////////////////////////////////////////////////////////////////////////////////

@Override
public Coin getRequestedBsq() {
return Coin.valueOf(requestedBsq);
}

public LegacyAddress getAddress() throws AddressFormatException {
// Remove leading 'B'
String underlyingBtcAddress = bsqAddress.substring(1, bsqAddress.length());
return LegacyAddress.fromBase58(Config.baseCurrencyNetworkParameters(), underlyingBtcAddress);
}


@Override
public ProposalType getType() {
return ProposalType.COMPENSATION_REQUEST;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@
import bisq.core.dao.state.model.blockchain.TxType;

import bisq.common.app.Version;
import bisq.common.config.Config;
import bisq.common.util.CollectionUtils;

import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.LegacyAddress;

import java.util.Date;
import java.util.Map;
Expand Down Expand Up @@ -113,17 +110,11 @@ public static ReimbursementProposal fromProto(protobuf.Proposal proto) {
// Getters
///////////////////////////////////////////////////////////////////////////////////////////

@Override
public Coin getRequestedBsq() {
return Coin.valueOf(requestedBsq);
}

public LegacyAddress getAddress() throws AddressFormatException {
// Remove leading 'B'
String underlyingBtcAddress = bsqAddress.substring(1, bsqAddress.length());
return LegacyAddress.fromBase58(Config.baseCurrencyNetworkParameters(), underlyingBtcAddress);
}


@Override
public ProposalType getType() {
return ProposalType.REIMBURSEMENT_REQUEST;
Expand Down
10 changes: 5 additions & 5 deletions core/src/main/java/bisq/core/util/coin/BsqFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
import bisq.common.config.Config;
import bisq.common.util.MathUtils;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.utils.MonetaryFormat;

import javax.inject.Inject;
Expand Down Expand Up @@ -94,7 +94,7 @@ private void switchLocale(Locale locale) {
* Returns the base-58 encoded String representation of this
* object, including version and checksum bytes.
*/
public String getBsqAddressStringFromAddress(LegacyAddress address) {
public String getBsqAddressStringFromAddress(Address address) {
final String addressString = address.toString();
if (useBsqAddressFormat)
return prefix + addressString;
Expand All @@ -103,13 +103,13 @@ public String getBsqAddressStringFromAddress(LegacyAddress address) {

}

public LegacyAddress getAddressFromBsqAddress(String encoded) {
public Address getAddressFromBsqAddress(String encoded) {
String maybeUpdatedEncoded = encoded;
if (useBsqAddressFormat)
maybeUpdatedEncoded = encoded.substring(prefix.length(), encoded.length());
maybeUpdatedEncoded = encoded.substring(prefix.length());

try {
return LegacyAddress.fromBase58(Config.baseCurrencyNetworkParameters(), maybeUpdatedEncoded);
return Address.fromString(Config.baseCurrencyNetworkParameters(), maybeUpdatedEncoded);
} catch (AddressFormatException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutput;

Expand Down Expand Up @@ -103,10 +102,9 @@ class BsqTxListItem extends TxConfidenceListItem {
WalletService.isOutputScriptConvertibleToAddress(output)) {
// We don't support send txs with multiple outputs to multiple receivers, so we can
// assume that only one output is not from our own wallets.
// We ignore segwit outputs
Address addressFromOutput = WalletService.getAddressFromOutput(output);
if (addressFromOutput instanceof LegacyAddress) {
sendToAddress = bsqFormatter.getBsqAddressStringFromAddress((LegacyAddress) addressFromOutput);
if (addressFromOutput != null) {
sendToAddress = bsqFormatter.getBsqAddressStringFromAddress(addressFromOutput);
break;
}
}
Expand All @@ -118,9 +116,8 @@ class BsqTxListItem extends TxConfidenceListItem {
for (TransactionOutput output : transaction.getOutputs()) {
if (WalletService.isOutputScriptConvertibleToAddress(output)) {
Address addressFromOutput = WalletService.getAddressFromOutput(output);
// We ignore segwit outputs
if (addressFromOutput instanceof LegacyAddress) {
receivedWithAddress = bsqFormatter.getBsqAddressStringFromAddress((LegacyAddress) addressFromOutput);
if (addressFromOutput != null) {
receivedWithAddress = bsqFormatter.getBsqAddressStringFromAddress(addressFromOutput);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private ValidationResult validateBsqAddress(String input) {
try {
bsqFormatter.getAddressFromBsqAddress(input);
return new ValidationResult(true);
} catch (Throwable e) {
} catch (RuntimeException e) {
return new ValidationResult(false, Res.get("validation.bsq.invalidFormat"));
}
}
Expand Down

0 comments on commit 3470429

Please sign in to comment.