Skip to content

Add frontier support #828

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 9 commits into from
Aug 21, 2020
4 changes: 2 additions & 2 deletions .github/workflows/vm-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
fork: ['Petersburg', 'Istanbul', 'MuirGlacier', 'Homestead', 'SpuriousDragon', 'TangerineWhistle', 'Byzantium']
fork: ['MuirGlacier', 'Petersburg', 'Istanbul', 'Byzantium', 'SpuriousDragon', 'TangerineWhistle', 'Homestead', 'Chainstart']
fail-fast: false
steps:
- uses: actions/setup-node@v1
Expand Down Expand Up @@ -85,7 +85,7 @@ jobs:
# This is the most fair division among 1 directory vs. everything else
args: ['--excludeDir=stTimeConsuming', '--dir=GeneralStateTests/stTimeConsuming']
# Run specific fork tests
fork: ['Homestead', 'Istanbul']
fork: ['MuirGlacier', 'Istanbul', 'Homestead', 'Chainstart']
fail-fast: false
steps:
- uses: actions/setup-node@v1
Expand Down
35 changes: 30 additions & 5 deletions packages/vm/lib/evm/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ export function OOGResult(gasLimit: BN): ExecResult {
exceptionError: new VmError(ERROR.OUT_OF_GAS),
}
}
// CodeDeposit OOG Result
export function COOGResult(gasUsedCreateCode: BN): ExecResult {
return {
returnValue: Buffer.alloc(0),
gasUsed: gasUsedCreateCode,
exceptionError: new VmError(ERROR.CODESTORE_OUT_OF_GAS),
}
}

/**
* EVM is responsible for executing an EVM message fully
Expand Down Expand Up @@ -130,8 +138,14 @@ export default class EVM {

const err = result.execResult.exceptionError
if (err) {
result.execResult.logs = []
await this._state.revert()
if (this._vm._common.gteHardfork('homestead') || err.error != ERROR.CODESTORE_OUT_OF_GAS) {
result.execResult.logs = []
await this._state.revert()
} else {
// we are in chainstart and the error was the code deposit error
// we do like nothing happened.
await this._state.commit()
}
} else {
await this._state.commit()
}
Expand Down Expand Up @@ -251,9 +265,10 @@ export default class EVM {

// fee for size of the return value
let totalGas = result.gasUsed
let returnFee = new BN(0)
if (!result.exceptionError) {
const returnFee = new BN(
result.returnValue.length * this._vm._common.param('gasPrices', 'createData'),
returnFee = new BN(result.returnValue.length).imuln(
this._vm._common.param('gasPrices', 'createData'),
)
totalGas = totalGas.add(returnFee)
}
Expand All @@ -273,7 +288,17 @@ export default class EVM {
) {
result.gasUsed = totalGas
} else {
result = { ...result, ...OOGResult(message.gasLimit) }
if (this._vm._common.gteHardfork('homestead')) {
result = { ...result, ...OOGResult(message.gasLimit) }
} else {
// we are in Frontier
if (totalGas.sub(returnFee).lte(message.gasLimit)) {
// we cannot pay the code deposit fee (but the deposit code actually did run)
result = { ...result, ...COOGResult(totalGas.sub(returnFee)) }
} else {
result = { ...result, ...OOGResult(message.gasLimit) }
}
}
}

// Save code if a new contract was created
Expand Down
8 changes: 4 additions & 4 deletions packages/vm/lib/evm/precompiles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ const precompiles: Precompiles = {
}

const precompileAvailability: PrecompileAvailability = {
'0000000000000000000000000000000000000001': 'homestead',
'0000000000000000000000000000000000000002': 'homestead',
[ripemdPrecompileAddress]: 'homestead',
'0000000000000000000000000000000000000004': 'homestead',
'0000000000000000000000000000000000000001': 'chainstart',
'0000000000000000000000000000000000000002': 'chainstart',
[ripemdPrecompileAddress]: 'chainstart',
'0000000000000000000000000000000000000004': 'chainstart',
'0000000000000000000000000000000000000005': 'byzantium',
'0000000000000000000000000000000000000006': 'byzantium',
'0000000000000000000000000000000000000007': 'byzantium',
Expand Down
1 change: 1 addition & 0 deletions packages/vm/lib/exceptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum ERROR {
OUT_OF_GAS = 'out of gas',
CODESTORE_OUT_OF_GAS = 'code store out of gas',
STACK_UNDERFLOW = 'stack underflow',
STACK_OVERFLOW = 'stack overflow',
INVALID_JUMP = 'invalid JUMP',
Expand Down
1 change: 1 addition & 0 deletions packages/vm/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export default class VM extends AsyncEventEmitter {
const chain = opts.chain ? opts.chain : 'mainnet'
const hardfork = opts.hardfork ? opts.hardfork : 'petersburg'
const supportedHardforks = [
'chainstart',
'homestead',
'tangerineWhistle',
'spuriousDragon',
Expand Down
13 changes: 10 additions & 3 deletions packages/vm/tests/BlockchainTestsRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,19 @@ module.exports = async function runBlockchainTest(options, testData, t) {
await cacheDB.close()
}

//console.log(testData)
const numBlocks = testData.blocks.length
let currentBlock = 0
let lastBlock = false
for (const raw of testData.blocks) {
currentBlock++
lastBlock = (currentBlock == numBlocks)
const paramFork = `expectException${options.forkConfigTestSuite}`
// Two naming conventions in ethereum/tests to indicate "exception occurs on all HFs" semantics
// Last checked: ethereumjs-testing v1.3.1 (2020-05-11)
const paramAll1 = 'expectExceptionALL'
const paramAll2 = 'expectException'
const expectException = raw[paramFork] ? raw[paramFork] : raw[paramAll1] || raw[paramAll2]
const expectException = raw[paramFork] ? raw[paramFork] : raw[paramAll1] || raw[paramAll2] || raw.blockHeader == undefined

try {
const block = new Block(Buffer.from(raw.rlp.slice(2), 'hex'), {
Expand Down Expand Up @@ -115,9 +121,10 @@ module.exports = async function runBlockchainTest(options, testData, t) {
// fix for BlockchainTests/GeneralStateTests/stRandom/*
testData.lastblockhash = testData.lastblockhash.substr(2)
}
if (expectException !== undefined) {
if (expectException !== undefined && lastBlock) { // only check last block hash on last block
t.equal(headBlock.hash().toString('hex'), testData.lastblockhash, 'last block hash')
}

// if the test fails, then block.header is the prej because
// vm.runBlock has a check that prevents the actual postState from being
// imported if it is not equal to the expected postState. it is useful
Expand All @@ -131,7 +138,7 @@ module.exports = async function runBlockchainTest(options, testData, t) {
if (options.debug) {
await verifyPostConditions(state, testData.postState, t)
}
if (expectException !== undefined) {
if (expectException !== undefined && lastBlock) {
t.equal(
blockchain.meta.rawHead.toString('hex'),
testData.lastblockhash,
Expand Down
4 changes: 4 additions & 0 deletions packages/vm/tests/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ const SKIP_VM = [
* @returns {String} Either an alias of the forkConfig param, or the forkConfig param itself
*/
function getRequiredForkConfigAlias(forkConfig) {
// Chainstart is also called Frontier and is called as such in the tests
if (String(forkConfig).match(/^chainstart$/i)) {
return 'Frontier'
}
// TangerineWhistle is named EIP150 (attention: misleading name)
// in the client-independent consensus test suite
if (String(forkConfig).match(/^tangerineWhistle$/i)) {
Expand Down