正在查看: imToken v3.28.8 应用的 Wallet.java JAVA 源代码文件
本页面展示 JAVA 反编译生成的源代码文件,支持语法高亮显示。 仅供安全研究与技术分析使用,严禁用于任何非法用途。请遵守相关法律法规。
正在查看: imToken v3.28.8 应用的 Wallet.java JAVA 源代码文件
本页面展示 JAVA 反编译生成的源代码文件,支持语法高亮显示。 仅供安全研究与技术分析使用,严禁用于任何非法用途。请遵守相关法律法规。
package org.bitcoinj.wallet;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.ByteString;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Thread;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import kotlin.jvm.internal.LongCompanionObject;
import net.jcip.annotations.GuardedBy;
import org.bitcoinj.core.AbstractBlockChain;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.BloomFilter;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Context;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.FilteredBlock;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.PeerFilterProvider;
import org.bitcoinj.core.ScriptException;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionBag;
import org.bitcoinj.core.TransactionBroadcast;
import org.bitcoinj.core.TransactionBroadcaster;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.UTXO;
import org.bitcoinj.core.UTXOProvider;
import org.bitcoinj.core.UTXOProviderException;
import org.bitcoinj.core.Utils;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.core.listeners.NewBestBlockListener;
import org.bitcoinj.core.listeners.ReorganizeListener;
import org.bitcoinj.core.listeners.TransactionConfidenceEventListener;
import org.bitcoinj.core.listeners.TransactionReceivedInBlockListener;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptChunk;
import org.bitcoinj.signers.LocalTransactionSigner;
import org.bitcoinj.signers.MissingSigResolutionSigner;
import org.bitcoinj.signers.TransactionSigner;
import org.bitcoinj.utils.BaseTaggableObject;
import org.bitcoinj.utils.ListenerRegistration;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.KeyChain;
import org.bitcoinj.wallet.Protos;
import org.bitcoinj.wallet.RiskAnalysis;
import org.bitcoinj.wallet.WalletFiles;
import org.bitcoinj.wallet.WalletTransaction;
import org.bitcoinj.wallet.listeners.KeyChainEventListener;
import org.bitcoinj.wallet.listeners.ScriptsChangeEventListener;
import org.bitcoinj.wallet.listeners.WalletChangeEventListener;
import org.bitcoinj.wallet.listeners.WalletCoinsReceivedEventListener;
import org.bitcoinj.wallet.listeners.WalletCoinsSentEventListener;
import org.bitcoinj.wallet.listeners.WalletEventListener;
import org.bitcoinj.wallet.listeners.WalletReorganizeEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;
public class Wallet extends BaseTaggableObject implements NewBestBlockListener, TransactionReceivedInBlockListener, PeerFilterProvider, KeyBag, TransactionBag, ReorganizeListener {
private static final int MINIMUM_BLOOM_DATA_LENGTH = 8;
private static final Logger log = LoggerFactory.getLogger(Wallet.class);
private boolean acceptRiskyTransactions;
@GuardedBy("lock")
private List<BalanceFutureRequest> balanceFutureRequests;
private final AtomicInteger bloomFilterGuard;
private final ArrayList<TransactionOutPoint> bloomOutPoints;
private final CopyOnWriteArrayList<ListenerRegistration<WalletChangeEventListener>> changeListeners;
protected CoinSelector coinSelector;
private final CopyOnWriteArrayList<ListenerRegistration<WalletCoinsReceivedEventListener>> coinsReceivedListeners;
private final CopyOnWriteArrayList<ListenerRegistration<WalletCoinsSentEventListener>> coinsSentListeners;
private Map<Transaction, TransactionConfidence.Listener.ChangeReason> confidenceChanged;
protected final Context context;
private final Map<Sha256Hash, Transaction> dead;
private String description;
private final HashMap<String, WalletExtension> extensions;
private boolean hardSaveOnNextBlock;
private HashSet<Sha256Hash> ignoreNextNewBlock;
private boolean insideReorg;
@GuardedBy("keyChainGroupLock")
private KeyChainGroup keyChainGroup;
protected final ReentrantLock keyChainGroupLock;
@Nullable
private Sha256Hash lastBlockSeenHash;
private int lastBlockSeenHeight;
private long lastBlockSeenTimeSecs;
protected final ReentrantLock lock;
protected final HashSet<TransactionOutput> myUnspents;
private int onWalletChangedSuppressions;
protected final NetworkParameters params;
private final Map<Sha256Hash, Transaction> pending;
private final CopyOnWriteArrayList<ListenerRegistration<WalletReorganizeEventListener>> reorganizeListeners;
private RiskAnalysis.Analyzer riskAnalyzer;
private final LinkedHashMap<Sha256Hash, Transaction> riskDropped;
private final CopyOnWriteArrayList<ListenerRegistration<ScriptsChangeEventListener>> scriptChangeListeners;
@GuardedBy("lock")
private List<TransactionSigner> signers;
private final Map<Sha256Hash, Transaction> spent;
private final CopyOnWriteArrayList<ListenerRegistration<TransactionConfidenceEventListener>> transactionConfidenceListeners;
protected final Map<Sha256Hash, Transaction> transactions;
private TransactionConfidence.Listener txConfidenceListener;
private final Map<Sha256Hash, Transaction> unspent;
protected volatile WalletFiles vFileManager;
private volatile long vKeyRotationTimestamp;
protected volatile TransactionBroadcaster vTransactionBroadcaster;
@Nullable
private volatile UTXOProvider vUTXOProvider;
private int version;
@GuardedBy("keyChainGroupLock")
private Set<Script> watchedScripts;
public enum BalanceType {
ESTIMATED,
AVAILABLE,
ESTIMATED_SPENDABLE,
AVAILABLE_SPENDABLE
}
public static class CompletionException extends RuntimeException {
}
public static class CouldNotAdjustDownwards extends CompletionException {
}
public static class DustySendRequested extends CompletionException {
}
public static class ExceededMaxTransactionSize extends CompletionException {
}
public enum MissingSigsMode {
USE_OP_ZERO,
USE_DUMMY_SIG,
THROW
}
public static class MultipleOpReturnRequested extends CompletionException {
}
public static class SendResult {
public TransactionBroadcast broadcast;
public ListenableFuture<Transaction> broadcastComplete;
public Transaction tx;
}
public Wallet(NetworkParameters networkParameters) {
this(Context.getOrCreate(networkParameters));
}
public Wallet(Context context) {
this(context, new KeyChainGroup(context.getParams()));
}
public static Wallet fromSeed(NetworkParameters networkParameters, DeterministicSeed deterministicSeed) {
return new Wallet(networkParameters, new KeyChainGroup(networkParameters, deterministicSeed));
}
public static Wallet fromWatchingKey(NetworkParameters networkParameters, DeterministicKey deterministicKey) {
return new Wallet(networkParameters, new KeyChainGroup(networkParameters, deterministicKey));
}
public static Wallet fromWatchingKeyB58(NetworkParameters networkParameters, String str, long j) {
DeterministicKey deserializeB58 = DeterministicKey.deserializeB58(null, str, networkParameters);
deserializeB58.setCreationTimeSeconds(j);
return fromWatchingKey(networkParameters, deserializeB58);
}
public static Wallet fromKeys(NetworkParameters networkParameters, List<ECKey> list) {
Iterator<ECKey> it = list.iterator();
while (it.hasNext()) {
Preconditions.checkArgument(!(it.next() instanceof DeterministicKey));
}
KeyChainGroup keyChainGroup = new KeyChainGroup(networkParameters);
keyChainGroup.importKeys(list);
return new Wallet(networkParameters, keyChainGroup);
}
public Wallet(NetworkParameters networkParameters, KeyChainGroup keyChainGroup) {
this(Context.getOrCreate(networkParameters), keyChainGroup);
}
private Wallet(Context context, KeyChainGroup keyChainGroup) {
this.lock = Threading.lock("wallet");
this.keyChainGroupLock = Threading.lock("wallet-keychaingroup");
this.myUnspents = Sets.newHashSet();
this.riskDropped = new LinkedHashMap<Sha256Hash, Transaction>() {
@Override
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, Transaction> entry) {
return size() > 1000;
}
};
this.changeListeners = new CopyOnWriteArrayList<>();
this.coinsReceivedListeners = new CopyOnWriteArrayList<>();
this.coinsSentListeners = new CopyOnWriteArrayList<>();
this.reorganizeListeners = new CopyOnWriteArrayList<>();
this.scriptChangeListeners = new CopyOnWriteArrayList<>();
this.transactionConfidenceListeners = new CopyOnWriteArrayList<>();
this.riskAnalyzer = DefaultRiskAnalysis.FACTORY;
this.coinSelector = new DefaultCoinSelector();
this.hardSaveOnNextBlock = false;
this.balanceFutureRequests = Lists.newLinkedList();
this.bloomOutPoints = Lists.newArrayList();
this.bloomFilterGuard = new AtomicInteger(0);
this.context = context;
this.params = context.getParams();
this.keyChainGroup = (KeyChainGroup) Preconditions.checkNotNull(keyChainGroup);
if (this.params.getId().equals(NetworkParameters.ID_UNITTESTNET)) {
this.keyChainGroup.setLookaheadSize(5);
}
if (this.keyChainGroup.numKeys() == 0) {
this.keyChainGroup.createAndActivateNewHDChain();
}
this.watchedScripts = Sets.newHashSet();
this.unspent = new HashMap();
this.spent = new HashMap();
this.pending = new HashMap();
this.dead = new HashMap();
this.transactions = new HashMap();
this.extensions = new HashMap<>();
this.confidenceChanged = new LinkedHashMap();
this.signers = new ArrayList();
addTransactionSigner(new LocalTransactionSigner());
createTransientState();
}
private void createTransientState() {
this.ignoreNextNewBlock = new HashSet<>();
this.txConfidenceListener = new TransactionConfidence.Listener() {
@Override
public void onConfidenceChanged(TransactionConfidence transactionConfidence, TransactionConfidence.Listener.ChangeReason changeReason) {
if (changeReason == TransactionConfidence.Listener.ChangeReason.SEEN_PEERS) {
Wallet.this.lock.lock();
try {
Wallet.this.checkBalanceFuturesLocked(null);
Wallet.this.queueOnTransactionConfidenceChanged(Wallet.this.getTransaction(transactionConfidence.getTransactionHash()));
Wallet.this.maybeQueueOnWalletChanged();
} finally {
Wallet.this.lock.unlock();
}
}
}
};
this.acceptRiskyTransactions = false;
}
public NetworkParameters getNetworkParameters() {
return this.params;
}
public DeterministicKeyChain getActiveKeyChain() {
return this.keyChainGroup.getActiveKeyChain();
}
public final void addTransactionSigner(TransactionSigner transactionSigner) {
this.lock.lock();
try {
if (transactionSigner.isReady()) {
this.signers.add(transactionSigner);
return;
}
throw new IllegalStateException("Signer instance is not ready to be added into Wallet: " + transactionSigner.getClass());
} finally {
this.lock.unlock();
}
}
public List<TransactionSigner> getTransactionSigners() {
this.lock.lock();
try {
return ImmutableList.copyOf(this.signers);
} finally {
this.lock.unlock();
}
}
public DeterministicKey currentKey(KeyChain.KeyPurpose keyPurpose) {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
return this.keyChainGroup.currentKey(keyPurpose);
} finally {
this.keyChainGroupLock.unlock();
}
}
public DeterministicKey currentReceiveKey() {
return currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
}
public Address currentAddress(KeyChain.KeyPurpose keyPurpose) {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
return this.keyChainGroup.currentAddress(keyPurpose);
} finally {
this.keyChainGroupLock.unlock();
}
}
public Address currentReceiveAddress() {
return currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS);
}
public DeterministicKey freshKey(KeyChain.KeyPurpose keyPurpose) {
return freshKeys(keyPurpose, 1).get(0);
}
public List<DeterministicKey> freshKeys(KeyChain.KeyPurpose keyPurpose, int i) {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
List<DeterministicKey> freshKeys = this.keyChainGroup.freshKeys(keyPurpose, i);
this.keyChainGroupLock.unlock();
saveNow();
return freshKeys;
} catch (Throwable th) {
this.keyChainGroupLock.unlock();
throw th;
}
}
public DeterministicKey freshReceiveKey() {
return freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
}
public Address freshAddress(KeyChain.KeyPurpose keyPurpose) {
this.keyChainGroupLock.lock();
try {
Address freshAddress = this.keyChainGroup.freshAddress(keyPurpose);
this.keyChainGroupLock.unlock();
saveNow();
return freshAddress;
} catch (Throwable th) {
this.keyChainGroupLock.unlock();
throw th;
}
}
public Address freshReceiveAddress() {
return freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS);
}
public List<ECKey> getIssuedReceiveKeys() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.getActiveKeyChain().getIssuedReceiveKeys();
} finally {
this.keyChainGroupLock.unlock();
}
}
public List<Address> getIssuedReceiveAddresses() {
List<ECKey> issuedReceiveKeys = getIssuedReceiveKeys();
ArrayList arrayList = new ArrayList(issuedReceiveKeys.size());
Iterator<ECKey> it = issuedReceiveKeys.iterator();
while (it.hasNext()) {
arrayList.add(it.next().toAddress(getParams()));
}
return arrayList;
}
public void upgradeToDeterministic(@Nullable KeyParameter keyParameter) throws DeterministicUpgradeRequiresPassword {
this.keyChainGroupLock.lock();
try {
this.keyChainGroup.upgradeToDeterministic(this.vKeyRotationTimestamp, keyParameter);
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean isDeterministicUpgradeRequired() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.isDeterministicUpgradeRequired();
} finally {
this.keyChainGroupLock.unlock();
}
}
private void maybeUpgradeToHD() throws DeterministicUpgradeRequiresPassword {
maybeUpgradeToHD(null);
}
@GuardedBy("keyChainGroupLock")
private void maybeUpgradeToHD(@Nullable KeyParameter keyParameter) throws DeterministicUpgradeRequiresPassword {
Preconditions.checkState(this.keyChainGroupLock.isHeldByCurrentThread());
if (this.keyChainGroup.isDeterministicUpgradeRequired()) {
log.info("Upgrade to HD wallets is required, attempting to do so.");
try {
upgradeToDeterministic(keyParameter);
} catch (DeterministicUpgradeRequiresPassword e) {
log.error("Failed to auto upgrade due to encryption. You should call wallet.upgradeToDeterministic with the users AES key to avoid this error.");
throw e;
}
}
}
public List<Script> getWatchedScripts() {
this.keyChainGroupLock.lock();
try {
return new ArrayList(this.watchedScripts);
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean removeKey(ECKey eCKey) {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.removeImportedKey(eCKey);
} finally {
this.keyChainGroupLock.unlock();
}
}
public int getKeyChainGroupSize() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.numKeys();
} finally {
this.keyChainGroupLock.unlock();
}
}
public int getKeyChainGroupCombinedKeyLookaheadEpochs() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.getCombinedKeyLookaheadEpochs();
} finally {
this.keyChainGroupLock.unlock();
}
}
public List<ECKey> getImportedKeys() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.getImportedKeys();
} finally {
this.keyChainGroupLock.unlock();
}
}
public Address currentChangeAddress() {
return currentAddress(KeyChain.KeyPurpose.CHANGE);
}
public Address getChangeAddress() {
return currentChangeAddress();
}
@Deprecated
public boolean addKey(ECKey eCKey) {
return importKey(eCKey);
}
public boolean importKey(ECKey eCKey) {
return importKeys(Lists.newArrayList(new ECKey[]{eCKey})) == 1;
}
@Deprecated
public int addKeys(List<ECKey> list) {
return importKeys(list);
}
public int importKeys(List<ECKey> list) {
checkNoDeterministicKeys(list);
this.keyChainGroupLock.lock();
try {
int importKeys = this.keyChainGroup.importKeys(list);
this.keyChainGroupLock.unlock();
saveNow();
return importKeys;
} catch (Throwable th) {
this.keyChainGroupLock.unlock();
throw th;
}
}
private void checkNoDeterministicKeys(List<ECKey> list) {
Iterator<ECKey> it = list.iterator();
while (it.hasNext()) {
if (it.next() instanceof DeterministicKey) {
throw new IllegalArgumentException("Cannot import HD keys back into the wallet");
}
}
}
public int importKeysAndEncrypt(List<ECKey> list, CharSequence charSequence) {
this.keyChainGroupLock.lock();
try {
Preconditions.checkNotNull(getKeyCrypter(), "Wallet is not encrypted");
return importKeysAndEncrypt(list, getKeyCrypter().deriveKey(charSequence));
} finally {
this.keyChainGroupLock.unlock();
}
}
public int importKeysAndEncrypt(List<ECKey> list, KeyParameter keyParameter) {
this.keyChainGroupLock.lock();
try {
checkNoDeterministicKeys(list);
return this.keyChainGroup.importKeysAndEncrypt(list, keyParameter);
} finally {
this.keyChainGroupLock.unlock();
}
}
public void addAndActivateHDChain(DeterministicKeyChain deterministicKeyChain) {
this.keyChainGroupLock.lock();
try {
this.keyChainGroup.addAndActivateHDChain(deterministicKeyChain);
} finally {
this.keyChainGroupLock.unlock();
}
}
public void setKeyChainGroupLookaheadSize(int i) {
this.keyChainGroupLock.lock();
try {
this.keyChainGroup.setLookaheadSize(i);
} finally {
this.keyChainGroupLock.unlock();
}
}
public int getKeyChainGroupLookaheadSize() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.getLookaheadSize();
} finally {
this.keyChainGroupLock.unlock();
}
}
public void setKeyChainGroupLookaheadThreshold(int i) {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
this.keyChainGroup.setLookaheadThreshold(i);
} finally {
this.keyChainGroupLock.unlock();
}
}
public int getKeyChainGroupLookaheadThreshold() {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
return this.keyChainGroup.getLookaheadThreshold();
} finally {
this.keyChainGroupLock.unlock();
}
}
public DeterministicKey getWatchingKey() {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
return this.keyChainGroup.getActiveKeyChain().getWatchingKey();
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean isWatching() {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
return this.keyChainGroup.isWatching();
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean isAddressWatched(Address address) {
return isWatchedScript(ScriptBuilder.createOutputScript(address));
}
public boolean addWatchedAddress(Address address) {
return addWatchedAddresses(Lists.newArrayList(new Address[]{address}), Utils.currentTimeMillis() / 1000) == 1;
}
public boolean addWatchedAddress(Address address, long j) {
return addWatchedAddresses(Lists.newArrayList(new Address[]{address}), j) == 1;
}
public int addWatchedAddresses(List<Address> list, long j) {
ArrayList newArrayList = Lists.newArrayList();
Iterator<Address> it = list.iterator();
while (it.hasNext()) {
Script createOutputScript = ScriptBuilder.createOutputScript(it.next());
createOutputScript.setCreationTimeSeconds(j);
newArrayList.add(createOutputScript);
}
return addWatchedScripts(newArrayList);
}
public int addWatchedScripts(List<Script> list) {
this.keyChainGroupLock.lock();
try {
int i = 0;
for (Script script : list) {
if (this.watchedScripts.contains(script)) {
this.watchedScripts.remove(script);
}
if (script.getCreationTimeSeconds() == 0) {
log.warn("Adding a script to the wallet with a creation time of zero, this will disable the checkpointing optimization! {}", script);
}
this.watchedScripts.add(script);
i++;
}
if (i > 0) {
queueOnScriptsChanged(list, true);
saveNow();
}
return i;
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean removeWatchedAddress(Address address) {
return removeWatchedAddresses(ImmutableList.of(address));
}
public boolean removeWatchedAddresses(List<Address> list) {
ArrayList newArrayList = Lists.newArrayList();
Iterator<Address> it = list.iterator();
while (it.hasNext()) {
newArrayList.add(ScriptBuilder.createOutputScript(it.next()));
}
return removeWatchedScripts(newArrayList);
}
public boolean removeWatchedScripts(List<Script> list) {
this.lock.lock();
try {
for (Script script : list) {
if (this.watchedScripts.contains(script)) {
this.watchedScripts.remove(script);
}
}
queueOnScriptsChanged(list, false);
saveNow();
return true;
} finally {
this.lock.unlock();
}
}
public List<Address> getWatchedAddresses() {
this.keyChainGroupLock.lock();
try {
LinkedList linkedList = new LinkedList();
for (Script script : this.watchedScripts) {
if (script.isSentToAddress()) {
linkedList.add(script.getToAddress(this.params));
}
}
return linkedList;
} finally {
this.keyChainGroupLock.unlock();
}
}
@Override
@Nullable
public ECKey findKeyFromPubHash(byte[] bArr) {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.findKeyFromPubHash(bArr);
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean hasKey(ECKey eCKey) {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.hasKey(eCKey);
} finally {
this.keyChainGroupLock.unlock();
}
}
@Override
public boolean isPubKeyHashMine(byte[] bArr) {
return findKeyFromPubHash(bArr) != null;
}
@Override
public boolean isWatchedScript(Script script) {
this.keyChainGroupLock.lock();
try {
return this.watchedScripts.contains(script);
} finally {
this.keyChainGroupLock.unlock();
}
}
@Override
@Nullable
public ECKey findKeyFromPubKey(byte[] bArr) {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.findKeyFromPubKey(bArr);
} finally {
this.keyChainGroupLock.unlock();
}
}
@Override
public boolean isPubKeyMine(byte[] bArr) {
return findKeyFromPubKey(bArr) != null;
}
@Override
@Nullable
public RedeemData findRedeemDataFromScriptHash(byte[] bArr) {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.findRedeemDataFromScriptHash(bArr);
} finally {
this.keyChainGroupLock.unlock();
}
}
@Override
public boolean isPayToScriptHashMine(byte[] bArr) {
return findRedeemDataFromScriptHash(bArr) != null;
}
private void markKeysAsUsed(Transaction transaction) {
this.keyChainGroupLock.lock();
try {
Iterator<TransactionOutput> it = transaction.getOutputs().iterator();
while (it.hasNext()) {
try {
Script scriptPubKey = it.next().getScriptPubKey();
if (scriptPubKey.isSentToRawPubKey()) {
this.keyChainGroup.markPubKeyAsUsed(scriptPubKey.getPubKey());
} else if (scriptPubKey.isSentToAddress()) {
this.keyChainGroup.markPubKeyHashAsUsed(scriptPubKey.getPubKeyHash());
} else if (scriptPubKey.isPayToScriptHash()) {
this.keyChainGroup.markP2SHAddressAsUsed(Address.fromP2SHScript(transaction.getParams(), scriptPubKey));
}
} catch (ScriptException e) {
log.warn("Could not parse tx output script: {}", e.toString());
}
}
} finally {
this.keyChainGroupLock.unlock();
}
}
public DeterministicSeed getKeyChainSeed() {
this.keyChainGroupLock.lock();
try {
DeterministicSeed seed = this.keyChainGroup.getActiveKeyChain().getSeed();
if (seed != null) {
return seed;
}
throw new ECKey.MissingPrivateKeyException();
} finally {
this.keyChainGroupLock.unlock();
}
}
public DeterministicKey getKeyByPath(List<ChildNumber> list) {
this.keyChainGroupLock.lock();
try {
maybeUpgradeToHD();
return this.keyChainGroup.getActiveKeyChain().getKeyByPath(list, false);
} finally {
this.keyChainGroupLock.unlock();
}
}
public void encrypt(CharSequence charSequence) {
this.keyChainGroupLock.lock();
try {
KeyCrypterScrypt keyCrypterScrypt = new KeyCrypterScrypt();
this.keyChainGroup.encrypt(keyCrypterScrypt, keyCrypterScrypt.deriveKey(charSequence));
this.keyChainGroupLock.unlock();
saveNow();
} catch (Throwable th) {
this.keyChainGroupLock.unlock();
throw th;
}
}
public void encrypt(KeyCrypter keyCrypter, KeyParameter keyParameter) {
this.keyChainGroupLock.lock();
try {
this.keyChainGroup.encrypt(keyCrypter, keyParameter);
this.keyChainGroupLock.unlock();
saveNow();
} catch (Throwable th) {
this.keyChainGroupLock.unlock();
throw th;
}
}
public void decrypt(CharSequence charSequence) {
this.keyChainGroupLock.lock();
try {
KeyCrypter keyCrypter = this.keyChainGroup.getKeyCrypter();
Preconditions.checkState(keyCrypter != null, "Not encrypted");
this.keyChainGroup.decrypt(keyCrypter.deriveKey(charSequence));
this.keyChainGroupLock.unlock();
saveNow();
} catch (Throwable th) {
this.keyChainGroupLock.unlock();
throw th;
}
}
public void decrypt(KeyParameter keyParameter) {
this.keyChainGroupLock.lock();
try {
this.keyChainGroup.decrypt(keyParameter);
this.keyChainGroupLock.unlock();
saveNow();
} catch (Throwable th) {
this.keyChainGroupLock.unlock();
throw th;
}
}
public boolean checkPassword(CharSequence charSequence) {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.checkPassword(charSequence);
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean checkAESKey(KeyParameter keyParameter) {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.checkAESKey(keyParameter);
} finally {
this.keyChainGroupLock.unlock();
}
}
@Nullable
public KeyCrypter getKeyCrypter() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.getKeyCrypter();
} finally {
this.keyChainGroupLock.unlock();
}
}
public Protos.Wallet.EncryptionType getEncryptionType() {
Protos.Wallet.EncryptionType encryptionType;
this.keyChainGroupLock.lock();
try {
KeyCrypter keyCrypter = this.keyChainGroup.getKeyCrypter();
if (keyCrypter != null) {
encryptionType = keyCrypter.getUnderstoodEncryptionType();
} else {
encryptionType = Protos.Wallet.EncryptionType.UNENCRYPTED;
}
return encryptionType;
} finally {
this.keyChainGroupLock.unlock();
}
}
public boolean isEncrypted() {
return getEncryptionType() != Protos.Wallet.EncryptionType.UNENCRYPTED;
}
public void changeEncryptionPassword(CharSequence charSequence, CharSequence charSequence2) {
this.keyChainGroupLock.lock();
try {
decrypt(charSequence);
encrypt(charSequence2);
} finally {
this.keyChainGroupLock.unlock();
}
}
public void changeEncryptionKey(KeyCrypter keyCrypter, KeyParameter keyParameter, KeyParameter keyParameter2) {
this.keyChainGroupLock.lock();
try {
decrypt(keyParameter);
encrypt(keyCrypter, keyParameter2);
} finally {
this.keyChainGroupLock.unlock();
}
}
public List<Protos.Key> serializeKeyChainGroupToProtobuf() {
this.keyChainGroupLock.lock();
try {
return this.keyChainGroup.serializeToProtobuf();
} finally {
this.keyChainGroupLock.unlock();
}
}
public void saveToFile(File file, File file2) throws IOException {
FileOutputStream fileOutputStream;
this.lock.lock();
FileOutputStream fileOutputStream2 = null;
try {
try {
fileOutputStream = new FileOutputStream(file);
} catch (Throwable th) {
th = th;
}
} catch (RuntimeException e) {
e = e;
}
try {
saveToFileStream(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.getFD().sync();
fileOutputStream.close();
if (Utils.isWindows()) {
File canonicalFile = file2.getCanonicalFile();
if (canonicalFile.exists() && !canonicalFile.delete()) {
throw new IOException("Failed to delete canonical wallet file for replacement with autosave");
}
if (!file.renameTo(canonicalFile)) {
throw new IOException("Failed to rename " + file + " to " + canonicalFile);
}
this.lock.unlock();
if (file.exists()) {
log.warn("Temp file still exists after failed save.");
return;
}
return;
}
if (!file.renameTo(file2)) {
throw new IOException("Failed to rename " + file + " to " + file2);
}
this.lock.unlock();
if (file.exists()) {
log.warn("Temp file still exists after failed save.");
}
} catch (RuntimeException e2) {
e = e2;
log.error("Failed whilst saving wallet", e);
throw e;
} catch (Throwable th2) {
th = th2;
fileOutputStream2 = fileOutputStream;
this.lock.unlock();
if (fileOutputStream2 != null) {
fileOutputStream2.close();
}
if (file.exists()) {
log.warn("Temp file still exists after failed save.");
}
throw th;
}
}
public void saveToFile(File file) throws IOException {
saveToFile(File.createTempFile("wallet", null, file.getAbsoluteFile().getParentFile()), file);
}
public void setAcceptRiskyTransactions(boolean z) {
this.lock.lock();
try {
this.acceptRiskyTransactions = z;
} finally {
this.lock.unlock();
}
}
public boolean isAcceptRiskyTransactions() {
this.lock.lock();
try {
return this.acceptRiskyTransactions;
} finally {
this.lock.unlock();
}
}
public void setRiskAnalyzer(RiskAnalysis.Analyzer analyzer) {
this.lock.lock();
try {
this.riskAnalyzer = (RiskAnalysis.Analyzer) Preconditions.checkNotNull(analyzer);
} finally {
this.lock.unlock();
}
}
public RiskAnalysis.Analyzer getRiskAnalyzer() {
this.lock.lock();
try {
return this.riskAnalyzer;
} finally {
this.lock.unlock();
}
}
public WalletFiles autosaveToFile(File file, long j, TimeUnit timeUnit, @Nullable WalletFiles.Listener listener) {
this.lock.lock();
try {
Preconditions.checkState(this.vFileManager == null, "Already auto saving this wallet.");
WalletFiles walletFiles = new WalletFiles(this, file, j, timeUnit);
if (listener != null) {
walletFiles.setListener(listener);
}
this.vFileManager = walletFiles;
return walletFiles;
} finally {
this.lock.unlock();
}
}
public void shutdownAutosaveAndWait() {
this.lock.lock();
try {
WalletFiles walletFiles = this.vFileManager;
this.vFileManager = null;
Preconditions.checkState(walletFiles != null, "Auto saving not enabled.");
walletFiles.shutdownAndWait();
} finally {
this.lock.unlock();
}
}
protected void saveLater() {
WalletFiles walletFiles = this.vFileManager;
if (walletFiles != null) {
walletFiles.saveLater();
}
}
protected void saveNow() {
WalletFiles walletFiles = this.vFileManager;
if (walletFiles != null) {
try {
walletFiles.saveNow();
} catch (IOException e) {
log.error("Failed to save wallet to disk!", e);
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Threading.uncaughtExceptionHandler;
if (uncaughtExceptionHandler != null) {
uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e);
}
}
}
}
public void saveToFileStream(OutputStream outputStream) throws IOException {
this.lock.lock();
try {
new WalletProtobufSerializer().writeWallet(this, outputStream);
} finally {
this.lock.unlock();
}
}
public NetworkParameters getParams() {
return this.params;
}
public Context getContext() {
return this.context;
}
public static Wallet loadFromFile(File file, @Nullable WalletExtension... walletExtensionArr) throws UnreadableWalletException {
FileInputStream fileInputStream;
FileInputStream fileInputStream2 = null;
try {
try {
fileInputStream = new FileInputStream(file);
} catch (Throwable th) {
th = th;
}
try {
Wallet loadFromFileStream = loadFromFileStream(fileInputStream, walletExtensionArr);
fileInputStream.close();
return loadFromFileStream;
} catch (Throwable th2) {
th = th2;
fileInputStream2 = fileInputStream;
if (fileInputStream2 != null) {
fileInputStream2.close();
}
throw th;
}
} catch (IOException e) {
throw new UnreadableWalletException("Could not open file", e);
}
}
public boolean isConsistent() {
try {
isConsistentOrThrow();
return true;
} catch (IllegalStateException e) {
log.error(e.getMessage());
try {
log.error(toString());
return false;
} catch (RuntimeException e2) {
log.error("Printing inconsistent wallet failed", e2);
return false;
}
}
}
public void isConsistentOrThrow() throws IllegalStateException {
this.lock.lock();
try {
Set<Transaction> transactions = getTransactions(true);
HashSet hashSet = new HashSet();
Iterator<Transaction> it = transactions.iterator();
while (it.hasNext()) {
hashSet.add(it.next().getHash());
}
int size = transactions.size();
if (size != hashSet.size()) {
throw new IllegalStateException("Two transactions with same hash");
}
int size2 = this.unspent.size() + this.spent.size() + this.pending.size() + this.dead.size();
if (size != size2) {
throw new IllegalStateException("Inconsistent wallet sizes: " + size + ", " + size2);
}
for (Transaction transaction : this.unspent.values()) {
if (!isTxConsistent(transaction, false)) {
throw new IllegalStateException("Inconsistent unspent tx: " + transaction.getHashAsString());
}
}
for (Transaction transaction2 : this.spent.values()) {
if (!isTxConsistent(transaction2, true)) {
throw new IllegalStateException("Inconsistent spent tx: " + transaction2.getHashAsString());
}
}
} finally {
this.lock.unlock();
}
}
boolean isTxConsistent(Transaction transaction, boolean z) {
boolean z2 = true;
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
if (transactionOutput.isAvailableForSpending()) {
if (transactionOutput.isMineOrWatched(this)) {
z2 = false;
}
if (transactionOutput.getSpentBy() != null) {
log.error("isAvailableForSpending != spentBy");
return false;
}
} else if (transactionOutput.getSpentBy() == null) {
log.error("isAvailableForSpending != spentBy");
return false;
}
}
return z2 == z;
}
public static Wallet loadFromFileStream(InputStream inputStream, @Nullable WalletExtension... walletExtensionArr) throws UnreadableWalletException {
Wallet readWallet = new WalletProtobufSerializer().readWallet(inputStream, walletExtensionArr);
if (!readWallet.isConsistent()) {
log.error("Loaded an inconsistent wallet");
}
return readWallet;
}
@Override
public boolean notifyTransactionIsInBlock(Sha256Hash sha256Hash, StoredBlock storedBlock, AbstractBlockChain.NewBlockType newBlockType, int i) throws VerificationException {
boolean z;
this.lock.lock();
try {
Transaction transaction = this.transactions.get(sha256Hash);
if (transaction == null) {
transaction = this.riskDropped.get(sha256Hash);
if (transaction == null) {
z = false;
return z;
}
log.info("Risk analysis dropped tx {} but was included in block anyway", transaction.getHash());
}
receive(transaction, storedBlock, newBlockType, i);
z = true;
return z;
} finally {
this.lock.unlock();
}
}
public void receivePending(Transaction transaction, @Nullable List<Transaction> list, boolean z) throws VerificationException {
this.lock.lock();
try {
transaction.verify();
if (!getContainingPools(transaction).equals(EnumSet.noneOf(WalletTransaction.Pool.class))) {
log.debug("Received tx we already saw in a block or created ourselves: " + transaction.getHashAsString());
} else if (z || isPendingTransactionRelevant(transaction)) {
if (isTransactionRisky(transaction, list) && !this.acceptRiskyTransactions) {
this.riskDropped.put(transaction.getHash(), transaction);
log.warn("There are now {} risk dropped transactions being kept in memory", Integer.valueOf(this.riskDropped.size()));
} else {
Coin valueSentToMe = transaction.getValueSentToMe(this);
Coin valueSentFromMe = transaction.getValueSentFromMe(this);
if (log.isInfoEnabled()) {
log.info(String.format(Locale.US, "Received a pending transaction %s that spends %s from our own wallet, and sends us %s", transaction.getHashAsString(), valueSentFromMe.toFriendlyString(), valueSentToMe.toFriendlyString()));
}
if (transaction.getConfidence().getSource().equals(TransactionConfidence.Source.UNKNOWN)) {
log.warn("Wallet received transaction with an unknown source. Consider tagging it!");
}
commitTx(transaction);
}
}
} finally {
this.lock.unlock();
}
}
public boolean isTransactionRisky(Transaction transaction, @Nullable List<Transaction> list) {
boolean z;
this.lock.lock();
if (list == null) {
try {
list = ImmutableList.of();
} finally {
this.lock.unlock();
}
}
RiskAnalysis create = this.riskAnalyzer.create(this, transaction, list);
if (create.analyze() != RiskAnalysis.Result.OK) {
log.warn("Pending transaction was considered risky: {}\n{}", create, transaction);
z = true;
} else {
z = false;
}
return z;
}
public void receivePending(Transaction transaction, @Nullable List<Transaction> list) throws VerificationException {
receivePending(transaction, list, false);
}
public boolean isPendingTransactionRelevant(Transaction transaction) throws ScriptException {
this.lock.lock();
try {
if (!getContainingPools(transaction).equals(EnumSet.noneOf(WalletTransaction.Pool.class))) {
log.debug("Received tx we already saw in a block or created ourselves: " + transaction.getHashAsString());
} else if (!isTransactionRelevant(transaction)) {
log.debug("Received tx that isn't relevant to this wallet, discarding.");
} else {
return true;
}
return false;
} finally {
this.lock.unlock();
}
}
public boolean isTransactionRelevant(Transaction transaction) throws ScriptException {
boolean z;
this.lock.lock();
try {
if (transaction.getValueSentFromMe(this).signum() <= 0 && transaction.getValueSentToMe(this).signum() <= 0) {
if (findDoubleSpendsAgainst(transaction, this.transactions).isEmpty()) {
z = false;
return z;
}
}
z = true;
return z;
} finally {
this.lock.unlock();
}
}
private Set<Transaction> findDoubleSpendsAgainst(Transaction transaction, Map<Sha256Hash, Transaction> map) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
if (transaction.isCoinBase()) {
return Sets.newHashSet();
}
HashSet hashSet = new HashSet();
Iterator<TransactionInput> it = transaction.getInputs().iterator();
while (it.hasNext()) {
hashSet.add(it.next().getOutpoint());
}
HashSet newHashSet = Sets.newHashSet();
for (Transaction transaction2 : map.values()) {
Iterator<TransactionInput> it2 = transaction2.getInputs().iterator();
while (it2.hasNext()) {
if (hashSet.contains(it2.next().getOutpoint())) {
newHashSet.add(transaction2);
}
}
}
return newHashSet;
}
void addTransactionsDependingOn(Set<Transaction> set, Set<Transaction> set2) {
LinkedHashMap linkedHashMap = new LinkedHashMap();
for (Transaction transaction : set) {
linkedHashMap.put(transaction.getHash(), transaction);
}
while (!linkedHashMap.isEmpty()) {
Transaction transaction2 = (Transaction) linkedHashMap.remove(linkedHashMap.keySet().iterator().next());
for (Transaction transaction3 : set2) {
if (!transaction3.equals(transaction2)) {
Iterator<TransactionInput> it = transaction3.getInputs().iterator();
while (it.hasNext()) {
if (it.next().getOutpoint().getHash().equals(transaction2.getHash()) && linkedHashMap.get(transaction3.getHash()) == null) {
linkedHashMap.put(transaction3.getHash(), transaction3);
set.add(transaction3);
}
}
}
}
}
}
@Override
public void receiveFromBlock(Transaction transaction, StoredBlock storedBlock, AbstractBlockChain.NewBlockType newBlockType, int i) throws VerificationException {
this.lock.lock();
try {
if (isTransactionRelevant(transaction)) {
receive(transaction, storedBlock, newBlockType, i);
}
} finally {
this.lock.unlock();
}
}
private void receive(Transaction transaction, StoredBlock storedBlock, AbstractBlockChain.NewBlockType newBlockType, int i) throws VerificationException {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Coin balance = getBalance();
Sha256Hash hash = transaction.getHash();
boolean z = newBlockType == AbstractBlockChain.NewBlockType.BEST_CHAIN;
boolean z2 = newBlockType == AbstractBlockChain.NewBlockType.SIDE_CHAIN;
Coin subtract = transaction.getValueSentToMe(this).subtract(transaction.getValueSentFromMe(this));
Logger logger = log;
Object[] objArr = new Object[5];
objArr[0] = z2 ? " on a side chain" : "";
objArr[1] = subtract.toFriendlyString();
objArr[2] = transaction.getHashAsString();
objArr[3] = Integer.valueOf(i);
objArr[4] = storedBlock != null ? storedBlock.getHeader().getHash() : "(unit test)";
logger.info("Received tx{} for {}: {} [{}] in block {}", objArr);
markKeysAsUsed(transaction);
this.onWalletChangedSuppressions++;
Transaction transaction2 = this.transactions.get(transaction.getHash());
if (transaction2 != null) {
transaction = transaction2;
}
boolean z3 = this.pending.remove(hash) != null;
if (z3) {
log.info(" <-pending");
}
if (z) {
boolean z4 = this.dead.remove(hash) != null;
if (z4) {
log.info(" <-dead");
}
if (z3) {
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
TransactionInput spentBy = transactionOutput.getSpentBy();
if (spentBy != null) {
Preconditions.checkState(this.myUnspents.add(transactionOutput));
spentBy.disconnect();
}
}
}
processTxFromBestChain(transaction, z3 || z4);
} else {
Preconditions.checkState(z2);
if (z3) {
addWalletTransaction(WalletTransaction.Pool.PENDING, transaction);
log.info(" ->pending");
} else {
Sha256Hash hash2 = transaction.getHash();
if (!this.unspent.containsKey(hash2) && !this.spent.containsKey(hash2) && !this.dead.containsKey(hash2)) {
commitTx(transaction);
}
}
}
if (storedBlock != null) {
transaction.setBlockAppearance(storedBlock, z, i);
if (z) {
this.ignoreNextNewBlock.add(hash);
HashSet newHashSet = Sets.newHashSet(new Transaction[]{transaction});
addTransactionsDependingOn(newHashSet, getTransactions(true));
newHashSet.remove(transaction);
for (Transaction transaction3 : sortTxnsByDependency(newHashSet)) {
if (transaction3.getConfidence().getConfidenceType().equals(TransactionConfidence.ConfidenceType.IN_CONFLICT) && isNotSpendingTxnsInConfidenceType(transaction3, TransactionConfidence.ConfidenceType.IN_CONFLICT)) {
transaction3.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING);
this.confidenceChanged.put(transaction3, TransactionConfidence.Listener.ChangeReason.TYPE);
}
}
}
}
this.onWalletChangedSuppressions--;
if (z) {
this.confidenceChanged.put(transaction, TransactionConfidence.Listener.ChangeReason.TYPE);
} else {
maybeQueueOnWalletChanged();
}
if (!this.insideReorg && z) {
Coin balance2 = getBalance();
log.info("Balance is now: " + balance2.toFriendlyString());
if (!z3) {
int signum = subtract.signum();
if (signum > 0) {
queueOnCoinsReceived(transaction, balance, balance2);
} else if (signum < 0) {
queueOnCoinsSent(transaction, balance, balance2);
}
}
checkBalanceFuturesLocked(balance2);
}
informConfidenceListenersIfNotReorganizing();
isConsistentOrThrow();
saveLater();
this.hardSaveOnNextBlock = true;
}
private boolean isNotSpendingTxnsInConfidenceType(Transaction transaction, TransactionConfidence.ConfidenceType confidenceType) {
Iterator<TransactionInput> it = transaction.getInputs().iterator();
while (it.hasNext()) {
Transaction transaction2 = getTransaction(it.next().getOutpoint().getHash());
if (transaction2 != null && transaction2.getConfidence().getConfidenceType().equals(confidenceType)) {
return false;
}
}
return true;
}
List<Transaction> sortTxnsByDependency(Set<Transaction> set) {
int i;
boolean z;
ArrayList arrayList = new ArrayList(set);
int i2 = 0;
while (i2 < arrayList.size() - 1) {
do {
i = i2 + 1;
int i3 = i;
while (true) {
if (i3 >= arrayList.size()) {
z = false;
break;
}
if (spends((Transaction) arrayList.get(i2), (Transaction) arrayList.get(i3))) {
arrayList.add(i3, (Transaction) arrayList.remove(i2));
z = true;
break;
}
i3++;
}
} while (z);
i2 = i;
}
return arrayList;
}
boolean spends(Transaction transaction, Transaction transaction2) {
Iterator<TransactionInput> it = transaction.getInputs().iterator();
while (it.hasNext()) {
if (it.next().getOutpoint().getHash().equals(transaction2.getHash())) {
return true;
}
}
return false;
}
private void informConfidenceListenersIfNotReorganizing() {
if (this.insideReorg) {
return;
}
for (Map.Entry<Transaction, TransactionConfidence.Listener.ChangeReason> entry : this.confidenceChanged.entrySet()) {
Transaction key = entry.getKey();
key.getConfidence().queueListeners(entry.getValue());
queueOnTransactionConfidenceChanged(key);
}
this.confidenceChanged.clear();
}
@Override
public void notifyNewBestBlock(StoredBlock storedBlock) throws VerificationException {
Sha256Hash hash = storedBlock.getHeader().getHash();
if (hash.equals(getLastBlockSeenHash())) {
return;
}
this.lock.lock();
try {
setLastBlockSeenHash(hash);
setLastBlockSeenHeight(storedBlock.getHeight());
setLastBlockSeenTimeSecs(storedBlock.getHeader().getTimeSeconds());
for (Transaction transaction : getTransactions(true)) {
if (this.ignoreNextNewBlock.contains(transaction.getHash())) {
this.ignoreNextNewBlock.remove(transaction.getHash());
} else {
TransactionConfidence confidence = transaction.getConfidence();
if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
if (confidence.incrementDepthInBlocks() > this.context.getEventHorizon()) {
confidence.clearBroadcastBy();
}
this.confidenceChanged.put(transaction, TransactionConfidence.Listener.ChangeReason.DEPTH);
}
}
}
informConfidenceListenersIfNotReorganizing();
maybeQueueOnWalletChanged();
if (this.hardSaveOnNextBlock) {
saveNow();
this.hardSaveOnNextBlock = false;
} else {
saveLater();
}
} finally {
this.lock.unlock();
}
}
private void processTxFromBestChain(Transaction transaction, boolean z) throws VerificationException {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Preconditions.checkState(!this.pending.containsKey(transaction.getHash()));
if (transaction.isCoinBase() && this.dead.containsKey(transaction.getHash())) {
log.info(" coinbase tx <-dead: confidence {}", transaction.getHashAsString(), transaction.getConfidence().getConfidenceType().name());
this.dead.remove(transaction.getHash());
}
updateForSpends(transaction, true);
if (transaction.getValueSentToMe(this).signum() > 0) {
if (transaction.isEveryOwnedOutputSpent(this)) {
log.info(" tx {} ->spent (by pending)", transaction.getHashAsString());
addWalletTransaction(WalletTransaction.Pool.SPENT, transaction);
} else {
log.info(" tx {} ->unspent", transaction.getHashAsString());
addWalletTransaction(WalletTransaction.Pool.UNSPENT, transaction);
}
} else if (transaction.getValueSentFromMe(this).signum() > 0) {
log.info(" tx {} ->spent", transaction.getHashAsString());
addWalletTransaction(WalletTransaction.Pool.SPENT, transaction);
} else if (z) {
log.info(" tx {} ->spent (manually added)", transaction.getHashAsString());
addWalletTransaction(WalletTransaction.Pool.SPENT, transaction);
}
Set<Transaction> findDoubleSpendsAgainst = findDoubleSpendsAgainst(transaction, this.pending);
if (findDoubleSpendsAgainst.isEmpty()) {
return;
}
killTxns(findDoubleSpendsAgainst, transaction);
}
private void updateForSpends(Transaction transaction, boolean z) throws VerificationException {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
if (z) {
Preconditions.checkState(!this.pending.containsKey(transaction.getHash()));
}
for (TransactionInput transactionInput : transaction.getInputs()) {
TransactionInput.ConnectionResult connect = transactionInput.connect(this.unspent, TransactionInput.ConnectMode.ABORT_ON_CONFLICT);
if (connect != TransactionInput.ConnectionResult.NO_SUCH_TX || (connect = transactionInput.connect(this.spent, TransactionInput.ConnectMode.ABORT_ON_CONFLICT)) != TransactionInput.ConnectionResult.NO_SUCH_TX || (connect = transactionInput.connect(this.pending, TransactionInput.ConnectMode.ABORT_ON_CONFLICT)) != TransactionInput.ConnectionResult.NO_SUCH_TX) {
TransactionOutput transactionOutput = (TransactionOutput) Preconditions.checkNotNull(transactionInput.getConnectedOutput());
if (connect == TransactionInput.ConnectionResult.ALREADY_SPENT) {
if (!z) {
log.warn("Saw two pending transactions double spend each other");
log.warn(" offending input is input {}", Integer.valueOf(transaction.getInputs().indexOf(transactionInput)));
log.warn("{}: {}", transaction.getHash(), Utils.HEX.encode(transaction.unsafeBitcoinSerialize()));
Transaction parentTransaction = transactionOutput.getSpentBy().getParentTransaction();
log.warn("{}: {}", parentTransaction.getHash(), Utils.HEX.encode(parentTransaction.unsafeBitcoinSerialize()));
}
} else if (connect == TransactionInput.ConnectionResult.SUCCESS) {
Transaction transaction2 = (Transaction) Preconditions.checkNotNull(transactionInput.getConnectedTransaction());
log.info(" marked {} as spent by {}", transactionInput.getOutpoint(), transaction.getHashAsString());
maybeMovePool(transaction2, "prevtx");
if (transactionOutput.isMineOrWatched(this)) {
Preconditions.checkState(this.myUnspents.remove(transactionOutput));
}
}
}
}
for (Transaction transaction3 : this.pending.values()) {
for (TransactionInput transactionInput2 : transaction3.getInputs()) {
TransactionInput.ConnectionResult connect2 = transactionInput2.connect(transaction, TransactionInput.ConnectMode.ABORT_ON_CONFLICT);
if (z) {
Preconditions.checkState(connect2 != TransactionInput.ConnectionResult.ALREADY_SPENT);
}
if (connect2 == TransactionInput.ConnectionResult.SUCCESS) {
log.info("Connected pending tx input {}:{}", transaction3.getHashAsString(), Integer.valueOf(transaction3.getInputs().indexOf(transactionInput2)));
if (this.myUnspents.remove(transactionInput2.getConnectedOutput())) {
log.info("Removed from UNSPENTS: {}", transactionInput2.getConnectedOutput());
}
}
}
}
if (z) {
return;
}
maybeMovePool(transaction, "pendingtx");
}
private void killTxns(Set<Transaction> set, @Nullable Transaction transaction) {
LinkedList linkedList = new LinkedList(set);
while (!linkedList.isEmpty()) {
Transaction transaction2 = (Transaction) linkedList.poll();
log.warn("TX {} killed{}", transaction2.getHashAsString(), transaction != null ? " by " + transaction.getHashAsString() : "");
log.warn("Disconnecting each input and moving connected transactions.");
this.pending.remove(transaction2.getHash());
this.unspent.remove(transaction2.getHash());
this.spent.remove(transaction2.getHash());
addWalletTransaction(WalletTransaction.Pool.DEAD, transaction2);
for (TransactionInput transactionInput : transaction2.getInputs()) {
Transaction connectedTransaction = transactionInput.getConnectedTransaction();
if (connectedTransaction != null) {
if (connectedTransaction.getConfidence().getConfidenceType() != TransactionConfidence.ConfidenceType.DEAD && transactionInput.getConnectedOutput().getSpentBy() != null && transactionInput.getConnectedOutput().getSpentBy().equals(transactionInput)) {
Preconditions.checkState(this.myUnspents.add(transactionInput.getConnectedOutput()));
log.info("Added to UNSPENTS: {} in {}", transactionInput.getConnectedOutput(), transactionInput.getConnectedOutput().getParentTransaction().getHash());
}
transactionInput.disconnect();
maybeMovePool(connectedTransaction, "kill");
}
}
transaction2.getConfidence().setOverridingTransaction(transaction);
this.confidenceChanged.put(transaction2, TransactionConfidence.Listener.ChangeReason.TYPE);
for (TransactionOutput transactionOutput : transaction2.getOutputs()) {
if (this.myUnspents.remove(transactionOutput)) {
log.info("XX Removed from UNSPENTS: {}", transactionOutput);
}
TransactionInput spentBy = transactionOutput.getSpentBy();
if (spentBy != null) {
Transaction parentTransaction = spentBy.getParentTransaction();
log.info("This death invalidated dependent tx {}", parentTransaction.getHash());
linkedList.push(parentTransaction);
}
}
}
if (transaction == null) {
return;
}
log.warn("Now attempting to connect the inputs of the overriding transaction.");
for (TransactionInput transactionInput2 : transaction.getInputs()) {
if (transactionInput2.connect(this.unspent, TransactionInput.ConnectMode.DISCONNECT_ON_CONFLICT) == TransactionInput.ConnectionResult.SUCCESS) {
maybeMovePool(transactionInput2.getConnectedTransaction(), "kill");
this.myUnspents.remove(transactionInput2.getConnectedOutput());
log.info("Removing from UNSPENTS: {}", transactionInput2.getConnectedOutput());
} else if (transactionInput2.connect(this.spent, TransactionInput.ConnectMode.DISCONNECT_ON_CONFLICT) == TransactionInput.ConnectionResult.SUCCESS) {
maybeMovePool(transactionInput2.getConnectedTransaction(), "kill");
this.myUnspents.remove(transactionInput2.getConnectedOutput());
log.info("Removing from UNSPENTS: {}", transactionInput2.getConnectedOutput());
}
}
}
private void maybeMovePool(Transaction transaction, String str) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
if (transaction.isEveryOwnedOutputSpent(this)) {
if (this.unspent.remove(transaction.getHash()) != null) {
if (log.isInfoEnabled()) {
log.info(" {} {} <-unspent ->spent", transaction.getHashAsString(), str);
}
this.spent.put(transaction.getHash(), transaction);
return;
}
return;
}
if (this.spent.remove(transaction.getHash()) != null) {
if (log.isInfoEnabled()) {
log.info(" {} {} <-spent ->unspent", transaction.getHashAsString(), str);
}
this.unspent.put(transaction.getHash(), transaction);
}
}
public boolean maybeCommitTx(org.bitcoinj.core.Transaction r8) throws org.bitcoinj.core.VerificationException {
throw new UnsupportedOperationException("Method not decompiled: org.bitcoinj.wallet.Wallet.maybeCommitTx(org.bitcoinj.core.Transaction):boolean");
}
public void commitTx(Transaction transaction) throws VerificationException {
Preconditions.checkArgument(maybeCommitTx(transaction), "commitTx called on the same transaction twice");
}
public void addEventListener(WalletEventListener walletEventListener) {
addChangeEventListener(Threading.USER_THREAD, walletEventListener);
addCoinsReceivedEventListener(Threading.USER_THREAD, walletEventListener);
addCoinsSentEventListener(Threading.USER_THREAD, walletEventListener);
addKeyChainEventListener(Threading.USER_THREAD, walletEventListener);
addReorganizeEventListener(Threading.USER_THREAD, walletEventListener);
addScriptChangeEventListener(Threading.USER_THREAD, walletEventListener);
addTransactionConfidenceEventListener(Threading.USER_THREAD, walletEventListener);
}
@Deprecated
public void addEventListener(WalletEventListener walletEventListener, Executor executor) {
addCoinsReceivedEventListener(executor, walletEventListener);
addCoinsSentEventListener(executor, walletEventListener);
addChangeEventListener(executor, walletEventListener);
addKeyChainEventListener(executor, walletEventListener);
addReorganizeEventListener(executor, walletEventListener);
addScriptChangeEventListener(executor, walletEventListener);
addTransactionConfidenceEventListener(executor, walletEventListener);
}
public void addChangeEventListener(WalletChangeEventListener walletChangeEventListener) {
addChangeEventListener(Threading.USER_THREAD, walletChangeEventListener);
}
public void addChangeEventListener(Executor executor, WalletChangeEventListener walletChangeEventListener) {
this.changeListeners.add(new ListenerRegistration<>(walletChangeEventListener, executor));
}
public void addCoinsReceivedEventListener(WalletCoinsReceivedEventListener walletCoinsReceivedEventListener) {
addCoinsReceivedEventListener(Threading.USER_THREAD, walletCoinsReceivedEventListener);
}
public void addCoinsReceivedEventListener(Executor executor, WalletCoinsReceivedEventListener walletCoinsReceivedEventListener) {
this.coinsReceivedListeners.add(new ListenerRegistration<>(walletCoinsReceivedEventListener, executor));
}
public void addCoinsSentEventListener(WalletCoinsSentEventListener walletCoinsSentEventListener) {
addCoinsSentEventListener(Threading.USER_THREAD, walletCoinsSentEventListener);
}
public void addCoinsSentEventListener(Executor executor, WalletCoinsSentEventListener walletCoinsSentEventListener) {
this.coinsSentListeners.add(new ListenerRegistration<>(walletCoinsSentEventListener, executor));
}
public void addKeyChainEventListener(KeyChainEventListener keyChainEventListener) {
this.keyChainGroup.addEventListener(keyChainEventListener, Threading.USER_THREAD);
}
public void addKeyChainEventListener(Executor executor, KeyChainEventListener keyChainEventListener) {
this.keyChainGroup.addEventListener(keyChainEventListener, executor);
}
public void addReorganizeEventListener(WalletReorganizeEventListener walletReorganizeEventListener) {
addReorganizeEventListener(Threading.USER_THREAD, walletReorganizeEventListener);
}
public void addReorganizeEventListener(Executor executor, WalletReorganizeEventListener walletReorganizeEventListener) {
this.reorganizeListeners.add(new ListenerRegistration<>(walletReorganizeEventListener, executor));
}
public void addScriptsChangeEventListener(ScriptsChangeEventListener scriptsChangeEventListener) {
addScriptChangeEventListener(Threading.USER_THREAD, scriptsChangeEventListener);
}
public void addScriptChangeEventListener(Executor executor, ScriptsChangeEventListener scriptsChangeEventListener) {
this.scriptChangeListeners.add(new ListenerRegistration<>(scriptsChangeEventListener, executor));
}
public void addTransactionConfidenceEventListener(TransactionConfidenceEventListener transactionConfidenceEventListener) {
addTransactionConfidenceEventListener(Threading.USER_THREAD, transactionConfidenceEventListener);
}
public void addTransactionConfidenceEventListener(Executor executor, TransactionConfidenceEventListener transactionConfidenceEventListener) {
this.transactionConfidenceListeners.add(new ListenerRegistration<>(transactionConfidenceEventListener, executor));
}
@Deprecated
public boolean removeEventListener(WalletEventListener walletEventListener) {
return removeChangeEventListener(walletEventListener) || removeCoinsReceivedEventListener(walletEventListener) || removeCoinsSentEventListener(walletEventListener) || removeKeyChainEventListener(walletEventListener) || removeReorganizeEventListener(walletEventListener) || removeTransactionConfidenceEventListener(walletEventListener);
}
public boolean removeChangeEventListener(WalletChangeEventListener walletChangeEventListener) {
return ListenerRegistration.removeFromList(walletChangeEventListener, this.changeListeners);
}
public boolean removeCoinsReceivedEventListener(WalletCoinsReceivedEventListener walletCoinsReceivedEventListener) {
return ListenerRegistration.removeFromList(walletCoinsReceivedEventListener, this.coinsReceivedListeners);
}
public boolean removeCoinsSentEventListener(WalletCoinsSentEventListener walletCoinsSentEventListener) {
return ListenerRegistration.removeFromList(walletCoinsSentEventListener, this.coinsSentListeners);
}
public boolean removeKeyChainEventListener(KeyChainEventListener keyChainEventListener) {
return this.keyChainGroup.removeEventListener(keyChainEventListener);
}
public boolean removeReorganizeEventListener(WalletReorganizeEventListener walletReorganizeEventListener) {
return ListenerRegistration.removeFromList(walletReorganizeEventListener, this.reorganizeListeners);
}
public boolean removeScriptChangeEventListener(ScriptsChangeEventListener scriptsChangeEventListener) {
return ListenerRegistration.removeFromList(scriptsChangeEventListener, this.scriptChangeListeners);
}
public boolean removeTransactionConfidenceEventListener(TransactionConfidenceEventListener transactionConfidenceEventListener) {
return ListenerRegistration.removeFromList(transactionConfidenceEventListener, this.transactionConfidenceListeners);
}
public void queueOnTransactionConfidenceChanged(final Transaction transaction) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Iterator<ListenerRegistration<TransactionConfidenceEventListener>> it = this.transactionConfidenceListeners.iterator();
while (it.hasNext()) {
final ListenerRegistration<TransactionConfidenceEventListener> next = it.next();
if (next.executor == Threading.SAME_THREAD) {
next.listener.onTransactionConfidenceChanged(this, transaction);
} else {
next.executor.execute(new Runnable() {
@Override
public void run() {
((TransactionConfidenceEventListener) next.listener).onTransactionConfidenceChanged(Wallet.this, transaction);
}
});
}
}
}
protected void maybeQueueOnWalletChanged() {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Preconditions.checkState(this.onWalletChangedSuppressions >= 0);
if (this.onWalletChangedSuppressions > 0) {
return;
}
Iterator<ListenerRegistration<WalletChangeEventListener>> it = this.changeListeners.iterator();
while (it.hasNext()) {
final ListenerRegistration<WalletChangeEventListener> next = it.next();
next.executor.execute(new Runnable() {
@Override
public void run() {
((WalletChangeEventListener) next.listener).onWalletChanged(Wallet.this);
}
});
}
}
protected void queueOnCoinsReceived(final Transaction transaction, final Coin coin, final Coin coin2) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Iterator<ListenerRegistration<WalletCoinsReceivedEventListener>> it = this.coinsReceivedListeners.iterator();
while (it.hasNext()) {
final ListenerRegistration<WalletCoinsReceivedEventListener> next = it.next();
next.executor.execute(new Runnable() {
@Override
public void run() {
((WalletCoinsReceivedEventListener) next.listener).onCoinsReceived(Wallet.this, transaction, coin, coin2);
}
});
}
}
protected void queueOnCoinsSent(final Transaction transaction, final Coin coin, final Coin coin2) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Iterator<ListenerRegistration<WalletCoinsSentEventListener>> it = this.coinsSentListeners.iterator();
while (it.hasNext()) {
final ListenerRegistration<WalletCoinsSentEventListener> next = it.next();
next.executor.execute(new Runnable() {
@Override
public void run() {
((WalletCoinsSentEventListener) next.listener).onCoinsSent(Wallet.this, transaction, coin, coin2);
}
});
}
}
protected void queueOnReorganize() {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Preconditions.checkState(this.insideReorg);
Iterator<ListenerRegistration<WalletReorganizeEventListener>> it = this.reorganizeListeners.iterator();
while (it.hasNext()) {
final ListenerRegistration<WalletReorganizeEventListener> next = it.next();
next.executor.execute(new Runnable() {
@Override
public void run() {
((WalletReorganizeEventListener) next.listener).onReorganize(Wallet.this);
}
});
}
}
protected void queueOnScriptsChanged(final List<Script> list, final boolean z) {
Iterator<ListenerRegistration<ScriptsChangeEventListener>> it = this.scriptChangeListeners.iterator();
while (it.hasNext()) {
final ListenerRegistration<ScriptsChangeEventListener> next = it.next();
next.executor.execute(new Runnable() {
@Override
public void run() {
((ScriptsChangeEventListener) next.listener).onScriptsChanged(Wallet.this, list, z);
}
});
}
}
public Set<Transaction> getTransactions(boolean z) {
this.lock.lock();
try {
HashSet hashSet = new HashSet();
hashSet.addAll(this.unspent.values());
hashSet.addAll(this.spent.values());
hashSet.addAll(this.pending.values());
if (z) {
hashSet.addAll(this.dead.values());
}
return hashSet;
} finally {
this.lock.unlock();
}
}
public Iterable<WalletTransaction> getWalletTransactions() {
this.lock.lock();
try {
HashSet hashSet = new HashSet();
addWalletTransactionsToSet(hashSet, WalletTransaction.Pool.UNSPENT, this.unspent.values());
addWalletTransactionsToSet(hashSet, WalletTransaction.Pool.SPENT, this.spent.values());
addWalletTransactionsToSet(hashSet, WalletTransaction.Pool.DEAD, this.dead.values());
addWalletTransactionsToSet(hashSet, WalletTransaction.Pool.PENDING, this.pending.values());
return hashSet;
} finally {
this.lock.unlock();
}
}
private static void addWalletTransactionsToSet(Set<WalletTransaction> set, WalletTransaction.Pool pool, Collection<Transaction> collection) {
Iterator<Transaction> it = collection.iterator();
while (it.hasNext()) {
set.add(new WalletTransaction(pool, it.next()));
}
}
public void addWalletTransaction(WalletTransaction walletTransaction) {
this.lock.lock();
try {
addWalletTransaction(walletTransaction.getPool(), walletTransaction.getTransaction());
} finally {
this.lock.unlock();
}
}
private void addWalletTransaction(WalletTransaction.Pool pool, Transaction transaction) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
this.transactions.put(transaction.getHash(), transaction);
int i = AnonymousClass11.$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool[pool.ordinal()];
if (i == 1) {
Preconditions.checkState(this.unspent.put(transaction.getHash(), transaction) == null);
} else if (i == 2) {
Preconditions.checkState(this.spent.put(transaction.getHash(), transaction) == null);
} else if (i == 3) {
Preconditions.checkState(this.pending.put(transaction.getHash(), transaction) == null);
} else if (i == 4) {
Preconditions.checkState(this.dead.put(transaction.getHash(), transaction) == null);
} else {
throw new RuntimeException("Unknown wallet transaction type " + pool);
}
if (pool == WalletTransaction.Pool.UNSPENT || pool == WalletTransaction.Pool.PENDING) {
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
if (transactionOutput.isAvailableForSpending() && transactionOutput.isMineOrWatched(this)) {
this.myUnspents.add(transactionOutput);
}
}
}
transaction.getConfidence().addEventListener(Threading.SAME_THREAD, this.txConfidenceListener);
}
static class AnonymousClass11 {
static final int[] $SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool;
static {
int[] iArr = new int[WalletTransaction.Pool.values().length];
$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool = iArr;
try {
iArr[WalletTransaction.Pool.UNSPENT.ordinal()] = 1;
} catch (NoSuchFieldError unused) {
}
try {
$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool[WalletTransaction.Pool.SPENT.ordinal()] = 2;
} catch (NoSuchFieldError unused2) {
}
try {
$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool[WalletTransaction.Pool.PENDING.ordinal()] = 3;
} catch (NoSuchFieldError unused3) {
}
try {
$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool[WalletTransaction.Pool.DEAD.ordinal()] = 4;
} catch (NoSuchFieldError unused4) {
}
}
}
public List<Transaction> getTransactionsByTime() {
return getRecentTransactions(0, false);
}
public List<Transaction> getRecentTransactions(int i, boolean z) {
this.lock.lock();
try {
Preconditions.checkArgument(i >= 0);
int size = this.unspent.size() + this.spent.size() + this.pending.size();
if (i > size || i == 0) {
i = size;
}
ArrayList arrayList = new ArrayList(getTransactions(z));
Collections.sort(arrayList, Transaction.SORT_TX_BY_UPDATE_TIME);
if (i != arrayList.size()) {
arrayList.subList(i, arrayList.size()).clear();
}
return arrayList;
} finally {
this.lock.unlock();
}
}
@Nullable
public Transaction getTransaction(Sha256Hash sha256Hash) {
this.lock.lock();
try {
return this.transactions.get(sha256Hash);
} finally {
this.lock.unlock();
}
}
@Override
public Map<Sha256Hash, Transaction> getTransactionPool(WalletTransaction.Pool pool) {
Map<Sha256Hash, Transaction> map;
this.lock.lock();
try {
int i = AnonymousClass11.$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool[pool.ordinal()];
if (i == 1) {
map = this.unspent;
} else if (i == 2) {
map = this.spent;
} else if (i == 3) {
map = this.pending;
} else if (i == 4) {
map = this.dead;
} else {
throw new RuntimeException("Unknown wallet transaction type " + pool);
}
return map;
} finally {
this.lock.unlock();
}
}
public void reset() {
this.lock.lock();
try {
clearTransactions();
this.lastBlockSeenHash = null;
this.lastBlockSeenHeight = -1;
this.lastBlockSeenTimeSecs = 0L;
saveLater();
maybeQueueOnWalletChanged();
} finally {
this.lock.unlock();
}
}
public void clearTransactions(int i) {
this.lock.lock();
try {
if (i == 0) {
clearTransactions();
saveLater();
return;
}
throw new UnsupportedOperationException();
} finally {
this.lock.unlock();
}
}
private void clearTransactions() {
this.unspent.clear();
this.spent.clear();
this.pending.clear();
this.dead.clear();
this.transactions.clear();
this.myUnspents.clear();
}
public List<TransactionOutput> getWatchedOutputs(boolean z) {
this.lock.lock();
this.keyChainGroupLock.lock();
try {
LinkedList newLinkedList = Lists.newLinkedList();
for (Transaction transaction : Iterables.concat(this.unspent.values(), this.pending.values())) {
if (!z || transaction.isMature()) {
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
if (transactionOutput.isAvailableForSpending()) {
try {
if (this.watchedScripts.contains(transactionOutput.getScriptPubKey())) {
newLinkedList.add(transactionOutput);
}
} catch (ScriptException unused) {
}
}
}
}
}
return newLinkedList;
} finally {
this.keyChainGroupLock.unlock();
this.lock.unlock();
}
}
public void cleanup() {
this.lock.lock();
boolean z = false;
try {
Iterator<Transaction> it = this.pending.values().iterator();
while (it.hasNext()) {
Transaction next = it.next();
if (isTransactionRisky(next, null) && !this.acceptRiskyTransactions) {
log.debug("Found risky transaction {} in wallet during cleanup.", next.getHashAsString());
if (!next.isAnyOutputSpent()) {
for (TransactionInput transactionInput : next.getInputs()) {
TransactionOutput connectedOutput = transactionInput.getConnectedOutput();
if (connectedOutput != null) {
if (connectedOutput.isMineOrWatched(this)) {
Preconditions.checkState(this.myUnspents.add(connectedOutput));
}
transactionInput.disconnect();
}
}
Iterator<TransactionOutput> it2 = next.getOutputs().iterator();
while (it2.hasNext()) {
this.myUnspents.remove(it2.next());
}
it.remove();
this.transactions.remove(next.getHash());
z = true;
log.info("Removed transaction {} from pending pool during cleanup.", next.getHashAsString());
} else {
log.info("Cannot remove transaction {} from pending pool during cleanup, as it's already spent partially.", next.getHashAsString());
}
}
}
if (z) {
isConsistentOrThrow();
saveLater();
if (log.isInfoEnabled()) {
log.info("Estimated balance is now: {}", getBalance(BalanceType.ESTIMATED).toFriendlyString());
}
}
} finally {
this.lock.unlock();
}
}
EnumSet<WalletTransaction.Pool> getContainingPools(Transaction transaction) {
this.lock.lock();
try {
EnumSet<WalletTransaction.Pool> noneOf = EnumSet.noneOf(WalletTransaction.Pool.class);
Sha256Hash hash = transaction.getHash();
if (this.unspent.containsKey(hash)) {
noneOf.add(WalletTransaction.Pool.UNSPENT);
}
if (this.spent.containsKey(hash)) {
noneOf.add(WalletTransaction.Pool.SPENT);
}
if (this.pending.containsKey(hash)) {
noneOf.add(WalletTransaction.Pool.PENDING);
}
if (this.dead.containsKey(hash)) {
noneOf.add(WalletTransaction.Pool.DEAD);
}
return noneOf;
} finally {
this.lock.unlock();
}
}
public int getPoolSize(WalletTransaction.Pool pool) {
int size;
this.lock.lock();
try {
int i = AnonymousClass11.$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool[pool.ordinal()];
if (i == 1) {
size = this.unspent.size();
} else if (i == 2) {
size = this.spent.size();
} else if (i == 3) {
size = this.pending.size();
} else if (i == 4) {
size = this.dead.size();
} else {
throw new RuntimeException("Unreachable");
}
return size;
} finally {
this.lock.unlock();
}
}
public boolean poolContainsTxHash(WalletTransaction.Pool pool, Sha256Hash sha256Hash) {
boolean containsKey;
this.lock.lock();
try {
int i = AnonymousClass11.$SwitchMap$org$bitcoinj$wallet$WalletTransaction$Pool[pool.ordinal()];
if (i == 1) {
containsKey = this.unspent.containsKey(sha256Hash);
} else if (i == 2) {
containsKey = this.spent.containsKey(sha256Hash);
} else if (i == 3) {
containsKey = this.pending.containsKey(sha256Hash);
} else if (i == 4) {
containsKey = this.dead.containsKey(sha256Hash);
} else {
throw new RuntimeException("Unreachable");
}
return containsKey;
} finally {
this.lock.unlock();
}
}
public List<TransactionOutput> getUnspents() {
this.lock.lock();
try {
return new ArrayList(this.myUnspents);
} finally {
this.lock.unlock();
}
}
public String toString() {
return toString(false, true, true, null);
}
public String toString(boolean z, boolean z2, boolean z3, @Nullable AbstractBlockChain abstractBlockChain) {
this.lock.lock();
this.keyChainGroupLock.lock();
try {
StringBuilder sb = new StringBuilder();
Coin balance = getBalance(BalanceType.ESTIMATED);
Coin balance2 = getBalance(BalanceType.AVAILABLE_SPENDABLE);
sb.append("Wallet containing ");
sb.append(balance.toFriendlyString());
sb.append(" (spendable: ");
sb.append(balance2.toFriendlyString());
sb.append(") in:\n");
sb.append(" ");
sb.append(this.pending.size());
sb.append(" pending transactions\n");
sb.append(" ");
sb.append(this.unspent.size());
sb.append(" unspent transactions\n");
sb.append(" ");
sb.append(this.spent.size());
sb.append(" spent transactions\n");
sb.append(" ");
sb.append(this.dead.size());
sb.append(" dead transactions\n");
Date lastBlockSeenTime = getLastBlockSeenTime();
sb.append("Last seen best block: ");
sb.append(getLastBlockSeenHeight());
sb.append(" (");
sb.append(lastBlockSeenTime == null ? "time unknown" : Utils.dateTimeFormat(lastBlockSeenTime));
sb.append("): ");
sb.append(getLastBlockSeenHash());
sb.append('\n');
KeyCrypter keyCrypter = this.keyChainGroup.getKeyCrypter();
if (keyCrypter != null) {
sb.append("Encryption: ");
sb.append(keyCrypter);
sb.append('\n');
}
if (isWatching()) {
sb.append("Wallet is watching.\n");
}
sb.append("\nKeys:\n");
sb.append("Earliest creation time: ");
sb.append(Utils.dateTimeFormat(getEarliestKeyCreationTime() * 1000));
sb.append('\n');
Date keyRotationTime = getKeyRotationTime();
if (keyRotationTime != null) {
sb.append("Key rotation time: ");
sb.append(Utils.dateTimeFormat(keyRotationTime));
sb.append('\n');
}
sb.append(this.keyChainGroup.toString(z));
if (!this.watchedScripts.isEmpty()) {
sb.append("\nWatched scripts:\n");
for (Script script : this.watchedScripts) {
sb.append(" ");
sb.append(script);
sb.append("\n");
}
}
if (z2) {
if (this.pending.size() > 0) {
sb.append("\n>>> PENDING:\n");
toStringHelper(sb, this.pending, abstractBlockChain, Transaction.SORT_TX_BY_UPDATE_TIME);
}
if (this.unspent.size() > 0) {
sb.append("\n>>> UNSPENT:\n");
toStringHelper(sb, this.unspent, abstractBlockChain, Transaction.SORT_TX_BY_HEIGHT);
}
if (this.spent.size() > 0) {
sb.append("\n>>> SPENT:\n");
toStringHelper(sb, this.spent, abstractBlockChain, Transaction.SORT_TX_BY_HEIGHT);
}
if (this.dead.size() > 0) {
sb.append("\n>>> DEAD:\n");
toStringHelper(sb, this.dead, abstractBlockChain, Transaction.SORT_TX_BY_UPDATE_TIME);
}
}
if (z3 && this.extensions.size() > 0) {
sb.append("\n>>> EXTENSIONS:\n");
Iterator<WalletExtension> it = this.extensions.values().iterator();
while (it.hasNext()) {
sb.append(it.next());
sb.append("\n\n");
}
}
return sb.toString();
} finally {
this.keyChainGroupLock.unlock();
this.lock.unlock();
}
}
private void toStringHelper(StringBuilder sb, Map<Sha256Hash, Transaction> map, @Nullable AbstractBlockChain abstractBlockChain, @Nullable Comparator<Transaction> comparator) {
Collection<Transaction> values;
Preconditions.checkState(this.lock.isHeldByCurrentThread());
if (comparator != null) {
values = new TreeSet<>(comparator);
values.addAll(map.values());
} else {
values = map.values();
}
for (Transaction transaction : values) {
try {
sb.append(transaction.getValue(this).toFriendlyString());
sb.append(" total value (sends ");
sb.append(transaction.getValueSentFromMe(this).toFriendlyString());
sb.append(" and receives ");
sb.append(transaction.getValueSentToMe(this).toFriendlyString());
sb.append(")\n");
} catch (ScriptException unused) {
}
if (transaction.hasConfidence()) {
sb.append(" confidence: ");
sb.append(transaction.getConfidence());
sb.append('\n');
}
sb.append(transaction.toString(abstractBlockChain));
}
}
public Collection<Transaction> getPendingTransactions() {
this.lock.lock();
try {
return Collections.unmodifiableCollection(this.pending.values());
} finally {
this.lock.unlock();
}
}
@Override
public long getEarliestKeyCreationTime() {
this.keyChainGroupLock.lock();
try {
long earliestKeyCreationTime = this.keyChainGroup.getEarliestKeyCreationTime();
Iterator<Script> it = this.watchedScripts.iterator();
while (it.hasNext()) {
earliestKeyCreationTime = Math.min(it.next().getCreationTimeSeconds(), earliestKeyCreationTime);
}
if (earliestKeyCreationTime == LongCompanionObject.MAX_VALUE) {
earliestKeyCreationTime = Utils.currentTimeSeconds();
}
return earliestKeyCreationTime;
} finally {
this.keyChainGroupLock.unlock();
}
}
@Nullable
public Sha256Hash getLastBlockSeenHash() {
this.lock.lock();
try {
return this.lastBlockSeenHash;
} finally {
this.lock.unlock();
}
}
public void setLastBlockSeenHash(@Nullable Sha256Hash sha256Hash) {
this.lock.lock();
try {
this.lastBlockSeenHash = sha256Hash;
} finally {
this.lock.unlock();
}
}
public void setLastBlockSeenHeight(int i) {
this.lock.lock();
try {
this.lastBlockSeenHeight = i;
} finally {
this.lock.unlock();
}
}
public void setLastBlockSeenTimeSecs(long j) {
this.lock.lock();
try {
this.lastBlockSeenTimeSecs = j;
} finally {
this.lock.unlock();
}
}
public long getLastBlockSeenTimeSecs() {
this.lock.lock();
try {
return this.lastBlockSeenTimeSecs;
} finally {
this.lock.unlock();
}
}
@Nullable
public Date getLastBlockSeenTime() {
long lastBlockSeenTimeSecs = getLastBlockSeenTimeSecs();
if (lastBlockSeenTimeSecs == 0) {
return null;
}
return new Date(lastBlockSeenTimeSecs * 1000);
}
public int getLastBlockSeenHeight() {
this.lock.lock();
try {
return this.lastBlockSeenHeight;
} finally {
this.lock.unlock();
}
}
public int getVersion() {
return this.version;
}
public void setVersion(int i) {
this.version = i;
}
public void setDescription(String str) {
this.description = str;
}
public String getDescription() {
return this.description;
}
@Deprecated
public Coin getWatchedBalance() {
return getBalance();
}
@Deprecated
public Coin getWatchedBalance(CoinSelector coinSelector) {
return getBalance(coinSelector);
}
public Coin getBalance() {
return getBalance(BalanceType.AVAILABLE);
}
public Coin getBalance(BalanceType balanceType) {
this.lock.lock();
try {
boolean z = true;
if (balanceType != BalanceType.AVAILABLE && balanceType != BalanceType.AVAILABLE_SPENDABLE) {
if (balanceType != BalanceType.ESTIMATED && balanceType != BalanceType.ESTIMATED_SPENDABLE) {
throw new AssertionError("Unknown balance type");
}
if (balanceType != BalanceType.ESTIMATED_SPENDABLE) {
z = false;
}
List<TransactionOutput> calculateAllSpendCandidates = calculateAllSpendCandidates(false, z);
Coin coin = Coin.ZERO;
Iterator<TransactionOutput> it = calculateAllSpendCandidates.iterator();
while (it.hasNext()) {
coin = coin.add(it.next().getValue());
}
return coin;
}
return this.coinSelector.select(NetworkParameters.MAX_MONEY, calculateAllSpendCandidates(true, balanceType == BalanceType.AVAILABLE_SPENDABLE)).valueGathered;
} finally {
this.lock.unlock();
}
}
public Coin getBalance(CoinSelector coinSelector) {
this.lock.lock();
try {
Preconditions.checkNotNull(coinSelector);
return coinSelector.select(this.params.getMaxMoney(), calculateAllSpendCandidates(true, false)).valueGathered;
} finally {
this.lock.unlock();
}
}
private static class BalanceFutureRequest {
public SettableFuture<Coin> future;
public BalanceType type;
public Coin value;
private BalanceFutureRequest() {
}
}
public ListenableFuture<Coin> getBalanceFuture(Coin coin, BalanceType balanceType) {
this.lock.lock();
try {
SettableFuture<Coin> create = SettableFuture.create();
Coin balance = getBalance(balanceType);
if (balance.compareTo(coin) >= 0) {
create.set(balance);
} else {
BalanceFutureRequest balanceFutureRequest = new BalanceFutureRequest();
balanceFutureRequest.future = create;
balanceFutureRequest.value = coin;
balanceFutureRequest.type = balanceType;
this.balanceFutureRequests.add(balanceFutureRequest);
}
return create;
} finally {
this.lock.unlock();
}
}
public void checkBalanceFuturesLocked(@Nullable Coin coin) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
ListIterator<BalanceFutureRequest> listIterator = this.balanceFutureRequests.listIterator();
while (listIterator.hasNext()) {
final BalanceFutureRequest next = listIterator.next();
final Coin balance = getBalance(next.type);
if (balance.compareTo(next.value) >= 0) {
listIterator.remove();
Threading.USER_THREAD.execute(new Runnable() {
@Override
public void run() {
next.future.set(balance);
}
});
}
}
}
public Coin getTotalReceived() {
Coin coin = Coin.ZERO;
for (Transaction transaction : this.transactions.values()) {
Coin coin2 = Coin.ZERO;
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
if (transactionOutput.isMine(this)) {
coin2 = coin2.add(transactionOutput.getValue());
}
}
Iterator<TransactionInput> it = transaction.getInputs().iterator();
while (it.hasNext()) {
TransactionOutput connectedOutput = it.next().getConnectedOutput();
if (connectedOutput != null && connectedOutput.isMine(this)) {
coin2 = coin2.subtract(connectedOutput.getValue());
}
}
if (coin2.isPositive()) {
coin = coin.add(coin2);
}
}
return coin;
}
public Coin getTotalSent() {
Coin coin = Coin.ZERO;
for (Transaction transaction : this.transactions.values()) {
Coin coin2 = Coin.ZERO;
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
if (!transactionOutput.isMine(this)) {
coin2 = coin2.add(transactionOutput.getValue());
}
}
Coin coin3 = Coin.ZERO;
Iterator<TransactionInput> it = transaction.getInputs().iterator();
while (it.hasNext()) {
TransactionOutput connectedOutput = it.next().getConnectedOutput();
if (connectedOutput != null && connectedOutput.isMine(this)) {
coin3 = coin3.add(connectedOutput.getValue());
}
}
Coin inputSum = transaction.getInputSum();
if (coin3 != inputSum) {
coin2 = Coin.valueOf(new BigInteger(coin2.toString()).multiply(new BigInteger(coin3.toString())).divide(new BigInteger(inputSum.toString())).longValue());
}
coin = coin.add(coin2);
}
return coin;
}
public Transaction createSend(Address address, Coin coin) throws InsufficientMoneyException {
SendRequest sendRequest = SendRequest.to(address, coin);
if (this.params.getId().equals(NetworkParameters.ID_UNITTESTNET)) {
sendRequest.shuffleOutputs = false;
}
completeTx(sendRequest);
return sendRequest.tx;
}
public Transaction sendCoinsOffline(SendRequest sendRequest) throws InsufficientMoneyException {
this.lock.lock();
try {
completeTx(sendRequest);
commitTx(sendRequest.tx);
return sendRequest.tx;
} finally {
this.lock.unlock();
}
}
public SendResult sendCoins(TransactionBroadcaster transactionBroadcaster, Address address, Coin coin) throws InsufficientMoneyException {
return sendCoins(transactionBroadcaster, SendRequest.to(address, coin));
}
public SendResult sendCoins(TransactionBroadcaster transactionBroadcaster, SendRequest sendRequest) throws InsufficientMoneyException {
Preconditions.checkState(!this.lock.isHeldByCurrentThread());
Transaction sendCoinsOffline = sendCoinsOffline(sendRequest);
SendResult sendResult = new SendResult();
sendResult.tx = sendCoinsOffline;
sendResult.broadcast = transactionBroadcaster.broadcastTransaction(sendCoinsOffline);
sendResult.broadcastComplete = sendResult.broadcast.future();
return sendResult;
}
public SendResult sendCoins(SendRequest sendRequest) throws InsufficientMoneyException {
TransactionBroadcaster transactionBroadcaster = this.vTransactionBroadcaster;
Preconditions.checkState(transactionBroadcaster != null, "No transaction broadcaster is configured");
return sendCoins(transactionBroadcaster, sendRequest);
}
public Transaction sendCoins(Peer peer, SendRequest sendRequest) throws InsufficientMoneyException {
Transaction sendCoinsOffline = sendCoinsOffline(sendRequest);
peer.sendMessage(sendCoinsOffline);
return sendCoinsOffline;
}
public void completeTx(SendRequest sendRequest) throws InsufficientMoneyException {
CoinSelection select;
this.lock.lock();
try {
Preconditions.checkArgument(!sendRequest.completed, "Given SendRequest has already been completed.");
Coin coin = Coin.ZERO;
Iterator<TransactionOutput> it = sendRequest.tx.getOutputs().iterator();
while (it.hasNext()) {
coin = coin.add(it.next().getValue());
}
log.info("Completing send tx with {} outputs totalling {} and a fee of {}/kB", new Object[]{Integer.valueOf(sendRequest.tx.getOutputs().size()), coin.toFriendlyString(), sendRequest.feePerKb.toFriendlyString()});
Coin coin2 = Coin.ZERO;
for (TransactionInput transactionInput : sendRequest.tx.getInputs()) {
if (transactionInput.getConnectedOutput() != null) {
coin2 = coin2.add(transactionInput.getConnectedOutput().getValue());
} else {
log.warn("SendRequest transaction already has inputs but we don't know how much they are worth - they will be added to fee.");
}
}
Coin subtract = coin.subtract(coin2);
ArrayList arrayList = new ArrayList(sendRequest.tx.getInputs());
if (sendRequest.ensureMinRequiredFee && !sendRequest.emptyWallet) {
int i = 0;
for (TransactionOutput transactionOutput : sendRequest.tx.getOutputs()) {
if (transactionOutput.isDust()) {
throw new DustySendRequested();
}
if (transactionOutput.getScriptPubKey().isOpReturn()) {
i++;
}
}
if (i > 1) {
throw new MultipleOpReturnRequested();
}
}
List<TransactionOutput> calculateAllSpendCandidates = calculateAllSpendCandidates(true, sendRequest.missingSigsMode == MissingSigsMode.THROW);
TransactionOutput transactionOutput2 = null;
if (!sendRequest.emptyWallet) {
FeeCalculation calculateFee = calculateFee(sendRequest, subtract, arrayList, sendRequest.ensureMinRequiredFee, calculateAllSpendCandidates);
select = calculateFee.bestCoinSelection;
transactionOutput2 = calculateFee.bestChangeOutput;
} else {
Preconditions.checkState(sendRequest.tx.getOutputs().size() == 1, "Empty wallet TX must have a single output only.");
select = (sendRequest.coinSelector == null ? this.coinSelector : sendRequest.coinSelector).select(this.params.getMaxMoney(), calculateAllSpendCandidates);
sendRequest.tx.getOutput(0L).setValue(select.valueGathered);
log.info(" emptying {}", select.valueGathered.toFriendlyString());
}
Iterator<TransactionOutput> it2 = select.gathered.iterator();
while (it2.hasNext()) {
sendRequest.tx.addInput(it2.next());
}
if (sendRequest.emptyWallet) {
if (!adjustOutputDownwardsForFee(sendRequest.tx, select, sendRequest.feePerKb == null ? Coin.ZERO : sendRequest.feePerKb, sendRequest.ensureMinRequiredFee)) {
throw new CouldNotAdjustDownwards();
}
}
if (transactionOutput2 != null) {
sendRequest.tx.addOutput(transactionOutput2);
log.info(" with {} change", transactionOutput2.getValue().toFriendlyString());
}
if (sendRequest.shuffleOutputs) {
sendRequest.tx.shuffleOutputs();
}
if (sendRequest.signInputs) {
signTransaction(sendRequest);
}
if (sendRequest.tx.unsafeBitcoinSerialize().length > 100000) {
throw new ExceededMaxTransactionSize();
}
sendRequest.tx.getConfidence().setSource(TransactionConfidence.Source.SELF);
sendRequest.tx.setPurpose(Transaction.Purpose.USER_PAYMENT);
sendRequest.tx.setExchangeRate(sendRequest.exchangeRate);
sendRequest.tx.setMemo(sendRequest.memo);
sendRequest.completed = true;
log.info(" completed: {}", sendRequest.tx);
} finally {
this.lock.unlock();
}
}
public void signTransaction(SendRequest sendRequest) {
this.lock.lock();
try {
Transaction transaction = sendRequest.tx;
List<TransactionInput> inputs = transaction.getInputs();
List<TransactionOutput> outputs = transaction.getOutputs();
Preconditions.checkState(inputs.size() > 0);
Preconditions.checkState(outputs.size() > 0);
DecryptingKeyBag decryptingKeyBag = new DecryptingKeyBag(this, sendRequest.aesKey);
int size = transaction.getInputs().size();
for (int i = 0; i < size; i++) {
long j = i;
TransactionInput input = transaction.getInput(j);
if (input.getConnectedOutput() != null) {
try {
input.getScriptSig().correctlySpends(transaction, j, input.getConnectedOutput().getScriptPubKey());
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", Integer.valueOf(i));
} catch (ScriptException e) {
log.debug("Input contained an incorrect signature", e);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
RedeemData connectedRedeemData = input.getConnectedRedeemData(decryptingKeyBag);
Preconditions.checkNotNull(connectedRedeemData, "Transaction exists in wallet that we cannot redeem: %s", new Object[]{input.getOutpoint().getHash()});
input.setScriptSig(scriptPubKey.createEmptyInputScript(connectedRedeemData.keys.get(0), connectedRedeemData.redeemScript));
}
}
}
TransactionSigner.ProposedTransaction proposedTransaction = new TransactionSigner.ProposedTransaction(transaction);
for (TransactionSigner transactionSigner : this.signers) {
if (!transactionSigner.signInputs(proposedTransaction, decryptingKeyBag)) {
log.info("{} returned false for the tx", transactionSigner.getClass().getName());
}
}
new MissingSigResolutionSigner(sendRequest.missingSigsMode).signInputs(proposedTransaction, decryptingKeyBag);
} finally {
this.lock.unlock();
}
}
private boolean adjustOutputDownwardsForFee(Transaction transaction, CoinSelection coinSelection, Coin coin, boolean z) {
Coin divide = coin.multiply(transaction.unsafeBitcoinSerialize().length + estimateBytesForSigning(coinSelection)).divide(1000L);
if (z && divide.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) {
divide = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
}
TransactionOutput output = transaction.getOutput(0L);
output.setValue(output.getValue().subtract(divide));
return !output.isDust();
}
public List<TransactionOutput> calculateAllSpendCandidates() {
return calculateAllSpendCandidates(true, true);
}
@Deprecated
public List<TransactionOutput> calculateAllSpendCandidates(boolean z) {
return calculateAllSpendCandidates(z, true);
}
public List<TransactionOutput> calculateAllSpendCandidates(boolean z, boolean z2) {
List<TransactionOutput> calculateAllSpendCandidatesFromUTXOProvider;
this.lock.lock();
try {
if (this.vUTXOProvider == null) {
calculateAllSpendCandidatesFromUTXOProvider = new ArrayList<>(this.myUnspents.size());
Iterator<TransactionOutput> it = this.myUnspents.iterator();
while (it.hasNext()) {
TransactionOutput next = it.next();
if (!z2 || canSignFor(next.getScriptPubKey())) {
Transaction transaction = (Transaction) Preconditions.checkNotNull(next.getParentTransaction());
if (!z || transaction.isMature()) {
calculateAllSpendCandidatesFromUTXOProvider.add(next);
}
}
}
} else {
calculateAllSpendCandidatesFromUTXOProvider = calculateAllSpendCandidatesFromUTXOProvider(z);
}
return calculateAllSpendCandidatesFromUTXOProvider;
} finally {
this.lock.unlock();
}
}
public boolean canSignFor(Script script) {
if (script.isSentToRawPubKey()) {
ECKey findKeyFromPubKey = findKeyFromPubKey(script.getPubKey());
if (findKeyFromPubKey != null) {
return findKeyFromPubKey.isEncrypted() || findKeyFromPubKey.hasPrivKey();
}
return false;
}
if (script.isPayToScriptHash()) {
RedeemData findRedeemDataFromScriptHash = findRedeemDataFromScriptHash(script.getPubKeyHash());
return findRedeemDataFromScriptHash != null && canSignFor(findRedeemDataFromScriptHash.redeemScript);
}
if (script.isSentToAddress()) {
ECKey findKeyFromPubHash = findKeyFromPubHash(script.getPubKeyHash());
if (findKeyFromPubHash != null) {
return findKeyFromPubHash.isEncrypted() || findKeyFromPubHash.hasPrivKey();
}
return false;
}
if (script.isSentToMultiSig()) {
Iterator<ECKey> it = script.getPubKeys().iterator();
while (it.hasNext()) {
ECKey findKeyFromPubKey2 = findKeyFromPubKey(it.next().getPubKey());
if (findKeyFromPubKey2 != null && (findKeyFromPubKey2.isEncrypted() || findKeyFromPubKey2.hasPrivKey())) {
return true;
}
}
} else if (script.isSentToCLTVPaymentChannel()) {
byte[] cLTVPaymentChannelSenderPubKey = script.getCLTVPaymentChannelSenderPubKey();
ECKey findKeyFromPubKey3 = findKeyFromPubKey(cLTVPaymentChannelSenderPubKey);
if (findKeyFromPubKey3 != null && (findKeyFromPubKey3.isEncrypted() || findKeyFromPubKey3.hasPrivKey())) {
return true;
}
script.getCLTVPaymentChannelRecipientPubKey();
ECKey findKeyFromPubKey4 = findKeyFromPubKey(cLTVPaymentChannelSenderPubKey);
if (findKeyFromPubKey4 != null && (findKeyFromPubKey4.isEncrypted() || findKeyFromPubKey4.hasPrivKey())) {
return true;
}
}
return false;
}
protected LinkedList<TransactionOutput> calculateAllSpendCandidatesFromUTXOProvider(boolean z) {
Preconditions.checkState(this.lock.isHeldByCurrentThread());
UTXOProvider uTXOProvider = (UTXOProvider) Preconditions.checkNotNull(this.vUTXOProvider, "No UTXO provider has been set");
LinkedList<TransactionOutput> newLinkedList = Lists.newLinkedList();
try {
int chainHeadHeight = uTXOProvider.getChainHeadHeight();
for (UTXO utxo : getStoredOutputsFromUTXOProvider()) {
boolean isCoinbase = utxo.isCoinbase();
int height = (chainHeadHeight - utxo.getHeight()) + 1;
if (!z || !isCoinbase || height >= this.params.getSpendableCoinbaseDepth()) {
newLinkedList.add(new FreeStandingTransactionOutput(this.params, utxo, chainHeadHeight));
}
}
for (Transaction transaction : this.pending.values()) {
for (TransactionInput transactionInput : transaction.getInputs()) {
if (transactionInput.getConnectedOutput().isMine(this)) {
newLinkedList.remove(transactionInput.getConnectedOutput());
}
}
if (!z || transaction.isMature()) {
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
if (transactionOutput.isAvailableForSpending() && transactionOutput.isMine(this)) {
newLinkedList.add(transactionOutput);
}
}
}
}
return newLinkedList;
} catch (UTXOProviderException e) {
throw new RuntimeException("UTXO provider error", e);
}
}
protected List<UTXO> getStoredOutputsFromUTXOProvider() throws UTXOProviderException {
UTXOProvider uTXOProvider = (UTXOProvider) Preconditions.checkNotNull(this.vUTXOProvider, "No UTXO provider has been set");
ArrayList arrayList = new ArrayList();
List<ECKey> importedKeys = getImportedKeys();
importedKeys.addAll(getActiveKeyChain().getLeafKeys());
ArrayList arrayList2 = new ArrayList();
Iterator<ECKey> it = importedKeys.iterator();
while (it.hasNext()) {
arrayList2.add(new Address(this.params, it.next().getPubKeyHash()));
}
arrayList.addAll(uTXOProvider.getOpenTransactionOutputs(arrayList2));
return arrayList;
}
public CoinSelector getCoinSelector() {
this.lock.lock();
try {
return this.coinSelector;
} finally {
this.lock.unlock();
}
}
public void setCoinSelector(CoinSelector coinSelector) {
this.lock.lock();
try {
this.coinSelector = (CoinSelector) Preconditions.checkNotNull(coinSelector);
} finally {
this.lock.unlock();
}
}
public void allowSpendingUnconfirmedTransactions() {
setCoinSelector(AllowUnconfirmedCoinSelector.get());
}
@Nullable
public UTXOProvider getUTXOProvider() {
this.lock.lock();
try {
return this.vUTXOProvider;
} finally {
this.lock.unlock();
}
}
public void setUTXOProvider(@Nullable UTXOProvider uTXOProvider) {
boolean z;
this.lock.lock();
if (uTXOProvider != null) {
try {
if (!uTXOProvider.getParams().equals(this.params)) {
z = false;
Preconditions.checkArgument(z);
this.vUTXOProvider = uTXOProvider;
this.lock.unlock();
}
} catch (Throwable th) {
this.lock.unlock();
throw th;
}
}
z = true;
Preconditions.checkArgument(z);
this.vUTXOProvider = uTXOProvider;
this.lock.unlock();
}
private class FreeStandingTransactionOutput extends TransactionOutput {
private int chainHeight;
private UTXO output;
public FreeStandingTransactionOutput(NetworkParameters networkParameters, UTXO utxo, int i) {
super(networkParameters, (Transaction) null, utxo.getValue(), utxo.getScript().getProgram());
this.output = utxo;
this.chainHeight = i;
}
public UTXO getUTXO() {
return this.output;
}
@Override
public int getParentTransactionDepthInBlocks() {
return (this.chainHeight - this.output.getHeight()) + 1;
}
@Override
public int getIndex() {
return (int) this.output.getIndex();
}
@Override
public Sha256Hash getParentTransactionHash() {
return this.output.getHash();
}
}
private static class TxOffsetPair implements Comparable<TxOffsetPair> {
public final int offset;
public final Transaction tx;
public TxOffsetPair(Transaction transaction, int i) {
this.tx = transaction;
this.offset = i;
}
@Override
public int compareTo(TxOffsetPair txOffsetPair) {
return Ints.compare(this.offset, txOffsetPair.offset);
}
}
@Override
public void reorganize(StoredBlock storedBlock, List<StoredBlock> list, List<StoredBlock> list2) throws VerificationException {
this.lock.lock();
try {
Preconditions.checkState(this.confidenceChanged.size() == 0);
Preconditions.checkState(!this.insideReorg);
this.insideReorg = true;
Preconditions.checkState(this.onWalletChangedSuppressions == 0);
this.onWalletChangedSuppressions++;
ArrayListMultimap create = ArrayListMultimap.create();
for (Transaction transaction : getTransactions(true)) {
Map<Sha256Hash, Integer> appearsInHashes = transaction.getAppearsInHashes();
if (appearsInHashes != null) {
for (Map.Entry<Sha256Hash, Integer> entry : appearsInHashes.entrySet()) {
create.put(entry.getKey(), new TxOffsetPair(transaction, entry.getValue().intValue()));
}
}
}
Iterator it = create.keySet().iterator();
while (it.hasNext()) {
Collections.sort(create.get((Sha256Hash) it.next()));
}
ArrayList arrayList = new ArrayList(list.size());
log.info("Old part of chain (top to bottom):");
for (StoredBlock storedBlock2 : list) {
log.info(" {}", storedBlock2.getHeader().getHashAsString());
arrayList.add(storedBlock2.getHeader().getHash());
}
log.info("New part of chain (top to bottom):");
Iterator<StoredBlock> it2 = list2.iterator();
while (it2.hasNext()) {
log.info(" {}", it2.next().getHeader().getHashAsString());
}
Collections.reverse(list2);
LinkedList newLinkedList = Lists.newLinkedList();
Iterator it3 = arrayList.iterator();
while (it3.hasNext()) {
Iterator it4 = create.get((Sha256Hash) it3.next()).iterator();
while (it4.hasNext()) {
Transaction transaction2 = ((TxOffsetPair) it4.next()).tx;
Sha256Hash hash = transaction2.getHash();
if (transaction2.isCoinBase()) {
log.warn("Coinbase killed by re-org: {}", transaction2.getHashAsString());
killTxns(ImmutableSet.of(transaction2), null);
} else {
for (TransactionOutput transactionOutput : transaction2.getOutputs()) {
TransactionInput spentBy = transactionOutput.getSpentBy();
if (spentBy != null) {
if (transactionOutput.isMineOrWatched(this)) {
Preconditions.checkState(this.myUnspents.add(transactionOutput));
}
spentBy.disconnect();
}
}
newLinkedList.add(transaction2);
this.unspent.remove(hash);
this.spent.remove(hash);
Preconditions.checkState(!this.pending.containsKey(hash));
Preconditions.checkState(!this.dead.containsKey(hash));
}
}
}
Iterator it5 = newLinkedList.iterator();
while (it5.hasNext()) {
Transaction transaction3 = (Transaction) it5.next();
if (!transaction3.isCoinBase()) {
log.info(" ->pending {}", transaction3.getHash());
transaction3.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING);
this.confidenceChanged.put(transaction3, TransactionConfidence.Listener.ChangeReason.TYPE);
addWalletTransaction(WalletTransaction.Pool.PENDING, transaction3);
updateForSpends(transaction3, false);
}
}
int size = list.size();
log.info("depthToSubtract = " + size);
subtractDepth(size, this.spent.values());
subtractDepth(size, this.unspent.values());
subtractDepth(size, this.dead.values());
setLastBlockSeenHash(storedBlock.getHeader().getHash());
for (StoredBlock storedBlock3 : list2) {
log.info("Replaying block {}", storedBlock3.getHeader().getHashAsString());
for (TxOffsetPair txOffsetPair : create.get(storedBlock3.getHeader().getHash())) {
log.info(" tx {}", txOffsetPair.tx.getHash());
try {
receive(txOffsetPair.tx, storedBlock3, AbstractBlockChain.NewBlockType.BEST_CHAIN, txOffsetPair.offset);
} catch (ScriptException e) {
throw new RuntimeException(e);
}
}
notifyNewBestBlock(storedBlock3);
}
isConsistentOrThrow();
Coin balance = getBalance();
log.info("post-reorg balance is {}", balance.toFriendlyString());
queueOnReorganize();
this.insideReorg = false;
this.onWalletChangedSuppressions--;
maybeQueueOnWalletChanged();
checkBalanceFuturesLocked(balance);
informConfidenceListenersIfNotReorganizing();
saveLater();
} finally {
this.lock.unlock();
}
}
private void subtractDepth(int i, Collection<Transaction> collection) {
for (Transaction transaction : collection) {
if (transaction.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
transaction.getConfidence().setDepthInBlocks(transaction.getConfidence().getDepthInBlocks() - i);
this.confidenceChanged.put(transaction, TransactionConfidence.Listener.ChangeReason.DEPTH);
}
}
}
@Override
public void beginBloomFilterCalculation() {
if (this.bloomFilterGuard.incrementAndGet() > 1) {
return;
}
this.lock.lock();
this.keyChainGroupLock.lock();
calcBloomOutPointsLocked();
}
private void calcBloomOutPointsLocked() {
this.bloomOutPoints.clear();
HashSet hashSet = new HashSet();
hashSet.addAll(this.unspent.values());
hashSet.addAll(this.spent.values());
hashSet.addAll(this.pending.values());
Iterator it = hashSet.iterator();
while (it.hasNext()) {
for (TransactionOutput transactionOutput : ((Transaction) it.next()).getOutputs()) {
try {
if (isTxOutputBloomFilterable(transactionOutput)) {
this.bloomOutPoints.add(transactionOutput.getOutPointFor());
}
} catch (ScriptException e) {
throw new RuntimeException(e);
}
}
}
}
@Override
@GuardedBy("keyChainGroupLock")
public void endBloomFilterCalculation() {
if (this.bloomFilterGuard.decrementAndGet() > 0) {
return;
}
this.bloomOutPoints.clear();
this.keyChainGroupLock.unlock();
this.lock.unlock();
}
@Override
public int getBloomFilterElementCount() {
beginBloomFilterCalculation();
try {
return this.bloomOutPoints.size() + this.keyChainGroup.getBloomFilterElementCount() + this.watchedScripts.size();
} finally {
endBloomFilterCalculation();
}
}
@Override
public boolean isRequiringUpdateAllBloomFilter() {
this.keyChainGroupLock.lock();
try {
return !this.watchedScripts.isEmpty();
} finally {
this.keyChainGroupLock.unlock();
}
}
public BloomFilter getBloomFilter(double d) {
beginBloomFilterCalculation();
try {
return getBloomFilter(getBloomFilterElementCount(), d, (long) (Math.random() * 9.223372036854776E18d));
} finally {
endBloomFilterCalculation();
}
}
@Override
@GuardedBy("keyChainGroupLock")
public BloomFilter getBloomFilter(int i, double d, long j) {
beginBloomFilterCalculation();
try {
BloomFilter bloomFilter = this.keyChainGroup.getBloomFilter(i, d, j);
Iterator<Script> it = this.watchedScripts.iterator();
while (it.hasNext()) {
for (ScriptChunk scriptChunk : it.next().getChunks()) {
if (!scriptChunk.isOpCode() && scriptChunk.data.length >= 8) {
bloomFilter.insert(scriptChunk.data);
}
}
}
Iterator<TransactionOutPoint> it2 = this.bloomOutPoints.iterator();
while (it2.hasNext()) {
bloomFilter.insert(it2.next().unsafeBitcoinSerialize());
}
return bloomFilter;
} finally {
endBloomFilterCalculation();
}
}
private boolean isTxOutputBloomFilterable(TransactionOutput transactionOutput) {
Script scriptPubKey = transactionOutput.getScriptPubKey();
return ((scriptPubKey.isSentToRawPubKey() || scriptPubKey.isPayToScriptHash()) && this.myUnspents.contains(transactionOutput)) || this.watchedScripts.contains(scriptPubKey);
}
public boolean checkForFilterExhaustion(FilteredBlock filteredBlock) {
this.keyChainGroupLock.lock();
try {
int combinedKeyLookaheadEpochs = this.keyChainGroup.getCombinedKeyLookaheadEpochs();
Iterator<Transaction> it = filteredBlock.getAssociatedTransactions().values().iterator();
while (it.hasNext()) {
markKeysAsUsed(it.next());
}
int combinedKeyLookaheadEpochs2 = this.keyChainGroup.getCombinedKeyLookaheadEpochs();
Preconditions.checkState(combinedKeyLookaheadEpochs2 >= combinedKeyLookaheadEpochs);
return combinedKeyLookaheadEpochs2 > combinedKeyLookaheadEpochs;
} finally {
this.keyChainGroupLock.unlock();
}
}
public void addExtension(WalletExtension walletExtension) {
String walletExtensionID = ((WalletExtension) Preconditions.checkNotNull(walletExtension)).getWalletExtensionID();
this.lock.lock();
try {
if (this.extensions.containsKey(walletExtensionID)) {
throw new IllegalStateException("Cannot add two extensions with the same ID: " + walletExtensionID);
}
this.extensions.put(walletExtensionID, walletExtension);
saveNow();
} finally {
this.lock.unlock();
}
}
public WalletExtension addOrGetExistingExtension(WalletExtension walletExtension) {
String walletExtensionID = ((WalletExtension) Preconditions.checkNotNull(walletExtension)).getWalletExtensionID();
this.lock.lock();
try {
WalletExtension walletExtension2 = this.extensions.get(walletExtensionID);
if (walletExtension2 != null) {
return walletExtension2;
}
this.extensions.put(walletExtensionID, walletExtension);
saveNow();
return walletExtension;
} finally {
this.lock.unlock();
}
}
public void addOrUpdateExtension(WalletExtension walletExtension) {
String walletExtensionID = ((WalletExtension) Preconditions.checkNotNull(walletExtension)).getWalletExtensionID();
this.lock.lock();
try {
this.extensions.put(walletExtensionID, walletExtension);
saveNow();
} finally {
this.lock.unlock();
}
}
public Map<String, WalletExtension> getExtensions() {
this.lock.lock();
try {
return ImmutableMap.copyOf(this.extensions);
} finally {
this.lock.unlock();
}
}
public void deserializeExtension(WalletExtension walletExtension, byte[] bArr) throws Exception {
this.lock.lock();
this.keyChainGroupLock.lock();
try {
walletExtension.deserializeWalletExtension(this, bArr);
this.extensions.put(walletExtension.getWalletExtensionID(), walletExtension);
} finally {
try {
} finally {
}
}
}
@Override
public void setTag(String str, ByteString byteString) {
super.setTag(str, byteString);
saveNow();
}
private static class FeeCalculation {
public TransactionOutput bestChangeOutput;
public CoinSelection bestCoinSelection;
private FeeCalculation() {
}
}
public org.bitcoinj.wallet.Wallet.FeeCalculation calculateFee(org.bitcoinj.wallet.SendRequest r21, org.bitcoinj.core.Coin r22, java.util.List<org.bitcoinj.core.TransactionInput> r23, boolean r24, java.util.List<org.bitcoinj.core.TransactionOutput> r25) throws org.bitcoinj.core.InsufficientMoneyException {
throw new UnsupportedOperationException("Method not decompiled: org.bitcoinj.wallet.Wallet.calculateFee(org.bitcoinj.wallet.SendRequest, org.bitcoinj.core.Coin, java.util.List, boolean, java.util.List):org.bitcoinj.wallet.Wallet$FeeCalculation");
}
private void resetTxInputs(SendRequest sendRequest, List<TransactionInput> list) {
sendRequest.tx.clearInputs();
Iterator<TransactionInput> it = list.iterator();
while (it.hasNext()) {
sendRequest.tx.addInput(it.next());
}
}
private int estimateBytesForSigning(CoinSelection coinSelection) {
Script script;
Iterator<TransactionOutput> it = coinSelection.gathered.iterator();
int i = 0;
while (it.hasNext()) {
try {
Script scriptPubKey = it.next().getScriptPubKey();
ECKey eCKey = null;
if (scriptPubKey.isSentToAddress()) {
ECKey findKeyFromPubHash = findKeyFromPubHash(scriptPubKey.getPubKeyHash());
Preconditions.checkNotNull(findKeyFromPubHash, "Coin selection includes unspendable outputs");
eCKey = findKeyFromPubHash;
script = null;
} else if (scriptPubKey.isPayToScriptHash()) {
script = findRedeemDataFromScriptHash(scriptPubKey.getPubKeyHash()).redeemScript;
Preconditions.checkNotNull(script, "Coin selection includes unspendable outputs");
} else {
script = null;
}
i += scriptPubKey.getNumberOfBytesRequiredToSpend(eCKey, script);
} catch (ScriptException e) {
throw new IllegalStateException(e);
}
}
return i;
}
public void setTransactionBroadcaster(@Nullable TransactionBroadcaster transactionBroadcaster) {
Transaction[] transactionArr = new Transaction[0];
this.lock.lock();
try {
if (this.vTransactionBroadcaster != transactionBroadcaster) {
this.vTransactionBroadcaster = transactionBroadcaster;
if (transactionBroadcaster != null) {
Transaction[] transactionArr2 = (Transaction[]) this.pending.values().toArray(transactionArr);
this.lock.unlock();
for (Transaction transaction : transactionArr2) {
TransactionConfidence.ConfidenceType confidenceType = transaction.getConfidence().getConfidenceType();
Preconditions.checkState(confidenceType == TransactionConfidence.ConfidenceType.PENDING || confidenceType == TransactionConfidence.ConfidenceType.IN_CONFLICT, "Expected PENDING or IN_CONFLICT, was %s.", new Object[]{confidenceType});
log.info("New broadcaster so uploading waiting tx {}", transaction.getHash());
transactionBroadcaster.broadcastTransaction(transaction);
}
}
}
} finally {
this.lock.unlock();
}
}
public void setKeyRotationTime(Date date) {
setKeyRotationTime(date.getTime() / 1000);
}
@Nullable
public Date getKeyRotationTime() {
long j = this.vKeyRotationTimestamp;
if (j != 0) {
return new Date(j * 1000);
}
return null;
}
public void setKeyRotationTime(long j) {
Preconditions.checkArgument(j <= Utils.currentTimeSeconds(), "Given time (%s) cannot be in the future.", new Object[]{Utils.dateTimeFormat(1000 * j)});
this.vKeyRotationTimestamp = j;
saveNow();
}
public boolean isKeyRotating(ECKey eCKey) {
long j = this.vKeyRotationTimestamp;
return j != 0 && eCKey.getCreationTimeSeconds() < j;
}
@Deprecated
public ListenableFuture<List<Transaction>> maybeDoMaintenance(@Nullable KeyParameter keyParameter, boolean z) throws DeterministicUpgradeRequiresPassword {
return doMaintenance(keyParameter, z);
}
public ListenableFuture<List<Transaction>> doMaintenance(@Nullable KeyParameter keyParameter, boolean z) throws DeterministicUpgradeRequiresPassword {
this.lock.lock();
this.keyChainGroupLock.lock();
try {
List<Transaction> maybeRotateKeys = maybeRotateKeys(keyParameter, z);
if (!z) {
return Futures.immediateFuture(maybeRotateKeys);
}
this.keyChainGroupLock.unlock();
this.lock.unlock();
Preconditions.checkState(!this.lock.isHeldByCurrentThread());
ArrayList arrayList = new ArrayList(maybeRotateKeys.size());
TransactionBroadcaster transactionBroadcaster = this.vTransactionBroadcaster;
Iterator<Transaction> it = maybeRotateKeys.iterator();
while (it.hasNext()) {
try {
ListenableFuture<Transaction> future = transactionBroadcaster.broadcastTransaction(it.next()).future();
arrayList.add(future);
Futures.addCallback(future, new FutureCallback<Transaction>() {
public void onSuccess(Transaction transaction) {
Wallet.log.info("Successfully broadcast key rotation tx: {}", transaction);
}
public void onFailure(Throwable th) {
Wallet.log.error("Failed to broadcast key rotation tx", th);
}
});
} catch (Exception e) {
log.error("Failed to broadcast rekey tx", e);
}
}
return Futures.allAsList(arrayList);
} finally {
this.keyChainGroupLock.unlock();
this.lock.unlock();
}
}
@GuardedBy("keyChainGroupLock")
private List<Transaction> maybeRotateKeys(@Nullable KeyParameter keyParameter, boolean z) throws DeterministicUpgradeRequiresPassword {
Transaction rekeyOneBatch;
Preconditions.checkState(this.lock.isHeldByCurrentThread());
Preconditions.checkState(this.keyChainGroupLock.isHeldByCurrentThread());
LinkedList newLinkedList = Lists.newLinkedList();
long j = this.vKeyRotationTimestamp;
if (j == 0) {
return newLinkedList;
}
boolean z2 = true;
Iterator<DeterministicKeyChain> it = this.keyChainGroup.getDeterministicKeyChains().iterator();
while (true) {
if (!it.hasNext()) {
break;
}
if (it.next().getEarliestKeyCreationTime() >= j) {
z2 = false;
break;
}
}
if (z2) {
try {
if (this.keyChainGroup.getImportedKeys().isEmpty()) {
log.info("All HD chains are currently rotating and we have no random keys, creating fresh HD chain ...");
this.keyChainGroup.createAndActivateNewHDChain();
} else {
log.info("All HD chains are currently rotating, attempting to create a new one from the next oldest non-rotating key material ...");
this.keyChainGroup.upgradeToDeterministic(j, keyParameter);
log.info(" ... upgraded to HD again, based on next best oldest key.");
}
} catch (AllRandomKeysRotating unused) {
log.info(" ... no non-rotating random keys available, generating entirely new HD tree: backup required after this.");
this.keyChainGroup.createAndActivateNewHDChain();
}
saveNow();
}
do {
rekeyOneBatch = rekeyOneBatch(j, keyParameter, newLinkedList, z);
if (rekeyOneBatch != null) {
newLinkedList.add(rekeyOneBatch);
}
if (rekeyOneBatch == null) {
break;
}
} while (rekeyOneBatch.getInputs().size() == 600);
return newLinkedList;
}
@Nullable
private Transaction rekeyOneBatch(long j, @Nullable KeyParameter keyParameter, List<Transaction> list, boolean z) {
this.lock.lock();
try {
try {
boolean z2 = true;
FilteringCoinSelector filteringCoinSelector = new FilteringCoinSelector(new KeyTimeCoinSelector(this, j, true));
Iterator<Transaction> it = list.iterator();
while (it.hasNext()) {
filteringCoinSelector.excludeOutputsSpentBy(it.next());
}
CoinSelection select = filteringCoinSelector.select(Coin.ZERO, calculateAllSpendCandidates());
if (!select.valueGathered.equals(Coin.ZERO)) {
maybeUpgradeToHD(keyParameter);
Transaction transaction = new Transaction(this.params);
Iterator<TransactionOutput> it2 = select.gathered.iterator();
while (it2.hasNext()) {
transaction.addInput(it2.next());
}
transaction.addOutput(select.valueGathered, z ? freshReceiveAddress() : currentReceiveAddress());
if (!adjustOutputDownwardsForFee(transaction, select, Transaction.DEFAULT_TX_FEE, true)) {
log.error("Failed to adjust rekey tx for fees.");
} else {
transaction.getConfidence().setSource(TransactionConfidence.Source.SELF);
transaction.setPurpose(Transaction.Purpose.KEY_ROTATION);
SendRequest forTx = SendRequest.forTx(transaction);
forTx.aesKey = keyParameter;
if (z) {
signTransaction(forTx);
}
if (transaction.unsafeBitcoinSerialize().length >= 100000) {
z2 = false;
}
Preconditions.checkState(z2);
return transaction;
}
}
return null;
} catch (VerificationException e) {
throw new RuntimeException(e);
}
} finally {
this.lock.unlock();
}
}
}