mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-03-13 11:36:15 +01:00
Address: deprecate fromString, replace with AddressParser
This change migrates from using `NetworkParameters` to `Network` for specifying the network and also decouples from static methods in `Address` with an interface/implementation approach. Note that there are 3 use cases for address parsing: 1. Any network is allowed - AddressParser.parseAddressAnyNetwork(String) 2. Parse for a specified network - AddressParser.parseAddress(String, Network) 3. Parse for a previously-specified (context dependent) network - AddressParser.Strict.parseAddress(String) In most use cases, an AddressParser instance can be accessed through the Wallet, which already knows the Network type and in this context validation for network makes sense. This is why `Wallet` is implementing `AddressParser.Strict` BitcoinURI allocates its own DefaultAddressParser for now, as do some other tests and examples that don't have access to a Wallet In the future DefaultAddressParser may be replaced by something loaded via the ServiceLoader mechanism or other dynamically configured mechanism.
This commit is contained in:
parent
279b35b25f
commit
51f1d69e87
14 changed files with 161 additions and 38 deletions
|
@ -30,10 +30,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
/**
|
||||
* Base class for addresses, e.g. native segwit addresses ({@link SegwitAddress}) or legacy addresses ({@link LegacyAddress}).
|
||||
* <p>
|
||||
* Use {@link #fromString(NetworkParameters, String)} to conveniently construct any kind of address from its textual
|
||||
* Use an implementation of {@link AddressParser#parseAddress(String, BitcoinNetwork)} to conveniently construct any kind of address from its textual
|
||||
* form.
|
||||
*/
|
||||
public abstract class Address implements Comparable<Address> {
|
||||
protected static final AddressParser addressParser = new DefaultAddressParser();
|
||||
protected final NetworkParameters params;
|
||||
protected final byte[] bytes;
|
||||
|
||||
|
@ -60,22 +61,14 @@ public abstract class Address implements Comparable<Address> {
|
|||
* if the given string doesn't parse or the checksum is invalid
|
||||
* @throws AddressFormatException.WrongNetwork
|
||||
* if the given string is valid but not for the expected network (eg testnet vs mainnet)
|
||||
* @deprecated Use {@link org.bitcoinj.wallet.Wallet#parseAddress(String)} or {@link AddressParser#parseAddress(String, BitcoinNetwork)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Address fromString(@Nullable NetworkParameters params, String str)
|
||||
throws AddressFormatException {
|
||||
try {
|
||||
return LegacyAddress.fromBase58(params, str);
|
||||
} catch (AddressFormatException.WrongNetwork x) {
|
||||
throw x;
|
||||
} catch (AddressFormatException x) {
|
||||
try {
|
||||
return SegwitAddress.fromBech32(params, str);
|
||||
} catch (AddressFormatException.WrongNetwork x2) {
|
||||
throw x;
|
||||
} catch (AddressFormatException x2) {
|
||||
throw new AddressFormatException(str);
|
||||
}
|
||||
}
|
||||
return (params != null)
|
||||
? addressParser.parseAddress(str, params.network())
|
||||
: addressParser.parseAddressAnyNetwork(str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
55
core/src/main/java/org/bitcoinj/core/AddressParser.java
Normal file
55
core/src/main/java/org/bitcoinj/core/AddressParser.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright by the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.base.BitcoinNetwork;
|
||||
import org.bitcoinj.base.exceptions.AddressFormatException;
|
||||
|
||||
|
||||
// TODO: Move this class to o.b.base
|
||||
/**
|
||||
* Interface for parsing and validating address strings.
|
||||
*/
|
||||
public interface AddressParser {
|
||||
/**
|
||||
* Parse an address that could be for any network
|
||||
* @param addressString string representation of address
|
||||
* @return A validated address object
|
||||
* @throws AddressFormatException invalid address string
|
||||
*/
|
||||
Address parseAddressAnyNetwork(String addressString) throws AddressFormatException;
|
||||
|
||||
/**
|
||||
* Parse an address and validate for specified network
|
||||
* @param addressString string representation of address
|
||||
* @param network the network the address string must represent
|
||||
* @return A validated address object
|
||||
* @throws AddressFormatException invalid address string or not valid for specified network
|
||||
*/
|
||||
Address parseAddress(String addressString, BitcoinNetwork network) throws AddressFormatException;
|
||||
|
||||
@FunctionalInterface
|
||||
interface Strict {
|
||||
/**
|
||||
* Parse an address in a strict context (e.g. the network must be valid)
|
||||
* @param addressString string representation of address
|
||||
* @return A validated address object
|
||||
* @throws AddressFormatException invalid address string or not valid for network (provided by context)
|
||||
*/
|
||||
Address parseAddress(String addressString) throws AddressFormatException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright by the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.base.BitcoinNetwork;
|
||||
import org.bitcoinj.base.exceptions.AddressFormatException;
|
||||
|
||||
/**
|
||||
* Address Parser that knows about the address types supported by bitcoinj core.
|
||||
*/
|
||||
public class DefaultAddressParser implements AddressParser {
|
||||
@Override
|
||||
public Address parseAddressAnyNetwork(String addressString) throws AddressFormatException {
|
||||
return parseAddress(addressString, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address parseAddress(String addressString, BitcoinNetwork network) throws AddressFormatException {
|
||||
NetworkParameters params = (network != null) ? NetworkParameters.of(network) : null;
|
||||
try {
|
||||
return LegacyAddress.fromBase58(params, addressString);
|
||||
} catch (AddressFormatException.WrongNetwork x) {
|
||||
throw x;
|
||||
} catch (AddressFormatException x) {
|
||||
try {
|
||||
return SegwitAddress.fromBech32(params, addressString);
|
||||
} catch (AddressFormatException.WrongNetwork x2) {
|
||||
throw x;
|
||||
} catch (AddressFormatException x2) {
|
||||
throw new AddressFormatException(addressString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -190,8 +190,7 @@ public class LegacyAddress extends Address {
|
|||
*/
|
||||
@Deprecated
|
||||
public static NetworkParameters getParametersFromAddress(String address) throws AddressFormatException {
|
||||
// TODO: Provide a `Network`-based mechanism for resolving "alt addresses"
|
||||
return NetworkParameters.fromAddress(LegacyAddress.fromBase58(null, address));
|
||||
return NetworkParameters.fromAddress(Address.addressParser.parseAddressAnyNetwork(address));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.bitcoinj.base.Network;
|
|||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.base.exceptions.AddressFormatException;
|
||||
import org.bitcoinj.base.Coin;
|
||||
import org.bitcoinj.core.AddressParser;
|
||||
import org.bitcoinj.core.DefaultAddressParser;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -78,6 +80,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
* @see <a href="https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki">BIP 0021</a>
|
||||
*/
|
||||
public class BitcoinURI {
|
||||
private AddressParser addressParser = new DefaultAddressParser();
|
||||
// Not worth turning into an enum
|
||||
public static final String FIELD_MESSAGE = "message";
|
||||
public static final String FIELD_LABEL = "label";
|
||||
|
@ -177,7 +180,9 @@ public class BitcoinURI {
|
|||
if (!addressToken.isEmpty()) {
|
||||
// Attempt to parse the addressToken as a Bitcoin address for this network
|
||||
try {
|
||||
Address address = Address.fromString(params, addressToken);
|
||||
Address address = (params != null)
|
||||
? addressParser.parseAddress(addressToken, params.network())
|
||||
: addressParser.parseAddressAnyNetwork(addressToken);
|
||||
putWithValidation(FIELD_ADDRESS, address);
|
||||
} catch (final AddressFormatException e) {
|
||||
throw new BitcoinURIParseException("Bad address", e);
|
||||
|
|
|
@ -26,14 +26,17 @@ import com.google.protobuf.ByteString;
|
|||
import net.jcip.annotations.GuardedBy;
|
||||
import org.bitcoinj.base.BitcoinNetwork;
|
||||
import org.bitcoinj.base.Network;
|
||||
import org.bitcoinj.base.exceptions.AddressFormatException;
|
||||
import org.bitcoinj.base.utils.StreamUtils;
|
||||
import org.bitcoinj.core.AbstractBlockChain;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.base.Base58;
|
||||
import org.bitcoinj.core.AddressParser;
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.BloomFilter;
|
||||
import org.bitcoinj.base.Coin;
|
||||
import org.bitcoinj.core.Context;
|
||||
import org.bitcoinj.core.DefaultAddressParser;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.FilteredBlock;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
|
@ -174,8 +177,11 @@ import static com.google.common.base.Preconditions.checkState;
|
|||
* for more information about this.</p>
|
||||
*/
|
||||
public class Wallet extends BaseTaggableObject
|
||||
implements NewBestBlockListener, TransactionReceivedInBlockListener, PeerFilterProvider, KeyBag, TransactionBag, ReorganizeListener {
|
||||
implements NewBestBlockListener, TransactionReceivedInBlockListener, PeerFilterProvider,
|
||||
KeyBag, TransactionBag, ReorganizeListener, AddressParser.Strict {
|
||||
private static final Logger log = LoggerFactory.getLogger(Wallet.class);
|
||||
private static final AddressParser addressParser = new DefaultAddressParser();
|
||||
|
||||
// Ordering: lock > keyChainGroupLock. KeyChainGroup is protected separately to allow fast querying of current receive address
|
||||
// even if the wallet itself is busy e.g. saving or processing a big reorg. Useful for reducing UI latency.
|
||||
protected final ReentrantLock lock = Threading.lock(Wallet.class);
|
||||
|
@ -494,6 +500,17 @@ public class Wallet extends BaseTaggableObject
|
|||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an address string using all formats this wallet knows about for the wallet's network type
|
||||
* @param addressString Address string to parse
|
||||
* @return A validated address
|
||||
* @throws AddressFormatException if invalid string
|
||||
*/
|
||||
@Override
|
||||
public Address parseAddress(String addressString) throws AddressFormatException {
|
||||
return addressParser.parseAddress(addressString, params.network());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active keychains via {@link KeyChainGroup#getActiveKeyChains(long)}.
|
||||
*/
|
||||
|
|
|
@ -31,6 +31,8 @@ import static org.junit.Assert.assertEquals;
|
|||
* the default comparators.
|
||||
*/
|
||||
public class AddressComparatorSortTest {
|
||||
private static final AddressParser addressParser = new DefaultAddressParser();
|
||||
|
||||
/**
|
||||
* A manually sorted list of address for verifying sorting with our default comparator.
|
||||
* See {@link Address#compareTo}.
|
||||
|
@ -48,7 +50,7 @@ public class AddressComparatorSortTest {
|
|||
// Test net, Segwit
|
||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
|
||||
"tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"
|
||||
).map(s -> Address.fromString(null, s))
|
||||
).map(addressParser::parseAddressAnyNetwork)
|
||||
.collect(StreamUtils.toUnmodifiableList());
|
||||
|
||||
@Test
|
||||
|
|
|
@ -46,8 +46,9 @@ public class DoubleSpend {
|
|||
System.out.println(kit.wallet());
|
||||
|
||||
kit.wallet().getBalanceFuture(COIN, Wallet.BalanceType.AVAILABLE).get();
|
||||
Transaction tx1 = kit.wallet().createSend(Address.fromString(kit.params(), "bcrt1qsmf9envp5dphlu6my2tpwfmce0793jvpvlg5ez"), CENT);
|
||||
Transaction tx2 = kit.wallet().createSend(Address.fromString(kit.params(), "bcrt1qsmf9envp5dphlu6my2tpwfmce0793jvpvlg5ez"), CENT.add(SATOSHI.multiply(10)));
|
||||
Address destinationAddress = kit.wallet().parseAddress("bcrt1qsmf9envp5dphlu6my2tpwfmce0793jvpvlg5ez");
|
||||
Transaction tx1 = kit.wallet().createSend(destinationAddress, CENT);
|
||||
Transaction tx2 = kit.wallet().createSend(destinationAddress, CENT.add(SATOSHI.multiply(10)));
|
||||
final Peer peer = kit.peerGroup().getConnectedPeers().get(0);
|
||||
peer.addPreMessageReceivedEventListener(Threading.SAME_THREAD,
|
||||
(peer1, m) -> {
|
||||
|
|
|
@ -21,9 +21,10 @@ import org.bitcoinj.base.ScriptType;
|
|||
import org.bitcoinj.base.Sha256Hash;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.base.Coin;
|
||||
import org.bitcoinj.core.AddressParser;
|
||||
import org.bitcoinj.core.Context;
|
||||
import org.bitcoinj.core.DefaultAddressParser;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionBroadcast;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
@ -69,13 +70,14 @@ public class ForwardingService implements AutoCloseable {
|
|||
// Figure out which network we should connect to. Each network gets its own set of files.
|
||||
Address address;
|
||||
BitcoinNetwork network;
|
||||
AddressParser addressParser = new DefaultAddressParser();
|
||||
if (args.length >= 2) {
|
||||
// Verify address belongs to network
|
||||
network = BitcoinNetwork.fromString(args[1]).orElseThrow();
|
||||
address = Address.fromString(NetworkParameters.of(network), args[0]);
|
||||
address = addressParser.parseAddress(args[0], network);
|
||||
} else {
|
||||
// Infer network from address
|
||||
address = Address.fromString(null, args[0]);
|
||||
address = addressParser.parseAddressAnyNetwork(args[0]);
|
||||
network = address.network();
|
||||
}
|
||||
|
||||
|
|
|
@ -60,13 +60,14 @@ public class PrivateKeys {
|
|||
key = ECKey.fromPrivate(privKey);
|
||||
}
|
||||
System.out.println("Address from private key is: " + SegwitAddress.fromKey(params, key).toString());
|
||||
// And the address ...
|
||||
Address destination = Address.fromString(params, args[1]);
|
||||
|
||||
// Import the private key to a fresh wallet.
|
||||
Wallet wallet = Wallet.createDeterministic(params, ScriptType.P2PKH);
|
||||
wallet.importKey(key);
|
||||
|
||||
// And the address ...
|
||||
Address destination = wallet.parseAddress(args[1]);
|
||||
|
||||
// Find the transactions that involve those coins.
|
||||
final MemoryBlockStore blockStore = new MemoryBlockStore(params);
|
||||
BlockChain chain = new BlockChain(params, wallet, blockStore);
|
||||
|
|
|
@ -48,7 +48,7 @@ public class SendRequest {
|
|||
|
||||
// To which address you want to send the coins?
|
||||
// The Address class represents a Bitcoin address.
|
||||
Address to = Address.fromString(kit.params(), "bcrt1qspfueag7fvty7m8htuzare3xs898zvh30fttu2");
|
||||
Address to = kit.wallet().parseAddress("bcrt1qspfueag7fvty7m8htuzare3xs898zvh30fttu2");
|
||||
System.out.println("Send money to: " + to.toString());
|
||||
|
||||
// There are different ways to create and publish a SendRequest. This is probably the easiest one.
|
||||
|
|
|
@ -16,13 +16,12 @@
|
|||
|
||||
package org.bitcoinj.walletfx.controls;
|
||||
|
||||
import org.bitcoinj.base.BitcoinNetwork;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.base.exceptions.AddressFormatException;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.TextField;
|
||||
import org.bitcoinj.core.AddressParser;
|
||||
import org.bitcoinj.walletfx.utils.TextFieldValidator;
|
||||
|
||||
/**
|
||||
|
@ -30,11 +29,12 @@ import org.bitcoinj.walletfx.utils.TextFieldValidator;
|
|||
* if the address is invalid for those params, and enable/disable the nodes.
|
||||
*/
|
||||
public class BitcoinAddressValidator {
|
||||
private BitcoinNetwork network;
|
||||
private final AddressParser.Strict parser;
|
||||
private Node[] nodes;
|
||||
|
||||
public BitcoinAddressValidator(BitcoinNetwork network, TextField field, Node... nodes) {
|
||||
this.network = network;
|
||||
|
||||
public BitcoinAddressValidator(AddressParser.Strict parser, TextField field, Node... nodes) {
|
||||
this.parser = parser;
|
||||
this.nodes = nodes;
|
||||
|
||||
// Handle the red highlighting, but don't highlight in red just when the field is empty because that makes
|
||||
|
@ -52,7 +52,7 @@ public class BitcoinAddressValidator {
|
|||
|
||||
private boolean testAddr(String text) {
|
||||
try {
|
||||
Address.fromString(NetworkParameters.of(network), text);
|
||||
parser.parseAddress(text);
|
||||
return true;
|
||||
} catch (AddressFormatException e) {
|
||||
return false;
|
||||
|
|
|
@ -69,7 +69,7 @@ public class SendMoneyController implements OverlayController<SendMoneyControlle
|
|||
app = WalletApplication.instance();
|
||||
Coin balance = app.walletAppKit().wallet().getBalance();
|
||||
checkState(!balance.isZero());
|
||||
new BitcoinAddressValidator(app.network(), address, sendBtn);
|
||||
new BitcoinAddressValidator(app.walletAppKit().wallet(), address, sendBtn);
|
||||
new TextFieldValidator(amountEdit, text ->
|
||||
!WTUtils.didThrow(() -> checkState(Coin.parseCoin(text).compareTo(balance) <= 0)));
|
||||
amountEdit.setText(balance.toPlainString());
|
||||
|
@ -84,7 +84,7 @@ public class SendMoneyController implements OverlayController<SendMoneyControlle
|
|||
// Address exception cannot happen as we validated it beforehand.
|
||||
try {
|
||||
Coin amount = Coin.parseCoin(amountEdit.getText());
|
||||
Address destination = Address.fromString(NetworkParameters.of(app.network()), address.getText());
|
||||
Address destination = app.walletAppKit().wallet().parseAddress(address.getText());
|
||||
SendRequest req;
|
||||
if (amount.equals(app.walletAppKit().wallet().getBalance()))
|
||||
req = SendRequest.emptyWallet(destination);
|
||||
|
|
|
@ -446,7 +446,7 @@ public class WalletTool implements Callable<Integer> {
|
|||
if (selectAddrStr != null) {
|
||||
Address selectAddr;
|
||||
try {
|
||||
selectAddr = Address.fromString(params, selectAddrStr);
|
||||
selectAddr = wallet.parseAddress(selectAddrStr);
|
||||
} catch (AddressFormatException x) {
|
||||
System.err.println("Could not parse given address, or wrong network: " + selectAddrStr);
|
||||
return 1;
|
||||
|
@ -761,7 +761,7 @@ public class WalletTool implements Callable<Integer> {
|
|||
addr = null;
|
||||
} else {
|
||||
// Treat as an address.
|
||||
addr = Address.fromString(params, destination);
|
||||
addr = wallet.parseAddress(destination);
|
||||
key = null;
|
||||
}
|
||||
}
|
||||
|
@ -1228,7 +1228,7 @@ public class WalletTool implements Callable<Integer> {
|
|||
key = wallet.findKeyFromPubKey(HEX.decode(pubKeyStr));
|
||||
} else {
|
||||
try {
|
||||
Address address = Address.fromString(wallet.getParams(), addrStr);
|
||||
Address address = wallet.parseAddress(addrStr);
|
||||
key = wallet.findKeyFromAddress(address);
|
||||
} catch (AddressFormatException e) {
|
||||
System.err.println(addrStr + " does not parse as a Bitcoin address of the right network parameters.");
|
||||
|
|
Loading…
Add table
Reference in a new issue