-
Notifications
You must be signed in to change notification settings - Fork 383
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/p/n2p5/loci |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// 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. | ||
package loci | ||
|
||
import ( | ||
"std" | ||
|
||
"gno.land/p/demo/avl" | ||
) | ||
|
||
// LociStore is a simple key-value store that uses | ||
// an AVL tree to store the data. | ||
type LociStore struct { | ||
internal *avl.Tree | ||
} | ||
|
||
// New creates a reference to a new LociStore. | ||
func New() *LociStore { | ||
return &LociStore{ | ||
internal: avl.NewTree(), | ||
} | ||
} | ||
|
||
// Set stores a byte slice in the AVL tree using the `std.PrevRealm().Addr()` | ||
// string as the key. | ||
func (s *LociStore) Set(value []byte) { | ||
key := string(std.PrevRealm().Addr()) | ||
s.internal.Set(key, value) | ||
} | ||
|
||
// Get retrieves a byte slice from the AVL tree using the provided address. | ||
// The return values are the byte slice value and a boolean indicating | ||
// whether the value exists. | ||
func (s *LociStore) Get(addr std.Address) []byte { | ||
value, exists := s.internal.Get(string(addr)) | ||
if !exists { | ||
return nil | ||
} | ||
return value.([]byte) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package loci | ||
|
||
import ( | ||
"std" | ||
"testing" | ||
|
||
"gno.land/p/demo/testutils" | ||
) | ||
|
||
func TestLociStore(t *testing.T) { | ||
t.Parallel() | ||
|
||
u1 := testutils.TestAddress("u1") | ||
u2 := testutils.TestAddress("u1") | ||
|
||
t.Run("TestSet", func(t *testing.T) { | ||
t.Parallel() | ||
store := New() | ||
u1 := testutils.TestAddress("u1") | ||
|
||
m1 := []byte("hello") | ||
m2 := []byte("world") | ||
std.TestSetOrigCaller(u1) | ||
|
||
// Ensure that the value is nil before setting it. | ||
r1 := store.Get(u1) | ||
if r1 != nil { | ||
t.Errorf("expected value to be nil, got '%s'", r1) | ||
} | ||
store.Set(m1) | ||
// Ensure that the value is correct after setting it. | ||
r2 := store.Get(u1) | ||
if string(r2) != "hello" { | ||
t.Errorf("expected value to be 'hello', got '%s'", r2) | ||
} | ||
store.Set(m2) | ||
// Ensure that the value is correct after overwriting it. | ||
r3 := store.Get(u1) | ||
if string(r3) != "world" { | ||
t.Errorf("expected value to be 'world', got '%s'", r3) | ||
} | ||
}) | ||
t.Run("TestGet", func(t *testing.T) { | ||
t.Parallel() | ||
store := New() | ||
u1 := testutils.TestAddress("u1") | ||
u2 := testutils.TestAddress("u2") | ||
u3 := testutils.TestAddress("u3") | ||
u4 := testutils.TestAddress("u4") | ||
|
||
m1 := []byte("hello") | ||
m2 := []byte("world") | ||
m3 := []byte("goodbye") | ||
|
||
std.TestSetOrigCaller(u1) | ||
store.Set(m1) | ||
std.TestSetOrigCaller(u2) | ||
store.Set(m2) | ||
std.TestSetOrigCaller(u3) | ||
store.Set(m3) | ||
|
||
// Ensure that the value is correct after setting it. | ||
r0 := store.Get(u4) | ||
if r0 != nil { | ||
t.Errorf("expected value to be nil, got '%s'", r0) | ||
} | ||
// Ensure that the value is correct after setting it. | ||
r1 := store.Get(u1) | ||
if string(r1) != "hello" { | ||
t.Errorf("expected value to be 'hello', got '%s'", r1) | ||
} | ||
// Ensure that the value is correct after setting it. | ||
r2 := store.Get(u2) | ||
if string(r2) != "world" { | ||
t.Errorf("expected value to be 'world', got '%s'", r2) | ||
} | ||
// Ensure that the value is correct after setting it. | ||
r3 := store.Get(u3) | ||
if string(r3) != "goodbye" { | ||
t.Errorf("expected value to be 'goodbye', got '%s'", r3) | ||
} | ||
}) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/r/n2p5/loci |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package loci | ||
|
||
import ( | ||
"encoding/base64" | ||
"std" | ||
|
||
"gno.land/p/demo/ufmt" | ||
"gno.land/p/n2p5/loci" | ||
) | ||
|
||
var store *loci.LociStore | ||
|
||
func init() { | ||
store = loci.New() | ||
} | ||
|
||
// Set takes a base64 encoded string and stores it in the Loci store. | ||
// Keyed by the address of the caller. It also emits a "set" event with | ||
// the address of the caller. | ||
func Set(value string) { | ||
b, err := base64.StdEncoding.DecodeString(value) | ||
if err != nil { | ||
panic(err) | ||
} | ||
store.Set(b) | ||
std.Emit("SetValue", "ForAddr", string(std.PrevRealm().Addr())) | ||
} | ||
|
||
// Get retrieves the value stored at the provided address and | ||
// returns it as a base64 encoded string. | ||
func Get(addr std.Address) string { | ||
return base64.StdEncoding.EncodeToString(store.Get(addr)) | ||
} | ||
|
||
func Render(path string) string { | ||
if path == "" { | ||
return about | ||
} | ||
return renderGet(std.Address(path)) | ||
} | ||
|
||
func renderGet(addr std.Address) string { | ||
value := "```\n" + Get(addr) + "\n```" | ||
|
||
return ufmt.Sprintf(` | ||
# Loci Value Viewer | ||
|
||
**Address:** %s | ||
|
||
%s | ||
|
||
`, addr, value) | ||
} | ||
|
||
const about = ` | ||
# Welcome to Loci | ||
|
||
Loci is a simple key-value store keyed by the caller's gno.land address. | ||
Only the caller can set the value for their address, but anyone can | ||
retrieve the value for any address. There are only two functions: Set and Get. | ||
If you'd like to set a value, simply base64 encode any message you'd like and | ||
it will be stored in in Loci. If you'd like to retrieve a value, simply provide | ||
the address of the value you'd like to retrieve. | ||
|
||
For convenience, you can also use gnoweb to view the value for a given address, | ||
if one exists. For instance append :g1j39fhg29uehm7twwnhvnpz3ggrm6tprhq65t0t to | ||
this URL to view the value stored at that address. | ||
` |