Skip to content
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

VM, Common: Implement EIP 1153, Transient storage opcodes #1768

Merged
merged 16 commits into from
Mar 25, 2022
22 changes: 22 additions & 0 deletions packages/common/src/eips/1153.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "EIP-1153",
"number": 1153,
"comment": "Transient Storage",
"url": "https://eips.ethereum.org/EIPS/eip-1153",
"status": "Review",
"minimumHardfork": "chainstart",
"requiredEIPs": [],
"gasConfig": {},
"gasPrices": {
"tstore": {
"v": 100,
"d": "Base fee of the TSTORE opcode"
},
"tload": {
"v": 100,
"d": "Base fee of the TLOAD opcode"
}
},
"vm": {},
"pow": {}
}
1 change: 1 addition & 0 deletions packages/common/src/eips/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { eipsType } from './../types'

export const EIPs: eipsType = {
1153: require('./1153.json'),
1559: require('./1559.json'),
2315: require('./2315.json'),
2537: require('./2537.json'),
Expand Down
31 changes: 29 additions & 2 deletions packages/vm/src/evm/eei.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { VmError, ERROR } from '../exceptions'
import Message from './message'
import EVM, { EVMResult } from './evm'
import { Log } from './types'
import { TransientStorage } from '../state'

const debugGas = createDebugLogger('vm:eei:gas')

Expand Down Expand Up @@ -69,8 +70,16 @@ export default class EEI {
_lastReturned: Buffer
_common: Common
_gasLeft: BN

constructor(env: Env, state: StateManager, evm: EVM, common: Common, gasLeft: BN) {
_transientStorage: TransientStorage

constructor(
env: Env,
state: StateManager,
evm: EVM,
common: Common,
gasLeft: BN,
transientStorage: TransientStorage
) {
this._env = env
this._state = state
this._evm = evm
Expand All @@ -82,6 +91,7 @@ export default class EEI {
returnValue: undefined,
selfdestruct: {},
}
this._transientStorage = transientStorage
}

/**
Expand Down Expand Up @@ -380,6 +390,23 @@ export default class EEI {
}
}

/**
* Store 256-bit a value in memory to transient storage.
* @param key - Storage key
* @param value - Storage value
*/
transientStorageStore(key: Buffer, value: Buffer): void {
return this._transientStorage.put(this._env.address, key, value)
}

/**
* Loads a 256-bit value to memory from transient storage.
* @param key - Storage key
*/
transientStorageLoad(key: Buffer): Buffer {
return this._transientStorage.get(this._env.address, key)
}

/**
* Returns the current gasCounter.
*/
Expand Down
22 changes: 19 additions & 3 deletions packages/vm/src/evm/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { short } from './opcodes/util'
import * as eof from './opcodes/eof'
import { Log } from './types'
import { default as Interpreter, InterpreterOpts, RunState } from './interpreter'
import VM from '../index'
import { TransientStorage } from '../state'

const debug = createDebugLogger('vm:evm')
const debugGas = createDebugLogger('vm:evm:gas')
Expand Down Expand Up @@ -130,21 +132,23 @@ export function VmErrorResult(error: VmError, gasUsed: BN): ExecResult {
* @ignore
*/
export default class EVM {
_vm: any
_vm: VM
_state: StateManager
_tx: TxContext
_block: Block
/**
* Amount of gas to refund from deleting storage values
*/
_refund: BN
_transientStorage: TransientStorage

constructor(vm: any, txContext: TxContext, block: Block) {
constructor(vm: VM, txContext: TxContext, block: Block) {
this._vm = vm
this._state = this._vm.stateManager
this._tx = txContext
this._block = block
this._refund = new BN(0)
this._transientStorage = new TransientStorage()
}

/**
Expand All @@ -163,6 +167,8 @@ export default class EVM {
const oldRefund = this._refund.clone()

await this._state.checkpoint()
this._transientStorage.checkpoint()

if (this._vm.DEBUG) {
debug('-'.repeat(100))
debug(`message checkpoint`)
Expand Down Expand Up @@ -212,19 +218,22 @@ export default class EVM {
if (this._vm._common.gteHardfork('homestead') || err.error != ERROR.CODESTORE_OUT_OF_GAS) {
result.execResult.logs = []
await this._state.revert()
this._transientStorage.revert()
if (this._vm.DEBUG) {
debug(`message checkpoint reverted`)
}
} else {
// we are in chainstart and the error was the code deposit error
// we do like nothing happened.
await this._state.commit()
this._transientStorage.commit()
if (this._vm.DEBUG) {
debug(`message checkpoint committed`)
}
}
} else {
await this._state.commit()
this._transientStorage.commit()
if (this._vm.DEBUG) {
debug(`message checkpoint committed`)
}
Expand Down Expand Up @@ -528,7 +537,14 @@ export default class EVM {
contract: await this._state.getAccount(message.to || Address.zero()),
codeAddress: message.codeAddress,
}
const eei = new EEI(env, this._state, this, this._vm._common, message.gasLimit.clone())
const eei = new EEI(
env,
this._state,
this,
this._vm._common,
message.gasLimit.clone(),
this._transientStorage
)
if (message.selfdestruct) {
eei._result.selfdestruct = message.selfdestruct
}
Expand Down
7 changes: 7 additions & 0 deletions packages/vm/src/evm/opcodes/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,13 @@ const hardforkOpcodes: { hardforkName: string; opcodes: OpcodeEntry }[] = [
]

const eipOpcodes: { eip: number; opcodes: OpcodeEntry }[] = [
{
eip: 1153,
opcodes: {
0xb3: { name: 'TLOAD', isAsync: false, dynamicGas: false },
0xb4: { name: 'TSTORE', isAsync: false, dynamicGas: false },
},
},
{
eip: 2315,
opcodes: {
Expand Down
28 changes: 28 additions & 0 deletions packages/vm/src/evm/opcodes/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,35 @@ export const handlers: Map<number, OpHandler> = new Map([
runState.eei.log(mem, topicsCount, topicsBuf)
},
],
// 0xb3: TLOAD
[
0xb3,
function (runState) {
const key = runState.stack.pop()
const keyBuf = key.toArrayLike(Buffer, 'be', 32)
const value = runState.eei.transientStorageLoad(keyBuf)
const valueBN = value.length ? new BN(value) : new BN(0)
runState.stack.push(valueBN)
},
],
// 0xb4: TSTORE
[
0xb4,
function (runState) {
const [key, val] = runState.stack.popN(2)

const keyBuf = key.toArrayLike(Buffer, 'be', 32)
// NOTE: this should be the shortest representation
let value
if (val.isZero()) {
value = Buffer.from([])
} else {
value = val.toArrayLike(Buffer, 'be')
}

runState.eei.transientStorageStore(keyBuf, value)
},
],
// '0xf0' range - closures
// 0xf0: CREATE
[
Expand Down
9 changes: 5 additions & 4 deletions packages/vm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface VMOpts {
*
* ### Supported EIPs
*
* - [EIP-1153](https://eips.ethereum.org/EIPS/eip-1153) - Transient Storage Opcodes (`experimental`)
* - [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - EIP-1559 Fee Market
* - [EIP-2315](https://eips.ethereum.org/EIPS/eip-2315) - VM simple subroutines (`experimental`)
* - [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) - BLS12-381 precompiles (`experimental`)
Expand Down Expand Up @@ -176,7 +177,7 @@ export default class VM extends AsyncEventEmitter {

protected readonly _opts: VMOpts
protected _isInitialized: boolean = false
protected readonly _allowUnlimitedContractSize: boolean
public readonly _allowUnlimitedContractSize: boolean
ryanio marked this conversation as resolved.
Show resolved Hide resolved
// This opcode data is always set since `getActiveOpcodes()` is called in the constructor
protected _opcodes!: OpcodeList
protected _handlers!: Map<number, OpHandler>
Expand Down Expand Up @@ -207,7 +208,7 @@ export default class VM extends AsyncEventEmitter {
* performance reasons to avoid string literal evaluation
* @hidden
*/
protected readonly DEBUG: boolean = false
readonly DEBUG: boolean = false
ryanio marked this conversation as resolved.
Show resolved Hide resolved

/**
* VM async constructor. Creates engine instance and initializes it.
Expand Down Expand Up @@ -239,8 +240,8 @@ export default class VM extends AsyncEventEmitter {
if (opts.common) {
// Supported EIPs
const supportedEIPs = [
1559, 2315, 2537, 2565, 2718, 2929, 2930, 3198, 3529, 3540, 3541, 3607, 3670, 3855, 3860,
4399,
1153, 1559, 2315, 2537, 2565, 2718, 2929, 2930, 3198, 3529, 3540, 3541, 3607, 3670, 3855,
3860, 4399,
]
for (const eip of opts.common.eips()) {
if (!supportedEIPs.includes(eip)) {
Expand Down
1 change: 1 addition & 0 deletions packages/vm/src/state/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { StateManager, EIP2929StateManager } from './interface'
export { BaseStateManager } from './baseStateManager'
export { default as DefaultStateManager, Proof } from './stateManager'
export { default as TransientStorage } from './transientStorage'
Loading