Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}