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: users namespace #2471

Merged
merged 69 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
51322b7
feat: r/system/names public functions
anarcher Oct 30, 2022
2a9fee6
test: names basic test
anarcher Nov 1, 2022
db517fd
chore: default inpause false
anarcher Nov 1, 2022
65fbc4d
feat(render): render index and namespace
anarcher Nov 2, 2022
180fb33
feat: reName and HasPerm
anarcher Nov 4, 2022
32a086f
feat: check namespace perm in vmkeeper.AddPackage
anarcher Nov 5, 2022
3b865ef
refactor: separate space methods and public function
anarcher Nov 5, 2022
5e18c06
refactor: assertIsAdmin
anarcher Nov 5, 2022
4a3c896
Update examples/gno.land/r/system/names/names.gno
anarcher Nov 10, 2022
2e9e7a5
Update examples/gno.land/r/system/names/names.gno
anarcher Nov 10, 2022
a0a83e8
refactor: move public functions to public.gno
anarcher Nov 10, 2022
32c816a
chore: comment TODO: prevent self
anarcher Nov 10, 2022
5f1c6c4
feat: caller can't remove self
anarcher Jan 16, 2023
905a458
feat: global variable `enabled` for skipping to check perms if adding
anarcher Jan 16, 2023
1595785
chore: remove todo comment
anarcher Jan 16, 2023
904bd0b
fix: avl.iterate
anarcher Jan 19, 2023
715e72f
chore: check enabled
anarcher Jun 7, 2023
d66c615
fix: avl.Tree.Iterate
anarcher Jun 8, 2023
6852b17
chore: make fmt
anarcher Jun 8, 2023
a61c9df
chore: update gno.mod
anarcher Jul 21, 2023
b8bd426
Merge remote-tracking branch 'origin/master' into feat/sys/names
gfanton Jun 19, 2024
5f3f975
wip: names
gfanton Jun 19, 2024
b20b163
wip: users
gfanton Jul 1, 2024
163a85c
feat: add keeper user check
gfanton Jul 1, 2024
dd31ab3
fix: typo
gfanton Jul 1, 2024
7418f7c
fix: increase gas test
gfanton Jul 1, 2024
3610ece
Merge branch 'master' into feat/sys/names
gfanton Jul 1, 2024
e101d63
chore: remove sys/names
gfanton Jul 1, 2024
dff9367
chore: cleanup duplicate method
gfanton Jul 1, 2024
763a39b
fix: keeper
gfanton Jul 1, 2024
5dabc77
fix: users resolve
gfanton Jul 1, 2024
1f9ba97
fix: undo change on user / add GetUserAddressByName
gfanton Jul 2, 2024
91bc22b
Update keeper.go
gfanton Jul 6, 2024
bf7fba2
wip: admin users package
gfanton Jul 6, 2024
2b88906
feat: add replace command for txtar
gfanton Jul 7, 2024
b451995
feat: Add sys/users
gfanton Jul 7, 2024
c1df7c3
chore: improve txtar comments
gfanton Jul 7, 2024
122d7d4
chore: use patchpkg instead of replace
gfanton Jul 8, 2024
0fca48f
Merge remote-tracking branch 'origin/master' into feat/check-user-perm
gfanton Jul 8, 2024
07057f3
chore: fix typo & update doc
gfanton Jul 8, 2024
a529632
feat: update user register fee to 20gnot
gfanton Jul 8, 2024
f798e3f
chore: lint
gfanton Jul 8, 2024
8873d15
chore: doc
gfanton Jul 8, 2024
2c714c9
Merge remote-tracking branch 'origin/master' into feat/check-user-perm
gfanton Jul 8, 2024
0dd8e21
feat: add preregister users/restricted
gfanton Jul 9, 2024
c03dab4
chore: typo
gfanton Jul 9, 2024
45ed16d
fix: small adjustement
gfanton Jul 9, 2024
e63ec47
chore: fix tests
gfanton Jul 9, 2024
eec6db9
fix: edit min fee
gfanton Jul 9, 2024
307810b
fix: test
gfanton Jul 9, 2024
f50dfc6
fix: users tidy
gfanton Jul 9, 2024
0799eec
chore: update typo warn
gfanton Jul 9, 2024
967aa65
fix: typo namespaces doc
gfanton Jul 9, 2024
2578356
chore: update gas deliver
gfanton Jul 9, 2024
cdd7ab2
chore: fix typo
gfanton Jul 9, 2024
cb7d3e3
chore: fix chain id
gfanton Jul 9, 2024
c754fcd
feat: use ownership for `sys/users`
gfanton Jul 9, 2024
af55828
chore: sys/users comment and name
gfanton Jul 9, 2024
adbddf1
chore: update info / remove famous name (breaking tests)
gfanton Jul 9, 2024
9cc0c97
fix: patchpkg
gfanton Jul 9, 2024
7ff351b
fix: bad user output test
gfanton Jul 9, 2024
69cab73
fix: namespace path clean
gfanton Jul 9, 2024
497038b
feat: set moul as admin for {sys/demo}/users
gfanton Jul 9, 2024
efef59e
fix: remove genesis loading from the file
gfanton Jul 9, 2024
1d43a00
Merge branch 'master' into feat/check-user-perm
gfanton Jul 9, 2024
cbe4cb9
fix: typo
gfanton Jul 9, 2024
f988b42
Update docs/concepts/namespaces.md
zivkovicmilos Jul 9, 2024
14b0c36
Update docs/concepts/namespaces.md
zivkovicmilos Jul 9, 2024
5df88c5
fix: genesis
gfanton Jul 9, 2024
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
56 changes: 52 additions & 4 deletions docs/concepts/namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ id: namespaces

# Namespaces

Namespaces provide users with the exclusive capability to publish contracts under their designated namespaces, similar to GitHub's user and organization model.
Namespaces provide users with the exclusive capability to publish contracts under their designated namespaces,
similar to GitHub's user and organization model.

This feature is currently a work in progress (WIP). To learn more about namespaces, please checkout https://github.com/gnolang/gno/issues/1107.
:::warn This feature isn't enabled by default on the chain and is currently only on test4.gno.land.
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved

# Package Path

A package path is a unique identifier for each package/realm. It specifies the location of the package source code which helps differentiate it from others. You can use a package path to:
A package path is a unique identifier for each package/realm. It specifies the location of the package source
code which helps differentiate it from others. You can use a package path to:

- Call a specific function from a package/realm. (e.g using `gnokey maketx call`)
- Import it in other packages/realms.
Expand All @@ -21,7 +23,9 @@ Here's a breakdown of the structure of a package path:
- Type: Defines the type of package.
- `p/`: [Package](packages.md)
- `r/`: [Realm](realms.md)
- Namespace: A namespace can be included after the type (e.g., user or organization name). Namespaces are a way to group related packages or realms, but currently ownership cannot be claimed. (see [Issue #1107](https://github.com/gnolang/gno/issues/1107) for more info)
- Namespace: A namespace can be included after the type (e.g., user or organization name). Namespaces are a
way to group related packages or realms, but currently ownership cannot be claimed. (see
[Issue#1107](https://github.com/gnolang/gno/issues/1107) for more info)
- Remaining Path: The remaining part of the path.
- Can only contain alphanumeric characters (letters and numbers) and underscores.
- No special characters allowed (except underscore).
Expand All @@ -33,3 +37,47 @@ Examples:

- `gno.land/p/demo/avl`: This signifies a package named `avl` within the `demo` namespace.
- `gno.land/r/gnoland/home`: This signifies a realm named `home` within the `gnoland` namespace.

## Registration Process

The registration process is contract-based. The `AddPkg` command references
`sys/users` for filtering, which in turn is based on `r/demo/users`.

When `sys/users` is enabled, you need to register a name using `r/demo/users`. You can call the
`r/demo/users.Register` function to register the name for the caller's address.

> ex: `test1` user registering as `patrick`
```bash
$ gnokey maketx call -pkgpath gno.land/r/demo/users \
-func Register \
-gas-fee 1000000ugnot -gas-wanted 2000000 \
-broadcast \
-chainid=test4 \
-send=20000000ugnot \
-args '' \
-args 'patrick' \
-args 'My Profile Quote' test1
```

:::note Do not forget to update chain id
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved


After successful registration, you can add a package under the registered namespace.

## Anonymous Namespace

Gno.land offers the ability to add a package without having a registered namespace.
gfanton marked this conversation as resolved.
Show resolved Hide resolved
You can do this by using your own address as a namespace. This is formatted as `{p,r}/{std.Address}/**`.

> ex: with `test1` user adding a package `microblog` using his own address as namespace
```bash
$ gnokey maketx addpkg \
--pkgpath "gno.land/r/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5/microblog" \
--pkgdir "examples/gno.land/p/demo/microblog" \
--deposit 100000000ugnot \
--gas-fee 1000000ugnot \
--gas-wanted 2000000 \
--broadcast \
--chainid test4 \
test1
```
4 changes: 2 additions & 2 deletions examples/gno.land/r/demo/boards/z_0_b_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 199000000ugnot
// SEND: 19900000ugnot

import (
"gno.land/r/demo/boards"
Expand All @@ -20,4 +20,4 @@ func main() {
}

// Error:
// payment must not be less than 200000000
// payment must not be less than 20000000
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_0_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 200000000ugnot
// SEND: 20000000ugnot

import (
"gno.land/r/demo/boards"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_10_b_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"strconv"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_11_a_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"strconv"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_11_b_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"strconv"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_11_c_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"strconv"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_11_d_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"strconv"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_11_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"strconv"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_5_b_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package main

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"std"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_5_c_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package main

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"std"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_5_d_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package main

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"std"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_7_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"gno.land/r/demo/boards"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_8_filetest.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PKGPATH: gno.land/r/demo/boards_test
package boards_test

// SEND: 2000000000ugnot
// SEND: 200000000ugnot

import (
"strconv"
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/groups/z_0_b_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ func main() {
}

// Error:
// payment must not be less than 200000000
// payment must not be less than 20000000
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/groups/z_1_a_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

var gid groups.GroupID

const admin = std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj")
const admin = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq")

func main() {
caller := std.GetOrigCaller() // main
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/groups/z_2_a_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

var gid groups.GroupID

const admin = std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj")
const admin = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq")

func main() {
caller := std.GetOrigCaller() // main
Expand Down
66 changes: 66 additions & 0 deletions examples/gno.land/r/demo/users/preregister.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package users

import (
"std"

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

// pre-restricted names
var preRestrictedNames = []string{
"bitcoin", "cosmos", "newtendermint", "ethereum",
}

// pre-registered users
var preRegisteredUsers = []struct {
Name string
Address std.Address
}{
// system name
{"archives", "g1xlnyjrnf03ju82v0f98ruhpgnquk28knmjfe5k"}, // -> @r_archives
{"demo", "g13ek2zz9qurzynzvssyc4sthwppnruhnp0gdz8n"}, // -> @r_demo
{"gno", "g19602kd9tfxrfd60sgreadt9zvdyyuudcyxsz8a"}, // -> @r_gno
{"gnoland", "g1g3lsfxhvaqgdv4ccemwpnms4fv6t3aq3p5z6u7"}, // -> @r_gnoland
{"gnolang", "g1yjlnm3z2630gg5mryjd79907e0zx658wxs9hnd"}, // -> @r_gnolang
{"gov", "g1g73v2anukg4ej7axwqpthsatzrxjsh0wk797da"}, // -> @r_gov
{"nt", "g15ge0ae9077eh40erwrn2eq0xw6wupwqthpv34l"}, // -> @r_nt
{"sys", "g1r929wt2qplfawe4lvqv9zuwfdcz4vxdun7qh8l"}, // -> @r_sys
{"x", "g164sdpew3c2t3rvxj3kmfv7c7ujlvcw2punzzuz"}, // -> @r_x
}

func init() {
// add pre-registered users
for _, res := range preRegisteredUsers {
// assert not already registered.
_, ok := name2User.Get(res.Name)
if ok {
panic("name already registered")
}

_, ok = addr2User.Get(res.Address.String())
if ok {
panic("address already registered")
}

counter++
user := &users.User{
Address: res.Address,
Name: res.Name,
Profile: "",
Number: counter,
Invites: int(0),
Inviter: admin,
}
name2User.Set(res.Name, user)
addr2User.Set(res.Address.String(), user)
}

// add pre-restricted names
for _, name := range preRestrictedNames {
if _, ok := name2User.Get(name); ok {
panic("name already registered")
}

restricted.Set(name, true)
}
}
61 changes: 51 additions & 10 deletions examples/gno.land/r/demo/users/users.gno
gfanton marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import (
// State

var (
admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj"
name2User avl.Tree // Name -> *users.User
addr2User avl.Tree // std.Address -> *users.User
invites avl.Tree // string(inviter+":"+invited) -> true
counter int // user id counter
minFee int64 = 200 * 1000000 // minimum gnot must be paid to register.
maxFeeMult int64 = 10 // maximum multiples of minFee accepted.
admin std.Address = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" // @moul

restricted avl.Tree // Name -> true - restricted name
name2User avl.Tree // Name -> *users.User
addr2User avl.Tree // std.Address -> *users.User
invites avl.Tree // string(inviter+":"+invited) -> true
counter int // user id counter
minFee int64 = 20 * 1_000_000 // minimum gnot must be paid to register.
maxFeeMult int64 = 10 // maximum multiples of minFee accepted.
)

//----------------------------------------
Expand All @@ -34,8 +36,10 @@ func Register(inviter std.Address, name string, profile string) {
if caller != std.GetOrigCaller() {
panic("should not happen") // because std.AssertOrigCall().
}

sentCoins := std.GetOrigSend()
minCoin := std.NewCoin("ugnot", minFee)

if inviter == "" {
// banker := std.GetBanker(std.BankerTypeOrigSend)
if len(sentCoins) == 1 && sentCoins[0].IsGTE(minCoin) {
Expand All @@ -55,19 +59,35 @@ func Register(inviter std.Address, name string, profile string) {
}
invites.Remove(invitekey)
}

// assert not already registered.
_, ok := name2User.Get(name)
if ok {
panic("name already registered")
panic("name already registered: " + name)
}
_, ok = addr2User.Get(caller.String())
if ok {
panic("address already registered")
panic("address already registered: " + caller.String())
}

isInviterAdmin := inviter == admin

// check for restricted name
if _, isRestricted := restricted.Get(name); isRestricted {
// only address invite by the admin can register restricted name
if !isInviterAdmin {
panic("restricted name: " + name)
}

restricted.Remove(name)
}

// assert name is valid.
if !reName.MatchString(name) {
// admin inviter can bypass name restriction
if !isInviterAdmin && !reName.MatchString(name) {
panic("invalid name: " + name + " (must be at least 6 characters, lowercase alphanumeric with underscore)")
}

// remainder of fees go toward invites.
invites := int(0)
if len(sentCoins) == 1 {
Expand Down Expand Up @@ -240,10 +260,31 @@ func Resolve(input users.AddressOrName) std.Address {
if !isName {
return std.Address(input) // TODO check validity
}

user := GetUserByName(name)
return user.Address
}

// Add restricted name to the list
func AdminAddRestrictedName(name string) {
// assert CallTx call.
std.AssertOriginCall()
// get caller
caller := std.GetOrigCaller()
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
// assert admin
if caller != admin {
panic("unauthorized")
}

if user := GetUserByName(name); user != nil {
panic("already registered name")
}

// register restricted name

restricted.Set(name, true)
}

//----------------------------------------
// Constants

Expand Down
Loading
Loading