HDW: Create/activate a new HD chain when a wallet is constructed if the passed KeyChainGroup is empty. This means you can back up a wallet immediately after construction even before you request a key. Thanks to Andreas for the suggestion.

This commit is contained in:
Mike Hearn 2014-08-15 14:53:31 +02:00
parent e893894f50
commit f8bc4d544e
5 changed files with 30 additions and 29 deletions

View File

@ -243,6 +243,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
this.keychain = checkNotNull(keyChainGroup);
if (params == UnitTestParams.get())
this.keychain.setLookaheadSize(5); // Cut down excess computation for unit tests.
// If this keychain was created fresh just now (new wallet), make HD so a backup can be made immediately
// without having to call current/freshReceiveKey. If there are already keys in the chain of any kind then
// we're probably being deserialized so leave things alone: the API user can upgrade later.
if (this.keychain.numKeys() == 0)
this.keychain.createAndActivateNewHDChain();
watchedScripts = Sets.newHashSet();
unspent = new HashMap<Sha256Hash, Transaction>();
spent = new HashMap<Sha256Hash, Transaction>();

View File

@ -18,6 +18,7 @@
package com.google.bitcoin.core;
import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.wallet.KeyChainGroup;
import org.junit.Test;
import java.util.Arrays;
@ -72,11 +73,11 @@ public class BloomFilterTest {
Address addr = privKey.getKey().toAddress(params);
assertTrue(addr.toString().equals("17Wx1GQfyPTNWpQMHrTwRSMTCAonSiZx9e"));
Wallet wallet = new Wallet(params);
wallet.importKey(privKey.getKey());
KeyChainGroup group = new KeyChainGroup(params);
// Add a random key which happens to have been used in a recent generation
wallet.importKey(ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99")));
group.importKeys(privKey.getKey(), ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99")));
Wallet wallet = new Wallet(params, group);
wallet.commitTx(new Transaction(params, HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038754030114062f503253482fffffffff01c05e559500000000232103cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99ac00000000")));
// We should have 2 per pubkey, and one for the pay-2-pubkey output we have

View File

@ -22,6 +22,7 @@ import com.google.bitcoin.params.UnitTestParams;
import com.google.bitcoin.store.MemoryBlockStore;
import com.google.bitcoin.testing.InboundMessageQueuer;
import com.google.bitcoin.testing.TestWithPeerGroup;
import com.google.bitcoin.wallet.KeyChainGroup;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@ -100,12 +101,13 @@ public class FilteredBlockAndPartialMerkleTreeTests extends TestWithPeerGroup {
assertEquals(tx3.getHash(),txHashList.get(3));
// A wallet which contains a pubkey used in each transaction from above
Wallet wallet = new Wallet(unitTestParams);
wallet.importKey(ECKey.fromPublicOnly(HEX.decode("04b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63")));
wallet.importKey(ECKey.fromPublicOnly(HEX.decode("04732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ec")));
wallet.importKey(ECKey.fromPublicOnly(HEX.decode("04cfb4113b3387637131ebec76871fd2760fc430dd16de0110f0eb07bb31ffac85e2607c189cb8582ea1ccaeb64ffd655409106589778f3000fdfe3263440b0350")));
wallet.importKey(ECKey.fromPublicOnly(HEX.decode("04b2f30018908a59e829c1534bfa5010d7ef7f79994159bba0f534d863ef9e4e973af6a8de20dc41dbea50bc622263ec8a770b2c9406599d39e4c9afe61f8b1613")));
KeyChainGroup group = new KeyChainGroup(unitTestParams);
group.importKeys(ECKey.fromPublicOnly(HEX.decode("04b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63")),
ECKey.fromPublicOnly(HEX.decode("04732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ec")),
ECKey.fromPublicOnly(HEX.decode("04cfb4113b3387637131ebec76871fd2760fc430dd16de0110f0eb07bb31ffac85e2607c189cb8582ea1ccaeb64ffd655409106589778f3000fdfe3263440b0350")),
ECKey.fromPublicOnly(HEX.decode("04b2f30018908a59e829c1534bfa5010d7ef7f79994159bba0f534d863ef9e4e973af6a8de20dc41dbea50bc622263ec8a770b2c9406599d39e4c9afe61f8b1613")));
Wallet wallet = new Wallet(unitTestParams, group);
BloomFilter filter = wallet.getBloomFilter(wallet.getKeychainSize()*2, 0.001, 0xDEADBEEF);
// Compare the serialized bloom filter to a known-good value
assertTrue(Arrays.equals(filter.bitcoinSerialize(), HEX.decode("0e1b091ca195e45a9164889b6bc46a09000000efbeadde02")));

View File

@ -1017,32 +1017,25 @@ public class WalletTest extends TestWithWallet {
@Test
public void keyCreationTime() throws Exception {
wallet = new Wallet(params);
Utils.setMockClock();
long now = Utils.currentTimeSeconds();
// No keys returns current time.
wallet = new Wallet(params);
assertEquals(now, wallet.getEarliestKeyCreationTime());
Utils.rollMockClock(60);
wallet.freshReceiveKey();
assertEquals(now + 60, wallet.getEarliestKeyCreationTime());
Utils.rollMockClock(60);
wallet.freshReceiveKey();
assertEquals(now + 60, wallet.getEarliestKeyCreationTime());
assertEquals(now, wallet.getEarliestKeyCreationTime());
}
@Test
public void scriptCreationTime() throws Exception {
wallet = new Wallet(params);
Utils.setMockClock();
long now = Utils.currentTimeSeconds();
// No keys returns current time.
wallet = new Wallet(params);
assertEquals(now, wallet.getEarliestKeyCreationTime());
Utils.rollMockClock(60);
Utils.rollMockClock(-120);
wallet.addWatchedAddress(new ECKey().toAddress(params));
Utils.rollMockClock(60);
wallet.freshReceiveKey();
assertEquals(now + 60, wallet.getEarliestKeyCreationTime());
assertEquals(now - 120, wallet.getEarliestKeyCreationTime());
}
@Test
@ -2483,9 +2476,9 @@ public class WalletTest extends TestWithWallet {
// much where it goes). Wallet on the other hand will try to auto-upgrade you when possible.
// Create an old-style random wallet.
wallet = new Wallet(params);
wallet.importKey(new ECKey());
wallet.importKey(new ECKey());
KeyChainGroup group = new KeyChainGroup(params);
group.importKeys(new ECKey(), new ECKey());
wallet = new Wallet(params, group);
assertTrue(wallet.isDeterministicUpgradeRequired());
// Use an HD feature.
wallet.freshReceiveKey();
@ -2495,9 +2488,9 @@ public class WalletTest extends TestWithWallet {
@Test
public void upgradeToHDEncrypted() throws Exception {
// Create an old-style random wallet.
wallet = new Wallet(params);
wallet.importKey(new ECKey());
wallet.importKey(new ECKey());
KeyChainGroup group = new KeyChainGroup(params);
group.importKeys(new ECKey(), new ECKey());
wallet = new Wallet(params, group);
assertTrue(wallet.isDeterministicUpgradeRequired());
KeyCrypter crypter = new KeyCrypterScrypt();
KeyParameter aesKey = crypter.deriveKey("abc");

View File

@ -111,7 +111,7 @@ public class WalletProtobufSerializerTest {
assertEquals(Protos.Key.Type.ORIGINAL, walletProto.getKey(0).getType());
assertEquals(0, walletProto.getExtensionCount());
assertEquals(1, walletProto.getTransactionCount());
assertEquals(1, walletProto.getKeyCount());
assertEquals(6, walletProto.getKeyCount());
Protos.Transaction t1p = walletProto.getTransaction(0);
assertEquals(0, t1p.getBlockHashCount());