Skip to content

Commit

Permalink
Merge branch 'master' into feat/gnoxchange
Browse files Browse the repository at this point in the history
  • Loading branch information
matijamarjanovic authored Feb 25, 2025
2 parents 9b86899 + 3288fe8 commit 8aee81a
Show file tree
Hide file tree
Showing 28 changed files with 1,824 additions and 63 deletions.
6 changes: 3 additions & 3 deletions docs/concepts/namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ similar to GitHub's user and organization model.

:::warning Not enabled

This feature isn't enabled by default on the portal loop chain and is currently available only on test4.gno.land.
This feature isn't enabled by default on the Portal Loop chain and is currently available only on test5.gno.land.

:::

Expand Down Expand Up @@ -56,7 +56,7 @@ $ gnokey maketx call -pkgpath gno.land/r/demo/users \
-func Register \
-gas-fee 1000000ugnot -gas-wanted 2000000 \
-broadcast \
-chainid=test4 \
-chainid=test5 \
-send=20000000ugnot \
-args '' \
-args 'patrick' \
Expand Down Expand Up @@ -86,6 +86,6 @@ $ gnokey maketx addpkg \
--gas-fee 1000000ugnot \
--gas-wanted 2000000 \
--broadcast \
--chainid test4 \
--chainid test5 \
test1
```
22 changes: 7 additions & 15 deletions docs/concepts/testnets.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,6 @@ Test5 was launched in November 2024.
- **Versioning strategy**:
- Test5 is to be release-based, following releases of the Gno tech stack.

## Test4

Test4 is the first permanent multi-node testnet, launched in July 2024.

- **Persistence of state:**
- State is fully persisted unless there are breaking changes in a new release,
where persistence partly depends on implementing a migration strategy
- **Timeliness of code:**
- Versioning mechanisms for packages & realms will be implemented for test4
- **Intended purpose**
- Running a full node, testing validator coordination, deploying stable Gno
dApps, creating tools that require persisted state & transaction history
- **Versioning strategy**:
- Test4 is the first gno.land testnet to be release-based, following releases
of the Gno tech stack.

## Staging

Expand All @@ -98,6 +83,13 @@ Staging is a testnet that is reset once every 60 minutes.

These testnets are deprecated and currently serve as archives of previous progress.

## Test4

Test4 is the first permanent multi-node testnet. Archived data for test4 can be found [here](https://github.com/gnolang/tx-exports/tree/main/test4.gno.land).

Launch date: July 10th 2024
Release commit: [194903d](https://github.com/gnolang/gno/commit/194903db0350ace7d57910e6c34125d3aa9817da)

### Test3 (archive)

The third Gno testnet. Archived data for test3 can be found [here](https://github.com/gnolang/tx-exports/tree/main/test3.gno.land).
Expand Down
1 change: 0 additions & 1 deletion docs/reference/network-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ id: network-config
|-------------|----------------------------------|---------------|
| Portal Loop | https://rpc.gno.land:443 | `portal-loop` |
| Test5 | https://rpc.test5.gno.land:443 | `test5` |
| Test4 | https://rpc.test4.gno.land:443 | `test4` |
| Staging | https://rpc.staging.gno.land:443 | `staging` |

### WebSocket endpoints
Expand Down
261 changes: 261 additions & 0 deletions examples/gno.land/p/moul/authz/authz.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// Package authz provides flexible authorization control for privileged actions.
//
// # Authorization Strategies
//
// The package supports multiple authorization strategies:
// - Member-based: Single user or team of users
// - Contract-based: Async authorization (e.g., via DAO)
// - Auto-accept: Allow all actions
// - Drop: Deny all actions
//
// Core Components
//
// - Authority interface: Base interface implemented by all authorities
// - Authorizer: Main wrapper object for authority management
// - MemberAuthority: Manages authorized addresses
// - ContractAuthority: Delegates to another contract
// - AutoAcceptAuthority: Accepts all actions
// - DroppedAuthority: Denies all actions
//
// Quick Start
//
// // Initialize with contract deployer as authority
// var auth = authz.New()
//
// // Create functions that require authorization
// func UpdateConfig(newValue string) error {
// return auth.Do("update_config", func() error {
// config = newValue
// return nil
// })
// }
//
// See example_test.gno for more usage examples.
package authz

import (
"errors"
"std"

"gno.land/p/demo/avl"
"gno.land/p/demo/avl/rotree"
"gno.land/p/demo/ufmt"
"gno.land/p/moul/addrset"
"gno.land/p/moul/once"
)

// Authorizer is the main wrapper object that handles authority management
type Authorizer struct {
current Authority
}

// Authority represents an entity that can authorize privileged actions
type Authority interface {
// Authorize executes a privileged action if the caller is authorized
// Additional args can be provided for context (e.g., for proposal creation)
Authorize(title string, action PrivilegedAction, args ...interface{}) error

// String returns a human-readable description of the authority
String() string
}

// PrivilegedAction defines a function that performs a privileged action.
type PrivilegedAction func() error

// PrivilegedActionHandler is called by contract-based authorities to handle
// privileged actions.
type PrivilegedActionHandler func(title string, action PrivilegedAction) error

// New creates a new Authorizer with the current realm's address as authority
func New() *Authorizer {
return &Authorizer{
current: NewMemberAuthority(std.PreviousRealm().Address()),
}
}

// NewWithAuthority creates a new Authorizer with a specific authority
func NewWithAuthority(authority Authority) *Authorizer {
return &Authorizer{
current: authority,
}
}

// Current returns the current authority implementation
func (a *Authorizer) Current() Authority {
return a.current
}

// Transfer changes the current authority after validation
func (a *Authorizer) Transfer(newAuthority Authority) error {
// Ask current authority to validate the transfer
return a.current.Authorize("transfer_authority", func() error {
a.current = newAuthority
return nil
})
}

// Do executes a privileged action through the current authority
func (a *Authorizer) Do(title string, action PrivilegedAction, args ...interface{}) error {
return a.current.Authorize(title, action, args...)
}

// String returns a string representation of the current authority
func (a *Authorizer) String() string {
return a.current.String()
}

// MemberAuthority is the default implementation using addrset for member management
type MemberAuthority struct {
members addrset.Set
}

func NewMemberAuthority(members ...std.Address) *MemberAuthority {
auth := &MemberAuthority{}
for _, addr := range members {
auth.members.Add(addr)
}
return auth
}

func (a *MemberAuthority) Authorize(title string, action PrivilegedAction, args ...interface{}) error {
caller := std.PreviousRealm().Address()
if !a.members.Has(caller) {
return errors.New("unauthorized")
}

if err := action(); err != nil {
return err
}
return nil
}

func (a *MemberAuthority) String() string {
return ufmt.Sprintf("member_authority[size=%d]", a.members.Size())
}

// AddMember adds a new member to the authority
func (a *MemberAuthority) AddMember(addr std.Address) error {
return a.Authorize("add_member", func() error {
a.members.Add(addr)
return nil
})
}

// RemoveMember removes a member from the authority
func (a *MemberAuthority) RemoveMember(addr std.Address) error {
return a.Authorize("remove_member", func() error {
a.members.Remove(addr)
return nil
})
}

// Tree returns a read-only view of the members tree
func (a *MemberAuthority) Tree() *rotree.ReadOnlyTree {
tree := a.members.Tree().(*avl.Tree)
return rotree.Wrap(tree, nil)
}

// Has checks if the given address is a member of the authority
func (a *MemberAuthority) Has(addr std.Address) bool {
return a.members.Has(addr)
}

// ContractAuthority implements async contract-based authority
type ContractAuthority struct {
contractPath string
contractAddr std.Address
contractHandler PrivilegedActionHandler
proposer Authority // controls who can create proposals
executionOnce once.Once
}

func NewContractAuthority(path string, handler PrivilegedActionHandler) *ContractAuthority {
return &ContractAuthority{
contractPath: path,
contractAddr: std.DerivePkgAddr(path),
contractHandler: handler,
proposer: NewAutoAcceptAuthority(), // default: anyone can propose
executionOnce: once.Once{}, // initialize execution once
}
}

// NewRestrictedContractAuthority creates a new contract authority with a proposer restriction
func NewRestrictedContractAuthority(path string, handler PrivilegedActionHandler, proposer Authority) Authority {
if path == "" {
panic("contract path cannot be empty")
}
if handler == nil {
panic("contract handler cannot be nil")
}
if proposer == nil {
panic("proposer cannot be nil")
}
return &ContractAuthority{
contractPath: path,
contractAddr: std.DerivePkgAddr(path),
contractHandler: handler,
proposer: proposer,
executionOnce: once.Once{},
}
}

func (a *ContractAuthority) Authorize(title string, action PrivilegedAction, args ...interface{}) error {
if a.contractHandler == nil {
return errors.New("contract handler is not set")
}

// Wrap the action to ensure it can only be executed by the contract
wrappedAction := func() error {
caller := std.PreviousRealm().Address()
if caller != a.contractAddr {
return errors.New("action can only be executed by the contract")
}
return a.executionOnce.DoErr(func() error {
return action()
})
}

// Use the proposer authority to control who can create proposals
return a.proposer.Authorize(title+"_proposal", func() error {
if err := a.contractHandler(title, wrappedAction); err != nil {
return err
}
return nil
}, args...)
}

func (a *ContractAuthority) String() string {
return ufmt.Sprintf("contract_authority[contract=%s]", a.contractPath)
}

// AutoAcceptAuthority implements an authority that accepts all actions
// AutoAcceptAuthority is a simple authority that automatically accepts all actions.
// It can be used as a proposer authority to allow anyone to create proposals.
type AutoAcceptAuthority struct{}

func NewAutoAcceptAuthority() *AutoAcceptAuthority {
return &AutoAcceptAuthority{}
}

func (a *AutoAcceptAuthority) Authorize(title string, action PrivilegedAction, args ...interface{}) error {
return action()
}

func (a *AutoAcceptAuthority) String() string {
return "auto_accept_authority"
}

// droppedAuthority implements an authority that denies all actions
type droppedAuthority struct{}

func NewDroppedAuthority() Authority {
return &droppedAuthority{}
}

func (a *droppedAuthority) Authorize(title string, action PrivilegedAction, args ...interface{}) error {
return errors.New("dropped authority: all actions are denied")
}

func (a *droppedAuthority) String() string {
return "dropped_authority"
}
Loading

0 comments on commit 8aee81a

Please sign in to comment.