Skip to content

Commit

Permalink
client: add missing tx fields to getBlockByHash (#1881)
Browse files Browse the repository at this point in the history
* client: add missing tx fields to getBlockByHash

* Add test for "includeTransactions"

* Fix test

* numbers to hex

* DRY

Co-authored-by: Ryan Ghods <ryan@ryanio.com>
  • Loading branch information
acolytec3 and ryanio committed May 16, 2022
1 parent 8e1c3cf commit 4217ed6
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 97 deletions.
79 changes: 32 additions & 47 deletions packages/client/lib/rpc/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ type JsonRpcBlock = {
gasLimit: string // the maximum gas allowed in this block.
gasUsed: string // the total used gas by all transactions in this block.
timestamp: string // the unix timestamp for when the block was collated.
transactions: JsonTx[] | string[] // Array of transaction objects, or 32 Bytes transaction hashes depending on the last given parameter.
transactions: Array<JsonRpcTx | string> // Array of transaction objects, or 32 Bytes transaction hashes depending on the last given parameter.
uncles: string[] // Array of uncle hashes
baseFeePerGas?: string // If EIP-1559 is enabled for this block, returns the base fee per gas
}
Expand Down Expand Up @@ -129,6 +129,34 @@ type JsonRpcLog = {
// (e.g. Deposit(address,bytes32,uint256)), except you declared the event with the anonymous specifier.)
}

/**
* Returns tx formatted to the standard JSON-RPC fields
*/
const jsonRpcTx = (tx: TypedTransaction, block?: Block, txIndex?: number): JsonRpcTx => {
const txJSON = tx.toJSON()
return {
blockHash: block ? bufferToHex(block.hash()) : null,
blockNumber: block ? bnToHex(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!,
}
}

/**
* Returns block formatted to the standard JSON-RPC fields
*/
Expand All @@ -139,25 +167,10 @@ const jsonRpcBlock = async (
): Promise<JsonRpcBlock> => {
const json = block.toJSON()
const header = json!.header!

let transactions
if (includeTransactions) {
transactions = block.transactions.map((tx) => {
const transaction = tx.toJSON()
const { gasLimit: gas, data: input, ...txData } = transaction
return {
...txData,
// RPC specs specify `input` rather than `data`, and `gas` rather than `gasLimit`
input,
gas,
}
})
} else {
transactions = block.transactions.map((tx) => bufferToHex(tx.hash()))
}

const transactions = block.transactions.map((tx, txIndex) =>
includeTransactions ? jsonRpcTx(tx, block, txIndex) : bufferToHex(tx.hash())
)
const td = await chain.getTd(block.hash(), block.header.number)

return {
number: header.number!,
hash: bufferToHex(block.hash()),
Expand All @@ -183,34 +196,6 @@ const jsonRpcBlock = async (
}
}

/**
* Returns tx formatted to the standard JSON-RPC fields
*/
const jsonRpcTx = (tx: TypedTransaction, block?: Block, txIndex?: number): JsonRpcTx => {
const txJSON = tx.toJSON()
return {
blockHash: block ? bufferToHex(block.hash()) : null,
blockNumber: block ? bnToHex(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!,
}
}

/**
* Returns log formatted to the standard JSON-RPC fields
*/
Expand Down
23 changes: 15 additions & 8 deletions packages/client/test/rpc/eth/getBlockByHash.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@ const method = 'eth_getBlockByHash'
tape(`${method}: call with valid arguments`, async (t) => {
const { server } = baseSetup()

const req = params(method, [
'0x910abca1728c53e8d6df870dd7af5352e974357dc58205dea1676be17ba6becf',
false,
])
const expectRes = (res: any) => {
const msg = 'should return the correct number'
t.equal(res.body.result.number, '0x444444', msg)
const blockHash = '0x910abca1728c53e8d6df870dd7af5352e974357dc58205dea1676be17ba6becf'
let includeTransactions = false
let req = params(method, [blockHash, includeTransactions])
let expectRes = (res: any) => {
t.equal(res.body.result.number, '0x444444', 'should return the correct number')
t.equal(typeof res.body.result.transactions[0], 'string', 'should only include tx hashes')
}
await baseRequest(t, server, req, 200, expectRes)
await baseRequest(t, server, req, 200, expectRes, false)

includeTransactions = true
req = params(method, [blockHash, includeTransactions])
expectRes = (res: any) => {
t.equal(res.body.result.number, '0x444444', 'should return the correct number')
t.equal(typeof res.body.result.transactions[0], 'object', 'should include tx objects')
}
await baseRequest(t, server, req, 200, expectRes, true) // pass endOnFinish=true for last test
})

tape(`${method}: call with false for second argument`, async (t) => {
Expand Down
41 changes: 12 additions & 29 deletions packages/client/test/rpc/eth/getBlockByNumber.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
import { Block } from '@ethereumjs/block'
import { BN, bufferToHex } from 'ethereumjs-util'
import { Transaction } from '@ethereumjs/tx'
import { BN } from 'ethereumjs-util'
import tape from 'tape'
import { INVALID_PARAMS } from '../../../lib/rpc/error-code'
import { startRPC, createManager, createClient, params, baseRequest } from '../helpers'
import { startRPC, createManager, createClient, params, baseRequest, dummy } from '../helpers'
import { checkError } from '../util'

const mockedTxData = {
nonce: '0x',
gasPrice: '0x',
gasLimit: '0x',
to: '0x',
value: '0x',
data: '0x',
v: '0x',
r: '0x',
s: '0x',
}
const mockedTx1 = Transaction.fromTxData({}).sign(dummy.privKey)
const mockedTx2 = Transaction.fromTxData({ nonce: 1 }).sign(dummy.privKey)

function createChain() {
const genesisBlockHash = Buffer.from(
Expand All @@ -26,36 +18,28 @@ function createChain() {
'dcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5',
'hex'
)
const txHash = Buffer.from(
'c6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b',
'hex'
)
const txHash2 = Buffer.from(
'a2285835057e8252ebd4980cf498f7538cedb3600dc183f1c523c6971b6889aa',
'hex'
)

const transactions = [{ hash: bufferToHex(txHash) }]
const transactions2 = [{ hash: bufferToHex(txHash2) }]
const transactions = [mockedTx1]
const transactions2 = [mockedTx2]
const genesisBlock = {
hash: () => genesisBlockHash,
header: {
number: new BN(0),
},
toJSON: () => ({ ...Block.fromBlockData({ header: { number: 0 } }).toJSON(), transactions }),
transactions: [{ hash: () => txHash }],
transactions,
uncleHeaders: [],
}
const block = {
hash: () => blockHash,
header: {
number: new BN(1),
hash: () => blockHash,
},
toJSON: () => ({
...Block.fromBlockData({ header: { number: 1 } }).toJSON(),
transactions: transactions2,
}),
transactions: [{ hash: () => txHash2, toJSON: () => mockedTxData }],
transactions: transactions2,
uncleHeaders: [],
}
return {
Expand Down Expand Up @@ -115,6 +99,7 @@ tape(`${method}: call with latest param`, async (t) => {
const expectRes = (res: any) => {
const msg = 'should return a block number'
t.equal(res.body.result.number, '0x1', msg)
t.equal(typeof res.body.result.transactions[0], 'string', 'should only include tx hashes')
}
await baseRequest(t, server, req, 200, expectRes)
})
Expand Down Expand Up @@ -176,9 +161,7 @@ tape(`${method}: call with transaction objects`, async (t) => {
const req = params(method, ['latest', true])

const expectRes = (res: any) => {
const [transactionData] = res.body.result.transactions
const { gasLimit: gas, data: input, ...txData } = mockedTxData
t.deepEqual(transactionData, { ...txData, input, gas })
t.equal(typeof res.body.result.transactions[0], 'object', 'should include tx objects')
}
await baseRequest(t, server, req, 200, expectRes)
})
19 changes: 6 additions & 13 deletions packages/client/test/rpc/mockBlockchain.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Block } from '@ethereumjs/block'
import { BN, bufferToHex, toBuffer } from 'ethereumjs-util'
import { Transaction } from '@ethereumjs/tx'
import { BN, toBuffer } from 'ethereumjs-util'
import { dummy } from './helpers'

export function mockBlockchain(options: any = {}) {
const number = options.number ?? '0x444444'
const blockHash =
options.hash ?? '0x910abca1728c53e8d6df870dd7af5352e974357dc58205dea1676be17ba6becf'
const txHash = Buffer.from(
'0be3065cf288b071ccff922c1c601e2e5628d488b66e781c260ecee36054a2dc',
'hex'
)
const transactions = options.transactions ?? [Transaction.fromTxData({}).sign(dummy.privKey)]
const block = {
hash: () => toBuffer(blockHash),
header: {
Expand All @@ -17,15 +16,9 @@ export function mockBlockchain(options: any = {}) {
toJSON: () => ({
...Block.fromBlockData({ header: { number } }).toJSON(),
hash: options.hash ?? blockHash,
transactions: options.transactions ?? [{ hash: bufferToHex(txHash) }],
transactions: transactions.map((t: Transaction) => t.toJSON()),
}),
transactions: options.transactions ?? [
{
hash: () => {
return txHash
},
},
],
transactions,
uncleHeaders: [],
}
return {
Expand Down

0 comments on commit 4217ed6

Please sign in to comment.