3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 23:32:16 +00:00

Script: ScriptChunk -> Script.Chunk and make it public.

This commit is contained in:
Mike Hearn 2013-04-22 14:46:58 +02:00
parent 80285832f8
commit a0d2b36c9f

View File

@ -35,23 +35,6 @@ import static com.google.common.base.Preconditions.checkArgument;
// TODO: Make this class a superclass with derived classes giving accessor methods for the various common templates. // TODO: Make this class a superclass with derived classes giving accessor methods for the various common templates.
/**
* A chunk in a script
*/
class ScriptChunk {
public boolean isOpCode;
public byte[] data;
public int startLocationInProgram;
public ScriptChunk(boolean isOpCode, byte[] data, int startLocationInProgram) {
this.isOpCode = isOpCode;
this.data = data;
this.startLocationInProgram = startLocationInProgram;
}
public boolean equalsOpCode(int opCode) {
return isOpCode && data.length == 1 && (0xFF & data[0]) == opCode;
}
}
/** /**
* <p>Programs embedded inside transactions that control redemption of payments.</p> * <p>Programs embedded inside transactions that control redemption of payments.</p>
* *
@ -66,11 +49,28 @@ class ScriptChunk {
public class Script { public class Script {
private static final Logger log = LoggerFactory.getLogger(Script.class); private static final Logger log = LoggerFactory.getLogger(Script.class);
/**
* An element that is either an opcode or a raw byte array (signature, pubkey, etc).
*/
public static class Chunk {
public boolean isOpCode;
public byte[] data;
public int startLocationInProgram;
public Chunk(boolean isOpCode, byte[] data, int startLocationInProgram) {
this.isOpCode = isOpCode;
this.data = data;
this.startLocationInProgram = startLocationInProgram;
}
public boolean equalsOpCode(int opCode) {
return isOpCode && data.length == 1 && (0xFF & data[0]) == opCode;
}
}
private byte[] program; private byte[] program;
private int cursor; private int cursor;
// The program is a set of byte[]s where each element is either [opcode] or [data, data, data ...] // The program is a set of chunks where each element is either [opcode] or [data, data, data ...]
List<ScriptChunk> chunks; List<Chunk> chunks;
// Only for internal use // Only for internal use
private Script() {} private Script() {}
@ -89,7 +89,7 @@ public class Script {
*/ */
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
for (ScriptChunk chunk : chunks) { for (Chunk chunk : chunks) {
if (chunk.isOpCode) { if (chunk.isOpCode) {
buf.append(getOpCodeName(chunk.data[0])); buf.append(getOpCodeName(chunk.data[0]));
buf.append(" "); buf.append(" ");
@ -151,28 +151,28 @@ public class Script {
System.arraycopy(programBytes, offset, program, 0, length); System.arraycopy(programBytes, offset, program, 0, length);
offset = 0; offset = 0;
chunks = new ArrayList<ScriptChunk>(10); // Arbitrary choice of initial size. chunks = new ArrayList<Chunk>(10); // Arbitrary choice of initial size.
cursor = offset; cursor = offset;
while (cursor < offset + length) { while (cursor < offset + length) {
int startLocationInProgram = cursor - offset; int startLocationInProgram = cursor - offset;
int opcode = readByte(); int opcode = readByte();
if (opcode >= 0 && opcode < OP_PUSHDATA1) { if (opcode >= 0 && opcode < OP_PUSHDATA1) {
// Read some bytes of data, where how many is the opcode value itself. // Read some bytes of data, where how many is the opcode value itself.
chunks.add(new ScriptChunk(false, getData(opcode), startLocationInProgram)); // opcode == len here. chunks.add(new Chunk(false, getData(opcode), startLocationInProgram)); // opcode == len here.
} else if (opcode == OP_PUSHDATA1) { } else if (opcode == OP_PUSHDATA1) {
int len = readByte(); int len = readByte();
chunks.add(new ScriptChunk(false, getData(len), startLocationInProgram)); chunks.add(new Chunk(false, getData(len), startLocationInProgram));
} else if (opcode == OP_PUSHDATA2) { } else if (opcode == OP_PUSHDATA2) {
// Read a short, then read that many bytes of data. // Read a short, then read that many bytes of data.
int len = readByte() | (readByte() << 8); int len = readByte() | (readByte() << 8);
chunks.add(new ScriptChunk(false, getData(len), startLocationInProgram)); chunks.add(new Chunk(false, getData(len), startLocationInProgram));
} else if (opcode == OP_PUSHDATA4) { } else if (opcode == OP_PUSHDATA4) {
// Read a uint32, then read that many bytes of data. // Read a uint32, then read that many bytes of data.
// Though this is allowed, because its value cannot be > 520, it should never actually be used // Though this is allowed, because its value cannot be > 520, it should never actually be used
long len = readByte() | (readByte() << 8) | (readByte() << 16) | (readByte() << 24); long len = readByte() | (readByte() << 8) | (readByte() << 16) | (readByte() << 24);
chunks.add(new ScriptChunk(false, getData((int)len), startLocationInProgram)); chunks.add(new Chunk(false, getData((int)len), startLocationInProgram));
} else { } else {
chunks.add(new ScriptChunk(true, new byte[]{(byte) opcode}, startLocationInProgram)); chunks.add(new Chunk(true, new byte[]{(byte) opcode}, startLocationInProgram));
} }
} }
} }
@ -369,10 +369,10 @@ public class Script {
////////////////////// Interface used during verification of transactions/blocks //////////////////////////////// ////////////////////// Interface used during verification of transactions/blocks ////////////////////////////////
private static int getSigOpCount(List<ScriptChunk> chunks, boolean accurate) throws ScriptException { private static int getSigOpCount(List<Chunk> chunks, boolean accurate) throws ScriptException {
int sigOps = 0; int sigOps = 0;
int lastOpCode = OP_INVALIDOPCODE; int lastOpCode = OP_INVALIDOPCODE;
for (ScriptChunk chunk : chunks) { for (Chunk chunk : chunks) {
if (chunk.isOpCode) { if (chunk.isOpCode) {
int opcode = 0xFF & chunk.data[0]; int opcode = 0xFF & chunk.data[0];
switch (opcode) { switch (opcode) {
@ -475,13 +475,13 @@ public class Script {
*/ */
public boolean isSentToMultiSig() { public boolean isSentToMultiSig() {
if (chunks.size() < 4) return false; if (chunks.size() < 4) return false;
ScriptChunk chunk = chunks.get(chunks.size() - 1); Chunk chunk = chunks.get(chunks.size() - 1);
// Must end in OP_CHECKMULTISIG[VERIFY]. // Must end in OP_CHECKMULTISIG[VERIFY].
if (!chunk.isOpCode) return false; if (!chunk.isOpCode) return false;
if (!(chunk.equalsOpCode(OP_CHECKMULTISIG) || chunk.equalsOpCode(OP_CHECKMULTISIGVERIFY))) return false; if (!(chunk.equalsOpCode(OP_CHECKMULTISIG) || chunk.equalsOpCode(OP_CHECKMULTISIGVERIFY))) return false;
try { try {
// Second to last chunk must be an OP_N opcode and there should be that many data chunks (keys). // Second to last chunk must be an OP_N opcode and there should be that many data chunks (keys).
ScriptChunk m = chunks.get(chunks.size() - 2); Chunk m = chunks.get(chunks.size() - 2);
if (!m.isOpCode) return false; if (!m.isOpCode) return false;
int numKeys = decodeFromOpN(m.data[0]); int numKeys = decodeFromOpN(m.data[0]);
if (chunks.size() != 3 + numKeys) return false; if (chunks.size() != 3 + numKeys) return false;
@ -577,7 +577,7 @@ public class Script {
LinkedList<byte[]> altstack = new LinkedList<byte[]>(); LinkedList<byte[]> altstack = new LinkedList<byte[]>();
LinkedList<Boolean> ifStack = new LinkedList<Boolean>(); LinkedList<Boolean> ifStack = new LinkedList<Boolean>();
for (ScriptChunk chunk : script.chunks) { for (Chunk chunk : script.chunks) {
boolean shouldExecute = !ifStack.contains(false); boolean shouldExecute = !ifStack.contains(false);
if (!chunk.isOpCode) { if (!chunk.isOpCode) {
@ -1225,7 +1225,7 @@ public class Script {
// TODO: Check if we can take out enforceP2SH if there's a checkpoint at the enforcement block. // TODO: Check if we can take out enforceP2SH if there's a checkpoint at the enforcement block.
if (enforceP2SH && scriptPubKey.isPayToScriptHash()) { if (enforceP2SH && scriptPubKey.isPayToScriptHash()) {
for (ScriptChunk chunk : chunks) for (Chunk chunk : chunks)
if (chunk.isOpCode && (chunk.data[0] & 0xff) > OP_16) if (chunk.isOpCode && (chunk.data[0] & 0xff) > OP_16)
throw new ScriptException("Attempted to spend a P2SH scriptPubKey with a script that contained script ops"); throw new ScriptException("Attempted to spend a P2SH scriptPubKey with a script that contained script ops");