diff --git a/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java b/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java index c26eba7a..e4855d55 100644 --- a/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java +++ b/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java @@ -276,6 +276,20 @@ public class DeterministicKey extends ECKey { return key; } + /** + * A deterministic key is considered to be 'public key only' if it hasn't got a private key part and it cannot be + * rederived. + */ + @Override + public boolean isPubKeyOnly() { + if (!super.isPubKeyOnly()) + return false; + if (parent != null) + return parent.isPubKeyOnly(); + else + return true; + } + /** * A deterministic key is considered to be encrypted if it has access to encrypted private key bytes, OR if its * parent does. The reason is because the parent would be encrypted under the same key and this key knows how to diff --git a/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java b/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java index 6bd37f9a..ab0c4f66 100644 --- a/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java +++ b/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java @@ -133,7 +133,7 @@ public final class HDKeyDerivation { * if the resulting derived key is invalid (eg. private key == 0). */ public static DeterministicKey deriveChildKey(DeterministicKey parent, ChildNumber childNumber) throws HDDerivationException { - if (parent.isPubKeyOnly()) { + if (!parent.hasPrivKey()) { RawKeyBytes rawKey = deriveChildKeyBytesFromPublic(parent, childNumber, PublicDeriveMode.NORMAL); return new DeterministicKey( HDUtils.append(parent.getPath(), childNumber), diff --git a/core/src/test/java/org/bitcoinj/crypto/ChildKeyDerivationTest.java b/core/src/test/java/org/bitcoinj/crypto/ChildKeyDerivationTest.java index 1a684dbb..c503e47b 100644 --- a/core/src/test/java/org/bitcoinj/crypto/ChildKeyDerivationTest.java +++ b/core/src/test/java/org/bitcoinj/crypto/ChildKeyDerivationTest.java @@ -171,9 +171,15 @@ public class ChildKeyDerivationTest { @Test public void pubOnlyDerivation() throws Exception { DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("satoshi lives!".getBytes()); + assertFalse(key1.isPubKeyOnly()); DeterministicKey key2 = HDKeyDerivation.deriveChildKey(key1, ChildNumber.ZERO_HARDENED); + assertFalse(key2.isPubKeyOnly()); DeterministicKey key3 = HDKeyDerivation.deriveChildKey(key2, ChildNumber.ZERO); - DeterministicKey pubkey3 = HDKeyDerivation.deriveChildKey(key2.getPubOnly(), ChildNumber.ZERO); + assertFalse(key3.isPubKeyOnly()); + DeterministicKey pubkey2 = key2.getPubOnly(); + assertTrue(pubkey2.isPubKeyOnly()); + DeterministicKey pubkey3 = HDKeyDerivation.deriveChildKey(pubkey2, ChildNumber.ZERO); + assertTrue(pubkey3.isPubKeyOnly()); assertEquals(key3.getPubKeyPoint(), pubkey3.getPubKeyPoint()); } diff --git a/core/src/test/java/org/bitcoinj/wallet/BasicKeyChainTest.java b/core/src/test/java/org/bitcoinj/wallet/BasicKeyChainTest.java index 0ef2a734..13d03d85 100644 --- a/core/src/test/java/org/bitcoinj/wallet/BasicKeyChainTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/BasicKeyChainTest.java @@ -147,6 +147,7 @@ public class BasicKeyChainTest { assertFalse(chain.checkPassword("wrong")); ECKey key = chain.findKeyFromPubKey(key1.getPubKey()); assertTrue(key.isEncrypted()); + assertTrue(key.isPubKeyOnly()); assertNull(key.getSecretBytes()); try { @@ -163,6 +164,7 @@ public class BasicKeyChainTest { chain = chain.toDecrypted(PASSWORD); key = chain.findKeyFromPubKey(key1.getPubKey()); assertFalse(key.isEncrypted()); + assertFalse(key.isPubKeyOnly()); key.getPrivKeyBytes(); } diff --git a/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java b/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java index a33df287..a2e58166 100644 --- a/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/DeterministicKeyChainTest.java @@ -54,7 +54,9 @@ public class DeterministicKeyChainTest { @Test public void derive() throws Exception { ECKey key1 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); + assertFalse(key1.isPubKeyOnly()); ECKey key2 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); + assertFalse(key2.isPubKeyOnly()); final Address address = new Address(UnitTestParams.get(), "n1bQNoEx8uhmCzzA5JPG6sFdtsUQhwiQJV"); assertEquals(address, key1.toAddress(UnitTestParams.get())); @@ -63,10 +65,13 @@ public class DeterministicKeyChainTest { assertEquals(key2, chain.findKeyFromPubKey(key2.getPubKey())); key1.sign(Sha256Hash.ZERO_HASH); + assertFalse(key1.isPubKeyOnly()); ECKey key3 = chain.getKey(KeyChain.KeyPurpose.CHANGE); + assertFalse(key3.isPubKeyOnly()); assertEquals("mqumHgVDqNzuXNrszBmi7A2UpmwaPMx4HQ", key3.toAddress(UnitTestParams.get()).toString()); key3.sign(Sha256Hash.ZERO_HASH); + assertFalse(key3.isPubKeyOnly()); } @Test