//go:build ignore // Opens with Go what the locator of datekeys-ts writes, and prints // src/lib/dkc/testing/locator-interop.json: each recipe of // src/lib/dkc/testing/locator-interop.ts, written by // scripts/locator-ts-samples.mjs, with the SHA-256 of each of its files and // the verdict of Go: // // - locator.Open of the sealed locator with Quicknet, its round and the // published release of the round, from the fixtures; // - Marshal of the locator that Open gives, which must be the plaintext // that TypeScript sealed, and Usable, which must keep every address; // - OpenEnvelope of the rest, which must give the .dkc; // - RestIn of the rest hidden after a host with Hide, at its offset, // which must give the rest again. // // A verdict is "ok" or the text of the first thing that failed. // // It imports only public packages of the reference implementation, and runs // in a module of it without changing anything there: an export of the tag // spec-v0.12, so that no change in progress in datekeys-go is read. // // node scripts/locator-ts-samples.mjs /tmp/samples // git -C ../datekeys-go archive spec-v0.12 | tar -x -C /tmp/dkgo // (cd /tmp/dkgo && go run .../datekeys-ts/scripts/locator-go-verdicts.go \ // -source spec-v0.12 -testdata .../datekeys-ts/testdata -samples /tmp/samples > .../locator-interop.json) package main import ( "bytes" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "flag" "fmt" "log" "os" "path/filepath" "runtime" "sort" "strings" "g.activething.com/go/DateKeys/locator" "g.activething.com/go/DateKeys/profile" "g.activething.com/go/DateKeys/provider" ) type obj = map[string]any func h(b []byte) string { return hex.EncodeToString(b) } func check(err error) { if err != nil { _, file, line, _ := runtime.Caller(1) log.Fatalf("%s:%d: %v", filepath.Base(file), line, err) } } func pattern(n int) []byte { b := make([]byte, n) for i := range b { b[i] = byte(31*i + 7) } return b } func releases(testdata string) map[uint64]provider.Release { out := map[uint64]provider.Release{} files, err := filepath.Glob(filepath.Join(testdata, "fixtures", "*.json")) check(err) sort.Strings(files) for _, f := range files { raw, err := os.ReadFile(f) check(err) var v struct { Release *struct { Round uint64 `json:"round"` Signature string `json:"signature"` } `json:"release"` } if err := json.Unmarshal(raw, &v); err != nil || v.Release == nil { continue } sig, err := hex.DecodeString(v.Release.Signature) check(err) out[v.Release.Round] = provider.Release{Round: v.Release.Round, Signature: sig} } return out } func verdict(r obj, files map[string][]byte, rel map[uint64]provider.Release) error { round := uint64(r["round"].(float64)) release, ok := rel[round] if !ok { return fmt.Errorf("no release of round %d", round) } loc, err := locator.Open(profile.Quicknet(), round, release, files["sealed"]) if err != nil { return err } pt, err := loc.Marshal() if err != nil { return err } if !bytes.Equal(pt, files["plaintext"]) { return errors.New("another plaintext") } if len(loc.Usable()) != len(r["addresses"].([]any)) || len(loc.Addresses) != len(loc.Usable()) { return errors.New("an address that a reader does not use") } dkc, err := loc.OpenEnvelope(files["rest"]) if err != nil { return err } if !bytes.Equal(dkc, files["dkc"]) || !bytes.Equal(dkc, pattern(int(r["dkc_length"].(float64)))) { return errors.New("another .dkc") } file, offset := locator.Hide([]byte("GIF89a, a host of some kind"), files["rest"]) rest, err := loc.RestIn(append(file, "and more"...), offset) if err != nil { return err } if !bytes.Equal(rest, files["rest"]) { return errors.New("another rest in the host") } return nil } func main() { samples := flag.String("samples", "", "the directory of the samples") src := flag.String("source", "", "the commit of datekeys-go") testdata := flag.String("testdata", "", "the testdata of this repository") flag.Parse() if *samples == "" || *src == "" || *testdata == "" { log.Fatal("usage: -source -testdata -samples ") } rel := releases(*testdata) raw, err := os.ReadFile(filepath.Join(*samples, "samples.json")) check(err) var doc struct { Samples []obj `json:"samples"` } check(json.Unmarshal(raw, &doc)) result := []obj{} for i, r := range doc.Samples { names, err := filepath.Glob(filepath.Join(*samples, fmt.Sprintf("%d.*", i))) check(err) files := map[string][]byte{} digests := obj{} for _, n := range names { b, err := os.ReadFile(n) check(err) suffix := strings.TrimPrefix(filepath.Ext(n), ".") files[suffix] = b s := sha256.Sum256(b) digests[suffix] = h(s[:]) } v := "ok" if err := verdict(r, files, rel); err != nil { v = err.Error() log.Printf("%s: %s", r["name"], v) } r["files"] = digests r["verdict"] = v result = append(result, r) } var buf bytes.Buffer e := json.NewEncoder(&buf) e.SetEscapeHTML(false) e.SetIndent("", " ") check(e.Encode(obj{"source": *src, "go": runtime.Version(), "description": "What Go makes of the sealed locators and envelopes that datekeys-ts writes from a seed, from the recipes of src/lib/dkc/testing/locator-interop.ts, by scripts/locator-go-verdicts.go: the SHA-256 of each file and the verdict of Go, ok or the text of what failed.", "samples": result})) os.Stdout.Write(buf.Bytes()) }