diff --git a/core/src/main/java/org/bitcoinj/core/Block.java b/core/src/main/java/org/bitcoinj/core/Block.java
index be1719acf..622234339 100644
--- a/core/src/main/java/org/bitcoinj/core/Block.java
+++ b/core/src/main/java/org/bitcoinj/core/Block.java
@@ -684,8 +684,8 @@ public class Block extends Message {
checkTransactions(height, flags);
checkMerkleRoot();
checkSigOps();
- for (Transaction transaction : transactions)
- transaction.verify();
+ for (Transaction tx : transactions)
+ Transaction.verify(params, tx);
}
/**
diff --git a/core/src/main/java/org/bitcoinj/core/Peer.java b/core/src/main/java/org/bitcoinj/core/Peer.java
index de516bccf..8eb954334 100644
--- a/core/src/main/java/org/bitcoinj/core/Peer.java
+++ b/core/src/main/java/org/bitcoinj/core/Peer.java
@@ -713,7 +713,7 @@ public class Peer extends PeerSocketHandler {
protected void processTransaction(final Transaction tx) throws VerificationException {
// Check a few basic syntax issues to ensure the received TX isn't nonsense.
- tx.verify();
+ tx.verify(params, tx);
lock.lock();
try {
if (log.isDebugEnabled())
diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java
index 9c3c08de1..65d3ff0cb 100644
--- a/core/src/main/java/org/bitcoinj/core/Transaction.java
+++ b/core/src/main/java/org/bitcoinj/core/Transaction.java
@@ -1727,59 +1727,6 @@ public class Transaction extends Message {
.orElse(null);
}
- /**
- *
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.
- * Specifically this method verifies:
- *
- *
- * - That there is at least one input and output.
- * - That the serialized size is not larger than the max block size.
- * - That no outputs have negative value.
- * - That the outputs do not sum to larger than the max allowed quantity of coin in the system.
- * - If the tx is a coinbase tx, the coinbase scriptSig size is within range. Otherwise that there are no
- * coinbase inputs in the tx.
- *
- *
- * @throws VerificationException
- */
- public void verify() throws VerificationException {
- if (inputs.size() == 0 || outputs.size() == 0)
- throw new VerificationException.EmptyInputsOrOutputs();
- if (this.getMessageSize() > Block.MAX_BLOCK_SIZE)
- throw new VerificationException.LargerThanMaxBlockSize();
-
- HashSet outpoints = new HashSet<>();
- for (TransactionInput input : inputs) {
- if (outpoints.contains(input.getOutpoint()))
- throw new VerificationException.DuplicatedOutPoint();
- outpoints.add(input.getOutpoint());
- }
-
- Coin valueOut = Coin.ZERO;
- for (TransactionOutput output : outputs) {
- Coin value = output.getValue();
- if (value.signum() < 0)
- throw new VerificationException.NegativeValueOutput();
- try {
- valueOut = valueOut.add(value);
- } catch (ArithmeticException e) {
- throw new VerificationException.ExcessiveValue();
- }
- if (params.network().exceedsMaxMoney(valueOut))
- throw new VerificationException.ExcessiveValue();
- }
-
- if (isCoinBase()) {
- if (inputs.get(0).getScriptBytes().length < 2 || inputs.get(0).getScriptBytes().length > 100)
- throw new VerificationException.CoinbaseScriptSizeOutOfRange();
- } else {
- for (TransactionInput input : inputs)
- if (input.isCoinBase())
- throw new VerificationException.UnexpectedCoinbaseInput();
- }
- }
-
/**
* A transaction is time-locked if at least one of its inputs is non-final and it has a lock time. A transaction can
* also have a relative lock time which this method doesn't tell. Use {@link #hasRelativeLockTime()} to find out.
@@ -1899,4 +1846,59 @@ public class Transaction extends Message {
public void setMemo(String memo) {
this.memo = memo;
}
+
+ /**
+ * 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.
+ * Specifically this method verifies:
+ *
+ *
+ * - That there is at least one input and output.
+ * - That the serialized size is not larger than the max block size.
+ * - That no outputs have negative value.
+ * - That the outputs do not sum to larger than the max allowed quantity of coin in the system.
+ * - If the tx is a coinbase tx, the coinbase scriptSig size is within range. Otherwise that there are no
+ * coinbase inputs in the tx.
+ *
+ *
+ * @param params parameters for the verification rules
+ * @param tx transaction to verify
+ * @throws VerificationException if at least one of the rules is violated
+ */
+ public static void verify(NetworkParameters params, Transaction tx) throws VerificationException {
+ if (tx.inputs.size() == 0 || tx.outputs.size() == 0)
+ throw new VerificationException.EmptyInputsOrOutputs();
+ if (tx.getMessageSize() > Block.MAX_BLOCK_SIZE)
+ throw new VerificationException.LargerThanMaxBlockSize();
+
+ HashSet outpoints = new HashSet<>();
+ for (TransactionInput input : tx.inputs) {
+ if (outpoints.contains(input.getOutpoint()))
+ throw new VerificationException.DuplicatedOutPoint();
+ outpoints.add(input.getOutpoint());
+ }
+
+ Coin valueOut = Coin.ZERO;
+ for (TransactionOutput output : tx.outputs) {
+ Coin value = output.getValue();
+ if (value.signum() < 0)
+ throw new VerificationException.NegativeValueOutput();
+ try {
+ valueOut = valueOut.add(value);
+ } catch (ArithmeticException e) {
+ throw new VerificationException.ExcessiveValue();
+ }
+ if (params.network().exceedsMaxMoney(valueOut))
+ throw new VerificationException.ExcessiveValue();
+ }
+
+ if (tx.isCoinBase()) {
+ if (tx.inputs.get(0).getScriptBytes().length < 2 || tx.inputs.get(0).getScriptBytes().length > 100)
+ throw new VerificationException.CoinbaseScriptSizeOutOfRange();
+ } else {
+ for (TransactionInput input : tx.inputs)
+ if (input.isCoinBase())
+ throw new VerificationException.UnexpectedCoinbaseInput();
+ }
+ }
}
diff --git a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java
index b0a96659f..876789503 100644
--- a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java
+++ b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java
@@ -345,7 +345,6 @@ public class PaymentProtocol {
@Nullable List refundOutputs, @Nullable String memo, @Nullable byte[] merchantData) {
Protos.Payment.Builder builder = Protos.Payment.newBuilder();
for (Transaction transaction : transactions) {
- transaction.verify();
builder.addTransactions(ByteString.copyFrom(transaction.bitcoinSerialize()));
}
if (refundOutputs != null) {
diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java
index cba23ab89..82a3ece23 100644
--- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java
+++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java
@@ -2002,7 +2002,7 @@ public class Wallet extends BaseTaggableObject
// spend against one of our other pending transactions.
lock.lock();
try {
- tx.verify();
+ Transaction.verify(params, tx);
// Ignore it if we already know about this transaction. Receiving a pending transaction never moves it
// between pools.
EnumSet containingPools = getContainingPools(tx);
@@ -2782,7 +2782,7 @@ public class Wallet extends BaseTaggableObject
* @throws VerificationException If transaction fails to verify
*/
public boolean maybeCommitTx(Transaction tx) throws VerificationException {
- tx.verify();
+ Transaction.verify(params, tx);
lock.lock();
try {
if (pending.containsKey(tx.getTxId()))
diff --git a/core/src/test/java/org/bitcoinj/core/TransactionTest.java b/core/src/test/java/org/bitcoinj/core/TransactionTest.java
index 58717fa72..09b65713a 100644
--- a/core/src/test/java/org/bitcoinj/core/TransactionTest.java
+++ b/core/src/test/java/org/bitcoinj/core/TransactionTest.java
@@ -87,21 +87,21 @@ public class TransactionTest {
public void emptyOutputs() {
Transaction tx = FakeTxBuilder.createFakeTx(TESTNET);
tx.clearOutputs();
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.EmptyInputsOrOutputs.class)
public void emptyInputs() {
Transaction tx = FakeTxBuilder.createFakeTx(TESTNET);
tx.clearInputs();
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.LargerThanMaxBlockSize.class)
public void tooHuge() {
Transaction tx = FakeTxBuilder.createFakeTx(TESTNET);
tx.getInput(0).setScriptBytes(new byte[Block.MAX_BLOCK_SIZE]);
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.DuplicatedOutPoint.class)
@@ -110,14 +110,14 @@ public class TransactionTest {
TransactionInput input = tx.getInput(0);
input.setScriptBytes(new byte[1]);
tx.addInput(input);
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.NegativeValueOutput.class)
public void negativeOutput() {
Transaction tx = FakeTxBuilder.createFakeTx(TESTNET);
tx.getOutput(0).setValue(Coin.NEGATIVE_SATOSHI);
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.ExcessiveValue.class)
@@ -126,14 +126,14 @@ public class TransactionTest {
Coin half = BitcoinNetwork.MAX_MONEY.divide(2).add(Coin.SATOSHI);
tx.getOutput(0).setValue(half);
tx.addOutput(half, ADDRESS);
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.UnexpectedCoinbaseInput.class)
public void coinbaseInputInNonCoinbaseTX() {
Transaction tx = FakeTxBuilder.createFakeTx(TESTNET);
tx.addInput(Sha256Hash.ZERO_HASH, 0xFFFFFFFFL, new ScriptBuilder().data(new byte[10]).build());
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.CoinbaseScriptSizeOutOfRange.class)
@@ -141,7 +141,7 @@ public class TransactionTest {
Transaction tx = FakeTxBuilder.createFakeTx(TESTNET);
tx.clearInputs();
tx.addInput(Sha256Hash.ZERO_HASH, 0xFFFFFFFFL, new ScriptBuilder().build());
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test(expected = VerificationException.CoinbaseScriptSizeOutOfRange.class)
@@ -150,7 +150,7 @@ public class TransactionTest {
tx.clearInputs();
TransactionInput input = tx.addInput(Sha256Hash.ZERO_HASH, 0xFFFFFFFFL, new ScriptBuilder().data(new byte[99]).build());
assertEquals(101, input.getScriptBytes().length);
- tx.verify();
+ Transaction.verify(TESTNET, tx);
}
@Test
diff --git a/core/src/test/java/org/bitcoinj/script/ScriptTest.java b/core/src/test/java/org/bitcoinj/script/ScriptTest.java
index cf2d8726e..56582b043 100644
--- a/core/src/test/java/org/bitcoinj/script/ScriptTest.java
+++ b/core/src/test/java/org/bitcoinj/script/ScriptTest.java
@@ -392,7 +392,7 @@ public class ScriptTest {
try {
Map scriptPubKeys = parseScriptPubKeys(test.get(0));
transaction = TESTNET.getDefaultSerializer().makeTransaction(ByteBuffer.wrap(ByteUtils.parseHex(test.get(1).asText().toLowerCase())));
- transaction.verify();
+ Transaction.verify(TESTNET, transaction);
Set verifyFlags = parseVerifyFlags(test.get(2).asText());
for (int i = 0; i < transaction.getInputs().size(); i++) {
@@ -433,7 +433,7 @@ public class ScriptTest {
boolean valid = true;
try {
- transaction.verify();
+ Transaction.verify(TESTNET, transaction);
} catch (VerificationException e) {
valid = false;
}