3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-12 10:15:52 +00:00
Add lots more nullity annotations.
Clear lots of nullity static analysis warnings.
Delete some dead code.
Simplify a few expressions.

Resolves issue 317.
This commit is contained in:
Mike Hearn 2013-12-24 00:40:19 +00:00
parent 3d99be48bc
commit d0cd770d62
29 changed files with 130 additions and 224 deletions

View File

@ -88,18 +88,19 @@ public class BlockChain extends AbstractBlockChain {
@Override @Override
protected TransactionOutputChanges connectTransactions(int height, Block block) { protected TransactionOutputChanges connectTransactions(int height, Block 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())
return null; throw new UnsupportedOperationException();
} }
@Override @Override
protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) { protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
// 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())
return null; throw new UnsupportedOperationException();
} }
@Override @Override
protected void disconnectTransactions(StoredBlock block) { 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 @Override

View File

@ -15,6 +15,8 @@
*/ */
package com.google.bitcoin.core; 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 * 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. * 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 { public abstract class ChildMessage extends Message {
private static final long serialVersionUID = -7657113383624517931L; private static final long serialVersionUID = -7657113383624517931L;
private Message parent; @Nullable private Message parent;
protected ChildMessage() { protected ChildMessage() {
} }
@ -47,13 +49,13 @@ public abstract class ChildMessage extends Message {
super(params, msg, offset); 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 { throws ProtocolException {
super(params, msg, offset, parseLazy, parseRetain, length); super(params, msg, offset, parseLazy, parseRetain, length);
this.parent = parent; this.parent = parent;
} }
public void setParent(Message parent) { public void setParent(@Nullable Message parent) {
if (this.parent != null && this.parent != parent && parent != null) { 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 // 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 // changes internally. To be safe we invalidate the parent cache to ensure it rebuilds

View File

@ -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 * is more convenient if you are importing a key from elsewhere. The public key will be automatically derived
* from the private key. * 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); 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 pubKey The keys public key
* @param keyCrypter The KeyCrypter that will be used, with an AES key, to encrypt and decrypt the private 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((byte[])null, pubKey);
this.keyCrypter = Preconditions.checkNotNull(keyCrypter); this.keyCrypter = Preconditions.checkNotNull(keyCrypter);
@ -186,13 +186,15 @@ public class ECKey implements Serializable {
* be used for signing. * be used for signing.
* @param compressed If set to true and pubKey is null, the derived public key will be in compressed form. * @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.priv = privKey;
this.pub = null; this.pub = null;
if (pubKey == null && privKey != null) { if (pubKey == null) {
// Derive public from private. // Derive public from private.
this.pub = publicKeyFromPrivate(privKey, compressed); 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 // We expect the pubkey to be in regular encoded form, just as a BigInteger. Therefore the first byte is
// a special marker byte. // a special marker byte.
// TODO: This is probably not a useful API and may be confusing. // 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 * the public key already correctly matches the public key. If only the public key is supplied, this ECKey cannot
* be used for signing. * be used for signing.
*/ */
private ECKey(BigInteger privKey, byte[] pubKey) { private ECKey(@Nullable BigInteger privKey, @Nullable byte[] pubKey) {
this(privKey, pubKey, false); this(privKey, pubKey, false);
} }
@ -381,7 +383,7 @@ public class ECKey implements Serializable {
r = (DERInteger) seq.getObjectAt(0); r = (DERInteger) seq.getObjectAt(0);
s = (DERInteger) seq.getObjectAt(1); s = (DERInteger) seq.getObjectAt(1);
} catch (ClassCastException e) { } catch (ClassCastException e) {
return null; throw new IllegalArgumentException(e);
} }
decoder.close(); decoder.close();
// OpenSSL deviates from the DER spec by interpreting these values as unsigned, though they should not be // 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. * @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. * @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) if (FAKE_SIGNATURES)
return TransactionSignature.dummy(); return TransactionSignature.dummy();
@ -601,7 +603,7 @@ public class ECKey implements Serializable {
* @throws IllegalStateException if this ECKey does not have the private part. * @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. * @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) if (priv == null)
throw new IllegalStateException("This ECKey does not have the private key necessary for signing."); throw new IllegalStateException("This ECKey does not have the private key necessary for signing.");
byte[] data = Utils.formatMessageForSigning(message); byte[] data = Utils.formatMessageForSigning(message);
@ -702,6 +704,7 @@ public class ECKey implements Serializable {
* @param compressed Whether or not the original pubkey was compressed. * @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. * @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) { public static ECKey recoverFromSignature(int recId, ECDSASignature sig, Sha256Hash message, boolean compressed) {
Preconditions.checkArgument(recId >= 0, "recId must be positive"); Preconditions.checkArgument(recId >= 0, "recId must be positive");
Preconditions.checkArgument(sig.r.compareTo(BigInteger.ZERO) >= 0, "r must be positive"); Preconditions.checkArgument(sig.r.compareTo(BigInteger.ZERO) >= 0, "r must be positive");

View File

@ -22,6 +22,7 @@ import com.google.bitcoin.store.FullPrunedBlockStore;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
@ -124,6 +125,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
this.tx = tx; this.prevOutScripts = prevOutScripts; this.enforcePayToScriptHash = enforcePayToScriptHash; this.tx = tx; this.prevOutScripts = prevOutScripts; this.enforcePayToScriptHash = enforcePayToScriptHash;
} }
@Nullable
@Override @Override
public VerificationException call() throws Exception { public VerificationException call() throws Exception {
try{ try{

View File

@ -380,7 +380,7 @@ public abstract class Message implements Serializable {
* so BitcoinSerializer can avoid 2 instanceof checks + a casting. * so BitcoinSerializer can avoid 2 instanceof checks + a casting.
*/ */
public Sha256Hash getHash() { 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. * 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. * This default implementation is a safe fall back that will ensure it returns a correct value by parsing the message.
*
* @return
*/ */
public int getMessageSize() { public int getMessageSize() {
if (length != UNKNOWN_LENGTH) if (length != UNKNOWN_LENGTH)

View File

@ -22,6 +22,7 @@ import com.google.bitcoin.script.ScriptOpCodes;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigInteger; 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. */ /** Returns the network parameters for the given string ID or NULL if not recognized. */
@Nullable
public static NetworkParameters fromID(String id) { public static NetworkParameters fromID(String id) {
if (id.equals(ID_MAINNET)) { if (id.equals(ID_MAINNET)) {
return MainNetParams.get(); return MainNetParams.get();

View File

@ -16,6 +16,7 @@
package com.google.bitcoin.core; package com.google.bitcoin.core;
import javax.annotation.Nullable;
import java.util.List; import java.util.List;
/** /**
@ -85,5 +86,6 @@ public interface PeerEventListener {
* <p>Note that this will never be called if registered with any executor other than * <p>Note that this will never be called if registered with any executor other than
* {@link com.google.bitcoin.utils.Threading#SAME_THREAD}</p> * {@link com.google.bitcoin.utils.Threading#SAME_THREAD}</p>
*/ */
@Nullable
public List<Message> getData(Peer peer, GetDataMessage m); public List<Message> getData(Peer peer, GetDataMessage m);
} }

View File

@ -149,14 +149,6 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
private LinkedBlockingQueue<Object> morePeersMailbox = new LinkedBlockingQueue<Object>(); private LinkedBlockingQueue<Object> morePeersMailbox = new LinkedBlockingQueue<Object>();
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 { private class PeerStartupListener extends AbstractPeerEventListener {
@Override @Override
public void onPeerConnected(Peer peer, int peerCount) { public void onPeerConnected(Peer peer, int peerCount) {

View File

@ -21,7 +21,6 @@ import com.google.bitcoin.crypto.TransactionSignature;
import com.google.bitcoin.script.Script; import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder; import com.google.bitcoin.script.ScriptBuilder;
import com.google.bitcoin.script.ScriptOpCodes; import com.google.bitcoin.script.ScriptOpCodes;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -35,6 +34,7 @@ import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import static com.google.bitcoin.core.Utils.*; import static com.google.bitcoin.core.Utils.*;
import static com.google.common.base.Preconditions.*;
/** /**
* <p>A transaction represents the movement of coins from some addresses to some other addresses. It can also represent * <p>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 * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException * @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 { throws ProtocolException {
super(params, msg, offset, parent, parseLazy, parseRetain, length); 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. * 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 { throws ProtocolException {
super(params, msg, 0, parent, parseLazy, parseRetain, length); super(params, msg, 0, parent, parseLazy, parseRetain, length);
} }
@ -363,29 +363,6 @@ public class Transaction extends ChildMessage implements Serializable {
return disconnected; 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<Sha256Hash, Transaction> 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. * 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. * @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 { public synchronized void signInputs(SigHash hashType, Wallet wallet, @Nullable KeyParameter aesKey) throws ScriptException {
Preconditions.checkState(inputs.size() > 0); checkState(inputs.size() > 0);
Preconditions.checkState(outputs.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. // 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 // 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 // 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. // Find the signing key we'll need to use.
ECKey key = input.getOutpoint().getConnectedKey(wallet); ECKey key = input.getOutpoint().getConnectedKey(wallet);
// This assert should never fire. If it does, it means the wallet is inconsistent. // 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", checkNotNull(key, "Transaction exists in wallet that we cannot redeem: %s", input.getOutpoint().getHash());
input.getOutpoint().getHash());
// Keep the key around for the script creation step below. // Keep the key around for the script creation step below.
signingKeys[i] = key; signingKeys[i] = key;
// The anyoneCanPay feature isn't used at the moment. // The anyoneCanPay feature isn't used at the moment.
@ -858,7 +834,9 @@ public class Transaction extends ChildMessage implements Serializable {
if (signatures[i] == null) if (signatures[i] == null)
continue; continue;
TransactionInput input = inputs.get(i); 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()) { if (scriptPubKey.isSentToAddress()) {
input.setScriptSig(ScriptBuilder.createInputScript(signatures[i], signingKeys[i])); input.setScriptSig(ScriptBuilder.createInputScript(signatures[i], signingKeys[i]));
} else if (scriptPubKey.isSentToRawPubKey()) { } 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. * @param anyoneCanPay Signing mode, see the SigHash enum for documentation.
* @return A newly calculated signature object that wraps the r, s and sighash components. * @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, byte[] connectedPubKeyScript,
SigHash hashType, boolean anyoneCanPay) { SigHash hashType, boolean anyoneCanPay) {
Sha256Hash hash = hashForSignature(inputIndex, connectedPubKeyScript, hashType, anyoneCanPay); Sha256Hash hash = hashForSignature(inputIndex, connectedPubKeyScript, hashType, anyoneCanPay);

View File

@ -22,6 +22,7 @@ import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import javax.annotation.Nullable;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ListIterator; import java.util.ListIterator;
@ -111,17 +112,6 @@ public class TransactionConfidence implements Serializable {
public int getValue() { public int getValue() {
return value; 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; 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 * 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. * 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; this.overridingTransaction = overridingTransaction;
setConfidenceType(ConfidenceType.DEAD); setConfidenceType(ConfidenceType.DEAD);
} }

View File

@ -19,6 +19,7 @@ package com.google.bitcoin.core;
import com.google.bitcoin.script.Script; import com.google.bitcoin.script.Script;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.OutputStream; 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); length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
} }
public TransactionInput(NetworkParameters params, Transaction parentTransaction, public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes,
byte[] scriptBytes,
TransactionOutPoint outpoint) { TransactionOutPoint outpoint) {
super(params); super(params);
this.scriptBytes = scriptBytes; this.scriptBytes = scriptBytes;
this.outpoint = outpoint; this.outpoint = outpoint;
this.sequence = NO_SEQUENCE; this.sequence = NO_SEQUENCE;
this.parentTransaction = parentTransaction; this.parentTransaction = parentTransaction;
length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length); 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. * @return The TransactionOutput or null if the transactions map doesn't contain the referenced tx.
*/ */
@Nullable
TransactionOutput getConnectedOutput(Map<Sha256Hash, Transaction> transactions) { TransactionOutput getConnectedOutput(Map<Sha256Hash, Transaction> transactions) {
Transaction tx = transactions.get(outpoint.getHash()); Transaction tx = transactions.get(outpoint.getHash());
if (tx == null) if (tx == null)
return null; return null;
TransactionOutput out = tx.getOutputs().get((int) outpoint.getIndex()); return tx.getOutputs().get((int) outpoint.getIndex());
return out;
} }
public enum ConnectMode { 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 * {@link TransactionInput#connect(TransactionOutput)} or variants at some point. If it wasn't connected, then
* this method returns null. * this method returns null.
*/ */
@Nullable
public TransactionOutput getConnectedOutput() { public TransactionOutput getConnectedOutput() {
return getOutpoint().getConnectedOutput(); return getOutpoint().getConnectedOutput();
} }

View File

@ -18,6 +18,7 @@ package com.google.bitcoin.core;
import com.google.bitcoin.script.Script; import com.google.bitcoin.script.Script;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -43,7 +44,7 @@ public class TransactionOutPoint extends ChildMessage implements Serializable {
// It points to the connected transaction. // It points to the connected transaction.
Transaction fromTx; Transaction fromTx;
public TransactionOutPoint(NetworkParameters params, long index, Transaction fromTx) { public TransactionOutPoint(NetworkParameters params, long index, @Nullable Transaction fromTx) {
super(params); super(params);
this.index = index; this.index = index;
if (fromTx != null) { 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 * 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. * if there is no such connection.
*/ */
@Nullable
public TransactionOutput getConnectedOutput() { public TransactionOutput getConnectedOutput() {
if (fromTx == null) return null; if (fromTx == null) return null;
return fromTx.getOutputs().get((int) index); 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. * Returns the pubkey script from the connected output.
* @throws java.lang.NullPointerException if there is no connected output.
*/ */
byte[] getConnectedPubKeyScript() { byte[] getConnectedPubKeyScript() {
byte[] result = checkNotNull(getConnectedOutput().getScriptBytes()); byte[] result = checkNotNull(getConnectedOutput()).getScriptBytes();
checkState(result.length > 0); checkState(result.length > 0);
return result; 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. * 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. * If the script forms cannot be understood, throws ScriptException.
* @return an ECKey or null if the connected key cannot be found in the wallet. * @return an ECKey or null if the connected key cannot be found in the wallet.
*/ */
@Nullable
public ECKey getConnectedKey(Wallet wallet) throws ScriptException { public ECKey getConnectedKey(Wallet wallet) throws ScriptException {
TransactionOutput connectedOutput = getConnectedOutput(); TransactionOutput connectedOutput = getConnectedOutput();
checkNotNull(connectedOutput, "Input is not connected so cannot retrieve key"); checkNotNull(connectedOutput, "Input is not connected so cannot retrieve key");

View File

@ -28,9 +28,7 @@ import java.io.OutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigInteger; import java.math.BigInteger;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.*;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/** /**
* A TransactionOutput message contains a scriptPubKey that controls who is able to spend its value. It is a sub-part * 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. * 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 { int offset) throws ProtocolException {
super(params, payload, offset); super(params, payload, offset);
parentTransaction = parent; 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() { public Transaction getParentTransaction() {
return parentTransaction; return checkNotNull(parentTransaction, "Free-standing TransactionOutput");
} }
/** /**

View File

@ -218,7 +218,7 @@ public class VersionMessage extends Message {
*/ */
@Override @Override
byte[] getChecksum() { byte[] getChecksum() {
return null; throw new UnsupportedOperationException();
} }
/** /**
@ -226,6 +226,7 @@ public class VersionMessage extends Message {
*/ */
@Override @Override
void setChecksum(byte[] checksum) { void setChecksum(byte[] checksum) {
throw new UnsupportedOperationException();
} }
@Override @Override

View File

@ -137,7 +137,7 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
private final NetworkParameters params; private final NetworkParameters params;
private Sha256Hash lastBlockSeenHash; @Nullable private Sha256Hash lastBlockSeenHash;
private int lastBlockSeenHeight; private int lastBlockSeenHeight;
private long lastBlockSeenTimeSecs; 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() { public Sha256Hash getLastBlockSeenHash() {
lock.lock(); lock.lock();
try { 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(); lock.lock();
try { try {
this.lastBlockSeenHash = lastBlockSeenHash; this.lastBlockSeenHash = lastBlockSeenHash;

View File

@ -156,9 +156,12 @@ public class TransactionSignature extends ECKey.ECDSASignature {
// Bitcoin encoding is DER signature + sighash byte. // Bitcoin encoding is DER signature + sighash byte.
if (requireCanonical && !isEncodingCanonical(bytes)) if (requireCanonical && !isEncodingCanonical(bytes))
throw new VerificationException("Signature encoding is not canonical."); throw new VerificationException("Signature encoding is not canonical.");
ECKey.ECDSASignature sig = ECKey.ECDSASignature.decodeFromDER(bytes); ECKey.ECDSASignature sig;
if (sig == null) try {
throw new VerificationException("Could not decode DER component."); sig = ECKey.ECDSASignature.decodeFromDER(bytes);
} catch (IllegalArgumentException e) {
throw new VerificationException("Could not decode DER", e);
}
TransactionSignature tsig = new TransactionSignature(sig.r, sig.s); 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 // In Bitcoin, any value of the final byte is valid, but not necessarily canonical. See javadocs for
// isEncodingCanonical to learn more about this. // isEncodingCanonical to learn more about this.

View File

@ -18,7 +18,6 @@ package com.google.bitcoin.net;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; 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_LOWER_BOUND = 4096;
private static final int BUFFER_SIZE_UPPER_BOUND = 65536; private static final int BUFFER_SIZE_UPPER_BOUND = 65536;
@Nonnull private final ByteBuffer dbuf; private final ByteBuffer dbuf;
@Nonnull private final Socket socket; private final Socket socket;
private volatile boolean vCloseRequested = false; private volatile boolean vCloseRequested = false;
/** /**

View File

@ -21,6 +21,7 @@ import com.google.bitcoin.utils.Threading;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -54,7 +55,7 @@ class ConnectionHandler implements MessageWriteTarget {
@GuardedBy("lock") private final ByteBuffer readBuff; @GuardedBy("lock") private final ByteBuffer readBuff;
@GuardedBy("lock") private final SocketChannel channel; @GuardedBy("lock") private final SocketChannel channel;
@GuardedBy("lock") private final SelectionKey key; @GuardedBy("lock") private final SelectionKey key;
@GuardedBy("lock") final StreamParser parser; @GuardedBy("lock") StreamParser parser;
@GuardedBy("lock") private boolean closeCalled = false; @GuardedBy("lock") private boolean closeCalled = false;
@GuardedBy("lock") private long bytesToWriteRemaining = 0; @GuardedBy("lock") private long bytesToWriteRemaining = 0;
@ -68,15 +69,15 @@ class ConnectionHandler implements MessageWriteTarget {
throw new IOException("Parser factory.getNewParser returned null"); 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.key = key;
this.channel = checkNotNull(((SocketChannel)key.channel())); this.channel = checkNotNull(((SocketChannel)key.channel()));
this.parser = parser;
if (parser == null) { if (parser == null) {
readBuff = null; readBuff = null;
closeConnection(); closeConnection();
return; return;
} }
this.parser = parser;
readBuff = ByteBuffer.allocateDirect(Math.min(Math.max(parser.getMaxMessageSize(), BUFFER_SIZE_LOWER_BOUND), BUFFER_SIZE_UPPER_BOUND)); 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) parser.setWriteTarget(this); // May callback into us (eg closeConnection() now)
connectedHandlers = null; 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 // "flip" the buffer - setting the limit to the current position and setting position to 0
handler.readBuff.flip(); handler.readBuff.flip();
// Use parser.receiveBytes's return value as a check that it stopped reading at the right location // 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); checkState(handler.readBuff.position() == bytesConsumed);
// Now drop the bytes which were read by compacting readBuff (resetting limit and keeping relative // Now drop the bytes which were read by compacting readBuff (resetting limit and keeping relative
// position) // position)

View File

@ -17,6 +17,7 @@ package com.google.bitcoin.net.discovery;
import com.google.bitcoin.core.NetworkParameters; import com.google.bitcoin.core.NetworkParameters;
import javax.annotation.Nullable;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -42,6 +43,7 @@ public class SeedPeers implements PeerDiscovery {
* @return InetSocketAddress - The address/port of the next node. * @return InetSocketAddress - The address/port of the next node.
* @throws PeerDiscoveryException * @throws PeerDiscoveryException
*/ */
@Nullable
public InetSocketAddress getPeer() throws PeerDiscoveryException { public InetSocketAddress getPeer() throws PeerDiscoveryException {
try { try {
return nextPeer(); return nextPeer();
@ -50,6 +52,7 @@ public class SeedPeers implements PeerDiscovery {
} }
} }
@Nullable
private InetSocketAddress nextPeer() throws UnknownHostException { private InetSocketAddress nextPeer() throws UnknownHostException {
if (pnseedIndex >= seedAddrs.length) return null; if (pnseedIndex >= seedAddrs.length) return null;
return new InetSocketAddress(convertAddress(seedAddrs[pnseedIndex++]), return new InetSocketAddress(convertAddress(seedAddrs[pnseedIndex++]),

View File

@ -16,14 +16,6 @@
package com.google.bitcoin.protocols.channels; 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.Sha256Hash;
import com.google.bitcoin.core.TransactionBroadcaster; import com.google.bitcoin.core.TransactionBroadcaster;
import com.google.bitcoin.core.Wallet; import com.google.bitcoin.core.Wallet;
@ -32,6 +24,13 @@ import com.google.bitcoin.net.ProtobufParser;
import com.google.bitcoin.net.StreamParserFactory; import com.google.bitcoin.net.StreamParserFactory;
import org.bitcoin.paymentchannel.Protos; 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; import static com.google.common.base.Preconditions.checkNotNull;
/** /**
@ -120,7 +119,7 @@ public class PaymentChannelServerListener {
private PaymentChannelCloseException.CloseReason closeReason; private PaymentChannelCloseException.CloseReason closeReason;
// The user-provided event handler // The user-provided event handler
@Nonnull private ServerConnectionEventHandler eventHandler; private ServerConnectionEventHandler eventHandler;
// The payment channel server which does the actual payment channel handling // The payment channel server which does the actual payment channel handling
private final PaymentChannelServer paymentChannelManager; private final PaymentChannelServer paymentChannelManager;

View File

@ -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 // 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) // disables our double-spend check next)
Transaction walletContract = wallet.getTransaction(multisigContract.getHash()); 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 // 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 // miss most double-spends due to bloom filtering right now anyway. This will eventually fixed by network-wide

View File

@ -22,6 +22,8 @@ import com.google.bitcoin.core.Sha256Hash;
import com.google.bitcoin.net.ProtobufParser; import com.google.bitcoin.net.ProtobufParser;
import org.bitcoin.paymentchannel.Protos; import org.bitcoin.paymentchannel.Protos;
import javax.annotation.Nullable;
/** /**
* A connection-specific event handler that handles events generated by client connections on a * A connection-specific event handler that handles events generated by client connections on a
* {@link PaymentChannelServerListener} * {@link PaymentChannelServerListener}
@ -30,7 +32,7 @@ public abstract class ServerConnectionEventHandler {
private ProtobufParser connectionChannel; private ProtobufParser connectionChannel;
// Called by ServerListener before channelOpen to set connectionChannel when it is ready to received application messages // 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() // 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; }
/** /**
* <p>Closes the channel with the client (will generate a * <p>Closes the channel with the client (will generate a

View File

@ -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. * Finds a channel with the given id and contract hash and returns it, or returns null.
*/ */
@Nullable
StoredClientChannel getChannel(Sha256Hash id, Sha256Hash contractHash) { StoredClientChannel getChannel(Sha256Hash id, Sha256Hash contractHash) {
lock.lock(); lock.lock();
try { try {

View File

@ -18,6 +18,7 @@ package com.google.bitcoin.protocols.channels;
import com.google.bitcoin.core.*; import com.google.bitcoin.core.*;
import javax.annotation.Nullable;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Date; import java.util.Date;
@ -41,8 +42,8 @@ public class StoredServerChannel {
private PaymentChannelServer connectedHandler = null; private PaymentChannelServer connectedHandler = null;
PaymentChannelServerState state = null; PaymentChannelServerState state = null;
StoredServerChannel(PaymentChannelServerState state, Transaction contract, TransactionOutput clientOutput, StoredServerChannel(@Nullable PaymentChannelServerState state, Transaction contract, TransactionOutput clientOutput,
long refundTransactionUnlockTimeSecs, ECKey myKey, BigInteger bestValueToMe, byte[] bestValueSignature) { long refundTransactionUnlockTimeSecs, ECKey myKey, BigInteger bestValueToMe, @Nullable byte[] bestValueSignature) {
this.contract = contract; this.contract = contract;
this.clientOutput = clientOutput; this.clientOutput = clientOutput;
this.refundTransactionUnlockTimeSecs = refundTransactionUnlockTimeSecs; this.refundTransactionUnlockTimeSecs = refundTransactionUnlockTimeSecs;

View File

@ -21,6 +21,7 @@ import com.google.common.collect.Lists;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -458,6 +459,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
} }
} }
@Nullable
public StoredBlock get(Sha256Hash hash, boolean wasUndoableOnly) throws BlockStoreException { public StoredBlock get(Sha256Hash hash, boolean wasUndoableOnly) throws BlockStoreException {
// Optimize for chain head // Optimize for chain head
if (chainHeadHash != null && chainHeadHash.equals(hash)) if (chainHeadHash != null && chainHeadHash.equals(hash))
@ -467,8 +469,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
maybeConnect(); maybeConnect();
PreparedStatement s = null; PreparedStatement s = null;
try { try {
s = conn.get() s = conn.get().prepareStatement("SELECT chainWork, height, header, wasUndoable FROM headers WHERE hash = ?");
.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 // We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
byte[] hashBytes = new byte[28]; byte[] hashBytes = new byte[28];
System.arraycopy(hash.getBytes(), 3, hashBytes, 0, 28); System.arraycopy(hash.getBytes(), 3, hashBytes, 0, 28);
@ -478,16 +479,13 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
return null; return null;
} }
// Parse it. // Parse it.
if (wasUndoableOnly && !results.getBoolean(4)) if (wasUndoableOnly && !results.getBoolean(4))
return null; return null;
BigInteger chainWork = new BigInteger(results.getBytes(1)); BigInteger chainWork = new BigInteger(results.getBytes(1));
int height = results.getInt(2); int height = results.getInt(2);
Block b = new Block(params, results.getBytes(3)); Block b = new Block(params, results.getBytes(3));
b.verifyHeader(); b.verifyHeader();
StoredBlock stored = new StoredBlock(b, chainWork, height); return new StoredBlock(b, chainWork, height);
return stored;
} catch (SQLException ex) { } catch (SQLException ex) {
throw new BlockStoreException(ex); throw new BlockStoreException(ex);
} catch (ProtocolException e) { } catch (ProtocolException e) {
@ -498,21 +496,27 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
// blocks. // blocks.
throw new BlockStoreException(e); throw new BlockStoreException(e);
} finally { } finally {
if (s != null) if (s != null) {
try { try {
s.close(); 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 { public StoredBlock get(Sha256Hash hash) throws BlockStoreException {
return get(hash, false); return get(hash, false);
} }
@Nullable
public StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException { public StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException {
return get(hash, true); return get(hash, true);
} }
@Nullable
public StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException { public StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException {
maybeConnect(); maybeConnect();
PreparedStatement s = null; PreparedStatement s = null;
@ -629,6 +633,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
} }
} }
@Nullable
public StoredTransactionOutput getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException { public StoredTransactionOutput getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException {
maybeConnect(); maybeConnect();
PreparedStatement s = null; PreparedStatement s = null;
@ -647,8 +652,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
int height = results.getInt(1); int height = results.getInt(1);
BigInteger value = new BigInteger(results.getBytes(2)); BigInteger value = new BigInteger(results.getBytes(2));
// Tell the StoredTransactionOutput that we are a coinbase, as that is encoded in height // 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 new StoredTransactionOutput(hash, index, value, height, true, results.getBytes(3));
return txout;
} catch (SQLException ex) { } catch (SQLException ex) {
throw new BlockStoreException(ex); throw new BlockStoreException(ex);
} finally { } finally {

View File

@ -21,99 +21,10 @@ import com.google.common.base.Objects;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import javax.annotation.Nullable;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; 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<StoredTransactionOutput> outputs;
private List<TransactionInput> inputs;
private long version;
private long lockTime;
private Sha256Hash hash;
public StoredTransaction(NetworkParameters params, Transaction tx, int height) {
inputs = new LinkedList<TransactionInput>();
outputs = new LinkedList<StoredTransactionOutput>();
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<TransactionInput> getInputs() {
return inputs;
}
/**
* The lits of outputs in this transaction
* Note that the hashes of all of these are null
*/
public List<StoredTransactionOutput> 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, * Used as a key for memory map (to avoid having to think about NetworkParameters,
* which is required for {@link TransactionOutPoint} * which is required for {@link TransactionOutPoint}
@ -203,6 +114,7 @@ class TransactionalHashMap<KeyType, ValueType> {
tempMap.remove(); tempMap.remove();
} }
@Nullable
public ValueType get(KeyType key) { public ValueType get(KeyType key) {
if (Boolean.TRUE.equals(inTransaction.get())) { if (Boolean.TRUE.equals(inTransaction.get())) {
if (tempMap.get() != null) { if (tempMap.get() != null) {
@ -228,6 +140,7 @@ class TransactionalHashMap<KeyType, ValueType> {
} }
} }
@Nullable
public ValueType remove(KeyType key) { public ValueType remove(KeyType key) {
if (Boolean.TRUE.equals(inTransaction.get())) { if (Boolean.TRUE.equals(inTransaction.get())) {
ValueType retVal = map.get(key); ValueType retVal = map.get(key);
@ -275,6 +188,7 @@ class TransactionalMultiKeyHashMap<UniqueKeyType, MultiKeyType, ValueType> {
mapValues.abortDatabaseBatchWrite(); mapValues.abortDatabaseBatchWrite();
} }
@Nullable
public ValueType get(UniqueKeyType key) { public ValueType get(UniqueKeyType key) {
return mapValues.get(key); return mapValues.get(key);
} }
@ -291,6 +205,7 @@ class TransactionalMultiKeyHashMap<UniqueKeyType, MultiKeyType, ValueType> {
} }
} }
@Nullable
public ValueType removeByUniqueKey(UniqueKeyType key) { public ValueType removeByUniqueKey(UniqueKeyType key) {
return mapValues.remove(key); return mapValues.remove(key);
} }
@ -360,18 +275,21 @@ public class MemoryFullPrunedBlockStore implements FullPrunedBlockStore {
blockMap.put(hash, new StoredBlockAndWasUndoableFlag(storedBlock, true)); blockMap.put(hash, new StoredBlockAndWasUndoableFlag(storedBlock, true));
} }
@Nullable
public synchronized StoredBlock get(Sha256Hash hash) throws BlockStoreException { public synchronized StoredBlock get(Sha256Hash hash) throws BlockStoreException {
Preconditions.checkNotNull(blockMap, "MemoryFullPrunedBlockStore is closed"); Preconditions.checkNotNull(blockMap, "MemoryFullPrunedBlockStore is closed");
StoredBlockAndWasUndoableFlag storedBlock = blockMap.get(hash); StoredBlockAndWasUndoableFlag storedBlock = blockMap.get(hash);
return storedBlock == null ? null : storedBlock.block; return storedBlock == null ? null : storedBlock.block;
} }
@Nullable
public synchronized StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException { public synchronized StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException {
Preconditions.checkNotNull(blockMap, "MemoryFullPrunedBlockStore is closed"); Preconditions.checkNotNull(blockMap, "MemoryFullPrunedBlockStore is closed");
StoredBlockAndWasUndoableFlag storedBlock = blockMap.get(hash); StoredBlockAndWasUndoableFlag storedBlock = blockMap.get(hash);
return (storedBlock != null && storedBlock.wasUndoable) ? storedBlock.block : null; return (storedBlock != null && storedBlock.wasUndoable) ? storedBlock.block : null;
} }
@Nullable
public synchronized StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException { public synchronized StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException {
Preconditions.checkNotNull(fullBlockMap, "MemoryFullPrunedBlockStore is closed"); Preconditions.checkNotNull(fullBlockMap, "MemoryFullPrunedBlockStore is closed");
return fullBlockMap.get(hash); return fullBlockMap.get(hash);
@ -408,6 +326,7 @@ public class MemoryFullPrunedBlockStore implements FullPrunedBlockStore {
transactionOutputMap = null; transactionOutputMap = null;
} }
@Nullable
public synchronized StoredTransactionOutput getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException { public synchronized StoredTransactionOutput getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException {
Preconditions.checkNotNull(transactionOutputMap, "MemoryFullPrunedBlockStore is closed"); Preconditions.checkNotNull(transactionOutputMap, "MemoryFullPrunedBlockStore is closed");
return transactionOutputMap.get(new StoredTransactionOutPoint(hash, index)); return transactionOutputMap.get(new StoredTransactionOutPoint(hash, index));

View File

@ -21,6 +21,7 @@ import com.google.bitcoin.utils.Threading;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; 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 // We don't care about the value in this cache. It is always notFoundMarker. Unfortunately LinkedHashSet does not
// provide the removeEldestEntry control. // provide the removeEldestEntry control.
protected static final StoredBlock notFoundMarker = new StoredBlock(null, null, -1); protected static final Object notFoundMarker = new Object();
protected LinkedHashMap<Sha256Hash, StoredBlock> notFoundCache = new LinkedHashMap<Sha256Hash, StoredBlock>() { protected LinkedHashMap<Sha256Hash, Object> notFoundCache = new LinkedHashMap<Sha256Hash, Object>() {
@Override @Override
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, StoredBlock> entry) { protected boolean removeEldestEntry(Map.Entry<Sha256Hash, Object> entry) {
return size() > 100; // This was chosen arbitrarily. return size() > 100; // This was chosen arbitrarily.
} }
}; };
@ -181,6 +182,7 @@ public class SPVBlockStore implements BlockStore {
} finally { lock.unlock(); } } finally { lock.unlock(); }
} }
@Nullable
public StoredBlock get(Sha256Hash hash) throws BlockStoreException { public StoredBlock get(Sha256Hash hash) throws BlockStoreException {
final MappedByteBuffer buffer = this.buffer; final MappedByteBuffer buffer = this.buffer;
if (buffer == null) throw new BlockStoreException("Store closed"); if (buffer == null) throw new BlockStoreException("Store closed");

View File

@ -364,7 +364,10 @@ public class WalletProtobufSerializer {
public Wallet readWallet(InputStream input) throws UnreadableWalletException { public Wallet readWallet(InputStream input) throws UnreadableWalletException {
try { try {
Protos.Wallet walletProto = parseToProto(input); 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); Wallet wallet = new Wallet(params);
readWallet(walletProto, wallet); readWallet(walletProto, wallet);
return wallet; return wallet;

View File

@ -25,6 +25,8 @@ import org.slf4j.LoggerFactory;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.LinkedList; 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. * 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)}. * 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); log.info("Skipping tx output {} because it's not of simple form.", output);
continue; continue;
} }
checkNotNull(controllingKey, "Coin selector given output as candidate for which we lack the key");
if (controllingKey.getCreationTimeSeconds() >= unixTimeSeconds) continue; if (controllingKey.getCreationTimeSeconds() >= unixTimeSeconds) continue;
// It's older than the cutoff time so select. // It's older than the cutoff time so select.
valueGathered = valueGathered.add(output.getValue()); valueGathered = valueGathered.add(output.getValue());