mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 18:01:05 +00:00
c0dd845b39
Signed-off-by: xermicus <cyrill@parity.io>
383 lines
10 KiB
Rust
383 lines
10 KiB
Rust
//! The EVM instruction.
|
|
|
|
pub mod codecopy;
|
|
pub mod jump;
|
|
pub mod name;
|
|
pub mod stack;
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
|
|
use self::name::Name;
|
|
|
|
/// The EVM instruction.
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct Instruction {
|
|
/// The opcode or tag identifier.
|
|
pub name: Name,
|
|
/// The optional value argument.
|
|
pub value: Option<String>,
|
|
|
|
/// The source code identifier.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub source: Option<isize>,
|
|
/// The source code location begin.
|
|
pub begin: isize,
|
|
/// The source code location end.
|
|
pub end: isize,
|
|
}
|
|
|
|
impl Instruction {
|
|
/// Returns the number of input stack arguments.
|
|
pub const fn input_size(&self, version: &semver::Version) -> usize {
|
|
match self.name {
|
|
Name::POP => 1,
|
|
|
|
Name::JUMP => 1,
|
|
Name::JUMPI => 2,
|
|
|
|
Name::ADD => 2,
|
|
Name::SUB => 2,
|
|
Name::MUL => 2,
|
|
Name::DIV => 2,
|
|
Name::MOD => 2,
|
|
Name::SDIV => 2,
|
|
Name::SMOD => 2,
|
|
|
|
Name::LT => 2,
|
|
Name::GT => 2,
|
|
Name::EQ => 2,
|
|
Name::ISZERO => 1,
|
|
Name::SLT => 2,
|
|
Name::SGT => 2,
|
|
|
|
Name::OR => 2,
|
|
Name::XOR => 2,
|
|
Name::NOT => 1,
|
|
Name::AND => 2,
|
|
Name::SHL => 2,
|
|
Name::SHR => 2,
|
|
Name::SAR => 2,
|
|
Name::BYTE => 2,
|
|
|
|
Name::ADDMOD => 3,
|
|
Name::MULMOD => 3,
|
|
Name::EXP => 2,
|
|
Name::SIGNEXTEND => 2,
|
|
Name::SHA3 => 2,
|
|
Name::KECCAK256 => 2,
|
|
|
|
Name::MLOAD => 1,
|
|
Name::MSTORE => 2,
|
|
Name::MSTORE8 => 2,
|
|
Name::MCOPY => 3,
|
|
|
|
Name::SLOAD => 1,
|
|
Name::SSTORE => 2,
|
|
Name::TLOAD => 1,
|
|
Name::TSTORE => 2,
|
|
Name::PUSHIMMUTABLE => 0,
|
|
Name::ASSIGNIMMUTABLE => {
|
|
if version.minor >= 8 {
|
|
2
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
|
|
Name::CALLDATALOAD => 1,
|
|
Name::CALLDATACOPY => 3,
|
|
Name::CODECOPY => 3,
|
|
Name::RETURNDATACOPY => 3,
|
|
Name::EXTCODESIZE => 1,
|
|
Name::EXTCODEHASH => 1,
|
|
|
|
Name::CALL => 7,
|
|
Name::CALLCODE => 7,
|
|
Name::STATICCALL => 6,
|
|
Name::DELEGATECALL => 6,
|
|
|
|
Name::RETURN => 2,
|
|
Name::REVERT => 2,
|
|
Name::SELFDESTRUCT => 1,
|
|
|
|
Name::LOG0 => 2,
|
|
Name::LOG1 => 3,
|
|
Name::LOG2 => 4,
|
|
Name::LOG3 => 5,
|
|
Name::LOG4 => 6,
|
|
|
|
Name::CREATE => 3,
|
|
Name::CREATE2 => 4,
|
|
|
|
Name::ZK_CREATE => 3,
|
|
Name::ZK_CREATE2 => 4,
|
|
|
|
Name::BALANCE => 1,
|
|
|
|
Name::BLOCKHASH => 1,
|
|
Name::BLOBHASH => 1,
|
|
|
|
Name::EXTCODECOPY => 4,
|
|
|
|
Name::RecursiveCall { input_size, .. } => input_size,
|
|
Name::RecursiveReturn { input_size } => input_size,
|
|
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
/// Returns the number of output stack arguments.
|
|
pub const fn output_size(&self) -> usize {
|
|
match self.name {
|
|
Name::PUSH => 1,
|
|
Name::PUSH_Data => 1,
|
|
Name::PUSH_Tag => 1,
|
|
Name::PUSH_ContractHash => 1,
|
|
Name::PUSH_ContractHashSize => 1,
|
|
Name::PUSHLIB => 1,
|
|
Name::PUSHDEPLOYADDRESS => 1,
|
|
|
|
Name::PUSH1 => 1,
|
|
Name::PUSH2 => 1,
|
|
Name::PUSH3 => 1,
|
|
Name::PUSH4 => 1,
|
|
Name::PUSH5 => 1,
|
|
Name::PUSH6 => 1,
|
|
Name::PUSH7 => 1,
|
|
Name::PUSH8 => 1,
|
|
Name::PUSH9 => 1,
|
|
Name::PUSH10 => 1,
|
|
Name::PUSH11 => 1,
|
|
Name::PUSH12 => 1,
|
|
Name::PUSH13 => 1,
|
|
Name::PUSH14 => 1,
|
|
Name::PUSH15 => 1,
|
|
Name::PUSH16 => 1,
|
|
Name::PUSH17 => 1,
|
|
Name::PUSH18 => 1,
|
|
Name::PUSH19 => 1,
|
|
Name::PUSH20 => 1,
|
|
Name::PUSH21 => 1,
|
|
Name::PUSH22 => 1,
|
|
Name::PUSH23 => 1,
|
|
Name::PUSH24 => 1,
|
|
Name::PUSH25 => 1,
|
|
Name::PUSH26 => 1,
|
|
Name::PUSH27 => 1,
|
|
Name::PUSH28 => 1,
|
|
Name::PUSH29 => 1,
|
|
Name::PUSH30 => 1,
|
|
Name::PUSH31 => 1,
|
|
Name::PUSH32 => 1,
|
|
|
|
Name::DUP1 => 1,
|
|
Name::DUP2 => 1,
|
|
Name::DUP3 => 1,
|
|
Name::DUP4 => 1,
|
|
Name::DUP5 => 1,
|
|
Name::DUP6 => 1,
|
|
Name::DUP7 => 1,
|
|
Name::DUP8 => 1,
|
|
Name::DUP9 => 1,
|
|
Name::DUP10 => 1,
|
|
Name::DUP11 => 1,
|
|
Name::DUP12 => 1,
|
|
Name::DUP13 => 1,
|
|
Name::DUP14 => 1,
|
|
Name::DUP15 => 1,
|
|
Name::DUP16 => 1,
|
|
|
|
Name::ADD => 1,
|
|
Name::SUB => 1,
|
|
Name::MUL => 1,
|
|
Name::DIV => 1,
|
|
Name::MOD => 1,
|
|
Name::SDIV => 1,
|
|
Name::SMOD => 1,
|
|
|
|
Name::LT => 1,
|
|
Name::GT => 1,
|
|
Name::EQ => 1,
|
|
Name::ISZERO => 1,
|
|
Name::SLT => 1,
|
|
Name::SGT => 1,
|
|
|
|
Name::OR => 1,
|
|
Name::XOR => 1,
|
|
Name::NOT => 1,
|
|
Name::AND => 1,
|
|
Name::SHL => 1,
|
|
Name::SHR => 1,
|
|
Name::SAR => 1,
|
|
Name::BYTE => 1,
|
|
|
|
Name::ADDMOD => 1,
|
|
Name::MULMOD => 1,
|
|
Name::EXP => 1,
|
|
Name::SIGNEXTEND => 1,
|
|
Name::SHA3 => 1,
|
|
Name::KECCAK256 => 1,
|
|
|
|
Name::MLOAD => 1,
|
|
|
|
Name::SLOAD => 1,
|
|
Name::TLOAD => 1,
|
|
Name::PUSHIMMUTABLE => 1,
|
|
|
|
Name::CALLDATALOAD => 1,
|
|
Name::CALLDATASIZE => 1,
|
|
Name::CODESIZE => 1,
|
|
Name::PUSHSIZE => 1,
|
|
Name::RETURNDATASIZE => 1,
|
|
Name::EXTCODESIZE => 1,
|
|
Name::EXTCODEHASH => 1,
|
|
|
|
Name::CALL => 1,
|
|
Name::CALLCODE => 1,
|
|
Name::STATICCALL => 1,
|
|
Name::DELEGATECALL => 1,
|
|
|
|
Name::CREATE => 1,
|
|
Name::CREATE2 => 1,
|
|
|
|
Name::ZK_CREATE => 1,
|
|
Name::ZK_CREATE2 => 1,
|
|
|
|
Name::ADDRESS => 1,
|
|
Name::CALLER => 1,
|
|
Name::TIMESTAMP => 1,
|
|
Name::NUMBER => 1,
|
|
|
|
Name::CALLVALUE => 1,
|
|
Name::GAS => 1,
|
|
Name::BALANCE => 1,
|
|
Name::SELFBALANCE => 1,
|
|
|
|
Name::GASLIMIT => 1,
|
|
Name::GASPRICE => 1,
|
|
Name::ORIGIN => 1,
|
|
Name::CHAINID => 1,
|
|
Name::BLOCKHASH => 1,
|
|
Name::BLOBHASH => 1,
|
|
Name::DIFFICULTY => 1,
|
|
Name::PREVRANDAO => 1,
|
|
Name::COINBASE => 1,
|
|
Name::MSIZE => 1,
|
|
|
|
Name::BASEFEE => 1,
|
|
Name::BLOBBASEFEE => 1,
|
|
Name::PC => 1,
|
|
|
|
Name::RecursiveCall { output_size, .. } => output_size,
|
|
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
/// Replaces the instruction data aliases with the actual data.
|
|
pub fn replace_data_aliases(
|
|
instructions: &mut [Self],
|
|
mapping: &BTreeMap<String, String>,
|
|
) -> anyhow::Result<()> {
|
|
for instruction in instructions.iter_mut() {
|
|
match instruction {
|
|
Instruction {
|
|
name: Name::PUSH_ContractHash | Name::PUSH_ContractHashSize,
|
|
value: Some(value),
|
|
..
|
|
} => {
|
|
*value = mapping.get(value.as_str()).cloned().ok_or_else(|| {
|
|
anyhow::anyhow!("Contract alias `{}` data not found", value)
|
|
})?;
|
|
}
|
|
Instruction {
|
|
name: Name::PUSH_Data,
|
|
value: Some(value),
|
|
..
|
|
} => {
|
|
let mut key_extended =
|
|
"0".repeat(revive_common::BYTE_LENGTH_WORD * 2 - value.len());
|
|
key_extended.push_str(value.as_str());
|
|
|
|
*value = mapping.get(key_extended.as_str()).cloned().ok_or_else(|| {
|
|
anyhow::anyhow!("Data chunk alias `{}` data not found", key_extended)
|
|
})?;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Initializes an `INVALID` instruction to terminate an invalid unreachable block part.
|
|
pub fn invalid(previous: &Self) -> Self {
|
|
Self {
|
|
name: Name::INVALID,
|
|
value: None,
|
|
|
|
source: previous.source,
|
|
begin: previous.begin,
|
|
end: previous.end,
|
|
}
|
|
}
|
|
|
|
/// Initializes a recursive function `Call` instruction.
|
|
pub fn recursive_call(
|
|
name: String,
|
|
entry_key: revive_llvm_context::PolkaVMFunctionBlockKey,
|
|
stack_hash: md5::Digest,
|
|
input_size: usize,
|
|
output_size: usize,
|
|
return_address: revive_llvm_context::PolkaVMFunctionBlockKey,
|
|
previous: &Self,
|
|
) -> Self {
|
|
Self {
|
|
name: Name::RecursiveCall {
|
|
name,
|
|
entry_key,
|
|
stack_hash,
|
|
input_size,
|
|
output_size,
|
|
return_address,
|
|
},
|
|
value: None,
|
|
|
|
source: previous.source,
|
|
begin: previous.begin,
|
|
end: previous.end,
|
|
}
|
|
}
|
|
|
|
/// Initializes a recursive function `Return` instruction.
|
|
pub fn recursive_return(input_size: usize, previous: &Self) -> Self {
|
|
Self {
|
|
name: Name::RecursiveReturn { input_size },
|
|
value: None,
|
|
|
|
source: previous.source,
|
|
begin: previous.begin,
|
|
end: previous.end,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Instruction {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let name = self.name.to_string();
|
|
match self.name {
|
|
Name::Tag => write!(f, "{:4}", name),
|
|
_ => write!(f, "{:15}", name),
|
|
}?;
|
|
match self.value {
|
|
Some(ref value) if value.len() <= 64 => write!(f, "{}", value)?,
|
|
Some(ref value) => write!(f, "... {}", &value[value.len() - 60..])?,
|
|
None => {}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|