-
Notifications
You must be signed in to change notification settings - Fork 95
feat: redefine sequencer blocks, celestia blobs as protobuf #395
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
Conversation
|
Opened a new PR because something broke #355. No changes with respect to the most recent review. |
5416bda to
4d09a0b
Compare
crates/astria-proto/proto/astria/sequencer/v1alpha2/celestia.proto
Outdated
Show resolved
Hide resolved
noot
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs more discussionin light of #401
c24fccb to
c857752
Compare
|
The proto lint will fail because this is a breaking proto buf change (which is ok, because the proto definitions were never used) |
87c569e to
fee81b9
Compare
fee81b9 to
316f304
Compare
| .await | ||
| .wrap_err("failed constructing optimism hook")?; | ||
|
|
||
| let executor = Executor::builder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
| // The proof that the rollup transactions are included in the CometBFT block this | ||
| // sequencer block is derived form. This proof together with | ||
| // `Sha256(MTH(rollup_transactions))` must match `header.data_hash`. | ||
| // `MTH(rollup_transactions)` is the Merkle Tree Hash derived from the | ||
| // rollup transactions. | ||
| astria.sequencer.v1alpha1.Proof rollup_transactions_proof = 3; | ||
| // The proof that the rollup IDs listed in `rollup_transactions` are included | ||
| // in the CometBFT block this sequencer block is derived form. | ||
| // | ||
| // This proof is used to verify that the relayer that posts to celestia | ||
| // includes all rollup IDs and does not censor any. | ||
| // | ||
| // This proof together with `Sha256(MTH(rollup_ids))` must match `header.data_hash`. | ||
| // `MTH(rollup_ids)` is the Merkle Tree Hash derived from the rollup IDs listed in | ||
| // the rollup transactions. | ||
| astria.sequencer.v1alpha1.Proof rollup_ids_proof = 4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the proofs are in here but not the commitments? the proof can't be used without the commitment (leaf) value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The leaves can be (and are) reconstructed ad hoc from the transactions. This used to be checked in the replaced sequencer block data (the leaf was reconstructed and checked against the root in the object).
I think the comment mentions this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, i see you are planning to just reconstruct the commitments everywhere
| // Tree Hash deriveed from the rollup transactions. | ||
| // Always 32 bytes. | ||
| bytes rollup_transactions_root = 3; | ||
| // The proof that the rollup transactions are included in sequencer block. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // The proof that the rollup transactions are included in sequencer block. | |
| // The proof that the rollup transactions are included in sequencer block header. |
| astria.sequencer.v1alpha1.Proof rollup_transactions_proof = 4; | ||
| // The proof that the rollup IDs are included in sequencer block. | ||
| // Corresponds to `astria.sequencer.v1alpha.SequencerBlock.rollup_ids_proof`. | ||
| astria.sequencer.v1alpha1.Proof rollup_ids_proof = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no rollup_ids_root? because you plan to reconstruct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be reconstructed ad hoc from the list of IDs.
In fact one needs to do this always anyways to ensure the stored is correct. But then we can just skip it.
See the comment and the constructor of the idiomatic native type.
| /// The original `CometBFT` header that was the input to this sequencer block. | ||
| header: tendermint::block::header::Header, | ||
| /// The collection of rollup transactions that were included in this block. | ||
| rollup_transactions: IndexMap<RollupId, Vec<Vec<u8>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how does IndexMap's ordering work compared to BTreeMap? assuming it's different, that's a breaking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new grouping mechanism is used inside sequencer as well. You can see that the snapshot test is unaffected.
When the indexmap is constructed it is ordered by its keys after the fact. But this is the same order as that used for the btreemap.
There was a problem hiding this comment.
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 IndexMap over BTreeMap?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BtreeMap is balancing while it's being constructed. That gives nlogn overhead. Insertion the indexmap is linear and the final ordering has the same nlogn
Access on the other hand is O(1) where it is log n for the btreemap.
The indexmap is cache efficient, the btreemap is not.
So for our purpose the index map is strictly better.
| // The proof that the rollup transactions are included in the `CometBFT` block this | ||
| // sequencer block is derived form. This proof together with | ||
| // `Sha256(MTH(rollup_transactions))` must match `header.data_hash`. | ||
| // `MTH(rollup_transactions)` is the Merkle Tree Hash derived from the | ||
| // rollup transactions. | ||
| rollup_transactions_proof: merkle::Proof, | ||
| // The proof that the rollup IDs listed in `rollup_transactions` are included | ||
| // in the `CometBFT` block this sequencer block is derived form. This proof together | ||
| // with `Sha256(MTH(rollup_ids))` must match `header.data_hash`. | ||
| // `MTH(rollup_ids)` is the Merkle Tree Hash derived from the rollup IDs listed in | ||
| // the rollup transactions. | ||
| rollup_ids_proof: merkle::Proof, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question as on the proto - how come the proofs are here but the commitments aren't? is it because you plan to always reconstruct the rollup_transactions_root from rollup_transactions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but this was always done anyway to ensure these data types uphold invariants and that the stored root and the data it is constructed from is correct.
But it need not be written to the wire format.
If it ever becomes necessary to give repeated access to this root we can add it to the idiomatic types as is done for the cometbft header block hash.
| }) | ||
| } | ||
|
|
||
| /// Converts from the raw decoded protobuf representatin of this type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// Converts from the raw decoded protobuf representatin of this type. | |
| /// Converts from the raw decoded protobuf representation of this type. |
| let rollup_transactions_proof = 'proof: { | ||
| let Some(rollup_transactions_proof) = rollup_transactions_proof else { | ||
| break 'proof Err(SequencerBlockError::FieldNotSet( | ||
| "rollup_transactions_proof", | ||
| )); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the 'proof syntax over just returning the error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two steps in this block: one ensures the protobuf value was set, the next one actually does the conversion.
These two steps belong together logically and I didn't want to declare variables at the function level namespace.
I also think this is neat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
break 'proof Err(SequencerBlockError::FieldNotSet(
"rollup_transactions_proof",
));
this is logically the same as:
return Err(SequencerBlockError::FieldNotSet(
"rollup_transactions_proof",
));
in this scenario right? just checking for my own sanity lol
| } | ||
|
|
||
| // TODO: This can all be done in-place once https://github.com/rust-lang/rust/issues/80552 is stabilized. | ||
| pub fn group_sequence_actions_in_signed_transaction_transactions_by_rollup_id( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| pub fn group_sequence_actions_in_signed_transaction_transactions_by_rollup_id( | |
| pub fn group_sequence_actions_in_signed_transactions_by_rollup_id( |
| UncheckedCelestiaSequencerBlob, | ||
| }; | ||
| use sequencer_types::ChainId; | ||
| // use sequencer_types::ChainId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove
| // The original CometBFT header that was the input to this sequencer block. | ||
| tendermint.types.Header header = 1; | ||
| // The collection of rollup transactions that were included in this block. | ||
| repeated RollupTransactions rollup_transactions = 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an order that matters here? The order of rollups shouldn't matter?
| let height = client | ||
| .submit_sequencer_blocks( | ||
| sequencer_block_data, | ||
| sequencer_blocks, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine right now, but I wonder if we should have an earlier step to process the blobs. So it's simply "publish the blobs" since we don't need to block creating the blobs for each sequencer block on celestia post finishing.
| // XXX: This log is very misleading. The field called `sequencer_chain_id` is actually | ||
| // the rollup ID. | ||
| // | ||
| // info!( | ||
| // celestia_namespace = %Base64Display::new(sequencer_namespace.as_bytes(), | ||
| // &STANDARD), sequencer_chain_id = %cfg.chain_id, | ||
| // "celestia namespace derived from sequencer chain id", | ||
| // ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove/improve
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| #[cfg_attr(feature = "serde", serde(transparent))] | ||
| pub struct ChainId { | ||
| pub struct RollupId { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should follow up with changes to any configs + helm charts
| /// The original `CometBFT` header that was the input to this sequencer block. | ||
| header: tendermint::block::header::Header, | ||
| /// The collection of rollup transactions that were included in this block. | ||
| rollup_transactions: IndexMap<RollupId, Vec<Vec<u8>>>, |
There was a problem hiding this comment.
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 IndexMap over BTreeMap?
| /// | ||
| /// At the moment there are no invariants upheld by [`CelestiaRollupBlob`] so | ||
| /// they can be converted directly into one another. This can change in the future. | ||
| pub struct UncheckedCelestiaRollupBlob { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the "unchecked" verbiage here? There is nothing checked when converting? It's just pub access vs not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, thats the case here.
Honestly no reason other than being symmetric with the sequencer blob.
I'm not a big fan of the unchecked versions. But yes, it's to give pub access to the fields to avoid allocations.
| @@ -1,24 +1 @@ | |||
| // Calculates the `last_commit_hash` given a Tendermint [`Commit`]. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete this file?
| let signals = spawn_signal_handler(); | ||
|
|
||
| let rollup_id = | ||
| proto::native::sequencer::v1alpha1::RollupId::from_unhashed_bytes(&cfg.chain_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename the config value also to rollup_id
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure about this. It's m technically not the ID but just the name. The ID are the 32 bytes and I don't want to cause confusion between these two.
Is rollup_name good? Also CC @joroshiba
| sequencer_chain_id = %cfg.chain_id, | ||
| "celestia namespace derived from sequencer chain id", | ||
| ); | ||
| // XXX: This log is very misleading. The field called `sequencer_chain_id` is actually |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, do you wanna delete/update it?
| /// | ||
| /// Drops a blob under the following conditions: | ||
| /// + the blob's namespace does not match the provided [`Namespace`] | ||
| /// + cannot be decode/convert to [`CelestiaRollupBlob`] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// + cannot be decode/convert to [`CelestiaRollupBlob`] | |
| /// + cannot decode/convert to [`CelestiaRollupBlob`] |
| /// + the blob's namespace does not match the provided [`Namespace`] | ||
| /// + cannot be decode/convert to [`CelestiaRollupBlob`] | ||
| /// + block hash does not match that of [`CcelestiaSequencerBlob`] | ||
| /// + the proof, ID, and transactions recorded in the blob cannot be verified against the seuencer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// + the proof, ID, and transactions recorded in the blob cannot be verified against the seuencer | |
| /// + the proof, ID, and transactions recorded in the blob cannot be verified against the sequencer |
Summary
This patch defines protobuf versions of various types that are currently only defined in Rust and exchange as json-serialized text.
Background
astria-protois intended to be the single source of truth for the wire format of data that is exchanged between services. At the momentSequencerBlockData,SequencerNamespaceData,RollupNamespaceDataandInclusionProofare only defined in Rust. Some older versions of these exist as protobuf but were never used in the public interfaces.Changes
SequencerBlockDataby a protobufSequencerBlock(rewriting this protobuf type is technically a breaking change, but it was never used, so in practice it is not)SequencerNamespaceDataandRollupNamespaceDataby protobufCelestiaSequencerBlobandCelestiaRollupBlob.RollupTransactions,Proofto decode/encode all required objectsTesting
Related Issues
#12 originally defined sequencer blocks and transactions as protobuf, but these were never actually implemented.
#86 is the reason this effort was started.