diff --git a/core/src/main/java/org/bitcoinj/wallet/DefaultKeyChainFactory.java b/core/src/main/java/org/bitcoinj/wallet/DefaultKeyChainFactory.java index 799261e2..d592e566 100644 --- a/core/src/main/java/org/bitcoinj/wallet/DefaultKeyChainFactory.java +++ b/core/src/main/java/org/bitcoinj/wallet/DefaultKeyChainFactory.java @@ -48,9 +48,6 @@ public class DefaultKeyChainFactory implements KeyChainFactory { @Override public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, boolean isFollowingKey, boolean isMarried) throws UnreadableWalletException { - if (accountKey.getPath().size() != DeterministicKeyChain.ACCOUNT_ZERO_PATH.size()) - throw new UnreadableWalletException("Expecting account key but found key with path: " + - HDUtils.formatPath(accountKey.getPath())); DeterministicKeyChain chain; if (isMarried) chain = new MarriedKeyChain(accountKey); diff --git a/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java b/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java index 7f49c57f..bb7a2c94 100644 --- a/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java +++ b/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java @@ -323,7 +323,6 @@ public class DeterministicKeyChain implements EncryptableKeyChain { */ public DeterministicKeyChain(DeterministicKey watchingKey) { checkArgument(watchingKey.isPubKeyOnly(), "Private subtrees not currently supported: if you got this key from DKC.getWatchingKey() then use .dropPrivate().dropParent() on it first."); - checkArgument(watchingKey.getPath().size() == getAccountPath().size(), "You can only watch an account key currently"); setAccountPath(watchingKey.getPath()); basicKeyChain = new BasicKeyChain(); this.seed = null; diff --git a/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java b/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java index 8161fde2..b99d2fde 100644 --- a/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java @@ -395,6 +395,42 @@ public class DeterministicKeyChainTest { assertEquals(key4.getPubKeyPoint(), rekey4.getPubKeyPoint()); } + @Test + public void watchingChainArbitraryPath() throws UnreadableWalletException { + Utils.setMockClock(); + DeterministicKey key1 = bip44chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); + DeterministicKey key2 = bip44chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); + DeterministicKey key3 = bip44chain.getKey(KeyChain.KeyPurpose.CHANGE); + DeterministicKey key4 = bip44chain.getKey(KeyChain.KeyPurpose.CHANGE); + + NetworkParameters params = MainNetParams.get(); + DeterministicKey watchingKey = bip44chain.getWatchingKey(); + watchingKey = watchingKey.dropPrivateBytes().dropParent(); + watchingKey.setCreationTimeSeconds(100000); + chain = DeterministicKeyChain.watch(watchingKey); + assertEquals(100000, chain.getEarliestKeyCreationTime()); + chain.setLookaheadSize(10); + chain.maybeLookAhead(); + + assertEquals(key1.getPubKeyPoint(), chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint()); + assertEquals(key2.getPubKeyPoint(), chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint()); + final DeterministicKey key = chain.getKey(KeyChain.KeyPurpose.CHANGE); + assertEquals(key3.getPubKeyPoint(), key.getPubKeyPoint()); + try { + // Can't sign with a key from a watching chain. + key.sign(Sha256Hash.ZERO_HASH); + fail(); + } catch (ECKey.MissingPrivateKeyException e) { + // Ignored. + } + // Test we can serialize and deserialize a watching chain OK. + List serialization = chain.serializeToProtobuf(); + checkSerialization(serialization, "watching-wallet-arbitrary-path-serialization.txt"); + chain = DeterministicKeyChain.fromProtobuf(serialization, null).get(0); + final DeterministicKey rekey4 = chain.getKey(KeyChain.KeyPurpose.CHANGE); + assertEquals(key4.getPubKeyPoint(), rekey4.getPubKeyPoint()); + } + @Test public void watchingChainAccountOne() throws UnreadableWalletException { Utils.setMockClock(); diff --git a/core/src/test/resources/org/bitcoinj/wallet/watching-wallet-arbitrary-path-serialization.txt b/core/src/test/resources/org/bitcoinj/wallet/watching-wallet-arbitrary-path-serialization.txt new file mode 100644 index 00000000..8dafbd60 --- /dev/null +++ b/core/src/test/resources/org/bitcoinj/wallet/watching-wallet-arbitrary-path-serialization.txt @@ -0,0 +1,321 @@ +type: DETERMINISTIC_KEY +public_key: "\002T5VO\274\362m\300\320\352\005\r!\307\024\250\307C\324\323\215[\232@\254S\270\217\362\370\214\362" +creation_timestamp: 100000000 +deterministic_key { + chain_code: "F\336\2067\377M\026)%\357ZL#\203\320\324\217-\3305\310\244\n\205\277E\323L\250ww\314" + path: 2147483692 + path: 2147483649 + path: 2147483648 +} + +type: DETERMINISTIC_KEY +public_key: "\003\2471\326i\331A\337|\373\276\3214\257\363\266Q\315x\341\317\200\243\234\336\220\017\302\241u\017DvD\2662\342\316" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 0 + path: 2 +} + +type: DETERMINISTIC_KEY +public_key: "\003 1\316\277\355_\343\214}\a\205\233\325\232\241n\256\325\300\2369\020\nh\335\0243\355\362$?7" +deterministic_key { + chain_code: "\334(\\u\022\245\370\t*\372\315\330\365\256Ms\254J_{B\035[f\333\351\272\261\363\373_\023" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 0 + path: 3 +} + +type: DETERMINISTIC_KEY +public_key: "\003\354\fhI\2731\026\222\v\274\027\357\327\033X\324\270\323\252}\314}\221\213\272\\\362k\352\334#" +deterministic_key { + chain_code: "#\351\"(\t\245\006\351\354f\334\216(\272\252\200\226\337\370\260XO\375\016/\377\306\263yE\222\311" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 0 + path: 4 +} + +type: DETERMINISTIC_KEY +public_key: "\002-7\rx2zP\r(B\247\350\026\205\210w\251G\b\254\213\000\227\271Q\272\342\357\304>G" +deterministic_key { + chain_code: "US\242J\307\2672<\373l\217\200[\316\352\361*~\324\f\304\267oD\273\300_\340K\247V\370" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 0 + path: 5 +} + +type: DETERMINISTIC_KEY +public_key: "\002Y\260\332\377;T\263\335\331\004\020kv\207E=\311|\270*hP>)\340\272\203LD\036\313\271" +deterministic_key { + chain_code: "\r34\'I\027\266\272\300\003\366f\274\333\260\006\311\3556!\227\216\301\361\247\354\025\305\321\376\274\214" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 0 + path: 6 +} + +type: DETERMINISTIC_KEY +public_key: "\002I\367x\235p9\334\234\034\366\247&\321\237\217\241V\252\017`w\212\301\000\305-\312\003\352`\302V" +deterministic_key { + chain_code: "\257\001\375\203E\315\221W\316&\035\244\306\037\351\361\027\020\346\305^Z\274O\212\363P\036\273n\367\326" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 0 + path: 7 +} + +type: DETERMINISTIC_KEY +public_key: "\0029\373\030u\305\214S\345/\373y%\t\252~\267\f\016t|\354\020\356\306\313\317\027\325\376\232kh" +deterministic_key { + chain_code: "\260\203\277\231\352\265y\020\356r_bO\374l\347\002\032i\216Ct\260\221-\207\200\243\364;\247I" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 0 + path: 8 +} + +type: DETERMINISTIC_KEY +public_key: "\002[\304\301.\342\253\256\364\025\'\017\356-t\340R\250Z\327\374\250\r\331\221`\334\362a[q\260\271" +deterministic_key { + chain_code: "\376\277#\275\035S\362`\323\246C73n\326P]{\260@\327\242\'\263$H\271\371\371YIJ" +deterministic_key { + chain_code: "9\214\315\275\300\206\253U;\235\002fA\016\215\222\235K\253\311\3648w20\2005\343\310\\:" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 3 +} + +type: DETERMINISTIC_KEY +public_key: "\002!\373+L\341\025\265\232\a\247J?v\274\273|v\035g\033\211\026\332\233\37378c\226\020\304\360" +deterministic_key { + chain_code: "i\003w\037b!Y]\214l\373]`x\355Je\\\v\205\n\310\254s\301\272\246\315\024}\366\037" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 4 +} + +type: DETERMINISTIC_KEY +public_key: "\002p\261|\230%\350\a\347?-}\317\274W\210\032\331\006\350\320\016\331\300\024\302\321[O\210E\231\342" +deterministic_key { + chain_code: "\367\263\236B#W\223\3206\3644!?Im\250\277\vY\322\302\254\212A\227\352\244\003\031)\374b" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 5 +} + +type: DETERMINISTIC_KEY +public_key: "\003\355\213b\333\3157\262iu\361\274\252\271\223\346\276^\350\260q\272m\025.\256\353\006\005\020\255&\017" +deterministic_key { + chain_code: "\003\265\376U\001\240P\'X\364\326\326\275\375s\306\225\373\264\306H5[\356\b\301>\227\325\323\315\344" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 6 +} + +type: DETERMINISTIC_KEY +public_key: "\003\317\215\311\246\322\335\226,\355\243\274E\270\027\307I\264\344\260\350_\230\372\034\340\363\3113T\222\274c" +deterministic_key { + chain_code: "r\203\252\217hI\312S\323\377](\331C\2711\214T.\031\277\333\267,U|\323x\006\003\263\233" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 7 +} + +type: DETERMINISTIC_KEY +public_key: "\002\277\306\215\221N\224\b=\v\001\216Ui\033v\250\326\361\221\332\215\343$\344A\306\357f\236\330\241\337" +deterministic_key { + chain_code: "1n@\331(\246#4\262\017\006\360a\206i\270\211n\344\363\343Y\0000\372,\231\352\252f\272!" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 8 +} + +type: DETERMINISTIC_KEY +public_key: "\002!\032\210\034\267\234\t\311T\'(~c-dKt;\366\030fI5[\026\242\372\310\342\'\205" +deterministic_key { + chain_code: "\004AX{\301\030\035K\353\353S\223m\271\352\323\272\'\202=_5\322\240J.\227\370[gZ?" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 9 +} + +type: DETERMINISTIC_KEY +public_key: "\002KZ\215?\f\365\"o\364\035\n\240\276_\335\\\256\277\212J\247\201A\325\220\361\356\213/!\301\224" +deterministic_key { + chain_code: "\247\233H)F\252\276\242\370\350\263\270\b:^\247d]\232\316QUA2\n\262\321U\003\r]" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 10 +} + +type: DETERMINISTIC_KEY +public_key: "\002\204U\310%\024\"\363\267\340\220\031\341koQ\210\037\022\224Y\354\016\370\360\374\346\216\354@B\247\233" +deterministic_key { + chain_code: "w\265\277h\352\025\351\274\233\310;rk\264`*-H-r\026\326\237%\230\034\005\236_6#a" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 11 +} + +type: DETERMINISTIC_KEY +public_key: "\002\213\241A\022c\322\031\367O\273\375\3354\'Vh\371\362\202\220\253\366\206:\033\347\300\227<6\252\034" +deterministic_key { + chain_code: "R\222\341\341.\352\306O\340+\276\266#K\211\022\264\203\225\240\246\263\023l\327\356 \350\342\242]F" + path: 2147483692 + path: 2147483649 + path: 2147483648 + path: 1 + path: 12 +} \ No newline at end of file