Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add r/sys/teams #2184

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions examples/gno.land/r/gnoland/home/home.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@ import (
"gno.land/p/demo/ufmt"
"gno.land/p/demo/ui"
blog "gno.land/r/gnoland/blog"
"gno.land/r/sys/teams"
)

func init() {
teams.SetMembers(
"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq", teams.AdminRole, // @manfred
// TODO: add more people in the init
)
}

// TODO: add helpers to manage the team set from a DAO

// XXX: p/demo/ui API is crappy, we need to make it more idiomatic
// XXX: use an updatable block system to update content from a DAO
// XXX: var blocks avl.Tree
Expand Down
89 changes: 89 additions & 0 deletions examples/gno.land/r/sys/teams/teams.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package teams

import (
"errors"
"std"
"strings"

"gno.land/p/demo/avl"
)

type Team struct {
Admins []std.Address
Editors []std.Address
}

var (
ErrNotHomeCaller = errors.New("must be called by r/<team>/home userland logic")
ErrInvalidMembers = errors.New("invalid members argument")
)

type Role uint

const (
RoleUnset = ""
RoleAdmin = "admin" // editor + right to upgrade r/<team>/home
RoleEditor = "editor" // can add package under {p,r}/<team>/...
// XXX: RoleReadonly = "readonly"?
)

var teams avl.Tree // team -> []users

// TODO: doc
func SetMembers(attrs ...string) {
// XXX: panic if members list is too big for now.

attrsLen := len(attrs)
if attrsLen%2 != 0 {
panic(ErrInvalidMembers)
}

prev := std.PrevRealm()
if prev.IsUser() {
panic(ErrNotHomeCaller)
}

prevPath := prev.PkgPath()
parts := strings.Split(prevPath, "/")
if len(parts) != 4 || !strings.HasPrefix(prevPath, "gno.land/r/") || !strings.HasSuffix(prevPath, "/home") {
panic(ErrNotHomeCaller)
}
teamName := parts[2]

team := Team{
Admins: []std.Address{},
Editors: []std.Address{},
}
for i := 0; i < attrsLen-1; i += 2 {
addr := std.Address(attrs[i])
role := attrs[i+1]
if role == RoleAdmin {
team.Admins = append(team.Admins, addr)
}
if role == RoleEditor {
team.Editors = append(team.Editors, addr)
}
}

teams.Set(teamName, &team)
}

func GetAdmins(teamName string) []std.Address {
t, ok := teams.Get(teamName)
if !ok {
return nil
}
team := t.(*Team)
return team.Admins
}

func GetEditors(teamName string) []std.Address {
t, ok := teams.Get(teamName)
if !ok {
return nil
}
team := t.(*Team)
return append(team.Admins, team.Editors...)
}

// XXX: func GetUserTeams(addr std.Address)
48 changes: 48 additions & 0 deletions examples/gno.land/r/sys/teams/teams_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package teams

import (
"std"
"testing"

"gno.land/p/demo/testutils"
)

func TestPackage(t *testing.T) {
if GetAdmins("gnoland") != nil {
t.Errorf("gnoland should have no admins")
}
if GetEditors("gnoland") != nil {
t.Errorf("gnoland should have no editors")
}

std.TestSetRealm(std.NewCodeRealm("gno.land/r/gnoland/home"))
test1 := testutils.TestAddress("test1")
test2 := testutils.TestAddress("test2")
SetMembers(
string(test1), string(RoleAdmin),
string(test2), string(RoleEditor),
)

admins := GetAdmins("gnoland")
if !isEqualAddressSlice(admins, []std.Address{test1}) {
t.Errorf("gnoland should have test1 as admins")
}
editors := GetEditors("gnoland")
if !isEqualAddressSlice(editors, []std.Address{test1, test2}) {
t.Errorf("gnoland should have test1 and test2 as editors")
}
}

func isEqualAddressSlice(a, b []std.Address) bool {
if len(a) != len(b) {
return false
}

for i, v := range a {
if v != b[i] {
return false
}
}

return true
}
6 changes: 0 additions & 6 deletions examples/gno.land/r/x/test_prev/gno.mod

This file was deleted.

18 changes: 0 additions & 18 deletions examples/gno.land/r/x/test_prev/test_prev.gno

This file was deleted.

32 changes: 0 additions & 32 deletions examples/gno.land/r/x/test_prev/test_prev_test.gno

This file was deleted.

16 changes: 8 additions & 8 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2058,19 +2058,19 @@
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Values[i]))
}

builder.WriteString(`Exprs: `)
builder.WriteString(" Exprs:\n")

Check warning on line 2061 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2061

Added line #L2061 was not covered by tests

for i := len(m.Exprs) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Exprs[i]))
}

builder.WriteString(`Stmts: `)
builder.WriteString(" Stmts:\n")

Check warning on line 2067 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2067

Added line #L2067 was not covered by tests

for i := len(m.Stmts) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Stmts[i]))
}

builder.WriteString(`Blocks: `)
builder.WriteString(" Blocks:\n")

Check warning on line 2073 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2073

Added line #L2073 was not covered by tests

for b := m.LastBlock(); b != nil; {
gen := builder.Len()/3 + 1
Expand Down Expand Up @@ -2107,7 +2107,7 @@
}
}

builder.WriteString(`Blocks (other): `)
builder.WriteString(" Blocks (other):\n")

Check warning on line 2110 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2110

Added line #L2110 was not covered by tests

for i := len(m.Blocks) - 2; i >= 0; i-- {
b := m.Blocks[i]
Expand All @@ -2119,17 +2119,17 @@
if _, ok := b.Source.(*PackageNode); ok {
break // done, skip *PackageNode.
} else {
builder.WriteString(fmt.Sprintf(" #%d %s", i,
builder.WriteString(fmt.Sprintf(" #%d %s\n", i,

Check warning on line 2122 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2122

Added line #L2122 was not covered by tests
b.StringIndented(" ")))
if b.Source != nil {
sb := b.GetSource(m.Store).GetStaticBlock().GetBlock()
builder.WriteString(fmt.Sprintf(" (static) #%d %s", i,
builder.WriteString(fmt.Sprintf(" (static) #%d %s\n", i,

Check warning on line 2126 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2126

Added line #L2126 was not covered by tests
sb.StringIndented(" ")))
}
}
}

builder.WriteString(`Frames: `)
builder.WriteString(" Frames:\n")

Check warning on line 2132 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2132

Added line #L2132 was not covered by tests

for i := len(m.Frames) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %s\n", i, m.Frames[i]))
Expand All @@ -2139,7 +2139,7 @@
builder.WriteString(fmt.Sprintf(" Realm:\n %s\n", m.Realm.Path))
}

builder.WriteString(`Exceptions: `)
builder.WriteString(" Exceptions:\n")

Check warning on line 2142 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2142

Added line #L2142 was not covered by tests

for _, ex := range m.Exceptions {
builder.WriteString(fmt.Sprintf(" %s\n", ex.Sprint(m)))
Expand Down
10 changes: 5 additions & 5 deletions gnovm/stdlibs/std/banker.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@
var reDenom = regexp.MustCompile("[a-z][a-z0-9]{2,15}")

func X_bankerGetCoins(m *gno.Machine, bt uint8, addr string) (denoms []string, amounts []int64) {
coins := m.Context.(ExecContext).Banker.GetCoins(crypto.Bech32Address(addr))
coins := GetContext(m).Banker.GetCoins(crypto.Bech32Address(addr))

Check warning on line 40 in gnovm/stdlibs/std/banker.go

View check run for this annotation

Codecov / codecov/patch

gnovm/stdlibs/std/banker.go#L40

Added line #L40 was not covered by tests
return ExpandCoins(coins)
}

func X_bankerSendCoins(m *gno.Machine, bt uint8, fromS, toS string, denoms []string, amounts []int64) {
// bt != BankerTypeReadonly (checked in gno)

ctx := m.Context.(ExecContext)
ctx := GetContext(m)

Check warning on line 47 in gnovm/stdlibs/std/banker.go

View check run for this annotation

Codecov / codecov/patch

gnovm/stdlibs/std/banker.go#L47

Added line #L47 was not covered by tests
amt := CompactCoins(denoms, amounts)
from, to := crypto.Bech32Address(fromS), crypto.Bech32Address(toS)

Expand Down Expand Up @@ -87,7 +87,7 @@
}

func X_bankerTotalCoin(m *gno.Machine, bt uint8, denom string) int64 {
return m.Context.(ExecContext).Banker.TotalCoin(denom)
return GetContext(m).Banker.TotalCoin(denom)

Check warning on line 90 in gnovm/stdlibs/std/banker.go

View check run for this annotation

Codecov / codecov/patch

gnovm/stdlibs/std/banker.go#L90

Added line #L90 was not covered by tests
}

func X_bankerIssueCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
Expand All @@ -104,7 +104,7 @@
// ibc_denom := 'ibc/' + hash('path' + 'base_denom')
// gno_realm_denom := '/' + 'pkg_path' + ':' + 'base_denom'
newDenom := "/" + m.Realm.Path + ":" + denom
m.Context.(ExecContext).Banker.IssueCoin(crypto.Bech32Address(addr), newDenom, amount)
GetContext(m).Banker.IssueCoin(crypto.Bech32Address(addr), newDenom, amount)

Check warning on line 107 in gnovm/stdlibs/std/banker.go

View check run for this annotation

Codecov / codecov/patch

gnovm/stdlibs/std/banker.go#L107

Added line #L107 was not covered by tests
}

func X_bankerRemoveCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
Expand All @@ -117,5 +117,5 @@
}

newDenom := "/" + m.Realm.Path + ":" + denom
m.Context.(ExecContext).Banker.RemoveCoin(crypto.Bech32Address(addr), newDenom, amount)
GetContext(m).Banker.RemoveCoin(crypto.Bech32Address(addr), newDenom, amount)

Check warning on line 120 in gnovm/stdlibs/std/banker.go

View check run for this annotation

Codecov / codecov/patch

gnovm/stdlibs/std/banker.go#L120

Added line #L120 was not covered by tests
}
26 changes: 26 additions & 0 deletions gnovm/stdlibs/std/context.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package std

import (
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/sdk"
"github.com/gnolang/gno/tm2/pkg/std"
Expand All @@ -19,3 +20,28 @@ type ExecContext struct {
Banker BankerInterface
EventLogger *sdk.EventLogger
}

// GetContext returns the execution context.
// This is used to allow extending the exec context using interfaces,
// for instance when testing.
func (e ExecContext) GetExecContext() ExecContext {
return e
}

var _ ExecContexter = ExecContext{}

// ExecContexter is a type capable of returning the parent [ExecContext]. When
// using these standard libraries, m.Context should always implement this
// interface. This can be obtained by embedding [ExecContext].
type ExecContexter interface {
GetExecContext() ExecContext
}

// NOTE: In order to make this work by simply embedding ExecContext in another
// context (like TestExecContext), the method needs to be something other than
// the field name.

// GetContext returns the context from the Gno machine.
func GetContext(m *gno.Machine) ExecContext {
return m.Context.(ExecContexter).GetExecContext()
}
2 changes: 1 addition & 1 deletion gnovm/stdlibs/std/emit_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func X_emit(m *gno.Machine, typ string, attrs []string) {
Func: fnIdent,
Attributes: eventAttrs,
}
ctx := m.Context.(ExecContext)
ctx := GetContext(m)
ctx.EventLogger.EmitEvent(evt)
}

Expand Down
Loading
Loading