Skip to content

Commit 2908c04

Browse files
feat(block): Add CLRequests test, example, and documentation (#4008)
* feat(block): add CLRequests test, example, and documentation * fix(block): update CLRequests examples to use bytesToHex * chore: specify Node.js 20 in .nvmrc
1 parent d2fbe9a commit 2908c04

File tree

4 files changed

+224
-0
lines changed

4 files changed

+224
-0
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
20

packages/block/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,39 @@ void main()
240240

241241
Starting with v10 this library supports requests to the consensus layer which have been introduced with [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) (`Hardfork.Prague` or higher). See the `@ethereumjs/util` [Request](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/util#module-request) README section for an overview of current request types.
242242

243+
```ts
244+
// ./examples/clrequests.ts
245+
246+
import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
247+
import { createCLRequest, CLRequestType, hexToBytes, bytesToHex } from '@ethereumjs/util'
248+
import { sha256 } from 'ethereum-cryptography/sha256.js'
249+
250+
import { createBlock, genRequestsRoot } from '../src'
251+
252+
// Enable EIP-7685 to support CLRequests
253+
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7685] })
254+
255+
// Create the three CLRequest types (Deposit, Withdrawal, Consolidation)
256+
const depositData = hexToBytes('0x00...') // Deposit request data
257+
const depositRequest = createCLRequest(depositData)
258+
259+
const withdrawalData = hexToBytes('0x01...') // Withdrawal request data
260+
const withdrawalRequest = createCLRequest(withdrawalData)
261+
262+
const consolidationData = hexToBytes('0x02...') // Consolidation request data
263+
const consolidationRequest = createCLRequest(consolidationData)
264+
265+
// CLRequests must be sorted by type (Deposit=0, Withdrawal=1, Consolidation=2)
266+
const requests = [depositRequest, withdrawalRequest, consolidationRequest]
267+
268+
// Generate the requestsHash
269+
const requestsHash = genRequestsRoot(requests, sha256)
270+
271+
// Create a block with the CLRequests hash
272+
const block = createBlock({ header: { requestsHash } }, { common })
273+
console.log(`Created block with CLRequests hash: 0x${bytesToHex(block.hash())}`)
274+
```
275+
243276
### Consensus Types
244277

245278
### Proof-of-Stake

packages/block/examples/clrequests.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
2+
import { CLRequestType, bytesToHex, createCLRequest, hexToBytes } from '@ethereumjs/util'
3+
import { sha256 } from 'ethereum-cryptography/sha256.js'
4+
5+
import { createBlock, genRequestsRoot } from '../src'
6+
7+
// Enable EIP-7685 to support CLRequests
8+
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7685] })
9+
10+
// Create examples of the three CLRequest types
11+
const createExampleRequests = () => {
12+
// Create a deposit request (type 0)
13+
const depositData = hexToBytes(
14+
'0x00ac842878bb70009552a4cfcad801d6e659c50bd50d7d03306790cb455ce7363c5b6972f0159d170f625a99b2064dbefc010000000000000000000000818ccb1c4eda80270b04d6df822b1e72dd83c3030040597307000000a747f75c72d0cf0d2b52504c7385b516f0523e2f0842416399f42b4aee5c6384a5674f6426b1cc3d0827886fa9b909e616f5c9f61f986013ed2b9bf37071cbae951136265b549f44e3c8e26233c0433e9124b7fd0dc86e82f9fedfc0a179d7690000000000000000',
15+
)
16+
const depositRequest = createCLRequest(depositData)
17+
18+
// Create a withdrawal request (type 1)
19+
const withdrawalData = hexToBytes(
20+
'0x01000000000000000000000000000000000000000001000000000000000000000de0b6b3a7640000',
21+
)
22+
const withdrawalRequest = createCLRequest(withdrawalData)
23+
24+
// Create a consolidation request (type 2)
25+
const consolidationData = hexToBytes('0x020000000100000000000000000000000000000000000001')
26+
const consolidationRequest = createCLRequest(consolidationData)
27+
28+
// CLRequests must be sorted by type (Deposit=0, Withdrawal=1, Consolidation=2)
29+
return [depositRequest, withdrawalRequest, consolidationRequest]
30+
}
31+
32+
// Generate a block with CLRequests
33+
function createBlockWithCLRequests() {
34+
const requests = createExampleRequests()
35+
console.log(`Created ${requests.length} CLRequests:`)
36+
37+
for (const req of requests) {
38+
console.log(
39+
`- Type: ${req.type} (${Object.keys(CLRequestType).find(
40+
(k) => CLRequestType[k as keyof typeof CLRequestType] === req.type,
41+
)})`,
42+
)
43+
}
44+
45+
// Generate the requestsHash by hashing all the CLRequests
46+
const requestsHash = genRequestsRoot(requests, sha256)
47+
console.log(`Generated requestsHash: 0x${bytesToHex(requestsHash)}`)
48+
49+
// Create a block with the CLRequests hash
50+
const block = createBlock({ header: { requestsHash } }, { common })
51+
console.log(`Created block hash: 0x${bytesToHex(block.hash())}`)
52+
53+
return block
54+
}
55+
56+
// Execute
57+
createBlockWithCLRequests()
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
2+
import { CLRequestType, createCLRequest, equalsBytes, hexToBytes } from '@ethereumjs/util'
3+
import { sha256 } from 'ethereum-cryptography/sha256.js'
4+
import { assert, describe, it } from 'vitest'
5+
6+
import { createBlock, genRequestsRoot } from '../src/index.ts'
7+
8+
import type { CLRequest } from '@ethereumjs/util'
9+
10+
describe('[Block]: CLRequests tests', () => {
11+
// Common with EIP-7685 enabled (CLRequests)
12+
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7685] })
13+
14+
function createDepositRequest(): CLRequest<CLRequestType> {
15+
// Example deposit data
16+
const sampleDepositRequest = hexToBytes(
17+
'0x00ac842878bb70009552a4cfcad801d6e659c50bd50d7d03306790cb455ce7363c5b6972f0159d170f625a99b2064dbefc010000000000000000000000818ccb1c4eda80270b04d6df822b1e72dd83c3030040597307000000a747f75c72d0cf0d2b52504c7385b516f0523e2f0842416399f42b4aee5c6384a5674f6426b1cc3d0827886fa9b909e616f5c9f61f986013ed2b9bf37071cbae951136265b549f44e3c8e26233c0433e9124b7fd0dc86e82f9fedfc0a179d7690000000000000000',
18+
)
19+
return createCLRequest(sampleDepositRequest)
20+
}
21+
22+
function createWithdrawalRequest(): CLRequest<CLRequestType> {
23+
// Type 1 (Withdrawal) + example data
24+
const withdrawalData = hexToBytes(
25+
'0x01000000000000000000000000000000000000000001000000000000000000000de0b6b3a7640000',
26+
)
27+
return createCLRequest(withdrawalData)
28+
}
29+
30+
function createConsolidationRequest(): CLRequest<CLRequestType> {
31+
// Type 2 (Consolidation) + example data
32+
const consolidationData = hexToBytes('0x020000000100000000000000000000000000000000000001')
33+
return createCLRequest(consolidationData)
34+
}
35+
36+
it('should create a block with deposit request', () => {
37+
const depositRequest = createDepositRequest()
38+
assert.equal(depositRequest.type, CLRequestType.Deposit, 'should be a deposit request')
39+
40+
const requestsHash = genRequestsRoot([depositRequest], sha256)
41+
const block = createBlock(
42+
{
43+
header: { requestsHash },
44+
},
45+
{ common },
46+
)
47+
48+
assert.isDefined(block.header.requestsHash, 'block should have requestsHash')
49+
assert.isTrue(
50+
equalsBytes(block.header.requestsHash!, requestsHash),
51+
'requestsHash should match the expected value',
52+
)
53+
})
54+
55+
it('should create a block with withdrawal request', () => {
56+
const withdrawalRequest = createWithdrawalRequest()
57+
assert.equal(withdrawalRequest.type, CLRequestType.Withdrawal, 'should be a withdrawal request')
58+
59+
const requestsHash = genRequestsRoot([withdrawalRequest], sha256)
60+
const block = createBlock(
61+
{
62+
header: { requestsHash },
63+
},
64+
{ common },
65+
)
66+
67+
assert.isDefined(block.header.requestsHash, 'block should have requestsHash')
68+
assert.isTrue(
69+
equalsBytes(block.header.requestsHash!, requestsHash),
70+
'requestsHash should match the expected value',
71+
)
72+
})
73+
74+
it('should create a block with consolidation request', () => {
75+
const consolidationRequest = createConsolidationRequest()
76+
assert.equal(
77+
consolidationRequest.type,
78+
CLRequestType.Consolidation,
79+
'should be a consolidation request',
80+
)
81+
82+
const requestsHash = genRequestsRoot([consolidationRequest], sha256)
83+
const block = createBlock(
84+
{
85+
header: { requestsHash },
86+
},
87+
{ common },
88+
)
89+
90+
assert.isDefined(block.header.requestsHash, 'block should have requestsHash')
91+
assert.isTrue(
92+
equalsBytes(block.header.requestsHash!, requestsHash),
93+
'requestsHash should match the expected value',
94+
)
95+
})
96+
97+
it('should create a block with multiple CLRequests', () => {
98+
const depositRequest = createDepositRequest()
99+
const withdrawalRequest = createWithdrawalRequest()
100+
const consolidationRequest = createConsolidationRequest()
101+
102+
// Requests should be sorted by type
103+
const requests = [depositRequest, withdrawalRequest, consolidationRequest]
104+
const requestsHash = genRequestsRoot(requests, sha256)
105+
106+
const block = createBlock(
107+
{
108+
header: { requestsHash },
109+
},
110+
{ common },
111+
)
112+
113+
assert.isDefined(block.header.requestsHash, 'block should have requestsHash')
114+
assert.isTrue(
115+
equalsBytes(block.header.requestsHash!, requestsHash),
116+
'requestsHash should match the expected value',
117+
)
118+
})
119+
120+
it('should validate the requests are sorted by type', () => {
121+
const depositRequest = createDepositRequest()
122+
const withdrawalRequest = createWithdrawalRequest()
123+
124+
// Requests in wrong order should throw
125+
const requests = [withdrawalRequest, depositRequest]
126+
127+
assert.throws(
128+
() => genRequestsRoot(requests, sha256),
129+
'requests are not sorted in ascending order',
130+
'should throw when requests are not sorted by type',
131+
)
132+
})
133+
})

0 commit comments

Comments
 (0)