diff --git a/examples/gno.land/p/jaekwon/book/README.md b/examples/gno.land/p/jaekwon/book/README.md new file mode 100644 index 00000000000..0eb64a4c613 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/README.md @@ -0,0 +1,20 @@ +Design choices for Actions + * Action must be in string serializable form for human review. + * Object references in args should be disallowed for simplicity; + an Action, like an HTTP Request, is discrete structure. + * Is "unmarshalling" opinionated? No, let people choose encoding. + +Secure Gno: (move elsewhere) + 1. An unexposed (lowercase) declaration can be used by anyone who holds it. + 1. Unexposed fields of any struct can still be copied by assignment. + 1. You can also copy an unexposed struct's unexposed field and get a + reference. `x := external.MakePrivateStructPtr(); y := *x; z := &y` + 1. You could *maybe* prevent the above by only returning interface + values, and generally preventing the holding of an unexposed declaration, + but this also depends on whether reflection supports instantiation, and the + user would still need to check that the type is what they expect it to be. + 1. In other words, don't expect to prevent creation of new references for + security. + 1. You can tell whether a reference was copied or not by checking the value of + a private field that was originally set to reference. + `x := &unexposedStruct{ptr:nil}; x.ptr = x` diff --git a/examples/gno.land/p/jaekwon/book/action.gno b/examples/gno.land/p/jaekwon/book/action.gno new file mode 100644 index 00000000000..5e7c0ba5009 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/action.gno @@ -0,0 +1,186 @@ +package book + +import ( + "errors" + "std" +) + +//---------------------------------------- +// Action + +// A actor (subject) acts upon an object with a verb and +// arguments. As if actor is calling object.verb(args...). +// +// The sequence is usually an incrementing number, +// but could be something else tracked by an action book. +// +// The actor and object are denoted by their respective +// paths (or URIs?). +type Action struct { + sequence string // typically an incrementing number + actor std.Address // actor (subject) id + object std.Address // object id + verb string // verb name + args []string // legible args + status ActionStatus // new, pending, etc +} + +func (a *Action) Sequence() string { return a.sequence } +func (a *Action) Actor() std.Address { return a.actor } +func (a *Action) Object() std.Address { return a.object } +func (a *Action) Verb() string { return a.verb } +func (a *Action) NumArgs() int { return len(a.args) } +func (a *Action) Arg(n int) string { return a.args[n] } +func (a *Action) Status() ActionStatus { return a.status } + +func (a *Action) setPending() { + if a.status != ActionStatusNew { + panic("should not happen") + } + a.status = ActionStatusPending +} + +func (a *Action) SetReceived() { + if a.status != ActionStatusPending { + panic("should not happen") + } + a.status = ActionStatusReceived +} + +func (a *Action) setComplete() { + if a.status != ActionStatusPending && + s.status != ActionStatusReceived { + panic("should not happen") + } + a.status = ActionStatusComplete +} + +func (a *Action) setError() { + a.status = ActionStatusError +} + +type ActionStatus int + +const ( + ActionStatusNew = iota + ActionStatusPending + ActionStatusReceived + ActionStatusComplete + ActionStatusError +) + +//---------------------------------------- +// Actor + +type Actor interface { + std.Addressable + // CheckAction verifies that action is + // valid as the next action for Actor. + // It should have no side effect. + CheckAction(action) error +} + +//---------------------------------------- +// Object + +type Object interface { + std.Addressable + // Receive performs action on self. + // Returns an error if action is not permitted. + ReceiveAction(*Action, Actor) error +} + +//---------------------------------------- +// Main entry methods + +// Perform the action on the actor/object in directory. +// How an action is authenticated is determined by the +// implementation of the actor; one could use cryptographic +// private key to authenticate an action, or, one could use +// a object (capabilities) private key, or any other method. +// +// In the implementations provided here, the action is +// first either added to a CryptoActionBook or a +// PrivateActionBook, each backed by an ActionBook (but an +// actor is not required to have an ActionBook). +// +// Case 1 w/ signatures +// 1. caller adds Action to CryptoActionBook. +// 2. CryptoActionBook checks signature & sequence. +// 3. CryptoActionBook calls Perform(). +// 4. Perform asks Actor to verify Action. +// 5. Actor's ActionBook says it is good. +// 6. Perform makes object receive action. +// +// Case 2 w/o signatures +// 1. caller adds Action to PrivActionBook. +// 2. PrivActionBook checks sequence. +// 3. PrivActionBook calls Perform(). +// 4. Perform asks Actor to verify Action. +// 5. Actor's ActionBook says it is good. +// 6. Perform makes object receive action. +func Perform(dir Directory, action Action) (err error) { + // Defer to set panic upon panic. + defer func() { + if r := recover(); r != nil { + action.setError() + panic(r) + } else if err != nil { + action.setError() + } + }() + + // Validate what can be validated. + err = action.ValidateBasic() + if err != nil { + return + } + // Check that status is new. + if action.Status() != ActionStatusNew { + return errors.New("expected a new action") + } + // Get actor and object. + actor := dir.Get(action.actor) + object := dir.Get(action.object) + // Ask Actor to verify action. + err = actor.CheckAction(action) + if err != nil { + return + } + // Set status as pending. + // (action.status == new) + action.setPending() + // (action.status == pending) + // Let object handle action. + err = object.ReceiveAction(action, actor) + if err != nil { + return + } + // (action.status == pending|received) + // Set status as complete. + action.setComplete() + // (action.status == complete) + + return nil +} + +//---------------------------------------- +// Authorization + +// Authorization struct is only needed for Actions that +// require cryptographic authorization, where the Action's +// actor has a pubkey to verify signatures with. +// +// Presumably once Authorization is validated (signature +// checked) the Action becomes committed, and given a index +// number. +type Authorization struct { + action Action + signatures []Signature +} + +type Signature struct { + account number // or address with some extra data unknown + sequence number // or alternative to sequence + signature []byte +} diff --git a/examples/gno.land/p/jaekwon/book/book.gno b/examples/gno.land/p/jaekwon/book/book.gno new file mode 100644 index 00000000000..d817ac3e1d1 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/book.gno @@ -0,0 +1,174 @@ +package book + +/* + +GOALS: + +0. Define Action, Actor, Object. + +1.a Create a Book (aka List) of text, + that is owned by a Book of Actors. + +1.b Create ActionBook. + +2. Allow a Book of Actors to split/fork. + +3. Later, a Person is interface { Actor ...}. + +*/ + +// TODO use this for Tiers. + +import "gno.land/p/demo/avl" + +//---------------------------------------- +// Usage example + +func main() { + // directory of subjects/objects. + directory := NewDirectory("test") + // action to perform. + action := Action{} + // XXX +} + +//---------------------------------------- +// Book + +// A Book is a basic data structure. +// It is like a linked list, but indexable from 0. +// XXX XXX how to do this w/ avl.Tree? +// TODO book merges? +// TODO flesh out book. +type Book struct { + attrs Attributes + entries avl.Tree +} + +var _ Object = &Book{} + +func (bk *Book) Append(XXX) XXX { + XXX +} + +func (bk *Book) Size() int { + XXX +} + +func (bk *Book) Get(n int) XXX { + XXX +} + +//---------------------------------------- +// PrivActionBook + +// This is a thought experiment to make books work for +// auth. Actions are considered authorized if appended in +// PrivActionBook. +// +// An PrivActionBook is meant to be owned privately by the +// object. This is similar to PrivKey in crypto; it is +// privileged. +// +// Actions need not necessarily be signed cryptographically +// to be authenticated in an PrivActionBook, because the +// test of authorization is merely inclusion. +// +// TODO implement: +// type CryptoActionBook struct { PubKey, PrivActionBook } +// A CryptoActionBook need not be privileged, +// perhaps anyone can append a signed action, +// +// Also, PrivActionBook. +// Also, ReadActionBook. +// All of these are backed by the same underlying "book". +type PrivActionBook struct { + + // All actions must have this object. + object std.FullAddress + + // Maybe PrivActionBook *is* Book? + // not sure yet. + book *Book + + // Number of actions to keep around. + capacity int + + // Validates sequences based on sequenceAccum, + // which is accumulated from sequences seen. + sequenceStrategy SequenceStrategy + + // Typically the last sequence value. + // The type of value depends on SequenceStrategy. + // This field allows the PrivActionBook to prune + // all previous Actions while preserving sequencing. + // XXX string or TextMarshaller() or Stringer() or? + sequenceAccum string +} + +func NewPrivActionBook() *PrivActionBook { + // XXX +} + +// If the action is valid, append to PrivActionBook, +// thereby making it officially authorized. +// The execution of action generally should happen +// atomically with authorization by caller. +// +// if err := pab.Append(action); err != nil { +// execute(action) +// } +func (pab *PrivActionBook) Append(action Action) error { + // XXX copy action. + + // XXX check action.sequence against ab.last + // XXX if good, append and return nil + // XXX otherwise return error + + // XXX match action.object with pab.object. + // XXX set action.object = nil for space. + + // XXX check capacity +} + +func (pab *PrivActionBook) Len() int { + return pab.book.Len() +} + +func (pab *PrivActionBook) Cap() int { + return pab.book.Cap() +} + +// XXX Not sure why this would be useful, +// XXX except to show clients previous actions, +// XXX but either way developers should not rely on it +// XXX for transactional logic. +func (pab *PrivActionBook) Get(idx int) Action { + // XXX fetch action from pab.book.Get() + // XXX copy action + // XXX set copy.object = pab.object + // XXX return copy +} + +// XXX SequenceStragegy.Name()? +// XXX or just make enums? +// XXX Either way need to make globally unique lookup. +func (pab *PrivActionBook) SequenceStrategy() SequenceStrategy { + // XXX +} + +// XXX clients will need this to sign, +// XXX especially after device reset. +func (pab *PrivActionBook) SequenceAccum() string { + // XXX +} + +//---------------------------------------- +// misc + +func hasPrefix(str, prefix string) bool { + if len(str) <= len(prefix) { + return false + } + return str[:len(prefix)] == prefix +} diff --git a/examples/gno.land/p/jaekwon/book/committee.gno b/examples/gno.land/p/jaekwon/book/committee.gno new file mode 100644 index 00000000000..f1d7741fbab --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/committee.gno @@ -0,0 +1,28 @@ +package book + +//---------------------------------------- +// Committee + +// XXX incomplete. +type Committee struct { + + // full addr. + faddr std.FullAddress + + // each account has an associated pab + pab *PrivActionBook +} + +// A Committee is an actor +var _ Actor = *Committee{} + +func NewCommittee(pkgPath, subpath string) *Committee { + return &Committee{ + faddr: xxx, + pab: xxx, + } +} + +func (cm *Committee) GetAddress() std.Address { + XXX +} diff --git a/examples/gno.land/p/jaekwon/book/directory.gno b/examples/gno.land/p/jaekwon/book/directory.gno new file mode 100644 index 00000000000..63eb5c565f3 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/directory.gno @@ -0,0 +1,79 @@ +package book + +import ( + "std" + + "gno.land/r/demo/avl" +) + +//---------------------------------------- +// Diretory + +// The Directory as interface is arguably a bit too powerful. +// Custom implementations can do anything, so be careful +// to use only fully vetted, audited, complete implementations. +type Directory interface { + GetName() string + Get(std.Address) std.Addressable + Has(std.Address) bool + Add(std.Addressable) + Remove(std.Addressable) + RemoveByAddress(std.Addressable) +} + +//---------------------------------------- +// defaultDirectory + +type defaultDirectory struct { + name string // name of directory + objects avl.Tree // std.Address -> Addressable +} + +func NewDefaultDirectory(name string) *defaultDirectory { + return &defaultDirectory{ + name: name, + objects: nil, + } +} + +func (dir *defaultDirectory) GetName() string { + return dir.name +} + +func (dir *defaultDirectory) Get(addr std.Address) std.Addressable { + obj, exists := dir.objects.Get(addr) + if !exists { + panic("object not found: invalid address") + } + return obj +} + +func (dir *defaultDirectory) Has(addr std.Address) bool { + return dir.objects.Has(addr) +} + +func (dir *defaultDirectory) Add(obj std.Addressable) { + addr := obj.GetAddress() + updated := dir.objects.Set(string(addr), obj) + if updated == true { + panic("duplicate address") + } +} + +func (dir *defaultDirectory) Remove(obj std.Addressable) { + addr := obj.GetAddress() + old, removed := dir.objects.Remove(string(addr)) + if !removed { + panic("cannot remove object: not found") + } + if old != obj { + panic("cannot remove conflicting object") + } +} + +func (dir *defaultDirectory) RemoveByAddress(addr std.Address) { + _, removed := dir.objects.Remove(string(addr)) + if !removed { + panic("cannot remove object: not found") + } +} diff --git a/examples/gno.land/p/jaekwon/book/gno.mod b/examples/gno.land/p/jaekwon/book/gno.mod new file mode 100644 index 00000000000..f7687c49462 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/gno.mod @@ -0,0 +1 @@ +module gno.land/p/jaekwon/book diff --git a/examples/gno.land/p/jaekwon/book/misc.gno b/examples/gno.land/p/jaekwon/book/misc.gno new file mode 100644 index 00000000000..e95a08889cc --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/misc.gno @@ -0,0 +1,10 @@ +package book + +//---------------------------------------- +// Attributes + +type Attributes struct { + meta avl.Tree // catchall + author Author + // TODO +} diff --git a/examples/gno.land/p/jaekwon/book/node.gno b/examples/gno.land/p/jaekwon/book/node.gno new file mode 100644 index 00000000000..958e47ce7f7 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/node.gno @@ -0,0 +1,353 @@ +package book + +/* +A Book is composed of nodes that behave much like avl nodes, +except nodes do not have keys. + +Insertion, deletion, and query all have best and worst case log(n) time. This +makes it better than indexable skiplists in the presence of an adversarial user +(where all the state is visible to everyone). + +This structure like avl.Tree is not concurrency safe. For concurrency consider +implementing an immutable version (like the avl in Tendermint). + +Related reading: + * https://people.computing.clemson.edu/~bcdean/skip_bst.pdf + * https://people.computing.clemson.edu/~bcdean/skip_bst.pdf + * https://en.wikipedia.org/wiki/Skip_list + * https://stackoverflow.com/a/28270537 +*/ + +//---------------------------------------- +// Node + +type Node struct { + value interface{} + height int8 + size int + leftNode *Node + rightNode *Node +} + +func NewNode(value interface{}) *Node { + return &Node{ + value: value, + height: 0, + size: 1, + } +} + +func (node *Node) Size() int { + if node == nil { + return 0 + } + return node.size +} + +func (node *Node) IsLeaf() bool { + return node.height == 0 +} + +func (node *Node) Value() interface{} { + return node.value +} + +func (node *Node) _copy() *Node { + if node.height == 0 { + panic("Why are you copying a value node?") + } + return &Node{ + height: node.height, + size: node.size, + leftNode: node.leftNode, + rightNode: node.rightNode, + } +} + +func (node *Node) GetByIndex(index int) (value interface{}) { + if node.height == 0 { + if index == 0 { + return node.value + } else { + panic("GetByIndex with invalid index") + } + } else { + // TODO: could improve this by storing the sizes + leftNode := node.getLeftNode() + if index < leftNode.size { + return leftNode.GetByIndex(index) + } else { + return node.getRightNode().GetByIndex(index - leftNode.size) + } + } +} + +// like splicing in an element at index, shifting the prior element to the right. +// keeps the tree balanced. +func (node *Node) SetByIndex(index int, value interface{}) (newSelf *Node) { + if node == nil { + if index != 0 { + panic("SetByIndex with invalid index") + } + return NewNode(value), false + } + if node.height == 0 { + if index == 0 { + return &Node{ + height: 1, + size: 2, + leftNode: NewNode(value), + rightNode: node, + }, false + } else if index == 1 { + return &Node{ + height: 1, + size: 2, + leftNode: node, + rightNode: NewNode(value), + }, false + } else { + panic("SetByIndex with invalid index") + } + } else { + leftNode := node.getLeftNode() + if index < leftNode.size { + node.leftNode = leftNode.SetByIndex(index, value) + } else { + rightNode := node.getRightNode() + node.rightNode = rightNode.SetByIndex(index-leftNode.size, value) + } + node.calcHeightAndSize() + return node.balance() + } +} + +// newNode: The new node to replace node after remove. +func (node *Node) RemoveByIndex(index string) ( + newNode *Node, value interface{}, +) { + if node == nil { + panic("RemoveByIndex on empty tree") + } + if node.height == 0 { + if index == 0 { + return nil, node.value + } else { + panic("RemoveByIndex with invalid index") + } + } else { + leftNode := node.getLeftNode() + if index < leftNode.size { + var newLeftNode *Node + newLeftNode, value = leftNode.RemoveByIndex(index) + if newLeftNode == nil { // left node held value, was removed + return node.getRightNode(), value + } + node = node._copy() + node.leftNode = newLeftNode + node.calcHeightAndSize() + node = node.balance() + return node, value + } else { + rightNode := node.getRightNode() + var newRightNode *Node + newRightNode, value = rightNode.RemoveByIndex(index - leftNode.size) + if newRightNode == nil { // right node held value, was removed + return leftNode, value + } + node = node._copy() + node.rightNode = newRightNode + node.calcHeightAndSize() + node = node.balance() + return node, value + } + } +} + +func (node *Node) getLeftNode() *Node { + return node.leftNode +} + +func (node *Node) getRightNode() *Node { + return node.rightNode +} + +// NOTE: overwrites node +// TODO: optimize balance & rotate +func (node *Node) rotateRight() *Node { + node = node._copy() + l := node.getLeftNode() + _l := l._copy() + + _lrCached := _l.rightNode + _l.rightNode = node + node.leftNode = _lrCached + + node.calcHeightAndSize() + _l.calcHeightAndSize() + + return _l +} + +// NOTE: overwrites node +// TODO: optimize balance & rotate +func (node *Node) rotateLeft() *Node { + node = node._copy() + r := node.getRightNode() + _r := r._copy() + + _rlCached := _r.leftNode + _r.leftNode = node + node.rightNode = _rlCached + + node.calcHeightAndSize() + _r.calcHeightAndSize() + + return _r +} + +// NOTE: mutates height and size +func (node *Node) calcHeightAndSize() { + node.height = maxInt8(node.getLeftNode().height, node.getRightNode().height) + 1 + node.size = node.getLeftNode().size + node.getRightNode().size +} + +func (node *Node) calcBalance() int { + return int(node.getLeftNode().height) - int(node.getRightNode().height) +} + +// NOTE: assumes that node can be modified +// TODO: optimize balance & rotate +func (node *Node) balance() (newSelf *Node) { + balance := node.calcBalance() + if balance > 1 { + if node.getLeftNode().calcBalance() >= 0 { + // Left Left Case + return node.rotateRight() + } else { + // Left Right Case + // node = node._copy() + left := node.getLeftNode() + node.leftNode = left.rotateLeft() + // node.calcHeightAndSize() + return node.rotateRight() + } + } + if balance < -1 { + if node.getRightNode().calcBalance() <= 0 { + // Right Right Case + return node.rotateLeft() + } else { + // Right Left Case + // node = node._copy() + right := node.getRightNode() + node.rightNode = right.rotateRight() + // node.calcHeightAndSize() + return node.rotateLeft() + } + } + // Nothing changed + return node +} + +// TraverseByOffset traverses all nodes, including inner nodes. +// A limit of math.MaxInt means no limit. +func (node *Node) TraverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool { + if node == nil { + return false + } + + // fast paths. these happen only if TraverseByOffset is called directly on a leaf. + if limit <= 0 || offset >= node.size { + return false + } + if node.IsLeaf() { + if offset > 0 { + return false + } + return cb(node) + } + + // go to the actual recursive function. + return node.traverseByOffset(offset, limit, descending, leavesOnly, cb) +} + +func (node *Node) traverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool { + // caller guarantees: offset < node.size; limit > 0. + + if !leavesOnly { + if cb(node) { + return true + } + } + first, second := node.getLeftNode(), node.getRightNode() + if descending { + first, second = second, first + } + if first.IsLeaf() { + // either run or skip, based on offset + if offset > 0 { + offset-- + } else { + cb(first) + limit-- + if limit <= 0 { + return false + } + } + } else { + // possible cases: + // 1 the offset given skips the first node entirely + // 2 the offset skips none or part of the first node, but the limit requires some of the second node. + // 3 the offset skips none or part of the first node, and the limit stops our search on the first node. + if offset >= first.size { + offset -= first.size // 1 + } else { + if first.traverseByOffset(offset, limit, descending, leavesOnly, cb) { + return true + } + // number of leaves which could actually be called from inside + delta := first.size - offset + offset = 0 + if delta >= limit { + return true // 3 + } + limit -= delta // 2 + } + } + + // because of the caller guarantees and the way we handle the first node, + // at this point we know that limit > 0 and there must be some values in + // this second node that we include. + + // => if the second node is a leaf, it has to be included. + if second.IsLeaf() { + return cb(second) + } + // => if it is not a leaf, it will still be enough to recursively call this + // function with the updated offset and limit + return second.traverseByOffset(offset, limit, descending, leavesOnly, cb) +} + +// Only used in testing... +func (node *Node) lmd() *Node { + if node.height == 0 { + return node + } + return node.getLeftNode().lmd() +} + +// Only used in testing... +func (node *Node) rmd() *Node { + if node.height == 0 { + return node + } + return node.getRightNode().rmd() +} + +func maxInt8(a, b int8) int8 { + if a > b { + return a + } + return b +} diff --git a/examples/gno.land/p/jaekwon/book/node_test.gno b/examples/gno.land/p/jaekwon/book/node_test.gno new file mode 100644 index 00000000000..a42cb3a97a8 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/node_test.gno @@ -0,0 +1,100 @@ +package avl + +import ( + "sort" + "strings" + "testing" +) + +func TestTraverseByOffset(t *testing.T) { + const testStrings = `Alfa +Alfred +Alpha +Alphabet +Beta +Beth +Book +Browser` + tt := []struct { + name string + desc bool + }{ + {"ascending", false}, + {"descending", true}, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + sl := strings.Split(testStrings, "\n") + + // sort a first time in the order opposite to how we'll be traversing + // the tree, to ensure that we are not just iterating through with + // insertion order. + sort.Sort(sort.StringSlice(sl)) + if !tc.desc { + reverseSlice(sl) + } + + r := NewNode(sl[0], nil) + for _, v := range sl[1:] { + r, _ = r.Set(v, nil) + } + + // then sort sl in the order we'll be traversing it, so that we can + // compare the result with sl. + reverseSlice(sl) + + var result []string + for i := 0; i < len(sl); i++ { + r.TraverseByOffset(i, 1, tc.desc, true, func(n *Node) bool { + result = append(result, n.Key()) + return false + }) + } + + if !slicesEqual(sl, result) { + t.Errorf("want %v got %v", sl, result) + } + + for l := 2; l <= len(sl); l++ { + // "slices" + for i := 0; i <= len(sl); i++ { + max := i + l + if max > len(sl) { + max = len(sl) + } + exp := sl[i:max] + actual := []string{} + + r.TraverseByOffset(i, l, tc.desc, true, func(tr *Node) bool { + actual = append(actual, tr.Key()) + return false + }) + // t.Log(exp, actual) + if !slicesEqual(exp, actual) { + t.Errorf("want %v got %v", exp, actual) + } + } + } + }) + } +} + +func slicesEqual(w1, w2 []string) bool { + if len(w1) != len(w2) { + return false + } + for i := 0; i < len(w1); i++ { + if w1[0] != w2[0] { + return false + } + } + return true +} + +func reverseSlice(ss []string) { + for i := 0; i < len(ss)/2; i++ { + j := len(ss) - 1 - i + ss[i], ss[j] = ss[j], ss[i] + } +} diff --git a/examples/gno.land/p/jaekwon/book/tree.gno b/examples/gno.land/p/jaekwon/book/tree.gno new file mode 100644 index 00000000000..7b33d28fbe3 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/tree.gno @@ -0,0 +1,82 @@ +package avl + +type IterCbFn func(key string, value interface{}) bool + +//---------------------------------------- +// Tree + +// The zero struct can be used as an empty tree. +type Tree struct { + node *Node +} + +func NewTree() *Tree { + return &Tree{ + node: nil, + } +} + +func (tree *Tree) Size() int { + return tree.node.Size() +} + +func (tree *Tree) Has(key string) (has bool) { + return tree.node.Has(key) +} + +func (tree *Tree) Get(key string) (value interface{}, exists bool) { + _, value, exists = tree.node.Get(key) + return +} + +func (tree *Tree) GetByIndex(index int) (key string, value interface{}) { + return tree.node.GetByIndex(index) +} + +func (tree *Tree) Set(key string, value interface{}) (updated bool) { + newnode, updated := tree.node.Set(key, value) + tree.node = newnode + return updated +} + +func (tree *Tree) Remove(key string) (value interface{}, removed bool) { + newnode, _, value, removed := tree.node.Remove(key) + tree.node = newnode + return value, removed +} + +// Shortcut for TraverseInRange. +func (tree *Tree) Iterate(start, end string, cb IterCbFn) bool { + return tree.node.TraverseInRange(start, end, true, true, + func(node *Node) bool { + return cb(node.Key(), node.Value()) + }, + ) +} + +// Shortcut for TraverseInRange. +func (tree *Tree) ReverseIterate(start, end string, cb IterCbFn) bool { + return tree.node.TraverseInRange(start, end, false, true, + func(node *Node) bool { + return cb(node.Key(), node.Value()) + }, + ) +} + +// Shortcut for TraverseByOffset. +func (tree *Tree) IterateByOffset(offset int, count int, cb IterCbFn) bool { + return tree.node.TraverseByOffset(offset, count, true, true, + func(node *Node) bool { + return cb(node.Key(), node.Value()) + }, + ) +} + +// Shortcut for TraverseByOffset. +func (tree *Tree) ReverseIterateByOffset(offset int, count int, cb IterCbFn) bool { + return tree.node.TraverseByOffset(offset, count, false, true, + func(node *Node) bool { + return cb(node.Key(), node.Value()) + }, + ) +} diff --git a/examples/gno.land/p/jaekwon/book/z_0_filetest.gno b/examples/gno.land/p/jaekwon/book/z_0_filetest.gno new file mode 100644 index 00000000000..814e19d6d49 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/z_0_filetest.gno @@ -0,0 +1,348 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "gno.land/p/demo/avl" +) + +var node *avl.Node + +func init() { + node = avl.NewNode("key0", "value0") + // node, _ = node.Set("key0", "value0") +} + +func main() { + var updated bool + node, updated = node.Set("key1", "value1") + // println(node, updated) + println(updated, node.Size()) +} + +// Output: +// false 2 + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key0" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "value0" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "ModTime": "5", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key1" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "value1" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key1" +// } +// }, +// {}, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "091729e38bda8724bce4c314f9624b91af679459", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "0b5493aa4ea42087780bdfcaebab2c3eec351c15", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "4", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Values": [ +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "init.0", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "15", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "6c9948281d4c60b2d95233b76388d54d8b1a2fad", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } +// } +// } +// } +// ] +// } diff --git a/examples/gno.land/p/jaekwon/book/z_1_filetest.gno b/examples/gno.land/p/jaekwon/book/z_1_filetest.gno new file mode 100644 index 00000000000..410e9e93601 --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/z_1_filetest.gno @@ -0,0 +1,373 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "gno.land/p/demo/avl" +) + +var node *avl.Node + +func init() { + node = avl.NewNode("key0", "value0") + node, _ = node.Set("key1", "value1") +} + +func main() { + var updated bool + node, updated = node.Set("key2", "value2") + // println(node, updated) + println(updated, node.Size()) +} + +// Output: +// false 3 + +// Realm: +// switchrealm["gno.land/r/test"] +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "value2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// {}, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "7a8a63e17a567d7b0891ac89d5cd90072a73787d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "ab5a297f4eb033d88bdf1677f4dc151ccb9fde9f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key1" +// } +// }, +// {}, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AwAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "627e8e517e7ae5db0f3b753e2a32b607989198b6", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "fe8afd501233fb95375016199f0443b3c6ab1fbc", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "6", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Values": [ +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "init.0", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "15", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "c5eefc40ed065461b4a920c1349ed734ffdead8f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } +// } +// ] +// } +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] diff --git a/examples/gno.land/p/jaekwon/book/z_2_filetest.gno b/examples/gno.land/p/jaekwon/book/z_2_filetest.gno new file mode 100644 index 00000000000..65181bffcac --- /dev/null +++ b/examples/gno.land/p/jaekwon/book/z_2_filetest.gno @@ -0,0 +1,292 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "gno.land/p/demo/avl" +) + +var tree avl.Tree + +func init() { + tree.Set("key0", "value0") + tree.Set("key1", "value1") +} + +func main() { + var updated bool + updated = tree.Set("key2", "value2") + println(updated, tree.Size()) +} + +// Output: +// false 3 + +// Realm: +// switchrealm["gno.land/r/test"] +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "value2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// {}, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "213baed7e3326f2403b5f30e5d4397510ba4f37d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "be751422ef4c2bc068a456f9467d2daca27db8ca", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key1" +// } +// }, +// {}, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AwAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "af4d0b158681d85eb2a7f6888b39a05ca7b790ee", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "ef853d70e334fd2c807d6c2c751da1fcd1e5ad58", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "3a5af0895c2c45b8a5e894644bcd689f1fdc4785", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "ModTime": "7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] diff --git a/gnovm/stdlibs/std/crypto.gno b/gnovm/stdlibs/std/crypto.gno index 3ebd802dc3f..01d08c4ed37 100644 --- a/gnovm/stdlibs/std/crypto.gno +++ b/gnovm/stdlibs/std/crypto.gno @@ -9,3 +9,35 @@ func (a Address) String() string { const RawAddressSize = 20 type RawAddress [RawAddressSize]byte + +type Addressable interface { + // XXX document requirements. + GetAddress() std.Address +} + +// TODO implement as a native function, +// see DerivePkgAddr. +func AddrFromPkgPath(pkgPath string) Address { + panic("not yet implemented") +} + +// Addresses can also be derived from a subpath. +// This way anything with an address also has a URI path. +// TODO implement as a native function. +func AddrFromAddressable(pkgPath, subpath string) Address { + // hash(pkgPath + "/" + subpath) + panic("not yet implemented") +} + +// A standard structure for storing Subpath and Address. +type SubpathAddress struct { + Subpath string + Address std.Address +} + +// A standard structure for storing all addr related fields. +type FullAddress struct { + PkgPath string + Subpath string + Address std.Address +} diff --git a/gnovm/stdlibs/stdlibs.go b/gnovm/stdlibs/stdlibs.go index fb230a0cf86..d334c935805 100644 --- a/gnovm/stdlibs/stdlibs.go +++ b/gnovm/stdlibs/stdlibs.go @@ -502,6 +502,8 @@ func InjectPackage(store gno.Store, pn *gno.PackageNode) { } }, ) + // XXX TO DEPRECATE. + // TODO migrate as native function std.AddrFromPkgPath(). pn.DefineNative("DerivePkgAddr", gno.Flds( // params "pkgPath", "string",