Skip to content

Commit

Permalink
Merge pull request #1264 from ethereumjs/add-runTx-blockGasUsed
Browse files Browse the repository at this point in the history
runTx: add blockGasUsed param
  • Loading branch information
holgerd77 committed May 21, 2021
2 parents 452c6ae + f79ec2b commit a1e7a34
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 66 deletions.
4 changes: 2 additions & 2 deletions packages/tx/src/eip1559Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export default class FeeMarketEIP1559Transaction extends BaseTransaction<FeeMark
// Verify the access list format.
AccessLists.verifyAccessList(this.accessList)

this.chainId = chainId ? new BN(toBuffer(chainId)) : new BN(this.common.chainId())
this.chainId = chainId ? new BN(toBuffer(chainId)) : this.common.chainIdBN()
this.maxFeePerGas = new BN(toBuffer(maxFeePerGas === '' ? '0x' : maxFeePerGas))
this.maxPriorityFeePerGas = new BN(
toBuffer(maxPriorityFeePerGas === '' ? '0x' : maxPriorityFeePerGas)
Expand All @@ -163,7 +163,7 @@ export default class FeeMarketEIP1559Transaction extends BaseTransaction<FeeMark
maxPriorityFeePerGas: this.maxPriorityFeePerGas,
})

if (!this.chainId.eq(new BN(this.common.chainId().toString()))) {
if (!this.chainId.eq(this.common.chainIdBN())) {
throw new Error('The chain ID does not match the chain ID of Common')
}

Expand Down
15 changes: 2 additions & 13 deletions packages/vm/lib/runBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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)

Expand Down
32 changes: 11 additions & 21 deletions packages/vm/lib/runTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
/**
Expand Down Expand Up @@ -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
}

/**
Expand Down Expand Up @@ -331,15 +325,11 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise<RunTxResult> {
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)
Expand Down Expand Up @@ -370,8 +360,8 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise<RunTxResult> {
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
Expand Down Expand Up @@ -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<TxReceipt> {
const baseReceipt: BaseTxReceipt = {
gasUsed: blockGasUsed.toArrayLike(Buffer),
gasUsed: cumulativeGasUsed.toArrayLike(Buffer),
bitvector: txResult.bloom.bitvector,
logs: txResult.execResult.logs ?? [],
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vm/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
/**
Expand Down
80 changes: 51 additions & 29 deletions packages/vm/tests/api/runBlock.spec.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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(
Expand Down
19 changes: 19 additions & 0 deletions packages/vm/tests/api/runTx.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
Expand Down

0 comments on commit a1e7a34

Please sign in to comment.