// SPDX-License-Identifier: MIT pragma solidity 0.8.36; /// @notice Minimal canonical RLP reader for post-merge Ethereum block headers. library EthereumBlockHeader { error InvalidRlp(); struct Item { uint256 payloadOffset; uint256 payloadLength; uint256 totalLength; bool isList; } function parse(bytes calldata encoded) internal pure returns (uint64 blockNumber, uint64 timestamp, bytes32 prevRandao) { Item memory header = _item(encoded, 0); if (!header.isList || header.totalLength != encoded.length) revert InvalidRlp(); uint256 cursor = header.payloadOffset; uint256 end = cursor + header.payloadLength; uint256 field; bool foundNumber; bool foundTimestamp; bool foundPrevRandao; while (cursor < end) { Item memory value = _item(encoded, cursor); if (cursor + value.totalLength > end) revert InvalidRlp(); if (field == 8) { blockNumber = _uint64(encoded, value); foundNumber = true; } else if (field == 11) { timestamp = _uint64(encoded, value); foundTimestamp = true; } else if (field == 13) { if (value.isList || value.payloadLength != 32) revert InvalidRlp(); bytes32 extracted; assembly ("memory-safe") { extracted := calldataload(add(encoded.offset, mload(value))) } prevRandao = extracted; foundPrevRandao = true; } cursor += value.totalLength; field += 1; } if (cursor != end || !foundNumber || !foundTimestamp || !foundPrevRandao) { revert InvalidRlp(); } } function _uint64(bytes calldata encoded, Item memory item) private pure returns (uint64 value) { if (item.isList || item.payloadLength == 0 || item.payloadLength > 8) revert InvalidRlp(); uint256 result = 0; for (uint256 index; index < item.payloadLength; ++index) { uint8 next = uint8(encoded[item.payloadOffset + index]); if (index == 0 && next == 0) revert InvalidRlp(); result = (result << 8) | next; } value = uint64(result); } function _item(bytes calldata encoded, uint256 offset) private pure returns (Item memory item) { if (offset >= encoded.length) revert InvalidRlp(); uint8 prefix = uint8(encoded[offset]); if (prefix <= 0x7f) { item = Item(offset, 1, 1, false); } else if (prefix <= 0xb7) { uint256 length = prefix - 0x80; item = Item(offset + 1, length, length + 1, false); if (length == 1 && offset + 1 < encoded.length && uint8(encoded[offset + 1]) <= 0x7f) { revert InvalidRlp(); } } else if (prefix <= 0xbf) { uint256 lengthOfLength = prefix - 0xb7; uint256 length = _length(encoded, offset + 1, lengthOfLength); if (length <= 55) revert InvalidRlp(); item = Item(offset + 1 + lengthOfLength, length, 1 + lengthOfLength + length, false); } else if (prefix <= 0xf7) { uint256 length = prefix - 0xc0; item = Item(offset + 1, length, length + 1, true); } else { uint256 lengthOfLength = prefix - 0xf7; uint256 length = _length(encoded, offset + 1, lengthOfLength); if (length <= 55) revert InvalidRlp(); item = Item(offset + 1 + lengthOfLength, length, 1 + lengthOfLength + length, true); } if (item.payloadOffset + item.payloadLength > encoded.length) revert InvalidRlp(); } function _length(bytes calldata encoded, uint256 offset, uint256 count) private pure returns (uint256 value) { if (count == 0 || count > 8 || offset + count > encoded.length) revert InvalidRlp(); for (uint256 index; index < count; ++index) { uint8 next = uint8(encoded[offset + index]); if (index == 0 && next == 0) revert InvalidRlp(); value = (value << 8) | next; } } }