installed_block_store.gno
6.89 Kb · 242 lines
1package block
2
3import (
4 "gno.land/p/akkadia/v0/ds/btree"
5 "gno.land/p/akkadia/v0/ds/btreeset"
6)
7
8var installedBlockStore = newInstalledBlockStore()
9
10// InstalledBlockStore owns installed block indexes, encoded installed payloads,
11// encoded use logs, and per-user use log indexes.
12//
13// Data shape:
14// - personalWorldInstalled: storeKey(string) -> *StringBTree(coordKey(string) -> *StringBTree(blockIndexKey(string) -> query-string payload(string)))
15// - systemChunkInstalled: storeKey(string) -> *StringBTree(coordKey(string) -> *StringBTree(blockIndexKey(string) -> query-string payload(string)))
16// - useLogs: logID(uint64) -> query-string payload(string)
17// - userLogIndex: user(address string) -> *Uint64BTreeSet(logID)
18//
19// Responsibilities:
20// - store canonical installed block payloads under position-derived storage keys
21// - encode installed block payloads and use logs as query strings
22// - allocate use log IDs and maintain per-user use log indexes
23// - return fresh maps from public read methods
24//
25// Non-responsibilities:
26// - construct installed block payloads from domain inputs
27// - perform auth, payment, freeze, migration, or event handling
28type InstalledBlockStore struct {
29 personalWorldInstalled *btree.StringBTree
30 systemChunkInstalled *btree.StringBTree
31 useLogs *btree.Uint64BTree
32 userLogIndex *btree.StringBTree
33 nextLogID uint64
34}
35
36func newInstalledBlockStore() *InstalledBlockStore {
37 return &InstalledBlockStore{
38 personalWorldInstalled: btree.NewStringBTree(32),
39 systemChunkInstalled: btree.NewStringBTree(32),
40 useLogs: btree.NewUint64BTree(32),
41 userLogIndex: btree.NewStringBTree(32),
42 nextLogID: 1,
43 }
44}
45
46// ==================== DANGER: MUTABLE MIGRATION CAPABILITIES ====================
47//
48// The getters in this section expose mutable store internals.
49// Only authorized migration exporter paths may depend on these capabilities.
50// Runtime reads and test setup must use copy-returning methods or total helpers.
51func (s *InstalledBlockStore) PersonalWorldInstalled() *btree.StringBTree {
52 return s.personalWorldInstalled
53}
54
55func (s *InstalledBlockStore) SystemChunkInstalled() *btree.StringBTree {
56 return s.systemChunkInstalled
57}
58
59func (s *InstalledBlockStore) UseLogs() *btree.Uint64BTree {
60 return s.useLogs
61}
62
63func (s *InstalledBlockStore) UserLogIndex() *btree.StringBTree {
64 return s.userLogIndex
65}
66
67// ================= END DANGER: MUTABLE MIGRATION CAPABILITIES ==================
68
69func (s *InstalledBlockStore) SaveInstalled(positionType string, storeKey string, coordKey string, blockIndexKey string, info map[string]string) map[string]string {
70 store := s.storeForPositionType(positionType)
71 stored := copyStringMap(info)
72 blockTree := s.getOrCreateInstalledBlockTree(store, storeKey, coordKey)
73 blockTree.Set(blockIndexKey, encodeStringMap(stored))
74
75 return stored
76}
77
78func (s *InstalledBlockStore) RemoveInstalled(positionType string, storeKey string, coordKey string, blockIndexKey string) {
79 store := s.storeForPositionType(positionType)
80 coordTreeVal, found := store.Get(storeKey)
81 if !found {
82 return
83 }
84
85 coordTree := coordTreeVal.(*btree.StringBTree)
86 blockTreeVal, found := coordTree.Get(coordKey)
87 if !found {
88 return
89 }
90
91 blockTree := blockTreeVal.(*btree.StringBTree)
92 blockTree.Remove(blockIndexKey)
93}
94
95func (s *InstalledBlockStore) GetInstalled(positionType string, storeKey string, coordKey string, blockIndexKey string) (map[string]string, bool) {
96 store := s.storeForPositionType(positionType)
97 coordTreeVal, found := store.Get(storeKey)
98 if !found {
99 return nil, false
100 }
101
102 coordTree := coordTreeVal.(*btree.StringBTree)
103 blockTreeVal, found := coordTree.Get(coordKey)
104 if !found {
105 return nil, false
106 }
107
108 blockTree := blockTreeVal.(*btree.StringBTree)
109 infoVal, found := blockTree.Get(blockIndexKey)
110 if !found {
111 return nil, false
112 }
113
114 return decodeStringMap(infoVal.(string)), true
115}
116
117func (s *InstalledBlockStore) HasInstalled(positionType string, storeKey string, coordKey string, blockIndexKey string) bool {
118 _, found := s.GetInstalled(positionType, storeKey, coordKey, blockIndexKey)
119 return found
120}
121
122func (s *InstalledBlockStore) appendUseLog(log map[string]string) uint64 {
123 logID := s.nextID()
124 nextLog := copyStringMap(log)
125 nextLog["id"] = logIDToString(logID)
126 s.useLogs.Set(logID, encodeStringMap(nextLog))
127 return logID
128}
129
130func (s *InstalledBlockStore) RecordUse(user address, result map[string]string) map[string]string {
131 if result["id"] != "" {
132 panic("use result already recorded")
133 }
134
135 logID := s.appendUseLog(result)
136 s.addUserLogIndex(user, logID)
137
138 recorded := copyStringMap(result)
139 recorded["id"] = logIDToString(logID)
140 return recorded
141}
142
143func (s *InstalledBlockStore) addUserLogIndex(user address, logID uint64) {
144 userStr := user.String()
145 logs, found := s.userLogIndex.Get(userStr)
146
147 var logIDs *btreeset.Uint64BTreeSet
148 if found {
149 logIDs = logs.(*btreeset.Uint64BTreeSet)
150 } else {
151 logIDs = btreeset.NewUint64BTreeSet(32)
152 s.userLogIndex.Set(userStr, logIDs)
153 }
154
155 logIDs.Set(logID)
156}
157
158func (s *InstalledBlockStore) ListUseLogs(user address, limit int) []map[string]string {
159 result := []map[string]string{}
160 if limit <= 0 {
161 return result
162 }
163
164 logs, found := s.userLogIndex.Get(user.String())
165 if !found {
166 return result
167 }
168
169 count := 0
170 logIDs := logs.(*btreeset.Uint64BTreeSet)
171 logIDs.ReverseIterate(nil, nil, func(logID uint64) bool {
172 if count >= limit {
173 return true
174 }
175
176 logData, found := s.useLogs.Get(logID)
177 if !found {
178 return false
179 }
180
181 result = append(result, decodeStringMap(logData.(string)))
182 count++
183 return false
184 })
185 return result
186}
187
188func (s *InstalledBlockStore) NextLogID() uint64 {
189 return s.nextLogID
190}
191
192func (s *InstalledBlockStore) UseLogTotal() int {
193 return s.useLogs.Size()
194}
195
196func (s *InstalledBlockStore) UserLogIndexTotal() int {
197 return s.userLogIndex.Size()
198}
199
200func (s *InstalledBlockStore) PersonalInstalledTotal() int {
201 return s.personalWorldInstalled.Size()
202}
203
204func (s *InstalledBlockStore) SystemInstalledTotal() int {
205 return s.systemChunkInstalled.Size()
206}
207
208func (s *InstalledBlockStore) storeForPositionType(positionType string) *btree.StringBTree {
209 if positionType == "personal" {
210 return s.personalWorldInstalled
211 }
212 if positionType == "system" {
213 return s.systemChunkInstalled
214 }
215 panic("unknown position type: " + positionType)
216}
217
218func (s *InstalledBlockStore) getOrCreateInstalledBlockTree(store *btree.StringBTree, storeKey string, coordKey string) *btree.StringBTree {
219 coordTreeVal, found := store.Get(storeKey)
220 var coordTree *btree.StringBTree
221 if found {
222 coordTree = coordTreeVal.(*btree.StringBTree)
223 } else {
224 coordTree = btree.NewStringBTree(32)
225 store.Set(storeKey, coordTree)
226 }
227
228 blockTreeVal, found := coordTree.Get(coordKey)
229 if found {
230 return blockTreeVal.(*btree.StringBTree)
231 }
232
233 blockTree := btree.NewStringBTree(32)
234 coordTree.Set(coordKey, blockTree)
235 return blockTree
236}
237
238func (s *InstalledBlockStore) nextID() uint64 {
239 id := s.nextLogID
240 s.nextLogID++
241 return id
242}