use crate::GlobalStateInterface;
macro_rules! forall_simple_opcodes {
($m:ident) => {
$m!(Nop);
$m!(Add);
$m!(Sub);
$m!(And);
$m!(Or);
$m!(Xor);
$m!(ShiftLeft);
$m!(ShiftRight);
$m!(RotateLeft);
$m!(RotateRight);
$m!(Mul);
$m!(Div);
$m!(NearCall);
$m!(Jump);
$m!(Event);
$m!(L2ToL1Message);
$m!(Decommit);
$m!(This);
$m!(Caller);
$m!(CodeAddress);
$m!(ErgsLeft);
$m!(SP);
$m!(ContextMeta);
$m!(ContextU128);
$m!(SetContextU128);
$m!(IncrementTxNumber);
$m!(AuxMutating0);
$m!(PrecompileCall);
$m!(HeapRead);
$m!(HeapWrite);
$m!(AuxHeapRead);
$m!(AuxHeapWrite);
$m!(PointerRead);
$m!(PointerAdd);
$m!(PointerSub);
$m!(PointerPack);
$m!(PointerShrink);
$m!(StorageRead);
$m!(StorageWrite);
$m!(TransientStorageRead);
$m!(TransientStorageWrite);
};
}
macro_rules! pub_struct {
($x:ident) => {
#[doc = concat!("`", stringify!($x), "` opcode.")]
#[derive(Debug)]
pub struct $x;
};
}
/// EraVM opcodes.
pub mod opcodes {
use std::marker::PhantomData;
use super::{CallingMode, ReturnType};
forall_simple_opcodes!(pub_struct);
#[derive(Debug)]
pub struct FarCall<M: TypeLevelCallingMode>(PhantomData<M>);
#[derive(Debug)]
pub struct Ret<T: TypeLevelReturnType>(PhantomData<T>);
#[derive(Debug)]
pub struct Normal;
#[derive(Debug)]
pub struct Delegate;
#[derive(Debug)]
pub struct Mimic;
#[derive(Debug)]
pub struct Revert;
#[derive(Debug)]
pub struct Panic;
pub trait TypeLevelCallingMode {
const VALUE: CallingMode;
}
impl TypeLevelCallingMode for Normal {
const VALUE: CallingMode = CallingMode::Normal;
}
impl TypeLevelCallingMode for Delegate {
const VALUE: CallingMode = CallingMode::Delegate;
}
impl TypeLevelCallingMode for Mimic {
const VALUE: CallingMode = CallingMode::Mimic;
}
pub trait TypeLevelReturnType {
const VALUE: ReturnType;
}
impl TypeLevelReturnType for Normal {
const VALUE: ReturnType = ReturnType::Normal;
}
impl TypeLevelReturnType for Revert {
const VALUE: ReturnType = ReturnType::Revert;
}
impl TypeLevelReturnType for Panic {
const VALUE: ReturnType = ReturnType::Panic;
}
}
#[allow(missing_docs)]
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
pub enum Opcode {
Nop,
Add,
Sub,
And,
Or,
Xor,
ShiftLeft,
ShiftRight,
RotateLeft,
RotateRight,
Mul,
Div,
NearCall,
FarCall(CallingMode),
Ret(ReturnType),
Jump,
Event,
L2ToL1Message,
Decommit,
This,
Caller,
CodeAddress,
ErgsLeft,
SP,
ContextMeta,
ContextU128,
SetContextU128,
IncrementTxNumber,
AuxMutating0,
PrecompileCall,
HeapRead,
HeapWrite,
AuxHeapRead,
AuxHeapWrite,
PointerRead,
PointerAdd,
PointerSub,
PointerPack,
PointerShrink,
StorageRead,
StorageWrite,
TransientStorageRead,
TransientStorageWrite,
}
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
pub enum CallingMode {
Normal,
Delegate,
Mimic,
}
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
pub enum ReturnType {
Normal,
Revert,
Panic,
}
impl ReturnType {
pub fn is_failure(&self) -> bool {
*self != ReturnType::Normal
}
}
pub trait OpcodeType {
const VALUE: Opcode;
}
macro_rules! impl_opcode {
($x:ident) => {
impl OpcodeType for opcodes::$x {
const VALUE: Opcode = Opcode::$x;
}
};
}
forall_simple_opcodes!(impl_opcode);
impl<M: opcodes::TypeLevelCallingMode> OpcodeType for opcodes::FarCall<M> {
const VALUE: Opcode = Opcode::FarCall(M::VALUE);
}
impl<T: opcodes::TypeLevelReturnType> OpcodeType for opcodes::Ret<T> {
const VALUE: Opcode = Opcode::Ret(T::VALUE);
}
pub trait Tracer {
fn before_instruction<OP: OpcodeType, S: GlobalStateInterface>(&mut self, state: &mut S) {
let _ = state;
}
#[must_use]
fn after_instruction<OP: OpcodeType, S: GlobalStateInterface>(
&mut self,
state: &mut S,
) -> ShouldStop {
let _ = state;
ShouldStop::Continue
}
fn on_extra_prover_cycles(&mut self, _stats: CycleStats) {}
}
#[derive(Debug)]
pub enum ShouldStop {
Stop,
Continue,
}
impl ShouldStop {
#[must_use]
#[inline(always)]
fn merge(self, other: ShouldStop) -> ShouldStop {
match (self, other) {
(ShouldStop::Continue, ShouldStop::Continue) => ShouldStop::Continue,
_ => ShouldStop::Stop,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CycleStats {
Keccak256(u32),
Sha256(u32),
EcRecover(u32),
Secp256r1Verify(u32),
ModExp(u32),
EcAdd(u32),
EcMul(u32),
EcPairing(u32),
Decommit(u32),
StorageRead,
StorageWrite,
}
impl Tracer for () {}
impl<A: Tracer, B: Tracer> Tracer for (A, B) {
fn before_instruction<OP: OpcodeType, S: GlobalStateInterface>(&mut self, state: &mut S) {
self.0.before_instruction::<OP, S>(state);
self.1.before_instruction::<OP, S>(state);
}
fn after_instruction<OP: OpcodeType, S: GlobalStateInterface>(
&mut self,
state: &mut S,
) -> ShouldStop {
self.0
.after_instruction::<OP, S>(state)
.merge(self.1.after_instruction::<OP, S>(state))
}
fn on_extra_prover_cycles(&mut self, stats: CycleStats) {
self.0.on_extra_prover_cycles(stats);
self.1.on_extra_prover_cycles(stats);
}
}
#[cfg(test)]
mod tests {
use super::{CallingMode, OpcodeType};
use crate::{opcodes, testonly::DummyState, GlobalStateInterface, Tracer};
struct FarCallCounter(usize);
impl Tracer for FarCallCounter {
fn before_instruction<OP: OpcodeType, S: GlobalStateInterface>(&mut self, _: &mut S) {
if let super::Opcode::FarCall(CallingMode::Normal) = OP::VALUE {
self.0 += 1;
}
}
}
#[test]
fn test_tracer() {
let mut tracer = FarCallCounter(0);
tracer.before_instruction::<opcodes::Nop, _>(&mut DummyState);
assert_eq!(tracer.0, 0);
tracer.before_instruction::<opcodes::FarCall<opcodes::Normal>, _>(&mut DummyState);
assert_eq!(tracer.0, 1);
tracer.before_instruction::<opcodes::FarCall<opcodes::Mimic>, _>(&mut DummyState);
assert_eq!(tracer.0, 1);
}
#[test]
fn test_aggregate_tracer() {
let mut tracer = (FarCallCounter(0), (FarCallCounter(0), FarCallCounter(0)));
tracer.before_instruction::<opcodes::Nop, _>(&mut DummyState);
assert_eq!(tracer.0 .0, 0);
assert_eq!(tracer.1 .0 .0, 0);
assert_eq!(tracer.1 .1 .0, 0);
tracer.before_instruction::<opcodes::FarCall<opcodes::Normal>, _>(&mut DummyState);
assert_eq!(tracer.0 .0, 1);
assert_eq!(tracer.1 .0 .0, 1);
assert_eq!(tracer.1 .1 .0, 1);
}
}