From 4e9f454669a489687bef17bf4b7e4ba089bde150 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sat, 25 Sep 2021 19:18:14 +0200 Subject: [PATCH] Update SSTORE(0x55) to EIP 3529&1283 --- src/lib.rs | 2 ++ src/opcodes.rs | 81 +++++++++++++++++++++++++++++++++++++----------- tests/execute.rs | 11 +++---- 3 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7d6a79f..9647852 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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>, pub storage: HashMap<[u8; 32], Vec>, pub mem: Vec, 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, diff --git a/src/opcodes.rs b/src/opcodes.rs index a2764e1..dc712d8 100644 --- a/src/opcodes.rs +++ b/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 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 = 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(()); + } + 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; + } } - println!( - "insert {:?} - {:?}", - vec_u8_to_hex(key.to_vec()), - vec_u8_to_hex(value.to_vec()) - ); + self.gas -= NETSSTOREDIRTYGAS; self.storage.insert(key, value.to_vec()); Ok(()) } diff --git a/tests/execute.rs b/tests/execute.rs index 668ade4..cc60dd7 100644 --- a/tests/execute.rs +++ b/tests/execute.rs @@ -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); }