diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index 451e2869..e7085795 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -36,6 +36,7 @@ import java.util.*; import static org.bitcoinj.core.Utils.*; import static com.google.common.base.Preconditions.checkState; +import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.bitcoinj.script.ScriptChunk; @@ -84,6 +85,8 @@ public class Transaction extends ChildMessage { /** Threshold for lockTime: below this value it is interpreted as block number, otherwise as timestamp. **/ public static final int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC + /** Same but as a BigInteger for CHECKLOCKTIMEVERIFY */ + public static final BigInteger LOCKTIME_THRESHOLD_BIG = BigInteger.valueOf(LOCKTIME_THRESHOLD); /** How many bytes a transaction can be before it won't be relayed anymore. Currently 100kb. */ public static final int MAX_STANDARD_TX_SIZE = 100000; diff --git a/core/src/main/java/org/bitcoinj/script/Script.java b/core/src/main/java/org/bitcoinj/script/Script.java index f1ae18bc..25ca6959 100644 --- a/core/src/main/java/org/bitcoinj/script/Script.java +++ b/core/src/main/java/org/bitcoinj/script/Script.java @@ -753,12 +753,34 @@ public class Script { return false; } + /** + * Cast a script chunk to a BigInteger. + * + * @see #castToBigInteger(byte[], int) for values with different maximum + * sizes. + * @throws ScriptException if the chunk is longer than 4 bytes. + */ private static BigInteger castToBigInteger(byte[] chunk) throws ScriptException { if (chunk.length > 4) throw new ScriptException("Script attempted to use an integer larger than 4 bytes"); return Utils.decodeMPI(Utils.reverseBytes(chunk), false); } + /** + * Cast a script chunk to a BigInteger. Normally you would want + * {@link #castToBigInteger(byte[])} instead, this is only for cases where + * the normal maximum length does not apply (i.e. CHECKLOCKTIMEVERIFY). + * + * @param maxLength the maximum length in bytes. + * @throws ScriptException if the chunk is longer than the specified maximum. + */ + private static BigInteger castToBigInteger(final byte[] chunk, final int maxLength) throws ScriptException { + if (chunk.length > maxLength) + throw new ScriptException("Script attempted to use an integer larger than " + + maxLength + " bytes"); + return Utils.decodeMPI(Utils.reverseBytes(chunk), false); + } + public boolean isOpReturn() { return chunks.size() > 0 && chunks.get(0).equalsOpCode(OP_RETURN); } @@ -1271,8 +1293,17 @@ public class Script { throw new IllegalStateException("Script attempted signature check but no tx was provided"); opCount = executeMultiSig(txContainingThis, (int) index, script, stack, opCount, lastCodeSepLocation, opcode, verifyFlags); break; + case OP_CHECKLOCKTIMEVERIFY: + if (!verifyFlags.contains(VerifyFlag.CHECKLOCKTIMEVERIFY)) { + // not enabled; treat as a NOP2 + if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) { + throw new ScriptException("Script used a reserved opcode " + opcode); + } + break; + } + executeCheckLockTimeVerify(txContainingThis, (int) index, script, stack, lastCodeSepLocation, opcode, verifyFlags); + break; case OP_NOP1: - case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: @@ -1281,6 +1312,9 @@ public class Script { case OP_NOP8: case OP_NOP9: case OP_NOP10: + if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) { + throw new ScriptException("Script used a reserved opcode " + opcode); + } break; default: @@ -1296,6 +1330,46 @@ public class Script { throw new ScriptException("OP_IF/OP_NOTIF without OP_ENDIF"); } + // This is more or less a direct translation of the code in Bitcoin Core + private static void executeCheckLockTimeVerify(Transaction txContainingThis, int index, Script script, LinkedList stack, + int lastCodeSepLocation, int opcode, + Set verifyFlags) throws ScriptException { + if (stack.size() < 1) + throw new ScriptException("Attempted OP_CHECKLOCKTIMEVERIFY on a stack with size < 1"); + + // Thus as a special case we tell CScriptNum to accept up + // to 5-byte bignums to avoid year 2038 issue. + final BigInteger nLockTime = castToBigInteger(stack.getLast(), 5); + + if (nLockTime.compareTo(BigInteger.ZERO) < 0) + throw new ScriptException("Negative locktime"); + + // There are two kinds of nLockTime, need to ensure we're comparing apples-to-apples + if (!( + ((txContainingThis.getLockTime() < Transaction.LOCKTIME_THRESHOLD) && (nLockTime.compareTo(Transaction.LOCKTIME_THRESHOLD_BIG)) < 0) || + ((txContainingThis.getLockTime() >= Transaction.LOCKTIME_THRESHOLD) && (nLockTime.compareTo(Transaction.LOCKTIME_THRESHOLD_BIG)) >= 0)) + ) + throw new ScriptException("Locktime requirement type mismatch"); + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nLockTime.compareTo(BigInteger.valueOf(txContainingThis.getLockTime())) > 0) + throw new ScriptException("Locktime requirement not satisfied"); + + // Finally the nLockTime feature can be disabled and thus + // CHECKLOCKTIMEVERIFY bypassed if every txin has been + // finalized by setting nSequence to maxint. The + // transaction would be allowed into the blockchain, making + // the opcode ineffective. + // + // Testing if this vin is not final is sufficient to + // prevent this condition. Alternatively we could test all + // inputs, but testing just this input minimizes the data + // required to prove correct CHECKLOCKTIMEVERIFY execution. + if (!txContainingThis.getInput(index).hasSequence()) + throw new ScriptException("Transaction contains a final transaction input for a CHECKLOCKTIMEVERIFY script."); + } + private static void executeCheckSig(Transaction txContainingThis, int index, Script script, LinkedList stack, int lastCodeSepLocation, int opcode, Set verifyFlags) throws ScriptException { diff --git a/core/src/main/java/org/bitcoinj/script/ScriptOpCodes.java b/core/src/main/java/org/bitcoinj/script/ScriptOpCodes.java index f2f9ba65..441e4032 100644 --- a/core/src/main/java/org/bitcoinj/script/ScriptOpCodes.java +++ b/core/src/main/java/org/bitcoinj/script/ScriptOpCodes.java @@ -142,9 +142,15 @@ public class ScriptOpCodes { public static final int OP_CHECKMULTISIG = 0xae; public static final int OP_CHECKMULTISIGVERIFY = 0xaf; + // block state + /** Check lock time of the block. Introduced in BIP 65, replacing OP_NOP2 */ + public static final int OP_CHECKLOCKTIMEVERIFY = 0xb1; + // expansion public static final int OP_NOP1 = 0xb0; - public static final int OP_NOP2 = 0xb1; + /** Deprecated by BIP 65 */ + @Deprecated + public static final int OP_NOP2 = OP_CHECKLOCKTIMEVERIFY; public static final int OP_NOP3 = 0xb2; public static final int OP_NOP4 = 0xb3; public static final int OP_NOP5 = 0xb4; @@ -258,7 +264,7 @@ public class ScriptOpCodes { .put(OP_CHECKMULTISIG, "CHECKMULTISIG") .put(OP_CHECKMULTISIGVERIFY, "CHECKMULTISIGVERIFY") .put(OP_NOP1, "NOP1") - .put(OP_NOP2, "NOP2") + .put(OP_CHECKLOCKTIMEVERIFY, "CHECKLOCKTIMEVERIFY") .put(OP_NOP3, "NOP3") .put(OP_NOP4, "NOP4") .put(OP_NOP5, "NOP5") @@ -371,6 +377,7 @@ public class ScriptOpCodes { .put("CHECKMULTISIG", OP_CHECKMULTISIG) .put("CHECKMULTISIGVERIFY", OP_CHECKMULTISIGVERIFY) .put("NOP1", OP_NOP1) + .put("CHECKLOCKTIMEVERIFY", OP_CHECKLOCKTIMEVERIFY) .put("NOP2", OP_NOP2) .put("NOP3", OP_NOP3) .put("NOP4", OP_NOP4)