Conversation
…air, confirm, finish, votes, gossip, and settlement all apply the same cold/warm/sibling rule. Bound slots stay exclusive; sibling warm keys are derived from consensus state without a bridge call or extra binding, and new endpoints should reuse SlotActors rather than a parallel identity check.
…signature cannot be replayed as another message type. Protobuf omits the type name, so MsgValidation/MsgValidationVote and timeout/error-miss contents could marshal identically; CanonicalSignedBytes prefixes unique tags and CI rejects interchangeable layouts.
…erAllowedLocked like confirm/finish so sibling acks stay state-only and a failed resolution cannot apply.
Draft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Unify host signature identity
This PR is three things only: unify identity, fix the bugs the old split allowed, and make proto preimage collisions fail CI instead of shipping as replay.
1. Refactoring for unification
Host authentication has two halves. They were mixed; they are not mixed now.
CanonicalAckBytes,CanonicalSignedBytes, …)signing.SlotActors.AllowsonlyThere is still one
ecrecover(signing.Verifier). There is no second crypto path. The refactor deletes per-message identity forks (IsWarmKeyForSlot, hand-written HeightAck cold-or-warm, settlement’s own map walk, repair’s own slot-key check).Allowsis the same sequence everywhere:WarmKeys[slot]is set: that binding is exclusive (cold or that warm). Stop.AcceptWarm(live authz). Nil = state only.Steps 1–3 are consensus state. Only step 4 may touch the chain, and only for an unbound slot.
Callers do not build
SlotActorsinline. One constructor per mutation right:WarmKeys?hostSignerAllowedLocked→ResolveWarmKeyhostSignerCachedLockedhost.SlotActors()(request-scoped memo)CheckWarmKey)LogPlaneState.actors()CheckWarmKey)HeightAck apply is not special. L2 still uses
CheckWarmKeybecause compose trials prefixes that can be dropped.applyHeightAckuseshostSignerAllowedLocked, same as confirm / finish / votes. HTTP admission and user-diff owner match stay outsideSlotActors.New signed messages reuse this: one canonical-bytes helper, recover with
Verifier, identity via the table above. Do not add a new “is this a warm key” helper.2. Bugs fixed
These were real, not style.
HeightAck identity ≠ confirm/finish identity. Same
ecrecover, different “who may sign.” Ack accepted cold or warm depending on which branch you hit; confirm used cached warm + sibling + bind-on-apply. A heartbeat can name a slot that has never executed, so the fork showed up as INVALID acks, Testermint hangs, and first-time / sibling warm acks that compose admitted and applyCore rejected.applyHeightAckfetched warm keys from chain as a side path. After L2 already admitted the signer it didif recovered != cold { ResolveWarmKey(...) }and ignored the result. Consequences:WarmKeys) still called the bridge.Fixed:
hostSignerAllowedLocked. Sibling stays state-only and does not writeWarmKeys[slot]. Failed resolution is an apply error.Bound slot was not exclusive on every path. A sibling warm key, or a live authz answer, could override a binding already in state.
Allowsstops at step 2 whenWarmKeys[slot]is set. Covered byTestApplyConfirmStart_BoundSlotRejectsSiblingWarmKey/ sibling HeightAck tests (no bridge, no second binding).Signature replay across proto types. Protobuf does not encode the message type name. Two messages with the same field numbers and wire types marshal to the same bytes, so a signature over one verified as the other:
MsgValidation(valid)MsgValidationVote(vote_valid)TimeoutVoteContentwithreasonomitted (proto3)ErrorMissVoteContent(no field 4)Fixed by domain prefixes on those four families (
devshard.validation.v1,devshard.validationvote.v1,devshard.timeoutvote.v1,devshard.errormissvote.v1). Protocol break for those families only: existing signatures will not verify. Do not mix this binary with a v5 signer on an escrow that already has those votes. Height-sync tags and unprefixed finish / receipt / state / user diffs are unchanged.Not fixed here: sticky
WarmKeysafter bind / rotate / revoke (warm-key-revoke.md).3. Automation against proto replay
Domain tags fix the collisions we know. CI must reject the next one.
TestSignedProtoPreimagesAreNotInterchangeable(and siblings indevshard/types/signed_preimage_test.go):proto/devshard/v1.CanonicalSignedBytesrejects a nil message.make testruns this. The unit-test workflow has a named step so a collision is its own check, not a buriedgo test ./...failure:Adding a signed proto without a unique domain or a disjoint layout cannot merge.
Test plan
go test ./signing/ ./types/ ./state/ ./heightsync/ ./host/ ./transport/ ./user/-runas CI)WarmKeysentryValidateDiffcarriesWarmKeyDeltaMsgValidationVote; timeout reject does not verify as error-miss accept