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}