|
1 | | -use alloy_primitives::{B256, Bytes}; |
| 1 | +use alloy_primitives::private::alloy_rlp::Encodable; |
| 2 | +use alloy_primitives::{B256, Bytes, keccak256}; |
2 | 3 | use futures::{StreamExt as _, stream}; |
3 | 4 | use moka::future::Cache; |
4 | 5 |
|
5 | 6 | use alloy_rpc_types_engine::{ExecutionPayload, ExecutionPayloadV3, PayloadId}; |
6 | 7 | use op_alloy_rpc_types_engine::{ |
7 | 8 | OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4, OpExecutionPayloadV4, |
| 9 | + OpPayloadAttributes, |
8 | 10 | }; |
9 | 11 |
|
10 | 12 | const CACHE_SIZE: u64 = 100; |
@@ -266,3 +268,70 @@ impl PayloadTraceContext { |
266 | 268 | } |
267 | 269 | } |
268 | 270 | } |
| 271 | + |
| 272 | +/// Generates the payload id for the configured payload from the [`OpPayloadAttributes`]. |
| 273 | +/// |
| 274 | +/// Returns an 8-byte identifier by hashing the payload components with sha256 hash. |
| 275 | +pub(crate) fn payload_id_optimism( |
| 276 | + parent: &B256, |
| 277 | + attributes: &OpPayloadAttributes, |
| 278 | + payload_version: u8, |
| 279 | +) -> PayloadId { |
| 280 | + use sha2::Digest; |
| 281 | + let mut hasher = sha2::Sha256::new(); |
| 282 | + hasher.update(parent.as_slice()); |
| 283 | + hasher.update(&attributes.payload_attributes.timestamp.to_be_bytes()[..]); |
| 284 | + hasher.update(attributes.payload_attributes.prev_randao.as_slice()); |
| 285 | + hasher.update( |
| 286 | + attributes |
| 287 | + .payload_attributes |
| 288 | + .suggested_fee_recipient |
| 289 | + .as_slice(), |
| 290 | + ); |
| 291 | + if let Some(withdrawals) = &attributes.payload_attributes.withdrawals { |
| 292 | + let mut buf = Vec::new(); |
| 293 | + withdrawals.encode(&mut buf); |
| 294 | + hasher.update(buf); |
| 295 | + } |
| 296 | + |
| 297 | + if let Some(parent_beacon_block) = attributes.payload_attributes.parent_beacon_block_root { |
| 298 | + hasher.update(parent_beacon_block); |
| 299 | + } |
| 300 | + |
| 301 | + let no_tx_pool = attributes.no_tx_pool.unwrap_or_default(); |
| 302 | + if no_tx_pool |
| 303 | + || attributes |
| 304 | + .transactions |
| 305 | + .as_ref() |
| 306 | + .is_some_and(|txs| !txs.is_empty()) |
| 307 | + { |
| 308 | + hasher.update([no_tx_pool as u8]); |
| 309 | + let txs_len = attributes |
| 310 | + .transactions |
| 311 | + .as_ref() |
| 312 | + .map(|txs| txs.len()) |
| 313 | + .unwrap_or_default(); |
| 314 | + hasher.update(&txs_len.to_be_bytes()[..]); |
| 315 | + if let Some(txs) = &attributes.transactions { |
| 316 | + for tx in txs { |
| 317 | + // we have to just hash the bytes here because otherwise we would need to decode |
| 318 | + // the transactions here which really isn't ideal |
| 319 | + let tx_hash = keccak256(tx); |
| 320 | + // maybe we can try just taking the hash and not decoding |
| 321 | + hasher.update(tx_hash) |
| 322 | + } |
| 323 | + } |
| 324 | + } |
| 325 | + |
| 326 | + if let Some(gas_limit) = attributes.gas_limit { |
| 327 | + hasher.update(gas_limit.to_be_bytes()); |
| 328 | + } |
| 329 | + |
| 330 | + if let Some(eip_1559_params) = attributes.eip_1559_params { |
| 331 | + hasher.update(eip_1559_params.as_slice()); |
| 332 | + } |
| 333 | + |
| 334 | + let mut out = hasher.finalize(); |
| 335 | + out[0] = payload_version; |
| 336 | + PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length")) |
| 337 | +} |
0 commit comments