diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/RegistryType.java b/src/main/java/com/sparrowwallet/hummingbird/registry/RegistryType.java index 822a496..217b1a9 100644 --- a/src/main/java/com/sparrowwallet/hummingbird/registry/RegistryType.java +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/RegistryType.java @@ -22,7 +22,17 @@ public enum RegistryType { CRYPTO_OUTPUT("crypto-output", 308, CryptoOutput.class), CRYPTO_SSKR("crypto-sskr", 309, CryptoSskr.class), CRYPTO_PSBT("crypto-psbt", 310, CryptoPSBT.class), - CRYPTO_ACCOUNT("crypto-account", 311, CryptoAccount.class); + CRYPTO_ACCOUNT("crypto-account", 311, CryptoAccount.class), + SEED("seed", 40300, URSeed.class), + HDKEY("hdkey", 40303, URHDKey.class), + KEYPATH("keypath", 40304, URKeypath.class), + COIN_INFO("coininfo", 40305, URCoinInfo.class), + ECKEY("eckey", 40306, URECKey.class), + ADDRESS("address", 40307, URAddress.class), + OUTPUT_DESCRIPTOR("output-descriptor", 40308, UROutputDescriptor.class), + SSKR("sskr", 40309, URSSKR.class), + PSBT("psbt", 40310, URPSBT.class), + ACCOUNT("account", 40311, URAccount.class); private final String type; private final Integer tag; diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URAccount.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URAccount.java new file mode 100644 index 0000000..49c1dfe --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URAccount.java @@ -0,0 +1,34 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.Array; +import co.nstant.in.cbor.model.DataItem; +import co.nstant.in.cbor.model.Map; +import co.nstant.in.cbor.model.UnsignedInteger; + +import java.util.ArrayList; +import java.util.List; + +public class URAccount extends CryptoAccount { + public URAccount(byte[] masterFingerprint, List outputDescriptors) { + super(masterFingerprint, outputDescriptors); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.ACCOUNT; + } + + public static URAccount fromCbor(DataItem cbor) { + Map accountMap = (Map)cbor; + + UnsignedInteger uintMasterFingerprint = (UnsignedInteger)accountMap.get(new UnsignedInteger(MASTER_FINGERPRINT_KEY)); + byte[] masterFingerprint = bigIntegerToBytes(uintMasterFingerprint.getValue(), 4); + Array outputDescriptors = (Array)accountMap.get(new UnsignedInteger(OUTPUT_DESCRIPTORS_KEY)); + List cryptoOutputs = new ArrayList<>(outputDescriptors.getDataItems().size()); + for(DataItem item : outputDescriptors.getDataItems()) { + cryptoOutputs.add(CryptoOutput.fromCbor(item)); + } + + return new URAccount(masterFingerprint, cryptoOutputs); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URAddress.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URAddress.java new file mode 100644 index 0000000..a9fbe12 --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URAddress.java @@ -0,0 +1,42 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.ByteString; +import co.nstant.in.cbor.model.DataItem; +import co.nstant.in.cbor.model.Map; +import co.nstant.in.cbor.model.UnsignedInteger; + +public class URAddress extends CryptoAddress { + public URAddress(CryptoCoinInfo info, Type type, byte[] data) { + super(info, type, data); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.ADDRESS; + } + + public static URAddress fromCbor(DataItem item) { + URCoinInfo info = null; + Type type = null; + byte[] data = null; + + Map map = (Map)item; + for(DataItem key : map.getKeys()) { + UnsignedInteger uintKey = (UnsignedInteger)key; + int intKey = uintKey.getValue().intValue(); + if(intKey == INFO) { + info = URCoinInfo.fromCbor(map.get(key)); + } else if(intKey == TYPE) { + type = Type.values()[((UnsignedInteger)map.get(key)).getValue().intValue()]; + } else if(intKey == DATA) { + data = ((ByteString)map.get(key)).getBytes(); + } + } + + if(data == null) { + throw new IllegalStateException("Data is null"); + } + + return new URAddress(info, type, data); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URCoinInfo.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URCoinInfo.java new file mode 100644 index 0000000..2841cd9 --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URCoinInfo.java @@ -0,0 +1,39 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.DataItem; +import co.nstant.in.cbor.model.Map; +import co.nstant.in.cbor.model.UnsignedInteger; + +public class URCoinInfo extends CryptoCoinInfo { + public URCoinInfo(Integer type, Integer network) { + super(type, network); + } + + public URCoinInfo(Type type, Network network) { + super(type, network); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.COIN_INFO; + } + + public static URCoinInfo fromCbor(DataItem item) { + Integer type = null; + Integer network = null; + + Map map = (Map)item; + for(DataItem key : map.getKeys()) { + UnsignedInteger uintKey = (UnsignedInteger)key; + int intKey = uintKey.getValue().intValue(); + + if(intKey == TYPE_KEY) { + type = ((UnsignedInteger)map.get(key)).getValue().intValue(); + } else if(intKey == NETWORK_KEY) { + network = ((UnsignedInteger)map.get(key)).getValue().intValue(); + } + } + + return new URCoinInfo(type, network); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URECKey.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URECKey.java new file mode 100644 index 0000000..db8799b --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URECKey.java @@ -0,0 +1,39 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.*; + +public class URECKey extends CryptoECKey { + public URECKey(Integer curve, Boolean privateKey, byte[] data) { + super(curve, privateKey, data); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.ECKEY; + } + + public static URECKey fromCbor(DataItem item) { + Integer curve = null; + Boolean privateKey = null; + byte[] data = null; + + Map map = (Map)item; + for(DataItem key : map.getKeys()) { + UnsignedInteger uintKey = (UnsignedInteger)key; + int intKey = uintKey.getValue().intValue(); + if(intKey == CURVE) { + curve = ((UnsignedInteger)map.get(key)).getValue().intValue(); + } else if(intKey == PRIVATE) { + privateKey = (map.get(key) == SimpleValue.TRUE); + } else if(intKey == DATA) { + data = ((ByteString)map.get(key)).getBytes(); + } + } + + if(data == null) { + throw new IllegalStateException("Data is null"); + } + + return new URECKey(curve, privateKey, data); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URHDKey.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URHDKey.java new file mode 100644 index 0000000..e21be8e --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URHDKey.java @@ -0,0 +1,97 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.*; + +public class URHDKey extends CryptoHDKey { + public URHDKey(byte[] key, byte[] chainCode) { + super(key, chainCode); + } + + public URHDKey(Boolean privateKey, byte[] key, byte[] chainCode, CryptoCoinInfo useInfo, CryptoKeypath origin, CryptoKeypath children, byte[] parentFingerprint) { + super(privateKey, key, chainCode, useInfo, origin, children, parentFingerprint); + } + + public URHDKey(Boolean privateKey, byte[] key, byte[] chainCode, CryptoCoinInfo useInfo, CryptoKeypath origin, CryptoKeypath children, byte[] parentFingerprint, String name, String note) { + super(privateKey, key, chainCode, useInfo, origin, children, parentFingerprint, name, note); + } + + public DataItem toCbor() { + Map map = (Map)super.toCbor(); + + DataItem useinfo = map.get(new UnsignedInteger(USE_INFO_KEY)); + if(useinfo != null) { + useinfo.setTag(RegistryType.COIN_INFO.getTag()); + } + + DataItem origin = map.get(new UnsignedInteger(ORIGIN_KEY)); + if(origin != null) { + origin.setTag(RegistryType.KEYPATH.getTag()); + } + + DataItem children = map.get(new UnsignedInteger(CHILDREN_KEY)); + if(children != null) { + children.setTag(RegistryType.KEYPATH.getTag()); + } + + return map; + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.HDKEY; + } + + public static URHDKey fromCbor(DataItem item) { + boolean isMasterKey = false; + Boolean isPrivateKey = null; + byte[] keyData = null; + byte[] chainCode = null; + URCoinInfo useInfo = null; + URKeypath origin = null; + URKeypath children = null; + byte[] parentFingerprint = null; + String name = null; + String note = null; + + Map map = (Map)item; + for(DataItem key : map.getKeys()) { + UnsignedInteger uintKey = (UnsignedInteger)key; + int intKey = uintKey.getValue().intValue(); + if(intKey == IS_MASTER_KEY) { + isMasterKey = (map.get(uintKey) == SimpleValue.TRUE); + } else if(intKey == IS_PRIVATE_KEY) { + isPrivateKey = (map.get(uintKey) == SimpleValue.TRUE); + } else if(intKey == KEY_DATA_KEY) { + keyData = ((ByteString)map.get(uintKey)).getBytes(); + } else if(intKey == CHAIN_CODE_KEY) { + chainCode = ((ByteString)map.get(uintKey)).getBytes(); + } else if(intKey == USE_INFO_KEY) { + useInfo = URCoinInfo.fromCbor(map.get(uintKey)); + } else if(intKey == ORIGIN_KEY) { + origin = URKeypath.fromCbor(map.get(uintKey)); + } else if(intKey == CHILDREN_KEY) { + children = URKeypath.fromCbor(map.get(uintKey)); + } else if(intKey == PARENT_FINGERPRINT_KEY) { + parentFingerprint = bigIntegerToBytes(((UnsignedInteger)map.get(uintKey)).getValue(), 4); + } else if(intKey == NAME_KEY) { + name = ((UnicodeString)map.get(uintKey)).getString(); + } else if(intKey == NOTE_KEY) { + note = ((UnicodeString)map.get(uintKey)).getString(); + } + } + + if(keyData == null) { + throw new IllegalStateException("Key data is null"); + } + + if(isMasterKey) { + if(chainCode == null) { + throw new IllegalStateException("Chain code data is null"); + } + + return new URHDKey(keyData, chainCode); + } else { + return new URHDKey(isPrivateKey, keyData, chainCode, useInfo, origin, children, parentFingerprint, name, note); + } + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URKeypath.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URKeypath.java new file mode 100644 index 0000000..234caf6 --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URKeypath.java @@ -0,0 +1,45 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.DataItem; +import co.nstant.in.cbor.model.Map; +import co.nstant.in.cbor.model.UnsignedInteger; +import com.sparrowwallet.hummingbird.registry.pathcomponent.PathComponent; + +import java.util.ArrayList; +import java.util.List; + +public class URKeypath extends CryptoKeypath { + public URKeypath(List components, byte[] sourceFingerprint) { + super(components, sourceFingerprint); + } + + public URKeypath(List components, byte[] sourceFingerprint, Integer depth) { + super(components, sourceFingerprint, depth); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.KEYPATH; + } + + public static URKeypath fromCbor(DataItem item) { + List components = new ArrayList<>(); + byte[] sourceFingerprint = null; + Integer depth = null; + + Map map = (Map)item; + for(DataItem key : map.getKeys()) { + UnsignedInteger uintKey = (UnsignedInteger)key; + int intKey = uintKey.getValue().intValue(); + if(intKey == COMPONENTS_KEY) { + components = PathComponent.fromCbor(map.get(key)); + } else if(intKey == SOURCE_FINGERPRINT_KEY) { + sourceFingerprint = bigIntegerToBytes(((UnsignedInteger)map.get(key)).getValue(), 4); + } else if(intKey == DEPTH_KEY) { + depth = ((UnsignedInteger)map.get(key)).getValue().intValue(); + } + } + + return new URKeypath(components, sourceFingerprint, depth); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/UROutputDescriptor.java b/src/main/java/com/sparrowwallet/hummingbird/registry/UROutputDescriptor.java new file mode 100644 index 0000000..efe57b4 --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/UROutputDescriptor.java @@ -0,0 +1,121 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.*; + +import java.util.ArrayList; +import java.util.List; + +public class UROutputDescriptor extends RegistryItem { + public static final long SOURCE = 1; + public static final long KEYS = 2; + public static final long NAME = 3; + public static final long NOTE = 4; + + private final String source; + private final List keys; + private final String name; + private final String note; + + public UROutputDescriptor(String source) { + this(source, null); + } + + public UROutputDescriptor(String source, List keys) { + this(source, keys, null, null); + } + + public UROutputDescriptor(String source, List keys, String name, String note) { + this.source = source; + this.keys = keys; + this.name = name; + this.note = note; + + if(keys != null && !keys.stream().allMatch(item -> item instanceof URHDKey || item instanceof URECKey || item instanceof URAddress)) { + throw new IllegalArgumentException("All keys must be one of HDKey, ECKey or Address"); + } + } + + public String getSource() { + return source; + } + + public List getKeys() { + return keys; + } + + public String getName() { + return name; + } + + public String getNote() { + return note; + } + + public DataItem toCbor() { + Map map = new Map(); + map.put(new UnsignedInteger(SOURCE), new UnicodeString(source)); + + if(keys != null && !keys.isEmpty()) { + Array array = new Array(); + for(RegistryItem key : keys) { + array.add(key.toCbor()); + } + map.put(new UnsignedInteger(KEYS), array); + } + + if(name != null) { + map.put(new UnsignedInteger(NAME), new UnicodeString(name)); + } + + if(note != null) { + map.put(new UnsignedInteger(NOTE), new UnicodeString(note)); + } + + return map; + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.OUTPUT_DESCRIPTOR; + } + + public static UROutputDescriptor fromCbor(DataItem item) { + String source = null; + List keys = null; + String name = null; + String note = null; + + Map map = (Map)item; + for(DataItem key : map.getKeys()) { + UnsignedInteger uintKey = (UnsignedInteger)key; + int intKey = uintKey.getValue().intValue(); + if(intKey == SOURCE) { + source = ((UnicodeString)map.get(key)).getString(); + } else if(intKey == KEYS) { + Array keyArray = (Array)map.get(key); + keys = new ArrayList<>(keyArray.getDataItems().size()); + for(DataItem keyItem : keyArray.getDataItems()) { + if(keyItem.getTag().getValue() == RegistryType.HDKEY.getTag()) { + keys.add(URHDKey.fromCbor(keyItem)); + } else if(keyItem.getTag().getValue() == RegistryType.ECKEY.getTag()) { + keys.add(URECKey.fromCbor(keyItem)); + } else if(keyItem.getTag().getValue() == RegistryType.ADDRESS.getTag()) { + keys.add(URAddress.fromCbor(keyItem)); + } else { + throw new IllegalArgumentException("All keys must be one of HDKey, ECKey or Address"); + } + } + } else if(intKey == NAME) { + name = ((UnicodeString)map.get(key)).getString(); + } else if(intKey == NOTE) { + note = ((UnicodeString)map.get(key)).getString(); + } + } + + if(source == null) { + throw new IllegalStateException("Source is null"); + } + + return new UROutputDescriptor(source, keys, name, note); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URPSBT.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URPSBT.java new file mode 100644 index 0000000..d2c1c4c --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URPSBT.java @@ -0,0 +1,19 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.ByteString; +import co.nstant.in.cbor.model.DataItem; + +public class URPSBT extends CryptoPSBT { + public URPSBT(byte[] psbt) { + super(psbt); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.PSBT; + } + + public static URPSBT fromCbor(DataItem item) { + return new URPSBT(((ByteString)item).getBytes()); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URSSKR.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URSSKR.java new file mode 100644 index 0000000..d06bd53 --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URSSKR.java @@ -0,0 +1,23 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.ByteString; +import co.nstant.in.cbor.model.DataItem; + +import java.util.Arrays; + +public class URSSKR extends CryptoSskr { + public URSSKR(byte[] split) { + super(split); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.SSKR; + } + + public static URSSKR fromCbor(DataItem item) { + byte[] itemBytes = ((ByteString)item).getBytes(); + byte[] normalisedSplit = Arrays.copyOfRange(itemBytes, 1, itemBytes.length); + return new URSSKR(normalisedSplit); + } +} diff --git a/src/main/java/com/sparrowwallet/hummingbird/registry/URSeed.java b/src/main/java/com/sparrowwallet/hummingbird/registry/URSeed.java new file mode 100644 index 0000000..af68c9e --- /dev/null +++ b/src/main/java/com/sparrowwallet/hummingbird/registry/URSeed.java @@ -0,0 +1,48 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.*; + +import java.util.Date; + +public class URSeed extends CryptoSeed { + public URSeed(byte[] seed, Date birthdate) { + super(seed, birthdate); + } + + public URSeed(byte[] seed, Date birthdate, String name, String note) { + super(seed, birthdate, name, note); + } + + @Override + public RegistryType getRegistryType() { + return RegistryType.SEED; + } + + public static URSeed fromCbor(DataItem item) { + byte[] seed = null; + Date birthdate = null; + String name = null; + String note = null; + + Map map = (Map)item; + for(DataItem key : map.getKeys()) { + UnsignedInteger uintKey = (UnsignedInteger)key; + int intKey = uintKey.getValue().intValue(); + if(intKey == PAYLOAD_KEY) { + seed = ((ByteString)map.get(key)).getBytes(); + } else if(intKey == BIRTHDATE_KEY) { + birthdate = new Date(((UnsignedInteger)map.get(key)).getValue().longValue() * 1000 * 60 * 60 * 24); + } else if(intKey == NAME_KEY) { + name = ((UnicodeString)map.get(key)).getString(); + } else if(intKey == NOTE_KEY) { + note = ((UnicodeString)map.get(key)).getString(); + } + } + + if(seed == null) { + throw new IllegalStateException("Seed is null"); + } + + return new URSeed(seed, birthdate, name, note); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URAccountTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URAccountTest.java new file mode 100644 index 0000000..f27e4fd --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URAccountTest.java @@ -0,0 +1,174 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import com.sparrowwallet.hummingbird.*; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class URAccountTest { + @Test + public void testSeedPreCosigner() throws CborException { + String hex = "A2011A37B5EED40287D90193D9012FA403582103EB3E2863911826374DE86C231A4B76F0B89DFA174AFB78D7F478199884D9DD320458206456A5DF2DB0F6D9AF72B2A1AF4B25F45200ED6FCC29C3440B311D4796B70B5B06D90130A10186182CF500F500F5081A99F9CDF7D90190D90194D9012FA403582102C7E4823730F6EE2CF864E2C352060A88E60B51A84E89E4C8C75EC22590AD6B690458209D2F86043276F9251A4A4F577166A5ABEB16B6EC61E226B5B8FA11038BFDA42D06D90130A101861831F500F500F5081AA80F7CDBD90194D9012FA403582103FD433450B6924B4F7EFDD5D1ED017D364BE95AB2B592DC8BDDB3B00C1C24F63F04582072EDE7334D5ACF91C6FDA622C205199C595A31F9218ED30792D301D5EE9E3A8806D90130A101861854F500F500F5081A0D5DE1D7D90190D9012FA4035821035CCD58B63A2CDC23D0812710603592E7457573211880CB59B1EF012E168E059A04582088D3299B448F87215D96B0C226235AFC027F9E7DC700284F3E912A34DAEB1A2306D90130A10182182DF5081A37B5EED4D90190D90191D9012FA4035821032C78EBFCABDAC6D735A0820EF8732F2821B4FB84CD5D6B26526938F90C0507110458207953EFE16A73E5D3F9F2D4C6E49BD88E22093BBD85BE5A7E862A4B98A16E0AB606D90130A101881830F500F500F501F5081A59B69B2AD90191D9012FA40358210260563EE80C26844621B06B74070BAF0E23FB76CE439D0237E87502EBBD3CA3460458202FA0E41C9DC43DC4518659BFCEF935BA8101B57DBC0812805DD983BC1D34B81306D90130A101881830F500F500F502F5081A59B69B2AD90199D9012FA403582102BBB97CF9EFA176B738EFD6EE1D4D0FA391A973394FBC16E4C5E78E536CD14D2D0458204B4693E1F794206ED1355B838DA24949A92B63D02E58910BF3BD3D9C242281E606D90130A101861856F500F500F5081ACEC7070C"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URAccount urAccount = URAccount.fromCbor(items.get(0)); + Assert.assertEquals("37b5eed4", TestUtils.bytesToHex(urAccount.getMasterFingerprint())); + + CryptoOutput cryptoOutput1 = urAccount.getOutputDescriptors().get(0); + Assert.assertEquals(Collections.singletonList(ScriptExpression.PUBLIC_KEY_HASH), cryptoOutput1.getScriptExpressions()); + Assert.assertEquals("03eb3e2863911826374de86c231a4b76f0b89dfa174afb78d7f478199884d9dd32", TestUtils.bytesToHex(cryptoOutput1.getHdKey().getKey())); + Assert.assertEquals("6456a5df2db0f6d9af72b2a1af4b25f45200ed6fcc29c3440b311d4796b70b5b", TestUtils.bytesToHex(cryptoOutput1.getHdKey().getChainCode())); + Assert.assertEquals("44'/0'/0'", cryptoOutput1.getHdKey().getOrigin().getPath()); + Assert.assertEquals("99f9cdf7", TestUtils.bytesToHex(cryptoOutput1.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput1.getHdKey().getChildren()); + + CryptoOutput cryptoOutput2 = urAccount.getOutputDescriptors().get(1); + Assert.assertEquals(Arrays.asList(ScriptExpression.SCRIPT_HASH, ScriptExpression.WITNESS_PUBLIC_KEY_HASH), cryptoOutput2.getScriptExpressions()); + Assert.assertEquals("02c7e4823730f6ee2cf864e2c352060a88e60b51a84e89e4c8c75ec22590ad6b69", TestUtils.bytesToHex(cryptoOutput2.getHdKey().getKey())); + Assert.assertEquals("9d2f86043276f9251a4a4f577166a5abeb16b6ec61e226b5b8fa11038bfda42d", TestUtils.bytesToHex(cryptoOutput2.getHdKey().getChainCode())); + Assert.assertEquals("49'/0'/0'", cryptoOutput2.getHdKey().getOrigin().getPath()); + Assert.assertEquals("a80f7cdb", TestUtils.bytesToHex(cryptoOutput2.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput2.getHdKey().getChildren()); + + CryptoOutput cryptoOutput3 = urAccount.getOutputDescriptors().get(2); + Assert.assertEquals(Collections.singletonList(ScriptExpression.WITNESS_PUBLIC_KEY_HASH), cryptoOutput3.getScriptExpressions()); + Assert.assertEquals("03fd433450b6924b4f7efdd5d1ed017d364be95ab2b592dc8bddb3b00c1c24f63f", TestUtils.bytesToHex(cryptoOutput3.getHdKey().getKey())); + Assert.assertEquals("72ede7334d5acf91c6fda622c205199c595a31f9218ed30792d301d5ee9e3a88", TestUtils.bytesToHex(cryptoOutput3.getHdKey().getChainCode())); + Assert.assertEquals("84'/0'/0'", cryptoOutput3.getHdKey().getOrigin().getPath()); + Assert.assertEquals("0d5de1d7", TestUtils.bytesToHex(cryptoOutput3.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput3.getHdKey().getChildren()); + + CryptoOutput cryptoOutput4 = urAccount.getOutputDescriptors().get(3); + Assert.assertEquals(Collections.singletonList(ScriptExpression.SCRIPT_HASH), cryptoOutput4.getScriptExpressions()); + Assert.assertEquals("035ccd58b63a2cdc23d0812710603592e7457573211880cb59b1ef012e168e059a", TestUtils.bytesToHex(cryptoOutput4.getHdKey().getKey())); + Assert.assertEquals("88d3299b448f87215d96b0c226235afc027f9e7dc700284f3e912a34daeb1a23", TestUtils.bytesToHex(cryptoOutput4.getHdKey().getChainCode())); + Assert.assertEquals("45'", cryptoOutput4.getHdKey().getOrigin().getPath()); + Assert.assertEquals("37b5eed4", TestUtils.bytesToHex(cryptoOutput4.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput4.getHdKey().getChildren()); + + CryptoOutput cryptoOutput5 = urAccount.getOutputDescriptors().get(4); + Assert.assertEquals(Arrays.asList(ScriptExpression.SCRIPT_HASH, ScriptExpression.WITNESS_SCRIPT_HASH), cryptoOutput5.getScriptExpressions()); + Assert.assertEquals("032c78ebfcabdac6d735a0820ef8732f2821b4fb84cd5d6b26526938f90c050711", TestUtils.bytesToHex(cryptoOutput5.getHdKey().getKey())); + Assert.assertEquals("7953efe16a73e5d3f9f2d4c6e49bd88e22093bbd85be5a7e862a4b98a16e0ab6", TestUtils.bytesToHex(cryptoOutput5.getHdKey().getChainCode())); + Assert.assertEquals("48'/0'/0'/1'", cryptoOutput5.getHdKey().getOrigin().getPath()); + Assert.assertEquals("59b69b2a", TestUtils.bytesToHex(cryptoOutput5.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput5.getHdKey().getChildren()); + + CryptoOutput cryptoOutput6 = urAccount.getOutputDescriptors().get(5); + Assert.assertEquals(Collections.singletonList(ScriptExpression.WITNESS_SCRIPT_HASH), cryptoOutput6.getScriptExpressions()); + Assert.assertEquals("0260563ee80c26844621b06b74070baf0e23fb76ce439d0237e87502ebbd3ca346", TestUtils.bytesToHex(cryptoOutput6.getHdKey().getKey())); + Assert.assertEquals("2fa0e41c9dc43dc4518659bfcef935ba8101b57dbc0812805dd983bc1d34b813", TestUtils.bytesToHex(cryptoOutput6.getHdKey().getChainCode())); + Assert.assertEquals("48'/0'/0'/2'", cryptoOutput6.getHdKey().getOrigin().getPath()); + Assert.assertEquals("59b69b2a", TestUtils.bytesToHex(cryptoOutput6.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput6.getHdKey().getChildren()); + + CryptoOutput cryptoOutput7 = urAccount.getOutputDescriptors().get(6); + Assert.assertEquals(Collections.singletonList(ScriptExpression.TAPROOT), cryptoOutput7.getScriptExpressions()); + Assert.assertEquals("02bbb97cf9efa176b738efd6ee1d4d0fa391a973394fbc16e4c5e78e536cd14d2d", TestUtils.bytesToHex(cryptoOutput7.getHdKey().getKey())); + Assert.assertEquals("4b4693e1f794206ed1355b838da24949a92b63d02e58910bf3bd3d9c242281e6", TestUtils.bytesToHex(cryptoOutput7.getHdKey().getChainCode())); + Assert.assertEquals("86'/0'/0'", cryptoOutput7.getHdKey().getOrigin().getPath()); + Assert.assertEquals("cec7070c", TestUtils.bytesToHex(cryptoOutput7.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput7.getHdKey().getChildren()); + + Assert.assertEquals("a2011a37b5eed40287d90134d90193d9012fa403582103eb3e2863911826374de86c231a4b76f0b89dfa174afb78d7f478199884d9dd320458206456a5df2db0f6d9af72b2a1af4b25f45200ed6fcc29c3440b311d4796b70b5b06d90130a10186182cf500f500f5081a99f9cdf7d90134d90190d90194d9012fa403582102c7e4823730f6ee2cf864e2c352060a88e60b51a84e89e4c8c75ec22590ad6b690458209d2f86043276f9251a4a4f577166a5abeb16b6ec61e226b5b8fa11038bfda42d06d90130a101861831f500f500f5081aa80f7cdbd90134d90194d9012fa403582103fd433450b6924b4f7efdd5d1ed017d364be95ab2b592dc8bddb3b00c1c24f63f04582072ede7334d5acf91c6fda622c205199c595a31f9218ed30792d301d5ee9e3a8806d90130a101861854f500f500f5081a0d5de1d7d90134d90190d9012fa4035821035ccd58b63a2cdc23d0812710603592e7457573211880cb59b1ef012e168e059a04582088d3299b448f87215d96b0c226235afc027f9e7dc700284f3e912a34daeb1a2306d90130a10182182df5081a37b5eed4d90134d90190d90191d9012fa4035821032c78ebfcabdac6d735a0820ef8732f2821b4fb84cd5d6b26526938f90c0507110458207953efe16a73e5d3f9f2d4c6e49bd88e22093bbd85be5a7e862a4b98a16e0ab606d90130a101881830f500f500f501f5081a59b69b2ad90134d90191d9012fa40358210260563ee80c26844621b06b74070baf0e23fb76ce439d0237e87502ebbd3ca3460458202fa0e41c9dc43dc4518659bfcef935ba8101b57dbc0812805dd983bc1d34b81306d90130a101881830f500f500f502f5081a59b69b2ad90134d90199d9012fa403582102bbb97cf9efa176b738efd6ee1d4d0fa391a973394fbc16e4c5e78e536cd14d2d0458204b4693e1f794206ed1355b838da24949a92b63d02e58910bf3bd3d9c242281e606d90130a101861856f500f500f5081acec7070c", TestUtils.encode(urAccount.toCbor())); + String ur = "ur:account/oeadcyemrewytyaolttaadeetaadmutaaddloxaxhdclaxwmfmdeiamecsdsemgtvsjzcncygrkowtrontzschgezokstswkkscfmklrtauteyaahdcxiehfonurdppfyntapejpproypegrdawkgmaewejlsfdtsrfybdehcaflmtrlbdhpamtaaddyoyadlncsdwykaeykaeykaycynlytsnyltaadeetaadmhtaadmwtaaddloxaxhdclaostvelfemdyynwydwyaievosrgmambklovabdgypdglldvespsthysadamhpmjeinaahdcxntdllnaaeykoytdacygegwhgjsiyonpywmcmrpwphsvodsrerozsbyaxluzcoxdpamtaaddyoyadlncsehykaeykaeykaycypdbskeuytaadeetaadmwtaaddloxaxhdclaxzcfxeegdrpmogrgwkbzctlttweadkiengrwlhtprremouoluutqdpfbncedkynfhaahdcxjpwevdeogthttkmeswzcolcpsaahcfnshkhtehytclmnteatmoteadtlwynnftloamtaaddyoyadlncsghykaeykaeykaycybthlvytstaadeetaadmhtaaddloxaxhdclaxhhsnhdrpftdwuocntilydibehnecmovdfekpjkclcslasbhkpawsaddmcmmnahnyaahdcxlotedtndfymyltclhlmtpfsadscnhtztaolbnnkistaedegwfmmedreetnwmcycnamtaaddyoyadlfcsdpykaycyemrewytytaadeetaadmhtaadmetaaddloxaxhdclaxdwkswmztpytnswtsecnblfbayajkdldeclqzzolrsnhljedsgminetytbnahatbyaahdcxkkguwsvyimjkvwteytwztyswvendtpmncpasfrrylprnhtkblndrgrmkoyjtbkrpamtaaddyoyadlocsdyykaeykaeykadykaycyhkrpnddrtaadeetaadmetaaddloxaxhdclaohnhffmvsbndslrfgclpfjejyatbdpebacnzokotofxntaoemvskpaowmryfnotfgaahdcxdlnbvecentssfsssgylnhkrstoytecrdlyadrekirfaybglahltalsrfcaeerobwamtaaddyoyadlocsdyykaeykaeykaoykaycyhkrpnddrtaadeetaadnltaaddloxaxhdclaorkrhkeytwsoykorletwstbwycagtbsotmeptjkesgwrfcmveskvdmngujzttgtdpaahdcxgrfgmuvyylmwcxjtttechplslgoegagaptdniatidmhdmebdwfryfsnsdkcplyvaamtaaddyoyadlncshfykaeykaeykaycytostatbnfpghsgbd"; + Assert.assertEquals(ur, urAccount.toUR().toString()); + } + + @Test + public void testSeed() throws CborException { + String hex = "a2011a37b5eed40287d90134d90193d9012fa403582103eb3e2863911826374de86c231a4b76f0b89dfa174afb78d7f478199884d9dd320458206456a5df2db0f6d9af72b2a1af4b25f45200ed6fcc29c3440b311d4796b70b5b06d90130a20186182cf500f500f5021a37b5eed4081a99f9cdf7d90134d90190d90194d9012fa403582102c7e4823730f6ee2cf864e2c352060a88e60b51a84e89e4c8c75ec22590ad6b690458209d2f86043276f9251a4a4f577166a5abeb16b6ec61e226b5b8fa11038bfda42d06d90130a201861831f500f500f5021a37b5eed4081aa80f7cdbd90134d90194d9012fa403582103fd433450b6924b4f7efdd5d1ed017d364be95ab2b592dc8bddb3b00c1c24f63f04582072ede7334d5acf91c6fda622c205199c595a31f9218ed30792d301d5ee9e3a8806d90130a201861854f500f500f5021a37b5eed4081a0d5de1d7d90134d90190d9019ad9012fa4035821035ccd58b63a2cdc23d0812710603592e7457573211880cb59b1ef012e168e059a04582088d3299b448f87215d96b0c226235afc027f9e7dc700284f3e912a34daeb1a2306d90130a20182182df5021a37b5eed4081a37b5eed4d90134d90190d90191d9019ad9012fa4035821032c78ebfcabdac6d735a0820ef8732f2821b4fb84cd5d6b26526938f90c0507110458207953efe16a73e5d3f9f2d4c6e49bd88e22093bbd85be5a7e862a4b98a16e0ab606d90130a201881830f500f500f501f5021a37b5eed4081a59b69b2ad90134d90191d9019ad9012fa40358210260563ee80c26844621b06b74070baf0e23fb76ce439d0237e87502ebbd3ca3460458202fa0e41c9dc43dc4518659bfcef935ba8101b57dbc0812805dd983bc1d34b81306d90130a201881830f500f500f502f5021a37b5eed4081a59b69b2ad90134d90199d9012fa403582102bbb97cf9efa176b738efd6ee1d4d0fa391a973394fbc16e4c5e78e536cd14d2d0458204b4693e1f794206ed1355b838da24949a92b63d02e58910bf3bd3d9c242281e606d90130a201861856f500f500f5021a37b5eed4081acec7070c"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URAccount urAccount = URAccount.fromCbor(items.get(0)); + Assert.assertEquals("37b5eed4", TestUtils.bytesToHex(urAccount.getMasterFingerprint())); + + CryptoOutput cryptoOutput1 = urAccount.getOutputDescriptors().get(0); + Assert.assertEquals(Collections.singletonList(ScriptExpression.PUBLIC_KEY_HASH), cryptoOutput1.getScriptExpressions()); + Assert.assertEquals("03eb3e2863911826374de86c231a4b76f0b89dfa174afb78d7f478199884d9dd32", TestUtils.bytesToHex(cryptoOutput1.getHdKey().getKey())); + Assert.assertEquals("6456a5df2db0f6d9af72b2a1af4b25f45200ed6fcc29c3440b311d4796b70b5b", TestUtils.bytesToHex(cryptoOutput1.getHdKey().getChainCode())); + Assert.assertEquals("44'/0'/0'", cryptoOutput1.getHdKey().getOrigin().getPath()); + Assert.assertEquals("99f9cdf7", TestUtils.bytesToHex(cryptoOutput1.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput1.getHdKey().getChildren()); + + CryptoOutput cryptoOutput2 = urAccount.getOutputDescriptors().get(1); + Assert.assertEquals(Arrays.asList(ScriptExpression.SCRIPT_HASH, ScriptExpression.WITNESS_PUBLIC_KEY_HASH), cryptoOutput2.getScriptExpressions()); + Assert.assertEquals("02c7e4823730f6ee2cf864e2c352060a88e60b51a84e89e4c8c75ec22590ad6b69", TestUtils.bytesToHex(cryptoOutput2.getHdKey().getKey())); + Assert.assertEquals("9d2f86043276f9251a4a4f577166a5abeb16b6ec61e226b5b8fa11038bfda42d", TestUtils.bytesToHex(cryptoOutput2.getHdKey().getChainCode())); + Assert.assertEquals("49'/0'/0'", cryptoOutput2.getHdKey().getOrigin().getPath()); + Assert.assertEquals("a80f7cdb", TestUtils.bytesToHex(cryptoOutput2.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput2.getHdKey().getChildren()); + + CryptoOutput cryptoOutput3 = urAccount.getOutputDescriptors().get(2); + Assert.assertEquals(Collections.singletonList(ScriptExpression.WITNESS_PUBLIC_KEY_HASH), cryptoOutput3.getScriptExpressions()); + Assert.assertEquals("03fd433450b6924b4f7efdd5d1ed017d364be95ab2b592dc8bddb3b00c1c24f63f", TestUtils.bytesToHex(cryptoOutput3.getHdKey().getKey())); + Assert.assertEquals("72ede7334d5acf91c6fda622c205199c595a31f9218ed30792d301d5ee9e3a88", TestUtils.bytesToHex(cryptoOutput3.getHdKey().getChainCode())); + Assert.assertEquals("84'/0'/0'", cryptoOutput3.getHdKey().getOrigin().getPath()); + Assert.assertEquals("0d5de1d7", TestUtils.bytesToHex(cryptoOutput3.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput3.getHdKey().getChildren()); + + CryptoOutput cryptoOutput4 = urAccount.getOutputDescriptors().get(3); + Assert.assertEquals(Arrays.asList(ScriptExpression.SCRIPT_HASH, ScriptExpression.COSIGNER), cryptoOutput4.getScriptExpressions()); + Assert.assertEquals("035ccd58b63a2cdc23d0812710603592e7457573211880cb59b1ef012e168e059a", TestUtils.bytesToHex(cryptoOutput4.getHdKey().getKey())); + Assert.assertEquals("88d3299b448f87215d96b0c226235afc027f9e7dc700284f3e912a34daeb1a23", TestUtils.bytesToHex(cryptoOutput4.getHdKey().getChainCode())); + Assert.assertEquals("45'", cryptoOutput4.getHdKey().getOrigin().getPath()); + Assert.assertEquals("37b5eed4", TestUtils.bytesToHex(cryptoOutput4.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput4.getHdKey().getChildren()); + + CryptoOutput cryptoOutput5 = urAccount.getOutputDescriptors().get(4); + Assert.assertEquals(Arrays.asList(ScriptExpression.SCRIPT_HASH, ScriptExpression.WITNESS_SCRIPT_HASH, ScriptExpression.COSIGNER), cryptoOutput5.getScriptExpressions()); + Assert.assertEquals("032c78ebfcabdac6d735a0820ef8732f2821b4fb84cd5d6b26526938f90c050711", TestUtils.bytesToHex(cryptoOutput5.getHdKey().getKey())); + Assert.assertEquals("7953efe16a73e5d3f9f2d4c6e49bd88e22093bbd85be5a7e862a4b98a16e0ab6", TestUtils.bytesToHex(cryptoOutput5.getHdKey().getChainCode())); + Assert.assertEquals("48'/0'/0'/1'", cryptoOutput5.getHdKey().getOrigin().getPath()); + Assert.assertEquals("59b69b2a", TestUtils.bytesToHex(cryptoOutput5.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput5.getHdKey().getChildren()); + + CryptoOutput cryptoOutput6 = urAccount.getOutputDescriptors().get(5); + Assert.assertEquals(Arrays.asList(ScriptExpression.WITNESS_SCRIPT_HASH, ScriptExpression.COSIGNER), cryptoOutput6.getScriptExpressions()); + Assert.assertEquals("0260563ee80c26844621b06b74070baf0e23fb76ce439d0237e87502ebbd3ca346", TestUtils.bytesToHex(cryptoOutput6.getHdKey().getKey())); + Assert.assertEquals("2fa0e41c9dc43dc4518659bfcef935ba8101b57dbc0812805dd983bc1d34b813", TestUtils.bytesToHex(cryptoOutput6.getHdKey().getChainCode())); + Assert.assertEquals("48'/0'/0'/2'", cryptoOutput6.getHdKey().getOrigin().getPath()); + Assert.assertEquals("59b69b2a", TestUtils.bytesToHex(cryptoOutput6.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput6.getHdKey().getChildren()); + + CryptoOutput cryptoOutput7 = urAccount.getOutputDescriptors().get(6); + Assert.assertEquals(Collections.singletonList(ScriptExpression.TAPROOT), cryptoOutput7.getScriptExpressions()); + Assert.assertEquals("02bbb97cf9efa176b738efd6ee1d4d0fa391a973394fbc16e4c5e78e536cd14d2d", TestUtils.bytesToHex(cryptoOutput7.getHdKey().getKey())); + Assert.assertEquals("4b4693e1f794206ed1355b838da24949a92b63d02e58910bf3bd3d9c242281e6", TestUtils.bytesToHex(cryptoOutput7.getHdKey().getChainCode())); + Assert.assertEquals("86'/0'/0'", cryptoOutput7.getHdKey().getOrigin().getPath()); + Assert.assertEquals("cec7070c", TestUtils.bytesToHex(cryptoOutput7.getHdKey().getParentFingerprint())); + Assert.assertNull(cryptoOutput7.getHdKey().getChildren()); + + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urAccount.toCbor())); + String ur = "ur:account/oeadcyemrewytyaolttaadeetaadmutaaddloxaxhdclaxwmfmdeiamecsdsemgtvsjzcncygrkowtrontzschgezokstswkkscfmklrtauteyaahdcxiehfonurdppfyntapejpproypegrdawkgmaewejlsfdtsrfybdehcaflmtrlbdhpamtaaddyoeadlncsdwykaeykaeykaocyemrewytyaycynlytsnyltaadeetaadmhtaadmwtaaddloxaxhdclaostvelfemdyynwydwyaievosrgmambklovabdgypdglldvespsthysadamhpmjeinaahdcxntdllnaaeykoytdacygegwhgjsiyonpywmcmrpwphsvodsrerozsbyaxluzcoxdpamtaaddyoeadlncsehykaeykaeykaocyemrewytyaycypdbskeuytaadeetaadmwtaaddloxaxhdclaxzcfxeegdrpmogrgwkbzctlttweadkiengrwlhtprremouoluutqdpfbncedkynfhaahdcxjpwevdeogthttkmeswzcolcpsaahcfnshkhtehytclmnteatmoteadtlwynnftloamtaaddyoeadlncsghykaeykaeykaocyemrewytyaycybthlvytstaadeetaadmhtaadnytaaddloxaxhdclaxhhsnhdrpftdwuocntilydibehnecmovdfekpjkclcslasbhkpawsaddmcmmnahnyaahdcxlotedtndfymyltclhlmtpfsadscnhtztaolbnnkistaedegwfmmedreetnwmcycnamtaaddyoeadlfcsdpykaocyemrewytyaycyemrewytytaadeetaadmhtaadmetaadnytaaddloxaxhdclaxdwkswmztpytnswtsecnblfbayajkdldeclqzzolrsnhljedsgminetytbnahatbyaahdcxkkguwsvyimjkvwteytwztyswvendtpmncpasfrrylprnhtkblndrgrmkoyjtbkrpamtaaddyoeadlocsdyykaeykaeykadykaocyemrewytyaycyhkrpnddrtaadeetaadmetaadnytaaddloxaxhdclaohnhffmvsbndslrfgclpfjejyatbdpebacnzokotofxntaoemvskpaowmryfnotfgaahdcxdlnbvecentssfsssgylnhkrstoytecrdlyadrekirfaybglahltalsrfcaeerobwamtaaddyoeadlocsdyykaeykaeykaoykaocyemrewytyaycyhkrpnddrtaadeetaadnltaaddloxaxhdclaorkrhkeytwsoykorletwstbwycagtbsotmeptjkesgwrfcmveskvdmngujzttgtdpaahdcxgrfgmuvyylmwcxjtttechplslgoegagaptdniatidmhdmebdwfryfsnsdkcplyvaamtaaddyoeadlncshfykaeykaeykaocyemrewytyaycytostatbngmdavolk"; + Assert.assertEquals(ur, urAccount.toUR().toString()); + } + + @Test + public void testAccount() throws Exception { + byte[] cbor = TestUtils.hexToBytes("A2011A37B5EED40286D90193D9012FA403582103EB3E2863911826374DE86C231A4B76F0B89DFA174AFB78D7F478199884D9DD320458206456A5DF2DB0F6D9AF72B2A1AF4B25F45200ED6FCC29C3440B311D4796B70B5B06D90130A10186182CF500F500F5081A99F9CDF7D90190D90194D9012FA403582102C7E4823730F6EE2CF864E2C352060A88E60B51A84E89E4C8C75EC22590AD6B690458209D2F86043276F9251A4A4F577166A5ABEB16B6EC61E226B5B8FA11038BFDA42D06D90130A101861831F500F500F5081AA80F7CDBD90194D9012FA403582103FD433450B6924B4F7EFDD5D1ED017D364BE95AB2B592DC8BDDB3B00C1C24F63F04582072EDE7334D5ACF91C6FDA622C205199C595A31F9218ED30792D301D5EE9E3A8806D90130A101861854F500F500F5081A0D5DE1D7D90190D9012FA4035821035CCD58B63A2CDC23D0812710603592E7457573211880CB59B1EF012E168E059A04582088D3299B448F87215D96B0C226235AFC027F9E7DC700284F3E912A34DAEB1A2306D90130A10182182DF5081A37B5EED4D90190D90191D9012FA4035821032C78EBFCABDAC6D735A0820EF8732F2821B4FB84CD5D6B26526938F90C0507110458207953EFE16A73E5D3F9F2D4C6E49BD88E22093BBD85BE5A7E862A4B98A16E0AB606D90130A101881830F500F500F501F5081A59B69B2AD90191D9012FA40358210260563EE80C26844621B06B74070BAF0E23FB76CE439D0237E87502EBBD3CA3460458202FA0E41C9DC43DC4518659BFCEF935BA8101B57DBC0812805DD983BC1D34B81306D90130A101881830F500F500F502F5081A59B69B2A"); + UR ur = new UR("account", cbor); + String encoded = UREncoder.encode(ur); + Assert.assertEquals("ur:account/oeadcyemrewytyaolntaadmutaaddloxaxhdclaxwmfmdeiamecsdsemgtvsjzcncygrkowtrontzschgezokstswkkscfmklrtauteyaahdcxiehfonurdppfyntapejpproypegrdawkgmaewejlsfdtsrfybdehcaflmtrlbdhpamtaaddyoyadlncsdwykaeykaeykaycynlytsnyltaadmhtaadmwtaaddloxaxhdclaostvelfemdyynwydwyaievosrgmambklovabdgypdglldvespsthysadamhpmjeinaahdcxntdllnaaeykoytdacygegwhgjsiyonpywmcmrpwphsvodsrerozsbyaxluzcoxdpamtaaddyoyadlncsehykaeykaeykaycypdbskeuytaadmwtaaddloxaxhdclaxzcfxeegdrpmogrgwkbzctlttweadkiengrwlhtprremouoluutqdpfbncedkynfhaahdcxjpwevdeogthttkmeswzcolcpsaahcfnshkhtehytclmnteatmoteadtlwynnftloamtaaddyoyadlncsghykaeykaeykaycybthlvytstaadmhtaaddloxaxhdclaxhhsnhdrpftdwuocntilydibehnecmovdfekpjkclcslasbhkpawsaddmcmmnahnyaahdcxlotedtndfymyltclhlmtpfsadscnhtztaolbnnkistaedegwfmmedreetnwmcycnamtaaddyoyadlfcsdpykaycyemrewytytaadmhtaadmetaaddloxaxhdclaxdwkswmztpytnswtsecnblfbayajkdldeclqzzolrsnhljedsgminetytbnahatbyaahdcxkkguwsvyimjkvwteytwztyswvendtpmncpasfrrylprnhtkblndrgrmkoyjtbkrpamtaaddyoyadlocsdyykaeykaeykadykaycyhkrpnddrtaadmetaaddloxaxhdclaohnhffmvsbndslrfgclpfjejyatbdpebacnzokotofxntaoemvskpaowmryfnotfgaahdcxdlnbvecentssfsssgylnhkrstoytecrdlyadrekirfaybglahltalsrfcaeerobwamtaaddyoyadlocsdyykaeykaeykaoykaycyhkrpnddrgdaogykb", encoded); + } + + @Test + public void testPairPathComponent() throws Exception { + String ur = "ur:account/oeadcylpvefyjeaolttaadeetaadmutaaddlonaxhdclaxhkfzdphtkplevtqzprkgnnsacagesgctzmspytctdstocsbgmkamurrdpffmtsseaahdcxpdnbdysgfeaycfsegwmhwfjewzfwdmesrlqdhglagahkytcflbrtsedraefwbweyamtaaddyoeadlncsdwykaeykaeykaocylpvefyjeattaaddyoyadlslraewkadwklawkaycypsjpsbfntaadeetaadmhtaadmwtaaddlonaxhdclaouypakomsrlhlqzvwlymesadevybkueqdoxhdcximrdhgfhtybwvyhdkeyklddpmwaahdcxwnvsaxgsdldtmurknyfpcedyiaiopautyafrpejoaxjncfhhhtpfdetteotnknsramtaaddyoeadlncsehykaeykaeykaocylpvefyjeattaaddyoyadlslraewkadwklawkaycyoeyagmmstaadeetaadmwtaaddlonaxhdclaorernoyonguhlfsdecnmohnuydwnnchjpuyroftdnaadkatcfkirdtkispsiajycxaahdcxlyutkbnymklbdskesbihbelysedwlupklfmhnntlfwsegsvwwspkghkgvwheiejoamtaaddyoeadlncsghykaeykaeykaocylpvefyjeattaaddyoyadlslraewkadwklawkaycywyqzwzkptaadeetaadmhtaadnytaaddlonaxhdclaoiorpcxecynfloxtamhlsaarkkotpclcydahtamluhnimkertlgftknoerymobaneaahdcxrsatfdqdenvtspvdieindaztrpcazsknzsoywkghfhtyswdkkpjstbneayfncxoxamtaaddyoeadlfcsdpykaocylpvefyjeattaaddyoyadlslraewkadwklawkaycylpvefyjetaadeetaadmhtaadmetaadnytaaddlonaxhdclaoldpfglrkwntlvwlarsdmnbzefmghkeeeoteerdpfframuoidstpacswmnnutcwkkaahdcxhkrozmjneceoldplnluetscyjnbdhsldenontncafgdkmdvlsnidamlnvwfzsstaamtaaddyoeadlocsdyykaeykaeykadykaocylpvefyjeattaaddyoyadlslraewkadwklawkaycyrhimryuotaadeetaadmetaadnytaaddlonaxhdclaowysfahwsqdclsnfmwmjlgoeerhzoteherosnmyhkinolrlspdadsasswlarpvtpfaahdcxwybnryoewshycwghlfhhwtbscxpyylenpkolmoftgymksbdpvsgtlbdefhrswnyaamtaaddyoeadlocsdyykaeykaeykaoykaocylpvefyjeattaaddyoyadlslraewkadwklawkaycyrhimryuotaadeetaadnltaaddlonaxhdclaxpmctzmjzemjspsplgrlgtyvssffrzcrlgogojnmncwbdtytelblyhlflmtdnkifdaahdcxgytkgujnjtaszejprdpmoskseehkamvlcersoxdlghsglagmjkjewmrlpfonwldwamtaaddyoeadlncshfykaeykaeykaocylpvefyjeattaaddyoyadlslraewkadwklawkaycynsmwdtfzryrorncp"; + URDecoder urDecoder = new URDecoder(); + urDecoder.receivePart(ur); + URDecoder.Result urResult = urDecoder.getResult(); + if(urResult.type == ResultType.SUCCESS) { + if(urResult.ur.getRegistryType().equals(RegistryType.CRYPTO_ACCOUNT)) { + URAccount urAccount = (URAccount)urResult.ur.decodeFromRegistry(); + Assert.assertEquals("<0;1>/*", urAccount.getOutputDescriptors().get(0).getHdKey().getChildren().getPath()); + } + } + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URAddressTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URAddressTest.java new file mode 100644 index 0000000..eead5d2 --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URAddressTest.java @@ -0,0 +1,24 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import com.sparrowwallet.hummingbird.TestUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class URAddressTest { + @Test + public void testAddress() throws CborException { + String hex = "A1035477BFF20C60E522DFAA3350C39B030A5D004E839A"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URAddress urAddress = URAddress.fromCbor(items.get(0)); + Assert.assertEquals("77bff20c60e522dfaa3350c39b030a5d004e839a", TestUtils.bytesToHex(urAddress.getData())); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urAddress.toCbor())); + String ur = "ur:address/oyaxghktrswzbnhnvwcpurpkeogdsrndaxbkhlaegllsnyolrsemgu"; + Assert.assertEquals(ur, urAddress.toUR().toString()); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URCoinInfoTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URCoinInfoTest.java new file mode 100644 index 0000000..d27b389 --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URCoinInfoTest.java @@ -0,0 +1,41 @@ +package com.sparrowwallet.hummingbird.registry; + +import org.junit.Assert; +import org.junit.Test; + +public class URCoinInfoTest { + @Test + public void testBitcoinCoinInfo() { + URCoinInfo urCoinInfo = new URCoinInfo(URCoinInfo.Type.BITCOIN, URCoinInfo.Network.MAINNET); + Assert.assertSame(urCoinInfo.getType().typeValue, URCoinInfo.Type.BITCOIN.typeValue); + } + + @Test + public void testEthereumCoinInfo() { + URCoinInfo urCoinInfo = new URCoinInfo(URCoinInfo.Type.ETHEREUM, URCoinInfo.Network.MAINNET); + Assert.assertSame(urCoinInfo.getType().typeValue, URCoinInfo.Type.ETHEREUM.typeValue); + } + + @Test + public void testGoerliEthereumCoinInfo() { + URCoinInfo urCoinInfo = new URCoinInfo(URCoinInfo.Type.ETHEREUM, URCoinInfo.Network.GOERLI); + Assert.assertSame(urCoinInfo.getType().typeValue, URCoinInfo.Type.ETHEREUM.typeValue); + Assert.assertSame(urCoinInfo.getNetwork().networkValue, URCoinInfo.Network.GOERLI.networkValue); + } + + @Test(expected = IllegalArgumentException.class) + public void testGoerliSupportedOnlyWithEthereum() { + new URCoinInfo(URCoinInfo.Type.BITCOIN, URCoinInfo.Network.GOERLI); + } + + @Test(expected = IllegalArgumentException.class) + public void testGoerliSupportedOnlyWithEthereumWithValues() { + new URCoinInfo(URCoinInfo.Type.BITCOIN.typeValue, URCoinInfo.Network.GOERLI.networkValue); + } + + @Test + public void testNullTypeCoinInfo() { + URCoinInfo urCoinInfo = new URCoinInfo(null, URCoinInfo.Network.MAINNET); + Assert.assertSame(urCoinInfo.getType(), URCoinInfo.Type.BITCOIN); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URECKeyTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URECKeyTest.java new file mode 100644 index 0000000..f82ac13 --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URECKeyTest.java @@ -0,0 +1,26 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import com.sparrowwallet.hummingbird.TestUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class URECKeyTest { + @Test + public void testECKey() throws CborException { + String hex = "A202F50358208C05C4B4F3E88840A4F4B5F155CFD69473EA169F3D0431B7A6787A23777F08AA"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URECKey urECKey = URECKey.fromCbor(items.get(0)); + Assert.assertEquals(0, urECKey.getCurve()); + Assert.assertTrue(urECKey.isPrivateKey()); + Assert.assertEquals("8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa", TestUtils.bytesToHex(urECKey.getData())); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urECKey.toCbor())); + String ur = "ur:eckey/oeaoykaxhdcxlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypkrphsmyid"; + Assert.assertEquals(ur, urECKey.toUR().toString()); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URHDKeyTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URHDKeyTest.java new file mode 100644 index 0000000..94d9fac --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URHDKeyTest.java @@ -0,0 +1,97 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import com.sparrowwallet.hummingbird.TestUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class URHDKeyTest { + @Test + public void testMasterKey() throws CborException { + String hex = "a301f503582100e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35045820873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URHDKey urHDKey = URHDKey.fromCbor(items.get(0)); + Assert.assertTrue(urHDKey.isMaster()); + Assert.assertEquals("00e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", TestUtils.bytesToHex(urHDKey.getKey())); + Assert.assertEquals("873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508", TestUtils.bytesToHex(urHDKey.getChainCode())); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urHDKey.toCbor())); + String ur = "ur:hdkey/otadykaxhdclaevswfdmjpfswpwkahcywspsmndwmusoskprbbehetchsnpfcybbmwrhchspfxjeecaahdcxltfszmlyrtdlgmhfcnzcctvwcmkbpsftgonbgauefsehgrqzdmvodizmweemtlaybakiylat"; + Assert.assertEquals(ur, urHDKey.toUR().toString()); + } + + @Test + public void testPublicTestnet() throws CborException { + String hex = "a5035821026fe2355745bb2db3630bbc80ef5d58951c963c841f54170ba6e5c12be7fc12a6045820ced155c72456255881793514edc5bd9447e7f74abb88c6d6b6480fd016ee8c8505d99d71a1020106d99d70a1018a182cf501f501f500f401f4081ae9181cf3"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URHDKey urHDKey = URHDKey.fromCbor(items.get(0)); + Assert.assertFalse(urHDKey.isMaster()); + Assert.assertFalse(urHDKey.isPrivateKey()); + Assert.assertEquals("026fe2355745bb2db3630bbc80ef5d58951c963c841f54170ba6e5c12be7fc12a6", TestUtils.bytesToHex(urHDKey.getKey())); + Assert.assertEquals("ced155c72456255881793514edc5bd9447e7f74abb88c6d6b6480fd016ee8c85", TestUtils.bytesToHex(urHDKey.getChainCode())); + Assert.assertEquals(urHDKey.getUseInfo().getNetwork(), URCoinInfo.Network.TESTNET); + Assert.assertEquals("44'/1'/1'/0/1", urHDKey.getOrigin().getPath()); + Assert.assertNull(urHDKey.getOrigin().getDepth()); + Assert.assertEquals("e9181cf3", TestUtils.bytesToHex(urHDKey.getParentFingerprint())); + Assert.assertNull(urHDKey.getChildren()); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urHDKey.toCbor())); + String ur = "ur:hdkey/onaxhdclaojlvoechgferkdpqdiabdrflawshlhdmdcemtfnlrctghchbdolvwsednvdztbgolaahdcxtottgostdkhfdahdlykkecbbweskrymwflvdylgerkloswtbrpfdbsticmwylklpahtantjsoyaoadamtantjooyadlecsdwykadykadykaewkadwkaycywlcscewfjnkpvllt"; + Assert.assertEquals(ur, urHDKey.toUR().toString()); + } + + @Test + public void testMasterPublicKey() throws CborException { + String hex = "a303582103ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9045820e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f952006d99d70a20180021a854bc782"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URHDKey urHDKey = URHDKey.fromCbor(items.get(0)); + Assert.assertFalse(urHDKey.isMaster()); + Assert.assertFalse(urHDKey.isPrivateKey()); + Assert.assertEquals("03ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9", TestUtils.bytesToHex(urHDKey.getKey())); + Assert.assertEquals("e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f9520", TestUtils.bytesToHex(urHDKey.getChainCode())); + Assert.assertNull(urHDKey.getOrigin().getPath()); + Assert.assertEquals("854bc782", TestUtils.bytesToHex(urHDKey.getOrigin().getSourceFingerprint())); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urHDKey.toCbor())); + String ur = "ur:hdkey/otaxhdclaxpsfswtmnsknejlceuogoqdaelbmhwnptlrecwpeehhfnpsfzbauecatleotsheptaahdcxvsbbhlrpdivdmelovygscttbstjpnllpasmtcaecmyvswpwftssffxrkcabsmdcxamtantjooeadlaaocylpgrstlfdtflonhd"; + Assert.assertEquals(ur, urHDKey.toUR().toString()); + } + + @Test + public void testZeroMasterFingerprint() throws CborException { + String hex = "a303582103ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9045820e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f952006d99d70a201800200"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URHDKey urHDKey = URHDKey.fromCbor(items.get(0)); + Assert.assertFalse(urHDKey.isMaster()); + Assert.assertFalse(urHDKey.isPrivateKey()); + Assert.assertEquals("03ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9", TestUtils.bytesToHex(urHDKey.getKey())); + Assert.assertEquals("e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f9520", TestUtils.bytesToHex(urHDKey.getChainCode())); + Assert.assertNull(urHDKey.getOrigin().getPath()); + Assert.assertEquals("00000000", TestUtils.bytesToHex(urHDKey.getOrigin().getSourceFingerprint())); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urHDKey.toCbor())); + String ur = "ur:hdkey/otaxhdclaxpsfswtmnsknejlceuogoqdaelbmhwnptlrecwpeehhfnpsfzbauecatleotsheptaahdcxvsbbhlrpdivdmelovygscttbstjpnllpasmtcaecmyvswpwftssffxrkcabsmdcxamtantjooeadlaaoaedpmwehrt"; + Assert.assertEquals(ur, urHDKey.toUR().toString()); + } + + @Test + public void testShortMasterFingerprint() throws CborException { + String hex = "a303582103ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9045820e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f952006d99d70a201800218ff"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URHDKey urHDKey = URHDKey.fromCbor(items.get(0)); + Assert.assertFalse(urHDKey.isMaster()); + Assert.assertFalse(urHDKey.isPrivateKey()); + Assert.assertEquals("03ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9", TestUtils.bytesToHex(urHDKey.getKey())); + Assert.assertEquals("e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f9520", TestUtils.bytesToHex(urHDKey.getChainCode())); + Assert.assertNull(urHDKey.getOrigin().getPath()); + Assert.assertEquals("000000ff", TestUtils.bytesToHex(urHDKey.getOrigin().getSourceFingerprint())); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urHDKey.toCbor())); + String ur = "ur:hdkey/otaxhdclaxpsfswtmnsknejlceuogoqdaelbmhwnptlrecwpeehhfnpsfzbauecatleotsheptaahdcxvsbbhlrpdivdmelovygscttbstjpnllpasmtcaecmyvswpwftssffxrkcabsmdcxamtantjooeadlaaocszmvagmtotp"; + Assert.assertEquals(ur, urHDKey.toUR().toString()); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/UROutputDescriptorTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/UROutputDescriptorTest.java new file mode 100644 index 0000000..de03c90 --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/UROutputDescriptorTest.java @@ -0,0 +1,65 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import com.sparrowwallet.hummingbird.TestUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class UROutputDescriptorTest { + @Test + public void testECKeyPublic() throws CborException { + String hex = "d99d74a20166706b284030290281d99d72a103582103e220e776d811c44075a4a260734445c8967865f5357ba98ead3bc6a6552c36f2"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + UROutputDescriptor urOutputDescriptor = UROutputDescriptor.fromCbor(items.get(0)); + Assert.assertEquals("pk(@0)", urOutputDescriptor.getSource()); + Assert.assertTrue(urOutputDescriptor.getKeys().size() == 1 && urOutputDescriptor.getKeys().get(0) instanceof URECKey); + URECKey urECKey = (URECKey) urOutputDescriptor.getKeys().get(0); + Assert.assertEquals("03e220e776d811c44075a4a260734445c8967865f5357ba98ead3bc6a6552c36f2", TestUtils.bytesToHex(urECKey.getData())); + } + + @Test + public void testAddress() throws CborException { + String hex = "d99d74a2016861646472284030290281d99d73a301d99d71a10201020203544efd3ded47d967e4122982422c9d84db60503972"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + UROutputDescriptor urOutputDescriptor = UROutputDescriptor.fromCbor(items.get(0)); + Assert.assertEquals("addr(@0)", urOutputDescriptor.getSource()); + Assert.assertTrue(urOutputDescriptor.getKeys().size() == 1 && urOutputDescriptor.getKeys().get(0) instanceof URAddress); + URAddress urAddress = (URAddress) urOutputDescriptor.getKeys().get(0); + Assert.assertEquals("4efd3ded47d967e4122982422c9d84db60503972", TestUtils.bytesToHex(urAddress.getData())); + } + + @Test + public void testECKeyPrivate() throws CborException { + String hex = "d99d74a20167706b68284030290281d99d72a202f5035820347c4acb73f7bf2268b958230e215986eda87a984959c4ddbd4d62c07de6310e"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + UROutputDescriptor urOutputDescriptor = UROutputDescriptor.fromCbor(items.get(0)); + Assert.assertEquals("pkh(@0)", urOutputDescriptor.getSource()); + Assert.assertTrue(urOutputDescriptor.getKeys().size() == 1 && urOutputDescriptor.getKeys().get(0) instanceof URECKey); + URECKey urECKey = (URECKey) urOutputDescriptor.getKeys().get(0); + Assert.assertEquals("347c4acb73f7bf2268b958230e215986eda87a984959c4ddbd4d62c07de6310e", TestUtils.bytesToHex(urECKey.getData())); + } + + @Test + public void testMulti() throws CborException { + String hex = "d99d74a301781c77736828736f727465646d756c746928322c40302c40312c403229290283d99d6fa5035821021c0b479ecf6e67713ddf0c43b634592f51c037b6f951fb1dc6361a98b1e5735e0458206b3a4cfb6a45f6305efe6e0e976b5d26ba27f7c344d7fc7abef7be2d06d52dfd06d99d70a201881830f500f500f502f5021adc56727607d99d70a101838400f401f480f4081a18f8c2e7d99d6fa50358210397fcf2274abd243d42d42d3c248608c6d1935efca46138afef43af08e9712896045820c887c72d9d8ac29cddd5b2b060e8b0239039a149c784abe6079e24445db4aa8a06d99d70a201881830f500f500f502f5021af245ae3807d99d70a101838400f401f480f4081a221eb5a0d99d6fa5035821028342f5f7773f6fab374e1c2d3ccdba26bc0933fc4f63828b662b4357e4cc37910458205afed56d755c088320ec9bc6acd84d33737b580083759e0a0ff8f26e429e0b7706d99d70a201881830f500f500f502f5021ac5d8729707d99d70a101838400f401f480f4081a1c0ae906036f5361746f7368692773205374617368"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + UROutputDescriptor urOutputDescriptor = UROutputDescriptor.fromCbor(items.get(0)); + Assert.assertEquals("wsh(sortedmulti(2,@0,@1,@2))", urOutputDescriptor.getSource()); + Assert.assertTrue(urOutputDescriptor.getKeys().size() == 3 && urOutputDescriptor.getKeys().stream().allMatch(item -> item instanceof URHDKey)); + URHDKey urHDKey1 = (URHDKey) urOutputDescriptor.getKeys().get(0); + Assert.assertNull(urHDKey1.getUseInfo()); + Assert.assertEquals("dc567276", TestUtils.bytesToHex(urHDKey1.getOrigin().getSourceFingerprint())); + Assert.assertEquals("48'/0'/0'/2'", urHDKey1.getOrigin().getPath()); + Assert.assertEquals("021c0b479ecf6e67713ddf0c43b634592f51c037b6f951fb1dc6361a98b1e5735e", TestUtils.bytesToHex(urHDKey1.getKey())); + Assert.assertEquals("6b3a4cfb6a45f6305efe6e0e976b5d26ba27f7c344d7fc7abef7be2d06d52dfd", TestUtils.bytesToHex(urHDKey1.getChainCode())); + Assert.assertEquals("Satoshi's Stash", urOutputDescriptor.getName()); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URPSBTTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URPSBTTest.java new file mode 100644 index 0000000..ad40d20 --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URPSBTTest.java @@ -0,0 +1,25 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import com.sparrowwallet.hummingbird.TestUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class URPSBTTest { + @Test + public void testEncode() throws CborException { + String hex = "58A770736274FF01009A020000000258E87A21B56DAF0C23BE8E7070456C336F7CBAA5C8757924F545887BB2ABDD750000000000FFFFFFFF838D0427D0EC650A68AA46BB0B098AEA4422C071B2CA78352A077959D07CEA1D0100000000FFFFFFFF0270AAF00800000000160014D85C2B71D0060B09C9886AEB815E50991DDA124D00E1F5050000000016001400AEA9A2E5F0F876A588DF5546E8742D1D87008F000000000000000000"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URPSBT urPsbt = URPSBT.fromCbor(items.get(0)); + String psbtHex = "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000"; + Assert.assertArrayEquals(TestUtils.hexToBytes(psbtHex), urPsbt.getPsbt()); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urPsbt.toCbor())); + String ur = "ur:psbt/hdosjojkidjyzmadaenyaoaeaeaeaohdvsknclrejnpebncnrnmnjojofejzeojlkerdonspkpkkdkykfelokgprpyutkpaeaeaeaeaezmzmzmzmlslgaaditiwpihbkispkfgrkbdaslewdfycprtjsprsgksecdratkkhktikewdcaadaeaeaeaezmzmzmzmaojopkwtayaeaeaeaecmaebbtphhdnjstiambdassoloimwmlyhygdnlcatnbggtaevyykahaeaeaeaecmaebbaeplptoevwwtyakoonlourgofgvsjydpcaltaemyaeaeaeaeaeaeaeaeaebkgdcarh"; + Assert.assertEquals(ur, urPsbt.toUR().toString()); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URSSKRTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URSSKRTest.java new file mode 100644 index 0000000..868c981 --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URSSKRTest.java @@ -0,0 +1,27 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.model.ByteString; +import com.sparrowwallet.hummingbird.TestUtils; +import com.sparrowwallet.hummingbird.UR; +import com.sparrowwallet.hummingbird.URDecoder; +import org.junit.Assert; +import org.junit.Test; + +public class URSSKRTest { + private final String splitVector = "4bbf1101025abd490ee65b6084859854ee67736e75"; + private final String urVector = "ur:sskr/gogrrsbyadaohtrygabavahphnlrlpmkghwyiojkjtkpmdkncfjp"; + + @Test + public void testSskrToUR() { + byte[] data = TestUtils.hexToBytes(splitVector); + URSSKR sskr = new URSSKR(data); + Assert.assertEquals(urVector, sskr.toUR().toString()); + } + + @Test + public void testURtoSskr() throws UR.URException { + UR ur = URDecoder.decode(urVector); + URSSKR sskr = URSSKR.fromCbor(new ByteString(ur.getCborBytes())); + Assert.assertEquals(splitVector, TestUtils.bytesToHex(sskr.getSplit())); + } +} diff --git a/src/test/java/com/sparrowwallet/hummingbird/registry/URSeedTest.java b/src/test/java/com/sparrowwallet/hummingbird/registry/URSeedTest.java new file mode 100644 index 0000000..5473232 --- /dev/null +++ b/src/test/java/com/sparrowwallet/hummingbird/registry/URSeedTest.java @@ -0,0 +1,30 @@ +package com.sparrowwallet.hummingbird.registry; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import com.sparrowwallet.hummingbird.TestUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +public class URSeedTest { + private final DateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH); + + @Test + public void testSeed() throws CborException { + String hex = "A20150C7098580125E2AB0981253468B2DBC5202D8641947DA"; + byte[] data = TestUtils.hexToBytes(hex); + List items = CborDecoder.decode(data); + URSeed urSeed = URSeed.fromCbor(items.get(0)); + Assert.assertEquals("c7098580125e2ab0981253468b2dbc52", TestUtils.bytesToHex(urSeed.getSeed())); + Assert.assertEquals("12 May 2020", dateFormat.format(urSeed.getBirthdate())); + Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(urSeed.toCbor())); + String ur = "ur:seed/oeadgdstaslplabghydrpfmkbggufgludprfgmaotpiecffltnlpqdenos"; + Assert.assertEquals(ur, urSeed.toUR().toString()); + } +}