use super::{
AccountOp, CallContextOp, MemoryOp, Op, OpEnum, Operation, PaddingOp, RWCounter, StackOp,
StartOp, StepStateOp, StorageOp, Target, TransientStorageOp, TxAccessListAccountOp,
TxAccessListAccountStorageOp, TxLogOp, TxReceiptOp, TxRefundOp, RW,
};
use crate::exec_trace::OperationRef;
use itertools::Itertools;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OperationContainer {
pub memory: Vec<Operation<MemoryOp>>,
pub stack: Vec<Operation<StackOp>>,
pub storage: Vec<Operation<StorageOp>>,
pub transient_storage: Vec<Operation<TransientStorageOp>>,
pub tx_access_list_account: Vec<Operation<TxAccessListAccountOp>>,
pub tx_access_list_account_storage: Vec<Operation<TxAccessListAccountStorageOp>>,
pub tx_refund: Vec<Operation<TxRefundOp>>,
pub account: Vec<Operation<AccountOp>>,
pub call_context: Vec<Operation<CallContextOp>>,
pub tx_receipt: Vec<Operation<TxReceiptOp>>,
pub tx_log: Vec<Operation<TxLogOp>>,
pub start: Vec<Operation<StartOp>>,
pub padding: Vec<Operation<PaddingOp>>,
pub step_state: Vec<Operation<StepStateOp>>,
}
impl Default for OperationContainer {
fn default() -> Self {
Self::new()
}
}
impl OperationContainer {
pub fn new() -> Self {
Self {
memory: Vec::new(),
stack: Vec::new(),
storage: Vec::new(),
transient_storage: Vec::new(),
tx_access_list_account: Vec::new(),
tx_access_list_account_storage: Vec::new(),
tx_refund: Vec::new(),
account: Vec::new(),
call_context: Vec::new(),
tx_receipt: Vec::new(),
tx_log: Vec::new(),
start: Vec::new(),
padding: Vec::new(),
step_state: Vec::new(),
}
}
pub fn insert<T: Op>(&mut self, op: Operation<T>) -> OperationRef {
let rwc = op.rwc();
let rwc_inner_chunk = op.rwc_inner_chunk();
let rw = op.rw();
let reversible = op.reversible();
self.insert_op_enum(rwc, rwc_inner_chunk, rw, reversible, op.op.into_enum())
}
pub fn insert_op_enum(
&mut self,
rwc: RWCounter,
rwc_inner_chunk: RWCounter,
rw: RW,
reversible: bool,
op_enum: OpEnum,
) -> OperationRef {
match op_enum {
OpEnum::Memory(op) => {
self.memory
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::Memory, self.memory.len() - 1))
}
OpEnum::Stack(op) => {
self.stack
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::Stack, self.stack.len() - 1))
}
OpEnum::Storage(op) => {
self.storage.push(if reversible {
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)
} else {
Operation::new(rwc, rwc_inner_chunk, rw, op)
});
OperationRef::from((Target::Storage, self.storage.len() - 1))
}
OpEnum::TransientStorage(op) => {
self.transient_storage.push(if reversible {
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)
} else {
Operation::new(rwc, rwc_inner_chunk, rw, op)
});
OperationRef::from((Target::TransientStorage, self.transient_storage.len() - 1))
}
OpEnum::TxAccessListAccount(op) => {
self.tx_access_list_account.push(if reversible {
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)
} else {
Operation::new(rwc, rwc_inner_chunk, rw, op)
});
OperationRef::from((
Target::TxAccessListAccount,
self.tx_access_list_account.len() - 1,
))
}
OpEnum::TxAccessListAccountStorage(op) => {
self.tx_access_list_account_storage.push(if reversible {
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)
} else {
Operation::new(rwc, rwc_inner_chunk, rw, op)
});
OperationRef::from((
Target::TxAccessListAccountStorage,
self.tx_access_list_account_storage.len() - 1,
))
}
OpEnum::TxRefund(op) => {
self.tx_refund.push(if reversible {
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)
} else {
Operation::new(rwc, rwc_inner_chunk, rw, op)
});
OperationRef::from((Target::TxRefund, self.tx_refund.len() - 1))
}
OpEnum::Account(op) => {
self.account.push(if reversible {
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)
} else {
Operation::new(rwc, rwc_inner_chunk, rw, op)
});
OperationRef::from((Target::Account, self.account.len() - 1))
}
OpEnum::CallContext(op) => {
self.call_context
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::CallContext, self.call_context.len() - 1))
}
OpEnum::TxReceipt(op) => {
self.tx_receipt
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::TxReceipt, self.tx_receipt.len() - 1))
}
OpEnum::TxLog(op) => {
self.tx_log
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::TxLog, self.tx_log.len() - 1))
}
OpEnum::Start(op) => {
self.start
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::Start, self.start.len() - 1))
}
OpEnum::Padding(op) => {
self.padding
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::Padding, self.padding.len() - 1))
}
OpEnum::StepState(op) => {
self.step_state
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::StepState, self.step_state.len() - 1))
}
}
}
pub fn sorted_memory(&self) -> Vec<Operation<MemoryOp>> {
self.memory.iter().sorted().cloned().collect()
}
pub fn sorted_stack(&self) -> Vec<Operation<StackOp>> {
self.stack.iter().sorted().cloned().collect()
}
pub fn sorted_storage(&self) -> Vec<Operation<StorageOp>> {
self.storage.iter().sorted().cloned().collect()
}
}
#[cfg(test)]
mod container_test {
use super::*;
use crate::operation::{RWCounter, RW};
use eth_types::{
evm_types::{MemoryAddress, StackAddress},
Address, Word,
};
#[test]
fn operation_container_test() {
let mut global_counter = RWCounter::default();
let mut intra_chunk_counter = RWCounter::default();
let mut operation_container = OperationContainer::default();
let stack_operation = Operation::new(
global_counter.inc_pre(),
intra_chunk_counter.inc_pre(),
RW::WRITE,
StackOp::new(1, StackAddress(1023), Word::from(0x100)),
);
let memory_operation = Operation::new(
global_counter.inc_pre(),
intra_chunk_counter.inc_pre(),
RW::WRITE,
MemoryOp::new(1, MemoryAddress::from(1), 1),
);
let storage_operation = Operation::new(
global_counter.inc_pre(),
intra_chunk_counter.inc_pre(),
RW::WRITE,
StorageOp::new(
Address::zero(),
Word::default(),
Word::from(0x1),
Word::default(),
1usize,
Word::default(),
),
);
let stack_ref = operation_container.insert(stack_operation.clone());
let memory_ref = operation_container.insert(memory_operation.clone());
let storage_ref = operation_container.insert(storage_operation.clone());
assert_eq!(operation_container.sorted_stack()[0], stack_operation);
assert_eq!(operation_container.sorted_memory()[0], memory_operation);
assert_eq!(operation_container.sorted_storage()[0], storage_operation);
assert_eq!(stack_ref, OperationRef::from((Target::Stack, 0)));
assert_eq!(memory_ref, OperationRef::from((Target::Memory, 0)));
assert_eq!(storage_ref, OperationRef::from((Target::Storage, 0)));
}
}