Search Apps Documentation Source Content File Folder Download Copy Actions Download

migration_research_store.gno

9.32 Kb · 326 lines
  1package chunk
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/g1nqnrt3aldzhu6zzeg75yw97wvavqy7wr77g56q/deploy-test/v0/v0/grc721"
  8	"gno.land/p/g1nqnrt3aldzhu6zzeg75yw97wvavqy7wr77g56q/deploy-test/v0/v0/rbac"
  9	oldchunk "gno.land/r/g1nqnrt3aldzhu6zzeg75yw97wvavqy7wr77g56q/deploy-test/v0/v0/chunk"
 10)
 11
 12type WorldStore = oldchunk.WorldStore
 13type NFTStore = oldchunk.NFTStore
 14type VerifierStore = oldchunk.VerifierStore
 15type ChunkGRC721Token = oldchunk.ChunkGRC721Token
 16
 17const (
 18	MigrationResearchExpectedWorldType      = "migration_research"
 19	MigrationResearchExpectedWorldName      = "Migration Research Chunk World"
 20	MigrationResearchExpectedWorldSlug      = "migration-research-chunk-world"
 21	MigrationResearchExpectedRole           = "migration_research_role"
 22	MigrationResearchExpectedRoleLabel      = "Migration Research Role"
 23	MigrationResearchExpectedPermission     = "migration:research"
 24	MigrationResearchExpectedPermissionDesc = "Migration Research Permission"
 25	MigrationResearchExpectedGrantableRole  = "editor"
 26	MigrationResearchUpdatedWorldName       = "Migration Research Chunk World Updated"
 27	MigrationResearchUpdatedWorldSlug       = "migration-research-chunk-world-updated"
 28	MigrationResearchUpdatedWorldProperty   = "updated"
 29	MigrationResearchUpdatedWorldRegion     = "migration_region_updated"
 30	MigrationResearchUpdatedHash            = "migration_hash_updated"
 31	MigrationResearchUpdatedVerifier        = "migration_verifier_updated"
 32)
 33
 34var MigrationResearchExpectedGrantee = address("g1x8l886kqzdlqt8v0znrj89mhas0h242caseqe9")
 35
 36const metadataSeparator = "|"
 37
 38var (
 39	worldStore     *WorldStore
 40	nftStore       *NFTStore
 41	verifierStore  *VerifierStore
 42	listLimit      int
 43	batchLimit     int
 44	frozen         bool
 45	chunkAuthzRBAC *rbac.RBAC
 46	worldAuthzRBAC *rbac.RBAC
 47)
 48
 49func TokenCount() int64 {
 50	return nftStore.TokenCount()
 51}
 52
 53func BalanceOf(user address) int64 {
 54	balance, err := nftStore.BalanceOf(user)
 55	if err != nil {
 56		panic("balanceOf failed: " + err.Error())
 57	}
 58	return balance
 59}
 60
 61func OwnerOf(tokenID grc721.TokenID) address {
 62	owner, found := nftStore.OwnerOfSafe(tokenID)
 63	if !found {
 64		_, err := nftStore.OwnerOf(tokenID)
 65		panic("ownerOf failed: " + err.Error())
 66	}
 67	return owner
 68}
 69
 70func TransferFrom(cur realm, from address, to address, tokenID grc721.TokenID) {
 71	worldID, _, _ := parseChunkKey(string(tokenID))
 72	err := nftStore.Transfer(from, from, to, tokenID, worldID)
 73	if err != nil {
 74		panic("transferFrom failed: " + err.Error())
 75	}
 76	chunkAuthzRBAC.DeleteEntity(tokenID.String())
 77}
 78
 79func Mint(cur realm, to address, worldType string, worldID uint32, x int, y int, chunkHashKey string, chunkVerifier string) grc721.TokenID {
 80	worldStore.AssertExists(worldID)
 81	chunkKey := buildChunkKey(worldID, x, y)
 82	tokenID := grc721.TokenID(chunkKey)
 83	err := nftStore.Mint(to, tokenID, worldID, buildChunkMetadataValue(worldType, chunkHashKey))
 84	if err != nil {
 85		panic("mint failed for " + chunkKey + ": " + err.Error())
 86	}
 87	if chunkVerifier != "" {
 88		verifierStore.Set(worldID, chunkKey, chunkVerifier)
 89	}
 90	return tokenID
 91}
 92
 93func SetChunkMetadata(cur realm, worldType string, worldID uint32, x int32, y int32, hash string) {
 94	worldStore.AssertExists(worldID)
 95	tokenID := grc721.TokenID(buildChunkKey(worldID, int(x), int(y)))
 96	nftStore.SetChunkMetadata(worldID, tokenID, buildChunkMetadataValue(worldType, hash))
 97}
 98
 99func GetChunkMetadata(chunkKey string) map[string]string {
100	worldID, _, _ := parseChunkKey(chunkKey)
101	row := nftStore.GetChunkMetadata(worldID, grc721.TokenID(chunkKey))
102	if row == nil {
103		return nil
104	}
105	return buildMetadataResponse(row)
106}
107
108func GetChunkMetadataSizeByWorld(worldID uint32) int {
109	return nftStore.WorldMetadataSize(worldID)
110}
111
112func GetOwnerTokenSize(worldID uint32, owner address) int {
113	return nftStore.OwnerTokenSize(worldID, owner)
114}
115
116func ListChunkKeysByOwner(worldID uint32, owner address, page int, count int) []string {
117	return nftStore.ListTokenIDsByOwner(worldID, owner, page, count)
118}
119
120func SetChunkVerifier(cur realm, worldID uint32, chunkKey string, verifier string) {
121	verifierStore.Set(worldID, normalizeChunkKey(worldID, chunkKey), verifier)
122}
123
124func GetChunkVerifier(worldID uint32, chunkKey string) string {
125	verifier, found := verifierStore.Get(worldID, normalizeChunkKey(worldID, chunkKey))
126	if !found {
127		return ""
128	}
129	return verifier
130}
131
132func GetTotalWorldSize() int {
133	return worldStore.Total()
134}
135
136func GetWorld(worldID uint32) map[string]string {
137	return worldStore.MustGet(worldID)
138}
139
140func GetWorldBySlug(slug string) map[string]string {
141	return worldStore.MustGetBySlug(slug)
142}
143
144func UpdateWorld(cur realm, worldID uint32, propKeys string, propValues string) {
145	world := copyStringMap(worldStore.MustGet(worldID))
146	props := migrationResearchProperties(propKeys, propValues)
147	for key, value := range props {
148		world[key] = value
149	}
150	world["updatedAt"] = "0"
151	worldStore.Update(worldID, world)
152}
153
154func IsFrozen() bool {
155	return frozen
156}
157
158func Unfreeze(cur realm) {
159	frozen = false
160}
161
162func GetListLimit() int {
163	return listLimit
164}
165
166func GetBatchLimit() int {
167	return batchLimit
168}
169
170func GetPermission(name string) map[string]string {
171	if !chunkAuthzRBAC.HasPermission(name) {
172		return nil
173	}
174	permission := chunkAuthzRBAC.GetPermission(name)
175	return map[string]string{
176		"name":        permission.Name,
177		"description": permission.Description,
178	}
179}
180
181func GetRoleInfo(roleName string) map[string]string {
182	if !chunkAuthzRBAC.HasRole(roleName) {
183		return nil
184	}
185	role := chunkAuthzRBAC.GetRole(roleName)
186	info := copyStringMap(role.Metadata)
187	info["name"] = role.Name
188	return info
189}
190
191func ListGrantables(granterRole string, page int, count int) []string {
192	if !chunkAuthzRBAC.HasRole(granterRole) {
193		return []string{}
194	}
195	return chunkAuthzRBAC.ListGrantableRoles(granterRole, page, count)
196}
197
198func GrantRole(cur realm, tokenID grc721.TokenID, roleName string, user address) {
199	if _, found := nftStore.OwnerOfSafe(tokenID); !found {
200		panic("token not found: " + string(tokenID))
201	}
202	chunkAuthzRBAC.AssignRole(tokenID.String(), user, roleName)
203}
204
205func RevokeRole(cur realm, tokenID grc721.TokenID, roleName string, user address) {
206	if _, found := nftStore.OwnerOfSafe(tokenID); !found {
207		panic("token not found: " + string(tokenID))
208	}
209	chunkAuthzRBAC.UnassignRole(tokenID.String(), user, roleName)
210}
211
212func HasRole(tokenID grc721.TokenID, user address, roleName string) bool {
213	if !chunkAuthzRBAC.HasRole(roleName) {
214		return false
215	}
216	return chunkAuthzRBAC.HasUserRole(tokenID.String(), user, roleName)
217}
218
219func ListRoleGrants(tokenID grc721.TokenID, roleName string, page int, count int) []address {
220	if !chunkAuthzRBAC.HasRole(roleName) {
221		return []address{}
222	}
223	return chunkAuthzRBAC.ListRoleUsers(tokenID.String(), roleName, page, count)
224}
225
226func RevokeMaster(cur realm, worldID uint32, user address) {
227	worldStore.AssertExists(worldID)
228	worldAuthzRBAC.UnassignRole(formatWorldID(worldID), user, "master")
229}
230
231func IsMaster(worldID uint32, user address) bool {
232	return worldAuthzRBAC.HasUserRole(formatWorldID(worldID), user, "master")
233}
234
235func buildChunkMetadataValue(worldType string, hash string) string {
236	return worldType + metadataSeparator + hash
237}
238
239func buildMetadataResponse(row map[string]string) map[string]string {
240	chunkKey := row["id"]
241	value := row["metadata"]
242	worldID, x, y := parseChunkKey(chunkKey)
243	parts := strings.SplitN(value, metadataSeparator, 2)
244	hash := ""
245	if len(parts) > 1 {
246		hash = parts[1]
247	}
248	return map[string]string{
249		"id":        chunkKey,
250		"worldId":   strconv.FormatUint(uint64(worldID), 10),
251		"x":         strconv.Itoa(x),
252		"y":         strconv.Itoa(y),
253		"worldType": parts[0],
254		"hash":      hash,
255	}
256}
257
258func formatWorldID(worldID uint32) string {
259	return strconv.FormatUint(uint64(worldID), 10)
260}
261
262func buildChunkKey(worldID uint32, x int, y int) string {
263	return strconv.FormatUint(uint64(worldID), 10) + ":" + strconv.Itoa(x) + "_" + strconv.Itoa(y)
264}
265
266func parseChunkKey(chunkKey string) (uint32, int, int) {
267	parts := strings.SplitN(chunkKey, ":", 2)
268	if len(parts) != 2 {
269		panic("invalid chunkKey format: " + chunkKey)
270	}
271	worldID, err := strconv.ParseUint(parts[0], 10, 32)
272	if err != nil {
273		panic("invalid worldID in chunkKey: " + chunkKey)
274	}
275	coords := strings.SplitN(parts[1], "_", 2)
276	if len(coords) != 2 {
277		panic("invalid coordinates in chunkKey: " + chunkKey)
278	}
279	x, errX := strconv.Atoi(coords[0])
280	if errX != nil {
281		panic("invalid x in chunkKey: " + chunkKey)
282	}
283	y, errY := strconv.Atoi(coords[1])
284	if errY != nil {
285		panic("invalid y in chunkKey: " + chunkKey)
286	}
287	return uint32(worldID), x, y
288}
289
290func normalizeChunkKey(worldID uint32, chunkKey string) string {
291	parsedWorldID, x, y := parseChunkKey(chunkKey)
292	if parsedWorldID != worldID {
293		panic("chunkKey worldID mismatch: expected " + formatWorldID(worldID) + ", got " + formatWorldID(parsedWorldID))
294	}
295	return buildChunkKey(worldID, x, y)
296}
297
298func migrationResearchProperties(keys string, values string) map[string]string {
299	if keys == "" {
300		panic("properties keys must be not empty")
301	}
302	keyList := strings.Split(keys, ",")
303	valueList := strings.Split(values, ",")
304	if len(keyList) != len(valueList) {
305		panic("properties keys and values count mismatch")
306	}
307	result := map[string]string{}
308	for i, key := range keyList {
309		if key == "" {
310			panic("properties key must be not empty")
311		}
312		result[key] = valueList[i]
313	}
314	return result
315}
316
317func copyStringMap(source map[string]string) map[string]string {
318	if source == nil {
319		return nil
320	}
321	result := map[string]string{}
322	for key, value := range source {
323		result[key] = value
324	}
325	return result
326}