From dcf8a61218bc9a8ef4e3f46a39ea437e7b8004c6 Mon Sep 17 00:00:00 2001 From: Caoyuan Deng Date: Sun, 31 Mar 2019 14:23:22 -0700 Subject: [PATCH] EIP-1283 implementation. #14 --- .../main/scala/khipu/ledger/TrieStorage.scala | 8 +++ .../src/main/scala/khipu/vm/EvmConfig.scala | 11 +++- .../src/main/scala/khipu/vm/OpCode.scala | 62 +++++++++++++++++-- .../src/main/scala/khipu/vm/Storage.scala | 1 + 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/khipu-eth/src/main/scala/khipu/ledger/TrieStorage.scala b/khipu-eth/src/main/scala/khipu/ledger/TrieStorage.scala index c70ca34..c56b944 100644 --- a/khipu-eth/src/main/scala/khipu/ledger/TrieStorage.scala +++ b/khipu-eth/src/main/scala/khipu/ledger/TrieStorage.scala @@ -25,11 +25,19 @@ final class TrieStorage private ( def underlying = underlyingTrie + private var originalValues = Map[UInt256, UInt256]() + + def getOriginalValue(address: UInt256): Option[UInt256] = + originalValues.get(address) orElse underlyingTrie.get(address) + def load(address: UInt256): UInt256 = { logs.get(address) match { case None => underlyingTrie.get(address) match { case Some(value) => + if (!originalValues.contains(address)) { + originalValues += (address -> value) + } logs += (address -> Original(value)) value case None => UInt256.Zero diff --git a/khipu-eth/src/main/scala/khipu/vm/EvmConfig.scala b/khipu-eth/src/main/scala/khipu/vm/EvmConfig.scala index 23b40ab..6fa42f6 100644 --- a/khipu-eth/src/main/scala/khipu/vm/EvmConfig.scala +++ b/khipu-eth/src/main/scala/khipu/vm/EvmConfig.scala @@ -49,7 +49,8 @@ object EvmConfig { eip198 = false, eip658 = false, eip145 = false, - eip1052 = false + eip1052 = false, + eip1283 = false ) val HomesteadConfig = EvmConfig( @@ -67,7 +68,8 @@ object EvmConfig { eip198 = false, eip658 = false, eip145 = false, - eip1052 = false + eip1052 = false, + eip1283 = false ) val PostEIP150Config = HomesteadConfig.copy( @@ -138,7 +140,8 @@ final case class EvmConfig( eip198: Boolean, eip658: Boolean, eip145: Boolean, - eip1052: Boolean + eip1052: Boolean, + eip1283: Boolean ) { import EvmConfig._ @@ -240,6 +243,7 @@ object FeeSchedule { override val G_jumpdest = 1 override val G_sset = 20000 override val G_sreset = 5000 + override val G_sreuse = 200 override val R_sclear = 15000 override val R_selfdestruct = 24000 override val G_selfdestruct = 0 @@ -300,6 +304,7 @@ trait FeeSchedule { def G_jumpdest: Long def G_sset: Long def G_sreset: Long + def G_sreuse: Long def R_sclear: Long def R_selfdestruct: Long def G_selfdestruct: Long diff --git a/khipu-eth/src/main/scala/khipu/vm/OpCode.scala b/khipu-eth/src/main/scala/khipu/vm/OpCode.scala index 38cff5f..7f92051 100644 --- a/khipu-eth/src/main/scala/khipu/vm/OpCode.scala +++ b/khipu-eth/src/main/scala/khipu/vm/OpCode.scala @@ -771,7 +771,40 @@ case object SSTORE extends OpCode[(UInt256, UInt256)](0x55, 2, 0) { } else { val (key, value) = params val oldValue = state.storage.load(key) - val refund = if (value.isZero && oldValue.nonZero) state.config.feeSchedule.R_sclear else 0 + var refund = 0L + if (state.config.eip1283) { + val origValue = state.storage.getOriginalValue(key).getOrElse(UInt256.Zero) + if (oldValue == origValue) { + if (origValue.isZero) { + // no refund + } else { + if (value.isZero) { + refund += state.config.feeSchedule.R_sclear + } + } + } else { // oldValue != origValue + if (origValue.nonZero) { + if (value.isZero) { + refund -= state.config.feeSchedule.R_sclear + } else { + refund += state.config.feeSchedule.R_sclear + } + } + + if (origValue == value) { + if (origValue.isZero) { + refund += (state.config.feeSchedule.G_sset - state.config.feeSchedule.R_sclear) + } else { + refund += (state.config.feeSchedule.G_sreset - state.config.feeSchedule.R_sclear) + } + } + } + } else { + if (oldValue.nonZero && value.isZero) { + refund += state.config.feeSchedule.R_sclear + } + } + val updatedStorage = state.storage.store(key, value) val world = state.world.saveStorage(state.ownAddress, updatedStorage) @@ -783,9 +816,30 @@ case object SSTORE extends OpCode[(UInt256, UInt256)](0x55, 2, 0) { } protected def varGas[W <: WorldState[W, S], S <: Storage[S]](state: ProgramState[W, S], params: (UInt256, UInt256)): Long = { - val (offset, value) = params - val oldValue = state.storage.load(offset) - if (oldValue.isZero && !value.isZero) state.config.feeSchedule.G_sset else state.config.feeSchedule.G_sreset + val (key, value) = params + val oldValue = state.storage.load(key) + if (state.config.eip1283) { + if (value == oldValue) { + state.config.feeSchedule.G_sreuse + } else { + val origValue = state.storage.getOriginalValue(key).getOrElse(UInt256.Zero) + if (oldValue == origValue) { + if (origValue.isZero) { + state.config.feeSchedule.G_sset + } else { + state.config.feeSchedule.G_sreset + } + } else { + state.config.feeSchedule.G_sreuse + } + } + } else { + if (oldValue.isZero && value.nonZero) { + state.config.feeSchedule.G_sset + } else { + state.config.feeSchedule.G_sreset + } + } } } diff --git a/khipu-eth/src/main/scala/khipu/vm/Storage.scala b/khipu-eth/src/main/scala/khipu/vm/Storage.scala index 3cd6e2d..a19bb35 100644 --- a/khipu-eth/src/main/scala/khipu/vm/Storage.scala +++ b/khipu-eth/src/main/scala/khipu/vm/Storage.scala @@ -8,4 +8,5 @@ import khipu.UInt256 trait Storage[S <: Storage[S]] { def store(offset: UInt256, value: UInt256): S def load(offset: UInt256): UInt256 + def getOriginalValue(address: UInt256): Option[UInt256] }