Skip to content

[ETCM-912] Magneto gas changes for BALANCE and *CALL codes #1084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 34 additions & 18 deletions src/main/scala/io/iohk/ethereum/vm/OpCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,25 @@ object OpCode {
val end = (offset + size).min(bytes.size).toInt
bytes.slice(start, end).padToByteString(size.toInt, 0.toByte)
}

def addressAccessCost[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S], address: Address)(
preGasFn: FeeSchedule => BigInt,
postColdGasFn: FeeSchedule => BigInt,
postWarmGasFn: FeeSchedule => BigInt
): BigInt = {
val currentBlockNumber = state.env.blockHeader.number
// FIXME: handle ETH Berlin here as well
val etcFork = state.config.blockchainConfig.etcForkForBlockNumber(currentBlockNumber)
Copy link
Contributor

@dzajkowski dzajkowski Aug 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mind adding a note that ETH needs to be also handled?

val eip2929Enabled = isEip2929Enabled(etcFork)
if (eip2929Enabled) {
val addr = address
if (state.accessedAddresses.contains(addr))
postWarmGasFn(state.config.feeSchedule)
else
postColdGasFn(state.config.feeSchedule)
} else
preGasFn(state.config.feeSchedule)
}
}

/** Base class for all the opcodes of the EVM
Expand Down Expand Up @@ -213,22 +232,10 @@ trait AddrAccessGas { self: OpCode =>
private def coldGasFn: FeeSchedule => BigInt = _.G_cold_account_access
private def warmGasFn: FeeSchedule => BigInt = _.G_warm_storage_read

override protected def baseGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt = {
val currentBlockNumber = state.env.blockHeader.number
val etcFork = state.config.blockchainConfig.etcForkForBlockNumber(currentBlockNumber)
val eip2929Enabled = isEip2929Enabled(etcFork)
if (eip2929Enabled) {
val addr = address(state)
if (state.accessedAddresses.contains(addr))
warmGasFn(state.config.feeSchedule)
else
coldGasFn(state.config.feeSchedule)
} else
baseGasFn(state.config.feeSchedule)
}
override protected def baseGas[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): BigInt =
OpCode.addressAccessCost(state, address(state))(baseGasFn, coldGasFn, warmGasFn)

protected def address[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): Address

}

sealed trait ConstGas { self: OpCode =>
Expand Down Expand Up @@ -386,12 +393,18 @@ case object SHA3 extends OpCode(0x20, 2, 1, _.G_sha3) {

case object ADDRESS extends ConstOp(0x30)(_.env.ownerAddr.toUInt256)

case object BALANCE extends OpCode(0x31, 1, 1, _.G_balance) with ConstGas {
case object BALANCE extends OpCode(0x31, 1, 1, _.G_balance) with AddrAccessGas with ConstGas {
protected def exec[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): ProgramState[W, S] = {
val (accountAddress, stack1) = state.stack.pop
val accountBalance = state.world.getBalance(Address(accountAddress))
val addr = Address(accountAddress)
val accountBalance = state.world.getBalance(addr)
val stack2 = stack1.push(accountBalance)
state.withStack(stack2).step()
state.withStack(stack2).addAccessedAddress(addr).step()
}

protected def address[W <: WorldStateProxy[W, S], S <: Storage[S]](state: ProgramState[W, S]): Address = {
val (accountAddress, _) = state.stack.pop
Address(accountAddress)
}
}

Expand Down Expand Up @@ -1059,6 +1072,7 @@ abstract class CallOp(code: Int, delta: Int, alpha: Int) extends OpCode(code, de
.withInternalTxs(internalTx +: result.internalTxs)
.withLogs(result.logs)
.withReturnData(result.returnData)
.addAccessedAddress(toAddr)
.step()
}
}
Expand Down Expand Up @@ -1153,7 +1167,9 @@ abstract class CallOp(code: Int, delta: Int, alpha: Int) extends OpCode(code, de
else 0

val c_xfer: BigInt = if (endowment.isZero) 0 else state.config.feeSchedule.G_callvalue
state.config.feeSchedule.G_call + c_xfer + c_new

val callCost: BigInt = OpCode.addressAccessCost(state, to)(_.G_call, _.G_cold_account_access, _.G_warm_storage_read)
callCost + c_xfer + c_new
}
}

Expand Down
25 changes: 20 additions & 5 deletions src/test/scala/io/iohk/ethereum/vm/CallOpFixture.scala
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class CallOpFixture(val config: EvmConfig, val startState: MockWorldState) {

val fakeHeader: BlockHeader = BlockFixtures.ValidBlock.header.copy(number = 0, unixTimestamp = 0)

val context: PC = ProgramContext(
lazy val context: PC = ProgramContext(
callerAddr = callerAddr,
originAddr = callerAddr,
recipientAddr = Some(ownerAddr),
Expand All @@ -181,7 +181,7 @@ class CallOpFixture(val config: EvmConfig, val startState: MockWorldState) {
originalWorld = worldWithExtAccount
)

case class CallResult(
case class ExecuteCall(
op: CallOp,
context: PC = context,
inputData: ByteString = inputData,
Expand All @@ -191,8 +191,9 @@ class CallOpFixture(val config: EvmConfig, val startState: MockWorldState) {
inOffset: UInt256 = UInt256.Zero,
inSize: UInt256 = inputData.size,
outOffset: UInt256 = inputData.size,
outSize: UInt256 = inputData.size / 2
) {
outSize: UInt256 = inputData.size / 2,
toAlreadyAccessed: Boolean = false
) extends CallResult {

val vm = new TestVM

Expand All @@ -205,7 +206,8 @@ class CallOpFixture(val config: EvmConfig, val startState: MockWorldState) {
private val stack = Stack.empty().push(if (op == DELEGATECALL) paramsForDelegate else params)
private val mem = Memory.empty.store(UInt256.Zero, inputData)

val stateIn: PS = ProgramState(vm, context, env).withStack(stack).withMemory(mem)
val baseStateIn: PS = ProgramState(vm, context, env).withStack(stack).withMemory(mem)
val stateIn: PS = if (toAlreadyAccessed) baseStateIn.addAccessedAddress(to) else baseStateIn
val stateOut: PS = op.execute(stateIn)
val world: MockWorldState = stateOut.world

Expand All @@ -216,3 +218,16 @@ class CallOpFixture(val config: EvmConfig, val startState: MockWorldState) {
val extStorage: MockStorage = world.getStorage(to)
}
}

protected[vm] trait CallResult {
def inputData: ByteString
def stateOut: PS
def world: MockWorldState
def ownBalance: UInt256
def extBalance: UInt256
def ownStorage: MockStorage
def extStorage: MockStorage
def outOffset: UInt256
def outSize: UInt256
def value: UInt256
}
Loading