Skip to content

Conversation

avalonche
Copy link
Contributor

@avalonche avalonche commented Sep 18, 2025

πŸ“ Summary

  • Replaces --debug-url with --quote_provider arg
  • Adds the flashtestations block proof at the end of the block

πŸ’‘ Motivation and Context


βœ… I have completed the following steps:

  • Run make lint
  • Run make test
  • Added tests (if applicable)

@avalonche avalonche force-pushed the flashtestations-builder-tx-rebased branch from fb9ed66 to 64be228 Compare September 19, 2025 21:53
let tee_address_bytes: [u8; 20] = tee_service_signer.address.into();

// Calculate keccak256 hash of empty bytes (32 bytes)
let ext_data = Bytes::from(b"");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is always empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Builder proof version
builder_proof_version: u8,
// Whether the workload and address has been registered
registered: Arc<AtomicBool>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnceLock would be nice in here, but it's nit


if !self.registered.load(Ordering::Relaxed) {
info!(target: "flashtestations", "tee service not registered yet, attempting to register");
self.set_registered(state_provider, ctx);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? If we set_registered to true in here we would still create tx in register_tee_service_tx, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function simulates the registration in the evm and we want to minimise the number of calls to this by only checking while we are not registered. The self.register_tee_service_tx will return None if there is an already registered error from the evm

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that side effect of set_registered is self.registered.store(registered, Ordering::Relaxed);
Should we do let (register_tx, _) = self.register_tee_service_tx(ctx, &mut evm)?; first and then set_registered?
Or maybe we should do self.set_registered(state_provider, ctx); and then check self.registered again, in case it's registered already and we don't need to register

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do let (register_tx, _) = self.register_tee_service_tx(ctx, &mut evm)?; first and then set_registered

the order doesn't matter since self.register_tee_service_tx(ctx, &mut evm)? simulates against the block state that has been built thus far and self.set_registered is simulated against the committed state. this is to prevent registered set to true when the register tx is committed at the top of the block and then ends up not being included in the canonical chain. It would also not guard against reorgs

let verify_block_proof_tx = self.signed_block_builder_proof_tx(
block_content_hash,
ctx,
gas_used * 64 / 63,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little bit confused in here
If we want to add buffer of size gas_used / 64, then we need to do
gas_used * 65 / 64

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so if the contract call uses 1000 gas and only 63/64 is forwarded:
needed = 1000 gas
forwarded = 1000 * 63 / 64 = 984.375
to adjust the needed gas limit equal to the forwarded amount:
(1000 * 64 / 63) * (63 / 64) = 1000

let nonce = get_nonce(evm.db_mut(), self.tee_service_signer.address)?;
let register_tx = self.signed_register_tee_service_tx(
self.attestation.clone(),
gas_used * 64 / 63, // Due to EIP-150, 63/64 of available gas is forwarded to external calls so need to add a buffer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as below

Comment on lines +231 to +232
warn!(target: "flashtestations", %err, "register tee service tx failed");
return Ok(TxSimulateResult::default());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it okay to ignore the error here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are not fatal errors should be handled separately (nonce, balance, gas issues)

Comment on lines +419 to +420
warn!(target: "flashtestations", "transaction did not emit TEEServiceRegistered log, FlashtestationRegistry contract address may be incorrect");
Ok((None, false))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not propagating the error here seems like it could get messy, why not propagate the errors and handle them in the caller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only fatal errors were returned as it was being propagated to the payload builder and stopping blocks being built. It should not fail on reverts / operational errors (e.g. insufficient balance).

if !self.registered.get().unwrap_or(&false) {
info!(target: "flashtestations", "tee service not registered yet, attempting to register");
builder_txs.extend(self.fund_tee_service_tx(ctx, &mut evm)?);
let (register_tx, _) = self.register_tee_service_tx(ctx, &mut evm)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the fact that this can silently fail and self.set_registered(..) is still called seems bad. also, set_registered() also calls register_tee_service_tx` internally, are these duplicate calls?

Copy link
Contributor Author

@avalonche avalonche Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.register_tee_service_tx is only a simulation of the potential state that is going into the built block while self.set_registered is simulated against the committed state. We don't want to be stuck in a bad state where registered = true when self.register_tee_service_tx succeeds and the builder block doesn't end up being included, and it never attempts registration again

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move set_registered away from generating the transactions to avoid this confusion

sol!(
#[sol(rpc, abi)]
#[derive(Debug)]
interface IFlashtestationRegistry {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add link to specific commit

@avalonche avalonche force-pushed the flashtestations-builder-tx-rebased branch from 6b72d3e to bf944d7 Compare September 25, 2025 18:17
pub signed_tx: Recovered<OpTransactionSigned>,
// whether the transaction should be a top of block or
// bottom of block transaction
pub is_top_of_block: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to switch to bottom of every flashblock how difficult will it be to change this? Not sure if we want something like this configurable or not but it might be easier to start that way

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in construction of each transaction added to builder_tx just flip the boolean

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you flip it? Is it a code change? Then it needs to be built and released and it's a lot more difficult to change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh you mean configuring via cli / env? I'm not sure this is something we want to change very often since changes to the builder tx position breaks external integrations / observability. Also the same builder tx have different positions in the block depending on whether the flashblock contract call is enabled or not etc. and the complexity would easily cause misconfiguration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants