arnaucube
arnaucube/evm-rsPublic Self-hosted repository
Update SSTORE(0x55) to EIP 3529&1283
arnaucube root@arnaucube.com
Sat, 25 Sep 2021 19:18:14 +0200
commit

4e9f454669a489687bef17bf4b7e4ba089bde150

parent

dac8a29e7e9dab93c8b071d95c0721863e52d89a

3 files changed, 69 insertions(+), 25 deletions(-)

jump to
M src/lib.rs → src/lib.rs

@@ -10,6 +10,7 @@ pub pc: usize,

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 @@ pc: 0,

calldata_i: 0, calldata_size: 32, stack: Vec::new(), + storage_committed: HashMap::new(), storage: HashMap::new(), mem: Vec::new(), gas: 10000000000,
M src/opcodes.rs → src/opcodes.rs

@@ -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 name: String,

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 @@ self.mem[pos as usize..].copy_from_slice(&val);

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(()) }
M tests/execute.rs → tests/execute.rs

@@ -216,10 +216,10 @@ let mut s = Stack::new();

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]

@@ -229,15 +229,12 @@ "606060405260e060020a6000350463a5f3c23b8114601a575b005b60243560043501600055601856",

) .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); }