-
Notifications
You must be signed in to change notification settings - Fork 770
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add txpool_content rpc method for pending txs fetch (#2410)
* Add txpool_content rpc method for pending txs fetch * Fix rpc export * Add tests for `txpool_content` * address feedback * Include vm by default in rpc tests * Missed commits from shandong Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>
- Loading branch information
Showing
6 changed files
with
166 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { bigIntToHex, bufferToHex, intToHex } from '@ethereumjs/util' | ||
|
||
import type { Block } from '@ethereumjs/block' | ||
import type { JsonRpcTx, TypedTransaction } from '@ethereumjs/tx' | ||
|
||
/** | ||
* Returns tx formatted to the standard JSON-RPC fields | ||
*/ | ||
export const jsonRpcTx = (tx: TypedTransaction, block?: Block, txIndex?: number): JsonRpcTx => { | ||
const txJSON = tx.toJSON() | ||
return { | ||
blockHash: block ? bufferToHex(block.hash()) : null, | ||
blockNumber: block ? bigIntToHex(block.header.number) : null, | ||
from: tx.getSenderAddress().toString(), | ||
gas: txJSON.gasLimit!, | ||
gasPrice: txJSON.gasPrice ?? txJSON.maxFeePerGas!, | ||
maxFeePerGas: txJSON.maxFeePerGas, | ||
maxPriorityFeePerGas: txJSON.maxPriorityFeePerGas, | ||
type: intToHex(tx.type), | ||
accessList: txJSON.accessList, | ||
chainId: txJSON.chainId, | ||
hash: bufferToHex(tx.hash()), | ||
input: txJSON.data!, | ||
nonce: txJSON.nonce!, | ||
to: tx.to?.toString() ?? null, | ||
transactionIndex: txIndex !== undefined ? intToHex(txIndex) : null, | ||
value: txJSON.value!, | ||
v: txJSON.v!, | ||
r: txJSON.r!, | ||
s: txJSON.s!, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
export const list = ['Eth', 'Engine', 'Web3', 'Net', 'Admin'] | ||
export const list = ['Eth', 'Engine', 'Web3', 'Net', 'Admin', 'TxPool'] | ||
|
||
export * from './admin' | ||
export * from './engine' | ||
export * from './eth' | ||
export * from './net' | ||
export * from './txpool' | ||
export * from './web3' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { jsonRpcTx } from '../helpers' | ||
import { middleware } from '../validation' | ||
|
||
import type { EthereumClient } from '../..' | ||
import type { FullEthereumService } from '../../service' | ||
import type { TxPool as Pool } from '../../service/txpool' | ||
import type { VM } from '@ethereumjs/vm' | ||
|
||
/** | ||
* web3_* RPC module | ||
* @memberof module:rpc/modules | ||
*/ | ||
export class TxPool { | ||
private _txpool: Pool | ||
private _vm: VM | ||
/** | ||
* Create web3_* RPC module | ||
* @param client Client to which the module binds | ||
*/ | ||
constructor(client: EthereumClient) { | ||
const service = client.services.find((s) => s.name === 'eth') as FullEthereumService | ||
this._txpool = service.txPool | ||
this._vm = service.execution.vm | ||
this.content = middleware(this.content.bind(this), 0, []) | ||
} | ||
|
||
/** | ||
* Returns the contents of the transaction pool | ||
* @param params An empty array | ||
*/ | ||
content(_params = []) { | ||
const pending = new Map() | ||
for (const pool of this._txpool.pool) { | ||
const pendingForAcct = new Map<bigint, any>() | ||
for (const tx of pool[1]) { | ||
pendingForAcct.set(tx.tx.nonce, jsonRpcTx(tx.tx)) | ||
} | ||
if (pendingForAcct.size > 0) pending.set('0x' + pool[0], Object.fromEntries(pendingForAcct)) | ||
} | ||
return { | ||
pending: Object.fromEntries(pending), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { Block, BlockHeader } from '@ethereumjs/block' | ||
import { Blockchain } from '@ethereumjs/blockchain' | ||
import { Chain, Common, Hardfork } from '@ethereumjs/common' | ||
import { TransactionFactory } from '@ethereumjs/tx' | ||
import { randomBytes } from 'crypto' | ||
import * as tape from 'tape' | ||
|
||
import { baseRequest, createClient, createManager, params, startRPC } from '../helpers' | ||
|
||
import type { FullEthereumService } from '../../../lib/service' | ||
|
||
const method = 'txpool_content' | ||
|
||
tape(`${method}: call with valid arguments`, async (t) => { | ||
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) | ||
const blockchain = await Blockchain.create({ | ||
common, | ||
validateBlocks: false, | ||
validateConsensus: false, | ||
}) | ||
|
||
const client = createClient({ blockchain, commonChain: common, includeVM: true }) | ||
const manager = createManager(client) | ||
const server = startRPC(manager.getMethods()) | ||
const { execution } = client.services.find((s) => s.name === 'eth') as FullEthereumService | ||
t.notEqual(execution, undefined, 'should have valid execution') | ||
const { vm } = execution | ||
await vm.eei.generateCanonicalGenesis(blockchain.genesisState()) | ||
const gasLimit = 2000000 | ||
const parent = await blockchain.getCanonicalHeadHeader() | ||
const block = Block.fromBlockData( | ||
{ | ||
header: { | ||
parentHash: parent.hash(), | ||
number: 1, | ||
gasLimit, | ||
}, | ||
}, | ||
{ common, calcDifficultyFromHeader: parent } | ||
) | ||
|
||
let ranBlock: Block | undefined = undefined | ||
vm.events.once('afterBlock', (result: any) => (ranBlock = result.block)) | ||
await vm.runBlock({ block, generate: true, skipBlockValidation: true }) | ||
await vm.blockchain.putBlock(ranBlock!) | ||
const service = client.services[0] as FullEthereumService | ||
service.execution.vm._common.setHardfork('london') | ||
service.chain.config.chainCommon.setHardfork('london') | ||
const headBlock = await service.chain.getCanonicalHeadBlock() | ||
const londonBlock = Block.fromBlockData( | ||
{ | ||
header: BlockHeader.fromHeaderData( | ||
{ | ||
baseFeePerGas: 1000000000n, | ||
number: 2n, | ||
parentHash: headBlock.header.hash(), | ||
}, | ||
{ | ||
common: service.chain.config.chainCommon, | ||
skipConsensusFormatValidation: true, | ||
calcDifficultyFromHeader: headBlock.header, | ||
} | ||
), | ||
}, | ||
{ common: service.chain.config.chainCommon } | ||
) | ||
|
||
vm.events.once('afterBlock', (result: any) => (ranBlock = result.block)) | ||
await vm.runBlock({ block: londonBlock, generate: true, skipBlockValidation: true }) | ||
await vm.blockchain.putBlock(ranBlock!) | ||
;(service.txPool as any).validate = () => {} | ||
await service.txPool.add(TransactionFactory.fromTxData({ type: 2 }, {}).sign(randomBytes(32))) | ||
|
||
const req = params(method, []) | ||
const expectedRes = (res: any) => { | ||
t.equal( | ||
Object.keys(res.body.result.pending).length, | ||
1, | ||
'received one pending transaction back from response' | ||
) | ||
} | ||
|
||
await baseRequest(t, server, req, 200, expectedRes) | ||
}) |