@@ -19,16 +19,107 @@ pragma solidity ^0.8.9;
1919/// @title Steel Library
2020/// @notice This library provides a collection of utilities to work with Steel commitments in Solidity.
2121library Steel {
22- /// @notice A Commitment struct representing a block number and its block hash.
22+ /// @notice Represents a commitment to a specific block in the blockchain.
23+ /// @dev The `blockID` encodes both the block identifier (block number or timestamp) and the version.
24+ /// @dev The `blockDigest` is the block hash or beacon block root, used for validation.
2325 struct Commitment {
24- uint256 blockNumber; // Block number at which the commitment was made.
25- bytes32 blockHash; // Hash of the block at the specified block number.
26+ uint256 blockID;
27+ bytes32 blockDigest;
2628 }
2729
30+ /// @notice The version of the Commitment is incorrect.
31+ error InvalidCommitmentVersion ();
32+
33+ /// @notice The Commitment is too old and can no longer be validated.
34+ error CommitmentTooOld ();
35+
2836 /// @notice Validates if the provided Commitment matches the block hash of the given block number.
2937 /// @param commitment The Commitment struct to validate.
30- /// @return isValid True if the commitment's block hash matches the block hash of the block number, false otherwise.
31- function validateCommitment (Commitment memory commitment ) internal view returns (bool isValid ) {
32- return commitment.blockHash == blockhash (commitment.blockNumber);
38+ /// @return True if the commitment's block hash matches the block hash of the block number, false otherwise.
39+ function validateCommitment (Commitment memory commitment ) internal view returns (bool ) {
40+ (uint240 blockID , uint16 version ) = Encoding.decodeVersionedID (commitment.blockID);
41+ if (version == 0 ) {
42+ return validateBlockCommitment (blockID, commitment.blockDigest);
43+ } else if (version == 1 ) {
44+ return validateBeaconCommitment (blockID, commitment.blockDigest);
45+ } else {
46+ revert InvalidCommitmentVersion ();
47+ }
48+ }
49+
50+ /// @notice Validates if the provided block commitment matches the block hash of the given block number.
51+ /// @param blockNumber The block number to compare against.
52+ /// @param blockHash The block hash to validate.
53+ /// @return True if the block's block hash matches the block hash, false otherwise.
54+ function validateBlockCommitment (uint256 blockNumber , bytes32 blockHash ) internal view returns (bool ) {
55+ if (block .number - blockNumber > 256 ) {
56+ revert CommitmentTooOld ();
57+ }
58+ return blockHash == blockhash (blockNumber);
59+ }
60+
61+ /// @notice Validates if the provided beacon commitment matches the block root of the given timestamp.
62+ /// @param blockTimestamp The timestamp to compare against.
63+ /// @param blockRoot The block root to validate.
64+ /// @return True if the block's block root matches the block root, false otherwise.
65+ function validateBeaconCommitment (uint256 blockTimestamp , bytes32 blockRoot ) internal view returns (bool ) {
66+ if (block .timestamp - blockTimestamp > 12 * 8191 ) {
67+ revert CommitmentTooOld ();
68+ }
69+ return blockRoot == Beacon.blockRoot (blockTimestamp);
70+ }
71+ }
72+
73+ /// @title Beacon Library
74+ library Beacon {
75+ /// @notice The address of the Beacon roots contract.
76+ /// @dev https://eips.ethereum.org/EIPS/eip-4788
77+ address internal constant BEACON_ROOTS_ADDRESS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02 ;
78+
79+ /// @notice The Beacon block root could not be found as the next block has not been issued yet.
80+ error NoParentBeaconBlock ();
81+
82+ /// @notice Attempts to find the root of the Beacon block with the given timestamp.
83+ /// @dev Since the Beacon roots contract only returns the parent Beacon block’s root, we need to find the next
84+ /// Beacon block instead. This is done by adding the block time of 12s until a value is returned.
85+ function blockRoot (uint256 timestamp ) internal view returns (bytes32 root ) {
86+ uint256 blockTimestamp = block .timestamp ;
87+ while (true ) {
88+ timestamp += 12 ; // Beacon block time is 12 seconds
89+ if (timestamp > blockTimestamp) revert NoParentBeaconBlock ();
90+
91+ (bool success , bytes memory result ) = BEACON_ROOTS_ADDRESS.staticcall (abi.encode (timestamp));
92+ if (success) {
93+ return abi.decode (result, (bytes32 ));
94+ }
95+ }
96+ }
97+ }
98+
99+ /// @title Encoding Library
100+ library Encoding {
101+ /// @notice Encodes a version and ID into a single uint256 value.
102+ /// @param id The base ID to be encoded, limited by 240 bits (or the maximum value of a uint240).
103+ /// @param version The version number to be encoded, limited by 16 bits (or the maximum value of a uint16).
104+ /// @return Returns a single uint256 value that contains both the `id` and the `version` encoded into it.
105+ function encodeVersionedID (uint240 id , uint16 version ) internal pure returns (uint256 ) {
106+ uint256 encoded;
107+ assembly {
108+ encoded := or (shl (240 , version), id)
109+ }
110+ return encoded;
111+ }
112+
113+ /// @notice Decodes a version and ID from a single uint256 value.
114+ /// @param id The single uint256 value to be decoded.
115+ /// @return Returns two values: a uint240 for the original base ID and a uint16 for the version number encoded into it.
116+ function decodeVersionedID (uint256 id ) internal pure returns (uint240 , uint16 ) {
117+ uint240 decoded;
118+ uint16 version;
119+ assembly {
120+ decoded := and (id, 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff )
121+ version := shr (240 , id)
122+ }
123+ return (decoded, version);
33124 }
34125}
0 commit comments