revme/cmd/
bytecode.rs

1use clap::Parser;
2use revm::{
3    bytecode::eof::{self, validate_eof_inner, CodeType, Eof, EofError},
4    primitives::{hex, Bytes},
5    specification::constants::MAX_INITCODE_SIZE,
6};
7use std::io;
8
9/// `bytecode` subcommand.
10#[derive(Parser, Debug)]
11pub struct Cmd {
12    /// Is EOF code in INITCODE mode.
13    #[arg(long)]
14    eof_initcode: bool,
15    /// Is EOF code in RUNTIME mode.
16    #[arg(long)]
17    eof_runtime: bool,
18    /// Bytecode in hex format string.
19    ///
20    /// - If bytes start with 0xFE it will be interpreted as a EOF.
21    /// - Otherwise, it will be interpreted as a EOF bytecode.
22    /// - If not provided, it will operate in interactive EOF validation mode.
23    #[arg()]
24    bytes: Option<String>,
25}
26
27#[inline]
28fn trim_decode(input: &str) -> Option<Bytes> {
29    let trimmed = input.trim().trim_start_matches("0x");
30    let decoded = hex::decode(trimmed).ok().map(Into::into);
31    if decoded.is_none() {
32        eprintln!("Invalid hex string");
33        return None;
34    }
35    decoded
36}
37
38impl Cmd {
39    /// Runs statetest command.
40    pub fn run(&self) {
41        let container_kind = if self.eof_initcode {
42            Some(CodeType::ReturnContract)
43        } else if self.eof_runtime {
44            Some(CodeType::ReturnOrStop)
45        } else {
46            None
47        };
48
49        if let Some(input_bytes) = &self.bytes {
50            let Some(bytes) = trim_decode(input_bytes) else {
51                return;
52            };
53
54            if bytes[0] == 0xEF {
55                match Eof::decode(bytes) {
56                    Ok(eof) => {
57                        println!("Decoding: {:#?}", eof);
58                        let res = validate_eof_inner(&eof, container_kind);
59                        println!("Validation: {:#?}", res);
60                    }
61                    Err(e) => eprintln!("Decoding Error: {:#?}", e),
62                }
63            } else {
64                eof::printer::print(&bytes)
65            }
66            return;
67        }
68
69        // Else run command in loop.
70        loop {
71            let mut input = String::new();
72            io::stdin().read_line(&mut input).expect("Input Error");
73            if input.len() == 1 {
74                // Just a newline, so exit
75                return;
76            }
77            let Some(bytes) = trim_decode(&input) else {
78                return;
79            };
80
81            if bytes.len() > MAX_INITCODE_SIZE {
82                println!(
83                    "err: bytes exceeds max code size {} > {}",
84                    bytes.len(),
85                    MAX_INITCODE_SIZE
86                );
87                continue;
88            }
89            match Eof::decode(bytes) {
90                Ok(eof) => match validate_eof_inner(&eof, container_kind) {
91                    Ok(_) => {
92                        println!(
93                            "OK {}/{}/{}",
94                            eof.body.code_section.len(),
95                            eof.body.container_section.len(),
96                            eof.body.data_section.len()
97                        );
98                    }
99                    Err(eof_error) => match eof_error {
100                        EofError::Decode(e) => println!("err decode: {}", e),
101                        EofError::Validation(e) => println!("err validation: {}", e),
102                    },
103                },
104                Err(e) => println!("err: {:#?}", e),
105            }
106        }
107    }
108}