mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 21:47:18 +00:00
KeyChainGroup: Remove ability to change default lookaheadSize and lookaheadThreshold after construction.
Use Builder.lookaheadSize() and Builder.lookaheadThreshold() if you want to change the defaults, or manage lookahead on DeterministicKeyChains directly.
This commit is contained in:
@@ -69,6 +69,7 @@ public class KeyChainGroup implements KeyBag {
|
||||
private final NetworkParameters params;
|
||||
private final KeyChainGroupStructure structure;
|
||||
private final List<DeterministicKeyChain> chains = new LinkedList<DeterministicKeyChain>();
|
||||
private int lookaheadSize = -1, lookaheadThreshold = -1;
|
||||
|
||||
private Builder(NetworkParameters params, KeyChainGroupStructure structure) {
|
||||
this.params = params;
|
||||
@@ -119,8 +120,26 @@ public class KeyChainGroup implements KeyBag {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom lookahead size for all deterministic chains
|
||||
* @param lookaheadSize lookahead size
|
||||
*/
|
||||
public Builder lookaheadSize(int lookaheadSize) {
|
||||
this.lookaheadSize = lookaheadSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom lookahead threshold for all deterministic chains
|
||||
* @param lookaheadThreshold lookahead threshold
|
||||
*/
|
||||
public Builder lookaheadThreshold(int lookaheadThreshold) {
|
||||
this.lookaheadThreshold = lookaheadThreshold;
|
||||
return this;
|
||||
}
|
||||
|
||||
public KeyChainGroup build() {
|
||||
return new KeyChainGroup(params, null, chains, null, null);
|
||||
return new KeyChainGroup(params, null, chains, lookaheadSize, lookaheadThreshold, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +165,7 @@ public class KeyChainGroup implements KeyBag {
|
||||
|
||||
/** Creates a keychain group with just a basic chain. No deterministic chains will be created automatically. */
|
||||
public static KeyChainGroup createBasic(NetworkParameters params) {
|
||||
return new KeyChainGroup(params, new BasicKeyChain(), null, null, null);
|
||||
return new KeyChainGroup(params, new BasicKeyChain(), null, -1, -1, null, null);
|
||||
}
|
||||
|
||||
public static KeyChainGroup.Builder builder(NetworkParameters params) {
|
||||
@@ -157,11 +176,28 @@ public class KeyChainGroup implements KeyBag {
|
||||
return new Builder(params, structure);
|
||||
}
|
||||
|
||||
private KeyChainGroup(NetworkParameters params, @Nullable BasicKeyChain basicKeyChain, @Nullable List<DeterministicKeyChain> chains,
|
||||
@Nullable EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys, @Nullable KeyCrypter crypter) {
|
||||
private KeyChainGroup(NetworkParameters params, @Nullable BasicKeyChain basicKeyChain,
|
||||
@Nullable List<DeterministicKeyChain> chains, int lookaheadSize, int lookaheadThreshold,
|
||||
@Nullable EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys, @Nullable KeyCrypter crypter) {
|
||||
this.params = params;
|
||||
this.basic = basicKeyChain == null ? new BasicKeyChain() : basicKeyChain;
|
||||
this.chains = chains != null ? new LinkedList<DeterministicKeyChain>(chains) : null;
|
||||
if (chains != null) {
|
||||
if (lookaheadSize > -1)
|
||||
this.lookaheadSize = lookaheadSize;
|
||||
else if (params.getId().equals(NetworkParameters.ID_UNITTESTNET))
|
||||
this.lookaheadSize = 5; // Cut down excess computation for unit tests.
|
||||
if (lookaheadThreshold > -1)
|
||||
this.lookaheadThreshold = lookaheadThreshold;
|
||||
this.chains = new LinkedList<DeterministicKeyChain>(chains);
|
||||
for (DeterministicKeyChain chain : this.chains) {
|
||||
if (this.lookaheadSize > -1)
|
||||
chain.setLookaheadSize(this.lookaheadSize);
|
||||
if (this.lookaheadThreshold > -1)
|
||||
chain.setLookaheadThreshold(this.lookaheadThreshold);
|
||||
}
|
||||
} else {
|
||||
this.chains = null;
|
||||
}
|
||||
this.keyCrypter = crypter;
|
||||
this.currentKeys = currentKeys == null
|
||||
? new EnumMap<KeyChain.KeyPurpose, DeterministicKey>(KeyChain.KeyPurpose.class)
|
||||
@@ -334,18 +370,6 @@ public class KeyChainGroup implements KeyBag {
|
||||
return chains.get(chains.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lookahead buffer size for ALL deterministic key chains as well as for following key chains if any exist,
|
||||
* see {@link DeterministicKeyChain#setLookaheadSize(int)}
|
||||
* for more information.
|
||||
*/
|
||||
public void setLookaheadSize(int lookaheadSize) {
|
||||
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
|
||||
this.lookaheadSize = lookaheadSize;
|
||||
for (DeterministicKeyChain chain : chains)
|
||||
chain.setLookaheadSize(lookaheadSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current lookahead size being used for ALL deterministic key chains. See
|
||||
* {@link DeterministicKeyChain#setLookaheadSize(int)}
|
||||
@@ -359,17 +383,6 @@ public class KeyChainGroup implements KeyBag {
|
||||
return lookaheadSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lookahead buffer threshold for ALL deterministic key chains, see
|
||||
* {@link DeterministicKeyChain#setLookaheadThreshold(int)}
|
||||
* for more information.
|
||||
*/
|
||||
public void setLookaheadThreshold(int num) {
|
||||
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
|
||||
for (DeterministicKeyChain chain : chains)
|
||||
chain.setLookaheadThreshold(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current lookahead threshold being used for ALL deterministic key chains. See
|
||||
* {@link DeterministicKeyChain#setLookaheadThreshold(int)}
|
||||
@@ -739,11 +752,16 @@ public class KeyChainGroup implements KeyBag {
|
||||
public static KeyChainGroup fromProtobufUnencrypted(NetworkParameters params, List<Protos.Key> keys, KeyChainFactory factory) throws UnreadableWalletException {
|
||||
BasicKeyChain basicKeyChain = BasicKeyChain.fromProtobufUnencrypted(keys);
|
||||
List<DeterministicKeyChain> chains = DeterministicKeyChain.fromProtobuf(keys, null, factory);
|
||||
int lookaheadSize = -1, lookaheadThreshold = -1;
|
||||
EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys = null;
|
||||
if (!chains.isEmpty())
|
||||
if (!chains.isEmpty()) {
|
||||
DeterministicKeyChain activeChain = chains.get(chains.size() - 1);
|
||||
lookaheadSize = activeChain.getLookaheadSize();
|
||||
lookaheadThreshold = activeChain.getLookaheadThreshold();
|
||||
currentKeys = createCurrentKeysMap(chains);
|
||||
}
|
||||
extractFollowingKeychains(chains);
|
||||
return new KeyChainGroup(params, basicKeyChain, chains, currentKeys, null);
|
||||
return new KeyChainGroup(params, basicKeyChain, chains, lookaheadSize, lookaheadThreshold, currentKeys, null);
|
||||
}
|
||||
|
||||
static KeyChainGroup fromProtobufEncrypted(NetworkParameters params, List<Protos.Key> keys, KeyCrypter crypter) throws UnreadableWalletException {
|
||||
@@ -754,11 +772,16 @@ public class KeyChainGroup implements KeyBag {
|
||||
checkNotNull(crypter);
|
||||
BasicKeyChain basicKeyChain = BasicKeyChain.fromProtobufEncrypted(keys, crypter);
|
||||
List<DeterministicKeyChain> chains = DeterministicKeyChain.fromProtobuf(keys, crypter, factory);
|
||||
int lookaheadSize = -1, lookaheadThreshold = -1;
|
||||
EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys = null;
|
||||
if (!chains.isEmpty())
|
||||
if (!chains.isEmpty()) {
|
||||
DeterministicKeyChain activeChain = chains.get(chains.size() - 1);
|
||||
lookaheadSize = activeChain.getLookaheadSize();
|
||||
lookaheadThreshold = activeChain.getLookaheadThreshold();
|
||||
currentKeys = createCurrentKeysMap(chains);
|
||||
}
|
||||
extractFollowingKeychains(chains);
|
||||
return new KeyChainGroup(params, basicKeyChain, chains, currentKeys, crypter);
|
||||
return new KeyChainGroup(params, basicKeyChain, chains, lookaheadSize, lookaheadThreshold, currentKeys, crypter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -471,9 +471,6 @@ public class Wallet extends BaseTaggableObject
|
||||
this.context = checkNotNull(context);
|
||||
this.params = checkNotNull(context.getParams());
|
||||
this.keyChainGroup = checkNotNull(keyChainGroup);
|
||||
if (this.keyChainGroup.isSupportsDeterministicChains()
|
||||
&& params.getId().equals(NetworkParameters.ID_UNITTESTNET))
|
||||
this.keyChainGroup.setLookaheadSize(5); // Cut down excess computation for unit tests.
|
||||
// If this keyChainGroup 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.
|
||||
@@ -900,16 +897,6 @@ public class Wallet extends BaseTaggableObject
|
||||
}
|
||||
}
|
||||
|
||||
/** See {@link DeterministicKeyChain#setLookaheadSize(int)} for more info on this. */
|
||||
public void setKeyChainGroupLookaheadSize(int lookaheadSize) {
|
||||
keyChainGroupLock.lock();
|
||||
try {
|
||||
keyChainGroup.setLookaheadSize(lookaheadSize);
|
||||
} finally {
|
||||
keyChainGroupLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** See {@link DeterministicKeyChain#setLookaheadSize(int)} for more info on this. */
|
||||
public int getKeyChainGroupLookaheadSize() {
|
||||
keyChainGroupLock.lock();
|
||||
@@ -920,17 +907,6 @@ public class Wallet extends BaseTaggableObject
|
||||
}
|
||||
}
|
||||
|
||||
/** See {@link DeterministicKeyChain#setLookaheadThreshold(int)} for more info on this. */
|
||||
public void setKeyChainGroupLookaheadThreshold(int num) {
|
||||
keyChainGroupLock.lock();
|
||||
try {
|
||||
maybeUpgradeToHD();
|
||||
keyChainGroup.setLookaheadThreshold(num);
|
||||
} finally {
|
||||
keyChainGroupLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** See {@link DeterministicKeyChain#setLookaheadThreshold(int)} for more info on this. */
|
||||
public int getKeyChainGroupLookaheadThreshold() {
|
||||
keyChainGroupLock.lock();
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.bitcoinj.net.discovery.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.testing.*;
|
||||
import org.bitcoinj.utils.*;
|
||||
import org.bitcoinj.wallet.KeyChainGroupStructure;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
@@ -643,8 +642,6 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
||||
public void testBloomResendOnNewKey() throws Exception {
|
||||
// Check that when we add a new key to the wallet, the Bloom filter is re-calculated and re-sent but only once
|
||||
// we exceed the lookahead threshold.
|
||||
wallet.setKeyChainGroupLookaheadSize(5);
|
||||
wallet.setKeyChainGroupLookaheadThreshold(4);
|
||||
peerGroup.start();
|
||||
// Create a couple of peers.
|
||||
InboundMessageQueuer p1 = connectPeer(1);
|
||||
@@ -788,9 +785,6 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
||||
for (int i = 0; i < NUM_KEYS; i++) {
|
||||
keys.add(shadow.freshReceiveKey());
|
||||
}
|
||||
// Reduce the number of keys we need to work with to speed up this test.
|
||||
wallet.setKeyChainGroupLookaheadSize(4);
|
||||
wallet.setKeyChainGroupLookaheadThreshold(2);
|
||||
|
||||
peerGroup.start();
|
||||
InboundMessageQueuer p1 = connectPeer(1);
|
||||
|
||||
@@ -89,7 +89,9 @@ public class TestWithNetworkConnections {
|
||||
this.blockStore = blockStore;
|
||||
// Allow subclasses to override the wallet object with their own.
|
||||
if (wallet == null) {
|
||||
wallet = new Wallet(UNITTEST, KeyChainGroup.builder(UNITTEST).build());
|
||||
// Reduce the number of keys we need to work with to speed up these tests.
|
||||
KeyChainGroup kcg = KeyChainGroup.builder(UNITTEST).lookaheadSize(4).lookaheadThreshold(2).build();
|
||||
wallet = new Wallet(UNITTEST, kcg);
|
||||
key = wallet.freshReceiveKey();
|
||||
address = LegacyAddress.fromKey(UNITTEST, key);
|
||||
}
|
||||
|
||||
@@ -59,18 +59,16 @@ public class KeyChainGroupTest {
|
||||
public void setup() {
|
||||
BriefLogFormatter.init();
|
||||
Utils.setMockClock();
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom(Script.ScriptType.P2PKH).build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
|
||||
group = KeyChainGroup.builder(MAINNET).lookaheadSize(LOOKAHEAD_SIZE).fromRandom(Script.ScriptType.P2PKH)
|
||||
.build();
|
||||
group.getActiveKeyChain(); // Force create a chain.
|
||||
|
||||
watchingAccountKey = DeterministicKey.deserializeB58(null, XPUB, MAINNET);
|
||||
}
|
||||
|
||||
private KeyChainGroup createMarriedKeyChainGroup() {
|
||||
KeyChainGroup group = KeyChainGroup.builder(MAINNET).build();
|
||||
DeterministicKeyChain chain = createMarriedKeyChain();
|
||||
group.addAndActivateHDChain(chain);
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE);
|
||||
KeyChainGroup group = KeyChainGroup.builder(MAINNET).lookaheadSize(LOOKAHEAD_SIZE).addChain(chain).build();
|
||||
group.getActiveKeyChain();
|
||||
return group;
|
||||
}
|
||||
@@ -305,8 +303,7 @@ public class KeyChainGroupTest {
|
||||
|
||||
@Test
|
||||
public void encryptionWhilstEmpty() throws Exception {
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom(Script.ScriptType.P2PKH).build();
|
||||
group.setLookaheadSize(5);
|
||||
group = KeyChainGroup.builder(MAINNET).lookaheadSize(5).fromRandom(Script.ScriptType.P2PKH).build();
|
||||
KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2);
|
||||
final KeyParameter aesKey = scrypt.deriveKey("password");
|
||||
group.encrypt(scrypt, aesKey);
|
||||
@@ -458,9 +455,8 @@ public class KeyChainGroupTest {
|
||||
|
||||
@Test
|
||||
public void serializeWatching() throws Exception {
|
||||
group = KeyChainGroup.builder(MAINNET).addChain(DeterministicKeyChain.builder().watch(watchingAccountKey)
|
||||
.outputScriptType(Script.ScriptType.P2PKH).build()).build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE);
|
||||
group = KeyChainGroup.builder(MAINNET).lookaheadSize(LOOKAHEAD_SIZE).addChain(DeterministicKeyChain.builder()
|
||||
.watch(watchingAccountKey).outputScriptType(Script.ScriptType.P2PKH).build()).build();
|
||||
group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
group.freshKey(KeyChain.KeyPurpose.CHANGE);
|
||||
group.getBloomFilterElementCount(); // Force lookahead.
|
||||
@@ -496,10 +492,9 @@ 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 = KeyChainGroup.builder(MAINNET)
|
||||
KeyChainGroup group2 = KeyChainGroup.builder(MAINNET).lookaheadSize(5)
|
||||
.addChain(DeterministicKeyChain.builder().seed(seed).outputScriptType(Script.ScriptType.P2PKH).build())
|
||||
.build();
|
||||
group2.setLookaheadSize(5);
|
||||
ECKey key2 = group2.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
assertEquals(key1, key2);
|
||||
}
|
||||
@@ -517,8 +512,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 = KeyChainGroup.builder(MAINNET).build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
|
||||
group = KeyChainGroup.builder(MAINNET).lookaheadSize(LOOKAHEAD_SIZE).build();
|
||||
ECKey key1 = new ECKey();
|
||||
Utils.rollMockClock(86400);
|
||||
ECKey key2 = new ECKey();
|
||||
@@ -545,8 +539,7 @@ public class KeyChainGroupTest {
|
||||
|
||||
@Test
|
||||
public void deterministicUpgradeRotating() throws Exception {
|
||||
group = KeyChainGroup.builder(MAINNET).build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
|
||||
group = KeyChainGroup.builder(MAINNET).lookaheadSize(LOOKAHEAD_SIZE).build();
|
||||
long now = Utils.currentTimeSeconds();
|
||||
ECKey key1 = new ECKey();
|
||||
Utils.rollMockClock(86400);
|
||||
@@ -639,10 +632,10 @@ public class KeyChainGroupTest {
|
||||
|
||||
@Test
|
||||
public void segwitKeyChainGroup() throws Exception {
|
||||
group = KeyChainGroup.builder(MAINNET).addChain(DeterministicKeyChain.builder().entropy(ENTROPY, 0)
|
||||
.outputScriptType(Script.ScriptType.P2WPKH).accountPath(DeterministicKeyChain.ACCOUNT_ONE_PATH).build())
|
||||
group = KeyChainGroup.builder(MAINNET).lookaheadSize(LOOKAHEAD_SIZE)
|
||||
.addChain(DeterministicKeyChain.builder().entropy(ENTROPY, 0).outputScriptType(Script.ScriptType.P2WPKH)
|
||||
.accountPath(DeterministicKeyChain.ACCOUNT_ONE_PATH).build())
|
||||
.build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
|
||||
assertEquals(Script.ScriptType.P2WPKH, group.getActiveKeyChain().getOutputScriptType());
|
||||
assertEquals("bc1qhcurdec849thpjjp3e27atvya43gy2snrechd9",
|
||||
group.currentAddress(KeyPurpose.RECEIVE_FUNDS).toString());
|
||||
|
||||
@@ -137,7 +137,6 @@ public class WalletTool {
|
||||
private static ValidationMode mode;
|
||||
private static String password;
|
||||
private static org.bitcoin.protocols.payments.Protos.PaymentRequest paymentRequest;
|
||||
private static OptionSpec<Integer> lookaheadSize;
|
||||
|
||||
public static class Condition {
|
||||
public enum Type {
|
||||
@@ -261,7 +260,6 @@ public class WalletTool {
|
||||
parser.accepts("allow-unconfirmed");
|
||||
parser.accepts("offline");
|
||||
parser.accepts("ignore-mandatory-extensions");
|
||||
lookaheadSize = parser.accepts("lookahead-size").withRequiredArg().ofType(Integer.class);
|
||||
OptionSpec<String> passwordFlag = parser.accepts("password").withRequiredArg();
|
||||
OptionSpec<String> paymentRequestLocation = parser.accepts("payment-request").withRequiredArg();
|
||||
parser.accepts("no-pki");
|
||||
@@ -1370,11 +1368,6 @@ public class WalletTool {
|
||||
if (options.has("privkey") || options.has("pubkey")) {
|
||||
importKey();
|
||||
} else {
|
||||
if (options.has(lookaheadSize)) {
|
||||
Integer size = options.valueOf(lookaheadSize);
|
||||
log.info("Setting keychain lookahead size to {}", size);
|
||||
wallet.setKeyChainGroupLookaheadSize(size);
|
||||
}
|
||||
ECKey key;
|
||||
try {
|
||||
key = wallet.freshReceiveKey();
|
||||
|
||||
@@ -23,7 +23,6 @@ Usage: wallet-tool --flags action-name
|
||||
If --privkey is specified, use as a WIF-, hex- or base58-encoded private key.
|
||||
Don't specify --pubkey in that case, it will be derived automatically.
|
||||
If --pubkey is specified, use as a hex/base58 encoded non-compressed public key.
|
||||
If --lookahead-size is specified, pre-generate at least this many keys ahead of where we are.
|
||||
add-addr Requires --addr to be specified, and adds it as a watching address.
|
||||
delete-key Removes the key specified by --pubkey or --addr from the wallet.
|
||||
current-receive-addr Prints the current receive address, deriving one if needed. Addresses derived with this action are
|
||||
|
||||
Reference in New Issue
Block a user