Skip to content

Commit

Permalink
vm: first batch of dynamic gas up to 0x3f
Browse files Browse the repository at this point in the history
  • Loading branch information
jochem-brouwer committed Jul 18, 2021
1 parent 76ae457 commit cb9169e
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 87 deletions.
6 changes: 3 additions & 3 deletions packages/vm/src/evm/opcodes/EIP2200.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function updateSstoreGasEIP2200(runState: RunState, found: any, value: Bu
if (current.equals(value)) {
const sstoreNoopCost = runState._common.param('gasPrices', 'sstoreNoopGasEIP2200')
return runState.eei.useGas(
new BN(adjustSstoreGasEIP2929(runState, key, sstoreNoopCost, 'noop')),
adjustSstoreGasEIP2929(runState, key, sstoreNoopCost, 'noop'),
'EIP-2200 -> sstoreNoopGasEIP2200'
)
}
Expand Down Expand Up @@ -69,14 +69,14 @@ export function updateSstoreGasEIP2200(runState: RunState, found: any, value: Bu
// Reset to original non-existent slot
const sstoreInitRefund = runState._common.param('gasPrices', 'sstoreInitRefundEIP2200')
runState.eei.refundGas(
new BN(adjustSstoreGasEIP2929(runState, key, sstoreInitRefund, 'initRefund')),
adjustSstoreGasEIP2929(runState, key, sstoreInitRefund, 'initRefund'),
'EIP-2200 -> initRefund'
)
} else {
// Reset to original existing slot
const sstoreCleanRefund = runState._common.param('gasPrices', 'sstoreCleanRefundEIP2200')
runState.eei.refundGas(
new BN(adjustSstoreGasEIP2929(runState, key, sstoreCleanRefund, 'cleanRefund')),
adjustSstoreGasEIP2929(runState, key, sstoreCleanRefund, 'cleanRefund'),
'EIP-2200 -> cleanRefund'
)
}
Expand Down
46 changes: 19 additions & 27 deletions packages/vm/src/evm/opcodes/EIP2929.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export function accessAddressEIP2929(
address: Address,
chargeGas = true,
isSelfdestruct = false
) {
if (!runState._common.isActivatedEIP(2929)) return
): BN {
if (!runState._common.isActivatedEIP(2929)) return new BN(0)

const addressStr = address.buf

Expand All @@ -27,18 +27,13 @@ export function accessAddressEIP2929(
// CREATE, CREATE2 opcodes have the address warmed for free.
// selfdestruct beneficiary address reads are charged an *additional* cold access
if (chargeGas) {
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'coldaccountaccess')),
'EIP-2929 -> coldaccountaccess'
)
return new BN(runState._common.param('gasPrices', 'coldaccountaccess'))
}
// Warm: (selfdestruct beneficiary address reads are not charged when warm)
} else if (chargeGas && !isSelfdestruct) {
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'warmstorageread')),
'EIP-2929 -> warmstorageread'
)
return new BN(runState._common.param('gasPrices', 'warmstorageread'))
}
return new BN(0)
}

/**
Expand All @@ -48,8 +43,8 @@ export function accessAddressEIP2929(
* @param {RunState} runState
* @param {Buffer} key (to storage slot)
*/
export function accessStorageEIP2929(runState: RunState, key: Buffer, isSstore: boolean) {
if (!runState._common.isActivatedEIP(2929)) return
export function accessStorageEIP2929(runState: RunState, key: Buffer, isSstore: boolean): BN {
if (!runState._common.isActivatedEIP(2929)) return new BN(0)

const address = runState.eei.getAddress().buf

Expand All @@ -59,16 +54,11 @@ export function accessStorageEIP2929(runState: RunState, key: Buffer, isSstore:
if (slotIsCold) {
// eslint-disable-next-line prettier/prettier
(<EIP2929StateManager>runState.stateManager).addWarmedStorage(address, key)
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'coldsload')),
'EIP-2929 -> coldsload'
)
return new BN(runState._common.param('gasPrices', 'coldsload'))
} else if (!isSstore) {
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'warmstorageread')),
'EIP-2929 -> warmstorageread'
)
return new BN(runState._common.param('gasPrices', 'warmstorageread'))
}
return new BN(0)
}

/**
Expand All @@ -85,8 +75,8 @@ export function adjustSstoreGasEIP2929(
key: Buffer,
defaultCost: number,
costName: string
): number {
if (!runState._common.isActivatedEIP(2929)) return defaultCost
): BN {
if (!runState._common.isActivatedEIP(2929)) return new BN(defaultCost)

const address = runState.eei.getAddress().buf
const warmRead = runState._common.param('gasPrices', 'warmstorageread')
Expand All @@ -95,15 +85,17 @@ export function adjustSstoreGasEIP2929(
if ((<EIP2929StateManager>runState.stateManager).isWarmedStorage(address, key)) {
switch (costName) {
case 'reset':
return defaultCost - coldSload
return new BN(defaultCost).isubn(coldSload)
case 'noop':
return warmRead
return new BN(warmRead)
case 'initRefund':
return runState._common.param('gasPrices', 'sstoreInitGasEIP2200') - warmRead
return new BN(runState._common.param('gasPrices', 'sstoreInitGasEIP2200')).isubn(warmRead)
case 'cleanRefund':
return runState._common.param('gasPrices', 'sstoreReset') - coldSload - warmRead
return new BN(runState._common.param('gasPrices', 'sstoreReset'))
.isubn(coldSload)
.isubn(warmRead)
}
}

return defaultCost
return new BN(defaultCost)
}
42 changes: 0 additions & 42 deletions packages/vm/src/evm/opcodes/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,16 +386,10 @@ export const handlers: Map<number, OpHandler> = new Map([
0x20,
function (runState: RunState) {
const [offset, length] = runState.stack.popN(2)
subMemUsage(runState, offset, length)
let data = Buffer.alloc(0)
if (!length.isZero()) {
data = runState.memory.read(offset.toNumber(), length.toNumber())
}
// copy fee
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'sha3Word')).imul(divCeil(length, new BN(32))),
'SHA3 opcode'
)
const r = new BN(keccak256(data))
runState.stack.push(r)
},
Expand All @@ -415,7 +409,6 @@ export const handlers: Map<number, OpHandler> = new Map([
async function (runState: RunState) {
const addressBN = runState.stack.pop()
const address = new Address(addressToBuffer(addressBN))
accessAddressEIP2929(runState, address)
const balance = await runState.eei.getExternalBalance(address)
runState.stack.push(balance)
},
Expand Down Expand Up @@ -473,14 +466,7 @@ export const handlers: Map<number, OpHandler> = new Map([
function (runState: RunState) {
const [memOffset, dataOffset, dataLength] = runState.stack.popN(3)

subMemUsage(runState, memOffset, dataLength)

if (!dataLength.eqn(0)) {
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(dataLength, new BN(32))),
'CALLDATACOPY opcode'
)

const data = getDataSlice(runState.eei.getCallData(), dataOffset, dataLength)
const memOffsetNum = memOffset.toNumber()
const dataLengthNum = dataLength.toNumber()
Expand All @@ -502,14 +488,7 @@ export const handlers: Map<number, OpHandler> = new Map([
function (runState: RunState) {
const [memOffset, codeOffset, dataLength] = runState.stack.popN(3)

subMemUsage(runState, memOffset, dataLength)

if (!dataLength.eqn(0)) {
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(dataLength, new BN(32))),
'CODECOPY opcode'
)

const data = getDataSlice(runState.eei.getCode(), codeOffset, dataLength)
const memOffsetNum = memOffset.toNumber()
const lengthNum = dataLength.toNumber()
Expand All @@ -523,8 +502,6 @@ export const handlers: Map<number, OpHandler> = new Map([
0x3b,
async function (runState: RunState) {
const addressBN = runState.stack.pop()
const address = new Address(addressToBuffer(addressBN))
accessAddressEIP2929(runState, address)
const size = await runState.eei.getExternalCodeSize(addressBN)
runState.stack.push(size)
},
Expand All @@ -535,18 +512,7 @@ export const handlers: Map<number, OpHandler> = new Map([
async function (runState: RunState) {
const [addressBN, memOffset, codeOffset, dataLength] = runState.stack.popN(4)

// FIXME: for some reason this must come before subGas
subMemUsage(runState, memOffset, dataLength)
const address = new Address(addressToBuffer(addressBN))
accessAddressEIP2929(runState, address)

if (!dataLength.eqn(0)) {
// copy fee
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(dataLength, new BN(32))),
'EXTCODECOPY opcode'
)

const code = await runState.eei.getExternalCode(addressBN)

const data = getDataSlice(code, codeOffset, dataLength)
Expand All @@ -563,7 +529,6 @@ export const handlers: Map<number, OpHandler> = new Map([
async function (runState: RunState) {
const addressBN = runState.stack.pop()
const address = new Address(addressToBuffer(addressBN))
accessAddressEIP2929(runState, address)
const empty = await runState.eei.isAccountEmpty(address)
if (empty) {
runState.stack.push(new BN(0))
Expand Down Expand Up @@ -596,14 +561,7 @@ export const handlers: Map<number, OpHandler> = new Map([
trap(ERROR.OUT_OF_GAS)
}

subMemUsage(runState, memOffset, dataLength)

if (!dataLength.eqn(0)) {
runState.eei.useGas(
new BN(runState._common.param('gasPrices', 'copy')).mul(divCeil(dataLength, new BN(32))),
'RETURNDATACOPY opcode'
)

const data = getDataSlice(runState.eei.getReturnData(), returnDataOffset, dataLength)
const memOffsetNum = memOffset.toNumber()
const lengthNum = dataLength.toNumber()
Expand Down
74 changes: 65 additions & 9 deletions packages/vm/src/evm/opcodes/gas.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,118 @@
import { BN } from '../../../../util/dist'
import { addressToBuffer, divCeil, subMemUsage } from '.'
import { Address, BN } from '../../../../util/dist'
import { RunState } from '../interpreter'
import { accessAddressEIP2929 } from './EIP2929'
import { DynamicGasHandler } from './functions'

/**
* This file returns the dynamic parts of opcodes which have dynamic gas
* These are not pure functions: some edit the size of the memory
* These functions are therefore not read-only
*/

export const dynamicGasHandlers: Map<number, DynamicGasHandler> = new Map([
[
/* SHA3 */
0x20,
function (runState: RunState): BN {
return new BN(0)
const [offset, length] = runState.stack.peek(2)
const gas = subMemUsage(runState, offset, length)
gas.iadd(
new BN(runState._common.param('gasPrices', 'sha3Word')).imul(divCeil(length, new BN(32)))
)
return gas
},
],
[
/* BALANCE */
0x31,
function (runState: RunState): BN {
return new BN(0)
const addressBN = runState.stack.peek()[0]
const address = new Address(addressToBuffer(addressBN))
return accessAddressEIP2929(runState, address)
},
],
[
/* CALLDATACOPY */
0x37,
function (runState: RunState): BN {
return new BN(0)
const [memOffset /*dataOffset*/, , dataLength] = runState.stack.popN(3)

const gas = subMemUsage(runState, memOffset, dataLength)
if (!dataLength.eqn(0)) {
gas.iadd(
new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(dataLength, new BN(32)))
)
}
return gas
},
],
[
/* CODECOPY */
0x39,
function (runState: RunState): BN {
return new BN(0)
const [memOffset /*codeOffset*/, , dataLength] = runState.stack.popN(3)

const gas = subMemUsage(runState, memOffset, dataLength)
if (!dataLength.eqn(0)) {
gas.iadd(
new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(dataLength, new BN(32)))
)
}
return gas
},
],
[
/* EXTCODESIZE */
0x3b,
function (runState: RunState): BN {
return new BN(0)
const addressBN = runState.stack.peek()[0]
const address = new Address(addressToBuffer(addressBN))
return accessAddressEIP2929(runState, address)
},
],
[
/* EXTCODECOPY */
0x3c,
function (runState: RunState): BN {
return new BN(0)
const [addressBN, memOffset /*codeOffset*/, , dataLength] = runState.stack.popN(4)

const gas = subMemUsage(runState, memOffset, dataLength)
const address = new Address(addressToBuffer(addressBN))
gas.iadd(accessAddressEIP2929(runState, address))

if (!dataLength.eqn(0)) {
gas.iadd(
new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(dataLength, new BN(32)))
)
}

return gas
},
],
[
/* RETURNDATACOPY */
0x3e,
function (runState: RunState): BN {
return new BN(0)
const [memOffset /*returnDataOffset*/, , dataLength] = runState.stack.popN(3)

const gas = subMemUsage(runState, memOffset, dataLength)

if (!dataLength.eqn(0)) {
gas.iadd(
new BN(runState._common.param('gasPrices', 'copy')).mul(divCeil(dataLength, new BN(32)))
)
}
return gas
},
],
[
/* EXTCODEHASH */
0x3f,
function (runState: RunState): BN {
return new BN(0)
const addressBN = runState.stack.pop()
const address = new Address(addressToBuffer(addressBN))
return accessAddressEIP2929(runState, address)
},
],
[
Expand Down
Loading

0 comments on commit cb9169e

Please sign in to comment.