From 671a5aec18654a8ef9d7beedbdc46362b1e13742 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 26 Dec 2012 18:14:39 +0000 Subject: [PATCH] Add a TransactionInput.verify() method that checks signatures and use it from the basicSpending test to ensure we generate valid signatures/scripts. --- .../java/com/google/bitcoin/core/Script.java | 16 +++++++++++----- .../google/bitcoin/core/TransactionInput.java | 13 +++++++++++++ .../java/com/google/bitcoin/core/WalletTest.java | 4 ++-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/core/Script.java b/core/src/main/java/com/google/bitcoin/core/Script.java index 520e51ab..98043c09 100644 --- a/core/src/main/java/com/google/bitcoin/core/Script.java +++ b/core/src/main/java/com/google/bitcoin/core/Script.java @@ -875,7 +875,8 @@ public class Script { return Utils.decodeMPI(Utils.reverseBytes(chunk), false); } - private static void executeScript(Transaction txContainingThis, long index, Script script, LinkedList stack) throws ScriptException { + private static void executeScript(Transaction txContainingThis, long index, + Script script, LinkedList stack) throws ScriptException { int opCount = 0; int lastCodeSepLocation = 0; @@ -1479,19 +1480,24 @@ public class Script { /** * Verifies that this script (interpreted as a scriptSig) correctly spends the given scriptPubKey. + * @param txContainingThis The transaction in which this input scriptSig resides. + * @param scriptSigIndex The index in txContainingThis of the scriptSig (note: NOT the index of the scriptPubKey). + * @param scriptPubKey The connected scriptPubKey containing the conditions needed to claim the value. + * @param enforceP2SH Whether "pay to script hash" rules should be enforced. If in doubt, set to true. * @throws VerificationException if this script does not correctly spend the scriptPubKey */ - public void correctlySpends(Transaction txContainingThis, long index, Script scriptPubKey, boolean enforceP2SH) throws ScriptException { + public void correctlySpends(Transaction txContainingThis, long scriptSigIndex, Script scriptPubKey, + boolean enforceP2SH) throws ScriptException { if (program.length > 10000 || scriptPubKey.program.length > 10000) throw new ScriptException("Script larger than 10,000 bytes"); LinkedList stack = new LinkedList(); LinkedList p2shStack = null; - executeScript(txContainingThis, index, this, stack); + executeScript(txContainingThis, scriptSigIndex, this, stack); if (enforceP2SH) p2shStack = new LinkedList(stack); - executeScript(txContainingThis, index, scriptPubKey, stack); + executeScript(txContainingThis, scriptSigIndex, scriptPubKey, stack); if (stack.size() == 0) throw new ScriptException("Stack empty at end of script execution."); @@ -1520,7 +1526,7 @@ public class Script { byte[] scriptPubKeyBytes = p2shStack.pollLast(); Script scriptPubKeyP2SH = new Script(params, scriptPubKeyBytes, 0, scriptPubKeyBytes.length); - executeScript(txContainingThis, index, scriptPubKeyP2SH, p2shStack); + executeScript(txContainingThis, scriptSigIndex, scriptPubKeyP2SH, p2shStack); if (p2shStack.size() == 0) throw new ScriptException("P2SH stack empty at end of script execution."); 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 d4279069..b4c66039 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionInput.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionInput.java @@ -340,4 +340,17 @@ public class TransactionInput extends ChildMessage implements Serializable { public boolean hasSequence() { return sequence != NO_SEQUENCE; } + + /** + * For a connected transaction, runs the script against the connected pubkey and verifies they are correct. + * @throws ScriptException if the script did not verify. + */ + public void verify() throws ScriptException { + Preconditions.checkNotNull(getOutpoint().fromTx, "Not connected"); + long spendingIndex = getOutpoint().getIndex(); + Script pubKey = getOutpoint().fromTx.getOutputs().get((int) spendingIndex).getScriptPubKey(); + Script sig = getScriptSig(); + int myIndex = parentTransaction.getInputs().indexOf(this); + sig.correctlySpends(parentTransaction, myIndex, pubKey, true); + } } diff --git a/core/src/test/java/com/google/bitcoin/core/WalletTest.java b/core/src/test/java/com/google/bitcoin/core/WalletTest.java index 3c4b9249..670fb5fb 100644 --- a/core/src/test/java/com/google/bitcoin/core/WalletTest.java +++ b/core/src/test/java/com/google/bitcoin/core/WalletTest.java @@ -89,8 +89,8 @@ public class WalletTest { assertEquals(destination, t2.getOutputs().get(0).getScriptPubKey().getToAddress()); assertEquals(wallet.getChangeAddress(), t2.getOutputs().get(1).getScriptPubKey().getToAddress()); assertEquals(toNanoCoins(0, 49), t2.getOutputs().get(1).getValue()); - - // We have NOT proven that the signature is correct! + // Check the script runs and signatures verify. + t2.getInputs().get(0).verify(); final LinkedList txns = Lists.newLinkedList(); wallet.addEventListener(new AbstractWalletEventListener() {