better handle NetworkParameters in Address to allow other kind of networks

This commit is contained in:
Giannis Dzegoutanis
2014-09-10 14:25:09 +03:00
committed by Mike Hearn
parent e9204fd196
commit a6613f9b9b
4 changed files with 120 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
/**
* Copyright 2011 Google Inc.
* Copyright 2014 Giannis Dzegoutanis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +17,7 @@
package com.google.bitcoin.core;
import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.params.TestNet3Params;
import com.google.bitcoin.params.Networks;
import com.google.bitcoin.script.Script;
import javax.annotation.Nullable;
@@ -42,6 +42,8 @@ public class Address extends VersionedChecksummedBytes {
*/
public static final int LENGTH = 20;
transient final NetworkParameters params;
/**
* Construct an address from parameters, the address version, and the hash160 form. Example:<p>
*
@@ -53,6 +55,7 @@ public class Address extends VersionedChecksummedBytes {
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
if (!isAcceptableVersion(params, version))
throw new WrongNetworkException(version, params.getAcceptableAddressCodes());
this.params = params;
}
/** Returns an Address that represents the given P2SH script hash. */
@@ -78,6 +81,7 @@ public class Address extends VersionedChecksummedBytes {
public Address(NetworkParameters params, byte[] hash160) {
super(params.getAddressHeader(), hash160);
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
this.params = params;
}
/**
@@ -96,6 +100,19 @@ public class Address extends VersionedChecksummedBytes {
if (!isAcceptableVersion(params, version)) {
throw new WrongNetworkException(version, params.getAcceptableAddressCodes());
}
this.params = params;
} else {
NetworkParameters paramsFound = null;
for (NetworkParameters p : Networks.get()) {
if (isAcceptableVersion(p, version)) {
paramsFound = p;
break;
}
}
if (paramsFound == null)
throw new AddressFormatException("No network found for " + address);
this.params = paramsFound;
}
}
@@ -119,27 +136,19 @@ public class Address extends VersionedChecksummedBytes {
* compatible with the current wallet. You should be able to handle a null response from this method. Note that the
* parameters returned is not necessarily the same as the one the Address was created with.
*
* @return a NetworkParameters representing the network the address is intended for, or null if unknown.
* @return a NetworkParameters representing the network the address is intended for.
*/
@Nullable
public NetworkParameters getParameters() {
// TODO: There should be a more generic way to get all supported networks.
NetworkParameters[] networks = { TestNet3Params.get(), MainNetParams.get() };
for (NetworkParameters params : networks) {
if (isAcceptableVersion(params, version)) {
return params;
}
}
return null;
return params;
}
/**
* Given an address, examines the version byte and attempts to find a matching NetworkParameters. If you aren't sure
* which network the address is intended for (eg, it was provided by a user), you can use this to decide if it is
* compatible with the current wallet.
* @return a NetworkParameters or null if the string wasn't of a known version.
* @return a NetworkParameters of the address
* @throws AddressFormatException if the string wasn't of a known version
*/
@Nullable
public static NetworkParameters getParametersFromAddress(String address) throws AddressFormatException {
try {
return new Address(null, address).getParameters();

View File

@@ -21,13 +21,15 @@ import com.google.bitcoin.params.*;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptOpCodes;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import static com.google.bitcoin.core.Coin.*;

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2014 Giannis Dzegoutanis
*
* 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 com.google.bitcoin.params;
import com.google.bitcoin.core.NetworkParameters;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.Set;
/**
* Utility class that holds all the registered NetworkParameters types used for Address auto discovery.
* By default only MainNetParams and TestNet3Params are used. If you want to use TestNet2, RegTestParams or
* UnitTestParams use the register and unregister the TestNet3Params as they don't have their own address
* version/type code.
*/
public class Networks {
/** Registered networks */
private static Set<NetworkParameters> networks = ImmutableSet.of(TestNet3Params.get(), MainNetParams.get());
public static Set<NetworkParameters> get() {
return networks;
}
public static void register(NetworkParameters network) {
register(Lists.newArrayList(network));
}
public static void register(Collection<? extends NetworkParameters> networks) {
ImmutableSet.Builder<NetworkParameters> builder = ImmutableSet.builder();
builder.addAll(Networks.networks);
builder.addAll(networks);
Networks.networks = builder.build();
}
public static void unregister(NetworkParameters network) {
if (networks.contains(network)) {
ImmutableSet.Builder<NetworkParameters> builder = ImmutableSet.builder();
for (NetworkParameters parameters : networks) {
if (parameters.equals(network))
continue;
builder.add(parameters);
}
networks = builder.build();
}
}
}

View File

@@ -18,6 +18,7 @@
package com.google.bitcoin.core;
import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.params.Networks;
import com.google.bitcoin.params.TestNet3Params;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder;
@@ -88,7 +89,7 @@ public class AddressTest {
fail();
}
}
@Test
public void getNetwork() throws Exception {
NetworkParameters params = Address.getParametersFromAddress("17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL");
@@ -96,6 +97,35 @@ public class AddressTest {
params = Address.getParametersFromAddress("n4eA2nbYqErp7H6jebchxAN59DmNpksexv");
assertEquals(TestNet3Params.get().getId(), params.getId());
}
@Test
public void getAltNetwork() throws Exception {
// An alternative network
class AltNetwork extends MainNetParams {
AltNetwork() {
super();
id = "alt.network";
addressHeader = 48;
p2shHeader = 5;
acceptableAddressCodes = new int[] { addressHeader, p2shHeader };
}
}
AltNetwork altNetwork = new AltNetwork();
// Add new network params
Networks.register(altNetwork);
// Check if can parse address
NetworkParameters params = Address.getParametersFromAddress("LLxSnHLN2CYyzB5eWTR9K9rS9uWtbTQFb6");
assertEquals(altNetwork.getId(), params.getId());
// Check if main network works as before
params = Address.getParametersFromAddress("17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL");
assertEquals(MainNetParams.get().getId(), params.getId());
// Unregister network
Networks.unregister(altNetwork);
try {
Address.getParametersFromAddress("LLxSnHLN2CYyzB5eWTR9K9rS9uWtbTQFb6");
fail();
} catch (AddressFormatException e) { }
}
@Test
public void p2shAddress() throws Exception {