mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-13 10:45:51 +00:00
Add OP_CHECKLOCKTIMEVERIFY script support
This commit is contained in:
parent
92bd6d0af1
commit
70f557a514
@ -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;
|
||||
|
@ -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<byte[]> stack,
|
||||
int lastCodeSepLocation, int opcode,
|
||||
Set<VerifyFlag> 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<byte[]> stack,
|
||||
int lastCodeSepLocation, int opcode,
|
||||
Set<VerifyFlag> verifyFlags) throws ScriptException {
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user