address parsing support

This commit is contained in:
Craig Raw 2020-06-30 09:22:23 +02:00
parent 24cde9d073
commit c4dd1cb9dd
5 changed files with 151 additions and 1 deletions

View file

@ -1,8 +1,13 @@
package com.sparrowwallet.drongo.address;
import com.sparrowwallet.drongo.protocol.Base58;
import com.sparrowwallet.drongo.protocol.Bech32;
import com.sparrowwallet.drongo.protocol.ScriptType;
import java.util.Arrays;
import static com.sparrowwallet.drongo.address.P2WPKHAddress.HRP;
public abstract class Address {
protected final byte[] hash;
@ -42,4 +47,53 @@ public abstract class Address {
public int hashCode() {
return getAddress().hashCode();
}
public static Address fromString(String address) throws InvalidAddressException {
Exception nested = null;
if(address != null && (address.startsWith("1") || address.startsWith("3"))) {
try {
byte[] decodedBytes = Base58.decodeChecked(address);
if(decodedBytes.length == 21) {
int version = decodedBytes[0];
byte[] hash = Arrays.copyOfRange(decodedBytes, 1, 21);
if(version == 0) {
return new P2PKHAddress(hash);
}
if(version == 5) {
return new P2SHAddress(hash);
}
}
} catch (Exception e) {
nested = e;
}
}
if(address != null && address.startsWith(HRP)) {
try {
Bech32.Bech32Data data = Bech32.decode(address);
if (data.hrp.equals(HRP)) {
int witnessVersion = data.data[0];
if (witnessVersion == 0) {
byte[] convertedProgram = Arrays.copyOfRange(data.data, 1, data.data.length);
byte[] witnessProgram = Bech32.convertBits(convertedProgram, 0, convertedProgram.length, 5, 8, false);
if (witnessProgram.length == 20) {
return new P2WPKHAddress(witnessProgram);
}
if (witnessProgram.length == 32) {
return new P2WSHAddress(witnessProgram);
}
}
}
} catch (Exception e) {
nested = e;
}
}
if(nested != null) {
throw new InvalidAddressException("Could not parse invalid address " + address, nested);
}
throw new InvalidAddressException("Could not parse invalid address " + address);
}
}

View file

@ -0,0 +1,19 @@
package com.sparrowwallet.drongo.address;
public class InvalidAddressException extends Exception {
public InvalidAddressException() {
super();
}
public InvalidAddressException(String msg) {
super(msg);
}
public InvalidAddressException(Throwable cause) {
super(cause);
}
public InvalidAddressException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -178,7 +178,7 @@ public class Bech32 {
/**
* Helper for re-arranging bits into groups.
*/
private static byte[] convertBits(final byte[] in, final int inStart, final int inLen, final int fromBits,
public static byte[] convertBits(final byte[] in, final int inStart, final int inLen, final int fromBits,
final int toBits, final boolean pad) {
int acc = 0;
int bits = 0;

View file

@ -0,0 +1,7 @@
package com.sparrowwallet.drongo.wallet;
import java.util.Collection;
public interface UtxoSelector {
Collection<BlockTransactionHashIndex> select(long targetValue, Collection<BlockTransactionHashIndex> candidates);
}

View file

@ -0,0 +1,70 @@
package com.sparrowwallet.drongo.address;
import org.junit.Assert;
import org.junit.Test;
import java.security.SecureRandom;
public class AddressTest {
@Test
public void validAddressTest() throws InvalidAddressException {
Address address1 = Address.fromString("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4");
Assert.assertTrue(address1 instanceof P2WPKHAddress);
Assert.assertEquals("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", address1.toString());
Address address2 = Address.fromString("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3");
Assert.assertTrue(address2 instanceof P2WSHAddress);
Assert.assertEquals("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", address2.toString());
Address address3 = Address.fromString("19Sp9dLinHy3dKo2Xxj53ouuZWAoVGGhg8");
Assert.assertTrue(address3 instanceof P2PKHAddress);
Assert.assertEquals("19Sp9dLinHy3dKo2Xxj53ouuZWAoVGGhg8", address3.toString());
Address address4 = Address.fromString("34jnjFM4SbaB7Q8aMtNDG849RQ1gUYgpgo");
Assert.assertTrue(address4 instanceof P2SHAddress);
Assert.assertEquals("34jnjFM4SbaB7Q8aMtNDG849RQ1gUYgpgo", address4.toString());
}
@Test
public void validRandomAddressTest() throws InvalidAddressException {
SecureRandom random = new SecureRandom();
byte[] values = new byte[20];
for(int i = 0; i < 100; i++) {
random.nextBytes(values);
Address address = (i % 2 == 0 ? new P2PKHAddress(values) : new P2WPKHAddress(values));
String strAddress = address.toString();
Address checkAddress = Address.fromString(strAddress);
Assert.assertArrayEquals(values, checkAddress.getHash());
}
byte[] values32 = new byte[32];
for(int i = 0; i < 100; i++) {
random.nextBytes(values32);
Address address = new P2WSHAddress(values32);
String strAddress = address.toString();
Address checkAddress = Address.fromString(strAddress);
Assert.assertArrayEquals(values32, checkAddress.getHash());
}
}
@Test(expected = InvalidAddressException.class)
public void invalidCharacterAddressTest() throws InvalidAddressException {
Address address1 = Address.fromString("bc1qw508d6qejxtdg4y5R3zarvary0c5xw7kv8f3t4");
}
@Test(expected = InvalidAddressException.class)
public void invalidVersionAddressTest() throws InvalidAddressException {
Address address1 = Address.fromString("44jnjFM4SbaB7Q8aMtNDG849RQ1gUYgpgo");
}
@Test(expected = InvalidAddressException.class)
public void invalidChecksumAddressTest() throws InvalidAddressException {
Address address1 = Address.fromString("34jnjFM4SbaB7Q7aMtNDG849RQ1gUYgpgo");
}
@Test(expected = InvalidAddressException.class)
public void invalidChecksumAddressTest2() throws InvalidAddressException {
Address address1 = Address.fromString("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmb3");
}
}