- internal/{address,identity,protocol,tce,transport,verify} -> pkg/ so
external Go projects can import the verified core; invariant tests
updated for the new paths
- Config.TrustProxy: key rate limiting by X-Forwarded-For when the relay
sits behind a reverse proxy (off by default, header never trusted
otherwise)
- examples/service + examples/approve: complete passwordless login round
trip (mint request -> wallet approves -> local verify), run live in CI
- docs/SERVICE-GUIDE.md: the integration recipe
94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
package tce
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// vectorsPath is the location of the frozen vector file relative to the
|
|
// package's test working directory.
|
|
const vectorsPath = "../../testdata/vectors/tce_vectors.json"
|
|
|
|
type vectorFile struct {
|
|
TceVersion int `json:"tce_version"`
|
|
NumberCanonicalization struct {
|
|
Accept map[string]string `json:"accept"`
|
|
Reject map[string]string `json:"reject"`
|
|
} `json:"number_canonicalization"`
|
|
}
|
|
|
|
func loadVectors(t *testing.T) *vectorFile {
|
|
t.Helper()
|
|
b, err := os.ReadFile(vectorsPath)
|
|
if err != nil {
|
|
t.Fatalf("read vectors: %v", err)
|
|
}
|
|
var vf vectorFile
|
|
if err := json.Unmarshal(b, &vf); err != nil {
|
|
t.Fatalf("parse vectors: %v", err)
|
|
}
|
|
return &vf
|
|
}
|
|
|
|
// TestNumberCanonicalizationVectors runs the frozen number vectors from the
|
|
// reference implementation: every accepted token must canonicalize to exactly
|
|
// the reference output, and every rejected token must be refused.
|
|
func TestNumberCanonicalizationVectors(t *testing.T) {
|
|
vf := loadVectors(t)
|
|
accept := vf.NumberCanonicalization.Accept
|
|
reject := vf.NumberCanonicalization.Reject
|
|
|
|
if len(accept) != 28 {
|
|
t.Errorf("expect 28 accepted tokens, got %d", len(accept))
|
|
}
|
|
if len(reject) != 18 {
|
|
t.Errorf("expect 18 rejected tokens, got %d", len(reject))
|
|
}
|
|
|
|
for token, want := range accept {
|
|
got, err := CanonicalNumber(token)
|
|
if err != nil {
|
|
t.Errorf("CanonicalNumber(%q): %v", token, err)
|
|
continue
|
|
}
|
|
if got != want {
|
|
t.Errorf("CanonicalNumber(%q) = %q, want %q", token, got, want)
|
|
}
|
|
}
|
|
|
|
for token, reason := range reject {
|
|
if strings.Contains(reason, "ERROR") {
|
|
t.Errorf("reference said %q is accepted but it is in the reject list", token)
|
|
}
|
|
if _, err := CanonicalNumber(token); err == nil {
|
|
t.Errorf("CanonicalNumber(%q) accepted, but the reference refuses it (%s)", token, reason)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCanonicalNumberIdempotence pins the §5.1 properties that make the
|
|
// encoding canonical: canonicalizing an already canonical token is a no-op,
|
|
// and every accepted token yields an accepted, further-canonical result.
|
|
func TestCanonicalNumberIdempotence(t *testing.T) {
|
|
for _, token := range []string{
|
|
"0", "1", "-1", "1.5", "0.001", "12345678901234567890",
|
|
"999999999999999999999999", "0.000000000000000001", "-0.000000000000000001",
|
|
} {
|
|
c, err := CanonicalNumber(token)
|
|
if err != nil {
|
|
t.Fatalf("CanonicalNumber(%q): %v", token, err)
|
|
}
|
|
again, err := CanonicalNumber(c)
|
|
if err != nil {
|
|
t.Fatalf("canonical form %q does not canonicalize: %v", c, err)
|
|
}
|
|
if again != c {
|
|
t.Fatalf("canonical form not fixed point: %q -> %q", c, again)
|
|
}
|
|
if !IsCanonicalNumber(c) {
|
|
t.Fatalf("IsCanonicalNumber(%q) = false", c)
|
|
}
|
|
}
|
|
}
|