Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runTx: add blockGasUsed param #1264

Merged
merged 5 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah, this is a lot more targeted, really somewhat relieved on seeing this change here. 😀

}

/**
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