diff --git a/core/src/main/java/com/google/bitcoin/core/Block.java b/core/src/main/java/com/google/bitcoin/core/Block.java index d9f858dd..88bd651f 100644 --- a/core/src/main/java/com/google/bitcoin/core/Block.java +++ b/core/src/main/java/com/google/bitcoin/core/Block.java @@ -712,7 +712,9 @@ public class Block extends Message { checkTransactions(); checkMerkleRoot(); checkSigOps(); - } + for (Transaction transaction : transactions) + transaction.verify(); + } /** * Verifies both the header and that the transactions hash to the merkle root. @@ -869,7 +871,7 @@ public class Block extends Message { // // Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple // counter in the scriptSig so every transaction has a different hash. - coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++})); + coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++, (byte) 1})); coinbase.addOutput(new TransactionOutput(params, coinbase, Script.createOutputScript(pubKeyTo), Utils.toNanoCoins(50, 0))); transactions.add(coinbase); 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 30b24347..d1159d1e 100644 --- a/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java +++ b/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java @@ -24,6 +24,7 @@ import java.math.BigInteger; import java.util.HashMap; import java.util.Map; +import static com.google.bitcoin.core.Utils.COIN; import static com.google.common.base.Preconditions.checkState; // TODO: Refactor this after we stop supporting serialization compatibility to use subclasses and singletons. @@ -151,6 +152,11 @@ public class NetworkParameters implements Serializable { public static final int TARGET_TIMESPAN = 14 * 24 * 60 * 60; // 2 weeks per difficulty cycle, on average. public static final int TARGET_SPACING = 10 * 60; // 10 minutes per block. public static final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING; + + /** + * The maximum money to be generated + */ + public final BigInteger MAX_MONEY = new BigInteger("21000000", 10).multiply(COIN); /** Sets up the given Networkparemets with testnet3 values. */ private static NetworkParameters createTestNet3(NetworkParameters n) { 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 f7209322..876ef728 100644 --- a/core/src/main/java/com/google/bitcoin/core/Transaction.java +++ b/core/src/main/java/com/google/bitcoin/core/Transaction.java @@ -882,4 +882,36 @@ public class Transaction extends ChildMessage implements Serializable { sigOps += Script.getSigOpCount(output.getScriptBytes()); return sigOps; } + + /** + * Checks the transaction contents for sanity, in ways that can be done in a standalone manner. + * Does not perform all checks on a transaction such as whether the inputs are already spent. + * + * @throws VerificationException + */ + public void verify() throws VerificationException { + maybeParse(); + if (inputs.size() == 0 || outputs.size() == 0) + throw new VerificationException("Transaction had no inputs or no outputs."); + if (this.getMessageSize() > Block.MAX_BLOCK_SIZE) + throw new VerificationException("Transaction larger than MAX_BLOCK_SIZE"); + + BigInteger valueOut = BigInteger.ZERO; + for (TransactionOutput output : outputs) { + if (output.getValue().compareTo(BigInteger.ZERO) < 0) + throw new VerificationException("Transaction output negative"); + valueOut = valueOut.add(output.getValue()); + } + if (valueOut.compareTo(params.MAX_MONEY) > 0) + throw new VerificationException("Total transaction output value greater than possible"); + + if (isCoinBase()) { + if (inputs.get(0).getScriptBytes().length < 2 || inputs.get(0).getScriptBytes().length > 100) + throw new VerificationException("Coinbase script size out of range"); + } else { + for (TransactionInput input : inputs) + if (input.isCoinBase()) + throw new VerificationException("Coinbase input as input in non-coinbase transaction"); + } + } }