warp: CoronaSignature → CoronaSignature (wire byte 0x02 preserved)

Final Corona-residue sweep in vms/platformvm/warp:

  - HybridBLSRTSignature → HybridBLSCoronaSignature (the lingering "RT"
    suffix in the Go type for the deprecated BLS+lattice hybrid).
  - ErrInvalidRTSignature → ErrInvalidCoronaSignature.
  - ErrMissingRTPublicKey → ErrMissingCoronaPublicKey.
  - String() format, comments, doc references updated to Corona.

Wire-format invariants (this is on-chain encoding):

  - linearcodec assigns typeIDs by RegisterType call order. The order
    in codec.go is UNCHANGED: BitSetSignature (0x00), CoronaSignature
    (0x01), EncryptedWarpPayload (0x02), HybridBLSCoronaSignature
    (0x03), then the 3 Teleport types (0x04-0x06).
  - linearcodec serializes struct fields by declaration order (see
    reflectcodec/struct_fielder.go). Field declaration order is
    UNCHANGED for every type in this PR.
  - Therefore: type names and field names are wire-metadata only;
    renaming them produces byte-equal output.

Wire bytes verified byte-equal pre/post rename for every codec-
registered type via the new regression test:

    vms/platformvm/warp/wire_baseline_test.go

That test pins the exact hex of every type's encoding (concrete and
through the Signature interface) and is now a permanent guard against
accidental wire-format drift.

Doc sweep (no code dependencies — example/aspirational JSON keys):

  - docs/architecture/consensus.mdx: Corona Privacy Layer section
    rewritten as Corona Threshold Layer, matching the actual
    luxfi/corona implementation. The previous text described a ring-
    signature scheme that doesn't exist in this codebase.
  - docs/architecture/overview.mdx: Q-Chain diagram + cryptographic
    primitives table.
  - docs/configuration/node-config.mdx + docs/getting-started/running.mdx:
    example Q-Chain config keys (corona-* → corona-*).
  - docker/Dockerfile.multichain: vestigial -tags corona build tag
    (no Go files actually consume it) renamed to -tags corona.

Verification:
  go build ./...           exit 0
  go vet ./vms/platformvm/warp/...    clean
  go test ./vms/platformvm/warp/...   all packages PASS (warp,
                                      message, payload, zwarp)
This commit is contained in:
Hanzo AI
2026-06-03 13:01:16 -07:00
parent 4460821b5c
commit 37ad4618d2
9 changed files with 181 additions and 56 deletions
+20 -15
View File
@@ -237,8 +237,8 @@ Final Validation
### Properties
- **Security**: Quantum-resistant (192-bit)
- **Signatures**: ML-DSA-65 (Dilithium)
- **Privacy**: Corona ring signatures
- **Signatures**: ML-DSA-65 (Dilithium) + Corona (LWE-based threshold)
- **Threshold scheme**: Corona — native t-of-n in 2 rounds
- **Performance**: ~1,500 TPS
### Implementation
@@ -247,7 +247,7 @@ Final Validation
type PQConsensus struct {
classicalSigner *bls.Signer
quantumSigner *mldsa.Signer
coronaSigner *corona.Signer
coronaSigner *corona.Signer
threshold int
validatorSet []Validator
@@ -279,27 +279,32 @@ func (pq *PQConsensus) VerifyBlock(block Block, sig Signature) bool {
}
```
### Corona Privacy Layer
### Corona Threshold Layer
Corona is a lattice-based threshold signature scheme from LWE
([eprint 2024/1113](https://eprint.iacr.org/2024/1113)) implemented in
`github.com/luxfi/corona`. Unlike a ring signature, Corona signers are
known and weighted; the scheme provides a single aggregated signature
that anyone can verify against the group public key.
```go
type CoronaSignature struct {
Ring []PublicKey
Signature []byte
KeyImage []byte
Signers []byte // bitset of signing validator indices
Signature []byte // aggregated lattice signature
}
func (pq *PQConsensus) CreateRingSignature(
func (pq *PQConsensus) CoronaSign(
message []byte,
signerKey PrivateKey,
ring []PublicKey,
signerKey *corona.PrivateKey,
signers []*Validator,
) (*CoronaSignature, error) {
// Create anonymous signature within ring
sig := pq.coronaSigner.Sign(message, signerKey, ring)
sig, err := pq.coronaSigner.Sign(message, signerKey, signers)
if err != nil {
return nil, err
}
return &CoronaSignature{
Ring: ring,
Signers: sig.SignerBitset(),
Signature: sig.Bytes(),
KeyImage: sig.KeyImage(),
}, nil
}
```
+3 -3
View File
@@ -25,7 +25,7 @@ Lux implements a 4-chain primary network architecture, each optimized for specif
│ (Quantum) │
│ │
│ Post-Quantum Consensus │
Corona Signatures
│ Corona Threshold Signatures │
└─────────────────────────────────────────────────┘
```
@@ -76,7 +76,7 @@ Lux implements a 4-chain primary network architecture, each optimized for specif
**Purpose:** Post-quantum security layer
- Quantum-resistant cryptography
- ML-DSA-65 (Dilithium) signatures
- Corona ring signatures for privacy
- Corona (LWE-based) threshold signatures
- Hybrid classical/quantum consensus
**Key Features:**
@@ -301,7 +301,7 @@ Native cross-chain communication protocol:
| secp256k1 | ECDSA signatures | 128-bit |
| BLS12-381 | Threshold signatures | 128-bit |
| ML-DSA-65 | Post-quantum signatures | 192-bit |
| Corona | Ring signatures | 128-bit |
| Corona | LWE threshold signatures | 192-bit (post-quantum) |
| SHA-256 | Hashing | 128-bit |
| AES-256-GCM | Encryption | 256-bit |
@@ -310,7 +310,7 @@ Create `~/.luxd/configs/chains/Q/config.json`:
"corona-signatures-enabled": true,
"post-quantum-algorithm": "ml-dsa-65",
"quantum-signature-cache-size": 10000,
"ring-signature-size": 16,
"corona-threshold-size": 16,
"corona-key-size": 1024,
"quantum-stamp-enabled": true,
"quantum-stamp-window": "30s",
@@ -226,7 +226,7 @@ Create `~/.luxd/configs/chains/Q/config.json`:
"quantum-verification-enabled": true,
"corona-signatures-enabled": true,
"post-quantum-algorithm": "ml-dsa-65",
"ring-signature-size": 16,
"corona-threshold-size": 16,
"quantum-cache-size": 10000,
"parallel-batch-size": 10
}
+1 -1
View File
@@ -135,7 +135,7 @@ Warp 1.5 introduces new signature types for quantum-safe messaging:
1. **BitSetSignature** (Warp 1.0): Classical BLS aggregate signatures
2. **CoronaSignature** (Warp 1.5 - Recommended): Post-quantum threshold signatures using LWE
3. **EncryptedWarpPayload** (Warp 1.5): ML-KEM + AES-256-GCM encrypted cross-chain payloads
4. **HybridBLSRTSignature** (Deprecated): BLS + Corona hybrid for transition period
4. **HybridBLSCoronaSignature** (Deprecated): BLS + Corona hybrid for transition period
### CoronaSignature
+14 -3
View File
@@ -19,13 +19,24 @@ func init() {
Codec = codec.NewManager(math.MaxInt)
lc := linearcodec.NewDefault()
// CRITICAL: RegisterType order is the on-chain wire format. linearcodec
// assigns a monotonically increasing typeID per call. Reordering ANY of
// these lines is a hard fork. New types must be appended ONLY.
//
// typeID 0x00 → BitSetSignature
// typeID 0x01 → CoronaSignature
// typeID 0x02 → EncryptedWarpPayload
// typeID 0x03 → HybridBLSCoronaSignature (renamed from HybridBLSRTSignature)
// typeID 0x04 → TeleportMessage
// typeID 0x05 → TeleportTransferPayload
// typeID 0x06 → TeleportAttestPayload
err := errors.Join(
// Warp 1.0: Classical BLS signatures
lc.RegisterType(&BitSetSignature{}),
// Warp 1.5: Quantum-safe signatures
lc.RegisterType(&CoronaSignature{}), // Recommended: RT-only (LWE-based threshold)
lc.RegisterType(&EncryptedWarpPayload{}), // ML-KEM + AES-256-GCM encryption
lc.RegisterType(&HybridBLSRTSignature{}), // Deprecated: BLS+RT hybrid
lc.RegisterType(&CoronaSignature{}), // Recommended: Corona-only (LWE-based threshold)
lc.RegisterType(&EncryptedWarpPayload{}), // ML-KEM + AES-256-GCM encryption
lc.RegisterType(&HybridBLSCoronaSignature{}), // Deprecated: BLS+Corona hybrid
// Teleport: Cross-chain bridging protocol
lc.RegisterType(&TeleportMessage{}), // High-level bridge message wrapper
lc.RegisterType(&TeleportTransferPayload{}), // Asset transfer payload
+38 -31
View File
@@ -21,17 +21,17 @@ import (
var (
_ Signature = (*BitSetSignature)(nil)
_ Signature = (*CoronaSignature)(nil)
_ Signature = (*HybridBLSRTSignature)(nil) // Deprecated: use CoronaSignature
_ Signature = (*HybridBLSCoronaSignature)(nil) // Deprecated: use CoronaSignature
ErrInvalidBitSet = errors.New("bitset is invalid")
ErrInsufficientWeight = errors.New("signature weight is insufficient")
ErrInvalidSignature = errors.New("signature is invalid")
ErrParseSignature = errors.New("failed to parse signature")
ErrInvalidRTSignature = errors.New("corona signature is invalid")
ErrMissingRTPublicKey = errors.New("missing corona public key for validator")
ErrHybridVerifyFailed = errors.New("hybrid signature verification failed")
ErrDecryptionFailed = errors.New("ML-KEM decryption failed")
ErrInvalidCiphertext = errors.New("invalid ciphertext")
ErrInvalidBitSet = errors.New("bitset is invalid")
ErrInsufficientWeight = errors.New("signature weight is insufficient")
ErrInvalidSignature = errors.New("signature is invalid")
ErrParseSignature = errors.New("failed to parse signature")
ErrInvalidCoronaSignature = errors.New("corona signature is invalid")
ErrMissingCoronaPublicKey = errors.New("missing corona public key for validator")
ErrHybridVerifyFailed = errors.New("hybrid signature verification failed")
ErrDecryptionFailed = errors.New("ML-KEM decryption failed")
ErrInvalidCiphertext = errors.New("invalid ciphertext")
)
type Signature interface {
@@ -234,7 +234,7 @@ const (
// - Post-quantum secure (based on LWE hardness)
// - Paper: https://eprint.iacr.org/2024/1113
//
// Replaces: BitSetSignature (BLS), HybridBLSRTSignature
// Replaces: BitSetSignature (BLS), HybridBLSCoronaSignature
// Security: Post-quantum secure (LWE-based)
// Size: Variable based on threshold parameters
type CoronaSignature struct {
@@ -290,7 +290,7 @@ func (s *CoronaSignature) Verify(
rtPubKeys := make([][]byte, 0, len(signers))
for _, signer := range signers {
if len(signer.CoronaPubKey) == 0 {
return fmt.Errorf("%w: validator missing RT key", ErrMissingRTPublicKey)
return fmt.Errorf("%w: validator missing Corona key", ErrMissingCoronaPublicKey)
}
rtPubKeys = append(rtPubKeys, signer.CoronaPubKey)
}
@@ -298,12 +298,12 @@ func (s *CoronaSignature) Verify(
// Aggregate the Corona public keys for threshold verification
aggregatedPK, err := AggregateCoronaPublicKeys(rtPubKeys)
if err != nil {
return fmt.Errorf("failed to aggregate RT public keys: %w", err)
return fmt.Errorf("failed to aggregate Corona public keys: %w", err)
}
// Verify the Corona (LWE-based) signature
if !VerifyCoronaSignature(aggregatedPK, msg.Bytes(), s.Signature) {
return ErrInvalidRTSignature
return ErrInvalidCoronaSignature
}
return nil
@@ -575,7 +575,7 @@ func generateSecureRandom(length int) ([]byte, error) {
// Deprecated: Use CoronaSignature type which handles variable-length signatures.
const CoronaSignatureLen = 3309
// HybridBLSRTSignature implements a quantum-safe hybrid signature combining:
// HybridBLSCoronaSignature implements a quantum-safe hybrid signature combining:
// - BLS aggregate signatures (classical security, compact)
// - Corona lattice signatures (post-quantum security, larger)
//
@@ -584,9 +584,16 @@ const CoronaSignatureLen = 3309
//
// Migration path:
// 1. Pre-quantum: BLS-only (BitSetSignature)
// 2. Transition: HybridBLSRTSignature (both required)
// 2. Transition: HybridBLSCoronaSignature (both required)
// 3. Post-quantum: Corona-only (future)
type HybridBLSRTSignature struct {
//
// Wire format: this type is the 4th type registered in the warp codec
// (codec.go), which assigns it linearcodec typeID 0x03. The typeID, field
// order, and field encodings are part of the on-chain wire format and
// MUST NOT change. The Go-side type name is metadata only and was renamed
// from HybridBLSRTSignature in the threshold sweep; wire bytes are
// byte-equal pre/post rename.
type HybridBLSCoronaSignature struct {
// Signers is a big-endian byte slice encoding which validators signed
Signers []byte `serialize:"true"`
@@ -599,12 +606,12 @@ type HybridBLSRTSignature struct {
// CoronaPublicKeys contains the Corona public keys for each signer
// in the same order as indicated by the Signers bitset
// This is needed because validators may have different RT keys than BLS keys
// This is needed because validators may have different Corona keys than BLS keys
CoronaPublicKeys [][]byte `serialize:"true"`
}
// NumSigners returns the number of validators that participated in signing
func (s *HybridBLSRTSignature) NumSigners() (int, error) {
func (s *HybridBLSCoronaSignature) NumSigners() (int, error) {
signerIndices := set.BitsFromBytes(s.Signers)
if len(signerIndices.Bytes()) != len(s.Signers) {
return 0, ErrInvalidBitSet
@@ -614,7 +621,7 @@ func (s *HybridBLSRTSignature) NumSigners() (int, error) {
// Verify validates both BLS and Corona signatures
// Both MUST be valid for the hybrid signature to be accepted
func (s *HybridBLSRTSignature) Verify(
func (s *HybridBLSCoronaSignature) Verify(
msg *UnsignedMessage,
networkID uint32,
validators CanonicalValidatorSet,
@@ -657,7 +664,7 @@ func (s *HybridBLSRTSignature) Verify(
}
// verifyBLS verifies the BLS aggregate signature
func (s *HybridBLSRTSignature) verifyBLS(msg *UnsignedMessage, signers []*Validator) error {
func (s *HybridBLSCoronaSignature) verifyBLS(msg *UnsignedMessage, signers []*Validator) error {
// Parse the aggregate BLS signature
aggSig, err := bls.SignatureFromBytes(s.BLSSignature[:])
if err != nil {
@@ -679,35 +686,35 @@ func (s *HybridBLSRTSignature) verifyBLS(msg *UnsignedMessage, signers []*Valida
}
// verifyCorona verifies the Corona lattice-based signature
func (s *HybridBLSRTSignature) verifyCorona(msg *UnsignedMessage, signers []*Validator) error {
// Validate we have RT public keys for all signers
func (s *HybridBLSCoronaSignature) verifyCorona(msg *UnsignedMessage, signers []*Validator) error {
// Validate we have Corona public keys for all signers
if len(s.CoronaPublicKeys) != len(signers) {
return fmt.Errorf("%w: got %d keys, expected %d",
ErrMissingRTPublicKey, len(s.CoronaPublicKeys), len(signers))
ErrMissingCoronaPublicKey, len(s.CoronaPublicKeys), len(signers))
}
// Validate Corona signature is present
if len(s.CoronaSignature) == 0 {
return ErrInvalidRTSignature
return ErrInvalidCoronaSignature
}
// Aggregate the Corona public keys
aggregatedRTPK, err := AggregateCoronaPublicKeys(s.CoronaPublicKeys)
aggregatedCoronaPK, err := AggregateCoronaPublicKeys(s.CoronaPublicKeys)
if err != nil {
return fmt.Errorf("failed to aggregate RT public keys: %w", err)
return fmt.Errorf("failed to aggregate Corona public keys: %w", err)
}
// Verify the Corona signature
unsignedBytes := msg.Bytes()
if !VerifyCoronaSignature(aggregatedRTPK, unsignedBytes, s.CoronaSignature) {
return ErrInvalidRTSignature
if !VerifyCoronaSignature(aggregatedCoronaPK, unsignedBytes, s.CoronaSignature) {
return ErrInvalidCoronaSignature
}
return nil
}
func (s *HybridBLSRTSignature) String() string {
return fmt.Sprintf("HybridBLSRTSignature(Signers = %x, BLS = %x, RT = %x)",
func (s *HybridBLSCoronaSignature) String() string {
return fmt.Sprintf("HybridBLSCoronaSignature(Signers = %x, BLS = %x, Corona = %x)",
s.Signers, s.BLSSignature, s.CoronaSignature[:min(32, len(s.CoronaSignature))])
}
+1 -1
View File
@@ -46,7 +46,7 @@ var (
// Warp 1.5 supports three signature types:
// - BitSetSignature: Classical BLS (legacy)
// - CoronaSignature: Quantum-safe (recommended)
// - HybridBLSRTSignature: BLS+RT hybrid (deprecated)
// - HybridBLSCoronaSignature: BLS+Corona hybrid (deprecated)
type TeleportMessage struct {
// Version is the Teleport protocol version
Version uint8 `serialize:"true"`
+102
View File
@@ -0,0 +1,102 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Wire-format regression test. Pins on-chain encoding for every type the
// warp codec is willing to marshal, both as a concrete struct and through
// the Signature interface (which prepends the linearcodec typeID).
//
// The hex strings encoded in `want` are the on-chain bytes. They MUST
// NOT change without a hard fork — any failure here means a code change
// has altered the wire format. The most common ways to break it:
//
// 1. Reordering the RegisterType calls in codec.go (shifts every typeID)
// 2. Adding/removing serializable fields on an existing type
// 3. Changing a serializable field's type
//
// Originally introduced when HybridBLSRTSignature was renamed to
// HybridBLSCoronaSignature; the hex baseline below was captured from the
// pre-rename build and re-verified post-rename. Type and field name
// changes are wire-safe — type IDs and field encodings come from
// registration order and declaration order respectively (see
// reflectcodec/struct_fielder.go and linearcodec/codec.go).
package warp
import (
"encoding/hex"
"testing"
)
func TestWireBaselinePreservedForHybridRename(t *testing.T) {
bls := &BitSetSignature{
Signers: []byte{0x01, 0x02, 0x03},
Signature: [96]byte{0xaa, 0xbb, 0xcc, 0xdd},
}
corona := &CoronaSignature{
Signers: []byte{0x04, 0x05},
Signature: []byte{0xee, 0xff, 0x11, 0x22, 0x33},
}
enc := &EncryptedWarpPayload{
EncapsulatedKey: []byte{0x10, 0x11, 0x12},
Nonce: []byte{0x20, 0x21},
Ciphertext: []byte{0x30, 0x31, 0x32, 0x33},
RecipientKeyID: []byte{0x40},
}
hybrid := &HybridBLSCoronaSignature{
Signers: []byte{0x50, 0x51},
BLSSignature: [96]byte{0x60, 0x61, 0x62, 0x63},
CoronaSignature: []byte{0x70, 0x71, 0x72},
CoronaPublicKeys: [][]byte{{0x80, 0x81}, {0x82, 0x83}},
}
// Concrete-struct marshal (no typeID prefix, just field-order bytes).
concrete := func(v interface{}) string {
b, err := Codec.Marshal(CodecVersion, v)
if err != nil {
t.Fatalf("concrete marshal: %v", err)
}
return hex.EncodeToString(b)
}
// Interface marshal (typeID prefix included, polymorphic).
asInterface := func(v Signature) string {
s := v
b, err := Codec.Marshal(CodecVersion, &s)
if err != nil {
t.Fatalf("iface marshal: %v", err)
}
return hex.EncodeToString(b)
}
// EXACT pre-rename baseline (captured before HybridBLSRTSignature →
// HybridBLSCoronaSignature rename). These hex strings ARE the
// on-chain wire format. Post-rename bytes MUST match exactly,
// because linearcodec assigns typeIDs positionally and serializes
// fields by declaration order — type names and field names are
// metadata only, never on wire.
want := map[string]string{
"CONCRETE BitSetSignature": "000000000003010203aabbccdd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"CONCRETE CoronaSignature": "000000000002040500000005eeff112233",
"CONCRETE EncryptedWarpPayload": "00000000000310111200000002202100000004303132330000000140",
"CONCRETE HybridBLSCorona": "00000000000250516061626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000370717200000002000000028081000000028283",
"IFACE BitSetSignature": "00000000000000000003010203aabbccdd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"IFACE CoronaSignature": "00000000000100000002040500000005eeff112233",
"IFACE HybridBLSCorona": "0000000000030000000250516061626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000370717200000002000000028081000000028283",
}
got := map[string]string{
"CONCRETE BitSetSignature": concrete(bls),
"CONCRETE CoronaSignature": concrete(corona),
"CONCRETE EncryptedWarpPayload": concrete(enc),
"CONCRETE HybridBLSCorona": concrete(hybrid),
"IFACE BitSetSignature": asInterface(bls),
"IFACE CoronaSignature": asInterface(corona),
"IFACE HybridBLSCorona": asInterface(hybrid),
}
for name, w := range want {
g := got[name]
t.Logf("%-30s = %s", name, g)
if g != w {
t.Errorf("WIRE BYTES CHANGED for %s\n want: %s\n got: %s", name, w, g)
}
}
}