Skip to content

Commit 1c706c2

Browse files
committed
Add workload id as metric to builder
1 parent b473f4e commit 1c706c2

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed

crates/op-rbuilder/src/flashtestations/attestation.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
11
use reqwest::Client;
2+
use sha3::{Digest, Keccak256};
23
use tracing::info;
34

45
const DEBUG_QUOTE_SERVICE_URL: &str = "http://ns31695324.ip-141-94-163.eu:10080/attest";
56

7+
// Automata serialized output structure constants
8+
// The output from Automata's verifyAndAttestOnChain has a 13-byte header:
9+
// quoteVersion (2 bytes) + tee (4 bytes) + tcbStatus (1 byte) + fmspcBytes (6 bytes)
10+
const SERIALIZED_OUTPUT_OFFSET: usize = 13;
11+
const TD_REPORT10_LENGTH: usize = 584;
12+
13+
// TDX workload constants
14+
const TD_XFAM_FPU: u64 = 0x0000000000000001;
15+
const TD_XFAM_SSE: u64 = 0x0000000000000002;
16+
const TD_TDATTRS_VE_DISABLED: u64 = 0x0000000010000000;
17+
const TD_TDATTRS_PKS: u64 = 0x0000000040000000;
18+
const TD_TDATTRS_KL: u64 = 0x0000000080000000;
19+
620
/// Configuration for attestation
721
#[derive(Default)]
822
pub struct AttestationConfig {
@@ -63,3 +77,73 @@ pub fn get_attestation_provider(config: AttestationConfig) -> RemoteAttestationP
6377
)
6478
}
6579
}
80+
81+
/// ComputeWorkloadID computes the workload ID from Automata's serialized verifier output
82+
/// This corresponds to QuoteParser.parseV4VerifierOutput in Solidity implementation
83+
/// https://github.com/flashbots/flashtestations/tree/7cc7f68492fe672a823dd2dead649793aac1f216
84+
/// The workload ID uniquely identifies a TEE workload based on its measurement registers
85+
pub fn compute_workload_id(serialized_output: &[u8]) -> eyre::Result<[u8; 32]> {
86+
// Validate output length
87+
if serialized_output.len() < SERIALIZED_OUTPUT_OFFSET + TD_REPORT10_LENGTH {
88+
eyre::bail!(
89+
"invalid output length: {}, expected at least {}",
90+
serialized_output.len(),
91+
SERIALIZED_OUTPUT_OFFSET + TD_REPORT10_LENGTH
92+
);
93+
}
94+
95+
// Skip the 13-byte header to get to the TD10ReportBody
96+
let report_body = &serialized_output[SERIALIZED_OUTPUT_OFFSET..];
97+
98+
// Extract fields exactly as parseRawReportBody does in Solidity
99+
// Using hardcoded offsets to match Solidity implementation exactly
100+
let mr_td = &report_body[136..136 + 48];
101+
let rt_mr0 = &report_body[328..328 + 48];
102+
let rt_mr1 = &report_body[376..376 + 48];
103+
let rt_mr2 = &report_body[424..424 + 48];
104+
let rt_mr3 = &report_body[472..472 + 48];
105+
let mr_config_id = &report_body[184..184 + 48];
106+
107+
// Extract xFAM and tdAttributes (8 bytes each)
108+
// In Solidity, bytes8 is treated as big-endian for bitwise operations
109+
let xfam = u64::from_be_bytes(report_body[128..128 + 8].try_into().unwrap());
110+
let td_attributes = u64::from_be_bytes(report_body[120..120 + 8].try_into().unwrap());
111+
112+
// Apply transformations as per the Solidity implementation
113+
// expectedXfamBits = TD_XFAM_FPU | TD_XFAM_SSE
114+
let expected_xfam_bits = TD_XFAM_FPU | TD_XFAM_SSE;
115+
116+
// ignoredTdAttributesBitmask = TD_TDATTRS_VE_DISABLED | TD_TDATTRS_PKS | TD_TDATTRS_KL
117+
let ignored_td_attributes_bitmask = TD_TDATTRS_VE_DISABLED | TD_TDATTRS_PKS | TD_TDATTRS_KL;
118+
119+
// Transform xFAM: xFAM ^ expectedXfamBits
120+
let transformed_xfam = xfam ^ expected_xfam_bits;
121+
122+
// Transform tdAttributes: tdAttributes & ~ignoredTdAttributesBitmask
123+
let transformed_td_attributes = td_attributes & !ignored_td_attributes_bitmask;
124+
125+
// Convert transformed values to bytes (big-endian, to match Solidity bytes8)
126+
let xfam_bytes = transformed_xfam.to_be_bytes();
127+
let td_attributes_bytes = transformed_td_attributes.to_be_bytes();
128+
129+
// Concatenate all fields
130+
let mut concatenated = Vec::new();
131+
concatenated.extend_from_slice(mr_td);
132+
concatenated.extend_from_slice(rt_mr0);
133+
concatenated.extend_from_slice(rt_mr1);
134+
concatenated.extend_from_slice(rt_mr2);
135+
concatenated.extend_from_slice(rt_mr3);
136+
concatenated.extend_from_slice(mr_config_id);
137+
concatenated.extend_from_slice(&xfam_bytes);
138+
concatenated.extend_from_slice(&td_attributes_bytes);
139+
140+
// Compute keccak256 hash
141+
let mut hasher = Keccak256::new();
142+
hasher.update(&concatenated);
143+
let result = hasher.finalize();
144+
145+
let mut workload_id = [0u8; 32];
146+
workload_id.copy_from_slice(&result);
147+
148+
Ok(workload_id)
149+
}

crates/op-rbuilder/src/flashtestations/service.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ use super::{
1313
tx_manager::TxManager,
1414
};
1515
use crate::{
16-
flashtestations::builder_tx::{FlashtestationsBuilderTx, FlashtestationsBuilderTxArgs},
16+
flashtestations::{
17+
attestation::compute_workload_id,
18+
builder_tx::{FlashtestationsBuilderTx, FlashtestationsBuilderTxArgs},
19+
},
20+
metrics::record_workload_id_metrics,
1721
tx_signer::{Signer, generate_key_from_seed, generate_signer},
1822
};
1923
use std::fmt::Debug;
@@ -74,6 +78,10 @@ where
7478
info!(target: "flashtestations", "requesting TDX attestation");
7579
let attestation = attestation_provider.get_attestation(report_data).await?;
7680

81+
// Compute workload id and record metrics
82+
let workload_id = compute_workload_id(&attestation)?;
83+
record_workload_id_metrics(workload_id);
84+
7785
// Use an external rpc when the builder is not the same as the builder actively building blocks onchain
7886
let registered = if let Some(rpc_url) = args.rpc_url {
7987
let tx_manager = TxManager::new(

crates/op-rbuilder/src/metrics.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,15 @@ pub fn record_flag_gauge_metrics(builder_args: &OpRbuilderArgs) {
202202
.set(builder_args.enable_revert_protection as i32);
203203
}
204204

205+
/// Set the workload id metrics
206+
pub fn record_workload_id_metrics(workload_id: [u8; 32]) {
207+
let encoded = hex::encode(workload_id);
208+
let encoded_static: &'static str = Box::leak(encoded.into_boxed_str());
209+
let labels: [(&str, &str); 1] = [("workload_id", encoded_static)];
210+
let gauge = gauge!("op_rbuilder_workload_id", &labels);
211+
gauge.set(1);
212+
}
213+
205214
/// Contains version information for the application.
206215
#[derive(Debug, Clone)]
207216
pub struct VersionInfo {

0 commit comments

Comments
 (0)