KeyChainGroup: Remove most constructors. Those that remain are considered an unstable API.

Migrate all use of constructors to use a builder instead.
This commit is contained in:
Andreas Schildbach 2019-02-10 13:03:29 +01:00
parent d1ec4b5901
commit 691a3b1de8
10 changed files with 130 additions and 103 deletions

View file

@ -434,17 +434,17 @@ public class WalletAppKit extends AbstractIdleService {
}
protected Wallet createWallet() {
KeyChainGroup kcg;
KeyChainGroup.Builder kcg = KeyChainGroup.builder(params);
if (restoreFromSeed != null)
kcg = new KeyChainGroup(params, restoreFromSeed);
kcg.addChain(DeterministicKeyChain.builder().seed(restoreFromSeed).build());
else if (restoreFromKey != null)
kcg = new KeyChainGroup(params, restoreFromKey, false);
kcg.addChain(DeterministicKeyChain.builder().spend(restoreFromKey).build());
else
kcg = new KeyChainGroup(params);
kcg.fromRandom();
if (walletFactory != null) {
return walletFactory.create(params, kcg);
return walletFactory.create(params, kcg.build());
} else {
return new Wallet(params, kcg); // default
return new Wallet(params, kcg.build()); // default
}
}

View file

@ -62,6 +62,62 @@ import static com.google.common.base.Preconditions.*;
*/
public class KeyChainGroup implements KeyBag {
/**
* Builder for {@link KeyChainGroup}. Use {@link KeyChainGroup#builder(NetworkParameters)} to acquire an instance.
*/
public static class Builder {
private final NetworkParameters params;
private final List<DeterministicKeyChain> chains = new LinkedList<DeterministicKeyChain>();
private Builder(NetworkParameters params) {
this.params = params;
}
/**
* Add chain from a random source.
*/
public Builder fromRandom() {
this.chains.clear();
DeterministicKeyChain chain = DeterministicKeyChain.builder().random(new SecureRandom()).build();
this.chains.add(chain);
return this;
}
/**
* Add chain from a given seed.
* @param seed deterministic seed to derive all keys from
*/
public Builder fromSeed(DeterministicSeed seed) {
this.chains.clear();
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
this.chains.add(chain);
return this;
}
/**
* Add a single chain.
* @param chain to add
*/
public Builder addChain(DeterministicKeyChain chain) {
this.chains.add(chain);
return this;
}
/**
* Add multiple chains.
* @param chains to add
*/
public Builder chains(List<DeterministicKeyChain> chains) {
this.chains.clear();
this.chains.addAll(chains);
return this;
}
public KeyChainGroup build() {
return new KeyChainGroup(params, null, chains, null, null);
}
}
static {
// Init proper random number generator, as some old Android installations have bugs that make it unsecure.
if (Utils.isAndroidRuntime())
@ -71,7 +127,7 @@ public class KeyChainGroup implements KeyBag {
private static final Logger log = LoggerFactory.getLogger(KeyChainGroup.class);
private BasicKeyChain basic;
private NetworkParameters params;
private final NetworkParameters params;
// Keychains for deterministically derived keys. If this is null, no chains should be created automatically.
protected final @Nullable LinkedList<DeterministicKeyChain> chains;
// currentKeys is used for normal, non-multisig/married wallets. currentAddresses is used when we're handing out
@ -87,41 +143,8 @@ public class KeyChainGroup implements KeyBag {
return new KeyChainGroup(params, new BasicKeyChain(), null, null, null);
}
/** Creates a keychain group with no basic chain, and a single, lazily created HD chain. */
public KeyChainGroup(NetworkParameters params) {
this(params, null, new ArrayList<DeterministicKeyChain>(1), null, null);
}
/** Creates a keychain group with no basic chain, and an HD chain initialized from the given seed. */
public KeyChainGroup(NetworkParameters params, DeterministicSeed seed) {
this(params, null, ImmutableList.of(DeterministicKeyChain.builder().seed(seed).build()), null, null);
}
/**
* Creates a keychain group with no basic chain, and an HD chain initialized from the given seed. Account path is
* provided.
*/
public KeyChainGroup(NetworkParameters params, DeterministicSeed seed, ImmutableList<ChildNumber> accountPath) {
this(params, null,
ImmutableList.of(DeterministicKeyChain.builder().seed(seed).accountPath(accountPath).build()), null,
null);
}
/**
* Creates a keychain group with no basic chain, and an HD chain that is watching the given watching key.
* This HAS to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
*/
public KeyChainGroup(NetworkParameters params, DeterministicKey watchKey) {
this(params, null, ImmutableList.of(DeterministicKeyChain.builder().watch(watchKey).build()), null, null);
}
/**
* Creates a keychain group with no basic chain, and an HD chain that is watching or spending the given key.
* This HAS to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
*/
public KeyChainGroup(NetworkParameters params, DeterministicKey accountKey, boolean watch) {
this(params, null, ImmutableList.of(watch ? DeterministicKeyChain.builder().watch(accountKey).build()
: DeterministicKeyChain.builder().spend(accountKey).build()), null, null);
public static KeyChainGroup.Builder builder(NetworkParameters params) {
return new Builder(params);
}
private KeyChainGroup(NetworkParameters params, @Nullable BasicKeyChain basicKeyChain, @Nullable List<DeterministicKeyChain> chains,

View file

@ -262,7 +262,7 @@ public class Wallet extends BaseTaggableObject
* {@link #loadFromFile}.
*/
public Wallet(Context context) {
this(context, new KeyChainGroup(context.getParams()));
this(context, KeyChainGroup.builder(context.getParams()).fromRandom().build());
}
/**
@ -281,7 +281,8 @@ public class Wallet extends BaseTaggableObject
* {@link DeterministicKeyChain#ACCOUNT_ZERO_PATH 0 hardened path}
*/
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed) {
return new Wallet(params, new KeyChainGroup(params, seed));
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
}
/**
@ -291,14 +292,17 @@ public class Wallet extends BaseTaggableObject
* @return an instance of a wallet from a deterministic seed.
*/
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed, ImmutableList<ChildNumber> accountPath) {
return new Wallet(params, new KeyChainGroup(params, seed, accountPath));
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).accountPath(accountPath).build();
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
}
/**
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key.
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. This HAS
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
*/
public static Wallet fromWatchingKey(NetworkParameters params, DeterministicKey watchKey) {
return new Wallet(params, new KeyChainGroup(params, watchKey));
DeterministicKeyChain chain = DeterministicKeyChain.builder().watch(watchKey).build();
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
}
/**
@ -313,11 +317,12 @@ public class Wallet extends BaseTaggableObject
}
/**
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given spending key.
* This wallet can also spend.
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given spending key. This HAS
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}. This wallet can also spend.
*/
public static Wallet fromSpendingKey(NetworkParameters params, DeterministicKey spendKey) {
return new Wallet(params, new KeyChainGroup(params, spendKey, false));
DeterministicKeyChain chain = DeterministicKeyChain.builder().spend(spendKey).build();
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
}
/**
@ -332,13 +337,16 @@ public class Wallet extends BaseTaggableObject
}
/**
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given spending key.
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given spending key. This HAS
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
*/
public static Wallet fromMasterKey(NetworkParameters params, DeterministicKey masterKey, ChildNumber accountNumber) {
public static Wallet fromMasterKey(NetworkParameters params, DeterministicKey masterKey,
ChildNumber accountNumber) {
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(masterKey, accountNumber);
accountKey = accountKey.dropParent();
accountKey.setCreationTimeSeconds(masterKey.getCreationTimeSeconds());
return new Wallet(params, new KeyChainGroup(params, accountKey, false));
DeterministicKeyChain chain = DeterministicKeyChain.builder().spend(accountKey).build();
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
}
/**
@ -348,7 +356,7 @@ public class Wallet extends BaseTaggableObject
for (ECKey key : keys)
checkArgument(!(key instanceof DeterministicKey));
KeyChainGroup group = new KeyChainGroup(params);
KeyChainGroup group = KeyChainGroup.builder(params).build();
group.importKeys(keys);
return new Wallet(params, group);
}

View file

@ -77,7 +77,7 @@ public class BloomFilterTest {
Address addr = LegacyAddress.fromKey(MAINNET, privKey.getKey());
assertTrue(addr.toString().equals("17Wx1GQfyPTNWpQMHrTwRSMTCAonSiZx9e"));
KeyChainGroup group = new KeyChainGroup(MAINNET);
KeyChainGroup group = KeyChainGroup.builder(MAINNET).build();
// Add a random key which happens to have been used in a recent generation
group.importKeys(ECKey.fromPublicOnly(privKey.getKey().getPubKeyPoint()), ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99")));
Wallet wallet = new Wallet(MAINNET, group);

View file

@ -56,7 +56,7 @@ public class FilteredBlockAndPartialMerkleTreeTests extends TestWithPeerGroup {
BigInteger.valueOf(1), 100000));
store.setChainHead(store.get(Sha256Hash.wrap("000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506")));
KeyChainGroup group = new KeyChainGroup(UNITTEST);
KeyChainGroup group = KeyChainGroup.builder(UNITTEST).build();
group.importKeys(ECKey.fromPublicOnly(HEX.decode("04b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63")),
ECKey.fromPublicOnly(HEX.decode("04732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ec")),
ECKey.fromPublicOnly(HEX.decode("04cfb4113b3387637131ebec76871fd2760fc430dd16de0110f0eb07bb31ffac85e2607c189cb8582ea1ccaeb64ffd655409106589778f3000fdfe3263440b0350")),

View file

@ -44,6 +44,7 @@ import org.bitcoinj.utils.BriefLogFormatter;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.KeyChain;
import org.bitcoinj.wallet.KeyChainGroup;
import com.google.common.io.ByteStreams;
import com.google.protobuf.ByteString;
@ -92,12 +93,10 @@ public class WalletProtobufSerializerTest {
BriefLogFormatter.initVerbose();
Context ctx = new Context(UNITTEST);
myWatchedKey = new ECKey();
myWallet = new Wallet(UNITTEST);
myKey = new ECKey();
myKey.setCreationTimeSeconds(123456789L);
myWallet.importKey(myKey);
myAddress = LegacyAddress.fromKey(UNITTEST, myKey);
myWallet = new Wallet(UNITTEST);
myWallet = new Wallet(UNITTEST, KeyChainGroup.builder(UNITTEST).build());
myWallet.importKey(myKey);
mScriptCreationTime = new Date().getTime() / 1000 - 1234;
myWallet.addWatchedAddress(LegacyAddress.fromKey(UNITTEST, myWatchedKey), mScriptCreationTime);

View file

@ -26,6 +26,7 @@ import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.MemoryBlockStore;
import org.bitcoinj.utils.BriefLogFormatter;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.KeyChainGroup;
import org.bitcoinj.wallet.Wallet;
import com.google.common.util.concurrent.SettableFuture;
@ -88,7 +89,7 @@ public class TestWithNetworkConnections {
this.blockStore = blockStore;
// Allow subclasses to override the wallet object with their own.
if (wallet == null) {
wallet = new Wallet(UNITTEST);
wallet = new Wallet(UNITTEST, KeyChainGroup.builder(UNITTEST).build());
key = wallet.freshReceiveKey();
address = LegacyAddress.fromKey(UNITTEST, key);
}

View file

@ -555,7 +555,8 @@ public class DeterministicKeyChainTest {
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(coinLevelKey, new ChildNumber(0, true));
accountKey = accountKey.dropParent();
accountKey.setCreationTimeSeconds(watchingKey.getCreationTimeSeconds());
KeyChainGroup group = new KeyChainGroup(params, accountKey, false);
KeyChainGroup group = KeyChainGroup.builder(params)
.addChain(DeterministicKeyChain.builder().spend(accountKey).build()).build();
DeterministicKeyChain fromMasterKeyChain = group.getActiveKeyChain();
assertEquals(BIP44_ACCOUNT_ONE_PATH, fromMasterKeyChain.getAccountPath());
assertEquals(secs, fromMasterKeyChain.getEarliestKeyCreationTime());

View file

@ -1,5 +1,6 @@
/*
* Copyright 2014 Mike Hearn
* Copyright 2019 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -55,7 +56,7 @@ public class KeyChainGroupTest {
public void setup() {
BriefLogFormatter.init();
Utils.setMockClock();
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).fromRandom().build();
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
group.getActiveKeyChain(); // Force create a chain.
@ -63,7 +64,7 @@ public class KeyChainGroupTest {
}
private KeyChainGroup createMarriedKeyChainGroup() {
KeyChainGroup group = new KeyChainGroup(MAINNET);
KeyChainGroup group = KeyChainGroup.builder(MAINNET).build();
DeterministicKeyChain chain = createMarriedKeyChain();
group.addAndActivateHDChain(chain);
group.setLookaheadSize(LOOKAHEAD_SIZE);
@ -301,7 +302,7 @@ public class KeyChainGroupTest {
@Test
public void encryptionWhilstEmpty() throws Exception {
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).fromRandom().build();
group.setLookaheadSize(5);
KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2);
final KeyParameter aesKey = scrypt.deriveKey("password");
@ -454,7 +455,8 @@ public class KeyChainGroupTest {
@Test
public void serializeWatching() throws Exception {
group = new KeyChainGroup(MAINNET, watchingAccountKey);
group = KeyChainGroup.builder(MAINNET)
.addChain(DeterministicKeyChain.builder().watch(watchingAccountKey).build()).build();
group.setLookaheadSize(LOOKAHEAD_SIZE);
group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
group.freshKey(KeyChain.KeyPurpose.CHANGE);
@ -491,7 +493,8 @@ public class KeyChainGroupTest {
public void constructFromSeed() throws Exception {
ECKey key1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
final DeterministicSeed seed = checkNotNull(group.getActiveKeyChain().getSeed());
KeyChainGroup group2 = new KeyChainGroup(MAINNET, seed);
KeyChainGroup group2 = KeyChainGroup.builder(MAINNET)
.addChain(DeterministicKeyChain.builder().seed(seed).build()).build();
group2.setLookaheadSize(5);
ECKey key2 = group2.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
assertEquals(key1, key2);
@ -500,7 +503,7 @@ public class KeyChainGroupTest {
@Test(expected = DeterministicUpgradeRequiredException.class)
public void deterministicUpgradeRequired() throws Exception {
// Check that if we try to use HD features in a KCG that only has random keys, we get an exception.
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).build();
group.importKeys(new ECKey(), new ECKey());
assertTrue(group.isDeterministicUpgradeRequired());
group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); // throws
@ -510,7 +513,7 @@ public class KeyChainGroupTest {
public void deterministicUpgradeUnencrypted() throws Exception {
// Check that a group that contains only random keys has its HD chain created using the private key bytes of
// the oldest random key, so upgrading the same wallet twice gives the same outcome.
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).build();
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
ECKey key1 = new ECKey();
Utils.rollMockClock(86400);
@ -538,7 +541,7 @@ public class KeyChainGroupTest {
@Test
public void deterministicUpgradeRotating() throws Exception {
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).build();
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
long now = Utils.currentTimeSeconds();
ECKey key1 = new ECKey();
@ -557,7 +560,7 @@ public class KeyChainGroupTest {
@Test
public void deterministicUpgradeEncrypted() throws Exception {
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).build();
final ECKey key = new ECKey();
group.importKeys(key);
final KeyCrypterScrypt crypter = new KeyCrypterScrypt();
@ -594,7 +597,7 @@ public class KeyChainGroupTest {
@Test
public void isNotWatching() {
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).fromRandom().build();
final ECKey key = ECKey.fromPrivate(BigInteger.TEN);
group.importKeys(key);
assertFalse(group.isWatching());
@ -602,12 +605,11 @@ public class KeyChainGroupTest {
@Test
public void isWatching() {
group = new KeyChainGroup(
MAINNET,
DeterministicKey
.deserializeB58(
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
MAINNET));
group = KeyChainGroup.builder(MAINNET)
.addChain(DeterministicKeyChain.builder().watch(DeterministicKey.deserializeB58(
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
MAINNET)).build())
.build();
final ECKey watchingKey = ECKey.fromPublicOnly(new ECKey().getPubKeyPoint());
group.importKeys(watchingKey);
assertTrue(group.isWatching());
@ -615,18 +617,17 @@ public class KeyChainGroupTest {
@Test(expected = IllegalStateException.class)
public void isWatchingNoKeys() {
group = new KeyChainGroup(MAINNET);
group = KeyChainGroup.builder(MAINNET).build();
group.isWatching();
}
@Test(expected = IllegalStateException.class)
public void isWatchingMixedKeys() {
group = new KeyChainGroup(
MAINNET,
DeterministicKey
.deserializeB58(
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
MAINNET));
group = KeyChainGroup.builder(MAINNET)
.addChain(DeterministicKeyChain.builder().watch(DeterministicKey.deserializeB58(
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
MAINNET)).build())
.build();
final ECKey key = ECKey.fromPrivate(BigInteger.TEN);
group.importKeys(key);
group.isWatching();

View file

@ -79,7 +79,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static org.bitcoinj.core.Coin.*;
import static org.bitcoinj.core.Utils.HEX;
@ -181,9 +180,10 @@ public class WalletTest extends TestWithWallet {
@Test
public void encryptDecryptWalletWithArbitraryPath() throws Exception {
final byte[] ENTROPY = Sha256Hash.hash("don't use a string seed like this in real life".getBytes());
KeyChainGroup keyChainGroup = new KeyChainGroup(UNITTEST,
new DeterministicSeed(ENTROPY, "", 1389353062L),
DeterministicKeyChain.BIP44_ACCOUNT_ZERO_PATH);
KeyChainGroup keyChainGroup = KeyChainGroup.builder(UNITTEST)
.addChain(DeterministicKeyChain.builder().seed(new DeterministicSeed(ENTROPY, "", 1389353062L))
.accountPath(DeterministicKeyChain.BIP44_ACCOUNT_ZERO_PATH).build())
.build();
Wallet encryptedWallet = new Wallet(UNITTEST, keyChainGroup);
encryptedWallet.encrypt(PASSWORD1);
encryptedWallet.decrypt(PASSWORD1);
@ -3040,17 +3040,11 @@ public class WalletTest extends TestWithWallet {
goodKey.setCreationTimeSeconds(Utils.currentTimeSeconds());
// Do an upgrade based on the bad key.
final AtomicReference<List<DeterministicKeyChain>> fChains = new AtomicReference<>();
KeyChainGroup kcg = new KeyChainGroup(UNITTEST) {
{
fChains.set(chains);
}
};
KeyChainGroup kcg = KeyChainGroup.builder(UNITTEST).build();
kcg.importKeys(badKey, goodKey);
Utils.rollMockClock(86400);
wallet = new Wallet(UNITTEST, kcg); // This avoids the automatic HD initialisation
assertTrue(fChains.get().isEmpty());
assertTrue(kcg.getDeterministicKeyChains().isEmpty());
wallet.upgradeToDeterministic(null);
DeterministicKey badWatchingKey = wallet.getWatchingKey();
assertEquals(badKey.getCreationTimeSeconds(), badWatchingKey.getCreationTimeSeconds());
@ -3065,17 +3059,17 @@ public class WalletTest extends TestWithWallet {
assertEquals(goodKey.getCreationTimeSeconds(), usedKey.getCreationTimeSeconds());
assertEquals(goodKey.getCreationTimeSeconds(), wallet.freshReceiveKey().getCreationTimeSeconds());
assertEquals("mrM3TpCnav5YQuVA1xLercCGJH4DXujMtv", LegacyAddress.fromKey(UNITTEST, usedKey).toString());
DeterministicKeyChain c = fChains.get().get(1);
DeterministicKeyChain c = kcg.getDeterministicKeyChains().get(1);
assertEquals(c.getEarliestKeyCreationTime(), goodKey.getCreationTimeSeconds());
assertEquals(2, fChains.get().size());
assertEquals(2, kcg.getDeterministicKeyChains().size());
// Commit the maint txns.
wallet.commitTx(txns.get(0));
// Check next maintenance does nothing.
assertTrue(wallet.doMaintenance(null, false).get().isEmpty());
assertEquals(c, fChains.get().get(1));
assertEquals(2, fChains.get().size());
assertEquals(c, kcg.getDeterministicKeyChains().get(1));
assertEquals(2, kcg.getDeterministicKeyChains().size());
}
@Test(expected = IllegalArgumentException.class)
@ -3277,7 +3271,7 @@ public class WalletTest extends TestWithWallet {
@Test
public void keyEvents() throws Exception {
// Check that we can register an event listener, generate some keys and the callbacks are invoked properly.
wallet = new Wallet(UNITTEST);
wallet = new Wallet(UNITTEST, KeyChainGroup.builder(UNITTEST).build());
final List<ECKey> keys = Lists.newLinkedList();
wallet.addKeyChainEventListener(Threading.SAME_THREAD, new KeyChainEventListener() {
@Override
@ -3298,7 +3292,7 @@ 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.
KeyChainGroup group = new KeyChainGroup(UNITTEST);
KeyChainGroup group = KeyChainGroup.builder(UNITTEST).build();
group.importKeys(new ECKey(), new ECKey());
wallet = new Wallet(UNITTEST, group);
assertTrue(wallet.isDeterministicUpgradeRequired());
@ -3310,7 +3304,7 @@ public class WalletTest extends TestWithWallet {
@Test
public void upgradeToHDEncrypted() throws Exception {
// Create an old-style random wallet.
KeyChainGroup group = new KeyChainGroup(UNITTEST);
KeyChainGroup group = KeyChainGroup.builder(UNITTEST).build();
group.importKeys(new ECKey(), new ECKey());
wallet = new Wallet(UNITTEST, group);
assertTrue(wallet.isDeterministicUpgradeRequired());