Skip to content

Commit

Permalink
Merge pull request #791 from ethereumjs/spuriousDragon
Browse files Browse the repository at this point in the history
SpuriousDragon HF Support
  • Loading branch information
holgerd77 authored Jul 2, 2020
2 parents cf82c4e + 2e9572f commit dacc853
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 15 deletions.
12 changes: 10 additions & 2 deletions packages/vm/lib/evm/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,18 @@ export default class EVM {
totalGas = totalGas.add(returnFee)
}

// if not enough gas
// Check for SpuriousDragon EIP-170 code size limit
let allowedCodeSize = true
if (
this._vm._common.gteHardfork('spuriousDragon') &&
result.returnValue.length > this._vm._common.param('vm', 'maxCodeSize')
) {
allowedCodeSize = false
}
// If enough gas and allowed code size
if (
totalGas.lte(message.gasLimit) &&
(this._vm.allowUnlimitedContractSize || result.returnValue.length <= 24576)
(this._vm.allowUnlimitedContractSize || allowedCodeSize)
) {
result.gasUsed = totalGas
} else {
Expand Down
12 changes: 12 additions & 0 deletions packages/vm/lib/evm/opFns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,9 +401,15 @@ export const handlers: { [k: string]: OpHandler } = {
runState.stack.push(new BN(keccak256(code)))
},
RETURNDATASIZE: function (runState: RunState) {
if (!runState._common.gteHardfork('byzantium')) {
trap(ERROR.INVALID_OPCODE)
}
runState.stack.push(runState.eei.getReturnDataSize())
},
RETURNDATACOPY: function (runState: RunState) {
if (!runState._common.gteHardfork('byzantium')) {
trap(ERROR.INVALID_OPCODE)
}
let [memOffset, returnDataOffset, length] = runState.stack.popN(3)

if (returnDataOffset.add(length).gt(runState.eei.getReturnDataSize())) {
Expand Down Expand Up @@ -769,6 +775,9 @@ export const handlers: { [k: string]: OpHandler } = {
runState.stack.push(ret)
},
STATICCALL: async function (runState: RunState) {
if (!runState._common.gteHardfork('byzantium')) {
trap(ERROR.INVALID_OPCODE)
}
const value = new BN(0)
let [gasLimit, toAddress, inOffset, inLength, outOffset, outLength] = runState.stack.popN(6)
const toAddressBuf = addressToBuffer(toAddress)
Expand Down Expand Up @@ -797,6 +806,9 @@ export const handlers: { [k: string]: OpHandler } = {
runState.eei.finish(returnData)
},
REVERT: function (runState: RunState) {
if (!runState._common.gteHardfork('byzantium')) {
trap(ERROR.INVALID_OPCODE)
}
const [offset, length] = runState.stack.popN(2)
subMemUsage(runState, offset, length)
let returnData = Buffer.alloc(0)
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 = [
'spuriousDragon',
'byzantium',
'constantinople',
'petersburg',
Expand Down
52 changes: 43 additions & 9 deletions packages/vm/lib/runBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,17 @@ export interface RunBlockResult {
/**
* Receipts generated for transactions in the block
*/
receipts: TxReceipt[]
receipts: (PreByzantiumTxReceipt | PostByzantiumTxReceipt)[]
/**
* Results of executing the transactions in the block
*/
results: RunTxResult[]
}

/**
* Receipt generated for a transaction
* Abstract interface with common transaction receipt fields
*/
export interface TxReceipt {
/**
* Status of transaction, `1` if successful, `0` if an exception occured
*/
status: 0 | 1
interface TxReceipt {
/**
* Gas used
*/
Expand All @@ -73,6 +69,28 @@ export interface TxReceipt {
logs: any[]
}

/**
* Pre-Byzantium receipt type with a field
* for the intermediary state root
*/
export interface PreByzantiumTxReceipt extends TxReceipt {
/**
* Intermediary state root
*/
stateRoot: Buffer
}

/**
* Receipt type for Byzantium and beyond replacing the intermediary
* state root field with a status code field (EIP-658)
*/
export interface PostByzantiumTxReceipt extends TxReceipt {
/**
* Status of transaction, `1` if successful, `0` if an exception occured
*/
status: 0 | 1
}

/**
* @ignore
*/
Expand Down Expand Up @@ -219,12 +237,28 @@ async function applyTransactions(this: VM, block: any, opts: RunBlockOpts) {
// Combine blooms via bitwise OR
bloom.or(txRes.bloom)

const txReceipt: TxReceipt = {
status: txRes.execResult.exceptionError ? 0 : 1, // Receipts have a 0 as status on error
const abstractTxReceipt: TxReceipt = {
gasUsed: gasUsed.toArrayLike(Buffer),
bitvector: txRes.bloom.bitvector,
logs: txRes.execResult.logs || [],
}
let txReceipt
if (this._common.gteHardfork('byzantium')) {
txReceipt = {
status: txRes.execResult.exceptionError ? 0 : 1, // Receipts have a 0 as status on error
...abstractTxReceipt,
} as PostByzantiumTxReceipt
} else {
// This is just using a dummy place holder for the state root right now.
// Giving the correct intermediary state root would need a too depp intervention
// into the current checkpointing mechanism which hasn't been considered
// to be worth it on a HF backport, 2020-06-26
txReceipt = {
stateRoot: Buffer.alloc(32),
...abstractTxReceipt,
} as PreByzantiumTxReceipt
}

receipts.push(txReceipt)

// Add receipt to trie to later calculate receipt root
Expand Down
4 changes: 2 additions & 2 deletions packages/vm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"coverage:test": "npm run build && tape './tests/api/**/*.js' ./tests/tester.js --state --dist",
"docs:build": "typedoc --options typedoc.js",
"test:state": "ts-node ./tests/tester --state",
"test:state:allForks": "npm run test:state -- --fork=Byzantium && npm run test:state -- --fork=Constantinople && npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=Istanbul && npm run test:state -- --fork=MuirGlacier",
"test:state:selectedForks": "npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=Istanbul && npm run test:state -- --fork=MuirGlacier",
"test:state:allForks": "npm run test:state -- --fork=SpuriousDragon && npm run test:state -- --fork=Byzantium && npm run test:state -- --fork=Constantinople && npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=Istanbul && npm run test:state -- --fork=MuirGlacier",
"test:state:selectedForks": "npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=SpuriousDragon",
"test:state:slow": "npm run test:state -- --runSkipped=slow",
"test:buildIntegrity": "npm run test:state -- --test='stackOverflow'",
"test:blockchain": "node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain",
Expand Down
29 changes: 29 additions & 0 deletions packages/vm/tests/api/runBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,32 @@ tape('should run valid block', async (t) => {

t.end()
})

async function runWithHf(hardfork) {
const vm = setupVM({ hardfork: hardfork })
const suite = setup(vm)

const block = new Block(util.rlp.decode(suite.data.blocks[0].rlp))

await setupPreConditions(suite.vm.stateManager._trie, suite.data)

let res = await suite.p.runBlock({
block,
generate: true,
skipBlockValidation: true,
})
return res
}

tape('should return correct HF receipts', async (t) => {
let res = await runWithHf('byzantium')
t.equal(res.receipts[0].status, 1, 'should return correct post-Byzantium receipt format')

res = await runWithHf('spuriousDragon')
t.deepEqual(
res.receipts[0].stateRoot,
Buffer.alloc(32),
'should return correct pre-Byzantium receipt format')

t.end()
})
8 changes: 7 additions & 1 deletion packages/vm/tests/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,17 @@ const SKIP_VM = [
* @returns {String} Either an alias of the forkConfig param, or the forkConfig param itself
*/
function getRequiredForkConfigAlias(forkConfig) {
// SpuriousDragon is named EIP158 (attention: misleading name)
// in the client-independent consensus test suite
if (String(forkConfig).match(/^spuriousDragon$/i)) {
return 'EIP158'
}
// Run the Istanbul tests for MuirGlacier since there are no dedicated tests
if (String(forkConfig).match(/^muirGlacier/i)) {
return 'Istanbul'
}
// Petersburg is named ConstantinopleFix in the client-independent consensus test suite
// Petersburg is named ConstantinopleFix
// in the client-independent consensus test suite
if (String(forkConfig).match(/^petersburg$/i)) {
return 'ConstantinopleFix'
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vm/tests/tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function runTests() {

const FORK_CONFIG = (argv.fork || config.DEFAULT_FORK_CONFIG)
const FORK_CONFIG_TEST_SUITE = config.getRequiredForkConfigAlias(FORK_CONFIG)
// Istanbul -> istanbul, MuirGlacier -> muirGlacier
// Examples: Istanbul -> istanbul, MuirGlacier -> muirGlacier
const FORK_CONFIG_VM = FORK_CONFIG.charAt(0).toLowerCase() + FORK_CONFIG.substring(1)

/**
Expand Down

0 comments on commit dacc853

Please sign in to comment.