Reformat the source code
This commit is contained in:
@@ -16,20 +16,20 @@
|
||||
|
||||
//! Evm interface.
|
||||
|
||||
use std::{ops, cmp, fmt};
|
||||
use ethereum_types::{U128, U256, U512};
|
||||
use vm::{Ext, Result, ReturnData, GasLeft, Error};
|
||||
use std::{cmp, fmt, ops};
|
||||
use vm::{Error, Ext, GasLeft, Result, ReturnData};
|
||||
|
||||
/// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing
|
||||
/// a return instruction.
|
||||
#[derive(Debug)]
|
||||
pub struct FinalizationResult {
|
||||
/// Final amount of gas left.
|
||||
pub gas_left: U256,
|
||||
/// Apply execution state changes or revert them.
|
||||
pub apply_state: bool,
|
||||
/// Return data buffer.
|
||||
pub return_data: ReturnData,
|
||||
/// Final amount of gas left.
|
||||
pub gas_left: U256,
|
||||
/// Apply execution state changes or revert them.
|
||||
pub apply_state: bool,
|
||||
/// Return data buffer.
|
||||
pub return_data: ReturnData,
|
||||
}
|
||||
|
||||
/// Types that can be "finalized" using an EVM.
|
||||
@@ -37,177 +37,188 @@ pub struct FinalizationResult {
|
||||
/// In practice, this is just used to define an inherent impl on
|
||||
/// `Reult<GasLeft<'a>>`.
|
||||
pub trait Finalize {
|
||||
/// Consume the externalities, call return if necessary, and produce call result.
|
||||
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult>;
|
||||
/// Consume the externalities, call return if necessary, and produce call result.
|
||||
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult>;
|
||||
}
|
||||
|
||||
impl Finalize for Result<GasLeft> {
|
||||
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
|
||||
match self {
|
||||
Ok(GasLeft::Known(gas_left)) => {
|
||||
Ok(FinalizationResult {
|
||||
gas_left,
|
||||
apply_state: true,
|
||||
return_data: ReturnData::empty()
|
||||
})
|
||||
},
|
||||
Ok(GasLeft::NeedsReturn { gas_left, data, apply_state }) => {
|
||||
ext.ret(&gas_left, &data, apply_state).map(|gas_left|
|
||||
FinalizationResult { gas_left, apply_state, return_data: data }
|
||||
)
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
|
||||
match self {
|
||||
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult {
|
||||
gas_left,
|
||||
apply_state: true,
|
||||
return_data: ReturnData::empty(),
|
||||
}),
|
||||
Ok(GasLeft::NeedsReturn {
|
||||
gas_left,
|
||||
data,
|
||||
apply_state,
|
||||
}) => ext
|
||||
.ret(&gas_left, &data, apply_state)
|
||||
.map(|gas_left| FinalizationResult {
|
||||
gas_left,
|
||||
apply_state,
|
||||
return_data: data,
|
||||
}),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Error {
|
||||
fn finalize<E: Ext>(self, _ext: E) -> Result<FinalizationResult> {
|
||||
Err(self)
|
||||
}
|
||||
fn finalize<E: Ext>(self, _ext: E) -> Result<FinalizationResult> {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
|
||||
pub trait CostType: Sized + From<usize> + Copy + Send
|
||||
+ ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> + ops::Sub<Output=Self>
|
||||
+ ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self>
|
||||
+ cmp::Ord + fmt::Debug {
|
||||
/// Converts this cost into `U256`
|
||||
fn as_u256(&self) -> U256;
|
||||
/// Tries to fit `U256` into this `Cost` type
|
||||
fn from_u256(val: U256) -> Result<Self>;
|
||||
/// Convert to usize (may panic)
|
||||
fn as_usize(&self) -> usize;
|
||||
/// Add with overflow
|
||||
fn overflow_add(self, other: Self) -> (Self, bool);
|
||||
/// Multiple with overflow
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool);
|
||||
/// Single-step full multiplication and shift: `(self*other) >> shr`
|
||||
/// Should not overflow on intermediate steps
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool);
|
||||
pub trait CostType:
|
||||
Sized
|
||||
+ From<usize>
|
||||
+ Copy
|
||||
+ Send
|
||||
+ ops::Mul<Output = Self>
|
||||
+ ops::Div<Output = Self>
|
||||
+ ops::Add<Output = Self>
|
||||
+ ops::Sub<Output = Self>
|
||||
+ ops::Shr<usize, Output = Self>
|
||||
+ ops::Shl<usize, Output = Self>
|
||||
+ cmp::Ord
|
||||
+ fmt::Debug
|
||||
{
|
||||
/// Converts this cost into `U256`
|
||||
fn as_u256(&self) -> U256;
|
||||
/// Tries to fit `U256` into this `Cost` type
|
||||
fn from_u256(val: U256) -> Result<Self>;
|
||||
/// Convert to usize (may panic)
|
||||
fn as_usize(&self) -> usize;
|
||||
/// Add with overflow
|
||||
fn overflow_add(self, other: Self) -> (Self, bool);
|
||||
/// Multiple with overflow
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool);
|
||||
/// Single-step full multiplication and shift: `(self*other) >> shr`
|
||||
/// Should not overflow on intermediate steps
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool);
|
||||
}
|
||||
|
||||
impl CostType for U256 {
|
||||
fn as_u256(&self) -> U256 {
|
||||
*self
|
||||
}
|
||||
fn as_u256(&self) -> U256 {
|
||||
*self
|
||||
}
|
||||
|
||||
fn from_u256(val: U256) -> Result<Self> {
|
||||
Ok(val)
|
||||
}
|
||||
fn from_u256(val: U256) -> Result<Self> {
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn as_usize(&self) -> usize {
|
||||
self.as_u64() as usize
|
||||
}
|
||||
fn as_usize(&self) -> usize {
|
||||
self.as_u64() as usize
|
||||
}
|
||||
|
||||
fn overflow_add(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_add(other)
|
||||
}
|
||||
fn overflow_add(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_add(other)
|
||||
}
|
||||
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_mul(other)
|
||||
}
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_mul(other)
|
||||
}
|
||||
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
||||
let x = self.full_mul(other);
|
||||
let U512(parts) = x;
|
||||
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
|
||||
let U512(parts) = x >> shr;
|
||||
(
|
||||
U256([parts[0], parts[1], parts[2], parts[3]]),
|
||||
overflow
|
||||
)
|
||||
}
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
||||
let x = self.full_mul(other);
|
||||
let U512(parts) = x;
|
||||
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
|
||||
let U512(parts) = x >> shr;
|
||||
(U256([parts[0], parts[1], parts[2], parts[3]]), overflow)
|
||||
}
|
||||
}
|
||||
|
||||
impl CostType for usize {
|
||||
fn as_u256(&self) -> U256 {
|
||||
U256::from(*self)
|
||||
}
|
||||
fn as_u256(&self) -> U256 {
|
||||
U256::from(*self)
|
||||
}
|
||||
|
||||
fn from_u256(val: U256) -> Result<Self> {
|
||||
let res = val.low_u64() as usize;
|
||||
fn from_u256(val: U256) -> Result<Self> {
|
||||
let res = val.low_u64() as usize;
|
||||
|
||||
// validate if value fits into usize
|
||||
if U256::from(res) != val {
|
||||
return Err(Error::OutOfGas);
|
||||
}
|
||||
// validate if value fits into usize
|
||||
if U256::from(res) != val {
|
||||
return Err(Error::OutOfGas);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn as_usize(&self) -> usize {
|
||||
*self
|
||||
}
|
||||
fn as_usize(&self) -> usize {
|
||||
*self
|
||||
}
|
||||
|
||||
fn overflow_add(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_add(other)
|
||||
}
|
||||
fn overflow_add(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_add(other)
|
||||
}
|
||||
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_mul(other)
|
||||
}
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_mul(other)
|
||||
}
|
||||
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
||||
let (c, o) = U128::from(self).overflowing_mul(U128::from(other));
|
||||
let U128(parts) = c;
|
||||
let overflow = o | (parts[1] > 0);
|
||||
let U128(parts) = c >> shr;
|
||||
let result = parts[0] as usize;
|
||||
let overflow = overflow | (parts[0] > result as u64);
|
||||
(result, overflow)
|
||||
}
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
||||
let (c, o) = U128::from(self).overflowing_mul(U128::from(other));
|
||||
let U128(parts) = c;
|
||||
let overflow = o | (parts[1] > 0);
|
||||
let U128(parts) = c >> shr;
|
||||
let result = parts[0] as usize;
|
||||
let overflow = overflow | (parts[0] > result as u64);
|
||||
(result, overflow)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ethereum_types::U256;
|
||||
use super::CostType;
|
||||
use super::CostType;
|
||||
use ethereum_types::U256;
|
||||
|
||||
#[test]
|
||||
fn should_calculate_overflow_mul_shr_without_overflow() {
|
||||
// given
|
||||
let num = 1048576;
|
||||
#[test]
|
||||
fn should_calculate_overflow_mul_shr_without_overflow() {
|
||||
// given
|
||||
let num = 1048576;
|
||||
|
||||
// when
|
||||
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
|
||||
let (res2, o2) = num.overflow_mul_shr(num, 20);
|
||||
// when
|
||||
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
|
||||
let (res2, o2) = num.overflow_mul_shr(num, 20);
|
||||
|
||||
// then
|
||||
assert_eq!(res1, U256::from(num));
|
||||
assert!(!o1);
|
||||
assert_eq!(res2, num);
|
||||
assert!(!o2);
|
||||
}
|
||||
// then
|
||||
assert_eq!(res1, U256::from(num));
|
||||
assert!(!o1);
|
||||
assert_eq!(res2, num);
|
||||
assert!(!o2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_calculate_overflow_mul_shr_with_overflow() {
|
||||
// given
|
||||
let max = u64::max_value();
|
||||
let num1 = U256([max, max, max, max]);
|
||||
let num2 = usize::max_value();
|
||||
#[test]
|
||||
fn should_calculate_overflow_mul_shr_with_overflow() {
|
||||
// given
|
||||
let max = u64::max_value();
|
||||
let num1 = U256([max, max, max, max]);
|
||||
let num2 = usize::max_value();
|
||||
|
||||
// when
|
||||
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
|
||||
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
|
||||
// when
|
||||
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
|
||||
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
|
||||
|
||||
// then
|
||||
assert_eq!(res2, num2 - 1);
|
||||
assert!(o2);
|
||||
// then
|
||||
assert_eq!(res2, num2 - 1);
|
||||
assert!(o2);
|
||||
|
||||
assert_eq!(res1, !U256::zero() - U256::one());
|
||||
assert!(o1);
|
||||
}
|
||||
assert_eq!(res1, !U256::zero() - U256::one());
|
||||
assert!(o1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_validate_u256_to_usize_conversion() {
|
||||
// given
|
||||
let v = U256::from(usize::max_value()) + U256::from(1);
|
||||
#[test]
|
||||
fn should_validate_u256_to_usize_conversion() {
|
||||
// given
|
||||
let v = U256::from(usize::max_value()) + U256::from(1);
|
||||
|
||||
// when
|
||||
let res = usize::from_u256(v);
|
||||
// when
|
||||
let res = usize::from_u256(v);
|
||||
|
||||
// then
|
||||
assert!(res.is_err());
|
||||
}
|
||||
// then
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,67 +16,76 @@
|
||||
|
||||
//! Evm factory.
|
||||
//!
|
||||
use super::{interpreter::SharedCache, vm::ActionParams, vmtype::VMType};
|
||||
use ethereum_types::U256;
|
||||
use std::sync::Arc;
|
||||
use vm::{Exec, Schedule};
|
||||
use ethereum_types::U256;
|
||||
use super::vm::ActionParams;
|
||||
use super::interpreter::SharedCache;
|
||||
use super::vmtype::VMType;
|
||||
|
||||
/// Evm factory. Creates appropriate Evm.
|
||||
#[derive(Clone)]
|
||||
pub struct Factory {
|
||||
evm: VMType,
|
||||
evm_cache: Arc<SharedCache>,
|
||||
evm: VMType,
|
||||
evm_cache: Arc<SharedCache>,
|
||||
}
|
||||
|
||||
impl Factory {
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Exec> {
|
||||
match self.evm {
|
||||
VMType::Interpreter => if Self::can_fit_in_usize(¶ms.gas) {
|
||||
Box::new(super::interpreter::Interpreter::<usize>::new(params, self.evm_cache.clone(), schedule, depth))
|
||||
} else {
|
||||
Box::new(super::interpreter::Interpreter::<U256>::new(params, self.evm_cache.clone(), schedule, depth))
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Exec> {
|
||||
match self.evm {
|
||||
VMType::Interpreter => {
|
||||
if Self::can_fit_in_usize(¶ms.gas) {
|
||||
Box::new(super::interpreter::Interpreter::<usize>::new(
|
||||
params,
|
||||
self.evm_cache.clone(),
|
||||
schedule,
|
||||
depth,
|
||||
))
|
||||
} else {
|
||||
Box::new(super::interpreter::Interpreter::<U256>::new(
|
||||
params,
|
||||
self.evm_cache.clone(),
|
||||
schedule,
|
||||
depth,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new instance of specific `VMType` factory, with a size in bytes
|
||||
/// for caching jump destinations.
|
||||
pub fn new(evm: VMType, cache_size: usize) -> Self {
|
||||
Factory {
|
||||
evm,
|
||||
evm_cache: Arc::new(SharedCache::new(cache_size)),
|
||||
}
|
||||
}
|
||||
/// Create new instance of specific `VMType` factory, with a size in bytes
|
||||
/// for caching jump destinations.
|
||||
pub fn new(evm: VMType, cache_size: usize) -> Self {
|
||||
Factory {
|
||||
evm,
|
||||
evm_cache: Arc::new(SharedCache::new(cache_size)),
|
||||
}
|
||||
}
|
||||
|
||||
fn can_fit_in_usize(gas: &U256) -> bool {
|
||||
gas == &U256::from(gas.low_u64() as usize)
|
||||
}
|
||||
fn can_fit_in_usize(gas: &U256) -> bool {
|
||||
gas == &U256::from(gas.low_u64() as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Factory {
|
||||
/// Returns native rust evm factory
|
||||
fn default() -> Factory {
|
||||
Factory {
|
||||
evm: VMType::Interpreter,
|
||||
evm_cache: Arc::new(SharedCache::default()),
|
||||
}
|
||||
}
|
||||
/// Returns native rust evm factory
|
||||
fn default() -> Factory {
|
||||
Factory {
|
||||
evm: VMType::Interpreter,
|
||||
evm_cache: Arc::new(SharedCache::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_vm() {
|
||||
use vm::Ext;
|
||||
use vm::tests::FakeExt;
|
||||
use bytes::Bytes;
|
||||
use bytes::Bytes;
|
||||
use vm::{tests::FakeExt, Ext};
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.code = Some(Arc::new(Bytes::default()));
|
||||
let ext = FakeExt::new();
|
||||
let _vm = Factory::default().create(params, ext.schedule(), ext.depth());
|
||||
let mut params = ActionParams::default();
|
||||
params.code = Some(Arc::new(Bytes::default()));
|
||||
let ext = FakeExt::new();
|
||||
let _vm = Factory::default().create(params, ext.schedule(), ext.depth());
|
||||
}
|
||||
|
||||
/// Create tests by injecting different VM factories
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,475 +14,503 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cmp;
|
||||
use ethereum_types::{U256, H256};
|
||||
use super::u256_to_address;
|
||||
use ethereum_types::{H256, U256};
|
||||
use std::cmp;
|
||||
|
||||
use {evm, vm};
|
||||
use evm;
|
||||
use instructions::{self, Instruction, InstructionInfo};
|
||||
use interpreter::stack::Stack;
|
||||
use vm::Schedule;
|
||||
use vm::{self, Schedule};
|
||||
|
||||
macro_rules! overflowing {
|
||||
($x: expr) => {{
|
||||
let (v, overflow) = $x;
|
||||
if overflow { return Err(vm::Error::OutOfGas); }
|
||||
v
|
||||
}}
|
||||
($x: expr) => {{
|
||||
let (v, overflow) = $x;
|
||||
if overflow {
|
||||
return Err(vm::Error::OutOfGas);
|
||||
}
|
||||
v
|
||||
}};
|
||||
}
|
||||
|
||||
enum Request<Cost: ::evm::CostType> {
|
||||
Gas(Cost),
|
||||
GasMem(Cost, Cost),
|
||||
GasMemProvide(Cost, Cost, Option<U256>),
|
||||
GasMemCopy(Cost, Cost, Cost)
|
||||
Gas(Cost),
|
||||
GasMem(Cost, Cost),
|
||||
GasMemProvide(Cost, Cost, Option<U256>),
|
||||
GasMemCopy(Cost, Cost, Cost),
|
||||
}
|
||||
|
||||
pub struct InstructionRequirements<Cost> {
|
||||
pub gas_cost: Cost,
|
||||
pub provide_gas: Option<Cost>,
|
||||
pub memory_total_gas: Cost,
|
||||
pub memory_required_size: usize,
|
||||
pub gas_cost: Cost,
|
||||
pub provide_gas: Option<Cost>,
|
||||
pub memory_total_gas: Cost,
|
||||
pub memory_required_size: usize,
|
||||
}
|
||||
|
||||
pub struct Gasometer<Gas> {
|
||||
pub current_gas: Gas,
|
||||
pub current_mem_gas: Gas,
|
||||
pub current_gas: Gas,
|
||||
pub current_mem_gas: Gas,
|
||||
}
|
||||
|
||||
impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
pub fn new(current_gas: Gas) -> Self {
|
||||
Gasometer {
|
||||
current_gas: current_gas,
|
||||
current_mem_gas: Gas::from(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(current_gas: Gas) -> Self {
|
||||
Gasometer {
|
||||
current_gas: current_gas,
|
||||
current_mem_gas: Gas::from(0),
|
||||
}
|
||||
}
|
||||
pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> {
|
||||
match &self.current_gas < gas_cost {
|
||||
true => Err(vm::Error::OutOfGas),
|
||||
false => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> {
|
||||
match &self.current_gas < gas_cost {
|
||||
true => Err(vm::Error::OutOfGas),
|
||||
false => Ok(())
|
||||
}
|
||||
}
|
||||
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
|
||||
/// and that we `requested` some.
|
||||
pub fn gas_provided(
|
||||
&self,
|
||||
schedule: &Schedule,
|
||||
needed: Gas,
|
||||
requested: Option<U256>,
|
||||
) -> vm::Result<Gas> {
|
||||
// Try converting requested gas to `Gas` (`U256/u64`)
|
||||
// but in EIP150 even if we request more we should never fail from OOG
|
||||
let requested = requested.map(Gas::from_u256);
|
||||
|
||||
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
|
||||
/// and that we `requested` some.
|
||||
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> vm::Result<Gas> {
|
||||
// Try converting requested gas to `Gas` (`U256/u64`)
|
||||
// but in EIP150 even if we request more we should never fail from OOG
|
||||
let requested = requested.map(Gas::from_u256);
|
||||
match schedule.sub_gas_cap_divisor {
|
||||
Some(cap_divisor) if self.current_gas >= needed => {
|
||||
let gas_remaining = self.current_gas - needed;
|
||||
let max_gas_provided = match cap_divisor {
|
||||
64 => gas_remaining - (gas_remaining >> 6),
|
||||
cap_divisor => gas_remaining - gas_remaining / Gas::from(cap_divisor),
|
||||
};
|
||||
|
||||
match schedule.sub_gas_cap_divisor {
|
||||
Some(cap_divisor) if self.current_gas >= needed => {
|
||||
let gas_remaining = self.current_gas - needed;
|
||||
let max_gas_provided = match cap_divisor {
|
||||
64 => gas_remaining - (gas_remaining >> 6),
|
||||
cap_divisor => gas_remaining - gas_remaining / Gas::from(cap_divisor),
|
||||
};
|
||||
if let Some(Ok(r)) = requested {
|
||||
Ok(cmp::min(r, max_gas_provided))
|
||||
} else {
|
||||
Ok(max_gas_provided)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(r) = requested {
|
||||
r
|
||||
} else if self.current_gas >= needed {
|
||||
Ok(self.current_gas - needed)
|
||||
} else {
|
||||
Ok(0.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(Ok(r)) = requested {
|
||||
Ok(cmp::min(r, max_gas_provided))
|
||||
} else {
|
||||
Ok(max_gas_provided)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if let Some(r) = requested {
|
||||
r
|
||||
} else if self.current_gas >= needed {
|
||||
Ok(self.current_gas - needed)
|
||||
} else {
|
||||
Ok(0.into())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Determine how much gas is used by the given instruction, given the machine's state.
|
||||
///
|
||||
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
|
||||
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
|
||||
/// it will be the amount of gas that the current context provides to the child context.
|
||||
pub fn requirements(
|
||||
&mut self,
|
||||
ext: &vm::Ext,
|
||||
instruction: Instruction,
|
||||
info: &InstructionInfo,
|
||||
stack: &Stack<U256>,
|
||||
current_mem_size: usize,
|
||||
) -> vm::Result<InstructionRequirements<Gas>> {
|
||||
let schedule = ext.schedule();
|
||||
let tier = info.tier.idx();
|
||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||
|
||||
/// Determine how much gas is used by the given instruction, given the machine's state.
|
||||
///
|
||||
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
|
||||
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
|
||||
/// it will be the amount of gas that the current context provides to the child context.
|
||||
pub fn requirements(
|
||||
&mut self,
|
||||
ext: &vm::Ext,
|
||||
instruction: Instruction,
|
||||
info: &InstructionInfo,
|
||||
stack: &Stack<U256>,
|
||||
current_mem_size: usize,
|
||||
) -> vm::Result<InstructionRequirements<Gas>> {
|
||||
let schedule = ext.schedule();
|
||||
let tier = info.tier.idx();
|
||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||
let cost = match instruction {
|
||||
instructions::JUMPDEST => Request::Gas(Gas::from(1)),
|
||||
instructions::SSTORE => {
|
||||
if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) {
|
||||
return Err(vm::Error::OutOfGas);
|
||||
}
|
||||
let address = H256::from(stack.peek(0));
|
||||
let newval = stack.peek(1);
|
||||
let val = U256::from(&*ext.storage_at(&address)?);
|
||||
|
||||
let cost = match instruction {
|
||||
instructions::JUMPDEST => {
|
||||
Request::Gas(Gas::from(1))
|
||||
},
|
||||
instructions::SSTORE => {
|
||||
if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) {
|
||||
return Err(vm::Error::OutOfGas);
|
||||
}
|
||||
let address = H256::from(stack.peek(0));
|
||||
let newval = stack.peek(1);
|
||||
let val = U256::from(&*ext.storage_at(&address)?);
|
||||
let gas = if schedule.eip1283 {
|
||||
let orig = U256::from(&*ext.initial_storage_at(&address)?);
|
||||
calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval)
|
||||
} else {
|
||||
if val.is_zero() && !newval.is_zero() {
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// Refund for below case is added when actually executing sstore
|
||||
// !is_zero(&val) && is_zero(newval)
|
||||
schedule.sstore_reset_gas
|
||||
}
|
||||
};
|
||||
Request::Gas(Gas::from(gas))
|
||||
}
|
||||
instructions::SLOAD => Request::Gas(Gas::from(schedule.sload_gas)),
|
||||
instructions::BALANCE => Request::Gas(Gas::from(schedule.balance_gas)),
|
||||
instructions::EXTCODESIZE => Request::Gas(Gas::from(schedule.extcodesize_gas)),
|
||||
instructions::EXTCODEHASH => Request::Gas(Gas::from(schedule.extcodehash_gas)),
|
||||
instructions::SUICIDE => {
|
||||
let mut gas = Gas::from(schedule.suicide_gas);
|
||||
|
||||
let gas = if schedule.eip1283 {
|
||||
let orig = U256::from(&*ext.initial_storage_at(&address)?);
|
||||
calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval)
|
||||
} else {
|
||||
if val.is_zero() && !newval.is_zero() {
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// Refund for below case is added when actually executing sstore
|
||||
// !is_zero(&val) && is_zero(newval)
|
||||
schedule.sstore_reset_gas
|
||||
}
|
||||
};
|
||||
Request::Gas(Gas::from(gas))
|
||||
},
|
||||
instructions::SLOAD => {
|
||||
Request::Gas(Gas::from(schedule.sload_gas))
|
||||
},
|
||||
instructions::BALANCE => {
|
||||
Request::Gas(Gas::from(schedule.balance_gas))
|
||||
},
|
||||
instructions::EXTCODESIZE => {
|
||||
Request::Gas(Gas::from(schedule.extcodesize_gas))
|
||||
},
|
||||
instructions::EXTCODEHASH => {
|
||||
Request::Gas(Gas::from(schedule.extcodehash_gas))
|
||||
},
|
||||
instructions::SUICIDE => {
|
||||
let mut gas = Gas::from(schedule.suicide_gas);
|
||||
let is_value_transfer = !ext.origin_balance()?.is_zero();
|
||||
let address = u256_to_address(stack.peek(0));
|
||||
if (!schedule.no_empty && !ext.exists(&address)?)
|
||||
|| (schedule.no_empty
|
||||
&& is_value_transfer
|
||||
&& !ext.exists_and_not_null(&address)?)
|
||||
{
|
||||
gas =
|
||||
overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
|
||||
}
|
||||
|
||||
let is_value_transfer = !ext.origin_balance()?.is_zero();
|
||||
let address = u256_to_address(stack.peek(0));
|
||||
if (
|
||||
!schedule.no_empty && !ext.exists(&address)?
|
||||
) || (
|
||||
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?
|
||||
) {
|
||||
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
|
||||
}
|
||||
Request::Gas(gas)
|
||||
}
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?)
|
||||
}
|
||||
instructions::MSTORE8 => {
|
||||
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
|
||||
}
|
||||
instructions::RETURN | instructions::REVERT => {
|
||||
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
}
|
||||
instructions::SHA3 => {
|
||||
let words = overflowing!(to_word_size(Gas::from_u256(*stack.peek(1))?));
|
||||
let gas = overflowing!(Gas::from(schedule.sha3_gas).overflow_add(overflowing!(
|
||||
Gas::from(schedule.sha3_word_gas).overflow_mul(words)
|
||||
)));
|
||||
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
}
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => {
|
||||
Request::GasMemCopy(
|
||||
default_gas,
|
||||
mem_needed(stack.peek(0), stack.peek(2))?,
|
||||
Gas::from_u256(*stack.peek(2))?,
|
||||
)
|
||||
}
|
||||
instructions::EXTCODECOPY => Request::GasMemCopy(
|
||||
schedule.extcodecopy_base_gas.into(),
|
||||
mem_needed(stack.peek(1), stack.peek(3))?,
|
||||
Gas::from_u256(*stack.peek(3))?,
|
||||
),
|
||||
instructions::LOG0
|
||||
| instructions::LOG1
|
||||
| instructions::LOG2
|
||||
| instructions::LOG3
|
||||
| instructions::LOG4 => {
|
||||
let no_of_topics = instruction
|
||||
.log_topics()
|
||||
.expect("log_topics always return some for LOG* instructions; qed");
|
||||
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
||||
|
||||
Request::Gas(gas)
|
||||
},
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?)
|
||||
},
|
||||
instructions::MSTORE8 => {
|
||||
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
|
||||
},
|
||||
instructions::RETURN | instructions::REVERT => {
|
||||
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
},
|
||||
instructions::SHA3 => {
|
||||
let words = overflowing!(to_word_size(Gas::from_u256(*stack.peek(1))?));
|
||||
let gas = overflowing!(Gas::from(schedule.sha3_gas).overflow_add(overflowing!(Gas::from(schedule.sha3_word_gas).overflow_mul(words))));
|
||||
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
},
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => {
|
||||
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
|
||||
},
|
||||
instructions::EXTCODECOPY => {
|
||||
Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), mem_needed(stack.peek(1), stack.peek(3))?, Gas::from_u256(*stack.peek(3))?)
|
||||
},
|
||||
instructions::LOG0 | instructions::LOG1 | instructions::LOG2 | instructions::LOG3 | instructions::LOG4 => {
|
||||
let no_of_topics = instruction.log_topics().expect("log_topics always return some for LOG* instructions; qed");
|
||||
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
||||
let data_gas =
|
||||
overflowing!(Gas::from_u256(*stack.peek(1))?
|
||||
.overflow_mul(Gas::from(schedule.log_data_gas)));
|
||||
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
|
||||
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
}
|
||||
instructions::CALL | instructions::CALLCODE => {
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
mem_needed(stack.peek(5), stack.peek(6))?,
|
||||
mem_needed(stack.peek(3), stack.peek(4))?,
|
||||
);
|
||||
|
||||
let data_gas = overflowing!(Gas::from_u256(*stack.peek(1))?.overflow_mul(Gas::from(schedule.log_data_gas)));
|
||||
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
|
||||
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE => {
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
mem_needed(stack.peek(5), stack.peek(6))?,
|
||||
mem_needed(stack.peek(3), stack.peek(4))?
|
||||
);
|
||||
let address = u256_to_address(stack.peek(1));
|
||||
let is_value_transfer = !stack.peek(2).is_zero();
|
||||
|
||||
let address = u256_to_address(stack.peek(1));
|
||||
let is_value_transfer = !stack.peek(2).is_zero();
|
||||
if instruction == instructions::CALL
|
||||
&& ((!schedule.no_empty && !ext.exists(&address)?)
|
||||
|| (schedule.no_empty
|
||||
&& is_value_transfer
|
||||
&& !ext.exists_and_not_null(&address)?))
|
||||
{
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
|
||||
}
|
||||
|
||||
if instruction == instructions::CALL && (
|
||||
(!schedule.no_empty && !ext.exists(&address)?)
|
||||
||
|
||||
(schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?)
|
||||
) {
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
|
||||
}
|
||||
if is_value_transfer {
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
|
||||
}
|
||||
|
||||
if is_value_transfer {
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
|
||||
}
|
||||
let requested = *stack.peek(0);
|
||||
|
||||
let requested = *stack.peek(0);
|
||||
Request::GasMemProvide(gas, mem, Some(requested))
|
||||
}
|
||||
instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||
let gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
mem_needed(stack.peek(4), stack.peek(5))?,
|
||||
mem_needed(stack.peek(2), stack.peek(3))?,
|
||||
);
|
||||
let requested = *stack.peek(0);
|
||||
|
||||
Request::GasMemProvide(gas, mem, Some(requested))
|
||||
},
|
||||
instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||
let gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
mem_needed(stack.peek(4), stack.peek(5))?,
|
||||
mem_needed(stack.peek(2), stack.peek(3))?
|
||||
);
|
||||
let requested = *stack.peek(0);
|
||||
Request::GasMemProvide(gas, mem, Some(requested))
|
||||
}
|
||||
instructions::CREATE => {
|
||||
let start = stack.peek(1);
|
||||
let len = stack.peek(2);
|
||||
|
||||
Request::GasMemProvide(gas, mem, Some(requested))
|
||||
},
|
||||
instructions::CREATE => {
|
||||
let start = stack.peek(1);
|
||||
let len = stack.peek(2);
|
||||
let gas = Gas::from(schedule.create_gas);
|
||||
let mem = mem_needed(start, len)?;
|
||||
|
||||
let gas = Gas::from(schedule.create_gas);
|
||||
let mem = mem_needed(start, len)?;
|
||||
Request::GasMemProvide(gas, mem, None)
|
||||
}
|
||||
instructions::CREATE2 => {
|
||||
let start = stack.peek(1);
|
||||
let len = stack.peek(2);
|
||||
|
||||
Request::GasMemProvide(gas, mem, None)
|
||||
},
|
||||
instructions::CREATE2 => {
|
||||
let start = stack.peek(1);
|
||||
let len = stack.peek(2);
|
||||
let base = Gas::from(schedule.create_gas);
|
||||
let word = overflowing!(to_word_size(Gas::from_u256(*len)?));
|
||||
let word_gas = overflowing!(Gas::from(schedule.sha3_word_gas).overflow_mul(word));
|
||||
let gas = overflowing!(base.overflow_add(word_gas));
|
||||
let mem = mem_needed(start, len)?;
|
||||
|
||||
let base = Gas::from(schedule.create_gas);
|
||||
let word = overflowing!(to_word_size(Gas::from_u256(*len)?));
|
||||
let word_gas = overflowing!(Gas::from(schedule.sha3_word_gas).overflow_mul(word));
|
||||
let gas = overflowing!(base.overflow_add(word_gas));
|
||||
let mem = mem_needed(start, len)?;
|
||||
Request::GasMemProvide(gas, mem, None)
|
||||
}
|
||||
instructions::EXP => {
|
||||
let expon = stack.peek(1);
|
||||
let bytes = ((expon.bits() + 7) / 8) as usize;
|
||||
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||
Request::Gas(gas)
|
||||
}
|
||||
instructions::BLOCKHASH => Request::Gas(Gas::from(schedule.blockhash_gas)),
|
||||
_ => Request::Gas(default_gas),
|
||||
};
|
||||
|
||||
Request::GasMemProvide(gas, mem, None)
|
||||
},
|
||||
instructions::EXP => {
|
||||
let expon = stack.peek(1);
|
||||
let bytes = ((expon.bits() + 7) / 8) as usize;
|
||||
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||
Request::Gas(gas)
|
||||
},
|
||||
instructions::BLOCKHASH => {
|
||||
Request::Gas(Gas::from(schedule.blockhash_gas))
|
||||
},
|
||||
_ => Request::Gas(default_gas),
|
||||
};
|
||||
Ok(match cost {
|
||||
Request::Gas(gas) => InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: 0,
|
||||
memory_total_gas: self.current_mem_gas,
|
||||
},
|
||||
Request::GasMem(gas, mem_size) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) =
|
||||
self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
}
|
||||
Request::GasMemProvide(gas, mem_size, requested) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) =
|
||||
self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
let provided = self.gas_provided(schedule, gas, requested)?;
|
||||
let total_gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
Ok(match cost {
|
||||
Request::Gas(gas) => {
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: 0,
|
||||
memory_total_gas: self.current_mem_gas,
|
||||
}
|
||||
},
|
||||
Request::GasMem(gas, mem_size) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
},
|
||||
Request::GasMemProvide(gas, mem_size, requested) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
let provided = self.gas_provided(schedule, gas, requested)?;
|
||||
let total_gas = overflowing!(gas.overflow_add(provided));
|
||||
InstructionRequirements {
|
||||
gas_cost: total_gas,
|
||||
provide_gas: Some(provided),
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
}
|
||||
Request::GasMemCopy(gas, mem_size, copy) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) =
|
||||
self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let copy = overflowing!(to_word_size(copy));
|
||||
let copy_gas = overflowing!(Gas::from(schedule.copy_gas).overflow_mul(copy));
|
||||
let gas = overflowing!(gas.overflow_add(copy_gas));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
|
||||
InstructionRequirements {
|
||||
gas_cost: total_gas,
|
||||
provide_gas: Some(provided),
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
},
|
||||
Request::GasMemCopy(gas, mem_size, copy) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let copy = overflowing!(to_word_size(copy));
|
||||
let copy_gas = overflowing!(Gas::from(schedule.copy_gas).overflow_mul(copy));
|
||||
let gas = overflowing!(gas.overflow_add(copy_gas));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
fn mem_gas_cost(
|
||||
&self,
|
||||
schedule: &Schedule,
|
||||
current_mem_size: usize,
|
||||
mem_size: &Gas,
|
||||
) -> vm::Result<(Gas, Gas, usize)> {
|
||||
let gas_for_mem = |mem_size: Gas| {
|
||||
let s = mem_size >> 5;
|
||||
// s * memory_gas + s * s / quad_coeff_div
|
||||
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
|
||||
|
||||
fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> vm::Result<(Gas, Gas, usize)> {
|
||||
let gas_for_mem = |mem_size: Gas| {
|
||||
let s = mem_size >> 5;
|
||||
// s * memory_gas + s * s / quad_coeff_div
|
||||
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
|
||||
// Calculate s*s/quad_coeff_div
|
||||
assert_eq!(schedule.quad_coeff_div, 512);
|
||||
let b = overflowing!(s.overflow_mul_shr(s, 9));
|
||||
Ok(overflowing!(a.overflow_add(b)))
|
||||
};
|
||||
|
||||
// Calculate s*s/quad_coeff_div
|
||||
assert_eq!(schedule.quad_coeff_div, 512);
|
||||
let b = overflowing!(s.overflow_mul_shr(s, 9));
|
||||
Ok(overflowing!(a.overflow_add(b)))
|
||||
};
|
||||
let current_mem_size = Gas::from(current_mem_size);
|
||||
let req_mem_size_rounded = overflowing!(to_word_size(*mem_size)) << 5;
|
||||
|
||||
let current_mem_size = Gas::from(current_mem_size);
|
||||
let req_mem_size_rounded = overflowing!(to_word_size(*mem_size)) << 5;
|
||||
let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size {
|
||||
let new_mem_gas = gas_for_mem(req_mem_size_rounded)?;
|
||||
(new_mem_gas - self.current_mem_gas, new_mem_gas)
|
||||
} else {
|
||||
(Gas::from(0), self.current_mem_gas)
|
||||
};
|
||||
|
||||
let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size {
|
||||
let new_mem_gas = gas_for_mem(req_mem_size_rounded)?;
|
||||
(new_mem_gas - self.current_mem_gas, new_mem_gas)
|
||||
} else {
|
||||
(Gas::from(0), self.current_mem_gas)
|
||||
};
|
||||
|
||||
Ok((mem_gas_cost, new_mem_gas, req_mem_size_rounded.as_usize()))
|
||||
}
|
||||
Ok((mem_gas_cost, new_mem_gas, req_mem_size_rounded.as_usize()))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mem_needed_const<Gas: evm::CostType>(mem: &U256, add: usize) -> vm::Result<Gas> {
|
||||
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
|
||||
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mem_needed<Gas: evm::CostType>(offset: &U256, size: &U256) -> vm::Result<Gas> {
|
||||
if size.is_zero() {
|
||||
return Ok(Gas::from(0));
|
||||
}
|
||||
if size.is_zero() {
|
||||
return Ok(Gas::from(0));
|
||||
}
|
||||
|
||||
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
|
||||
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_gas_usize<Gas: evm::CostType>(value: Gas, num: usize) -> (Gas, bool) {
|
||||
value.overflow_add(Gas::from(num))
|
||||
value.overflow_add(Gas::from(num))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_word_size<Gas: evm::CostType>(value: Gas) -> (Gas, bool) {
|
||||
let (gas, overflow) = add_gas_usize(value, 31);
|
||||
if overflow {
|
||||
return (gas, overflow);
|
||||
}
|
||||
let (gas, overflow) = add_gas_usize(value, 31);
|
||||
if overflow {
|
||||
return (gas, overflow);
|
||||
}
|
||||
|
||||
(gas >> 5, false)
|
||||
(gas >> 5, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(schedule: &Schedule, original: &U256, current: &U256, new: &U256) -> Gas {
|
||||
Gas::from(
|
||||
if current == new {
|
||||
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
schedule.sload_gas
|
||||
} else {
|
||||
// 2. If current value does not equal new value
|
||||
if original == current {
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||
if original.is_zero() {
|
||||
// 2.1.1. If original value is 0, 20000 gas is deducted.
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted.
|
||||
schedule.sstore_reset_gas
|
||||
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(
|
||||
schedule: &Schedule,
|
||||
original: &U256,
|
||||
current: &U256,
|
||||
new: &U256,
|
||||
) -> Gas {
|
||||
Gas::from(if current == new {
|
||||
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
schedule.sload_gas
|
||||
} else {
|
||||
// 2. If current value does not equal new value
|
||||
if original == current {
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||
if original.is_zero() {
|
||||
// 2.1.1. If original value is 0, 20000 gas is deducted.
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted.
|
||||
schedule.sstore_reset_gas
|
||||
|
||||
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
|
||||
}
|
||||
} else {
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
|
||||
schedule.sload_gas
|
||||
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
|
||||
}
|
||||
} else {
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
|
||||
schedule.sload_gas
|
||||
|
||||
// 2.2.1. If original value is not 0
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
||||
// 2.2.1. If original value is not 0
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
||||
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||
}
|
||||
}
|
||||
)
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_eip1283_sstore_clears_refund(ext: &mut vm::Ext, original: &U256, current: &U256, new: &U256) {
|
||||
let sstore_clears_schedule = ext.schedule().sstore_refund_gas;
|
||||
pub fn handle_eip1283_sstore_clears_refund(
|
||||
ext: &mut vm::Ext,
|
||||
original: &U256,
|
||||
current: &U256,
|
||||
new: &U256,
|
||||
) {
|
||||
let sstore_clears_schedule = ext.schedule().sstore_refund_gas;
|
||||
|
||||
if current == new {
|
||||
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
} else {
|
||||
// 2. If current value does not equal new value
|
||||
if original == current {
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||
if original.is_zero() {
|
||||
// 2.1.1. If original value is 0, 20000 gas is deducted.
|
||||
} else {
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted.
|
||||
if new.is_zero() {
|
||||
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
|
||||
ext.add_sstore_refund(sstore_clears_schedule);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
|
||||
if current == new {
|
||||
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
} else {
|
||||
// 2. If current value does not equal new value
|
||||
if original == current {
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||
if original.is_zero() {
|
||||
// 2.1.1. If original value is 0, 20000 gas is deducted.
|
||||
} else {
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted.
|
||||
if new.is_zero() {
|
||||
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
|
||||
ext.add_sstore_refund(sstore_clears_schedule);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
|
||||
|
||||
if !original.is_zero() {
|
||||
// 2.2.1. If original value is not 0
|
||||
if current.is_zero() {
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
ext.sub_sstore_refund(sstore_clears_schedule);
|
||||
} else if new.is_zero() {
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
||||
ext.add_sstore_refund(sstore_clears_schedule);
|
||||
}
|
||||
}
|
||||
if !original.is_zero() {
|
||||
// 2.2.1. If original value is not 0
|
||||
if current.is_zero() {
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
ext.sub_sstore_refund(sstore_clears_schedule);
|
||||
} else if new.is_zero() {
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
||||
ext.add_sstore_refund(sstore_clears_schedule);
|
||||
}
|
||||
}
|
||||
|
||||
if original == new {
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||
if original.is_zero() {
|
||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||
let refund = ext.schedule().sstore_set_gas - ext.schedule().sload_gas;
|
||||
ext.add_sstore_refund(refund);
|
||||
} else {
|
||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||
let refund = ext.schedule().sstore_reset_gas - ext.schedule().sload_gas;
|
||||
ext.add_sstore_refund(refund);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if original == new {
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||
if original.is_zero() {
|
||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||
let refund = ext.schedule().sstore_set_gas - ext.schedule().sload_gas;
|
||||
ext.add_sstore_refund(refund);
|
||||
} else {
|
||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||
let refund = ext.schedule().sstore_reset_gas - ext.schedule().sload_gas;
|
||||
ext.add_sstore_refund(refund);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_gas_cost() {
|
||||
// given
|
||||
let gasometer = Gasometer::<U256>::new(U256::zero());
|
||||
let schedule = Schedule::default();
|
||||
let current_mem_size = 5;
|
||||
let mem_size = !U256::zero();
|
||||
// given
|
||||
let gasometer = Gasometer::<U256>::new(U256::zero());
|
||||
let schedule = Schedule::default();
|
||||
let current_mem_size = 5;
|
||||
let mem_size = !U256::zero();
|
||||
|
||||
// when
|
||||
let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size);
|
||||
// when
|
||||
let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size);
|
||||
|
||||
// then
|
||||
if result.is_ok() {
|
||||
assert!(false, "Should fail with OutOfGas");
|
||||
}
|
||||
// then
|
||||
if result.is_ok() {
|
||||
assert!(false, "Should fail with OutOfGas");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_mem_cost() {
|
||||
// given
|
||||
let gasometer = Gasometer::<usize>::new(0);
|
||||
let schedule = Schedule::default();
|
||||
let current_mem_size = 0;
|
||||
let mem_size = 5;
|
||||
// given
|
||||
let gasometer = Gasometer::<usize>::new(0);
|
||||
let schedule = Schedule::default();
|
||||
let current_mem_size = 0;
|
||||
let mem_size = 5;
|
||||
|
||||
// when
|
||||
let (mem_cost, new_mem_gas, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
|
||||
// when
|
||||
let (mem_cost, new_mem_gas, mem_size) = gasometer
|
||||
.mem_gas_cost(&schedule, current_mem_size, &mem_size)
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(mem_cost, 3);
|
||||
assert_eq!(new_mem_gas, 3);
|
||||
assert_eq!(mem_size, 32);
|
||||
// then
|
||||
assert_eq!(mem_cost, 3);
|
||||
assert_eq!(new_mem_gas, 3);
|
||||
assert_eq!(mem_size, 32);
|
||||
}
|
||||
|
||||
@@ -19,144 +19,156 @@ pub use self::inner::*;
|
||||
#[macro_use]
|
||||
#[cfg(not(feature = "evm-debug"))]
|
||||
mod inner {
|
||||
macro_rules! evm_debug {
|
||||
($x: expr) => {}
|
||||
}
|
||||
macro_rules! evm_debug {
|
||||
($x: expr) => {};
|
||||
}
|
||||
|
||||
pub struct EvmInformant;
|
||||
impl EvmInformant {
|
||||
pub fn new(_depth: usize) -> Self {
|
||||
EvmInformant {}
|
||||
}
|
||||
pub fn done(&mut self) {}
|
||||
}
|
||||
pub struct EvmInformant;
|
||||
impl EvmInformant {
|
||||
pub fn new(_depth: usize) -> Self {
|
||||
EvmInformant {}
|
||||
}
|
||||
pub fn done(&mut self) {}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(feature = "evm-debug")]
|
||||
mod inner {
|
||||
use std::iter;
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Instant, Duration};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
iter,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use ethereum_types::U256;
|
||||
use ethereum_types::U256;
|
||||
|
||||
use interpreter::stack::Stack;
|
||||
use instructions::{Instruction, InstructionInfo};
|
||||
use CostType;
|
||||
use instructions::{Instruction, InstructionInfo};
|
||||
use interpreter::stack::Stack;
|
||||
use CostType;
|
||||
|
||||
macro_rules! evm_debug {
|
||||
($x: expr) => {
|
||||
$x
|
||||
}
|
||||
}
|
||||
macro_rules! evm_debug {
|
||||
($x: expr) => {
|
||||
$x
|
||||
};
|
||||
}
|
||||
|
||||
fn print(data: String) {
|
||||
if cfg!(feature = "evm-debug-tests") {
|
||||
println!("{}", data);
|
||||
} else {
|
||||
debug!(target: "evm", "{}", data);
|
||||
}
|
||||
}
|
||||
fn print(data: String) {
|
||||
if cfg!(feature = "evm-debug-tests") {
|
||||
println!("{}", data);
|
||||
} else {
|
||||
debug!(target: "evm", "{}", data);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EvmInformant {
|
||||
spacing: String,
|
||||
last_instruction: Instant,
|
||||
stats: HashMap<Instruction, Stats>,
|
||||
}
|
||||
pub struct EvmInformant {
|
||||
spacing: String,
|
||||
last_instruction: Instant,
|
||||
stats: HashMap<Instruction, Stats>,
|
||||
}
|
||||
|
||||
impl EvmInformant {
|
||||
impl EvmInformant {
|
||||
fn color(instruction: Instruction, name: &str) -> String {
|
||||
let c = instruction as usize % 6;
|
||||
let colors = [31, 34, 33, 32, 35, 36];
|
||||
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
|
||||
}
|
||||
|
||||
fn color(instruction: Instruction, name: &str) -> String {
|
||||
let c = instruction as usize % 6;
|
||||
let colors = [31, 34, 33, 32, 35, 36];
|
||||
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
|
||||
}
|
||||
fn as_micro(duration: &Duration) -> u64 {
|
||||
let mut sec = duration.as_secs();
|
||||
let subsec = duration.subsec_nanos() as u64;
|
||||
sec = sec.saturating_mul(1_000_000u64);
|
||||
sec += subsec / 1_000;
|
||||
sec
|
||||
}
|
||||
|
||||
fn as_micro(duration: &Duration) -> u64 {
|
||||
let mut sec = duration.as_secs();
|
||||
let subsec = duration.subsec_nanos() as u64;
|
||||
sec = sec.saturating_mul(1_000_000u64);
|
||||
sec += subsec / 1_000;
|
||||
sec
|
||||
}
|
||||
pub fn new(depth: usize) -> Self {
|
||||
EvmInformant {
|
||||
spacing: iter::repeat(".").take(depth).collect(),
|
||||
last_instruction: Instant::now(),
|
||||
stats: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(depth: usize) -> Self {
|
||||
EvmInformant {
|
||||
spacing: iter::repeat(".").take(depth).collect(),
|
||||
last_instruction: Instant::now(),
|
||||
stats: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn before_instruction<Cost: CostType>(
|
||||
&mut self,
|
||||
pc: usize,
|
||||
instruction: Instruction,
|
||||
info: &InstructionInfo,
|
||||
current_gas: &Cost,
|
||||
stack: &Stack<U256>,
|
||||
) {
|
||||
let time = self.last_instruction.elapsed();
|
||||
self.last_instruction = Instant::now();
|
||||
|
||||
pub fn before_instruction<Cost: CostType>(&mut self, pc: usize, instruction: Instruction, info: &InstructionInfo, current_gas: &Cost, stack: &Stack<U256>) {
|
||||
let time = self.last_instruction.elapsed();
|
||||
self.last_instruction = Instant::now();
|
||||
print(format!(
|
||||
"{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)",
|
||||
&self.spacing,
|
||||
pc,
|
||||
Self::color(instruction, info.name),
|
||||
instruction as u8,
|
||||
current_gas,
|
||||
Self::as_micro(&time),
|
||||
));
|
||||
|
||||
print(format!("{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)",
|
||||
&self.spacing,
|
||||
pc,
|
||||
Self::color(instruction, info.name),
|
||||
instruction as u8,
|
||||
current_gas,
|
||||
Self::as_micro(&time),
|
||||
));
|
||||
if info.args > 0 {
|
||||
for (idx, item) in stack.peek_top(info.args).iter().enumerate() {
|
||||
print(format!("{} |{:2}: {:?}", self.spacing, idx, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info.args > 0 {
|
||||
for (idx, item) in stack.peek_top(info.args).iter().enumerate() {
|
||||
print(format!("{} |{:2}: {:?}", self.spacing, idx, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn after_instruction(&mut self, instruction: Instruction) {
|
||||
let stats = self
|
||||
.stats
|
||||
.entry(instruction)
|
||||
.or_insert_with(|| Stats::default());
|
||||
let took = self.last_instruction.elapsed();
|
||||
stats.note(took);
|
||||
}
|
||||
|
||||
pub fn after_instruction(&mut self, instruction: Instruction) {
|
||||
let stats = self.stats.entry(instruction).or_insert_with(|| Stats::default());
|
||||
let took = self.last_instruction.elapsed();
|
||||
stats.note(took);
|
||||
}
|
||||
pub fn done(&mut self) {
|
||||
// Print out stats
|
||||
let mut stats: Vec<(_, _)> = self.stats.drain().collect();
|
||||
stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg()));
|
||||
|
||||
pub fn done(&mut self) {
|
||||
// Print out stats
|
||||
let mut stats: Vec<(_,_)> = self.stats.drain().collect();
|
||||
stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg()));
|
||||
print(format!("\n{}-------OPCODE STATS:", self.spacing));
|
||||
for (instruction, stats) in stats.into_iter() {
|
||||
let info = instruction.info();
|
||||
print(format!(
|
||||
"{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs",
|
||||
self.spacing,
|
||||
Self::color(instruction, info.name),
|
||||
instruction as u8,
|
||||
stats.count,
|
||||
stats.avg(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print(format!("\n{}-------OPCODE STATS:", self.spacing));
|
||||
for (instruction, stats) in stats.into_iter() {
|
||||
let info = instruction.info();
|
||||
print(format!("{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs",
|
||||
self.spacing,
|
||||
Self::color(instruction, info.name),
|
||||
instruction as u8,
|
||||
stats.count,
|
||||
stats.avg(),
|
||||
));
|
||||
}
|
||||
}
|
||||
struct Stats {
|
||||
count: u64,
|
||||
total_duration: Duration,
|
||||
}
|
||||
|
||||
}
|
||||
impl Default for Stats {
|
||||
fn default() -> Self {
|
||||
Stats {
|
||||
count: 0,
|
||||
total_duration: Duration::from_secs(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Stats {
|
||||
count: u64,
|
||||
total_duration: Duration,
|
||||
}
|
||||
impl Stats {
|
||||
fn note(&mut self, took: Duration) {
|
||||
self.count += 1;
|
||||
self.total_duration += took;
|
||||
}
|
||||
|
||||
impl Default for Stats {
|
||||
fn default() -> Self {
|
||||
Stats {
|
||||
count: 0,
|
||||
total_duration: Duration::from_secs(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
fn note(&mut self, took: Duration) {
|
||||
self.count += 1;
|
||||
self.total_duration += took;
|
||||
}
|
||||
|
||||
fn avg(&self) -> u64 {
|
||||
EvmInformant::as_micro(&self.total_duration) / self.count
|
||||
}
|
||||
}
|
||||
fn avg(&self) -> u64 {
|
||||
EvmInformant::as_micro(&self.total_duration) / self.count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,172 +20,175 @@ use vm::ReturnData;
|
||||
const MAX_RETURN_WASTE_BYTES: usize = 16384;
|
||||
|
||||
pub trait Memory {
|
||||
/// Retrieve current size of the memory
|
||||
fn size(&self) -> usize;
|
||||
/// Resize (shrink or expand) the memory to specified size (fills 0)
|
||||
fn resize(&mut self, new_size: usize);
|
||||
/// Resize the memory only if its smaller
|
||||
fn expand(&mut self, new_size: usize);
|
||||
/// Write single byte to memory
|
||||
fn write_byte(&mut self, offset: U256, value: U256);
|
||||
/// Write a word to memory. Does not resize memory!
|
||||
fn write(&mut self, offset: U256, value: U256);
|
||||
/// Read a word from memory
|
||||
fn read(&self, offset: U256) -> U256;
|
||||
/// Write slice of bytes to memory. Does not resize memory!
|
||||
fn write_slice(&mut self, offset: U256, &[u8]);
|
||||
/// Retrieve part of the memory between offset and offset + size
|
||||
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
|
||||
/// Retrieve writeable part of memory
|
||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
|
||||
/// Convert memory into return data.
|
||||
fn into_return_data(self, offset: U256, size: U256) -> ReturnData;
|
||||
/// Retrieve current size of the memory
|
||||
fn size(&self) -> usize;
|
||||
/// Resize (shrink or expand) the memory to specified size (fills 0)
|
||||
fn resize(&mut self, new_size: usize);
|
||||
/// Resize the memory only if its smaller
|
||||
fn expand(&mut self, new_size: usize);
|
||||
/// Write single byte to memory
|
||||
fn write_byte(&mut self, offset: U256, value: U256);
|
||||
/// Write a word to memory. Does not resize memory!
|
||||
fn write(&mut self, offset: U256, value: U256);
|
||||
/// Read a word from memory
|
||||
fn read(&self, offset: U256) -> U256;
|
||||
/// Write slice of bytes to memory. Does not resize memory!
|
||||
fn write_slice(&mut self, offset: U256, &[u8]);
|
||||
/// Retrieve part of the memory between offset and offset + size
|
||||
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
|
||||
/// Retrieve writeable part of memory
|
||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8];
|
||||
/// Convert memory into return data.
|
||||
fn into_return_data(self, offset: U256, size: U256) -> ReturnData;
|
||||
}
|
||||
|
||||
/// Checks whether offset and size is valid memory range
|
||||
pub fn is_valid_range(off: usize, size: usize) -> bool {
|
||||
// When size is zero we haven't actually expanded the memory
|
||||
let overflow = off.overflowing_add(size).1;
|
||||
size > 0 && !overflow
|
||||
pub fn is_valid_range(off: usize, size: usize) -> bool {
|
||||
// When size is zero we haven't actually expanded the memory
|
||||
let overflow = off.overflowing_add(size).1;
|
||||
size > 0 && !overflow
|
||||
}
|
||||
|
||||
impl Memory for Vec<u8> {
|
||||
fn size(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
|
||||
let off = init_off_u.low_u64() as usize;
|
||||
let size = init_size_u.low_u64() as usize;
|
||||
if !is_valid_range(off, size) {
|
||||
&self[0..0]
|
||||
} else {
|
||||
&self[off..off+size]
|
||||
}
|
||||
}
|
||||
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
|
||||
let off = init_off_u.low_u64() as usize;
|
||||
let size = init_size_u.low_u64() as usize;
|
||||
if !is_valid_range(off, size) {
|
||||
&self[0..0]
|
||||
} else {
|
||||
&self[off..off + size]
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, offset: U256) -> U256 {
|
||||
let off = offset.low_u64() as usize;
|
||||
U256::from(&self[off..off+32])
|
||||
}
|
||||
fn read(&self, offset: U256) -> U256 {
|
||||
let off = offset.low_u64() as usize;
|
||||
U256::from(&self[off..off + 32])
|
||||
}
|
||||
|
||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
|
||||
let off = offset.low_u64() as usize;
|
||||
let s = size.low_u64() as usize;
|
||||
if !is_valid_range(off, s) {
|
||||
&mut self[0..0]
|
||||
} else {
|
||||
&mut self[off..off+s]
|
||||
}
|
||||
}
|
||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
|
||||
let off = offset.low_u64() as usize;
|
||||
let s = size.low_u64() as usize;
|
||||
if !is_valid_range(off, s) {
|
||||
&mut self[0..0]
|
||||
} else {
|
||||
&mut self[off..off + s]
|
||||
}
|
||||
}
|
||||
|
||||
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
|
||||
if !slice.is_empty() {
|
||||
let off = offset.low_u64() as usize;
|
||||
self[off..off+slice.len()].copy_from_slice(slice);
|
||||
}
|
||||
}
|
||||
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
|
||||
if !slice.is_empty() {
|
||||
let off = offset.low_u64() as usize;
|
||||
self[off..off + slice.len()].copy_from_slice(slice);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: U256, value: U256) {
|
||||
let off = offset.low_u64() as usize;
|
||||
value.to_big_endian(&mut self[off..off+32]);
|
||||
}
|
||||
fn write(&mut self, offset: U256, value: U256) {
|
||||
let off = offset.low_u64() as usize;
|
||||
value.to_big_endian(&mut self[off..off + 32]);
|
||||
}
|
||||
|
||||
fn write_byte(&mut self, offset: U256, value: U256) {
|
||||
let off = offset.low_u64() as usize;
|
||||
let val = value.low_u64() as u64;
|
||||
self[off] = val as u8;
|
||||
}
|
||||
fn write_byte(&mut self, offset: U256, value: U256) {
|
||||
let off = offset.low_u64() as usize;
|
||||
let val = value.low_u64() as u64;
|
||||
self[off] = val as u8;
|
||||
}
|
||||
|
||||
fn resize(&mut self, new_size: usize) {
|
||||
self.resize(new_size, 0);
|
||||
}
|
||||
fn resize(&mut self, new_size: usize) {
|
||||
self.resize(new_size, 0);
|
||||
}
|
||||
|
||||
fn expand(&mut self, size: usize) {
|
||||
if size > self.len() {
|
||||
Memory::resize(self, size)
|
||||
}
|
||||
}
|
||||
fn expand(&mut self, size: usize) {
|
||||
if size > self.len() {
|
||||
Memory::resize(self, size)
|
||||
}
|
||||
}
|
||||
|
||||
fn into_return_data(mut self, offset: U256, size: U256) -> ReturnData {
|
||||
let mut offset = offset.low_u64() as usize;
|
||||
let size = size.low_u64() as usize;
|
||||
fn into_return_data(mut self, offset: U256, size: U256) -> ReturnData {
|
||||
let mut offset = offset.low_u64() as usize;
|
||||
let size = size.low_u64() as usize;
|
||||
|
||||
if !is_valid_range(offset, size) {
|
||||
return ReturnData::empty();
|
||||
}
|
||||
if !is_valid_range(offset, size) {
|
||||
return ReturnData::empty();
|
||||
}
|
||||
|
||||
if self.len() - size > MAX_RETURN_WASTE_BYTES {
|
||||
if offset == 0 {
|
||||
self.truncate(size);
|
||||
self.shrink_to_fit();
|
||||
} else {
|
||||
self = self[offset..(offset + size)].to_vec();
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
ReturnData::new(self, offset, size)
|
||||
}
|
||||
if self.len() - size > MAX_RETURN_WASTE_BYTES {
|
||||
if offset == 0 {
|
||||
self.truncate(size);
|
||||
self.shrink_to_fit();
|
||||
} else {
|
||||
self = self[offset..(offset + size)].to_vec();
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
ReturnData::new(self, offset, size)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ethereum_types::U256;
|
||||
use super::Memory;
|
||||
use super::Memory;
|
||||
use ethereum_types::U256;
|
||||
|
||||
#[test]
|
||||
fn test_memory_read_and_write() {
|
||||
// given
|
||||
let mem: &mut Memory = &mut vec![];
|
||||
mem.resize(0x80 + 32);
|
||||
#[test]
|
||||
fn test_memory_read_and_write() {
|
||||
// given
|
||||
let mem: &mut Memory = &mut vec![];
|
||||
mem.resize(0x80 + 32);
|
||||
|
||||
// when
|
||||
mem.write(U256::from(0x80), U256::from(0xabcdef));
|
||||
// when
|
||||
mem.write(U256::from(0x80), U256::from(0xabcdef));
|
||||
|
||||
// then
|
||||
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
|
||||
}
|
||||
// then
|
||||
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_read_and_write_byte() {
|
||||
// given
|
||||
let mem: &mut Memory = &mut vec![];
|
||||
mem.resize(32);
|
||||
#[test]
|
||||
fn test_memory_read_and_write_byte() {
|
||||
// given
|
||||
let mem: &mut Memory = &mut vec![];
|
||||
mem.resize(32);
|
||||
|
||||
// when
|
||||
mem.write_byte(U256::from(0x1d), U256::from(0xab));
|
||||
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
|
||||
mem.write_byte(U256::from(0x1f), U256::from(0xef));
|
||||
// when
|
||||
mem.write_byte(U256::from(0x1d), U256::from(0xab));
|
||||
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
|
||||
mem.write_byte(U256::from(0x1f), U256::from(0xef));
|
||||
|
||||
// then
|
||||
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
|
||||
}
|
||||
// then
|
||||
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_read_slice_and_write_slice() {
|
||||
let mem: &mut Memory = &mut vec![];
|
||||
mem.resize(32);
|
||||
#[test]
|
||||
fn test_memory_read_slice_and_write_slice() {
|
||||
let mem: &mut Memory = &mut vec![];
|
||||
mem.resize(32);
|
||||
|
||||
{
|
||||
let slice = "abcdefghijklmnopqrstuvwxyz012345".as_bytes();
|
||||
mem.write_slice(U256::from(0), slice);
|
||||
{
|
||||
let slice = "abcdefghijklmnopqrstuvwxyz012345".as_bytes();
|
||||
mem.write_slice(U256::from(0), slice);
|
||||
|
||||
assert_eq!(mem.read_slice(U256::from(0), U256::from(32)), slice);
|
||||
}
|
||||
assert_eq!(mem.read_slice(U256::from(0), U256::from(32)), slice);
|
||||
}
|
||||
|
||||
// write again
|
||||
{
|
||||
let slice = "67890".as_bytes();
|
||||
mem.write_slice(U256::from(0x1), slice);
|
||||
// write again
|
||||
{
|
||||
let slice = "67890".as_bytes();
|
||||
mem.write_slice(U256::from(0x1), slice);
|
||||
|
||||
assert_eq!(mem.read_slice(U256::from(0), U256::from(7)), "a67890g".as_bytes());
|
||||
}
|
||||
assert_eq!(
|
||||
mem.read_slice(U256::from(0), U256::from(7)),
|
||||
"a67890g".as_bytes()
|
||||
);
|
||||
}
|
||||
|
||||
// write empty slice out of bounds
|
||||
{
|
||||
let slice = [];
|
||||
mem.write_slice(U256::from(0x1000), &slice);
|
||||
assert_eq!(mem.size(), 32);
|
||||
}
|
||||
}
|
||||
// write empty slice out of bounds
|
||||
{
|
||||
let slice = [];
|
||||
mem.write_slice(U256::from(0x1000), &slice);
|
||||
assert_eq!(mem.size(), 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,14 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use super::super::instructions::{self, Instruction};
|
||||
use bit_set::BitSet;
|
||||
use ethereum_types::H256;
|
||||
use hash::KECCAK_EMPTY;
|
||||
use heapsize::HeapSizeOf;
|
||||
use ethereum_types::H256;
|
||||
use parking_lot::Mutex;
|
||||
use memory_cache::MemoryLruCache;
|
||||
use bit_set::BitSet;
|
||||
use super::super::instructions::{self, Instruction};
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
@@ -29,84 +29,86 @@ const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
|
||||
struct Bits(Arc<BitSet>);
|
||||
|
||||
impl HeapSizeOf for Bits {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
// dealing in bits here
|
||||
self.0.capacity() * 8
|
||||
}
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
// dealing in bits here
|
||||
self.0.capacity() * 8
|
||||
}
|
||||
}
|
||||
|
||||
/// Global cache for EVM interpreter
|
||||
pub struct SharedCache {
|
||||
jump_destinations: Mutex<MemoryLruCache<H256, Bits>>,
|
||||
jump_destinations: Mutex<MemoryLruCache<H256, Bits>>,
|
||||
}
|
||||
|
||||
impl SharedCache {
|
||||
/// Create a jump destinations cache with a maximum size in bytes
|
||||
/// to cache.
|
||||
pub fn new(max_size: usize) -> Self {
|
||||
SharedCache {
|
||||
jump_destinations: Mutex::new(MemoryLruCache::new(max_size)),
|
||||
}
|
||||
}
|
||||
/// Create a jump destinations cache with a maximum size in bytes
|
||||
/// to cache.
|
||||
pub fn new(max_size: usize) -> Self {
|
||||
SharedCache {
|
||||
jump_destinations: Mutex::new(MemoryLruCache::new(max_size)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get jump destinations bitmap for a contract.
|
||||
pub fn jump_destinations(&self, code_hash: &Option<H256>, code: &[u8]) -> Arc<BitSet> {
|
||||
if let Some(ref code_hash) = code_hash {
|
||||
if code_hash == &KECCAK_EMPTY {
|
||||
return Self::find_jump_destinations(code);
|
||||
}
|
||||
/// Get jump destinations bitmap for a contract.
|
||||
pub fn jump_destinations(&self, code_hash: &Option<H256>, code: &[u8]) -> Arc<BitSet> {
|
||||
if let Some(ref code_hash) = code_hash {
|
||||
if code_hash == &KECCAK_EMPTY {
|
||||
return Self::find_jump_destinations(code);
|
||||
}
|
||||
|
||||
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
|
||||
return d.0.clone();
|
||||
}
|
||||
}
|
||||
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
|
||||
return d.0.clone();
|
||||
}
|
||||
}
|
||||
|
||||
let d = Self::find_jump_destinations(code);
|
||||
let d = Self::find_jump_destinations(code);
|
||||
|
||||
if let Some(ref code_hash) = code_hash {
|
||||
self.jump_destinations.lock().insert(*code_hash, Bits(d.clone()));
|
||||
}
|
||||
if let Some(ref code_hash) = code_hash {
|
||||
self.jump_destinations
|
||||
.lock()
|
||||
.insert(*code_hash, Bits(d.clone()));
|
||||
}
|
||||
|
||||
d
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
fn find_jump_destinations(code: &[u8]) -> Arc<BitSet> {
|
||||
let mut jump_dests = BitSet::with_capacity(code.len());
|
||||
let mut position = 0;
|
||||
fn find_jump_destinations(code: &[u8]) -> Arc<BitSet> {
|
||||
let mut jump_dests = BitSet::with_capacity(code.len());
|
||||
let mut position = 0;
|
||||
|
||||
while position < code.len() {
|
||||
let instruction = Instruction::from_u8(code[position]);
|
||||
while position < code.len() {
|
||||
let instruction = Instruction::from_u8(code[position]);
|
||||
|
||||
if let Some(instruction) = instruction {
|
||||
if instruction == instructions::JUMPDEST {
|
||||
jump_dests.insert(position);
|
||||
} else if let Some(push_bytes) = instruction.push_bytes() {
|
||||
position += push_bytes;
|
||||
}
|
||||
}
|
||||
position += 1;
|
||||
}
|
||||
if let Some(instruction) = instruction {
|
||||
if instruction == instructions::JUMPDEST {
|
||||
jump_dests.insert(position);
|
||||
} else if let Some(push_bytes) = instruction.push_bytes() {
|
||||
position += push_bytes;
|
||||
}
|
||||
}
|
||||
position += 1;
|
||||
}
|
||||
|
||||
jump_dests.shrink_to_fit();
|
||||
Arc::new(jump_dests)
|
||||
}
|
||||
jump_dests.shrink_to_fit();
|
||||
Arc::new(jump_dests)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SharedCache {
|
||||
fn default() -> Self {
|
||||
SharedCache::new(DEFAULT_CACHE_SIZE)
|
||||
}
|
||||
fn default() -> Self {
|
||||
SharedCache::new(DEFAULT_CACHE_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_jump_destinations() {
|
||||
use rustc_hex::FromHex;
|
||||
// given
|
||||
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
|
||||
use rustc_hex::FromHex;
|
||||
// given
|
||||
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
|
||||
|
||||
// when
|
||||
let valid_jump_destinations = SharedCache::find_jump_destinations(&code);
|
||||
// when
|
||||
let valid_jump_destinations = SharedCache::find_jump_destinations(&code);
|
||||
|
||||
// then
|
||||
assert!(valid_jump_destinations.contains(66));
|
||||
// then
|
||||
assert!(valid_jump_destinations.contains(66));
|
||||
}
|
||||
|
||||
@@ -14,80 +14,85 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use instructions;
|
||||
use std::fmt;
|
||||
|
||||
/// Stack trait with VM-friendly API
|
||||
pub trait Stack<T> {
|
||||
/// Returns `Stack[len(Stack) - no_from_top]`
|
||||
fn peek(&self, no_from_top: usize) -> &T;
|
||||
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
|
||||
fn swap_with_top(&mut self, no_from_top: usize);
|
||||
/// Returns true if Stack has at least `no_of_elems` elements
|
||||
fn has(&self, no_of_elems: usize) -> bool;
|
||||
/// Get element from top and remove it from Stack. Panics if stack is empty.
|
||||
fn pop_back(&mut self) -> T;
|
||||
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
|
||||
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
|
||||
/// Add element on top of the Stack
|
||||
fn push(&mut self, elem: T);
|
||||
/// Get number of elements on Stack
|
||||
fn size(&self) -> usize;
|
||||
/// Returns all data on stack.
|
||||
fn peek_top(&self, no_of_elems: usize) -> &[T];
|
||||
/// Returns `Stack[len(Stack) - no_from_top]`
|
||||
fn peek(&self, no_from_top: usize) -> &T;
|
||||
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
|
||||
fn swap_with_top(&mut self, no_from_top: usize);
|
||||
/// Returns true if Stack has at least `no_of_elems` elements
|
||||
fn has(&self, no_of_elems: usize) -> bool;
|
||||
/// Get element from top and remove it from Stack. Panics if stack is empty.
|
||||
fn pop_back(&mut self) -> T;
|
||||
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
|
||||
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
|
||||
/// Add element on top of the Stack
|
||||
fn push(&mut self, elem: T);
|
||||
/// Get number of elements on Stack
|
||||
fn size(&self) -> usize;
|
||||
/// Returns all data on stack.
|
||||
fn peek_top(&self, no_of_elems: usize) -> &[T];
|
||||
}
|
||||
|
||||
pub struct VecStack<S> {
|
||||
stack: Vec<S>,
|
||||
logs: [S; instructions::MAX_NO_OF_TOPICS]
|
||||
stack: Vec<S>,
|
||||
logs: [S; instructions::MAX_NO_OF_TOPICS],
|
||||
}
|
||||
|
||||
impl<S : Copy> VecStack<S> {
|
||||
pub fn with_capacity(capacity: usize, zero: S) -> Self {
|
||||
VecStack {
|
||||
stack: Vec::with_capacity(capacity),
|
||||
logs: [zero; instructions::MAX_NO_OF_TOPICS]
|
||||
}
|
||||
}
|
||||
impl<S: Copy> VecStack<S> {
|
||||
pub fn with_capacity(capacity: usize, zero: S) -> Self {
|
||||
VecStack {
|
||||
stack: Vec::with_capacity(capacity),
|
||||
logs: [zero; instructions::MAX_NO_OF_TOPICS],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S : fmt::Display> Stack<S> for VecStack<S> {
|
||||
fn peek(&self, no_from_top: usize) -> &S {
|
||||
&self.stack[self.stack.len() - no_from_top - 1]
|
||||
}
|
||||
impl<S: fmt::Display> Stack<S> for VecStack<S> {
|
||||
fn peek(&self, no_from_top: usize) -> &S {
|
||||
&self.stack[self.stack.len() - no_from_top - 1]
|
||||
}
|
||||
|
||||
fn swap_with_top(&mut self, no_from_top: usize) {
|
||||
let len = self.stack.len();
|
||||
self.stack.swap(len - no_from_top - 1, len - 1);
|
||||
}
|
||||
fn swap_with_top(&mut self, no_from_top: usize) {
|
||||
let len = self.stack.len();
|
||||
self.stack.swap(len - no_from_top - 1, len - 1);
|
||||
}
|
||||
|
||||
fn has(&self, no_of_elems: usize) -> bool {
|
||||
self.stack.len() >= no_of_elems
|
||||
}
|
||||
fn has(&self, no_of_elems: usize) -> bool {
|
||||
self.stack.len() >= no_of_elems
|
||||
}
|
||||
|
||||
fn pop_back(&mut self) -> S {
|
||||
self.stack.pop().expect("instruction validation prevents from popping too many items; qed")
|
||||
}
|
||||
fn pop_back(&mut self) -> S {
|
||||
self.stack
|
||||
.pop()
|
||||
.expect("instruction validation prevents from popping too many items; qed")
|
||||
}
|
||||
|
||||
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
|
||||
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
|
||||
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
|
||||
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
|
||||
|
||||
for i in 0..no_of_elems {
|
||||
self.logs[i] = self.pop_back();
|
||||
}
|
||||
&self.logs[0..no_of_elems]
|
||||
}
|
||||
for i in 0..no_of_elems {
|
||||
self.logs[i] = self.pop_back();
|
||||
}
|
||||
&self.logs[0..no_of_elems]
|
||||
}
|
||||
|
||||
fn push(&mut self, elem: S) {
|
||||
self.stack.push(elem);
|
||||
}
|
||||
fn push(&mut self, elem: S) {
|
||||
self.stack.push(elem);
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
fn peek_top(&self, no_from_top: usize) -> &[S] {
|
||||
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
|
||||
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
|
||||
}
|
||||
fn peek_top(&self, no_from_top: usize) -> &[S] {
|
||||
assert!(
|
||||
self.stack.len() >= no_from_top,
|
||||
"peek_top asked for more items than exist."
|
||||
);
|
||||
&self.stack[self.stack.len() - no_from_top..self.stack.len()]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
|
||||
extern crate bit_set;
|
||||
extern crate ethereum_types;
|
||||
extern crate parking_lot;
|
||||
extern crate heapsize;
|
||||
extern crate vm;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate memory_cache;
|
||||
extern crate parity_bytes as bytes;
|
||||
extern crate num_bigint;
|
||||
extern crate parity_bytes as bytes;
|
||||
extern crate parking_lot;
|
||||
extern crate vm;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
@@ -32,28 +32,29 @@ extern crate lazy_static;
|
||||
#[cfg_attr(feature = "evm-debug", macro_use)]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rustc_hex;
|
||||
#[cfg(test)]
|
||||
extern crate hex_literal;
|
||||
#[cfg(test)]
|
||||
extern crate rustc_hex;
|
||||
|
||||
pub mod evm;
|
||||
pub mod interpreter;
|
||||
|
||||
#[macro_use]
|
||||
pub mod factory;
|
||||
mod vmtype;
|
||||
mod instructions;
|
||||
mod vmtype;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use vm::{
|
||||
Schedule, CleanDustMode, EnvInfo, CallType, ActionParams, Ext,
|
||||
ContractCreateResult, MessageCallResult, CreateContractAddress,
|
||||
GasLeft, ReturnData
|
||||
pub use self::{
|
||||
evm::{CostType, FinalizationResult, Finalize},
|
||||
factory::Factory,
|
||||
instructions::{Instruction, InstructionInfo},
|
||||
vmtype::VMType,
|
||||
};
|
||||
pub use vm::{
|
||||
ActionParams, CallType, CleanDustMode, ContractCreateResult, CreateContractAddress, EnvInfo,
|
||||
Ext, GasLeft, MessageCallResult, ReturnData, Schedule,
|
||||
};
|
||||
pub use self::evm::{Finalize, FinalizationResult, CostType};
|
||||
pub use self::instructions::{InstructionInfo, Instruction};
|
||||
pub use self::vmtype::VMType;
|
||||
pub use self::factory::Factory;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,27 +19,31 @@ use std::fmt;
|
||||
/// Type of EVM to use.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum VMType {
|
||||
/// RUST EVM
|
||||
Interpreter
|
||||
/// RUST EVM
|
||||
Interpreter,
|
||||
}
|
||||
|
||||
impl fmt::Display for VMType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", match *self {
|
||||
VMType::Interpreter => "INT"
|
||||
})
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match *self {
|
||||
VMType::Interpreter => "INT",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VMType {
|
||||
fn default() -> Self {
|
||||
VMType::Interpreter
|
||||
}
|
||||
fn default() -> Self {
|
||||
VMType::Interpreter
|
||||
}
|
||||
}
|
||||
|
||||
impl VMType {
|
||||
/// Return all possible VMs (Interpreter)
|
||||
pub fn all() -> Vec<VMType> {
|
||||
vec![VMType::Interpreter]
|
||||
}
|
||||
/// Return all possible VMs (Interpreter)
|
||||
pub fn all() -> Vec<VMType> {
|
||||
vec![VMType::Interpreter]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user