diff --git a/examples/gno.land/r/demo/boards/z_0_filetest.gno b/examples/gno.land/r/demo/boards/z_0_filetest.gno index 13ea7226432..9f28920c3ac 100644 --- a/examples/gno.land/r/demo/boards/z_0_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_filetest.gno @@ -14,7 +14,7 @@ var bid boards.BoardID func init() { std.TestSetRealm(std.NewUserRealm(std.Address("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm"))) // so that CurrentRealm.Addr() matches OrigCaller - err := users.Register("gnouser123") + users.Register("gnouser123") bid = boards.CreateBoard("test_board") boards.CreateThread(bid, "First Post (title)", "Body of the first post. (body)") diff --git a/examples/gno.land/r/demo/groups/z_2_b_filetest.gno b/examples/gno.land/r/demo/groups/z_2_b_filetest.gno index 1049382a94a..f799c69bde7 100644 --- a/examples/gno.land/r/demo/groups/z_2_b_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_b_filetest.gno @@ -19,4 +19,4 @@ func main() { } // Error: -// user not found +// r/gnoland/users: non-user call diff --git a/examples/gno.land/r/demo/groups/z_2_d_filetest.gno b/examples/gno.land/r/demo/groups/z_2_d_filetest.gno index 500c6c9f80a..fbaacfe453c 100644 --- a/examples/gno.land/r/demo/groups/z_2_d_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_d_filetest.gno @@ -28,4 +28,4 @@ func main() { } // Error: -// user not found +// r/gnoland/users: non-user call diff --git a/examples/gno.land/r/gnoland/users/users.gno b/examples/gno.land/r/gnoland/users/users.gno index 5eca5a272e6..c84c4e6b07f 100644 --- a/examples/gno.land/r/gnoland/users/users.gno +++ b/examples/gno.land/r/gnoland/users/users.gno @@ -11,8 +11,8 @@ import ( ) var ( - changelog = releases.NewChangelog("r/gnoland/users") cd = std.ChainDomain() + changelog = releases.NewChangelog("r/gnoland/users") ) const usersPrefix = "gno.land/r/gnoland/users/" diff --git a/examples/gno.land/r/gnoland/users/v1/admin.gno b/examples/gno.land/r/gnoland/users/v1/admin.gno index 68937dd1d3e..948d7a73a52 100644 --- a/examples/gno.land/r/gnoland/users/v1/admin.gno +++ b/examples/gno.land/r/gnoland/users/v1/admin.gno @@ -11,8 +11,8 @@ import ( var paused = false // XXX: replace with p/moul/authz -// NewSetPausedExecutor allows GovDAO to pause or unpause this realm -func NewSetPausedExecutor(newPausedValue bool) dao.Executor { +// ProposeNewPausedValue allows GovDAO to pause or unpause this realm +func ProposeNewPausedValue(newPausedValue bool) dao.Executor { cb := func() error { paused = newPausedValue return nil diff --git a/examples/gno.land/r/gnoland/users/v1/errors.gno b/examples/gno.land/r/gnoland/users/v1/errors.gno index 6acc476b8c5..0647f0f4fd3 100644 --- a/examples/gno.land/r/gnoland/users/v1/errors.gno +++ b/examples/gno.land/r/gnoland/users/v1/errors.gno @@ -7,7 +7,8 @@ import ( ) var ( + ErrNonUserCall = errors.New("r/gnoland/users: non-user call") ErrPaused = errors.New("r/gnoland/users: paused") - ErrInvalidPayment = ufmt.Errorf("r/gnoland/users: you need to send exactly %s ugnot", registerPrice) + ErrInvalidPayment = ufmt.Errorf("r/gnoland/users: you need to send exactly %d ugnot", registerPrice) ErrInvalidUsername = errors.New("r/gnoland/users: invalid username") ) diff --git a/examples/gno.land/r/gnoland/users/v1/users.gno b/examples/gno.land/r/gnoland/users/v1/users.gno index 6d09935494e..56280392458 100644 --- a/examples/gno.land/r/gnoland/users/v1/users.gno +++ b/examples/gno.land/r/gnoland/users/v1/users.gno @@ -18,33 +18,33 @@ var ( reUsername = regexp.MustCompile(reValidUsername) ) -// Register registers a new username for the caller +// Register registers a new username for the caller. // A valid username must start with a minimum of 3 letters, // end with a minimum of 3 numbers, and be less than 20 chars long. // All letters must be lowercase, and the only valid special char is `_`. // Only calls from EOAs are supported. -func Register(username string) error { - std.AssertOriginCall() +func Register(username string) { + if !std.PreviousRealm().IsUser() { + panic(ErrNonUserCall) + } if paused { - return ErrPaused + panic(ErrPaused) } if std.OriginSend().AmountOf("ugnot") != registerPrice { - return ErrInvalidPayment + panic(ErrInvalidPayment) } if matched := reUsername.MatchString(username); !matched { - return ErrInvalidUsername + panic(ErrInvalidUsername) } registrant := std.PreviousRealm().Address() if err := susers.RegisterUser(username, registrant); err != nil { - return err + panic(err) } latestUsers.Append(username) std.Emit("Registeration", "address", registrant.String(), "name", username) - - return nil } diff --git a/examples/gno.land/r/gnoland/users/v1/users_test.gno b/examples/gno.land/r/gnoland/users/v1/users_test.gno index 53e2ee808c7..9fcba047e4a 100644 --- a/examples/gno.land/r/gnoland/users/v1/users_test.gno +++ b/examples/gno.land/r/gnoland/users/v1/users_test.gno @@ -23,7 +23,10 @@ func TestRegister_Valid(t *testing.T) { std.TestSetRealm(std.NewUserRealm(aliceAddr)) std.TestSetOriginCaller(aliceAddr) - uassert.NoError(t, Register(alice)) + uassert.NotPanics(t, func() { + Register(alice) + }) + res, latest := susers.ResolveName(alice) uassert.NotEqual(t, nil, res) @@ -39,24 +42,54 @@ func TestRegister_Invalid(t *testing.T) { std.TestSetOriginCaller(bobAddr) // Invalid usernames - uassert.Error(t, Register("alice"), ErrInvalidUsername.Error()) // vanity - uassert.Error(t, Register(""), ErrInvalidUsername.Error()) // empty - uassert.Error(t, Register(" "), ErrInvalidUsername.Error()) // empty - uassert.Error(t, Register("123"), ErrInvalidUsername.Error()) // only numbers - uassert.Error(t, Register("alice&#($)"), ErrInvalidUsername.Error()) // non-allowed chars - uassert.Error(t, Register("Alice123"), ErrInvalidUsername.Error()) // upper-case - uassert.Error(t, Register("toolongusernametoolongusernametoolongusername123"), - ErrInvalidUsername.Error()) // too long + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register("alice") // vanity + }) + + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register("") // empty + }) + + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register(" ") // empty + }) + + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register("123") // empty + }) + + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register("123") // only numbers + }) + + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register("alice&#($)") // non-allowed chars + }) + + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register("Alice123") // upper-case + }) + + uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { + Register("toolongusernametoolongusernametoolongusername123") // too long + }) // Name taken - urequire.NoError(t, Register(bob)) - uassert.Error(t, Register(bob), susers.ErrNameTaken.Error()) -} + urequire.NotPanics(t, func() { + Register(bob) + }) + uassert.PanicsWithMessage(t, susers.ErrNameTaken.Error(), func() { + Register(bob) // already registered + }) +} func TestRegister_InvalidPayment(t *testing.T) { std.TestSetRealm(std.NewUserRealm(bobAddr)) std.TestSetOriginCaller(bobAddr) std.TestSetOriginSend(std.NewCoins(std.NewCoin("ugnot", 12)), nil) // invalid payment amount - uassert.Error(t, Register("alice"), ErrInvalidPayment.Error()) // vanity + + uassert.PanicsWithMessage(t, ErrInvalidPayment.Error(), func() { + Register(alice) + }) } diff --git a/examples/gno.land/r/sys/users/admin.gno b/examples/gno.land/r/sys/users/admin.gno index 15183a41856..4d8d2d6fad5 100644 --- a/examples/gno.land/r/sys/users/admin.gno +++ b/examples/gno.land/r/sys/users/admin.gno @@ -35,6 +35,25 @@ func ProposeControllerRemoval(addr std.Address) dao.Executor { return bridge.GovDAO().NewGovDAOExecutor(cb) } +// ProposeControllerAdditionAndRemoval allows GovDAO to add a new caller and remove an old caller in the same proposal. +func ProposeControllerAdditionAndRemoval(toAdd, toRemove std.Address) dao.Executor { + cb := func() error { + err := addToWhitelist(toAdd) + if err != nil { + panic(err) + } + + err = deleteFromwhitelist(toRemove) + if err != nil { + panic(err) + } + + return nil + } + + return bridge.GovDAO().NewGovDAOExecutor(cb) +} + // Helpers func deleteFromwhitelist(addr std.Address) error { diff --git a/examples/gno.land/r/sys/users/users.gno b/examples/gno.land/r/sys/users/users.gno index 4715ffa25e5..61d8ccdfe42 100644 --- a/examples/gno.land/r/sys/users/users.gno +++ b/examples/gno.land/r/sys/users/users.gno @@ -52,5 +52,8 @@ func makeUserDataSafe(data any) any { if cpy.deleted { return nil } + + // Note: when requesting data from this AVL tree, (exists bool) will be true + // Even if the data is "deleted". This is currently unavoidable return cpy } diff --git a/gno.land/genesis/genesis_txs.jsonl b/gno.land/genesis/genesis_txs.jsonl index b42d6cc52c4..29953f5bd2f 100644 --- a/gno.land/genesis/genesis_txs.jsonl +++ b/gno.land/genesis/genesis_txs.jsonl @@ -1,5 +1,5 @@ {"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"1000000ugnot","pkg_path":"gno.land/r/gnoland/users/v1","func":"Register","args":["administrator123"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":""}],"memo":""}} -{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt","send":"1000000ugnot","pkg_path":"gno.land/r/gnoland/users/v1","func":"Register","args":["zo_oma123"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A6yg5/iiktruezVw5vZJwLlGwyrvw8RlqOToTRMWXkE2"},"signature":""}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt","send":"1000000ugnot","pkg_path":"gno.land/r/gnoland/users/v1","func":"Register","args":["zoo_ma123"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A6yg5/iiktruezVw5vZJwLlGwyrvw8RlqOToTRMWXkE2"},"signature":""}],"memo":""}} {"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1manfred47kzduec920z88wfr64ylksmdcedlf5","send":"1000000ugnot","pkg_path":"gno.land/r/gnoland/users/v1","func":"Register","args":["moul001"]}],"fee":{"gas_wanted":"2000000","gas_fee":"200000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":""}],"memo":""}} {"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9","send":"1000000ugnot","pkg_path":"gno.land/r/gnoland/users/v1","func":"Register","args":["piupiu123"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"Ar68lqbU2YC63fbMcYUtJhYO3/66APM/EqF7m0nUjGyz"},"signature":""}],"memo":""}} {"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5","send":"1000000ugnot","pkg_path":"gno.land/r/gnoland/users/v1","func":"Register","args":["anarcher123"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AjpLbKdQeH+yB/1OCB148l5GlRRrXma71hdA8EES3H7f"},"signature":""}],"memo":""}} diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno index 1b15bc21c29..7366494825f 100644 --- a/gnovm/stdlibs/std/native.gno +++ b/gnovm/stdlibs/std/native.gno @@ -1,7 +1,7 @@ package std // AssertOriginCall panics if the calling method is not invoked via a direct -// MsgCall. It panics for for other cases, like if the calling method +// MsgCall. It panics for other cases, like if the calling method // is invoked by another method (even from the same realm or package). // It also panic every time when the transaction is broadcasted via // MsgRun.