导航菜单

页面标题

页面副标题

imToken v3.28.8 - DeterministicKeyChain.java 源代码

正在查看: imToken v3.28.8 应用的 DeterministicKeyChain.java JAVA 源代码文件

本页面展示 JAVA 反编译生成的源代码文件,支持语法高亮显示。 仅供安全研究与技术分析使用,严禁用于任何非法用途。请遵守相关法律法规。


package org.bitcoinj.wallet;

import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.UnmodifiableIterator;
import com.google.protobuf.ByteString;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import org.bitcoinj.core.BloomFilter;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Utils;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicHierarchy;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.EncryptedData;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.crypto.HDUtils;
import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.KeyCrypterException;
import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.script.Script;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.KeyChain;
import org.bitcoinj.wallet.Protos;
import org.bitcoinj.wallet.listeners.KeyChainEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;

public class DeterministicKeyChain implements EncryptableKeyChain {
    public static final String DEFAULT_PASSPHRASE_FOR_MNEMONIC = "";
    private static final int LAZY_CALCULATE_LOOKAHEAD = -1;
    private final BasicKeyChain basicKeyChain;
    private DeterministicKey externalParentKey;
    private DeterministicHierarchy hierarchy;
    private DeterministicKey internalParentKey;
    private boolean isFollowing;
    private int issuedExternalKeys;
    private int issuedInternalKeys;
    private int keyLookaheadEpoch;
    protected final ReentrantLock lock;
    protected int lookaheadSize;
    protected int lookaheadThreshold;

    @Nullable
    private DeterministicKey rootKey;

    @Nullable
    private DeterministicSeed seed;
    protected int sigsRequiredToSpend;
    private static final Logger log = LoggerFactory.getLogger(DeterministicKeyChain.class);
    public static final ImmutableList<ChildNumber> ACCOUNT_ZERO_PATH = ImmutableList.of(ChildNumber.ZERO_HARDENED);
    public static final ImmutableList<ChildNumber> EXTERNAL_SUBPATH = ImmutableList.of(ChildNumber.ZERO);
    public static final ImmutableList<ChildNumber> INTERNAL_SUBPATH = ImmutableList.of(ChildNumber.ONE);
    public static final ImmutableList<ChildNumber> EXTERNAL_PATH = HDUtils.concat(ACCOUNT_ZERO_PATH, EXTERNAL_SUBPATH);
    public static final ImmutableList<ChildNumber> INTERNAL_PATH = HDUtils.concat(ACCOUNT_ZERO_PATH, INTERNAL_SUBPATH);
    public static final ImmutableList<ChildNumber> BIP44_ACCOUNT_ZERO_PATH = ImmutableList.of(new ChildNumber(44, true), ChildNumber.ZERO_HARDENED, ChildNumber.ZERO_HARDENED);

    @Nullable
    public RedeemData findRedeemDataByScriptHash(ByteString byteString) {
        return null;
    }

    public boolean isMarried() {
        return false;
    }

    public void maybeLookAheadScripts() {
    }

    private int calcDefaultLookaheadThreshold() {
        return this.lookaheadSize / 3;
    }

    public static class Builder<T extends Builder<T>> {
        protected int bits = 128;
        protected byte[] entropy;
        protected String passphrase;
        protected SecureRandom random;
        protected DeterministicSeed seed;
        protected long seedCreationTimeSecs;
        protected DeterministicKey watchingKey;

        protected T self() {
            return this;
        }

        protected Builder() {
        }

        public T entropy(byte[] bArr) {
            this.entropy = bArr;
            return self();
        }

        public T seed(DeterministicSeed deterministicSeed) {
            this.seed = deterministicSeed;
            return self();
        }

        public T random(SecureRandom secureRandom, int i) {
            this.random = secureRandom;
            this.bits = i;
            return self();
        }

        public T random(SecureRandom secureRandom) {
            this.random = secureRandom;
            return self();
        }

        public T watchingKey(DeterministicKey deterministicKey) {
            this.watchingKey = deterministicKey;
            return self();
        }

        public T seedCreationTimeSecs(long j) {
            this.seedCreationTimeSecs = j;
            return self();
        }

        public T passphrase(String str) {
            this.passphrase = str;
            return self();
        }

        public DeterministicKeyChain build() {
            Preconditions.checkState((this.random == null && this.entropy == null && this.seed == null && this.watchingKey == null) ? false : true, "Must provide either entropy or random or seed or watchingKey");
            Preconditions.checkState(this.passphrase == null || this.seed == null, "Passphrase must not be specified with seed");
            SecureRandom secureRandom = this.random;
            if (secureRandom != null) {
                return new DeterministicKeyChain(secureRandom, this.bits, getPassphrase(), this.seedCreationTimeSecs);
            }
            byte[] bArr = this.entropy;
            if (bArr != null) {
                return new DeterministicKeyChain(bArr, getPassphrase(), this.seedCreationTimeSecs);
            }
            DeterministicSeed deterministicSeed = this.seed;
            if (deterministicSeed != null) {
                deterministicSeed.setCreationTimeSeconds(this.seedCreationTimeSecs);
                return new DeterministicKeyChain(this.seed);
            }
            this.watchingKey.setCreationTimeSeconds(this.seedCreationTimeSecs);
            return new DeterministicKeyChain(this.watchingKey);
        }

        protected String getPassphrase() {
            String str = this.passphrase;
            return str != null ? str : "";
        }
    }

    public static Builder<?> builder() {
        return new Builder<>();
    }

    public DeterministicKeyChain(SecureRandom secureRandom) {
        this(secureRandom, 128, "", Utils.currentTimeSeconds());
    }

    public DeterministicKeyChain(SecureRandom secureRandom, int i) {
        this(secureRandom, i, "", Utils.currentTimeSeconds());
    }

    public DeterministicKeyChain(SecureRandom secureRandom, int i, String str, long j) {
        this(new DeterministicSeed(secureRandom, i, str, j));
    }

    public DeterministicKeyChain(byte[] bArr, String str, long j) {
        this(new DeterministicSeed(bArr, str, j));
    }

    protected DeterministicKeyChain(DeterministicSeed deterministicSeed) {
        this(deterministicSeed, (KeyCrypter) null);
    }

    public DeterministicKeyChain(DeterministicKey deterministicKey) {
        this.lock = Threading.lock("DeterministicKeyChain");
        this.lookaheadSize = 100;
        this.lookaheadThreshold = calcDefaultLookaheadThreshold();
        this.sigsRequiredToSpend = 1;
        Preconditions.checkArgument(deterministicKey.isPubKeyOnly(), "Private subtrees not currently supported: if you got this key from DKC.getWatchingKey() then use .dropPrivate().dropParent() on it first.");
        Preconditions.checkArgument(deterministicKey.getPath().size() == getAccountPath().size(), "You can only watch an account key currently");
        this.basicKeyChain = new BasicKeyChain();
        this.seed = null;
        this.rootKey = null;
        addToBasicChain(deterministicKey);
        this.hierarchy = new DeterministicHierarchy(deterministicKey);
        initializeHierarchyUnencrypted(deterministicKey);
    }

    protected DeterministicKeyChain(DeterministicKey deterministicKey, boolean z) {
        this(deterministicKey);
        this.isFollowing = z;
    }

    public static DeterministicKeyChain watchAndFollow(DeterministicKey deterministicKey) {
        return new DeterministicKeyChain(deterministicKey, true);
    }

    public static DeterministicKeyChain watch(DeterministicKey deterministicKey) {
        return new DeterministicKeyChain(deterministicKey);
    }

    protected DeterministicKeyChain(DeterministicSeed deterministicSeed, @Nullable KeyCrypter keyCrypter) {
        this.lock = Threading.lock("DeterministicKeyChain");
        this.lookaheadSize = 100;
        this.lookaheadThreshold = calcDefaultLookaheadThreshold();
        this.sigsRequiredToSpend = 1;
        this.seed = deterministicSeed;
        this.basicKeyChain = new BasicKeyChain(keyCrypter);
        if (deterministicSeed.isEncrypted()) {
            return;
        }
        DeterministicKey createMasterPrivateKey = HDKeyDerivation.createMasterPrivateKey((byte[]) Preconditions.checkNotNull(deterministicSeed.getSeedBytes()));
        this.rootKey = createMasterPrivateKey;
        createMasterPrivateKey.setCreationTimeSeconds(deterministicSeed.getCreationTimeSeconds());
        addToBasicChain(this.rootKey);
        this.hierarchy = new DeterministicHierarchy(this.rootKey);
        for (int i = 1; i <= getAccountPath().size(); i++) {
            addToBasicChain(this.hierarchy.get(getAccountPath().subList(0, i), false, true));
        }
        initializeHierarchyUnencrypted(this.rootKey);
    }

    protected DeterministicKeyChain(KeyCrypter keyCrypter, KeyParameter keyParameter, DeterministicKeyChain deterministicKeyChain) {
        this.lock = Threading.lock("DeterministicKeyChain");
        this.lookaheadSize = 100;
        this.lookaheadThreshold = calcDefaultLookaheadThreshold();
        this.sigsRequiredToSpend = 1;
        Preconditions.checkNotNull(deterministicKeyChain.rootKey);
        Preconditions.checkNotNull(deterministicKeyChain.seed);
        Preconditions.checkArgument(!deterministicKeyChain.rootKey.isEncrypted(), "Chain already encrypted");
        this.issuedExternalKeys = deterministicKeyChain.issuedExternalKeys;
        this.issuedInternalKeys = deterministicKeyChain.issuedInternalKeys;
        this.lookaheadSize = deterministicKeyChain.lookaheadSize;
        this.lookaheadThreshold = deterministicKeyChain.lookaheadThreshold;
        this.seed = deterministicKeyChain.seed.encrypt(keyCrypter, keyParameter);
        this.basicKeyChain = new BasicKeyChain(keyCrypter);
        DeterministicKey encrypt = deterministicKeyChain.rootKey.encrypt(keyCrypter, keyParameter, null);
        this.rootKey = encrypt;
        this.hierarchy = new DeterministicHierarchy(encrypt);
        this.basicKeyChain.importKey(this.rootKey);
        for (int i = 1; i < getAccountPath().size(); i++) {
            encryptNonLeaf(keyParameter, deterministicKeyChain, this.rootKey, getAccountPath().subList(0, i));
        }
        DeterministicKey encryptNonLeaf = encryptNonLeaf(keyParameter, deterministicKeyChain, this.rootKey, getAccountPath());
        this.externalParentKey = encryptNonLeaf(keyParameter, deterministicKeyChain, encryptNonLeaf, HDUtils.concat(getAccountPath(), EXTERNAL_SUBPATH));
        this.internalParentKey = encryptNonLeaf(keyParameter, deterministicKeyChain, encryptNonLeaf, HDUtils.concat(getAccountPath(), INTERNAL_SUBPATH));
        Iterator<ECKey> it = deterministicKeyChain.basicKeyChain.getKeys().iterator();
        while (it.hasNext()) {
            DeterministicKey deterministicKey = (DeterministicKey) it.next();
            if (deterministicKey.getPath().size() == getAccountPath().size() + 2) {
                DeterministicKey deterministicKey2 = new DeterministicKey(deterministicKey.dropPrivateBytes(), this.hierarchy.get(((DeterministicKey) Preconditions.checkNotNull(deterministicKey.getParent())).getPath(), false, false));
                this.hierarchy.putKey(deterministicKey2);
                this.basicKeyChain.importKey(deterministicKey2);
            }
        }
    }

    protected ImmutableList<ChildNumber> getAccountPath() {
        return ACCOUNT_ZERO_PATH;
    }

    private DeterministicKey encryptNonLeaf(KeyParameter keyParameter, DeterministicKeyChain deterministicKeyChain, DeterministicKey deterministicKey, ImmutableList<ChildNumber> immutableList) {
        DeterministicKey encrypt = deterministicKeyChain.hierarchy.get(immutableList, false, false).encrypt((KeyCrypter) Preconditions.checkNotNull(this.basicKeyChain.getKeyCrypter()), keyParameter, deterministicKey);
        this.hierarchy.putKey(encrypt);
        this.basicKeyChain.importKey(encrypt);
        return encrypt;
    }

    private void initializeHierarchyUnencrypted(DeterministicKey deterministicKey) {
        this.externalParentKey = this.hierarchy.deriveChild(getAccountPath(), false, false, ChildNumber.ZERO);
        this.internalParentKey = this.hierarchy.deriveChild(getAccountPath(), false, false, ChildNumber.ONE);
        addToBasicChain(this.externalParentKey);
        addToBasicChain(this.internalParentKey);
    }

    @Override
    public DeterministicKey getKey(KeyChain.KeyPurpose keyPurpose) {
        return getKeys(keyPurpose, 1).get(0);
    }

    @Override
    public List<DeterministicKey> getKeys(KeyChain.KeyPurpose keyPurpose, int i) {
        int i2;
        DeterministicKey deterministicKey;
        Preconditions.checkArgument(i > 0);
        this.lock.lock();
        try {
            int i3 = AnonymousClass1.$SwitchMap$org$bitcoinj$wallet$KeyChain$KeyPurpose[keyPurpose.ordinal()];
            if (i3 == 1 || i3 == 2) {
                i2 = this.issuedExternalKeys + i;
                this.issuedExternalKeys = i2;
                deterministicKey = this.externalParentKey;
            } else {
                if (i3 != 3 && i3 != 4) {
                    throw new UnsupportedOperationException();
                }
                i2 = this.issuedInternalKeys + i;
                this.issuedInternalKeys = i2;
                deterministicKey = this.internalParentKey;
            }
            this.basicKeyChain.importKeys(maybeLookAhead(deterministicKey, i2, 0, 0));
            ArrayList arrayList = new ArrayList(i);
            for (int i4 = 0; i4 < i; i4++) {
                DeterministicKey deterministicKey2 = this.hierarchy.get(HDUtils.append(deterministicKey.getPath(), new ChildNumber((i2 - i) + i4, false)), false, false);
                checkForBitFlip(deterministicKey2);
                arrayList.add(deterministicKey2);
            }
            return arrayList;
        } finally {
            this.lock.unlock();
        }
    }

    static class AnonymousClass1 {
        static final int[] $SwitchMap$org$bitcoinj$wallet$KeyChain$KeyPurpose;

        static {
            int[] iArr = new int[KeyChain.KeyPurpose.values().length];
            $SwitchMap$org$bitcoinj$wallet$KeyChain$KeyPurpose = iArr;
            try {
                iArr[KeyChain.KeyPurpose.RECEIVE_FUNDS.ordinal()] = 1;
            } catch (NoSuchFieldError unused) {
            }
            try {
                $SwitchMap$org$bitcoinj$wallet$KeyChain$KeyPurpose[KeyChain.KeyPurpose.REFUND.ordinal()] = 2;
            } catch (NoSuchFieldError unused2) {
            }
            try {
                $SwitchMap$org$bitcoinj$wallet$KeyChain$KeyPurpose[KeyChain.KeyPurpose.AUTHENTICATION.ordinal()] = 3;
            } catch (NoSuchFieldError unused3) {
            }
            try {
                $SwitchMap$org$bitcoinj$wallet$KeyChain$KeyPurpose[KeyChain.KeyPurpose.CHANGE.ordinal()] = 4;
            } catch (NoSuchFieldError unused4) {
            }
        }
    }

    private void checkForBitFlip(DeterministicKey deterministicKey) {
        byte[] bArr = HDKeyDerivation.deriveChildKeyBytesFromPublic((DeterministicKey) Preconditions.checkNotNull(deterministicKey.getParent()), deterministicKey.getChildNumber(), HDKeyDerivation.PublicDeriveMode.WITH_INVERSION).keyBytes;
        byte[] pubKey = deterministicKey.getPubKey();
        if (!Arrays.equals(bArr, pubKey)) {
            throw new IllegalStateException(String.format(Locale.US, "Bit-flip check failed: %s vs %s", Arrays.toString(bArr), Arrays.toString(pubKey)));
        }
    }

    private void addToBasicChain(DeterministicKey deterministicKey) {
        this.basicKeyChain.importKeys((List<? extends ECKey>) ImmutableList.of(deterministicKey));
    }

    public DeterministicKey markKeyAsUsed(DeterministicKey deterministicKey) {
        int i = deterministicKey.getChildNumber().i() + 1;
        if (deterministicKey.getParent() == this.internalParentKey) {
            if (this.issuedInternalKeys < i) {
                this.issuedInternalKeys = i;
                maybeLookAhead();
            }
        } else if (deterministicKey.getParent() == this.externalParentKey && this.issuedExternalKeys < i) {
            this.issuedExternalKeys = i;
            maybeLookAhead();
        }
        return deterministicKey;
    }

    public DeterministicKey findKeyFromPubHash(byte[] bArr) {
        this.lock.lock();
        try {
            return (DeterministicKey) this.basicKeyChain.findKeyFromPubHash(bArr);
        } finally {
            this.lock.unlock();
        }
    }

    public DeterministicKey findKeyFromPubKey(byte[] bArr) {
        this.lock.lock();
        try {
            return (DeterministicKey) this.basicKeyChain.findKeyFromPubKey(bArr);
        } finally {
            this.lock.unlock();
        }
    }

    @Nullable
    public DeterministicKey markPubHashAsUsed(byte[] bArr) {
        this.lock.lock();
        try {
            DeterministicKey deterministicKey = (DeterministicKey) this.basicKeyChain.findKeyFromPubHash(bArr);
            if (deterministicKey != null) {
                markKeyAsUsed(deterministicKey);
            }
            return deterministicKey;
        } finally {
            this.lock.unlock();
        }
    }

    @Nullable
    public DeterministicKey markPubKeyAsUsed(byte[] bArr) {
        this.lock.lock();
        try {
            DeterministicKey deterministicKey = (DeterministicKey) this.basicKeyChain.findKeyFromPubKey(bArr);
            if (deterministicKey != null) {
                markKeyAsUsed(deterministicKey);
            }
            return deterministicKey;
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public boolean hasKey(ECKey eCKey) {
        this.lock.lock();
        try {
            return this.basicKeyChain.hasKey(eCKey);
        } finally {
            this.lock.unlock();
        }
    }

    protected DeterministicKey getKeyByPath(ChildNumber... childNumberArr) {
        return getKeyByPath((List<ChildNumber>) ImmutableList.copyOf(childNumberArr));
    }

    protected DeterministicKey getKeyByPath(List<ChildNumber> list) {
        return getKeyByPath(list, false);
    }

    public DeterministicKey getKeyByPath(List<ChildNumber> list, boolean z) {
        return this.hierarchy.get(list, false, z);
    }

    public DeterministicKey getWatchingKey() {
        return getKeyByPath((List<ChildNumber>) getAccountPath());
    }

    public boolean isWatching() {
        return getWatchingKey().isWatching();
    }

    @Override
    public int numKeys() {
        this.lock.lock();
        try {
            maybeLookAhead();
            return this.basicKeyChain.numKeys();
        } finally {
            this.lock.unlock();
        }
    }

    public int numLeafKeysIssued() {
        this.lock.lock();
        try {
            return this.issuedExternalKeys + this.issuedInternalKeys;
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public long getEarliestKeyCreationTime() {
        DeterministicSeed deterministicSeed = this.seed;
        if (deterministicSeed != null) {
            return deterministicSeed.getCreationTimeSeconds();
        }
        return getWatchingKey().getCreationTimeSeconds();
    }

    @Override
    public void addEventListener(KeyChainEventListener keyChainEventListener) {
        this.basicKeyChain.addEventListener(keyChainEventListener);
    }

    @Override
    public void addEventListener(KeyChainEventListener keyChainEventListener, Executor executor) {
        this.basicKeyChain.addEventListener(keyChainEventListener, executor);
    }

    @Override
    public boolean removeEventListener(KeyChainEventListener keyChainEventListener) {
        return this.basicKeyChain.removeEventListener(keyChainEventListener);
    }

    @Nullable
    public List<String> getMnemonicCode() {
        if (this.seed == null) {
            return null;
        }
        this.lock.lock();
        try {
            return this.seed.getMnemonicCode();
        } finally {
            this.lock.unlock();
        }
    }

    public boolean isFollowing() {
        return this.isFollowing;
    }

    @Override
    public List<Protos.Key> serializeToProtobuf() {
        ArrayList newArrayList = Lists.newArrayList();
        this.lock.lock();
        try {
            newArrayList.addAll(serializeMyselfToProtobuf());
            return newArrayList;
        } finally {
            this.lock.unlock();
        }
    }

    protected List<Protos.Key> serializeMyselfToProtobuf() {
        LinkedList newLinkedList = Lists.newLinkedList();
        DeterministicSeed deterministicSeed = this.seed;
        if (deterministicSeed != null) {
            Protos.Key.Builder serializeEncryptableItem = BasicKeyChain.serializeEncryptableItem(deterministicSeed);
            serializeEncryptableItem.setType(Protos.Key.Type.DETERMINISTIC_MNEMONIC);
            serializeSeedEncryptableItem(this.seed, serializeEncryptableItem);
            newLinkedList.add(serializeEncryptableItem.m2291build());
        }
        for (Map.Entry<ECKey, Protos.Key.Builder> entry : this.basicKeyChain.serializeToEditableProtobufs().entrySet()) {
            DeterministicKey deterministicKey = (DeterministicKey) entry.getKey();
            Protos.Key.Builder value = entry.getValue();
            value.setType(Protos.Key.Type.DETERMINISTIC_KEY);
            Protos.DeterministicKey.Builder deterministicKeyBuilder = value.getDeterministicKeyBuilder();
            deterministicKeyBuilder.setChainCode(ByteString.copyFrom(deterministicKey.getChainCode()));
            UnmodifiableIterator it = deterministicKey.getPath().iterator();
            while (it.hasNext()) {
                deterministicKeyBuilder.addPath(((ChildNumber) it.next()).i());
            }
            if (deterministicKey.equals(this.externalParentKey)) {
                deterministicKeyBuilder.setIssuedSubkeys(this.issuedExternalKeys);
                deterministicKeyBuilder.setLookaheadSize(this.lookaheadSize);
                deterministicKeyBuilder.setSigsRequiredToSpend(getSigsRequiredToSpend());
            } else if (deterministicKey.equals(this.internalParentKey)) {
                deterministicKeyBuilder.setIssuedSubkeys(this.issuedInternalKeys);
                deterministicKeyBuilder.setLookaheadSize(this.lookaheadSize);
                deterministicKeyBuilder.setSigsRequiredToSpend(getSigsRequiredToSpend());
            }
            if (newLinkedList.isEmpty() && isFollowing()) {
                deterministicKeyBuilder.setIsFollowing(true);
            }
            if (deterministicKey.getParent() != null) {
                value.clearCreationTimestamp();
            }
            newLinkedList.add(value.m2291build());
        }
        return newLinkedList;
    }

    static List<DeterministicKeyChain> fromProtobuf(List<Protos.Key> list, @Nullable KeyCrypter keyCrypter) throws UnreadableWalletException {
        return fromProtobuf(list, keyCrypter, new DefaultKeyChainFactory());
    }

    public static java.util.List<org.bitcoinj.wallet.DeterministicKeyChain> fromProtobuf(java.util.List<org.bitcoinj.wallet.Protos.Key> r27, @javax.annotation.Nullable org.bitcoinj.crypto.KeyCrypter r28, org.bitcoinj.wallet.KeyChainFactory r29) throws org.bitcoinj.wallet.UnreadableWalletException {
        throw new UnsupportedOperationException("Method not decompiled: org.bitcoinj.wallet.DeterministicKeyChain.fromProtobuf(java.util.List, org.bitcoinj.crypto.KeyCrypter, org.bitcoinj.wallet.KeyChainFactory):java.util.List");
    }

    @Override
    public DeterministicKeyChain toEncrypted(CharSequence charSequence) {
        Preconditions.checkNotNull(charSequence);
        Preconditions.checkArgument(charSequence.length() > 0);
        Preconditions.checkState(this.seed != null, "Attempt to encrypt a watching chain.");
        Preconditions.checkState(!this.seed.isEncrypted());
        KeyCrypterScrypt keyCrypterScrypt = new KeyCrypterScrypt();
        return toEncrypted((KeyCrypter) keyCrypterScrypt, keyCrypterScrypt.deriveKey(charSequence));
    }

    @Override
    public DeterministicKeyChain toEncrypted(KeyCrypter keyCrypter, KeyParameter keyParameter) {
        return new DeterministicKeyChain(keyCrypter, keyParameter, this);
    }

    @Override
    public DeterministicKeyChain toDecrypted(CharSequence charSequence) {
        Preconditions.checkNotNull(charSequence);
        Preconditions.checkArgument(charSequence.length() > 0);
        KeyCrypter keyCrypter = getKeyCrypter();
        Preconditions.checkState(keyCrypter != null, "Chain not encrypted");
        return toDecrypted(keyCrypter.deriveKey(charSequence));
    }

    @Override
    public DeterministicKeyChain toDecrypted(KeyParameter keyParameter) {
        Preconditions.checkState(getKeyCrypter() != null, "Key chain not encrypted");
        Preconditions.checkState(this.seed != null, "Can't decrypt a watching chain");
        Preconditions.checkState(this.seed.isEncrypted());
        DeterministicKeyChain makeKeyChainFromSeed = makeKeyChainFromSeed(this.seed.decrypt(getKeyCrypter(), "", keyParameter));
        if (!makeKeyChainFromSeed.getWatchingKey().getPubKeyPoint().equals(getWatchingKey().getPubKeyPoint())) {
            throw new KeyCrypterException("Provided AES key is wrong");
        }
        makeKeyChainFromSeed.lookaheadSize = this.lookaheadSize;
        Iterator<ECKey> it = this.basicKeyChain.getKeys().iterator();
        while (it.hasNext()) {
            DeterministicKey deterministicKey = (DeterministicKey) it.next();
            if (deterministicKey.getPath().size() == getAccountPath().size() + 2) {
                Preconditions.checkState(deterministicKey.isEncrypted());
                DeterministicKey deterministicKey2 = new DeterministicKey(deterministicKey.dropPrivateBytes(), makeKeyChainFromSeed.hierarchy.get(((DeterministicKey) Preconditions.checkNotNull(deterministicKey.getParent())).getPath(), false, false));
                makeKeyChainFromSeed.hierarchy.putKey(deterministicKey2);
                makeKeyChainFromSeed.basicKeyChain.importKey(deterministicKey2);
            }
        }
        makeKeyChainFromSeed.issuedExternalKeys = this.issuedExternalKeys;
        makeKeyChainFromSeed.issuedInternalKeys = this.issuedInternalKeys;
        return makeKeyChainFromSeed;
    }

    protected DeterministicKeyChain makeKeyChainFromSeed(DeterministicSeed deterministicSeed) {
        return new DeterministicKeyChain(deterministicSeed);
    }

    @Override
    public boolean checkPassword(CharSequence charSequence) {
        Preconditions.checkNotNull(charSequence);
        Preconditions.checkState(getKeyCrypter() != null, "Key chain not encrypted");
        return checkAESKey(getKeyCrypter().deriveKey(charSequence));
    }

    @Override
    public boolean checkAESKey(KeyParameter keyParameter) {
        Preconditions.checkState(this.rootKey != null, "Can't check password for a watching chain");
        Preconditions.checkNotNull(keyParameter);
        Preconditions.checkState(getKeyCrypter() != null, "Key chain not encrypted");
        try {
            return this.rootKey.decrypt(keyParameter).getPubKeyPoint().equals(this.rootKey.getPubKeyPoint());
        } catch (KeyCrypterException unused) {
            return false;
        }
    }

    @Override
    @Nullable
    public KeyCrypter getKeyCrypter() {
        return this.basicKeyChain.getKeyCrypter();
    }

    @Override
    public int numBloomFilterEntries() {
        return numKeys() * 2;
    }

    @Override
    public BloomFilter getFilter(int i, double d, long j) {
        this.lock.lock();
        try {
            Preconditions.checkArgument(i >= numBloomFilterEntries());
            maybeLookAhead();
            return this.basicKeyChain.getFilter(i, d, j);
        } finally {
            this.lock.unlock();
        }
    }

    public int getLookaheadSize() {
        this.lock.lock();
        try {
            return this.lookaheadSize;
        } finally {
            this.lock.unlock();
        }
    }

    public void setLookaheadSize(int i) {
        this.lock.lock();
        try {
            boolean z = this.lookaheadThreshold == calcDefaultLookaheadThreshold();
            this.lookaheadSize = i;
            if (z) {
                this.lookaheadThreshold = calcDefaultLookaheadThreshold();
            }
        } finally {
            this.lock.unlock();
        }
    }

    public void setLookaheadThreshold(int i) {
        this.lock.lock();
        try {
            if (i >= this.lookaheadSize) {
                throw new IllegalArgumentException("Threshold larger or equal to the lookaheadSize");
            }
            this.lookaheadThreshold = i;
        } finally {
            this.lock.unlock();
        }
    }

    public int getLookaheadThreshold() {
        this.lock.lock();
        try {
            return this.lookaheadThreshold >= this.lookaheadSize ? 0 : this.lookaheadThreshold;
        } finally {
            this.lock.unlock();
        }
    }

    public void maybeLookAhead() {
        this.lock.lock();
        try {
            List<DeterministicKey> maybeLookAhead = maybeLookAhead(this.externalParentKey, this.issuedExternalKeys);
            maybeLookAhead.addAll(maybeLookAhead(this.internalParentKey, this.issuedInternalKeys));
            if (maybeLookAhead.isEmpty()) {
                return;
            }
            this.keyLookaheadEpoch++;
            this.basicKeyChain.importKeys(maybeLookAhead);
        } finally {
            this.lock.unlock();
        }
    }

    private List<DeterministicKey> maybeLookAhead(DeterministicKey deterministicKey, int i) {
        Preconditions.checkState(this.lock.isHeldByCurrentThread());
        return maybeLookAhead(deterministicKey, i, getLookaheadSize(), getLookaheadThreshold());
    }

    private List<DeterministicKey> maybeLookAhead(DeterministicKey deterministicKey, int i, int i2, int i3) {
        Preconditions.checkState(this.lock.isHeldByCurrentThread());
        int numChildren = this.hierarchy.getNumChildren(deterministicKey.getPath());
        int i4 = ((i + i2) + i3) - numChildren;
        if (i4 <= i3) {
            return new ArrayList();
        }
        log.info("{} keys needed for {} = {} issued + {} lookahead size + {} lookahead threshold - {} num children", new Object[]{Integer.valueOf(i4), deterministicKey.getPathAsString(), Integer.valueOf(i), Integer.valueOf(i2), Integer.valueOf(i3), Integer.valueOf(numChildren)});
        ArrayList arrayList = new ArrayList(i4);
        Stopwatch createStarted = Stopwatch.createStarted();
        for (int i5 = 0; i5 < i4; i5++) {
            DeterministicKey dropPrivateBytes = HDKeyDerivation.deriveThisOrNextChildKey(deterministicKey, numChildren).dropPrivateBytes();
            this.hierarchy.putKey(dropPrivateBytes);
            arrayList.add(dropPrivateBytes);
            numChildren = dropPrivateBytes.getChildNumber().num() + 1;
        }
        createStarted.stop();
        log.info("Took {}", createStarted);
        return arrayList;
    }

    public int getIssuedExternalKeys() {
        this.lock.lock();
        try {
            return this.issuedExternalKeys;
        } finally {
            this.lock.unlock();
        }
    }

    public int getIssuedInternalKeys() {
        this.lock.lock();
        try {
            return this.issuedInternalKeys;
        } finally {
            this.lock.unlock();
        }
    }

    @Nullable
    public DeterministicSeed getSeed() {
        this.lock.lock();
        try {
            return this.seed;
        } finally {
            this.lock.unlock();
        }
    }

    List<ECKey> getKeys(boolean z) {
        List<ECKey> keys = this.basicKeyChain.getKeys();
        if (z) {
            return keys;
        }
        int size = this.internalParentKey.getPath().size();
        LinkedList linkedList = new LinkedList();
        Iterator<ECKey> it = keys.iterator();
        while (it.hasNext()) {
            DeterministicKey deterministicKey = (DeterministicKey) it.next();
            DeterministicKey parent = deterministicKey.getParent();
            if (parent != null && deterministicKey.getPath().size() > size && (!parent.equals(this.internalParentKey) || deterministicKey.getChildNumber().i() < this.issuedInternalKeys)) {
                if (!parent.equals(this.externalParentKey) || deterministicKey.getChildNumber().i() < this.issuedExternalKeys) {
                    linkedList.add(deterministicKey);
                }
            }
        }
        return linkedList;
    }

    public List<ECKey> getIssuedReceiveKeys() {
        ArrayList arrayList = new ArrayList(getKeys(false));
        Iterator it = arrayList.iterator();
        while (it.hasNext()) {
            DeterministicKey parent = ((DeterministicKey) it.next()).getParent();
            if (parent == null || !this.externalParentKey.equals(parent)) {
                it.remove();
            }
        }
        return arrayList;
    }

    public List<DeterministicKey> getLeafKeys() {
        ImmutableList.Builder builder = ImmutableList.builder();
        Iterator<ECKey> it = getKeys(true).iterator();
        while (it.hasNext()) {
            DeterministicKey deterministicKey = (DeterministicKey) it.next();
            if (deterministicKey.getPath().size() == getAccountPath().size() + 2) {
                builder.add(deterministicKey);
            }
        }
        return builder.build();
    }

    static void serializeSeedEncryptableItem(DeterministicSeed deterministicSeed, Protos.Key.Builder builder) {
        if (deterministicSeed.isEncrypted() && deterministicSeed.getEncryptedSeedData() != null) {
            EncryptedData encryptedSeedData = deterministicSeed.getEncryptedSeedData();
            builder.getEncryptedDeterministicSeedBuilder().setEncryptedPrivateKey(ByteString.copyFrom(encryptedSeedData.encryptedBytes)).setInitialisationVector(ByteString.copyFrom(encryptedSeedData.initialisationVector));
            Preconditions.checkState(deterministicSeed.getEncryptionType() == Protos.Wallet.EncryptionType.ENCRYPTED_SCRYPT_AES);
        } else {
            byte[] seedBytes = deterministicSeed.getSeedBytes();
            if (seedBytes != null) {
                builder.setDeterministicSeed(ByteString.copyFrom(seedBytes));
            }
        }
    }

    public int getKeyLookaheadEpoch() {
        this.lock.lock();
        try {
            return this.keyLookaheadEpoch;
        } finally {
            this.lock.unlock();
        }
    }

    public RedeemData getRedeemData(DeterministicKey deterministicKey) {
        throw new UnsupportedOperationException();
    }

    public Script freshOutputScript(KeyChain.KeyPurpose keyPurpose) {
        throw new UnsupportedOperationException();
    }

    public String toString(boolean z, NetworkParameters networkParameters) {
        DeterministicKey watchingKey = getWatchingKey();
        StringBuilder sb = new StringBuilder();
        DeterministicSeed deterministicSeed = this.seed;
        if (deterministicSeed != null) {
            if (deterministicSeed.isEncrypted()) {
                sb.append("Seed is encrypted\n");
            } else if (z) {
                List<String> mnemonicCode = this.seed.getMnemonicCode();
                sb.append("Seed as words: ");
                sb.append(Utils.join(mnemonicCode));
                sb.append('\n');
                sb.append("Seed as hex:   ");
                sb.append(this.seed.toHexString());
                sb.append('\n');
            }
            sb.append("Seed birthday: ");
            sb.append(this.seed.getCreationTimeSeconds());
            sb.append("  [");
            sb.append(Utils.dateTimeFormat(this.seed.getCreationTimeSeconds() * 1000));
            sb.append("]\n");
        } else {
            sb.append("Key birthday:  ");
            sb.append(watchingKey.getCreationTimeSeconds());
            sb.append("  [");
            sb.append(Utils.dateTimeFormat(watchingKey.getCreationTimeSeconds() * 1000));
            sb.append("]\n");
        }
        sb.append("Key to watch:  ");
        sb.append(watchingKey.serializePubB58(networkParameters));
        sb.append('\n');
        formatAddresses(z, networkParameters, sb);
        return sb.toString();
    }

    protected void formatAddresses(boolean z, NetworkParameters networkParameters, StringBuilder sb) {
        Iterator<ECKey> it = getKeys(false).iterator();
        while (it.hasNext()) {
            it.next().formatKeyWithAddress(z, sb, networkParameters);
        }
    }

    public void setSigsRequiredToSpend(int i) {
        this.sigsRequiredToSpend = i;
    }

    public int getSigsRequiredToSpend() {
        return this.sigsRequiredToSpend;
    }
}