All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bitcoinj.wallet.Wallet Maven / Gradle / Ivy

There is a newer version: 0.17-beta1
Show newest version
/*
 * 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.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.math.IntMath;
import com.google.protobuf.ByteString;
import net.jcip.annotations.GuardedBy;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Network;
import org.bitcoinj.base.exceptions.AddressFormatException;
import org.bitcoinj.base.internal.PlatformUtils;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.base.internal.StreamUtils;
import org.bitcoinj.crypto.AesKey;
import org.bitcoinj.core.AbstractBlockChain;
import org.bitcoinj.base.Address;
import org.bitcoinj.base.Base58;
import org.bitcoinj.base.AddressParser;
import org.bitcoinj.core.BlockChain;
import org.bitcoinj.core.BloomFilter;
import org.bitcoinj.base.Coin;
import org.bitcoinj.core.Context;
import org.bitcoinj.base.DefaultAddressParser;
import org.bitcoinj.crypto.ECKey;
import org.bitcoinj.core.FilteredBlock;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.base.LegacyAddress;
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.base.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.TransactionConfidence.ConfidenceType;
import org.bitcoinj.core.TransactionConfidence.Listener;
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.VerificationException;
import org.bitcoinj.core.listeners.NewBestBlockListener;
import org.bitcoinj.core.listeners.ReorganizeListener;
import org.bitcoinj.core.listeners.TransactionConfidenceEventListener;
import org.bitcoinj.core.listeners.TransactionReceivedInBlockListener;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicHierarchy;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.KeyCrypterException;
import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.script.Script;
import org.bitcoinj.base.ScriptType;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptChunk;
import org.bitcoinj.script.ScriptException;
import org.bitcoinj.script.ScriptPattern;
import org.bitcoinj.signers.LocalTransactionSigner;
import org.bitcoinj.signers.MissingSigResolutionSigner;
import org.bitcoinj.signers.TransactionSigner;
import org.bitcoinj.utils.BaseTaggableObject;
import org.bitcoinj.base.internal.FutureUtils;
import org.bitcoinj.utils.ListenableCompletableFuture;
import org.bitcoinj.utils.ListenerRegistration;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.Protos.Wallet.EncryptionType;
import org.bitcoinj.wallet.WalletTransaction.Pool;
import org.bitcoinj.wallet.listeners.CurrentKeyChangeEventListener;
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.WalletReorganizeEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

import static org.bitcoinj.base.internal.Preconditions.checkArgument;
import static org.bitcoinj.base.internal.Preconditions.checkState;

// 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!

/**
 * 

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 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(File, long, TimeUnit, WalletFiles.Listener)} * for more information about this.

*/ public class Wallet extends BaseTaggableObject implements NewBestBlockListener, TransactionReceivedInBlockListener, PeerFilterProvider, KeyBag, TransactionBag, ReorganizeListener, AddressParser.Strict { private static final Logger log = LoggerFactory.getLogger(Wallet.class); private static final AddressParser addressParser = new DefaultAddressParser(); // 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.class); protected final ReentrantLock keyChainGroupLock = Threading.lock("Wallet-KeyChainGroup lock"); private static final int MINIMUM_BLOOM_DATA_LENGTH = 8; // 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 pending; private final Map unspent; private final Map spent; private final Map dead; // All transactions together. protected final Map 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 myUnspents = new HashSet<>(); // 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; } }; // 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 final KeyChainGroup keyChainGroup; // A list of scripts watched by this wallet. @GuardedBy("keyChainGroupLock") private final Set