-
Notifications
You must be signed in to change notification settings - Fork 77
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
feat(sequencer): implement proving of action tree root inside Header::data_hash
#286
Conversation
…ria into noot/sequencer-proofs
…ria into noot/sequencer-proofs
…ria into noot/sequencer-proofs
// we don't need to verify the action tree root here, | ||
// as [`SequencerBlockData`] would not be constructable | ||
// if it was invalid | ||
|
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 empty line between code + comment?
action_tree_root_inclusion_proof | ||
.verify(action_tree_root.as_bytes(), data_hash) | ||
.wrap_err("failed to verify action tree root inclusion proof")?; | ||
// TODO: also verify last_commit_hash (#270) |
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: full link preferable
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.
Please provide a full link to the issue or PR, like so:
// TODO: also verify last_commit_hash (#270) | |
// TODO(https://github.com/astriaorg/astria/issues/270): also verify last_commit_hash |
@@ -97,12 +122,23 @@ impl SequencerBlockData { | |||
|
|||
#[allow(clippy::type_complexity)] | |||
#[must_use] | |||
pub fn into_values(self) -> (Hash, Header, Option<Commit>, HashMap<Namespace, RollupData>) { | |||
pub fn into_values( | |||
self, |
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 this formatting correct? Feels weird to push the self to new line because the return is
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 is what rustfmt wants :|
@@ -4,7 +4,7 @@ version = "0.1.0" | |||
edition = "2021" | |||
|
|||
[dependencies] | |||
ct-merkle = "0.1" | |||
ct-merkle = { version = "0.1", features = ["serde"] } |
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.
Should this be using the workspace version?
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 workspace version is under a patch, not an actual import, so it overrides this w/ the patch
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.
Saw a few things that should be fixed prior to accepting.
Orthogonal to this PR I had a chance to read the wrapper of CtMerkle
. I was wondering why you chose to implement this as a newtype wrapper instead of using an extension trait (or simply utility functions)?
The difficulty with wrappers is often that one ends up reimplementing more and more of the methods of the wrapped type, without providing proper documentation. That could be avoided by simply using the type directly.
EDIT: After reading #256 and thinking it through I agree with not directly exposing ctmerkle and its types.
@@ -4,7 +4,7 @@ version = "0.1.0" | |||
edition = "2021" | |||
|
|||
[dependencies] | |||
ct-merkle = "0.1" | |||
ct-merkle = { version = "0.1", features = ["serde"] } |
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.
ct-merkle
is imported here, but there is a ct-merkle
in the workspace cargo toml that refers to a git repo under the astria org.
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 that's under the patch section (i had to make some changes to the lib, see here rozbb/ct-merkle#2)
pub struct SequencerBlockData { | ||
pub(crate) block_hash: Hash, | ||
pub(crate) header: Header, | ||
/// This field should be set for every block with height > 1. | ||
pub(crate) last_commit: Option<Commit>, | ||
/// namespace -> rollup data (chain ID and transactions) | ||
pub(crate) rollup_data: HashMap<Namespace, RollupData>, | ||
/// The root of the action tree for this block. | ||
pub(crate) action_tree_root: Hash, | ||
/// The inclusion proof that the action tree root in included |
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.
typo:
/// The inclusion proof that the action tree root in included | |
/// The inclusion proof that the action tree root is included |
action_tree_root_inclusion_proof | ||
.verify(action_tree_root.as_bytes(), data_hash) | ||
.wrap_err("failed to verify action tree root inclusion proof")?; | ||
// TODO: also verify last_commit_hash (#270) |
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.
Please provide a full link to the issue or PR, like so:
// TODO: also verify last_commit_hash (#270) | |
// TODO(https://github.com/astriaorg/astria/issues/270): also verify last_commit_hash |
@@ -97,12 +122,23 @@ impl SequencerBlockData { | |||
|
|||
#[allow(clippy::type_complexity)] | |||
#[must_use] | |||
pub fn into_values(self) -> (Hash, Header, Option<Commit>, HashMap<Namespace, RollupData>) { | |||
pub fn into_values( |
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 is a code smell. We should differentiate between a "raw" SequencerBlockData
as a (de)serialization format (where it's ok to make all fields pub), and "verified" SequencerBlockData
that upholds invariants (where you want to prevent anyone pulling the rug from under you). Permitting clippy::type_complexity
proves that point.
My suggestion: keep the SequencerBlockData
as is, but introduce a RawSequencerBlockData
that has exactly the same fields, but comes with a RawSequencerBlockData::verify(self) -> eyre::Result<SequencerBlockData>
. This way you can keep using SequencerBlockData
everywhere as is.
Next, scrap SequencerBlockData::into_values
and introduce SequencerBlockData::into_raw
. This way users can do the following instead of destructuring tuples:
fn do_something(data: SequencerBlockData) {
let RawSequencerBlockData {
block_hash,
...
} = data.into_raw();
further_processing(block_has);
}
We then have an easier path forward to move this into astria-proto
.
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 I didn't really like this either but wasn't sure if I should just make another type with all pub fields. will update!
@@ -136,19 +172,36 @@ impl SequencerBlockData { | |||
/// # Errors | |||
/// | |||
/// - if the block has no data hash | |||
/// - if the block has no 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.
This list is great when eventually moving to typed errors, which we should really do...
); | ||
let action_tree_root_inclusion_proof = tx_tree | ||
.prove_inclusion(0) // action tree root is always the first tx in a block | ||
.expect("failed to generate inclusion proof for action tree root"); |
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.
Looks like this whould have been a wrap_err
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.
Looks good to me. Please address the comment re deserialization so that there is no backdoor into creating types where invariants are violated (in this case inclusion proofs).
crates/astria-sequencer/Cargo.toml
Outdated
@@ -10,6 +10,7 @@ astria-sequencer-validation = { path = "../astria-sequencer-validation" } | |||
telemetry = { package = "astria-telemetry", path = "../astria-telemetry" } | |||
|
|||
anyhow = "1" | |||
digest = "0.10" |
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.
rm?
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.
woops haha
@@ -63,15 +72,29 @@ impl SequencerBlockData { | |||
header: Header, | |||
last_commit: Option<Commit>, | |||
rollup_data: HashMap<Namespace, RollupData>, | |||
action_tree_root: Hash, | |||
action_tree_root_inclusion_proof: InclusionProof, |
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.
Comment on this entire new constructor: now that you have RawSequencerBlockData
, I would replace this ever-growing function with SequencerBlockData::try_from_raw
or smth like that.
self.last_commit, | ||
self.rollup_data, | ||
) | ||
pub fn into_raw(self) -> RawSequencerBlockData { |
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.
Please add a comment on top of this function.
Nit: destructuring self
looks slightly nicer (less self.
)
let Self {
block_hash,
header,
// and the rest
} = self;
RawSequencerBlockData {
block_hash,
header,
// and the rest
}
@@ -42,14 +46,19 @@ pub struct RollupData { | |||
|
|||
/// `SequencerBlockData` represents a sequencer block's data | |||
/// to be submitted to the DA layer. | |||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] | |||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] | |||
pub struct SequencerBlockData { | |||
pub(crate) block_hash: Hash, |
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.
Do you still need all of this to be pub(crate)
?
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 :D
@@ -42,14 +46,19 @@ pub struct RollupData { | |||
|
|||
/// `SequencerBlockData` represents a sequencer block's data | |||
/// to be submitted to the DA layer. | |||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] | |||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] |
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.
serde provides this attribute: https://serde.rs/container-attrs.html#try_from
Can you implement a TryFrom<RawSequencerBlockData>
(I'd prefer to not impl a single-use TryFrom
and instead use a custom deserializer, but in this case it's probably fine).
This way you can still deserialize, but in addition you get verification. Right now you still serialize directly into a SequencerBlockData
and have no assurance that it's legitimate (invariants upheld, etc).
Also see https://serde.rs/container-attrs.html#into for the inverse
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, added!
* main: fix(sequencer): fix `deliver_tx` to accept the action tree root (#291) feat(composer): adding more docs (#295) feat(sequencer): implement proving of action tree root inside `Header::data_hash` (#286) ci: Update to reusable success, nested names (#306) feat(sequencer, relayer): generate and write merkle inclusion proofs as part of `RollupNamespaceData` (#256) ci: broad restructuring of worklows (#280)
Summary
data_hash
(aka transactions root)Background
Changes
SequencerBlockData
andSequencerNamespaceData
to containaction_tree_root
andaction_tree_root_inclusion_proof
SequencerBlockData::from_tendermint_block
now generates the inclusion proofSequencerBlockData::new()
checks the inclusion proofTesting
Related Issues
#153 (does not close yet, follow up will close it)