use std::iter;
use anyhow::Context as _;
use sha2::{Digest, Sha256};
use crate::{H256, U256};
const MAX_BYTECODE_LENGTH_IN_WORDS: usize = (1 << 16) - 1;
const MAX_BYTECODE_LENGTH_BYTES: usize = MAX_BYTECODE_LENGTH_IN_WORDS * 32;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum InvalidBytecodeError {
#[error("Bytecode too long: {0} bytes, while max {1} allowed")]
BytecodeTooLong(usize, usize),
#[error("Bytecode length is not divisible by 32")]
BytecodeLengthIsNotDivisibleBy32,
#[error("Bytecode has even number of 32-byte words")]
BytecodeLengthInWordsIsEven,
}
pub fn validate_bytecode(code: &[u8]) -> Result<(), InvalidBytecodeError> {
let bytecode_len = code.len();
if bytecode_len > MAX_BYTECODE_LENGTH_BYTES {
return Err(InvalidBytecodeError::BytecodeTooLong(
bytecode_len,
MAX_BYTECODE_LENGTH_BYTES,
));
}
if bytecode_len % 32 != 0 {
return Err(InvalidBytecodeError::BytecodeLengthIsNotDivisibleBy32);
}
let bytecode_len_words = bytecode_len / 32;
if bytecode_len_words % 2 == 0 {
return Err(InvalidBytecodeError::BytecodeLengthInWordsIsEven);
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BytecodeHash(H256);
impl BytecodeHash {
pub fn for_bytecode(bytecode: &[u8]) -> Self {
Self::for_generic_bytecode(BytecodeMarker::EraVm, bytecode, bytecode.len())
}
pub fn for_evm_bytecode(raw_bytecode_len: usize, bytecode: &[u8]) -> Self {
Self::for_generic_bytecode(BytecodeMarker::Evm, bytecode, raw_bytecode_len)
}
pub fn for_raw_evm_bytecode(bytecode: &[u8]) -> Self {
let padded_evm_bytecode = pad_evm_bytecode(bytecode);
Self::for_evm_bytecode(bytecode.len(), &padded_evm_bytecode)
}
fn for_generic_bytecode(
kind: BytecodeMarker,
bytecode: &[u8],
bytecode_len_in_bytes: usize,
) -> Self {
validate_bytecode(bytecode).expect("invalid bytecode");
let mut hasher = Sha256::new();
let len = match kind {
BytecodeMarker::EraVm => (bytecode_len_in_bytes / 32) as u16,
BytecodeMarker::Evm => bytecode_len_in_bytes as u16,
};
hasher.update(bytecode);
let result = hasher.finalize();
let mut output = [0u8; 32];
output[..].copy_from_slice(result.as_slice());
output[0] = kind as u8;
output[1] = 0;
output[2..4].copy_from_slice(&len.to_be_bytes());
Self(H256(output))
}
pub fn marker(&self) -> BytecodeMarker {
match self.0.as_bytes()[0] {
val if val == BytecodeMarker::EraVm as u8 => BytecodeMarker::EraVm,
val if val == BytecodeMarker::Evm as u8 => BytecodeMarker::Evm,
_ => unreachable!(),
}
}
pub fn len_in_bytes(&self) -> usize {
let bytes = self.0.as_bytes();
let raw_len = u16::from_be_bytes([bytes[2], bytes[3]]);
match self.marker() {
BytecodeMarker::EraVm => raw_len as usize * 32,
BytecodeMarker::Evm => raw_len as usize,
}
}
pub fn value(self) -> H256 {
self.0
}
pub fn value_u256(self) -> U256 {
crate::h256_to_u256(self.0)
}
}
impl TryFrom<H256> for BytecodeHash {
type Error = anyhow::Error;
fn try_from(raw_hash: H256) -> Result<Self, Self::Error> {
BytecodeMarker::new(raw_hash).context("unknown bytecode hash marker")?;
Ok(Self(raw_hash))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum BytecodeMarker {
EraVm = 1,
Evm = 2,
}
impl BytecodeMarker {
pub fn new(bytecode_hash: H256) -> Option<Self> {
Some(match bytecode_hash.as_bytes()[0] {
val if val == Self::EraVm as u8 => Self::EraVm,
val if val == Self::Evm as u8 => Self::Evm,
_ => return None,
})
}
pub fn detect(raw_bytecode: &[u8]) -> Self {
if validate_bytecode(raw_bytecode).is_err() {
Self::Evm
} else if raw_bytecode.first() == Some(&0) {
Self::EraVm
} else {
Self::Evm
}
}
}
pub fn trim_bytecode(bytecode_hash: BytecodeHash, raw: &[u8]) -> anyhow::Result<&[u8]> {
match bytecode_hash.marker() {
BytecodeMarker::EraVm => Ok(raw),
BytecodeMarker::Evm => trim_padded_evm_bytecode(bytecode_hash, raw),
}
}
pub fn trim_padded_evm_bytecode(bytecode_hash: BytecodeHash, raw: &[u8]) -> anyhow::Result<&[u8]> {
if bytecode_hash.marker() != BytecodeMarker::Evm {
anyhow::bail!("only EVM bytecode hashes allowed")
}
validate_bytecode(raw).context("bytecode fails basic validity checks")?;
let bytecode_len: usize = bytecode_hash.len_in_bytes();
let bytecode = raw.get(0..bytecode_len).with_context(|| {
format!(
"encoded length ({bytecode_len}) exceeds real length ({})",
raw.len()
)
})?;
let padding = &raw[bytecode_len..];
anyhow::ensure!(
padding.iter().all(|&b| b == 0),
"bytecode padding contains non-zero bytes"
);
Ok(bytecode)
}
pub fn pad_evm_bytecode(deployed_bytecode: &[u8]) -> Vec<u8> {
let mut padded = Vec::with_capacity(deployed_bytecode.len());
padded.extend_from_slice(deployed_bytecode);
if padded.len() % 32 != 0 {
padded.extend(iter::repeat_n(0, 32 - padded.len() % 32));
}
assert_eq!(padded.len() % 32, 0);
if (padded.len() / 32) % 2 != 1 {
padded.extend_from_slice(&[0; 32]);
}
assert_eq!((padded.len() / 32) % 2, 1);
padded
}
#[doc(hidden)] pub mod testonly {
use const_decoder::Decoder;
pub const PADDED_EVM_BYTECODE: &[u8] = &const_decoder::decode!(
Decoder::Hex,
b"6080604052348015600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063\
fb5343f314604c575b5f80fd5b604a60048036038101906046919060a6565b6066565b005b605260\
6f565b604051605d919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd\
5b5f819050919050565b6088816078565b81146091575f80fd5b50565b5f8135905060a081608156\
5b92915050565b5f6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092\
915050565b60d3816078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056\
fea2646970667358221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9\
ce4d0964736f6c634300081a00330000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000"
);
pub const PROCESSED_EVM_BYTECODE: &[u8] = &const_decoder::decode!(
Decoder::Hex,
b"6080604052348015600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063\
fb5343f314604c575b5f80fd5b604a60048036038101906046919060a6565b6066565b005b605260\
6f565b604051605d919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd\
5b5f819050919050565b6088816078565b81146091575f80fd5b50565b5f8135905060a081608156\
5b92915050565b5f6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092\
915050565b60d3816078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056\
fea2646970667358221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9\
ce4d0964736f6c634300081a0033"
);
}
#[cfg(test)]
mod tests {
use super::{
testonly::{PADDED_EVM_BYTECODE, PROCESSED_EVM_BYTECODE},
*,
};
#[test]
fn bytecode_markers_are_valid() {
let bytecode_hash = BytecodeHash::for_bytecode(&[0; 32]);
assert_eq!(bytecode_hash.marker(), BytecodeMarker::EraVm);
assert_eq!(bytecode_hash.len_in_bytes(), 32);
let bytecode_hash = BytecodeHash::for_raw_evm_bytecode(&[0; 32]);
assert_eq!(bytecode_hash.marker(), BytecodeMarker::Evm);
assert_eq!(bytecode_hash.len_in_bytes(), 32);
let bytecode_hash = BytecodeHash::for_evm_bytecode(32, &[0; 96]);
assert_eq!(bytecode_hash.marker(), BytecodeMarker::Evm);
assert_eq!(bytecode_hash.len_in_bytes(), 32);
}
#[test]
fn preparing_evm_bytecode() {
let bytecode_hash =
BytecodeHash::for_evm_bytecode(PROCESSED_EVM_BYTECODE.len(), PADDED_EVM_BYTECODE);
let prepared = trim_padded_evm_bytecode(bytecode_hash, PADDED_EVM_BYTECODE).unwrap();
assert_eq!(prepared, PROCESSED_EVM_BYTECODE);
}
}