Add evmtool pretty-print support for legacy code. (#8398)

* Add pretty-print support for legacy code.

Add support for non-EOF contained code to `evmtool pretty-print`.

Signed-off-by: Danno Ferrin <danno@numisight.com>

* Add junit test

Signed-off-by: Danno Ferrin <danno@numisight.com>

* JUMPDEST hadling

Signed-off-by: Danno Ferrin <danno@numisight.com>

---------

Signed-off-by: Danno Ferrin <danno@numisight.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
This commit is contained in:
Danno Ferrin
2025-03-12 16:54:42 -06:00
committed by GitHub
parent 2db46e964c
commit 9bd3c166fc
4 changed files with 332 additions and 11 deletions

View File

@@ -20,6 +20,9 @@ import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.JumpDestOperation;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.function.Supplier;
@@ -340,6 +343,43 @@ public class CodeV0 implements Code {
@Override
public String prettyPrint() {
return bytes.toHexString();
int i = 0;
int len = bytes.size();
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("0x # Legacy EVM Code");
while (i < len) {
i += printInstruction(i, ps);
}
return out.toString(StandardCharsets.UTF_8);
}
/**
* Prints an individual instruction, including immediate data
*
* @param offset Offset within the code
* @param out the print stream to write to
* @return the number of bytes to advance the PC (includes consideration of immediate arguments)
*/
public int printInstruction(final int offset, final PrintStream out) {
int codeByte = bytes.get(offset) & 0xff;
OpcodeInfo info = OpcodeInfo.getLegacyOpcode(codeByte);
String push = "";
String decimalPush = "";
if (info.pcAdvance() > 1) {
int start = Math.min(bytes.size(), offset + 1);
int end = Math.min(bytes.size(), info.pcAdvance() - 1);
Bytes slice = bytes.slice(start, end);
push = slice.toUnprefixedHexString();
if (info.pcAdvance() < 5) {
decimalPush = "(" + slice.toLong() + ")";
}
}
String name = info.name();
if (codeByte == 0x5b) {
name = "JUMPDEST";
}
out.printf("%02x%s # [ %d ] %s%s%n", codeByte, push, offset, name, decimalPush);
return Math.max(1, info.pcAdvance());
}
}

View File

@@ -333,4 +333,274 @@ public record OpcodeInfo(
OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, 1),
OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff),
};
/**
* Gets the opcode info for a specific opcode
*
* @param i opcode
* @return the OpcodeInfo object describing that opcode
*/
public static OpcodeInfo getLegacyOpcode(final int i) {
Preconditions.checkArgument(i >= 0 && i <= 255);
return V0_OPCODES[i];
}
static final OpcodeInfo[] V0_OPCODES = {
OpcodeInfo.validOpcode("STOP", 0x00, 0, 0, 1),
OpcodeInfo.validOpcode("ADD", 0x01, 2, 1, 1),
OpcodeInfo.validOpcode("MUL", 0x02, 2, 1, 1),
OpcodeInfo.validOpcode("SUB", 0x03, 2, 1, 1),
OpcodeInfo.validOpcode("DIV", 0x04, 2, 1, 1),
OpcodeInfo.validOpcode("SDIV", 0x05, 2, 1, 1),
OpcodeInfo.validOpcode("MOD", 0x06, 2, 1, 1),
OpcodeInfo.validOpcode("SMOD", 0x07, 2, 1, 1),
OpcodeInfo.validOpcode("ADDMOD", 0x08, 3, 1, 1),
OpcodeInfo.validOpcode("MULMOD", 0x09, 3, 1, 1),
OpcodeInfo.validOpcode("EXP", 0x0a, 2, 1, 1),
OpcodeInfo.validOpcode("SIGNEXTEND", 0x0b, 2, 1, 1),
OpcodeInfo.unallocatedOpcode(0x0c),
OpcodeInfo.unallocatedOpcode(0x0d),
OpcodeInfo.unallocatedOpcode(0x0e),
OpcodeInfo.unallocatedOpcode(0x0f),
OpcodeInfo.validOpcode("LT", 0x10, 2, 1, 1),
OpcodeInfo.validOpcode("GT", 0x11, 2, 1, 1),
OpcodeInfo.validOpcode("SLT", 0x12, 2, 1, 1),
OpcodeInfo.validOpcode("SGT", 0x13, 2, 1, 1),
OpcodeInfo.validOpcode("EQ", 0x14, 2, 1, 1),
OpcodeInfo.validOpcode("ISZERO", 0x15, 1, 1, 1),
OpcodeInfo.validOpcode("AND", 0x16, 2, 1, 1),
OpcodeInfo.validOpcode("OR", 0x17, 2, 1, 1),
OpcodeInfo.validOpcode("XOR", 0x18, 2, 1, 1),
OpcodeInfo.validOpcode("NOT", 0x19, 1, 1, 1),
OpcodeInfo.validOpcode("BYTE", 0x1a, 2, 1, 1),
OpcodeInfo.validOpcode("SHL", 0x1b, 2, 1, 1),
OpcodeInfo.validOpcode("SHR", 0x1c, 2, 1, 1),
OpcodeInfo.validOpcode("SAR", 0x1d, 2, 1, 1),
OpcodeInfo.unallocatedOpcode(0x1e),
OpcodeInfo.unallocatedOpcode(0x1f),
OpcodeInfo.validOpcode("SHA3", 0x20, 2, 1, 1),
OpcodeInfo.unallocatedOpcode(0x21),
OpcodeInfo.unallocatedOpcode(0x22),
OpcodeInfo.unallocatedOpcode(0x23),
OpcodeInfo.unallocatedOpcode(0x24),
OpcodeInfo.unallocatedOpcode(0x25),
OpcodeInfo.unallocatedOpcode(0x26),
OpcodeInfo.unallocatedOpcode(0x27),
OpcodeInfo.unallocatedOpcode(0x28),
OpcodeInfo.unallocatedOpcode(0x29),
OpcodeInfo.unallocatedOpcode(0x2a),
OpcodeInfo.unallocatedOpcode(0x2b),
OpcodeInfo.unallocatedOpcode(0x2c),
OpcodeInfo.unallocatedOpcode(0x2d),
OpcodeInfo.unallocatedOpcode(0x2e),
OpcodeInfo.unallocatedOpcode(0x2f),
OpcodeInfo.validOpcode("ADDRESS", 0x30, 0, 1, 1),
OpcodeInfo.validOpcode("BALANCE", 0x31, 1, 1, 1),
OpcodeInfo.validOpcode("ORIGIN", 0x32, 0, 1, 1),
OpcodeInfo.validOpcode("CALLER", 0x33, 0, 1, 1),
OpcodeInfo.validOpcode("CALLVALUE", 0x34, 0, 1, 1),
OpcodeInfo.validOpcode("CALLDATALOAD", 0x35, 1, 1, 1),
OpcodeInfo.validOpcode("CALLDATASIZE", 0x36, 0, 1, 1),
OpcodeInfo.validOpcode("CALLDATACOPY", 0x37, 3, 0, 1),
OpcodeInfo.validOpcode("CODESIZE", 0x38, 1, 1, 1),
OpcodeInfo.validOpcode("CODECOPY", 0x39, 3, 1, 1),
OpcodeInfo.validOpcode("GASPRICE", 0x3a, 0, 1, 1),
OpcodeInfo.validOpcode("EXTCODESIZE", 0x3b, 1, 1, 1),
OpcodeInfo.validOpcode("EXTCODECOPY", 0x3c, 4, 0, 1),
OpcodeInfo.validOpcode("RETURNDATASIZE", 0x3d, 0, 1, 1),
OpcodeInfo.validOpcode("RETURNDATACOPY", 0x3e, 3, 0, 1),
OpcodeInfo.validOpcode("EXTCODEHASH", 0x3f, 1, 1, 1),
OpcodeInfo.validOpcode("BLOCKHASH", 0x40, 1, 1, 1),
OpcodeInfo.validOpcode("COINBASE", 0x41, 0, 1, 1),
OpcodeInfo.validOpcode("TIMESTAMP", 0x42, 0, 1, 1),
OpcodeInfo.validOpcode("NUMBER", 0x43, 0, 1, 1),
OpcodeInfo.validOpcode("PREVRANDAO", 0x44, 0, 1, 1), // was DIFFICULTY
OpcodeInfo.validOpcode("GASLIMIT", 0x45, 0, 1, 1),
OpcodeInfo.validOpcode("CHAINID", 0x46, 0, 1, 1),
OpcodeInfo.validOpcode("SELFBALANCE", 0x47, 0, 1, 1),
OpcodeInfo.validOpcode("BASEFEE", 0x48, 0, 1, 1),
OpcodeInfo.validOpcode("BLOBAHASH", 0x49, 1, 1, 1),
OpcodeInfo.validOpcode("BLOBBASEFEE", 0x4a, 0, 1, 1),
OpcodeInfo.unallocatedOpcode(0x4b),
OpcodeInfo.unallocatedOpcode(0x4c),
OpcodeInfo.unallocatedOpcode(0x4d),
OpcodeInfo.unallocatedOpcode(0x4e),
OpcodeInfo.unallocatedOpcode(0x4f),
OpcodeInfo.validOpcode("POP", 0x50, 1, 0, 1),
OpcodeInfo.validOpcode("MLOAD", 0x51, 1, 1, 1),
OpcodeInfo.validOpcode("MSTORE", 0x52, 2, 0, 1),
OpcodeInfo.validOpcode("MSTORE8", 0x53, 2, 0, 1),
OpcodeInfo.validOpcode("SLOAD", 0x54, 1, 1, 1),
OpcodeInfo.validOpcode("SSTORE", 0x55, 2, 0, 1),
OpcodeInfo.validOpcode("JUMP", 0x56, 1, 0, 1),
OpcodeInfo.validOpcode("JUMPI", 0x57, 2, 0, 1),
OpcodeInfo.validOpcode("PC", 0x58, 0, 1, 1),
OpcodeInfo.validOpcode("MSIZE", 0x59, 0, 1, 1),
OpcodeInfo.validOpcode("GAS", 0x5a, 0, 1, 1),
OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST
OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1),
OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1),
OpcodeInfo.validOpcode("MCOPY", 0x5e, 3, 0, 1),
OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1),
OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2),
OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3),
OpcodeInfo.validOpcode("PUSH3", 0x62, 0, 1, 4),
OpcodeInfo.validOpcode("PUSH4", 0x63, 0, 1, 5),
OpcodeInfo.validOpcode("PUSH5", 0x64, 0, 1, 6),
OpcodeInfo.validOpcode("PUSH6", 0x65, 0, 1, 7),
OpcodeInfo.validOpcode("PUSH7", 0x66, 0, 1, 8),
OpcodeInfo.validOpcode("PUSH8", 0x67, 0, 1, 9),
OpcodeInfo.validOpcode("PUSH9", 0x68, 0, 1, 10),
OpcodeInfo.validOpcode("PUSH10", 0x69, 0, 1, 11),
OpcodeInfo.validOpcode("PUSH11", 0x6a, 0, 1, 12),
OpcodeInfo.validOpcode("PUSH12", 0x6b, 0, 1, 13),
OpcodeInfo.validOpcode("PUSH13", 0x6c, 0, 1, 14),
OpcodeInfo.validOpcode("PUSH14", 0x6d, 0, 1, 15),
OpcodeInfo.validOpcode("PUSH15", 0x6e, 0, 1, 16),
OpcodeInfo.validOpcode("PUSH16", 0x6f, 0, 1, 17),
OpcodeInfo.validOpcode("PUSH17", 0x70, 0, 1, 18),
OpcodeInfo.validOpcode("PUSH18", 0x71, 0, 1, 19),
OpcodeInfo.validOpcode("PUSH19", 0x72, 0, 1, 20),
OpcodeInfo.validOpcode("PUSH20", 0x73, 0, 1, 21),
OpcodeInfo.validOpcode("PUSH21", 0x74, 0, 1, 22),
OpcodeInfo.validOpcode("PUSH22", 0x75, 0, 1, 23),
OpcodeInfo.validOpcode("PUSH23", 0x76, 0, 1, 24),
OpcodeInfo.validOpcode("PUSH24", 0x77, 0, 1, 25),
OpcodeInfo.validOpcode("PUSH25", 0x78, 0, 1, 26),
OpcodeInfo.validOpcode("PUSH26", 0x79, 0, 1, 27),
OpcodeInfo.validOpcode("PUSH27", 0x7a, 0, 1, 28),
OpcodeInfo.validOpcode("PUSH28", 0x7b, 0, 1, 29),
OpcodeInfo.validOpcode("PUSH29", 0x7c, 0, 1, 30),
OpcodeInfo.validOpcode("PUSH30", 0x7d, 0, 1, 31),
OpcodeInfo.validOpcode("PUSH31", 0x7e, 0, 1, 32),
OpcodeInfo.validOpcode("PUSH32", 0x7f, 0, 1, 33),
OpcodeInfo.validOpcode("DUP1", 0x80, 1, 2, 1),
OpcodeInfo.validOpcode("DUP2", 0x81, 2, 3, 1),
OpcodeInfo.validOpcode("DUP3", 0x82, 3, 4, 1),
OpcodeInfo.validOpcode("DUP4", 0x83, 4, 5, 1),
OpcodeInfo.validOpcode("DUP5", 0x84, 5, 6, 1),
OpcodeInfo.validOpcode("DUP6", 0x85, 6, 7, 1),
OpcodeInfo.validOpcode("DUP7", 0x86, 7, 8, 1),
OpcodeInfo.validOpcode("DUP8", 0x87, 8, 9, 1),
OpcodeInfo.validOpcode("DUP9", 0x88, 9, 10, 1),
OpcodeInfo.validOpcode("DUP10", 0x89, 10, 11, 1),
OpcodeInfo.validOpcode("DUP11", 0x8a, 11, 12, 1),
OpcodeInfo.validOpcode("DUP12", 0x8b, 12, 13, 1),
OpcodeInfo.validOpcode("DUP13", 0x8c, 13, 14, 1),
OpcodeInfo.validOpcode("DUP14", 0x8d, 14, 15, 1),
OpcodeInfo.validOpcode("DUP15", 0x8e, 15, 16, 1),
OpcodeInfo.validOpcode("DUP16", 0x8f, 16, 17, 1),
OpcodeInfo.validOpcode("SWAP1", 0x90, 2, 2, 1),
OpcodeInfo.validOpcode("SWAP2", 0x91, 3, 3, 1),
OpcodeInfo.validOpcode("SWAP3", 0x92, 4, 4, 1),
OpcodeInfo.validOpcode("SWAP4", 0x93, 5, 5, 1),
OpcodeInfo.validOpcode("SWAP5", 0x94, 6, 6, 1),
OpcodeInfo.validOpcode("SWAP6", 0x95, 7, 7, 1),
OpcodeInfo.validOpcode("SWAP7", 0x96, 8, 8, 1),
OpcodeInfo.validOpcode("SWAP8", 0x97, 9, 9, 1),
OpcodeInfo.validOpcode("SWAP9", 0x98, 10, 10, 1),
OpcodeInfo.validOpcode("SWAP10", 0x99, 11, 11, 1),
OpcodeInfo.validOpcode("SWAP11", 0x9a, 12, 12, 1),
OpcodeInfo.validOpcode("SWAP12", 0x9b, 13, 13, 1),
OpcodeInfo.validOpcode("SWAP13", 0x9c, 14, 14, 1),
OpcodeInfo.validOpcode("SWAP14", 0x9d, 15, 15, 1),
OpcodeInfo.validOpcode("SWAP15", 0x9e, 16, 16, 1),
OpcodeInfo.validOpcode("SWAP16", 0x9f, 17, 17, 1),
OpcodeInfo.validOpcode("LOG0", 0xa0, 2, 0, 1),
OpcodeInfo.validOpcode("LOG1", 0xa1, 3, 0, 1),
OpcodeInfo.validOpcode("LOG2", 0xa2, 4, 0, 1),
OpcodeInfo.validOpcode("LOG3", 0xa3, 5, 0, 1),
OpcodeInfo.validOpcode("LOG4", 0xa4, 6, 0, 1),
OpcodeInfo.unallocatedOpcode(0xa5),
OpcodeInfo.unallocatedOpcode(0xa6),
OpcodeInfo.unallocatedOpcode(0xa7),
OpcodeInfo.unallocatedOpcode(0xa8),
OpcodeInfo.unallocatedOpcode(0xa9),
OpcodeInfo.unallocatedOpcode(0xaa),
OpcodeInfo.unallocatedOpcode(0xab),
OpcodeInfo.unallocatedOpcode(0xac),
OpcodeInfo.unallocatedOpcode(0xad),
OpcodeInfo.unallocatedOpcode(0xae),
OpcodeInfo.unallocatedOpcode(0xaf),
OpcodeInfo.unallocatedOpcode(0xb0),
OpcodeInfo.unallocatedOpcode(0xb1),
OpcodeInfo.unallocatedOpcode(0xb2),
OpcodeInfo.unallocatedOpcode(0xb3),
OpcodeInfo.unallocatedOpcode(0xb4),
OpcodeInfo.unallocatedOpcode(0xb5),
OpcodeInfo.unallocatedOpcode(0xb6),
OpcodeInfo.unallocatedOpcode(0xb7),
OpcodeInfo.unallocatedOpcode(0xb8),
OpcodeInfo.unallocatedOpcode(0xb9),
OpcodeInfo.unallocatedOpcode(0xba),
OpcodeInfo.unallocatedOpcode(0xbb),
OpcodeInfo.unallocatedOpcode(0xbc),
OpcodeInfo.unallocatedOpcode(0xbd),
OpcodeInfo.unallocatedOpcode(0xbe),
OpcodeInfo.unallocatedOpcode(0xbf),
OpcodeInfo.unallocatedOpcode(0xc0),
OpcodeInfo.unallocatedOpcode(0xc1),
OpcodeInfo.unallocatedOpcode(0xc2),
OpcodeInfo.unallocatedOpcode(0xc3),
OpcodeInfo.unallocatedOpcode(0xc4),
OpcodeInfo.unallocatedOpcode(0xc5),
OpcodeInfo.unallocatedOpcode(0xc6),
OpcodeInfo.unallocatedOpcode(0xc7),
OpcodeInfo.unallocatedOpcode(0xc8),
OpcodeInfo.unallocatedOpcode(0xc9),
OpcodeInfo.unallocatedOpcode(0xca),
OpcodeInfo.unallocatedOpcode(0xcb),
OpcodeInfo.unallocatedOpcode(0xcc),
OpcodeInfo.unallocatedOpcode(0xcd),
OpcodeInfo.unallocatedOpcode(0xce),
OpcodeInfo.unallocatedOpcode(0xcf),
OpcodeInfo.unallocatedOpcode(0xd0),
OpcodeInfo.unallocatedOpcode(0xd1),
OpcodeInfo.unallocatedOpcode(0xd2),
OpcodeInfo.unallocatedOpcode(0xd3),
OpcodeInfo.unallocatedOpcode(0xd4),
OpcodeInfo.unallocatedOpcode(0xd5),
OpcodeInfo.unallocatedOpcode(0xd6),
OpcodeInfo.unallocatedOpcode(0xd7),
OpcodeInfo.unallocatedOpcode(0xd8),
OpcodeInfo.unallocatedOpcode(0xd9),
OpcodeInfo.unallocatedOpcode(0xda),
OpcodeInfo.unallocatedOpcode(0xdb),
OpcodeInfo.unallocatedOpcode(0xdc),
OpcodeInfo.unallocatedOpcode(0xdd),
OpcodeInfo.unallocatedOpcode(0xde),
OpcodeInfo.unallocatedOpcode(0xdf),
OpcodeInfo.unallocatedOpcode(0xe0),
OpcodeInfo.unallocatedOpcode(0xe1),
OpcodeInfo.unallocatedOpcode(0xe2),
OpcodeInfo.unallocatedOpcode(0xe3),
OpcodeInfo.unallocatedOpcode(0xe4),
OpcodeInfo.unallocatedOpcode(0xe5),
OpcodeInfo.unallocatedOpcode(0xe6),
OpcodeInfo.unallocatedOpcode(0xe7),
OpcodeInfo.unallocatedOpcode(0xe8),
OpcodeInfo.unallocatedOpcode(0xe9),
OpcodeInfo.unallocatedOpcode(0xea),
OpcodeInfo.unallocatedOpcode(0xeb),
OpcodeInfo.unallocatedOpcode(0xec),
OpcodeInfo.unallocatedOpcode(0xed),
OpcodeInfo.unallocatedOpcode(0xee),
OpcodeInfo.unallocatedOpcode(0xef),
OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1),
OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1),
OpcodeInfo.validOpcode("CALLCODE", 0xf2, 7, 1, 1),
OpcodeInfo.validOpcode("RETURN", 0xf3, 2, 0, 1),
OpcodeInfo.validOpcode("DELEGATECALL", 0xf4, 6, 1, 1),
OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1),
OpcodeInfo.unallocatedOpcode(0xf6),
OpcodeInfo.unallocatedOpcode(0xf7),
OpcodeInfo.unallocatedOpcode(0xf8),
OpcodeInfo.unallocatedOpcode(0xf9),
OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1),
OpcodeInfo.unallocatedOpcode(0xfb),
OpcodeInfo.unallocatedOpcode(0xfc),
OpcodeInfo.validOpcode("REVERT", 0xfd, 2, 0, 1),
OpcodeInfo.validOpcode("INVALID", 0xfe, 0, 0, 1),
OpcodeInfo.validOpcode("SELFDESTRUCT", 0xff, 1, 0, 0),
};
}