From 3139c08ea30c0c0b5dfd2ace931480a49160f78e Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 20 May 2021 19:17:42 -0700 Subject: [PATCH 1/4] runBlock/runTx: add blockGasUsed, remove cliqueBeneficiary --- packages/vm/lib/runBlock.ts | 15 +---- packages/vm/lib/runTx.ts | 24 +++----- packages/vm/tests/api/runBlock.spec.ts | 81 +++++++++++++++++--------- 3 files changed, 61 insertions(+), 59 deletions(-) diff --git a/packages/vm/lib/runBlock.ts b/packages/vm/lib/runBlock.ts index 86fb0f3765..e2dc147d8a 100644 --- a/packages/vm/lib/runBlock.ts +++ b/packages/vm/lib/runBlock.ts @@ -236,11 +236,6 @@ async function applyTransactions(this: VM, block: Block, opts: RunBlockOpts) { const receipts = [] const txResults = [] - let cliqueBeneficiary - if (this._common.consensusType() === 'poa' && 'cliqueSigner' in block.header) { - cliqueBeneficiary = block.header.cliqueSigner() - } - /* * Process transactions */ @@ -264,18 +259,12 @@ async function applyTransactions(this: VM, block: Block, opts: RunBlockOpts) { // Run the tx through the VM const { skipBalance, skipNonce } = opts - // Construct a block with the current gasUsed for accurate tx receipt generation - const blockWithGasUsed = Block.fromBlockData( - { ...block, header: { ...block.header, gasUsed } }, - { common: this._common } - ) - const txRes = await this.runTx({ tx, - block: blockWithGasUsed, + block, skipBalance, skipNonce, - cliqueBeneficiary, + blockGasUsed: gasUsed, }) txResults.push(txRes) diff --git a/packages/vm/lib/runTx.ts b/packages/vm/lib/runTx.ts index c78eca0bb6..603aa32dca 100644 --- a/packages/vm/lib/runTx.ts +++ b/packages/vm/lib/runTx.ts @@ -31,9 +31,6 @@ export interface RunTxOpts { /** * The `@ethereumjs/block` the `tx` belongs to. * If omitted, a default blank block will be used. - * To obtain an accurate `TxReceipt`, please pass a block - * with the header field `gasUsed` set to the value - * prior to this tx being run. */ block?: Block /** @@ -68,12 +65,9 @@ export interface RunTxOpts { reportAccessList?: boolean /** - * Optional clique Address: if the consensus algorithm is on clique, - * and this parameter is provided, use this as the beneficiary of transaction fees - * If it is not provided and the consensus algorithm is clique, instead - * get it from the block using `cliqueSigner()` + * To obtain an accurate tx receipt input the block gas used up until this tx. */ - cliqueBeneficiary?: Address + blockGasUsed?: BN } /** @@ -331,15 +325,11 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { miner = block.header.coinbase } else { // Backwards-compatibilty check - // TODO: can be removed along VM v5 release - if (opts.cliqueBeneficiary) { - miner = opts.cliqueBeneficiary + // TODO: can be removed along VM v6 release + if ('cliqueSigner' in block.header) { + miner = block.header.cliqueSigner() } else { - if ('cliqueSigner' in block.header) { - miner = block.header.cliqueSigner() - } else { - miner = Address.zero() - } + miner = Address.zero() } } const minerAccount = await state.getAccount(miner) @@ -370,7 +360,7 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { state.clearOriginalStorageCache() // Generate the tx receipt - const blockGasUsed = block.header.gasUsed.add(results.gasUsed) + const blockGasUsed = (opts.blockGasUsed ?? block.header.gasUsed).add(results.gasUsed) results.receipt = await generateTxReceipt.bind(this)(tx, results, blockGasUsed) /** diff --git a/packages/vm/tests/api/runBlock.spec.ts b/packages/vm/tests/api/runBlock.spec.ts index 06e913510f..d47a749750 100644 --- a/packages/vm/tests/api/runBlock.spec.ts +++ b/packages/vm/tests/api/runBlock.spec.ts @@ -1,5 +1,5 @@ import tape from 'tape' -import { Address, BN, rlp, KECCAK256_RLP } from 'ethereumjs-util' +import { Address, BN, rlp, KECCAK256_RLP, Account } from 'ethereumjs-util' import Common from '@ethereumjs/common' import { Block } from '@ethereumjs/block' import { @@ -20,21 +20,12 @@ const testData = require('./testdata/blockchain.json') const common = new Common({ chain: 'mainnet', hardfork: 'berlin' }) tape('runBlock() -> successful API parameter usage', async (t) => { - async function simpleRun(vm: VM, opts: any = {}) { - const common = opts.common - + async function simpleRun(vm: VM) { const genesisRlp = testData.genesisRLP - const genesis = Block.fromRLPSerializedBlock(genesisRlp, { common }) + const genesis = Block.fromRLPSerializedBlock(genesisRlp) const blockRlp = testData.blocks[0].rlp - const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) - - if (opts.cliqueBeneficiary) { - // eslint-disable-next-line no-extra-semi - ;(block.header as any).cliqueSigner = () => { - return opts.cliqueBeneficiary - } - } + const block = Block.fromRLPSerializedBlock(blockRlp) //@ts-ignore await setupPreConditions(vm.stateManager._trie, testData) @@ -45,17 +36,11 @@ tape('runBlock() -> successful API parameter usage', async (t) => { 'genesis state root should match calculated state root' ) - let generate = false - if (opts.generate) { - generate = true - } - const res = await vm.runBlock({ block, // @ts-ignore root: vm.stateManager._trie.root, skipBlockValidation: true, - generate, }) t.equal( @@ -90,16 +75,6 @@ tape('runBlock() -> successful API parameter usage', async (t) => { t.end() }) - t.test("PoA block, should use block header's cliqueSigner", async (t) => { - const common = new Common({ chain: 'goerli', hardfork: 'berlin' }) - const vm = setupVM({ common }) - const cliqueBeneficiary = Address.fromString('0x70732c08fb6dbb06a64bf619c816c22aed12267a') - await simpleRun(vm, { common, cliqueBeneficiary, generate: true }) - const account = await vm.stateManager.getAccount(cliqueBeneficiary) - t.ok(account.balance.gtn(0), 'beneficiary balance should be updated') - t.end() - }) - t.test('hardforkByBlockNumber option', async (t) => { const common1 = new Common({ chain: 'mainnet', @@ -292,6 +267,54 @@ tape('runBlock() -> runtime behavior', async (t) => { t.end() }) + + t.test('should allocate to correct clique beneficiary', async (t) => { + const common = new Common({ chain: 'goerli' }) + const vm = setupVM({ common }) + + const signer = { + address: new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), + privateKey: Buffer.from( + '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', + 'hex' + ), + publicKey: Buffer.from( + '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195', + 'hex' + ), + } + + const otherUser = { + address: new Address(Buffer.from('6f62d8382bf2587361db73ceca28be91b2acb6df', 'hex')), + privateKey: Buffer.from( + '2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6', + 'hex' + ), + publicKey: Buffer.from( + 'ca0a55f6e81cb897aee6a1c390aa83435c41048faa0564b226cfc9f3df48b73e846377fb0fd606df073addc7bd851f22547afbbdd5c3b028c91399df802083a2', + 'hex' + ), + } + + // add balance to otherUser to send two txs to zero address + await vm.stateManager.putAccount(otherUser.address, new Account(new BN(0), new BN(42000))) + const tx = Transaction.fromTxData( + { to: Address.zero(), gasLimit: 21000, gasPrice: 1 }, + { common } + ).sign(otherUser.privateKey) + + // create block with the signer and txs + const block = Block.genesis( + { transactions: [tx, tx] }, + { common, cliqueSigner: signer.privateKey } + ) + + await vm.runBlock({ block, skipNonce: true, skipBlockValidation: true, generate: true }) + const account = await vm.stateManager.getAccount(signer.address) + + t.ok(account.balance.eqn(42000), 'beneficiary balance should equal the cost of the txs') + t.end() + }) }) async function runBlockAndGetAfterBlockEvent( From 718dbbe8e492e6c523b99a74f8aa2a5ab0313209 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 20 May 2021 19:20:49 -0700 Subject: [PATCH 2/4] misc. nit: use chainIdBN in 1559 tx --- packages/tx/src/eip1559Transaction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tx/src/eip1559Transaction.ts b/packages/tx/src/eip1559Transaction.ts index 35c038cc14..a85dbec2b8 100644 --- a/packages/tx/src/eip1559Transaction.ts +++ b/packages/tx/src/eip1559Transaction.ts @@ -152,7 +152,7 @@ export default class FeeMarketEIP1559Transaction extends BaseTransaction Date: Thu, 20 May 2021 19:38:19 -0700 Subject: [PATCH 3/4] runTx: add blockGasUsed test, improve receipt gasUsed type definition --- packages/vm/lib/types.ts | 2 +- packages/vm/tests/api/runTx.spec.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/vm/lib/types.ts b/packages/vm/lib/types.ts index 583a8a7b13..7b8a4ebddb 100644 --- a/packages/vm/lib/types.ts +++ b/packages/vm/lib/types.ts @@ -7,7 +7,7 @@ export type TxReceipt = PreByzantiumTxReceipt | PostByzantiumTxReceipt | EIP2930 */ export interface BaseTxReceipt { /** - * Gas used + * Cumulative gas used in the block including this tx */ gasUsed: Buffer /** diff --git a/packages/vm/tests/api/runTx.spec.ts b/packages/vm/tests/api/runTx.spec.ts index 32ea535a90..6e85de653e 100644 --- a/packages/vm/tests/api/runTx.spec.ts +++ b/packages/vm/tests/api/runTx.spec.ts @@ -49,6 +49,25 @@ tape('runTx() -> successful API parameter usage', async (t) => { t.end() }) + t.test('should use passed in blockGasUsed to generate tx receipt', async (t) => { + const common = new Common({ chain: 'mainnet', hardfork: 'istanbul' }) + const vm = new VM({ common }) + + const tx = getTransaction(vm._common, 0, true) + + const caller = tx.getSenderAddress() + const acc = createAccount() + await vm.stateManager.putAccount(caller, acc) + + const blockGasUsed = new BN(1000) + const res = await vm.runTx({ tx, blockGasUsed }) + t.ok( + new BN(res.receipt.gasUsed).eq(blockGasUsed.add(res.gasUsed)), + 'receipt.gasUsed should equal block gas used + tx gas used' + ) + t.end() + }) + t.test('Legacy Transaction with HF set to pre-Berlin', async (t) => { const common = new Common({ chain: 'mainnet', hardfork: 'istanbul' }) const vm = new VM({ common }) From eb9dd07f6b71a5c09d68a2bf8e005d1c293f4e3c Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 20 May 2021 20:16:10 -0700 Subject: [PATCH 4/4] clarify block gas used --- packages/vm/lib/runTx.ts | 10 +++++----- packages/vm/tests/api/runBlock.spec.ts | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/vm/lib/runTx.ts b/packages/vm/lib/runTx.ts index 603aa32dca..001ae35be3 100644 --- a/packages/vm/lib/runTx.ts +++ b/packages/vm/lib/runTx.ts @@ -360,8 +360,8 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { state.clearOriginalStorageCache() // Generate the tx receipt - const blockGasUsed = (opts.blockGasUsed ?? block.header.gasUsed).add(results.gasUsed) - results.receipt = await generateTxReceipt.bind(this)(tx, results, blockGasUsed) + const cumulativeGasUsed = (opts.blockGasUsed ?? block.header.gasUsed).add(results.gasUsed) + results.receipt = await generateTxReceipt.bind(this)(tx, results, cumulativeGasUsed) /** * The `afterTx` event @@ -402,16 +402,16 @@ function txLogsBloom(logs?: any[]): Bloom { * @param this The vm instance * @param tx The transaction * @param txResult The tx result - * @param blockGasUsed The amount of gas used in the block up until this tx + * @param cumulativeGasUsed The gas used in the block including this tx */ export async function generateTxReceipt( this: VM, tx: TypedTransaction, txResult: RunTxResult, - blockGasUsed: BN + cumulativeGasUsed: BN ): Promise { const baseReceipt: BaseTxReceipt = { - gasUsed: blockGasUsed.toArrayLike(Buffer), + gasUsed: cumulativeGasUsed.toArrayLike(Buffer), bitvector: txResult.bloom.bitvector, logs: txResult.execResult.logs ?? [], } diff --git a/packages/vm/tests/api/runBlock.spec.ts b/packages/vm/tests/api/runBlock.spec.ts index d47a749750..c6f3f4ee65 100644 --- a/packages/vm/tests/api/runBlock.spec.ts +++ b/packages/vm/tests/api/runBlock.spec.ts @@ -311,7 +311,6 @@ tape('runBlock() -> runtime behavior', async (t) => { await vm.runBlock({ block, skipNonce: true, skipBlockValidation: true, generate: true }) const account = await vm.stateManager.getAccount(signer.address) - t.ok(account.balance.eqn(42000), 'beneficiary balance should equal the cost of the txs') t.end() })