Skip to content

Commit

Permalink
refactor withdrawal and enhance spec test
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Nov 16, 2022
1 parent b640cf8 commit 6f8dd71
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 26 deletions.
10 changes: 3 additions & 7 deletions packages/block/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
TxOptions,
TypedTransaction,
} from '@ethereumjs/tx'
import type { WithdrawalBuffer } from '@ethereumjs/util'

/**
* An object that represents the block.
Expand Down Expand Up @@ -178,7 +179,7 @@ export class Block {
uncleHeaders.push(BlockHeader.fromValuesArray(uncleHeaderData, uncleOpts))
}

const withdrawals = withdrawalsBuffer
const withdrawals = (withdrawalsBuffer as WithdrawalBuffer[])
?.map(([index, validatorIndex, address, amount]) => ({
index,
validatorIndex,
Expand Down Expand Up @@ -500,12 +501,7 @@ export class Block {
toJSON(): JsonBlock {
const withdrawalsAttr = this.withdrawals
? {
withdrawals: this.withdrawals.map((wt) => ({
index: bigIntToHex(wt.index),
validatorIndex: bigIntToHex(wt.validatorIndex),
address: '0x' + wt.address.buf.toString('hex'),
amount: bigIntToHex(wt.amount),
})),
withdrawals: this.withdrawals.map((wt) => wt.toJSON()),
}
: {}
return {
Expand Down
23 changes: 12 additions & 11 deletions packages/block/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ import type {
JsonTx,
TxData,
} from '@ethereumjs/tx'
import type { AddressLike, BigIntLike, BufferLike, WithdrawalData } from '@ethereumjs/util'
import type {
AddressLike,
BigIntLike,
BufferLike,
JsonRpcWithdrawal,
WithdrawalBuffer,
WithdrawalData,
} from '@ethereumjs/util'

/**
* An object to set to which blockchain the blocks and their headers belong. This could be specified
Expand Down Expand Up @@ -112,17 +119,18 @@ export interface BlockData {
withdrawals?: Array<WithdrawalData>
}

export type WithdrawalsBuffer = WithdrawalBuffer[]

export type BlockBuffer =
| [BlockHeaderBuffer, TransactionsBuffer, UncleHeadersBuffer]
| [BlockHeaderBuffer, TransactionsBuffer, UncleHeadersBuffer, WithdrawalBuffer]
| [BlockHeaderBuffer, TransactionsBuffer, UncleHeadersBuffer, WithdrawalsBuffer]
export type BlockHeaderBuffer = Buffer[]
export type BlockBodyBuffer = [TransactionsBuffer, UncleHeadersBuffer, WithdrawalBuffer?]
export type BlockBodyBuffer = [TransactionsBuffer, UncleHeadersBuffer, WithdrawalsBuffer?]
/**
* TransactionsBuffer can be an array of serialized txs for Typed Transactions or an array of Buffer Arrays for legacy transactions.
*/
export type TransactionsBuffer = Buffer[][] | Buffer[]
export type UncleHeadersBuffer = Buffer[][]
export type WithdrawalBuffer = Buffer[][]

/**
* An object with the block's data represented as strings.
Expand Down Expand Up @@ -160,13 +168,6 @@ export interface JsonHeader {
withdrawalsRoot?: string
}

export interface JsonRpcWithdrawal {
index: string // QUANTITY - bigint 8 bytes
validatorIndex: string // QUANTITY - bigint 8 bytes
address: string // DATA, 20 Bytes address to withdraw to
amount: string // QUANTITY - bigint amount in wei 32 bytes
}

/*
* Based on https://ethereum.org/en/developers/docs/apis/json-rpc/
*/
Expand Down
26 changes: 23 additions & 3 deletions packages/block/test/eip4895block.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Chain, Common, Hardfork } from '@ethereumjs/common'
import { Address, KECCAK256_RLP } from '@ethereumjs/util'
import { decode } from '@ethereumjs/rlp'
import { Address, KECCAK256_RLP, Withdrawal } from '@ethereumjs/util'
import * as tape from 'tape'

import { Block } from '../src/block'
import { BlockHeader } from '../src/header'

import type { WithdrawalData } from '@ethereumjs/util'
import type { WithdrawalBuffer, WithdrawalData } from '@ethereumjs/util'

const gethWithdrawals8BlockRlp =
'f903e1f90213a0fe950635b1bd2a416ff6283b0bbd30176e1b1125ad06fa729da9f3f4c1c61710a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794aa00000000000000000000000000000000000000a07f7510a0cb6203f456e34ec3e2ce30d6c5590ded42c10a9cf3f24784119c5afba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080018401c9c380802f80a0ff0000000000000000000000000000000000000000000000000000000000000088000000000000000007a0b695b29ec7ee934ef6a68838b13729f2d49fffe26718de16a1a9ed94a4d7d06dc0c0f901c6da8082ffff94000000000000000000000000000000000000000080f83b0183010000940100000000000000000000000000000000000000a00100000000000000000000000000000000000000000000000000000000000000f83b0283010001940200000000000000000000000000000000000000a00200000000000000000000000000000000000000000000000000000000000000f83b0383010002940300000000000000000000000000000000000000a00300000000000000000000000000000000000000000000000000000000000000f83b0483010003940400000000000000000000000000000000000000a00400000000000000000000000000000000000000000000000000000000000000f83b0583010004940500000000000000000000000000000000000000a00500000000000000000000000000000000000000000000000000000000000000f83b0683010005940600000000000000000000000000000000000000a00600000000000000000000000000000000000000000000000000000000000000f83b0783010006940700000000000000000000000000000000000000a00700000000000000000000000000000000000000000000000000000000000000'

const common = new Common({
eips: [4895],
Expand All @@ -25,7 +29,23 @@ common.hardforkBlock = function (hardfork: string | undefined) {
return BigInt(0)
}

tape('EIP1559 tests', function (t) {
tape('EIP4895 tests', function (t) {
t.test('should correctly generate withdrawalsRoot', async function (st) {
// get withdwalsArray
const gethBlockBufferArray = decode(Buffer.from(gethWithdrawals8BlockRlp, 'hex'))
const withdrawals = (gethBlockBufferArray[3] as WithdrawalBuffer[]).map((wa) =>
Withdrawal.fromValuesArray(wa)
)
st.equal(withdrawals.length, 8, '8 withdrawals should have been found')
const gethWitdrawalsRoot = (gethBlockBufferArray[0] as Buffer[])[16] as Buffer
st.deepEqual(
await Block.genWithdrawalsTrieRoot(withdrawals),
gethWitdrawalsRoot,
'withdrawalsRoot should be valid'
)
st.end()
})

t.test('Header tests', function (st) {
const earlyCommon = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul })
st.throws(() => {
Expand Down
31 changes: 28 additions & 3 deletions packages/util/src/withdrawal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Address } from './address'
import { bigIntToHex } from './bytes'
import { TypeOutput, toType } from './types'

import type { AddressLike, BigIntLike } from './types'
Expand All @@ -10,6 +11,15 @@ export type WithdrawalData = {
amount: BigIntLike
}

export interface JsonRpcWithdrawal {
index: string // QUANTITY - bigint 8 bytes
validatorIndex: string // QUANTITY - bigint 8 bytes
address: string // DATA, 20 Bytes address to withdraw to
amount: string // QUANTITY - bigint amount in wei 32 bytes
}

export type WithdrawalBuffer = [Buffer, Buffer, Buffer, Buffer]

export class Withdrawal {
constructor(
public readonly index: bigint,
Expand All @@ -33,14 +43,20 @@ export class Withdrawal {
return new Withdrawal(index, validatorIndex, address, amount)
}

public static fromValuesArray(withdrawalArray: WithdrawalBuffer) {
if (withdrawalArray.length !== 4) {
throw Error(`Invalid withdrawalArray length expected=4 actual=${withdrawalArray.length}`)
}
const [index, validatorIndex, address, amount] = withdrawalArray
return Withdrawal.fromWithdrawalData({ index, validatorIndex, address, amount })
}

/**
* Convert a withdrawal to a buffer array
* @param withdrawal the withdrawal to convert
* @returns buffer array of the withdrawal
*/
public static toBufferArray(
withdrawal: Withdrawal | WithdrawalData
): [Buffer, Buffer, Buffer, Buffer] {
public static toBufferArray(withdrawal: Withdrawal | WithdrawalData): WithdrawalBuffer {
const { index, validatorIndex, address, amount } = withdrawal
const indexBuffer =
toType(index, TypeOutput.BigInt) === BigInt(0)
Expand All @@ -67,4 +83,13 @@ export class Withdrawal {
raw() {
return Withdrawal.toBufferArray(this)
}

toJSON() {
return {
index: bigIntToHex(this.index),
validatorIndex: bigIntToHex(this.validatorIndex),
address: '0x' + this.address.buf.toString('hex'),
amount: bigIntToHex(this.amount),
}
}
}
15 changes: 13 additions & 2 deletions packages/util/test/withdrawal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import * as tape from 'tape'

import { Withdrawal, bigIntToHex, intToHex } from '../src'

import type { WithdrawalBuffer } from '../src'

const withdrawalsVector = [
{
Index: 0,
Expand Down Expand Up @@ -66,18 +68,27 @@ tape('Withdrawal', (t) => {
// gethWithdrawals8Rlp is rlp encoded block with withdrawals in the 4th element of the top array
const gethWithdrawalsBuffer = decode(Buffer.from(gethWithdrawals8BlockRlp, 'hex'))[3]!
const gethWithdrawalsRlp = Buffer.from(encode(gethWithdrawalsBuffer)).toString('hex')
t.test('Correct fromWithdrawalData and toBufferArray', (st) => {
t.test('fromWithdrawalData and toBufferArray', (st) => {
const withdrawals = withdrawalsGethVector.map(Withdrawal.fromWithdrawalData)
const withdrawalsToBufferArr = withdrawals.map((wt) => wt.raw())
const withdrawalsToRlp = Buffer.from(encode(withdrawalsToBufferArr)).toString('hex')
st.equal(gethWithdrawalsRlp, withdrawalsToRlp, 'The withdrawals to buffer should match')
st.end()
})

t.test(`Direct withdrawalData to toBufferArray`, (st) => {
t.test('toBufferArray from withdrawalData', (st) => {
const withdrawalsDataToBufferArr = withdrawalsGethVector.map(Withdrawal.toBufferArray)
const withdrawalsDataToRlp = Buffer.from(encode(withdrawalsDataToBufferArr)).toString('hex')
st.equal(gethWithdrawalsRlp, withdrawalsDataToRlp, 'The withdrawals to buffer should match')
st.end()
})

t.test('fromValuesArray and toJSON', (st) => {
const withdrawals = (gethWithdrawalsBuffer as WithdrawalBuffer[]).map(
Withdrawal.fromValuesArray
)
const withdrawalsJson = withdrawals.map((wt) => wt.toJSON())
st.deepEqual(withdrawalsGethVector, withdrawalsJson, 'Withdrawals json should match')
st.end()
})
})

0 comments on commit 6f8dd71

Please sign in to comment.