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

Remove storageReader and move logic to stateManager #534

Merged
merged 1 commit into from
May 29, 2019
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
8 changes: 2 additions & 6 deletions lib/evm/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from 'ethereumjs-util'
import Account from 'ethereumjs-account'
import { VmError, ERROR } from '../exceptions'
import { StorageReader } from '../state'
import PStateManager from '../state/promisified'
import { getPrecompile, PrecompileFunc, PrecompileResult } from './precompiles'
import { OOGResult } from './precompiles/types'
Expand Down Expand Up @@ -86,14 +85,12 @@ export interface ExecResult {
export default class Interpreter {
_vm: any
_state: PStateManager
_storageReader: StorageReader
_tx: TxContext
_block: any

constructor(vm: any, txContext: TxContext, block: any, storageReader: StorageReader) {
constructor(vm: any, txContext: TxContext, block: any) {
this._vm = vm
this._state = new PStateManager(this._vm.stateManager)
this._storageReader = storageReader || new StorageReader(this._state._wrapped)
this._tx = txContext
this._block = block
}
Expand Down Expand Up @@ -270,8 +267,7 @@ export default class Interpreter {
}

const loop = new Loop(this._vm, eei)
const opts = Object.assign({ storageReader: this._storageReader }, loopOpts)
const loopRes = await loop.run(message.code as Buffer, opts)
const loopRes = await loop.run(message.code as Buffer, loopOpts)

let result = eei._result
let gasUsed = message.gasLimit.sub(eei._gasLeft)
Expand Down
6 changes: 1 addition & 5 deletions lib/evm/loop.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import BN = require('bn.js')
import Common from 'ethereumjs-common'
import { StateManager, StorageReader } from '../state'
import { StateManager } from '../state'
import PStateManager from '../state/promisified'
import { ERROR, VmError } from '../exceptions'
import Memory from './memory'
Expand All @@ -13,7 +13,6 @@ export type IsException = 0 | 1

export interface RunOpts {
pc?: number
storageReader?: StorageReader
}

export interface RunState {
Expand All @@ -27,7 +26,6 @@ export interface RunState {
validJumps: number[]
_common: Common
stateManager: StateManager
storageReader: StorageReader
eei: EEI
}

Expand Down Expand Up @@ -59,7 +57,6 @@ export default class Loop {
// TODO: Replace with EEI methods
_common: this._vm._common,
stateManager: this._state._wrapped,
storageReader: new StorageReader(this._state._wrapped),
eei: this._eei,
}
}
Expand All @@ -69,7 +66,6 @@ export default class Loop {
code: code,
programCounter: opts.pc || this._runState.programCounter,
validJumps: this._getValidJumpDests(code),
storageReader: opts.storageReader || this._runState.storageReader,
})

// Check that the programCounter is in range
Expand Down
22 changes: 16 additions & 6 deletions lib/evm/opFns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -892,15 +892,25 @@ function maxCallGas(gasLimit: BN, gasLeft: BN): BN {

function getContractStorage(runState: RunState, address: Buffer, key: Buffer) {
return new Promise((resolve, reject) => {
const cb = (err: Error, res: any) => {
const cb = (err: Error | null, res: any) => {
if (err) return reject(err)
resolve(res)
}
if (runState._common.hardfork() === 'constantinople') {
runState.storageReader.getContractStorage(address, key, cb)
} else {
runState.stateManager.getContractStorage(address, key, cb)
}
runState.stateManager.getContractStorage(address, key, (err: Error, current: Buffer) => {
if (err) return cb(err, null)
if (runState._common.hardfork() === 'constantinople') {
runState.stateManager.getOriginalContractStorage(
address,
key,
(err: Error, original: Buffer) => {
if (err) return cb(err, null)
cb(null, { current, original })
},
)
} else {
cb(null, current)
}
})
})
}

Expand Down
5 changes: 1 addition & 4 deletions lib/runCall.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import BN = require('bn.js')
import { zeros } from 'ethereumjs-util'
import VM from './index'
import { StorageReader } from './state'
import TxContext from './evm/txContext'
import Message from './evm/message'
import { default as Interpreter, InterpreterResult } from './evm/interpreter'
Expand All @@ -12,7 +11,6 @@ const Block = require('ethereumjs-block')
*/
export interface RunCallOpts {
block?: any
storageReader?: StorageReader
gasPrice?: Buffer
origin?: Buffer
caller?: Buffer
Expand Down Expand Up @@ -48,7 +46,6 @@ export interface RunCallCb {
*/
export default function runCall(this: VM, opts: RunCallOpts, cb: RunCallCb): void {
const block = opts.block || new Block()
const storageReader = opts.storageReader || new StorageReader(this.stateManager)

const txContext = new TxContext(
opts.gasPrice || Buffer.alloc(0),
Expand All @@ -69,7 +66,7 @@ export default function runCall(this: VM, opts: RunCallOpts, cb: RunCallCb): voi
delegatecall: opts.delegatecall || false,
})

const interpreter = new Interpreter(this, txContext, block, storageReader)
const interpreter = new Interpreter(this, txContext, block)
interpreter
.executeMessage(message)
.then(results => cb(null, results))
Expand Down
8 changes: 1 addition & 7 deletions lib/runCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import VM from './index'
import TxContext from './evm/txContext'
import Message from './evm/message'
import { default as Interpreter, ExecResult } from './evm/interpreter'
import { StorageReader } from './state'
const Block = require('ethereumjs-block')

/**
Expand All @@ -27,7 +26,6 @@ export interface RunCodeOpts {
* The [`Block`](https://github.com/ethereumjs/ethereumjs-block) the `tx` belongs to. If omitted a blank block will be used
*/
block?: any
storageReader?: StorageReader
interpreter?: Interpreter
txContext?: TxContext
gasPrice?: Buffer
Expand Down Expand Up @@ -88,10 +86,6 @@ export default function runCode(this: VM, opts: RunCodeOpts, cb: RunCodeCb): voi
opts.block = new Block()
}

if (!opts.storageReader) {
opts.storageReader = new StorageReader(this.stateManager)
}

// Backwards compatibility
if (!opts.txContext) {
opts.txContext = new TxContext(
Expand All @@ -115,7 +109,7 @@ export default function runCode(this: VM, opts: RunCodeOpts, cb: RunCodeCb): voi

let interpreter = opts.interpreter
if (!interpreter) {
interpreter = new Interpreter(this, opts.txContext, opts.block, opts.storageReader)
interpreter = new Interpreter(this, opts.txContext, opts.block)
}

interpreter
Expand Down
5 changes: 2 additions & 3 deletions lib/runTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Bloom from './bloom'
import { default as Interpreter, InterpreterResult } from './evm/interpreter'
import Message from './evm/message'
import TxContext from './evm/txContext'
import { StorageReader } from './state'
import PStateManager from './state/promisified'
const Block = require('ethereumjs-block')

Expand Down Expand Up @@ -157,8 +156,8 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise<RunTxResult> {
value: tx.value,
data: tx.data,
})
const storageReader = new StorageReader(this.stateManager)
const interpreter = new Interpreter(this, txContext, block, storageReader)
state._wrapped._clearOriginalStorageCache()
const interpreter = new Interpreter(this, txContext, block)
const results = (await interpreter.executeMessage(message)) as RunTxResult

/*
Expand Down
1 change: 0 additions & 1 deletion lib/state/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { default as StateManager } from './stateManager'
export { default as StorageReader } from './storageReader'
4 changes: 4 additions & 0 deletions lib/state/promisified.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export default class PStateManager {
return promisify(this._wrapped.getContractStorage.bind(this._wrapped))(addr, key)
}

getOriginalContractStorage(addr: Buffer, key: Buffer): Promise<any> {
return promisify(this._wrapped.getOriginalContractStorage.bind(this._wrapped))(addr, key)
}

putContractStorage(addr: Buffer, key: Buffer, value: Buffer): Promise<void> {
return promisify(this._wrapped.putContractStorage.bind(this._wrapped))(addr, key, value)
}
Expand Down
43 changes: 43 additions & 0 deletions lib/state/stateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default class StateManager {
_touched: Set<string>
_touchedStack: Set<string>[]
_checkpointCount: number
_originalStorageCache: Map<string, Map<string, Buffer>>

/**
* Instantiate the StateManager interface.
Expand All @@ -48,6 +49,7 @@ export default class StateManager {
this._touched = new Set()
this._touchedStack = []
this._checkpointCount = 0
this._originalStorageCache = new Map()
}

/**
Expand Down Expand Up @@ -221,6 +223,38 @@ export default class StateManager {
})
}

/**
* Caches the storage value associated with the provided `address` and `key`
* on first invocation, and returns the cached (original) value from then
* onwards. This is used to get the original value of a storage slot for
* computing gas costs according to EIP-1283.
* @param address - Address of the account to get the storage for
* @param key - Key in the account's storage to get the value for
*/
getOriginalContractStorage(address: Buffer, key: Buffer, cb: any): void {
const addressHex = address.toString('hex')
const keyHex = key.toString('hex')

let map: Map<string, Buffer>
if (!this._originalStorageCache.has(addressHex)) {
map = new Map()
this._originalStorageCache.set(addressHex, map)
} else {
map = this._originalStorageCache.get(addressHex) as Map<string, Buffer>
}

if (map.has(keyHex)) {
cb(null, map.get(keyHex))
} else {
this.getContractStorage(address, key, (err: Error, current: Buffer) => {
if (err) return cb(err)

map.set(keyHex, current)
cb(null, current)
})
}
}

/**
* Modifies the storage trie of an account
* @private
Expand Down Expand Up @@ -582,4 +616,13 @@ export default class StateManager {
},
)
}

/**
* Clears the original storage cache. Refer to [[getOriginalContractStorage]]
* for more explanation.
* @ignore
*/
_clearOriginalStorageCache(): void {
this._originalStorageCache = new Map()
}
}
45 changes: 0 additions & 45 deletions lib/state/storageReader.ts

This file was deleted.

Loading