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

store.gno

11.63 Kb · 354 lines
  1package ucs03_zkgm
  2
  3import (
  4	types "gno.land/p/onbloc/ibc/union/types"
  5
  6	"gno.land/p/nt/bptree/v0"
  7	z "gno.land/p/onbloc/ibc/union/zkgm"
  8	tb "gno.land/p/onbloc/ibc/union/zkgm/tokenbucket"
  9	u256 "gno.land/p/onbloc/math/uint256"
 10)
 11
 12var (
 13	// store is the proxy-owned singleton holding all persistent domain state. It is
 14	// the dependency injected into the implementation realm: the impl receives this
 15	// pointer and mutates state through the SetXXX(_ int, rlm realm, ...) methods,
 16	// whose trailing realm token carries the caller frame so the write satisfies the
 17	// interrealm borrow rule. Reads use plain getters (no mutation, no realm token).
 18	//
 19	// Mapping to the reference CosmWasm contract (state.rs):
 20	//
 21	//	tokenOrigin        <- TOKEN_ORIGIN          Map<denom, U256>
 22	//	channelBalanceV2   <- CHANNEL_BALANCE_V2    Map<(channel,path,base,quote), U256>
 23	//	metadataImageOf    <- METADATA_IMAGE_OF     Map<denom, H256>
 24	//	hashToForeignToken <- HASH_TO_FOREIGN_TOKEN Map<denom, Bytes>
 25	//	inFlightPacket     <- IN_FLIGHT_PACKET      Map<packetHash, Packet>
 26	//	tokenBucket        <- TOKEN_BUCKET          Map<denom, TokenBucket>
 27	//	rateLimitDisabled  <- CONFIG.rate_limit_disabled
 28	//
 29	// CONFIG.{admin,ibc_host} are not duplicated here: they are sourced from core.
 30	// The CosmWasm code_id fields (token_minter/dummy/cw_account) and the
 31	// transaction-scratch items (EXECUTING_PACKET, EXECUTION_ACK, MARKET_MAKER, ...)
 32	// are intentionally omitted: gno calls are synchronous, so that data is carried
 33	// as parameters/locals instead of persisted across submessage hops.
 34	store *Store
 35
 36	// paused is a proxy-level gate checked by the App receivers before delegating to
 37	// the impl. It is governance state, not impl-visible state, so it lives on the
 38	// proxy package rather than inside the injected Store.
 39	paused bool
 40
 41	// upgrade.gno is the single admin-gated swap point for the zkgm app logic. The
 42	// proxy keeps the Store, identity, and escrow funds stable across upgrades; only
 43	// the installed impl changes.
 44	//
 45	// The swap is two-phase, mirroring core's host upgrade model: an impl realm
 46	// registers its constructor under its own package path via RegisterImpl (from its
 47	// init), and the admin later activates a registered path via UpdateImpl. The
 48	// proxy injects its own Store into the constructor, so the store needs no public
 49	// getter — the only way to obtain it is through this gated install.
 50	proxyPkgPath     string  = ""
 51	proxyAddress     address = ""
 52	currentImplPath  string
 53	currentImpl      IApp
 54	implConstructors map[string]func(store IStore) IApp
 55)
 56
 57func init(cur realm) {
 58	store = NewStore()
 59	paused = false
 60
 61	proxyPkgPath = cur.PkgPath()
 62	proxyAddress = cur.Address()
 63	currentImpl = nil
 64	currentImplPath = ""
 65	implConstructors = make(map[string]func(store IStore) IApp)
 66}
 67
 68type Store struct {
 69	tokenOrigin        *bptree.BPTree // denom -> *u256.Uint  (mint path)
 70	channelBalanceV2   *bptree.BPTree // key   -> *u256.Uint  (escrow balance)
 71	metadataImageOf    *bptree.BPTree // denom -> [32]byte
 72	hashToForeignToken *bptree.BPTree // denom -> []byte
 73	inFlightPacket     *bptree.BPTree // packetHash -> types.Packet (parent)
 74	tokenBucket        *bptree.BPTree // denom -> *tb.TokenBucket
 75	vouchers           *bptree.BPTree // ibcDenom -> *Voucher
 76	// lockedTokenAmounts is managed alongside vouchers: both track non-escrow
 77	// custody of a base token (native or grc20) that zkgm cannot supply-burn,
 78	// as opposed to vouchers, which zkgm mints/burns directly.
 79	lockedTokenAmounts *bptree.BPTree // denom -> *u256.Uint (permanently locked base token)
 80	receivers          *bptree.BPTree // pkgPath -> z.Zkgmable
 81	rateLimitDisabled  bool
 82}
 83
 84func NewStore() *Store {
 85	return &Store{
 86		tokenOrigin:        bptree.NewBPTree32(),
 87		channelBalanceV2:   bptree.NewBPTree32(),
 88		metadataImageOf:    bptree.NewBPTree32(),
 89		hashToForeignToken: bptree.NewBPTree32(),
 90		inFlightPacket:     bptree.NewBPTree32(),
 91		tokenBucket:        bptree.NewBPTree32(),
 92		vouchers:           bptree.NewBPTree32(),
 93		lockedTokenAmounts: bptree.NewBPTree32(),
 94		receivers:          bptree.NewBPTree32(),
 95		// Rate limiting is opt-in: enabled per-denom by an admin who first
 96		// configures a token bucket (see SetBucketConfig). With it enabled, a denom
 97		// without a bucket is rejected (union TokenBucketIsAbsent), so it must stay
 98		// disabled by default to avoid bricking every unconfigured denom.
 99		rateLimitDisabled: true,
100	}
101}
102
103// SetTokenOrigin records the mint path for a wrapped denom.
104func (s *Store) SetTokenOrigin(_ int, rlm realm, denom string, path *u256.Uint) {
105	assertIsRlmCurrent(0, rlm)
106
107	pathClone := u256.Zero().Set(path).Clone()
108
109	s.tokenOrigin.Set(denom, pathClone)
110}
111
112// GetTokenOrigin returns the recorded mint path for a denom.
113func (s *Store) GetTokenOrigin(denom string) (*u256.Uint, bool) {
114	v := s.tokenOrigin.Get(denom)
115	if v == nil {
116		return nil, false
117	}
118
119	return v.(*u256.Uint), true
120}
121
122// SetChannelBalanceV2 stores the escrow balance for a channel-balance key.
123func (s *Store) SetChannelBalanceV2(_ int, rlm realm, key string, amount *u256.Uint) {
124	assertIsRlmCurrent(0, rlm)
125
126	calculatedAmount := u256.Zero().Set(amount).Clone()
127
128	s.channelBalanceV2.Set(key, calculatedAmount)
129}
130
131// RemoveChannelBalanceV2 clears a drained channel-balance entry.
132func (s *Store) RemoveChannelBalanceV2(_ int, rlm realm, key string) {
133	assertIsRlmCurrent(0, rlm)
134
135	s.channelBalanceV2.Remove(key)
136}
137
138// GetChannelBalanceV2 returns the escrow balance for a key.
139func (s *Store) GetChannelBalanceV2(key string) (*u256.Uint, bool) {
140	v := s.channelBalanceV2.Get(key)
141	if v == nil {
142		return nil, false
143	}
144
145	return v.(*u256.Uint), true
146}
147
148// SetMetadataImageOf records a wrapped denom's metadata image.
149func (s *Store) SetMetadataImageOf(_ int, rlm realm, denom string, image [32]byte) {
150	assertIsRlmCurrent(0, rlm)
151
152	s.metadataImageOf.Set(denom, image)
153}
154
155// GetMetadataImageOf returns a wrapped denom's metadata image.
156func (s *Store) GetMetadataImageOf(denom string) ([32]byte, bool) {
157	v := s.metadataImageOf.Get(denom)
158	if v == nil {
159		return [32]byte{}, false
160	}
161
162	return v.([32]byte), true
163}
164
165// SetForeignToken records the origin token bytes for a wrapped denom.
166func (s *Store) SetForeignToken(_ int, rlm realm, denom string, token []byte) {
167	assertIsRlmCurrent(0, rlm)
168
169	s.hashToForeignToken.Set(denom, token)
170}
171
172// GetForeignToken returns the origin token bytes for a wrapped denom.
173func (s *Store) GetForeignToken(denom string) ([]byte, bool) {
174	v := s.hashToForeignToken.Get(denom)
175	if v == nil {
176		return nil, false
177	}
178
179	return v.([]byte), true
180}
181
182// SetInFlightPacket records the parent packet awaiting a forwarded child.
183func (s *Store) SetInFlightPacket(_ int, rlm realm, packetHash string, parent types.Packet) {
184	assertIsRlmCurrent(0, rlm)
185
186	s.inFlightPacket.Set(packetHash, parent)
187}
188
189// GetInFlightPacket returns the parent packet for a forwarded-child hash.
190func (s *Store) GetInFlightPacket(packetHash string) (types.Packet, bool) {
191	v := s.inFlightPacket.Get(packetHash)
192	if v == nil {
193		return types.Packet{}, false
194	}
195
196	return v.(types.Packet), true
197}
198
199// RemoveInFlightPacket clears a resolved forwarded-child entry.
200func (s *Store) RemoveInFlightPacket(_ int, rlm realm, packetHash string) {
201	assertIsRlmCurrent(0, rlm)
202
203	s.inFlightPacket.Remove(packetHash)
204}
205
206// SetTokenBucket stores the rate-limit bucket for a denom.
207func (s *Store) SetTokenBucket(_ int, rlm realm, denom string, bucket *tb.TokenBucket) {
208	assertIsRlmCurrent(0, rlm)
209
210	s.tokenBucket.Set(denom, bucket)
211}
212
213// GetTokenBucket returns the rate-limit bucket for a denom.
214func (s *Store) GetTokenBucket(denom string) (*tb.TokenBucket, bool) {
215	v := s.tokenBucket.Get(denom)
216	if v == nil {
217		return nil, false
218	}
219
220	return v.(*tb.TokenBucket), true
221}
222
223// RemoveTokenBucket clears a denom's rate-limit bucket.
224func (s *Store) RemoveTokenBucket(_ int, rlm realm, denom string) {
225	assertIsRlmCurrent(0, rlm)
226
227	s.tokenBucket.Remove(denom)
228}
229
230// SetVoucher stores the wrapped voucher for an ibc denom.
231func (s *Store) SetVoucher(_ int, rlm realm, ibcDenom string, v *Voucher) {
232	assertIsRlmCurrent(0, rlm)
233
234	s.vouchers.Set(ibcDenom, v)
235}
236
237// GetVoucher returns the wrapped voucher for an ibc denom.
238func (s *Store) GetVoucher(ibcDenom string) (*Voucher, bool) {
239	v := s.vouchers.Get(ibcDenom)
240	if v == nil {
241		return nil, false
242	}
243
244	return v.(*Voucher), true
245}
246
247// GetVoucherList returns a page of up to count vouchers starting at offset,
248// ordered by denom. The vouchers tree only grows (there is no voucher
249// deletion), so pagination keeps a single call's gas/response size bounded
250// regardless of how many wrapped tokens have been registered over time.
251func (s *Store) GetVoucherList(offset, count int) []VoucherInfo {
252	voucherList := []VoucherInfo{}
253
254	// The callback returns false to keep iterating (true stops early).
255	s.vouchers.IterateByOffset(offset, count, func(key string, value any) bool {
256		voucher, ok := value.(*Voucher)
257		if !ok {
258			panic(ErrInterfaceConversion)
259		}
260
261		voucherList = append(voucherList, NewVoucherInfo(
262			key,
263			voucher.token.GetName(),
264			voucher.token.GetSymbol(),
265			voucher.originDecimals,
266			uint8(voucher.token.GetDecimals()),
267			voucher.token.TotalSupply(),
268		))
269
270		return false // continue iteration
271	})
272
273	return voucherList
274}
275
276// GetVoucherSize returns the total number of registered vouchers, i.e. the
277// key count callers need to drive GetVoucherList's offset/count pagination.
278func (s *Store) GetVoucherSize() int {
279	return s.vouchers.Size()
280}
281
282// HasVoucher reports whether a voucher exists for an ibc denom.
283func (s *Store) HasVoucher(ibcDenom string) bool {
284	return s.vouchers.Has(ibcDenom)
285}
286
287// AddLockedTokenAmount tracks base-token escrow locked after burn-address settlement
288// (native or a registered grc20; a voucher is supply-burned instead via burnVoucher).
289// Locked base remains in proxy escrow but is excluded from channel balances.
290// Invariant: proxy balance == sum(channelBalanceV2) + sum(lockedTokenAmounts).
291func (s *Store) AddLockedTokenAmount(_ int, rlm realm, denom string, amount *u256.Uint) {
292	assertIsRlmCurrent(0, rlm)
293
294	totalLockedAmount := u256.Zero()
295
296	lockedTokenAmount, exists := s.GetLockedTokenAmount(denom)
297	if exists && lockedTokenAmount != nil {
298		totalLockedAmount = u256.Zero().Set(lockedTokenAmount)
299	}
300
301	// Reject a wraparound past 2^256-1 rather than silently storing a smaller
302	// totalLockedAmount, which would break the proxy balance invariant.
303	// Astronomically unreachable for real coin supplies, but guarded for safety.
304	totalLockedAmount, overflow := u256.Zero().AddOverflow(totalLockedAmount, amount)
305	if overflow {
306		panic(ErrLockedTokenAmountOverflow)
307	}
308
309	s.lockedTokenAmounts.Set(denom, totalLockedAmount.Clone())
310}
311
312// GetLockedTokenAmount returns the total base-token amount permanently locked for a denom.
313func (s *Store) GetLockedTokenAmount(denom string) (*u256.Uint, bool) {
314	v := s.lockedTokenAmounts.Get(denom)
315	if v == nil {
316		return nil, false
317	}
318
319	lockedTokenAmount, ok := v.(*u256.Uint)
320	if !ok {
321		panic(ErrInterfaceConversion)
322	}
323
324	return lockedTokenAmount, true
325}
326
327// SetReceiver registers a Zkgmable callback target at a package path.
328func (s *Store) SetReceiver(_ int, rlm realm, path string, receiver z.Zkgmable) {
329	assertIsRlmCurrent(0, rlm)
330
331	s.receivers.Set(path, receiver)
332}
333
334// GetReceiver returns the callback target registered at a package path.
335func (s *Store) GetReceiver(path string) (z.Zkgmable, bool) {
336	r := s.receivers.Get(path)
337	if r == nil {
338		return nil, false
339	}
340
341	return r.(z.Zkgmable), true
342}
343
344// SetRateLimitDisabled toggles the global rate-limit kill switch.
345func (s *Store) SetRateLimitDisabled(_ int, rlm realm, disabled bool) {
346	assertIsRlmCurrent(0, rlm)
347
348	s.rateLimitDisabled = disabled
349}
350
351// RateLimitDisabled reports whether rate limiting is globally disabled.
352func (s *Store) RateLimitDisabled() bool {
353	return s.rateLimitDisabled
354}