Skip to content

Commit 4b0c341

Browse files
authored
feat(examples): add {p,r}/n2p5/loci (#3338)
# loci (package and realm) This is a realm I've developed as part of a larger project I have in the works. While I have a specific purpose for it, the loci realm is free to be used by anyone who wants to have a mutable data store for placing a byte slice tied to their caller address. This can be useful for pointing to other immutable data. `loci` is a single purpose datastore keyed by the caller's address. It has two functions: Set and Get. loci is plural for locus, which is a central or core place where something is found or from which it originates. In this case, it's a simple key-value store where an address (the key) can store exactly one value (in the form of a byte slice). Only the caller can set the value for their address, but anyone can retrieve the value for any address.
1 parent 705f424 commit 4b0c341

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed

examples/gno.land/p/n2p5/loci/gno.mod

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/p/n2p5/loci
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// loci is a single purpose datastore keyed by the caller's address. It has two
2+
// functions: Set and Get. loci is plural for locus, which is a central or core
3+
// place where something is found or from which it originates. In this case,
4+
// it's a simple key-value store where an address (the key) can store exactly
5+
// one value (in the form of a byte slice). Only the caller can set the value
6+
// for their address, but anyone can retrieve the value for any address.
7+
package loci
8+
9+
import (
10+
"std"
11+
12+
"gno.land/p/demo/avl"
13+
)
14+
15+
// LociStore is a simple key-value store that uses
16+
// an AVL tree to store the data.
17+
type LociStore struct {
18+
internal *avl.Tree
19+
}
20+
21+
// New creates a reference to a new LociStore.
22+
func New() *LociStore {
23+
return &LociStore{
24+
internal: avl.NewTree(),
25+
}
26+
}
27+
28+
// Set stores a byte slice in the AVL tree using the `std.PrevRealm().Addr()`
29+
// string as the key.
30+
func (s *LociStore) Set(value []byte) {
31+
key := string(std.PrevRealm().Addr())
32+
s.internal.Set(key, value)
33+
}
34+
35+
// Get retrieves a byte slice from the AVL tree using the provided address.
36+
// The return values are the byte slice value and a boolean indicating
37+
// whether the value exists.
38+
func (s *LociStore) Get(addr std.Address) []byte {
39+
value, exists := s.internal.Get(string(addr))
40+
if !exists {
41+
return nil
42+
}
43+
return value.([]byte)
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package loci
2+
3+
import (
4+
"std"
5+
"testing"
6+
7+
"gno.land/p/demo/testutils"
8+
)
9+
10+
func TestLociStore(t *testing.T) {
11+
t.Parallel()
12+
13+
u1 := testutils.TestAddress("u1")
14+
u2 := testutils.TestAddress("u1")
15+
16+
t.Run("TestSet", func(t *testing.T) {
17+
t.Parallel()
18+
store := New()
19+
u1 := testutils.TestAddress("u1")
20+
21+
m1 := []byte("hello")
22+
m2 := []byte("world")
23+
std.TestSetOrigCaller(u1)
24+
25+
// Ensure that the value is nil before setting it.
26+
r1 := store.Get(u1)
27+
if r1 != nil {
28+
t.Errorf("expected value to be nil, got '%s'", r1)
29+
}
30+
store.Set(m1)
31+
// Ensure that the value is correct after setting it.
32+
r2 := store.Get(u1)
33+
if string(r2) != "hello" {
34+
t.Errorf("expected value to be 'hello', got '%s'", r2)
35+
}
36+
store.Set(m2)
37+
// Ensure that the value is correct after overwriting it.
38+
r3 := store.Get(u1)
39+
if string(r3) != "world" {
40+
t.Errorf("expected value to be 'world', got '%s'", r3)
41+
}
42+
})
43+
t.Run("TestGet", func(t *testing.T) {
44+
t.Parallel()
45+
store := New()
46+
u1 := testutils.TestAddress("u1")
47+
u2 := testutils.TestAddress("u2")
48+
u3 := testutils.TestAddress("u3")
49+
u4 := testutils.TestAddress("u4")
50+
51+
m1 := []byte("hello")
52+
m2 := []byte("world")
53+
m3 := []byte("goodbye")
54+
55+
std.TestSetOrigCaller(u1)
56+
store.Set(m1)
57+
std.TestSetOrigCaller(u2)
58+
store.Set(m2)
59+
std.TestSetOrigCaller(u3)
60+
store.Set(m3)
61+
62+
// Ensure that the value is correct after setting it.
63+
r0 := store.Get(u4)
64+
if r0 != nil {
65+
t.Errorf("expected value to be nil, got '%s'", r0)
66+
}
67+
// Ensure that the value is correct after setting it.
68+
r1 := store.Get(u1)
69+
if string(r1) != "hello" {
70+
t.Errorf("expected value to be 'hello', got '%s'", r1)
71+
}
72+
// Ensure that the value is correct after setting it.
73+
r2 := store.Get(u2)
74+
if string(r2) != "world" {
75+
t.Errorf("expected value to be 'world', got '%s'", r2)
76+
}
77+
// Ensure that the value is correct after setting it.
78+
r3 := store.Get(u3)
79+
if string(r3) != "goodbye" {
80+
t.Errorf("expected value to be 'goodbye', got '%s'", r3)
81+
}
82+
})
83+
84+
}

examples/gno.land/r/n2p5/loci/gno.mod

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/r/n2p5/loci
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package loci
2+
3+
import (
4+
"encoding/base64"
5+
"std"
6+
7+
"gno.land/p/demo/ufmt"
8+
"gno.land/p/n2p5/loci"
9+
)
10+
11+
var store *loci.LociStore
12+
13+
func init() {
14+
store = loci.New()
15+
}
16+
17+
// Set takes a base64 encoded string and stores it in the Loci store.
18+
// Keyed by the address of the caller. It also emits a "set" event with
19+
// the address of the caller.
20+
func Set(value string) {
21+
b, err := base64.StdEncoding.DecodeString(value)
22+
if err != nil {
23+
panic(err)
24+
}
25+
store.Set(b)
26+
std.Emit("SetValue", "ForAddr", string(std.PrevRealm().Addr()))
27+
}
28+
29+
// Get retrieves the value stored at the provided address and
30+
// returns it as a base64 encoded string.
31+
func Get(addr std.Address) string {
32+
return base64.StdEncoding.EncodeToString(store.Get(addr))
33+
}
34+
35+
func Render(path string) string {
36+
if path == "" {
37+
return about
38+
}
39+
return renderGet(std.Address(path))
40+
}
41+
42+
func renderGet(addr std.Address) string {
43+
value := "```\n" + Get(addr) + "\n```"
44+
45+
return ufmt.Sprintf(`
46+
# Loci Value Viewer
47+
48+
**Address:** %s
49+
50+
%s
51+
52+
`, addr, value)
53+
}
54+
55+
const about = `
56+
# Welcome to Loci
57+
58+
Loci is a simple key-value store keyed by the caller's gno.land address.
59+
Only the caller can set the value for their address, but anyone can
60+
retrieve the value for any address. There are only two functions: Set and Get.
61+
If you'd like to set a value, simply base64 encode any message you'd like and
62+
it will be stored in in Loci. If you'd like to retrieve a value, simply provide
63+
the address of the value you'd like to retrieve.
64+
65+
For convenience, you can also use gnoweb to view the value for a given address,
66+
if one exists. For instance append :g1j39fhg29uehm7twwnhvnpz3ggrm6tprhq65t0t to
67+
this URL to view the value stored at that address.
68+
`

0 commit comments

Comments
 (0)