mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-15 11: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 org.bitcoinj.core.Utils.*;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import org.bitcoinj.script.ScriptChunk;
|
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. **/
|
/** 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
|
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. */
|
/** 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;
|
public static final int MAX_STANDARD_TX_SIZE = 100000;
|
||||||
|
@ -753,12 +753,34 @@ public class Script {
|
|||||||
return false;
|
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 {
|
private static BigInteger castToBigInteger(byte[] chunk) throws ScriptException {
|
||||||
if (chunk.length > 4)
|
if (chunk.length > 4)
|
||||||
throw new ScriptException("Script attempted to use an integer larger than 4 bytes");
|
throw new ScriptException("Script attempted to use an integer larger than 4 bytes");
|
||||||
return Utils.decodeMPI(Utils.reverseBytes(chunk), false);
|
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() {
|
public boolean isOpReturn() {
|
||||||
return chunks.size() > 0 && chunks.get(0).equalsOpCode(OP_RETURN);
|
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");
|
throw new IllegalStateException("Script attempted signature check but no tx was provided");
|
||||||
opCount = executeMultiSig(txContainingThis, (int) index, script, stack, opCount, lastCodeSepLocation, opcode, verifyFlags);
|
opCount = executeMultiSig(txContainingThis, (int) index, script, stack, opCount, lastCodeSepLocation, opcode, verifyFlags);
|
||||||
break;
|
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_NOP1:
|
||||||
case OP_NOP2:
|
|
||||||
case OP_NOP3:
|
case OP_NOP3:
|
||||||
case OP_NOP4:
|
case OP_NOP4:
|
||||||
case OP_NOP5:
|
case OP_NOP5:
|
||||||
@ -1281,6 +1312,9 @@ public class Script {
|
|||||||
case OP_NOP8:
|
case OP_NOP8:
|
||||||
case OP_NOP9:
|
case OP_NOP9:
|
||||||
case OP_NOP10:
|
case OP_NOP10:
|
||||||
|
if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) {
|
||||||
|
throw new ScriptException("Script used a reserved opcode " + opcode);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1296,6 +1330,46 @@ public class Script {
|
|||||||
throw new ScriptException("OP_IF/OP_NOTIF without OP_ENDIF");
|
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,
|
private static void executeCheckSig(Transaction txContainingThis, int index, Script script, LinkedList<byte[]> stack,
|
||||||
int lastCodeSepLocation, int opcode,
|
int lastCodeSepLocation, int opcode,
|
||||||
Set<VerifyFlag> verifyFlags) throws ScriptException {
|
Set<VerifyFlag> verifyFlags) throws ScriptException {
|
||||||
|
@ -142,9 +142,15 @@ public class ScriptOpCodes {
|
|||||||
public static final int OP_CHECKMULTISIG = 0xae;
|
public static final int OP_CHECKMULTISIG = 0xae;
|
||||||
public static final int OP_CHECKMULTISIGVERIFY = 0xaf;
|
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
|
// expansion
|
||||||
public static final int OP_NOP1 = 0xb0;
|
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_NOP3 = 0xb2;
|
||||||
public static final int OP_NOP4 = 0xb3;
|
public static final int OP_NOP4 = 0xb3;
|
||||||
public static final int OP_NOP5 = 0xb4;
|
public static final int OP_NOP5 = 0xb4;
|
||||||
@ -258,7 +264,7 @@ public class ScriptOpCodes {
|
|||||||
.put(OP_CHECKMULTISIG, "CHECKMULTISIG")
|
.put(OP_CHECKMULTISIG, "CHECKMULTISIG")
|
||||||
.put(OP_CHECKMULTISIGVERIFY, "CHECKMULTISIGVERIFY")
|
.put(OP_CHECKMULTISIGVERIFY, "CHECKMULTISIGVERIFY")
|
||||||
.put(OP_NOP1, "NOP1")
|
.put(OP_NOP1, "NOP1")
|
||||||
.put(OP_NOP2, "NOP2")
|
.put(OP_CHECKLOCKTIMEVERIFY, "CHECKLOCKTIMEVERIFY")
|
||||||
.put(OP_NOP3, "NOP3")
|
.put(OP_NOP3, "NOP3")
|
||||||
.put(OP_NOP4, "NOP4")
|
.put(OP_NOP4, "NOP4")
|
||||||
.put(OP_NOP5, "NOP5")
|
.put(OP_NOP5, "NOP5")
|
||||||
@ -371,6 +377,7 @@ public class ScriptOpCodes {
|
|||||||
.put("CHECKMULTISIG", OP_CHECKMULTISIG)
|
.put("CHECKMULTISIG", OP_CHECKMULTISIG)
|
||||||
.put("CHECKMULTISIGVERIFY", OP_CHECKMULTISIGVERIFY)
|
.put("CHECKMULTISIGVERIFY", OP_CHECKMULTISIGVERIFY)
|
||||||
.put("NOP1", OP_NOP1)
|
.put("NOP1", OP_NOP1)
|
||||||
|
.put("CHECKLOCKTIMEVERIFY", OP_CHECKLOCKTIMEVERIFY)
|
||||||
.put("NOP2", OP_NOP2)
|
.put("NOP2", OP_NOP2)
|
||||||
.put("NOP3", OP_NOP3)
|
.put("NOP3", OP_NOP3)
|
||||||
.put("NOP4", OP_NOP4)
|
.put("NOP4", OP_NOP4)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user