mirror of
https://github.com/arnaucube/evm-rs.git
synced 2026-02-02 17:06:40 +01:00
Update SSTORE(0x55) to EIP 3529&1283
This commit is contained in:
@@ -10,6 +10,7 @@ pub struct Stack {
|
||||
pub calldata_i: usize,
|
||||
pub calldata_size: usize,
|
||||
pub stack: Vec<[u8; 32]>,
|
||||
pub storage_committed: HashMap<[u8; 32], Vec<u8>>,
|
||||
pub storage: HashMap<[u8; 32], Vec<u8>>,
|
||||
pub mem: Vec<u8>,
|
||||
pub gas: u64,
|
||||
@@ -23,6 +24,7 @@ impl Stack {
|
||||
calldata_i: 0,
|
||||
calldata_size: 32,
|
||||
stack: Vec::new(),
|
||||
storage_committed: HashMap::new(),
|
||||
storage: HashMap::new(),
|
||||
mem: Vec::new(),
|
||||
gas: 10000000000,
|
||||
|
||||
@@ -11,6 +11,14 @@ const GSTORAGEKILL: usize = 5000;
|
||||
const GSTORAGEMOD: usize = 5000;
|
||||
const GSTORAGEADD: usize = 20000;
|
||||
|
||||
const NETSSTORENOOPGAS: u64 = 200;
|
||||
const NETSSTOREINITGAS: u64 = 20000;
|
||||
const NETSSTORECLEARREFUND: u64 = 15000;
|
||||
const NETSSTORERESETCLEARREFUND: u64 = 19800;
|
||||
const NETSSTORERESETREFUND: u64 = 4800;
|
||||
const NETSSTORECLEANGAS: u64 = 5000;
|
||||
const NETSSTOREDIRTYGAS: u64 = 200;
|
||||
|
||||
const GEXPONENTBYTE: usize = 10; // cost of EXP exponent per byte
|
||||
const EXP_SUPPLEMENTAL_GAS: usize = 40;
|
||||
const GCOPY: usize = 3; // cost to copy one 32 byte word
|
||||
@@ -40,7 +48,6 @@ pub struct Opcode {
|
||||
pub ins: u32,
|
||||
pub outs: u32,
|
||||
pub gas: u64,
|
||||
// operation: fn(),
|
||||
}
|
||||
|
||||
pub fn new_opcode(name: &str, ins: u32, outs: u32, gas: u64) -> Opcode {
|
||||
@@ -378,28 +385,66 @@ impl Stack {
|
||||
Ok(())
|
||||
}
|
||||
pub fn sstore(&mut self) -> Result<(), String> {
|
||||
// TODO WIP
|
||||
// https://eips.ethereum.org/EIPS/eip-3529
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1283.md
|
||||
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
// 2. If current value does not equal new value
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||
// 2.1.1. If original value is 0, 20000 gas is deducted.
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
let empty: Vec<u8> = Vec::new();
|
||||
let key = self.pop()?;
|
||||
let value = self.pop()?;
|
||||
if self.storage.contains_key(&key) {
|
||||
let old_value = self.storage.get(&key).unwrap();
|
||||
if &value.to_vec() == old_value {
|
||||
// if the new value is the same as the old one, do not set
|
||||
let original = match self.storage_committed.get(&key) {
|
||||
Some(v) => v.clone(),
|
||||
None => empty.clone(),
|
||||
};
|
||||
let current = match self.storage.get(&key) {
|
||||
Some(v) => v.clone(),
|
||||
None => {
|
||||
self.gas -= 2100;
|
||||
empty.clone()
|
||||
}
|
||||
};
|
||||
|
||||
if current == value {
|
||||
self.gas -= NETSSTORENOOPGAS;
|
||||
return Ok(());
|
||||
}
|
||||
if original == current {
|
||||
if original.is_empty() {
|
||||
self.gas -= NETSSTOREINITGAS;
|
||||
return Ok(());
|
||||
}
|
||||
// if value (from self.pop()) does not exist in the stack, is a STORAGEKILL TODO
|
||||
println!("mingas {:?}", GSTORAGEMOD);
|
||||
self.gas -= GSTORAGEMOD as u64;
|
||||
} else {
|
||||
// if value does not exist, substract gas for the addition
|
||||
println!("mingas {:?}", GSTORAGEADD);
|
||||
self.gas -= GSTORAGEADD as u64;
|
||||
if value.is_empty() {
|
||||
self.gas += NETSSTORECLEARREFUND;
|
||||
}
|
||||
self.gas -= NETSSTORECLEANGAS;
|
||||
return Ok(());
|
||||
}
|
||||
println!(
|
||||
"insert {:?} - {:?}",
|
||||
vec_u8_to_hex(key.to_vec()),
|
||||
vec_u8_to_hex(value.to_vec())
|
||||
);
|
||||
if !original.is_empty() {
|
||||
if current.is_empty() {
|
||||
self.gas -= NETSSTORECLEARREFUND;
|
||||
} else if value.is_empty() {
|
||||
self.gas += NETSSTORECLEARREFUND;
|
||||
}
|
||||
}
|
||||
if original == value {
|
||||
if original.is_empty() {
|
||||
self.gas += NETSSTORERESETCLEARREFUND;
|
||||
} else {
|
||||
self.gas += NETSSTORERESETREFUND;
|
||||
}
|
||||
}
|
||||
self.gas -= NETSSTOREDIRTYGAS;
|
||||
self.storage.insert(key, value.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -216,10 +216,10 @@ fn execute_opcodes_9() {
|
||||
s.execute(&code, &calldata, false).unwrap();
|
||||
|
||||
// assert_eq!(s.gas, 9999977788); // TODO WIP geth reported gas
|
||||
assert_eq!(s.gas, 9999974988);
|
||||
assert_eq!(s.gas, 9999955788);
|
||||
assert_eq!(s.pc, 10);
|
||||
assert_eq!(s.stack.len(), 0);
|
||||
assert_eq!(s.storage.len(), 1);
|
||||
assert_eq!(s.storage.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -230,14 +230,11 @@ fn execute_opcodes_10() {
|
||||
.unwrap();
|
||||
let calldata = hex::decode("a5f3c23b00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000004").unwrap();
|
||||
|
||||
println!("LEN {:?}", calldata.len());
|
||||
|
||||
let mut s = Stack::new();
|
||||
s.execute(&code, &calldata, true).unwrap();
|
||||
|
||||
// assert_eq!(s.gas, 9999977752); // WIP correct sstore gas computation
|
||||
assert_eq!(s.gas, 9999979852);
|
||||
assert_eq!(s.gas, 9999977752);
|
||||
assert_eq!(s.pc, 25);
|
||||
assert_eq!(s.stack.len(), 1);
|
||||
assert_eq!(s.storage.len(), 1);
|
||||
assert_eq!(s.storage.len(), 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user