mirror of
https://github.com/luxfi/threshold.git
synced 2026-10-30 15:15:53 +00:00
Squashed batch covering:
- protocols/pulsar/: lattice threshold lane (round-based wrapper for
github.com/luxfi/pulsar kernel); doc.go documents Photon/Lumen/Beam/
Pulsar/Pulse/Prism/Horizon/Quasar vocabulary stack from LP-105
- protocols/lens/: planned curve threshold sister kernel (design
intent only; mirrors Pulsar shape; replaces stub LSS-FROST)
- protocols/lss/lss_pulsar.go + tests: LSS-Pulsar adapter
(DynamicResharePulsar + PulsarSnapshotManager + BuildActivationTranscript).
Now SHA3-suite-aware: PulsarConfig carries NebulaRoot, HashSuiteID,
ImplementationVersion as optional transcript-binding fields that
flow through to the activation message.
10/10 acceptance tests pass:
1. GroupKey preservation
2. KeyEraID preservation
3. Generation +1
4. RollbackFrom=0 on forward
5. t_old != t_new
6. Disjoint set rotation
7. Valid signature under unchanged GroupKey
8. Pairwise material regenerated
9. Rollback semantics
10. Error surface
Plus 1 new test for BuildActivationTranscript Nebula+suite fields.
- protocols/lss/lss_frost.go: marked DEPRECATED (placeholder Sign()/
Refresh(), single-process simulation, hardcoded ChainKey/RID); will
be replaced by lss_lens.go.
- corona KAT cross-oracle vs Go reference (N=16 deterministic seeds)
- corona+protocol+harness hardening against parallel-run flakes
- lss/adapters: drop NewXRPL/NewCardano panic; ship real XRPL address
derivation
- corona SignWithConfig API expansion + test fixes
- v1.6.5 changelog
- go.sum h1 hashes for luxfi/log and 4 deps
Architecture (LP-105 / pulsar/DESIGN.md):
LSS owns lifecycle (Generation, Rollback, snapshots, dealer/
coordinator role separation). Pulsar owns lattice math (R_q shares,
lattice Pedersen commits, Sign1/Sign2/Combine, Pulsar-SHA3 hash
profile). The lss_pulsar adapter wires them. lss_lens (planned) does
the same for curve math.
go.mod adds local replace github.com/luxfi/pulsar => ../pulsar.
107 lines
3.2 KiB
Go
107 lines
3.2 KiB
Go
// Copyright (c) 2026, Lux Industries Inc.
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package adapters
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"testing"
|
|
)
|
|
|
|
// TestClassicAddressFromPubkey exercises the XRPL address derivation
|
|
// against published test vectors. The vectors are taken from the
|
|
// xrpl-py reference implementation
|
|
// (https://github.com/XRPLF/xrpl-py, BSD-3-Clause) which itself derives
|
|
// from the canonical rippled C++ keypair test data.
|
|
//
|
|
// Spec: https://xrpl.org/accounts.html#address-encoding
|
|
//
|
|
// AccountID = RIPEMD-160(SHA-256(pubkey))
|
|
// payload = 0x00 || AccountID
|
|
// checksum = SHA-256(SHA-256(payload))[:4]
|
|
// address = base58(payload || checksum) // XRPL alphabet
|
|
func TestClassicAddressFromPubkey(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
pubHex string
|
|
want string
|
|
}{
|
|
{
|
|
// Canonical XRPL secp256k1 KAT: derive the Genesis account
|
|
// "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" from its public key.
|
|
// The seed is the well-known "masterpassphrase"; the
|
|
// pubkey is what rippled's own keypair_test.cpp asserts
|
|
// against. Compressed secp256k1, 33 bytes.
|
|
name: "secp256k1_genesis",
|
|
pubHex: "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
|
|
want: "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
pub, err := hex.DecodeString(tc.pubHex)
|
|
if err != nil {
|
|
t.Fatalf("decode pubkey hex: %v", err)
|
|
}
|
|
got := classicAddressFromPubkey(pub)
|
|
if got != tc.want {
|
|
t.Fatalf("classicAddressFromPubkey(%s)\n got: %s\nwant: %s", tc.pubHex, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestClassicAddressFromPubkey_Roundtrip asserts that the encoding is
|
|
// stable for arbitrary 33-byte inputs (no panics, deterministic output,
|
|
// always begins with 'r').
|
|
func TestClassicAddressFromPubkey_Roundtrip(t *testing.T) {
|
|
pub := make([]byte, 33)
|
|
for i := range pub {
|
|
pub[i] = byte(i)
|
|
}
|
|
a := classicAddressFromPubkey(pub)
|
|
b := classicAddressFromPubkey(pub)
|
|
if a != b {
|
|
t.Fatalf("classicAddressFromPubkey not deterministic: %s vs %s", a, b)
|
|
}
|
|
if len(a) == 0 || a[0] != 'r' {
|
|
t.Fatalf("classic address must start with 'r', got %q", a)
|
|
}
|
|
}
|
|
|
|
// TestNewXRPLAdapter_RejectsUnsupportedSigType asserts that the
|
|
// constructor returns a structured error rather than panicking when
|
|
// given a signature type XRPL does not support.
|
|
func TestNewXRPLAdapter_RejectsUnsupportedSigType(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
st SignatureType
|
|
}{
|
|
{"schnorr_unsupported", SignatureSchnorr},
|
|
{"corona_unsupported", SignatureCorona},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
a, err := NewXRPLAdapter(tc.st, false)
|
|
if err == nil {
|
|
t.Fatal("expected error for unsupported signature type, got nil")
|
|
}
|
|
if a != nil {
|
|
t.Fatal("expected nil adapter on error, got non-nil")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestNewCardanoAdapter_RejectsUnsupportedSigType mirrors the XRPL
|
|
// check for the Cardano constructor.
|
|
func TestNewCardanoAdapter_RejectsUnsupportedSigType(t *testing.T) {
|
|
a, err := NewCardanoAdapter(SignatureCorona, 0x01, EraBabbage)
|
|
if err == nil {
|
|
t.Fatal("expected error for unsupported signature type, got nil")
|
|
}
|
|
if a != nil {
|
|
t.Fatal("expected nil adapter on error, got non-nil")
|
|
}
|
|
}
|