/* * Copyright 2013 Google Inc. * Copyright 2014 Andreas Schildbach * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.bitcoinj.wallet; import com.google.common.annotations.*; import com.google.common.base.*; import com.google.common.collect.*; import com.google.common.primitives.*; import com.google.common.util.concurrent.*; import com.google.protobuf.*; import net.jcip.annotations.*; import org.bitcoin.protocols.payments.Protos.*; import org.bitcoinj.core.listeners.*; import org.bitcoinj.core.AbstractBlockChain; import org.bitcoinj.core.Address; import org.bitcoinj.core.BlockChain; 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.Message; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Peer; import org.bitcoinj.core.PeerFilterProvider; import org.bitcoinj.core.PeerGroup; 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.VarInt; import org.bitcoinj.core.VerificationException; import org.bitcoinj.core.TransactionConfidence.*; import org.bitcoinj.crypto.*; import org.bitcoinj.script.*; import org.bitcoinj.signers.*; import org.bitcoinj.utils.*; import org.bitcoinj.wallet.Protos.Wallet.*; 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.*; import org.spongycastle.crypto.params.*; import javax.annotation.*; import java.io.*; import java.math.BigInteger; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util.concurrent.locks.*; import static com.google.common.base.Preconditions.*; // To do list: // // - Take all wallet-relevant data out of Transaction and put it into WalletTransaction. Make Transaction immutable. // - Only store relevant transaction outputs, don't bother storing the rest of the data. Big RAM saving. // - Split block chain and tx output tracking into a superclass that doesn't have any key or spending related code. // - Simplify how transactions are tracked and stored: in particular, have the wallet maintain positioning information // for transactions independent of the transactions themselves, so the timeline can be walked without having to // process and sort every single transaction. // - Split data persistence out into a backend class and make the wallet transactional, so we can store a wallet // in a database not just in RAM. // - Make clearing of transactions able to only rewind the wallet a certain distance instead of all blocks. // - Make it scale: // - eliminate all the algorithms with quadratic complexity (or worse) // - don't require everything to be held in RAM at once // - consider allowing eviction of no longer re-orgable transactions or keys that were used up // // Finally, find more ways to break the class up and decompose it. Currently every time we move code out, other code // fills up the lines saved! /** * <p>A Wallet stores keys and a record of transactions that send and receive value from those keys. Using these, * it is able to create new transactions that spend the recorded transactions, and this is the fundamental operation * of the Bitcoin protocol.</p> * * <p>To learn more about this class, read <b><a href="https://bitcoinj.github.io/working-with-the-wallet"> * working with the wallet.</a></b></p> * * <p>To fill up a Wallet with transactions, you need to use it in combination with a {@link BlockChain} and various * other objects, see the <a href="https://bitcoinj.github.io/getting-started">Getting started</a> tutorial * on the website to learn more about how to set everything up.</p> * * <p>Wallets can be serialized using protocol buffers. You need to save the wallet whenever it changes, there is an * auto-save feature that simplifies this for you although you're still responsible for manually triggering a save when * your app is about to quit because the auto-save feature waits a moment before actually committing to disk to avoid IO * thrashing when the wallet is changing very fast (eg due to a block chain sync). See * {@link Wallet#autosaveToFile(java.io.File, long, java.util.concurrent.TimeUnit, org.bitcoinj.wallet.WalletFiles.Listener)} * for more information about this.</p> */ public class Wallet extends BaseTaggableObject implements NewBestBlockListener, TransactionReceivedInBlockListener, PeerFilterProvider, KeyBag, TransactionBag, ReorganizeListener { private static final Logger log = LoggerFactory.getLogger(Wallet.class); private static final int MINIMUM_BLOOM_DATA_LENGTH = 8; // Ordering: lock > keyChainGroupLock. KeyChainGroup is protected separately to allow fast querying of current receive address // even if the wallet itself is busy e.g. saving or processing a big reorg. Useful for reducing UI latency. protected final ReentrantLock lock = Threading.lock("wallet"); protected final ReentrantLock keyChainGroupLock = Threading.lock("wallet-keychaingroup"); // The various pools below give quick access to wallet-relevant transactions by the state they're in: // // Pending: Transactions that didn't make it into the best chain yet. Pending transactions can be killed if a // double spend against them appears in the best chain, in which case they move to the dead pool. // If a double spend appears in the pending state as well, we update the confidence type // of all txns in conflict to IN_CONFLICT and wait for the miners to resolve the race. // Unspent: Transactions that appeared in the best chain and have outputs we can spend. Note that we store the // entire transaction in memory even though for spending purposes we only really need the outputs, the // reason being that this simplifies handling of re-orgs. It would be worth fixing this in future. // Spent: Transactions that appeared in the best chain but don't have any spendable outputs. They're stored here // for history browsing/auditing reasons only and in future will probably be flushed out to some other // kind of cold storage or just removed. // Dead: Transactions that we believe will never confirm get moved here, out of pending. Note that Bitcoin // Core has no notion of dead-ness: the assumption is that double spends won't happen so there's no // need to notify the user about them. We take a more pessimistic approach and try to track the fact that // transactions have been double spent so applications can do something intelligent (cancel orders, show // to the user in the UI, etc). A transaction can leave dead and move into spent/unspent if there is a // re-org to a chain that doesn't include the double spend. private final Map<Sha256Hash, Transaction> pending; private final Map<Sha256Hash, Transaction> unspent; private final Map<Sha256Hash, Transaction> spent; private final Map<Sha256Hash, Transaction> dead; // All transactions together. protected final Map<Sha256Hash, Transaction> transactions; // All the TransactionOutput objects that we could spend (ignoring whether we have the private key or not). // Used to speed up various calculations. protected final HashSet<TransactionOutput> myUnspents = Sets.newHashSet(); // Transactions that were dropped by the risk analysis system. These are not in any pools and not serialized // to disk. We have to keep them around because if we ignore a tx because we think it will never confirm, but // then it actually does confirm and does so within the same network session, remote peers will not resend us // the tx data along with the Bloom filtered block, as they know we already received it once before // (so it would be wasteful to repeat). Thus we keep them around here for a while. If we drop our network // connections then the remote peers will forget that we were sent the tx data previously and send it again // when relaying a filtered merkleblock. private final LinkedHashMap<Sha256Hash, Transaction> riskDropped = new LinkedHashMap<Sha256Hash, Transaction>() { @Override protected boolean removeEldestEntry(Map.Entry<Sha256Hash, Transaction> eldest) { return size() > 1000; } }; // The key chain group is not thread safe, and generally the whole hierarchy of objects should not be mutated // outside the wallet lock. So don't expose this object directly via any accessors! @GuardedBy("keyChainGroupLock") private KeyChainGroup keyChainGroup; // A list of scripts watched by this wallet. @GuardedBy("keyChainGroupLock") private Set<Script> watchedScripts; protected final Context context; protected final NetworkParameters params; @Nullable private Sha256Hash lastBlockSeenHash; private int lastBlockSeenHeight; private long lastBlockSeenTimeSecs; private final CopyOnWriteArrayList<ListenerRegistration<WalletChangeEventListener>> changeListeners = new CopyOnWriteArrayList<ListenerRegistration<WalletChangeEventListener>>(); private final CopyOnWriteArrayList<ListenerRegistration<WalletCoinsReceivedEventListener>> coinsReceivedListeners = new CopyOnWriteArrayList<ListenerRegistration<WalletCoinsReceivedEventListener>>(); private final CopyOnWriteArrayList<ListenerRegistration<WalletCoinsSentEventListener>> coinsSentListeners = new CopyOnWriteArrayList<ListenerRegistration<WalletCoinsSentEventListener>>(); private final CopyOnWriteArrayList<ListenerRegistration<WalletReorganizeEventListener>> reorganizeListeners = new CopyOnWriteArrayList<ListenerRegistration<WalletReorganizeEventListener>>(); private final CopyOnWriteArrayList<ListenerRegistration<ScriptsChangeEventListener>> scriptChangeListeners = new CopyOnWriteArrayList<ListenerRegistration<ScriptsChangeEventListener>>(); private final CopyOnWriteArrayList<ListenerRegistration<TransactionConfidenceEventListener>> transactionConfidenceListeners = new CopyOnWriteArrayList<ListenerRegistration<TransactionConfidenceEventListener>>(); // A listener that relays confidence changes from the transaction confidence object to the wallet event listener, // as a convenience to API users so they don't have to register on every transaction themselves. private TransactionConfidence.Listener txConfidenceListener; // If a TX hash appears in this set then notifyNewBestBlock will ignore it, as its confidence was already set up // in receive() via Transaction.setBlockAppearance(). As the BlockChain always calls notifyNewBestBlock even if // it sent transactions to the wallet, without this we'd double count. private HashSet<Sha256Hash> ignoreNextNewBlock; // Whether or not to ignore pending transactions that are considered risky by the configured risk analyzer. private boolean acceptRiskyTransactions; // Object that performs risk analysis of pending transactions. We might reject transactions that seem like // a high risk of being a double spending attack. private RiskAnalysis.Analyzer riskAnalyzer = DefaultRiskAnalysis.FACTORY; // Stuff for notifying transaction objects that we changed their confidences. The purpose of this is to avoid // spuriously sending lots of repeated notifications to listeners that API users aren't really interested in as a // side effect of how the code is written (e.g. during re-orgs confidence data gets adjusted multiple times). private int onWalletChangedSuppressions; private boolean insideReorg; private Map<Transaction, TransactionConfidence.Listener.ChangeReason> confidenceChanged; protected volatile WalletFiles vFileManager; // Object that is used to send transactions asynchronously when the wallet requires it. protected volatile TransactionBroadcaster vTransactionBroadcaster; // UNIX time in seconds. Money controlled by keys created before this time will be automatically respent to a key // that was created after it. Useful when you believe some keys have been compromised. private volatile long vKeyRotationTimestamp; protected CoinSelector coinSelector = new DefaultCoinSelector(); // The wallet version. This is an int that can be used to track breaking changes in the wallet format. // You can also use it to detect wallets that come from the future (ie they contain features you // do not know how to deal with). private int version; // User-provided description that may help people keep track of what a wallet is for. private String description; // Stores objects that know how to serialize/unserialize themselves to byte streams and whether they're mandatory // or not. The string key comes from the extension itself. private final HashMap<String, WalletExtension> extensions; // Objects that perform transaction signing. Applied subsequently one after another @GuardedBy("lock") private List<TransactionSigner> signers; // If this is set then the wallet selects spendable candidate outputs from a UTXO provider. @Nullable private volatile UTXOProvider vUTXOProvider; /** * Creates a new, empty wallet with a randomly chosen seed and no transactions. Make sure to provide for sufficient * backup! Any keys will be derived from the seed. If you want to restore a wallet from disk instead, see * {@link #loadFromFile}. */ public Wallet(NetworkParameters params) { this(Context.getOrCreate(params)); } /** * Creates a new, empty wallet with a randomly chosen seed and no transactions. Make sure to provide for sufficient * backup! Any keys will be derived from the seed. If you want to restore a wallet from disk instead, see * {@link #loadFromFile}. */ public Wallet(Context context) { this(context, new KeyChainGroup(context.getParams())); } public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed) { return new Wallet(params, new KeyChainGroup(params, seed)); } /** * Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. A * watching key corresponds to account zero in the recommended BIP32 key hierarchy. */ public static Wallet fromWatchingKey(NetworkParameters params, DeterministicKey watchKey) { return new Wallet(params, new KeyChainGroup(params, watchKey)); } /** * Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. A * watching key corresponds to account zero in the recommended BIP32 key hierarchy. The key is specified in base58 * notation and the creation time of the key. If you don't know the creation time, you can pass * {@link DeterministicHierarchy#BIP32_STANDARDISATION_TIME_SECS}. */ public static Wallet fromWatchingKeyB58(NetworkParameters params, String watchKeyB58, long creationTimeSeconds) { final DeterministicKey watchKey = DeterministicKey.deserializeB58(null, watchKeyB58, params); watchKey.setCreationTimeSeconds(creationTimeSeconds); return fromWatchingKey(params, watchKey); } /** * Creates a wallet containing a given set of keys. All further keys will be derived from the oldest key. */ public static Wallet fromKeys(NetworkParameters params, List<ECKey> keys) { for (ECKey key : keys) checkArgument(!(key instanceof DeterministicKey)); KeyChainGroup group = new KeyChainGroup(params); group.importKeys(keys); return new Wallet(params, group); } public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) { this(Context.getOrCreate(params), keyChainGroup); } private Wallet(Context context, KeyChainGroup keyChainGroup) { this.context = context; this.params = context.getParams(); this.keyChainGroup = checkNotNull(keyChainGroup); if (params.getId().equals(NetworkParameters.ID_UNITTESTNET)) this.keyChainGroup.setLookaheadSize(5); // Cut down excess computation for unit tests. // If this keyChainGroup was created fresh just now (new wallet), make HD so a backup can be made immediately // without having to call current/freshReceiveKey. If there are already keys in the chain of any kind then // we're probably being deserialized so leave things alone: the API user can upgrade later. if (this.keyChainGroup.numKeys() == 0) this.keyChainGroup.createAndActivateNewHDChain(); watchedScripts = Sets.newHashSet(); unspent = new HashMap<Sha256Hash, Transaction>(); spent = new HashMap<Sha256Hash, Transaction>(); pending = new HashMap<Sha256Hash, Transaction>(); dead = new HashMap<Sha256Hash, Transaction>(); transactions = new HashMap<Sha256Hash, Transaction>(); extensions = new HashMap<String, WalletExtension>(); // Use a linked hash map to ensure ordering of event listeners is correct. confidenceChanged = new LinkedHashMap<Transaction, TransactionConfidence.Listener.ChangeReason>(); signers = new ArrayList<TransactionSigner>(); addTransactionSigner(new LocalTransactionSigner()); createTransientState(); } private void createTransientState() { ignoreNextNewBlock = new HashSet<Sha256Hash>(); txConfidenceListener = new TransactionConfidence.Listener() { @Override public void onConfidenceChanged(TransactionConfidence confidence, TransactionConfidence.Listener.ChangeReason reason) { // This will run on the user code thread so we shouldn't do anything too complicated here. // We only want to queue a wallet changed event and auto-save if the number of peers announcing // the transaction has changed, as that confidence change is made by the networking code which // doesn't necessarily know at that point which wallets contain which transactions, so it's up // to us to listen for that. Other types of confidence changes (type, etc) are triggered by us, // so we'll queue up a wallet change event in other parts of the code. if (reason == ChangeReason.SEEN_PEERS) { lock.lock(); try { checkBalanceFuturesLocked(null); Transaction tx = getTransaction(confidence.getTransactionHash()); queueOnTransactionConfidenceChanged(tx); maybeQueueOnWalletChanged(); } finally { lock.unlock(); } } } }; acceptRiskyTransactions = false; } public NetworkParameters getNetworkParameters() { return params; } /** * Gets the active keychain via {@link KeyChainGroup#getActiveKeyChain()} */ public DeterministicKeyChain getActiveKeyChain() { return keyChainGroup.getActiveKeyChain(); } /** * <p>Adds given transaction signer to the list of signers. It will be added to the end of the signers list, so if * this wallet already has some signers added, given signer will be executed after all of them.</p> * <p>Transaction signer should be fully initialized before adding to the wallet, otherwise {@link IllegalStateException} * will be thrown</p> */ public final void addTransactionSigner(TransactionSigner signer) { lock.lock(); try { if (signer.isReady()) signers.add(signer); else throw new IllegalStateException("Signer instance is not ready to be added into Wallet: " + signer.getClass()); } finally { lock.unlock(); } } public List<TransactionSigner> getTransactionSigners() { lock.lock(); try { return ImmutableList.copyOf(signers); } finally { lock.unlock(); } } /******************************************************************************************************************/ //region Key Management /** * Returns a key that hasn't been seen in a transaction yet, and which is suitable for displaying in a wallet * user interface as "a convenient key to receive funds on" when the purpose parameter is * {@link org.bitcoinj.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS}. The returned key is stable until * it's actually seen in a pending or confirmed transaction, at which point this method will start returning * a different key (for each purpose independently). */ public DeterministicKey currentKey(KeyChain.KeyPurpose purpose) { keyChainGroupLock.lock(); try { maybeUpgradeToHD(); return keyChainGroup.currentKey(purpose); } finally { keyChainGroupLock.unlock(); } } /** * An alias for calling {@link #currentKey(org.bitcoinj.wallet.KeyChain.KeyPurpose)} with * {@link org.bitcoinj.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter. */ public DeterministicKey currentReceiveKey() { return currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); } /** * Returns address for a {@link #currentKey(org.bitcoinj.wallet.KeyChain.KeyPurpose)} */ public Address currentAddress(KeyChain.KeyPurpose purpose) { keyChainGroupLock.lock(); try { maybeUpgradeToHD(); return keyChainGroup.currentAddress(purpose); } finally { keyChainGroupLock.unlock(); } } /** * An alias for calling {@link #currentAddress(org.bitcoinj.wallet.KeyChain.KeyPurpose)} with * {@link org.bitcoinj.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter. */ public Address currentReceiveAddress() { return currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); } /** * Returns a key that has not been returned by this method before (fresh). You can think of this as being * a newly created key, although the notion of "create" is not really valid for a * {@link org.bitcoinj.wallet.DeterministicKeyChain}. When the parameter is * {@link org.bitcoinj.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} the returned key is suitable for being put * into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key out * to someone who wishes to send money. */ public DeterministicKey freshKey(KeyChain.KeyPurpose purpose) { return freshKeys(purpose, 1).get(0); } /** * Returns a key/s that has not been returned by this method before (fresh). You can think of this as being * a newly created key/s, although the notion of "create" is not really valid for a * {@link org.bitcoinj.wallet.DeterministicKeyChain}. When the parameter is * {@link org.bitcoinj.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} the returned key is suitable for being put * into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key/s out * to someone who wishes to send money. */ public List<DeterministicKey> freshKeys(KeyChain.KeyPurpose purpose, int numberOfKeys) { List<DeterministicKey> keys; keyChainGroupLock.lock(); try { maybeUpgradeToHD(); keys = keyChainGroup.freshKeys(purpose, numberOfKeys); } finally { keyChainGroupLock.unlock(); } // Do we really need an immediate hard save? Arguably all this is doing is saving the 'current' key // and that's not quite so important, so we could coalesce for more performance. saveNow(); return keys; } /** * An alias for calling {@link #freshKey(org.bitcoinj.wallet.KeyChain.KeyPurpose)} with * {@link org.bitcoinj.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter. */ public DeterministicKey freshReceiveKey() { return freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); } /** * Returns address for a {@link #freshKey(org.bitcoinj.wallet.KeyChain.KeyPurpose)} */ public Address freshAddress(KeyChain.KeyPurpose purpose) { Address key; keyChainGroupLock.lock(); try { key = keyChainGroup.freshAddress(purpose); } finally { keyChainGroupLock.unlock(); } saveNow(); return key; } /** * An alias for calling {@link #freshAddress(org.bitcoinj.wallet.KeyChain.KeyPurpose)} with * {@link org.bitcoinj.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter. */ public Address freshReceiveAddress() { return freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); } /** * Returns only the keys that have been issued by {@link #freshReceiveKey()}, {@link #freshReceiveAddress()}, * {@link #currentReceiveKey()} or {@link #currentReceiveAddress()}. */ public List<ECKey> getIssuedReceiveKeys() { keyChainGroupLock.lock(); try { return keyChainGroup.getActiveKeyChain().getIssuedReceiveKeys(); } finally { keyChainGroupLock.unlock(); } } /** * Returns only the addresses that have been issued by {@link #freshReceiveKey()}, {@link #freshReceiveAddress()}, * {@link #currentReceiveKey()} or {@link #currentReceiveAddress()}. */ public List<Address> getIssuedReceiveAddresses() { final List<ECKey> keys = getIssuedReceiveKeys(); List<Address> addresses = new ArrayList<Address>(keys.size()); for (ECKey key : keys) addresses.add(key.toAddress(getParams())); return addresses; } /** * Upgrades the wallet to be deterministic (BIP32). You should call this, possibly providing the users encryption * key, after loading a wallet produced by previous versions of bitcoinj. If the wallet is encrypted the key * <b>must</b> be provided, due to the way the seed is derived deterministically from private key bytes: failing * to do this will result in an exception being thrown. For non-encrypted wallets, the upgrade will be done for * you automatically the first time a new key is requested (this happens when spending due to the change address). */ public void upgradeToDeterministic(@Nullable KeyParameter aesKey) throws DeterministicUpgradeRequiresPassword { keyChainGroupLock.lock(); try { keyChainGroup.upgradeToDeterministic(vKeyRotationTimestamp, aesKey); } finally { keyChainGroupLock.unlock(); } } /** * Returns true if the wallet contains random keys and no HD chains, in which case you should call * {@link #upgradeToDeterministic(org.spongycastle.crypto.params.KeyParameter)} before attempting to do anything * that would require a new address or key. */ public boolean isDeterministicUpgradeRequired() { keyChainGroupLock.lock(); try { return keyChainGroup.isDeterministicUpgradeRequired(); } finally { keyChainGroupLock.unlock(); } } private void maybeUpgradeToHD() throws DeterministicUpgradeRequiresPassword { maybeUpgradeToHD(null); } @GuardedBy("keyChainGroupLock") private void maybeUpgradeToHD(@Nullable KeyParameter aesKey) throws DeterministicUpgradeRequiresPassword { checkState(keyChainGroupLock.isHeldByCurrentThread()); if (keyChainGroup.isDeterministicUpgradeRequired()) { log.info("Upgrade to HD wallets is required, attempting to do so."); try { upgradeToDeterministic(aesKey); } 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; } } } /** * Returns a snapshot of the watched scripts. This view is not live. */ public List<Script> getWatchedScripts() { keyChainGroupLock.lock(); try { return new ArrayList<Script>(watchedScripts); } finally { keyChainGroupLock.unlock(); } } /** * Removes the given key from the basicKeyChain. Be very careful with this - losing a private key <b>destroys the * money associated with it</b>. * @return Whether the key was removed or not. */ public boolean removeKey(ECKey key) { keyChainGroupLock.lock(); try { return keyChainGroup.removeImportedKey(key); } finally { keyChainGroupLock.unlock(); } } /** * Returns the number of keys in the key chain group, including lookahead keys. */ public int getKeyChainGroupSize() { keyChainGroupLock.lock(); try { return keyChainGroup.numKeys(); } finally { keyChainGroupLock.unlock(); } } @VisibleForTesting public int getKeyChainGroupCombinedKeyLookaheadEpochs() { keyChainGroupLock.lock(); try { return keyChainGroup.getCombinedKeyLookaheadEpochs(); } finally { keyChainGroupLock.unlock(); } } /** * Returns a list of the non-deterministic keys that have been imported into the wallet, or the empty list if none. */ public List<ECKey> getImportedKeys() { keyChainGroupLock.lock(); try { return keyChainGroup.getImportedKeys(); } finally { keyChainGroupLock.unlock(); } } /** Returns the address used for change outputs. Note: this will probably go away in future. */ public Address currentChangeAddress() { return currentAddress(KeyChain.KeyPurpose.CHANGE); } /** * @deprecated use {@link #currentChangeAddress()} instead. */ public Address getChangeAddress() { return currentChangeAddress(); } /** * <p>Deprecated alias for {@link #importKey(ECKey)}.</p> * * <p><b>Replace with either {@link #freshReceiveKey()} if your call is addKey(new ECKey()), or with {@link #importKey(ECKey)} * which does the same thing this method used to, but with a better name.</b></p> */ @Deprecated public boolean addKey(ECKey key) { return importKey(key); } /** * <p>Imports the given ECKey to the wallet.</p> * * <p>If the wallet is configured to auto save to a file, triggers a save immediately. Runs the onKeysAdded event * handler. If the key already exists in the wallet, does nothing and returns false.</p> */ public boolean importKey(ECKey key) { return importKeys(Lists.newArrayList(key)) == 1; } /** Replace with {@link #importKeys(java.util.List)}, which does the same thing but with a better name. */ @Deprecated public int addKeys(List<ECKey> keys) { return importKeys(keys); } /** * Imports the given keys to the wallet. * If {@link Wallet#autosaveToFile(java.io.File, long, java.util.concurrent.TimeUnit, org.bitcoinj.wallet.WalletFiles.Listener)} * has been called, triggers an auto save bypassing the normal coalescing delay and event handlers. * Returns the number of keys added, after duplicates are ignored. The onKeyAdded event will be called for each key * in the list that was not already present. */ public int importKeys(final List<ECKey> keys) { // API usage check. checkNoDeterministicKeys(keys); int result; keyChainGroupLock.lock(); try { result = keyChainGroup.importKeys(keys); } finally { keyChainGroupLock.unlock(); } saveNow(); return result; } private void checkNoDeterministicKeys(List<ECKey> keys) { // Watch out for someone doing wallet.importKey(wallet.freshReceiveKey()); or equivalent: we never tested this. for (ECKey key : keys) if (key instanceof DeterministicKey) throw new IllegalArgumentException("Cannot import HD keys back into the wallet"); } /** Takes a list of keys and a password, then encrypts and imports them in one step using the current keycrypter. */ public int importKeysAndEncrypt(final List<ECKey> keys, CharSequence password) { keyChainGroupLock.lock(); try { checkNotNull(getKeyCrypter(), "Wallet is not encrypted"); return importKeysAndEncrypt(keys, getKeyCrypter().deriveKey(password)); } finally { keyChainGroupLock.unlock(); } } /** Takes a list of keys and an AES key, then encrypts and imports them in one step using the current keycrypter. */ public int importKeysAndEncrypt(final List<ECKey> keys, KeyParameter aesKey) { keyChainGroupLock.lock(); try { checkNoDeterministicKeys(keys); return keyChainGroup.importKeysAndEncrypt(keys, aesKey); } finally { keyChainGroupLock.unlock(); } } /** * Add a pre-configured keychain to the wallet. Useful for setting up a complex keychain, * such as for a married wallet. For example: * <pre> * MarriedKeyChain chain = MarriedKeyChain.builder() * .random(new SecureRandom()) * .followingKeys(followingKeys) * .threshold(2).build(); * wallet.addAndActivateHDChain(chain); * </p> */ public void addAndActivateHDChain(DeterministicKeyChain chain) { keyChainGroupLock.lock(); try { keyChainGroup.addAndActivateHDChain(chain); } finally { keyChainGroupLock.unlock(); } } /** See {@link org.bitcoinj.wallet.DeterministicKeyChain#setLookaheadSize(int)} for more info on this. */ public void setKeyChainGroupLookaheadSize(int lookaheadSize) { keyChainGroupLock.lock(); try { keyChainGroup.setLookaheadSize(lookaheadSize); } finally { keyChainGroupLock.unlock(); } } /** See {@link org.bitcoinj.wallet.DeterministicKeyChain#setLookaheadSize(int)} for more info on this. */ public int getKeyChainGroupLookaheadSize() { keyChainGroupLock.lock(); try { return keyChainGroup.getLookaheadSize(); } finally { keyChainGroupLock.unlock(); } } /** See {@link org.bitcoinj.wallet.DeterministicKeyChain#setLookaheadThreshold(int)} for more info on this. */ public void setKeyChainGroupLookaheadThreshold(int num) { keyChainGroupLock.lock(); try { maybeUpgradeToHD(); keyChainGroup.setLookaheadThreshold(num); } finally { keyChainGroupLock.unlock(); } } /** See {@link org.bitcoinj.wallet.DeterministicKeyChain#setLookaheadThreshold(int)} for more info on this. */ public int getKeyChainGroupLookaheadThreshold() { keyChainGroupLock.lock(); try { maybeUpgradeToHD(); return keyChainGroup.getLookaheadThreshold(); } finally { keyChainGroupLock.unlock(); } } /** * Returns a public-only DeterministicKey that can be used to set up a watching wallet: that is, a wallet that * can import transactions from the block chain just as the normal wallet can, but which cannot spend. Watching * wallets are very useful for things like web servers that accept payments. This key corresponds to the account * zero key in the recommended BIP32 hierarchy. */ public DeterministicKey getWatchingKey() { keyChainGroupLock.lock(); try { maybeUpgradeToHD(); return keyChainGroup.getActiveKeyChain().getWatchingKey(); } finally { keyChainGroupLock.unlock(); } } /** * Returns whether this wallet consists entirely of watching keys (unencrypted keys with no private part). Mixed * wallets are forbidden. * * @throws IllegalStateException * if there are no keys, or if there is a mix between watching and non-watching keys. */ public boolean isWatching() { keyChainGroupLock.lock(); try { maybeUpgradeToHD(); return keyChainGroup.isWatching(); } finally { keyChainGroupLock.unlock(); } } /** * Return true if we are watching this address. */ public boolean isAddressWatched(Address address) { Script script = ScriptBuilder.createOutputScript(address); return isWatchedScript(script); } /** * Same as {@link #addWatchedAddress(Address, long)} with the current time as the creation time. */ public boolean addWatchedAddress(final Address address) { long now = Utils.currentTimeMillis() / 1000; return addWatchedAddresses(Lists.newArrayList(address), now) == 1; } /** * Adds the given address to the wallet to be watched. Outputs can be retrieved by {@link #getWatchedOutputs(boolean)}. * * @param creationTime creation time in seconds since the epoch, for scanning the blockchain * @return whether the address was added successfully (not already present) */ public boolean addWatchedAddress(final Address address, long creationTime) { return addWatchedAddresses(Lists.newArrayList(address), creationTime) == 1; } /** * Adds the given address to the wallet to be watched. Outputs can be retrieved * by {@link #getWatchedOutputs(boolean)}. * * @return how many addresses were added successfully */ public int addWatchedAddresses(final List<Address> addresses, long creationTime) { List<Script> scripts = Lists.newArrayList(); for (Address address : addresses) { Script script = ScriptBuilder.createOutputScript(address); script.setCreationTimeSeconds(creationTime); scripts.add(script); } return addWatchedScripts(scripts); } /** * Adds the given output scripts to the wallet to be watched. Outputs can be retrieved by {@link #getWatchedOutputs(boolean)}. * If a script is already being watched, the object is replaced with the one in the given list. As {@link Script} * equality is defined in terms of program bytes only this lets you update metadata such as creation time. Note that * you should be careful not to add scripts with a creation time of zero (the default!) because otherwise it will * disable the important wallet checkpointing optimisation. * * @return how many scripts were added successfully */ public int addWatchedScripts(final List<Script> scripts) { int added = 0; keyChainGroupLock.lock(); try { for (final Script script : scripts) { // Script.equals/hashCode() only takes into account the program bytes, so this step lets the user replace // a script in the wallet with an incorrect creation time. if (watchedScripts.contains(script)) 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); watchedScripts.add(script); added++; } } finally { keyChainGroupLock.unlock(); } if (added > 0) { queueOnScriptsChanged(scripts, true); saveNow(); } return added; } /** * Removes the given output scripts from the wallet that were being watched. * * @return true if successful */ public boolean removeWatchedAddress(final Address address) { return removeWatchedAddresses(ImmutableList.of(address)); } /** * Removes the given output scripts from the wallet that were being watched. * * @return true if successful */ public boolean removeWatchedAddresses(final List<Address> addresses) { List<Script> scripts = Lists.newArrayList(); for (Address address : addresses) { Script script = ScriptBuilder.createOutputScript(address); scripts.add(script); } return removeWatchedScripts(scripts); } /** * Removes the given output scripts from the wallet that were being watched. * * @return true if successful */ public boolean removeWatchedScripts(final List<Script> scripts) { lock.lock(); try { for (final Script script : scripts) { if (!watchedScripts.contains(script)) continue; watchedScripts.remove(script); } queueOnScriptsChanged(scripts, false); saveNow(); return true; } finally { lock.unlock(); } } /** * Returns all addresses watched by this wallet. */ public List<Address> getWatchedAddresses() { keyChainGroupLock.lock(); try { List<Address> addresses = new LinkedList<Address>(); for (Script script : watchedScripts) if (script.isSentToAddress()) addresses.add(script.getToAddress(params)); return addresses; } finally { keyChainGroupLock.unlock(); } } /** * Locates a keypair from the basicKeyChain given the hash of the public key. This is needed when finding out which * key we need to use to redeem a transaction output. * * @return ECKey object or null if no such key was found. */ @Override @Nullable public ECKey findKeyFromPubHash(byte[] pubkeyHash) { keyChainGroupLock.lock(); try { return keyChainGroup.findKeyFromPubHash(pubkeyHash); } finally { keyChainGroupLock.unlock(); } } /** Returns true if the given key is in the wallet, false otherwise. Currently an O(N) operation. */ public boolean hasKey(ECKey key) { keyChainGroupLock.lock(); try { return keyChainGroup.hasKey(key); } finally { keyChainGroupLock.unlock(); } } /** {@inheritDoc} */ @Override public boolean isPubKeyHashMine(byte[] pubkeyHash) { return findKeyFromPubHash(pubkeyHash) != null; } /** {@inheritDoc} */ @Override public boolean isWatchedScript(Script script) { keyChainGroupLock.lock(); try { return watchedScripts.contains(script); } finally { keyChainGroupLock.unlock(); } } /** * Locates a keypair from the basicKeyChain given the raw public key bytes. * @return ECKey or null if no such key was found. */ @Override @Nullable public ECKey findKeyFromPubKey(byte[] pubkey) { keyChainGroupLock.lock(); try { return keyChainGroup.findKeyFromPubKey(pubkey); } finally { keyChainGroupLock.unlock(); } } /** {@inheritDoc} */ @Override public boolean isPubKeyMine(byte[] pubkey) { return findKeyFromPubKey(pubkey) != null; } /** * Locates a redeem data (redeem script and keys) from the keyChainGroup given the hash of the script. * Returns RedeemData object or null if no such data was found. */ @Nullable @Override public RedeemData findRedeemDataFromScriptHash(byte[] payToScriptHash) { keyChainGroupLock.lock(); try { return keyChainGroup.findRedeemDataFromScriptHash(payToScriptHash); } finally { keyChainGroupLock.unlock(); } } /** {@inheritDoc} */ @Override public boolean isPayToScriptHashMine(byte[] payToScriptHash) { return findRedeemDataFromScriptHash(payToScriptHash) != null; } /** * Marks all keys used in the transaction output as used in the wallet. * See {@link org.bitcoinj.wallet.DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this. */ private void markKeysAsUsed(Transaction tx) { keyChainGroupLock.lock(); try { for (TransactionOutput o : tx.getOutputs()) { try { Script script = o.getScriptPubKey(); if (script.isSentToRawPubKey()) { byte[] pubkey = script.getPubKey(); keyChainGroup.markPubKeyAsUsed(pubkey); } else if (script.isSentToAddress()) { byte[] pubkeyHash = script.getPubKeyHash(); keyChainGroup.markPubKeyHashAsUsed(pubkeyHash); } else if (script.isPayToScriptHash()) { Address a = Address.fromP2SHScript(tx.getParams(), script); keyChainGroup.markP2SHAddressAsUsed(a); } } catch (ScriptException e) { // Just means we didn't understand the output of this transaction: ignore it. log.warn("Could not parse tx output script: {}", e.toString()); } } } finally { keyChainGroupLock.unlock(); } } /** * Returns the immutable seed for the current active HD chain. * @throws org.bitcoinj.core.ECKey.MissingPrivateKeyException if the seed is unavailable (watching wallet) */ public DeterministicSeed getKeyChainSeed() { keyChainGroupLock.lock(); try { DeterministicSeed seed = keyChainGroup.getActiveKeyChain().getSeed(); if (seed == null) throw new ECKey.MissingPrivateKeyException(); return seed; } finally { keyChainGroupLock.unlock(); } } /** * Returns a key for the given HD path, assuming it's already been derived. You normally shouldn't use this: * use currentReceiveKey/freshReceiveKey instead. */ public DeterministicKey getKeyByPath(List<ChildNumber> path) { keyChainGroupLock.lock(); try { maybeUpgradeToHD(); return keyChainGroup.getActiveKeyChain().getKeyByPath(path, false); } finally { keyChainGroupLock.unlock(); } } /** * Convenience wrapper around {@link Wallet#encrypt(org.bitcoinj.crypto.KeyCrypter, * org.spongycastle.crypto.params.KeyParameter)} which uses the default Scrypt key derivation algorithm and * parameters to derive a key from the given password. */ public void encrypt(CharSequence password) { keyChainGroupLock.lock(); try { final KeyCrypterScrypt scrypt = new KeyCrypterScrypt(); keyChainGroup.encrypt(scrypt, scrypt.deriveKey(password)); } finally { keyChainGroupLock.unlock(); } saveNow(); } /** * Encrypt the wallet using the KeyCrypter and the AES key. A good default KeyCrypter to use is * {@link org.bitcoinj.crypto.KeyCrypterScrypt}. * * @param keyCrypter The KeyCrypter that specifies how to encrypt/ decrypt a key * @param aesKey AES key to use (normally created using KeyCrypter#deriveKey and cached as it is time consuming to create from a password) * @throws KeyCrypterException Thrown if the wallet encryption fails. If so, the wallet state is unchanged. */ public void encrypt(KeyCrypter keyCrypter, KeyParameter aesKey) { keyChainGroupLock.lock(); try { keyChainGroup.encrypt(keyCrypter, aesKey); } finally { keyChainGroupLock.unlock(); } saveNow(); } /** * Decrypt the wallet with the wallets keyCrypter and password. * @throws KeyCrypterException Thrown if the wallet decryption fails. If so, the wallet state is unchanged. */ public void decrypt(CharSequence password) { keyChainGroupLock.lock(); try { final KeyCrypter crypter = keyChainGroup.getKeyCrypter(); checkState(crypter != null, "Not encrypted"); keyChainGroup.decrypt(crypter.deriveKey(password)); } finally { keyChainGroupLock.unlock(); } saveNow(); } /** * Decrypt the wallet with the wallets keyCrypter and AES key. * * @param aesKey AES key to use (normally created using KeyCrypter#deriveKey and cached as it is time consuming to create from a password) * @throws KeyCrypterException Thrown if the wallet decryption fails. If so, the wallet state is unchanged. */ public void decrypt(KeyParameter aesKey) { keyChainGroupLock.lock(); try { keyChainGroup.decrypt(aesKey); } finally { keyChainGroupLock.unlock(); } saveNow(); } /** * Check whether the password can decrypt the first key in the wallet. * This can be used to check the validity of an entered password. * * @return boolean true if password supplied can decrypt the first private key in the wallet, false otherwise. * @throws IllegalStateException if the wallet is not encrypted. */ public boolean checkPassword(CharSequence password) { keyChainGroupLock.lock(); try { return keyChainGroup.checkPassword(password); } finally { keyChainGroupLock.unlock(); } } /** * Check whether the AES key can decrypt the first encrypted key in the wallet. * * @return boolean true if AES key supplied can decrypt the first encrypted private key in the wallet, false otherwise. */ public boolean checkAESKey(KeyParameter aesKey) { keyChainGroupLock.lock(); try { return keyChainGroup.checkAESKey(aesKey); } finally { keyChainGroupLock.unlock(); } } /** * Get the wallet's KeyCrypter, or null if the wallet is not encrypted. * (Used in encrypting/ decrypting an ECKey). */ @Nullable public KeyCrypter getKeyCrypter() { keyChainGroupLock.lock(); try { return keyChainGroup.getKeyCrypter(); } finally { keyChainGroupLock.unlock(); } } /** * Get the type of encryption used for this wallet. * * (This is a convenience method - the encryption type is actually stored in the keyCrypter). */ public EncryptionType getEncryptionType() { keyChainGroupLock.lock(); try { KeyCrypter crypter = keyChainGroup.getKeyCrypter(); if (crypter != null) return crypter.getUnderstoodEncryptionType(); else return EncryptionType.UNENCRYPTED; } finally { keyChainGroupLock.unlock(); } } /** Returns true if the wallet is encrypted using any scheme, false if not. */ public boolean isEncrypted() { return getEncryptionType() != EncryptionType.UNENCRYPTED; } /** Changes wallet encryption password, this is atomic operation. */ public void changeEncryptionPassword(CharSequence currentPassword, CharSequence newPassword){ keyChainGroupLock.lock(); try { decrypt(currentPassword); encrypt(newPassword); } finally { keyChainGroupLock.unlock(); } } /** Changes wallet AES encryption key, this is atomic operation. */ public void changeEncryptionKey(KeyCrypter keyCrypter, KeyParameter currentAesKey, KeyParameter newAesKey){ keyChainGroupLock.lock(); try { decrypt(currentAesKey); encrypt(keyCrypter, newAesKey); } finally { keyChainGroupLock.unlock(); } } //endregion /******************************************************************************************************************/ //region Serialization support // TODO: Make this package private once the classes finish moving around. /** Internal use only. */ public List<Protos.Key> serializeKeyChainGroupToProtobuf() { keyChainGroupLock.lock(); try { return keyChainGroup.serializeToProtobuf(); } finally { keyChainGroupLock.unlock(); } } /** Saves the wallet first to the given temp file, then renames to the dest file. */ public void saveToFile(File temp, File destFile) throws IOException { FileOutputStream stream = null; lock.lock(); try { stream = new FileOutputStream(temp); saveToFileStream(stream); // Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide // to not write through to physical media for at least a few seconds, but this is the best we can do. stream.flush(); stream.getFD().sync(); stream.close(); stream = null; if (Utils.isWindows()) { // Work around an issue on Windows whereby you can't rename over existing files. File canonical = destFile.getCanonicalFile(); if (canonical.exists() && !canonical.delete()) throw new IOException("Failed to delete canonical wallet file for replacement with autosave"); if (temp.renameTo(canonical)) return; // else fall through. throw new IOException("Failed to rename " + temp + " to " + canonical); } else if (!temp.renameTo(destFile)) { throw new IOException("Failed to rename " + temp + " to " + destFile); } } catch (RuntimeException e) { log.error("Failed whilst saving wallet", e); throw e; } finally { lock.unlock(); if (stream != null) { stream.close(); } if (temp.exists()) { log.warn("Temp file still exists after failed save."); } } } /** * Uses protobuf serialization to save the wallet to the given file. To learn more about this file format, see * {@link WalletProtobufSerializer}. Writes out first to a temporary file in the same directory and then renames * once written. */ public void saveToFile(File f) throws IOException { File directory = f.getAbsoluteFile().getParentFile(); File temp = File.createTempFile("wallet", null, directory); saveToFile(temp, f); } /** * <p>Whether or not the wallet will ignore pending transactions that fail the selected * {@link RiskAnalysis}. By default, if a transaction is considered risky then it won't enter the wallet * and won't trigger any event listeners. If you set this property to true, then all transactions will * be allowed in regardless of risk. For example, the {@link DefaultRiskAnalysis} checks for non-finality of * transactions.</p> * * <p>Note that this property is not serialized. You have to set it each time a Wallet object is constructed, * even if it's loaded from a protocol buffer.</p> */ public void setAcceptRiskyTransactions(boolean acceptRiskyTransactions) { lock.lock(); try { this.acceptRiskyTransactions = acceptRiskyTransactions; } finally { lock.unlock(); } } /** * See {@link Wallet#setAcceptRiskyTransactions(boolean)} for an explanation of this property. */ public boolean isAcceptRiskyTransactions() { lock.lock(); try { return acceptRiskyTransactions; } finally { lock.unlock(); } } /** * Sets the {@link RiskAnalysis} implementation to use for deciding whether received pending transactions are risky * or not. If the analyzer says a transaction is risky, by default it will be dropped. You can customize this * behaviour with {@link #setAcceptRiskyTransactions(boolean)}. */ public void setRiskAnalyzer(RiskAnalysis.Analyzer analyzer) { lock.lock(); try { this.riskAnalyzer = checkNotNull(analyzer); } finally { lock.unlock(); } } /** * Gets the current {@link RiskAnalysis} implementation. The default is {@link DefaultRiskAnalysis}. */ public RiskAnalysis.Analyzer getRiskAnalyzer() { lock.lock(); try { return riskAnalyzer; } finally { lock.unlock(); } } /** * <p>Sets up the wallet to auto-save itself to the given file, using temp files with atomic renames to ensure * consistency. After connecting to a file, you no longer need to save the wallet manually, it will do it * whenever necessary. Protocol buffer serialization will be used.</p> * * <p>If delayTime is set, a background thread will be created and the wallet will only be saved to * disk every so many time units. If no changes have occurred for the given time period, nothing will be written. * In this way disk IO can be rate limited. It's a good idea to set this as otherwise the wallet can change very * frequently, eg if there are a lot of transactions in it or during block sync, and there will be a lot of redundant * writes. Note that when a new key is added, that always results in an immediate save regardless of * delayTime. <b>You should still save the wallet manually when your program is about to shut down as the JVM * will not wait for the background thread.</b></p> * * <p>An event listener can be provided. If a delay >0 was specified, it will be called on a background thread * with the wallet locked when an auto-save occurs. If delay is zero or you do something that always triggers * an immediate save, like adding a key, the event listener will be invoked on the calling threads.</p> * * @param f The destination file to save to. * @param delayTime How many time units to wait until saving the wallet on a background thread. * @param timeUnit the unit of measurement for delayTime. * @param eventListener callback to be informed when the auto-save thread does things, or null */ public WalletFiles autosaveToFile(File f, long delayTime, TimeUnit timeUnit, @Nullable WalletFiles.Listener eventListener) { lock.lock(); try { checkState(vFileManager == null, "Already auto saving this wallet."); WalletFiles manager = new WalletFiles(this, f, delayTime, timeUnit); if (eventListener != null) manager.setListener(eventListener); vFileManager = manager; return manager; } finally { lock.unlock(); } } /** * <p> * Disables auto-saving, after it had been enabled with * {@link Wallet#autosaveToFile(java.io.File, long, java.util.concurrent.TimeUnit, org.bitcoinj.wallet.WalletFiles.Listener)} * before. This method blocks until finished. * </p> */ public void shutdownAutosaveAndWait() { lock.lock(); try { WalletFiles files = vFileManager; vFileManager = null; checkState(files != null, "Auto saving not enabled."); files.shutdownAndWait(); } finally { lock.unlock(); } } /** Requests an asynchronous save on a background thread */ protected void saveLater() { WalletFiles files = vFileManager; if (files != null) files.saveLater(); } /** If auto saving is enabled, do an immediate sync write to disk ignoring any delays. */ protected void saveNow() { WalletFiles files = vFileManager; if (files != null) { try { files.saveNow(); // This calls back into saveToFile(). } catch (IOException e) { // Can't really do much at this point, just let the API user know. log.error("Failed to save wallet to disk!", e); Thread.UncaughtExceptionHandler handler = Threading.uncaughtExceptionHandler; if (handler != null) handler.uncaughtException(Thread.currentThread(), e); } } } /** * Uses protobuf serialization to save the wallet to the given file stream. To learn more about this file format, see * {@link WalletProtobufSerializer}. */ public void saveToFileStream(OutputStream f) throws IOException { lock.lock(); try { new WalletProtobufSerializer().writeWallet(this, f); } finally { lock.unlock(); } } /** Returns the parameters this wallet was created with. */ public NetworkParameters getParams() { return params; } /** Returns the API context that this wallet was created with. */ public Context getContext() { return context; } /** * <p>Returns a wallet deserialized from the given file. Extensions previously saved with the wallet can be * deserialized by calling @{@link WalletExtension#deserializeWalletExtension(Wallet, byte[])}}</p> * * @param file the wallet file to read * @param walletExtensions extensions possibly added to the wallet. */ public static Wallet loadFromFile(File file, @Nullable WalletExtension... walletExtensions) throws UnreadableWalletException { try { FileInputStream stream = null; try { stream = new FileInputStream(file); return loadFromFileStream(stream, walletExtensions); } finally { if (stream != null) stream.close(); } } catch (IOException e) { throw new UnreadableWalletException("Could not open file", e); } } /** * Returns if this wallet is structurally consistent, so e.g. no duplicate transactions. First inconsistency and a * dump of the wallet will be logged. */ public boolean isConsistent() { try { isConsistentOrThrow(); return true; } catch (IllegalStateException x) { log.error(x.getMessage()); try { log.error(toString()); } catch (RuntimeException x2) { log.error("Printing inconsistent wallet failed", x2); } return false; } } /** * Variant of {@link Wallet#isConsistent()} that throws an {@link IllegalStateException} describing the first * inconsistency. */ public void isConsistentOrThrow() throws IllegalStateException { lock.lock(); try { Set<Transaction> transactions = getTransactions(true); Set<Sha256Hash> hashes = new HashSet<Sha256Hash>(); for (Transaction tx : transactions) { hashes.add(tx.getHash()); } int size1 = transactions.size(); if (size1 != hashes.size()) { throw new IllegalStateException("Two transactions with same hash"); } int size2 = unspent.size() + spent.size() + pending.size() + dead.size(); if (size1 != size2) { throw new IllegalStateException("Inconsistent wallet sizes: " + size1 + ", " + size2); } for (Transaction tx : unspent.values()) { if (!isTxConsistent(tx, false)) { throw new IllegalStateException("Inconsistent unspent tx: " + tx.getHashAsString()); } } for (Transaction tx : spent.values()) { if (!isTxConsistent(tx, true)) { throw new IllegalStateException("Inconsistent spent tx: " + tx.getHashAsString()); } } } finally { lock.unlock(); } } /* * If isSpent - check that all my outputs spent, otherwise check that there at least * one unspent. */ @VisibleForTesting boolean isTxConsistent(final Transaction tx, final boolean isSpent) { boolean isActuallySpent = true; for (TransactionOutput o : tx.getOutputs()) { if (o.isAvailableForSpending()) { if (o.isMineOrWatched(this)) isActuallySpent = false; if (o.getSpentBy() != null) { log.error("isAvailableForSpending != spentBy"); return false; } } else { if (o.getSpentBy() == null) { log.error("isAvailableForSpending != spentBy"); return false; } } } return isActuallySpent == isSpent; } /** Returns a wallet deserialized from the given input stream and wallet extensions. */ public static Wallet loadFromFileStream(InputStream stream, @Nullable WalletExtension... walletExtensions) throws UnreadableWalletException { Wallet wallet = new WalletProtobufSerializer().readWallet(stream, walletExtensions); if (!wallet.isConsistent()) { log.error("Loaded an inconsistent wallet"); } return wallet; } //endregion /******************************************************************************************************************/ //region Inbound transaction reception and processing /** * Called by the {@link BlockChain} when we receive a new filtered block that contains a transactions previously * received by a call to {@link #receivePending}.<p> * * This is necessary for the internal book-keeping Wallet does. When a transaction is received that sends us * coins it is added to a pool so we can use it later to create spends. When a transaction is received that * consumes outputs they are marked as spent so they won't be used in future.<p> * * A transaction that spends our own coins can be received either because a spend we created was accepted by the * network and thus made it into a block, or because our keys are being shared between multiple instances and * some other node spent the coins instead. We still have to know about that to avoid accidentally trying to * double spend.<p> * * A transaction may be received multiple times if is included into blocks in parallel chains. The blockType * parameter describes whether the containing block is on the main/best chain or whether it's on a presently * inactive side chain. We must still record these transactions and the blocks they appear in because a future * block might change which chain is best causing a reorganize. A re-org can totally change our balance! */ @Override public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, BlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException { lock.lock(); try { Transaction tx = transactions.get(txHash); if (tx == null) { tx = riskDropped.get(txHash); if (tx != null) { // If this happens our risk analysis is probably wrong and should be improved. log.info("Risk analysis dropped tx {} but was included in block anyway", tx.getHash()); } else { // False positive that was broadcast to us and ignored by us because it was irrelevant to our keys. return false; } } receive(tx, block, blockType, relativityOffset); return true; } finally { lock.unlock(); } } /** * <p>Called when we have found a transaction (via network broadcast or otherwise) that is relevant to this wallet * and want to record it. Note that we <b>cannot verify these transactions at all</b>, they may spend fictional * coins or be otherwise invalid. They are useful to inform the user about coins they can expect to receive soon, * and if you trust the sender of the transaction you can choose to assume they are in fact valid and will not * be double spent as an optimization.</p> * * <p>This is the same as {@link Wallet#receivePending(Transaction, java.util.List)} but allows you to override the * {@link Wallet#isPendingTransactionRelevant(Transaction)} sanity-check to keep track of transactions that are not * spendable or spend our coins. This can be useful when you want to keep track of transaction confidence on * arbitrary transactions. Note that transactions added in this way will still be relayed to peers and appear in * transaction lists like any other pending transaction (even when not relevant).</p> */ public void receivePending(Transaction tx, @Nullable List<Transaction> dependencies, boolean overrideIsRelevant) throws VerificationException { // Can run in a peer thread. This method will only be called if a prior call to isPendingTransactionRelevant // returned true, so we already know by this point that it sends coins to or from our wallet, or is a double // spend against one of our other pending transactions. lock.lock(); try { tx.verify(); // Ignore it if we already know about this transaction. Receiving a pending transaction never moves it // between pools. EnumSet<Pool> containingPools = getContainingPools(tx); if (!containingPools.equals(EnumSet.noneOf(Pool.class))) { log.debug("Received tx we already saw in a block or created ourselves: " + tx.getHashAsString()); return; } // Repeat the check of relevancy here, even though the caller may have already done so - this is to avoid // race conditions where receivePending may be being called in parallel. if (!overrideIsRelevant && !isPendingTransactionRelevant(tx)) return; if (isTransactionRisky(tx, dependencies) && !acceptRiskyTransactions) { // isTransactionRisky already logged the reason. riskDropped.put(tx.getHash(), tx); log.warn("There are now {} risk dropped transactions being kept in memory", riskDropped.size()); return; } Coin valueSentToMe = tx.getValueSentToMe(this); Coin valueSentFromMe = tx.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", tx.getHashAsString(), valueSentFromMe.toFriendlyString(), valueSentToMe.toFriendlyString())); } if (tx.getConfidence().getSource().equals(TransactionConfidence.Source.UNKNOWN)) { log.warn("Wallet received transaction with an unknown source. Consider tagging it!"); } // If this tx spends any of our unspent outputs, mark them as spent now, then add to the pending pool. This // ensures that if some other client that has our keys broadcasts a spend we stay in sync. Also updates the // timestamp on the transaction and registers/runs event listeners. commitTx(tx); } finally { lock.unlock(); } // maybeRotateKeys() will ignore pending transactions so we don't bother calling it here (see the comments // in that function for an explanation of why). } /** * Given a transaction and an optional list of dependencies (recursive/flattened), returns true if the given * transaction would be rejected by the analyzer, or false otherwise. The result of this call is independent * of the value of {@link #isAcceptRiskyTransactions()}. Risky transactions yield a logged warning. If you * want to know the reason why a transaction is risky, create an instance of the {@link RiskAnalysis} yourself * using the factory returned by {@link #getRiskAnalyzer()} and use it directly. */ public boolean isTransactionRisky(Transaction tx, @Nullable List<Transaction> dependencies) { lock.lock(); try { if (dependencies == null) dependencies = ImmutableList.of(); RiskAnalysis analysis = riskAnalyzer.create(this, tx, dependencies); RiskAnalysis.Result result = analysis.analyze(); if (result != RiskAnalysis.Result.OK) { log.warn("Pending transaction was considered risky: {}\n{}", analysis, tx); return true; } return false; } finally { lock.unlock(); } } /** * <p>Called when we have found a transaction (via network broadcast or otherwise) that is relevant to this wallet * and want to record it. Note that we <b>cannot verify these transactions at all</b>, they may spend fictional * coins or be otherwise invalid. They are useful to inform the user about coins they can expect to receive soon, * and if you trust the sender of the transaction you can choose to assume they are in fact valid and will not * be double spent as an optimization.</p> * * <p>Before this method is called, {@link Wallet#isPendingTransactionRelevant(Transaction)} should have been * called to decide whether the wallet cares about the transaction - if it does, then this method expects the * transaction and any dependencies it has which are still in the memory pool.</p> */ public void receivePending(Transaction tx, @Nullable List<Transaction> dependencies) throws VerificationException { receivePending(tx, dependencies, false); } /** * This method is used by a {@link Peer} to find out if a transaction that has been announced is interesting, * that is, whether we should bother downloading its dependencies and exploring the transaction to decide how * risky it is. If this method returns true then {@link Wallet#receivePending(Transaction, java.util.List)} * will soon be called with the transactions dependencies as well. */ public boolean isPendingTransactionRelevant(Transaction tx) throws ScriptException { lock.lock(); try { // Ignore it if we already know about this transaction. Receiving a pending transaction never moves it // between pools. EnumSet<Pool> containingPools = getContainingPools(tx); if (!containingPools.equals(EnumSet.noneOf(Pool.class))) { log.debug("Received tx we already saw in a block or created ourselves: " + tx.getHashAsString()); return false; } // We only care about transactions that: // - Send us coins // - Spend our coins // - Double spend a tx in our wallet if (!isTransactionRelevant(tx)) { log.debug("Received tx that isn't relevant to this wallet, discarding."); return false; } return true; } finally { lock.unlock(); } } /** * <p>Returns true if the given transaction sends coins to any of our keys, or has inputs spending any of our outputs, * and also returns true if tx has inputs that are spending outputs which are * not ours but which are spent by pending transactions.</p> * * <p>Note that if the tx has inputs containing one of our keys, but the connected transaction is not in the wallet, * it will not be considered relevant.</p> */ public boolean isTransactionRelevant(Transaction tx) throws ScriptException { lock.lock(); try { return tx.getValueSentFromMe(this).signum() > 0 || tx.getValueSentToMe(this).signum() > 0 || !findDoubleSpendsAgainst(tx, transactions).isEmpty(); } finally { lock.unlock(); } } /** * Finds transactions in the specified candidates that double spend "tx". Not a general check, but it can work even if * the double spent inputs are not ours. * @return The set of transactions that double spend "tx". */ private Set<Transaction> findDoubleSpendsAgainst(Transaction tx, Map<Sha256Hash, Transaction> candidates) { checkState(lock.isHeldByCurrentThread()); if (tx.isCoinBase()) return Sets.newHashSet(); // Compile a set of outpoints that are spent by tx. HashSet<TransactionOutPoint> outpoints = new HashSet<TransactionOutPoint>(); for (TransactionInput input : tx.getInputs()) { outpoints.add(input.getOutpoint()); } // Now for each pending transaction, see if it shares any outpoints with this tx. Set<Transaction> doubleSpendTxns = Sets.newHashSet(); for (Transaction p : candidates.values()) { for (TransactionInput input : p.getInputs()) { // This relies on the fact that TransactionOutPoint equality is defined at the protocol not object // level - outpoints from two different inputs that point to the same output compare the same. TransactionOutPoint outpoint = input.getOutpoint(); if (outpoints.contains(outpoint)) { // It does, it's a double spend against the candidates, which makes it relevant. doubleSpendTxns.add(p); } } } return doubleSpendTxns; } /** * Adds to txSet all the txns in txPool spending outputs of txns in txSet, * and all txns spending the outputs of those txns, recursively. */ void addTransactionsDependingOn(Set<Transaction> txSet, Set<Transaction> txPool) { Map<Sha256Hash, Transaction> txQueue = new LinkedHashMap<Sha256Hash, Transaction>(); for (Transaction tx : txSet) { txQueue.put(tx.getHash(), tx); } while(!txQueue.isEmpty()) { Transaction tx = txQueue.remove(txQueue.keySet().iterator().next()); for (Transaction anotherTx : txPool) { if (anotherTx.equals(tx)) continue; for (TransactionInput input : anotherTx.getInputs()) { if (input.getOutpoint().getHash().equals(tx.getHash())) { if (txQueue.get(anotherTx.getHash()) == null) { txQueue.put(anotherTx.getHash(), anotherTx); txSet.add(anotherTx); } } } } } } /** * Called by the {@link BlockChain} when we receive a new block that sends coins to one of our addresses or * spends coins from one of our addresses (note that a single transaction can do both).<p> * * This is necessary for the internal book-keeping Wallet does. When a transaction is received that sends us * coins it is added to a pool so we can use it later to create spends. When a transaction is received that * consumes outputs they are marked as spent so they won't be used in future.<p> * * A transaction that spends our own coins can be received either because a spend we created was accepted by the * network and thus made it into a block, or because our keys are being shared between multiple instances and * some other node spent the coins instead. We still have to know about that to avoid accidentally trying to * double spend.<p> * * A transaction may be received multiple times if is included into blocks in parallel chains. The blockType * parameter describes whether the containing block is on the main/best chain or whether it's on a presently * inactive side chain. We must still record these transactions and the blocks they appear in because a future * block might change which chain is best causing a reorganize. A re-org can totally change our balance! */ @Override public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException { lock.lock(); try { if (!isTransactionRelevant(tx)) return; receive(tx, block, blockType, relativityOffset); } finally { lock.unlock(); } } // Whether to do a saveNow or saveLater when we are notified of the next best block. private boolean hardSaveOnNextBlock = false; private void receive(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException { // Runs in a peer thread. checkState(lock.isHeldByCurrentThread()); Coin prevBalance = getBalance(); Sha256Hash txHash = tx.getHash(); boolean bestChain = blockType == BlockChain.NewBlockType.BEST_CHAIN; boolean sideChain = blockType == BlockChain.NewBlockType.SIDE_CHAIN; Coin valueSentFromMe = tx.getValueSentFromMe(this); Coin valueSentToMe = tx.getValueSentToMe(this); Coin valueDifference = valueSentToMe.subtract(valueSentFromMe); log.info("Received tx{} for {}: {} [{}] in block {}", sideChain ? " on a side chain" : "", valueDifference.toFriendlyString(), tx.getHashAsString(), relativityOffset, block != null ? block.getHeader().getHash() : "(unit test)"); // Inform the key chains that the issued keys were observed in a transaction, so they know to // calculate more keys for the next Bloom filters. markKeysAsUsed(tx); onWalletChangedSuppressions++; // If this transaction is already in the wallet we may need to move it into a different pool. At the very // least we need to ensure we're manipulating the canonical object rather than a duplicate. { Transaction tmp = transactions.get(tx.getHash()); if (tmp != null) tx = tmp; } boolean wasPending = pending.remove(txHash) != null; if (wasPending) log.info(" <-pending"); if (bestChain) { boolean wasDead = dead.remove(txHash) != null; if (wasDead) log.info(" <-dead"); if (wasPending) { // Was pending and is now confirmed. Disconnect the outputs in case we spent any already: they will be // re-connected by processTxFromBestChain below. for (TransactionOutput output : tx.getOutputs()) { final TransactionInput spentBy = output.getSpentBy(); if (spentBy != null) { checkState(myUnspents.add(output)); spentBy.disconnect(); } } } processTxFromBestChain(tx, wasPending || wasDead); } else { checkState(sideChain); // Transactions that appear in a side chain will have that appearance recorded below - we assume that // some miners are also trying to include the transaction into the current best chain too, so let's treat // it as pending, except we don't need to do any risk analysis on it. if (wasPending) { // Just put it back in without touching the connections or confidence. addWalletTransaction(Pool.PENDING, tx); log.info(" ->pending"); } else { // Ignore the case where a tx appears on a side chain at the same time as the best chain (this is // quite normal and expected). Sha256Hash hash = tx.getHash(); if (!unspent.containsKey(hash) && !spent.containsKey(hash) && !dead.containsKey(hash)) { // Otherwise put it (possibly back) into pending. // Committing it updates the spent flags and inserts into the pool as well. commitTx(tx); } } } if (block != null) { // Mark the tx as appearing in this block so we can find it later after a re-org. This also tells the tx // confidence object about the block and sets its depth appropriately. tx.setBlockAppearance(block, bestChain, relativityOffset); if (bestChain) { // Don't notify this tx of work done in notifyNewBestBlock which will be called immediately after // this method has been called by BlockChain for all relevant transactions. Otherwise we'd double // count. ignoreNextNewBlock.add(txHash); // When a tx is received from the best chain, if other txns that spend this tx are IN_CONFLICT, // change its confidence to PENDING (Unless they are also spending other txns IN_CONFLICT). // Consider dependency chains. Set<Transaction> currentTxDependencies = Sets.newHashSet(tx); addTransactionsDependingOn(currentTxDependencies, getTransactions(true)); currentTxDependencies.remove(tx); List<Transaction> currentTxDependenciesSorted = sortTxnsByDependency(currentTxDependencies); for (Transaction txDependency : currentTxDependenciesSorted) { if (txDependency.getConfidence().getConfidenceType().equals(ConfidenceType.IN_CONFLICT)) { if (isNotSpendingTxnsInConfidenceType(txDependency, ConfidenceType.IN_CONFLICT)) { txDependency.getConfidence().setConfidenceType(ConfidenceType.PENDING); confidenceChanged.put(txDependency, TransactionConfidence.Listener.ChangeReason.TYPE); } } } } } onWalletChangedSuppressions--; // Side chains don't affect confidence. if (bestChain) { // notifyNewBestBlock will be invoked next and will then call maybeQueueOnWalletChanged for us. confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.TYPE); } else { maybeQueueOnWalletChanged(); } // Inform anyone interested that we have received or sent coins but only if: // - This is not due to a re-org. // - The coins appeared on the best chain. // - We did in fact receive some new money. // - We have not already informed the user about the coins when we received the tx broadcast, or for our // own spends. If users want to know when a broadcast tx becomes confirmed, they need to use tx confidence // listeners. if (!insideReorg && bestChain) { Coin newBalance = getBalance(); // This is slow. log.info("Balance is now: " + newBalance.toFriendlyString()); if (!wasPending) { int diff = valueDifference.signum(); // We pick one callback based on the value difference, though a tx can of course both send and receive // coins from the wallet. if (diff > 0) { queueOnCoinsReceived(tx, prevBalance, newBalance); } else if (diff < 0) { queueOnCoinsSent(tx, prevBalance, newBalance); } } checkBalanceFuturesLocked(newBalance); } informConfidenceListenersIfNotReorganizing(); isConsistentOrThrow(); // Optimization for the case where a block has tons of relevant transactions. saveLater(); hardSaveOnNextBlock = true; } /** Finds if tx is NOT spending other txns which are in the specified confidence type */ private boolean isNotSpendingTxnsInConfidenceType(Transaction tx, ConfidenceType confidenceType) { for (TransactionInput txInput : tx.getInputs()) { Transaction connectedTx = this.getTransaction(txInput.getOutpoint().getHash()); if (connectedTx != null && connectedTx.getConfidence().getConfidenceType().equals(confidenceType)) { return false; } } return true; } /** * Creates and returns a new List with the same txns as inputSet * but txns are sorted by depencency (a topological sort). * If tx B spends tx A, then tx A should be before tx B on the returned List. * Several invocations to this method with the same inputSet could result in lists with txns in different order, * as there is no guarantee on the order of the returned txns besides what was already stated. */ List<Transaction> sortTxnsByDependency(Set<Transaction> inputSet) { ArrayList<Transaction> result = new ArrayList<Transaction>(inputSet); for (int i = 0; i < result.size()-1; i++) { boolean txAtISpendsOtherTxInTheList; do { txAtISpendsOtherTxInTheList = false; for (int j = i+1; j < result.size(); j++) { if (spends(result.get(i), result.get(j))) { Transaction transactionAtI = result.remove(i); result.add(j, transactionAtI); txAtISpendsOtherTxInTheList = true; break; } } } while (txAtISpendsOtherTxInTheList); } return result; } /** Finds whether txA spends txB */ boolean spends(Transaction txA, Transaction txB) { for (TransactionInput txInput : txA.getInputs()) { if (txInput.getOutpoint().getHash().equals(txB.getHash())) { return true; } } return false; } private void informConfidenceListenersIfNotReorganizing() { if (insideReorg) return; for (Map.Entry<Transaction, TransactionConfidence.Listener.ChangeReason> entry : confidenceChanged.entrySet()) { final Transaction tx = entry.getKey(); tx.getConfidence().queueListeners(entry.getValue()); queueOnTransactionConfidenceChanged(tx); } confidenceChanged.clear(); } /** * <p>Called by the {@link BlockChain} when a new block on the best chain is seen, AFTER relevant wallet * transactions are extracted and sent to us UNLESS the new block caused a re-org, in which case this will * not be called (the {@link Wallet#reorganize(StoredBlock, java.util.List, java.util.List)} method will * call this one in that case).</p> * <p/> * <p>Used to update confidence data in each transaction and last seen block hash. Triggers auto saving. * Invokes the onWalletChanged event listener if there were any affected transactions.</p> */ @Override public void notifyNewBestBlock(StoredBlock block) throws VerificationException { // Check to see if this block has been seen before. Sha256Hash newBlockHash = block.getHeader().getHash(); if (newBlockHash.equals(getLastBlockSeenHash())) return; lock.lock(); try { // Store the new block hash. setLastBlockSeenHash(newBlockHash); setLastBlockSeenHeight(block.getHeight()); setLastBlockSeenTimeSecs(block.getHeader().getTimeSeconds()); // Notify all the BUILDING transactions of the new block. // This is so that they can update their depth. Set<Transaction> transactions = getTransactions(true); for (Transaction tx : transactions) { if (ignoreNextNewBlock.contains(tx.getHash())) { // tx was already processed in receive() due to it appearing in this block, so we don't want to // increment the tx confidence depth twice, it'd result in miscounting. ignoreNextNewBlock.remove(tx.getHash()); } else { TransactionConfidence confidence = tx.getConfidence(); if (confidence.getConfidenceType() == ConfidenceType.BUILDING) { // Erase the set of seen peers once the tx is so deep that it seems unlikely to ever go // pending again. We could clear this data the moment a tx is seen in the block chain, but // in cases where the chain re-orgs, this would mean that wallets would perceive a newly // pending tx has zero confidence at all, which would not be right: we expect it to be // included once again. We could have a separate was-in-chain-and-now-isn't confidence type // but this way is backwards compatible with existing software, and the new state probably // wouldn't mean anything different to just remembering peers anyway. if (confidence.incrementDepthInBlocks() > context.getEventHorizon()) confidence.clearBroadcastBy(); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.DEPTH); } } } informConfidenceListenersIfNotReorganizing(); maybeQueueOnWalletChanged(); if (hardSaveOnNextBlock) { saveNow(); hardSaveOnNextBlock = false; } else { // Coalesce writes to avoid throttling on disk access when catching up with the chain. saveLater(); } } finally { lock.unlock(); } } /** * Handle when a transaction becomes newly active on the best chain, either due to receiving a new block or a * re-org. Places the tx into the right pool, handles coinbase transactions, handles double-spends and so on. */ private void processTxFromBestChain(Transaction tx, boolean forceAddToPool) throws VerificationException { checkState(lock.isHeldByCurrentThread()); checkState(!pending.containsKey(tx.getHash())); // This TX may spend our existing outputs even though it was not pending. This can happen in unit // tests, if keys are moved between wallets, if we're catching up to the chain given only a set of keys, // or if a dead coinbase transaction has moved back onto the main chain. boolean isDeadCoinbase = tx.isCoinBase() && dead.containsKey(tx.getHash()); if (isDeadCoinbase) { // There is a dead coinbase tx being received on the best chain. A coinbase tx is made dead when it moves // to a side chain but it can be switched back on a reorg and resurrected back to spent or unspent. // So take it out of the dead pool. Note that we don't resurrect dependent transactions here, even though // we could. Bitcoin Core nodes on the network have deleted the dependent transactions from their mempools // entirely by this point. We could and maybe should rebroadcast them so the network remembers and tries // to confirm them again. But this is a deeply unusual edge case that due to the maturity rule should never // happen in practice, thus for simplicities sake we ignore it here. log.info(" coinbase tx <-dead: confidence {}", tx.getHashAsString(), tx.getConfidence().getConfidenceType().name()); dead.remove(tx.getHash()); } // Update tx and other unspent/pending transactions by connecting inputs/outputs. updateForSpends(tx, true); // Now make sure it ends up in the right pool. Also, handle the case where this TX is double-spending // against our pending transactions. Note that a tx may double spend our pending transactions and also send // us money/spend our money. boolean hasOutputsToMe = tx.getValueSentToMe(this).signum() > 0; if (hasOutputsToMe) { // Needs to go into either unspent or spent (if the outputs were already spent by a pending tx). if (tx.isEveryOwnedOutputSpent(this)) { log.info(" tx {} ->spent (by pending)", tx.getHashAsString()); addWalletTransaction(Pool.SPENT, tx); } else { log.info(" tx {} ->unspent", tx.getHashAsString()); addWalletTransaction(Pool.UNSPENT, tx); } } else if (tx.getValueSentFromMe(this).signum() > 0) { // Didn't send us any money, but did spend some. Keep it around for record keeping purposes. log.info(" tx {} ->spent", tx.getHashAsString()); addWalletTransaction(Pool.SPENT, tx); } else if (forceAddToPool) { // Was manually added to pending, so we should keep it to notify the user of confidence information log.info(" tx {} ->spent (manually added)", tx.getHashAsString()); addWalletTransaction(Pool.SPENT, tx); } // Kill txns in conflict with this tx Set<Transaction> doubleSpendTxns = findDoubleSpendsAgainst(tx, pending); if (!doubleSpendTxns.isEmpty()) { // no need to addTransactionsDependingOn(doubleSpendTxns) because killTxns() already kills dependencies; killTxns(doubleSpendTxns, tx); } } /** * <p>Updates the wallet by checking if this TX spends any of our outputs, and marking them as spent if so. If * fromChain is true, also checks to see if any pending transaction spends outputs of this transaction and marks * the spent flags appropriately.</p> * * <p>It can be called in two contexts. One is when we receive a transaction on the best chain but it wasn't pending, * this most commonly happens when we have a set of keys but the wallet transactions were wiped and we are catching * up with the block chain. It can also happen if a block includes a transaction we never saw at broadcast time. * If this tx double spends, it takes precedence over our pending transactions and the pending tx goes dead.</p> * * <p>The other context it can be called is from {@link Wallet#receivePending(Transaction, java.util.List)}, * ie we saw a tx be broadcast or one was submitted directly that spends our own coins. If this tx double spends * it does NOT take precedence because the winner will be resolved by the miners - we assume that our version will * win, if we are wrong then when a block appears the tx will go dead.</p> * * @param tx The transaction which is being updated. * @param fromChain If true, the tx appeared on the current best chain, if false it was pending. */ private void updateForSpends(Transaction tx, boolean fromChain) throws VerificationException { checkState(lock.isHeldByCurrentThread()); if (fromChain) checkState(!pending.containsKey(tx.getHash())); for (TransactionInput input : tx.getInputs()) { TransactionInput.ConnectionResult result = input.connect(unspent, TransactionInput.ConnectMode.ABORT_ON_CONFLICT); if (result == TransactionInput.ConnectionResult.NO_SUCH_TX) { // Not found in the unspent map. Try again with the spent map. result = input.connect(spent, TransactionInput.ConnectMode.ABORT_ON_CONFLICT); if (result == TransactionInput.ConnectionResult.NO_SUCH_TX) { // Not found in the unspent and spent maps. Try again with the pending map. result = input.connect(pending, TransactionInput.ConnectMode.ABORT_ON_CONFLICT); if (result == TransactionInput.ConnectionResult.NO_SUCH_TX) { // Doesn't spend any of our outputs or is coinbase. continue; } } } TransactionOutput output = checkNotNull(input.getConnectedOutput()); if (result == TransactionInput.ConnectionResult.ALREADY_SPENT) { if (fromChain) { // Can be: // (1) We already marked this output as spent when we saw the pending transaction (most likely). // Now it's being confirmed of course, we cannot mark it as spent again. // (2) A double spend from chain: this will be handled later by findDoubleSpendsAgainst()/killTxns(). // // In any case, nothing to do here. } else { // We saw two pending transactions that double spend each other. We don't know which will win. // This can happen in the case of bad network nodes that mutate transactions. Do a hex dump // so the exact nature of the mutation can be examined. log.warn("Saw two pending transactions double spend each other"); log.warn(" offending input is input {}", tx.getInputs().indexOf(input)); log.warn("{}: {}", tx.getHash(), Utils.HEX.encode(tx.unsafeBitcoinSerialize())); Transaction other = output.getSpentBy().getParentTransaction(); log.warn("{}: {}", other.getHash(), Utils.HEX.encode(other.unsafeBitcoinSerialize())); } } else if (result == TransactionInput.ConnectionResult.SUCCESS) { // Otherwise we saw a transaction spend our coins, but we didn't try and spend them ourselves yet. // The outputs are already marked as spent by the connect call above, so check if there are any more for // us to use. Move if not. Transaction connected = checkNotNull(input.getConnectedTransaction()); log.info(" marked {} as spent by {}", input.getOutpoint(), tx.getHashAsString()); maybeMovePool(connected, "prevtx"); // Just because it's connected doesn't mean it's actually ours: sometimes we have total visibility. if (output.isMineOrWatched(this)) { checkState(myUnspents.remove(output)); } } } // Now check each output and see if there is a pending transaction which spends it. This shouldn't normally // ever occur because we expect transactions to arrive in temporal order, but this assumption can be violated // when we receive a pending transaction from the mempool that is relevant to us, which spends coins that we // didn't see arrive on the best chain yet. For instance, because of a chain replay or because of our keys were // used by another wallet somewhere else. Also, unconfirmed transactions can arrive from the mempool in more or // less random order. for (Transaction pendingTx : pending.values()) { for (TransactionInput input : pendingTx.getInputs()) { TransactionInput.ConnectionResult result = input.connect(tx, TransactionInput.ConnectMode.ABORT_ON_CONFLICT); if (fromChain) { // This TX is supposed to have just appeared on the best chain, so its outputs should not be marked // as spent yet. If they are, it means something is happening out of order. checkState(result != TransactionInput.ConnectionResult.ALREADY_SPENT); } if (result == TransactionInput.ConnectionResult.SUCCESS) { log.info("Connected pending tx input {}:{}", pendingTx.getHashAsString(), pendingTx.getInputs().indexOf(input)); // The unspents map might not have it if we never saw this tx until it was included in the chain // and thus becomes spent the moment we become aware of it. if (myUnspents.remove(input.getConnectedOutput())) log.info("Removed from UNSPENTS: {}", input.getConnectedOutput()); } } } if (!fromChain) { maybeMovePool(tx, "pendingtx"); } else { // If the transactions outputs are now all spent, it will be moved into the spent pool by the // processTxFromBestChain method. } } // Updates the wallet when a double spend occurs. overridingTx can be null for the case of coinbases private void killTxns(Set<Transaction> txnsToKill, @Nullable Transaction overridingTx) { LinkedList<Transaction> work = new LinkedList<Transaction>(txnsToKill); while (!work.isEmpty()) { final Transaction tx = work.poll(); log.warn("TX {} killed{}", tx.getHashAsString(), overridingTx != null ? " by " + overridingTx.getHashAsString() : ""); log.warn("Disconnecting each input and moving connected transactions."); // TX could be pending (finney attack), or in unspent/spent (coinbase killed by reorg). pending.remove(tx.getHash()); unspent.remove(tx.getHash()); spent.remove(tx.getHash()); addWalletTransaction(Pool.DEAD, tx); for (TransactionInput deadInput : tx.getInputs()) { Transaction connected = deadInput.getConnectedTransaction(); if (connected == null) continue; if (connected.getConfidence().getConfidenceType() != ConfidenceType.DEAD && deadInput.getConnectedOutput().getSpentBy() != null && deadInput.getConnectedOutput().getSpentBy().equals(deadInput)) { checkState(myUnspents.add(deadInput.getConnectedOutput())); log.info("Added to UNSPENTS: {} in {}", deadInput.getConnectedOutput(), deadInput.getConnectedOutput().getParentTransaction().getHash()); } deadInput.disconnect(); maybeMovePool(connected, "kill"); } tx.getConfidence().setOverridingTransaction(overridingTx); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.TYPE); // Now kill any transactions we have that depended on this one. for (TransactionOutput deadOutput : tx.getOutputs()) { if (myUnspents.remove(deadOutput)) log.info("XX Removed from UNSPENTS: {}", deadOutput); TransactionInput connected = deadOutput.getSpentBy(); if (connected == null) continue; final Transaction parentTransaction = connected.getParentTransaction(); log.info("This death invalidated dependent tx {}", parentTransaction.getHash()); work.push(parentTransaction); } } if (overridingTx == null) return; log.warn("Now attempting to connect the inputs of the overriding transaction."); for (TransactionInput input : overridingTx.getInputs()) { TransactionInput.ConnectionResult result = input.connect(unspent, TransactionInput.ConnectMode.DISCONNECT_ON_CONFLICT); if (result == TransactionInput.ConnectionResult.SUCCESS) { maybeMovePool(input.getConnectedTransaction(), "kill"); myUnspents.remove(input.getConnectedOutput()); log.info("Removing from UNSPENTS: {}", input.getConnectedOutput()); } else { result = input.connect(spent, TransactionInput.ConnectMode.DISCONNECT_ON_CONFLICT); if (result == TransactionInput.ConnectionResult.SUCCESS) { maybeMovePool(input.getConnectedTransaction(), "kill"); myUnspents.remove(input.getConnectedOutput()); log.info("Removing from UNSPENTS: {}", input.getConnectedOutput()); } } } } /** * If the transactions outputs are all marked as spent, and it's in the unspent map, move it. * If the owned transactions outputs are not all marked as spent, and it's in the spent map, move it. */ private void maybeMovePool(Transaction tx, String context) { checkState(lock.isHeldByCurrentThread()); if (tx.isEveryOwnedOutputSpent(this)) { // There's nothing left I can spend in this transaction. if (unspent.remove(tx.getHash()) != null) { if (log.isInfoEnabled()) { log.info(" {} {} <-unspent ->spent", tx.getHashAsString(), context); } spent.put(tx.getHash(), tx); } } else { if (spent.remove(tx.getHash()) != null) { if (log.isInfoEnabled()) { log.info(" {} {} <-spent ->unspent", tx.getHashAsString(), context); } unspent.put(tx.getHash(), tx); } } } /** * Calls {@link Wallet#commitTx} if tx is not already in the pending pool * * @return true if the tx was added to the wallet, or false if it was already in the pending pool */ public boolean maybeCommitTx(Transaction tx) throws VerificationException { tx.verify(); lock.lock(); try { if (pending.containsKey(tx.getHash())) return false; log.info("commitTx of {}", tx.getHashAsString()); Coin balance = getBalance(); tx.setUpdateTime(Utils.now()); // Put any outputs that are sending money back to us into the unspents map, and calculate their total value. Coin valueSentToMe = Coin.ZERO; for (TransactionOutput o : tx.getOutputs()) { if (!o.isMineOrWatched(this)) continue; valueSentToMe = valueSentToMe.add(o.getValue()); } // Mark the outputs we're spending as spent so we won't try and use them in future creations. This will also // move any transactions that are now fully spent to the spent map so we can skip them when creating future // spends. updateForSpends(tx, false); Set<Transaction> doubleSpendPendingTxns = findDoubleSpendsAgainst(tx, pending); Set<Transaction> doubleSpendUnspentTxns = findDoubleSpendsAgainst(tx, unspent); Set<Transaction> doubleSpendSpentTxns = findDoubleSpendsAgainst(tx, spent); if (!doubleSpendUnspentTxns.isEmpty() || !doubleSpendSpentTxns.isEmpty() || !isNotSpendingTxnsInConfidenceType(tx, ConfidenceType.DEAD)) { // tx is a double spend against a tx already in the best chain or spends outputs of a DEAD tx. // Add tx to the dead pool and schedule confidence listener notifications. log.info("->dead: {}", tx.getHashAsString()); tx.getConfidence().setConfidenceType(ConfidenceType.DEAD); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.TYPE); addWalletTransaction(Pool.DEAD, tx); } else if (!doubleSpendPendingTxns.isEmpty() || !isNotSpendingTxnsInConfidenceType(tx, ConfidenceType.IN_CONFLICT)) { // tx is a double spend against a pending tx or spends outputs of a tx already IN_CONFLICT. // Add tx to the pending pool. Update the confidence type of tx, the txns in conflict with tx and all // their dependencies to IN_CONFLICT and schedule confidence listener notifications. log.info("->pending (IN_CONFLICT): {}", tx.getHashAsString()); addWalletTransaction(Pool.PENDING, tx); doubleSpendPendingTxns.add(tx); addTransactionsDependingOn(doubleSpendPendingTxns, getTransactions(true)); for (Transaction doubleSpendTx : doubleSpendPendingTxns) { doubleSpendTx.getConfidence().setConfidenceType(ConfidenceType.IN_CONFLICT); confidenceChanged.put(doubleSpendTx, TransactionConfidence.Listener.ChangeReason.TYPE); } } else { // No conflict detected. // Add to the pending pool and schedule confidence listener notifications. log.info("->pending: {}", tx.getHashAsString()); tx.getConfidence().setConfidenceType(ConfidenceType.PENDING); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.TYPE); addWalletTransaction(Pool.PENDING, tx); } if (log.isInfoEnabled()) log.info("Estimated balance is now: {}", getBalance(BalanceType.ESTIMATED).toFriendlyString()); // Mark any keys used in the outputs as "used", this allows wallet UI's to auto-advance the current key // they are showing to the user in qr codes etc. markKeysAsUsed(tx); try { Coin valueSentFromMe = tx.getValueSentFromMe(this); Coin newBalance = balance.add(valueSentToMe).subtract(valueSentFromMe); if (valueSentToMe.signum() > 0) { checkBalanceFuturesLocked(null); queueOnCoinsReceived(tx, balance, newBalance); } if (valueSentFromMe.signum() > 0) queueOnCoinsSent(tx, balance, newBalance); maybeQueueOnWalletChanged(); } catch (ScriptException e) { // Cannot happen as we just created this transaction ourselves. throw new RuntimeException(e); } isConsistentOrThrow(); informConfidenceListenersIfNotReorganizing(); saveNow(); } finally { lock.unlock(); } return true; } /** * <p>Updates the wallet with the given transaction: puts it into the pending pool, sets the spent flags and runs * the onCoinsSent/onCoinsReceived event listener. Used in two situations:</p> * * <ol> * <li>When we have just successfully transmitted the tx we created to the network.</li> * <li>When we receive a pending transaction that didn't appear in the chain yet, and we did not create it.</li> * </ol> * * <p>Triggers an auto save.</p> */ public void commitTx(Transaction tx) throws VerificationException { checkArgument(maybeCommitTx(tx), "commitTx called on the same transaction twice"); } //endregion /******************************************************************************************************************/ //region Event listeners /** * Adds an event listener object. Methods on this object are called when something interesting happens, * like receiving money. Runs the listener methods in the user thread. */ public void addEventListener(WalletEventListener listener) { addChangeEventListener(Threading.USER_THREAD, listener); addCoinsReceivedEventListener(Threading.USER_THREAD, listener); addCoinsSentEventListener(Threading.USER_THREAD, listener); addKeyChainEventListener(Threading.USER_THREAD, listener); addReorganizeEventListener(Threading.USER_THREAD, listener); addScriptChangeEventListener(Threading.USER_THREAD, listener); addTransactionConfidenceEventListener(Threading.USER_THREAD, listener); } /** Use the more specific listener methods instead */ @Deprecated public void addEventListener(WalletEventListener listener, Executor executor) { addCoinsReceivedEventListener(executor, listener); addCoinsSentEventListener(executor, listener); addChangeEventListener(executor, listener); addKeyChainEventListener(executor, listener); addReorganizeEventListener(executor, listener); addScriptChangeEventListener(executor, listener); addTransactionConfidenceEventListener(executor, listener); } /** * Adds an event listener object. Methods on this object are called when something interesting happens, * like receiving money. Runs the listener methods in the user thread. */ public void addChangeEventListener(WalletChangeEventListener listener) { addChangeEventListener(Threading.USER_THREAD, listener); } /** * Adds an event listener object. Methods on this object are called when something interesting happens, * like receiving money. The listener is executed by the given executor. */ public void addChangeEventListener(Executor executor, WalletChangeEventListener listener) { // This is thread safe, so we don't need to take the lock. changeListeners.add(new ListenerRegistration<WalletChangeEventListener>(listener, executor)); } /** * Adds an event listener object called when coins are received. * Runs the listener methods in the user thread. */ public void addCoinsReceivedEventListener(WalletCoinsReceivedEventListener listener) { addCoinsReceivedEventListener(Threading.USER_THREAD, listener); } /** * Adds an event listener object called when coins are received. * The listener is executed by the given executor. */ public void addCoinsReceivedEventListener(Executor executor, WalletCoinsReceivedEventListener listener) { // This is thread safe, so we don't need to take the lock. coinsReceivedListeners.add(new ListenerRegistration<WalletCoinsReceivedEventListener>(listener, executor)); } /** * Adds an event listener object called when coins are sent. * Runs the listener methods in the user thread. */ public void addCoinsSentEventListener(WalletCoinsSentEventListener listener) { addCoinsSentEventListener(Threading.USER_THREAD, listener); } /** * Adds an event listener object called when coins are sent. * The listener is executed by the given executor. */ public void addCoinsSentEventListener(Executor executor, WalletCoinsSentEventListener listener) { // This is thread safe, so we don't need to take the lock. coinsSentListeners.add(new ListenerRegistration<WalletCoinsSentEventListener>(listener, executor)); } /** * Adds an event listener object. Methods on this object are called when keys are * added. The listener is executed in the user thread. */ public void addKeyChainEventListener(KeyChainEventListener listener) { keyChainGroup.addEventListener(listener, Threading.USER_THREAD); } /** * Adds an event listener object. Methods on this object are called when keys are * added. The listener is executed by the given executor. */ public void addKeyChainEventListener(Executor executor, KeyChainEventListener listener) { keyChainGroup.addEventListener(listener, executor); } /** * Adds an event listener object. Methods on this object are called when something interesting happens, * like receiving money. Runs the listener methods in the user thread. */ public void addReorganizeEventListener(WalletReorganizeEventListener listener) { addReorganizeEventListener(Threading.USER_THREAD, listener); } /** * Adds an event listener object. Methods on this object are called when something interesting happens, * like receiving money. The listener is executed by the given executor. */ public void addReorganizeEventListener(Executor executor, WalletReorganizeEventListener listener) { // This is thread safe, so we don't need to take the lock. reorganizeListeners.add(new ListenerRegistration<WalletReorganizeEventListener>(listener, executor)); } /** * Adds an event listener object. Methods on this object are called when scripts * watched by this wallet change. Runs the listener methods in the user thread. */ public void addScriptsChangeEventListener(ScriptsChangeEventListener listener) { addScriptChangeEventListener(Threading.USER_THREAD, listener); } /** * Adds an event listener object. Methods on this object are called when scripts * watched by this wallet change. The listener is executed by the given executor. */ public void addScriptChangeEventListener(Executor executor, ScriptsChangeEventListener listener) { // This is thread safe, so we don't need to take the lock. scriptChangeListeners.add(new ListenerRegistration<ScriptsChangeEventListener>(listener, executor)); } /** * Adds an event listener object. Methods on this object are called when confidence * of a transaction changes. Runs the listener methods in the user thread. */ public void addTransactionConfidenceEventListener(TransactionConfidenceEventListener listener) { addTransactionConfidenceEventListener(Threading.USER_THREAD, listener); } /** * Adds an event listener object. Methods on this object are called when confidence * of a transaction changes. The listener is executed by the given executor. */ public void addTransactionConfidenceEventListener(Executor executor, TransactionConfidenceEventListener listener) { // This is thread safe, so we don't need to take the lock. transactionConfidenceListeners.add(new ListenerRegistration<TransactionConfidenceEventListener>(listener, executor)); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. * @deprecated use the fine-grain event listeners instead. */ @Deprecated public boolean removeEventListener(WalletEventListener listener) { return removeChangeEventListener(listener) || removeCoinsReceivedEventListener(listener) || removeCoinsSentEventListener(listener) || removeKeyChainEventListener(listener) || removeReorganizeEventListener(listener) || removeTransactionConfidenceEventListener(listener); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. */ public boolean removeChangeEventListener(WalletChangeEventListener listener) { return ListenerRegistration.removeFromList(listener, changeListeners); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. */ public boolean removeCoinsReceivedEventListener(WalletCoinsReceivedEventListener listener) { return ListenerRegistration.removeFromList(listener, coinsReceivedListeners); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. */ public boolean removeCoinsSentEventListener(WalletCoinsSentEventListener listener) { return ListenerRegistration.removeFromList(listener, coinsSentListeners); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. */ public boolean removeKeyChainEventListener(KeyChainEventListener listener) { return keyChainGroup.removeEventListener(listener); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. */ public boolean removeReorganizeEventListener(WalletReorganizeEventListener listener) { return ListenerRegistration.removeFromList(listener, reorganizeListeners); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. */ public boolean removeScriptChangeEventListener(ScriptsChangeEventListener listener) { return ListenerRegistration.removeFromList(listener, scriptChangeListeners); } /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. */ public boolean removeTransactionConfidenceEventListener(TransactionConfidenceEventListener listener) { return ListenerRegistration.removeFromList(listener, transactionConfidenceListeners); } private void queueOnTransactionConfidenceChanged(final Transaction tx) { checkState(lock.isHeldByCurrentThread()); for (final ListenerRegistration<TransactionConfidenceEventListener> registration : transactionConfidenceListeners) { if (registration.executor == Threading.SAME_THREAD) { registration.listener.onTransactionConfidenceChanged(this, tx); } else { registration.executor.execute(new Runnable() { @Override public void run() { registration.listener.onTransactionConfidenceChanged(Wallet.this, tx); } }); } } } protected void maybeQueueOnWalletChanged() { // Don't invoke the callback in some circumstances, eg, whilst we are re-organizing or fiddling with // transactions due to a new block arriving. It will be called later instead. checkState(lock.isHeldByCurrentThread()); checkState(onWalletChangedSuppressions >= 0); if (onWalletChangedSuppressions > 0) return; for (final ListenerRegistration<WalletChangeEventListener> registration : changeListeners) { registration.executor.execute(new Runnable() { @Override public void run() { registration.listener.onWalletChanged(Wallet.this); } }); } } protected void queueOnCoinsReceived(final Transaction tx, final Coin balance, final Coin newBalance) { checkState(lock.isHeldByCurrentThread()); for (final ListenerRegistration<WalletCoinsReceivedEventListener> registration : coinsReceivedListeners) { registration.executor.execute(new Runnable() { @Override public void run() { registration.listener.onCoinsReceived(Wallet.this, tx, balance, newBalance); } }); } } protected void queueOnCoinsSent(final Transaction tx, final Coin prevBalance, final Coin newBalance) { checkState(lock.isHeldByCurrentThread()); for (final ListenerRegistration<WalletCoinsSentEventListener> registration : coinsSentListeners) { registration.executor.execute(new Runnable() { @Override public void run() { registration.listener.onCoinsSent(Wallet.this, tx, prevBalance, newBalance); } }); } } protected void queueOnReorganize() { checkState(lock.isHeldByCurrentThread()); checkState(insideReorg); for (final ListenerRegistration<WalletReorganizeEventListener> registration : reorganizeListeners) { registration.executor.execute(new Runnable() { @Override public void run() { registration.listener.onReorganize(Wallet.this); } }); } } protected void queueOnScriptsChanged(final List<Script> scripts, final boolean isAddingScripts) { for (final ListenerRegistration<ScriptsChangeEventListener> registration : scriptChangeListeners) { registration.executor.execute(new Runnable() { @Override public void run() { registration.listener.onScriptsChanged(Wallet.this, scripts, isAddingScripts); } }); } } //endregion /******************************************************************************************************************/ //region Vending transactions and other internal state /** * Returns a set of all transactions in the wallet. * @param includeDead If true, transactions that were overridden by a double spend are included. */ public Set<Transaction> getTransactions(boolean includeDead) { lock.lock(); try { Set<Transaction> all = new HashSet<Transaction>(); all.addAll(unspent.values()); all.addAll(spent.values()); all.addAll(pending.values()); if (includeDead) all.addAll(dead.values()); return all; } finally { lock.unlock(); } } /** * Returns a set of all WalletTransactions in the wallet. */ public Iterable<WalletTransaction> getWalletTransactions() { lock.lock(); try { Set<WalletTransaction> all = new HashSet<WalletTransaction>(); addWalletTransactionsToSet(all, Pool.UNSPENT, unspent.values()); addWalletTransactionsToSet(all, Pool.SPENT, spent.values()); addWalletTransactionsToSet(all, Pool.DEAD, dead.values()); addWalletTransactionsToSet(all, Pool.PENDING, pending.values()); return all; } finally { lock.unlock(); } } private static void addWalletTransactionsToSet(Set<WalletTransaction> txns, Pool poolType, Collection<Transaction> pool) { for (Transaction tx : pool) { txns.add(new WalletTransaction(poolType, tx)); } } /** * Adds a transaction that has been associated with a particular wallet pool. This is intended for usage by * deserialization code, such as the {@link WalletProtobufSerializer} class. It isn't normally useful for * applications. It does not trigger auto saving. */ public void addWalletTransaction(WalletTransaction wtx) { lock.lock(); try { addWalletTransaction(wtx.getPool(), wtx.getTransaction()); } finally { lock.unlock(); } } /** * Adds the given transaction to the given pools and registers a confidence change listener on it. */ private void addWalletTransaction(Pool pool, Transaction tx) { checkState(lock.isHeldByCurrentThread()); transactions.put(tx.getHash(), tx); switch (pool) { case UNSPENT: checkState(unspent.put(tx.getHash(), tx) == null); break; case SPENT: checkState(spent.put(tx.getHash(), tx) == null); break; case PENDING: checkState(pending.put(tx.getHash(), tx) == null); break; case DEAD: checkState(dead.put(tx.getHash(), tx) == null); break; default: throw new RuntimeException("Unknown wallet transaction type " + pool); } if (pool == Pool.UNSPENT || pool == Pool.PENDING) { for (TransactionOutput output : tx.getOutputs()) { if (output.isAvailableForSpending() && output.isMineOrWatched(this)) myUnspents.add(output); } } // This is safe even if the listener has been added before, as TransactionConfidence ignores duplicate // registration requests. That makes the code in the wallet simpler. tx.getConfidence().addEventListener(Threading.SAME_THREAD, txConfidenceListener); } /** * Returns all non-dead, active transactions ordered by recency. */ public List<Transaction> getTransactionsByTime() { return getRecentTransactions(0, false); } /** * Returns an list of N transactions, ordered by increasing age. Transactions on side chains are not included. * Dead transactions (overridden by double spends) are optionally included. <p> * <p/> * Note: the current implementation is O(num transactions in wallet). Regardless of how many transactions are * requested, the cost is always the same. In future, requesting smaller numbers of transactions may be faster * depending on how the wallet is implemented (eg if backed by a database). */ public List<Transaction> getRecentTransactions(int numTransactions, boolean includeDead) { lock.lock(); try { checkArgument(numTransactions >= 0); // Firstly, put all transactions into an array. int size = unspent.size() + spent.size() + pending.size(); if (numTransactions > size || numTransactions == 0) { numTransactions = size; } ArrayList<Transaction> all = new ArrayList<Transaction>(getTransactions(includeDead)); // Order by update time. Collections.sort(all, Transaction.SORT_TX_BY_UPDATE_TIME); if (numTransactions == all.size()) { return all; } else { all.subList(numTransactions, all.size()).clear(); return all; } } finally { lock.unlock(); } } /** * Returns a transaction object given its hash, if it exists in this wallet, or null otherwise. */ @Nullable public Transaction getTransaction(Sha256Hash hash) { lock.lock(); try { return transactions.get(hash); } finally { lock.unlock(); } } /** {@inheritDoc} */ @Override public Map<Sha256Hash, Transaction> getTransactionPool(Pool pool) { lock.lock(); try { switch (pool) { case UNSPENT: return unspent; case SPENT: return spent; case PENDING: return pending; case DEAD: return dead; default: throw new RuntimeException("Unknown wallet transaction type " + pool); } } finally { lock.unlock(); } } /** * Prepares the wallet for a blockchain replay. Removes all transactions (as they would get in the way of the * replay) and makes the wallet think it has never seen a block. {@link WalletEventListener#onWalletChanged} will * be fired. */ public void reset() { lock.lock(); try { clearTransactions(); lastBlockSeenHash = null; lastBlockSeenHeight = -1; // Magic value for 'never'. lastBlockSeenTimeSecs = 0; saveLater(); maybeQueueOnWalletChanged(); } finally { lock.unlock(); } } /** * Deletes transactions which appeared above the given block height from the wallet, but does not touch the keys. * This is useful if you have some keys and wish to replay the block chain into the wallet in order to pick them up. * Triggers auto saving. */ public void clearTransactions(int fromHeight) { lock.lock(); try { if (fromHeight == 0) { clearTransactions(); saveLater(); } else { throw new UnsupportedOperationException(); } } finally { lock.unlock(); } } private void clearTransactions() { unspent.clear(); spent.clear(); pending.clear(); dead.clear(); transactions.clear(); myUnspents.clear(); } /** * Returns all the outputs that match addresses or scripts added via {@link #addWatchedAddress(Address)} or * {@link #addWatchedScripts(java.util.List)}. * @param excludeImmatureCoinbases Whether to ignore outputs that are unspendable due to being immature. */ public List<TransactionOutput> getWatchedOutputs(boolean excludeImmatureCoinbases) { lock.lock(); keyChainGroupLock.lock(); try { LinkedList<TransactionOutput> candidates = Lists.newLinkedList(); for (Transaction tx : Iterables.concat(unspent.values(), pending.values())) { if (excludeImmatureCoinbases && !tx.isMature()) continue; for (TransactionOutput output : tx.getOutputs()) { if (!output.isAvailableForSpending()) continue; try { Script scriptPubKey = output.getScriptPubKey(); if (!watchedScripts.contains(scriptPubKey)) continue; candidates.add(output); } catch (ScriptException e) { // Ignore } } } return candidates; } finally { keyChainGroupLock.unlock(); lock.unlock(); } } /** * Clean up the wallet. Currently, it only removes risky pending transaction from the wallet and only if their * outputs have not been spent. */ public void cleanup() { lock.lock(); try { boolean dirty = false; for (Iterator<Transaction> i = pending.values().iterator(); i.hasNext();) { Transaction tx = i.next(); if (isTransactionRisky(tx, null) && !acceptRiskyTransactions) { log.debug("Found risky transaction {} in wallet during cleanup.", tx.getHashAsString()); if (!tx.isAnyOutputSpent()) { // Sync myUnspents with the change. for (TransactionInput input : tx.getInputs()) { TransactionOutput output = input.getConnectedOutput(); if (output == null) continue; if (output.isMineOrWatched(this)) checkState(myUnspents.add(output)); input.disconnect(); } for (TransactionOutput output : tx.getOutputs()) myUnspents.remove(output); i.remove(); transactions.remove(tx.getHash()); dirty = true; log.info("Removed transaction {} from pending pool during cleanup.", tx.getHashAsString()); } else { log.info( "Cannot remove transaction {} from pending pool during cleanup, as it's already spent partially.", tx.getHashAsString()); } } } if (dirty) { isConsistentOrThrow(); saveLater(); if (log.isInfoEnabled()) log.info("Estimated balance is now: {}", getBalance(BalanceType.ESTIMATED).toFriendlyString()); } } finally { lock.unlock(); } } EnumSet<Pool> getContainingPools(Transaction tx) { lock.lock(); try { EnumSet<Pool> result = EnumSet.noneOf(Pool.class); Sha256Hash txHash = tx.getHash(); if (unspent.containsKey(txHash)) { result.add(Pool.UNSPENT); } if (spent.containsKey(txHash)) { result.add(Pool.SPENT); } if (pending.containsKey(txHash)) { result.add(Pool.PENDING); } if (dead.containsKey(txHash)) { result.add(Pool.DEAD); } return result; } finally { lock.unlock(); } } @VisibleForTesting public int getPoolSize(WalletTransaction.Pool pool) { lock.lock(); try { switch (pool) { case UNSPENT: return unspent.size(); case SPENT: return spent.size(); case PENDING: return pending.size(); case DEAD: return dead.size(); } throw new RuntimeException("Unreachable"); } finally { lock.unlock(); } } @VisibleForTesting public boolean poolContainsTxHash(final WalletTransaction.Pool pool, final Sha256Hash txHash) { lock.lock(); try { switch (pool) { case UNSPENT: return unspent.containsKey(txHash); case SPENT: return spent.containsKey(txHash); case PENDING: return pending.containsKey(txHash); case DEAD: return dead.containsKey(txHash); } throw new RuntimeException("Unreachable"); } finally { lock.unlock(); } } /** Returns a copy of the internal unspent outputs list */ public List<TransactionOutput> getUnspents() { lock.lock(); try { return new ArrayList<TransactionOutput>(myUnspents); } finally { lock.unlock(); } } @Override public String toString() { return toString(false, true, true, null); } /** * Formats the wallet as a human readable piece of text. Intended for debugging, the format is not meant to be * stable or human readable. * @param includePrivateKeys Whether raw private key data should be included. * @param includeTransactions Whether to print transaction data. * @param includeExtensions Whether to print extension data. * @param chain If set, will be used to estimate lock times for block timelocked transactions. */ public String toString(boolean includePrivateKeys, boolean includeTransactions, boolean includeExtensions, @Nullable AbstractBlockChain chain) { lock.lock(); keyChainGroupLock.lock(); try { StringBuilder builder = new StringBuilder(); Coin estimatedBalance = getBalance(BalanceType.ESTIMATED); Coin availableBalance = getBalance(BalanceType.AVAILABLE_SPENDABLE); builder.append("Wallet containing ").append(estimatedBalance.toFriendlyString()).append(" (spendable: ") .append(availableBalance.toFriendlyString()).append(") in:\n"); builder.append(" ").append(pending.size()).append(" pending transactions\n"); builder.append(" ").append(unspent.size()).append(" unspent transactions\n"); builder.append(" ").append(spent.size()).append(" spent transactions\n"); builder.append(" ").append(dead.size()).append(" dead transactions\n"); final Date lastBlockSeenTime = getLastBlockSeenTime(); builder.append("Last seen best block: ").append(getLastBlockSeenHeight()).append(" (") .append(lastBlockSeenTime == null ? "time unknown" : Utils.dateTimeFormat(lastBlockSeenTime)) .append("): ").append(getLastBlockSeenHash()).append('\n'); final KeyCrypter crypter = keyChainGroup.getKeyCrypter(); if (crypter != null) builder.append("Encryption: ").append(crypter).append('\n'); if (isWatching()) builder.append("Wallet is watching.\n"); // Do the keys. builder.append("\nKeys:\n"); builder.append("Earliest creation time: ").append(Utils.dateTimeFormat(getEarliestKeyCreationTime() * 1000)) .append('\n'); final Date keyRotationTime = getKeyRotationTime(); if (keyRotationTime != null) builder.append("Key rotation time: ").append(Utils.dateTimeFormat(keyRotationTime)).append('\n'); builder.append(keyChainGroup.toString(includePrivateKeys)); if (!watchedScripts.isEmpty()) { builder.append("\nWatched scripts:\n"); for (Script script : watchedScripts) { builder.append(" ").append(script).append("\n"); } } if (includeTransactions) { // Print the transactions themselves if (pending.size() > 0) { builder.append("\n>>> PENDING:\n"); toStringHelper(builder, pending, chain, Transaction.SORT_TX_BY_UPDATE_TIME); } if (unspent.size() > 0) { builder.append("\n>>> UNSPENT:\n"); toStringHelper(builder, unspent, chain, Transaction.SORT_TX_BY_HEIGHT); } if (spent.size() > 0) { builder.append("\n>>> SPENT:\n"); toStringHelper(builder, spent, chain, Transaction.SORT_TX_BY_HEIGHT); } if (dead.size() > 0) { builder.append("\n>>> DEAD:\n"); toStringHelper(builder, dead, chain, Transaction.SORT_TX_BY_UPDATE_TIME); } } if (includeExtensions && extensions.size() > 0) { builder.append("\n>>> EXTENSIONS:\n"); for (WalletExtension extension : extensions.values()) { builder.append(extension).append("\n\n"); } } return builder.toString(); } finally { keyChainGroupLock.unlock(); lock.unlock(); } } private void toStringHelper(StringBuilder builder, Map<Sha256Hash, Transaction> transactionMap, @Nullable AbstractBlockChain chain, @Nullable Comparator<Transaction> sortOrder) { checkState(lock.isHeldByCurrentThread()); final Collection<Transaction> txns; if (sortOrder != null) { txns = new TreeSet<Transaction>(sortOrder); txns.addAll(transactionMap.values()); } else { txns = transactionMap.values(); } for (Transaction tx : txns) { try { builder.append(tx.getValue(this).toFriendlyString()); builder.append(" total value (sends "); builder.append(tx.getValueSentFromMe(this).toFriendlyString()); builder.append(" and receives "); builder.append(tx.getValueSentToMe(this).toFriendlyString()); builder.append(")\n"); } catch (ScriptException e) { // Ignore and don't print this line. } if (tx.hasConfidence()) builder.append(" confidence: ").append(tx.getConfidence()).append('\n'); builder.append(tx.toString(chain)); } } /** * Returns an immutable view of the transactions currently waiting for network confirmations. */ public Collection<Transaction> getPendingTransactions() { lock.lock(); try { return Collections.unmodifiableCollection(pending.values()); } finally { lock.unlock(); } } /** * Returns the earliest creation time of keys or watched scripts in this wallet, in seconds since the epoch, ie the min * of {@link org.bitcoinj.core.ECKey#getCreationTimeSeconds()}. This can return zero if at least one key does * not have that data (was created before key timestamping was implemented). <p> * * This method is most often used in conjunction with {@link PeerGroup#setFastCatchupTimeSecs(long)} in order to * optimize chain download for new users of wallet apps. Backwards compatibility notice: if you get zero from this * method, you can instead use the time of the first release of your software, as it's guaranteed no users will * have wallets pre-dating this time. <p> * * If there are no keys in the wallet, the current time is returned. */ @Override public long getEarliestKeyCreationTime() { keyChainGroupLock.lock(); try { long earliestTime = keyChainGroup.getEarliestKeyCreationTime(); for (Script script : watchedScripts) earliestTime = Math.min(script.getCreationTimeSeconds(), earliestTime); if (earliestTime == Long.MAX_VALUE) return Utils.currentTimeSeconds(); return earliestTime; } finally { keyChainGroupLock.unlock(); } } /** Returns the hash of the last seen best-chain block, or null if the wallet is too old to store this data. */ @Nullable public Sha256Hash getLastBlockSeenHash() { lock.lock(); try { return lastBlockSeenHash; } finally { lock.unlock(); } } public void setLastBlockSeenHash(@Nullable Sha256Hash lastBlockSeenHash) { lock.lock(); try { this.lastBlockSeenHash = lastBlockSeenHash; } finally { lock.unlock(); } } public void setLastBlockSeenHeight(int lastBlockSeenHeight) { lock.lock(); try { this.lastBlockSeenHeight = lastBlockSeenHeight; } finally { lock.unlock(); } } public void setLastBlockSeenTimeSecs(long timeSecs) { lock.lock(); try { lastBlockSeenTimeSecs = timeSecs; } finally { lock.unlock(); } } /** * Returns the UNIX time in seconds since the epoch extracted from the last best seen block header. This timestamp * is <b>not</b> the local time at which the block was first observed by this application but rather what the block * (i.e. miner) self declares. It is allowed to have some significant drift from the real time at which the block * was found, although most miners do use accurate times. If this wallet is old and does not have a recorded * time then this method returns zero. */ public long getLastBlockSeenTimeSecs() { lock.lock(); try { return lastBlockSeenTimeSecs; } finally { lock.unlock(); } } /** * Returns a {@link Date} representing the time extracted from the last best seen block header. This timestamp * is <b>not</b> the local time at which the block was first observed by this application but rather what the block * (i.e. miner) self declares. It is allowed to have some significant drift from the real time at which the block * was found, although most miners do use accurate times. If this wallet is old and does not have a recorded * time then this method returns null. */ @Nullable public Date getLastBlockSeenTime() { final long secs = getLastBlockSeenTimeSecs(); if (secs == 0) return null; else return new Date(secs * 1000); } /** * Returns the height of the last seen best-chain block. Can be 0 if a wallet is brand new or -1 if the wallet * is old and doesn't have that data. */ public int getLastBlockSeenHeight() { lock.lock(); try { return lastBlockSeenHeight; } finally { lock.unlock(); } } /** * Get the version of the Wallet. * This is an int you can use to indicate which versions of wallets your code understands, * and which come from the future (and hence cannot be safely loaded). */ public int getVersion() { return version; } /** * Set the version number of the wallet. See {@link Wallet#getVersion()}. */ public void setVersion(int version) { this.version = version; } /** * Set the description of the wallet. * This is a Unicode encoding string typically entered by the user as descriptive text for the wallet. */ public void setDescription(String description) { this.description = description; } /** * Get the description of the wallet. See {@link Wallet#setDescription(String))} */ public String getDescription() { return description; } //endregion /******************************************************************************************************************/ //region Balance and balance futures /** * <p>It's possible to calculate a wallets balance from multiple points of view. This enum selects which * {@link #getBalance(BalanceType)} should use.</p> * * <p>Consider a real-world example: you buy a snack costing $5 but you only have a $10 bill. At the start you have * $10 viewed from every possible angle. After you order the snack you hand over your $10 bill. From the * perspective of your wallet you have zero dollars (AVAILABLE). But you know in a few seconds the shopkeeper * will give you back $5 change so most people in practice would say they have $5 (ESTIMATED).</p> * * <p>The fact that the wallet can track transactions which are not spendable by itself ("watching wallets") adds * another type of balance to the mix. Although the wallet won't do this by default, advanced use cases that * override the relevancy checks can end up with a mix of spendable and unspendable transactions.</p> */ public enum BalanceType { /** * Balance calculated assuming all pending transactions are in fact included into the best chain by miners. * This includes the value of immature coinbase transactions. */ ESTIMATED, /** * Balance that could be safely used to create new spends, if we had all the needed private keys. This is * whatever the default coin selector would make available, which by default means transaction outputs with at * least 1 confirmation and pending transactions created by our own wallet which have been propagated across * the network. Whether we <i>actually</i> have the private keys or not is irrelevant for this balance type. */ AVAILABLE, /** Same as ESTIMATED but only for outputs we have the private keys for and can sign ourselves. */ ESTIMATED_SPENDABLE, /** Same as AVAILABLE but only for outputs we have the private keys for and can sign ourselves. */ AVAILABLE_SPENDABLE } /** @deprecated Use {@link #getBalance()} instead as including watched balances is now the default behaviour */ @Deprecated public Coin getWatchedBalance() { return getBalance(); } /** @deprecated Use {@link #getBalance(CoinSelector)} instead as including watched balances is now the default behaviour */ @Deprecated public Coin getWatchedBalance(CoinSelector selector) { return getBalance(selector); } /** * Returns the AVAILABLE balance of this wallet. See {@link BalanceType#AVAILABLE} for details on what this * means. */ public Coin getBalance() { return getBalance(BalanceType.AVAILABLE); } /** * Returns the balance of this wallet as calculated by the provided balanceType. */ public Coin getBalance(BalanceType balanceType) { lock.lock(); try { if (balanceType == BalanceType.AVAILABLE || balanceType == BalanceType.AVAILABLE_SPENDABLE) { List<TransactionOutput> candidates = calculateAllSpendCandidates(true, balanceType == BalanceType.AVAILABLE_SPENDABLE); CoinSelection selection = coinSelector.select(NetworkParameters.MAX_MONEY, candidates); return selection.valueGathered; } else if (balanceType == BalanceType.ESTIMATED || balanceType == BalanceType.ESTIMATED_SPENDABLE) { List<TransactionOutput> all = calculateAllSpendCandidates(false, balanceType == BalanceType.ESTIMATED_SPENDABLE); Coin value = Coin.ZERO; for (TransactionOutput out : all) value = value.add(out.getValue()); return value; } else { throw new AssertionError("Unknown balance type"); // Unreachable. } } finally { lock.unlock(); } } /** * Returns the balance that would be considered spendable by the given coin selector, including watched outputs * (i.e. balance includes outputs we don't have the private keys for). Just asks it to select as many coins as * possible and returns the total. */ public Coin getBalance(CoinSelector selector) { lock.lock(); try { checkNotNull(selector); List<TransactionOutput> candidates = calculateAllSpendCandidates(true, false); CoinSelection selection = selector.select(params.getMaxMoney(), candidates); return selection.valueGathered; } finally { lock.unlock(); } } private static class BalanceFutureRequest { public SettableFuture<Coin> future; public Coin value; public BalanceType type; } @GuardedBy("lock") private List<BalanceFutureRequest> balanceFutureRequests = Lists.newLinkedList(); /** * <p>Returns a future that will complete when the balance of the given type has becom equal or larger to the given * value. If the wallet already has a large enough balance the future is returned in a pre-completed state. Note * that this method is not blocking, if you want to actually wait immediately, you have to call .get() on * the result.</p> * * <p>Also note that by the time the future completes, the wallet may have changed yet again if something else * is going on in parallel, so you should treat the returned balance as advisory and be prepared for sending * money to fail! Finally please be aware that any listeners on the future will run either on the calling thread * if it completes immediately, or eventually on a background thread if the balance is not yet at the right * level. If you do something that means you know the balance should be sufficient to trigger the future, * you can use {@link org.bitcoinj.utils.Threading#waitForUserCode()} to block until the future had a * chance to be updated.</p> */ public ListenableFuture<Coin> getBalanceFuture(final Coin value, final BalanceType type) { lock.lock(); try { final SettableFuture<Coin> future = SettableFuture.create(); final Coin current = getBalance(type); if (current.compareTo(value) >= 0) { // Already have enough. future.set(current); } else { // Will be checked later in checkBalanceFutures. We don't just add an event listener for ourselves // here so that running getBalanceFuture().get() in the user code thread works - generally we must // avoid giving the user back futures that require the user code thread to be free. BalanceFutureRequest req = new BalanceFutureRequest(); req.future = future; req.value = value; req.type = type; balanceFutureRequests.add(req); } return future; } finally { lock.unlock(); } } // Runs any balance futures in the user code thread. @SuppressWarnings("FieldAccessNotGuarded") private void checkBalanceFuturesLocked(@Nullable Coin avail) { checkState(lock.isHeldByCurrentThread()); final ListIterator<BalanceFutureRequest> it = balanceFutureRequests.listIterator(); while (it.hasNext()) { final BalanceFutureRequest req = it.next(); Coin val = getBalance(req.type); // This could be slow for lots of futures. if (val.compareTo(req.value) < 0) continue; // Found one that's finished. it.remove(); final Coin v = val; // Don't run any user-provided future listeners with our lock held. Threading.USER_THREAD.execute(new Runnable() { @Override public void run() { req.future.set(v); } }); } } /** * Returns the amount of bitcoin ever received via output. <b>This is not the balance!</b> If an output spends from a * transaction whose inputs are also to our wallet, the input amounts are deducted from the outputs contribution, with a minimum of zero * contribution. The idea behind this is we avoid double counting money sent to us. * @return the total amount of satoshis received, regardless of whether it was spent or not. */ public Coin getTotalReceived() { Coin total = Coin.ZERO; // Include outputs to us if they were not just change outputs, ie the inputs to us summed to less // than the outputs to us. for (Transaction tx: transactions.values()) { Coin txTotal = Coin.ZERO; for (TransactionOutput output : tx.getOutputs()) { if (output.isMine(this)) { txTotal = txTotal.add(output.getValue()); } } for (TransactionInput in : tx.getInputs()) { TransactionOutput prevOut = in.getConnectedOutput(); if (prevOut != null && prevOut.isMine(this)) { txTotal = txTotal.subtract(prevOut.getValue()); } } if (txTotal.isPositive()) { total = total.add(txTotal); } } return total; } /** * Returns the amount of bitcoin ever sent via output. If an output is sent to our own wallet, because of change or * rotating keys or whatever, we do not count it. If the wallet was * involved in a shared transaction, i.e. there is some input to the transaction that we don't have the key for, then * we multiply the sum of the output values by the proportion of satoshi coming in to our inputs. Essentially we treat * inputs as pooling into the transaction, becoming fungible and being equally distributed to all outputs. * @return the total amount of satoshis sent by us */ public Coin getTotalSent() { Coin total = Coin.ZERO; for (Transaction tx: transactions.values()) { // Count spent outputs to only if they were not to us. This means we don't count change outputs. Coin txOutputTotal = Coin.ZERO; for (TransactionOutput out : tx.getOutputs()) { if (out.isMine(this) == false) { txOutputTotal = txOutputTotal.add(out.getValue()); } } // Count the input values to us Coin txOwnedInputsTotal = Coin.ZERO; for (TransactionInput in : tx.getInputs()) { TransactionOutput prevOut = in.getConnectedOutput(); if (prevOut != null && prevOut.isMine(this)) { txOwnedInputsTotal = txOwnedInputsTotal.add(prevOut.getValue()); } } // If there is an input that isn't from us, i.e. this is a shared transaction Coin txInputsTotal = tx.getInputSum(); if (txOwnedInputsTotal != txInputsTotal) { // multiply our output total by the appropriate proportion to account for the inputs that we don't own BigInteger txOutputTotalNum = new BigInteger(txOutputTotal.toString()); txOutputTotalNum = txOutputTotalNum.multiply(new BigInteger(txOwnedInputsTotal.toString())); txOutputTotalNum = txOutputTotalNum.divide(new BigInteger(txInputsTotal.toString())); txOutputTotal = Coin.valueOf(txOutputTotalNum.longValue()); } total = total.add(txOutputTotal); } return total; } //endregion /******************************************************************************************************************/ //region Creating and sending transactions /** A SendResult is returned to you as part of sending coins to a recipient. */ public static class SendResult { /** The Bitcoin transaction message that moves the money. */ public Transaction tx; /** A future that will complete once the tx message has been successfully broadcast to the network. This is just the result of calling broadcast.future() */ public ListenableFuture<Transaction> broadcastComplete; /** The broadcast object returned by the linked TransactionBroadcaster */ public TransactionBroadcast broadcast; } /** * Enumerates possible resolutions for missing signatures. */ public enum MissingSigsMode { /** Input script will have OP_0 instead of missing signatures */ USE_OP_ZERO, /** * Missing signatures will be replaced by dummy sigs. This is useful when you'd like to know the fee for * a transaction without knowing the user's password, as fee depends on size. */ USE_DUMMY_SIG, /** * If signature is missing, {@link org.bitcoinj.signers.TransactionSigner.MissingSignatureException} * will be thrown for P2SH and {@link ECKey.MissingPrivateKeyException} for other tx types. */ THROW } /** * <p>Statelessly creates a transaction that sends the given value to address. The change is sent to * {@link Wallet#currentChangeAddress()}, so you must have added at least one key.</p> * * <p>If you just want to send money quickly, you probably want * {@link Wallet#sendCoins(TransactionBroadcaster, Address, Coin)} instead. That will create the sending * transaction, commit to the wallet and broadcast it to the network all in one go. This method is lower level * and lets you see the proposed transaction before anything is done with it.</p> * * <p>This is a helper method that is equivalent to using {@link SendRequest#to(Address, Coin)} * followed by {@link Wallet#completeTx(Wallet.SendRequest)} and returning the requests transaction object. * Note that this means a fee may be automatically added if required, if you want more control over the process, * just do those two steps yourself.</p> * * <p>IMPORTANT: This method does NOT update the wallet. If you call createSend again you may get two transactions * that spend the same coins. You have to call {@link Wallet#commitTx(Transaction)} on the created transaction to * prevent this, but that should only occur once the transaction has been accepted by the network. This implies * you cannot have more than one outstanding sending tx at once.</p> * * <p>You MUST ensure that the value is not smaller than {@link Transaction#MIN_NONDUST_OUTPUT} or the transaction * will almost certainly be rejected by the network as dust.</p> * * @param address The Bitcoin address to send the money to. * @param value How much currency to send. * @return either the created Transaction or null if there are insufficient coins. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public Transaction createSend(Address address, Coin value) throws InsufficientMoneyException { SendRequest req = SendRequest.to(address, value); if (params.getId().equals(NetworkParameters.ID_UNITTESTNET)) req.shuffleOutputs = false; completeTx(req); return req.tx; } /** * Sends coins to the given address but does not broadcast the resulting pending transaction. It is still stored * in the wallet, so when the wallet is added to a {@link PeerGroup} or {@link Peer} the transaction will be * announced to the network. The given {@link SendRequest} is completed first using * {@link Wallet#completeTx(Wallet.SendRequest)} to make it valid. * * @return the Transaction that was created * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws IllegalArgumentException if you try and complete the same SendRequest twice * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public Transaction sendCoinsOffline(SendRequest request) throws InsufficientMoneyException { lock.lock(); try { completeTx(request); commitTx(request.tx); return request.tx; } finally { lock.unlock(); } } /** * <p>Sends coins to the given address, via the given {@link PeerGroup}. Change is returned to * {@link Wallet#currentChangeAddress()}. Note that a fee may be automatically added if one may be required for the * transaction to be confirmed.</p> * * <p>The returned object provides both the transaction, and a future that can be used to learn when the broadcast * is complete. Complete means, if the PeerGroup is limited to only one connection, when it was written out to * the socket. Otherwise when the transaction is written out and we heard it back from a different peer.</p> * * <p>Note that the sending transaction is committed to the wallet immediately, not when the transaction is * successfully broadcast. This means that even if the network hasn't heard about your transaction you won't be * able to spend those same coins again.</p> * * <p>You MUST ensure that value is not smaller than {@link Transaction#MIN_NONDUST_OUTPUT} or the transaction will * almost certainly be rejected by the network as dust.</p> * * @param broadcaster a {@link TransactionBroadcaster} to use to send the transactions out. * @param to Which address to send coins to. * @param value How much value to send. * @return An object containing the transaction that was created, and a future for the broadcast of it. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin value) throws InsufficientMoneyException { SendRequest request = SendRequest.to(to, value); return sendCoins(broadcaster, request); } /** * <p>Sends coins according to the given request, via the given {@link TransactionBroadcaster}.</p> * * <p>The returned object provides both the transaction, and a future that can be used to learn when the broadcast * is complete. Complete means, if the PeerGroup is limited to only one connection, when it was written out to * the socket. Otherwise when the transaction is written out and we heard it back from a different peer.</p> * * <p>Note that the sending transaction is committed to the wallet immediately, not when the transaction is * successfully broadcast. This means that even if the network hasn't heard about your transaction you won't be * able to spend those same coins again.</p> * * @param broadcaster the target to use for broadcast. * @param request the SendRequest that describes what to do, get one using static methods on SendRequest itself. * @return An object containing the transaction that was created, and a future for the broadcast of it. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws IllegalArgumentException if you try and complete the same SendRequest twice * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public SendResult sendCoins(TransactionBroadcaster broadcaster, SendRequest request) throws InsufficientMoneyException { // Should not be locked here, as we're going to call into the broadcaster and that might want to hold its // own lock. sendCoinsOffline handles everything that needs to be locked. checkState(!lock.isHeldByCurrentThread()); // Commit the TX to the wallet immediately so the spent coins won't be reused. // TODO: We should probably allow the request to specify tx commit only after the network has accepted it. Transaction tx = sendCoinsOffline(request); SendResult result = new SendResult(); result.tx = tx; // The tx has been committed to the pending pool by this point (via sendCoinsOffline -> commitTx), so it has // a txConfidenceListener registered. Once the tx is broadcast the peers will update the memory pool with the // count of seen peers, the memory pool will update the transaction confidence object, that will invoke the // txConfidenceListener which will in turn invoke the wallets event listener onTransactionConfidenceChanged // method. result.broadcast = broadcaster.broadcastTransaction(tx); result.broadcastComplete = result.broadcast.future(); return result; } /** * Satisfies the given {@link SendRequest} using the default transaction broadcaster configured either via * {@link PeerGroup#addWallet(Wallet)} or directly with {@link #setTransactionBroadcaster(TransactionBroadcaster)}. * * @param request the SendRequest that describes what to do, get one using static methods on SendRequest itself. * @return An object containing the transaction that was created, and a future for the broadcast of it. * @throws IllegalStateException if no transaction broadcaster has been configured. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws IllegalArgumentException if you try and complete the same SendRequest twice * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public SendResult sendCoins(SendRequest request) throws InsufficientMoneyException { TransactionBroadcaster broadcaster = vTransactionBroadcaster; checkState(broadcaster != null, "No transaction broadcaster is configured"); return sendCoins(broadcaster, request); } /** * Sends coins to the given address, via the given {@link Peer}. Change is returned to {@link Wallet#currentChangeAddress()}. * If an exception is thrown by {@link Peer#sendMessage(Message)} the transaction is still committed, so the * pending transaction must be broadcast <b>by you</b> at some other time. Note that a fee may be automatically added * if one may be required for the transaction to be confirmed. * * @return The {@link Transaction} that was created or null if there was insufficient balance to send the coins. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws IllegalArgumentException if you try and complete the same SendRequest twice * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public Transaction sendCoins(Peer peer, SendRequest request) throws InsufficientMoneyException { Transaction tx = sendCoinsOffline(request); peer.sendMessage(tx); return tx; } /** * Class of exceptions thrown in {@link Wallet#completeTx(SendRequest)}. */ public static class CompletionException extends RuntimeException {} /** * Thrown if the resultant transaction would violate the dust rules (an output that's too small to be worthwhile). */ public static class DustySendRequested extends CompletionException {} /** * Thrown if there is more than one OP_RETURN output for the resultant transaction. */ public static class MultipleOpReturnRequested extends CompletionException {} /** * Thrown when we were trying to empty the wallet, and the total amount of money we were trying to empty after * being reduced for the fee was smaller than the min payment. Note that the missing field will be null in this * case. */ public static class CouldNotAdjustDownwards extends CompletionException {} /** * Thrown if the resultant transaction is too big for Bitcoin to process. Try breaking up the amounts of value. */ public static class ExceededMaxTransactionSize extends CompletionException {} /** * Given a spend request containing an incomplete transaction, makes it valid by adding outputs and signed inputs * according to the instructions in the request. The transaction in the request is modified by this method. * * @param req a SendRequest that contains the incomplete transaction and details for how to make it valid. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws IllegalArgumentException if you try and complete the same SendRequest twice * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public void completeTx(SendRequest req) throws InsufficientMoneyException { lock.lock(); try { checkArgument(!req.completed, "Given SendRequest has already been completed."); // Calculate the amount of value we need to import. Coin value = Coin.ZERO; for (TransactionOutput output : req.tx.getOutputs()) { value = value.add(output.getValue()); } log.info("Completing send tx with {} outputs totalling {} and a fee of {}/kB", req.tx.getOutputs().size(), value.toFriendlyString(), req.feePerKb.toFriendlyString()); // If any inputs have already been added, we don't need to get their value from wallet Coin totalInput = Coin.ZERO; for (TransactionInput input : req.tx.getInputs()) if (input.getConnectedOutput() != null) totalInput = totalInput.add(input.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."); value = value.subtract(totalInput); List<TransactionInput> originalInputs = new ArrayList<TransactionInput>(req.tx.getInputs()); // Check for dusty sends and the OP_RETURN limit. if (req.ensureMinRequiredFee && !req.emptyWallet) { // Min fee checking is handled later for emptyWallet. int opReturnCount = 0; for (TransactionOutput output : req.tx.getOutputs()) { if (output.isDust()) throw new DustySendRequested(); if (output.getScriptPubKey().isOpReturn()) ++opReturnCount; } if (opReturnCount > 1) // Only 1 OP_RETURN per transaction allowed. throw new MultipleOpReturnRequested(); } // Calculate a list of ALL potential candidates for spending and then ask a coin selector to provide us // with the actual outputs that'll be used to gather the required amount of value. In this way, users // can customize coin selection policies. The call below will ignore immature coinbases and outputs // we don't have the keys for. List<TransactionOutput> candidates = calculateAllSpendCandidates(true, req.missingSigsMode == MissingSigsMode.THROW); CoinSelection bestCoinSelection; TransactionOutput bestChangeOutput = null; if (!req.emptyWallet) { // This can throw InsufficientMoneyException. FeeCalculation feeCalculation = calculateFee(req, value, originalInputs, req.ensureMinRequiredFee, candidates); bestCoinSelection = feeCalculation.bestCoinSelection; bestChangeOutput = feeCalculation.bestChangeOutput; } else { // We're being asked to empty the wallet. What this means is ensuring "tx" has only a single output // of the total value we can currently spend as determined by the selector, and then subtracting the fee. checkState(req.tx.getOutputs().size() == 1, "Empty wallet TX must have a single output only."); CoinSelector selector = req.coinSelector == null ? coinSelector : req.coinSelector; bestCoinSelection = selector.select(params.getMaxMoney(), candidates); candidates = null; // Selector took ownership and might have changed candidates. Don't access again. req.tx.getOutput(0).setValue(bestCoinSelection.valueGathered); log.info(" emptying {}", bestCoinSelection.valueGathered.toFriendlyString()); } for (TransactionOutput output : bestCoinSelection.gathered) req.tx.addInput(output); if (req.emptyWallet) { final Coin feePerKb = req.feePerKb == null ? Coin.ZERO : req.feePerKb; if (!adjustOutputDownwardsForFee(req.tx, bestCoinSelection, feePerKb, req.ensureMinRequiredFee)) throw new CouldNotAdjustDownwards(); } if (bestChangeOutput != null) { req.tx.addOutput(bestChangeOutput); log.info(" with {} change", bestChangeOutput.getValue().toFriendlyString()); } // Now shuffle the outputs to obfuscate which is the change. if (req.shuffleOutputs) req.tx.shuffleOutputs(); // Now sign the inputs, thus proving that we are entitled to redeem the connected outputs. if (req.signInputs) signTransaction(req); // Check size. final int size = req.tx.unsafeBitcoinSerialize().length; if (size > Transaction.MAX_STANDARD_TX_SIZE) throw new ExceededMaxTransactionSize(); // Label the transaction as being self created. We can use this later to spend its change output even before // the transaction is confirmed. We deliberately won't bother notifying listeners here as there's not much // point - the user isn't interested in a confidence transition they made themselves. req.tx.getConfidence().setSource(TransactionConfidence.Source.SELF); // Label the transaction as being a user requested payment. This can be used to render GUI wallet // transaction lists more appropriately, especially when the wallet starts to generate transactions itself // for internal purposes. req.tx.setPurpose(Transaction.Purpose.USER_PAYMENT); // Record the exchange rate that was valid when the transaction was completed. req.tx.setExchangeRate(req.exchangeRate); req.tx.setMemo(req.memo); req.completed = true; log.info(" completed: {}", req.tx); } finally { lock.unlock(); } } /** * <p>Given a send request containing transaction, attempts to sign it's inputs. This method expects transaction * to have all necessary inputs connected or they will be ignored.</p> * <p>Actual signing is done by pluggable {@link #signers} and it's not guaranteed that * transaction will be complete in the end.</p> */ public void signTransaction(SendRequest req) { lock.lock(); try { Transaction tx = req.tx; List<TransactionInput> inputs = tx.getInputs(); List<TransactionOutput> outputs = tx.getOutputs(); checkState(inputs.size() > 0); checkState(outputs.size() > 0); KeyBag maybeDecryptingKeyBag = new DecryptingKeyBag(this, req.aesKey); int numInputs = tx.getInputs().size(); for (int i = 0; i < numInputs; i++) { TransactionInput txIn = tx.getInput(i); if (txIn.getConnectedOutput() == null) { // Missing connected output, assuming already signed. continue; } try { // We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when // we sign missing pieces (to check this would require either assuming any signatures are signing // standard output types or a way to get processed signatures out of script execution) txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey()); log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i); continue; } catch (ScriptException e) { log.debug("Input contained an incorrect signature", e); // Expected. } Script scriptPubKey = txIn.getConnectedOutput().getScriptPubKey(); RedeemData redeemData = txIn.getConnectedRedeemData(maybeDecryptingKeyBag); checkNotNull(redeemData, "Transaction exists in wallet that we cannot redeem: %s", txIn.getOutpoint().getHash()); txIn.setScriptSig(scriptPubKey.createEmptyInputScript(redeemData.keys.get(0), redeemData.redeemScript)); } TransactionSigner.ProposedTransaction proposal = new TransactionSigner.ProposedTransaction(tx); for (TransactionSigner signer : signers) { if (!signer.signInputs(proposal, maybeDecryptingKeyBag)) log.info("{} returned false for the tx", signer.getClass().getName()); } // resolve missing sigs if any new MissingSigResolutionSigner(req.missingSigsMode).signInputs(proposal, maybeDecryptingKeyBag); } finally { lock.unlock(); } } /** Reduce the value of the first output of a transaction to pay the given feePerKb as appropriate for its size. */ private boolean adjustOutputDownwardsForFee(Transaction tx, CoinSelection coinSelection, Coin feePerKb, boolean ensureMinRequiredFee) { final int size = tx.unsafeBitcoinSerialize().length + estimateBytesForSigning(coinSelection); Coin fee = feePerKb.multiply(size).divide(1000); if (ensureMinRequiredFee && fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; TransactionOutput output = tx.getOutput(0); output.setValue(output.getValue().subtract(fee)); return !output.isDust(); } /** * Returns a list of the outputs that can potentially be spent, i.e. that we have the keys for and are unspent * according to our knowledge of the block chain. */ public List<TransactionOutput> calculateAllSpendCandidates() { return calculateAllSpendCandidates(true, true); } /** @deprecated Use {@link #calculateAllSpendCandidates(boolean, boolean)} or the zero-parameter form instead. */ @Deprecated public List<TransactionOutput> calculateAllSpendCandidates(boolean excludeImmatureCoinbases) { return calculateAllSpendCandidates(excludeImmatureCoinbases, true); } /** * Returns a list of all outputs that are being tracked by this wallet either from the {@link UTXOProvider} * (in this case the existence or not of private keys is ignored), or the wallets internal storage (the default) * taking into account the flags. * * @param excludeImmatureCoinbases Whether to ignore coinbase outputs that we will be able to spend in future once they mature. * @param excludeUnsignable Whether to ignore outputs that we are tracking but don't have the keys to sign for. */ public List<TransactionOutput> calculateAllSpendCandidates(boolean excludeImmatureCoinbases, boolean excludeUnsignable) { lock.lock(); try { List<TransactionOutput> candidates; if (vUTXOProvider == null) { candidates = new ArrayList<TransactionOutput>(myUnspents.size()); for (TransactionOutput output : myUnspents) { if (excludeUnsignable && !canSignFor(output.getScriptPubKey())) continue; Transaction transaction = checkNotNull(output.getParentTransaction()); if (excludeImmatureCoinbases && !transaction.isMature()) continue; candidates.add(output); } } else { candidates = calculateAllSpendCandidatesFromUTXOProvider(excludeImmatureCoinbases); } return candidates; } finally { lock.unlock(); } } /** * Returns true if this wallet has at least one of the private keys needed to sign for this scriptPubKey. Returns * false if the form of the script is not known or if the script is OP_RETURN. */ public boolean canSignFor(Script script) { if (script.isSentToRawPubKey()) { byte[] pubkey = script.getPubKey(); ECKey key = findKeyFromPubKey(pubkey); return key != null && (key.isEncrypted() || key.hasPrivKey()); } if (script.isPayToScriptHash()) { RedeemData data = findRedeemDataFromScriptHash(script.getPubKeyHash()); return data != null && canSignFor(data.redeemScript); } else if (script.isSentToAddress()) { ECKey key = findKeyFromPubHash(script.getPubKeyHash()); return key != null && (key.isEncrypted() || key.hasPrivKey()); } else if (script.isSentToMultiSig()) { for (ECKey pubkey : script.getPubKeys()) { ECKey key = findKeyFromPubKey(pubkey.getPubKey()); if (key != null && (key.isEncrypted() || key.hasPrivKey())) return true; } } else if (script.isSentToCLTVPaymentChannel()) { // Any script for which we are the recipient or sender counts. byte[] sender = script.getCLTVPaymentChannelSenderPubKey(); ECKey senderKey = findKeyFromPubKey(sender); if (senderKey != null && (senderKey.isEncrypted() || senderKey.hasPrivKey())) { return true; } byte[] recipient = script.getCLTVPaymentChannelRecipientPubKey(); ECKey recipientKey = findKeyFromPubKey(sender); if (recipientKey != null && (recipientKey.isEncrypted() || recipientKey.hasPrivKey())) { return true; } return false; } return false; } /** * Returns the spendable candidates from the {@link UTXOProvider} based on keys that the wallet contains. * @return The list of candidates. */ protected LinkedList<TransactionOutput> calculateAllSpendCandidatesFromUTXOProvider(boolean excludeImmatureCoinbases) { checkState(lock.isHeldByCurrentThread()); UTXOProvider utxoProvider = checkNotNull(vUTXOProvider, "No UTXO provider has been set"); LinkedList<TransactionOutput> candidates = Lists.newLinkedList(); try { int chainHeight = utxoProvider.getChainHeadHeight(); for (UTXO output : getStoredOutputsFromUTXOProvider()) { boolean coinbase = output.isCoinbase(); int depth = chainHeight - output.getHeight() + 1; // the current depth of the output (1 = same as head). // Do not try and spend coinbases that were mined too recently, the protocol forbids it. if (!excludeImmatureCoinbases || !coinbase || depth >= params.getSpendableCoinbaseDepth()) { candidates.add(new FreeStandingTransactionOutput(params, output, chainHeight)); } } } catch (UTXOProviderException e) { throw new RuntimeException("UTXO provider error", e); } // We need to handle the pending transactions that we know about. for (Transaction tx : pending.values()) { // Remove the spent outputs. for (TransactionInput input : tx.getInputs()) { if (input.getConnectedOutput().isMine(this)) { candidates.remove(input.getConnectedOutput()); } } // Add change outputs. Do not try and spend coinbases that were mined too recently, the protocol forbids it. if (!excludeImmatureCoinbases || tx.isMature()) { for (TransactionOutput output : tx.getOutputs()) { if (output.isAvailableForSpending() && output.isMine(this)) { candidates.add(output); } } } } return candidates; } /** * Get all the {@link UTXO}'s from the {@link UTXOProvider} based on keys that the * wallet contains. * @return The list of stored outputs. */ protected List<UTXO> getStoredOutputsFromUTXOProvider() throws UTXOProviderException { UTXOProvider utxoProvider = checkNotNull(vUTXOProvider, "No UTXO provider has been set"); List<UTXO> candidates = new ArrayList<UTXO>(); List<ECKey> keys = getImportedKeys(); keys.addAll(getActiveKeyChain().getLeafKeys()); List<Address> addresses = new ArrayList<Address>(); for (ECKey key : keys) { Address address = new Address(params, key.getPubKeyHash()); addresses.add(address); } candidates.addAll(utxoProvider.getOpenTransactionOutputs(addresses)); return candidates; } /** Returns the {@link CoinSelector} object which controls which outputs can be spent by this wallet. */ public CoinSelector getCoinSelector() { lock.lock(); try { return coinSelector; } finally { lock.unlock(); } } /** * A coin selector is responsible for choosing which outputs to spend when creating transactions. The default * selector implements a policy of spending transactions that appeared in the best chain and pending transactions * that were created by this wallet, but not others. You can override the coin selector for any given send * operation by changing {@link SendRequest#coinSelector}. */ public void setCoinSelector(CoinSelector coinSelector) { lock.lock(); try { this.coinSelector = checkNotNull(coinSelector); } finally { lock.unlock(); } } /** * Convenience wrapper for <tt>setCoinSelector(Wallet.AllowUnconfirmedCoinSelector.get())</tt>. If this method * is called on the wallet then transactions will be used for spending regardless of their confidence. This can * be dangerous - only use this if you absolutely know what you're doing! */ public void allowSpendingUnconfirmedTransactions() { setCoinSelector(AllowUnconfirmedCoinSelector.get()); } /** * Get the {@link UTXOProvider}. * @return The UTXO provider. */ @Nullable public UTXOProvider getUTXOProvider() { lock.lock(); try { return vUTXOProvider; } finally { lock.unlock(); } } /** * Set the {@link UTXOProvider}. * * <p>The wallet will query the provider for spendable candidates, i.e. outputs controlled exclusively * by private keys contained in the wallet.</p> * * <p>Note that the associated provider must be reattached after a wallet is loaded from disk. * The association is not serialized.</p> */ public void setUTXOProvider(@Nullable UTXOProvider provider) { lock.lock(); try { checkArgument(provider == null || provider.getParams().equals(params)); this.vUTXOProvider = provider; } finally { lock.unlock(); } } //endregion /******************************************************************************************************************/ /** * A custom {@link TransactionOutput} that is free standing. This contains all the information * required for spending without actually having all the linked data (i.e parent tx). * */ private class FreeStandingTransactionOutput extends TransactionOutput { private UTXO output; private int chainHeight; /** * Construct a free standing Transaction Output. * @param params The network parameters. * @param output The stored output (free standing). */ public FreeStandingTransactionOutput(NetworkParameters params, UTXO output, int chainHeight) { super(params, null, output.getValue(), output.getScript().getProgram()); this.output = output; this.chainHeight = chainHeight; } /** * Get the {@link UTXO}. * @return The stored output. */ public UTXO getUTXO() { return output; } /** * Get the depth withing the chain of the parent tx, depth is 1 if it the output height is the height of * the latest block. * @return The depth. */ @Override public int getParentTransactionDepthInBlocks() { return chainHeight - output.getHeight() + 1; } @Override public int getIndex() { return (int) output.getIndex(); } @Override public Sha256Hash getParentTransactionHash() { return output.getHash(); } } /******************************************************************************************************************/ /******************************************************************************************************************/ private static class TxOffsetPair implements Comparable<TxOffsetPair> { public final Transaction tx; public final int offset; public TxOffsetPair(Transaction tx, int offset) { this.tx = tx; this.offset = offset; } @Override public int compareTo(TxOffsetPair o) { // note that in this implementation compareTo() is not consistent with equals() return Ints.compare(offset, o.offset); } } //region Reorganisations /** * <p>Don't call this directly. It's not intended for API users.</p> * * <p>Called by the {@link BlockChain} when the best chain (representing total work done) has changed. This can * cause the number of confirmations of a transaction to go higher, lower, drop to zero and can even result in * a transaction going dead (will never confirm) due to a double spend.</p> * * <p>The oldBlocks/newBlocks lists are ordered height-wise from top first to bottom last.</p> */ @Override public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks) throws VerificationException { lock.lock(); try { // This runs on any peer thread with the block chain locked. // // The reorganize functionality of the wallet is tested in ChainSplitTest.java // // receive() has been called on the block that is triggering the re-org before this is called, with type // of SIDE_CHAIN. // // Note that this code assumes blocks are not invalid - if blocks contain duplicated transactions, // transactions that double spend etc then we can calculate the incorrect result. This could open up // obscure DoS attacks if someone successfully mines a throwaway invalid block and feeds it to us, just // to try and corrupt the internal data structures. We should try harder to avoid this but it's tricky // because there are so many ways the block can be invalid. // Avoid spuriously informing the user of wallet/tx confidence changes whilst we're re-organizing. checkState(confidenceChanged.size() == 0); checkState(!insideReorg); insideReorg = true; checkState(onWalletChangedSuppressions == 0); onWalletChangedSuppressions++; // Map block hash to transactions that appear in it. We ensure that the map values are sorted according // to their relative position within those blocks. ArrayListMultimap<Sha256Hash, TxOffsetPair> mapBlockTx = ArrayListMultimap.create(); for (Transaction tx : getTransactions(true)) { Map<Sha256Hash, Integer> appearsIn = tx.getAppearsInHashes(); if (appearsIn == null) continue; // Pending. for (Map.Entry<Sha256Hash, Integer> block : appearsIn.entrySet()) mapBlockTx.put(block.getKey(), new TxOffsetPair(tx, block.getValue())); } for (Sha256Hash blockHash : mapBlockTx.keySet()) Collections.sort(mapBlockTx.get(blockHash)); List<Sha256Hash> oldBlockHashes = new ArrayList<Sha256Hash>(oldBlocks.size()); log.info("Old part of chain (top to bottom):"); for (StoredBlock b : oldBlocks) { log.info(" {}", b.getHeader().getHashAsString()); oldBlockHashes.add(b.getHeader().getHash()); } log.info("New part of chain (top to bottom):"); for (StoredBlock b : newBlocks) { log.info(" {}", b.getHeader().getHashAsString()); } Collections.reverse(newBlocks); // Need bottom-to-top but we get top-to-bottom. // For each block in the old chain, disconnect the transactions in reverse order. LinkedList<Transaction> oldChainTxns = Lists.newLinkedList(); for (Sha256Hash blockHash : oldBlockHashes) { for (TxOffsetPair pair : mapBlockTx.get(blockHash)) { Transaction tx = pair.tx; final Sha256Hash txHash = tx.getHash(); if (tx.isCoinBase()) { // All the transactions that we have in our wallet which spent this coinbase are now invalid // and will never confirm. Hopefully this should never happen - that's the point of the maturity // rule that forbids spending of coinbase transactions for 100 blocks. // // This could be recursive, although of course because we don't have the full transaction // graph we can never reliably kill all transactions we might have that were rooted in // this coinbase tx. Some can just go pending forever, like the Bitcoin Core. However we // can do our best. log.warn("Coinbase killed by re-org: {}", tx.getHashAsString()); killTxns(ImmutableSet.of(tx), null); } else { for (TransactionOutput output : tx.getOutputs()) { TransactionInput input = output.getSpentBy(); if (input != null) { if (output.isMineOrWatched(this)) checkState(myUnspents.add(output)); input.disconnect(); } } oldChainTxns.add(tx); unspent.remove(txHash); spent.remove(txHash); checkState(!pending.containsKey(txHash)); checkState(!dead.containsKey(txHash)); } } } // Put all the disconnected transactions back into the pending pool and re-connect them. for (Transaction tx : oldChainTxns) { // Coinbase transactions on the old part of the chain are dead for good and won't come back unless // there's another re-org. if (tx.isCoinBase()) continue; log.info(" ->pending {}", tx.getHash()); tx.getConfidence().setConfidenceType(ConfidenceType.PENDING); // Wipe height/depth/work data. confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.TYPE); addWalletTransaction(Pool.PENDING, tx); updateForSpends(tx, false); } // Note that dead transactions stay dead. Consider a chain that Finney attacks T1 and replaces it with // T2, so we move T1 into the dead pool. If there's now a re-org to a chain that doesn't include T2, it // doesn't matter - the miners deleted T1 from their mempool, will resurrect T2 and put that into the // mempool and so T1 is still seen as a losing double spend. // The old blocks have contributed to the depth for all the transactions in the // wallet that are in blocks up to and including the chain split block. // The total depth is calculated here and then subtracted from the appropriate transactions. int depthToSubtract = oldBlocks.size(); log.info("depthToSubtract = " + depthToSubtract); // Remove depthToSubtract from all transactions in the wallet except for pending. subtractDepth(depthToSubtract, spent.values()); subtractDepth(depthToSubtract, unspent.values()); subtractDepth(depthToSubtract, dead.values()); // The effective last seen block is now the split point so set the lastSeenBlockHash. setLastBlockSeenHash(splitPoint.getHeader().getHash()); // For each block in the new chain, work forwards calling receive() and notifyNewBestBlock(). // This will pull them back out of the pending pool, or if the tx didn't appear in the old chain and // does appear in the new chain, will treat it as such and possibly kill pending transactions that // conflict. for (StoredBlock block : newBlocks) { log.info("Replaying block {}", block.getHeader().getHashAsString()); for (TxOffsetPair pair : mapBlockTx.get(block.getHeader().getHash())) { log.info(" tx {}", pair.tx.getHash()); try { receive(pair.tx, block, BlockChain.NewBlockType.BEST_CHAIN, pair.offset); } catch (ScriptException e) { throw new RuntimeException(e); // Cannot happen as these blocks were already verified. } } notifyNewBestBlock(block); } isConsistentOrThrow(); final Coin balance = getBalance(); log.info("post-reorg balance is {}", balance.toFriendlyString()); // Inform event listeners that a re-org took place. queueOnReorganize(); insideReorg = false; onWalletChangedSuppressions--; maybeQueueOnWalletChanged(); checkBalanceFuturesLocked(balance); informConfidenceListenersIfNotReorganizing(); saveLater(); } finally { lock.unlock(); } } /** * Subtract the supplied depth from the given transactions. */ private void subtractDepth(int depthToSubtract, Collection<Transaction> transactions) { for (Transaction tx : transactions) { if (tx.getConfidence().getConfidenceType() == ConfidenceType.BUILDING) { tx.getConfidence().setDepthInBlocks(tx.getConfidence().getDepthInBlocks() - depthToSubtract); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.DEPTH); } } } //endregion /******************************************************************************************************************/ //region Bloom filtering private final ArrayList<TransactionOutPoint> bloomOutPoints = Lists.newArrayList(); // Used to track whether we must automatically begin/end a filter calculation and calc outpoints/take the locks. private final AtomicInteger bloomFilterGuard = new AtomicInteger(0); @Override public void beginBloomFilterCalculation() { if (bloomFilterGuard.incrementAndGet() > 1) return; lock.lock(); keyChainGroupLock.lock(); //noinspection FieldAccessNotGuarded calcBloomOutPointsLocked(); } private void calcBloomOutPointsLocked() { // TODO: This could be done once and then kept up to date. bloomOutPoints.clear(); Set<Transaction> all = new HashSet<Transaction>(); all.addAll(unspent.values()); all.addAll(spent.values()); all.addAll(pending.values()); for (Transaction tx : all) { for (TransactionOutput out : tx.getOutputs()) { try { if (isTxOutputBloomFilterable(out)) bloomOutPoints.add(out.getOutPointFor()); } catch (ScriptException e) { // If it is ours, we parsed the script correctly, so this shouldn't happen. throw new RuntimeException(e); } } } } @Override @GuardedBy("keyChainGroupLock") public void endBloomFilterCalculation() { if (bloomFilterGuard.decrementAndGet() > 0) return; bloomOutPoints.clear(); keyChainGroupLock.unlock(); lock.unlock(); } /** * Returns the number of distinct data items (note: NOT keys) that will be inserted into a bloom filter, when it * is constructed. */ @Override public int getBloomFilterElementCount() { beginBloomFilterCalculation(); try { int size = bloomOutPoints.size(); size += keyChainGroup.getBloomFilterElementCount(); // Some scripts may have more than one bloom element. That should normally be okay, because under-counting // just increases false-positive rate. size += watchedScripts.size(); return size; } finally { endBloomFilterCalculation(); } } /** * If we are watching any scripts, the bloom filter must update on peers whenever an output is * identified. This is because we don't necessarily have the associated pubkey, so we can't * watch for it on spending transactions. */ @Override public boolean isRequiringUpdateAllBloomFilter() { // This is typically called by the PeerGroup, in which case it will have already explicitly taken the lock // before calling, but because this is public API we must still lock again regardless. keyChainGroupLock.lock(); try { return !watchedScripts.isEmpty(); } finally { keyChainGroupLock.unlock(); } } /** * Gets a bloom filter that contains all of the public keys from this wallet, and which will provide the given * false-positive rate. See the docs for {@link BloomFilter} for a brief explanation of anonymity when using filters. */ public BloomFilter getBloomFilter(double falsePositiveRate) { beginBloomFilterCalculation(); try { return getBloomFilter(getBloomFilterElementCount(), falsePositiveRate, (long) (Math.random() * Long.MAX_VALUE)); } finally { endBloomFilterCalculation(); } } /** * <p>Gets a bloom filter that contains all of the public keys from this wallet, and which will provide the given * false-positive rate if it has size elements. Keep in mind that you will get 2 elements in the bloom filter for * each key in the wallet, for the public key and the hash of the public key (address form).</p> * * <p>This is used to generate a BloomFilter which can be {@link BloomFilter#merge(BloomFilter)}d with another. * It could also be used if you have a specific target for the filter's size.</p> * * <p>See the docs for {@link BloomFilter(int, double)} for a brief explanation of anonymity when using bloom * filters.</p> */ @Override @GuardedBy("keyChainGroupLock") public BloomFilter getBloomFilter(int size, double falsePositiveRate, long nTweak) { beginBloomFilterCalculation(); try { BloomFilter filter = keyChainGroup.getBloomFilter(size, falsePositiveRate, nTweak); for (Script script : watchedScripts) { for (ScriptChunk chunk : script.getChunks()) { // Only add long (at least 64 bit) data to the bloom filter. // If any long constants become popular in scripts, we will need logic // here to exclude them. if (!chunk.isOpCode() && chunk.data.length >= MINIMUM_BLOOM_DATA_LENGTH) { filter.insert(chunk.data); } } } for (TransactionOutPoint point : bloomOutPoints) filter.insert(point.unsafeBitcoinSerialize()); return filter; } finally { endBloomFilterCalculation(); } } // Returns true if the output is one that won't be selected by a data element matching in the scriptSig. private boolean isTxOutputBloomFilterable(TransactionOutput out) { Script script = out.getScriptPubKey(); boolean isScriptTypeSupported = script.isSentToRawPubKey() || script.isPayToScriptHash(); return (isScriptTypeSupported && myUnspents.contains(out)) || watchedScripts.contains(script); } /** * Used by {@link Peer} to decide whether or not to discard this block and any blocks building upon it, in case * the Bloom filter used to request them may be exhausted, that is, not have sufficient keys in the deterministic * sequence within it to reliably find relevant transactions. */ public boolean checkForFilterExhaustion(FilteredBlock block) { keyChainGroupLock.lock(); try { int epoch = keyChainGroup.getCombinedKeyLookaheadEpochs(); for (Transaction tx : block.getAssociatedTransactions().values()) { markKeysAsUsed(tx); } int newEpoch = keyChainGroup.getCombinedKeyLookaheadEpochs(); checkState(newEpoch >= epoch); // If the key lookahead epoch has advanced, there was a call to addKeys and the PeerGroup already has a // pending request to recalculate the filter queued up on another thread. The calling Peer should abandon // block at this point and await a new filter before restarting the download. return newEpoch > epoch; } finally { keyChainGroupLock.unlock(); } } //endregion /******************************************************************************************************************/ //region Extensions to the wallet format. /** * By providing an object implementing the {@link WalletExtension} interface, you can save and load arbitrary * additional data that will be stored with the wallet. Each extension is identified by an ID, so attempting to * add the same extension twice (or two different objects that use the same ID) will throw an IllegalStateException. */ public void addExtension(WalletExtension extension) { String id = checkNotNull(extension).getWalletExtensionID(); lock.lock(); try { if (extensions.containsKey(id)) throw new IllegalStateException("Cannot add two extensions with the same ID: " + id); extensions.put(id, extension); saveNow(); } finally { lock.unlock(); } } /** * Atomically adds extension or returns an existing extension if there is one with the same id already present. */ public WalletExtension addOrGetExistingExtension(WalletExtension extension) { String id = checkNotNull(extension).getWalletExtensionID(); lock.lock(); try { WalletExtension previousExtension = extensions.get(id); if (previousExtension != null) return previousExtension; extensions.put(id, extension); saveNow(); return extension; } finally { lock.unlock(); } } /** * Either adds extension as a new extension or replaces the existing extension if one already exists with the same * id. This also triggers wallet auto-saving, so may be useful even when called with the same extension as is * already present. */ public void addOrUpdateExtension(WalletExtension extension) { String id = checkNotNull(extension).getWalletExtensionID(); lock.lock(); try { extensions.put(id, extension); saveNow(); } finally { lock.unlock(); } } /** Returns a snapshot of all registered extension objects. The extensions themselves are not copied. */ public Map<String, WalletExtension> getExtensions() { lock.lock(); try { return ImmutableMap.copyOf(extensions); } finally { lock.unlock(); } } /** * Deserialize the wallet extension with the supplied data and then install it, replacing any existing extension * that may have existed with the same ID. If an exception is thrown then the extension is removed from the wallet, * if already present. */ public void deserializeExtension(WalletExtension extension, byte[] data) throws Exception { lock.lock(); keyChainGroupLock.lock(); try { // This method exists partly to establish a lock ordering of wallet > extension. extension.deserializeWalletExtension(this, data); extensions.put(extension.getWalletExtensionID(), extension); } catch (Throwable throwable) { log.error("Error during extension deserialization", throwable); extensions.remove(extension.getWalletExtensionID()); Throwables.propagate(throwable); } finally { keyChainGroupLock.unlock(); lock.unlock(); } } @Override public void setTag(String tag, ByteString value) { super.setTag(tag, value); saveNow(); } //endregion /******************************************************************************************************************/ private static class FeeCalculation { public CoinSelection bestCoinSelection; public TransactionOutput bestChangeOutput; } //region Fee calculation code public FeeCalculation calculateFee(SendRequest req, Coin value, List<TransactionInput> originalInputs, boolean needAtLeastReferenceFee, List<TransactionOutput> candidates) throws InsufficientMoneyException { checkState(lock.isHeldByCurrentThread()); // There are 3 possibilities for what adding change might do: // 1) No effect // 2) Causes increase in fee (change < 0.01 COINS) // 3) Causes the transaction to have a dust output or change < fee increase (ie change will be thrown away) // If we get either of the last 2, we keep note of what the inputs looked like at the time and try to // add inputs as we go up the list (keeping track of minimum inputs for each category). At the end, we pick // the best input set as the one which generates the lowest total fee. Coin additionalValueForNextCategory = null; CoinSelection selection3 = null; CoinSelection selection2 = null; TransactionOutput selection2Change = null; CoinSelection selection1 = null; TransactionOutput selection1Change = null; // We keep track of the last size of the transaction we calculated but only if the act of adding inputs and // change resulted in the size crossing a 1000 byte boundary. Otherwise it stays at zero. int lastCalculatedSize = 0; Coin valueNeeded, valueMissing = null; while (true) { resetTxInputs(req, originalInputs); Coin fees = req.feePerKb.multiply(lastCalculatedSize).divide(1000); if (needAtLeastReferenceFee && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) fees = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; valueNeeded = value.add(fees); if (additionalValueForNextCategory != null) valueNeeded = valueNeeded.add(additionalValueForNextCategory); Coin additionalValueSelected = additionalValueForNextCategory; // Of the coins we could spend, pick some that we actually will spend. CoinSelector selector = req.coinSelector == null ? coinSelector : req.coinSelector; // selector is allowed to modify candidates list. CoinSelection selection = selector.select(valueNeeded, new LinkedList<TransactionOutput>(candidates)); // Can we afford this? if (selection.valueGathered.compareTo(valueNeeded) < 0) { valueMissing = valueNeeded.subtract(selection.valueGathered); break; } checkState(selection.gathered.size() > 0 || originalInputs.size() > 0); // We keep track of an upper bound on transaction size to calculate fees that need to be added. // Note that the difference between the upper bound and lower bound is usually small enough that it // will be very rare that we pay a fee we do not need to. // // We can't be sure a selection is valid until we check fee per kb at the end, so we just store // them here temporarily. boolean eitherCategory2Or3 = false; boolean isCategory3 = false; Coin change = selection.valueGathered.subtract(valueNeeded); if (additionalValueSelected != null) change = change.add(additionalValueSelected); // If change is < 0.01 BTC, we will need to have at least minfee to be accepted by the network if (req.ensureMinRequiredFee && !change.equals(Coin.ZERO) && change.compareTo(Coin.CENT) < 0 && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) { // This solution may fit into category 2, but it may also be category 3, we'll check that later eitherCategory2Or3 = true; additionalValueForNextCategory = Coin.CENT; // If the change is smaller than the fee we want to add, this will be negative change = change.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(fees)); } int size = 0; TransactionOutput changeOutput = null; if (change.signum() > 0) { // The value of the inputs is greater than what we want to send. Just like in real life then, // we need to take back some coins ... this is called "change". Add another output that sends the change // back to us. The address comes either from the request or currentChangeAddress() as a default. Address changeAddress = req.changeAddress; if (changeAddress == null) changeAddress = currentChangeAddress(); changeOutput = new TransactionOutput(params, req.tx, change, changeAddress); // If the change output would result in this transaction being rejected as dust, just drop the change and make it a fee if (req.ensureMinRequiredFee && changeOutput.isDust()) { // This solution definitely fits in category 3 isCategory3 = true; additionalValueForNextCategory = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add( changeOutput.getMinNonDustValue().add(Coin.SATOSHI)); } else { size += changeOutput.unsafeBitcoinSerialize().length + VarInt.sizeOf(req.tx.getOutputs().size()) - VarInt.sizeOf(req.tx.getOutputs().size() - 1); // This solution is either category 1 or 2 if (!eitherCategory2Or3) // must be category 1 additionalValueForNextCategory = null; } } else { if (eitherCategory2Or3) { // This solution definitely fits in category 3 (we threw away change because it was smaller than MIN_TX_FEE) isCategory3 = true; additionalValueForNextCategory = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(Coin.SATOSHI); } } // Now add unsigned inputs for the selected coins. for (TransactionOutput output : selection.gathered) { TransactionInput input = req.tx.addInput(output); // If the scriptBytes don't default to none, our size calculations will be thrown off. checkState(input.getScriptBytes().length == 0); } // Estimate transaction size and loop again if we need more fee per kb. The serialized tx doesn't // include things we haven't added yet like input signatures/scripts or the change output. size += req.tx.unsafeBitcoinSerialize().length; size += estimateBytesForSigning(selection); if (size > lastCalculatedSize && req.feePerKb.signum() > 0) { lastCalculatedSize = size; // We need more fees anyway, just try again with the same additional value additionalValueForNextCategory = additionalValueSelected; continue; } if (isCategory3) { if (selection3 == null) selection3 = selection; } else if (eitherCategory2Or3) { // If we are in selection2, we will require at least CENT additional. If we do that, there is no way // we can end up back here because CENT additional will always get us to 1 checkState(selection2 == null); checkState(additionalValueForNextCategory.equals(Coin.CENT)); selection2 = selection; selection2Change = checkNotNull(changeOutput); // If we get no change in category 2, we are actually in category 3 } else { // Once we get a category 1 (change kept), we should break out of the loop because we can't do better checkState(selection1 == null); checkState(additionalValueForNextCategory == null); selection1 = selection; selection1Change = changeOutput; } if (additionalValueForNextCategory != null) { if (additionalValueSelected != null) checkState(additionalValueForNextCategory.compareTo(additionalValueSelected) > 0); continue; } break; } resetTxInputs(req, originalInputs); if (selection3 == null && selection2 == null && selection1 == null) { checkNotNull(valueMissing); log.warn("Insufficient value in wallet for send: needed {} more", valueMissing.toFriendlyString()); throw new InsufficientMoneyException(valueMissing); } Coin lowestFee = null; FeeCalculation result = new FeeCalculation(); if (selection1 != null) { if (selection1Change != null) lowestFee = selection1.valueGathered.subtract(selection1Change.getValue()); else lowestFee = selection1.valueGathered; result.bestCoinSelection = selection1; result.bestChangeOutput = selection1Change; } if (selection2 != null) { Coin fee = selection2.valueGathered.subtract(checkNotNull(selection2Change).getValue()); if (lowestFee == null || fee.compareTo(lowestFee) < 0) { lowestFee = fee; result.bestCoinSelection = selection2; result.bestChangeOutput = selection2Change; } } if (selection3 != null) { if (lowestFee == null || selection3.valueGathered.compareTo(lowestFee) < 0) { result.bestCoinSelection = selection3; result.bestChangeOutput = null; } } return result; } private void resetTxInputs(SendRequest req, List<TransactionInput> originalInputs) { req.tx.clearInputs(); for (TransactionInput input : originalInputs) req.tx.addInput(input); } private int estimateBytesForSigning(CoinSelection selection) { int size = 0; for (TransactionOutput output : selection.gathered) { try { Script script = output.getScriptPubKey(); ECKey key = null; Script redeemScript = null; if (script.isSentToAddress()) { key = findKeyFromPubHash(script.getPubKeyHash()); checkNotNull(key, "Coin selection includes unspendable outputs"); } else if (script.isPayToScriptHash()) { redeemScript = findRedeemDataFromScriptHash(script.getPubKeyHash()).redeemScript; checkNotNull(redeemScript, "Coin selection includes unspendable outputs"); } size += script.getNumberOfBytesRequiredToSpend(key, redeemScript); } catch (ScriptException e) { // If this happens it means an output script in a wallet tx could not be understood. That should never // happen, if it does it means the wallet has got into an inconsistent state. throw new IllegalStateException(e); } } return size; } //endregion /******************************************************************************************************************/ //region Wallet maintenance transactions // Wallet maintenance transactions. These transactions may not be directly connected to a payment the user is // making. They may be instead key rotation transactions for when old keys are suspected to be compromised, // de/re-fragmentation transactions for when our output sizes are inappropriate or suboptimal, privacy transactions // and so on. Because these transactions may require user intervention in some way (e.g. entering their password) // the wallet application is expected to poll the Wallet class to get SendRequests. Ideally security systems like // hardware wallets or risk analysis providers are programmed to auto-approve transactions that send from our own // keys back to our own keys. /** * <p>Specifies that the given {@link TransactionBroadcaster}, typically a {@link PeerGroup}, should be used for * sending transactions to the Bitcoin network by default. Some sendCoins methods let you specify a broadcaster * explicitly, in that case, they don't use this broadcaster. If null is specified then the wallet won't attempt * to broadcast transactions itself.</p> * * <p>You don't normally need to call this. A {@link PeerGroup} will automatically set itself as the wallets * broadcaster when you use {@link PeerGroup#addWallet(Wallet)}. A wallet can use the broadcaster when you ask * it to send money, but in future also at other times to implement various features that may require asynchronous * re-organisation of the wallet contents on the block chain. For instance, in future the wallet may choose to * optimise itself to reduce fees or improve privacy.</p> */ public void setTransactionBroadcaster(@Nullable org.bitcoinj.core.TransactionBroadcaster broadcaster) { Transaction[] toBroadcast = {}; lock.lock(); try { if (vTransactionBroadcaster == broadcaster) return; vTransactionBroadcaster = broadcaster; if (broadcaster == null) return; toBroadcast = pending.values().toArray(toBroadcast); } finally { lock.unlock(); } // Now use it to upload any pending transactions we have that are marked as not being seen by any peers yet. // Don't hold the wallet lock whilst doing this, so if the broadcaster accesses the wallet at some point there // is no inversion. for (Transaction tx : toBroadcast) { ConfidenceType confidenceType = tx.getConfidence().getConfidenceType(); checkState(confidenceType == ConfidenceType.PENDING || confidenceType == ConfidenceType.IN_CONFLICT, "Expected PENDING or IN_CONFLICT, was %s.", confidenceType); // Re-broadcast even if it's marked as already seen for two reasons // 1) Old wallets may have transactions marked as broadcast by 1 peer when in reality the network // never saw it, due to bugs. // 2) It can't really hurt. log.info("New broadcaster so uploading waiting tx {}", tx.getHash()); broadcaster.broadcastTransaction(tx); } } /** * When a key rotation time is set, and money controlled by keys created before the given timestamp T will be * automatically respent to any key that was created after T. This can be used to recover from a situation where * a set of keys is believed to be compromised. Once the time is set transactions will be created and broadcast * immediately. New coins that come in after calling this method will be automatically respent immediately. The * rotation time is persisted to the wallet. You can stop key rotation by calling this method again with zero * as the argument. */ public void setKeyRotationTime(Date time) { setKeyRotationTime(time.getTime() / 1000); } /** * Returns the key rotation time, or null if unconfigured. See {@link #setKeyRotationTime(Date)} for a description * of the field. */ public @Nullable Date getKeyRotationTime() { final long keyRotationTimestamp = vKeyRotationTimestamp; if (keyRotationTimestamp != 0) return new Date(keyRotationTimestamp * 1000); else return null; } /** * <p>When a key rotation time is set, any money controlled by keys created before the given timestamp T will be * automatically respent to any key that was created after T. This can be used to recover from a situation where * a set of keys is believed to be compromised. You can stop key rotation by calling this method again with zero * as the argument. Once set up, calling {@link #doMaintenance(org.spongycastle.crypto.params.KeyParameter, boolean)} * will create and possibly send rotation transactions: but it won't be done automatically (because you might have * to ask for the users password).</p> * * <p>The given time cannot be in the future.</p> */ public void setKeyRotationTime(long unixTimeSeconds) { checkArgument(unixTimeSeconds <= Utils.currentTimeSeconds(), "Given time (%s) cannot be in the future.", Utils.dateTimeFormat(unixTimeSeconds * 1000)); vKeyRotationTimestamp = unixTimeSeconds; saveNow(); } /** Returns whether the keys creation time is before the key rotation time, if one was set. */ public boolean isKeyRotating(ECKey key) { long time = vKeyRotationTimestamp; return time != 0 && key.getCreationTimeSeconds() < time; } /** @deprecated Renamed to doMaintenance */ @Deprecated public ListenableFuture<List<Transaction>> maybeDoMaintenance(@Nullable KeyParameter aesKey, boolean andSend) throws DeterministicUpgradeRequiresPassword { return doMaintenance(aesKey, andSend); } /** * A wallet app should call this from time to time in order to let the wallet craft and send transactions needed * to re-organise coins internally. A good time to call this would be after receiving coins for an unencrypted * wallet, or after sending money for an encrypted wallet. If you have an encrypted wallet and just want to know * if some maintenance needs doing, call this method with andSend set to false and look at the returned list of * transactions. Maintenance might also include internal changes that involve some processing or work but * which don't require making transactions - these will happen automatically unless the password is required * in which case an exception will be thrown. * * @param aesKey the users password, if any. * @param signAndSend if true, send the transactions via the tx broadcaster and return them, if false just return them. * @return A list of transactions that the wallet just made/will make for internal maintenance. Might be empty. * @throws org.bitcoinj.wallet.DeterministicUpgradeRequiresPassword if key rotation requires the users password. */ public ListenableFuture<List<Transaction>> doMaintenance(@Nullable KeyParameter aesKey, boolean signAndSend) throws DeterministicUpgradeRequiresPassword { List<Transaction> txns; lock.lock(); keyChainGroupLock.lock(); try { txns = maybeRotateKeys(aesKey, signAndSend); if (!signAndSend) return Futures.immediateFuture(txns); } finally { keyChainGroupLock.unlock(); lock.unlock(); } checkState(!lock.isHeldByCurrentThread()); ArrayList<ListenableFuture<Transaction>> futures = new ArrayList<ListenableFuture<Transaction>>(txns.size()); TransactionBroadcaster broadcaster = vTransactionBroadcaster; for (Transaction tx : txns) { try { final ListenableFuture<Transaction> future = broadcaster.broadcastTransaction(tx).future(); futures.add(future); Futures.addCallback(future, new FutureCallback<Transaction>() { @Override public void onSuccess(Transaction transaction) { log.info("Successfully broadcast key rotation tx: {}", transaction); } @Override public void onFailure(Throwable throwable) { log.error("Failed to broadcast key rotation tx", throwable); } }); } catch (Exception e) { log.error("Failed to broadcast rekey tx", e); } } return Futures.allAsList(futures); } // Checks to see if any coins are controlled by rotating keys and if so, spends them. @GuardedBy("keyChainGroupLock") private List<Transaction> maybeRotateKeys(@Nullable KeyParameter aesKey, boolean sign) throws DeterministicUpgradeRequiresPassword { checkState(lock.isHeldByCurrentThread()); checkState(keyChainGroupLock.isHeldByCurrentThread()); List<Transaction> results = Lists.newLinkedList(); // TODO: Handle chain replays here. final long keyRotationTimestamp = vKeyRotationTimestamp; if (keyRotationTimestamp == 0) return results; // Nothing to do. // We might have to create a new HD hierarchy if the previous ones are now rotating. boolean allChainsRotating = true; for (DeterministicKeyChain chain : keyChainGroup.getDeterministicKeyChains()) { if (chain.getEarliestKeyCreationTime() >= keyRotationTimestamp) { allChainsRotating = false; break; } } if (allChainsRotating) { try { if (keyChainGroup.getImportedKeys().isEmpty()) { log.info("All HD chains are currently rotating and we have no random keys, creating fresh HD chain ..."); 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 ..."); keyChainGroup.upgradeToDeterministic(keyRotationTimestamp, aesKey); log.info(" ... upgraded to HD again, based on next best oldest key."); } } catch (AllRandomKeysRotating rotating) { log.info(" ... no non-rotating random keys available, generating entirely new HD tree: backup required after this."); keyChainGroup.createAndActivateNewHDChain(); } saveNow(); } // Because transactions are size limited, we might not be able to re-key the entire wallet in one go. So // loop around here until we no longer produce transactions with the max number of inputs. That means we're // fully done, at least for now (we may still get more transactions later and this method will be reinvoked). Transaction tx; do { tx = rekeyOneBatch(keyRotationTimestamp, aesKey, results, sign); if (tx != null) results.add(tx); } while (tx != null && tx.getInputs().size() == KeyTimeCoinSelector.MAX_SIMULTANEOUS_INPUTS); return results; } @Nullable private Transaction rekeyOneBatch(long timeSecs, @Nullable KeyParameter aesKey, List<Transaction> others, boolean sign) { lock.lock(); try { // Build the transaction using some custom logic for our special needs. Last parameter to // KeyTimeCoinSelector is whether to ignore pending transactions or not. // // We ignore pending outputs because trying to rotate these is basically racing an attacker, and // we're quite likely to lose and create stuck double spends. Also, some users who have 0.9 wallets // have already got stuck double spends in their wallet due to the Bloom-filtering block reordering // bug that was fixed in 0.10, thus, making a re-key transaction depend on those would cause it to // never confirm at all. CoinSelector keyTimeSelector = new KeyTimeCoinSelector(this, timeSecs, true); FilteringCoinSelector selector = new FilteringCoinSelector(keyTimeSelector); for (Transaction other : others) selector.excludeOutputsSpentBy(other); // TODO: Make this use the standard SendRequest. CoinSelection toMove = selector.select(Coin.ZERO, calculateAllSpendCandidates()); if (toMove.valueGathered.equals(Coin.ZERO)) return null; // Nothing to do. maybeUpgradeToHD(aesKey); Transaction rekeyTx = new Transaction(params); for (TransactionOutput output : toMove.gathered) { rekeyTx.addInput(output); } // When not signing, don't waste addresses. rekeyTx.addOutput(toMove.valueGathered, sign ? freshReceiveAddress() : currentReceiveAddress()); if (!adjustOutputDownwardsForFee(rekeyTx, toMove, Transaction.DEFAULT_TX_FEE, true)) { log.error("Failed to adjust rekey tx for fees."); return null; } rekeyTx.getConfidence().setSource(TransactionConfidence.Source.SELF); rekeyTx.setPurpose(Transaction.Purpose.KEY_ROTATION); SendRequest req = SendRequest.forTx(rekeyTx); req.aesKey = aesKey; if (sign) signTransaction(req); // KeyTimeCoinSelector should never select enough inputs to push us oversize. checkState(rekeyTx.unsafeBitcoinSerialize().length < Transaction.MAX_STANDARD_TX_SIZE); return rekeyTx; } catch (VerificationException e) { throw new RuntimeException(e); // Cannot happen. } finally { lock.unlock(); } } //endregion }