Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

render.gno

6.49 Kb · 186 lines
  1package wesh
  2
  3import (
  4	"chain/runtime"
  5	"encoding/hex"
  6	"strconv"
  7	"strings"
  8	"time"
  9
 10	"gno.land/p/moul/x/wesh/v0"
 11)
 12
 13// Render serves the directory index at "" and one identity card at "<name>".
 14//
 15// The index deliberately does not compute a Berty link per row: base58 over a
 16// 70-byte payload is big-integer work, and it belongs on the page that a human
 17// actually asked for.
 18func Render(path string) string {
 19	path = strings.Trim(path, "/")
 20	if path == "" {
 21		return renderIndex()
 22	}
 23	return renderIdentity(path)
 24}
 25
 26func renderIndex() string {
 27	var b strings.Builder
 28	b.WriteString("# Wesh directory\n\n")
 29	b.WriteString("Resolvable, self-signed [Wesh protocol](https://berty.tech/docs/protocol/) ")
 30	b.WriteString("identities, on top of [`p/moul/x/wesh`](/p/moul/x/wesh/v0).\n\n")
 31	b.WriteString("A Berty identity normally travels out of band as a QR code, cannot be ")
 32	b.WriteString("looked up, cannot announce a seed rotation, and can never revoke a device. ")
 33	b.WriteString("This realm addresses those three, and stores no secret of any kind.\n\n")
 34
 35	if byName.Size() == 0 {
 36		b.WriteString("_No identity registered yet._\n")
 37		return b.String()
 38	}
 39
 40	b.WriteString("| name | owner | rev | mode | devices |\n")
 41	b.WriteString("|---|---|---|---|---|\n")
 42	byName.Iterate("", "", func(key string, value any) bool {
 43		id := value.(*identity)
 44		b.WriteString("| [" + id.name + "](/r/moul/x/wesh/v0:" + id.name + ") ")
 45		b.WriteString("| `" + id.owner.String() + "` ")
 46		b.WriteString("| " + strconv.Itoa(id.revision) + " ")
 47		b.WriteString("| " + modeLabel(id) + " ")
 48		b.WriteString("| " + strconv.Itoa(activeDevices(id)) + " active / " + strconv.Itoa(len(id.devices)) + " entries |\n")
 49		return false
 50	})
 51	n := byName.Size()
 52	if n == 1 {
 53		b.WriteString("\n1 identity.\n")
 54	} else {
 55		b.WriteString("\n" + strconv.Itoa(n) + " identities.\n")
 56	}
 57	return b.String()
 58}
 59
 60func renderIdentity(name string) string {
 61	id := lookup(name)
 62	if id == nil {
 63		return "# Not found\n\nNo identity named `" + name + "`. [Back to the directory](/r/moul/x/wesh/v0).\n"
 64	}
 65
 66	var b strings.Builder
 67	b.WriteString("# " + id.name + "\n\n")
 68	if id.displayName != "" {
 69		b.WriteString("_" + id.displayName + "_\n\n")
 70	}
 71	b.WriteString("| field | value |\n|---|---|\n")
 72	b.WriteString("| owner | `" + id.owner.String() + "` |\n")
 73	b.WriteString("| account key | `" + hex.EncodeToString(id.accountPK) + "` |\n")
 74	b.WriteString("| mode | " + modeLabel(id) + " |\n")
 75	b.WriteString("| revision | " + strconv.Itoa(id.revision) + " |\n")
 76	b.WriteString("| registered | block " + strconv.FormatInt(id.history[0].height, 10) + " |\n")
 77
 78	b.WriteString("\n## Contact\n\n")
 79	if id.committed {
 80		b.WriteString("This identity publishes a **commitment**, `")
 81		b.WriteString(hex.EncodeToString(id.payload))
 82		b.WriteString("`, not its rendezvous seed.\n\n")
 83		b.WriteString("The chain attests that this account key claimed that commitment from ")
 84		b.WriteString("that gno address. The seed itself is shared out of band, and whoever ")
 85		b.WriteString("receives it can check it here with `H(seed || salt)` before using it. ")
 86		b.WriteString("The rendezvous point stays off chain.\n")
 87	} else {
 88		link, err := contactOf(id).WebLink()
 89		if err != nil {
 90			b.WriteString("_Contact is unrenderable: " + err.Error() + "_\n")
 91		} else {
 92			b.WriteString("Scan or open this in Berty to send a contact request:\n\n")
 93			b.WriteString("```\n" + link + "\n```\n\n")
 94			b.WriteString("| field | value |\n|---|---|\n")
 95			b.WriteString("| rendezvous seed | `" + hex.EncodeToString(id.payload) + "` |\n")
 96			now := time.Now().Unix()
 97			period := wesh.RoundPeriod(now, wesh.DefaultRotationInterval)
 98			point, _ := contactOf(id).RendezvousPointAt(now, wesh.DefaultRotationInterval)
 99			b.WriteString("| rendezvous point, period " + strconv.FormatInt(period, 10) + " | `" + hex.EncodeToString(point) + "` |\n")
100			b.WriteString("\nThe point is `HMAC-SHA256(accountPK || seed, be64(period))`, derived ")
101			b.WriteString("exactly as weshnet derives it, and it rotates every 24h. Anyone can ")
102			b.WriteString("recompute it and check the DHT, so this entry is verifiable rather ")
103			b.WriteString("than merely asserted.\n")
104		}
105	}
106
107	b.WriteString("\n## Devices\n\n")
108	if len(id.devices) == 0 {
109		b.WriteString("No sigchain entry yet. Head is the genesis digest `")
110		b.WriteString(hex.EncodeToString(id.head) + "`.\n")
111	} else {
112		b.WriteString("Every entry below was signed by the account key and verified on chain, ")
113		b.WriteString("and chains to the digest of the one before it.\n\n")
114		b.WriteString("| seq | op | device | block | digest |\n|---|---|---|---|---|\n")
115		for _, e := range id.devices {
116			b.WriteString("| " + strconv.Itoa(e.seq) + " ")
117			b.WriteString("| " + e.op + " ")
118			b.WriteString("| `" + short(hex.EncodeToString(e.devicePK)) + "` ")
119			b.WriteString("| " + strconv.FormatInt(e.height, 10) + " ")
120			b.WriteString("| `" + short(hex.EncodeToString(e.digest)) + "` |\n")
121		}
122		b.WriteString("\nHead: `" + hex.EncodeToString(id.head) + "`\n")
123	}
124
125	if len(id.history) > 1 {
126		b.WriteString("\n## Rotation history\n\n")
127		b.WriteString("Weshnet has no way to announce that a seed was reset, so every link ")
128		b.WriteString("ever shared dies silently. These are the superseded values, kept so a ")
129		b.WriteString("stale link can be recognised as stale.\n\n")
130		b.WriteString("| rev | value | block |\n|---|---|---|\n")
131		for _, r := range id.history {
132			mark := ""
133			if r.revision == id.revision {
134				mark = " (current)"
135			}
136			b.WriteString("| " + strconv.Itoa(r.revision) + mark + " ")
137			b.WriteString("| `" + short(hex.EncodeToString(r.payload)) + "` ")
138			b.WriteString("| " + strconv.FormatInt(r.height, 10) + " |\n")
139		}
140	}
141
142	b.WriteString("\n[Back to the directory](/r/moul/x/wesh/v0) · chain `" + runtime.ChainID() + "`\n")
143	return b.String()
144}
145
146func modeLabel(id *identity) string {
147	if id.committed {
148		return "committed"
149	}
150	return "published"
151}
152
153// activeDevices counts the distinct devices whose latest sigchain entry is an
154// add. A device can be added, revoked and added again, so only the last entry
155// for each key decides.
156func activeDevices(id *identity) int {
157	seen := []string{}
158	for _, e := range id.devices {
159		key := string(e.devicePK)
160		known := false
161		for _, s := range seen {
162			if s == key {
163				known = true
164				break
165			}
166		}
167		if !known {
168			seen = append(seen, key)
169		}
170	}
171	n := 0
172	for _, k := range seen {
173		if deviceIsActive(id, []byte(k)) {
174			n++
175		}
176	}
177	return n
178}
179
180// short abbreviates a long hex string for table cells.
181func short(s string) string {
182	if len(s) <= 16 {
183		return s
184	}
185	return s[:8] + "…" + s[len(s)-8:]
186}