mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2024-11-19 18:00:39 +01: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.
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL;
|
||||
|
||||
@ -569,6 +582,21 @@ public class Block extends Message {
|
||||
if (time > currentTime + ALLOWED_TIME_DRIFT)
|
||||
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 {
|
||||
Sha256Hash calculatedRoot = calculateMerkleRoot();
|
||||
@ -683,6 +711,7 @@ public class Block extends Message {
|
||||
maybeParseTransactions();
|
||||
checkTransactions();
|
||||
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 ...]
|
||||
List<ScriptChunk> chunks;
|
||||
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.
|
||||
@ -694,4 +699,56 @@ public class Script {
|
||||
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();
|
||||
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…
Reference in New Issue
Block a user