Skip to content

Commit

Permalink
feat: canyon support (#191)
Browse files Browse the repository at this point in the history
* canyon withdrawal formatting

* bump op-geth docker version

* migrate to engine api V2

* make execution payload backwards compatible with v1

* fix get_payload response type

* debug

* fix payload deserialization

* fix payload

* implement blocks v2 gossip

* fix tests

* clippy
  • Loading branch information
ncitron authored Dec 19, 2023
1 parent 35f0f62 commit 8e8e4f1
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 29 deletions.
2 changes: 1 addition & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ services:
<<: *logging

op-geth:
image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101304.1
image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101304.2
container_name: op-geth
profiles:
- op-geth
Expand Down
12 changes: 12 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ pub struct ChainConfig {
pub max_seq_drift: u64,
/// Timestamp of the regolith hardfork
pub regolith_time: u64,
/// Timestamp of the canyon hardfork
pub canyon_time: u64,
/// Network blocktime
#[serde(default = "default_blocktime")]
pub blocktime: u64,
Expand Down Expand Up @@ -252,6 +254,7 @@ impl ChainConfig {
max_seq_drift: 600,
blocktime: 2,
regolith_time: 0,
canyon_time: u64::MAX,
}
}

Expand Down Expand Up @@ -289,6 +292,7 @@ impl ChainConfig {
seq_window_size: 3600,
max_seq_drift: 600,
regolith_time: 1679079600,
canyon_time: 1699981200,
blocktime: 2,
}
}
Expand Down Expand Up @@ -326,6 +330,7 @@ impl ChainConfig {
seq_window_size: 3600,
max_seq_drift: 600,
regolith_time: 0,
canyon_time: 1699981200,
blocktime: 2,
}
}
Expand Down Expand Up @@ -363,6 +368,7 @@ impl ChainConfig {
max_seq_drift: 600,
blocktime: 2,
regolith_time: 0,
canyon_time: u64::MAX,
}
}

Expand Down Expand Up @@ -398,6 +404,7 @@ impl ChainConfig {
seq_window_size: 3600,
max_seq_drift: 600,
regolith_time: 1683219600,
canyon_time: 1699981200,
blocktime: 2,
}
}
Expand Down Expand Up @@ -476,6 +483,7 @@ pub struct ExternalChainConfig {
l1_chain_id: u64,
l2_chain_id: u64,
regolith_time: u64,
canyon_time: u64,
batch_inbox_address: Address,
deposit_contract_address: Address,
l1_system_config_address: Address,
Expand Down Expand Up @@ -537,6 +545,7 @@ impl From<ExternalChainConfig> for ChainConfig {
seq_window_size: external.seq_window_size,
max_seq_drift: external.max_sequencer_drift,
regolith_time: external.regolith_time,
canyon_time: external.canyon_time,
blocktime: external.block_time,
l2_to_l1_message_passer: addr("0x4200000000000000000000000000000000000016"),
}
Expand Down Expand Up @@ -582,6 +591,7 @@ impl From<ChainConfig> for ExternalChainConfig {
l1_chain_id: chain_config.l1_chain_id,
l2_chain_id: chain_config.l2_chain_id,
regolith_time: chain_config.regolith_time,
canyon_time: chain_config.canyon_time,
batch_inbox_address: chain_config.batch_inbox,
deposit_contract_address: chain_config.deposit_contract,
l1_system_config_address: chain_config.system_config_contract,
Expand Down Expand Up @@ -708,6 +718,7 @@ mod test {
"l1_chain_id": 900,
"l2_chain_id": 901,
"regolith_time": 0,
"canyon_time": 0,
"batch_inbox_address": "0xff00000000000000000000000000000000000000",
"deposit_contract_address": "0x6900000000000000000000000000000000000001",
"l1_system_config_address": "0x6900000000000000000000000000000000000009"
Expand Down Expand Up @@ -754,6 +765,7 @@ mod test {
assert_eq!(chain.seq_window_size, 200);
assert_eq!(chain.max_seq_drift, 300);
assert_eq!(chain.regolith_time, 0);
assert_eq!(chain.canyon_time, 0);
assert_eq!(chain.blocktime, 2);
assert_eq!(
chain.l2_to_l1_message_passer,
Expand Down
7 changes: 7 additions & 0 deletions src/derive/stages/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ impl Attributes {
timestamp: l1_info.block_info.timestamp,
});

let withdrawals = if batch.timestamp >= self.config.chain.canyon_time {
Some(Vec::new())
} else {
None
};

let timestamp = U64([batch.timestamp]);
let l1_inclusion_block = Some(batch.l1_inclusion_block);
let seq_number = Some(self.sequence_number);
Expand All @@ -88,6 +94,7 @@ impl Attributes {
transactions,
no_tx_pool: true,
gas_limit: U64([l1_info.system_config.gas_limit.as_u64()]),
withdrawals,
epoch,
l1_inclusion_block,
seq_number,
Expand Down
20 changes: 14 additions & 6 deletions src/engine/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::engine::DEFAULT_AUTH_PORT;
use crate::engine::ENGINE_GET_PAYLOAD_V1;

use super::{
Engine, ExecutionPayload, ForkChoiceUpdate, ForkchoiceState, JwtSecret, PayloadAttributes,
PayloadId, PayloadStatus, ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_NEW_PAYLOAD_V1,
PayloadId, PayloadStatus, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_GET_PAYLOAD_V2,
ENGINE_NEW_PAYLOAD_V2,
};

use super::{JSONRPC_VERSION, STATIC_ID};
Expand Down Expand Up @@ -217,25 +217,33 @@ impl Engine for EngineApi {
};
let forkchoice_state_param = serde_json::to_value(forkchoice_state)?;
let params = vec![forkchoice_state_param, payload_attributes_param];
let res = self.post(ENGINE_FORKCHOICE_UPDATED_V1, params).await?;
let res = self.post(ENGINE_FORKCHOICE_UPDATED_V2, params).await?;
Ok(res)
}

async fn new_payload(&self, execution_payload: ExecutionPayload) -> Result<PayloadStatus> {
let params = vec![serde_json::to_value(execution_payload)?];
let res = self.post(ENGINE_NEW_PAYLOAD_V1, params).await?;
let res = self.post(ENGINE_NEW_PAYLOAD_V2, params).await?;
Ok(res)
}

async fn get_payload(&self, payload_id: PayloadId) -> Result<ExecutionPayload> {
let encoded = format!("{:x}", payload_id);
let padded = format!("0x{:0>16}", encoded);
let params = vec![Value::String(padded)];
let res = self.post(ENGINE_GET_PAYLOAD_V1, params).await?;
Ok(res)
let res = self
.post::<GetPayloadResponse>(ENGINE_GET_PAYLOAD_V2, params)
.await?;
Ok(res.execution_payload)
}
}

#[derive(Debug, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
struct GetPayloadResponse {
execution_payload: ExecutionPayload,
}

#[cfg(test)]
mod tests {
use std::time::SystemTime;
Expand Down
8 changes: 8 additions & 0 deletions src/engine/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ pub struct ExecutionPayload {
pub block_hash: H256,
/// An array of transaction objects where each object is a byte list
pub transactions: Vec<RawTransaction>,
/// 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<()>>,
}

impl TryFrom<Block<Transaction>> for ExecutionPayload {
Expand Down Expand Up @@ -71,6 +74,7 @@ impl TryFrom<Block<Transaction>> for ExecutionPayload {
.into(),
block_hash: value.hash.unwrap(),
transactions: encoded_txs,
withdrawals: Some(Vec::new()),
})
}
}
Expand All @@ -97,6 +101,10 @@ pub struct PayloadAttributes {
/// This field overrides the gas limit used during block-building.
/// If not specified as rollup, a STATUS_INVALID is returned.
pub gas_limit: U64,
/// Beaconchain withdrawals. This exists only for compatibility with L1, and is not used. Prior
/// 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<()>>,
/// The batch epoch number from derivation. This value is not expected by the engine is skipped
/// during serialization and deserialization.
#[serde(skip)]
Expand Down
9 changes: 3 additions & 6 deletions src/engine/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,19 @@ pub const STATIC_ID: u32 = 1;
pub const JSONRPC_VERSION: &str = "2.0";

/// The new payload method string
pub const ENGINE_NEW_PAYLOAD_V1: &str = "engine_newPayloadV1";
// pub const ENGINE_NEW_PAYLOAD_V2: &str = "engine_newPayloadV2";
pub const ENGINE_NEW_PAYLOAD_V2: &str = "engine_newPayloadV2";

/// The new payload timeout
pub const ENGINE_NEW_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(8);

/// The get payload method string
pub const ENGINE_GET_PAYLOAD_V1: &str = "engine_getPayloadV1";
// pub const ENGINE_GET_PAYLOAD_V2: &str = "engine_getPayloadV2";
pub const ENGINE_GET_PAYLOAD_V2: &str = "engine_getPayloadV2";

/// The get payload timeout
pub const ENGINE_GET_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(2);

/// The forkchoice updated method string
pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1";
// pub const ENGINE_FORKCHOICE_UPDATED_V2: &str = "engine_forkchoiceUpdatedV2";
pub const ENGINE_FORKCHOICE_UPDATED_V2: &str = "engine_forkchoiceUpdatedV2";

/// The forkchoice updated timeout
pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_secs(8);
82 changes: 74 additions & 8 deletions src/network/handlers/block_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@ pub struct BlockHandler {
chain_id: u64,
block_sender: Sender<ExecutionPayload>,
unsafe_signer_recv: watch::Receiver<Address>,
blocks_v1_topic: IdentTopic,
blocks_v2_topic: IdentTopic,
}

impl Handler for BlockHandler {
fn handle(&self, msg: Message) -> MessageAcceptance {
tracing::debug!("received block");

match decode_block_msg(msg.data) {
let decoded = if msg.topic == self.blocks_v1_topic.hash() {
decode_block_msg::<ExecutionPayloadV1SSZ>(msg.data)
} else if msg.topic == self.blocks_v2_topic.hash() {
decode_block_msg::<ExecutionPayloadV2SSZ>(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);
Expand All @@ -39,8 +49,8 @@ impl Handler for BlockHandler {
}
}

fn topic(&self) -> TopicHash {
IdentTopic::new(format!("/optimism/{}/0/blocks", self.chain_id)).into()
fn topics(&self) -> Vec<TopicHash> {
vec![self.blocks_v1_topic.hash(), self.blocks_v2_topic.hash()]
}
}

Expand All @@ -55,6 +65,8 @@ impl BlockHandler {
chain_id,
block_sender: sender,
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)),
};

(handler, recv)
Expand Down Expand Up @@ -83,15 +95,19 @@ impl BlockHandler {
}
}

fn decode_block_msg(data: Vec<u8>) -> Result<(ExecutionPayload, Signature, PayloadHash)> {
fn decode_block_msg<T>(data: Vec<u8>) -> Result<(ExecutionPayload, Signature, PayloadHash)>
where
T: SimpleSerialize,
ExecutionPayload: From<T>,
{
let mut decoder = snap::raw::Decoder::new();
let decompressed = decoder.decompress_vec(&data)?;
let sig_data = &decompressed[..65];
let block_data = &decompressed[65..];

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

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

let payload_hash = PayloadHash::from(block_data);
Expand Down Expand Up @@ -129,7 +145,7 @@ type VecAddress = Vector<u8, 20>;
type Transaction = List<u8, 1073741824>;

#[derive(SimpleSerialize, Default)]
struct ExecutionPayloadSSZ {
struct ExecutionPayloadV1SSZ {
pub parent_hash: Bytes32,
pub fee_recipient: VecAddress,
pub state_root: Bytes32,
Expand All @@ -146,8 +162,57 @@ struct ExecutionPayloadSSZ {
pub transactions: List<Transaction, 1048576>,
}

impl From<ExecutionPayloadSSZ> for ExecutionPayload {
fn from(value: ExecutionPayloadSSZ) -> Self {
impl From<ExecutionPayloadV1SSZ> for ExecutionPayload {
fn from(value: ExecutionPayloadV1SSZ) -> 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: None,
}
}
}

#[derive(SimpleSerialize, Default)]
struct ExecutionPayloadV2SSZ {
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>,
}

#[derive(SimpleSerialize, Default)]
struct Withdrawal {
index: u64,
validator_index: u64,
address: VecAddress,
amount: u64,
}

impl From<ExecutionPayloadV2SSZ> for ExecutionPayload {
fn from(value: ExecutionPayloadV2SSZ) -> Self {
Self {
parent_hash: convert_hash(value.parent_hash),
fee_recipient: convert_address(value.fee_recipient),
Expand All @@ -163,6 +228,7 @@ impl From<ExecutionPayloadSSZ> for ExecutionPayload {
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()),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/network/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ pub mod block_handler;

pub trait Handler: Send {
fn handle(&self, msg: Message) -> MessageAcceptance;
fn topic(&self) -> TopicHash;
fn topics(&self) -> Vec<TopicHash>;
}
2 changes: 1 addition & 1 deletion src/network/service/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn start(addr: NetworkAddress, chain_id: u64) -> Result<Receiver<Peer>> {
}
}

sleep(Duration::from_secs(30)).await;
sleep(Duration::from_secs(10)).await;
}
});

Expand Down
Loading

0 comments on commit 8e8e4f1

Please sign in to comment.