#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use bigint::M256;
#[cfg(not(feature = "std"))]
use core::cmp::min;
#[cfg(feature = "std")]
use std::cmp::min;
use super::errors::OnChainError;
use super::Patch;
use crate::util::opcode::Opcode;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum Instruction {
STOP,
ADD,
MUL,
SUB,
DIV,
SDIV,
MOD,
SMOD,
ADDMOD,
MULMOD,
EXP,
SIGNEXTEND,
LT,
GT,
SLT,
SGT,
EQ,
ISZERO,
AND,
OR,
XOR,
NOT,
BYTE,
SHL,
SHR,
SAR,
SHA3,
ADDRESS,
BALANCE,
ORIGIN,
CALLER,
CALLVALUE,
CALLDATALOAD,
CALLDATASIZE,
CALLDATACOPY,
CODESIZE,
CODECOPY,
GASPRICE,
EXTCODESIZE,
EXTCODECOPY,
EXTCODEHASH,
BLOCKHASH,
COINBASE,
TIMESTAMP,
NUMBER,
DIFFICULTY,
GASLIMIT,
POP,
MLOAD,
MSTORE,
MSTORE8,
SLOAD,
SSTORE,
JUMP,
JUMPI,
PC,
MSIZE,
GAS,
JUMPDEST,
CREATE,
CREATE2,
CALL,
CALLCODE,
RETURN,
DELEGATECALL,
SUICIDE,
STATICCALL,
REVERT,
RETURNDATASIZE,
RETURNDATACOPY,
PUSH(M256),
DUP(usize),
SWAP(usize),
LOG(usize),
}
pub struct Valids(Vec<bool>);
impl Valids {
pub fn new(code: &[u8]) -> Self {
let mut valids: Vec<bool> = Vec::with_capacity(code.len());
valids.resize(code.len(), false);
let mut i = 0;
while i < code.len() {
let opcode: Opcode = code[i].into();
match opcode {
Opcode::JUMPDEST => {
valids[i] = true;
i += 1;
}
Opcode::PUSH(v) => {
i += v + 1;
}
_ => {
i += 1;
}
}
}
Valids(valids)
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn is_valid(&self, position: usize) -> bool {
if position >= self.0.len() {
return false;
}
if !self.0[position] {
return false;
}
true
}
}
pub struct PC<'a, P: Patch> {
position: &'a usize,
code: &'a [u8],
valids: &'a Valids,
patch: &'a P,
}
pub struct PCMut<'a, P: Patch> {
position: &'a mut usize,
code: &'a [u8],
valids: &'a Valids,
patch: &'a P,
}
macro_rules! impl_pc {
($t: tt) => {
impl<'a, P: Patch> $t<'a, P> {
fn read_bytes(&self, from_position: usize, byte_count: usize) -> Result<M256, OnChainError> {
if from_position > self.code.len() {
return Err(OnChainError::PCOverflow);
}
let position = from_position;
let max = min(position.saturating_add(byte_count), self.code.len());
Ok(M256::from(&self.code[position..max]))
}
pub fn code(&self) -> &[u8] {
&self.code
}
pub fn position(&self) -> usize {
*self.position
}
pub fn opcode_position(&self) -> usize {
let mut o = 0;
let mut i = 0;
while i <= *self.position {
let opcode: Opcode = self.code[i].into();
match opcode {
Opcode::PUSH(v) => {
i += v + 1;
}
_ => {
i += 1;
}
}
o += 1;
}
o
}
pub fn is_valid(&self, position: usize) -> bool {
self.valids.is_valid(position)
}
pub fn is_end(&self) -> bool {
*self.position == self.code.len()
}
pub fn peek_opcode(&self) -> Result<Opcode, OnChainError> {
if *self.position >= self.code.len() {
return Err(OnChainError::PCOverflow);
}
Ok(self.code[*self.position].into())
}
pub fn peek(&self) -> Result<Instruction, OnChainError> {
let opcode: Opcode = self.peek_opcode()?;
Ok(match opcode {
Opcode::STOP => Instruction::STOP,
Opcode::ADD => Instruction::ADD,
Opcode::MUL => Instruction::MUL,
Opcode::SUB => Instruction::SUB,
Opcode::DIV => Instruction::DIV,
Opcode::SDIV => Instruction::SDIV,
Opcode::MOD => Instruction::MOD,
Opcode::SMOD => Instruction::SMOD,
Opcode::ADDMOD => Instruction::ADDMOD,
Opcode::MULMOD => Instruction::MULMOD,
Opcode::EXP => Instruction::EXP,
Opcode::SIGNEXTEND => Instruction::SIGNEXTEND,
Opcode::LT => Instruction::LT,
Opcode::GT => Instruction::GT,
Opcode::SLT => Instruction::SLT,
Opcode::SGT => Instruction::SGT,
Opcode::EQ => Instruction::EQ,
Opcode::ISZERO => Instruction::ISZERO,
Opcode::AND => Instruction::AND,
Opcode::OR => Instruction::OR,
Opcode::XOR => Instruction::XOR,
Opcode::NOT => Instruction::NOT,
Opcode::BYTE => Instruction::BYTE,
Opcode::SHL => {
if self.patch.has_bitwise_shift() {
Instruction::SHL
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::SHR => {
if self.patch.has_bitwise_shift() {
Instruction::SHR
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::SAR => {
if self.patch.has_bitwise_shift() {
Instruction::SAR
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::SHA3 => Instruction::SHA3,
Opcode::ADDRESS => Instruction::ADDRESS,
Opcode::BALANCE => Instruction::BALANCE,
Opcode::ORIGIN => Instruction::ORIGIN,
Opcode::CALLER => Instruction::CALLER,
Opcode::CALLVALUE => Instruction::CALLVALUE,
Opcode::CALLDATALOAD => Instruction::CALLDATALOAD,
Opcode::CALLDATASIZE => Instruction::CALLDATASIZE,
Opcode::CALLDATACOPY => Instruction::CALLDATACOPY,
Opcode::CODESIZE => Instruction::CODESIZE,
Opcode::CODECOPY => Instruction::CODECOPY,
Opcode::GASPRICE => Instruction::GASPRICE,
Opcode::EXTCODESIZE => Instruction::EXTCODESIZE,
Opcode::EXTCODECOPY => Instruction::EXTCODECOPY,
Opcode::EXTCODEHASH => Instruction::EXTCODEHASH,
Opcode::BLOCKHASH => Instruction::BLOCKHASH,
Opcode::COINBASE => Instruction::COINBASE,
Opcode::TIMESTAMP => Instruction::TIMESTAMP,
Opcode::NUMBER => Instruction::NUMBER,
Opcode::DIFFICULTY => Instruction::DIFFICULTY,
Opcode::GASLIMIT => Instruction::GASLIMIT,
Opcode::POP => Instruction::POP,
Opcode::MLOAD => Instruction::MLOAD,
Opcode::MSTORE => Instruction::MSTORE,
Opcode::MSTORE8 => Instruction::MSTORE8,
Opcode::SLOAD => Instruction::SLOAD,
Opcode::SSTORE => Instruction::SSTORE,
Opcode::JUMP => Instruction::JUMP,
Opcode::JUMPI => Instruction::JUMPI,
Opcode::PC => Instruction::PC,
Opcode::MSIZE => Instruction::MSIZE,
Opcode::GAS => Instruction::GAS,
Opcode::JUMPDEST => Instruction::JUMPDEST,
Opcode::PUSH(v) => {
let param = self.read_bytes(*self.position + 1, v)?;
Instruction::PUSH(param)
}
Opcode::DUP(v) => Instruction::DUP(v),
Opcode::SWAP(v) => Instruction::SWAP(v),
Opcode::LOG(v) => Instruction::LOG(v),
Opcode::CREATE => Instruction::CREATE,
Opcode::CREATE2 => {
if self.patch.has_create2() {
Instruction::CREATE2
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::CALL => Instruction::CALL,
Opcode::CALLCODE => Instruction::CALLCODE,
Opcode::RETURN => Instruction::RETURN,
Opcode::DELEGATECALL => {
if self.patch.has_delegate_call() {
Instruction::DELEGATECALL
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::STATICCALL => {
if self.patch.has_static_call() {
Instruction::STATICCALL
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::REVERT => {
if self.patch.has_revert() {
Instruction::REVERT
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::RETURNDATASIZE => {
if self.patch.has_return_data() {
Instruction::RETURNDATASIZE
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::RETURNDATACOPY => {
if self.patch.has_return_data() {
Instruction::RETURNDATACOPY
} else {
return Err(OnChainError::InvalidOpcode);
}
}
Opcode::INVALID => {
return Err(OnChainError::InvalidOpcode);
}
Opcode::SUICIDE => Instruction::SUICIDE,
})
}
}
};
}
impl_pc!(PC);
impl_pc!(PCMut);
impl<'a, P: Patch> PC<'a, P> {
pub fn new(patch: &'a P, code: &'a [u8], valids: &'a Valids, position: &'a usize) -> Self {
Self {
code,
valids,
position,
patch,
}
}
}
impl<'a, P: Patch> PCMut<'a, P> {
pub fn new(patch: &'a P, code: &'a [u8], valids: &'a Valids, position: &'a mut usize) -> Self {
Self {
code,
valids,
position,
patch,
}
}
pub fn jump(&mut self, position: usize) -> Result<(), OnChainError> {
if position >= self.code.len() {
return Err(OnChainError::PCOverflow);
}
if !self.is_valid(position) {
return Err(OnChainError::BadJumpDest);
}
*self.position = position;
Ok(())
}
pub fn read(&mut self) -> Result<Instruction, OnChainError> {
let result = self.peek()?;
let opcode: Opcode = self.code[*self.position].into();
match opcode {
Opcode::PUSH(v) => {
*self.position = min(*self.position + v + 1, self.code.len());
}
_ => {
*self.position += 1;
}
}
Ok(result)
}
}