refactor: extract from_parts on SSVMessage and SignedSSVMessage - #991
refactor: extract from_parts on SSVMessage and SignedSSVMessage#991shane-moore wants to merge 1 commit into
Conversation
|
@claude, please take a look |
|
Claude finished @shane-moore's task in 43s —— View job Code Review:
|
b7ec220 to
e7e753b
Compare
e7e753b to
7dc5570
Compare
|
I do not think we should add a public constructor that can create semantically invalid The SSZ decode path is already necessarily unvalidated because decoding has to happen before semantic validation. But that does not mean we should add another public construction path that bypasses The spec-test motivation makes sense, but I think the workaround should stay outside the production constructor API: build raw SSZ fixture bytes in the spec-test crate, add a clearly test-only helper, or introduce a separate unchecked/raw fixture type. I would prefer not to expose this on the production |
|
Fair point on production API surface — agreed. Will gate reason for this approach besides keeping the default public surface clean: this is essentially your option 2 (clearly test-only helper) in concrete form. The symbol only exists when the spec-test feature is on, the feature name explicitly labels it test-only, and it matches the arbitrary-fuzz pattern already on this file. If you had a different shape in mind for option 2, let me know. Options 1 and 3 we'd want to avoid because neither actually exercises the production encoder |
|
I opened #1009 to track the broader type-design issue here: raw SSZ-shaped messages and validated protocol messages are currently represented by the same Rust types. For this PR, I do not think we need to solve that full refactor. The immediate thing I would avoid is adding an inherent constructor on The long-term direction in #1009 is to introduce raw wire-container types, so SSZ decoding and max-size fixture construction can work with raw messages while normal production code receives validated message types. |
|
Thanks for opening #1009 — that's the right home for this. Rather than implement the bytes-returning helper, I'd like to defer the Concretely:
Wdyt? |
Problem, Evidence, and Context
SSVMessage::newandSignedSSVMessage::newbundle two distinct concerns: SSZ-bounds construction and semantic validation (operator_idsnon-zero/sorted/deduped,MsgType↔Datashape, etc.). Callers that need to construct a message at the on-wire size limit but bypass semantic invariants — e.g. spec-test fixtures that probe the SSZ encoder against intentionally-malformed-but-bounds-valid inputs — have no clean path today, leaving a duplication-or-private-fields choice.Change Overview
pub fn from_parts(...)on both types: bounds-only construction (variable-length size checks viatry_to_variable_list).new()becomes a one-liner:Self::from_parts(...)?.validate()?on both.new()— sameResult, same error variants in the same order (bounds first, then validate).Risks, Trade-offs, and Mitigations
from_partsis a lower-guarantee constructor thannew()— production code should keep usingnew(). The doc comment on eachfrom_partsreferencesvalidateso the bypass is visible at the call site. No production callers offrom_partsare added in this PR.Validation
cargo test -p ssv_types --release: 80 unit tests + 2 doctests passmake cargo-fmt-checkcleanmake lintclean for the touched cratenew()callers exercise the samefrom_parts → validatepath post-refactorRollback
Single-file revert. No data, config, or runtime impact.
Additional Info / Next Steps
Splits out of the
test/structure-size-testbranch so this small refactor can land independently. The follow-up spec-test PR (which addsmaxmsgsize.StructureSizeTest) consumesfrom_partsto encode max-size SSV/SignedSSV fixtures whose Go-side construction intentionally violates Anchor's strictervalidate()— e.g., zero-paddedOperatorIDsto reach max wire size, or oversizedDataagainstMsgType.