Skip to content

Commit f7e3cab

Browse files
committed
wip
1 parent 17a9b23 commit f7e3cab

File tree

4 files changed

+226
-45
lines changed

4 files changed

+226
-45
lines changed

src/StdRlp.sol

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.6.0 <0.9.0;
3+
4+
pragma experimental ABIEncoderV2;
5+
6+
import {VmSafe} from "./Vm.sol";
7+
8+
// TODO: Account Fields (CodeHash, Balance, Nonce, StorageRoot).
9+
10+
/// @notice A general-purpose library for working with RLP (Recursive Length Prefix)
11+
/// encoded data in Ethereum.
12+
///
13+
/// @dev This library provides utilities for decoding and working with RLP-encoded
14+
/// data structures. Currently focused on block header parsing, but designed
15+
/// to be extensible for other RLP use cases.
16+
///
17+
/// Block header usage:
18+
/// ```solidity
19+
/// import {stdRlp} from "forge-std/StdRlp.sol";
20+
///
21+
/// stdRlp.BlockHeader memory header = stdRlp.getBlockHeader(block.number);
22+
/// console.log("stateRoot:", header.stateRoot);
23+
///
24+
/// bytes memory rawHeader = vm.getRawBlockHeader(block.number);
25+
/// stdRlp.BlockHeader memory header = stdRlp.toBlockHeader(rawHeader);
26+
/// console.log("stateRoot:", header.stateRoot);
27+
/// ```
28+
library stdRlp {
29+
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
30+
31+
/// @notice Represents a parsed Ethereum block header with all standard fields.
32+
/// @dev Contains all fields from modern Ethereum block headers, including
33+
/// post-merge (baseFeePerGas), post-Shapella (withdrawalsRoot), post-Cancun
34+
/// (blobGasUsed, excessBlobGas, parentBeaconRoot), and post-Dencun (requestsHash) fields.
35+
struct BlockHeader {
36+
bytes32 hash;
37+
bytes32 parentHash;
38+
bytes32 ommersHash;
39+
address beneficiary;
40+
bytes32 stateRoot;
41+
bytes32 transactionsRoot;
42+
bytes32 receiptsRoot;
43+
bytes logsBloom;
44+
uint256 difficulty;
45+
uint256 number;
46+
uint256 gasLimit;
47+
uint256 gasUsed;
48+
uint256 timestamp;
49+
bytes extraData;
50+
bytes32 mixHash;
51+
uint256 nonce;
52+
uint256 baseFeePerGas;
53+
bytes32 withdrawalsRoot;
54+
uint256 blobGasUsed;
55+
uint256 excessBlobGas;
56+
bytes32 parentBeaconRoot;
57+
bytes32 requestsHash;
58+
}
59+
60+
/// @notice Parses a raw RLP-encoded block header into a structured `BlockHeader`.
61+
/// @dev Uses the Foundry cheatcode `vm.fromRlp` to decode the RLP structure.
62+
/// The block hash is computed as the keccak256 of the raw header bytes.
63+
/// Fields are extracted in the order defined by the Ethereum specification.
64+
/// @param rawBlockHeader The RLP-encoded block header bytes.
65+
/// @return blockHeader The parsed block header with all fields populated.
66+
function toBlockHeader(bytes memory rawBlockHeader) internal pure returns (BlockHeader memory blockHeader) {
67+
bytes[] memory fields = vm.fromRlp(rawBlockHeader);
68+
69+
blockHeader.hash = keccak256(rawBlockHeader);
70+
blockHeader.parentHash = bytes32(fields[0]);
71+
blockHeader.ommersHash = bytes32(fields[1]);
72+
blockHeader.beneficiary = address(bytes20(fields[2]));
73+
blockHeader.stateRoot = bytes32(fields[3]);
74+
blockHeader.transactionsRoot = bytes32(fields[4]);
75+
blockHeader.receiptsRoot = bytes32(fields[5]);
76+
blockHeader.logsBloom = fields[6];
77+
blockHeader.difficulty = _toUint(fields[7]);
78+
blockHeader.number = _toUint(fields[8]);
79+
blockHeader.gasLimit = _toUint(fields[9]);
80+
blockHeader.gasUsed = _toUint(fields[10]);
81+
blockHeader.timestamp = _toUint(fields[11]);
82+
blockHeader.extraData = fields[12];
83+
blockHeader.mixHash = bytes32(fields[13]);
84+
blockHeader.nonce = _toUint(fields[14]);
85+
blockHeader.baseFeePerGas = _toUint(fields[15]);
86+
blockHeader.withdrawalsRoot = bytes32(fields[16]);
87+
blockHeader.blobGasUsed = _toUint(fields[17]);
88+
blockHeader.excessBlobGas = _toUint(fields[18]);
89+
blockHeader.parentBeaconRoot = bytes32(fields[19]);
90+
blockHeader.requestsHash = bytes32(fields[20]);
91+
92+
return blockHeader;
93+
}
94+
95+
/// @notice Fetches and parses the block header for a specific block number.
96+
/// @dev Combines `vm.getRawBlockHeader` with `toBlockHeader` for convenience.
97+
/// This is a view function because it reads blockchain state via the vm cheatcode.
98+
/// @param blockNumber The block number to fetch the header for.
99+
/// @return blockHeader The parsed block header with all fields populated.
100+
function getBlockHeader(uint256 blockNumber) internal view returns (BlockHeader memory blockHeader) {
101+
return toBlockHeader(vm.getRawBlockHeader(blockNumber));
102+
}
103+
104+
/// @dev Internal helper to convert variable-length bytes to uint256.
105+
/// Handles RLP-encoded integers by treating the bytes as big-endian
106+
/// and right-shifting to account for shorter lengths.
107+
function _toUint(bytes memory b) internal pure returns (uint256 r) {
108+
unchecked {
109+
return uint256(bytes32(b)) >> (8 * (32 - b.length));
110+
}
111+
}
112+
}

src/Test.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {stdError} from "./StdError.sol";
1818
import {StdInvariant} from "./StdInvariant.sol";
1919
import {stdJson} from "./StdJson.sol";
2020
import {stdMath} from "./StdMath.sol";
21+
import {stdRlp} from "./StdRlp.sol";
2122
import {StdStorage, stdStorage} from "./StdStorage.sol";
2223
import {StdStyle} from "./StdStyle.sol";
2324
import {stdToml} from "./StdToml.sol";

src/Vm.sol

Lines changed: 43 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)