Skip to content

Conversation

fjarri
Copy link
Contributor

@fjarri fjarri commented Jun 7, 2025

Main changes:

  • Round now uses associated types for messages, payloads, and artifacts. Fixes Typed Round trait #65. Most dynamic parts (BoxedFormat, ProtocolMessagePart, serialization/deserialization of messages etc) are hidden from the user.
  • Evidence verification methods are moved from Protocol to corresponding Round implementors. This allows one to keep the related logic (receive_message() and evidence verification) together.

Corollary changes:

#61 and #62 need to be reconsidered - while writing an extended/zipped protocol is easier with static rounds, the evidence verification is complicated since one needs to cast EvidenceMessages to the inner protocol type somehow.

@dvdplm
Copy link
Contributor

dvdplm commented Jun 9, 2025

I have skimmed the code, so this is not a review.

Thoughts:

  • manul is likely never going to be a performance bottleneck
  • …so we can "afford" to prioritize readability, ergonomics, correctness and maintainability.
  • I think we should make opinionated choices, aiming at giving users one correct way to do what they need to do.

Given the above I think we should provide either dynamic or static rounds, but not both. When we have a concrete use case that requires both dynamic and static we can re-assess.

Is switching to only static rounds possible? It'd be interesting to see how synedrion would look with only static rounds and if there'd be less boilerplate.

@fjarri
Copy link
Contributor Author

fjarri commented Jun 9, 2025

Is switching to only static rounds possible?

For protocols themselves, yes, but evidence verification is not limited to one round and needs access to the message types from the previous rounds - so the user would have to manually transform them from some untyped form to typed messages.

Although it is possible to automate deserialization (and the handling of its errors) - by making every Round type responsible for deserializing its messages and putting them in a Box<>, which the user would later downcast to a specific type. A possible error for this operation is a LocalError.

This would need some kind of "routing" of round number -> boxed round type (not the round object, since we don't need the round state for that). This may be also used to simplify evidence checking for invalid messages (#82).

@fjarri fjarri force-pushed the static-round branch 2 times, most recently from 8de19fb to 0ef0c98 Compare June 10, 2025 21:35
@coveralls
Copy link

coveralls commented Jun 10, 2025

Pull Request Test Coverage Report for Build 17362353327

Details

  • 666 of 1311 (50.8%) changed or added relevant lines in 20 files are covered.
  • 66 unchanged lines in 11 files lost coverage.
  • Overall coverage decreased (-5.1%) to 67.065%

Changes Missing Coverage Covered Lines Changed/Added Lines %
manul/src/dev/run_sync.rs 6 8 75.0%
manul/src/session/transcript.rs 0 2 0.0%
manul/src/protocol/wire_format.rs 6 12 50.0%
manul/src/session/message.rs 18 24 75.0%
manul/src/protocol/dyn_evidence.rs 28 35 80.0%
manul/src/protocol/errors.rs 0 8 0.0%
manul/src/protocol/rng.rs 0 12 0.0%
manul/src/protocol/round_id.rs 22 34 64.71%
manul/src/session/session.rs 28 40 70.0%
manul/src/protocol/message.rs 8 29 27.59%
Files with Coverage Reduction New Missed Lines %
manul/src/dev/tokio.rs 1 96.67%
manul/src/session/transcript.rs 1 59.56%
manul/src/session/echo.rs 2 51.63%
manul/src/session/message.rs 2 90.21%
manul/src/dev/run_sync.rs 4 83.43%
manul/src/session/tokio.rs 4 87.18%
manul/src/protocol/message.rs 5 61.62%
manul/src/combinators/chain.rs 7 48.52%
manul/src/protocol/round_id.rs 8 60.0%
manul/src/session/session.rs 9 78.6%
Totals Coverage Status
Change from base Build 15695642792: -5.1%
Covered Lines: 2299
Relevant Lines: 3428

💛 - Coveralls

@fjarri
Copy link
Contributor Author

fjarri commented Jun 11, 2025

Current roadblock: handling protocols where some nodes do not send or do not receive messages (e.g. KeyResharing). This means that the type of the round is not enough to determine whether a message is expected, it's also the state that's needed.

In the n-of-n case the static rounds eliminate the need for verify_*_is_invalid() methods, and also allow one to split evidence checking into per-round methods, making them much easier to maintain.

@fjarri fjarri force-pushed the static-round branch 2 times, most recently from dc0371a to fa2028b Compare June 18, 2025 22:51
@fjarri fjarri force-pushed the static-round branch 4 times, most recently from 2b67199 to 49bb410 Compare June 29, 2025 23:25
@fjarri fjarri force-pushed the static-round branch 5 times, most recently from 733fd6b to 9ff447c Compare July 18, 2025 17:48
@fjarri fjarri marked this pull request as ready for review July 18, 2025 18:55
Copy link
Contributor

@dvdplm dvdplm left a comment

Choose a reason for hiding this comment

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

Another partial review.

@@ -217,6 +200,14 @@ impl Round<DinerId> for Round1 {

impl Round<DinerId> for Round2 {
type Protocol = DiningCryptographersProtocol;
type ProtocolError = NoProtocolErrors<Self>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Naming nitpick: the associated type is in the singular, so it's a bit odd to assign a value whose name is plural. OTOH calling it "NoProtocolError" isn't great either. Not sure there is a great solution to be found here (idiomatically it should really be () but that isn't possible as we saw above).

Maybe DummyProtocolError?

Copy link
Contributor

Choose a reason for hiding this comment

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

A random thought I had:
I wonder how far we'd be able to get if we changed the Round trait to make the type ProtocolError take a impl core::error::Error. Maybe then we could impl Round with () as the error type when there are no errors, but then we'd need some clever trick to transform or cast the impl core::error::Error into an actual ProtocolError with all the methods and trait bounds we need.

I read through rust-lang/rust#99301 which seems to be about ways to access data from nested errors in a generic way. Seems a bit stuck though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we can instead rename the associated type to ProtocolErrors?

Copy link
Contributor

Choose a reason for hiding this comment

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

But wouldn't the plural on a type name imply that it is a collection of types of protocol errors?

Copy link
Contributor

Choose a reason for hiding this comment

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

I tried to impl ProtocolError for core::convert::Infallible (described as "The error type for errors that can never happen." in the docs) and it kinda works if it wasn't for the ser/deser bounds on ProtocolError. That's sort of what I was trying to hint at by saying "it'd be so nice if we could use a impl core::error::Error and then – handwaves – transfom/cast to concrete error types": we could have less bounds.

Anyway, all of this is nitpicking. The code is fine as-is, modulo perhaps the name.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can instead rename the associated type to ProtocolErrors?

This is better.

I still think it's awkward that there isn't a better way to do this but that has nothing to do with this PR.

@@ -217,6 +200,14 @@ impl Round<DinerId> for Round1 {

impl Round<DinerId> for Round2 {
type Protocol = DiningCryptographersProtocol;
type ProtocolError = NoProtocolErrors<Self>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can instead rename the associated type to ProtocolErrors?

This is better.

I still think it's awkward that there isn't a better way to do this but that has nothing to do with this PR.

@fjarri
Copy link
Contributor Author

fjarri commented Aug 28, 2025

Most of the stuff seems to be addressed, what's left is the naming. To summarize various comments, the problematic parts are:

  • Round::ProtocolError and NoProtocolErrors
  • Extension
  • Extendable
  • whether extend should be a part of combinators or moved to dev

@dvdplm
Copy link
Contributor

dvdplm commented Aug 29, 2025

Extendable

For this, like for trait Extension I think we must name the type explicitly and use a noun for structs. Here it could be ExtendedEP or OverriddenEP.

@dvdplm
Copy link
Contributor

dvdplm commented Aug 29, 2025

whether extend should be a part of combinators or moved to dev

I don't have a strong opinion on this. The conservative option is to move it to dev so we can change it without caring about semver.

@fjarri
Copy link
Contributor Author

fjarri commented Aug 29, 2025

Renamed Extendable -> ExtendableEntryPoint, and Extension -> RoundExtension (RoundExt kind of clashes with the trait naming convention in the standard library)

I don't have a strong opinion on this. The conservative option is to move it to dev so we can change it without caring about semver.

We still have to care, dev is a part of the public API.

@fjarri
Copy link
Contributor Author

fjarri commented Aug 29, 2025

I like your suggestion of naming it Round::ProtocolErrors (plural).

Still thinking about this. Round::ProtocolError implies "what is the error type" (singular). NoProtocolErrors kind of fits because 0 items is plural in English.

@fjarri fjarri force-pushed the static-round branch 4 times, most recently from bddb986 to 36fa0f8 Compare August 30, 2025 18:51
@dvdplm
Copy link
Contributor

dvdplm commented Sep 1, 2025

We still have to care, dev is a part of the public API.

Riiiiight, but would a breaking change here automatically imply a major release? If it's in combinators then that would be the case for sure. If it's in dev... well, maybe yes maybe not (imo).

@fjarri fjarri merged commit 6c01a17 into entropyxyz:master Sep 2, 2025
9 checks passed
@fjarri fjarri deleted the static-round branch September 2, 2025 22:45
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.

ProtocolError mandates Display Consider renaming verify_*_is_invalid methods Simplify invalid message evidence verification Typed Round trait
3 participants