event_provenance_filetest.gno
4.28 Kb · 139 lines
1// PKGPATH: gno.land/r/demo/grc20provenance
2
3// This filetest is the reference for how an indexer attributes a grc20 event to
4// the realm that issued the token. It is the cross-realm counterpart to
5// newtoken_event_filetest.gno, which covers the within-realm duplicate signal.
6//
7// Two fields matter, and neither is sufficient alone:
8//
9// - pkg_path is stamped by the VM from the package whose code calls
10// chain.Emit. Every genuine grc20 event therefore carries
11// gno.land/p/nt/grc20/v0 — a constant, so it identifies the library
12// but never the token.
13// - the "token" attribute carries Token.ID(), whose leading component is
14// rlm.PkgPath() captured under IsCurrent() in NewToken. A realm cannot
15// produce a Token whose id claims a different realm.
16//
17// Chained, they are sufficient: pkg_path proves the event came from grc20's
18// code, and grc20's code only ever writes an IsCurrent-verified realm prefix
19// into "token". So for any event bearing grc20's pkg_path, the realm prefix in
20// "token" is unforgeable.
21//
22// The realm below is an impostor trying to pass its own transfers off as
23// another realm's token, by both routes available to it, and failing at both.
24package grc20provenance
25
26import (
27 "chain"
28
29 "gno.land/p/nt/grc20/v0"
30)
31
32// Stand-in for a token issued and registered by some other realm. Written as a
33// literal rather than imported: a filetest under a /p/ package that imports an
34// /r/ realm which itself imports that /p/ package forms a genesis dependency
35// cycle (grc20 -> realm -> grc20), which breaks package loading chain-wide.
36const canonical = "gno.land/r/demo/canonicaltoken.FOO.0000000"
37
38func main(cur realm) {
39 println("canonical id:", canonical)
40
41 // Route 1: ask grc20 for a token using the canonical token's own
42 // name/symbol/decimals/id. NewToken derives the id prefix from the live
43 // crossing frame, so the id names *this* realm. No argument changes that.
44 impostor, impostorLedger := grc20.NewToken("Foo", "FOO", 4, 0, cur)
45 println("impostor id: ", impostor.ID())
46 println("impostor claims canonical id:", impostor.ID() == canonical)
47
48 // A genuine grc20 event from the impostor. It carries grc20's pkg_path —
49 // identical to the canonical token's events — but the "token" prefix is
50 // this realm, so an indexer attributes it here.
51 impostorLedger.Mint(cur.Address(), 999_999)
52
53 // Route 2: skip grc20 and hand-emit an event carrying the canonical id.
54 // The attributes are fully attacker-chosen, but pkg_path is not: the VM
55 // stamps this realm. An indexer that accepts Transfer only from grc20's
56 // pkg_path drops this event.
57 chain.Emit(
58 "Transfer",
59 "token", canonical,
60 "from", "",
61 "to", cur.Address().String(),
62 "value", "999999",
63 )
64}
65
66// Output:
67// canonical id: gno.land/r/demo/canonicaltoken.FOO.0000000
68// impostor id: gno.land/r/demo/grc20provenance.FOO.0000000
69// impostor claims canonical id: false
70
71// Events:
72// [
73// {
74// "type": "NewToken",
75// "attrs": [
76// {
77// "key": "token",
78// "value": "gno.land/r/demo/grc20provenance.FOO.0000000"
79// },
80// {
81// "key": "name",
82// "value": "Foo"
83// },
84// {
85// "key": "symbol",
86// "value": "FOO"
87// },
88// {
89// "key": "decimals",
90// "value": "4"
91// }
92// ],
93// "pkg_path": "gno.land/p/nt/grc20/v0"
94// },
95// {
96// "type": "Transfer",
97// "attrs": [
98// {
99// "key": "token",
100// "value": "gno.land/r/demo/grc20provenance.FOO.0000000"
101// },
102// {
103// "key": "from",
104// "value": ""
105// },
106// {
107// "key": "to",
108// "value": "g1vz4y807zq7p3s63dmyng9g80l58nmuc3lgmfjs"
109// },
110// {
111// "key": "value",
112// "value": "999999"
113// }
114// ],
115// "pkg_path": "gno.land/p/nt/grc20/v0"
116// },
117// {
118// "type": "Transfer",
119// "attrs": [
120// {
121// "key": "token",
122// "value": "gno.land/r/demo/canonicaltoken.FOO.0000000"
123// },
124// {
125// "key": "from",
126// "value": ""
127// },
128// {
129// "key": "to",
130// "value": "g1vz4y807zq7p3s63dmyng9g80l58nmuc3lgmfjs"
131// },
132// {
133// "key": "value",
134// "value": "999999"
135// }
136// ],
137// "pkg_path": "gno.land/r/demo/grc20provenance"
138// }
139// ]