Skip to content

Commit

Permalink
support Bitcoin raw block
Browse files Browse the repository at this point in the history
  • Loading branch information
zxh0 committed Oct 13, 2018
1 parent 4d672d9 commit d2b4f22
Show file tree
Hide file tree
Showing 21 changed files with 486 additions and 3 deletions.
3 changes: 3 additions & 0 deletions classpy-bitcoin/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
compile project(':classpy-common')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.github.zxh.classpy.bitcoin;

public class Block extends BlockComponent {

{
uint32("Version");
hash ("HashPrevBlock");
hash ("HashMerkleRoot");
uint32("Time");
uint32("Bits");
uint32("Nonce");
table ("Transactions", Transaction::new);
}

// https://en.bitcoin.it/wiki/Transaction
private class Transaction extends BlockComponent {

{
uint32("Version");
table ("Txins", TxIn::new);
table ("Txouts", TxOut::new);
uint32("LockTime");
}

}

private static class TxIn extends BlockComponent {

{
hash ("PreviousTransactionHash");
uint32("PreviousTxoutIndex");
script("TxinScript");
bytes ("SequenceNo", 4);
}

}

private static class TxOut extends BlockComponent {

{
uint64("value");
script("TxoutScript");
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.github.zxh.classpy.bitcoin;

import com.github.zxh.classpy.bitcoin.types.*;
import com.github.zxh.classpy.common.FileComponent;

import java.util.function.Supplier;

public class BlockComponent extends FileComponent {

public final void read(BlockReader reader) {
try {
int offset = reader.getPosition();
readContent(reader);
int length = reader.getPosition() - offset;
super.setOffset(offset);
super.setLength(length);
} catch (Exception e) {
System.out.println("error parsing: " + getClass());
throw e;
}
}

protected void readContent(BlockReader reader) {
for (FileComponent fc : getComponents()) {
((BlockComponent) fc).read(reader);
}
}

protected long readVarInt(BlockReader reader, String name) {
VarInt varInt = new VarInt();
add(name, varInt);
varInt.read(reader);
return varInt.getValue();
}

protected void uint32(String name) {
add(name, new UInt32());
}

protected void uint64(String name) {
add(name, new UInt64());
}

protected void hash(String name) {
add(name, new Hash());
}

protected void bytes(String name, int n) {
add(name, new Bytes(n));
}

protected void script(String name) {
add(name, new Script());
}

protected void table(String name,
Supplier<? extends BlockComponent> supplier) {
add(name, new Table(supplier));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.zxh.classpy.bitcoin;

import com.github.zxh.classpy.common.FileParser;

public class BlockParser implements FileParser {

@Override
public Block parse(byte[] data) {
Block block = new Block();
try {
block.read(new BlockReader(data));
} catch (Exception e) {
e.printStackTrace(System.err);
}
return block;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.zxh.classpy.bitcoin;

import com.github.zxh.classpy.common.BytesReader;

import java.nio.ByteOrder;

// https://en.wikipedia.org/wiki/LEB128
public class BlockReader extends BytesReader {

public BlockReader(byte[] data) {
super(data, ByteOrder.LITTLE_ENDIAN); // ?
}

public long readVarInt() {
int b = readByte() & 0xFF;
if (b < 0xFD) {
return b;
} if (b == 0xFD) {
return readUnsignedShort();
} if (b == 0xFE) {
return readUnsignedInt();
} else {
return readLong();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.github.zxh.classpy.bitcoin.types;

import com.github.zxh.classpy.bitcoin.BlockComponent;
import com.github.zxh.classpy.bitcoin.BlockReader;

public class Bytes extends BlockComponent {

private final int n;
private byte[] bytes;

public Bytes(int n) {
this.n = n;
}

public byte[] getBytes() {
return bytes;
}

@Override
protected void readContent(BlockReader reader) {
bytes = reader.readBytes(n);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.zxh.classpy.bitcoin.types;

import com.github.zxh.classpy.bitcoin.BlockComponent;
import com.github.zxh.classpy.bitcoin.BlockReader;

public class Hash extends BlockComponent {

@Override
protected void readContent(BlockReader reader) {
byte[] bytes = reader.readBytes(32);

StringBuilder sb = new StringBuilder();
for (int i = 31; i >= 0; i--) {
sb.append(Integer.toHexString(bytes[i] & 0xFF));
}
setDesc(sb.toString());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.github.zxh.classpy.bitcoin.types;

import com.github.zxh.classpy.bitcoin.BlockComponent;
import com.github.zxh.classpy.bitcoin.BlockReader;
import com.github.zxh.classpy.common.ParseException;

// https://en.bitcoin.it/wiki/Script
public class Script extends BlockComponent {

@Override
protected void readContent(BlockReader reader) {
long n = readVarInt(reader, "Length");

Bytes bytes = new Bytes((int) n);
bytes.read(reader);
byte[] code = bytes.getBytes();
try {
decodeScript(new BlockReader(code) {
int basePos = reader.getPosition() - code.length;
@Override
public int getPosition() {
return basePos + super.getPosition();
}
});
} catch (Exception e) {
e.printStackTrace(System.err);
//add("Script", bytes);
}
}

private void decodeScript(BlockReader reader) {
while (reader.remaining() > 0) {
Instr instr = new Instr();
add("Instr", instr);
instr.read(reader);
}
}


private static class Instr extends BlockComponent {

@Override
protected void readContent(BlockReader reader) {
int opcode = reader.readByte() & 0xFF;
if (opcode == 0) {
setName("OP_0"); // OP_FALSE
} else if (opcode <= 0x4B) {
setName("OP_PUSH<" + opcode + ">");
reader.readBytes(opcode);
} else if (opcode == 0x4C) {
setName("OP_PUSHDATA1");
int n = reader.readUnsignedByte();
reader.readBytes(n);
} else if (opcode == 0x4D) {
setName("OP_PUSHDATA2");
int n = reader.readUnsignedShort();
reader.readBytes(n);
} else if (opcode == 0x4E) {
setName("OP_PUSHDATA4");
int n = (int) reader.readUnsignedInt();
reader.readBytes(n);
} else {
setOpcodeName(opcode);
}
}

private void setOpcodeName(int opcode) {
switch (opcode) {
// 0x4F ~ 0x60 Constants
case 0x4F: setName("OP_1NEGATE"); break;
case 0x51: setName("OP_1"); break; // OP_TRUE
case 0x52: setName("OP_2"); break;
case 0x53: setName("OP_3"); break;
case 0x54: setName("OP_4"); break;
case 0x55: setName("OP_5"); break;
case 0x56: setName("OP_6"); break;
case 0x57: setName("OP_7"); break;
case 0x58: setName("OP_8"); break;
case 0x59: setName("OP_9"); break;
case 0x5A: setName("OP_10"); break;
case 0x5B: setName("OP_11"); break;
case 0x5C: setName("OP_12"); break;
case 0x5D: setName("OP_13"); break;
case 0x5E: setName("OP_14"); break;
case 0x5F: setName("OP_15"); break;
case 0x60: setName("OP_16"); break;
// 0x61 ~ 0x6A Flow control
case 0x61: setName("OP_NOP"); break;
case 0x63: setName("OP_IF"); break;
case 0x64: setName("OP_NOTIF"); break;
case 0x66: setName("OP_ELSE"); break;
case 0x68: setName("OP_ENDIF"); break;
case 0x69: setName("OP_VERIFY"); break;
case 0x6A: setName("OP_RETURN"); break;
// 0x6B ~ 0x7D Stack
case 0x6b: setName("OP_TOALTSTACK"); break;
case 0x6c: setName("OP_FROMALTSTACK"); break;
case 0x73: setName("OP_IFDUP"); break;
case 0x74: setName("OP_DEPTH"); break;
case 0x75: setName("OP_DROP"); break;
case 0x76: setName("OP_DUP"); break;
case 0x77: setName("OP_NIP"); break;
case 0x78: setName("OP_OVER"); break;
case 0x79: setName("OP_PICK"); break;
case 0x7a: setName("OP_ROLL"); break;
case 0x7b: setName("OP_ROT"); break;
case 0x7c: setName("OP_SWAP"); break;
case 0x7d: setName("OP_TUCK"); break;
case 0x6d: setName("OP_2DROP"); break;
case 0x6e: setName("OP_2DUP"); break;
case 0x6f: setName("OP_3DUP"); break;
case 0x70: setName("OP_2OVER"); break;
case 0x71: setName("OP_2ROT"); break;
case 0x72: setName("OP_2SWAP"); break;
// 0x7E ~ 0x82 Splice
case 0x7E: setName("OP_CAT!"); break;
case 0x7F: setName("OP_SUBSTR!"); break;
case 0x80: setName("OP_LEFT!"); break;
case 0x81: setName("OP_RIGHT!"); break;
case 0x82: setName("OP_SIZE"); break;
// 0x83 ~ 0x88 Bitwise logic
case 0x83: setName("OP_INVERT!"); break;
case 0x84: setName("OP_AND!"); break;
case 0x85: setName("OP_OR!"); break;
case 0x86: setName("OP_XOR!"); break;
case 0x87: setName("OP_EQUAL"); break;
case 0x88: setName("OP_EQUALVERIFY"); break;
// 0xA6 ~ 0xAF Crypto
case 0xA6: setName("OP_RIPEMD160"); break;
case 0xA7: setName("OP_SHA1"); break;
case 0xA8: setName("OP_SHA256"); break;
case 0xA9: setName("OP_HASH160"); break;
case 0xAA: setName("OP_HASH256"); break;
case 0xAB: setName("OP_CODESEPARATOR"); break;
case 0xAC: setName("OP_CHECKSIG"); break;
case 0xAD: setName("OP_CHECKSIGVERIFY"); break;
case 0xAE: setName("OP_CHECKMULTISIG"); break;
case 0xAF: setName("OP_CHECKMULTISIGVERIFY"); break;
default: throw new ParseException(
String.format("Invalid opcode: 0x%02X", opcode));
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.github.zxh.classpy.bitcoin.types;

import com.github.zxh.classpy.bitcoin.BlockComponent;
import com.github.zxh.classpy.bitcoin.BlockReader;

import java.util.function.Supplier;

public class Table extends BlockComponent {

private Supplier<? extends BlockComponent> supplier;
private String componentName;

public Table(Supplier<? extends BlockComponent> supplier) {
this.supplier = supplier;
componentName = supplier.get().getClass().getSimpleName();
}

@Override
protected void readContent(BlockReader reader) {
long count = readVarInt(reader, "Count");
for (long i = 0; i < count; i++) {
BlockComponent element = supplier.get();
add(componentName + "#" + i, element);
element.read(reader);
}
setDesc(Long.toString(count));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.zxh.classpy.bitcoin.types;

import com.github.zxh.classpy.bitcoin.BlockComponent;
import com.github.zxh.classpy.bitcoin.BlockReader;

public class UInt32 extends BlockComponent {

@Override
protected void readContent(BlockReader reader) {
long value = reader.readUnsignedInt();
setDesc(Long.toString(value));
}

}
Loading

0 comments on commit d2b4f22

Please sign in to comment.