From 0ef4add2ec34acc3c90bb338e0ffa12d7dfc5074 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 25 Jun 2020 10:27:01 +0200 Subject: [PATCH 1/6] vm > spuriousDragon: added HF and alias (EIP-158) support to tester, VM and package.json --- packages/vm/lib/index.ts | 1 + packages/vm/package.json | 4 ++-- packages/vm/tests/config.js | 8 +++++++- packages/vm/tests/tester.js | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/vm/lib/index.ts b/packages/vm/lib/index.ts index dd33afa716..537926b93a 100644 --- a/packages/vm/lib/index.ts +++ b/packages/vm/lib/index.ts @@ -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', diff --git a/packages/vm/package.json b/packages/vm/package.json index 737d041236..544c148bcd 100644 --- a/packages/vm/package.json +++ b/packages/vm/package.json @@ -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", diff --git a/packages/vm/tests/config.js b/packages/vm/tests/config.js index 2c3bca1979..78cdc89427 100644 --- a/packages/vm/tests/config.js +++ b/packages/vm/tests/config.js @@ -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' } diff --git a/packages/vm/tests/tester.js b/packages/vm/tests/tester.js index 3041a023c6..991d44de24 100755 --- a/packages/vm/tests/tester.js +++ b/packages/vm/tests/tester.js @@ -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) /** From d6fcb8e9a61993e8483a9b6509c5b78f9eba727b Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 25 Jun 2020 11:09:28 +0200 Subject: [PATCH 2/6] vm > spuriousDragon: added allowed Code size check to EVM message execution --- packages/vm/lib/evm/evm.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/vm/lib/evm/evm.ts b/packages/vm/lib/evm/evm.ts index f2afa09323..88acdae009 100644 --- a/packages/vm/lib/evm/evm.ts +++ b/packages/vm/lib/evm/evm.ts @@ -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 { From e91cbb4ddfbaa88e8147d25325819ba8ec5f3786 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 25 Jun 2020 11:42:54 +0200 Subject: [PATCH 3/6] vm -> spuriousDragon: added EIP-211 Byzantium HF checks for RETURNDATASIZE and RETURNDATACOPY --- packages/vm/lib/evm/opFns.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vm/lib/evm/opFns.ts b/packages/vm/lib/evm/opFns.ts index 85be450d5c..361c17d3f5 100644 --- a/packages/vm/lib/evm/opFns.ts +++ b/packages/vm/lib/evm/opFns.ts @@ -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())) { From 7b4bf9394077b4fa2c527d6c50816372fc65068e Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 25 Jun 2020 11:50:17 +0200 Subject: [PATCH 4/6] vm -> spuriousDragon: added Byzantium HF checks for STATICCALL (EIP-214) and REVERT (EIP-140) --- packages/vm/lib/evm/opFns.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vm/lib/evm/opFns.ts b/packages/vm/lib/evm/opFns.ts index 361c17d3f5..33af20f40e 100644 --- a/packages/vm/lib/evm/opFns.ts +++ b/packages/vm/lib/evm/opFns.ts @@ -775,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) @@ -803,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) From 024f5c91ed056e7fdeedecd9a2d7233051b775cf Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 25 Jun 2020 11:59:10 +0200 Subject: [PATCH 5/6] vm -> spuriousDragon: added dedicated Byzantium HF list to getOpcodesForHF functionality --- packages/vm/lib/evm/opcodes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/lib/evm/opcodes.ts b/packages/vm/lib/evm/opcodes.ts index 6da3ed8dd2..77958fe0a3 100644 --- a/packages/vm/lib/evm/opcodes.ts +++ b/packages/vm/lib/evm/opcodes.ts @@ -293,4 +293,4 @@ export function getOpcodesForHF(common: Common): OpcodeList { } return opcodeBuilder -} +} \ No newline at end of file From 2e9572f8eea7be94e9c41b2f9d4f04477fd16ce6 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Fri, 26 Jun 2020 10:26:12 +0200 Subject: [PATCH 6/6] vm -> spuriousDragon: added pre-Byzantium tx receipt format (EIP-658) to runBlock.js --- packages/vm/lib/evm/opcodes.ts | 2 +- packages/vm/lib/runBlock.ts | 52 +++++++++++++++++++++++++------ packages/vm/tests/api/runBlock.js | 29 +++++++++++++++++ 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/packages/vm/lib/evm/opcodes.ts b/packages/vm/lib/evm/opcodes.ts index 77958fe0a3..6da3ed8dd2 100644 --- a/packages/vm/lib/evm/opcodes.ts +++ b/packages/vm/lib/evm/opcodes.ts @@ -293,4 +293,4 @@ export function getOpcodesForHF(common: Common): OpcodeList { } return opcodeBuilder -} \ No newline at end of file +} diff --git a/packages/vm/lib/runBlock.ts b/packages/vm/lib/runBlock.ts index 59fadd7062..d9d1233782 100644 --- a/packages/vm/lib/runBlock.ts +++ b/packages/vm/lib/runBlock.ts @@ -44,7 +44,7 @@ export interface RunBlockResult { /** * Receipts generated for transactions in the block */ - receipts: TxReceipt[] + receipts: (PreByzantiumTxReceipt | PostByzantiumTxReceipt)[] /** * Results of executing the transactions in the block */ @@ -52,13 +52,9 @@ export interface RunBlockResult { } /** - * 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 */ @@ -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 */ @@ -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 diff --git a/packages/vm/tests/api/runBlock.js b/packages/vm/tests/api/runBlock.js index a49f583df6..92582e1595 100644 --- a/packages/vm/tests/api/runBlock.js +++ b/packages/vm/tests/api/runBlock.js @@ -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() +}) \ No newline at end of file