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 { 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,8 +360,8 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { state.clearOriginalStorageCache() // Generate the tx receipt - const 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 @@ -412,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/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/runBlock.spec.ts b/packages/vm/tests/api/runBlock.spec.ts index 06e913510f..c6f3f4ee65 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,53 @@ 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( 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 })