admin.gno
2.42 Kb · 77 lines
1package core
2
3import (
4 "chain/runtime/unsafe"
5
6 "gno.land/p/nt/bptree/v0"
7 "gno.land/p/nt/ufmt/v0"
8)
9
10var (
11 admin address
12 relayers = bptree.NewBPTree32()
13)
14
15func init() {
16 // Set admin to the deployer's EOA. In production this is non-empty.
17 // In the gno test framework the base store is pre-loaded with
18 // OriginCaller="" so admin stays empty; SetAdmin then acts as bootstrap.
19 admin = unsafe.OriginCaller()
20}
21
22// SetAdmin sets or transfers the admin role.
23// If no admin is currently set (admin was not initialized in init, e.g. in
24// tests), the first caller becomes admin. Once set, only the current admin
25// can change it.
26func SetAdmin(cur realm, newAdmin address) {
27 if admin != "" {
28 ensureAdminCaller()
29 }
30 admin = newAdmin
31}
32
33// AddRelayer adds an address to the authorized relayer whitelist.
34// Can only be called by the admin.
35func AddRelayer(cur realm, addr address) {
36 ensureAdminCaller()
37 relayers.Set(string(addr), true)
38}
39
40// RemoveRelayer removes an address from the authorized relayer whitelist.
41// Can only be called by the admin.
42func RemoveRelayer(cur realm, addr address) {
43 ensureAdminCaller()
44 relayers.Remove(string(addr))
45}
46
47// ensureAdminCaller panics if the EOA that signed the tx is not the admin.
48// Uses unsafe.OriginCaller for tx-level EOA identity — admin authority is
49// intentionally bound to the EOA that submitted the tx, which has no cur
50// equivalent (cur.Previous() identifies the immediate frame, not the
51// signer). Mirrors the v2 admin pattern in r/gnoland/blog/admin.gno.
52//
53// Trade-off: this allows the admin EOA to authorize admin ops through any
54// intermediate realm (e.g. via maketx run, or a wrapper realm). That is
55// the same tx.origin risk class as `runtime.OriginCaller` in v1; callers
56// who set admin to an EOA must ensure that EOA does not call into
57// untrusted code in the same tx.
58func ensureAdminCaller() {
59 caller := unsafe.OriginCaller()
60 if caller != admin {
61 panic(ufmt.Sprintf("unauthorized: caller %s is not the admin", caller))
62 }
63}
64
65// ensureAuthorizedRelayer panics if the EOA that signed the tx is not in the
66// relayer whitelist. If the whitelist is empty any EOA is allowed. Returns
67// the relayer's EOA address. Same EOA-binding rationale as ensureAdminCaller.
68func ensureAuthorizedRelayer() address {
69 caller := unsafe.OriginCaller()
70 if relayers.Size() == 0 {
71 return caller
72 }
73 if !relayers.Has(string(caller)) {
74 panic(ufmt.Sprintf("unauthorized relayer %s", caller))
75 }
76 return caller
77}