forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new govdao pattern with context (gnolang#2380)
This PR introduces a new pattern for arbitrary govdao proposals using Gno code using contexts. It involves wrapping the provided closure with a system that configures a `context.Context` with a field indicating that the execution occurs in the context of an `approvedByGovDao` proposal. This opens the door to a new ACL system, not based on `PrevRealm()`, but on a context-based "certification" system. I believe this pattern makes sense to exist; however, here are some drawbacks I can see: 1. `context.Context` is not yet needed (no goroutine support), and we may eventually never need this package depending on how we implement goroutines internally. (h/t @thehowl) 2. `context.Context` is used like an environment variable, which introduces implicitness—something we usually try to avoid to make Gno a very explicit language. Explicitness brings “simplicity” and “verifiability.” 3. The usual Go idiomatic way of using `context` is to pass it as the first argument. If this becomes more common, it will result in more contracts having exposed functions that are not callable by `maketx call` (though they can be called with `maketx run` or via an import). Depends on gnolang#2379 Related to gnolang#2386 --------- Signed-off-by: moul <94029+moul@users.noreply.github.com> Co-authored-by: Antonio Navarro Perez <antnavper@gmail.com>
- Loading branch information
Showing
9 changed files
with
324 additions
and
13 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,72 @@ | ||
// Package context provides a minimal implementation of Go context with support | ||
// for Value and WithValue. | ||
// | ||
// Adapted from https://github.com/golang/go/tree/master/src/context/. | ||
// Copyright 2016 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
package context | ||
|
||
type Context interface { | ||
// Value returns the value associated with this context for key, or nil | ||
// if no value is associated with key. | ||
Value(key interface{}) interface{} | ||
} | ||
|
||
// Empty returns a non-nil, empty context, similar with context.Background and | ||
// context.TODO in Go. | ||
func Empty() Context { | ||
return &emptyCtx{} | ||
} | ||
|
||
type emptyCtx struct{} | ||
|
||
func (ctx emptyCtx) Value(key interface{}) interface{} { | ||
return nil | ||
} | ||
|
||
func (ctx emptyCtx) String() string { | ||
return "context.Empty" | ||
} | ||
|
||
type valueCtx struct { | ||
parent Context | ||
key, val interface{} | ||
} | ||
|
||
func (ctx *valueCtx) Value(key interface{}) interface{} { | ||
if ctx.key == key { | ||
return ctx.val | ||
} | ||
return ctx.parent.Value(key) | ||
} | ||
|
||
func stringify(v interface{}) string { | ||
switch s := v.(type) { | ||
case stringer: | ||
return s.String() | ||
case string: | ||
return s | ||
} | ||
return "non-stringer" | ||
} | ||
|
||
type stringer interface { | ||
String() string | ||
} | ||
|
||
func (c *valueCtx) String() string { | ||
return stringify(c.parent) + ".WithValue(" + | ||
stringify(c.key) + ", " + | ||
stringify(c.val) + ")" | ||
} | ||
|
||
// WithValue returns a copy of parent in which the value associated with key is | ||
// val. | ||
func WithValue(parent Context, key, val interface{}) Context { | ||
if key == nil { | ||
panic("nil key") | ||
} | ||
// XXX: if !reflect.TypeOf(key).Comparable() { panic("key is not comparable") } | ||
return &valueCtx{parent, key, val} | ||
} |
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,96 @@ | ||
package context | ||
|
||
import "testing" | ||
|
||
func TestContextExample(t *testing.T) { | ||
type favContextKey string | ||
|
||
k := favContextKey("language") | ||
ctx := WithValue(Empty(), k, "Gno") | ||
|
||
if v := ctx.Value(k); v != nil { | ||
if string(v) != "Gno" { | ||
t.Errorf("language value should be Gno, but is %s", v) | ||
} | ||
} else { | ||
t.Errorf("language key value was not found") | ||
} | ||
|
||
if v := ctx.Value(favContextKey("color")); v != nil { | ||
t.Errorf("color key was found") | ||
} | ||
} | ||
|
||
// otherContext is a Context that's not one of the types defined in context.go. | ||
// This lets us test code paths that differ based on the underlying type of the | ||
// Context. | ||
type otherContext struct { | ||
Context | ||
} | ||
|
||
type ( | ||
key1 int | ||
key2 int | ||
) | ||
|
||
// func (k key2) String() string { return fmt.Sprintf("%[1]T(%[1]d)", k) } | ||
|
||
var ( | ||
k1 = key1(1) | ||
k2 = key2(1) // same int as k1, different type | ||
k3 = key2(3) // same type as k2, different int | ||
) | ||
|
||
func TestValues(t *testing.T) { | ||
check := func(c Context, nm, v1, v2, v3 string) { | ||
if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { | ||
t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) | ||
} | ||
if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { | ||
t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) | ||
} | ||
if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { | ||
t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) | ||
} | ||
} | ||
|
||
c0 := Empty() | ||
check(c0, "c0", "", "", "") | ||
|
||
t.Skip() // XXX: depends on https://github.com/gnolang/gno/issues/2386 | ||
|
||
c1 := WithValue(Empty(), k1, "c1k1") | ||
check(c1, "c1", "c1k1", "", "") | ||
|
||
/*if got, want := c1.String(), `context.Empty.WithValue(context_test.key1, c1k1)`; got != want { | ||
t.Errorf("c.String() = %q want %q", got, want) | ||
}*/ | ||
|
||
c2 := WithValue(c1, k2, "c2k2") | ||
check(c2, "c2", "c1k1", "c2k2", "") | ||
|
||
/*if got, want := fmt.Sprint(c2), `context.Empty.WithValue(context_test.key1, c1k1).WithValue(context_test.key2(1), c2k2)`; got != want { | ||
t.Errorf("c.String() = %q want %q", got, want) | ||
}*/ | ||
|
||
c3 := WithValue(c2, k3, "c3k3") | ||
check(c3, "c2", "c1k1", "c2k2", "c3k3") | ||
|
||
c4 := WithValue(c3, k1, nil) | ||
check(c4, "c4", "", "c2k2", "c3k3") | ||
|
||
o0 := otherContext{Empty()} | ||
check(o0, "o0", "", "", "") | ||
|
||
o1 := otherContext{WithValue(Empty(), k1, "c1k1")} | ||
check(o1, "o1", "c1k1", "", "") | ||
|
||
o2 := WithValue(o1, k2, "o2k2") | ||
check(o2, "o2", "c1k1", "o2k2", "") | ||
|
||
o3 := otherContext{c4} | ||
check(o3, "o3", "", "c2k2", "c3k3") | ||
|
||
o4 := WithValue(o3, k3, nil) | ||
check(o4, "o4", "", "c2k2", "") | ||
} |
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/demo/context |
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 |
---|---|---|
@@ -1,3 +1,6 @@ | ||
module gno.land/p/gov/proposal | ||
|
||
require gno.land/p/demo/uassert v0.0.0-latest | ||
require ( | ||
gno.land/p/demo/context v0.0.0-latest | ||
gno.land/p/demo/uassert v0.0.0-latest | ||
) |
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
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
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
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
Oops, something went wrong.