compact_test.go raw

   1  package crypto
   2  
   3  import (
   4  	"testing"
   5  
   6  	"git.mleku.dev/mleku/dendrite/pkg/ratio"
   7  )
   8  
   9  func TestMarshalUnmarshal(t *testing.T) {
  10  	l := buildMatureLattice()
  11  	params := DefaultParams(Security128)
  12  	kp := GenerateKeyPair(l, params, testFactory)
  13  
  14  	msg := []byte("compact round-trip")
  15  	sig, err := Sign(&kp.Private, msg, params)
  16  	if err != nil {
  17  		t.Fatalf("Sign: %v", err)
  18  	}
  19  
  20  	data, err := sig.Marshal()
  21  	if err != nil {
  22  		t.Fatalf("Marshal: %v", err)
  23  	}
  24  
  25  	sig2, err := UnmarshalSignature(data)
  26  	if err != nil {
  27  		t.Fatalf("Unmarshal: %v", err)
  28  	}
  29  
  30  	// Challenge must round-trip exactly.
  31  	if sig2.Challenge != sig.Challenge {
  32  		t.Error("challenge mismatch after round-trip")
  33  	}
  34  
  35  	// Occupied count must match.
  36  	origOcc := 0
  37  	for _, s := range sig.Response {
  38  		if s.Occupied {
  39  			origOcc++
  40  		}
  41  	}
  42  	rtOcc := 0
  43  	for _, s := range sig2.Response {
  44  		if s.Occupied {
  45  			rtOcc++
  46  		}
  47  	}
  48  	if origOcc != rtOcc {
  49  		t.Errorf("occupied count: orig=%d, round-trip=%d", origOcc, rtOcc)
  50  	}
  51  
  52  	// Proof lengths must be consistent.
  53  	if len(sig2.Proof.LockIns) != len(sig2.Proof.NeighborCounts) {
  54  		t.Error("proof length mismatch in round-trip")
  55  	}
  56  	if len(sig2.Proof.LockIns) != len(sig2.Proof.HexTrace) {
  57  		t.Error("proof/hex trace length mismatch in round-trip")
  58  	}
  59  }
  60  
  61  func TestMarshalSize(t *testing.T) {
  62  	l := buildMatureLattice()
  63  	params := DefaultParams(Security128)
  64  	kp := GenerateKeyPair(l, params, testFactory)
  65  
  66  	msg := []byte("size test")
  67  	sig, err := Sign(&kp.Private, msg, params)
  68  	if err != nil {
  69  		t.Fatalf("Sign: %v", err)
  70  	}
  71  
  72  	data, err := sig.Marshal()
  73  	if err != nil {
  74  		t.Fatalf("Marshal: %v", err)
  75  	}
  76  
  77  	t.Logf("compact signature size: %d bytes", len(data))
  78  
  79  	// For Security128 (N=256), compact should be well under 2 KB.
  80  	if len(data) > 2048 {
  81  		t.Errorf("compact signature too large: %d bytes (want <= 2048)", len(data))
  82  	}
  83  }
  84  
  85  func TestMarshalNilSignature(t *testing.T) {
  86  	var s *Signature
  87  	_, err := s.Marshal()
  88  	if err == nil {
  89  		t.Error("expected error marshaling nil signature")
  90  	}
  91  }
  92  
  93  func TestUnmarshalTruncated(t *testing.T) {
  94  	_, err := UnmarshalSignature([]byte{1, 2, 3})
  95  	if err == nil {
  96  		t.Error("expected error with truncated data")
  97  	}
  98  }
  99  
 100  func TestQuantizeLockIn(t *testing.T) {
 101  	tests := []struct {
 102  		name string
 103  		num  int64
 104  		den  int64
 105  		want uint8
 106  	}{
 107  		{"zero", 0, 1, 0},
 108  		{"half", 1, 2, 127},
 109  		{"one", 1, 1, 255},
 110  		{"quarter", 1, 4, 63},
 111  		{"over_one", 3, 2, 255}, // clamped
 112  	}
 113  	for _, tt := range tests {
 114  		t.Run(tt.name, func(t *testing.T) {
 115  			r := ratio.New(tt.num, tt.den)
 116  			got := quantizeLockIn(r)
 117  			if got != tt.want {
 118  				t.Errorf("quantizeLockIn(%s) = %d, want %d", r, got, tt.want)
 119  			}
 120  		})
 121  	}
 122  }
 123  
 124  func TestDequantizeRoundTrip(t *testing.T) {
 125  	// Quantize then dequantize should be approximately equal.
 126  	r := ratio.New(3, 7)
 127  	q := quantizeLockIn(r)
 128  	back := dequantizeLockIn(q)
 129  
 130  	// The dequantized value should be within 1/255 of the original.
 131  	diff := r.Sub(back).Abs()
 132  	tolerance := ratio.New(2, 255)
 133  	if diff.Greater(tolerance) {
 134  		t.Errorf("round-trip error too large: %s → %d → %s (diff=%s)",
 135  			r, q, back, diff)
 136  	}
 137  }
 138  
 139  func TestMarshalDeterministic(t *testing.T) {
 140  	l := buildMatureLattice()
 141  	params := DefaultParams(Security128)
 142  	kp := GenerateKeyPair(l, params, testFactory)
 143  
 144  	msg := []byte("deterministic test")
 145  	sig, err := Sign(&kp.Private, msg, params)
 146  	if err != nil {
 147  		t.Fatalf("Sign: %v", err)
 148  	}
 149  
 150  	data1, err := sig.Marshal()
 151  	if err != nil {
 152  		t.Fatalf("Marshal 1: %v", err)
 153  	}
 154  	data2, err := sig.Marshal()
 155  	if err != nil {
 156  		t.Fatalf("Marshal 2: %v", err)
 157  	}
 158  
 159  	if string(data1) != string(data2) {
 160  		t.Error("marshaling the same signature twice should produce identical bytes")
 161  	}
 162  }
 163