mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 05:27:17 +00:00
Add script verification flags for DER format
Add script verification flags for DER format encoding, low-S signatures and strict encoding. Pass script verification flags through to signature parser. Convert block verification flags to a sub-enum of Block. Remove DER signature format verification flag from block flags. Add test transaction with a non-standard DER signature, with the verify flag set/unset accordingly, to tx_invalid.json and tx_valid.json
This commit is contained in:
committed by
Andreas Schildbach
parent
1c74aac27e
commit
829e147ec7
@@ -451,7 +451,7 @@ public abstract class AbstractBlockChain {
|
||||
|
||||
final StoredBlock storedPrev;
|
||||
final int height;
|
||||
final EnumSet<VerificationFlags> flags;
|
||||
final EnumSet<Block.VerifyFlag> flags;
|
||||
|
||||
// Prove the block is internally valid: hash is lower than target, etc. This only checks the block contents
|
||||
// if there is a tx sending or receiving coins using an address in one of our wallets. And those transactions
|
||||
@@ -465,7 +465,7 @@ public abstract class AbstractBlockChain {
|
||||
} else {
|
||||
height = Block.BLOCK_HEIGHT_UNKNOWN;
|
||||
}
|
||||
flags = params.getValidationFlags(block, versionTally, height);
|
||||
flags = params.getBlockVerificationFlags(block, versionTally, height);
|
||||
if (shouldVerifyTransactions())
|
||||
block.verifyTransactions(height, flags);
|
||||
} catch (VerificationException e) {
|
||||
@@ -1073,4 +1073,8 @@ public abstract class AbstractBlockChain {
|
||||
falsePositiveTrend = 0;
|
||||
previousFalsePositiveRate = 0;
|
||||
}
|
||||
|
||||
protected VersionTally getVersionTally() {
|
||||
return versionTally;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,15 @@ import static org.bitcoinj.core.Sha256Hash.*;
|
||||
* specifically using {@link Peer#getBlock(Sha256Hash)}, or grab one from a downloaded {@link BlockChain}.
|
||||
*/
|
||||
public class Block extends Message {
|
||||
/**
|
||||
* Flags used to control which elements of block validation are done on
|
||||
* received blocks.
|
||||
*/
|
||||
public enum VerifyFlag {
|
||||
/** Check that block height is in coinbase transaction (BIP 34). */
|
||||
HEIGHT_IN_COINBASE
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Block.class);
|
||||
|
||||
/** How many bytes are required to represent a block header WITHOUT the trailing 00 length byte. */
|
||||
@@ -631,12 +640,12 @@ public class Block extends Message {
|
||||
* to validate the coinbase input script of v2 and above blocks.
|
||||
* @throws VerificationException if there was an error verifying the block.
|
||||
*/
|
||||
private void checkTransactions(final int height, final EnumSet<VerificationFlags> flags)
|
||||
private void checkTransactions(final int height, final EnumSet<VerifyFlag> flags)
|
||||
throws VerificationException {
|
||||
// The first transaction in a block must always be a coinbase transaction.
|
||||
if (!transactions.get(0).isCoinBase())
|
||||
throw new VerificationException("First tx is not coinbase");
|
||||
if (flags.contains(VerificationFlags.HEIGHT_IN_COINBASE) && height >= BLOCK_HEIGHT_GENESIS) {
|
||||
if (flags.contains(Block.VerifyFlag.HEIGHT_IN_COINBASE) && height >= BLOCK_HEIGHT_GENESIS) {
|
||||
transactions.get(0).checkCoinBaseHeight(height);
|
||||
}
|
||||
// The rest must not be.
|
||||
@@ -673,7 +682,7 @@ public class Block extends Message {
|
||||
* whether to test for height in the coinbase transaction).
|
||||
* @throws VerificationException if there was an error verifying the block.
|
||||
*/
|
||||
public void verifyTransactions(final int height, final EnumSet<VerificationFlags> flags) throws VerificationException {
|
||||
public void verifyTransactions(final int height, final EnumSet<VerifyFlag> flags) throws VerificationException {
|
||||
// Now we need to check that the body of the block actually matches the headers. The network won't generate
|
||||
// an invalid block, but if we didn't validate this then an untrusted man-in-the-middle could obtain the next
|
||||
// valid block from the network and simply replace the transactions in it with their own fictional
|
||||
@@ -697,7 +706,7 @@ public class Block extends Message {
|
||||
* whether to test for height in the coinbase transaction).
|
||||
* @throws VerificationException if there was an error verifying the block.
|
||||
*/
|
||||
public void verify(final int height, final EnumSet<VerificationFlags> flags) throws VerificationException {
|
||||
public void verify(final int height, final EnumSet<VerifyFlag> flags) throws VerificationException {
|
||||
verifyHeader();
|
||||
verifyTransactions(height, flags);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.core.listeners.TransactionReceivedInBlockListener;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.Script.VerifyFlag;
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
@@ -28,7 +27,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
@@ -221,9 +219,6 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
LinkedList<UTXO> txOutsSpent = new LinkedList<UTXO>();
|
||||
LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>();
|
||||
long sigOps = 0;
|
||||
final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class);
|
||||
if (block.getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME)
|
||||
verifyFlags.add(VerifyFlag.P2SH);
|
||||
|
||||
if (scriptVerificationExecutor.isShutdown())
|
||||
scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
@@ -235,6 +230,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
// checkpoints list and we therefore only check non-checkpoints for duplicated transactions here. See the
|
||||
// BIP30 document for more details on this: https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
|
||||
for (Transaction tx : block.transactions) {
|
||||
final Set<VerifyFlag> verifyFlags = params.getTransactionVerificationFlags(block, tx, getVersionTally(), height);
|
||||
Sha256Hash hash = tx.getHash();
|
||||
// If we already have unspent outputs for this hash, we saw the tx already. Either the block is
|
||||
// being added twice (bug) or the block is a BIP30 violator.
|
||||
@@ -251,6 +247,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
Coin valueIn = Coin.ZERO;
|
||||
Coin valueOut = Coin.ZERO;
|
||||
final List<Script> prevOutScripts = new LinkedList<Script>();
|
||||
final Set<VerifyFlag> verifyFlags = params.getTransactionVerificationFlags(block, tx, getVersionTally(), height);
|
||||
if (!isCoinBase) {
|
||||
// For each input of the transaction remove the corresponding output from the set of unspent
|
||||
// outputs.
|
||||
@@ -366,9 +363,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
LinkedList<UTXO> txOutsSpent = new LinkedList<UTXO>();
|
||||
LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>();
|
||||
long sigOps = 0;
|
||||
final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class);
|
||||
if (newBlock.getHeader().getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME)
|
||||
verifyFlags.add(VerifyFlag.P2SH);
|
||||
|
||||
if (!params.isCheckpoint(newBlock.getHeight())) {
|
||||
for (Transaction tx : transactions) {
|
||||
Sha256Hash hash = tx.getHash();
|
||||
@@ -383,10 +378,13 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
List<Future<VerificationException>> listScriptVerificationResults = new ArrayList<Future<VerificationException>>(transactions.size());
|
||||
for (final Transaction tx : transactions) {
|
||||
final Set<VerifyFlag> verifyFlags =
|
||||
params.getTransactionVerificationFlags(newBlock.getHeader(), tx, getVersionTally(), Integer.SIZE);
|
||||
boolean isCoinBase = tx.isCoinBase();
|
||||
Coin valueIn = Coin.ZERO;
|
||||
Coin valueOut = Coin.ZERO;
|
||||
final List<Script> prevOutScripts = new LinkedList<Script>();
|
||||
|
||||
if (!isCoinBase) {
|
||||
for (int index = 0; index < tx.getInputs().size(); index++) {
|
||||
final TransactionInput in = tx.getInputs().get(index);
|
||||
@@ -404,6 +402,8 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
throw new VerificationException("Too many P2SH SigOps in block");
|
||||
}
|
||||
|
||||
// TODO: Enforce DER signature format
|
||||
|
||||
prevOutScripts.add(prevOut.getScript());
|
||||
|
||||
blockStore.removeUnspentTransactionOutput(prevOut);
|
||||
|
||||
@@ -487,22 +487,34 @@ public abstract class NetworkParameters {
|
||||
* @param height height of the block, if known, null otherwise. Returned
|
||||
* tests should be a safe subset if block height is unknown.
|
||||
*/
|
||||
public EnumSet<VerificationFlags> getValidationFlags(final Block block,
|
||||
public EnumSet<Block.VerifyFlag> getBlockVerificationFlags(final Block block,
|
||||
final VersionTally tally, final Integer height) {
|
||||
final EnumSet<VerificationFlags> flags = EnumSet.noneOf(VerificationFlags.class);
|
||||
final EnumSet<Block.VerifyFlag> flags = EnumSet.noneOf(Block.VerifyFlag.class);
|
||||
|
||||
if (block.getVersion() >= Block.BLOCK_VERSION_BIP34) {
|
||||
final Integer count = tally.getCountAtOrAbove(Block.BLOCK_VERSION_BIP34);
|
||||
if (null != count && count >= getMajorityEnforceBlockUpgrade()) {
|
||||
flags.add(VerificationFlags.HEIGHT_IN_COINBASE);
|
||||
}
|
||||
}
|
||||
if (block.getVersion() >= Block.BLOCK_VERSION_BIP66) {
|
||||
final Integer count = tally.getCountAtOrAbove(Block.BLOCK_VERSION_BIP66);
|
||||
if (null != count && count >= getMajorityEnforceBlockUpgrade()) {
|
||||
flags.add(VerificationFlags.DER_SIGNATURE_FORMAT);
|
||||
flags.add(Block.VerifyFlag.HEIGHT_IN_COINBASE);
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* The flags indicating which script validation tests should be applied to
|
||||
* the given transaction. Enables support for alternative blockchains which enable
|
||||
* tests based on different criteria.
|
||||
*
|
||||
* @param block block the transaction belongs to.
|
||||
* @param transaction to determine flags for.
|
||||
* @param height height of the block, if known, null otherwise. Returned
|
||||
* tests should be a safe subset if block height is unknown.
|
||||
*/
|
||||
public EnumSet<Script.VerifyFlag> getTransactionVerificationFlags(final Block block,
|
||||
final Transaction transaction, final VersionTally tally, final Integer height) {
|
||||
final EnumSet<Script.VerifyFlag> verifyFlags = EnumSet.noneOf(Script.VerifyFlag.class);
|
||||
if (block.getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME)
|
||||
verifyFlags.add(Script.VerifyFlag.P2SH);
|
||||
return verifyFlags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Ross Nicoll
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.bitcoinj.core;
|
||||
|
||||
/**
|
||||
* Flags used to control which elements of block validation are performed on
|
||||
* received blocks.
|
||||
*/
|
||||
public enum VerificationFlags {
|
||||
/** Check that block height is in coinbase transaction (BIP 34) */
|
||||
HEIGHT_IN_COINBASE,
|
||||
/** Check DER signature format is exact (BIP 66) */
|
||||
DER_SIGNATURE_FORMAT
|
||||
}
|
||||
@@ -18,8 +18,6 @@
|
||||
package org.bitcoinj.params;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bitcoinj.core.Block;
|
||||
import org.bitcoinj.core.Coin;
|
||||
@@ -35,8 +33,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.bitcoinj.core.BitcoinSerializer;
|
||||
import org.bitcoinj.core.VerificationFlags;
|
||||
import org.bitcoinj.utils.VersionTally;
|
||||
|
||||
/**
|
||||
* Parameters for Bitcoin-like networks.
|
||||
|
||||
@@ -62,10 +62,19 @@ public class Script {
|
||||
P2SH
|
||||
}
|
||||
|
||||
/** Flags to pass to {@link Script#correctlySpends(Transaction, long, Script, Set)}. */
|
||||
/** Flags to pass to {@link Script#correctlySpends(Transaction, long, Script, Set)}.
|
||||
* Note currently only P2SH, DERSIG and NULLDUMMY are actually supported.
|
||||
*/
|
||||
public enum VerifyFlag {
|
||||
P2SH, // Enable BIP16-style subscript evaluation.
|
||||
NULLDUMMY // Verify dummy stack item consumed by CHECKMULTISIG is of zero-length.
|
||||
STRICTENC, // Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
||||
DERSIG, // Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP66 rule 1)
|
||||
LOW_S, // Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
||||
NULLDUMMY, // Verify dummy stack item consumed by CHECKMULTISIG is of zero-length.
|
||||
SIGPUSHONLY, // Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2).
|
||||
MINIMALDATA, // Require minimal encodings for all push operations
|
||||
DISCOURAGE_UPGRADABLE_NOPS, // Discourage use of NOPs reserved for upgrades (NOP1-10)
|
||||
CLEANSTACK // Require that only a single stack element remains after evaluation.
|
||||
}
|
||||
public static final EnumSet<VerifyFlag> ALL_VERIFY_FLAGS = EnumSet.allOf(VerifyFlag.class);
|
||||
|
||||
@@ -759,9 +768,29 @@ public class Script {
|
||||
* {@link org.bitcoinj.script.Script#correctlySpends(org.bitcoinj.core.Transaction, long, Script)}. This method
|
||||
* is useful if you need more precise control or access to the final state of the stack. This interface is very
|
||||
* likely to change in future.
|
||||
*
|
||||
* @deprecated Use {@link #executeScript(org.bitcoinj.core.Transaction, long, org.bitcoinj.script.Script, java.util.LinkedList, java.util.Set)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void executeScript(@Nullable Transaction txContainingThis, long index,
|
||||
Script script, LinkedList<byte[]> stack, boolean enforceNullDummy) throws ScriptException {
|
||||
final EnumSet<VerifyFlag> flags = enforceNullDummy
|
||||
? EnumSet.of(VerifyFlag.NULLDUMMY)
|
||||
: EnumSet.noneOf(VerifyFlag.class);
|
||||
|
||||
executeScript(txContainingThis, index, script, stack, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the script interpreter. Normally you should not use this directly, instead use
|
||||
* {@link org.bitcoinj.core.TransactionInput#verify(org.bitcoinj.core.TransactionOutput)} or
|
||||
* {@link org.bitcoinj.script.Script#correctlySpends(org.bitcoinj.core.Transaction, long, Script)}. This method
|
||||
* is useful if you need more precise control or access to the final state of the stack. This interface is very
|
||||
* likely to change in future.
|
||||
*/
|
||||
public static void executeScript(@Nullable Transaction txContainingThis, long index,
|
||||
Script script, LinkedList<byte[]> stack, Set<VerifyFlag> verifyFlags) throws ScriptException {
|
||||
int opCount = 0;
|
||||
int lastCodeSepLocation = 0;
|
||||
|
||||
@@ -1233,13 +1262,13 @@ public class Script {
|
||||
case OP_CHECKSIGVERIFY:
|
||||
if (txContainingThis == null)
|
||||
throw new IllegalStateException("Script attempted signature check but no tx was provided");
|
||||
executeCheckSig(txContainingThis, (int) index, script, stack, lastCodeSepLocation, opcode);
|
||||
executeCheckSig(txContainingThis, (int) index, script, stack, lastCodeSepLocation, opcode, verifyFlags);
|
||||
break;
|
||||
case OP_CHECKMULTISIG:
|
||||
case OP_CHECKMULTISIGVERIFY:
|
||||
if (txContainingThis == null)
|
||||
throw new IllegalStateException("Script attempted signature check but no tx was provided");
|
||||
opCount = executeMultiSig(txContainingThis, (int) index, script, stack, opCount, lastCodeSepLocation, opcode, enforceNullDummy);
|
||||
opCount = executeMultiSig(txContainingThis, (int) index, script, stack, opCount, lastCodeSepLocation, opcode, verifyFlags);
|
||||
break;
|
||||
case OP_NOP1:
|
||||
case OP_NOP2:
|
||||
@@ -1267,7 +1296,11 @@ public class Script {
|
||||
}
|
||||
|
||||
private static void executeCheckSig(Transaction txContainingThis, int index, Script script, LinkedList<byte[]> stack,
|
||||
int lastCodeSepLocation, int opcode) throws ScriptException {
|
||||
int lastCodeSepLocation, int opcode,
|
||||
Set<VerifyFlag> verifyFlags) throws ScriptException {
|
||||
final boolean requireCanonical = verifyFlags.contains(VerifyFlag.STRICTENC)
|
||||
|| verifyFlags.contains(VerifyFlag.DERSIG)
|
||||
|| verifyFlags.contains(VerifyFlag.LOW_S);
|
||||
if (stack.size() < 2)
|
||||
throw new ScriptException("Attempted OP_CHECKSIG(VERIFY) on a stack with size < 2");
|
||||
byte[] pubKey = stack.pollLast();
|
||||
@@ -1287,7 +1320,10 @@ public class Script {
|
||||
// TODO: Use int for indexes everywhere, we can't have that many inputs/outputs
|
||||
boolean sigValid = false;
|
||||
try {
|
||||
TransactionSignature sig = TransactionSignature.decodeFromBitcoin(sigBytes, false);
|
||||
// TODO: Should pass through LOW_S verification flag
|
||||
TransactionSignature sig = TransactionSignature.decodeFromBitcoin(sigBytes, requireCanonical);
|
||||
|
||||
// TODO: Should check hash type is known
|
||||
Sha256Hash hash = txContainingThis.hashForSignature(index, connectedScript, (byte) sig.sighashFlags);
|
||||
sigValid = ECKey.verify(hash.getBytes(), sig, pubKey);
|
||||
} catch (Exception e1) {
|
||||
@@ -1297,7 +1333,7 @@ public class Script {
|
||||
// This RuntimeException occurs when signing as we run partial/invalid scripts to see if they need more
|
||||
// signing work to be done inside LocalTransactionSigner.signInputs.
|
||||
if (!e1.getMessage().contains("Reached past end of ASN.1 stream"))
|
||||
log.warn("Signature checking failed! {}", e1.toString());
|
||||
log.warn("Signature checking failed!", e1);
|
||||
}
|
||||
|
||||
if (opcode == OP_CHECKSIG)
|
||||
@@ -1308,7 +1344,11 @@ public class Script {
|
||||
}
|
||||
|
||||
private static int executeMultiSig(Transaction txContainingThis, int index, Script script, LinkedList<byte[]> stack,
|
||||
int opCount, int lastCodeSepLocation, int opcode, boolean enforceNullDummy) throws ScriptException {
|
||||
int opCount, int lastCodeSepLocation, int opcode,
|
||||
Set<VerifyFlag> verifyFlags) throws ScriptException {
|
||||
final boolean requireCanonical = verifyFlags.contains(VerifyFlag.STRICTENC)
|
||||
|| verifyFlags.contains(VerifyFlag.DERSIG)
|
||||
|| verifyFlags.contains(VerifyFlag.LOW_S);
|
||||
if (stack.size() < 2)
|
||||
throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < 2");
|
||||
int pubKeyCount = castToBigInteger(stack.pollLast()).intValue();
|
||||
@@ -1357,7 +1397,7 @@ public class Script {
|
||||
// We could reasonably move this out of the loop, but because signature verification is significantly
|
||||
// more expensive than hashing, its not a big deal.
|
||||
try {
|
||||
TransactionSignature sig = TransactionSignature.decodeFromBitcoin(sigs.getFirst(), false);
|
||||
TransactionSignature sig = TransactionSignature.decodeFromBitcoin(sigs.getFirst(), requireCanonical);
|
||||
Sha256Hash hash = txContainingThis.hashForSignature(index, connectedScript, (byte) sig.sighashFlags);
|
||||
if (ECKey.verify(hash.getBytes(), sig, pubKey))
|
||||
sigs.pollFirst();
|
||||
@@ -1374,7 +1414,7 @@ public class Script {
|
||||
|
||||
// We uselessly remove a stack object to emulate a reference client bug.
|
||||
byte[] nullDummy = stack.pollLast();
|
||||
if (enforceNullDummy && nullDummy.length > 0)
|
||||
if (verifyFlags.contains(VerifyFlag.NULLDUMMY) && nullDummy.length > 0)
|
||||
throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with non-null nulldummy: " + Arrays.toString(nullDummy));
|
||||
|
||||
if (opcode == OP_CHECKMULTISIG) {
|
||||
@@ -1393,7 +1433,11 @@ public class Script {
|
||||
* Accessing txContainingThis from another thread while this method runs results in undefined behavior.
|
||||
* @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.
|
||||
* @deprecated Use {@link #correctlySpends(org.bitcoinj.core.Transaction, long, org.bitcoinj.script.Script, java.util.Set)}
|
||||
* instead so that verification flags do not change as new verification options
|
||||
* are added.
|
||||
*/
|
||||
@Deprecated
|
||||
public void correctlySpends(Transaction txContainingThis, long scriptSigIndex, Script scriptPubKey)
|
||||
throws ScriptException {
|
||||
correctlySpends(txContainingThis, scriptSigIndex, scriptPubKey, ALL_VERIFY_FLAGS);
|
||||
@@ -1423,10 +1467,10 @@ public class Script {
|
||||
LinkedList<byte[]> stack = new LinkedList<byte[]>();
|
||||
LinkedList<byte[]> p2shStack = null;
|
||||
|
||||
executeScript(txContainingThis, scriptSigIndex, this, stack, verifyFlags.contains(VerifyFlag.NULLDUMMY));
|
||||
executeScript(txContainingThis, scriptSigIndex, this, stack, verifyFlags);
|
||||
if (verifyFlags.contains(VerifyFlag.P2SH))
|
||||
p2shStack = new LinkedList<byte[]>(stack);
|
||||
executeScript(txContainingThis, scriptSigIndex, scriptPubKey, stack, verifyFlags.contains(VerifyFlag.NULLDUMMY));
|
||||
executeScript(txContainingThis, scriptSigIndex, scriptPubKey, stack, verifyFlags);
|
||||
|
||||
if (stack.size() == 0)
|
||||
throw new ScriptException("Stack empty at end of script execution.");
|
||||
@@ -1455,7 +1499,7 @@ public class Script {
|
||||
byte[] scriptPubKeyBytes = p2shStack.pollLast();
|
||||
Script scriptPubKeyP2SH = new Script(scriptPubKeyBytes);
|
||||
|
||||
executeScript(txContainingThis, scriptSigIndex, scriptPubKeyP2SH, p2shStack, verifyFlags.contains(VerifyFlag.NULLDUMMY));
|
||||
executeScript(txContainingThis, scriptSigIndex, scriptPubKeyP2SH, p2shStack, verifyFlags);
|
||||
|
||||
if (p2shStack.size() == 0)
|
||||
throw new ScriptException("P2SH stack empty at end of script execution.");
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.bitcoinj.signers;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.ScriptException;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
@@ -22,6 +23,7 @@ import org.bitcoinj.core.TransactionInput;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.Script.VerifyFlag;
|
||||
import org.bitcoinj.wallet.KeyBag;
|
||||
import org.bitcoinj.wallet.RedeemData;
|
||||
import org.slf4j.Logger;
|
||||
@@ -42,6 +44,13 @@ import org.slf4j.LoggerFactory;
|
||||
public class LocalTransactionSigner extends StatelessTransactionSigner {
|
||||
private static final Logger log = LoggerFactory.getLogger(LocalTransactionSigner.class);
|
||||
|
||||
/**
|
||||
* Verify flags that are safe to use when testing if an input is already
|
||||
* signed.
|
||||
*/
|
||||
private static final EnumSet<VerifyFlag> MINIMUM_VERIFY_FLAGS = EnumSet.of(VerifyFlag.P2SH,
|
||||
VerifyFlag.NULLDUMMY);
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return true;
|
||||
@@ -62,7 +71,7 @@ public class LocalTransactionSigner extends StatelessTransactionSigner {
|
||||
// We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when
|
||||
// we sign missing pieces (to check this would require either assuming any signatures are signing
|
||||
// standard output types or a way to get processed signatures out of script execution)
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey());
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey(), MINIMUM_VERIFY_FLAGS);
|
||||
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i);
|
||||
continue;
|
||||
} catch (ScriptException e) {
|
||||
|
||||
@@ -58,7 +58,7 @@ public class BlockTest {
|
||||
@Test
|
||||
public void testBlockVerification() throws Exception {
|
||||
Block block = params.getDefaultSerializer().makeBlock(blockBytes);
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
assertEquals("00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935", block.getHashAsString());
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class BlockTest {
|
||||
Block block = params.getDefaultSerializer().makeBlock(blockBytes);
|
||||
block.setNonce(12346);
|
||||
try {
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
// Expected.
|
||||
@@ -85,18 +85,18 @@ public class BlockTest {
|
||||
// from containing artificially weak difficulties.
|
||||
block.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
|
||||
// Now it should pass.
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
// Break the nonce again at the lower difficulty level so we can try solving for it.
|
||||
block.setNonce(1);
|
||||
try {
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
// Expected to fail as the nonce is no longer correct.
|
||||
}
|
||||
// Should find an acceptable nonce.
|
||||
block.solve();
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
assertEquals(block.getNonce(), 2);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ public class BlockTest {
|
||||
block.transactions.set(0, tx2);
|
||||
block.transactions.set(1, tx1);
|
||||
try {
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
// We should get here.
|
||||
|
||||
@@ -64,7 +64,7 @@ public class CoinbaseBlockTest {
|
||||
|
||||
// Check block.
|
||||
assertNotNull(block);
|
||||
block.verify(169482, EnumSet.noneOf(VerificationFlags.class));
|
||||
block.verify(169482, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
assertEquals(BLOCK_NONCE, block.getNonce());
|
||||
|
||||
StoredBlock storedBlock = new StoredBlock(block, BigInteger.ONE, BLOCK_OF_INTEREST); // Nonsense work - not used in test.
|
||||
|
||||
@@ -46,6 +46,7 @@ import static org.bitcoinj.script.ScriptOpCodes.OP_0;
|
||||
import static org.bitcoinj.script.ScriptOpCodes.OP_INVALIDOPCODE;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
|
||||
public class ScriptTest {
|
||||
// From tx 05e04c26c12fe408a3c1b71aa7996403f6acad1045252b1c62e055496f4d2cb1 on the testnet.
|
||||
@@ -58,6 +59,11 @@ public class ScriptTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ScriptTest.class);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Context context = new Context(params);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptSig() throws Exception {
|
||||
byte[] sigProgBytes = HEX.decode(sigProg);
|
||||
|
||||
@@ -109,5 +109,9 @@
|
||||
[[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]],
|
||||
"01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "P2SH"],
|
||||
|
||||
["A transaction with a non-standard DER signature."],
|
||||
[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]],
|
||||
"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH,DERSIG"],
|
||||
|
||||
["Make diffs cleaner by leaving a comment here without comma at the end"]
|
||||
]
|
||||
|
||||
@@ -177,6 +177,9 @@
|
||||
["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]],
|
||||
"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH"],
|
||||
|
||||
["A transaction with a non-standard DER signature."],
|
||||
[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]],
|
||||
"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH"],
|
||||
|
||||
["Make diffs cleaner by leaving a comment here without comma at the end"]
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user