← Back to Realms

Realm

gno.land/r/moul/x/upgrade/selfreg/facade/v0

Overview

Realm Path
gno.land/r/moul/x/upgrade/selfreg/facade/v0
Exported Functions
8
State Entries
7
Source Files
4
Total Package Entries
16

Exported Functions

8 exported functions

State

7 state entries

Source Code

FILES
facade.gno
go
1// Package facade is the permanent entry point of the "self-registering
2// implementation" upgrade pattern (pattern E of the exploration; see
3// ../../README.md).
4//
5// The path callers import never changes and holds no business logic: it holds
6// an interface value. A new implementation realm takes over simply by being
7// deployed, because it registers itself from its own init. Nobody has to send
8// a transaction to switch, which is the pattern's whole appeal and also its
9// whole risk: whoever can deploy under the guarded prefix can take the realm.
10package facade
11
12import (
13	"strings"
14
15	"gno.land/p/nt/ufmt/v0"
16)
17
18// Impl is the contract an implementation realm must satisfy. This interface is
19// the one thing here that can never change: it is compiled into every caller.
20type Impl interface {
21	Greet(name string) string
22	Version() string
23}
24
25// prefix is the only gate. An implementation must live under it.
26//
27// gno ships p/nt/nestedpkg/v0 for exactly this check, but its AssertCallerIsSubPath
28// wants the implementation nested UNDER the facade's own path, and with the
29// version segment last (facade/v0, impl/v0) no sibling is a sub-path of another.
30// IsSameNamespace is the other shipped option and is far too loose: it would let
31// any realm in the whole namespace seize this one. Hence an explicit prefix.
32const prefix = "gno.land/r/moul/x/upgrade/selfreg/impl/"
33
34// The stage ladder. Sui gates package upgrades with an UpgradeCap whose policy
35// runs compatible, additive, dependency-only, immutable, and the rule that makes
36// it worth copying is that a policy can only ever become MORE restrictive.
37// CosmWasm and Solana land on the same primitive from different directions: a
38// contract with no admin, a program whose upgrade authority is None. All three
39// say the same thing, that the way out of "you are trusting the owner rather
40// than the code" is an authority you can drop, permanently.
41//
42// This pattern has no owner, so its ladder has two rungs rather than four: the
43// only actor the facade already trusts is whichever implementation is live.
44const (
45	StageOpen   = 0 // any realm under the prefix takes over by deploying
46	StageSealed = 1 // nothing may register again, the live implementation is final
47)
48
49var (
50	live     Impl
51	livePath string
52	stage    = StageOpen
53)
54
55// Register makes the calling realm the live implementation. Called from the
56// implementation's own init, so deploying IS the upgrade.
57func Register(cur realm, impl Impl) {
58	if stage != StageOpen {
59		panic("selfreg/facade/v0 is sealed, " + livePath + " is final")
60	}
61	caller := cur.Previous().PkgPath()
62	if !strings.HasPrefix(caller, prefix) {
63		panic("unauthorized: " + caller + " is not under " + prefix)
64	}
65	if impl == nil {
66		panic("implementation must not be nil")
67	}
68	live, livePath = impl, caller
69}
70
71// Seal ends this realm's upgradeability, forever. Callable only by the
72// implementation currently serving, because with no owner that is the only
73// actor the facade already trusts. It grants nothing new: whoever could deploy
74// under the prefix could already take the realm over, and this only lets them
75// make that the last word.
76//
77// One-way, and there is no rung above it.
78func Seal(cur realm) {
79	caller := cur.Previous().PkgPath()
80	if livePath == "" || caller != livePath {
81		panic("unauthorized: only the live implementation may seal, and that is " + livePath)
82	}
83	stage = StageSealed
84}
85
86// Stage is the rung this realm is on. It only ever goes up.
87func Stage() int {
88	return stage
89}
90
91// StageName is Stage as the word a caller reads in Render.
92func StageName() string {
93	if stage == StageSealed {
94		return "sealed"
95	}
96	return "open"
97}
98
99// Live is the package path currently serving, or "" before the first deploy.
100func Live() string {
101	return livePath
102}
103
104// Greet forwards to the live implementation.
105func Greet(name string) string {
106	assertLive()
107	return live.Greet(name)
108}
109
110// Version reports the live implementation's own version string.
111func Version() string {
112	assertLive()
113	return live.Version()
114}
115
116func assertLive() {
117	if live == nil {
118		panic("no implementation registered")
119	}
120}
121
122func Render(_ string) string {
123	if live == nil {
124		return ufmt.Sprintf("selfreg/facade/v0 [%s]: no implementation registered\n", StageName())
125	}
126	return ufmt.Sprintf("selfreg/facade/v0 [%s]: %s (%s)\n%s\n", StageName(), live.Version(), livePath, live.Greet("world"))
127}
128

Raw Package Data

Raw JSON data