Skip to content

Commit 9188019

Browse files
✨ verifyBlockHash + toShortHeader (#1483)
* wip * wip * wip * cleanup * wip * typo * wip * cleanup
1 parent 208e4f3 commit 9188019

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

src/utils/BlockHashLib.sol

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.4;
33

4+
/// @dev Ethereum block header fields relevant to historical MPT proofs.
5+
struct ShortHeader {
6+
bytes32 parentHash;
7+
bytes32 stateRoot;
8+
bytes32 transactionsRoot;
9+
bytes32 receiptsRoot;
10+
bytes32[8] logsBloom;
11+
}
12+
413
/// @notice Library for accessing block hashes way beyond the 256-block limit.
514
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/BlockHashLib.sol)
615
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Blockhash.sol)
716
library BlockHashLib {
17+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
18+
/* CUSTOM ERRORS */
19+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
20+
21+
/// @dev Invalid block hash.
22+
error InvalidBlockHeader();
23+
824
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
925
/* CONSTANTS */
1026
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
@@ -35,4 +51,46 @@ library BlockHashLib {
3551
result := mload(0x00)
3652
}
3753
}
54+
55+
/// @dev Returns whether the hash of a provided RLP-encoded block `header` equals the block hash at `blockNumber`.
56+
function verifyBlockHash(bytes calldata header, uint256 blockNumber)
57+
internal
58+
view
59+
returns (bytes32 result)
60+
{
61+
result = blockHash(blockNumber);
62+
/// @solidity memory-safe-assembly
63+
assembly {
64+
calldatacopy(mload(0x40), header.offset, header.length)
65+
if iszero(eq(result, keccak256(mload(0x40), header.length))) {
66+
mstore(0x00, 0x464db2f8) // InvalidBlockHeader()
67+
revert(0x1c, 0x04)
68+
}
69+
}
70+
}
71+
72+
/// @dev Retrieves the most relevant fields for MPT proofs from an RLP-encoded block `header`.
73+
/// Leading fields are always present and have fixed offsets and lengths.
74+
/// This function allows efficient extraction of these fields from calldata without full RLP decoding.
75+
/// For the specification of field order and lengths, please refer to prefix. 6 of the Ethereum Yellow Paper:
76+
/// (https://ethereum.github.io/yellowpaper/paper.pdf)
77+
/// and the Ethereum Wiki (https://epf.wiki/#/wiki/EL/RLP).
78+
function toShortHeader(bytes calldata header)
79+
internal
80+
pure
81+
returns (ShortHeader memory result)
82+
{
83+
/// @solidity memory-safe-assembly
84+
assembly {
85+
let m := mload(0x40)
86+
let o := add(header.offset, sub(byte(0, calldataload(header.offset)), 0xF6))
87+
mstore(result, calldataload(add(1, o))) // parentHash
88+
mstore(add(0x20, result), calldataload(add(88, o))) // stateRoot
89+
mstore(add(0x40, result), calldataload(add(121, o))) // transactionsRoot
90+
mstore(add(0x60, result), calldataload(add(154, o))) // receiptsRoot
91+
mstore(add(0x80, result), m) // logsBloom
92+
calldatacopy(m, add(189, o), 0x100)
93+
mstore(0x40, add(0x100, m)) // Allocate the memory.
94+
}
95+
}
3896
}

test/BlockHashLib.t.sol

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.4;
33

44
import "./utils/SoladyTest.sol";
5-
import {BlockHashLib} from "../src/utils/BlockHashLib.sol";
5+
import {BlockHashLib, ShortHeader} from "../src/utils/BlockHashLib.sol";
66

77
contract BlockHashLibTest is SoladyTest {
88
uint256 internal startingBlock;
@@ -12,6 +12,15 @@ contract BlockHashLibTest is SoladyTest {
1212
bytes private constant _HISTORY_STORAGE_BYTECODE =
1313
hex"3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500";
1414

15+
// cast block 23270177 --raw
16+
// vm.getRawBlockHeader(23270177)
17+
bytes private constant _ETH_BLOCK_23270177 =
18+
hex"f9027da01581f4448b16694d5a728161cd65f8c80b88f5352a6f5bd2d2315b970582958da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794dadb0d80178819f2319190d340ce9a924f783711a010d2afa5dabcf2dbfe3aa82b758427938e07880bd6fef3c82c404d0dd7c3f0f3a0f81230c715a462c827898bf2e337982907a7af90e5be20f911785bda05dab93ca0740f11bc75cf25e40d78d892d2e03083eaa573e5b4c26913fcc1b833db854c94b9010085f734fb06ea8fe377abbcb2e27f9ac99751ba817dc327327db101fd76f964ed0b7ca161f148fc165b9e5b575dc7473f17f4b8ebbf4a7b02b3e1e642197f27b2af54680834449abaf833619ac7d18afb50b19d5f6944dca0dc952edfdd9837573783c339ee6a36353ce6e536eaaf29fcd569c426091d4e24568dc353347f98c74fb6f8c91d68d358467c437563f66566377fe6c3f9e8301dbeb5fc7e7adee7a85ef5f8fa905cedbaf26601e21ba91646cac4034601e51d889d49739ee6990943a6a41927660f68e1f50b9f9209ee29551a7dae478d88e0547eefc83334ea770bb6fbac620fc47479c2c59389622bf32f55e36a75e56a5fc47c38bf8ef211fc0e8084016313218402af50e883fc53b78468b5ea9b974275696c6465724e657420284e65746865726d696e6429a0580ca94e91c0e7aef26ffb0c86f6ae48ef40df6dd1629f203a1930e0ce0be9d188000000000000000084479c1e2aa00345740e1b79edb2fbb3a20220e1a497ea9bb82aaba7dc7a881f7f3cae8a8ea38080a06675ad2a40134499a753924a04b75898ae09efc6fba6b3d7a506203042cb7611a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
19+
20+
// keccak256(_ETH_BLOCK_23270177)
21+
bytes32 private constant _ETH_BLOCK_HASH_23270177 =
22+
0x5def79dc43d588fafa396f3fbf0bcfb9bf83eaf8003f4508a626b6d3e806b29f;
23+
1524
function testBlockHash(
1625
uint256 simulationBlockNumber,
1726
uint256 queryBlockNumber,
@@ -57,4 +66,55 @@ contract BlockHashLibTest is SoladyTest {
5766
result := mload(0x00)
5867
}
5968
}
69+
70+
function beforeTestSetup(bytes4 selector) public pure returns (bytes[] memory cd) {
71+
if (selector == this.testToShortHeader.selector) {
72+
cd = new bytes[](1);
73+
cd[0] = abi.encodeWithSelector(this.checkToShortHeader.selector, _ETH_BLOCK_23270177);
74+
}
75+
if (selector == this.testVerifyBlockHash.selector) {
76+
cd = new bytes[](1);
77+
cd[0] = abi.encodeWithSelector(this.checkVerifyBlockHash.selector, _ETH_BLOCK_23270177);
78+
}
79+
}
80+
81+
function checkToShortHeader(bytes calldata h) public {
82+
ShortHeader memory expected = ShortHeader({
83+
parentHash: 0x1581f4448b16694d5a728161cd65f8c80b88f5352a6f5bd2d2315b970582958d,
84+
stateRoot: 0x10d2afa5dabcf2dbfe3aa82b758427938e07880bd6fef3c82c404d0dd7c3f0f3,
85+
transactionsRoot: 0xf81230c715a462c827898bf2e337982907a7af90e5be20f911785bda05dab93c,
86+
receiptsRoot: 0x740f11bc75cf25e40d78d892d2e03083eaa573e5b4c26913fcc1b833db854c94,
87+
logsBloom: [
88+
bytes32(0x85f734fb06ea8fe377abbcb2e27f9ac99751ba817dc327327db101fd76f964ed), // lol
89+
0x0b7ca161f148fc165b9e5b575dc7473f17f4b8ebbf4a7b02b3e1e642197f27b2,
90+
0xaf54680834449abaf833619ac7d18afb50b19d5f6944dca0dc952edfdd983757,
91+
0x3783c339ee6a36353ce6e536eaaf29fcd569c426091d4e24568dc353347f98c7,
92+
0x4fb6f8c91d68d358467c437563f66566377fe6c3f9e8301dbeb5fc7e7adee7a8,
93+
0x5ef5f8fa905cedbaf26601e21ba91646cac4034601e51d889d49739ee6990943,
94+
0xa6a41927660f68e1f50b9f9209ee29551a7dae478d88e0547eefc83334ea770b,
95+
0xb6fbac620fc47479c2c59389622bf32f55e36a75e56a5fc47c38bf8ef211fc0e
96+
]
97+
});
98+
99+
ShortHeader memory actual = BlockHashLib.toShortHeader(h);
100+
assertEq(actual.parentHash, expected.parentHash, "parentHash");
101+
assertEq(actual.stateRoot, expected.stateRoot, "stateRoot");
102+
assertEq(actual.transactionsRoot, expected.transactionsRoot, "transactionsRoot");
103+
assertEq(actual.receiptsRoot, expected.receiptsRoot, "receiptsRoot");
104+
assertEq(
105+
keccak256(abi.encodePacked(actual.logsBloom)),
106+
keccak256(abi.encodePacked(expected.logsBloom)),
107+
"logsBloom"
108+
);
109+
}
110+
111+
function checkVerifyBlockHash(bytes calldata h) public {
112+
vm.roll(23270177 + 1);
113+
vm.setBlockhash(23270177, _ETH_BLOCK_HASH_23270177);
114+
assertEq(BlockHashLib.verifyBlockHash(h, 23270177), _ETH_BLOCK_HASH_23270177);
115+
}
116+
117+
function testToShortHeader() public view {}
118+
119+
function testVerifyBlockHash() public view {}
60120
}

0 commit comments

Comments
 (0)