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

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// ]