├── .gitignore ├── bytecode ├── bytecode.txt └── bytecode-large.txt ├── Cargo.lock ├── run.sh ├── Cargo.toml ├── README.md └── src ├── operand.rs ├── main.rs ├── stats.rs ├── disassembler.rs └── opcode.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /bytecode/bytecode.txt: -------------------------------------------------------------------------------- 1 | 0x604260005260206000F3 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "halstead" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | # cargo run /home/shafu/halstead/bytecode.txt 3 | cargo run /home/shafu/halstead/bytecode-large.txt --rm-metadata 4 | # cargo run /Users/shafu/halstead/bytecode-large.txt --rm-metadata 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halstead" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## halstead-bytecode-complexity 2 | 3 | Calculate the [halstead metrics](https://en.wikipedia.org/wiki/Halstead_complexity_measures) for EVM bytecode. 4 | 5 | Inspired by [this](https://twitter.com/devtooligan/status/1698588856340406416) 6 | 7 | #### how to run 8 | 9 | ``` 10 | cargo run ${PATH_TO_DIR} 11 | ``` 12 | 13 | ##### flags 14 | 15 | --rm-metadata : Remove the metada if the bytecode comes from solc 16 | --v : Verbose 17 | -------------------------------------------------------------------------------- /src/operand.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 2 | pub struct Operand { 3 | pub value: String, 4 | pub size: usize, 5 | } 6 | 7 | impl Operand { 8 | pub fn new(size: usize) -> Self { 9 | Self { 10 | value: String::new(), 11 | size: size, 12 | } 13 | } 14 | 15 | pub fn set_value(&mut self, bytecode: &Vec, start: usize, end: usize) { 16 | self.value = bytecode[start..end].iter().collect(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | 4 | mod disassembler; 5 | mod opcode; 6 | mod operand; 7 | mod stats; 8 | 9 | use disassembler::run_dis; 10 | 11 | const METADATA_FLAG: &str = "--rm-metadata"; 12 | const VERBOSE_FLAG: &str = "--v"; 13 | 14 | fn main() { 15 | let args: Vec = env::args().collect(); 16 | 17 | if args.len() < 2 { 18 | eprintln!( 19 | "Usage: {} [{}] [{}]", 20 | args[0], VERBOSE_FLAG, METADATA_FLAG 21 | ); 22 | std::process::exit(1); 23 | } 24 | 25 | let dir = &args[1]; 26 | let remove_metadata = args.contains(&METADATA_FLAG.to_string()); 27 | let verbose = args.contains(&VERBOSE_FLAG.to_string()); 28 | 29 | match fs::read_dir(dir) { 30 | Ok(files) => { 31 | for file in files { 32 | let path = file.unwrap().path(); 33 | if path.is_file() { 34 | run_dis(&path.to_str().unwrap(), remove_metadata, verbose); 35 | } 36 | } 37 | } 38 | Err(e) => { 39 | eprintln!("Error: {}", e); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/stats.rs: -------------------------------------------------------------------------------- 1 | use crate::opcode::Opcode; 2 | use std::collections::HashSet; 3 | 4 | pub struct Stats { 5 | pub opcodes: Vec, 6 | } 7 | 8 | impl Stats { 9 | pub fn new() -> Self { 10 | Self { 11 | opcodes: Vec::new(), 12 | } 13 | } 14 | 15 | pub fn add_opcode(&mut self, opcode: Opcode) { 16 | self.opcodes.push(opcode); 17 | } 18 | 19 | fn count_operands(&self) -> usize { 20 | self.opcodes 21 | .iter() 22 | .map(|opcode| opcode.stack_input_size) 23 | .sum() 24 | } 25 | 26 | fn count_unique_opcodes(&self) -> usize { 27 | self.opcodes 28 | .iter() 29 | .map(|opcode| &opcode.name) 30 | .collect::>() 31 | .len() 32 | } 33 | 34 | fn count_unique_operands(&self) -> usize { 35 | self.opcodes 36 | .iter() 37 | .map(|opcode| &opcode.operand.value) 38 | .collect::>() 39 | .len() 40 | } 41 | 42 | pub fn print(&self) { 43 | let unique_opcodes = self.count_unique_opcodes(); 44 | let unique_operands = self.count_unique_operands(); 45 | let total_opcodes = self.opcodes.len(); 46 | let total_operands = self.count_operands(); 47 | 48 | let vocabulary = unique_opcodes + unique_operands; 49 | println!("Vocabulary: {}", vocabulary); 50 | 51 | let length = total_opcodes + total_operands; 52 | println!("Length: {}", length); 53 | 54 | let volume = length as f64 * (vocabulary as f64).log2(); 55 | println!("Volume: {:.2}", volume); 56 | 57 | let difficulty = 58 | (unique_opcodes as f64) / 2.0 * (total_operands as f64) / (unique_operands as f64); 59 | println!("Difficulty: {:.2}", difficulty); 60 | 61 | let effort = difficulty * volume; 62 | println!("Effort: {:.2}", effort); 63 | println!(""); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/disassembler.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use crate::opcode::Opcode; 4 | use crate::stats::Stats; 5 | 6 | pub struct Disassembler { 7 | pub bytecode: Vec, 8 | pub stats: Stats, 9 | pub line_number: usize, 10 | index: usize, 11 | } 12 | 13 | fn read_bytecode(path: &String, remove_metadata: bool) -> Result, String> { 14 | let mut bytecode = match fs::read_to_string(path) { 15 | Ok(contents) => contents, 16 | Err(e) => return Err(format!("Failed to read file: {}", e)), 17 | }; 18 | 19 | // remove 0x if it exists 20 | if &bytecode[0..2] == "0x" { 21 | bytecode = bytecode[2..].to_string(); 22 | } 23 | // remove metadata 24 | if remove_metadata { 25 | bytecode = strip_metadata(&bytecode); 26 | } 27 | 28 | Ok(bytecode.chars().collect()) 29 | } 30 | 31 | // strip the metadata from the bytecode 32 | fn strip_metadata(bytecode: &String) -> String { 33 | // metadata length is given by the last byte 34 | let last_byte: String = bytecode[bytecode.len() - 3..bytecode.len() - 1].to_string(); 35 | let metadata_len = usize::from_str_radix(&last_byte, 16).unwrap(); 36 | bytecode[0..bytecode.len() - (metadata_len * 2) - 4].to_string() 37 | } 38 | 39 | impl Disassembler { 40 | pub fn new(path: &String, remove_metadata: bool) -> Disassembler { 41 | Disassembler { 42 | bytecode: read_bytecode(path, remove_metadata).expect("Error reading bytecode"), 43 | index: 0, 44 | line_number: 0, 45 | stats: Stats::new(), 46 | } 47 | } 48 | 49 | /// This function is responsible for parsing and extracting opcodes 50 | /// and operands from the bytecode. 51 | /// 52 | /// Reads the next opcode from the bytecode, advances the index, 53 | /// and returns the opcode as a Result. 54 | /// 55 | /// # Errors 56 | /// 57 | /// - If the function encounters the end of the bytecode, it returns 58 | /// an `Err` with an "End of bytecode" message. 59 | /// 60 | /// # Returns 61 | /// 62 | /// - If the parsing is successful, it returns an `Ok` variant with 63 | /// the parsed `Opcode`. 64 | pub fn next_opcode(&mut self) -> Result { 65 | if self.index >= self.bytecode.len() - 1 { 66 | return Err("End of bytecode".to_string()); 67 | } 68 | 69 | let mut opcode_string = String::new(); 70 | opcode_string.push(self.bytecode[self.index]); 71 | opcode_string.push(self.bytecode[self.index + 1]); 72 | self.index += 2; 73 | 74 | let mut opcode = Opcode::from_byte(&opcode_string); 75 | 76 | if opcode.operand.size > 0 { 77 | opcode.operand.set_value( 78 | &self.bytecode, 79 | self.index, 80 | self.index + (opcode.operand.size * 2), 81 | ); 82 | self.index += opcode.operand.size * 2; 83 | } 84 | 85 | self.stats.add_opcode(opcode.clone()); 86 | self.line_number += 1; 87 | 88 | Ok(opcode) 89 | } 90 | 91 | pub fn print_stats(&self) { 92 | self.stats.print(); 93 | } 94 | } 95 | 96 | /// This function is responsible for running the disassembler on a given 97 | /// bytecode file. 98 | /// 99 | /// # Arguments 100 | /// 101 | /// - `path` - The path to the bytecode file. 102 | /// - `remove_metadata` - A boolean flag indicating whether to remove the 103 | /// metadata from the bytecode. 104 | /// - `verbose` - A boolean flag indicating whether to print the disassembled 105 | /// bytecode to stdout. 106 | pub fn run_dis(path: &str, remove_metadata: bool, verbose: bool) { 107 | println!("{}", path); 108 | let mut disassembler = Disassembler::new(&path.to_string(), remove_metadata); 109 | while let Ok(opcode) = disassembler.next_opcode() { 110 | if verbose { 111 | println!("{:>5}: {}", disassembler.line_number, opcode); 112 | } 113 | } 114 | disassembler.print_stats(); 115 | } 116 | -------------------------------------------------------------------------------- /bytecode/bytecode-large.txt: -------------------------------------------------------------------------------- 1 | 0x6060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029 2 | -------------------------------------------------------------------------------- /src/opcode.rs: -------------------------------------------------------------------------------- 1 | use crate::operand::Operand; 2 | use std::fmt; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 5 | pub struct Opcode { 6 | pub byte: String, 7 | pub name: String, 8 | pub operand: Operand, 9 | pub stack_input_size: usize, 10 | } 11 | 12 | impl fmt::Display for Opcode { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | write!(f, "{:<5} {}", self.name, self.operand.value) 15 | } 16 | } 17 | 18 | impl Opcode { 19 | pub fn new(byte: String) -> Opcode { 20 | Opcode { 21 | byte, 22 | name: "NOP".to_string(), 23 | operand: Operand::new(0), 24 | stack_input_size: 0, 25 | } 26 | } 27 | 28 | pub fn from_byte(byte: &String) -> Opcode { 29 | let mut opcode = Self::new(byte.to_string()); 30 | match byte.as_str() { 31 | "00" => opcode.name = "STOP".to_string(), 32 | "01" => { 33 | opcode.name = "ADD".to_string(); 34 | opcode.stack_input_size = 2; 35 | } 36 | "02" => { 37 | opcode.name = "MUL".to_string(); 38 | opcode.stack_input_size = 2; 39 | } 40 | "03" => { 41 | opcode.name = "SUB".to_string(); 42 | opcode.stack_input_size = 2; 43 | } 44 | "04" => { 45 | opcode.name = "DIV".to_string(); 46 | opcode.stack_input_size = 2; 47 | } 48 | "05" => { 49 | opcode.name = "SDIV".to_string(); 50 | opcode.stack_input_size = 2; 51 | } 52 | "06" => { 53 | opcode.name = "MOD".to_string(); 54 | opcode.stack_input_size = 2; 55 | } 56 | "07" => { 57 | opcode.name = "SMOD".to_string(); 58 | opcode.stack_input_size = 2; 59 | } 60 | "08" => { 61 | opcode.name = "ADDMOD".to_string(); 62 | opcode.stack_input_size = 3; 63 | } 64 | "09" => { 65 | opcode.name = "MULMOD".to_string(); 66 | opcode.stack_input_size = 3; 67 | } 68 | "0A" => { 69 | opcode.name = "EXP".to_string(); 70 | opcode.stack_input_size = 2; 71 | } 72 | "0B" => { 73 | opcode.name = "SIGNEXTEND".to_string(); 74 | opcode.stack_input_size = 2; 75 | } 76 | "10" => { 77 | opcode.name = "LT".to_string(); 78 | opcode.stack_input_size = 2; 79 | } 80 | "11" => { 81 | opcode.name = "GT".to_string(); 82 | opcode.stack_input_size = 2; 83 | } 84 | "12" => { 85 | opcode.name = "SLT".to_string(); 86 | opcode.stack_input_size = 2; 87 | } 88 | "13" => { 89 | opcode.name = "SGT".to_string(); 90 | opcode.stack_input_size = 2; 91 | } 92 | "14" => { 93 | opcode.name = "EQ".to_string(); 94 | opcode.stack_input_size = 2; 95 | } 96 | "15" => { 97 | opcode.name = "ISZERO".to_string(); 98 | opcode.stack_input_size = 1; 99 | } 100 | "16" => { 101 | opcode.name = "AND".to_string(); 102 | opcode.stack_input_size = 2; 103 | } 104 | "17" => { 105 | opcode.name = "OR".to_string(); 106 | opcode.stack_input_size = 2; 107 | } 108 | "18" => { 109 | opcode.name = "XOR".to_string(); 110 | opcode.stack_input_size = 2; 111 | } 112 | "19" => { 113 | opcode.name = "NOT".to_string(); 114 | opcode.stack_input_size = 1; 115 | } 116 | "1A" => { 117 | opcode.name = "BYTE".to_string(); 118 | opcode.stack_input_size = 1; 119 | } 120 | "1B" => { 121 | opcode.name = "SHL".to_string(); 122 | opcode.stack_input_size = 2; 123 | } 124 | "1C" => { 125 | opcode.name = "SHR".to_string(); 126 | opcode.stack_input_size = 2; 127 | } 128 | "1D" => { 129 | opcode.name = "SAR".to_string(); 130 | opcode.stack_input_size = 2; 131 | } 132 | "20" => { 133 | opcode.name = "SHA3".to_string(); 134 | opcode.stack_input_size = 2; 135 | } 136 | "30" => opcode.name = "ADDRESS".to_string(), 137 | "31" => { 138 | opcode.name = "BALANCE".to_string(); 139 | opcode.stack_input_size = 1; 140 | } 141 | "32" => opcode.name = "ORIGIN".to_string(), 142 | "33" => opcode.name = "CALLER".to_string(), 143 | "34" => opcode.name = "CALLVALUE".to_string(), 144 | "35" => { 145 | opcode.name = "CALLDATALOAD".to_string(); 146 | opcode.stack_input_size = 1; 147 | } 148 | "36" => opcode.name = "CALLDATASIZE".to_string(), 149 | "37" => { 150 | opcode.name = "CALLDATACOPY".to_string(); 151 | opcode.stack_input_size = 3; 152 | } 153 | "38" => opcode.name = "CODESIZE".to_string(), 154 | "39" => { 155 | opcode.name = "CODECOPY".to_string(); 156 | opcode.stack_input_size = 3; 157 | } 158 | "3A" => opcode.name = "GASPRICE".to_string(), 159 | "3B" => { 160 | opcode.name = "EXTCODESIZE".to_string(); 161 | opcode.stack_input_size = 1; 162 | } 163 | "3C" => { 164 | opcode.name = "EXTCODECOPY".to_string(); 165 | opcode.stack_input_size = 4; 166 | } 167 | "3D" => opcode.name = "RETURNDATASIZE".to_string(), 168 | "3E" => { 169 | opcode.name = "RETURNDATACOPY".to_string(); 170 | opcode.stack_input_size = 3; 171 | } 172 | "3F" => { 173 | opcode.name = "EXTCODEHASH".to_string(); 174 | opcode.stack_input_size = 1; 175 | } 176 | "40" => { 177 | opcode.name = "BLOCKHASH".to_string(); 178 | opcode.stack_input_size = 1; 179 | } 180 | "41" => opcode.name = "COINBASE".to_string(), 181 | "42" => opcode.name = "TIMESTAMP".to_string(), 182 | "43" => opcode.name = "NUMBER".to_string(), 183 | "44" => opcode.name = "PREVRANDAO".to_string(), 184 | "45" => opcode.name = "GASLIMIT".to_string(), 185 | "46" => opcode.name = "CHAINID".to_string(), 186 | "47" => opcode.name = "SELFBALANCE".to_string(), 187 | "48" => opcode.name = "BASEFEE".to_string(), 188 | "50" => { 189 | opcode.name = "POP".to_string(); 190 | opcode.stack_input_size = 1; 191 | } 192 | "51" => { 193 | opcode.name = "MLOAD".to_string(); 194 | opcode.stack_input_size = 1; 195 | } 196 | "52" => { 197 | opcode.name = "MSTORE".to_string(); 198 | opcode.stack_input_size = 2; 199 | } 200 | "53" => { 201 | opcode.name = "MSTORE8".to_string(); 202 | opcode.stack_input_size = 2; 203 | } 204 | "54" => { 205 | opcode.name = "SLOAD".to_string(); 206 | opcode.stack_input_size = 1; 207 | } 208 | "55" => { 209 | opcode.name = "SSTORE".to_string(); 210 | opcode.stack_input_size = 2; 211 | } 212 | "56" => { 213 | opcode.name = "JUMP".to_string(); 214 | opcode.stack_input_size = 1; 215 | } 216 | "57" => { 217 | opcode.name = "JUMPI".to_string(); 218 | opcode.stack_input_size = 2; 219 | } 220 | "58" => opcode.name = "PC".to_string(), 221 | "59" => opcode.name = "MSIZE".to_string(), 222 | "5A" => opcode.name = "GAS".to_string(), 223 | "5B" => opcode.name = "JUMPDEST".to_string(), 224 | "A0" => { 225 | opcode.name = "LOG0".to_string(); 226 | opcode.stack_input_size = 2; 227 | } 228 | "A1" => { 229 | opcode.name = "LOG1".to_string(); 230 | opcode.stack_input_size = 3; 231 | } 232 | "A2" => { 233 | opcode.name = "LOG2".to_string(); 234 | opcode.stack_input_size = 4; 235 | } 236 | "A3" => { 237 | opcode.name = "LOG3".to_string(); 238 | opcode.stack_input_size = 5; 239 | } 240 | "A4" => { 241 | opcode.name = "LOG4".to_string(); 242 | opcode.stack_input_size = 6; 243 | } 244 | "F0" => { 245 | opcode.name = "CREATE".to_string(); 246 | opcode.stack_input_size = 3; 247 | } 248 | "F1" => { 249 | opcode.name = "CALL".to_string(); 250 | opcode.stack_input_size = 7; 251 | } 252 | "F2" => { 253 | opcode.name = "CALLCODE".to_string(); 254 | opcode.stack_input_size = 7; 255 | } 256 | "F3" => { 257 | opcode.name = "RETURN".to_string(); 258 | opcode.stack_input_size = 2; 259 | } 260 | "F4" => { 261 | opcode.name = "DELEGATECALL".to_string(); 262 | opcode.stack_input_size = 6; 263 | } 264 | "F5" => { 265 | opcode.name = "CREATE2".to_string(); 266 | opcode.stack_input_size = 4; 267 | } 268 | "FA" => { 269 | opcode.name = "STATICCALL".to_string(); 270 | opcode.stack_input_size = 6; 271 | } 272 | "FD" => { 273 | opcode.name = "REVERT".to_string(); 274 | opcode.stack_input_size = 2; 275 | } 276 | "FE" => opcode.name = "INVALID".to_string(), 277 | "FF" => { 278 | opcode.name = "SELFDESTRUCT".to_string(); 279 | opcode.stack_input_size = 1; 280 | } 281 | byte => { 282 | let byte = usize::from_str_radix(&byte, 16).unwrap(); 283 | match byte { 284 | 0x5F..=0x7F => { 285 | let operand_size = byte - 0x5F; 286 | opcode.name = format!("PUSH{}", operand_size); 287 | opcode.operand.size = operand_size; 288 | opcode.stack_input_size = 0; 289 | } 290 | 0x80..=0x8F => { 291 | opcode.name = format!("DUP{}", byte - 0x7F); 292 | opcode.stack_input_size = 2; 293 | } 294 | 0x90..=0x9F => { 295 | opcode.name = format!("SWAP{}", byte - 0x87); 296 | opcode.stack_input_size = 2; 297 | } 298 | _ => (), 299 | } 300 | } 301 | } 302 | opcode 303 | } 304 | } 305 | --------------------------------------------------------------------------------