mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 05:27:17 +00:00
Rename DeterministicKey.getPubOnly() to dropPrivateBytes() to reflect what it's actually trying to do, and add dropParent() as well for the cases where we actually need a truly privkey-lacking object. Update the call sites to do whatever is appropriate for those situations.
This commit is contained in:
@@ -43,7 +43,7 @@ public class DeterministicKey extends ECKey {
|
||||
private final DeterministicKey parent;
|
||||
private final ImmutableList<ChildNumber> childNumberPath;
|
||||
private final int depth;
|
||||
private final int parentFingerprint; // 0 if this key is root node of key hierarchy
|
||||
private int parentFingerprint; // 0 if this key is root node of key hierarchy
|
||||
|
||||
/** 32 bytes */
|
||||
private final byte[] chainCode;
|
||||
@@ -240,13 +240,32 @@ public class DeterministicKey extends ECKey {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same key with the private part removed. May return the same instance.
|
||||
* Returns the same key with the private bytes removed. May return the same instance. The purpose of this is to save
|
||||
* memory: the private key can always be very efficiently rederived from a parent that a private key, so storing
|
||||
* all the private keys in RAM is a poor tradeoff especially on constrained devices. This means that the returned
|
||||
* key may still be usable for signing and so on, so don't expect it to be a true pubkey-only object! If you want
|
||||
* that then you should follow this call with a call to {@link #dropParent()}.
|
||||
*/
|
||||
public DeterministicKey getPubOnly() {
|
||||
if (isPubKeyOnly()) return this;
|
||||
return new DeterministicKey(getPath(), getChainCode(), pub, null, parent);
|
||||
public DeterministicKey dropPrivateBytes() {
|
||||
if (isPubKeyOnly())
|
||||
return this;
|
||||
else
|
||||
return new DeterministicKey(getPath(), getChainCode(), pub, null, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the same key with the parent pointer removed (it still knows its own path and the parent fingerprint).</p>
|
||||
*
|
||||
* <p>If this key doesn't have private key bytes stored/cached itself, but could rederive them from the parent, then
|
||||
* the new key returned by this method won't be able to do that. Thus, using dropPrivateBytes().dropParent() on a
|
||||
* regular DeterministicKey will yield a new DeterministicKey that cannot sign or do other things involving the
|
||||
* private key at all.</p>
|
||||
*/
|
||||
public DeterministicKey dropParent() {
|
||||
DeterministicKey key = new DeterministicKey(getPath(), getChainCode(), pub, priv, null);
|
||||
key.parentFingerprint = parentFingerprint;
|
||||
return key;
|
||||
}
|
||||
|
||||
static byte[] addChecksum(byte[] input) {
|
||||
int inputLength = input.length;
|
||||
|
||||
@@ -45,6 +45,6 @@ public class KeyChainTransactionSigner extends CustomTransactionSigner {
|
||||
protected SignatureAndKey getSignature(Sha256Hash sighash, List<ChildNumber> derivationPath) {
|
||||
ImmutableList<ChildNumber> keyPath = ImmutableList.copyOf(derivationPath);
|
||||
DeterministicKey key = keyChain.getKeyByPath(keyPath, true);
|
||||
return new SignatureAndKey(key.sign(sighash), key.getPubOnly());
|
||||
return new SignatureAndKey(key.sign(sighash), key.dropPrivateBytes().dropParent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.google.protobuf.ByteString;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.math.BigInteger;
|
||||
@@ -309,7 +308,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
* this method to watch an arbitrary fragment of some other tree, this limitation may be removed in future.
|
||||
*/
|
||||
public DeterministicKeyChain(DeterministicKey watchingKey, long creationTimeSeconds) {
|
||||
checkArgument(watchingKey.isPubKeyOnly(), "Private subtrees not currently supported");
|
||||
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() == 1, "You can only watch an account key currently");
|
||||
basicKeyChain = new BasicKeyChain();
|
||||
this.creationTimeSeconds = creationTimeSeconds;
|
||||
@@ -402,7 +401,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
if (key.getPath().size() != 3) continue; // Not a leaf key.
|
||||
DeterministicKey parent = hierarchy.get(checkNotNull(key.getParent()).getPath(), false, false);
|
||||
// Clone the key to the new encrypted hierarchy.
|
||||
key = new DeterministicKey(key.getPubOnly(), parent);
|
||||
key = new DeterministicKey(key.dropPrivateBytes(), parent);
|
||||
hierarchy.putKey(key);
|
||||
basicKeyChain.importKey(key);
|
||||
}
|
||||
@@ -616,13 +615,17 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>An alias for <code>getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH).getPubOnly()</code>.
|
||||
* Use this when you would like to create a watching key chain that follows this one, but can't spend money from it.
|
||||
* <p>An alias for <code>getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH)</code>.</p>
|
||||
*
|
||||
* <p>Use this when you would like to create a watching key chain that follows this one, but can't spend money from it.
|
||||
* The returned key can be serialized and then passed into {@link #watch(org.bitcoinj.crypto.DeterministicKey)}
|
||||
* on another system to watch the hierarchy.</p>
|
||||
*
|
||||
* <p>Note that the returned key is not pubkey only unless this key chain already is: the returned key can still
|
||||
* be used for signing etc if the private key bytes are available.</p>
|
||||
*/
|
||||
public DeterministicKey getWatchingKey() {
|
||||
return getKeyByPath(ACCOUNT_ZERO_PATH).getPubOnly();
|
||||
return getKeyByPath(ACCOUNT_ZERO_PATH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -969,7 +972,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
checkState(key.isEncrypted());
|
||||
DeterministicKey parent = chain.hierarchy.get(checkNotNull(key.getParent()).getPath(), false, false);
|
||||
// Clone the key to the new decrypted hierarchy.
|
||||
key = new DeterministicKey(key.getPubOnly(), parent);
|
||||
key = new DeterministicKey(key.dropPrivateBytes(), parent);
|
||||
chain.hierarchy.putKey(key);
|
||||
chain.basicKeyChain.importKey(key);
|
||||
}
|
||||
@@ -1143,7 +1146,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
int nextChild = numChildren;
|
||||
for (int i = 0; i < needed; i++) {
|
||||
DeterministicKey key = HDKeyDerivation.deriveThisOrNextChildKey(parent, nextChild);
|
||||
key = key.getPubOnly();
|
||||
key = key.dropPrivateBytes();
|
||||
hierarchy.putKey(key);
|
||||
result.add(key);
|
||||
nextChild = key.getChildNumber().num() + 1;
|
||||
|
||||
@@ -17,16 +17,12 @@
|
||||
|
||||
package org.bitcoinj.crypto;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.bitcoinj.params.UnitTestParams;
|
||||
import org.junit.Test;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.params.*;
|
||||
import org.junit.*;
|
||||
import org.spongycastle.crypto.params.*;
|
||||
|
||||
import static org.bitcoinj.core.Utils.HEX;
|
||||
import static org.bitcoinj.core.Utils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@@ -113,27 +109,27 @@ public class ChildKeyDerivationTest {
|
||||
DeterministicKey ekpub_1_IN_4095 = HDKeyDerivation.deriveChildKey(ekpub_1_IN, 4095);
|
||||
// ExtendedHierarchicKey ekpub_1_IN_4bil = HDKeyDerivation.deriveChildKey(ekpub_1_IN, 4294967295L);
|
||||
|
||||
assertEquals(hexEncodePub(ekprv.getPubOnly()), hexEncodePub(ekpub));
|
||||
assertEquals(hexEncodePub(ekprv_0.getPubOnly()), hexEncodePub(ekpub_0));
|
||||
assertEquals(hexEncodePub(ekprv_1.getPubOnly()), hexEncodePub(ekpub_1));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN.getPubOnly()), hexEncodePub(ekpub_0_IN));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN_0.getPubOnly()), hexEncodePub(ekpub_0_IN_0));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN_1.getPubOnly()), hexEncodePub(ekpub_0_IN_1));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN_2.getPubOnly()), hexEncodePub(ekpub_0_IN_2));
|
||||
assertEquals(hexEncodePub(ekprv_0_EX_0.getPubOnly()), hexEncodePub(ekpub_0_EX_0));
|
||||
assertEquals(hexEncodePub(ekprv_0_EX_1.getPubOnly()), hexEncodePub(ekpub_0_EX_1));
|
||||
assertEquals(hexEncodePub(ekprv_0_EX_2.getPubOnly()), hexEncodePub(ekpub_0_EX_2));
|
||||
assertEquals(hexEncodePub(ekprv_1_IN.getPubOnly()), hexEncodePub(ekpub_1_IN));
|
||||
assertEquals(hexEncodePub(ekprv_1_IN_4095.getPubOnly()), hexEncodePub(ekpub_1_IN_4095));
|
||||
//assertEquals(hexEncodePub(ekprv_1_IN_4bil.getPubOnly()), hexEncodePub(ekpub_1_IN_4bil));
|
||||
assertEquals(hexEncodePub(ekprv.dropPrivateBytes().dropParent()), hexEncodePub(ekpub));
|
||||
assertEquals(hexEncodePub(ekprv_0.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0));
|
||||
assertEquals(hexEncodePub(ekprv_1.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_1));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0_IN));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN_0.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0_IN_0));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN_1.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0_IN_1));
|
||||
assertEquals(hexEncodePub(ekprv_0_IN_2.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0_IN_2));
|
||||
assertEquals(hexEncodePub(ekprv_0_EX_0.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0_EX_0));
|
||||
assertEquals(hexEncodePub(ekprv_0_EX_1.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0_EX_1));
|
||||
assertEquals(hexEncodePub(ekprv_0_EX_2.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_0_EX_2));
|
||||
assertEquals(hexEncodePub(ekprv_1_IN.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_1_IN));
|
||||
assertEquals(hexEncodePub(ekprv_1_IN_4095.dropPrivateBytes().dropParent()), hexEncodePub(ekpub_1_IN_4095));
|
||||
//assertEquals(hexEncodePub(ekprv_1_IN_4bil.dropPrivateBytes()), hexEncodePub(ekpub_1_IN_4bil));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inverseEqualsNormal() throws Exception {
|
||||
DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("Wired / Aug 13th 2014 / Snowden: I Left the NSA Clues, But They Couldn't Find Them".getBytes());
|
||||
HDKeyDerivation.RawKeyBytes key2 = HDKeyDerivation.deriveChildKeyBytesFromPublic(key1.getPubOnly(), ChildNumber.ZERO, HDKeyDerivation.PublicDeriveMode.NORMAL);
|
||||
HDKeyDerivation.RawKeyBytes key3 = HDKeyDerivation.deriveChildKeyBytesFromPublic(key1.getPubOnly(), ChildNumber.ZERO, HDKeyDerivation.PublicDeriveMode.WITH_INVERSION);
|
||||
HDKeyDerivation.RawKeyBytes key2 = HDKeyDerivation.deriveChildKeyBytesFromPublic(key1.dropPrivateBytes().dropParent(), ChildNumber.ZERO, HDKeyDerivation.PublicDeriveMode.NORMAL);
|
||||
HDKeyDerivation.RawKeyBytes key3 = HDKeyDerivation.deriveChildKeyBytesFromPublic(key1.dropPrivateBytes().dropParent(), ChildNumber.ZERO, HDKeyDerivation.PublicDeriveMode.WITH_INVERSION);
|
||||
assertArrayEquals(key2.keyBytes, key3.keyBytes);
|
||||
assertArrayEquals(key2.chainCode, key3.chainCode);
|
||||
}
|
||||
@@ -176,8 +172,14 @@ public class ChildKeyDerivationTest {
|
||||
assertFalse(key2.isPubKeyOnly());
|
||||
DeterministicKey key3 = HDKeyDerivation.deriveChildKey(key2, ChildNumber.ZERO);
|
||||
assertFalse(key3.isPubKeyOnly());
|
||||
DeterministicKey pubkey2 = key2.getPubOnly();
|
||||
assertTrue(pubkey2.isPubKeyOnly());
|
||||
|
||||
key2 = key2.dropPrivateBytes();
|
||||
assertFalse(key2.isPubKeyOnly()); // still got private key bytes from the parents!
|
||||
|
||||
// pubkey2 got its cached private key bytes (if any) dropped, and now it'll lose its parent too, so now it
|
||||
// becomes a true pubkey-only object.
|
||||
DeterministicKey pubkey2 = key2.dropParent();
|
||||
|
||||
DeterministicKey pubkey3 = HDKeyDerivation.deriveChildKey(pubkey2, ChildNumber.ZERO);
|
||||
assertTrue(pubkey3.isPubKeyOnly());
|
||||
assertEquals(key3.getPubKeyPoint(), pubkey3.getPubKeyPoint());
|
||||
|
||||
@@ -273,7 +273,7 @@ public class DeterministicKeyChainTest {
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void watchingCannotEncrypt() throws Exception {
|
||||
final DeterministicKey accountKey = chain.getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH);
|
||||
chain = DeterministicKeyChain.watch(accountKey.getPubOnly());
|
||||
chain = DeterministicKeyChain.watch(accountKey.dropPrivateBytes().dropParent());
|
||||
chain = chain.toEncrypted("this doesn't make any sense");
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ public class DeterministicKeyChainTest {
|
||||
DeterministicKey[] keys = new DeterministicKey[100];
|
||||
for (int i = 0; i < keys.length; i++)
|
||||
keys[i] = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
chain = DeterministicKeyChain.watch(chain.getWatchingKey());
|
||||
chain = DeterministicKeyChain.watch(chain.getWatchingKey().dropPrivateBytes().dropParent());
|
||||
int e = chain.numBloomFilterEntries();
|
||||
BloomFilter filter = chain.getFilter(e, 0.001, 1);
|
||||
for (DeterministicKey key : keys)
|
||||
|
||||
Reference in New Issue
Block a user