diff --git a/crates/op-rbuilder/src/args/op.rs b/crates/op-rbuilder/src/args/op.rs
index b0b90f84..1d81c382 100644
--- a/crates/op-rbuilder/src/args/op.rs
+++ b/crates/op-rbuilder/src/args/op.rs
@@ -167,6 +167,15 @@ pub struct FlashblocksArgs {
)]
pub flashblocks_number_contract_address: Option
,
+ /// Use permit signatures if flashtestations is enabled with the flashtestation key
+ /// to increment the flashblocks number
+ #[arg(
+ long = "flashblocks.number-contract-use-permit",
+ env = "FLASHBLOCK_NUMBER_CONTRACT_USE_PERMIT",
+ default_value = "false"
+ )]
+ pub flashblocks_number_contract_use_permit: bool,
+
/// Flashblocks p2p configuration
#[command(flatten)]
pub p2p: FlashblocksP2pArgs,
diff --git a/crates/op-rbuilder/src/builders/builder_tx.rs b/crates/op-rbuilder/src/builders/builder_tx.rs
index 11194a38..7cc56b07 100644
--- a/crates/op-rbuilder/src/builders/builder_tx.rs
+++ b/crates/op-rbuilder/src/builders/builder_tx.rs
@@ -126,7 +126,7 @@ impl From for PayloadBuilderError {
BuilderTransactionError::EvmExecutionError(e) => {
PayloadBuilderError::EvmExecutionError(e)
}
- _ => PayloadBuilderError::Other(Box::new(error)),
+ _ => PayloadBuilderError::other(error),
}
}
}
diff --git a/crates/op-rbuilder/src/builders/flashblocks/builder_tx.rs b/crates/op-rbuilder/src/builders/flashblocks/builder_tx.rs
index 04afc64d..da32fbcb 100644
--- a/crates/op-rbuilder/src/builders/flashblocks/builder_tx.rs
+++ b/crates/op-rbuilder/src/builders/flashblocks/builder_tx.rs
@@ -1,31 +1,25 @@
-use alloy_consensus::TxEip1559;
use alloy_eips::Encodable2718;
use alloy_evm::{Database, Evm};
use alloy_op_evm::OpEvm;
-use alloy_primitives::{Address, TxKind};
-use alloy_sol_types::{Error, SolCall, SolEvent, SolInterface, sol};
+use alloy_primitives::{Address, B256, Signature, U256};
+use alloy_rpc_types_eth::TransactionInput;
+use alloy_sol_types::{SolCall, SolEvent, sol};
use core::fmt::Debug;
-use op_alloy_consensus::OpTypedTransaction;
-use op_revm::OpHaltReason;
+use op_alloy_rpc_types::OpTransactionRequest;
use reth_evm::{ConfigureEvm, precompiles::PrecompilesMap};
-use reth_optimism_primitives::OpTransactionSigned;
-use reth_primitives::Recovered;
use reth_provider::StateProvider;
use reth_revm::State;
-use revm::{
- DatabaseRef,
- context::result::{ExecutionResult, ResultAndState},
- inspector::NoOpInspector,
-};
+use revm::{DatabaseRef, inspector::NoOpInspector};
use tracing::warn;
use crate::{
builders::{
BuilderTransactionCtx, BuilderTransactionError, BuilderTransactions,
- InvalidContractDataError,
- builder_tx::{BuilderTxBase, get_nonce},
+ SimulationSuccessResult,
+ builder_tx::BuilderTxBase,
context::OpPayloadBuilderCtx,
flashblocks::payload::{FlashblocksExecutionInfo, FlashblocksExtraCtx},
+ get_nonce,
},
flashtestations::builder_tx::FlashtestationsBuilderTx,
primitives::reth::ExecutionInfo,
@@ -37,8 +31,17 @@ sol!(
#[sol(rpc, abi)]
#[derive(Debug)]
interface IFlashblockNumber {
+ uint256 public flashblockNumber;
+
function incrementFlashblockNumber() external;
+ function permitIncrementFlashblockNumber(uint256 currentFlashblockNumber, bytes memory signature) external;
+
+ function computeStructHash(uint256 currentFlashblockNumber) external pure returns (bytes32);
+
+ function hashTypedDataV4(bytes32 structHash) external view returns (bytes32);
+
+
// @notice Emitted when flashblock index is incremented
// @param newFlashblockIndex The new flashblock index (0-indexed within each L2 block)
event FlashblockIncremented(uint256 newFlashblockIndex);
@@ -51,16 +54,6 @@ sol!(
}
);
-#[derive(Debug, thiserror::Error)]
-pub(super) enum FlashblockNumberError {
- #[error("flashblocks number contract tx reverted: {0:?}")]
- Revert(IFlashblockNumber::IFlashblockNumberErrors),
- #[error("unknown revert: {0} err: {1}")]
- Unknown(String, Error),
- #[error("halt: {0:?}")]
- Halt(OpHaltReason),
-}
-
// This will be the end of block transaction of a regular block
#[derive(Debug, Clone)]
pub(super) struct FlashblocksBuilderTx {
@@ -133,8 +126,9 @@ impl BuilderTransactions for Flas
// This will be the end of block transaction of a regular block
#[derive(Debug, Clone)]
pub(super) struct FlashblocksNumberBuilderTx {
- pub signer: Option,
+ pub signer: Signer,
pub flashblock_number_address: Address,
+ pub use_permit: bool,
pub base_builder_tx: BuilderTxBase,
pub flashtestations_builder_tx:
Option>,
@@ -142,85 +136,129 @@ pub(super) struct FlashblocksNumberBuilderTx {
impl FlashblocksNumberBuilderTx {
pub(super) fn new(
- signer: Option,
+ signer: Signer,
flashblock_number_address: Address,
+ use_permit: bool,
flashtestations_builder_tx: Option<
FlashtestationsBuilderTx,
>,
) -> Self {
- let base_builder_tx = BuilderTxBase::new(signer);
+ let base_builder_tx = BuilderTxBase::new(Some(signer));
Self {
signer,
flashblock_number_address,
+ use_permit,
base_builder_tx,
flashtestations_builder_tx,
}
}
- // TODO: remove and clean up in favour of simulate_call()
- fn estimate_flashblock_number_tx_gas(
+ fn signed_increment_flashblocks_tx(
&self,
ctx: &OpPayloadBuilderCtx,
- evm: &mut OpEvm,
- signer: &Signer,
- nonce: u64,
- ) -> Result {
- let tx = self.signed_flashblock_number_tx(ctx, ctx.block_gas_limit(), nonce, signer)?;
- let ResultAndState { result, .. } = match evm.transact(&tx) {
- Ok(res) => res,
- Err(err) => {
- return Err(BuilderTransactionError::EvmExecutionError(Box::new(err)));
- }
- };
+ evm: &mut OpEvm,
+ ) -> Result {
+ let calldata = IFlashblockNumber::incrementFlashblockNumberCall {};
+ self.increment_flashblocks_tx(calldata, &self.signer, ctx, evm)
+ }
- match result {
- ExecutionResult::Success { gas_used, logs, .. } => {
- if logs.iter().any(|log| {
- log.topics().first()
- == Some(&IFlashblockNumber::FlashblockIncremented::SIGNATURE_HASH)
- }) {
- Ok(gas_used)
- } else {
- Err(BuilderTransactionError::InvalidContract(
- self.flashblock_number_address,
- InvalidContractDataError::InvalidLogs(
- vec![IFlashblockNumber::FlashblockIncremented::SIGNATURE_HASH],
- vec![],
- ),
- ))
- }
- }
- ExecutionResult::Revert { output, .. } => Err(BuilderTransactionError::other(
- IFlashblockNumber::IFlashblockNumberErrors::abi_decode(&output)
- .map(FlashblockNumberError::Revert)
- .unwrap_or_else(|e| FlashblockNumberError::Unknown(hex::encode(output), e)),
- )),
- ExecutionResult::Halt { reason, .. } => Err(BuilderTransactionError::other(
- FlashblockNumberError::Halt(reason),
- )),
- }
+ fn increment_flashblocks_permit_signature(
+ &self,
+ flashtestations_signer: &Signer,
+ current_flashblock_number: U256,
+ ctx: &OpPayloadBuilderCtx,
+ evm: &mut OpEvm,
+ ) -> Result {
+ let struct_hash_calldata = IFlashblockNumber::computeStructHashCall {
+ currentFlashblockNumber: current_flashblock_number,
+ };
+ let SimulationSuccessResult { output, .. } =
+ self.simulate_flashblocks_readonly_call(struct_hash_calldata, ctx, evm)?;
+ let typed_data_hash_calldata =
+ IFlashblockNumber::hashTypedDataV4Call { structHash: output };
+ let SimulationSuccessResult { output, .. } =
+ self.simulate_flashblocks_readonly_call(typed_data_hash_calldata, ctx, evm)?;
+ let signature = flashtestations_signer.sign_message(output)?;
+ Ok(signature)
}
- fn signed_flashblock_number_tx(
+ fn signed_increment_flashblocks_permit_tx(
&self,
+ flashtestations_signer: &Signer,
ctx: &OpPayloadBuilderCtx,
- gas_limit: u64,
- nonce: u64,
+ evm: &mut OpEvm,
+ ) -> Result {
+ let current_flashblock_calldata = IFlashblockNumber::flashblockNumberCall {};
+ let SimulationSuccessResult { output, .. } =
+ self.simulate_flashblocks_readonly_call(current_flashblock_calldata, ctx, evm)?;
+ let signature =
+ self.increment_flashblocks_permit_signature(flashtestations_signer, output, ctx, evm)?;
+ let calldata = IFlashblockNumber::permitIncrementFlashblockNumberCall {
+ currentFlashblockNumber: output,
+ signature: signature.as_bytes().into(),
+ };
+ self.increment_flashblocks_tx(calldata, flashtestations_signer, ctx, evm)
+ }
+
+ fn increment_flashblocks_tx(
+ &self,
+ calldata: T,
signer: &Signer,
- ) -> Result, secp256k1::Error> {
- let calldata = IFlashblockNumber::incrementFlashblockNumberCall {}.abi_encode();
- // Create the EIP-1559 transaction
- let tx = OpTypedTransaction::Eip1559(TxEip1559 {
- chain_id: ctx.chain_id(),
- nonce,
- gas_limit,
- max_fee_per_gas: ctx.base_fee().into(),
- max_priority_fee_per_gas: 0,
- to: TxKind::Call(self.flashblock_number_address),
- input: calldata.into(),
- ..Default::default()
- });
- signer.sign_tx(tx)
+ ctx: &OpPayloadBuilderCtx,
+ evm: &mut OpEvm,
+ ) -> Result {
+ let SimulationSuccessResult { gas_used, .. } = self.simulate_flashblocks_call(
+ calldata.clone(),
+ vec![IFlashblockNumber::FlashblockIncremented::SIGNATURE_HASH],
+ ctx,
+ evm,
+ )?;
+ let signed_tx = self.sign_tx(
+ self.flashblock_number_address,
+ *signer,
+ gas_used,
+ calldata.abi_encode().into(),
+ ctx,
+ evm.db_mut(),
+ )?;
+ let da_size =
+ op_alloy_flz::tx_estimated_size_fjord_bytes(signed_tx.encoded_2718().as_slice());
+ Ok(BuilderTransactionCtx {
+ signed_tx,
+ gas_used,
+ da_size,
+ is_top_of_block: true,
+ })
+ }
+
+ fn simulate_flashblocks_readonly_call(
+ &self,
+ calldata: T,
+ ctx: &OpPayloadBuilderCtx,
+ evm: &mut OpEvm,
+ ) -> Result, BuilderTransactionError> {
+ self.simulate_flashblocks_call(calldata, vec![], ctx, evm)
+ }
+
+ fn simulate_flashblocks_call(
+ &self,
+ calldata: T,
+ expected_logs: Vec,
+ ctx: &OpPayloadBuilderCtx,
+ evm: &mut OpEvm,
+ ) -> Result, BuilderTransactionError> {
+ let tx_req = OpTransactionRequest::default()
+ .gas_limit(ctx.block_gas_limit())
+ .max_fee_per_gas(ctx.base_fee().into())
+ .to(self.flashblock_number_address)
+ .from(self.signer.address) // use tee key as signer for simulations
+ .nonce(get_nonce(evm.db(), self.signer.address)?)
+ .input(TransactionInput::new(calldata.abi_encode().into()));
+ self.simulate_call::(
+ tx_req,
+ expected_logs,
+ evm,
+ )
}
}
@@ -242,46 +280,35 @@ impl BuilderTransactions
builder_txs.extend(self.base_builder_tx.simulate_builder_tx(ctx, &mut *db)?);
} else {
// we increment the flashblock number for the next flashblock so we don't increment in the last flashblock
- if let Some(signer) = &self.signer {
- let mut evm = ctx.evm_config.evm_with_env(&mut *db, ctx.evm_env.clone());
- evm.modify_cfg(|cfg| {
- cfg.disable_balance_check = true;
- cfg.disable_block_gas_limit = true;
- });
+ let mut evm = ctx.evm_config.evm_with_env(&mut *db, ctx.evm_env.clone());
+ evm.modify_cfg(|cfg| {
+ cfg.disable_balance_check = true;
+ cfg.disable_block_gas_limit = true;
+ });
- let nonce = get_nonce(evm.db_mut(), signer.address)?;
-
- let tx = match self.estimate_flashblock_number_tx_gas(ctx, &mut evm, signer, nonce)
- {
- Ok(gas_used) => {
- // Due to EIP-150, 63/64 of available gas is forwarded to external calls so need to add a buffer
- let signed_tx = self.signed_flashblock_number_tx(
- ctx,
- gas_used * 64 / 63,
- nonce,
- signer,
- )?;
+ let flashblocks_num_tx = if let Some(flashtestations) = &self.flashtestations_builder_tx
+ && self.use_permit
+ {
+ self.signed_increment_flashblocks_permit_tx(
+ flashtestations.tee_signer(),
+ ctx,
+ &mut evm,
+ )
+ } else {
+ self.signed_increment_flashblocks_tx(ctx, &mut evm)
+ };
- let da_size = op_alloy_flz::tx_estimated_size_fjord_bytes(
- signed_tx.encoded_2718().as_slice(),
- );
- Some(BuilderTransactionCtx {
- gas_used,
- da_size,
- signed_tx,
- is_top_of_block: true, // number tx at top of flashblock
- })
- }
- Err(e) => {
- warn!(target: "builder_tx", error = ?e, "Flashblocks number contract tx simulation failed, defaulting to fallback builder tx");
- self.base_builder_tx
- .simulate_builder_tx(ctx, &mut *db)?
- .map(|tx| tx.set_top_of_block())
- }
- };
+ let tx = match flashblocks_num_tx {
+ Ok(tx) => Some(tx),
+ Err(e) => {
+ warn!(target: "builder_tx", error = ?e, "flashblocks number contract tx simulation failed, defaulting to fallback builder tx");
+ self.base_builder_tx
+ .simulate_builder_tx(ctx, &mut *db)?
+ .map(|tx| tx.set_top_of_block())
+ }
+ };
- builder_txs.extend(tx);
- }
+ builder_txs.extend(tx);
}
if ctx.is_last_flashblock() {
diff --git a/crates/op-rbuilder/src/builders/flashblocks/config.rs b/crates/op-rbuilder/src/builders/flashblocks/config.rs
index a3345edb..a47cc046 100644
--- a/crates/op-rbuilder/src/builders/flashblocks/config.rs
+++ b/crates/op-rbuilder/src/builders/flashblocks/config.rs
@@ -39,6 +39,9 @@ pub struct FlashblocksConfig {
/// If set a builder tx will be added to the start of every flashblock instead of the regular builder tx.
pub flashblocks_number_contract_address: Option,
+ /// whether to use permit signatures for the contract calls
+ pub flashblocks_number_contract_use_permit: bool,
+
/// Whether to enable the p2p node for flashblocks
pub p2p_enabled: bool,
@@ -64,6 +67,7 @@ impl Default for FlashblocksConfig {
fixed: false,
calculate_state_root: true,
flashblocks_number_contract_address: None,
+ flashblocks_number_contract_use_permit: false,
p2p_enabled: false,
p2p_port: 9009,
p2p_private_key_file: None,
@@ -93,6 +97,9 @@ impl TryFrom for FlashblocksConfig {
let flashblocks_number_contract_address =
args.flashblocks.flashblocks_number_contract_address;
+ let flashblocks_number_contract_use_permit =
+ args.flashblocks.flashblocks_number_contract_use_permit;
+
Ok(Self {
ws_addr,
interval,
@@ -100,6 +107,7 @@ impl TryFrom for FlashblocksConfig {
fixed,
calculate_state_root,
flashblocks_number_contract_address,
+ flashblocks_number_contract_use_permit,
p2p_enabled: args.flashblocks.p2p.p2p_enabled,
p2p_port: args.flashblocks.p2p.p2p_port,
p2p_private_key_file: args.flashblocks.p2p.p2p_private_key_file,
diff --git a/crates/op-rbuilder/src/builders/flashblocks/service.rs b/crates/op-rbuilder/src/builders/flashblocks/service.rs
index e11fa2f2..bf3e1c2f 100644
--- a/crates/op-rbuilder/src/builders/flashblocks/service.rs
+++ b/crates/op-rbuilder/src/builders/flashblocks/service.rs
@@ -188,15 +188,18 @@ where
None
};
- if let Some(flashblocks_number_contract_address) =
- self.0.specific.flashblocks_number_contract_address
+ if let Some(builder_signer) = signer
+ && let Some(flashblocks_number_contract_address) =
+ self.0.specific.flashblocks_number_contract_address
{
+ let use_permit = self.0.specific.flashblocks_number_contract_use_permit;
self.spawn_payload_builder_service(
ctx,
pool,
FlashblocksNumberBuilderTx::new(
- signer,
+ builder_signer,
flashblocks_number_contract_address,
+ use_permit,
flashtestations_builder_tx,
),
)
diff --git a/crates/op-rbuilder/src/flashtestations/args.rs b/crates/op-rbuilder/src/flashtestations/args.rs
index 7856bb01..44d1b126 100644
--- a/crates/op-rbuilder/src/flashtestations/args.rs
+++ b/crates/op-rbuilder/src/flashtestations/args.rs
@@ -83,6 +83,14 @@ pub struct FlashtestationsArgs {
default_value = "1"
)]
pub builder_proof_version: u8,
+
+ /// Use permit for the flashtestation builder tx
+ #[arg(
+ long = "flashtestations.use-permit",
+ env = "FLASHTESTATIONS_USE_PERMIT",
+ default_value = "false"
+ )]
+ pub flashtestations_use_permit: bool,
}
impl Default for FlashtestationsArgs {
diff --git a/crates/op-rbuilder/src/flashtestations/builder_tx.rs b/crates/op-rbuilder/src/flashtestations/builder_tx.rs
index 005dcab5..a031f57b 100644
--- a/crates/op-rbuilder/src/flashtestations/builder_tx.rs
+++ b/crates/op-rbuilder/src/flashtestations/builder_tx.rs
@@ -89,6 +89,10 @@ where
}
}
+ pub fn tee_signer(&self) -> &Signer {
+ &self.tee_service_signer
+ }
+
/// Computes the block content hash according to the formula:
/// keccak256(abi.encode(parentHash, blockNumber, timestamp, transactionHashes))
/// https://github.com/flashbots/rollup-boost/blob/main/specs/flashtestations.md#block-building-process
@@ -136,6 +140,7 @@ where
.evm_with_env(&mut simulation_state, ctx.evm_env.clone());
evm.modify_cfg(|cfg| {
cfg.disable_balance_check = true;
+ cfg.disable_nonce_check = true;
});
let calldata = IFlashtestationRegistry::getRegistrationStatusCall {
teeAddress: self.tee_service_signer.address,
@@ -336,7 +341,7 @@ where
.gas_limit(ctx.block_gas_limit())
.max_fee_per_gas(ctx.base_fee().into())
.to(contract_address)
- .from(self.tee_service_signer.address) // use tee key as signer for simulations
+ .from(self.builder_signer.address)
.nonce(get_nonce(evm.db(), self.tee_service_signer.address)?)
.input(TransactionInput::new(calldata.abi_encode().into()));
if contract_address == self.registry_address {
diff --git a/crates/op-rbuilder/src/tests/flashblocks.rs b/crates/op-rbuilder/src/tests/flashblocks.rs
index a623aaff..cd92da9f 100644
--- a/crates/op-rbuilder/src/tests/flashblocks.rs
+++ b/crates/op-rbuilder/src/tests/flashblocks.rs
@@ -526,7 +526,7 @@ async fn test_flashblocks_number_contract_builder_tx(rbuilder: LocalInstance) ->
let init_tx = driver
.create_transaction()
.init_flashblock_number_contract(true)
- .with_to(contract_address)
+ .with_to(FLASHBLOCKS_NUMBER_ADDRESS)
.with_bundle(BundleOpts::default())
.send()
.await?;
diff --git a/crates/op-rbuilder/src/tests/flashtestations.rs b/crates/op-rbuilder/src/tests/flashtestations.rs
index c0cc0250..8ca8795f 100644
--- a/crates/op-rbuilder/src/tests/flashtestations.rs
+++ b/crates/op-rbuilder/src/tests/flashtestations.rs
@@ -97,7 +97,6 @@ async fn test_flashtestations_unauthorized_workload(rbuilder: LocalInstance) ->
// check that only the regular builder tx is in the block
let (tx_hash, block) = driver.build_new_block_with_valid_transaction().await?;
let txs = block.transactions.into_transactions_vec();
-
if_flashblocks!(
assert_eq!(txs.len(), 4, "Expected 4 transactions in block"); // deposit + valid tx + 2 builder tx
// Check builder tx
@@ -312,7 +311,6 @@ async fn test_flashtestations_permit_with_flashblocks_number_contract(
.send()
.await?;
let block = driver.build_new_block_with_current_timestamp(None).await?;
- // check the builder tx, funding tx and registration tx is in the block
let num_txs = block.transactions.len();
let txs = block.transactions.into_transactions_vec();
// // 1 deposit tx, 1 regular builder tx, 4 flashblocks number tx, 1 user tx, 1 block proof tx
@@ -358,6 +356,121 @@ async fn test_flashtestations_permit_with_flashblocks_number_contract(
Ok(())
}
+#[rb_test(flashblocks, args = OpRbuilderArgs {
+ chain_block_time: 1000,
+ enable_revert_protection: true,
+ flashblocks: FlashblocksArgs {
+ flashblocks_number_contract_address: Some(FLASHBLOCKS_NUMBER_ADDRESS),
+ flashblocks_number_contract_use_permit: true,
+ ..Default::default()
+ },
+ flashtestations: FlashtestationsArgs {
+ flashtestations_enabled: true,
+ registry_address: Some(FLASHTESTATION_REGISTRY_ADDRESS),
+ builder_policy_address: Some(BLOCK_BUILDER_POLICY_ADDRESS),
+ debug: true,
+ flashtestations_use_permit: true,
+ enable_block_proofs: true,
+ ..Default::default()
+ },
+ ..Default::default()
+})]
+async fn test_flashtestations_permit_with_flashblocks_number_permit(
+ rbuilder: LocalInstance,
+) -> eyre::Result<()> {
+ let driver = rbuilder.driver().await?;
+ let provider = rbuilder.provider().await?;
+ setup_flashblock_number_contract(&driver, &provider, false).await?;
+ setup_flashtestation_contracts(&driver, &provider, true, true).await?;
+ // Verify flashblock number is not incremented and builder address is not authorized
+ let contract = FlashblocksNumber::new(FLASHBLOCKS_NUMBER_ADDRESS, provider.clone());
+ let current_number = contract.getFlashblockNumber().call().await?;
+ assert!(
+ current_number.is_zero(), // contract deployments incremented the number but we built at least 1 full block
+ "Flashblock number should not be incremented"
+ );
+ let is_authorized = contract.isBuilder(builder_signer().address).call().await?;
+ assert!(!is_authorized, "builder should not be authorized");
+
+ // add tee signer address to authorized builders
+ let add_builder_tx = driver
+ .create_transaction()
+ .add_authorized_builder(TEE_DEBUG_ADDRESS)
+ .with_to(FLASHBLOCKS_NUMBER_ADDRESS)
+ .with_bundle(BundleOpts::default().with_flashblock_number_min(4))
+ .send()
+ .await?;
+ let block = driver.build_new_block_with_current_timestamp(None).await?;
+ provider
+ .get_transaction_receipt(*add_builder_tx.tx_hash())
+ .await?
+ .expect("add builder tx not mined");
+ let num_txs = block.transactions.len();
+ let txs = block.transactions.into_transactions_vec();
+ // 1 deposit tx, 5 regular builder tx, 1 add builder tx, 1 block proof tx
+ assert_eq!(num_txs, 8, "Expected 8 transactions in block");
+ // Check no transactions to the flashblocks number contract as tee signer is not authorized
+ for i in 1..6 {
+ assert_eq!(
+ txs[i].to(),
+ Some(Address::ZERO),
+ "builder tx should send to flashblocks number contract at index {}",
+ i
+ );
+ }
+ // add builder tx
+ assert_eq!(
+ txs[6].tx_hash(),
+ *add_builder_tx.tx_hash(),
+ "add builder tx should be in correct position in block"
+ );
+ assert_eq!(
+ txs[7].to(),
+ Some(BLOCK_BUILDER_POLICY_ADDRESS),
+ "builder tx should send verify block builder proof"
+ );
+
+ let tx = driver
+ .create_transaction()
+ .random_valid_transfer()
+ .with_bundle(BundleOpts::default().with_flashblock_number_min(4))
+ .send()
+ .await?;
+ let block = driver.build_new_block_with_current_timestamp(None).await?;
+ let txs = block.transactions.into_transactions_vec();
+ // 1 deposit tx, 1 regular builder tx, 4 flashblocks builder tx, 1 user tx, 1 block proof tx
+ assert_eq!(txs.len(), 8, "Expected 8 transactions in block");
+ // flashblocks number contract
+ for i in 2..6 {
+ assert_eq!(
+ txs[i].to(),
+ Some(FLASHBLOCKS_NUMBER_ADDRESS),
+ "builder tx should send to flashblocks number contract at index {}",
+ i
+ );
+ }
+ // user tx
+ assert_eq!(
+ txs[6].tx_hash(),
+ *tx.tx_hash(),
+ "user tx should be in correct position in block"
+ );
+ // check that the tee signer did not send any transactions
+ let balance = provider.get_balance(TEE_DEBUG_ADDRESS).await?;
+ assert!(balance.is_zero());
+ let nonce = provider.get_transaction_count(TEE_DEBUG_ADDRESS).await?;
+ assert_eq!(nonce, 0);
+ // Verify flashblock number incremented correctly
+ let contract = FlashblocksNumber::new(FLASHBLOCKS_NUMBER_ADDRESS, provider.clone());
+ let current_number = contract.getFlashblockNumber().call().await?;
+ assert_eq!(
+ current_number,
+ U256::from(4),
+ "Flashblock number not incremented correctly"
+ );
+ Ok(())
+}
+
async fn setup_flashtestation_contracts(
driver: &ChainDriver,
provider: &RootProvider,
diff --git a/crates/op-rbuilder/src/tests/framework/instance.rs b/crates/op-rbuilder/src/tests/framework/instance.rs
index ccfadaf0..d698bf45 100644
--- a/crates/op-rbuilder/src/tests/framework/instance.rs
+++ b/crates/op-rbuilder/src/tests/framework/instance.rs
@@ -52,7 +52,6 @@ use std::{
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
use tokio_tungstenite::{connect_async, tungstenite::Message};
use tokio_util::sync::CancellationToken;
-use tracing::warn;
/// Represents a type that emulates a local in-process instance of the OP builder node.
/// This node uses IPC as the communication channel for the RPC server Engine API.
@@ -410,7 +409,6 @@ impl FlashblocksListener {
}
Some(Ok(Message::Text(text))) = read.next() => {
let fb = serde_json::from_str(&text).unwrap();
- warn!("GOT FB: {fb:#?}");
flashblocks_clone.lock().push(fb);
}
}
diff --git a/crates/op-rbuilder/src/tests/framework/utils.rs b/crates/op-rbuilder/src/tests/framework/utils.rs
index 35a5f2a5..99772de1 100644
--- a/crates/op-rbuilder/src/tests/framework/utils.rs
+++ b/crates/op-rbuilder/src/tests/framework/utils.rs
@@ -33,6 +33,7 @@ pub trait TransactionBuilderExt {
// flashblocks number methods
fn deploy_flashblock_number_contract(self) -> Self;
fn init_flashblock_number_contract(self, register_builder: bool) -> Self;
+ fn add_authorized_builder(self, builder: Address) -> Self;
// flashtestations methods
fn deploy_flashtestation_registry_contract(self) -> Self;
fn init_flashtestation_registry_contract(self, dcap_address: Address) -> Self;
@@ -85,6 +86,13 @@ impl TransactionBuilderExt for TransactionBuilder {
.with_signer(flashblocks_number_signer())
}
+ fn add_authorized_builder(self, builder: Address) -> Self {
+ let calldata = FlashblocksNumber::addBuilderCall { builder }.abi_encode();
+
+ self.with_input(calldata.into())
+ .with_signer(flashblocks_number_signer())
+ }
+
fn deploy_flashtestation_registry_contract(self) -> Self {
self.with_create()
.with_input(FlashtestationRegistry::BYTECODE.clone())