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 89fc0759a..ffa480c45 100644 --- a/core/src/main/java/com/google/bitcoin/core/FullPrunedBlockChain.java +++ b/core/src/main/java/com/google/bitcoin/core/FullPrunedBlockChain.java @@ -113,7 +113,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain { LinkedList txOutsSpent = new LinkedList(); LinkedList txOutsCreated = new LinkedList(); long sigOps = 0; - boolean enforceBIP16 = block.getTimeSeconds() >= params.BIP16_ENFORCE_TIME; + final boolean enforceBIP16 = block.getTimeSeconds() >= params.BIP16_ENFORCE_TIME; if (scriptVerificationExecutor.isShutdown()) scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); @@ -200,7 +200,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain { FutureTask future = new FutureTask(new Callable() { public VerificationException call() { try{ - scriptSig.correctlySpends(txCache, currentIndex, scriptPubKey); + scriptSig.correctlySpends(txCache, currentIndex, scriptPubKey, enforceBIP16); } catch (ScriptException e) { return new VerificationException("Error verifying script: " + e.getMessage()); } catch (VerificationException e) { @@ -292,7 +292,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain { LinkedList txOutsSpent = new LinkedList(); LinkedList txOutsCreated = new LinkedList(); long sigOps = 0; - boolean enforcePayToScriptHash = newBlock.getHeader().getTimeSeconds() >= params.BIP16_ENFORCE_TIME; + final boolean enforcePayToScriptHash = newBlock.getHeader().getTimeSeconds() >= params.BIP16_ENFORCE_TIME; if (!params.isCheckpoint(newBlock.getHeight())) { for(StoredTransaction tx : transactions) { Sha256Hash hash = tx.getHash(); @@ -356,7 +356,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain { FutureTask future = new FutureTask(new Callable() { public VerificationException call() { try{ - scriptSig.correctlySpends(txCache, currentIndex, scriptPubKey); + scriptSig.correctlySpends(txCache, currentIndex, scriptPubKey, enforcePayToScriptHash); } catch (ScriptException e) { return new VerificationException("Error verifying script: " + e.getMessage()); } catch (VerificationException e) { 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 743b2caa7..3e8916a9e 100644 --- a/core/src/main/java/com/google/bitcoin/core/Script.java +++ b/core/src/main/java/com/google/bitcoin/core/Script.java @@ -1479,13 +1479,16 @@ public class Script { * Verifies that this script (interpreted as a scriptSig) correctly spends the given scriptPubKey * @throws VerificationException if this script does not correctly spend the scriptPubKey */ - public void correctlySpends(Transaction txContainingThis, long index, Script scriptPubKey) throws VerificationException, ScriptException { + public void correctlySpends(Transaction txContainingThis, long index, Script scriptPubKey, boolean enforceP2SH) throws VerificationException, 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); + if (enforceP2SH) + p2shStack = new LinkedList(stack); executeScript(txContainingThis, index, scriptPubKey, stack); if (stack.size() == 0) @@ -1493,5 +1496,22 @@ public class Script { if (!castToBool(stack.pollLast())) throw new VerificationException("Script resulted in a non-true stack"); + + if(enforceP2SH && scriptPubKey.isPayToScriptHash()) { + for (ScriptChunk chunk : chunks) + if (chunk.isOpCode && (chunk.data[0] & 0xff) > OP_16) + throw new VerificationException("Attempted to spend a P2SH scriptPubKey with a script that contained script ops"); + + byte[] scriptPubKeyBytes = p2shStack.pollLast(); + Script scriptPubKeyP2SH = new Script(params, scriptPubKeyBytes, 0, scriptPubKeyBytes.length); + + executeScript(txContainingThis, index, scriptPubKeyP2SH, p2shStack); + + if (p2shStack.size() == 0) + throw new VerificationException("P2SH stack empty at end of script execution."); + + if (!castToBool(p2shStack.pollLast())) + throw new VerificationException("P2SH script execution resulted in a non-true stack"); + } } }