com.google.bitcoin.core.Wallet Maven / Gradle / Ivy
/**
* 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 com.google.bitcoin.core;
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
import com.google.bitcoin.crypto.KeyCrypter;
import com.google.bitcoin.crypto.KeyCrypterException;
import com.google.bitcoin.crypto.KeyCrypterScrypt;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder;
import com.google.bitcoin.script.ScriptChunk;
import com.google.bitcoin.store.UnreadableWalletException;
import com.google.bitcoin.store.WalletProtobufSerializer;
import com.google.bitcoin.utils.ListenerRegistration;
import com.google.bitcoin.utils.Threading;
import com.google.bitcoin.wallet.*;
import com.google.bitcoin.wallet.WalletTransaction.Pool;
import com.google.common.collect.*;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import org.bitcoinj.wallet.Protos.Wallet.EncryptionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.util.encoders.Hex;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import static com.google.bitcoin.core.Utils.bitcoinValueToFriendlyString;
import static com.google.bitcoin.core.Utils.bitcoinValueToPlainString;
import static com.google.common.base.Preconditions.*;
// To do list:
//
// This whole class has evolved over a period of years and needs a ground-up rewrite.
//
// - 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.
// - 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.
// - Decompose the class where possible: break logic out into classes that can be customized/replaced by the user.
// - [Auto]saving to a backing store
// - Key management
// - just generally make Wallet smaller and easier to work with
// - 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
/**
* 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.
*
* To learn more about this class, read
* working with the wallet.
*
* To fill up a Wallet with transactions, you need to use it in combination with a {@link BlockChain} and various
* other objects, see the Getting started tutorial
* on the website to learn more about how to set everything up.
*
* Wallets can be serialized using either Java serialization - this is not compatible across versions of bitcoinj,
* or protocol buffer serialization. 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, com.google.bitcoin.wallet.WalletFiles.Listener)}
* for more information about this.
*/
public class Wallet implements Serializable, BlockChainListener, PeerFilterProvider {
private static final Logger log = LoggerFactory.getLogger(Wallet.class);
private static final long serialVersionUID = 2L;
private static final int MINIMUM_BLOOM_DATA_LENGTH = 8;
protected final ReentrantLock lock = Threading.lock("wallet");
// 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, currently we just ignore the second
// 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 the Satoshi
// client 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.
final Map pending;
final Map unspent;
final Map spent;
final Map dead;
// All transactions together.
final Map transactions;
// 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 riskDropped = new LinkedHashMap() {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > 1000;
}
};
// A list of public/private EC keys owned by this user. Access it using addKey[s], hasKey[s] and findPubKeyFromHash.
private ArrayList keychain;
// A list of scripts watched by this wallet.
private Set