Search Apps Documentation Source Content File Folder Download Copy Actions Download

block.gno

8.19 Kb ยท 397 lines
  1package block
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6
  7	"gno.land/p/nt/avl"
  8	"gno.land/p/nt/ufmt"
  9)
 10
 11const (
 12	CreateBlockEvent       = "CreateBlock"
 13	CreateSystemBlockEvent = "CreateSystemBlock"
 14	DeleteBlockEvent       = "DeleteBlock"
 15	DeleteSystemBlockEvent = "DeleteSystemBlock"
 16	SetBlockPropertyEvent  = "SetBlockProperty"
 17)
 18
 19var (
 20	blocks      = avl.NewTree()
 21	nextBlockID uint32 = 100000
 22	typeIndex   = avl.NewTree() // blockType -> map[blockID]bool
 23)
 24
 25func CreateBlock(
 26	cur realm,
 27	name, blockType, layer, drawType, textureLayer, textureURL, previewURL string,
 28	maxSupply, mintPrice, usePrice, installerBPS, shape, state uint32,
 29	emission uint8,
 30	option string,
 31) uint32 {
 32	if name == "" {
 33		panic("name cannot be empty")
 34	}
 35
 36	if maxSupply == 0 {
 37		panic("max supply must be greater than 0")
 38	}
 39
 40	if installerBPS > 10000 {
 41		panic("installer bps must be between 0 and 10000")
 42	}
 43
 44	caller := runtime.OriginCaller()
 45
 46	blockID := getNextBlockID()
 47	blockIDStr := blockIDToString(blockID)
 48	blockIDKey := blockIDToKey(blockID)
 49
 50	maxSupplyStr := ufmt.Sprintf("%d", maxSupply)
 51	mintPriceStr := ufmt.Sprintf("%d", mintPrice)
 52	usePriceStr := ufmt.Sprintf("%d", usePrice)
 53	installerBPSStr := ufmt.Sprintf("%d", installerBPS)
 54	shapeStr := ufmt.Sprintf("%d", shape)
 55	stateStr := ufmt.Sprintf("%d", state)
 56	emissionStr := ufmt.Sprintf("%d", emission)
 57
 58	properties := map[string]string{
 59		"id":           blockIDStr,
 60		"name":         name,
 61		"blockType":    blockType,
 62		"layer":        layer,
 63		"drawType":     drawType,
 64		"textureLayer": textureLayer,
 65		"textureURL":   textureURL,
 66		"previewURL":   previewURL,
 67		"maxSupply":    maxSupplyStr,
 68		"mintPrice":    mintPriceStr,
 69		"usePrice":     usePriceStr,
 70		"installerBps": installerBPSStr,
 71		"shape":        shapeStr,
 72		"state":        stateStr,
 73		"emission":     emissionStr,
 74		"option":       option,
 75		"creator":      caller.String(),
 76	}
 77
 78	blocks.Set(blockIDKey, properties)
 79
 80	if blockType != "" {
 81		addToTypeIndex(uint32(blockID), blockType)
 82	}
 83
 84	chain.Emit(
 85		CreateBlockEvent,
 86		"id", blockIDStr,
 87		"name", name,
 88		"creator", caller.String(),
 89	)
 90
 91	return blockID
 92}
 93
 94func CreateSystemBlock(
 95	cur realm,
 96	blockID uint32,
 97	name, blockType, layer, drawType, textureLayer, textureURL, previewURL string,
 98	maxSupply int64, mintPrice, usePrice, installerBPS, shape, state uint32,
 99	emission uint8,
100	option string,
101) {
102	caller := runtime.OriginCaller()
103	assertIsAdmin(caller)
104
105	if name == "" {
106		panic("name cannot be empty")
107	}
108
109	blockIDStr := blockIDToString(blockID)
110	blockIDKey := blockIDToKey(blockID)
111
112	if !isSystemBlockTid(blockID) {
113		panic("block ID must be less than 100000")
114	}
115
116	if _, found := blocks.Get(blockIDKey); found {
117		panic("block already exists: " + blockIDStr)
118	}
119
120	if maxSupply < -1 {
121		panic("max supply must be greater than -2")
122	}
123
124	if installerBPS > 10000 {
125		panic("installer bps must be between 0 and 10000")
126	}
127
128	maxSupplyStr := ufmt.Sprintf("%d", maxSupply)
129	mintPriceStr := ufmt.Sprintf("%d", mintPrice)
130	usePriceStr := ufmt.Sprintf("%d", usePrice)
131	installerBPSStr := ufmt.Sprintf("%d", installerBPS)
132	shapeStr := ufmt.Sprintf("%d", shape)
133	stateStr := ufmt.Sprintf("%d", state)
134	emissionStr := ufmt.Sprintf("%d", emission)
135
136	properties := map[string]string{
137		"id":           blockIDStr,
138		"name":         name,
139		"blockType":    blockType,
140		"layer":        layer,
141		"drawType":     drawType,
142		"textureLayer": textureLayer,
143		"textureURL":   textureURL,
144		"previewURL":   previewURL,
145		"maxSupply":    maxSupplyStr,
146		"mintPrice":    mintPriceStr,
147		"usePrice":     usePriceStr,
148		"installerBps": installerBPSStr,
149		"shape":        shapeStr,
150		"state":        stateStr,
151		"emission":     emissionStr,
152		"option":       option,
153		"creator":      caller.String(),
154	}
155
156	blocks.Set(blockIDKey, properties)
157
158	if blockType != "" {
159		addToTypeIndex(blockID, blockType)
160	}
161
162	chain.Emit(
163		CreateSystemBlockEvent,
164		"id", blockIDStr,
165		"name", name,
166		"creator", caller.String(),
167	)
168}
169
170func DeleteBlock(cur realm, blockID uint32) {
171	caller := runtime.OriginCaller()
172	assertIsAdmin(caller)
173
174	if isSystemBlockTid(blockID) {
175		panic("cannot delete system block: " + blockIDToString(blockID))
176	}
177
178	deleteBlock(blockID)
179
180	chain.Emit(
181		DeleteBlockEvent,
182		"id", blockIDToString(blockID),
183		"caller", caller.String(),
184	)
185}
186
187func DeleteSystemBlock(cur realm, blockID uint32) {
188	caller := runtime.OriginCaller()
189	assertIsAdmin(caller)
190
191	if !isSystemBlockTid(blockID) {
192		panic("not a system block: " + blockIDToString(blockID))
193	}
194
195	deleteBlock(blockID)
196
197	chain.Emit(
198		DeleteSystemBlockEvent,
199		"id", blockIDToString(blockID),
200		"caller", caller.String(),
201	)
202}
203
204func deleteBlock(blockID uint32) {
205	blockIDKey := blockIDToKey(blockID)
206	block, found := blocks.Get(blockIDKey)
207	if !found {
208		panic("block not found: " + blockIDToString(blockID))
209	}
210
211	blockType := block.(map[string]string)["blockType"]
212	if blockType != "" {
213		removeFromTypeIndex(blockID, blockType)
214	}
215
216	blocks.Remove(blockIDKey)
217}
218
219func SetBlockProperty(cur realm, blockID uint32, key string, value string) {
220	caller := runtime.OriginCaller()
221	assertIsAdmin(caller)
222
223	if key == "id" {
224		panic("reserved key: id")
225	}
226
227	blockIDStr := blockIDToString(blockID)
228	blockIDKey := blockIDToKey(blockID)
229	block, found := blocks.Get(blockIDKey)
230	if !found {
231		panic("block not found: " + blockIDStr)
232	}
233
234	block.(map[string]string)[key] = value
235
236	chain.Emit(
237		SetBlockPropertyEvent,
238		"id", blockIDStr,
239		"key", key,
240		"value", value,
241	)
242}
243
244func GetCurrentBlockID() uint32 {
245	return nextBlockID
246}
247
248func GetTotalBlockSize() int {
249	return blocks.Size()
250}
251
252func ListBlocks(page, count int) []map[string]string {
253	if page < 1 {
254		panic("page must be at least 1")
255	}
256	if count < 1 {
257		panic("count must be at least 1")
258	}
259	if count > listLimit {
260		panic("count exceeds listLimit")
261	}
262
263	result := []map[string]string{}
264
265	offset := (page - 1) * count
266
267	blocks.IterateByOffset(offset, count, func(_ string, value interface{}) bool {
268		properties := value.(map[string]string)
269		result = append(result, properties)
270		return false
271	})
272
273	return result
274}
275
276func ListBlocksFromTo(from, to, limit uint32) []map[string]string {
277	if limit > uint32(listLimit) {
278		panic("limit exceeds listLimit")
279	}
280
281	result := []map[string]string{}
282
283	if from > to {
284		panic("from is greater than to")
285	}
286
287	size := uint32(blocks.Size())
288	if to >= size {
289		to = size - 1
290	}
291
292	for ; from <= to; from++ {
293		blockIDKey := blockIDToKey(from)
294		blockVal, found := blocks.Get(blockIDKey)
295		if !found {
296			continue
297		}
298
299		block := blockVal.(map[string]string)
300		result = append(result, block)
301
302		if len(result) >= int(limit) {
303			break
304		}
305	}
306
307	return result
308}
309
310func ListBlocksByIDs(blockIDs ...uint32) []map[string]string {
311	if len(blockIDs) > listLimit {
312		panic("blockIDs exceeds listLimit")
313	}
314
315	result := []map[string]string{}
316
317	if len(blockIDs) == 0 {
318		return result
319	}
320
321	for _, blockID := range blockIDs {
322		blockIDKey := blockIDToKey(blockID)
323		blockVal, found := blocks.Get(blockIDKey)
324		if !found {
325			continue
326		}
327
328		block := blockVal.(map[string]string)
329		result = append(result, block)
330	}
331
332	return result
333}
334
335func ListBlocksByType(blockType string) []map[string]string {
336	result := []map[string]string{}
337
338	blockIDsVal, found := typeIndex.Get(blockType)
339	if !found {
340		return result
341	}
342
343	blockIDs := blockIDsVal.(map[uint32]bool)
344
345	for blockID := range blockIDs {
346		blockIDKey := blockIDToKey(blockID)
347		blockVal, found := blocks.Get(blockIDKey)
348		if !found {
349			continue
350		}
351
352		block := blockVal.(map[string]string)
353		result = append(result, block)
354	}
355
356	return result
357}
358
359// addToTypeIndex adds a block to the type index
360func addToTypeIndex(blockID uint32, blockType string) {
361	blockIDsVal, found := typeIndex.Get(blockType)
362
363	var blockIDs map[uint32]bool
364	if found {
365		blockIDs = blockIDsVal.(map[uint32]bool)
366	} else {
367		blockIDs = make(map[uint32]bool)
368		typeIndex.Set(blockType, blockIDs)
369	}
370
371	blockIDs[blockID] = true
372}
373
374// removeFromTypeIndex removes a block from the type index
375func removeFromTypeIndex(blockID uint32, blockType string) {
376	blockIDsVal, found := typeIndex.Get(blockType)
377	if !found {
378		return
379	}
380
381	blockIDs := blockIDsVal.(map[uint32]bool)
382	delete(blockIDs, blockID)
383
384	if len(blockIDs) == 0 {
385		typeIndex.Remove(blockType)
386	}
387}
388
389func isSystemBlockTid(blockID uint32) bool {
390	return blockID < 100000
391}
392
393func getNextBlockID() uint32 {
394	id := nextBlockID
395	nextBlockID++
396	return id
397}