Skip to content

Commit a475671

Browse files
MikaelVallenetn0izn0izNorman Meier
authored
feat: add permissions & voting power per roles per features (#1443)
Signed-off-by: Norman Meier <norman@samourai.coop> Co-authored-by: n0izn0iz <n0izn0iz@users.noreply.github.com> Co-authored-by: Norman Meier <norman@samourai.coop>
1 parent f53046e commit a475671

File tree

34 files changed

+1356
-238
lines changed

34 files changed

+1356
-238
lines changed

gno/p/dao_core/dao_core.gno

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,17 @@ func NewDAOCore(
4848
proposalModules: make([]dao_interfaces.ActivableProposalModule, len(proposalModulesFactories)),
4949
}
5050

51-
core.votingModule = votingModuleFactory(core)
52-
if core.votingModule == nil {
53-
panic("voting module factory returned nil")
54-
}
55-
51+
// important to keep this order since voting module might depend on roles module
5652
core.rolesModule = rolesModuleFactory(core)
5753
if core.rolesModule == nil {
5854
panic("roles module factory returned nil")
5955
}
6056

57+
core.votingModule = votingModuleFactory(core)
58+
if core.votingModule == nil {
59+
panic("voting module factory returned nil")
60+
}
61+
6162
for i, modFactory := range proposalModulesFactories {
6263
mod := modFactory(core)
6364
if mod == nil {
@@ -157,7 +158,7 @@ func (d *daoCore) GetMembersJSON(start, end string, limit uint64, height int64)
157158
}
158159

159160
func (d *daoCore) VotingPowerAtHeight(address std.Address, height int64) uint64 {
160-
return d.VotingModule().VotingPowerAtHeight(address, height)
161+
return d.VotingModule().VotingPowerAtHeight(address, height, []string{})
161162
}
162163

163164
func (d *daoCore) ActiveProposalModuleCount() int {

gno/p/dao_core/dao_core_test.gno

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (vm *votingModule) Render(path string) string {
3838
return "# Test Voting Module"
3939
}
4040

41-
func (vm *votingModule) VotingPowerAtHeight(address std.Address, height int64) uint64 {
41+
func (vm *votingModule) VotingPowerAtHeight(address std.Address, height int64, resources []string) uint64 {
4242
return 0
4343
}
4444

@@ -73,7 +73,7 @@ func (rm *rolesModule) HasRole(address std.Address, role string) bool {
7373
return false
7474
}
7575

76-
func (rm *rolesModule) NewRole(roleName string) {
76+
func (rm *rolesModule) NewRoleJSON(roleName, resourcesJSON string) {
7777
panic("not implemented")
7878
}
7979

@@ -93,6 +93,10 @@ func (rm *rolesModule) GetMemberRoles(address std.Address) []string {
9393
return []string{}
9494
}
9595

96+
func (rm *rolesModule) GetMemberResourceVPower(address std.Address, resource string) uint64 {
97+
return 0
98+
}
99+
96100
type proposalModule struct {
97101
core dao_interfaces.IDAOCore
98102
}

gno/p/dao_interfaces/modules.gno

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type IVotingModule interface {
1818
ConfigJSON() string
1919
GetMembersJSON(start, end string, limit uint64, height int64) string
2020
Render(path string) string
21-
VotingPowerAtHeight(address std.Address, height int64) (power uint64)
21+
VotingPowerAtHeight(address std.Address, height int64, resources []string) (power uint64)
2222
TotalPowerAtHeight(height int64) uint64
2323
}
2424

@@ -43,8 +43,9 @@ type IRolesModule interface {
4343
ConfigJSON() string
4444
Render(path string) string
4545
GetMemberRoles(address std.Address) []string
46+
GetMemberResourceVPower(address std.Address, resource string) uint64
4647
HasRole(address std.Address, role string) bool
47-
NewRole(roleName string)
48+
NewRoleJSON(roleName, resourcesJSON string)
4849
DeleteRole(roleName string)
4950
GrantRole(address std.Address, role string)
5051
RevokeRole(address std.Address, role string)

gno/p/dao_proposal_single/dao_proposal_single.gno

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,12 @@ func (d *DAOProposalSingle) VoteJSON(proposalID int, voteJSON string) {
331331
panic("proposal is expired")
332332
}
333333

334-
votePower := d.core.VotingModule().VotingPowerAtHeight(voter, proposal.StartHeight)
334+
resources := make([]string, len(proposal.Messages))
335+
for i, m := range proposal.Messages {
336+
resources[i] = m.Type()
337+
}
338+
339+
votePower := d.core.VotingModule().VotingPowerAtHeight(voter, proposal.StartHeight, resources)
335340
if votePower == 0 {
336341
panic("not registered")
337342
}

gno/p/dao_roles_group/roles_group.gno

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dao_roles_group
33
import (
44
"std"
55

6+
"gno.land/p/demo/avl"
67
"gno.land/p/demo/json"
78
dao_interfaces "gno.land/p/teritori/dao_interfaces"
89
"gno.land/p/teritori/jsonutil"
@@ -12,12 +13,19 @@ import (
1213
type RolesGroup struct {
1314
dao_interfaces.IRolesModule
1415

15-
rm *role_manager.RoleManager
16+
rm *role_manager.RoleManager
17+
resourcesVPower *avl.Tree // roles -> ResourceVPower[]
18+
}
19+
20+
type ResourceVPower struct {
21+
Resource string
22+
Power uint64
1623
}
1724

1825
func NewRolesGroup() *RolesGroup {
1926
return &RolesGroup{
20-
rm: role_manager.NewWithAddress(std.PrevRealm().Addr()),
27+
rm: role_manager.NewWithAddress(std.PrevRealm().Addr()),
28+
resourcesVPower: avl.NewTree(),
2129
}
2230
}
2331

@@ -42,8 +50,25 @@ func (r *RolesGroup) HasRole(address std.Address, role string) bool {
4250
return r.rm.HasRole(address, role)
4351
}
4452

45-
func (r *RolesGroup) NewRole(roleName string) {
53+
func (r *RolesGroup) NewRoleJSON(roleName, resourcesJSON string) {
54+
node := json.Must(json.Unmarshal([]byte(resourcesJSON)))
55+
arr := node.MustArray()
56+
resources := make([]ResourceVPower, len(arr))
57+
for i, n := range arr {
58+
node := n.MustObject()
59+
resources[i] = ResourceVPower{
60+
Resource: node["resource"].MustString(),
61+
Power: jsonutil.MustUint64(node["power"]),
62+
}
63+
}
64+
r.NewRole(roleName, resources)
65+
}
66+
67+
func (r *RolesGroup) NewRole(roleName string, resources []ResourceVPower) {
4668
r.rm.CreateNewRole(roleName, []string{})
69+
if len(resources) > 0 {
70+
r.resourcesVPower.Set(roleName, resources)
71+
}
4772
}
4873

4974
func (r *RolesGroup) DeleteRole(roleName string) {
@@ -61,3 +86,21 @@ func (r *RolesGroup) RevokeRole(address std.Address, role string) {
6186
func (r *RolesGroup) GetMemberRoles(address std.Address) []string {
6287
return r.rm.GetUserRoles(address)
6388
}
89+
90+
func (r *RolesGroup) GetMemberResourceVPower(address std.Address, resource string) uint64 {
91+
roles := r.rm.GetUserRoles(address)
92+
power := uint64(0)
93+
for _, role := range roles {
94+
resourcesRaw, exists := r.resourcesVPower.Get(role)
95+
if !exists {
96+
continue
97+
}
98+
resources := resourcesRaw.([]ResourceVPower)
99+
for _, r := range resources {
100+
if r.Resource == resource && r.Power > power {
101+
power = r.Power
102+
}
103+
}
104+
}
105+
return power
106+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/p/teritori/dao_roles_voting_group
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package dao_roles_voting_group
2+
3+
import (
4+
"gno.land/p/demo/json"
5+
"gno.land/p/teritori/dao_interfaces"
6+
)
7+
8+
const updateMembersType = "gno.land/p/teritori/dao_voting_group.UpdateMembers"
9+
10+
type UpdateMembersExecutableMessage []Member
11+
12+
var _ dao_interfaces.ExecutableMessage = (*UpdateMembersExecutableMessage)(nil)
13+
14+
func (m *UpdateMembersExecutableMessage) FromJSON(ast *json.Node) {
15+
changes := ast.MustArray()
16+
*m = make([]Member, len(changes))
17+
for i, change := range changes {
18+
(*m)[i].FromJSON(change)
19+
}
20+
}
21+
22+
func (m *UpdateMembersExecutableMessage) ToJSON() *json.Node {
23+
changes := make([]*json.Node, len(*m))
24+
for i, change := range *m {
25+
changes[i] = change.ToJSON()
26+
}
27+
28+
return json.ArrayNode("", changes)
29+
}
30+
31+
func (m *UpdateMembersExecutableMessage) String() string {
32+
return m.ToJSON().String()
33+
}
34+
35+
func (m *UpdateMembersExecutableMessage) Type() string {
36+
return updateMembersType
37+
}
38+
39+
type updateMembersHandler struct {
40+
vg *RolesVotingGroup
41+
}
42+
43+
var _ dao_interfaces.MessageHandler = (*updateMembersHandler)(nil)
44+
45+
func (h *updateMembersHandler) Type() string {
46+
return updateMembersType
47+
}
48+
49+
func (h *updateMembersHandler) Execute(msg dao_interfaces.ExecutableMessage) {
50+
m, ok := msg.(*UpdateMembersExecutableMessage)
51+
if !ok {
52+
panic("unexpected message type")
53+
}
54+
55+
for _, change := range *m {
56+
h.vg.SetMemberPower(change.Address, change.Power)
57+
}
58+
}
59+
60+
func (h *updateMembersHandler) Instantiate() dao_interfaces.ExecutableMessage {
61+
return &UpdateMembersExecutableMessage{}
62+
}

0 commit comments

Comments
 (0)