#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use alloc::rc::Rc;
#[cfg(feature = "std")]
use std::rc::Rc;
use super::cost::code_deposit_gas;
use super::util::copy_into_memory_apply;
use super::{GasUsage, Machine, MachineStatus};
use crate::{
commit::AccountState,
errors::{OnChainError, RequireError},
AccountPatch, Memory, Patch,
};
use bigint::{Address, Gas, M256, U256};
impl<'a, M: Memory, P: Patch> Machine<'a, M, P> {
pub fn initialize_call(&mut self, preclaimed_value: U256) -> Result<(), RequireError> {
self.state.account_state.require(self.state.context.address)?;
if !self.state.context.is_system {
self.state
.account_state
.decrease_balance(self.state.context.caller, preclaimed_value);
self.state
.account_state
.decrease_balance(self.state.context.caller, self.state.context.value);
}
self.state
.account_state
.increase_balance(self.state.context.address, self.state.context.value);
Ok(())
}
pub fn invoke_call(&mut self) -> Result<(), RequireError> {
self.state.account_state.require(self.state.context.address)?;
if !self.state.context.is_system {
self.state
.account_state
.decrease_balance(self.state.context.caller, self.state.context.value);
}
self.state
.account_state
.increase_balance(self.state.context.address, self.state.context.value);
Ok(())
}
pub fn initialize_create(&mut self, preclaimed_value: U256) -> Result<(), RequireError> {
self.state.account_state.require(self.state.context.address)?;
if !self.state.context.is_system {
self.state
.account_state
.decrease_balance(self.state.context.caller, preclaimed_value);
self.state
.account_state
.decrease_balance(self.state.context.caller, self.state.context.value);
}
self.state
.account_state
.create(self.state.context.address, self.state.context.value)
.unwrap();
Ok(())
}
pub fn invoke_create(&mut self) -> Result<(), RequireError> {
self.state.account_state.require(self.state.context.address)?;
if !self.state.context.is_system {
self.state
.account_state
.decrease_balance(self.state.context.caller, self.state.context.value);
}
self.state
.account_state
.create(self.state.context.address, self.state.context.value)
.unwrap();
Ok(())
}
#[allow(clippy::collapsible_if)]
pub fn code_deposit(&mut self) {
match self.status() {
MachineStatus::ExitedOk | MachineStatus::ExitedErr(_) => (),
_ => panic!(),
}
if self.state.patch.code_deposit_limit().is_some() {
if self.state.out.len() > self.state.patch.code_deposit_limit().unwrap() {
reset_error_hard!(self, OnChainError::EmptyGas);
return;
}
}
let deposit_cost = code_deposit_gas(self.state.out.len());
if deposit_cost > self.state.available_gas() {
if !self.state.patch.force_code_deposit() {
reset_error_hard!(self, OnChainError::EmptyGas);
} else {
self.state
.account_state
.code_deposit(self.state.context.address, Rc::new(Vec::new()));
}
} else {
self.state.used_gas += deposit_cost;
self.state
.account_state
.code_deposit(self.state.context.address, self.state.out.clone());
}
}
pub fn finalize_transaction(
&mut self,
beneficiary: Address,
real_used_gas: Gas,
preclaimed_value: U256,
fresh_account_state: &AccountState<'a, P::Account>,
) -> Result<(), RequireError> {
self.state.account_state.require(self.state.context.address)?;
if !self.state.patch.account_patch().allow_partial_change() {
self.state.account_state.require(beneficiary)?;
}
match self.status() {
MachineStatus::ExitedOk => {
for address in &self.state.removed {
self.state.account_state.require(*address)?;
}
}
MachineStatus::ExitedErr(_) => {
self.state.account_state = fresh_account_state.clone();
self.state.removed = Vec::new();
if !self.state.context.is_system {
self.state
.account_state
.decrease_balance(self.state.context.caller, preclaimed_value);
}
}
_ => panic!(),
}
let gas_dec = real_used_gas * self.state.context.gas_price;
if !self.state.context.is_system {
self.state
.account_state
.increase_balance(self.state.context.caller, preclaimed_value);
self.state
.account_state
.decrease_balance(self.state.context.caller, gas_dec.into());
self.state.account_state.increase_balance(beneficiary, gas_dec.into());
}
for address in &self.state.removed {
self.state.account_state.remove(*address).unwrap();
}
match self.status() {
MachineStatus::ExitedOk => Ok(()),
MachineStatus::ExitedErr(_) => Ok(()),
_ => panic!(),
}
}
pub fn finalize_context(&mut self, fresh_account_state: &AccountState<'a, P::Account>) {
match self.status() {
MachineStatus::ExitedOk => (),
MachineStatus::ExitedErr(_) => {
self.state.account_state = fresh_account_state.clone();
self.state.removed = Vec::new();
}
_ => panic!(),
}
}
pub fn apply_sub(&mut self, sub: Machine<'a, M, P>) {
#[cfg(feature = "std")]
use std::mem::swap;
#[cfg(not(feature = "std"))]
use core::mem::swap;
let mut status = MachineStatus::Running;
swap(&mut status, &mut self.status);
match status {
MachineStatus::InvokeCreate(_) => {
self.apply_create(sub);
}
MachineStatus::InvokeCall(_, (out_start, out_len)) => {
self.apply_call(sub, out_start, out_len);
}
_ => panic!(),
}
}
fn apply_create(&mut self, mut sub: Machine<'a, M, P>) {
sub.code_deposit();
let sub_total_used_gas = sub.state.total_used_gas();
self.state.logs.append(&mut sub.state.logs);
self.state.used_gas += sub_total_used_gas;
self.state.refunded_gas = self.state.refunded_gas + sub.state.refunded_gas;
self.state.ret = sub.state.out.clone();
match sub.status() {
MachineStatus::ExitedOk => {
self.state.account_state = sub.state.account_state;
self.state.removed = sub.state.removed;
}
MachineStatus::ExitedErr(_) => {
self.state.stack.pop().unwrap();
self.state.stack.push(M256::zero()).unwrap();
}
_ => panic!(),
}
}
fn apply_call(&mut self, mut sub: Machine<'a, M, P>, out_start: U256, out_len: U256) {
let sub_total_used_gas = sub.state.total_used_gas();
self.state.logs.append(&mut sub.state.logs);
self.state.used_gas += sub_total_used_gas;
self.state.refunded_gas = self.state.refunded_gas + sub.state.refunded_gas;
copy_into_memory_apply(&mut self.state.memory, sub.state.out.as_slice(), out_start, out_len);
match sub.status() {
MachineStatus::ExitedOk => {
self.state.account_state = sub.state.account_state;
self.state.removed = sub.state.removed;
self.state.ret = Rc::new(Vec::new());
}
MachineStatus::ExitedErr(_) => {
self.state.stack.pop().unwrap();
self.state.stack.push(M256::zero()).unwrap();
self.state.ret = sub.state.out.clone();
}
_ => panic!(),
}
}
}