From b191a567b9fadc746c13ec680ab998b859689172 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 10 Dec 2024 20:12:06 +0100 Subject: [PATCH] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/rotree/gno.mod | 1 - .../gno.land/p/demo/avl/rotree/rotree.gno | 162 ------------- .../p/demo/avl/rotree/rotree_test.gno | 222 ------------------ 3 files changed, 385 deletions(-) delete mode 100644 examples/gno.land/p/demo/avl/rotree/gno.mod delete mode 100644 examples/gno.land/p/demo/avl/rotree/rotree.gno delete mode 100644 examples/gno.land/p/demo/avl/rotree/rotree_test.gno diff --git a/examples/gno.land/p/demo/avl/rotree/gno.mod b/examples/gno.land/p/demo/avl/rotree/gno.mod deleted file mode 100644 index d2cb439b2eb..00000000000 --- a/examples/gno.land/p/demo/avl/rotree/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/p/demo/avl/rotree diff --git a/examples/gno.land/p/demo/avl/rotree/rotree.gno b/examples/gno.land/p/demo/avl/rotree/rotree.gno deleted file mode 100644 index 3e093c4d0e0..00000000000 --- a/examples/gno.land/p/demo/avl/rotree/rotree.gno +++ /dev/null @@ -1,162 +0,0 @@ -// Package rotree provides a read-only wrapper for avl.Tree with safe value transformation. -// -// It is useful when you want to expose a read-only view of a tree while ensuring that -// the sensitive data cannot be modified. -// -// Example: -// -// // Define a user structure with sensitive data -// type User struct { -// Name string -// Balance int -// Internal string // sensitive field -// } -// -// // Create and populate the original tree -// privateTree := avl.NewTree() -// privateTree.Set("alice", &User{ -// Name: "Alice", -// Balance: 100, -// Internal: "sensitive", -// }) -// -// // Create a safe transformation function that copies the struct -// // while excluding sensitive data -// makeEntrySafeFn := func(v interface{}) interface{} { -// u := v.(*User) -// return &User{ -// Name: u.Name, -// Balance: u.Balance, -// Internal: "", // omit sensitive data -// } -// } -// -// // Create a read-only view of the tree -// PublicTree := rotree.Wrap(tree, makeEntrySafeFn) -// -// // Safely access the data -// value, _ := roTree.Get("alice") -// user := value.(*User) -// // user.Name == "Alice" -// // user.Balance == 100 -// // user.Internal == "" (sensitive data is filtered) -package rotree - -import ( - "gno.land/p/demo/avl" -) - -// Wrap creates a new ReadOnlyTree from an existing avl.Tree and a safety transformation function. -// If makeEntrySafeFn is nil, values will be returned as-is without transformation. -// -// makeEntrySafeFn is a function that transforms a tree entry into a safe version that can be exposed to external users. -// This function should be implemented based on the specific safety requirements of your use case: -// -// 1. No-op transformation: For primitive types (int, string, etc.) or already safe objects, -// simply pass nil as the makeEntrySafeFn to return values as-is. -// -// 2. Defensive copying: For mutable types like slices or maps, you should create a deep copy -// to prevent modification of the original data. -// Example: func(v interface{}) interface{} { return append([]int{}, v.([]int)...) } -// -// 3. Read-only wrapper: Return a read-only version of the object that implements -// a limited interface. -// Example: func(v interface{}) interface{} { return NewReadOnlyObject(v) } -// -// 4. DAO transformation: Transform the object into a data access object that -// controls how the underlying data can be accessed. -// Example: func(v interface{}) interface{} { return NewDAO(v) } -// -// The function ensures that the returned object is safe to expose to untrusted code, -// preventing unauthorized modifications to the original data structure. -func Wrap(tree *avl.Tree, makeEntrySafeFn func(interface{}) interface{}) *ReadOnlyTree { - return &ReadOnlyTree{ - tree: tree, - makeEntrySafeFn: makeEntrySafeFn, - } -} - -// ReadOnlyTree wraps an avl.Tree and provides read-only access. -type ReadOnlyTree struct { - tree *avl.Tree - makeEntrySafeFn func(interface{}) interface{} -} - -// Verify that ReadOnlyTree implements ITree -var _ avl.ITree = (*ReadOnlyTree)(nil) - -// getSafeValue applies the makeEntrySafeFn if it exists, otherwise returns the original value -func (roTree *ReadOnlyTree) getSafeValue(value interface{}) interface{} { - if roTree.makeEntrySafeFn == nil { - return value - } - return roTree.makeEntrySafeFn(value) -} - -// Size returns the number of key-value pairs in the tree. -func (roTree *ReadOnlyTree) Size() int { - return roTree.tree.Size() -} - -// Has checks whether a key exists in the tree. -func (roTree *ReadOnlyTree) Has(key string) bool { - return roTree.tree.Has(key) -} - -// Get retrieves the value associated with the given key, converted to a safe format. -func (roTree *ReadOnlyTree) Get(key string) (interface{}, bool) { - value, exists := roTree.tree.Get(key) - if !exists { - return nil, false - } - return roTree.getSafeValue(value), true -} - -// GetByIndex retrieves the key-value pair at the specified index in the tree, with the value converted to a safe format. -func (roTree *ReadOnlyTree) GetByIndex(index int) (string, interface{}) { - key, value := roTree.tree.GetByIndex(index) - return key, roTree.getSafeValue(value) -} - -// Iterate performs an in-order traversal of the tree within the specified key range. -func (roTree *ReadOnlyTree) Iterate(start, end string, cb avl.IterCbFn) bool { - return roTree.tree.Iterate(start, end, func(key string, value interface{}) bool { - return cb(key, roTree.getSafeValue(value)) - }) -} - -// ReverseIterate performs a reverse in-order traversal of the tree within the specified key range. -func (roTree *ReadOnlyTree) ReverseIterate(start, end string, cb avl.IterCbFn) bool { - return roTree.tree.ReverseIterate(start, end, func(key string, value interface{}) bool { - return cb(key, roTree.getSafeValue(value)) - }) -} - -// IterateByOffset performs an in-order traversal of the tree starting from the specified offset. -func (roTree *ReadOnlyTree) IterateByOffset(offset int, count int, cb avl.IterCbFn) bool { - return roTree.tree.IterateByOffset(offset, count, func(key string, value interface{}) bool { - return cb(key, roTree.getSafeValue(value)) - }) -} - -// ReverseIterateByOffset performs a reverse in-order traversal of the tree starting from the specified offset. -func (roTree *ReadOnlyTree) ReverseIterateByOffset(offset int, count int, cb avl.IterCbFn) bool { - return roTree.tree.ReverseIterateByOffset(offset, count, func(key string, value interface{}) bool { - return cb(key, roTree.getSafeValue(value)) - }) -} - -// Set is not supported on ReadOnlyTree and will panic. -func (roTree *ReadOnlyTree) Set(key string, value interface{}) bool { - panic("Set operation not supported on ReadOnlyTree") -} - -// Remove is not supported on ReadOnlyTree and will panic. -func (roTree *ReadOnlyTree) Remove(key string) (value interface{}, removed bool) { - panic("Remove operation not supported on ReadOnlyTree") -} - -// RemoveByIndex is not supported on ReadOnlyTree and will panic. -func (roTree *ReadOnlyTree) RemoveByIndex(index int) (key string, value interface{}) { - panic("RemoveByIndex operation not supported on ReadOnlyTree") -} diff --git a/examples/gno.land/p/demo/avl/rotree/rotree_test.gno b/examples/gno.land/p/demo/avl/rotree/rotree_test.gno deleted file mode 100644 index fbc14bd688d..00000000000 --- a/examples/gno.land/p/demo/avl/rotree/rotree_test.gno +++ /dev/null @@ -1,222 +0,0 @@ -package rotree - -import ( - "testing" - - "gno.land/p/demo/avl" -) - -func TestExample(t *testing.T) { - // User represents our internal data structure - type User struct { - ID string - Name string - Balance int - Internal string // sensitive internal data - } - - // Create and populate the original tree with user pointers - tree := avl.NewTree() - tree.Set("alice", &User{ - ID: "1", - Name: "Alice", - Balance: 100, - Internal: "sensitive_data_1", - }) - tree.Set("bob", &User{ - ID: "2", - Name: "Bob", - Balance: 200, - Internal: "sensitive_data_2", - }) - - // Define a makeEntrySafeFn that: - // 1. Creates a defensive copy of the User struct - // 2. Omits sensitive internal data - makeEntrySafeFn := func(v interface{}) interface{} { - originalUser := v.(*User) - return &User{ - ID: originalUser.ID, - Name: originalUser.Name, - Balance: originalUser.Balance, - Internal: "", // Omit sensitive data - } - } - - // Create a read-only view of the tree - roTree := Wrap(tree, makeEntrySafeFn) - - // Test retrieving and verifying a user - t.Run("Get User", func(t *testing.T) { - // Get user from read-only tree - value, exists := roTree.Get("alice") - if !exists { - t.Fatal("User 'alice' not found") - } - - user := value.(*User) - - // Verify user data is correct - if user.Name != "Alice" || user.Balance != 100 { - t.Errorf("Unexpected user data: got name=%s balance=%d", user.Name, user.Balance) - } - - // Verify sensitive data is not exposed - if user.Internal != "" { - t.Error("Sensitive data should not be exposed") - } - - // Verify it's a different instance than the original - originalValue, _ := tree.Get("alice") - originalUser := originalValue.(*User) - if user == originalUser { - t.Error("Read-only tree should return a copy, not the original pointer") - } - }) - - // Test iterating over users - t.Run("Iterate Users", func(t *testing.T) { - count := 0 - roTree.Iterate("", "", func(key string, value interface{}) bool { - user := value.(*User) - // Verify each user has empty Internal field - if user.Internal != "" { - t.Error("Sensitive data exposed during iteration") - } - count++ - return false - }) - - if count != 2 { - t.Errorf("Expected 2 users, got %d", count) - } - }) - - // Verify that modifications to the returned user don't affect the original - t.Run("Modification Safety", func(t *testing.T) { - value, _ := roTree.Get("alice") - user := value.(*User) - - // Try to modify the returned user - user.Balance = 999 - user.Internal = "hacked" - - // Verify original is unchanged - originalValue, _ := tree.Get("alice") - originalUser := originalValue.(*User) - if originalUser.Balance != 100 || originalUser.Internal != "sensitive_data_1" { - t.Error("Original user data was modified") - } - }) -} - -func TestReadOnlyTree(t *testing.T) { - // Example of a makeEntrySafeFn that appends "_readonly" to demonstrate transformation - makeEntrySafeFn := func(value interface{}) interface{} { - return value.(string) + "_readonly" - } - - tree := avl.NewTree() - tree.Set("key1", "value1") - tree.Set("key2", "value2") - tree.Set("key3", "value3") - - roTree := Wrap(tree, makeEntrySafeFn) - - tests := []struct { - name string - key string - expected interface{} - exists bool - }{ - {"ExistingKey1", "key1", "value1_readonly", true}, - {"ExistingKey2", "key2", "value2_readonly", true}, - {"NonExistingKey", "key4", nil, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - value, exists := roTree.Get(tt.key) - if exists != tt.exists || value != tt.expected { - t.Errorf("For key %s, expected %v (exists: %v), got %v (exists: %v)", tt.key, tt.expected, tt.exists, value, exists) - } - }) - } -} - -// Add example tests showing different makeEntrySafeFn implementations -func TestMakeEntrySafeFnVariants(t *testing.T) { - tree := avl.NewTree() - tree.Set("slice", []int{1, 2, 3}) - tree.Set("map", map[string]int{"a": 1}) - - tests := []struct { - name string - makeEntrySafeFn func(interface{}) interface{} - key string - validate func(t *testing.T, value interface{}) - }{ - { - name: "Defensive Copy Slice", - makeEntrySafeFn: func(v interface{}) interface{} { - original := v.([]int) - return append([]int{}, original...) - }, - key: "slice", - validate: func(t *testing.T, value interface{}) { - slice := value.([]int) - // Modify the returned slice - slice[0] = 999 - // Verify original is unchanged - originalValue, _ := tree.Get("slice") - original := originalValue.([]int) - if original[0] != 1 { - t.Error("Original slice was modified") - } - }, - }, - // Add more test cases for different makeEntrySafeFn implementations - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - roTree := Wrap(tree, tt.makeEntrySafeFn) - value, exists := roTree.Get(tt.key) - if !exists { - t.Fatal("Key not found") - } - tt.validate(t, value) - }) - } -} - -func TestNilMakeEntrySafeFn(t *testing.T) { - // Create a tree with some test data - tree := avl.NewTree() - originalValue := []int{1, 2, 3} - tree.Set("test", originalValue) - - // Create a ReadOnlyTree with nil makeEntrySafeFn - roTree := Wrap(tree, nil) - - // Test that we get back the original value - value, exists := roTree.Get("test") - if !exists { - t.Fatal("Key not found") - } - - // Verify it's the exact same slice (not a copy) - retrievedSlice := value.([]int) - if &retrievedSlice[0] != &originalValue[0] { - t.Error("Expected to get back the original slice reference") - } - - // Test through iteration as well - roTree.Iterate("", "", func(key string, value interface{}) bool { - retrievedSlice := value.([]int) - if &retrievedSlice[0] != &originalValue[0] { - t.Error("Expected to get back the original slice reference in iteration") - } - return false - }) -}