core.gno
6.23 Kb · 188 lines
1// Package core is the platform's immutable ledger (design §2.5.2): every
2// collection, its official grc721 token and royalty, its provenance
3// (commit–reveal v2), the engine registry, fees and the admin timelock.
4//
5// What core guarantees, whoever holds the admin key:
6//
7// - I-1 admin cannot move, approve or burn a token, and holds no funds;
8// the op list is a closed set of parameters (admin.gno).
9// - I-2 pause only gates create, mint and trade. Transfer, burn,
10// finalize, distribute and every cancel stay open, and a pause expires
11// by itself after MaxPauseBlocks.
12// - I-5 supply only grows through the collection's bound minter, never
13// past maxSupply, and a token id is never minted twice.
14// - I-6 after Launch the engine, params, supply, payees, sale window and
15// commitments are frozen.
16// - I-10/11 a collection becomes Final at most once, only with both
17// reveals or after revealDeadline, and that deadline is bounded, so
18// the pre-reveal transfer lock always ends.
19// - I-12 engine code never runs on a state-changing path, except the
20// creator's own CreateCollection (ValidateParams) and the engine's own
21// RegisterEngine (one sample render). Every exported non-crossing
22// function is read-only, so an engine cannot call back into a mutator.
23// - I-13 every stored string passed safe.AssertText.
24//
25// core imports no mutable realm: minters and engines are known only by
26// package path and interface value.
27package core
28
29import (
30 "chain"
31 "chain/runtime"
32 "chain/runtime/unsafe"
33 "strconv"
34
35 "gno.land/p/nt/avl/v0"
36 "gno.land/p/nt/grc721/royalty/v0"
37 "gno.land/p/nt/grc721/v0"
38
39 "gno.land/p/g19dzk54hhsdc3gllpksuj6gu4a3e6fl8cr6rn4w/sinopia/safe/v1"
40 "gno.land/p/g19dzk54hhsdc3gllpksuj6gu4a3e6fl8cr6rn4w/sinopia/types/v1"
41)
42
43// Hard limits. They are constants on purpose: nobody, admin included, can
44// raise them after deploy (block counts assume ~3.54 s blocks, RA M-1).
45const (
46 ProtocolFeeCapBps = 500 // 5% of any sale
47 RoyaltyCapBps = 1000 // 10% (RA M-3)
48 CreateFeeCap = 1_000_000_000 // 1,000 GNOT in ugnot
49 MaxPayees = 4
50 MaxSupplyCap = 10_000
51 MaxMintPerTx = 10 // hypothesis: measure n=10 gas/deposit on Onyx
52 MaxReserveBps = 1000 // creator reserve <= 10% of supply
53 MaxSaleBlocks = 732_000 // ~30 days
54 RevealWindowMin = 1_017 // ~1 hour
55 RevealWindowMax = 170_000 // ~7 days
56 BeaconGraceBlocks = 254 // beacon may reveal first in the window's last ~15 min (< RevealWindowMin, so the creator always gets to go first)
57 TimelockBlocks = 48_800 // ~48 hours
58 MaxPauseBlocks = 195_000 // ~8 days
59
60 MaxNameLen = 64
61 MaxDescriptionLen = 1024
62 MaxParamsLen = 1024
63 MinSecretLen = 32
64 MaxSecretLen = 256
65 MaxPageSize = 50
66)
67
68// Provenance badges (design §4.3).
69const (
70 BadgeBoth = "2/2"
71 BadgeCreatorOnly = "creator-only"
72 BadgeBeaconOnly = "beacon-only"
73 BadgeFallback = "fallback"
74)
75
76type collection struct {
77 id, name, symbol, description string
78 creator, pendingCreator address
79 engineID, params string
80 maxSupply int64
81 nextTID int64 // next token id; minted so far = nextTID-1
82 reserved int64
83 royaltyBps int64
84 primaryPayees, royaltyPayees []types.Payee
85 minter string
86 state types.State
87 startHeight, endHeight int64
88 revealWindow int64
89 closeHeight int64 // sell-out height, or endHeight once time ran out
90 useBeacon bool
91 createdHeight int64
92 prov *provenance
93 token *grc721.Token
94 ledger *grc721.PrivateLedger
95 roy *royalty.Royalty
96 st *tokenState
97 mintedBy avl.Tree // address -> int64, lifetime, written on every mint (I-9)
98}
99
100func (c *collection) minted() int64 { return c.nextTID - 1 }
101
102var (
103 // selfPath is this realm's package path, captured once in init so read
104 // functions can derive sub-addresses without a cur.
105 selfPath string
106
107 collections avl.Tree // safe.PadInt(cid) -> *collection
108 byCreator avl.Tree // creator + ":" + padded cid -> cid
109 collCount int64
110 usedCommits avl.Tree // commit hex -> cid; a commitment is single-use platform-wide (RA M-5)
111)
112
113func init(cur realm) {
114 selfPath = cur.PkgPath()
115}
116
117const cidWidth = 10
118
119func collKey(cid string) string {
120 return safe.PadInt(safe.ParseID("cid", cid), cidWidth)
121}
122
123func mustCollection(cid string) *collection {
124 v := collections.Get(collKey(cid))
125 if v == nil {
126 panic("unknown collection " + strconv.Quote(cid))
127 }
128 return v.(*collection)
129}
130
131// stateAt is the effective state at height h; the stored state lags until
132// the next write materializes it (refresh).
133func stateAt(c *collection, h int64) types.State {
134 switch c.state {
135 case types.StateScheduled:
136 if h > c.endHeight {
137 return types.StateClosed
138 }
139 if h >= c.startHeight {
140 return types.StateMinting
141 }
142 case types.StateMinting:
143 if h > c.endHeight {
144 return types.StateClosed
145 }
146 }
147 return c.state
148}
149
150func refresh(c *collection, h int64) {
151 s := stateAt(c, h)
152 if s == c.state {
153 return
154 }
155 if s == types.StateClosed && c.closeHeight == 0 {
156 c.closeHeight = c.endHeight
157 }
158 c.state = s
159 chain.Emit("CollectionState", "cid", c.id, "state", s.String())
160}
161
162// revealDeadline is when anyone may finalize without the missing secrets.
163// It is at most endHeight + RevealWindowMax, which bounds the transfer
164// lock (I-11).
165func revealDeadline(c *collection) int64 {
166 closed := c.closeHeight
167 if closed == 0 {
168 closed = c.endHeight
169 }
170 return closed + c.revealWindow
171}
172
173func caller(cur realm) address {
174 return cur.Previous().Address()
175}
176
177// assertNoDeposit refuses coins on functions that take none, so nothing can
178// get stuck in core (RA L-1). The origin-send envelope belongs to the first
179// realm of the call; it is only ours to judge when a wallet called us.
180func assertNoDeposit(cur realm) {
181 if cur.Previous().IsUserCall() && !unsafe.OriginSend().IsZero() {
182 panic("this function does not accept coins")
183 }
184}
185
186func itoa(n int64) string { return strconv.FormatInt(n, 10) }
187
188func height() int64 { return runtime.ChainHeight() }