mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 18:25:51 +00:00
Verify SigOp counts when verifying Blocks.
This commit is contained in:
parent
1cbd5c07b4
commit
626ff2fa2c
@ -52,6 +52,19 @@ public class Block extends Message {
|
|||||||
|
|
||||||
static final long ALLOWED_TIME_DRIFT = 2 * 60 * 60; // Same value as official client.
|
static final long ALLOWED_TIME_DRIFT = 2 * 60 * 60; // Same value as official client.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant shared by the entire network: how large in bytes a block is allowed to be. One day we may have to
|
||||||
|
* upgrade everyone to change this, so Bitcoin can continue to grow. For now it exists as an anti-DoS measure to
|
||||||
|
* avoid somebody creating a titanically huge but valid block and forcing everyone to download/store it forever.
|
||||||
|
*/
|
||||||
|
public static final int MAX_BLOCK_SIZE = 1 * 1000 * 1000;
|
||||||
|
/**
|
||||||
|
* A "sigop" is a signature verification operation. Because they're expensive we also impose a separate limit on
|
||||||
|
* the number in a block to prevent somebody mining a huge block that has way more sigops than normal, so is very
|
||||||
|
* expensive/slow to verify.
|
||||||
|
*/
|
||||||
|
public static final int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE / 50;
|
||||||
|
|
||||||
/** A value for difficultyTarget (nBits) that allows half of all possible hash solutions. Used in unit testing. */
|
/** A value for difficultyTarget (nBits) that allows half of all possible hash solutions. Used in unit testing. */
|
||||||
static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL;
|
static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL;
|
||||||
|
|
||||||
@ -569,6 +582,21 @@ public class Block extends Message {
|
|||||||
if (time > currentTime + ALLOWED_TIME_DRIFT)
|
if (time > currentTime + ALLOWED_TIME_DRIFT)
|
||||||
throw new VerificationException("Block too far in future");
|
throw new VerificationException("Block too far in future");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkSigOps() throws VerificationException {
|
||||||
|
// Check there aren't too many signature verifications in the block. This is an anti-DoS measure, see the
|
||||||
|
// comments for MAX_BLOCK_SIGOPS.
|
||||||
|
int sigOps = 0;
|
||||||
|
for (Transaction tx : transactions) {
|
||||||
|
try {
|
||||||
|
sigOps += tx.getSigOpCount();
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
throw new VerificationException("Unreadable script in transaction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sigOps > MAX_BLOCK_SIGOPS)
|
||||||
|
throw new VerificationException("Block had too many Signature Operations");
|
||||||
|
}
|
||||||
|
|
||||||
private void checkMerkleRoot() throws VerificationException {
|
private void checkMerkleRoot() throws VerificationException {
|
||||||
Sha256Hash calculatedRoot = calculateMerkleRoot();
|
Sha256Hash calculatedRoot = calculateMerkleRoot();
|
||||||
@ -683,6 +711,7 @@ public class Block extends Message {
|
|||||||
maybeParseTransactions();
|
maybeParseTransactions();
|
||||||
checkTransactions();
|
checkTransactions();
|
||||||
checkMerkleRoot();
|
checkMerkleRoot();
|
||||||
|
checkSigOps();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,6 +195,11 @@ public class Script {
|
|||||||
// The program is a set of byte[]s where each element is either [opcode] or [data, data, data ...]
|
// The program is a set of byte[]s where each element is either [opcode] or [data, data, data ...]
|
||||||
List<ScriptChunk> chunks;
|
List<ScriptChunk> chunks;
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
|
|
||||||
|
// Only for internal use
|
||||||
|
private Script() {
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a Script using the given network parameters and a range of the programBytes array.
|
* Construct a Script using the given network parameters and a range of the programBytes array.
|
||||||
@ -694,4 +699,56 @@ public class Script {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////// Interface used during verification of transactions/blocks ////////////////////////////////
|
||||||
|
|
||||||
|
private static int getSigOpCount(List<ScriptChunk> chunks, boolean accurate) throws ScriptException {
|
||||||
|
int sigOps = 0;
|
||||||
|
int lastOpCode = OP_INVALIDOPCODE;
|
||||||
|
for (ScriptChunk chunk : chunks) {
|
||||||
|
if (chunk.isOpCode) {
|
||||||
|
int opcode = 0xFF & chunk.data[0];
|
||||||
|
switch (opcode) {
|
||||||
|
case OP_CHECKSIG:
|
||||||
|
case OP_CHECKSIGVERIFY:
|
||||||
|
sigOps++;
|
||||||
|
break;
|
||||||
|
case OP_CHECKMULTISIG:
|
||||||
|
case OP_CHECKMULTISIGVERIFY:
|
||||||
|
if (accurate && lastOpCode >= OP_1 && lastOpCode <= OP_16)
|
||||||
|
sigOps += getOpNValue(lastOpCode);
|
||||||
|
else
|
||||||
|
sigOps += 20;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastOpCode = opcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sigOps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convince method to get the int value of OP_N
|
||||||
|
*/
|
||||||
|
private static int getOpNValue(int opcode) throws ScriptException {
|
||||||
|
if (opcode == OP_0)
|
||||||
|
return 0;
|
||||||
|
if (opcode < OP_1 || opcode > OP_16) // This should absolutely never happen
|
||||||
|
throw new ScriptException("getOpNValue called on non OP_N opcode");
|
||||||
|
return opcode + 1 - OP_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the count of regular SigOps in the script program (counting multisig ops as 20)
|
||||||
|
*/
|
||||||
|
public static int getSigOpCount(byte[] program) throws ScriptException {
|
||||||
|
Script script = new Script();
|
||||||
|
try {
|
||||||
|
script.parse(program, 0, program.length);
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
// Ignore errors and count up to the parse-able length
|
||||||
|
}
|
||||||
|
return getSigOpCount(script.chunks, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -869,4 +869,17 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
maybeParse();
|
maybeParse();
|
||||||
out.defaultWriteObject();
|
out.defaultWriteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the count of regular SigOps in this transactions
|
||||||
|
*/
|
||||||
|
public int getSigOpCount() throws ScriptException {
|
||||||
|
maybeParse();
|
||||||
|
int sigOps = 0;
|
||||||
|
for (TransactionInput input : inputs)
|
||||||
|
sigOps += Script.getSigOpCount(input.getScriptBytes());
|
||||||
|
for (TransactionOutput output : outputs)
|
||||||
|
sigOps += Script.getSigOpCount(output.getScriptBytes());
|
||||||
|
return sigOps;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user