From d0cd770d62d5d415b5ba2108bea8e757e2c4872b Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Tue, 24 Dec 2013 00:40:19 +0000 Subject: [PATCH] Cleanup. Add lots more nullity annotations. Clear lots of nullity static analysis warnings. Delete some dead code. Simplify a few expressions. Resolves issue 317. --- .../com/google/bitcoin/core/BlockChain.java | 7 +- .../com/google/bitcoin/core/ChildMessage.java | 8 +- .../java/com/google/bitcoin/core/ECKey.java | 21 ++-- .../bitcoin/core/FullPrunedBlockChain.java | 2 + .../java/com/google/bitcoin/core/Message.java | 4 +- .../bitcoin/core/NetworkParameters.java | 2 + .../bitcoin/core/PeerEventListener.java | 2 + .../com/google/bitcoin/core/PeerGroup.java | 8 -- .../com/google/bitcoin/core/Transaction.java | 44 +++------ .../bitcoin/core/TransactionConfidence.java | 14 +-- .../google/bitcoin/core/TransactionInput.java | 12 +-- .../bitcoin/core/TransactionOutPoint.java | 15 ++- .../bitcoin/core/TransactionOutput.java | 11 +-- .../google/bitcoin/core/VersionMessage.java | 3 +- .../java/com/google/bitcoin/core/Wallet.java | 7 +- .../bitcoin/crypto/TransactionSignature.java | 9 +- .../google/bitcoin/net/BlockingClient.java | 5 +- .../google/bitcoin/net/ConnectionHandler.java | 9 +- .../bitcoin/net/discovery/SeedPeers.java | 3 + .../PaymentChannelServerListener.java | 17 ++-- .../channels/PaymentChannelServerState.java | 2 +- .../ServerConnectionEventHandler.java | 4 +- .../StoredPaymentChannelClientStates.java | 1 + .../channels/StoredServerChannel.java | 5 +- .../bitcoin/store/H2FullPrunedBlockStore.java | 24 +++-- .../store/MemoryFullPrunedBlockStore.java | 99 ++----------------- .../google/bitcoin/store/SPVBlockStore.java | 8 +- .../store/WalletProtobufSerializer.java | 5 +- .../bitcoin/wallet/KeyTimeCoinSelector.java | 3 + 29 files changed, 130 insertions(+), 224 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/core/BlockChain.java b/core/src/main/java/com/google/bitcoin/core/BlockChain.java index 52a840fb..c7175905 100644 --- a/core/src/main/java/com/google/bitcoin/core/BlockChain.java +++ b/core/src/main/java/com/google/bitcoin/core/BlockChain.java @@ -88,18 +88,19 @@ public class BlockChain extends AbstractBlockChain { @Override protected TransactionOutputChanges connectTransactions(int height, Block block) { // Don't have to do anything as this is only called if(shouldVerifyTransactions()) - return null; + throw new UnsupportedOperationException(); } @Override protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) { // Don't have to do anything as this is only called if(shouldVerifyTransactions()) - return null; + throw new UnsupportedOperationException(); } @Override protected void disconnectTransactions(StoredBlock block) { - // Don't have to do anything as this is only called if(shouldVerifyTransactions()) + // Don't have to do anything as this is only called if(shouldVerifyTransactions()) + throw new UnsupportedOperationException(); } @Override diff --git a/core/src/main/java/com/google/bitcoin/core/ChildMessage.java b/core/src/main/java/com/google/bitcoin/core/ChildMessage.java index 22749e47..b2b4f161 100644 --- a/core/src/main/java/com/google/bitcoin/core/ChildMessage.java +++ b/core/src/main/java/com/google/bitcoin/core/ChildMessage.java @@ -15,6 +15,8 @@ */ package com.google.bitcoin.core; +import javax.annotation.Nullable; + /** * Represents a Message type that can be contained within another Message. ChildMessages that have a cached * backing byte array need to invalidate their parent's caches as well as their own if they are modified. @@ -24,7 +26,7 @@ package com.google.bitcoin.core; public abstract class ChildMessage extends Message { private static final long serialVersionUID = -7657113383624517931L; - private Message parent; + @Nullable private Message parent; protected ChildMessage() { } @@ -47,13 +49,13 @@ public abstract class ChildMessage extends Message { super(params, msg, offset); } - public ChildMessage(NetworkParameters params, byte[] msg, int offset, Message parent, boolean parseLazy, boolean parseRetain, int length) + public ChildMessage(NetworkParameters params, byte[] msg, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException { super(params, msg, offset, parseLazy, parseRetain, length); this.parent = parent; } - public void setParent(Message parent) { + public void setParent(@Nullable Message parent) { if (this.parent != null && this.parent != parent && parent != null) { // After old parent is unlinked it won't be able to receive notice if this ChildMessage // changes internally. To be safe we invalidate the parent cache to ensure it rebuilds diff --git a/core/src/main/java/com/google/bitcoin/core/ECKey.java b/core/src/main/java/com/google/bitcoin/core/ECKey.java index 03ace6b3..3e4ec749 100644 --- a/core/src/main/java/com/google/bitcoin/core/ECKey.java +++ b/core/src/main/java/com/google/bitcoin/core/ECKey.java @@ -161,7 +161,7 @@ public class ECKey implements Serializable { * is more convenient if you are importing a key from elsewhere. The public key will be automatically derived * from the private key. */ - public ECKey(byte[] privKeyBytes, byte[] pubKey) { + public ECKey(@Nullable byte[] privKeyBytes, @Nullable byte[] pubKey) { this(privKeyBytes == null ? null : new BigInteger(1, privKeyBytes), pubKey); } @@ -172,7 +172,7 @@ public class ECKey implements Serializable { * @param pubKey The keys public key * @param keyCrypter The KeyCrypter that will be used, with an AES key, to encrypt and decrypt the private key */ - public ECKey(EncryptedPrivateKey encryptedPrivateKey, byte[] pubKey, KeyCrypter keyCrypter) { + public ECKey(@Nullable EncryptedPrivateKey encryptedPrivateKey, @Nullable byte[] pubKey, KeyCrypter keyCrypter) { this((byte[])null, pubKey); this.keyCrypter = Preconditions.checkNotNull(keyCrypter); @@ -186,13 +186,15 @@ public class ECKey implements Serializable { * be used for signing. * @param compressed If set to true and pubKey is null, the derived public key will be in compressed form. */ - public ECKey(BigInteger privKey, byte[] pubKey, boolean compressed) { + public ECKey(@Nullable BigInteger privKey, @Nullable byte[] pubKey, boolean compressed) { + if (privKey == null && pubKey == null) + throw new IllegalArgumentException("ECKey requires at least private or public key"); this.priv = privKey; this.pub = null; - if (pubKey == null && privKey != null) { + if (pubKey == null) { // Derive public from private. this.pub = publicKeyFromPrivate(privKey, compressed); - } else if (pubKey != null) { + } else { // We expect the pubkey to be in regular encoded form, just as a BigInteger. Therefore the first byte is // a special marker byte. // TODO: This is probably not a useful API and may be confusing. @@ -206,7 +208,7 @@ public class ECKey implements Serializable { * the public key already correctly matches the public key. If only the public key is supplied, this ECKey cannot * be used for signing. */ - private ECKey(BigInteger privKey, byte[] pubKey) { + private ECKey(@Nullable BigInteger privKey, @Nullable byte[] pubKey) { this(privKey, pubKey, false); } @@ -381,7 +383,7 @@ public class ECKey implements Serializable { r = (DERInteger) seq.getObjectAt(0); s = (DERInteger) seq.getObjectAt(1); } catch (ClassCastException e) { - return null; + throw new IllegalArgumentException(e); } decoder.close(); // OpenSSL deviates from the DER spec by interpreting these values as unsigned, though they should not be @@ -431,7 +433,7 @@ public class ECKey implements Serializable { * @param aesKey The AES key to use for decryption of the private key. If null then no decryption is required. * @throws KeyCrypterException if this ECKey doesn't have a private part. */ - public ECDSASignature sign(Sha256Hash input, KeyParameter aesKey) throws KeyCrypterException { + public ECDSASignature sign(Sha256Hash input, @Nullable KeyParameter aesKey) throws KeyCrypterException { if (FAKE_SIGNATURES) return TransactionSignature.dummy(); @@ -601,7 +603,7 @@ public class ECKey implements Serializable { * @throws IllegalStateException if this ECKey does not have the private part. * @throws KeyCrypterException if this ECKey is encrypted and no AESKey is provided or it does not decrypt the ECKey. */ - public String signMessage(String message, KeyParameter aesKey) throws KeyCrypterException { + public String signMessage(String message, @Nullable KeyParameter aesKey) throws KeyCrypterException { if (priv == null) throw new IllegalStateException("This ECKey does not have the private key necessary for signing."); byte[] data = Utils.formatMessageForSigning(message); @@ -702,6 +704,7 @@ public class ECKey implements Serializable { * @param compressed Whether or not the original pubkey was compressed. * @return An ECKey containing only the public part, or null if recovery wasn't possible. */ + @Nullable public static ECKey recoverFromSignature(int recId, ECDSASignature sig, Sha256Hash message, boolean compressed) { Preconditions.checkArgument(recId >= 0, "recId must be positive"); Preconditions.checkArgument(sig.r.compareTo(BigInteger.ZERO) >= 0, "r must be positive"); diff --git a/core/src/main/java/com/google/bitcoin/core/FullPrunedBlockChain.java b/core/src/main/java/com/google/bitcoin/core/FullPrunedBlockChain.java index 3899010c..d5cd3ee0 100644 --- a/core/src/main/java/com/google/bitcoin/core/FullPrunedBlockChain.java +++ b/core/src/main/java/com/google/bitcoin/core/FullPrunedBlockChain.java @@ -22,6 +22,7 @@ import com.google.bitcoin.store.FullPrunedBlockStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.math.BigInteger; import java.util.ArrayList; import java.util.LinkedList; @@ -124,6 +125,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain { this.tx = tx; this.prevOutScripts = prevOutScripts; this.enforcePayToScriptHash = enforcePayToScriptHash; } + @Nullable @Override public VerificationException call() throws Exception { try{ diff --git a/core/src/main/java/com/google/bitcoin/core/Message.java b/core/src/main/java/com/google/bitcoin/core/Message.java index 48b2ed8b..7bb4ee84 100644 --- a/core/src/main/java/com/google/bitcoin/core/Message.java +++ b/core/src/main/java/com/google/bitcoin/core/Message.java @@ -380,7 +380,7 @@ public abstract class Message implements Serializable { * so BitcoinSerializer can avoid 2 instanceof checks + a casting. */ public Sha256Hash getHash() { - return null; + throw new UnsupportedOperationException(); } /** @@ -388,8 +388,6 @@ public abstract class Message implements Serializable { * implemented in a subclass of ChildMessage lazy parsing may have no effect. * * This default implementation is a safe fall back that will ensure it returns a correct value by parsing the message. - * - * @return */ public int getMessageSize() { if (length != UNKNOWN_LENGTH) diff --git a/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java b/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java index 8ef1e84a..5b6d3474 100644 --- a/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java +++ b/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java @@ -22,6 +22,7 @@ import com.google.bitcoin.script.ScriptOpCodes; import com.google.common.base.Objects; import org.spongycastle.util.encoders.Hex; +import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.Serializable; import java.math.BigInteger; @@ -185,6 +186,7 @@ public abstract class NetworkParameters implements Serializable { } /** Returns the network parameters for the given string ID or NULL if not recognized. */ + @Nullable public static NetworkParameters fromID(String id) { if (id.equals(ID_MAINNET)) { return MainNetParams.get(); diff --git a/core/src/main/java/com/google/bitcoin/core/PeerEventListener.java b/core/src/main/java/com/google/bitcoin/core/PeerEventListener.java index 5caa87b9..0ef15f9c 100644 --- a/core/src/main/java/com/google/bitcoin/core/PeerEventListener.java +++ b/core/src/main/java/com/google/bitcoin/core/PeerEventListener.java @@ -16,6 +16,7 @@ package com.google.bitcoin.core; +import javax.annotation.Nullable; import java.util.List; /** @@ -85,5 +86,6 @@ public interface PeerEventListener { *

Note that this will never be called if registered with any executor other than * {@link com.google.bitcoin.utils.Threading#SAME_THREAD}

*/ + @Nullable public List getData(Peer peer, GetDataMessage m); } diff --git a/core/src/main/java/com/google/bitcoin/core/PeerGroup.java b/core/src/main/java/com/google/bitcoin/core/PeerGroup.java index 4b9b1bde..8384ada2 100644 --- a/core/src/main/java/com/google/bitcoin/core/PeerGroup.java +++ b/core/src/main/java/com/google/bitcoin/core/PeerGroup.java @@ -149,14 +149,6 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac private LinkedBlockingQueue morePeersMailbox = new LinkedBlockingQueue(); - private void handleBlocksDownloaded() { - double rate = chain.getFalsePositiveRate(); - if (rate > bloomFilterFPRate * MAX_FP_RATE_INCREASE) { - log.info("Force update Bloom filter due to high false positive rate"); - recalculateFastCatchupAndFilter(true); - } - } - private class PeerStartupListener extends AbstractPeerEventListener { @Override public void onPeerConnected(Peer peer, int peerCount) { diff --git a/core/src/main/java/com/google/bitcoin/core/Transaction.java b/core/src/main/java/com/google/bitcoin/core/Transaction.java index 5311c7b2..0e2610a9 100644 --- a/core/src/main/java/com/google/bitcoin/core/Transaction.java +++ b/core/src/main/java/com/google/bitcoin/core/Transaction.java @@ -21,7 +21,6 @@ import com.google.bitcoin.crypto.TransactionSignature; import com.google.bitcoin.script.Script; import com.google.bitcoin.script.ScriptBuilder; import com.google.bitcoin.script.ScriptOpCodes; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,6 +34,7 @@ import java.text.SimpleDateFormat; import java.util.*; import static com.google.bitcoin.core.Utils.*; +import static com.google.common.base.Preconditions.*; /** *

A transaction represents the movement of coins from some addresses to some other addresses. It can also represent @@ -172,7 +172,7 @@ public class Transaction extends ChildMessage implements Serializable { * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH * @throws ProtocolException */ - public Transaction(NetworkParameters params, byte[] msg, int offset, Message parent, boolean parseLazy, boolean parseRetain, int length) + public Transaction(NetworkParameters params, byte[] msg, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException { super(params, msg, offset, parent, parseLazy, parseRetain, length); } @@ -180,7 +180,7 @@ public class Transaction extends ChildMessage implements Serializable { /** * Creates a transaction by reading payload starting from offset bytes in. Length of a transaction is fixed. */ - public Transaction(NetworkParameters params, byte[] msg, Message parent, boolean parseLazy, boolean parseRetain, int length) + public Transaction(NetworkParameters params, byte[] msg, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException { super(params, msg, 0, parent, parseLazy, parseRetain, length); } @@ -363,29 +363,6 @@ public class Transaction extends ChildMessage implements Serializable { return disconnected; } - /** - * Connects all inputs using the provided transactions. If any input cannot be connected returns that input or - * null on success. - */ - TransactionInput connectForReorganize(Map transactions) { - maybeParse(); - for (TransactionInput input : inputs) { - // Coinbase transactions, by definition, do not have connectable inputs. - if (input.isCoinBase()) continue; - TransactionInput.ConnectionResult result = - input.connect(transactions, TransactionInput.ConnectMode.ABORT_ON_CONFLICT); - // Connected to another tx in the wallet? - if (result == TransactionInput.ConnectionResult.SUCCESS) - continue; - // The input doesn't exist in the wallet, eg because it belongs to somebody else (inbound spend). - if (result == TransactionInput.ConnectionResult.NO_SUCH_TX) - continue; - // Could not connect this input, so return it and abort. - return input; - } - return null; - } - /** * Returns true if every output is marked as spent. */ @@ -795,11 +772,11 @@ public class Transaction extends ChildMessage implements Serializable { * @param aesKey The AES key to use to decrypt the key before signing. Null if no decryption is required. */ public synchronized void signInputs(SigHash hashType, Wallet wallet, @Nullable KeyParameter aesKey) throws ScriptException { - Preconditions.checkState(inputs.size() > 0); - Preconditions.checkState(outputs.size() > 0); + checkState(inputs.size() > 0); + checkState(outputs.size() > 0); // I don't currently have an easy way to test other modes work, as the official client does not use them. - Preconditions.checkArgument(hashType == SigHash.ALL, "Only SIGHASH_ALL is currently supported"); + checkArgument(hashType == SigHash.ALL, "Only SIGHASH_ALL is currently supported"); // The transaction is signed with the input scripts empty except for the input we are signing. In the case // where addInput has been used to set up a new transaction, they are already all empty. The input being signed @@ -832,8 +809,7 @@ public class Transaction extends ChildMessage implements Serializable { // Find the signing key we'll need to use. ECKey key = input.getOutpoint().getConnectedKey(wallet); // This assert should never fire. If it does, it means the wallet is inconsistent. - Preconditions.checkNotNull(key, "Transaction exists in wallet that we cannot redeem: %s", - input.getOutpoint().getHash()); + checkNotNull(key, "Transaction exists in wallet that we cannot redeem: %s", input.getOutpoint().getHash()); // Keep the key around for the script creation step below. signingKeys[i] = key; // The anyoneCanPay feature isn't used at the moment. @@ -858,7 +834,9 @@ public class Transaction extends ChildMessage implements Serializable { if (signatures[i] == null) continue; TransactionInput input = inputs.get(i); - Script scriptPubKey = input.getOutpoint().getConnectedOutput().getScriptPubKey(); + final TransactionOutput connectedOutput = input.getOutpoint().getConnectedOutput(); + checkNotNull(connectedOutput); // Quiet static analysis: is never null here but cannot be statically proven + Script scriptPubKey = connectedOutput.getScriptPubKey(); if (scriptPubKey.isSentToAddress()) { input.setScriptSig(ScriptBuilder.createInputScript(signatures[i], signingKeys[i])); } else if (scriptPubKey.isSentToRawPubKey()) { @@ -887,7 +865,7 @@ public class Transaction extends ChildMessage implements Serializable { * @param anyoneCanPay Signing mode, see the SigHash enum for documentation. * @return A newly calculated signature object that wraps the r, s and sighash components. */ - public synchronized TransactionSignature calculateSignature(int inputIndex, ECKey key, KeyParameter aesKey, + public synchronized TransactionSignature calculateSignature(int inputIndex, ECKey key, @Nullable KeyParameter aesKey, byte[] connectedPubKeyScript, SigHash hashType, boolean anyoneCanPay) { Sha256Hash hash = hashForSignature(inputIndex, connectedPubKeyScript, hashType, anyoneCanPay); diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java b/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java index 4d56d62f..65d69c38 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java @@ -22,6 +22,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import javax.annotation.Nullable; import java.io.Serializable; import java.math.BigInteger; import java.util.ListIterator; @@ -111,17 +112,6 @@ public class TransactionConfidence implements Serializable { public int getValue() { return value; } - - public static ConfidenceType valueOf(int value) { - switch (value) { - case 0: return UNKNOWN; - case 1: return BUILDING; - case 2: return PENDING; - case 4: return DEAD; - default: return null; - } - } - } private ConfidenceType confidenceType = ConfidenceType.UNKNOWN; @@ -402,7 +392,7 @@ public class TransactionConfidence implements Serializable { * in such a way that the double-spending transaction takes precedence over this one. It will not become valid now * unless there is a re-org. Automatically sets the confidence type to DEAD. */ - public synchronized void setOverridingTransaction(Transaction overridingTransaction) { + public synchronized void setOverridingTransaction(@Nullable Transaction overridingTransaction) { this.overridingTransaction = overridingTransaction; setConfidenceType(ConfidenceType.DEAD); } diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionInput.java b/core/src/main/java/com/google/bitcoin/core/TransactionInput.java index dc909b3f..8ce9d33a 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionInput.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionInput.java @@ -19,6 +19,7 @@ package com.google.bitcoin.core; import com.google.bitcoin.script.Script; import com.google.common.base.Preconditions; +import javax.annotation.Nullable; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; @@ -67,15 +68,13 @@ public class TransactionInput extends ChildMessage implements Serializable { length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length); } - public TransactionInput(NetworkParameters params, Transaction parentTransaction, - byte[] scriptBytes, - TransactionOutPoint outpoint) { + public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes, + TransactionOutPoint outpoint) { super(params); this.scriptBytes = scriptBytes; this.outpoint = outpoint; this.sequence = NO_SEQUENCE; this.parentTransaction = parentTransaction; - length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length); } @@ -278,12 +277,12 @@ public class TransactionInput extends ChildMessage implements Serializable { * * @return The TransactionOutput or null if the transactions map doesn't contain the referenced tx. */ + @Nullable TransactionOutput getConnectedOutput(Map transactions) { Transaction tx = transactions.get(outpoint.getHash()); if (tx == null) return null; - TransactionOutput out = tx.getOutputs().get((int) outpoint.getIndex()); - return out; + return tx.getOutputs().get((int) outpoint.getIndex()); } public enum ConnectMode { @@ -414,6 +413,7 @@ public class TransactionInput extends ChildMessage implements Serializable { * {@link TransactionInput#connect(TransactionOutput)} or variants at some point. If it wasn't connected, then * this method returns null. */ + @Nullable public TransactionOutput getConnectedOutput() { return getOutpoint().getConnectedOutput(); } diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionOutPoint.java b/core/src/main/java/com/google/bitcoin/core/TransactionOutPoint.java index 2b7a4ce3..3e0c61ea 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionOutPoint.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionOutPoint.java @@ -18,6 +18,7 @@ package com.google.bitcoin.core; import com.google.bitcoin.script.Script; +import javax.annotation.Nullable; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; @@ -43,7 +44,7 @@ public class TransactionOutPoint extends ChildMessage implements Serializable { // It points to the connected transaction. Transaction fromTx; - public TransactionOutPoint(NetworkParameters params, long index, Transaction fromTx) { + public TransactionOutPoint(NetworkParameters params, long index, @Nullable Transaction fromTx) { super(params); this.index = index; if (fromTx != null) { @@ -114,6 +115,7 @@ public class TransactionOutPoint extends ChildMessage implements Serializable { * sides in memory, and they have been linked together, this returns a pointer to the connected output, or null * if there is no such connection. */ + @Nullable public TransactionOutput getConnectedOutput() { if (fromTx == null) return null; return fromTx.getOutputs().get((int) index); @@ -121,25 +123,20 @@ public class TransactionOutPoint extends ChildMessage implements Serializable { /** * Returns the pubkey script from the connected output. + * @throws java.lang.NullPointerException if there is no connected output. */ byte[] getConnectedPubKeyScript() { - byte[] result = checkNotNull(getConnectedOutput().getScriptBytes()); + byte[] result = checkNotNull(getConnectedOutput()).getScriptBytes(); checkState(result.length > 0); return result; } - /** - * Convenience method to get the connected outputs pubkey hash. - */ - byte[] getConnectedPubKeyHash() throws ScriptException { - return getConnectedOutput().getScriptPubKey().getPubKeyHash(); - } - /** * Returns the ECKey identified in the connected output, for either pay-to-address scripts or pay-to-key scripts. * If the script forms cannot be understood, throws ScriptException. * @return an ECKey or null if the connected key cannot be found in the wallet. */ + @Nullable public ECKey getConnectedKey(Wallet wallet) throws ScriptException { TransactionOutput connectedOutput = getConnectedOutput(); checkNotNull(connectedOutput, "Input is not connected so cannot retrieve key"); diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java b/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java index 242b7b92..f7c30c59 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java @@ -28,9 +28,7 @@ import java.io.OutputStream; import java.io.Serializable; import java.math.BigInteger; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Preconditions.*; /** * A TransactionOutput message contains a scriptPubKey that controls who is able to spend its value. It is a sub-part @@ -62,7 +60,7 @@ public class TransactionOutput extends ChildMessage implements Serializable { /** * Deserializes a transaction output message. This is usually part of a transaction message. */ - public TransactionOutput(NetworkParameters params, Transaction parent, byte[] payload, + public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, byte[] payload, int offset) throws ProtocolException { super(params, payload, offset); parentTransaction = parent; @@ -311,11 +309,10 @@ public class TransactionOutput extends ChildMessage implements Serializable { } /** - * Returns the transaction that owns this output, or null if this is a free standing object. + * Returns the transaction that owns this output, or throws NullPointerException if unowned. */ - @Nullable public Transaction getParentTransaction() { - return parentTransaction; + return checkNotNull(parentTransaction, "Free-standing TransactionOutput"); } /** diff --git a/core/src/main/java/com/google/bitcoin/core/VersionMessage.java b/core/src/main/java/com/google/bitcoin/core/VersionMessage.java index 20ee9fdf..8d942f5e 100644 --- a/core/src/main/java/com/google/bitcoin/core/VersionMessage.java +++ b/core/src/main/java/com/google/bitcoin/core/VersionMessage.java @@ -218,7 +218,7 @@ public class VersionMessage extends Message { */ @Override byte[] getChecksum() { - return null; + throw new UnsupportedOperationException(); } /** @@ -226,6 +226,7 @@ public class VersionMessage extends Message { */ @Override void setChecksum(byte[] checksum) { + throw new UnsupportedOperationException(); } @Override diff --git a/core/src/main/java/com/google/bitcoin/core/Wallet.java b/core/src/main/java/com/google/bitcoin/core/Wallet.java index da6b1cbd..8cd036ed 100644 --- a/core/src/main/java/com/google/bitcoin/core/Wallet.java +++ b/core/src/main/java/com/google/bitcoin/core/Wallet.java @@ -137,7 +137,7 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi private final NetworkParameters params; - private Sha256Hash lastBlockSeenHash; + @Nullable private Sha256Hash lastBlockSeenHash; private int lastBlockSeenHeight; private long lastBlockSeenTimeSecs; @@ -2584,7 +2584,8 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi } } - /** Returns the hash of the last seen best-chain block. */ + /** 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 { @@ -2594,7 +2595,7 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi } } - public void setLastBlockSeenHash(Sha256Hash lastBlockSeenHash) { + public void setLastBlockSeenHash(@Nullable Sha256Hash lastBlockSeenHash) { lock.lock(); try { this.lastBlockSeenHash = lastBlockSeenHash; diff --git a/core/src/main/java/com/google/bitcoin/crypto/TransactionSignature.java b/core/src/main/java/com/google/bitcoin/crypto/TransactionSignature.java index 64430c61..57dd870e 100644 --- a/core/src/main/java/com/google/bitcoin/crypto/TransactionSignature.java +++ b/core/src/main/java/com/google/bitcoin/crypto/TransactionSignature.java @@ -156,9 +156,12 @@ public class TransactionSignature extends ECKey.ECDSASignature { // Bitcoin encoding is DER signature + sighash byte. if (requireCanonical && !isEncodingCanonical(bytes)) throw new VerificationException("Signature encoding is not canonical."); - ECKey.ECDSASignature sig = ECKey.ECDSASignature.decodeFromDER(bytes); - if (sig == null) - throw new VerificationException("Could not decode DER component."); + ECKey.ECDSASignature sig; + try { + sig = ECKey.ECDSASignature.decodeFromDER(bytes); + } catch (IllegalArgumentException e) { + throw new VerificationException("Could not decode DER", e); + } TransactionSignature tsig = new TransactionSignature(sig.r, sig.s); // In Bitcoin, any value of the final byte is valid, but not necessarily canonical. See javadocs for // isEncodingCanonical to learn more about this. diff --git a/core/src/main/java/com/google/bitcoin/net/BlockingClient.java b/core/src/main/java/com/google/bitcoin/net/BlockingClient.java index c05cfb3b..e2ecf9d6 100644 --- a/core/src/main/java/com/google/bitcoin/net/BlockingClient.java +++ b/core/src/main/java/com/google/bitcoin/net/BlockingClient.java @@ -18,7 +18,6 @@ package com.google.bitcoin.net; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.io.InputStream; @@ -42,8 +41,8 @@ public class BlockingClient implements MessageWriteTarget { private static final int BUFFER_SIZE_LOWER_BOUND = 4096; private static final int BUFFER_SIZE_UPPER_BOUND = 65536; - @Nonnull private final ByteBuffer dbuf; - @Nonnull private final Socket socket; + private final ByteBuffer dbuf; + private final Socket socket; private volatile boolean vCloseRequested = false; /** diff --git a/core/src/main/java/com/google/bitcoin/net/ConnectionHandler.java b/core/src/main/java/com/google/bitcoin/net/ConnectionHandler.java index bccb5c9b..46a8971c 100644 --- a/core/src/main/java/com/google/bitcoin/net/ConnectionHandler.java +++ b/core/src/main/java/com/google/bitcoin/net/ConnectionHandler.java @@ -21,6 +21,7 @@ import com.google.bitcoin.utils.Threading; import com.google.common.base.Throwables; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import java.io.IOException; import java.nio.ByteBuffer; @@ -54,7 +55,7 @@ class ConnectionHandler implements MessageWriteTarget { @GuardedBy("lock") private final ByteBuffer readBuff; @GuardedBy("lock") private final SocketChannel channel; @GuardedBy("lock") private final SelectionKey key; - @GuardedBy("lock") final StreamParser parser; + @GuardedBy("lock") StreamParser parser; @GuardedBy("lock") private boolean closeCalled = false; @GuardedBy("lock") private long bytesToWriteRemaining = 0; @@ -68,15 +69,15 @@ class ConnectionHandler implements MessageWriteTarget { throw new IOException("Parser factory.getNewParser returned null"); } - private ConnectionHandler(StreamParser parser, SelectionKey key) { + private ConnectionHandler(@Nullable StreamParser parser, SelectionKey key) { this.key = key; this.channel = checkNotNull(((SocketChannel)key.channel())); - this.parser = parser; if (parser == null) { readBuff = null; closeConnection(); return; } + this.parser = parser; readBuff = ByteBuffer.allocateDirect(Math.min(Math.max(parser.getMaxMessageSize(), BUFFER_SIZE_LOWER_BOUND), BUFFER_SIZE_UPPER_BOUND)); parser.setWriteTarget(this); // May callback into us (eg closeConnection() now) connectedHandlers = null; @@ -212,7 +213,7 @@ class ConnectionHandler implements MessageWriteTarget { // "flip" the buffer - setting the limit to the current position and setting position to 0 handler.readBuff.flip(); // Use parser.receiveBytes's return value as a check that it stopped reading at the right location - int bytesConsumed = handler.parser.receiveBytes(handler.readBuff); + int bytesConsumed = checkNotNull(handler.parser).receiveBytes(handler.readBuff); checkState(handler.readBuff.position() == bytesConsumed); // Now drop the bytes which were read by compacting readBuff (resetting limit and keeping relative // position) diff --git a/core/src/main/java/com/google/bitcoin/net/discovery/SeedPeers.java b/core/src/main/java/com/google/bitcoin/net/discovery/SeedPeers.java index d52220a5..c70bc457 100644 --- a/core/src/main/java/com/google/bitcoin/net/discovery/SeedPeers.java +++ b/core/src/main/java/com/google/bitcoin/net/discovery/SeedPeers.java @@ -17,6 +17,7 @@ package com.google.bitcoin.net.discovery; import com.google.bitcoin.core.NetworkParameters; +import javax.annotation.Nullable; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -42,6 +43,7 @@ public class SeedPeers implements PeerDiscovery { * @return InetSocketAddress - The address/port of the next node. * @throws PeerDiscoveryException */ + @Nullable public InetSocketAddress getPeer() throws PeerDiscoveryException { try { return nextPeer(); @@ -50,6 +52,7 @@ public class SeedPeers implements PeerDiscovery { } } + @Nullable private InetSocketAddress nextPeer() throws UnknownHostException { if (pnseedIndex >= seedAddrs.length) return null; return new InetSocketAddress(convertAddress(seedAddrs[pnseedIndex++]), diff --git a/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerListener.java b/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerListener.java index fdc871fa..3da360b4 100644 --- a/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerListener.java +++ b/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerListener.java @@ -16,14 +16,6 @@ package com.google.bitcoin.protocols.channels; -import java.io.IOException; -import java.math.BigInteger; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import com.google.bitcoin.core.Sha256Hash; import com.google.bitcoin.core.TransactionBroadcaster; import com.google.bitcoin.core.Wallet; @@ -32,6 +24,13 @@ import com.google.bitcoin.net.ProtobufParser; import com.google.bitcoin.net.StreamParserFactory; import org.bitcoin.paymentchannel.Protos; +import javax.annotation.Nullable; +import java.io.IOException; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -120,7 +119,7 @@ public class PaymentChannelServerListener { private PaymentChannelCloseException.CloseReason closeReason; // The user-provided event handler - @Nonnull private ServerConnectionEventHandler eventHandler; + private ServerConnectionEventHandler eventHandler; // The payment channel server which does the actual payment channel handling private final PaymentChannelServer paymentChannelManager; diff --git a/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerState.java b/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerState.java index 82729cbe..a83437bc 100644 --- a/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerState.java +++ b/core/src/main/java/com/google/bitcoin/protocols/channels/PaymentChannelServerState.java @@ -303,7 +303,7 @@ public class PaymentChannelServerState { // was not connected to the peergroup when the contract was broadcast (which may cause issues down the road, and // disables our double-spend check next) Transaction walletContract = wallet.getTransaction(multisigContract.getHash()); - checkState(walletContract != null, "Wallet did not contain multisig contract {} after state was marked READY", multisigContract.getHash()); + checkNotNull(walletContract, "Wallet did not contain multisig contract {} after state was marked READY", multisigContract.getHash()); // Note that we check for DEAD state here, but this test is essentially useless in production because we will // miss most double-spends due to bloom filtering right now anyway. This will eventually fixed by network-wide diff --git a/core/src/main/java/com/google/bitcoin/protocols/channels/ServerConnectionEventHandler.java b/core/src/main/java/com/google/bitcoin/protocols/channels/ServerConnectionEventHandler.java index 589ff815..027e9795 100644 --- a/core/src/main/java/com/google/bitcoin/protocols/channels/ServerConnectionEventHandler.java +++ b/core/src/main/java/com/google/bitcoin/protocols/channels/ServerConnectionEventHandler.java @@ -22,6 +22,8 @@ import com.google.bitcoin.core.Sha256Hash; import com.google.bitcoin.net.ProtobufParser; import org.bitcoin.paymentchannel.Protos; +import javax.annotation.Nullable; + /** * A connection-specific event handler that handles events generated by client connections on a * {@link PaymentChannelServerListener} @@ -30,7 +32,7 @@ public abstract class ServerConnectionEventHandler { private ProtobufParser connectionChannel; // Called by ServerListener before channelOpen to set connectionChannel when it is ready to received application messages // Also called with null to clear connectionChannel after channelClosed() - synchronized void setConnectionChannel(ProtobufParser connectionChannel) { this.connectionChannel = connectionChannel; } + synchronized void setConnectionChannel(@Nullable ProtobufParser connectionChannel) { this.connectionChannel = connectionChannel; } /** *

Closes the channel with the client (will generate a diff --git a/core/src/main/java/com/google/bitcoin/protocols/channels/StoredPaymentChannelClientStates.java b/core/src/main/java/com/google/bitcoin/protocols/channels/StoredPaymentChannelClientStates.java index 9cfa26f5..50f70f93 100644 --- a/core/src/main/java/com/google/bitcoin/protocols/channels/StoredPaymentChannelClientStates.java +++ b/core/src/main/java/com/google/bitcoin/protocols/channels/StoredPaymentChannelClientStates.java @@ -141,6 +141,7 @@ public class StoredPaymentChannelClientStates implements WalletExtension { /** * Finds a channel with the given id and contract hash and returns it, or returns null. */ + @Nullable StoredClientChannel getChannel(Sha256Hash id, Sha256Hash contractHash) { lock.lock(); try { diff --git a/core/src/main/java/com/google/bitcoin/protocols/channels/StoredServerChannel.java b/core/src/main/java/com/google/bitcoin/protocols/channels/StoredServerChannel.java index df34c115..9dad7952 100644 --- a/core/src/main/java/com/google/bitcoin/protocols/channels/StoredServerChannel.java +++ b/core/src/main/java/com/google/bitcoin/protocols/channels/StoredServerChannel.java @@ -18,6 +18,7 @@ package com.google.bitcoin.protocols.channels; import com.google.bitcoin.core.*; +import javax.annotation.Nullable; import java.math.BigInteger; import java.util.Date; @@ -41,8 +42,8 @@ public class StoredServerChannel { private PaymentChannelServer connectedHandler = null; PaymentChannelServerState state = null; - StoredServerChannel(PaymentChannelServerState state, Transaction contract, TransactionOutput clientOutput, - long refundTransactionUnlockTimeSecs, ECKey myKey, BigInteger bestValueToMe, byte[] bestValueSignature) { + StoredServerChannel(@Nullable PaymentChannelServerState state, Transaction contract, TransactionOutput clientOutput, + long refundTransactionUnlockTimeSecs, ECKey myKey, BigInteger bestValueToMe, @Nullable byte[] bestValueSignature) { this.contract = contract; this.clientOutput = clientOutput; this.refundTransactionUnlockTimeSecs = refundTransactionUnlockTimeSecs; diff --git a/core/src/main/java/com/google/bitcoin/store/H2FullPrunedBlockStore.java b/core/src/main/java/com/google/bitcoin/store/H2FullPrunedBlockStore.java index 06581c30..45f30697 100644 --- a/core/src/main/java/com/google/bitcoin/store/H2FullPrunedBlockStore.java +++ b/core/src/main/java/com/google/bitcoin/store/H2FullPrunedBlockStore.java @@ -21,6 +21,7 @@ import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -458,6 +459,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore { } } + @Nullable public StoredBlock get(Sha256Hash hash, boolean wasUndoableOnly) throws BlockStoreException { // Optimize for chain head if (chainHeadHash != null && chainHeadHash.equals(hash)) @@ -467,8 +469,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore { maybeConnect(); PreparedStatement s = null; try { - s = conn.get() - .prepareStatement("SELECT chainWork, height, header, wasUndoable FROM headers WHERE hash = ?"); + s = conn.get().prepareStatement("SELECT chainWork, height, header, wasUndoable FROM headers WHERE hash = ?"); // We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes byte[] hashBytes = new byte[28]; System.arraycopy(hash.getBytes(), 3, hashBytes, 0, 28); @@ -478,16 +479,13 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore { return null; } // Parse it. - if (wasUndoableOnly && !results.getBoolean(4)) return null; - BigInteger chainWork = new BigInteger(results.getBytes(1)); int height = results.getInt(2); Block b = new Block(params, results.getBytes(3)); b.verifyHeader(); - StoredBlock stored = new StoredBlock(b, chainWork, height); - return stored; + return new StoredBlock(b, chainWork, height); } catch (SQLException ex) { throw new BlockStoreException(ex); } catch (ProtocolException e) { @@ -498,21 +496,27 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore { // blocks. throw new BlockStoreException(e); } finally { - if (s != null) + if (s != null) { try { s.close(); - } catch (SQLException e) { throw new BlockStoreException("Failed to close PreparedStatement"); } + } catch (SQLException e) { + throw new BlockStoreException("Failed to close PreparedStatement"); + } + } } } + @Nullable public StoredBlock get(Sha256Hash hash) throws BlockStoreException { return get(hash, false); } + @Nullable public StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException { return get(hash, true); } + @Nullable public StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException { maybeConnect(); PreparedStatement s = null; @@ -629,6 +633,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore { } } + @Nullable public StoredTransactionOutput getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException { maybeConnect(); PreparedStatement s = null; @@ -647,8 +652,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore { int height = results.getInt(1); BigInteger value = new BigInteger(results.getBytes(2)); // Tell the StoredTransactionOutput that we are a coinbase, as that is encoded in height - StoredTransactionOutput txout = new StoredTransactionOutput(hash, index, value, height, true, results.getBytes(3)); - return txout; + return new StoredTransactionOutput(hash, index, value, height, true, results.getBytes(3)); } catch (SQLException ex) { throw new BlockStoreException(ex); } finally { diff --git a/core/src/main/java/com/google/bitcoin/store/MemoryFullPrunedBlockStore.java b/core/src/main/java/com/google/bitcoin/store/MemoryFullPrunedBlockStore.java index 9de652a3..e00e02be 100644 --- a/core/src/main/java/com/google/bitcoin/store/MemoryFullPrunedBlockStore.java +++ b/core/src/main/java/com/google/bitcoin/store/MemoryFullPrunedBlockStore.java @@ -21,99 +21,10 @@ import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; +import javax.annotation.Nullable; import java.io.Serializable; import java.util.*; - -/** - * A StoredTransaction message contains the information necessary to check a transaction later (ie after a reorg). - * It is used to avoid having to store the entire transaction when we only need its inputs+outputs. - */ -class StoredTransaction implements Serializable { - private static final long serialVersionUID = 6243881368122528323L; - - /** - * A transaction has some value and a script used for authenticating that the redeemer is allowed to spend - * this output. - */ - private List outputs; - private List inputs; - private long version; - private long lockTime; - private Sha256Hash hash; - - public StoredTransaction(NetworkParameters params, Transaction tx, int height) { - inputs = new LinkedList(); - outputs = new LinkedList(); - for (TransactionInput in : tx.getInputs()) - inputs.add(new TransactionInput(params, null, in.getScriptBytes(), in.getOutpoint())); - for (TransactionOutput out : tx.getOutputs()) - outputs.add(new StoredTransactionOutput(null, out, height, tx.isCoinBase())); - this.version = tx.getVersion(); - this.lockTime = tx.getLockTime(); - this.hash = tx.getHash(); - } - - /** - * The lits of inputs in this transaction - */ - public List getInputs() { - return inputs; - } - - /** - * The lits of outputs in this transaction - * Note that the hashes of all of these are null - */ - public List getOutputs() { - return outputs; - } - - /** - * The hash of this stored transaction - */ - public Sha256Hash getHash() { - return hash; - } - - /** - * The lockTime of the stored transaction - */ - public long getLockTime() { - return lockTime; - } - - /** - * The version of the stored transaction - */ - public long getVersion() { - return version; - } - - /** - * A coinbase transaction is one that creates a new coin. They are the first transaction in each block and their - * value is determined by a formula that all implementations of BitCoin share. In 2011 the value of a coinbase - * transaction is 50 coins, but in future it will be less. A coinbase transaction is defined not only by its - * position in a block but by the data in the inputs. - */ - public boolean isCoinBase() { - return inputs.get(0).isCoinBase(); - } - - public String toString() { - return "Stored Transaction: " + hash.toString(); - } - - public int hashCode() { - return getHash().hashCode(); - } - - public boolean equals(Object o) { - if (!(o instanceof StoredTransaction)) return false; - return ((StoredTransaction) o).getHash().equals(this.getHash()); - } -} - /** * Used as a key for memory map (to avoid having to think about NetworkParameters, * which is required for {@link TransactionOutPoint} @@ -203,6 +114,7 @@ class TransactionalHashMap { tempMap.remove(); } + @Nullable public ValueType get(KeyType key) { if (Boolean.TRUE.equals(inTransaction.get())) { if (tempMap.get() != null) { @@ -228,6 +140,7 @@ class TransactionalHashMap { } } + @Nullable public ValueType remove(KeyType key) { if (Boolean.TRUE.equals(inTransaction.get())) { ValueType retVal = map.get(key); @@ -275,6 +188,7 @@ class TransactionalMultiKeyHashMap { mapValues.abortDatabaseBatchWrite(); } + @Nullable public ValueType get(UniqueKeyType key) { return mapValues.get(key); } @@ -291,6 +205,7 @@ class TransactionalMultiKeyHashMap { } } + @Nullable public ValueType removeByUniqueKey(UniqueKeyType key) { return mapValues.remove(key); } @@ -360,18 +275,21 @@ public class MemoryFullPrunedBlockStore implements FullPrunedBlockStore { blockMap.put(hash, new StoredBlockAndWasUndoableFlag(storedBlock, true)); } + @Nullable public synchronized StoredBlock get(Sha256Hash hash) throws BlockStoreException { Preconditions.checkNotNull(blockMap, "MemoryFullPrunedBlockStore is closed"); StoredBlockAndWasUndoableFlag storedBlock = blockMap.get(hash); return storedBlock == null ? null : storedBlock.block; } + @Nullable public synchronized StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException { Preconditions.checkNotNull(blockMap, "MemoryFullPrunedBlockStore is closed"); StoredBlockAndWasUndoableFlag storedBlock = blockMap.get(hash); return (storedBlock != null && storedBlock.wasUndoable) ? storedBlock.block : null; } + @Nullable public synchronized StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException { Preconditions.checkNotNull(fullBlockMap, "MemoryFullPrunedBlockStore is closed"); return fullBlockMap.get(hash); @@ -408,6 +326,7 @@ public class MemoryFullPrunedBlockStore implements FullPrunedBlockStore { transactionOutputMap = null; } + @Nullable public synchronized StoredTransactionOutput getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException { Preconditions.checkNotNull(transactionOutputMap, "MemoryFullPrunedBlockStore is closed"); return transactionOutputMap.get(new StoredTransactionOutPoint(hash, index)); diff --git a/core/src/main/java/com/google/bitcoin/store/SPVBlockStore.java b/core/src/main/java/com/google/bitcoin/store/SPVBlockStore.java index 42618b5e..ba28626f 100644 --- a/core/src/main/java/com/google/bitcoin/store/SPVBlockStore.java +++ b/core/src/main/java/com/google/bitcoin/store/SPVBlockStore.java @@ -21,6 +21,7 @@ import com.google.bitcoin.utils.Threading; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; @@ -74,10 +75,10 @@ public class SPVBlockStore implements BlockStore { // // We don't care about the value in this cache. It is always notFoundMarker. Unfortunately LinkedHashSet does not // provide the removeEldestEntry control. - protected static final StoredBlock notFoundMarker = new StoredBlock(null, null, -1); - protected LinkedHashMap notFoundCache = new LinkedHashMap() { + protected static final Object notFoundMarker = new Object(); + protected LinkedHashMap notFoundCache = new LinkedHashMap() { @Override - protected boolean removeEldestEntry(Map.Entry entry) { + protected boolean removeEldestEntry(Map.Entry entry) { return size() > 100; // This was chosen arbitrarily. } }; @@ -181,6 +182,7 @@ public class SPVBlockStore implements BlockStore { } finally { lock.unlock(); } } + @Nullable public StoredBlock get(Sha256Hash hash) throws BlockStoreException { final MappedByteBuffer buffer = this.buffer; if (buffer == null) throw new BlockStoreException("Store closed"); diff --git a/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java b/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java index 86019450..b35198ad 100644 --- a/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java +++ b/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java @@ -364,7 +364,10 @@ public class WalletProtobufSerializer { public Wallet readWallet(InputStream input) throws UnreadableWalletException { try { Protos.Wallet walletProto = parseToProto(input); - NetworkParameters params = NetworkParameters.fromID(walletProto.getNetworkIdentifier()); + final String paramsID = walletProto.getNetworkIdentifier(); + NetworkParameters params = NetworkParameters.fromID(paramsID); + if (params == null) + throw new UnreadableWalletException("Unknown network parameters ID " + paramsID); Wallet wallet = new Wallet(params); readWallet(walletProto, wallet); return wallet; diff --git a/core/src/main/java/com/google/bitcoin/wallet/KeyTimeCoinSelector.java b/core/src/main/java/com/google/bitcoin/wallet/KeyTimeCoinSelector.java index 3e38b5c7..e98f25b9 100644 --- a/core/src/main/java/com/google/bitcoin/wallet/KeyTimeCoinSelector.java +++ b/core/src/main/java/com/google/bitcoin/wallet/KeyTimeCoinSelector.java @@ -25,6 +25,8 @@ import org.slf4j.LoggerFactory; import java.math.BigInteger; import java.util.LinkedList; +import static com.google.common.base.Preconditions.checkNotNull; + /** * A coin selector that takes all coins assigned to keys created before the given timestamp. * Used as part of the implementation of {@link Wallet#setKeyRotationTime(java.util.Date)}. @@ -65,6 +67,7 @@ public class KeyTimeCoinSelector implements CoinSelector { log.info("Skipping tx output {} because it's not of simple form.", output); continue; } + checkNotNull(controllingKey, "Coin selector given output as candidate for which we lack the key"); if (controllingKey.getCreationTimeSeconds() >= unixTimeSeconds) continue; // It's older than the cutoff time so select. valueGathered = valueGathered.add(output.getValue());