package crypto import ( "bytes" "testing" ) func TestGnarlHashDeterministic(t *testing.T) { msg := []byte("determinism test message") h1 := GHash(msg) h2 := GHash(msg) if h1 != h2 { t.Fatalf("GHash not deterministic: %x != %x", h1, h2) } m1 := GMid(msg) m2 := GMid(msg) if m1 != m2 { t.Fatalf("GMid not deterministic") } s1 := GShard(msg) s2 := GShard(msg) if s1 != s2 { t.Fatalf("GShard not deterministic") } } func TestGnarlHashDistinct(t *testing.T) { h1 := GHash([]byte("message one")) h2 := GHash([]byte("message two")) if h1 == h2 { t.Fatalf("GHash collision on distinct messages") } m1 := GMid([]byte("message one")) m2 := GMid([]byte("message two")) if m1 == m2 { t.Fatalf("GMid collision on distinct messages") } } func TestGnarlHashEmpty(t *testing.T) { h := GHash([]byte{}) if h.IsZero() { t.Fatalf("GHash of empty input is zero") } } func TestGnarlHashLong(t *testing.T) { // Message longer than one block (41 bytes). msg := make([]byte, 500) for i := range msg { msg[i] = byte(i % 256) } h1 := GHash(msg) if h1.IsZero() { t.Fatalf("GHash of 500-byte message is zero") } // Flip one bit: hash should change. msg[250] ^= 0x01 h2 := GHash(msg) if h1 == h2 { t.Fatalf("GHash did not change after bit flip") } } func TestGnarlHashMultiBlock(t *testing.T) { // Messages that span exactly 1, 2, and 3 blocks. for _, size := range []int{30, 60, 120} { msg := make([]byte, size) for i := range msg { msg[i] = byte(i*7 + 13) } h := GHash(msg) if h.IsZero() { t.Fatalf("GHash of %d-byte message is zero", size) } } } func TestGnarlPackUnpack243(t *testing.T) { // Pack and unpack should be inverse operations. var coeffs [GnarlN]uint16 for i := range GnarlN { coeffs[i] = uint16((i*37 + 13) % GnarlP) } packed := packCoeffs243(coeffs) unpacked := unpackCoeffs243(packed) for i := range GnarlN { if unpacked[i] != coeffs[i] { t.Fatalf("pack/unpack243 mismatch at %d: got %d, want %d", i, unpacked[i], coeffs[i]) } } } func TestGnarlPackUnpack243MaxValue(t *testing.T) { // All coefficients at max value (270). var coeffs [GnarlN]uint16 for i := range GnarlN { coeffs[i] = 270 } packed := packCoeffs243(coeffs) unpacked := unpackCoeffs243(packed) for i := range GnarlN { if unpacked[i] != 270 { t.Fatalf("max value pack/unpack243 mismatch at %d: got %d, want 270", i, unpacked[i]) } } } func TestGnarlMidFromHash(t *testing.T) { msg := []byte("tier extraction test") h := GHash(msg) m := GMid(msg) // Mid extracted from GnarlHash should match GMid computed directly. mFromH := h.Mid() if m != mFromH { t.Fatalf("GMid from GHash differs from direct GMid") } } func TestGnarlShardFromHash(t *testing.T) { msg := []byte("shard extraction test") h := GHash(msg) s := GShard(msg) sFromH := h.Shard() if s != sFromH { t.Fatalf("GShard from GHash differs from direct GShard") } } func TestGnarlShardValues(t *testing.T) { // Each trit in the shard should be 0, 1, or 2. msg := []byte("trit value test") s := GShard(msg) bitPos := 0 for i := range GnarlN { var v byte for b := range 2 { byteIdx := bitPos / 8 bitIdx := uint(bitPos % 8) if s[byteIdx]&(1< 2 { t.Fatalf("trit %d has value %d (should be 0, 1, or 2)", i, v) } } } func TestGnarlHashSum(t *testing.T) { h1 := GHash([]byte("alpha")) h2 := GHash([]byte("beta")) sum := h1.Sum(h2) if sum.IsZero() { t.Fatalf("sum of two hashes is zero") } // Sum should be commutative. sum2 := h2.Sum(h1) if sum != sum2 { t.Fatalf("GnarlHash.Sum is not commutative") } } func TestGnarlHashSizes(t *testing.T) { if GnarlHashBytes != 31 { t.Fatalf("GnarlHashBytes = %d, want 31", GnarlHashBytes) } if GnarlMidBytes != 27 { t.Fatalf("GnarlMidBytes = %d, want 27", GnarlMidBytes) } if GnarlShardBytes != 7 { t.Fatalf("GnarlShardBytes = %d, want 7", GnarlShardBytes) } } func TestGnarlHashDispersion(t *testing.T) { // Hash many messages and check that all bytes of the GnarlMid are exercised. var orBits [GnarlMidBytes]byte for i := range 100 { msg := []byte{byte(i), byte(i >> 8), byte(i >> 16)} m := GMid(msg) for j := range GnarlMidBytes { orBits[j] |= m[j] } } for i, b := range orBits { if b == 0 { t.Fatalf("byte %d of GnarlMid never set across 100 hashes", i) } } } func TestGnarlLengthExtension(t *testing.T) { // Different-length messages with same prefix should hash differently. short := []byte("short") long := append([]byte("short"), bytes.Repeat([]byte{0}, 50)...) h1 := GHash(short) h2 := GHash(long) if h1 == h2 { t.Fatalf("length extension: same hash for different-length messages") } }