Skip to content

Commit

Permalink
wip: parent beacon block root in v3 block handler
Browse files Browse the repository at this point in the history
  • Loading branch information
merklefruit committed Feb 18, 2024
1 parent 3ab65fb commit ec1a61c
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 22 deletions.
3 changes: 0 additions & 3 deletions src/derive/stages/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ impl Attributes {
let transactions = Some(self.derive_transactions(input, l1_info));
let suggested_fee_recipient = SystemAccounts::default().fee_vault;

let parent_beacon_block_root = None; // TODO(nicolas)

PayloadAttributes {
timestamp,
prev_randao,
Expand All @@ -108,7 +106,6 @@ impl Attributes {
epoch,
l1_inclusion_block,
seq_number,
parent_beacon_block_root,
}
}

Expand Down
8 changes: 6 additions & 2 deletions src/engine/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ pub struct ExecutionPayload {
/// An array of beaconchain withdrawals. Always empty as this exists only for L1 compatibility
#[serde(skip_serializing_if = "Option::is_none")]
pub withdrawals: Option<Vec<()>>,
/// None if not present (pre-Ecotone)
pub blob_gas_used: Option<U64>,
/// None if not present (pre-Ecotone)
pub excess_blob_gas: Option<U64>,
}

impl TryFrom<Block<Transaction>> for ExecutionPayload {
Expand Down Expand Up @@ -76,6 +80,8 @@ impl TryFrom<Block<Transaction>> for ExecutionPayload {
block_hash: value.hash.unwrap(),
transactions: encoded_txs,
withdrawals: Some(Vec::new()),
blob_gas_used: value.blob_gas_used.map(|v| v.as_u64().into()),
excess_blob_gas: value.excess_blob_gas.map(|v| v.as_u64().into()),
})
}
}
Expand Down Expand Up @@ -106,8 +112,6 @@ pub struct PayloadAttributes {
/// to Canyon, this value is always None. After Canyon it is an empty array. Note that we use
/// the () type here since we never have a non empty array.
pub withdrawals: Option<Vec<()>>,
/// Optional extension enabled in Ecotone and later, containing the block root of the parent beacon block.
pub parent_beacon_block_root: Option<H256>,
/// The batch epoch number from derivation. This value is not expected by the engine is skipped
/// during serialization and deserialization.
#[serde(skip)]
Expand Down
5 changes: 5 additions & 0 deletions src/l1/l1_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub struct L1BlockInfo {
pub base_fee: U256,
/// L1 mix hash (prevrandao)
pub mix_hash: H256,
/// Post-Ecotone beacon block root
pub parent_beacon_block_root: Option<H256>,
}

impl TryFrom<&Block<Transaction>> for L1BlockInfo {
Expand All @@ -52,12 +54,15 @@ impl TryFrom<&Block<Transaction>> for L1BlockInfo {

let mix_hash = value.mix_hash.ok_or(eyre::eyre!("block not included"))?;

let parent_beacon_block_root = value.parent_beacon_block_root;

Ok(L1BlockInfo {
number,
hash,
timestamp,
base_fee,
mix_hash,
parent_beacon_block_root,
})
}
}
122 changes: 105 additions & 17 deletions src/network/handlers/block_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ pub struct BlockHandler {
blocks_v1_topic: IdentTopic,
/// The libp2p topic for Canyon/Delta blocks: `/optimism/{chain_id}/1/blocks`
blocks_v2_topic: IdentTopic,
blocks_v3_topic: IdentTopic,
}

struct ExecutionPayloadEnvelope {
parent_beacon_block_root: Option<H256>,
payload: ExecutionPayload,
signature: Signature,
hash: PayloadHash,
}

impl Handler for BlockHandler {
Expand All @@ -35,14 +43,16 @@ impl Handler for BlockHandler {
decode_block_msg::<ExecutionPayloadV1SSZ>(msg.data)
} else if msg.topic == self.blocks_v2_topic.hash() {
decode_block_msg::<ExecutionPayloadV2SSZ>(msg.data)
} else if msg.topic == self.blocks_v3_topic.hash() {
decode_ecotone_block_msg(msg.data)
} else {
return MessageAcceptance::Reject;
};

match decoded {
Ok((payload, signature, payload_hash)) => {
if self.block_valid(&payload, signature, payload_hash) {
_ = self.block_sender.send(payload);
Ok(envelope) => {
if self.block_valid(&envelope) {
_ = self.block_sender.send(envelope.payload);
MessageAcceptance::Accept
} else {
tracing::warn!("invalid unsafe block");
Expand Down Expand Up @@ -76,6 +86,7 @@ impl BlockHandler {
unsafe_signer_recv: unsafe_recv,
blocks_v1_topic: IdentTopic::new(format!("/optimism/{}/0/blocks", chain_id)),
blocks_v2_topic: IdentTopic::new(format!("/optimism/{}/1/blocks", chain_id)),
blocks_v3_topic: IdentTopic::new(format!("/optimism/{}/2/blocks", chain_id)),
};

(handler, recv)
Expand All @@ -84,31 +95,26 @@ impl BlockHandler {
/// Determines if a block is valid.
///
/// True if the block is less than 1 minute old, and correctly signed by the unsafe block signer.
fn block_valid(
&self,
payload: &ExecutionPayload,
sig: Signature,
payload_hash: PayloadHash,
) -> bool {
fn block_valid(&self, envelope: &ExecutionPayloadEnvelope) -> bool {
let current_timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();

let is_future = payload.timestamp.as_u64() > current_timestamp + 5;
let is_past = payload.timestamp.as_u64() < current_timestamp - 60;
let is_future = envelope.payload.timestamp.as_u64() > current_timestamp + 5;
let is_past = envelope.payload.timestamp.as_u64() < current_timestamp - 60;
let time_valid = !(is_future || is_past);

let msg = payload_hash.signature_message(self.chain_id);
let msg = envelope.hash.signature_message(self.chain_id);
let block_signer = *self.unsafe_signer_recv.borrow();
let sig_valid = sig.verify(msg, block_signer).is_ok();
let sig_valid = envelope.signature.verify(msg, block_signer).is_ok();

time_valid && sig_valid
}
}

/// Decodes a sequence of bytes to a tuple of ([ExecutionPayload], [Signature], [PayloadHash])
fn decode_block_msg<T>(data: Vec<u8>) -> Result<(ExecutionPayload, Signature, PayloadHash)>
/// Decodes a sequence of bytes to an [ExecutionPayloadEnvelope]
fn decode_block_msg<T>(data: Vec<u8>) -> Result<ExecutionPayloadEnvelope>
where
T: SimpleSerialize,
ExecutionPayload: From<T>,
Expand All @@ -123,9 +129,42 @@ where
let payload: T = deserialize(block_data)?;
let payload: ExecutionPayload = ExecutionPayload::from(payload);

let payload_hash = PayloadHash::from(block_data);
let hash = PayloadHash::from(block_data);

Ok(ExecutionPayloadEnvelope {
parent_beacon_block_root: None,
signature,
payload,
hash,
})
}

/// The Ecotone V3 block topic encoding includes the parent beacon block root
/// as described in the [specs].
///
/// [specs]: https://specs.optimism.io/protocol/rollup-node-p2p.html#block-encoding
fn decode_ecotone_block_msg(data: Vec<u8>) -> Result<ExecutionPayloadEnvelope> {
let mut decoder = snap::raw::Decoder::new();
let decompressed = decoder.decompress_vec(&data)?;
let sig_data = &decompressed[..65];
let parent_beacon_block_root = &decompressed[65..97];
let block_data = &decompressed[97..];

let signature = Signature::try_from(sig_data)?;

let parent_beacon_block_root = Some(H256::from_slice(parent_beacon_block_root));

let payload: ExecutionPayloadV3SSZ = deserialize(block_data)?;
let payload = ExecutionPayload::from(payload);

let hash = PayloadHash::from(block_data);

Ok((payload, signature, payload_hash))
Ok(ExecutionPayloadEnvelope {
parent_beacon_block_root,
signature,
payload,
hash,
})
}

/// Represents the Keccak256 hash of the block
Expand Down Expand Up @@ -214,6 +253,8 @@ impl From<ExecutionPayloadV1SSZ> for ExecutionPayload {
block_hash: convert_hash(value.block_hash),
transactions: convert_tx_list(value.transactions),
withdrawals: None,
blob_gas_used: None,
excess_blob_gas: None,
}
}
}
Expand Down Expand Up @@ -286,6 +327,53 @@ impl From<ExecutionPayloadV2SSZ> for ExecutionPayload {
block_hash: convert_hash(value.block_hash),
transactions: convert_tx_list(value.transactions),
withdrawals: Some(Vec::new()),
blob_gas_used: None,
excess_blob_gas: None,
}
}
}

#[derive(SimpleSerialize, Default)]
struct ExecutionPayloadV3SSZ {
pub parent_hash: Bytes32,
pub fee_recipient: VecAddress,
pub state_root: Bytes32,
pub receipts_root: Bytes32,
pub logs_bloom: Vector<u8, 256>,
pub prev_randao: Bytes32,
pub block_number: u64,
pub gas_limit: u64,
pub gas_used: u64,
pub timestamp: u64,
pub extra_data: List<u8, 32>,
pub base_fee_per_gas: U256,
pub block_hash: Bytes32,
pub transactions: List<Transaction, 1048576>,
pub withdrawals: List<Withdrawal, 16>,
pub blob_gas_used: u64,
pub excess_blob_gas: u64,
}

impl From<ExecutionPayloadV3SSZ> for ExecutionPayload {
fn from(value: ExecutionPayloadV3SSZ) -> Self {
Self {
parent_hash: convert_hash(value.parent_hash),
fee_recipient: convert_address(value.fee_recipient),
state_root: convert_hash(value.state_root),
receipts_root: convert_hash(value.receipts_root),
logs_bloom: convert_byte_vector(value.logs_bloom),
prev_randao: convert_hash(value.prev_randao),
block_number: value.block_number.into(),
gas_limit: value.gas_limit.into(),
gas_used: value.gas_used.into(),
timestamp: value.timestamp.into(),
extra_data: convert_byte_list(value.extra_data),
base_fee_per_gas: convert_uint(value.base_fee_per_gas),
block_hash: convert_hash(value.block_hash),
transactions: convert_tx_list(value.transactions),
withdrawals: Some(Vec::new()),
blob_gas_used: Some(value.blob_gas_used.into()),
excess_blob_gas: Some(value.excess_blob_gas.into()),
}
}
}
Expand Down

0 comments on commit ec1a61c

Please sign in to comment.