Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ COPY ./pkg/chain/ethereum/common/gen $APP_DIR/pkg/chain/ethereum/common/gen
COPY ./pkg/chain/ethereum/ecdsa/gen $APP_DIR/pkg/chain/ethereum/ecdsa/gen
COPY ./pkg/chain/ethereum/tbtc/gen $APP_DIR/pkg/chain/ethereum/tbtc/gen
COPY ./pkg/chain/ethereum/threshold/gen $APP_DIR/pkg/chain/ethereum/threshold/gen
COPY ./pkg/frost/roast/gen $APP_DIR/pkg/frost/roast/gen
COPY ./pkg/net/gen $APP_DIR/pkg/net/gen
COPY ./pkg/tbtc/gen $APP_DIR/pkg/tbtc/gen
COPY ./pkg/tecdsa/dkg/gen $APP_DIR/pkg/tecdsa/dkg/gen
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ else
$(foreach module,$(modules),$(call get_npm_package,$(module),$(environment)))
endif

# NOTE: a new *.proto package must also be added to the gen-directory COPY
# allowlist in the Dockerfile: the image strips committed **/gen/**/*.go
# (.dockerignore) and regenerates protobufs in-image, and make generate only
# sees gen directories copied before it runs.
proto_files := $(shell find ./pkg -name '*.proto')
proto_targets := $(proto_files:.proto=.pb.go)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,12 +631,30 @@ optimization given the current key model.

=== Evidence message format

*Decision: JSON payload wrapped in the existing `pkg/net/gen/pb`
envelope, routed via the `net.Message` interface.*

This matches the FROST/tbtc-signer protocol messages (Phase 1B)
and inherits the network layer's operator-key signing
automatically. Raw JSON does not appear on the wire.
*Decision: signed-body protobuf envelopes
(`pkg/frost/roast/gen/pb/evidence.proto`), routed via the
`net.Message` interface.*

Evidence signatures cover exact serialized body bytes, and those
bytes travel verbatim: a snapshot is
`SignedLocalEvidenceSnapshot{body, operator_signature}` where
`body` is the serialized `LocalEvidenceSnapshotBody`, and a
transition message is `SignedTransitionMessage{body,
coordinator_signature}` whose body embeds every member's signed
snapshot envelope verbatim (`repeated bytes signed_snapshots`).
A verifier checks each signature over the bytes it received and
only then parses them; nothing in the evidence chain is ever
re-encoded. Signature validity therefore never depends on any
serializer's canonical form -- across protobuf library versions
or across languages (the Phase 7 Rust signer verifies and parses
these same bytes). Producers marshal a body exactly once, at
signing time, and transmit those bytes.

The original Phase 3 implementation used canonical JSON
(`json.Marshal` over field-order-stable structs) as the signed
encoding; it was replaced before any persisted or cross-component
evidence existed because canonical-JSON byte stability is a
Go-implementation accident, not a portable contract.

=== Maximum evidence-message size

Expand All @@ -645,7 +663,7 @@ chunking.*

Under coordinator-aggregation, the per-transition payload is
`O(N)` not `O(N^2)`. At a 100-signer group with all four
quotas saturated the JSON-encoded bundle is ~10-20 KiB,
quotas saturated the encoded bundle is ~10-20 KiB,
comfortably within libp2p's per-message limits.

=== Session-handle binding TTL eviction
Expand Down
6 changes: 3 additions & 3 deletions pkg/frost/roast/bundle_aggregation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func signSnapshotForTest(
) *LocalEvidenceSnapshot {
t.Helper()
signer := &fakeSigner{id: snap.SenderID()}
payload, err := CanonicalSnapshotBytes(snap)
payload, err := snap.SignableBytes()
if err != nil {
t.Fatalf("canonical: %v", err)
}
Expand Down Expand Up @@ -418,7 +418,7 @@ func TestVerifyBundle_DetectsCoordinatorSignatureForgery(t *testing.T) {
// Tamper: re-sign the bundle as a different (non-elected) member.
const wrongSigner group.MemberIndex = 99
bundle.CoordinatorIDValue = uint32(wrongSigner)
payload, _ := CanonicalBundleBytes(bundle)
payload, _ := bundle.SignableBytes()
forged, _ := (&fakeSigner{id: wrongSigner}).Sign(payload)
bundle.CoordinatorSignature = forged

Expand Down Expand Up @@ -458,7 +458,7 @@ func TestVerifyBundle_DetectsSnapshotSignatureForgery(t *testing.T) {
// bundle with the new garbage signature so the bundle-level
// signature appears valid but the snapshot signature does not.
bundle.Bundle[0].OperatorSignature = []byte{0xde, 0xad}
payload, _ := CanonicalBundleBytes(bundle)
payload, _ := bundle.SignableBytes()
resign, _ := (&fakeSigner{id: elected}).Sign(payload)
bundle.CoordinatorSignature = resign

Expand Down
12 changes: 6 additions & 6 deletions pkg/frost/roast/coordinator_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,13 @@ func (c *inMemoryCoordinator) RecordEvidence(
}

if existing, present := record.snapshots[snapshot.SenderID()]; present {
existingBytes, err := CanonicalSnapshotBytes(existing)
existingBytes, err := existing.SignableBytes()
if err != nil {
return fmt.Errorf("coordinator: canonical existing: %w", err)
return fmt.Errorf("coordinator: existing signable bytes: %w", err)
}
newBytes, err := CanonicalSnapshotBytes(snapshot)
newBytes, err := snapshot.SignableBytes()
if err != nil {
return fmt.Errorf("coordinator: canonical new: %w", err)
return fmt.Errorf("coordinator: new signable bytes: %w", err)
}
if !bytes.Equal(existingBytes, newBytes) ||
!bytes.Equal(existing.OperatorSignature, snapshot.OperatorSignature) {
Expand Down Expand Up @@ -414,10 +414,10 @@ func (c *inMemoryCoordinator) AggregateBundle(
CoordinatorIDValue: uint32(coord),
Bundle: bundle,
}
payload, err := CanonicalBundleBytes(msg)
payload, err := msg.SignableBytes()
if err != nil {
c.markTransitionedLocked(handle.id)
return nil, fmt.Errorf("coordinator: canonical bundle: %w", err)
return nil, fmt.Errorf("coordinator: bundle signable bytes: %w", err)
}
sig, err := c.signer.Sign(payload)
if err != nil {
Expand Down
Loading
Loading