Skip to content

Commit

Permalink
Merge pull request #1112 from ethereumjs/common-add-event-emitter
Browse files Browse the repository at this point in the history
Common: Add EventEmitter and hardforkChanged event
  • Loading branch information
holgerd77 authored Feb 17, 2021
2 parents 3725f14 + 5b291a0 commit e1ffc7f
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 25 deletions.
2 changes: 0 additions & 2 deletions packages/client/lib/sync/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export class VMExecution extends Execution {
const blockNumber = headBlock.header.number.toNumber()
this.config.execCommon.setHardforkByBlockNumber(blockNumber)
this.hardfork = this.config.execCommon.hardfork()
this.vm._updateOpcodes()
this.config.logger.info(`Initializing VM execution hardfork=${this.hardfork}`)
}

Expand Down Expand Up @@ -113,7 +112,6 @@ export class VMExecution extends Execution {
`Execution hardfork switch on block number=${blockNumber} hash=${hash} old=${this.hardfork} new=${hardfork}`
)
this.hardfork = this.config.execCommon.setHardforkByBlockNumber(blockNumber)
this.vm._updateOpcodes()
}
// Block validation is redundant here and leads to consistency problems
// on PoA clique along blockchain-including validation checks
Expand Down
9 changes: 9 additions & 0 deletions packages/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ to ease `blockNumber` based access to parameters.

- [API Docs](./docs/README.md)

# EVENTS

The `Common` class is implemented as an `EventEmitter` and is emitting the following events
on which you can react within your code:

| Event | Description |
| - | - |
| `hardforkChanged` | Emitted when a hardfork change occurs in the Common object |

# SETUP

## Chains
Expand Down
15 changes: 10 additions & 5 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EventEmitter } from 'events'
import { buf as crc32Buffer } from 'crc-32'
import { _getInitializedChains } from './chains'
import { hardforks as HARDFORK_CHANGES } from './hardforks'
Expand Down Expand Up @@ -56,7 +57,7 @@ interface hardforkOptions {
/**
* Common class to access chain and hardfork parameters
*/
export default class Common {
export default class Common extends EventEmitter {
readonly DEFAULT_HARDFORK: string

private _chainParams: Chain
Expand Down Expand Up @@ -115,6 +116,7 @@ export default class Common {
* @constructor
*/
constructor(opts: CommonOpts) {
super()
this._customChains = opts.customChains ?? []
this._chainParams = this.setChain(opts.chain)
this.DEFAULT_HARDFORK = this._chainParams.defaultHardfork ?? 'istanbul'
Expand Down Expand Up @@ -166,14 +168,17 @@ export default class Common {
if (!this._isSupportedHardfork(hardfork)) {
throw new Error(`Hardfork ${hardfork} not set as supported in supportedHardforks`)
}
let changed = false
let existing = false
for (const hfChanges of HARDFORK_CHANGES) {
if (hfChanges[0] === hardfork) {
this._hardfork = hardfork
changed = true
if (this._hardfork !== hardfork) {
this._hardfork = hardfork
this.emit('hardforkChanged', hardfork)
}
existing = true
}
}
if (!changed) {
if (!existing) {
throw new Error(`Hardfork with name ${hardfork} not supported`)
}
}
Expand Down
9 changes: 9 additions & 0 deletions packages/common/tests/hardforks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ tape('[Common]: Hardfork logic', function (t: tape.Test) {
st.end()
})

t.test('setHardfork(): hardforkChanged event', function (st) {
const c = new Common({ chain: 'mainnet', hardfork: 'istanbul' })
c.on('hardforkChanged', (hardfork: string) => {
st.equal(hardfork, 'byzantium', 'should send correct hardforkChanged event')
st.end()
})
c.setHardfork('byzantium')
})

t.test('hardforkBlock()', function (st: tape.Test) {
let c = new Common({ chain: 'ropsten' })
let msg = 'should return the correct HF change block for byzantium (provided)'
Expand Down
11 changes: 11 additions & 0 deletions packages/vm/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ export default class VM extends AsyncEventEmitter {
supportedHardforks,
})
}
this._common.on('hardforkChanged', () => {
this._updateOpcodes()
})

// Set list of opcodes based on HF
// TODO: make this EIP-friendly
Expand Down Expand Up @@ -330,6 +333,14 @@ export default class VM extends AsyncEventEmitter {
return runCode.bind(this)(opts)
}

/**
* Returns a list with the currently activated opcodes
* available for VM execution
*/
getActiveOpcodes(): OpcodeList {
return getOpcodesForHF(this._common)
}

/**
* Returns a copy of the [[VM]] instance.
*/
Expand Down
4 changes: 0 additions & 4 deletions packages/vm/lib/runBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,7 @@ export default async function runBlock(this: VM, opts: RunBlockOpts): Promise<Ru
await this._emit('beforeBlock', block)

if (this._selectHardforkByBlockNumber) {
const currentHf = this._common.hardfork()
this._common.setHardforkByBlockNumber(block.header.number.toNumber())
if (this._common.hardfork() != currentHf) {
this._updateOpcodes()
}
}
debug('-'.repeat(100))
debug(
Expand Down
61 changes: 47 additions & 14 deletions packages/vm/tests/api/opcodes.spec.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
import tape from 'tape'
import Common from '@ethereumjs/common'
import { getOpcodesForHF } from '../../lib/evm/opcodes'
import VM from '../../lib'

const CHAINID = 0x46
tape('VM -> getActiveOpcodes()', (t) => {
const CHAINID = 0x46 //istanbul opcode

tape('getOpcodesForHF', (t) => {
t.test('shouldnt apply istanbul opcode changes for petersburg', (st) => {
const c = new Common({ chain: 'mainnet', hardfork: 'petersburg' })
const opcodes = getOpcodesForHF(c)
st.assert(opcodes.get(CHAINID) === undefined)
t.test('should not expose opcodes from a follow-up HF (istanbul -> petersburg)', (st) => {
const common = new Common({ chain: 'mainnet', hardfork: 'petersburg' })
const vm = new VM({ common })
st.equal(
vm.getActiveOpcodes().get(CHAINID),
undefined,
'istanbul opcode not exposed (HF: < istanbul (petersburg)'
)
st.end()
})

t.test('should correctly apply istanbul opcode when hf >= istanbul', (st) => {
let c = new Common({ chain: 'mainnet', hardfork: 'istanbul' })
let opcodes = getOpcodesForHF(c)
st.equal(opcodes.get(CHAINID)!.name, 'CHAINID')
t.test('should expose opcodes when HF is active (>= istanbul)', (st) => {
let common = new Common({ chain: 'mainnet', hardfork: 'istanbul' })
let vm = new VM({ common })
st.equal(
vm.getActiveOpcodes().get(CHAINID)!.name,
'CHAINID',
'istanbul opcode exposed (HF: istanbul)'
)

c = new Common({ chain: 'mainnet', hardfork: 'muirGlacier' })
opcodes = getOpcodesForHF(c)
st.equal(opcodes.get(CHAINID)!.name, 'CHAINID')
common = new Common({ chain: 'mainnet', hardfork: 'muirGlacier' })
vm = new VM({ common })
st.equal(
vm.getActiveOpcodes().get(CHAINID)!.name,
'CHAINID',
'istanbul opcode exposed (HF: > istanbul (muirGlacier)'
)

st.end()
})

t.test('should update opcodes on a hardfork change', async (st) => {
const common = new Common({ chain: 'mainnet', hardfork: 'istanbul' })
const vm = new VM({ common })

common.setHardfork('byzantium')
st.equal(
vm.getActiveOpcodes().get(CHAINID),
undefined,
'opcode not exposed after HF change (-> < istanbul)'
)

common.setHardfork('istanbul')
st.equal(
vm.getActiveOpcodes().get(CHAINID)!.name,
'CHAINID',
'opcode exposed after HF change (-> istanbul)'
)

st.end()
})
Expand Down

0 comments on commit e1ffc7f

Please sign in to comment.