3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-15 11:45:51 +00:00

Add Wallet.isWatching() to determine if the wallet is a watching wallet. Comes with tests.

This commit is contained in:
Andreas Schildbach 2014-12-06 18:06:20 +01:00 committed by Mike Hearn
parent 1e6ce4b1ba
commit ed6821ed15
9 changed files with 129 additions and 1 deletions

View File

@ -401,6 +401,11 @@ public class ECKey implements EncryptableItem, Serializable {
return priv != null;
}
/** Returns true if this key is watch only, meaning it has a public key but no private key. */
public boolean isWatching() {
return isPubKeyOnly() && !isEncrypted();
}
/**
* Output this ECKey as an ASN.1 encoded private key, as understood by OpenSSL or used by the Bitcoin reference
* implementation in its wallet storage format.

View File

@ -750,6 +750,23 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
}
}
/**
* Returns whether this wallet consists entirely of watching keys (unencrypted keys with no private part). Mixed
* wallets are forbidden.
*
* @throws IllegalStateException
* if there are no keys, or if there is a mix between watching and non-watching keys.
*/
public boolean isWatching() {
keychainLock.lock();
try {
maybeUpgradeToHD();
return keychain.isWatching();
} finally {
keychainLock.unlock();
}
}
/**
* Return true if we are watching this address.
*/

View File

@ -47,6 +47,7 @@ public class BasicKeyChain implements EncryptableKeyChain {
private final LinkedHashMap<ByteString, ECKey> hashToKeys;
private final LinkedHashMap<ByteString, ECKey> pubkeyToKeys;
@Nullable private final KeyCrypter keyCrypter;
private boolean isWatching;
private final CopyOnWriteArrayList<ListenerRegistration<KeyChainEventListener>> listeners;
@ -167,6 +168,14 @@ public class BasicKeyChain implements EncryptableKeyChain {
}
private void importKeyLocked(ECKey key) {
if (hashToKeys.isEmpty()) {
isWatching = key.isWatching();
} else {
if (key.isWatching() && !isWatching)
throw new IllegalArgumentException("Key is watching but chain is not");
if (!key.isWatching() && isWatching)
throw new IllegalArgumentException("Key is not watching but chain is");
}
ECKey previousKey = pubkeyToKeys.put(ByteString.copyFrom(key.getPubKey()), key);
hashToKeys.put(ByteString.copyFrom(key.getPubKeyHash()), key);
checkState(previousKey == null);
@ -221,6 +230,21 @@ public class BasicKeyChain implements EncryptableKeyChain {
return pubkeyToKeys.size();
}
/**
* Returns whether this chain consists entirely of watching keys (unencrypted keys with no private part). Mixed
* chains are forbidden. Null means the chain is empty.
*/
public Boolean isWatching() {
lock.lock();
try {
if (hashToKeys.isEmpty())
return null;
return isWatching;
} finally {
lock.unlock();
}
}
/**
* Removes the given key from the keychain. Be very careful with this - losing a private key <b>destroys the
* money associated with it</b>.

View File

@ -628,6 +628,12 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
return getKeyByPath(ACCOUNT_ZERO_PATH);
}
/** Returns true if this chain is watch only, meaning it has public keys but no private key. */
public boolean isWatching() {
DeterministicKey key = getKeyByPath(ACCOUNT_ZERO_PATH);
return key.isWatching();
}
@Override
public int numKeys() {
// We need to return here the total number of keys including the lookahead zone, not the number of keys we

View File

@ -551,6 +551,30 @@ public class KeyChainGroup implements KeyBag {
return keyCrypter != null;
}
/**
* Returns whether this chain has only watching keys (unencrypted keys with no private part). Mixed chains are
* forbidden.
*
* @throws IllegalStateException
* if there are no keys, or if there is a mix between watching and non-watching keys.
*/
public boolean isWatching() {
Boolean basicChainIsWatching = basic.isWatching();
Boolean deterministicChainIsWatching = !chains.isEmpty() ? getActiveKeyChain().isWatching() : null;
if (basicChainIsWatching == null && deterministicChainIsWatching == null)
throw new IllegalStateException("No keys");
if (basicChainIsWatching == null && deterministicChainIsWatching != null)
return deterministicChainIsWatching;
if (basicChainIsWatching != null && deterministicChainIsWatching == null)
return basicChainIsWatching;
if (basicChainIsWatching == deterministicChainIsWatching)
return deterministicChainIsWatching;
if (basicChainIsWatching && !deterministicChainIsWatching)
throw new IllegalStateException("Basic chain is watching, deterministic chain is not");
else
throw new IllegalStateException("Basic chain is not watching, deterministic chain is");
}
/** Returns the key crypter or null if the group is not encrypted. */
@Nullable public KeyCrypter getKeyCrypter() { return keyCrypter; }

View File

@ -77,7 +77,7 @@ public class BloomFilterTest {
KeyChainGroup group = new KeyChainGroup(params);
// Add a random key which happens to have been used in a recent generation
group.importKeys(privKey.getKey(), ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99")));
group.importKeys(ECKey.fromPublicOnly(privKey.getKey().getPubKeyPoint()), ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99")));
Wallet wallet = new Wallet(context, group);
wallet.commitTx(new Transaction(params, HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038754030114062f503253482fffffffff01c05e559500000000232103cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99ac00000000")));

View File

@ -1152,6 +1152,15 @@ public class WalletTest extends TestWithWallet {
log.info(t2.toString(chain));
}
@Test
public void isWatching() {
assertFalse(wallet.isWatching());
Wallet watchingWallet = Wallet.fromWatchingKey(params, wallet.getWatchingKey().dropPrivateBytes().dropParent());
assertTrue(watchingWallet.isWatching());
wallet.encrypt(PASSWORD1);
assertFalse(wallet.isWatching());
}
@Test(expected = ECKey.MissingPrivateKeyException.class)
public void watchingWallet() throws Exception {
DeterministicKey watchKey = wallet.getWatchingKey();

View File

@ -148,6 +148,7 @@ public class BasicKeyChainTest {
ECKey key = chain.findKeyFromPubKey(key1.getPubKey());
assertTrue(key.isEncrypted());
assertTrue(key.isPubKeyOnly());
assertFalse(key.isWatching());
assertNull(key.getSecretBytes());
try {
@ -165,6 +166,7 @@ public class BasicKeyChainTest {
key = chain.findKeyFromPubKey(key1.getPubKey());
assertFalse(key.isEncrypted());
assertFalse(key.isPubKeyOnly());
assertFalse(key.isWatching());
key.getPrivKeyBytes();
}

View File

@ -27,6 +27,7 @@ import org.junit.Test;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.util.Arrays;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@ -582,4 +583,44 @@ public class KeyChainGroupTest {
Address addr3 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS);
assertNotEquals(addr2, addr3);
}
@Test
public void isNotWatching() {
group = new KeyChainGroup(params);
final ECKey key = ECKey.fromPrivate(BigInteger.TEN);
group.importKeys(key);
assertFalse(group.isWatching());
}
@Test
public void isWatching() {
group = new KeyChainGroup(
params,
DeterministicKey
.deserializeB58(
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
params));
final ECKey watchingKey = ECKey.fromPublicOnly(new ECKey().getPubKeyPoint());
group.importKeys(watchingKey);
assertTrue(group.isWatching());
}
@Test(expected = IllegalStateException.class)
public void isWatchingNoKeys() {
group = new KeyChainGroup(params);
group.isWatching();
}
@Test(expected = IllegalStateException.class)
public void isWatchingMixedKeys() {
group = new KeyChainGroup(
params,
DeterministicKey
.deserializeB58(
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
params));
final ECKey key = ECKey.fromPrivate(BigInteger.TEN);
group.importKeys(key);
group.isWatching();
}
}