Skip to content

Commit 8c9ec4c

Browse files
[ETCM-912] Magneto gas changes in EXT* codes family (#1074)
* [ETCM-912] Add Magneto block height to EVM config * [ETCM-912] Add accessedAddresses and accessedStorageKeys to ProgramState * [ETCM-912] Add newly created address to accessedaddresses * [ETCM-912] Add Magneto fee schedule * [ETCM-912] Implement EXTCODESIZE changes * [ETCM-921] Implement EXTCODECOPY * fixup! [ETCM-912] Add newly created address to accessedaddresses * fixup! [ETCM-912] Implement EXTCODESIZE changes * fixup! [ETCM-921] Implement EXTCODECOPY * [ETCM-921] Format * [ETCM-912] Fix style * fixup! [ETCM-912] Fix style * [ETCM-912] Implement gas calculation changes in EXTCODEHASH * [ETCM-912] Remove leftovers * [ETCM-912] Document ProgramState * [ETCM-912] Move isEip2929Enabled flag to BlockchainConfigForEvm * [ETCM-912] Rename constGas -> baseGas
1 parent f86aea9 commit 8c9ec4c

14 files changed

+267
-45
lines changed

src/main/scala/io/iohk/ethereum/extvm/VMServer.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ class VMServer(messageHandler: MessageHandler) extends Logger {
207207
aghartaBlockNumber = BigInt(9573000), //TODO include agharta block number in protobuf
208208
petersburgBlockNumber = BigInt(10000000), //TODO include petersburg block number in protobuf
209209
phoenixBlockNumber = BigInt(10500839), //TODO include phoenix block number in protobuf
210+
magnetoBlockNumber = BigInt(13189133), //TODO include magneto block number in protobuf
210211
chainId = 0x3d.toByte //TODO include chainId in protobuf
211212
)
212213
}

src/main/scala/io/iohk/ethereum/vm/BlockchainConfigForEvm.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks.Agharta
66
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks.Atlantis
77
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks.BeforeAtlantis
88
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks.EtcFork
9+
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks.Magneto
910
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks.Phoenix
1011
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EthForks.BeforeByzantium
1112
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EthForks.Byzantium
@@ -35,13 +36,15 @@ case class BlockchainConfigForEvm(
3536
aghartaBlockNumber: BigInt,
3637
petersburgBlockNumber: BigInt,
3738
phoenixBlockNumber: BigInt,
39+
magnetoBlockNumber: BigInt,
3840
chainId: Byte
3941
) {
4042
def etcForkForBlockNumber(blockNumber: BigInt): EtcFork = blockNumber match {
4143
case _ if blockNumber < atlantisBlockNumber => BeforeAtlantis
4244
case _ if blockNumber < aghartaBlockNumber => Atlantis
4345
case _ if blockNumber < phoenixBlockNumber => Agharta
44-
case _ if blockNumber >= phoenixBlockNumber => Phoenix
46+
case _ if blockNumber < magnetoBlockNumber => Phoenix
47+
case _ if blockNumber >= magnetoBlockNumber => Magneto
4548
}
4649

4750
def ethForkForBlockNumber(blockNumber: BigInt): BlockchainConfigForEvm.EthForks.Value = blockNumber match {
@@ -65,6 +68,8 @@ object BlockchainConfigForEvm {
6568
val BeforeByzantium, Byzantium, Constantinople, Petersburg, Istanbul, Berlin = Value
6669
}
6770

71+
def isEip2929Enabled(etcFork: EtcFork): Boolean = etcFork >= EtcForks.Magneto
72+
6873
def apply(blockchainConfig: BlockchainConfig): BlockchainConfigForEvm = {
6974
import blockchainConfig._
7075
BlockchainConfigForEvm(
@@ -82,6 +87,7 @@ object BlockchainConfigForEvm {
8287
aghartaBlockNumber = forkBlockNumbers.aghartaBlockNumber,
8388
petersburgBlockNumber = forkBlockNumbers.petersburgBlockNumber,
8489
phoenixBlockNumber = forkBlockNumbers.phoenixBlockNumber,
90+
magnetoBlockNumber = forkBlockNumbers.magnetoBlockNumber,
8591
chainId = chainId
8692
)
8793
}

src/main/scala/io/iohk/ethereum/vm/EvmConfig.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ object EvmConfig {
4242
(blockchainConfig.aghartaBlockNumber, 7, AghartaConfigBuilder),
4343
(blockchainConfig.petersburgBlockNumber, 8, PetersburgConfigBuilder),
4444
(blockchainConfig.istanbulBlockNumber, 9, IstanbulConfigBuilder),
45-
(blockchainConfig.phoenixBlockNumber, 9, PhoenixConfigBuilder)
45+
(blockchainConfig.phoenixBlockNumber, 9, PhoenixConfigBuilder),
46+
(blockchainConfig.magnetoBlockNumber, 10, MagnetoConfigBuilder)
4647
)
4748

4849
// highest transition block that is less/equal to `blockNumber`
@@ -61,6 +62,7 @@ object EvmConfig {
6162
val ConstantinopleOpCodes: OpCodeList = OpCodeList(OpCodes.ConstantinopleOpCodes)
6263
val AghartaOpCodes = ConstantinopleOpCodes
6364
val PhoenixOpCodes: OpCodeList = OpCodeList(OpCodes.PhoenixOpCodes)
65+
val MagnetoOpCodes: OpCodeList = PhoenixOpCodes
6466

6567
val FrontierConfigBuilder: EvmConfigBuilder = config =>
6668
EvmConfig(
@@ -132,6 +134,12 @@ object EvmConfig {
132134
opCodeList = PhoenixOpCodes
133135
)
134136

137+
val MagnetoConfigBuilder: EvmConfigBuilder = config =>
138+
PhoenixConfigBuilder(config).copy(
139+
feeSchedule = new ethereum.vm.FeeSchedule.MagnetoFeeSchedule,
140+
opCodeList = MagnetoOpCodes
141+
)
142+
135143
case class OpCodeList(opCodes: List[OpCode]) {
136144
val byteToOpCode: Map[Byte, OpCode] =
137145
opCodes.map(op => op.code -> op).toMap
@@ -251,6 +259,9 @@ object FeeSchedule {
251259
override val G_copy = 3
252260
override val G_blockhash = 20
253261
override val G_extcode = 20
262+
override val G_cold_sload = 2100
263+
override val G_cold_account_access = 2600
264+
override val G_warm_storage_read = 100
254265
}
255266

256267
class HomesteadFeeSchedule extends FrontierFeeSchedule {
@@ -283,6 +294,10 @@ object FeeSchedule {
283294
override val G_txdatanonzero = 16
284295
}
285296

297+
class MagnetoFeeSchedule extends PhoenixFeeSchedule {
298+
override val G_sload: BigInt = G_warm_storage_read
299+
override val G_sreset: BigInt = 5000 - G_cold_sload
300+
}
286301
}
287302

288303
trait FeeSchedule {
@@ -321,4 +336,7 @@ trait FeeSchedule {
321336
val G_copy: BigInt
322337
val G_blockhash: BigInt
323338
val G_extcode: BigInt
339+
val G_cold_sload: BigInt
340+
val G_cold_account_access: BigInt
341+
val G_warm_storage_read: BigInt
324342
}

src/main/scala/io/iohk/ethereum/vm/OpCode.scala

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks
1313
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EtcForks.EtcFork
1414
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EthForks
1515
import io.iohk.ethereum.vm.BlockchainConfigForEvm.EthForks.EthFork
16+
import io.iohk.ethereum.vm.BlockchainConfigForEvm._
1617

1718
// scalastyle:off magic.number
1819
// scalastyle:off number.of.types
@@ -171,7 +172,7 @@ object OpCode {
171172
* @param delta number of words to be popped from stack
172173
* @param alpha number of words to be pushed to stack
173174
*/
174-
abstract class OpCode(val code: Byte, val delta: Int, val alpha: Int, val constGasFn: FeeSchedule => BigInt)
175+
abstract class OpCode(val code: Byte, val delta: Int, val alpha: Int, val baseGasFn: FeeSchedule => BigInt)
175176
extends Product
176177
with Serializable {
177178
def this(code: Int, pop: Int, push: Int, constGasFn: FeeSchedule => BigInt) = this(code.toByte, pop, push, constGasFn)
@@ -184,21 +185,50 @@ abstract class OpCode(val code: Byte, val delta: Int, val alpha: Int, val constG
184185
else if (state.stack.size - delta + alpha > state.stack.maxSize)
185186
state.withError(StackOverflow)
186187
else {
187-
val constGas: BigInt = constGasFn(state.config.feeSchedule)
188-
189-
val gas: BigInt = constGas + varGas(state)
188+
val gas: BigInt = calcGas(state)
190189
if (gas > state.gas)
191190
state.copy(gas = 0).withError(OutOfGas)
192191
else
193192
exec(state).spendGas(gas)
194193
}
195194

195+
protected def calcGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt =
196+
baseGas(state) + varGas(state)
197+
198+
protected def baseGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt = baseGasFn(
199+
state.config.feeSchedule
200+
)
201+
196202
protected def varGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt
197203

198204
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S]
199205

200206
protected def availableInContext[W <: WorldStateProxy[W, S], S <: Storage[S]]: ProgramState[W, S] => Boolean = _ =>
201207
true
208+
209+
}
210+
211+
trait AddrAccessGas { self: OpCode =>
212+
213+
private def coldGasFn: FeeSchedule => BigInt = _.G_cold_account_access
214+
private def warmGasFn: FeeSchedule => BigInt = _.G_warm_storage_read
215+
216+
override protected def baseGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt = {
217+
val currentBlockNumber = state.env.blockHeader.number
218+
val etcFork = state.config.blockchainConfig.etcForkForBlockNumber(currentBlockNumber)
219+
val eip2929Enabled = isEip2929Enabled(etcFork)
220+
if (eip2929Enabled) {
221+
val addr = address(state)
222+
if (state.accessedAddresses.contains(addr))
223+
warmGasFn(state.config.feeSchedule)
224+
else
225+
coldGasFn(state.config.feeSchedule)
226+
} else
227+
baseGasFn(state.config.feeSchedule)
228+
}
229+
230+
protected def address[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): Address
231+
202232
}
203233

204234
sealed trait ConstGas { self: OpCode =>
@@ -210,8 +240,8 @@ case object STOP extends OpCode(0x00, 0, 0, _.G_zero) with ConstGas {
210240
state.withReturnData(ByteString.empty).halt
211241
}
212242

213-
sealed abstract class UnaryOp(code: Int, constGasFn: FeeSchedule => BigInt)(val f: UInt256 => UInt256)
214-
extends OpCode(code, 1, 1, constGasFn)
243+
sealed abstract class UnaryOp(code: Int, baseGasFn: FeeSchedule => BigInt)(val f: UInt256 => UInt256)
244+
extends OpCode(code, 1, 1, baseGasFn)
215245
with ConstGas {
216246

217247
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S] = {
@@ -222,8 +252,8 @@ sealed abstract class UnaryOp(code: Int, constGasFn: FeeSchedule => BigInt)(val
222252
}
223253
}
224254

225-
sealed abstract class BinaryOp(code: Int, constGasFn: FeeSchedule => BigInt)(val f: (UInt256, UInt256) => UInt256)
226-
extends OpCode(code.toByte, 2, 1, constGasFn) {
255+
sealed abstract class BinaryOp(code: Int, baseGasFn: FeeSchedule => BigInt)(val f: (UInt256, UInt256) => UInt256)
256+
extends OpCode(code.toByte, 2, 1, baseGasFn) {
227257

228258
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S] = {
229259
val (Seq(a, b), stack1) = state.stack.pop(2)
@@ -233,9 +263,9 @@ sealed abstract class BinaryOp(code: Int, constGasFn: FeeSchedule => BigInt)(val
233263
}
234264
}
235265

236-
sealed abstract class TernaryOp(code: Int, constGasFn: FeeSchedule => BigInt)(
266+
sealed abstract class TernaryOp(code: Int, baseGasFn: FeeSchedule => BigInt)(
237267
val f: (UInt256, UInt256, UInt256) => UInt256
238-
) extends OpCode(code.toByte, 3, 1, constGasFn) {
268+
) extends OpCode(code.toByte, 3, 1, baseGasFn) {
239269

240270
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S] = {
241271
val (Seq(a, b, c), stack1) = state.stack.pop(3)
@@ -365,7 +395,8 @@ case object BALANCE extends OpCode(0x31, 1, 1, _.G_balance) with ConstGas {
365395
}
366396
}
367397

368-
case object EXTCODEHASH extends OpCode(0x3f, 1, 1, _.G_balance) with ConstGas {
398+
case object EXTCODEHASH extends OpCode(0x3f, 1, 1, _.G_balance) with AddrAccessGas with ConstGas {
399+
369400
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S] = {
370401
val (accountAddress, stack1) = state.stack.pop
371402
val address = Address(accountAddress)
@@ -395,7 +426,12 @@ case object EXTCODEHASH extends OpCode(0x3f, 1, 1, _.G_balance) with ConstGas {
395426
}
396427

397428
val stack2 = stack1.push(codeHash)
398-
state.withStack(stack2).step()
429+
state.withStack(stack2).addAccessedAddress(address).step()
430+
}
431+
432+
protected def address[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): Address = {
433+
val (accountAddress, _) = state.stack.pop
434+
Address(accountAddress)
399435
}
400436
}
401437

@@ -452,29 +488,42 @@ case object CODECOPY extends OpCode(0x39, 3, 0, _.G_verylow) {
452488

453489
case object GASPRICE extends ConstOp(0x3a)(_.env.gasPrice)
454490

455-
case object EXTCODESIZE extends OpCode(0x3b, 1, 1, _.G_extcode) with ConstGas {
491+
case object EXTCODESIZE extends OpCode(0x3b, 1, 1, _.G_extcode) with AddrAccessGas with ConstGas {
456492
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S] = {
457-
val (addr, stack1) = state.stack.pop
458-
val codeSize = state.world.getCode(Address(addr)).size
493+
val (addrUint, stack1) = state.stack.pop
494+
val addr = Address(addrUint)
495+
val codeSize = state.world.getCode(addr).size
459496
val stack2 = stack1.push(UInt256(codeSize))
460-
state.withStack(stack2).step()
497+
state.withStack(stack2).addAccessedAddress(addr).step()
498+
}
499+
500+
protected def address[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): Address = {
501+
val (accountAddress, _) = state.stack.pop
502+
Address(accountAddress)
461503
}
462504
}
463505

464-
case object EXTCODECOPY extends OpCode(0x3c, 4, 0, _.G_extcode) {
506+
case object EXTCODECOPY extends OpCode(0x3c, 4, 0, _.G_extcode) with AddrAccessGas {
507+
465508
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S] = {
466509
val (Seq(address, memOffset, codeOffset, size), stack1) = state.stack.pop(4)
467-
val codeCopy = OpCode.sliceBytes(state.world.getCode(Address(address)), codeOffset, size)
510+
val addr = Address(address)
511+
val codeCopy = OpCode.sliceBytes(state.world.getCode(addr), codeOffset, size)
468512
val mem1 = state.memory.store(memOffset, codeCopy)
469-
state.withStack(stack1).withMemory(mem1).step()
513+
state.withStack(stack1).withMemory(mem1).addAccessedAddress(addr).step()
470514
}
471515

472-
protected def varGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt = {
516+
override protected def varGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt = {
473517
val (Seq(_, memOffset, _, size), _) = state.stack.pop(4)
474518
val memCost = state.config.calcMemCost(state.memory.size, memOffset, size)
475519
val copyCost = state.config.feeSchedule.G_copy * wordsForBytes(size)
476520
memCost + copyCost
477521
}
522+
523+
protected def address[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): Address = {
524+
val (Seq(accountAddress, _, _, _), _) = state.stack.pop(4)
525+
Address(accountAddress)
526+
}
478527
}
479528

480529
case object RETURNDATASIZE extends ConstOp(0x3d)(_.returnData.size)
@@ -845,7 +894,7 @@ abstract class CreateOp(code: Int, delta: Int) extends OpCode(code, delta, 1, _.
845894

846895
//FIXME: to avoid calculating this twice, we could adjust state.gas prior to execution in OpCode#execute
847896
//not sure how this would affect other opcodes [EC-243]
848-
val availableGas = state.gas - (constGasFn(state.config.feeSchedule) + varGas(state))
897+
val availableGas = state.gas - (baseGasFn(state.config.feeSchedule) + varGas(state))
849898
val startGas = state.config.gasCap(availableGas)
850899
val (initCode, memory1) = state.memory.load(inOffset, inSize)
851900
val world1 = state.world.increaseNonce(state.ownAddress)
@@ -885,6 +934,7 @@ abstract class CreateOp(code: Int, delta: Int) extends OpCode(code, delta, 1, _.
885934
.withWorld(world2)
886935
.withStack(resultStack)
887936
.withReturnData(returnData)
937+
.addAccessedAddress(newAddress)
888938
.step()
889939

890940
case None =>
@@ -902,6 +952,7 @@ abstract class CreateOp(code: Int, delta: Int) extends OpCode(code, delta, 1, _.
902952
.withMemory(memory1)
903953
.withInternalTxs(internalTx +: result.internalTxs)
904954
.withReturnData(ByteString.empty)
955+
.addAccessedAddress(newAddress)
905956
.step()
906957
}
907958
}

src/main/scala/io/iohk/ethereum/vm/PrecompiledContracts.scala

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,22 @@ object PrecompiledContracts {
6363

6464
private def getContract(context: ProgramContext[_, _]): Option[PrecompiledContract] =
6565
context.recipientAddr.flatMap { addr =>
66-
val ethFork = context.evmConfig.blockchainConfig.ethForkForBlockNumber(context.blockHeader.number)
67-
val etcFork = context.evmConfig.blockchainConfig.etcForkForBlockNumber(context.blockHeader.number)
68-
69-
if (ethFork >= EthForks.Istanbul || etcFork >= EtcForks.Phoenix) {
70-
istanbulPhoenixContracts.get(addr)
71-
} else if (ethFork >= EthForks.Byzantium || etcFork >= EtcForks.Atlantis) {
72-
// byzantium and atlantis hard fork introduce the same set of precompiled contracts
73-
byzantiumAtlantisContracts.get(addr)
74-
} else
75-
contracts.get(addr)
66+
getContracts(context).get(addr)
7667
}
7768

69+
def getContracts(context: ProgramContext[_, _]): Map[Address, PrecompiledContract] = {
70+
val ethFork = context.evmConfig.blockchainConfig.ethForkForBlockNumber(context.blockHeader.number)
71+
val etcFork = context.evmConfig.blockchainConfig.etcForkForBlockNumber(context.blockHeader.number)
72+
73+
if (ethFork >= EthForks.Istanbul || etcFork >= EtcForks.Phoenix) {
74+
istanbulPhoenixContracts
75+
} else if (ethFork >= EthForks.Byzantium || etcFork >= EtcForks.Atlantis) {
76+
// byzantium and atlantis hard fork introduce the same set of precompiled contracts
77+
byzantiumAtlantisContracts
78+
} else
79+
contracts
80+
}
81+
7882
sealed trait PrecompiledContract {
7983
protected def exec(inputData: ByteString): Option[ByteString]
8084
protected def gas(inputData: ByteString, etcFork: EtcFork, ethFork: EthFork): BigInt

src/main/scala/io/iohk/ethereum/vm/ProgramState.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ object ProgramState {
1919
world = context.world,
2020
staticCtx = context.staticCtx,
2121
addressesToDelete = context.initialAddressesToDelete,
22-
originalWorld = context.originalWorld
22+
originalWorld = context.originalWorld,
23+
accessedAddresses = PrecompiledContracts.getContracts(context).keySet ++ Set(
24+
context.originAddr,
25+
context.recipientAddr.getOrElse(context.callerAddr)
26+
),
27+
accessedStorageKeys = Set.empty
2328
)
2429
}
2530

@@ -40,6 +45,8 @@ object ProgramState {
4045
* @param staticCtx a flag to indicate static context (EIP-214)
4146
* @param error indicates whether the program terminated abnormally
4247
* @param originalWorld state of the world at the beginning og the current transaction, read-only,
48+
* @param accessedAddresses set of addresses which have already been accessed in this transaction (EIP-2929)
49+
* @param accessedStorageKeys set of storage slots which have already been accessed in this transaction (EIP-2929)
4350
* needed for https://eips.ethereum.org/EIPS/eip-1283
4451
*/
4552
case class ProgramState[W <: WorldStateProxy[W, S], S <: Storage[S]](
@@ -58,7 +65,9 @@ case class ProgramState[W <: WorldStateProxy[W, S], S <: Storage[S]](
5865
halted: Boolean = false,
5966
staticCtx: Boolean = false,
6067
error: Option[ProgramError] = None,
61-
originalWorld: W
68+
originalWorld: W,
69+
accessedAddresses: Set[Address],
70+
accessedStorageKeys: Set[(Address, BigInt)]
6271
) {
6372

6473
def config: EvmConfig = env.evmConfig
@@ -126,6 +135,12 @@ case class ProgramState[W <: WorldStateProxy[W, S], S <: Storage[S]](
126135
def revert(data: ByteString): ProgramState[W, S] =
127136
copy(error = Some(RevertOccurs), returnData = data, halted = true)
128137

138+
def addAccessedAddress(addr: Address): ProgramState[W, S] =
139+
copy(accessedAddresses = accessedAddresses + addr)
140+
141+
def addAccessedStorageKey(addr: Address, storageKey: BigInt): ProgramState[W, S] =
142+
copy(accessedStorageKeys = accessedStorageKeys + ((addr, storageKey)))
143+
129144
def toResult: ProgramResult[W, S] =
130145
ProgramResult[W, S](
131146
returnData,

0 commit comments

Comments
 (0)