Skip to content
This repository was archived by the owner on Nov 20, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
**/*.rs.bk
.idea
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

132 changes: 102 additions & 30 deletions ethcore-builtin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ struct Linear {
#[derive(Debug)]
struct ModexpPricer {
divisor: u64,
is_eip_2565: bool,
}

impl Pricer for Linear {
Expand Down Expand Up @@ -157,17 +158,79 @@ impl Pricer for AltBn128PairingPricer {

impl Pricer for ModexpPricer {
fn cost(&self, input: &[u8]) -> U256 {
let mut reader = input.chain(io::repeat(0));
let mut buf = [0; 32];
if self.is_eip_2565 {
let mut buf = [0; 32];
let (base_len, exp_len, mod_len) = Self::read_lengths(input, &mut buf);
let exp = Self::read_exp(input, base_len, exp_len, &mut buf);
Self::eip_2565_cost(
self.divisor.into(),
base_len,
mod_len,
exp_len,
exp,
)
} else {
Self::cost(self.divisor, input)
}
}
}

// read lengths as U256 here for accurate gas calculation.
impl ModexpPricer {
fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 {
let bit_index = if exp_low.is_zero() { 0 } else { (255 - exp_low.leading_zeros()) as u64 };
if len <= 32 {
bit_index
} else {
8 * (len - 32) + bit_index
}
}

fn mult_complexity(x: u64) -> u64 {
match x {
x if x <= 64 => x * x,
x if x <= 1024 => (x * x) / 4 + 96 * x - 3072,
x => (x * x) / 16 + 480 * x - 199_680,
}
}

fn read_lengths(input: &[u8], buf: &mut [u8; 32]) -> (U256, U256, U256) {
let mut reader = input.chain(io::repeat(0));
let mut read_len = || {
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
U256::from_big_endian(&buf[..])
};
let base_len = read_len();
let exp_len = read_len();
let mod_len = read_len();
(base_len, exp_len, mod_len)
}

fn read_exp(input: &[u8], base_len: U256, exp_len: U256, buf: &mut [u8; 32]) -> U256 {
let base_len = if base_len > U256::from(u32::MAX) {
return U256::zero();
} else {
base_len.low_u64()
};
if base_len + 96 >= input.len() as u64 {
U256::zero()
} else {
buf.iter_mut().for_each(|b| *b = 0);
let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0));
let len = if exp_len < U256::from(32) {
exp_len.low_u64() as usize
} else {
32
};
reader.read_exact(&mut buf[(32 - len)..]).expect("reading from zero-extended memory cannot fail; qed");
U256::from_big_endian(&buf[..])
}
}

fn cost(divisor: u64, input: &[u8]) -> U256 {
let mut buf = [0; 32];

// read lengths as U256 here for accurate gas calculation.
let (base_len, exp_len, mod_len) = Self::read_lengths(input, &mut buf);

if mod_len.is_zero() && base_len.is_zero() {
return U256::zero()
Expand All @@ -177,46 +240,54 @@ impl Pricer for ModexpPricer {
if base_len > max_len || mod_len > max_len || exp_len > max_len {
return U256::max_value();
}

// read fist 32-byte word of the exponent.
let exp_low = Self::read_exp(input, base_len, exp_len, &mut buf);

let (base_len, exp_len, mod_len) = (base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64());

let m = max(mod_len, base_len);
// read fist 32-byte word of the exponent.
let exp_low = if base_len + 96 >= input.len() as u64 {
U256::zero()
} else {
buf.iter_mut().for_each(|b| *b = 0);
let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0));
let len = min(exp_len, 32) as usize;
reader.read_exact(&mut buf[(32 - len)..]).expect("reading from zero-extended memory cannot fail; qed");
U256::from_big_endian(&buf[..])
};

let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low);

let (gas, overflow) = Self::mult_complexity(m).overflowing_mul(max(adjusted_exp_len, 1));
if overflow {
return U256::max_value();
}
(gas / self.divisor as u64).into()
(gas / divisor as u64).into()
}
}

impl ModexpPricer {
fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 {
let bit_index = if exp_low.is_zero() { 0 } else { (255 - exp_low.leading_zeros()) as u64 };
if len <= 32 {
bit_index
fn eip_2565_mul_complexity(base_length: U256, modulus_length: U256) -> U256 {
let max_length = std::cmp::max(base_length, modulus_length);
let words = { // div_ceil(max_length, 8);
let tmp = max_length / 8;
if (max_length % 8).is_zero() {
tmp
} else {
tmp + 1
}
};
words.saturating_mul(words)
}

fn eip_2565_iter_count(exponent_length: U256, exponent: U256) -> U256 {
let thirty_two = U256::from(32);
let it = if exponent_length <= thirty_two && exponent.is_zero() {
U256::zero()
} else if exponent_length <= thirty_two {
U256::from(exponent.bits()) - U256::from(1)
} else {
8 * (len - 32) + bit_index
}
// else > 32
U256::from(8).saturating_mul(exponent_length - thirty_two)
.saturating_add(U256::from(exponent.bits()).saturating_sub(U256::from(1)))
};
std::cmp::max(it, U256::one())
}

fn mult_complexity(x: u64) -> u64 {
match x {
x if x <= 64 => x * x,
x if x <= 1024 => (x * x) / 4 + 96 * x - 3072,
x => (x * x) / 16 + 480 * x - 199_680,
}
fn eip_2565_cost(divisor: U256, base_length: U256, modulus_length: U256, exponent_length: U256, exponent: U256) -> U256 {
let multiplication_complexity = Self::eip_2565_mul_complexity(base_length, modulus_length);
let iteration_count = Self::eip_2565_iter_count(exponent_length, exponent);
std::cmp::max(U256::from(200), multiplication_complexity.saturating_mul(iteration_count) / divisor)
}
}

Expand Down Expand Up @@ -406,7 +477,8 @@ impl From<ethjson::spec::builtin::Pricing> for Pricing {
10
} else {
exp.divisor
}
},
is_eip_2565: exp.is_eip_2565,
})
}
ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => {
Expand Down Expand Up @@ -1359,7 +1431,7 @@ mod tests {
#[test]
fn modexp() {
let f = Builtin {
pricer: btreemap![0 => Pricing::Modexp(ModexpPricer { divisor: 20 })],
pricer: btreemap![0 => Pricing::Modexp(ModexpPricer { divisor: 20, is_eip_2565: false })],
native: EthereumBuiltin::from_str("modexp").unwrap(),
};

Expand Down
6 changes: 4 additions & 2 deletions ethjson/src/spec/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub struct Linear {
pub struct Modexp {
/// Price divisor.
pub divisor: u64,
/// Use price scheme from EIP-2565
pub is_eip_2565: bool,
}

/// Pricing for constant alt_bn128 operations (ECADD and ECMUL)
Expand Down Expand Up @@ -273,15 +275,15 @@ mod tests {
let s = r#"{
"name": "late_start",
"activate_at": 100000,
"pricing": { "modexp": { "divisor": 5 } }
"pricing": { "modexp": { "divisor": 5, "is_eip_2565": false } }
}"#;

let builtin: Builtin = serde_json::from_str::<BuiltinCompat>(s).unwrap().into();
assert_eq!(builtin.name, "late_start");
assert_eq!(builtin.pricing, btreemap![
100_000 => PricingAt {
info: None,
price: Pricing::Modexp(Modexp { divisor: 5 })
price: Pricing::Modexp(Modexp { divisor: 5, is_eip_2565: false })
}
]);
}
Expand Down
4 changes: 3 additions & 1 deletion ethjson/src/spec/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ pub enum ForkSpec {
ConstantinopleFix,
/// Istanbul (#9,069,000, 2019-12-08)
Istanbul,
/// Berlin (To be announced)
/// Berlin (#12,244,000, 2021-04-15)
Berlin,
/// London (To be announced)
London,
/// Byzantium transition test-net
EIP158ToByzantiumAt5,
/// Homestead transition test-net
Expand Down
26 changes: 25 additions & 1 deletion ethjson/src/test_helpers/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ pub struct State {
pub struct MultiTransaction {
/// Transaction data set.
pub data: Vec<Bytes>,
/// Access lists (see EIP-2930)
#[serde(default)]
pub access_lists: Vec<AccessList>,
/// Gas limit set.
pub gas_limit: Vec<Uint>,
/// Gas price.
Expand All @@ -70,8 +73,14 @@ pub struct MultiTransaction {
impl MultiTransaction {
/// Build transaction with given indexes.
pub fn select(&self, indexes: &PostStateIndexes) -> Transaction {
let data_index = indexes.data as usize;
let access_list = if data_index < self.access_lists.len() {
self.access_lists[data_index].iter().map(|a| (a.address, a.storage_keys.clone())).collect()
} else {
Vec::new()
};
Transaction {
data: self.data[indexes.data as usize].clone(),
data: self.data[data_index].clone(),
gas_limit: self.gas_limit[indexes.gas as usize],
gas_price: self.gas_price,
nonce: self.nonce,
Expand All @@ -81,10 +90,25 @@ impl MultiTransaction {
s: Default::default(),
v: Default::default(),
secret: self.secret.clone(),
access_list,
}
}
}

/// Type alias for access lists (see EIP-2930)
pub type AccessList = Vec<AccessListTuple>;

/// Access list tuple (see https://eips.ethereum.org/EIPS/eip-2930).
/// Example test spec: https://github.com/ethereum/tests/blob/5490db3ff58d371c0c74826280256ba016b0bd5c/GeneralStateTests/stExample/accessListExample.json
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccessListTuple {
/// Address to access
pub address: Address,
/// Keys (slots) to access at that address
pub storage_keys: Vec<H256>,
}

/// State test indexes deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub struct PostStateIndexes {
Expand Down
3 changes: 3 additions & 0 deletions ethjson/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ use serde::Deserialize;
pub struct Transaction {
/// Transaction data.
pub data: Bytes,
/// Transaction access list (see EIP-2930).
#[serde(default)]
pub access_list: Vec<(Address, Vec<H256>)>,
/// Gas limit.
pub gas_limit: Uint,
/// Gas price.
Expand Down
2 changes: 1 addition & 1 deletion evm
Submodule evm updated from 9631bf to 2ebb88
1 change: 1 addition & 0 deletions jsontests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ rlp = "0.5"
sha3 = "0.8"
parity-bytes = "0.1"
env_logger = "0.8"
lazy_static = "1.4.0"
52 changes: 52 additions & 0 deletions jsontests/res/berlin_builtins.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"0000000000000000000000000000000000000001": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } },
"0000000000000000000000000000000000000002": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } },
"0000000000000000000000000000000000000003": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } },
"0000000000000000000000000000000000000004": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } },
"0000000000000000000000000000000000000005": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 3, "is_eip_2565": true } } },
"0000000000000000000000000000000000000006": {
"name": "alt_bn128_add",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 500 }}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 150 }}
}
}
},
"0000000000000000000000000000000000000007": {
"name": "alt_bn128_mul",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 40000 }}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 6000 }}
}
}
},
"0000000000000000000000000000000000000008": {
"name": "alt_bn128_pairing",
"pricing": {
"0": {
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
}
}
},
"0000000000000000000000000000000000000009": {
"name": "blake2_f",
"activate_at": "0x00",
"pricing": {
"blake2_f": {
"gas_per_round": 1
}
}
}
}
2 changes: 1 addition & 1 deletion jsontests/res/ethtests
2 changes: 1 addition & 1 deletion jsontests/res/istanbul_builtins.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"0000000000000000000000000000000000000002": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } },
"0000000000000000000000000000000000000003": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } },
"0000000000000000000000000000000000000004": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } },
"0000000000000000000000000000000000000005": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } },
"0000000000000000000000000000000000000005": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20, "is_eip_2565": false } } },
"0000000000000000000000000000000000000006": {
"name": "alt_bn128_add",
"pricing": {
Expand Down
Loading