revm-rwasm-bytecode 8.0.1

EVM Bytecodes
Documentation
//! Compact representation of analyzed EVM bytecode.
//!
//! Stores padded bytecode, original length, jump table, and code hash for
//! fast interpreter setup and inexpensive serialization.
use crate::{legacy::analyze_legacy, JumpTable};
use bincode::{
    de::read::SliceReader, decode_from_reader, enc::write::SliceWriter, encode_into_writer, error,
};
use bitvec::vec::BitVec;
use primitives::{Bytes, B256};
use std::vec::Vec;

#[derive(Debug)]
/// A legacy bytecode
pub struct LegacyBytecode {
    /// A code hash of the bytecode
    pub hash: B256,
    /// Original EVM bytecode
    pub bytecode: Bytes,
}

#[derive(Debug)]
/// Bytecode plus metadata required by the interpreter.
pub struct AnalyzedBytecode {
    /// A padded bytecode (length might be different)
    pub bytecode: Bytes,
    /// An original bytecode len (w/o padding)
    pub len: u64,
    /// Jump table for JUMPDEST checks
    pub jump_table: JumpTable,
    /// Code hash of bytecode (non-padded version)
    pub hash: B256,
}

impl Default for AnalyzedBytecode {
    fn default() -> Self {
        Self::new(Bytes::default(), B256::ZERO)
    }
}

impl AnalyzedBytecode {
    /// Analyze legacy bytecode, compute jump table, and keep orthe iginal length and hash.
    pub fn new(bytecode: Bytes, hash: B256) -> Self {
        let len = bytecode.len() as u64;
        let (jump_table, bytecode) = analyze_legacy(bytecode);
        Self {
            bytecode,
            len,
            jump_table,
            hash,
        }
    }

    /// Returns a size hint for data serialization
    pub fn hint_size(&self) -> usize {
        8 // padded bytecode len
        + self.bytecode.len() // padded bytecode
        + 8 // original bytecode len
        + 8 // jump table len
        + self.jump_table.as_slice().len() // jump table
        + 32 // code hash
    }

    /// Serialize into a contiguous buffer suitable for metadata storage.
    pub fn serialize(&self, buffer: &mut [u8]) -> Result<(), error::EncodeError> {
        debug_assert!(buffer.len() >= self.hint_size());
        let mut writer = SliceWriter::new(buffer);
        let config = bincode::config::legacy();
        encode_into_writer(self.hash.0, &mut writer, config)?;
        encode_into_writer(self.len, &mut writer, config)?;
        encode_into_writer(&self.bytecode[..], &mut writer, config)?;
        encode_into_writer(self.jump_table.as_slice(), &mut writer, config)?;
        Ok(())
    }

    /// Deserialize from a buffer produced by `serialize`.
    pub fn deserialize(bytes: &[u8]) -> Result<Self, error::DecodeError> {
        let config = bincode::config::legacy();
        let mut reader = SliceReader::new(bytes);
        let hash: [u8; 32] = decode_from_reader(&mut reader, config)?;
        let len: u64 = decode_from_reader(&mut reader, config)?;
        let bytecode: Vec<u8> = decode_from_reader(&mut reader, config)?;
        let jump_table: Vec<u8> = decode_from_reader(&mut reader, config)?;
        if len > bytecode.len() as u64 {
            return Err(error::DecodeError::ArrayLengthMismatch {
                required: len as usize,
                found: bytecode.len(),
            });
        }
        Ok(Self {
            bytecode: bytecode.into(),
            len,
            jump_table: JumpTable::new(BitVec::from_vec(jump_table)),
            hash: B256::from(hash),
        })
    }

    /// Return the original (unpadded) bytecode slice.
    #[inline]
    pub fn as_slice(&self) -> &[u8] {
        &self.bytecode[..self.len as usize]
    }

    /// Original bytecode length (before padding).
    #[inline]
    pub fn len(&self) -> usize {
        self.len as usize
    }

    /// Checks is bytecode empty
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use primitives::hex;

    #[test]
    fn test_analyzed_bytecode_encoding() {
        let bytecode = [
            0x60, 0x03, // PUSH1 3
            0x56, // JUMP
            0x5b, // JUMPDEST
        ];
        let original_bytecode = AnalyzedBytecode::new(bytecode.into(), B256::ZERO);
        let mut raw = vec![0u8; original_bytecode.hint_size()];
        original_bytecode.serialize(&mut raw).unwrap();
        let new_bytecode = AnalyzedBytecode::deserialize(&raw).unwrap();
        assert_eq!(original_bytecode.bytecode, new_bytecode.bytecode);
        assert_eq!(original_bytecode.len, new_bytecode.len);
        assert_eq!(
            original_bytecode.jump_table.as_slice(),
            new_bytecode.jump_table.as_slice()
        );
        for (pc, _) in bytecode.iter().enumerate() {
            assert_eq!(
                original_bytecode.jump_table.is_valid(pc),
                new_bytecode.jump_table.is_valid(pc)
            );
        }
    }

    #[test]
    fn test_analyzed_bytecode() {
        let bytecode = hex!("0x608060405234801561000f575f80fd5b506004361061003f575f3560e01c80633b2e97481461004357806345773e4e1461007357806348b8bcc314610091575b5f80fd5b61005d600480360381019061005891906102e5565b6100af565b60405161006a919061039a565b60405180910390f35b61007b6100dd565b604051610088919061039a565b60405180910390f35b61009961011a565b6040516100a6919061039a565b60405180910390f35b60605f8273ffffffffffffffffffffffffffffffffffffffff163190506100d58161012f565b915050919050565b60606040518060400160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b60605f4790506101298161012f565b91505090565b60605f8203610175576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050610282565b5f8290505f5b5f82146101a457808061018d906103f0565b915050600a8261019d9190610464565b915061017b565b5f8167ffffffffffffffff8111156101bf576101be610494565b5b6040519080825280601f01601f1916602001820160405280156101f15781602001600182028036833780820191505090505b5090505b5f851461027b578180610207906104c1565b925050600a8561021791906104e8565b60306102239190610518565b60f81b8183815181106102395761023861054b565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600a856102749190610464565b94506101f5565b8093505050505b919050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102b48261028b565b9050919050565b6102c4816102aa565b81146102ce575f80fd5b50565b5f813590506102df816102bb565b92915050565b5f602082840312156102fa576102f9610287565b5b5f610307848285016102d1565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561034757808201518184015260208101905061032c565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61036c82610310565b610376818561031a565b935061038681856020860161032a565b61038f81610352565b840191505092915050565b5f6020820190508181035f8301526103b28184610362565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f819050919050565b5f6103fa826103e7565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361042c5761042b6103ba565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61046e826103e7565b9150610479836103e7565b92508261048957610488610437565b5b828204905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f6104cb826103e7565b91505f82036104dd576104dc6103ba565b5b600182039050919050565b5f6104f2826103e7565b91506104fd836103e7565b92508261050d5761050c610437565b5b828206905092915050565b5f610522826103e7565b915061052d836103e7565b9250828201905080821115610545576105446103ba565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea2646970667358221220feebf5ace29c3c3146cb63bf7ca9009c2005f349075639d267cfbd817adde3e564736f6c63430008180033");
        let original_bytecode = AnalyzedBytecode::new(bytecode.into(), B256::ZERO);
        assert!(original_bytecode.jump_table.is_valid(0x039a));
        let mut raw = vec![0u8; original_bytecode.hint_size()];
        original_bytecode.serialize(&mut raw).unwrap();
        let new_bytecode = AnalyzedBytecode::deserialize(&raw).unwrap();
        assert!(new_bytecode.jump_table.is_valid(0x039a));
    }
}