Skip to content

Timeboost Bundle Format #296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Apr 1, 2025
Merged

Timeboost Bundle Format #296

merged 23 commits into from
Apr 1, 2025

Conversation

akonring
Copy link
Contributor

@akonring akonring commented Mar 26, 2025

This PR is an implementation of the Bundle-centric approach for Timeboost transactions and roughly follows the design proposal (see Design Proposal for detailed description).

tl;dr:

  1. No need to "shoe-horn" bundles and encrypted transactions into Ethereum transaction format.
  2. While the spec addendum describes a union type (priority bundles, encrypted non-priority tx, regular non-priority tx), this PR (and design proposal) instead introduces non-priority bundles.
  3. Priority bundle submission should closely mirror centralized timeboost; the idea being that a PLC should be able to use the same approach to submit a priority bundle to the new decentralized timeboost.

Content

This PR refactors timeboost phases to handle the new format for (regular) Bundle and modified PriorityBundle. Most of the underlying logic remains unchanged.

Other points

  • This PR touches almost all parts of the Timeboost stack and thus will not be easy to maintain.
  • Final confirmation is needed for this design to be merged. In any case, modifying the new design to conform to the union type (described above) is a trivial addition to this PR.

@akonring akonring marked this pull request as ready for review March 26, 2025 10:03
@akonring akonring mentioned this pull request Mar 26, 2025
@akonring akonring changed the title Bundle Format Timeboost Bundle Format Mar 26, 2025
Copy link
Contributor

@twittner twittner left a comment

Choose a reason for hiding this comment

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

Thanks for putting so much effort into getting the bundle format details right!

}

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Bundle {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps this should be named RegularBundle and BundleVariant can become Bundle?

) -> arbitrary::Result<PriorityBundle<Signed>> {
let bundle = Bundle::arbitrary(u)?;
let auction = Address::default();
let seqno = SeqNo::from(u.int_in_range(1..=u64::MAX)?);
Copy link
Contributor

Choose a reason for hiding this comment

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

If the sequence number space is that large, chances are that there will always be gaps in tests.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Addendum: do we hit any tricky behavior when we hit u64::MAX in anything? It's overwhelmingly unlikely that we'll hit this, of course, but just a thought. Not sure if we do any math anywhere.

Copy link
Contributor Author

@akonring akonring Apr 1, 2025

Choose a reason for hiding this comment

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

If the sequence number space is that large, chances are that there will always be gaps in tests.

Fixed: 24ec63e.
But there will be gaps, regardless. We should fix test/validation - currently no priority bundles make it through the inclusion phase. But consider this out-of-scope for this PR.

Addendum: do we hit any tricky behavior when we hit u64::MAX in anything? It's overwhelmingly unlikely that we'll hit this, of course, but just a thought. Not sure if we do any math anywhere.

iiuc it is the responsibility of the PLC to ensure correct bundle format (incl. correct seqno). We could do saturating/checked add in places like:

.any(|(x, y)| *x + 1 != *y)
Think Rust does silent overflows by default in release-mode which also seems ok.

Copy link
Collaborator

@jparr721 jparr721 left a comment

Choose a reason for hiding this comment

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

Some comments

let data = dec
.get(i + ptxs.len())
.get(i + priority_bundles.len())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since we're touching this, it might be nice to get a comment here. Not sure what this offset is doing with i + len. Mind throwing something here to make drive-by edits easier?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added a comment to the function to make it easier to parse.

@@ -21,7 +24,7 @@ pub struct Includer {
/// Consensus delayed inbox index.
index: DelayedInboxIndex,
/// Cache of transaction hashes for the previous 8 rounds.
cache: BTreeMap<RoundNumber, HashSet<Hash>>,
cache: BTreeMap<RoundNumber, HashSet<[u8; 32]>>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why 32?

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 is just a 256-bit keccak hash.

match b {
BundleVariant::Regular(b) => inner.regular.push_back((now, b)),
BundleVariant::Priority(b) =>
// TODO: Check auction contract address on bundle
Copy link
Collaborator

Choose a reason for hiding this comment

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

In case this happens when you're not here when we tackle this, can we get a bit more detail on this? I will make it a ticket as well. Basically, what are we checking? We can either document here, or can you please add a ticket in asana so I know what to do?

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 remove it from here since the TODO is already present in Bundle::validate.
The check in centralized timeboost: https://github.com/OffchainLabs/nitro/blob/1e16dc408d24a7784f19acd1e76a71daac528a22/execution/gethexec/express_lane_service.go#L414.
Applying Occam's razor it seems like an extra check in case of multiple auctions on the same chain.

Comment on lines +28 to +29
.map(|p| (p.bundle(), true))
.chain(regular.iter().map(|r| (r, false)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

How come we aren't just using the Priority types here instead?

Err(err) => {
warn!(%err, "failed to decode transaction")
}
}
}
}
Err(err) => {
warn!(?err, "failed to ssz-decode priority bundle")
warn!(?err, "failed to ssz-decode bundle")
Copy link
Collaborator

Choose a reason for hiding this comment

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

What happens when this dies? Do we throw everything away? If it's a serious issue, perhaps it could be an error? Who is at fault when such a circumstance happens? The sender? Sailfish?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you mean by die. If ssz_decode results in an error then there is not much to do. The bundle will simply be ignored but hopefully all honest nodes will come to the same conclusion. imo warn is an appropriate verbosity level and we can always adjust this.

self.epoch
}

pub fn data(&self) -> &Bytes {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need trivial getters instead of just making the fields public?

&self.bundle
}

pub fn auction(&self) -> Address {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit for later, Address and cliquenet::Address are sometimes head-spinning if they crop up in the same file. Is this Address here an ethereum address? Perhaps we can rename? Doon't do it here, just want thoughts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure. rename sounds good.

Ok(())
}

pub fn sender(&self) -> Result<Address, ValidationError> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we get a doc comment here? It's not super clear why this is a failible operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

) -> arbitrary::Result<PriorityBundle<Signed>> {
let bundle = Bundle::arbitrary(u)?;
let auction = Address::default();
let seqno = SeqNo::from(u.int_in_range(1..=u64::MAX)?);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Addendum: do we hit any tricky behavior when we hit u64::MAX in anything? It's overwhelmingly unlikely that we'll hit this, of course, but just a thought. Not sure if we do any math anywhere.

Comment on lines +6 to +11
[route.submit-priority]
PATH = ["/submit-priority"]
METHOD = "POST"
DOC = "Submit transaction to Timeboost"
DOC = "Submit priority bundle to Timeboost"

[route.submit-regular]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this correct? Would a user ever submit a priority bundle to the node? Doesn't the user just submit a transaction and only the PLC has priority tx? How do we not spoof this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do you envision a PLC submitting priority bundles if not through an API endpoint? The bundle will eventually be authenticated (PLC signature will be checked) before being queued.

@@ -5,6 +5,9 @@ version.workspace = true
edition.workspace = true
rust-version.workspace = true

[features]
default = ["arbitrary"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than making this test-related feature a default I think the relevant code sections should be feature-gated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. That is indeed better. Fixed in e855b6f.

Copy link
Contributor

@twittner twittner left a comment

Choose a reason for hiding this comment

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

👍

@akonring akonring merged commit ae818c5 into main Apr 1, 2025
6 checks passed
@akonring akonring deleted the ak/bundle-format branch April 1, 2025 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants