Skip to content

Add StaticRound to eliminate some boilerplate when writing protocols - #117

Merged
fjarri merged 15 commits into
entropyxyz:masterfrom
fjarri:static-round
Sep 2, 2025
Merged

Add StaticRound to eliminate some boilerplate when writing protocols#117
fjarri merged 15 commits into
entropyxyz:masterfrom
fjarri:static-round

Conversation

@fjarri

@fjarri fjarri commented Jun 7, 2025

Copy link
Copy Markdown
Contributor

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

dvdplm commented Jun 9, 2025

Copy link
Copy Markdown
Contributor

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

fjarri commented Jun 9, 2025

Copy link
Copy Markdown
Contributor Author

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

coveralls commented Jun 10, 2025

Copy link
Copy Markdown

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

fjarri commented Jun 11, 2025

Copy link
Copy Markdown
Contributor Author

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

@dvdplm dvdplm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Another partial review.

Comment thread examples/dining_cryptographers.rs
Comment thread examples/dining_cryptographers.rs
Comment thread examples/dining_cryptographers.rs Outdated

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Comment thread examples/dining_cryptographers.rs Outdated
Comment thread examples/dining_cryptographers.rs
Comment thread manul/benches/empty_rounds.rs
Comment thread manul/src/combinators/chain.rs Outdated
Comment thread manul/src/combinators/chain.rs Outdated
Comment thread manul/src/combinators/extend.rs Outdated
Comment thread manul/src/combinators/extend.rs Outdated
Comment thread manul/src/combinators/extend.rs Outdated
Comment thread manul/src/combinators/extend.rs Outdated
Comment thread manul/src/dev/run_sync.rs Outdated
Comment thread manul/src/combinators/chain.rs
Comment thread examples/dining_cryptographers.rs
Comment thread examples/dining_cryptographers.rs
Comment thread manul/src/protocol/dyn_evidence.rs Outdated
Comment thread manul/src/protocol/dyn_evidence.rs Outdated
Comment thread manul/src/protocol/dyn_round.rs Outdated
Comment thread manul/src/protocol/dyn_round.rs Outdated
Comment thread manul/src/protocol/dyn_round.rs Outdated

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

Copy link
Copy Markdown
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.

Comment thread examples/src/simple.rs Outdated
Comment thread examples/src/simple.rs
Comment thread manul/benches/async_session.rs Outdated
Comment thread manul/src/combinators/extend.rs Outdated
Comment thread manul/src/session/session.rs
Comment thread manul/src/session/wire_format.rs Outdated
Comment thread manul/src/tests/partial_echo.rs
Comment thread manul/src/session/evidence.rs Outdated
Comment thread manul/src/protocol/round.rs
@fjarri

fjarri commented Aug 28, 2025

Copy link
Copy Markdown
Contributor Author

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

Comment thread examples/src/simple.rs Outdated
@dvdplm

dvdplm commented Aug 29, 2025

Copy link
Copy Markdown
Contributor

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.

Comment thread examples/src/simple.rs
@fjarri

fjarri commented Aug 29, 2025

Copy link
Copy Markdown
Contributor Author

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

fjarri commented Aug 29, 2025

Copy link
Copy Markdown
Contributor Author

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

dvdplm commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

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