3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-14 11:15:51 +00:00

Use a ScriptChunk class in place of byte[] arrays.

This commit is contained in:
Matt Corallo 2012-07-13 20:49:51 +02:00 committed by Mike Hearn
parent ca1466e628
commit 20d72d7de4
2 changed files with 41 additions and 24 deletions

View File

@ -27,6 +27,23 @@ import java.util.List;
import static com.google.bitcoin.core.Utils.bytesToHexString;
/**
* A chunk in a script
*/
class ScriptChunk {
public boolean isOpCode;
public byte[] data;
public ScriptChunk(boolean isOpCode, byte[] data) {
this.isOpCode = isOpCode;
this.data = data;
}
public boolean equalsOpCode(int opCode) {
return isOpCode &&
data.length == 1 &&
(0xFF & data[0]) == opCode;
}
}
/**
* Bitcoin transactions don't specify what they do directly. Instead <a href="https://en.bitcoin.it/wiki/Script">a
* small binary stack language</a> is used to define programs that when evaluated return whether the transaction
@ -53,8 +70,7 @@ public class Script {
private int cursor;
// The program is a set of byte[]s where each element is either [opcode] or [data, data, data ...]
// TODO: Differentiate between 1 byte data chunks and opcodes here.
List<byte[]> chunks;
List<ScriptChunk> chunks;
byte[] programCopy; // TODO: remove this
private final NetworkParameters params;
@ -77,10 +93,10 @@ public class Script {
*/
public String toString() {
StringBuffer buf = new StringBuffer();
for (byte[] chunk : chunks) {
if (chunk.length == 1) {
for (ScriptChunk chunk : chunks) {
if (chunk.isOpCode) {
String opName;
int opcode = 0xFF & chunk[0];
int opcode = 0xFF & chunk.data[0];
switch (opcode) {
case OP_DUP:
opName = "DUP";
@ -103,9 +119,9 @@ public class Script {
} else {
// Data chunk
buf.append("[");
buf.append(chunk.length);
buf.append(chunk.data.length);
buf.append("]");
buf.append(bytesToHexString(chunk));
buf.append(bytesToHexString(chunk.data));
buf.append(" ");
}
}
@ -156,7 +172,7 @@ public class Script {
program = programCopy;
offset = 0;
chunks = new ArrayList<byte[]>(10); // Arbitrary choice of initial size.
chunks = new ArrayList<ScriptChunk>(10); // Arbitrary choice of initial size.
cursor = offset;
while (cursor < offset + length) {
int opcode = readByte();
@ -167,19 +183,19 @@ public class Script {
if (opcode > 0 && opcode < OP_PUSHDATA1) {
// Read some bytes of data, where how many is the opcode value itself.
chunks.add(getData(opcode)); // opcode == len here.
chunks.add(new ScriptChunk(false, getData(opcode))); // opcode == len here.
} else if (opcode == OP_PUSHDATA1) {
int len = readByte();
chunks.add(getData(len));
chunks.add(new ScriptChunk(false, getData(len)));
} else if (opcode == OP_PUSHDATA2) {
// Read a short, then read that many bytes of data.
int len = readByte() | (readByte() << 8);
chunks.add(getData(len));
chunks.add(new ScriptChunk(false, getData(len)));
} else if (opcode == OP_PUSHDATA4) {
// Read a uint32, then read that many bytes of data.
log.error("PUSHDATA4: Unimplemented");
} else {
chunks.add(new byte[]{(byte) opcode});
chunks.add(new ScriptChunk(true, new byte[]{(byte) opcode}));
}
}
}
@ -193,7 +209,8 @@ public class Script {
public boolean isSentToRawPubKey() {
if (chunks.size() != 2)
return false;
return (0xFF & chunks.get(1)[0]) == OP_CHECKSIG && chunks.get(0).length > 1;
return chunks.get(1).equalsOpCode(OP_CHECKSIG) &&
!chunks.get(0).isOpCode && chunks.get(0).data.length > 1;
}
/**
@ -204,11 +221,11 @@ public class Script {
*/
public boolean isSentToAddress() {
if (chunks.size() != 5) return false;
return (0xFF & chunks.get(0)[0]) == OP_DUP &&
(0xFF & chunks.get(1)[0]) == OP_HASH160 &&
chunks.get(2).length == Address.LENGTH &&
(0xFF & chunks.get(3)[0]) == OP_EQUALVERIFY &&
(0xFF & chunks.get(4)[0]) == OP_CHECKSIG;
return chunks.get(0).equalsOpCode(OP_DUP) &&
chunks.get(1).equalsOpCode(OP_HASH160) &&
chunks.get(2).data.length == Address.LENGTH &&
chunks.get(3).equalsOpCode(OP_EQUALVERIFY) &&
chunks.get(4).equalsOpCode(OP_CHECKSIG);
}
/**
@ -221,7 +238,7 @@ public class Script {
if (!isSentToAddress())
throw new ScriptException("Script not in the standard scriptPubKey form");
// Otherwise, the third element is the hash of the public key, ie the bitcoin address.
return chunks.get(2);
return chunks.get(2).data;
}
/**
@ -236,12 +253,12 @@ public class Script {
if (chunks.size() != 2) {
throw new ScriptException("Script not of right size, expecting 2 but got " + chunks.size());
}
if (chunks.get(0).length > 2 && chunks.get(1).length > 2) {
if (chunks.get(0).data.length > 2 && chunks.get(1).data.length > 2) {
// If we have two large constants assume the input to a pay-to-address output.
return chunks.get(1);
} else if (chunks.get(1).length == 1 && (0xFF & chunks.get(1)[0]) == OP_CHECKSIG && chunks.get(0).length > 2) {
return chunks.get(1).data;
} else if (chunks.get(1).data.length == 1 && chunks.get(1).equalsOpCode(OP_CHECKSIG) && chunks.get(0).data.length > 2) {
// A large constant followed by an OP_CHECKSIG is the key.
return chunks.get(0);
return chunks.get(0).data;
} else {
throw new ScriptException("Script did not match expected form: " + toString());
}

View File

@ -699,7 +699,7 @@ public class WalletTest {
assertNotNull(t2);
// TODO: This code is messy, improve the Script class and fixinate!
assertEquals(t2.toString(), 1, t2.getInputs().get(0).getScriptSig().chunks.size());
assertTrue(t2.getInputs().get(0).getScriptSig().chunks.get(0).length > 50);
assertTrue(t2.getInputs().get(0).getScriptSig().chunks.get(0).data.length > 50);
System.out.println(t2);
}