example_test.gno
6.32 Kb · 157 lines
1package authz
2
3// Example_basic demonstrates initializing and using a basic member authority
4func Example_basic(cur realm) {
5 // Initialize from the EOA caller (e.g. in init(cur realm) of a realm
6 // being deployed): caller passes the EOA address; the realm itself
7 // is responsible for verifying the caller is an EOA when needed.
8 auth := NewWithMembers(cur.Previous().Address())
9
10 // Authorize with DoByPrevious, NOT DoByCurrent: the member set holds
11 // cur.Previous().Address(), and DoByCurrent would present
12 // cur.Address() -- this realm -- which is never in that set, so the
13 // action would silently never run. Seeding and authorizing must read
14 // the SAME side of the frame. Matches the package Quick Start.
15 if err := auth.DoByPrevious(0, cur, "update_config", func() error {
16 // config = newValue
17 return nil
18 }); err != nil {
19 panic(err)
20 }
21}
22
23// Example_addingMembers demonstrates how to add new members to a member authority
24func Example_addingMembers(cur realm) {
25 // Seed with Previous(), not Address(): MemberAuthority.AddMember
26 // derives its principal as rlm.Previous().Address(), so a set holding
27 // only cur.Address() authorizes nobody and the AddMember below fails.
28 auth := NewWithMembers(cur.Previous().Address())
29
30 // Add a new member to the authority
31 memberAuth := auth.Authority().(*MemberAuthority)
32 if err := memberAuth.AddMember(0, cur, address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5")); err != nil {
33 panic(err)
34 }
35}
36
37// Example_contractAuthority demonstrates a contract-based authority.
38//
39// A plain NewContractAuthority accepts exactly one principal:
40// chain.PackageAddress(path). Point `path` at the principal you want to
41// ASSERT -- normally the governance realm that will drive the action --
42// and authorize with DoByPrevious, so the comparison is against whoever
43// crossed in. See NewContractAuthority's godoc.
44//
45// Do NOT pair a plain ContractAuthority on your OWN path with
46// DoByCurrent. rlm.Address() inside any crossing frame of a realm is
47// unconditionally PackageAddress(ownPath), so that gate compares the
48// realm to itself, can never reject, and any exported crossing
49// entrypoint next to it becomes an unauthenticated privileged write --
50// the self-comparing-gate shape. Own-path is right only when something
51// OTHER than your own frame supplies the caller, i.e. DoByPrevious from
52// a realm you deliberately let call in, or Transfer.
53//
54// Nothing here catches a wrong pairing: Example_* functions that take
55// any parameter are never executed by `gno test` (see isExampleFunc in
56// gnovm/pkg/test/test.go -- it rejects on method receiver, parameters or
57// results; "// Output:" plays no part in the predicate, so adding one
58// does NOT make the body run), so a sentinel panic in here still reports
59// ok. filetests/z_contract_authority_shape_filetest.gno executes the
60// gate from a real realm instead.
61//
62// Corollary of the identity gate: do not export anything that returns a
63// crossing closure of this realm, or callers inherit the authority.
64func Example_contractAuthority(cur realm) {
65 // This realm asserts "the governance realm governs me".
66 auth := NewWithAuthority(
67 NewContractAuthority(
68 "gno.land/r/gov/dao", // the principal asserted, not this realm
69 mockDAOHandler, // defined elsewhere for example
70 ),
71 )
72
73 // Authorized only when gno.land/r/gov/dao is the realm that crossed
74 // into this function -- in practice, when a passed proposal executes.
75 // Every other caller presents its own address here and is refused,
76 // which is the whole point of the gate. Never discard this error.
77 if err := auth.DoByPrevious(0, cur, "update_params", func() error {
78 return nil
79 }); err != nil {
80 panic(err)
81 }
82}
83
84// Example_restrictedContractAuthority demonstrates a contract authority with member-only proposals
85func Example_restrictedContractAuthority(cur realm) {
86 // Initialize member authority for proposers
87 proposerAuth := NewMemberAuthority(
88 address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), // admin1
89 address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj"), // admin2
90 )
91
92 // Create contract authority with restricted proposers
93 auth := NewWithAuthority(
94 NewRestrictedContractAuthority(
95 "gno.land/r/demo/dao",
96 mockDAOHandler,
97 proposerAuth,
98 ),
99 )
100
101 // Only members can propose, and contract must approve.
102 //
103 // DoByPrevious, not DoByCurrent: the proposer set holds EOA
104 // addresses, and DoByCurrent would present this realm's own address,
105 // which is in no member set -- the proposer would reject and the
106 // action would silently never run.
107 if err := auth.DoByPrevious(0, cur, "update_params", func() error {
108 // Executes after:
109 // 1. Proposer initiates
110 // 2. DAO approves
111 return nil
112 }); err != nil {
113 panic(err)
114 }
115}
116
117// Example_switchingAuthority demonstrates switching from member to contract
118// authority.
119//
120// Note what a plain ContractAuthority on a FOREIGN path means after the
121// transfer: this realm loses the ability to take the authority back, and
122// DoByCurrent against it is dead. It is NOT a one-way door in the
123// stronger sense an earlier draft implied — the foreign realm can still
124// drive and rotate it via Previous() once it crosses in, which is the
125// intended async-DAO shape. What is given up is the original owner's
126// claim, not the authority's mutability. (The ADR for this change states
127// the same thing; keep the two in step.) To keep the authority here
128// while letting others propose, transfer to
129// NewRestrictedContractAuthority(ownPath, handler, proposerAuth)
130// instead.
131func Example_switchingAuthority(cur realm) {
132 // Start with member authority.
133 //
134 // Seed the member set with Previous(), not Address(): Transfer
135 // derives its principal as rlm.Previous().Address(), so a set
136 // containing only cur.Address() authorizes nobody but a
137 // same-package cross-call, and the Transfer below would abort for
138 // every external caller — including a realm that copies this into
139 // init(cur realm), which then fails its deploy tx. Matches
140 // Example_basic and NewWithMembers' godoc.
141 auth := NewWithMembers(cur.Previous().Address())
142
143 // Create and switch to contract authority — control moves to
144 // gno.land/r/demo/dao.
145 daoAuthority := NewContractAuthority(
146 "gno.land/r/demo/dao",
147 mockDAOHandler,
148 )
149 if err := auth.Transfer(0, cur, daoAuthority); err != nil {
150 panic(err)
151 }
152}
153
154// Mock handler for examples
155func mockDAOHandler(title string, action PrivilegedAction) error {
156 return action()
157}