diff --git a/classpy-bitcoin/build.gradle b/classpy-bitcoin/build.gradle new file mode 100644 index 00000000..35336e69 --- /dev/null +++ b/classpy-bitcoin/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(':classpy-common') +} \ No newline at end of file diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/Block.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/Block.java new file mode 100644 index 00000000..83e912ba --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/Block.java @@ -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"); + } + + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockComponent.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockComponent.java new file mode 100644 index 00000000..f749668a --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockComponent.java @@ -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 supplier) { + add(name, new Table(supplier)); + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockParser.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockParser.java new file mode 100644 index 00000000..6a2646ab --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockParser.java @@ -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; + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockReader.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockReader.java new file mode 100644 index 00000000..47f5a94c --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockReader.java @@ -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(); + } + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Bytes.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Bytes.java new file mode 100644 index 00000000..3493d097 --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Bytes.java @@ -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); + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Hash.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Hash.java new file mode 100644 index 00000000..110c7dbb --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Hash.java @@ -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()); + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Script.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Script.java new file mode 100644 index 00000000..cf26755d --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Script.java @@ -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)); + } + } + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Table.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Table.java new file mode 100644 index 00000000..6cbc4103 --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Table.java @@ -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 supplier; + private String componentName; + + public Table(Supplier 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)); + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt32.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt32.java new file mode 100644 index 00000000..17701c92 --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt32.java @@ -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)); + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt64.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt64.java new file mode 100644 index 00000000..ff606c34 --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt64.java @@ -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 UInt64 extends BlockComponent { + + @Override + protected void readContent(BlockReader reader) { + long value = reader.readLong(); + setDesc(Long.toString(value)); + } + +} diff --git a/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/VarInt.java b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/VarInt.java new file mode 100644 index 00000000..9b989d57 --- /dev/null +++ b/classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/VarInt.java @@ -0,0 +1,21 @@ +package com.github.zxh.classpy.bitcoin.types; + +import com.github.zxh.classpy.bitcoin.BlockComponent; +import com.github.zxh.classpy.bitcoin.BlockReader; + +// https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer +public class VarInt extends BlockComponent { + + private long value; + + public long getValue() { + return value; + } + + @Override + protected void readContent(BlockReader reader) { + value = reader.readVarInt(); + setDesc(Long.toString(value)); + } + +} diff --git a/classpy-common/src/main/java/com/github/zxh/classpy/helper/StringHelper.java b/classpy-common/src/main/java/com/github/zxh/classpy/helper/StringHelper.java index 2c14a92d..317b2029 100644 --- a/classpy-common/src/main/java/com/github/zxh/classpy/helper/StringHelper.java +++ b/classpy-common/src/main/java/com/github/zxh/classpy/helper/StringHelper.java @@ -45,5 +45,19 @@ public static String cutAndAppendEllipsis(String str, int maxLength) { return str.substring(0, cutPos) + "..."; } } - + + /** + * Convert hex string to byte array. + * @param str + * @return + */ + public static byte[] hex2Bytes(String str) { + byte[] bytes = new byte[str.length() / 2]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) Integer.parseInt( + str.substring(2 * i, 2 * i + 2), 16); + } + return bytes; + } + } diff --git a/classpy-common/src/main/java/com/github/zxh/classpy/helper/UrlHelper.java b/classpy-common/src/main/java/com/github/zxh/classpy/helper/UrlHelper.java index cff360f3..59477bf0 100644 --- a/classpy-common/src/main/java/com/github/zxh/classpy/helper/UrlHelper.java +++ b/classpy-common/src/main/java/com/github/zxh/classpy/helper/UrlHelper.java @@ -1,11 +1,19 @@ package com.github.zxh.classpy.helper; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; public class UrlHelper { + public static String readOneLine(URL url) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) { + return reader.readLine(); + } + } + public static byte[] readData(URL url) throws IOException { try (InputStream is = url.openStream()) { byte[] data = new byte[is.available()]; diff --git a/classpy-gui/build.gradle b/classpy-gui/build.gradle index 09e00040..5962a35d 100644 --- a/classpy-gui/build.gradle +++ b/classpy-gui/build.gradle @@ -13,6 +13,7 @@ dependencies { compile project(':classpy-common') compile project(':classpy-classfile') compile project(':classpy-binarychunk') + compile project(':classpy-bitcoin') compile project(':classpy-wasm') // compile "org.openjfx:javafx-base:11:${platform}" diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java index 8f7b550c..0d1b5d0b 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java @@ -7,9 +7,12 @@ import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; +import javafx.scene.control.TextInputDialog; import javafx.scene.image.ImageView; +import java.net.MalformedURLException; import java.net.URL; +import java.util.Optional; import java.util.function.BiConsumer; /** @@ -47,6 +50,7 @@ private Menu createOpenMenu() { openMenu.getItems().add(createOpenMenuItem(FileType.JAVA_CLASS)); openMenu.getItems().add(createOpenMenuItem(FileType.LUA_BC)); openMenu.getItems().add(createOpenMenuItem(FileType.WASM)); + openMenu.getItems().add(createOpenBcBlockMenuItem()); openMenu.setMnemonicParsing(true); return openMenu; } @@ -94,6 +98,30 @@ private void createHelpMenu() { getMenus().add(helpMenu); } + private MenuItem createOpenBcBlockMenuItem() { + String apiUrl = "https://blockchain.info/rawblock/?format=hex"; + String genesisBlockHash = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"; + + MenuItem item = createOpenMenuItem(FileType.BITCOIN_BLOCK); + item.setOnAction(e -> { + TextInputDialog dialog = new TextInputDialog(genesisBlockHash); + dialog.setTitle("Block Hash Input Dialog"); + dialog.setHeaderText("API: " + apiUrl); + dialog.setContentText("hash: "); + dialog.setResizable(true); + + // Traditional way to get the response value. + Optional result = dialog.showAndWait(); + if (result.isPresent()){ + try { + String url = apiUrl.replace("", result.get()); + onOpenFile.accept(FileType.BITCOIN_BLOCK, new URL(url)); + } catch (MalformedURLException ignored) {} + } + }); + return item; + } + public void setOnOpenFile(BiConsumer onOpenFile) { this.onOpenFile = onOpenFile; } diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java index d5319279..899470f8 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java @@ -12,6 +12,7 @@ public enum FileType { JAVA_CLASS("/java.png", "Java Class", "*.class"), LUA_BC("/lua.png", "Lua Binary Chunk", "*.luac"), WASM("/wasm.png", "WebAssembly Binary Code", "*.wasm"), + BITCOIN_BLOCK("/bitcoin.png", "Bitcoin Block", "?"), UNKNOWN("/file.png", "Unknown", "*.*"), ; diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java index 73006732..a784c7ba 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java @@ -16,6 +16,9 @@ public class FileTypeInferer { public static FileType inferFileType(URL url) { String filename = url.toString().toLowerCase(); + if (filename.startsWith("https://blockchain.info/rawblock")) { + return FileType.BITCOIN_BLOCK; + } if (filename.endsWith(".jar")) { return FileType.JAVA_JAR; } diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java index aadd85b7..2f17b57a 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java @@ -1,10 +1,12 @@ package com.github.zxh.classpy.gui.support; +import com.github.zxh.classpy.bitcoin.BlockParser; import com.github.zxh.classpy.classfile.ClassFileParser; import com.github.zxh.classpy.common.FileComponent; import com.github.zxh.classpy.gui.jar.JarTreeLoader; import com.github.zxh.classpy.gui.jar.JarTreeNode; import com.github.zxh.classpy.gui.parsed.HexText; +import com.github.zxh.classpy.helper.StringHelper; import com.github.zxh.classpy.helper.UrlHelper; import com.github.zxh.classpy.lua.binarychunk.BinaryChunkParser; @@ -33,7 +35,9 @@ protected OpenFileResult call() throws Exception { return new OpenFileResult(url, fileType, rootNode); } - byte[] data = UrlHelper.readData(url); + byte[] data = fileType == FileType.BITCOIN_BLOCK + ? StringHelper.hex2Bytes(UrlHelper.readOneLine(url)) + : UrlHelper.readData(url); if (fileType == FileType.UNKNOWN) { fileType = FileTypeInferer.inferFileType(data); } @@ -51,6 +55,7 @@ private static FileComponent parse(byte[] data, FileType fileType) { case JAVA_CLASS: return new ClassFileParser().parse(data); case LUA_BC: return new BinaryChunkParser().parse(data); case WASM: return new WasmBinParser().parse(data); + case BITCOIN_BLOCK: return new BlockParser().parse(data); default: return new FileComponent() {}; // todo } } diff --git a/classpy-gui/src/main/resources/bitcoin.png b/classpy-gui/src/main/resources/bitcoin.png new file mode 100644 index 00000000..3509837b Binary files /dev/null and b/classpy-gui/src/main/resources/bitcoin.png differ diff --git a/settings.gradle b/settings.gradle index 52a03dd3..e067a939 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,5 +3,6 @@ rootProject.name = 'classpy' include 'classpy-common' include 'classpy-classfile' include 'classpy-binarychunk' +include 'classpy-bitcoin' include 'classpy-wasm' -include 'classpy-gui' \ No newline at end of file +include 'classpy-gui'