Skip to content

Commit a937efd

Browse files
authored
feat: Flashtestations (#39)
1 parent d6597c9 commit a937efd

File tree

12 files changed

+1102
-26
lines changed

12 files changed

+1102
-26
lines changed

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
22
members = [
3-
".", # The main rblib library
3+
".", # The main rblib library
44
"tools/fbutil",
55
]
66
resolver = "2"
@@ -34,7 +34,7 @@ jemalloc = ["rblib/jemalloc"]
3434
debug = ["tokio/full", "tokio/tracing", "dep:console-subscriber"]
3535

3636
[dependencies]
37-
rblib = { git = "https://github.com/flashbots/rblib", rev = "198cbe65e59f7467c756d5ba6422155c98402291" }
37+
rblib = { git = "https://github.com/flashbots/rblib", rev = "b0e1ba3cfba5e190479e6dbcc95889422f0444ad" }
3838

3939
futures = "0.3"
4040
tokio = "1.46"
@@ -44,9 +44,11 @@ eyre = "0.6"
4444
moka = "0.12"
4545
tracing = "0.1"
4646
clap = { version = "4.4", features = ["derive", "env", "string"] }
47+
reqwest = "0.12.24"
4748
serde = { version = "1.0", features = ["derive"] }
4849
serde_json = "1.0"
4950
serde_yaml = "0.9.33"
51+
itertools = "0.14.0"
5052

5153
url = "2.5.2"
5254
rand = "0.9"
@@ -75,7 +77,7 @@ console-subscriber = { version = "0.4", optional = true }
7577
tracing-subscriber = "0.3.20"
7678

7779
[dev-dependencies]
78-
rblib = { git = "https://github.com/flashbots/rblib", rev = "198cbe65e59f7467c756d5ba6422155c98402291", features = [
80+
rblib = { git = "https://github.com/flashbots/rblib", rev = "b0e1ba3cfba5e190479e6dbcc95889422f0444ad", features = [
7981
"test-utils",
8082
] }
8183

src/args.rs

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
//! Command line interface extensions for the Flashblocks builder.
22
33
use {
4-
crate::playground::PlaygroundOptions,
4+
crate::{
5+
flashtestations::FlashtestationsArgs,
6+
playground::PlaygroundOptions,
7+
signer::BuilderSigner,
8+
},
59
clap::{CommandFactory, FromArgMatches, Parser},
610
core::{net::SocketAddr, time::Duration},
7-
derive_more::{Deref, From, FromStr, Into},
811
eyre::{Result, eyre},
9-
rblib::{
10-
alloy::signers::local::PrivateKeySigner,
11-
reth::optimism::{
12-
cli::{Cli as OpCli, chainspec::OpChainSpecParser, commands::Commands},
13-
node::args::RollupArgs,
14-
},
12+
rblib::reth::optimism::{
13+
cli::{Cli as OpCli, chainspec::OpChainSpecParser, commands::Commands},
14+
node::args::RollupArgs,
1515
},
1616
std::path::PathBuf,
1717
};
@@ -50,6 +50,9 @@ pub struct BuilderArgs {
5050

5151
#[command(flatten)]
5252
pub flashblocks_args: FlashblocksArgs,
53+
54+
#[command(flatten)]
55+
pub flashtestations: FlashtestationsArgs,
5356
}
5457

5558
#[derive(Debug, Clone, PartialEq, Eq, clap::Args)]
@@ -118,20 +121,6 @@ impl FlashblocksArgs {
118121
}
119122
}
120123

121-
/// This type is used to store the builder's secret key for signing the last
122-
/// transaction in the block.
123-
#[derive(Debug, Clone, Deref, FromStr, Into, From)]
124-
pub struct BuilderSigner {
125-
signer: PrivateKeySigner,
126-
}
127-
128-
impl PartialEq for BuilderSigner {
129-
fn eq(&self, other: &Self) -> bool {
130-
self.signer.address() == other.signer.address()
131-
}
132-
}
133-
impl Eq for BuilderSigner {}
134-
135124
/// This trait is used to extend Reth's CLI with additional functionality that
136125
/// are specific to the OP builder, such as populating default values for CLI
137126
/// arguments when running in the playground mode or checking the builder mode.

src/flashtestations/args.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use {
2+
crate::args::Cli,
3+
clap::Parser,
4+
rblib::{
5+
alloy::primitives::Address,
6+
reth::optimism::cli::commands::Commands,
7+
},
8+
};
9+
10+
/// Parameters for Flashtestations configuration
11+
/// The names in the struct are prefixed with `flashtestations`
12+
#[derive(Debug, Clone, PartialEq, Eq, clap::Args)]
13+
pub struct FlashtestationsArgs {
14+
/// When set to true, the builder will initiate the flashtestations
15+
/// workflow within the bootstrapping and block building process.
16+
#[arg(
17+
long = "flashtestations.enabled",
18+
default_value = "false",
19+
env = "ENABLE_FLASHTESTATIONS"
20+
)]
21+
pub flashtestations_enabled: bool,
22+
23+
/// Whether to use the debug HTTP service for quotes
24+
#[arg(
25+
long = "flashtestations.debug",
26+
default_value = "false",
27+
env = "FLASHTESTATIONS_DEBUG"
28+
)]
29+
pub debug: bool,
30+
31+
// Debug static key for the tee key. DO NOT USE IN PRODUCTION
32+
#[arg(
33+
long = "flashtestations.debug-tee-key-seed",
34+
env = "FLASHTESTATIONS_DEBUG_TEE_KEY_SEED",
35+
default_value = "debug"
36+
)]
37+
pub debug_tee_key_seed: String,
38+
39+
/// Path to save ephemeral TEE key between restarts
40+
#[arg(
41+
long = "flashtestations.tee-key-path",
42+
env = "FLASHTESTATIONS_TEE_KEY_PATH",
43+
default_value = "/run/flashtestation.key"
44+
)]
45+
pub flashtestations_key_path: String,
46+
47+
// Remote url for attestations
48+
#[arg(
49+
long = "flashtestations.quote-provider",
50+
env = "FLASHTESTATIONS_QUOTE_PROVIDER"
51+
)]
52+
pub quote_provider: Option<String>,
53+
54+
/// The rpc url to post the onchain attestation requests to
55+
#[arg(long = "flashtestations.rpc-url", env = "FLASHTESTATIONS_RPC_URL")]
56+
rpc_url: Option<String>,
57+
58+
/// Enable end of block TEE proof
59+
#[arg(
60+
long = "flashtestations.enable-block-proofs",
61+
env = "FLASHTESTATIONS_ENABLE_BLOCK_PROOFS",
62+
default_value = "false"
63+
)]
64+
pub enable_block_proofs: bool,
65+
66+
/// The address of the flashtestations registry contract
67+
#[arg(
68+
long = "flashtestations.registry-address",
69+
env = "FLASHTESTATIONS_REGISTRY_ADDRESS",
70+
required_if_eq("flashtestations_enabled", "true")
71+
)]
72+
pub registry_address: Option<Address>,
73+
74+
/// The address of the builder policy contract
75+
#[arg(
76+
long = "flashtestations.builder-policy-address",
77+
env = "FLASHTESTATIONS_BUILDER_POLICY_ADDRESS",
78+
required_if_eq("flashtestations_enabled", "true")
79+
)]
80+
pub builder_policy_address: Option<Address>,
81+
82+
/// The version of the block builder verification proof
83+
#[arg(
84+
long = "flashtestations.builder-proof-version",
85+
env = "FLASHTESTATIONS_BUILDER_PROOF_VERSION",
86+
default_value = "1"
87+
)]
88+
pub builder_proof_version: u8,
89+
}
90+
91+
impl Default for FlashtestationsArgs {
92+
fn default() -> Self {
93+
let args = Cli::parse_from(["dummy", "node"]);
94+
let Commands::Node(node_command) = args.command else {
95+
unreachable!()
96+
};
97+
node_command.ext.flashtestations
98+
}
99+
}

src/flashtestations/attestation.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use {
2+
crate::signer::BuilderSigner,
3+
eyre::ContextCompat,
4+
rblib::alloy::{
5+
hex,
6+
primitives::{Bytes, keccak256},
7+
},
8+
reqwest::Client,
9+
tracing::info,
10+
};
11+
12+
const DEBUG_QUOTE_SERVICE_URL: &str =
13+
"http://ns31695324.ip-141-94-163.eu:10080/attest";
14+
15+
/// Remote attestation provider
16+
#[derive(Debug, Clone)]
17+
pub struct RemoteAttestationProvider {
18+
client: Client,
19+
service_url: String,
20+
}
21+
22+
impl RemoteAttestationProvider {
23+
pub fn try_new(
24+
debug: bool,
25+
quote_provider: Option<String>,
26+
) -> eyre::Result<Self> {
27+
let client = Client::new();
28+
29+
let service_url = if debug {
30+
quote_provider.unwrap_or(DEBUG_QUOTE_SERVICE_URL.to_string())
31+
} else {
32+
quote_provider.context(
33+
"remote quote provider must be specified when not in debug mode",
34+
)?
35+
};
36+
37+
Ok(Self {
38+
client,
39+
service_url,
40+
})
41+
}
42+
}
43+
44+
impl RemoteAttestationProvider {
45+
pub async fn get_attestation(
46+
&self,
47+
signer: &BuilderSigner,
48+
) -> eyre::Result<Vec<u8>> {
49+
let report_data = prepare_report_data(signer);
50+
self.query_client(report_data).await
51+
}
52+
53+
async fn query_client(&self, report_data: [u8; 64]) -> eyre::Result<Vec<u8>> {
54+
let report_data_hex = hex::encode(report_data);
55+
let url = format!("{}/{}", self.service_url, report_data_hex);
56+
57+
info!(target: "flashtestations", url = url, "fetching quote from remote attestation provider");
58+
59+
let response = self
60+
.client
61+
.get(&url)
62+
.timeout(std::time::Duration::from_secs(10))
63+
.send()
64+
.await?
65+
.error_for_status()?;
66+
let body = response.bytes().await?.to_vec();
67+
68+
// TODO: Validate response?
69+
70+
Ok(body)
71+
}
72+
}
73+
74+
fn prepare_report_data(signer: &BuilderSigner) -> [u8; 64] {
75+
// Prepare report data:
76+
// - TEE address (20 bytes) at reportData[0:20]
77+
// - Extended registration data hash (32 bytes) at reportData[20:52]
78+
// - Total: 52 bytes, padded to 64 bytes with zeros
79+
80+
// Extract TEE address as 20 bytes
81+
let tee_address_bytes: [u8; 20] = signer.address().into();
82+
83+
// Calculate keccak256 hash of empty bytes (32 bytes)
84+
let empty_registration_data = Bytes::default();
85+
let empty_registration_data_hash = keccak256(&empty_registration_data);
86+
87+
// Create 64-byte report data array
88+
let mut report_data = [0u8; 64];
89+
90+
// Copy TEE address (20 bytes) to positions 0-19
91+
report_data[0..20].copy_from_slice(&tee_address_bytes);
92+
93+
// Copy extended registration data hash (32 bytes) to positions 20-51
94+
report_data[20..52].copy_from_slice(empty_registration_data_hash.as_ref());
95+
96+
report_data
97+
}

0 commit comments

Comments
 (0)